/ethica130820_maquette/src/jx9.c

https://github.com/vrx/ethica · C · 44386 lines · 36159 code · 130 blank · 8097 comment · 4958 complexity · dfa6379633ff9bb33331493c0d91a612 MD5 · raw file

  1. /*
  2. * Symisc Jx9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
  3. * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
  4. * Version 1.7.2
  5. * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  6. * please contact Symisc Systems via:
  7. * legal@symisc.net
  8. * licensing@symisc.net
  9. * contact@symisc.net
  10. * or visit:
  11. * http://jx9.symisc.net/
  12. */
  13. /*
  14. * Copyright (C) 2012, 2013 Symisc Systems. All rights reserved.
  15. *
  16. * Redistribution and use in source and binary forms, with or without
  17. * modification, are permitted provided that the following conditions
  18. * are met:
  19. * 1. Redistributions of source code must retain the above copyright
  20. * notice, this list of conditions and the following disclaimer.
  21. * 2. Redistributions in binary form must reproduce the above copyright
  22. * notice, this list of conditions and the following disclaimer in the
  23. * documentation and/or other materials provided with the distribution.
  24. * 3. Redistributions in any form must be accompanied by information on
  25. * how to obtain complete source code for the JX9 engine and any
  26. * accompanying software that uses the JX9 engine software.
  27. * The source code must either be included in the distribution
  28. * or be available for no more than the cost of distribution plus
  29. * a nominal fee, and must be freely redistributable under reasonable
  30. * conditions. For an executable file, complete source code means
  31. * the source code for all modules it contains.It does not include
  32. * source code for modules or files that typically accompany the major
  33. * components of the operating system on which the executable file runs.
  34. *
  35. * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS
  36. * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  37. * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
  38. * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SYMISC SYSTEMS
  39. * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  40. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  41. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
  42. * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  43. * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
  44. * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
  45. * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  46. */
  47. /*
  48. * $SymiscID: jx9.c v1.7 UNIX|Win32/64 2013-01-04 09:00 stable <chm@symisc.net> $
  49. */
  50. /* This file is an amalgamation of many separate C source files from JX9 version 1.7.2
  51. * By combining all the individual C code files into this single large file, the entire code
  52. * can be compiled as a single translation unit. This allows many compilers to do optimization's
  53. * that would not be possible if the files were compiled separately. Performance improvements
  54. * are commonly seen when JX9 is compiled as a single translation unit.
  55. *
  56. * This file is all you need to compile JX9. To use JX9 in other programs, you need
  57. * this file and the "jx9.h" header file that defines the programming interface to the
  58. * JX9 engine.(If you do not have the "jx9.h" header file at hand, you will find
  59. * a copy embedded within the text of this file.Search for "Header file: <jx9.h>" to find
  60. * the start of the embedded jx9.h header file.) Additional code files may be needed if
  61. * you want a wrapper to interface JX9 with your choice of programming language.
  62. * To get the official documentation, please visit http://jx9.symisc.net/
  63. */
  64. /*
  65. * Make the sure the following directive is defined in the amalgamation build.
  66. */
  67. #ifndef JX9_AMALGAMATION
  68. #define JX9_AMALGAMATION
  69. #endif /* JX9_AMALGAMATION */
  70. /*
  71. * Embedded header file for Jx9: <jx9.h>
  72. */
  73. /*
  74. * ----------------------------------------------------------
  75. * File: jx9.h
  76. * MD5: 2b1283746396e4a0a504caa586f398f5
  77. * ----------------------------------------------------------
  78. */
  79. /* This file was automatically generated. Do not edit (except for compile time directive)! */
  80. #ifndef _JX9H_
  81. #define _JX9H_
  82. /*
  83. * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
  84. * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
  85. * Version 1.7.2
  86. * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  87. * please contact Symisc Systems via:
  88. * legal@symisc.net
  89. * licensing@symisc.net
  90. * contact@symisc.net
  91. * or visit:
  92. * http://jx9.symisc.net/
  93. */
  94. /*
  95. * Copyright (C) 2012, 2013 Symisc Systems. All rights reserved.
  96. *
  97. * Redistribution and use in source and binary forms, with or without
  98. * modification, are permitted provided that the following conditions
  99. * are met:
  100. * 1. Redistributions of source code must retain the above copyright
  101. * notice, this list of conditions and the following disclaimer.
  102. * 2. Redistributions in binary form must reproduce the above copyright
  103. * notice, this list of conditions and the following disclaimer in the
  104. * documentation and/or other materials provided with the distribution.
  105. * 3. Redistributions in any form must be accompanied by information on
  106. * how to obtain complete source code for the JX9 engine and any
  107. * accompanying software that uses the JX9 engine software.
  108. * The source code must either be included in the distribution
  109. * or be available for no more than the cost of distribution plus
  110. * a nominal fee, and must be freely redistributable under reasonable
  111. * conditions. For an executable file, complete source code means
  112. * the source code for all modules it contains.It does not include
  113. * source code for modules or files that typically accompany the major
  114. * components of the operating system on which the executable file runs.
  115. *
  116. * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS
  117. * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  118. * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
  119. * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SYMISC SYSTEMS
  120. * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  121. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  122. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
  123. * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  124. * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
  125. * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
  126. * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  127. */
  128. /* $SymiscID: jx9.h v2.1 UNIX|WIN32/64 2012-09-15 09:43 stable <chm@symisc.net> $ */
  129. #include <stdarg.h> /* needed for the definition of va_list */
  130. /*
  131. * Compile time engine version, signature, identification in the symisc source tree
  132. * and copyright notice.
  133. * Each macro have an equivalent C interface associated with it that provide the same
  134. * information but are associated with the library instead of the header file.
  135. * Refer to [jx9_lib_version()], [jx9_lib_signature()], [jx9_lib_ident()] and
  136. * [jx9_lib_copyright()] for more information.
  137. */
  138. /*
  139. * The JX9_VERSION C preprocessor macroevaluates to a string literal
  140. * that is the jx9 version in the format "X.Y.Z" where X is the major
  141. * version number and Y is the minor version number and Z is the release
  142. * number.
  143. */
  144. #define JX9_VERSION "1.7.2"
  145. /*
  146. * The JX9_VERSION_NUMBER C preprocessor macro resolves to an integer
  147. * with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same
  148. * numbers used in [JX9_VERSION].
  149. */
  150. #define JX9_VERSION_NUMBER 1007002
  151. /*
  152. * The JX9_SIG C preprocessor macro evaluates to a string
  153. * literal which is the public signature of the jx9 engine.
  154. * This signature could be included for example in a host-application
  155. * generated Server MIME header as follows:
  156. * Server: YourWebServer/x.x Jx9/x.x.x \r\n
  157. */
  158. #define JX9_SIG "Jx9/1.7.2"
  159. /*
  160. * JX9 identification in the Symisc source tree:
  161. * Each particular check-in of a particular software released
  162. * by symisc systems have an unique identifier associated with it.
  163. * This macro hold the one associated with jx9.
  164. */
  165. #define JX9_IDENT "jx9:d217a6e8c7f10fb35a8becb2793101fd2036aeb7"
  166. /*
  167. * Copyright notice.
  168. * If you have any questions about the licensing situation, please
  169. * visit http://jx9.symisc.net/licensing.html
  170. * or contact Symisc Systems via:
  171. * legal@symisc.net
  172. * licensing@symisc.net
  173. * contact@symisc.net
  174. */
  175. #define JX9_COPYRIGHT "Copyright (C) Symisc Systems 2012-2013, http://jx9.symisc.net/"
  176. /* Make sure we can call this stuff from C++ */
  177. #ifdef __cplusplus
  178. extern "C" {
  179. #endif
  180. /* Forward declaration to public objects */
  181. typedef struct jx9_io_stream jx9_io_stream;
  182. typedef struct jx9_context jx9_context;
  183. typedef struct jx9_value jx9_value;
  184. typedef struct jx9_vfs jx9_vfs;
  185. typedef struct jx9_vm jx9_vm;
  186. typedef struct jx9 jx9;
  187. /*
  188. * ------------------------------
  189. * Compile time directives
  190. * ------------------------------
  191. * For most purposes, JX9 can be built just fine using the default compilation options.
  192. * However, if required, the compile-time options documented below can be used to omit
  193. * JX9 features (resulting in a smaller compiled library size) or to change the default
  194. * values of some parameters.
  195. * Every effort has been made to ensure that the various combinations of compilation
  196. * options work harmoniously and produce a working library.
  197. *
  198. * JX9_ENABLE_THREADS
  199. * This option controls whether or not code is included in JX9 to enable it to operate
  200. * safely in a multithreaded environment. The default is not. That is, all mutexing code
  201. * is omitted and it is unsafe to use JX9 in a multithreaded program. When compiled
  202. * with the JX9_ENABLE_THREADS directive enabled, JX9 can be used in a multithreaded
  203. * program and it's safe to share the same virtual machine and engine instance between
  204. * two or more threads.
  205. * The value of JX9_ENABLE_THREADS can be determined at run-time using the
  206. * jx9_lib_is_threadsafe() interface.When JX9 has been compiled with JX9_ENABLE_THREAD
  207. * then the threading mode can be altered at run-time using the jx9_lib_config()
  208. * interface together with one of these verbs:
  209. * JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE
  210. * JX9_LIB_CONFIG_THREAD_LEVEL_MULTI
  211. * Also note, platforms others than Windows and UNIX systems must install their own
  212. * mutex subsystem via jx9_lib_config() with a configuration verb set to
  213. * JX9_LIB_CONFIG_USER_MUTEX. Otherwise the library is not threadsafe.
  214. * Note that you must link JX9 with the POSIX threads library under UNIX-like systems
  215. * (i.e: -lpthread).Otherwise you will get a link time error.
  216. * Options To Omit/Enable Features:
  217. * The following options can be used to reduce the size of the compiled library
  218. * by omitting optional features. This is probably only useful in embedded systems
  219. * where space is especially tight, as even with all features included the JX9 library
  220. * is relatively small. Don't forget to tell your compiler to optimize for binary
  221. * size! (the -Os option if using GCC).
  222. * Telling your compiler to optimize for size usually has a much larger impact
  223. * on library footprint than employing any of these compile-time options.
  224. * JX9_DISABLE_BUILTIN_FUNC
  225. * JX9 come with more than 460 built-in functions suitable for most purposes ranging
  226. * from string/XML/INI processing to ZIP extracting, Base64 encoding/decoding and so on.
  227. * If this directive is enabled, all the built-in functions are omitted from the build.
  228. * Note that language construct functions such as is_int(), is_string(), func_get_arg(),
  229. * define(), etc. are not omitted from the build and are not affected by this directive.
  230. * JX9_ENABLE_MATH_FUNC
  231. * If this directive is enabled, built-in math functions such as sqrt(), abs(),
  232. * log(), ceil(), etc. are included in the build. Note that you may need to link
  233. * JX9 with the math library in same linux/BSD flavor (i.e: -lm).Otherwise you
  234. * will get a link time error.
  235. * JX9_DISABLE_DISK_IO
  236. * If this directive is enabled, built-in Virtual File System functions such as
  237. * chdir(), mkdir(), chroot(), unlink(), delete(), etc. are omitted from the build.
  238. * JX9_DISABLE_HASH_IO
  239. * If this directive is enabled, built-in hash functions such as md5(), sha1(),
  240. * md5_file(), crc32(), etc. are omitted from the build.
  241. * JX9_OMIT_FLOATING_POINT
  242. * This option is used to omit floating-point number support from the JX9 library
  243. * if compiling for a processor that lacks floating point support. When specified
  244. * the library will substitute 64-bit integer arithmetic for floating-point which
  245. * mean that 25.e-3 and 25 are equals and are of type integer.
  246. */
  247. /* Symisc public definitions */
  248. #if !defined(SYMISC_STANDARD_DEFS)
  249. #define SYMISC_STANDARD_DEFS
  250. #if defined (_WIN32) || defined (WIN32) || defined(__MINGW32__) || defined (_MSC_VER) || defined (_WIN32_WCE)
  251. /* Windows Systems */
  252. #if !defined(__WINNT__)
  253. #define __WINNT__
  254. #endif
  255. #else
  256. /*
  257. * By default we will assume that we are compiling on a UNIX systems.
  258. * Otherwise the OS_OTHER directive must be defined.
  259. */
  260. #if !defined(OS_OTHER)
  261. #if !defined(__UNIXES__)
  262. #define __UNIXES__
  263. #endif /* __UNIXES__ */
  264. #else
  265. #endif /* OS_OTHER */
  266. #endif /* __WINNT__/__UNIXES__ */
  267. #if defined(_MSC_VER) || defined(__BORLANDC__)
  268. typedef signed __int64 sxi64; /* 64 bits(8 bytes) signed int64 */
  269. typedef unsigned __int64 sxu64; /* 64 bits(8 bytes) unsigned int64 */
  270. #else
  271. typedef signed long long int sxi64; /* 64 bits(8 bytes) signed int64 */
  272. typedef unsigned long long int sxu64; /* 64 bits(8 bytes) unsigned int64 */
  273. #endif /* _MSC_VER */
  274. /* Signature of the consumer routine */
  275. typedef int (*ProcConsumer)(const void *, unsigned int, void *);
  276. /* Forward reference */
  277. typedef struct SyMutexMethods SyMutexMethods;
  278. typedef struct SyMemMethods SyMemMethods;
  279. typedef struct SyString SyString;
  280. typedef struct syiovec syiovec;
  281. typedef struct SyMutex SyMutex;
  282. typedef struct Sytm Sytm;
  283. /* Scatter and gather array. */
  284. struct syiovec
  285. {
  286. #if defined (__WINNT__)
  287. /* Same fields type and offset as WSABUF structure defined one winsock2 header */
  288. unsigned long nLen;
  289. char *pBase;
  290. #else
  291. void *pBase;
  292. unsigned long nLen;
  293. #endif
  294. };
  295. struct SyString
  296. {
  297. const char *zString; /* Raw string (may not be null terminated) */
  298. unsigned int nByte; /* Raw string length */
  299. };
  300. /* Time structure. */
  301. struct Sytm
  302. {
  303. int tm_sec; /* seconds (0 - 60) */
  304. int tm_min; /* minutes (0 - 59) */
  305. int tm_hour; /* hours (0 - 23) */
  306. int tm_mday; /* day of month (1 - 31) */
  307. int tm_mon; /* month of year (0 - 11) */
  308. int tm_year; /* year + 1900 */
  309. int tm_wday; /* day of week (Sunday = 0) */
  310. int tm_yday; /* day of year (0 - 365) */
  311. int tm_isdst; /* is summer time in effect? */
  312. char *tm_zone; /* abbreviation of timezone name */
  313. long tm_gmtoff; /* offset from UTC in seconds */
  314. };
  315. /* Convert a tm structure (struct tm *) found in <time.h> to a Sytm structure */
  316. #define STRUCT_TM_TO_SYTM(pTM, pSYTM) \
  317. (pSYTM)->tm_hour = (pTM)->tm_hour;\
  318. (pSYTM)->tm_min = (pTM)->tm_min;\
  319. (pSYTM)->tm_sec = (pTM)->tm_sec;\
  320. (pSYTM)->tm_mon = (pTM)->tm_mon;\
  321. (pSYTM)->tm_mday = (pTM)->tm_mday;\
  322. (pSYTM)->tm_year = (pTM)->tm_year + 1900;\
  323. (pSYTM)->tm_yday = (pTM)->tm_yday;\
  324. (pSYTM)->tm_wday = (pTM)->tm_wday;\
  325. (pSYTM)->tm_isdst = (pTM)->tm_isdst;\
  326. (pSYTM)->tm_gmtoff = 0;\
  327. (pSYTM)->tm_zone = 0;
  328. /* Convert a SYSTEMTIME structure (LPSYSTEMTIME: Windows Systems only ) to a Sytm structure */
  329. #define SYSTEMTIME_TO_SYTM(pSYSTIME, pSYTM) \
  330. (pSYTM)->tm_hour = (pSYSTIME)->wHour;\
  331. (pSYTM)->tm_min = (pSYSTIME)->wMinute;\
  332. (pSYTM)->tm_sec = (pSYSTIME)->wSecond;\
  333. (pSYTM)->tm_mon = (pSYSTIME)->wMonth - 1;\
  334. (pSYTM)->tm_mday = (pSYSTIME)->wDay;\
  335. (pSYTM)->tm_year = (pSYSTIME)->wYear;\
  336. (pSYTM)->tm_yday = 0;\
  337. (pSYTM)->tm_wday = (pSYSTIME)->wDayOfWeek;\
  338. (pSYTM)->tm_gmtoff = 0;\
  339. (pSYTM)->tm_isdst = -1;\
  340. (pSYTM)->tm_zone = 0;
  341. /* Dynamic memory allocation methods. */
  342. struct SyMemMethods
  343. {
  344. void * (*xAlloc)(unsigned int); /* [Required:] Allocate a memory chunk */
  345. void * (*xRealloc)(void *, unsigned int); /* [Required:] Re-allocate a memory chunk */
  346. void (*xFree)(void *); /* [Required:] Release a memory chunk */
  347. unsigned int (*xChunkSize)(void *); /* [Optional:] Return chunk size */
  348. int (*xInit)(void *); /* [Optional:] Initialization callback */
  349. void (*xRelease)(void *); /* [Optional:] Release callback */
  350. void *pUserData; /* [Optional:] First argument to xInit() and xRelease() */
  351. };
  352. /* Out of memory callback signature. */
  353. typedef int (*ProcMemError)(void *);
  354. /* Mutex methods. */
  355. struct SyMutexMethods
  356. {
  357. int (*xGlobalInit)(void); /* [Optional:] Global mutex initialization */
  358. void (*xGlobalRelease)(void); /* [Optional:] Global Release callback () */
  359. SyMutex * (*xNew)(int); /* [Required:] Request a new mutex */
  360. void (*xRelease)(SyMutex *); /* [Optional:] Release a mutex */
  361. void (*xEnter)(SyMutex *); /* [Required:] Enter mutex */
  362. int (*xTryEnter)(SyMutex *); /* [Optional:] Try to enter a mutex */
  363. void (*xLeave)(SyMutex *); /* [Required:] Leave a locked mutex */
  364. };
  365. #if defined (_MSC_VER) || defined (__MINGW32__) || defined (__GNUC__) && defined (__declspec)
  366. #define SX_APIIMPORT __declspec(dllimport)
  367. #define SX_APIEXPORT __declspec(dllexport)
  368. #else
  369. #define SX_APIIMPORT
  370. #define SX_APIEXPORT
  371. #endif
  372. /* Standard return values from Symisc public interfaces */
  373. #define SXRET_OK 0 /* Not an error */
  374. #define SXERR_MEM (-1) /* Out of memory */
  375. #define SXERR_IO (-2) /* IO error */
  376. #define SXERR_EMPTY (-3) /* Empty field */
  377. #define SXERR_LOCKED (-4) /* Locked operation */
  378. #define SXERR_ORANGE (-5) /* Out of range value */
  379. #define SXERR_NOTFOUND (-6) /* Item not found */
  380. #define SXERR_LIMIT (-7) /* Limit reached */
  381. #define SXERR_MORE (-8) /* Need more input */
  382. #define SXERR_INVALID (-9) /* Invalid parameter */
  383. #define SXERR_ABORT (-10) /* User callback request an operation abort */
  384. #define SXERR_EXISTS (-11) /* Item exists */
  385. #define SXERR_SYNTAX (-12) /* Syntax error */
  386. #define SXERR_UNKNOWN (-13) /* Unknown error */
  387. #define SXERR_BUSY (-14) /* Busy operation */
  388. #define SXERR_OVERFLOW (-15) /* Stack or buffer overflow */
  389. #define SXERR_WILLBLOCK (-16) /* Operation will block */
  390. #define SXERR_NOTIMPLEMENTED (-17) /* Operation not implemented */
  391. #define SXERR_EOF (-18) /* End of input */
  392. #define SXERR_PERM (-19) /* Permission error */
  393. #define SXERR_NOOP (-20) /* No-op */
  394. #define SXERR_FORMAT (-21) /* Invalid format */
  395. #define SXERR_NEXT (-22) /* Not an error */
  396. #define SXERR_OS (-23) /* System call return an error */
  397. #define SXERR_CORRUPT (-24) /* Corrupted pointer */
  398. #define SXERR_CONTINUE (-25) /* Not an error: Operation in progress */
  399. #define SXERR_NOMATCH (-26) /* No match */
  400. #define SXERR_RESET (-27) /* Operation reset */
  401. #define SXERR_DONE (-28) /* Not an error */
  402. #define SXERR_SHORT (-29) /* Buffer too short */
  403. #define SXERR_PATH (-30) /* Path error */
  404. #define SXERR_TIMEOUT (-31) /* Timeout */
  405. #define SXERR_BIG (-32) /* Too big for processing */
  406. #define SXERR_RETRY (-33) /* Retry your call */
  407. #define SXERR_IGNORE (-63) /* Ignore */
  408. #endif /* SYMISC_PUBLIC_DEFS */
  409. /* Standard JX9 return values */
  410. #define JX9_OK SXRET_OK /* Successful result */
  411. /* beginning-of-error-codes */
  412. #define JX9_NOMEM SXERR_MEM /* Out of memory */
  413. #define JX9_ABORT SXERR_ABORT /* Foreign Function request operation abort/Another thread have released this instance */
  414. #define JX9_IO_ERR SXERR_IO /* IO error */
  415. #define JX9_CORRUPT SXERR_CORRUPT /* Corrupt pointer/Unknown configuration option */
  416. #define JX9_LOOKED SXERR_LOCKED /* Forbidden Operation */
  417. #define JX9_COMPILE_ERR (-70) /* Compilation error */
  418. #define JX9_VM_ERR (-71) /* Virtual machine error */
  419. /* end-of-error-codes */
  420. /*
  421. * If compiling for a processor that lacks floating point
  422. * support, substitute integer for floating-point.
  423. */
  424. #ifdef JX9_OMIT_FLOATING_POINT
  425. typedef sxi64 jx9_real;
  426. #else
  427. typedef double jx9_real;
  428. #endif
  429. typedef sxi64 jx9_int64;
  430. #define JX9_APIEXPORT SX_APIEXPORT
  431. /*
  432. * Engine Configuration Commands.
  433. *
  434. * The following set of constants are the available configuration verbs that can
  435. * be used by the host-application to configure the JX9 engine.
  436. * These constants must be passed as the second argument to the [jx9_config()]
  437. * interface.
  438. * Each options require a variable number of arguments.
  439. * The [jx9_config()] interface will return JX9_OK on success, any other
  440. * return value indicates failure.
  441. * For a full discussion on the configuration verbs and their expected
  442. * parameters, please refer to this page:
  443. * http://jx9.symisc.net/c_api_func.html#jx9_config
  444. */
  445. #define JX9_CONFIG_ERR_ABORT 1 /* RESERVED FOR FUTURE USE */
  446. #define JX9_CONFIG_ERR_LOG 2 /* TWO ARGUMENTS: const char **pzBuf, int *pLen */
  447. /*
  448. * Virtual Machine Configuration Commands.
  449. *
  450. * The following set of constants are the available configuration verbs that can
  451. * be used by the host-application to configure the JX9 Virtual machine.
  452. * These constants must be passed as the second argument to the [jx9_vm_config()]
  453. * interface.
  454. * Each options require a variable number of arguments.
  455. * The [jx9_vm_config()] interface will return JX9_OK on success, any other return
  456. * value indicates failure.
  457. * There are many options but the most importants are: JX9_VM_CONFIG_OUTPUT which install
  458. * a VM output consumer callback, JX9_VM_CONFIG_HTTP_REQUEST which parse and register
  459. * a HTTP request and JX9_VM_CONFIG_ARGV_ENTRY which populate the $argv array.
  460. * For a full discussion on the configuration verbs and their expected parameters, please
  461. * refer to this page:
  462. * http://jx9.symisc.net/c_api_func.html#jx9_vm_config
  463. */
  464. #define JX9_VM_CONFIG_OUTPUT 1 /* TWO ARGUMENTS: int (*xConsumer)(const void *pOut, unsigned int nLen, void *pUserData), void *pUserData */
  465. #define JX9_VM_CONFIG_IMPORT_PATH 2 /* ONE ARGUMENT: const char *zIncludePath */
  466. #define JX9_VM_CONFIG_ERR_REPORT 3 /* NO ARGUMENTS: Report all run-time errors in the VM output */
  467. #define JX9_VM_CONFIG_RECURSION_DEPTH 4 /* ONE ARGUMENT: int nMaxDepth */
  468. #define JX9_VM_OUTPUT_LENGTH 5 /* ONE ARGUMENT: unsigned int *pLength */
  469. #define JX9_VM_CONFIG_CREATE_VAR 6 /* TWO ARGUMENTS: const char *zName, jx9_value *pValue */
  470. #define JX9_VM_CONFIG_HTTP_REQUEST 7 /* TWO ARGUMENTS: const char *zRawRequest, int nRequestLength */
  471. #define JX9_VM_CONFIG_SERVER_ATTR 8 /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
  472. #define JX9_VM_CONFIG_ENV_ATTR 9 /* THREE ARGUMENTS: const char *zKey, const char *zValue, int nLen */
  473. #define JX9_VM_CONFIG_EXEC_VALUE 10 /* ONE ARGUMENT: jx9_value **ppValue */
  474. #define JX9_VM_CONFIG_IO_STREAM 11 /* ONE ARGUMENT: const jx9_io_stream *pStream */
  475. #define JX9_VM_CONFIG_ARGV_ENTRY 12 /* ONE ARGUMENT: const char *zValue */
  476. #define JX9_VM_CONFIG_EXTRACT_OUTPUT 13 /* TWO ARGUMENTS: const void **ppOut, unsigned int *pOutputLen */
  477. /*
  478. * Global Library Configuration Commands.
  479. *
  480. * The following set of constants are the available configuration verbs that can
  481. * be used by the host-application to configure the whole library.
  482. * These constants must be passed as the first argument to the [jx9_lib_config()]
  483. * interface.
  484. * Each options require a variable number of arguments.
  485. * The [jx9_lib_config()] interface will return JX9_OK on success, any other return
  486. * value indicates failure.
  487. * Notes:
  488. * The default configuration is recommended for most applications and so the call to
  489. * [jx9_lib_config()] is usually not necessary. It is provided to support rare
  490. * applications with unusual needs.
  491. * The [jx9_lib_config()] interface is not threadsafe. The application must insure that
  492. * no other [jx9_*()] interfaces are invoked by other threads while [jx9_lib_config()]
  493. * is running. Furthermore, [jx9_lib_config()] may only be invoked prior to library
  494. * initialization using [jx9_lib_init()] or [jx9_init()] or after shutdown
  495. * by [jx9_lib_shutdown()]. If [jx9_lib_config()] is called after [jx9_lib_init()]
  496. * or [jx9_init()] and before [jx9_lib_shutdown()] then it will return jx9LOCKED.
  497. * For a full discussion on the configuration verbs and their expected parameters, please
  498. * refer to this page:
  499. * http://jx9.symisc.net/c_api_func.html#Global_Library_Management_Interfaces
  500. */
  501. #define JX9_LIB_CONFIG_USER_MALLOC 1 /* ONE ARGUMENT: const SyMemMethods *pMemMethods */
  502. #define JX9_LIB_CONFIG_MEM_ERR_CALLBACK 2 /* TWO ARGUMENTS: int (*xMemError)(void *), void *pUserData */
  503. #define JX9_LIB_CONFIG_USER_MUTEX 3 /* ONE ARGUMENT: const SyMutexMethods *pMutexMethods */
  504. #define JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE 4 /* NO ARGUMENTS */
  505. #define JX9_LIB_CONFIG_THREAD_LEVEL_MULTI 5 /* NO ARGUMENTS */
  506. #define JX9_LIB_CONFIG_VFS 6 /* ONE ARGUMENT: const jx9_vfs *pVfs */
  507. /*
  508. * Call Context Error Message Serverity Level.
  509. *
  510. * The following constans are the allowed severity level that can
  511. * passed as the second argument to the [jx9_context_throw_error()] or
  512. * [jx9_context_throw_error_format()] interfaces.
  513. * Refer to the official documentation for additional information.
  514. */
  515. #define JX9_CTX_ERR 1 /* Call context error such as unexpected number of arguments, invalid types and so on. */
  516. #define JX9_CTX_WARNING 2 /* Call context Warning */
  517. #define JX9_CTX_NOTICE 3 /* Call context Notice */
  518. /* Current VFS structure version*/
  519. #define JX9_VFS_VERSION 2
  520. /*
  521. * JX9 Virtual File System (VFS).
  522. *
  523. * An instance of the jx9_vfs object defines the interface between the JX9 core
  524. * and the underlying operating system. The "vfs" in the name of the object stands
  525. * for "virtual file system". The vfs is used to implement JX9 system functions
  526. * such as mkdir(), chdir(), stat(), get_user_name() and many more.
  527. * The value of the iVersion field is initially 2 but may be larger in future versions
  528. * of JX9.
  529. * Additional fields may be appended to this object when the iVersion value is increased.
  530. * Only a single vfs can be registered within the JX9 core. Vfs registration is done
  531. * using the jx9_lib_config() interface with a configuration verb set to JX9_LIB_CONFIG_VFS.
  532. * Note that Windows and UNIX (Linux, FreeBSD, Solaris, Mac OS X, etc.) users does not have to
  533. * worry about registering and installing a vfs since JX9 come with a built-in vfs for these
  534. * platforms which implement most the methods defined below.
  535. * Host-application running on exotic systems (ie: Other than Windows and UNIX systems) must
  536. * register their own vfs in order to be able to use and call JX9 system functions.
  537. * Also note that the jx9_compile_file() interface depend on the xMmap() method of the underlying
  538. * vfs which mean that this method must be available (Always the case using the built-in VFS)
  539. * in order to use this interface.
  540. * Developers wishing to implement their own vfs an contact symisc systems to obtain
  541. * the JX9 VFS C/C++ Specification manual.
  542. */
  543. struct jx9_vfs
  544. {
  545. const char *zName; /* Underlying VFS name [i.e: FreeBSD/Linux/Windows...] */
  546. int iVersion; /* Current VFS structure version [default 2] */
  547. /* Directory functions */
  548. int (*xChdir)(const char *); /* Change directory */
  549. int (*xChroot)(const char *); /* Change the root directory */
  550. int (*xGetcwd)(jx9_context *); /* Get the current working directory */
  551. int (*xMkdir)(const char *, int, int); /* Make directory */
  552. int (*xRmdir)(const char *); /* Remove directory */
  553. int (*xIsdir)(const char *); /* Tells whether the filename is a directory */
  554. int (*xRename)(const char *, const char *); /* Renames a file or directory */
  555. int (*xRealpath)(const char *, jx9_context *); /* Return canonicalized absolute pathname*/
  556. /* Systems functions */
  557. int (*xSleep)(unsigned int); /* Delay execution in microseconds */
  558. int (*xUnlink)(const char *); /* Deletes a file */
  559. int (*xFileExists)(const char *); /* Checks whether a file or directory exists */
  560. int (*xChmod)(const char *, int); /* Changes file mode */
  561. int (*xChown)(const char *, const char *); /* Changes file owner */
  562. int (*xChgrp)(const char *, const char *); /* Changes file group */
  563. jx9_int64 (*xFreeSpace)(const char *); /* Available space on filesystem or disk partition */
  564. jx9_int64 (*xTotalSpace)(const char *); /* Total space on filesystem or disk partition */
  565. jx9_int64 (*xFileSize)(const char *); /* Gets file size */
  566. jx9_int64 (*xFileAtime)(const char *); /* Gets last access time of file */
  567. jx9_int64 (*xFileMtime)(const char *); /* Gets file modification time */
  568. jx9_int64 (*xFileCtime)(const char *); /* Gets inode change time of file */
  569. int (*xStat)(const char *, jx9_value *, jx9_value *); /* Gives information about a file */
  570. int (*xlStat)(const char *, jx9_value *, jx9_value *); /* Gives information about a file */
  571. int (*xIsfile)(const char *); /* Tells whether the filename is a regular file */
  572. int (*xIslink)(const char *); /* Tells whether the filename is a symbolic link */
  573. int (*xReadable)(const char *); /* Tells whether a file exists and is readable */
  574. int (*xWritable)(const char *); /* Tells whether the filename is writable */
  575. int (*xExecutable)(const char *); /* Tells whether the filename is executable */
  576. int (*xFiletype)(const char *, jx9_context *); /* Gets file type [i.e: fifo, dir, file..] */
  577. int (*xGetenv)(const char *, jx9_context *); /* Gets the value of an environment variable */
  578. int (*xSetenv)(const char *, const char *); /* Sets the value of an environment variable */
  579. int (*xTouch)(const char *, jx9_int64, jx9_int64); /* Sets access and modification time of file */
  580. int (*xMmap)(const char *, void **, jx9_int64 *); /* Read-only memory map of the whole file */
  581. void (*xUnmap)(void *, jx9_int64); /* Unmap a memory view */
  582. int (*xLink)(const char *, const char *, int); /* Create hard or symbolic link */
  583. int (*xUmask)(int); /* Change the current umask */
  584. void (*xTempDir)(jx9_context *); /* Get path of the temporary directory */
  585. unsigned int (*xProcessId)(void); /* Get running process ID */
  586. int (*xUid)(void); /* user ID of the process */
  587. int (*xGid)(void); /* group ID of the process */
  588. void (*xUsername)(jx9_context *); /* Running username */
  589. int (*xExec)(const char *, jx9_context *); /* Execute an external program */
  590. };
  591. /* Current JX9 IO stream structure version. */
  592. #define JX9_IO_STREAM_VERSION 1
  593. /*
  594. * Possible open mode flags that can be passed to the xOpen() routine
  595. * of the underlying IO stream device .
  596. * Refer to the JX9 IO Stream C/C++ specification manual (http://jx9.symisc.net/io_stream_spec.html)
  597. * for additional information.
  598. */
  599. #define JX9_IO_OPEN_RDONLY 0x001 /* Read-only open */
  600. #define JX9_IO_OPEN_WRONLY 0x002 /* Write-only open */
  601. #define JX9_IO_OPEN_RDWR 0x004 /* Read-write open. */
  602. #define JX9_IO_OPEN_CREATE 0x008 /* If the file does not exist it will be created */
  603. #define JX9_IO_OPEN_TRUNC 0x010 /* Truncate the file to zero length */
  604. #define JX9_IO_OPEN_APPEND 0x020 /* Append mode.The file offset is positioned at the end of the file */
  605. #define JX9_IO_OPEN_EXCL 0x040 /* Ensure that this call creates the file, the file must not exist before */
  606. #define JX9_IO_OPEN_BINARY 0x080 /* Simple hint: Data is binary */
  607. #define JX9_IO_OPEN_TEMP 0x100 /* Simple hint: Temporary file */
  608. #define JX9_IO_OPEN_TEXT 0x200 /* Simple hint: Data is textual */
  609. /*
  610. * JX9 IO Stream Device.
  611. *
  612. * An instance of the jx9_io_stream object defines the interface between the JX9 core
  613. * and the underlying stream device.
  614. * A stream is a smart mechanism for generalizing file, network, data compression
  615. * and other IO operations which share a common set of functions using an abstracted
  616. * unified interface.
  617. * A stream device is additional code which tells the stream how to handle specific
  618. * protocols/encodings. For example, the http device knows how to translate a URL
  619. * into an HTTP/1.1 request for a file on a remote server.
  620. * JX9 come with two built-in IO streams device:
  621. * The file:// stream which perform very efficient disk IO and the jx9:// stream
  622. * which is a special stream that allow access various I/O streams (See the JX9 official
  623. * documentation for more information on this stream).
  624. * A stream is referenced as: scheme://target
  625. * scheme(string) - The name of the wrapper to be used. Examples include: file, http, https, ftp,
  626. * ftps, compress.zlib, compress.bz2, and jx9. If no wrapper is specified, the function default
  627. * is used (typically file://).
  628. * target - Depends on the device used. For filesystem related streams this is typically a path
  629. * and filename of the desired file.For network related streams this is typically a hostname, often
  630. * with a path appended.
  631. * IO stream devices are registered using a call to jx9_vm_config() with a configuration verb
  632. * set to JX9_VM_CONFIG_IO_STREAM.
  633. * Currently the JX9 development team is working on the implementation of the http:// and ftp://
  634. * IO stream protocols. These devices will be available in the next major release of the JX9 engine.
  635. * Developers wishing to implement their own IO stream devices must understand and follow
  636. * The JX9 IO Stream C/C++ specification manual (http://jx9.symisc.net/io_stream_spec.html).
  637. */
  638. struct jx9_io_stream
  639. {
  640. const char *zName; /* Underlying stream name [i.e: file/http/zip/jx9, ..] */
  641. int iVersion; /* IO stream structure version [default 1]*/
  642. int (*xOpen)(const char *, int, jx9_value *, void **); /* Open handle*/
  643. int (*xOpenDir)(const char *, jx9_value *, void **); /* Open directory handle */
  644. void (*xClose)(void *); /* Close file handle */
  645. void (*xCloseDir)(void *); /* Close directory handle */
  646. jx9_int64 (*xRead)(void *, void *, jx9_int64); /* Read from the open stream */
  647. int (*xReadDir)(void *, jx9_context *); /* Read entry from directory handle */
  648. jx9_int64 (*xWrite)(void *, const void *, jx9_int64); /* Write to the open stream */
  649. int (*xSeek)(void *, jx9_int64, int); /* Seek on the open stream */
  650. int (*xLock)(void *, int); /* Lock/Unlock the open stream */
  651. void (*xRewindDir)(void *); /* Rewind directory handle */
  652. jx9_int64 (*xTell)(void *); /* Current position of the stream read/write pointer */
  653. int (*xTrunc)(void *, jx9_int64); /* Truncates the open stream to a given length */
  654. int (*xSync)(void *); /* Flush open stream data */
  655. int (*xStat)(void *, jx9_value *, jx9_value *); /* Stat an open stream handle */
  656. };
  657. /*
  658. * C-API-REF: Please refer to the official documentation for interfaces
  659. * purpose and expected parameters.
  660. */
  661. /* Engine Handling Interfaces */
  662. JX9_APIEXPORT int jx9_init(jx9 **ppEngine);
  663. JX9_APIEXPORT int jx9_config(jx9 *pEngine, int nConfigOp, ...);
  664. JX9_APIEXPORT int jx9_release(jx9 *pEngine);
  665. /* Compile Interfaces */
  666. JX9_APIEXPORT int jx9_compile(jx9 *pEngine, const char *zSource, int nLen, jx9_vm **ppOutVm);
  667. JX9_APIEXPORT int jx9_compile_file(jx9 *pEngine, const char *zFilePath, jx9_vm **ppOutVm);
  668. /* Virtual Machine Handling Interfaces */
  669. JX9_APIEXPORT int jx9_vm_config(jx9_vm *pVm, int iConfigOp, ...);
  670. JX9_APIEXPORT int jx9_vm_exec(jx9_vm *pVm, int *pExitStatus);
  671. JX9_APIEXPORT jx9_value * jx9_vm_extract_variable(jx9_vm *pVm,const char *zVarname);
  672. JX9_APIEXPORT int jx9_vm_reset(jx9_vm *pVm);
  673. JX9_APIEXPORT int jx9_vm_release(jx9_vm *pVm);
  674. JX9_APIEXPORT int jx9_vm_dump_v2(jx9_vm *pVm, int (*xConsumer)(const void *, unsigned int, void *), void *pUserData);
  675. /* In-process Extending Interfaces */
  676. JX9_APIEXPORT int jx9_create_function(jx9_vm *pVm, const char *zName, int (*xFunc)(jx9_context *, int, jx9_value **), void *pUserData);
  677. JX9_APIEXPORT int jx9_delete_function(jx9_vm *pVm, const char *zName);
  678. JX9_APIEXPORT int jx9_create_constant(jx9_vm *pVm, const char *zName, void (*xExpand)(jx9_value *, void *), void *pUserData);
  679. JX9_APIEXPORT int jx9_delete_constant(jx9_vm *pVm, const char *zName);
  680. /* Foreign Function Parameter Values */
  681. JX9_APIEXPORT int jx9_value_to_int(jx9_value *pValue);
  682. JX9_APIEXPORT int jx9_value_to_bool(jx9_value *pValue);
  683. JX9_APIEXPORT jx9_int64 jx9_value_to_int64(jx9_value *pValue);
  684. JX9_APIEXPORT double jx9_value_to_double(jx9_value *pValue);
  685. JX9_APIEXPORT const char * jx9_value_to_string(jx9_value *pValue, int *pLen);
  686. JX9_APIEXPORT void * jx9_value_to_resource(jx9_value *pValue);
  687. JX9_APIEXPORT int jx9_value_compare(jx9_value *pLeft, jx9_value *pRight, int bStrict);
  688. /* Setting The Result Of A Foreign Function */
  689. JX9_APIEXPORT int jx9_result_int(jx9_context *pCtx, int iValue);
  690. JX9_APIEXPORT int jx9_result_int64(jx9_context *pCtx, jx9_int64 iValue);
  691. JX9_APIEXPORT int jx9_result_bool(jx9_context *pCtx, int iBool);
  692. JX9_APIEXPORT int jx9_result_double(jx9_context *pCtx, double Value);
  693. JX9_APIEXPORT int jx9_result_null(jx9_context *pCtx);
  694. JX9_APIEXPORT int jx9_result_string(jx9_context *pCtx, const char *zString, int nLen);
  695. JX9_APIEXPORT int jx9_result_string_format(jx9_context *pCtx, const char *zFormat, ...);
  696. JX9_APIEXPORT int jx9_result_value(jx9_context *pCtx, jx9_value *pValue);
  697. JX9_APIEXPORT int jx9_result_resource(jx9_context *pCtx, void *pUserData);
  698. /* Call Context Handling Interfaces */
  699. JX9_APIEXPORT int jx9_context_output(jx9_context *pCtx, const char *zString, int nLen);
  700. JX9_APIEXPORT int jx9_context_output_format(jx9_context *pCtx, const char *zFormat, ...);
  701. JX9_APIEXPORT int jx9_context_throw_error(jx9_context *pCtx, int iErr, const char *zErr);
  702. JX9_APIEXPORT int jx9_context_throw_error_format(jx9_context *pCtx, int iErr, const char *zFormat, ...);
  703. JX9_APIEXPORT unsigned int jx9_context_random_num(jx9_context *pCtx);
  704. JX9_APIEXPORT int jx9_context_random_string(jx9_context *pCtx, char *zBuf, int nBuflen);
  705. JX9_APIEXPORT void * jx9_context_user_data(jx9_context *pCtx);
  706. JX9_APIEXPORT int jx9_context_push_aux_data(jx9_context *pCtx, void *pUserData);
  707. JX9_APIEXPORT void * jx9_context_peek_aux_data(jx9_context *pCtx);
  708. JX9_APIEXPORT void * jx9_context_pop_aux_data(jx9_context *pCtx);
  709. JX9_APIEXPORT unsigned int jx9_context_result_buf_length(jx9_context *pCtx);
  710. JX9_APIEXPORT const char * jx9_function_name(jx9_context *pCtx);
  711. /* Call Context Memory Management Interfaces */
  712. JX9_APIEXPORT void * jx9_context_alloc_chunk(jx9_context *pCtx, unsigned int nByte, int ZeroChunk, int AutoRelease);
  713. JX9_APIEXPORT void * jx9_context_realloc_chunk(jx9_context *pCtx, void *pChunk, unsigned int nByte);
  714. JX9_APIEXPORT void jx9_context_free_chunk(jx9_context *pCtx, void *pChunk);
  715. /* On Demand Dynamically Typed Value Object allocation interfaces */
  716. JX9_APIEXPORT jx9_value * jx9_new_scalar(jx9_vm *pVm);
  717. JX9_APIEXPORT jx9_value * jx9_new_array(jx9_vm *pVm);
  718. JX9_APIEXPORT int jx9_release_value(jx9_vm *pVm, jx9_value *pValue);
  719. JX9_APIEXPORT jx9_value * jx9_context_new_scalar(jx9_context *pCtx);
  720. JX9_APIEXPORT jx9_value * jx9_context_new_array(jx9_context *pCtx);
  721. JX9_APIEXPORT void jx9_context_release_value(jx9_context *pCtx, jx9_value *pValue);
  722. /* Dynamically Typed Value Object Management Interfaces */
  723. JX9_APIEXPORT int jx9_value_int(jx9_value *pVal, int iValue);
  724. JX9_APIEXPORT int jx9_value_int64(jx9_value *pVal, jx9_int64 iValue);
  725. JX9_APIEXPORT int jx9_value_bool(jx9_value *pVal, int iBool);
  726. JX9_APIEXPORT int jx9_value_null(jx9_value *pVal);
  727. JX9_APIEXPORT int jx9_value_double(jx9_value *pVal, double Value);
  728. JX9_APIEXPORT int jx9_value_string(jx9_value *pVal, const char *zString, int nLen);
  729. JX9_APIEXPORT int jx9_value_string_format(jx9_value *pVal, const char *zFormat, ...);
  730. JX9_APIEXPORT int jx9_value_reset_string_cursor(jx9_value *pVal);
  731. JX9_APIEXPORT int jx9_value_resource(jx9_value *pVal, void *pUserData);
  732. JX9_APIEXPORT int jx9_value_release(jx9_value *pVal);
  733. /* JSON Array/Object Management Interfaces */
  734. JX9_APIEXPORT jx9_value * jx9_array_fetch(jx9_value *pArray, const char *zKey, int nByte);
  735. JX9_APIEXPORT int jx9_array_walk(jx9_value *pArray, int (*xWalk)(jx9_value *, jx9_value *, void *), void *pUserData);
  736. JX9_APIEXPORT int jx9_array_add_elem(jx9_value *pArray, jx9_value *pKey, jx9_value *pValue);
  737. JX9_APIEXPORT int jx9_array_add_strkey_elem(jx9_value *pArray, const char *zKey, jx9_value *pValue);
  738. JX9_APIEXPORT unsigned int jx9_array_count(jx9_value *pArray);
  739. /* Dynamically Typed Value Object Query Interfaces */
  740. JX9_APIEXPORT int jx9_value_is_int(jx9_value *pVal);
  741. JX9_APIEXPORT int jx9_value_is_float(jx9_value *pVal);
  742. JX9_APIEXPORT int jx9_value_is_bool(jx9_value *pVal);
  743. JX9_APIEXPORT int jx9_value_is_string(jx9_value *pVal);
  744. JX9_APIEXPORT int jx9_value_is_null(jx9_value *pVal);
  745. JX9_APIEXPORT int jx9_value_is_numeric(jx9_value *pVal);
  746. JX9_APIEXPORT int jx9_value_is_callable(jx9_value *pVal);
  747. JX9_APIEXPORT int jx9_value_is_scalar(jx9_value *pVal);
  748. JX9_APIEXPORT int jx9_value_is_json_array(jx9_value *pVal);
  749. JX9_APIEXPORT int jx9_value_is_json_object(jx9_value *pVal);
  750. JX9_APIEXPORT int jx9_value_is_resource(jx9_value *pVal);
  751. JX9_APIEXPORT int jx9_value_is_empty(jx9_value *pVal);
  752. /* Global Library Management Interfaces */
  753. JX9_APIEXPORT int jx9_lib_init(void);
  754. JX9_APIEXPORT int jx9_lib_config(int nConfigOp, ...);
  755. JX9_APIEXPORT int jx9_lib_shutdown(void);
  756. JX9_APIEXPORT int jx9_lib_is_threadsafe(void);
  757. JX9_APIEXPORT const char * jx9_lib_version(void);
  758. JX9_APIEXPORT const char * jx9_lib_signature(void);
  759. JX9_APIEXPORT const char * jx9_lib_ident(void);
  760. JX9_APIEXPORT const char * jx9_lib_copyright(void);
  761. #ifdef __cplusplus
  762. }
  763. #endif /* __cplusplus */
  764. #endif /* _JX9H_ */
  765. /*
  766. * ----------------------------------------------------------
  767. * File: jx9Int.h
  768. * MD5: e8105144ba748be0e69264041eb7def2
  769. * ----------------------------------------------------------
  770. */
  771. /*
  772. * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
  773. * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
  774. * Version 1.7.2
  775. * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  776. * please contact Symisc Systems via:
  777. * legal@symisc.net
  778. * licensing@symisc.net
  779. * contact@symisc.net
  780. * or visit:
  781. * http://jx9.symisc.net/
  782. */
  783. /* $SymiscID: jx9Int.h v1.9 FreeBSD 2012-08-13 23:25 devel <chm@symisc.net> $ */
  784. #ifndef __JX9INT_H__
  785. #define __JX9INT_H__
  786. /* Internal interface definitions for JX9. */
  787. #ifdef JX9_AMALGAMATION
  788. /* Marker for routines not intended for external use */
  789. #define JX9_PRIVATE static
  790. #else
  791. #define JX9_PRIVATE
  792. #include "jx9.h"
  793. #endif
  794. #ifndef JX9_PI
  795. /* Value of PI */
  796. #define JX9_PI 3.1415926535898
  797. #endif
  798. /*
  799. * Constants for the largest and smallest possible 64-bit signed integers.
  800. * These macros are designed to work correctly on both 32-bit and 64-bit
  801. * compilers.
  802. */
  803. #ifndef LARGEST_INT64
  804. #define LARGEST_INT64 (0xffffffff|(((sxi64)0x7fffffff)<<32))
  805. #endif
  806. #ifndef SMALLEST_INT64
  807. #define SMALLEST_INT64 (((sxi64)-1) - LARGEST_INT64)
  808. #endif
  809. /* Forward declaration of private structures */
  810. typedef struct jx9_foreach_info jx9_foreach_info;
  811. typedef struct jx9_foreach_step jx9_foreach_step;
  812. typedef struct jx9_hashmap_node jx9_hashmap_node;
  813. typedef struct jx9_hashmap jx9_hashmap;
  814. /* Symisc Standard types */
  815. #if !defined(SYMISC_STD_TYPES)
  816. #define SYMISC_STD_TYPES
  817. #ifdef __WINNT__
  818. /* Disable nuisance warnings on Borland compilers */
  819. #if defined(__BORLANDC__)
  820. #pragma warn -rch /* unreachable code */
  821. #pragma warn -ccc /* Condition is always true or false */
  822. #pragma warn -aus /* Assigned value is never used */
  823. #pragma warn -csu /* Comparing signed and unsigned */
  824. #pragma warn -spa /* Suspicious pointer arithmetic */
  825. #endif
  826. #endif
  827. typedef signed char sxi8; /* signed char */
  828. typedef unsigned char sxu8; /* unsigned char */
  829. typedef signed short int sxi16; /* 16 bits(2 bytes) signed integer */
  830. typedef unsigned short int sxu16; /* 16 bits(2 bytes) unsigned integer */
  831. typedef int sxi32; /* 32 bits(4 bytes) integer */
  832. typedef unsigned int sxu32; /* 32 bits(4 bytes) unsigned integer */
  833. typedef long sxptr;
  834. typedef unsigned long sxuptr;
  835. typedef long sxlong;
  836. typedef unsigned long sxulong;
  837. typedef sxi32 sxofft;
  838. typedef sxi64 sxofft64;
  839. typedef long double sxlongreal;
  840. typedef double sxreal;
  841. #define SXI8_HIGH 0x7F
  842. #define SXU8_HIGH 0xFF
  843. #define SXI16_HIGH 0x7FFF
  844. #define SXU16_HIGH 0xFFFF
  845. #define SXI32_HIGH 0x7FFFFFFF
  846. #define SXU32_HIGH 0xFFFFFFFF
  847. #define SXI64_HIGH 0x7FFFFFFFFFFFFFFF
  848. #define SXU64_HIGH 0xFFFFFFFFFFFFFFFF
  849. #if !defined(TRUE)
  850. #define TRUE 1
  851. #endif
  852. #if !defined(FALSE)
  853. #define FALSE 0
  854. #endif
  855. /*
  856. * The following macros are used to cast pointers to integers and
  857. * integers to pointers.
  858. */
  859. #if defined(__PTRDIFF_TYPE__)
  860. # define SX_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X))
  861. # define SX_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X))
  862. #elif !defined(__GNUC__)
  863. # define SX_INT_TO_PTR(X) ((void*)&((char*)0)[X])
  864. # define SX_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0))
  865. #else
  866. # define SX_INT_TO_PTR(X) ((void*)(X))
  867. # define SX_PTR_TO_INT(X) ((int)(X))
  868. #endif
  869. #define SXMIN(a, b) ((a < b) ? (a) : (b))
  870. #define SXMAX(a, b) ((a < b) ? (b) : (a))
  871. #endif /* SYMISC_STD_TYPES */
  872. /* Symisc Run-time API private definitions */
  873. #if !defined(SYMISC_PRIVATE_DEFS)
  874. #define SYMISC_PRIVATE_DEFS
  875. typedef sxi32 (*ProcRawStrCmp)(const SyString *, const SyString *);
  876. #define SyStringData(RAW) ((RAW)->zString)
  877. #define SyStringLength(RAW) ((RAW)->nByte)
  878. #define SyStringInitFromBuf(RAW, ZBUF, NLEN){\
  879. (RAW)->zString = (const char *)ZBUF;\
  880. (RAW)->nByte = (sxu32)(NLEN);\
  881. }
  882. #define SyStringUpdatePtr(RAW, NBYTES){\
  883. if( NBYTES > (RAW)->nByte ){\
  884. (RAW)->nByte = 0;\
  885. }else{\
  886. (RAW)->zString += NBYTES;\
  887. (RAW)->nByte -= NBYTES;\
  888. }\
  889. }
  890. #define SyStringDupPtr(RAW1, RAW2)\
  891. (RAW1)->zString = (RAW2)->zString;\
  892. (RAW1)->nByte = (RAW2)->nByte;
  893. #define SyStringTrimLeadingChar(RAW, CHAR)\
  894. while((RAW)->nByte > 0 && (RAW)->zString[0] == CHAR ){\
  895. (RAW)->zString++;\
  896. (RAW)->nByte--;\
  897. }
  898. #define SyStringTrimTrailingChar(RAW, CHAR)\
  899. while((RAW)->nByte > 0 && (RAW)->zString[(RAW)->nByte - 1] == CHAR){\
  900. (RAW)->nByte--;\
  901. }
  902. #define SyStringCmp(RAW1, RAW2, xCMP)\
  903. (((RAW1)->nByte == (RAW2)->nByte) ? xCMP((RAW1)->zString, (RAW2)->zString, (RAW2)->nByte) : (sxi32)((RAW1)->nByte - (RAW2)->nByte))
  904. #define SyStringCmp2(RAW1, RAW2, xCMP)\
  905. (((RAW1)->nByte >= (RAW2)->nByte) ? xCMP((RAW1)->zString, (RAW2)->zString, (RAW2)->nByte) : (sxi32)((RAW2)->nByte - (RAW1)->nByte))
  906. #define SyStringCharCmp(RAW, CHAR) \
  907. (((RAW)->nByte == sizeof(char)) ? ((RAW)->zString[0] == CHAR ? 0 : CHAR - (RAW)->zString[0]) : ((RAW)->zString[0] == CHAR ? 0 : (RAW)->nByte - sizeof(char)))
  908. #define SX_ADDR(PTR) ((sxptr)PTR)
  909. #define SX_ARRAYSIZE(X) (sizeof(X)/sizeof(X[0]))
  910. #define SXUNUSED(P) (P = 0)
  911. #define SX_EMPTY(PTR) (PTR == 0)
  912. #define SX_EMPTY_STR(STR) (STR == 0 || STR[0] == 0 )
  913. typedef struct SyMemBackend SyMemBackend;
  914. typedef struct SyBlob SyBlob;
  915. typedef struct SySet SySet;
  916. /* Standard function signatures */
  917. typedef sxi32 (*ProcCmp)(const void *, const void *, sxu32);
  918. typedef sxi32 (*ProcPatternMatch)(const char *, sxu32, const char *, sxu32, sxu32 *);
  919. typedef sxi32 (*ProcSearch)(const void *, sxu32, const void *, sxu32, ProcCmp, sxu32 *);
  920. typedef sxu32 (*ProcHash)(const void *, sxu32);
  921. typedef sxi32 (*ProcHashSum)(const void *, sxu32, unsigned char *, sxu32);
  922. typedef sxi32 (*ProcSort)(void *, sxu32, sxu32, ProcCmp);
  923. #define MACRO_LIST_PUSH(Head, Item)\
  924. Item->pNext = Head;\
  925. Head = Item;
  926. #define MACRO_LD_PUSH(Head, Item)\
  927. if( Head == 0 ){\
  928. Head = Item;\
  929. }else{\
  930. Item->pNext = Head;\
  931. Head->pPrev = Item;\
  932. Head = Item;\
  933. }
  934. #define MACRO_LD_REMOVE(Head, Item)\
  935. if( Head == Item ){\
  936. Head = Head->pNext;\
  937. }\
  938. if( Item->pPrev ){ Item->pPrev->pNext = Item->pNext;}\
  939. if( Item->pNext ){ Item->pNext->pPrev = Item->pPrev;}
  940. /*
  941. * A generic dynamic set.
  942. */
  943. struct SySet
  944. {
  945. SyMemBackend *pAllocator; /* Memory backend */
  946. void *pBase; /* Base pointer */
  947. sxu32 nUsed; /* Total number of used slots */
  948. sxu32 nSize; /* Total number of available slots */
  949. sxu32 eSize; /* Size of a single slot */
  950. sxu32 nCursor; /* Loop cursor */
  951. void *pUserData; /* User private data associated with this container */
  952. };
  953. #define SySetBasePtr(S) ((S)->pBase)
  954. #define SySetBasePtrJump(S, OFFT) (&((char *)(S)->pBase)[OFFT*(S)->eSize])
  955. #define SySetUsed(S) ((S)->nUsed)
  956. #define SySetSize(S) ((S)->nSize)
  957. #define SySetElemSize(S) ((S)->eSize)
  958. #define SySetCursor(S) ((S)->nCursor)
  959. #define SySetGetAllocator(S) ((S)->pAllocator)
  960. #define SySetSetUserData(S, DATA) ((S)->pUserData = DATA)
  961. #define SySetGetUserData(S) ((S)->pUserData)
  962. /*
  963. * A variable length containers for generic data.
  964. */
  965. struct SyBlob
  966. {
  967. SyMemBackend *pAllocator; /* Memory backend */
  968. void *pBlob; /* Base pointer */
  969. sxu32 nByte; /* Total number of used bytes */
  970. sxu32 mByte; /* Total number of available bytes */
  971. sxu32 nFlags; /* Blob internal flags, see below */
  972. };
  973. #define SXBLOB_LOCKED 0x01 /* Blob is locked [i.e: Cannot auto grow] */
  974. #define SXBLOB_STATIC 0x02 /* Not allocated from heap */
  975. #define SXBLOB_RDONLY 0x04 /* Read-Only data */
  976. #define SyBlobFreeSpace(BLOB) ((BLOB)->mByte - (BLOB)->nByte)
  977. #define SyBlobLength(BLOB) ((BLOB)->nByte)
  978. #define SyBlobData(BLOB) ((BLOB)->pBlob)
  979. #define SyBlobCurData(BLOB) ((void*)(&((char*)(BLOB)->pBlob)[(BLOB)->nByte]))
  980. #define SyBlobDataAt(BLOB, OFFT) ((void *)(&((char *)(BLOB)->pBlob)[OFFT]))
  981. #define SyBlobGetAllocator(BLOB) ((BLOB)->pAllocator)
  982. #define SXMEM_POOL_INCR 3
  983. #define SXMEM_POOL_NBUCKETS 12
  984. #define SXMEM_BACKEND_MAGIC 0xBAC3E67D
  985. #define SXMEM_BACKEND_CORRUPT(BACKEND) (BACKEND == 0 || BACKEND->nMagic != SXMEM_BACKEND_MAGIC)
  986. #define SXMEM_BACKEND_RETRY 3
  987. /* A memory backend subsystem is defined by an instance of the following structures */
  988. typedef union SyMemHeader SyMemHeader;
  989. typedef struct SyMemBlock SyMemBlock;
  990. struct SyMemBlock
  991. {
  992. SyMemBlock *pNext, *pPrev; /* Chain of allocated memory blocks */
  993. #ifdef UNTRUST
  994. sxu32 nGuard; /* magic number associated with each valid block, so we
  995. * can detect misuse.
  996. */
  997. #endif
  998. };
  999. /*
  1000. * Header associated with each valid memory pool block.
  1001. */
  1002. union SyMemHeader
  1003. {
  1004. SyMemHeader *pNext; /* Next chunk of size 1 << (nBucket + SXMEM_POOL_INCR) in the list */
  1005. sxu32 nBucket; /* Bucket index in aPool[] */
  1006. };
  1007. struct SyMemBackend
  1008. {
  1009. const SyMutexMethods *pMutexMethods; /* Mutex methods */
  1010. const SyMemMethods *pMethods; /* Memory allocation methods */
  1011. SyMemBlock *pBlocks; /* List of valid memory blocks */
  1012. sxu32 nBlock; /* Total number of memory blocks allocated so far */
  1013. ProcMemError xMemError; /* Out-of memory callback */
  1014. void *pUserData; /* First arg to xMemError() */
  1015. SyMutex *pMutex; /* Per instance mutex */
  1016. sxu32 nMagic; /* Sanity check against misuse */
  1017. SyMemHeader *apPool[SXMEM_POOL_NBUCKETS+SXMEM_POOL_INCR]; /* Pool of memory chunks */
  1018. };
  1019. /* Mutex types */
  1020. #define SXMUTEX_TYPE_FAST 1
  1021. #define SXMUTEX_TYPE_RECURSIVE 2
  1022. #define SXMUTEX_TYPE_STATIC_1 3
  1023. #define SXMUTEX_TYPE_STATIC_2 4
  1024. #define SXMUTEX_TYPE_STATIC_3 5
  1025. #define SXMUTEX_TYPE_STATIC_4 6
  1026. #define SXMUTEX_TYPE_STATIC_5 7
  1027. #define SXMUTEX_TYPE_STATIC_6 8
  1028. #define SyMutexGlobalInit(METHOD){\
  1029. if( (METHOD)->xGlobalInit ){\
  1030. (METHOD)->xGlobalInit();\
  1031. }\
  1032. }
  1033. #define SyMutexGlobalRelease(METHOD){\
  1034. if( (METHOD)->xGlobalRelease ){\
  1035. (METHOD)->xGlobalRelease();\
  1036. }\
  1037. }
  1038. #define SyMutexNew(METHOD, TYPE) (METHOD)->xNew(TYPE)
  1039. #define SyMutexRelease(METHOD, MUTEX){\
  1040. if( MUTEX && (METHOD)->xRelease ){\
  1041. (METHOD)->xRelease(MUTEX);\
  1042. }\
  1043. }
  1044. #define SyMutexEnter(METHOD, MUTEX){\
  1045. if( MUTEX ){\
  1046. (METHOD)->xEnter(MUTEX);\
  1047. }\
  1048. }
  1049. #define SyMutexTryEnter(METHOD, MUTEX){\
  1050. if( MUTEX && (METHOD)->xTryEnter ){\
  1051. (METHOD)->xTryEnter(MUTEX);\
  1052. }\
  1053. }
  1054. #define SyMutexLeave(METHOD, MUTEX){\
  1055. if( MUTEX ){\
  1056. (METHOD)->xLeave(MUTEX);\
  1057. }\
  1058. }
  1059. /* Comparison, byte swap, byte copy macros */
  1060. #define SX_MACRO_FAST_CMP(X1, X2, SIZE, RC){\
  1061. register unsigned char *r1 = (unsigned char *)X1;\
  1062. register unsigned char *r2 = (unsigned char *)X2;\
  1063. register sxu32 LEN = SIZE;\
  1064. for(;;){\
  1065. if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\
  1066. if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\
  1067. if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\
  1068. if( !LEN ){ break; }if( r1[0] != r2[0] ){ break; } r1++; r2++; LEN--;\
  1069. }\
  1070. RC = !LEN ? 0 : r1[0] - r2[0];\
  1071. }
  1072. #define SX_MACRO_FAST_MEMCPY(SRC, DST, SIZ){\
  1073. register unsigned char *xSrc = (unsigned char *)SRC;\
  1074. register unsigned char *xDst = (unsigned char *)DST;\
  1075. register sxu32 xLen = SIZ;\
  1076. for(;;){\
  1077. if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\
  1078. if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\
  1079. if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\
  1080. if( !xLen ){ break; }xDst[0] = xSrc[0]; xDst++; xSrc++; --xLen;\
  1081. }\
  1082. }
  1083. #define SX_MACRO_BYTE_SWAP(X, Y, Z){\
  1084. register unsigned char *s = (unsigned char *)X;\
  1085. register unsigned char *d = (unsigned char *)Y;\
  1086. sxu32 ZLong = Z; \
  1087. sxi32 c; \
  1088. for(;;){\
  1089. if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\
  1090. if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\
  1091. if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\
  1092. if(!ZLong){ break; } c = s[0] ; s[0] = d[0]; d[0] = (unsigned char)c; s++; d++; --ZLong;\
  1093. }\
  1094. }
  1095. #define SX_MSEC_PER_SEC (1000) /* Millisec per seconds */
  1096. #define SX_USEC_PER_SEC (1000000) /* Microsec per seconds */
  1097. #define SX_NSEC_PER_SEC (1000000000) /* Nanosec per seconds */
  1098. #endif /* SYMISC_PRIVATE_DEFS */
  1099. /* Symisc Run-time API auxiliary definitions */
  1100. #if !defined(SYMISC_PRIVATE_AUX_DEFS)
  1101. #define SYMISC_PRIVATE_AUX_DEFS
  1102. typedef struct SyHashEntry_Pr SyHashEntry_Pr;
  1103. typedef struct SyHashEntry SyHashEntry;
  1104. typedef struct SyHash SyHash;
  1105. /*
  1106. * Each public hashtable entry is represented by an instance
  1107. * of the following structure.
  1108. */
  1109. struct SyHashEntry
  1110. {
  1111. const void *pKey; /* Hash key */
  1112. sxu32 nKeyLen; /* Key length */
  1113. void *pUserData; /* User private data */
  1114. };
  1115. #define SyHashEntryGetUserData(ENTRY) ((ENTRY)->pUserData)
  1116. #define SyHashEntryGetKey(ENTRY) ((ENTRY)->pKey)
  1117. /* Each active hashtable is identified by an instance of the following structure */
  1118. struct SyHash
  1119. {
  1120. SyMemBackend *pAllocator; /* Memory backend */
  1121. ProcHash xHash; /* Hash function */
  1122. ProcCmp xCmp; /* Comparison function */
  1123. SyHashEntry_Pr *pList, *pCurrent; /* Linked list of hash entries user for linear traversal */
  1124. sxu32 nEntry; /* Total number of entries */
  1125. SyHashEntry_Pr **apBucket; /* Hash buckets */
  1126. sxu32 nBucketSize; /* Current bucket size */
  1127. };
  1128. #define SXHASH_BUCKET_SIZE 16 /* Initial bucket size: must be a power of two */
  1129. #define SXHASH_FILL_FACTOR 3
  1130. /* Hash access macro */
  1131. #define SyHashFunc(HASH) ((HASH)->xHash)
  1132. #define SyHashCmpFunc(HASH) ((HASH)->xCmp)
  1133. #define SyHashTotalEntry(HASH) ((HASH)->nEntry)
  1134. #define SyHashGetPool(HASH) ((HASH)->pAllocator)
  1135. /*
  1136. * An instance of the following structure define a single context
  1137. * for an Pseudo Random Number Generator.
  1138. *
  1139. * Nothing in this file or anywhere else in the library does any kind of
  1140. * encryption. The RC4 algorithm is being used as a PRNG (pseudo-random
  1141. * number generator) not as an encryption device.
  1142. * This implementation is taken from the SQLite3 source tree.
  1143. */
  1144. typedef struct SyPRNGCtx SyPRNGCtx;
  1145. struct SyPRNGCtx
  1146. {
  1147. sxu8 i, j; /* State variables */
  1148. unsigned char s[256]; /* State variables */
  1149. sxu16 nMagic; /* Sanity check */
  1150. };
  1151. typedef sxi32 (*ProcRandomSeed)(void *, unsigned int, void *);
  1152. /* High resolution timer.*/
  1153. typedef struct sytime sytime;
  1154. struct sytime
  1155. {
  1156. long tm_sec; /* seconds */
  1157. long tm_usec; /* microseconds */
  1158. };
  1159. /* Forward declaration */
  1160. typedef struct SyStream SyStream;
  1161. typedef struct SyToken SyToken;
  1162. typedef struct SyLex SyLex;
  1163. /*
  1164. * Tokenizer callback signature.
  1165. */
  1166. typedef sxi32 (*ProcTokenizer)(SyStream *, SyToken *, void *, void *);
  1167. /*
  1168. * Each token in the input is represented by an instance
  1169. * of the following structure.
  1170. */
  1171. struct SyToken
  1172. {
  1173. SyString sData; /* Token text and length */
  1174. sxu32 nType; /* Token type */
  1175. sxu32 nLine; /* Token line number */
  1176. void *pUserData; /* User private data associated with this token */
  1177. };
  1178. /*
  1179. * During tokenization, information about the state of the input
  1180. * stream is held in an instance of the following structure.
  1181. */
  1182. struct SyStream
  1183. {
  1184. const unsigned char *zInput; /* Complete text of the input */
  1185. const unsigned char *zText; /* Current input we are processing */
  1186. const unsigned char *zEnd; /* End of input marker */
  1187. sxu32 nLine; /* Total number of processed lines */
  1188. sxu32 nIgn; /* Total number of ignored tokens */
  1189. SySet *pSet; /* Token containers */
  1190. };
  1191. /*
  1192. * Each lexer is represented by an instance of the following structure.
  1193. */
  1194. struct SyLex
  1195. {
  1196. SyStream sStream; /* Input stream */
  1197. ProcTokenizer xTokenizer; /* Tokenizer callback */
  1198. void * pUserData; /* Third argument to xTokenizer() */
  1199. SySet *pTokenSet; /* Token set */
  1200. };
  1201. #define SyLexTotalToken(LEX) SySetTotalEntry(&(LEX)->aTokenSet)
  1202. #define SyLexTotalLines(LEX) ((LEX)->sStream.nLine)
  1203. #define SyLexTotalIgnored(LEX) ((LEX)->sStream.nIgn)
  1204. #define XLEX_IN_LEN(STREAM) (sxu32)(STREAM->zEnd - STREAM->zText)
  1205. #endif /* SYMISC_PRIVATE_AUX_DEFS */
  1206. /*
  1207. ** Notes on UTF-8 (According to SQLite3 authors):
  1208. **
  1209. ** Byte-0 Byte-1 Byte-2 Byte-3 Value
  1210. ** 0xxxxxxx 00000000 00000000 0xxxxxxx
  1211. ** 110yyyyy 10xxxxxx 00000000 00000yyy yyxxxxxx
  1212. ** 1110zzzz 10yyyyyy 10xxxxxx 00000000 zzzzyyyy yyxxxxxx
  1213. ** 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 000uuuuu zzzzyyyy yyxxxxxx
  1214. **
  1215. */
  1216. /*
  1217. ** Assuming zIn points to the first byte of a UTF-8 character,
  1218. ** advance zIn to point to the first byte of the next UTF-8 character.
  1219. */
  1220. #define SX_JMP_UTF8(zIn, zEnd)\
  1221. while(zIn < zEnd && (((unsigned char)zIn[0] & 0xc0) == 0x80) ){ zIn++; }
  1222. #define SX_WRITE_UTF8(zOut, c) { \
  1223. if( c<0x00080 ){ \
  1224. *zOut++ = (sxu8)(c&0xFF); \
  1225. }else if( c<0x00800 ){ \
  1226. *zOut++ = 0xC0 + (sxu8)((c>>6)&0x1F); \
  1227. *zOut++ = 0x80 + (sxu8)(c & 0x3F); \
  1228. }else if( c<0x10000 ){ \
  1229. *zOut++ = 0xE0 + (sxu8)((c>>12)&0x0F); \
  1230. *zOut++ = 0x80 + (sxu8)((c>>6) & 0x3F); \
  1231. *zOut++ = 0x80 + (sxu8)(c & 0x3F); \
  1232. }else{ \
  1233. *zOut++ = 0xF0 + (sxu8)((c>>18) & 0x07); \
  1234. *zOut++ = 0x80 + (sxu8)((c>>12) & 0x3F); \
  1235. *zOut++ = 0x80 + (sxu8)((c>>6) & 0x3F); \
  1236. *zOut++ = 0x80 + (sxu8)(c & 0x3F); \
  1237. } \
  1238. }
  1239. /* Rely on the standard ctype */
  1240. #include <ctype.h>
  1241. #define SyToUpper(c) toupper(c)
  1242. #define SyToLower(c) tolower(c)
  1243. #define SyisUpper(c) isupper(c)
  1244. #define SyisLower(c) islower(c)
  1245. #define SyisSpace(c) isspace(c)
  1246. #define SyisBlank(c) isspace(c)
  1247. #define SyisAlpha(c) isalpha(c)
  1248. #define SyisDigit(c) isdigit(c)
  1249. #define SyisHex(c) isxdigit(c)
  1250. #define SyisPrint(c) isprint(c)
  1251. #define SyisPunct(c) ispunct(c)
  1252. #define SyisSpec(c) iscntrl(c)
  1253. #define SyisCtrl(c) iscntrl(c)
  1254. #define SyisAscii(c) isascii(c)
  1255. #define SyisAlphaNum(c) isalnum(c)
  1256. #define SyisGraph(c) isgraph(c)
  1257. #define SyDigToHex(c) "0123456789ABCDEF"[c & 0x0F]
  1258. #define SyDigToInt(c) ((c < 0xc0 && SyisDigit(c))? (c - '0') : 0 )
  1259. #define SyCharToUpper(c) ((c < 0xc0 && SyisLower(c))? SyToUpper(c) : c)
  1260. #define SyCharToLower(c) ((c < 0xc0 && SyisUpper(c))? SyToLower(c) : c)
  1261. /* Remove white space/NUL byte from a raw string */
  1262. #define SyStringLeftTrim(RAW)\
  1263. while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0 && SyisSpace((RAW)->zString[0])){\
  1264. (RAW)->nByte--;\
  1265. (RAW)->zString++;\
  1266. }
  1267. #define SyStringLeftTrimSafe(RAW)\
  1268. while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0 && ((RAW)->zString[0] == 0 || SyisSpace((RAW)->zString[0]))){\
  1269. (RAW)->nByte--;\
  1270. (RAW)->zString++;\
  1271. }
  1272. #define SyStringRightTrim(RAW)\
  1273. while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0 && SyisSpace((RAW)->zString[(RAW)->nByte - 1])){\
  1274. (RAW)->nByte--;\
  1275. }
  1276. #define SyStringRightTrimSafe(RAW)\
  1277. while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0 && \
  1278. (( RAW)->zString[(RAW)->nByte - 1] == 0 || SyisSpace((RAW)->zString[(RAW)->nByte - 1]))){\
  1279. (RAW)->nByte--;\
  1280. }
  1281. #define SyStringFullTrim(RAW)\
  1282. while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0 && SyisSpace((RAW)->zString[0])){\
  1283. (RAW)->nByte--;\
  1284. (RAW)->zString++;\
  1285. }\
  1286. while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0 && SyisSpace((RAW)->zString[(RAW)->nByte - 1])){\
  1287. (RAW)->nByte--;\
  1288. }
  1289. #define SyStringFullTrimSafe(RAW)\
  1290. while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[0] < 0xc0 && \
  1291. ( (RAW)->zString[0] == 0 || SyisSpace((RAW)->zString[0]))){\
  1292. (RAW)->nByte--;\
  1293. (RAW)->zString++;\
  1294. }\
  1295. while((RAW)->nByte > 0 && (unsigned char)(RAW)->zString[(RAW)->nByte - 1] < 0xc0 && \
  1296. ( (RAW)->zString[(RAW)->nByte - 1] == 0 || SyisSpace((RAW)->zString[(RAW)->nByte - 1]))){\
  1297. (RAW)->nByte--;\
  1298. }
  1299. #ifndef JX9_DISABLE_BUILTIN_FUNC
  1300. /*
  1301. * An XML raw text, CDATA, tag name and son is parsed out and stored
  1302. * in an instance of the following structure.
  1303. */
  1304. typedef struct SyXMLRawStr SyXMLRawStr;
  1305. struct SyXMLRawStr
  1306. {
  1307. const char *zString; /* Raw text [UTF-8 ENCODED EXCEPT CDATA] [NOT NULL TERMINATED] */
  1308. sxu32 nByte; /* Text length */
  1309. sxu32 nLine; /* Line number this text occurs */
  1310. };
  1311. /*
  1312. * Event callback signatures.
  1313. */
  1314. typedef sxi32 (*ProcXMLStartTagHandler)(SyXMLRawStr *, SyXMLRawStr *, sxu32, SyXMLRawStr *, void *);
  1315. typedef sxi32 (*ProcXMLTextHandler)(SyXMLRawStr *, void *);
  1316. typedef sxi32 (*ProcXMLEndTagHandler)(SyXMLRawStr *, SyXMLRawStr *, void *);
  1317. typedef sxi32 (*ProcXMLPIHandler)(SyXMLRawStr *, SyXMLRawStr *, void *);
  1318. typedef sxi32 (*ProcXMLDoctypeHandler)(SyXMLRawStr *, void *);
  1319. typedef sxi32 (*ProcXMLSyntaxErrorHandler)(const char *, int, SyToken *, void *);
  1320. typedef sxi32 (*ProcXMLStartDocument)(void *);
  1321. typedef sxi32 (*ProcXMLNameSpaceStart)(SyXMLRawStr *, SyXMLRawStr *, void *);
  1322. typedef sxi32 (*ProcXMLNameSpaceEnd)(SyXMLRawStr *, void *);
  1323. typedef sxi32 (*ProcXMLEndDocument)(void *);
  1324. /* XML processing control flags */
  1325. #define SXML_ENABLE_NAMESPACE 0x01 /* Parse XML with namespace support enbaled */
  1326. #define SXML_ENABLE_QUERY 0x02 /* Not used */
  1327. #define SXML_OPTION_CASE_FOLDING 0x04 /* Controls whether case-folding is enabled for this XML parser */
  1328. #define SXML_OPTION_SKIP_TAGSTART 0x08 /* Specify how many characters should be skipped in the beginning of a tag name.*/
  1329. #define SXML_OPTION_SKIP_WHITE 0x10 /* Whether to skip values consisting of whitespace characters. */
  1330. #define SXML_OPTION_TARGET_ENCODING 0x20 /* Default encoding: UTF-8 */
  1331. /* XML error codes */
  1332. enum xml_err_code{
  1333. SXML_ERROR_NONE = 1,
  1334. SXML_ERROR_NO_MEMORY,
  1335. SXML_ERROR_SYNTAX,
  1336. SXML_ERROR_NO_ELEMENTS,
  1337. SXML_ERROR_INVALID_TOKEN,
  1338. SXML_ERROR_UNCLOSED_TOKEN,
  1339. SXML_ERROR_PARTIAL_CHAR,
  1340. SXML_ERROR_TAG_MISMATCH,
  1341. SXML_ERROR_DUPLICATE_ATTRIBUTE,
  1342. SXML_ERROR_JUNK_AFTER_DOC_ELEMENT,
  1343. SXML_ERROR_PARAM_ENTITY_REF,
  1344. SXML_ERROR_UNDEFINED_ENTITY,
  1345. SXML_ERROR_RECURSIVE_ENTITY_REF,
  1346. SXML_ERROR_ASYNC_ENTITY,
  1347. SXML_ERROR_BAD_CHAR_REF,
  1348. SXML_ERROR_BINARY_ENTITY_REF,
  1349. SXML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF,
  1350. SXML_ERROR_MISPLACED_XML_PI,
  1351. SXML_ERROR_UNKNOWN_ENCODING,
  1352. SXML_ERROR_INCORRECT_ENCODING,
  1353. SXML_ERROR_UNCLOSED_CDATA_SECTION,
  1354. SXML_ERROR_EXTERNAL_ENTITY_HANDLING
  1355. };
  1356. /* Each active XML SAX parser is represented by an instance
  1357. * of the following structure.
  1358. */
  1359. typedef struct SyXMLParser SyXMLParser;
  1360. struct SyXMLParser
  1361. {
  1362. SyMemBackend *pAllocator; /* Memory backend */
  1363. void *pUserData; /* User private data forwarded varbatim by the XML parser
  1364. * as the last argument to the users callbacks.
  1365. */
  1366. SyHash hns; /* Namespace hashtable */
  1367. SySet sToken; /* XML tokens */
  1368. SyLex sLex; /* Lexical analyzer */
  1369. sxi32 nFlags; /* Control flags */
  1370. /* User callbacks */
  1371. ProcXMLStartTagHandler xStartTag; /* Start element handler */
  1372. ProcXMLEndTagHandler xEndTag; /* End element handler */
  1373. ProcXMLTextHandler xRaw; /* Raw text/CDATA handler */
  1374. ProcXMLDoctypeHandler xDoctype; /* DOCTYPE handler */
  1375. ProcXMLPIHandler xPi; /* Processing instruction (PI) handler*/
  1376. ProcXMLSyntaxErrorHandler xError; /* Error handler */
  1377. ProcXMLStartDocument xStartDoc; /* StartDoc handler */
  1378. ProcXMLEndDocument xEndDoc; /* EndDoc handler */
  1379. ProcXMLNameSpaceStart xNameSpace; /* Namespace declaration handler */
  1380. ProcXMLNameSpaceEnd xNameSpaceEnd; /* End namespace declaration handler */
  1381. };
  1382. /*
  1383. * --------------
  1384. * Archive extractor:
  1385. * --------------
  1386. * Each open ZIP/TAR archive is identified by an instance of the following structure.
  1387. * That is, a process can open one or more archives and manipulates them in thread safe
  1388. * way by simply working with pointers to the following structure.
  1389. * Each entry in the archive is remembered in a hashtable.
  1390. * Lookup is very fast and entry with the same name are chained together.
  1391. */
  1392. typedef struct SyArchiveEntry SyArchiveEntry;
  1393. typedef struct SyArchive SyArchive;
  1394. struct SyArchive
  1395. {
  1396. SyMemBackend *pAllocator; /* Memory backend */
  1397. SyArchiveEntry *pCursor; /* Cursor for linear traversal of archive entries */
  1398. SyArchiveEntry *pList; /* Pointer to the List of the loaded archive */
  1399. SyArchiveEntry **apHash; /* Hashtable for archive entry */
  1400. ProcRawStrCmp xCmp; /* Hash comparison function */
  1401. ProcHash xHash; /* Hash Function */
  1402. sxu32 nSize; /* Hashtable size */
  1403. sxu32 nEntry; /* Total number of entries in the zip/tar archive */
  1404. sxu32 nLoaded; /* Total number of entries loaded in memory */
  1405. sxu32 nCentralOfft; /* Central directory offset(ZIP only. Otherwise Zero) */
  1406. sxu32 nCentralSize; /* Central directory size(ZIP only. Otherwise Zero) */
  1407. void *pUserData; /* Upper layer private data */
  1408. sxu32 nMagic; /* Sanity check */
  1409. };
  1410. #define SXARCH_MAGIC 0xDEAD635A
  1411. #define SXARCH_INVALID(ARCH) (ARCH == 0 || ARCH->nMagic != SXARCH_MAGIC)
  1412. #define SXARCH_ENTRY_INVALID(ENTRY) (ENTRY == 0 || ENTRY->nMagic != SXARCH_MAGIC)
  1413. #define SyArchiveHashFunc(ARCH) (ARCH)->xHash
  1414. #define SyArchiveCmpFunc(ARCH) (ARCH)->xCmp
  1415. #define SyArchiveUserData(ARCH) (ARCH)->pUserData
  1416. #define SyArchiveSetUserData(ARCH, DATA) (ARCH)->pUserData = DATA
  1417. /*
  1418. * Each loaded archive record is identified by an instance
  1419. * of the following structure.
  1420. */
  1421. struct SyArchiveEntry
  1422. {
  1423. sxu32 nByte; /* Contents size before compression */
  1424. sxu32 nByteCompr; /* Contents size after compression */
  1425. sxu32 nReadCount; /* Read counter */
  1426. sxu32 nCrc; /* Contents CRC32 */
  1427. Sytm sFmt; /* Last-modification time */
  1428. sxu32 nOfft; /* Data offset. */
  1429. sxu16 nComprMeth; /* Compression method 0 == stored/8 == deflated and so on (see appnote.txt)*/
  1430. sxu16 nExtra; /* Extra size if any */
  1431. SyString sFileName; /* entry name & length */
  1432. sxu32 nDup; /* Total number of entries with the same name */
  1433. SyArchiveEntry *pNextHash, *pPrevHash; /* Hash collision chains */
  1434. SyArchiveEntry *pNextName; /* Next entry with the same name */
  1435. SyArchiveEntry *pNext, *pPrev; /* Next and previous entry in the list */
  1436. sxu32 nHash; /* Hash of the entry name */
  1437. void *pUserData; /* User data */
  1438. sxu32 nMagic; /* Sanity check */
  1439. };
  1440. /*
  1441. * Extra flags for extending the file local header
  1442. */
  1443. #define SXZIP_EXTRA_TIMESTAMP 0x001 /* Extended UNIX timestamp */
  1444. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  1445. #ifndef JX9_DISABLE_HASH_FUNC
  1446. /* MD5 context */
  1447. typedef struct MD5Context MD5Context;
  1448. struct MD5Context {
  1449. sxu32 buf[4];
  1450. sxu32 bits[2];
  1451. unsigned char in[64];
  1452. };
  1453. /* SHA1 context */
  1454. typedef struct SHA1Context SHA1Context;
  1455. struct SHA1Context {
  1456. unsigned int state[5];
  1457. unsigned int count[2];
  1458. unsigned char buffer[64];
  1459. };
  1460. #endif /* JX9_DISABLE_HASH_FUNC */
  1461. /* JX9 private declaration */
  1462. /*
  1463. * Memory Objects.
  1464. * Internally, the JX9 virtual machine manipulates nearly all JX9 values
  1465. * [i.e: string, int, float, resource, object, bool, null] as jx9_values structures.
  1466. * Each jx9_values struct may cache multiple representations (string, integer etc.)
  1467. * of the same value.
  1468. */
  1469. struct jx9_value
  1470. {
  1471. union{
  1472. jx9_real rVal; /* Real value */
  1473. sxi64 iVal; /* Integer value */
  1474. void *pOther; /* Other values (Object, Array, Resource, Namespace, etc.) */
  1475. }x;
  1476. sxi32 iFlags; /* Control flags (see below) */
  1477. jx9_vm *pVm; /* VM this instance belong */
  1478. SyBlob sBlob; /* String values */
  1479. sxu32 nIdx; /* Object index in the global pool */
  1480. };
  1481. /* Allowed value types.
  1482. */
  1483. #define MEMOBJ_STRING 0x001 /* Memory value is a UTF-8 string */
  1484. #define MEMOBJ_INT 0x002 /* Memory value is an integer */
  1485. #define MEMOBJ_REAL 0x004 /* Memory value is a real number */
  1486. #define MEMOBJ_BOOL 0x008 /* Memory value is a boolean */
  1487. #define MEMOBJ_NULL 0x020 /* Memory value is NULL */
  1488. #define MEMOBJ_HASHMAP 0x040 /* Memory value is a hashmap (JSON representation of Array and Objects) */
  1489. #define MEMOBJ_RES 0x100 /* Memory value is a resource [User private data] */
  1490. /* Mask of all known types */
  1491. #define MEMOBJ_ALL (MEMOBJ_STRING|MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)
  1492. /* Scalar variables
  1493. * According to the JX9 language reference manual
  1494. * Scalar variables are those containing an integer, float, string or boolean.
  1495. * Types array, object and resource are not scalar.
  1496. */
  1497. #define MEMOBJ_SCALAR (MEMOBJ_STRING|MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL)
  1498. /*
  1499. * The following macro clear the current jx9_value type and replace
  1500. * it with the given one.
  1501. */
  1502. #define MemObjSetType(OBJ, TYPE) ((OBJ)->iFlags = ((OBJ)->iFlags&~MEMOBJ_ALL)|TYPE)
  1503. /* jx9_value cast method signature */
  1504. typedef sxi32 (*ProcMemObjCast)(jx9_value *);
  1505. /* Forward reference */
  1506. typedef struct jx9_output_consumer jx9_output_consumer;
  1507. typedef struct jx9_user_func jx9_user_func;
  1508. typedef struct jx9_conf jx9_conf;
  1509. /*
  1510. * An instance of the following structure store the default VM output
  1511. * consumer and it's private data.
  1512. * Client-programs can register their own output consumer callback
  1513. * via the [JX9_VM_CONFIG_OUTPUT] configuration directive.
  1514. * Please refer to the official documentation for more information
  1515. * on how to register an output consumer callback.
  1516. */
  1517. struct jx9_output_consumer
  1518. {
  1519. ProcConsumer xConsumer; /* VM output consumer routine */
  1520. void *pUserData; /* Third argument to xConsumer() */
  1521. ProcConsumer xDef; /* Default output consumer routine */
  1522. void *pDefData; /* Third argument to xDef() */
  1523. };
  1524. /*
  1525. * JX9 engine [i.e: jx9 instance] configuration is stored in
  1526. * an instance of the following structure.
  1527. * Please refer to the official documentation for more information
  1528. * on how to configure your jx9 engine instance.
  1529. */
  1530. struct jx9_conf
  1531. {
  1532. ProcConsumer xErr; /* Compile-time error consumer callback */
  1533. void *pErrData; /* Third argument to xErr() */
  1534. SyBlob sErrConsumer; /* Default error consumer */
  1535. };
  1536. /*
  1537. * Signature of the C function responsible of expanding constant values.
  1538. */
  1539. typedef void (*ProcConstant)(jx9_value *, void *);
  1540. /*
  1541. * Each registered constant [i.e: __TIME__, __DATE__, JX9_OS, INT_MAX, etc.] is stored
  1542. * in an instance of the following structure.
  1543. * Please refer to the official documentation for more information
  1544. * on how to create/install foreign constants.
  1545. */
  1546. typedef struct jx9_constant jx9_constant;
  1547. struct jx9_constant
  1548. {
  1549. SyString sName; /* Constant name */
  1550. ProcConstant xExpand; /* Function responsible of expanding constant value */
  1551. void *pUserData; /* Last argument to xExpand() */
  1552. };
  1553. typedef struct jx9_aux_data jx9_aux_data;
  1554. /*
  1555. * Auxiliary data associated with each foreign function is stored
  1556. * in a stack of the following structure.
  1557. * Note that automatic tracked chunks are also stored in an instance
  1558. * of this structure.
  1559. */
  1560. struct jx9_aux_data
  1561. {
  1562. void *pAuxData; /* Aux data */
  1563. };
  1564. /* Foreign functions signature */
  1565. typedef int (*ProcHostFunction)(jx9_context *, int, jx9_value **);
  1566. /*
  1567. * Each installed foreign function is recored in an instance of the following
  1568. * structure.
  1569. * Please refer to the official documentation for more information on how
  1570. * to create/install foreign functions.
  1571. */
  1572. struct jx9_user_func
  1573. {
  1574. jx9_vm *pVm; /* VM that own this instance */
  1575. SyString sName; /* Foreign function name */
  1576. ProcHostFunction xFunc; /* Implementation of the foreign function */
  1577. void *pUserData; /* User private data [Refer to the official documentation for more information]*/
  1578. SySet aAux; /* Stack of auxiliary data [Refer to the official documentation for more information]*/
  1579. };
  1580. /*
  1581. * The 'context' argument for an installable function. A pointer to an
  1582. * instance of this structure is the first argument to the routines used
  1583. * implement the foreign functions.
  1584. */
  1585. struct jx9_context
  1586. {
  1587. jx9_user_func *pFunc; /* Function information. */
  1588. jx9_value *pRet; /* Return value is stored here. */
  1589. SySet sVar; /* Container of dynamically allocated jx9_values
  1590. * [i.e: Garbage collection purposes.]
  1591. */
  1592. SySet sChunk; /* Track dynamically allocated chunks [jx9_aux_data instance].
  1593. * [i.e: Garbage collection purposes.]
  1594. */
  1595. jx9_vm *pVm; /* Virtual machine that own this context */
  1596. sxi32 iFlags; /* Call flags */
  1597. };
  1598. /* Hashmap control flags */
  1599. #define HASHMAP_JSON_OBJECT 0x001 /* Hashmap represent JSON Object*/
  1600. /*
  1601. * Each hashmap entry [i.e: array(4, 5, 6)] is recorded in an instance
  1602. * of the following structure.
  1603. */
  1604. struct jx9_hashmap_node
  1605. {
  1606. jx9_hashmap *pMap; /* Hashmap that own this instance */
  1607. sxi32 iType; /* Node type */
  1608. union{
  1609. sxi64 iKey; /* Int key */
  1610. SyBlob sKey; /* Blob key */
  1611. }xKey;
  1612. sxi32 iFlags; /* Control flags */
  1613. sxu32 nHash; /* Key hash value */
  1614. sxu32 nValIdx; /* Value stored in this node */
  1615. jx9_hashmap_node *pNext, *pPrev; /* Link to other entries [i.e: linear traversal] */
  1616. jx9_hashmap_node *pNextCollide, *pPrevCollide; /* Collision chain */
  1617. };
  1618. /*
  1619. * Each active hashmap aka array in the JX9 jargon is represented
  1620. * by an instance of the following structure.
  1621. */
  1622. struct jx9_hashmap
  1623. {
  1624. jx9_vm *pVm; /* VM that own this instance */
  1625. jx9_hashmap_node **apBucket; /* Hash bucket */
  1626. jx9_hashmap_node *pFirst; /* First inserted entry */
  1627. jx9_hashmap_node *pLast; /* Last inserted entry */
  1628. jx9_hashmap_node *pCur; /* Current entry */
  1629. sxu32 nSize; /* Bucket size */
  1630. sxu32 nEntry; /* Total number of inserted entries */
  1631. sxu32 (*xIntHash)(sxi64); /* Hash function for int_keys */
  1632. sxu32 (*xBlobHash)(const void *, sxu32); /* Hash function for blob_keys */
  1633. sxi32 iFlags; /* Hashmap control flags */
  1634. sxi64 iNextIdx; /* Next available automatically assigned index */
  1635. sxi32 iRef; /* Reference count */
  1636. };
  1637. /* An instance of the following structure is the context
  1638. * for the FOREACH_STEP/FOREACH_INIT VM instructions.
  1639. * Those instructions are used to implement the 'foreach'
  1640. * statement.
  1641. * This structure is made available to these instructions
  1642. * as the P3 operand.
  1643. */
  1644. struct jx9_foreach_info
  1645. {
  1646. SyString sKey; /* Key name. Empty otherwise*/
  1647. SyString sValue; /* Value name */
  1648. sxi32 iFlags; /* Control flags */
  1649. SySet aStep; /* Stack of steps [i.e: jx9_foreach_step instance] */
  1650. };
  1651. struct jx9_foreach_step
  1652. {
  1653. sxi32 iFlags; /* Control flags (see below) */
  1654. /* Iterate on this map*/
  1655. jx9_hashmap *pMap; /* Hashmap [i.e: array in the JX9 jargon] iteration
  1656. * Ex: foreach(array(1, 2, 3) as $key=>$value){}
  1657. */
  1658. };
  1659. /* Foreach step control flags */
  1660. #define JX9_4EACH_STEP_KEY 0x001 /* Make Key available */
  1661. /*
  1662. * Each JX9 engine is identified by an instance of the following structure.
  1663. * Please refer to the official documentation for more information
  1664. * on how to configure your JX9 engine instance.
  1665. */
  1666. struct jx9
  1667. {
  1668. SyMemBackend sAllocator; /* Low level memory allocation subsystem */
  1669. const jx9_vfs *pVfs; /* Underlying Virtual File System */
  1670. jx9_conf xConf; /* Configuration */
  1671. #if defined(JX9_ENABLE_THREADS)
  1672. SyMutex *pMutex; /* Per-engine mutex */
  1673. #endif
  1674. jx9_vm *pVms; /* List of active VM */
  1675. sxi32 iVm; /* Total number of active VM */
  1676. jx9 *pNext, *pPrev; /* List of active engines */
  1677. sxu32 nMagic; /* Sanity check against misuse */
  1678. };
  1679. /* Code generation data structures */
  1680. typedef sxi32 (*ProcErrorGen)(void *, sxi32, sxu32, const char *, ...);
  1681. typedef struct jx9_expr_node jx9_expr_node;
  1682. typedef struct jx9_expr_op jx9_expr_op;
  1683. typedef struct jx9_gen_state jx9_gen_state;
  1684. typedef struct GenBlock GenBlock;
  1685. typedef sxi32 (*ProcLangConstruct)(jx9_gen_state *);
  1686. typedef sxi32 (*ProcNodeConstruct)(jx9_gen_state *, sxi32);
  1687. /*
  1688. * Each supported operator [i.e: +, -, ==, *, %, >>, >=, new, etc.] is represented
  1689. * by an instance of the following structure.
  1690. * The JX9 parser does not use any external tools and is 100% handcoded.
  1691. * That is, the JX9 parser is thread-safe , full reentrant, produce consistant
  1692. * compile-time errrors and at least 7 times faster than the standard JX9 parser.
  1693. */
  1694. struct jx9_expr_op
  1695. {
  1696. SyString sOp; /* String representation of the operator [i.e: "+", "*", "=="...] */
  1697. sxi32 iOp; /* Operator ID */
  1698. sxi32 iPrec; /* Operator precedence: 1 == Highest */
  1699. sxi32 iAssoc; /* Operator associativity (either left, right or non-associative) */
  1700. sxi32 iVmOp; /* VM OP code for this operator [i.e: JX9_OP_EQ, JX9_OP_LT, JX9_OP_MUL...]*/
  1701. };
  1702. /*
  1703. * Each expression node is parsed out and recorded
  1704. * in an instance of the following structure.
  1705. */
  1706. struct jx9_expr_node
  1707. {
  1708. const jx9_expr_op *pOp; /* Operator ID or NULL if literal, constant, variable, function or object method call */
  1709. jx9_expr_node *pLeft; /* Left expression tree */
  1710. jx9_expr_node *pRight; /* Right expression tree */
  1711. SyToken *pStart; /* Stream of tokens that belong to this node */
  1712. SyToken *pEnd; /* End of token stream */
  1713. sxi32 iFlags; /* Node construct flags */
  1714. ProcNodeConstruct xCode; /* C routine responsible of compiling this node */
  1715. SySet aNodeArgs; /* Node arguments. Only used by postfix operators [i.e: function call]*/
  1716. jx9_expr_node *pCond; /* Condition: Only used by the ternary operator '?:' */
  1717. };
  1718. /* Node Construct flags */
  1719. #define EXPR_NODE_PRE_INCR 0x01 /* Pre-icrement/decrement [i.e: ++$i, --$j] node */
  1720. /*
  1721. * A block of instructions is recorded in an instance of the following structure.
  1722. * This structure is used only during compile-time and have no meaning
  1723. * during bytecode execution.
  1724. */
  1725. struct GenBlock
  1726. {
  1727. jx9_gen_state *pGen; /* State of the code generator */
  1728. GenBlock *pParent; /* Upper block or NULL if global */
  1729. sxu32 nFirstInstr; /* First instruction to execute */
  1730. sxi32 iFlags; /* Block control flags (see below) */
  1731. SySet aJumpFix; /* Jump fixup (JumpFixup instance) */
  1732. void *pUserData; /* Upper layer private data */
  1733. /* The following two fields are used only when compiling
  1734. * the 'do..while()' language construct.
  1735. */
  1736. sxu8 bPostContinue; /* TRUE when compiling the do..while() statement */
  1737. SySet aPostContFix; /* Post-continue jump fix */
  1738. };
  1739. /*
  1740. * Code generator state is remembered in an instance of the following
  1741. * structure. We put the information in this structure and pass around
  1742. * a pointer to this structure, rather than pass around all of the
  1743. * information separately. This helps reduce the number of arguments
  1744. * to generator functions.
  1745. * This structure is used only during compile-time and have no meaning
  1746. * during bytecode execution.
  1747. */
  1748. struct jx9_gen_state
  1749. {
  1750. jx9_vm *pVm; /* VM that own this instance */
  1751. SyHash hLiteral; /* Constant string Literals table */
  1752. SyHash hNumLiteral; /* Numeric literals table */
  1753. SyHash hVar; /* Collected variable hashtable */
  1754. GenBlock *pCurrent; /* Current processed block */
  1755. GenBlock sGlobal; /* Global block */
  1756. ProcConsumer xErr; /* Error consumer callback */
  1757. void *pErrData; /* Third argument to xErr() */
  1758. SyToken *pIn; /* Current processed token */
  1759. SyToken *pEnd; /* Last token in the stream */
  1760. sxu32 nErr; /* Total number of compilation error */
  1761. };
  1762. /* Forward references */
  1763. typedef struct jx9_vm_func_static_var jx9_vm_func_static_var;
  1764. typedef struct jx9_vm_func_arg jx9_vm_func_arg;
  1765. typedef struct jx9_vm_func jx9_vm_func;
  1766. typedef struct VmFrame VmFrame;
  1767. /*
  1768. * Each collected function argument is recorded in an instance
  1769. * of the following structure.
  1770. * Note that as an extension, JX9 implements full type hinting
  1771. * which mean that any function can have it's own signature.
  1772. * Example:
  1773. * function foo(int $a, string $b, float $c, ClassInstance $d){}
  1774. * This is how the powerful function overloading mechanism is
  1775. * implemented.
  1776. * Note that as an extension, JX9 allow function arguments to have
  1777. * any complex default value associated with them unlike the standard
  1778. * JX9 engine.
  1779. * Example:
  1780. * function foo(int $a = rand() & 1023){}
  1781. * now, when foo is called without arguments [i.e: foo()] the
  1782. * $a variable (first parameter) will be set to a random number
  1783. * between 0 and 1023 inclusive.
  1784. * Refer to the official documentation for more information on this
  1785. * mechanism and other extension introduced by the JX9 engine.
  1786. */
  1787. struct jx9_vm_func_arg
  1788. {
  1789. SyString sName; /* Argument name */
  1790. SySet aByteCode; /* Compiled default value associated with this argument */
  1791. sxu32 nType; /* Type of this argument [i.e: array, int, string, float, object, etc.] */
  1792. sxi32 iFlags; /* Configuration flags */
  1793. };
  1794. /*
  1795. * Each static variable is parsed out and remembered in an instance
  1796. * of the following structure.
  1797. * Note that as an extension, JX9 allow static variable have
  1798. * any complex default value associated with them unlike the standard
  1799. * JX9 engine.
  1800. * Example:
  1801. * static $rand_str = 'JX9'.rand_str(3); // Concatenate 'JX9' with
  1802. * // a random three characters(English alphabet)
  1803. * dump($rand_str);
  1804. * //You should see something like this
  1805. * string(6 'JX9awt');
  1806. */
  1807. struct jx9_vm_func_static_var
  1808. {
  1809. SyString sName; /* Static variable name */
  1810. SySet aByteCode; /* Compiled initialization expression */
  1811. sxu32 nIdx; /* Object index in the global memory object container */
  1812. };
  1813. /* Function configuration flags */
  1814. #define VM_FUNC_ARG_HAS_DEF 0x001 /* Argument has default value associated with it */
  1815. #define VM_FUNC_ARG_IGNORE 0x002 /* Do not install argument in the current frame */
  1816. /*
  1817. * Each user defined function is parsed out and stored in an instance
  1818. * of the following structure.
  1819. * JX9 introduced some powerfull extensions to the JX9 5 programming
  1820. * language like function overloading, type hinting, complex default
  1821. * arguments values and many more.
  1822. * Please refer to the official documentation for more information.
  1823. */
  1824. struct jx9_vm_func
  1825. {
  1826. SySet aArgs; /* Expected arguments (jx9_vm_func_arg instance) */
  1827. SySet aStatic; /* Static variable (jx9_vm_func_static_var instance) */
  1828. SyString sName; /* Function name */
  1829. SySet aByteCode; /* Compiled function body */
  1830. sxi32 iFlags; /* VM function configuration */
  1831. SyString sSignature; /* Function signature used to implement function overloading
  1832. * (Refer to the official docuemntation for more information
  1833. * on this powerfull feature)
  1834. */
  1835. void *pUserData; /* Upper layer private data associated with this instance */
  1836. jx9_vm_func *pNextName; /* Next VM function with the same name as this one */
  1837. };
  1838. /* Forward reference */
  1839. typedef struct jx9_builtin_constant jx9_builtin_constant;
  1840. typedef struct jx9_builtin_func jx9_builtin_func;
  1841. /*
  1842. * Each built-in foreign function (C function) is stored in an
  1843. * instance of the following structure.
  1844. * Please refer to the official documentation for more information
  1845. * on how to create/install foreign functions.
  1846. */
  1847. struct jx9_builtin_func
  1848. {
  1849. const char *zName; /* Function name [i.e: strlen(), rand(), array_merge(), etc.]*/
  1850. ProcHostFunction xFunc; /* C routine performing the computation */
  1851. };
  1852. /*
  1853. * Each built-in foreign constant is stored in an instance
  1854. * of the following structure.
  1855. * Please refer to the official documentation for more information
  1856. * on how to create/install foreign constants.
  1857. */
  1858. struct jx9_builtin_constant
  1859. {
  1860. const char *zName; /* Constant name */
  1861. ProcConstant xExpand; /* C routine responsible of expanding constant value*/
  1862. };
  1863. /*
  1864. * A single instruction of the virtual machine has an opcode
  1865. * and as many as three operands.
  1866. * Each VM instruction resulting from compiling a JX9 script
  1867. * is stored in an instance of the following structure.
  1868. */
  1869. typedef struct VmInstr VmInstr;
  1870. struct VmInstr
  1871. {
  1872. sxu8 iOp; /* Operation to preform */
  1873. sxi32 iP1; /* First operand */
  1874. sxu32 iP2; /* Second operand (Often the jump destination) */
  1875. void *p3; /* Third operand (Often Upper layer private data) */
  1876. };
  1877. /* Forward reference */
  1878. typedef struct jx9_case_expr jx9_case_expr;
  1879. typedef struct jx9_switch jx9_switch;
  1880. /*
  1881. * Each compiled case block in a swicth statement is compiled
  1882. * and stored in an instance of the following structure.
  1883. */
  1884. struct jx9_case_expr
  1885. {
  1886. SySet aByteCode; /* Compiled body of the case block */
  1887. sxu32 nStart; /* First instruction to execute */
  1888. };
  1889. /*
  1890. * Each compiled switch statement is parsed out and stored
  1891. * in an instance of the following structure.
  1892. */
  1893. struct jx9_switch
  1894. {
  1895. SySet aCaseExpr; /* Compile case block */
  1896. sxu32 nOut; /* First instruction to execute after this statement */
  1897. sxu32 nDefault; /* First instruction to execute in the default block */
  1898. };
  1899. /* Assertion flags */
  1900. #define JX9_ASSERT_DISABLE 0x01 /* Disable assertion */
  1901. #define JX9_ASSERT_WARNING 0x02 /* Issue a warning for each failed assertion */
  1902. #define JX9_ASSERT_BAIL 0x04 /* Terminate execution on failed assertions */
  1903. #define JX9_ASSERT_QUIET_EVAL 0x08 /* Not used */
  1904. #define JX9_ASSERT_CALLBACK 0x10 /* Callback to call on failed assertions */
  1905. /*
  1906. * An instance of the following structure hold the bytecode instructions
  1907. * resulting from compiling a JX9 script.
  1908. * This structure contains the complete state of the virtual machine.
  1909. */
  1910. struct jx9_vm
  1911. {
  1912. SyMemBackend sAllocator; /* Memory backend */
  1913. #if defined(JX9_ENABLE_THREADS)
  1914. SyMutex *pMutex; /* Recursive mutex associated with VM. */
  1915. #endif
  1916. jx9 *pEngine; /* Interpreter that own this VM */
  1917. SySet aByteCode; /* Default bytecode container */
  1918. SySet *pByteContainer; /* Current bytecode container */
  1919. VmFrame *pFrame; /* Stack of active frames */
  1920. SyPRNGCtx sPrng; /* PRNG context */
  1921. SySet aMemObj; /* Object allocation table */
  1922. SySet aLitObj; /* Literals allocation table */
  1923. jx9_value *aOps; /* Operand stack */
  1924. SySet aFreeObj; /* Stack of free memory objects */
  1925. SyHash hConstant; /* Host-application and user defined constants container */
  1926. SyHash hHostFunction; /* Host-application installable functions */
  1927. SyHash hFunction; /* Compiled functions */
  1928. SyHash hSuper; /* Global variable */
  1929. SyBlob sConsumer; /* Default VM consumer [i.e Redirect all VM output to this blob] */
  1930. SyBlob sWorker; /* General purpose working buffer */
  1931. SyBlob sArgv; /* $argv[] collector [refer to the [getopt()] implementation for more information] */
  1932. SySet aFiles; /* Stack of processed files */
  1933. SySet aPaths; /* Set of import paths */
  1934. SySet aIncluded; /* Set of included files */
  1935. SySet aIOstream; /* Installed IO stream container */
  1936. const jx9_io_stream *pDefStream; /* Default IO stream [i.e: typically this is the 'file://' stream] */
  1937. jx9_value sExec; /* Compiled script return value [Can be extracted via the JX9_VM_CONFIG_EXEC_VALUE directive]*/
  1938. void *pStdin; /* STDIN IO stream */
  1939. void *pStdout; /* STDOUT IO stream */
  1940. void *pStderr; /* STDERR IO stream */
  1941. int bErrReport; /* TRUE to report all runtime Error/Warning/Notice */
  1942. int nRecursionDepth; /* Current recursion depth */
  1943. int nMaxDepth; /* Maximum allowed recusion depth */
  1944. sxu32 nOutputLen; /* Total number of generated output */
  1945. jx9_output_consumer sVmConsumer; /* Registered output consumer callback */
  1946. int iAssertFlags; /* Assertion flags */
  1947. jx9_value sAssertCallback; /* Callback to call on failed assertions */
  1948. sxi32 iExitStatus; /* Script exit status */
  1949. jx9_gen_state sCodeGen; /* Code generator module */
  1950. jx9_vm *pNext, *pPrev; /* List of active VM's */
  1951. sxu32 nMagic; /* Sanity check against misuse */
  1952. };
  1953. /*
  1954. * Allowed value for jx9_vm.nMagic
  1955. */
  1956. #define JX9_VM_INIT 0xEA12CD72 /* VM correctly initialized */
  1957. #define JX9_VM_RUN 0xBA851227 /* VM ready to execute JX9 bytecode */
  1958. #define JX9_VM_EXEC 0xCDFE1DAD /* VM executing JX9 bytecode */
  1959. #define JX9_VM_STALE 0xDEAD2BAD /* Stale VM */
  1960. /*
  1961. * Error codes according to the JX9 language reference manual.
  1962. */
  1963. enum iErrCode
  1964. {
  1965. E_ERROR = 1, /* Fatal run-time errors. These indicate errors that can not be recovered
  1966. * from, such as a memory allocation problem. Execution of the script is
  1967. * halted.
  1968. * The only fatal error under JX9 is an out-of-memory. All others erros
  1969. * even a call to undefined function will not halt script execution.
  1970. */
  1971. E_WARNING , /* Run-time warnings (non-fatal errors). Execution of the script is not halted. */
  1972. E_PARSE , /* Compile-time parse errors. Parse errors should only be generated by the parser.*/
  1973. E_NOTICE , /* Run-time notices. Indicate that the script encountered something that could
  1974. * indicate an error, but could also happen in the normal course of running a script.
  1975. */
  1976. };
  1977. /*
  1978. * Each VM instruction resulting from compiling a JX9 script is represented
  1979. * by one of the following OP codes.
  1980. * The program consists of a linear sequence of operations. Each operation
  1981. * has an opcode and 3 operands.Operands P1 is an integer.
  1982. * Operand P2 is an unsigned integer and operand P3 is a memory address.
  1983. * Few opcodes use all 3 operands.
  1984. */
  1985. enum jx9_vm_op {
  1986. JX9_OP_DONE = 1, /* Done */
  1987. JX9_OP_HALT, /* Halt */
  1988. JX9_OP_LOAD, /* Load memory object */
  1989. JX9_OP_LOADC, /* Load constant */
  1990. JX9_OP_LOAD_IDX, /* Load array entry */
  1991. JX9_OP_LOAD_MAP, /* Load hashmap('array') */
  1992. JX9_OP_NOOP, /* NOOP */
  1993. JX9_OP_JMP, /* Unconditional jump */
  1994. JX9_OP_JZ, /* Jump on zero (FALSE jump) */
  1995. JX9_OP_JNZ, /* Jump on non-zero (TRUE jump) */
  1996. JX9_OP_POP, /* Stack POP */
  1997. JX9_OP_CAT, /* Concatenation */
  1998. JX9_OP_CVT_INT, /* Integer cast */
  1999. JX9_OP_CVT_STR, /* String cast */
  2000. JX9_OP_CVT_REAL, /* Float cast */
  2001. JX9_OP_CALL, /* Function call */
  2002. JX9_OP_UMINUS, /* Unary minus '-'*/
  2003. JX9_OP_UPLUS, /* Unary plus '+'*/
  2004. JX9_OP_BITNOT, /* Bitwise not '~' */
  2005. JX9_OP_LNOT, /* Logical not '!' */
  2006. JX9_OP_MUL, /* Multiplication '*' */
  2007. JX9_OP_DIV, /* Division '/' */
  2008. JX9_OP_MOD, /* Modulus '%' */
  2009. JX9_OP_ADD, /* Add '+' */
  2010. JX9_OP_SUB, /* Sub '-' */
  2011. JX9_OP_SHL, /* Left shift '<<' */
  2012. JX9_OP_SHR, /* Right shift '>>' */
  2013. JX9_OP_LT, /* Less than '<' */
  2014. JX9_OP_LE, /* Less or equal '<=' */
  2015. JX9_OP_GT, /* Greater than '>' */
  2016. JX9_OP_GE, /* Greater or equal '>=' */
  2017. JX9_OP_EQ, /* Equal '==' */
  2018. JX9_OP_NEQ, /* Not equal '!=' */
  2019. JX9_OP_TEQ, /* Type equal '===' */
  2020. JX9_OP_TNE, /* Type not equal '!==' */
  2021. JX9_OP_BAND, /* Bitwise and '&' */
  2022. JX9_OP_BXOR, /* Bitwise xor '^' */
  2023. JX9_OP_BOR, /* Bitwise or '|' */
  2024. JX9_OP_LAND, /* Logical and '&&','and' */
  2025. JX9_OP_LOR, /* Logical or '||','or' */
  2026. JX9_OP_LXOR, /* Logical xor 'xor' */
  2027. JX9_OP_STORE, /* Store Object */
  2028. JX9_OP_STORE_IDX, /* Store indexed object */
  2029. JX9_OP_PULL, /* Stack pull */
  2030. JX9_OP_SWAP, /* Stack swap */
  2031. JX9_OP_YIELD, /* Stack yield */
  2032. JX9_OP_CVT_BOOL, /* Boolean cast */
  2033. JX9_OP_CVT_NUMC, /* Numeric (integer, real or both) type cast */
  2034. JX9_OP_INCR, /* Increment ++ */
  2035. JX9_OP_DECR, /* Decrement -- */
  2036. JX9_OP_ADD_STORE, /* Add and store '+=' */
  2037. JX9_OP_SUB_STORE, /* Sub and store '-=' */
  2038. JX9_OP_MUL_STORE, /* Mul and store '*=' */
  2039. JX9_OP_DIV_STORE, /* Div and store '/=' */
  2040. JX9_OP_MOD_STORE, /* Mod and store '%=' */
  2041. JX9_OP_CAT_STORE, /* Cat and store '.=' */
  2042. JX9_OP_SHL_STORE, /* Shift left and store '>>=' */
  2043. JX9_OP_SHR_STORE, /* Shift right and store '<<=' */
  2044. JX9_OP_BAND_STORE, /* Bitand and store '&=' */
  2045. JX9_OP_BOR_STORE, /* Bitor and store '|=' */
  2046. JX9_OP_BXOR_STORE, /* Bitxor and store '^=' */
  2047. JX9_OP_CONSUME, /* Consume VM output */
  2048. JX9_OP_MEMBER, /* Object member run-time access */
  2049. JX9_OP_UPLINK, /* Run-Time frame link */
  2050. JX9_OP_CVT_NULL, /* NULL cast */
  2051. JX9_OP_CVT_ARRAY, /* Array cast */
  2052. JX9_OP_FOREACH_INIT, /* For each init */
  2053. JX9_OP_FOREACH_STEP, /* For each step */
  2054. JX9_OP_SWITCH /* Switch operation */
  2055. };
  2056. /* -- END-OF INSTRUCTIONS -- */
  2057. /*
  2058. * Expression Operators ID.
  2059. */
  2060. enum jx9_expr_id {
  2061. EXPR_OP_DOT, /* Member access */
  2062. EXPR_OP_DC, /* :: */
  2063. EXPR_OP_SUBSCRIPT, /* []: Subscripting */
  2064. EXPR_OP_FUNC_CALL, /* func_call() */
  2065. EXPR_OP_INCR, /* ++ */
  2066. EXPR_OP_DECR, /* -- */
  2067. EXPR_OP_BITNOT, /* ~ */
  2068. EXPR_OP_UMINUS, /* Unary minus */
  2069. EXPR_OP_UPLUS, /* Unary plus */
  2070. EXPR_OP_TYPECAST, /* Type cast [i.e: (int), (float), (string)...] */
  2071. EXPR_OP_ALT, /* @ */
  2072. EXPR_OP_INSTOF, /* instanceof */
  2073. EXPR_OP_LOGNOT, /* logical not ! */
  2074. EXPR_OP_MUL, /* Multiplication */
  2075. EXPR_OP_DIV, /* division */
  2076. EXPR_OP_MOD, /* Modulus */
  2077. EXPR_OP_ADD, /* Addition */
  2078. EXPR_OP_SUB, /* Substraction */
  2079. EXPR_OP_DDOT, /* Concatenation */
  2080. EXPR_OP_SHL, /* Left shift */
  2081. EXPR_OP_SHR, /* Right shift */
  2082. EXPR_OP_LT, /* Less than */
  2083. EXPR_OP_LE, /* Less equal */
  2084. EXPR_OP_GT, /* Greater than */
  2085. EXPR_OP_GE, /* Greater equal */
  2086. EXPR_OP_EQ, /* Equal == */
  2087. EXPR_OP_NE, /* Not equal != <> */
  2088. EXPR_OP_TEQ, /* Type equal === */
  2089. EXPR_OP_TNE, /* Type not equal !== */
  2090. EXPR_OP_SEQ, /* String equal 'eq' */
  2091. EXPR_OP_SNE, /* String not equal 'ne' */
  2092. EXPR_OP_BAND, /* Biwise and '&' */
  2093. EXPR_OP_REF, /* Reference operator '&' */
  2094. EXPR_OP_XOR, /* bitwise xor '^' */
  2095. EXPR_OP_BOR, /* bitwise or '|' */
  2096. EXPR_OP_LAND, /* Logical and '&&','and' */
  2097. EXPR_OP_LOR, /* Logical or '||','or'*/
  2098. EXPR_OP_LXOR, /* Logical xor 'xor' */
  2099. EXPR_OP_QUESTY, /* Ternary operator '?' */
  2100. EXPR_OP_ASSIGN, /* Assignment '=' */
  2101. EXPR_OP_ADD_ASSIGN, /* Combined operator: += */
  2102. EXPR_OP_SUB_ASSIGN, /* Combined operator: -= */
  2103. EXPR_OP_MUL_ASSIGN, /* Combined operator: *= */
  2104. EXPR_OP_DIV_ASSIGN, /* Combined operator: /= */
  2105. EXPR_OP_MOD_ASSIGN, /* Combined operator: %= */
  2106. EXPR_OP_DOT_ASSIGN, /* Combined operator: .= */
  2107. EXPR_OP_AND_ASSIGN, /* Combined operator: &= */
  2108. EXPR_OP_OR_ASSIGN, /* Combined operator: |= */
  2109. EXPR_OP_XOR_ASSIGN, /* Combined operator: ^= */
  2110. EXPR_OP_SHL_ASSIGN, /* Combined operator: <<= */
  2111. EXPR_OP_SHR_ASSIGN, /* Combined operator: >>= */
  2112. EXPR_OP_COMMA /* Comma expression */
  2113. };
  2114. /*
  2115. * Lexer token codes
  2116. * The following set of constants are the tokens recognized
  2117. * by the lexer when processing JX9 input.
  2118. * Important: Token values MUST BE A POWER OF TWO.
  2119. */
  2120. #define JX9_TK_INTEGER 0x0000001 /* Integer */
  2121. #define JX9_TK_REAL 0x0000002 /* Real number */
  2122. #define JX9_TK_NUM (JX9_TK_INTEGER|JX9_TK_REAL) /* Numeric token, either integer or real */
  2123. #define JX9_TK_KEYWORD 0x0000004 /* Keyword [i.e: while, for, if, foreach...] */
  2124. #define JX9_TK_ID 0x0000008 /* Alphanumeric or UTF-8 stream */
  2125. #define JX9_TK_DOLLAR 0x0000010 /* '$' Dollar sign */
  2126. #define JX9_TK_OP 0x0000020 /* Operator [i.e: +, *, /...] */
  2127. #define JX9_TK_OCB 0x0000040 /* Open curly brace'{' */
  2128. #define JX9_TK_CCB 0x0000080 /* Closing curly brace'}' */
  2129. #define JX9_TK_DOT 0x0000100 /* Dot . */
  2130. #define JX9_TK_LPAREN 0x0000200 /* Left parenthesis '(' */
  2131. #define JX9_TK_RPAREN 0x0000400 /* Right parenthesis ')' */
  2132. #define JX9_TK_OSB 0x0000800 /* Open square bracket '[' */
  2133. #define JX9_TK_CSB 0x0001000 /* Closing square bracket ']' */
  2134. #define JX9_TK_DSTR 0x0002000 /* Double quoted string "$str" */
  2135. #define JX9_TK_SSTR 0x0004000 /* Single quoted string 'str' */
  2136. #define JX9_TK_NOWDOC 0x0010000 /* Nowdoc <<< */
  2137. #define JX9_TK_COMMA 0x0020000 /* Comma ',' */
  2138. #define JX9_TK_SEMI 0x0040000 /* Semi-colon ";" */
  2139. #define JX9_TK_BSTR 0x0080000 /* Backtick quoted string [i.e: Shell command `date`] */
  2140. #define JX9_TK_COLON 0x0100000 /* single Colon ':' */
  2141. #define JX9_TK_AMPER 0x0200000 /* Ampersand '&' */
  2142. #define JX9_TK_EQUAL 0x0400000 /* Equal '=' */
  2143. #define JX9_TK_OTHER 0x1000000 /* Other symbols */
  2144. /*
  2145. * JX9 keyword.
  2146. * These words have special meaning in JX9. Some of them represent things which look like
  2147. * functions, some look like constants, and so on, but they're not, really: they are language constructs.
  2148. * You cannot use any of the following words as constants, object names, function or method names.
  2149. * Using them as variable names is generally OK, but could lead to confusion.
  2150. */
  2151. #define JX9_TKWRD_SWITCH 1 /* switch */
  2152. #define JX9_TKWRD_PRINT 2 /* print */
  2153. #define JX9_TKWRD_ELIF 0x4000000 /* elseif: MUST BE A POWER OF TWO */
  2154. #define JX9_TKWRD_ELSE 0x8000000 /* else: MUST BE A POWER OF TWO */
  2155. #define JX9_TKWRD_IF 3 /* if */
  2156. #define JX9_TKWRD_STATIC 4 /* static */
  2157. #define JX9_TKWRD_CASE 5 /* case */
  2158. #define JX9_TKWRD_FUNCTION 6 /* function */
  2159. #define JX9_TKWRD_CONST 7 /* const */
  2160. /* The number '8' is reserved for JX9_TK_ID */
  2161. #define JX9_TKWRD_WHILE 9 /* while */
  2162. #define JX9_TKWRD_DEFAULT 10 /* default */
  2163. #define JX9_TKWRD_AS 11 /* as */
  2164. #define JX9_TKWRD_CONTINUE 12 /* continue */
  2165. #define JX9_TKWRD_EXIT 13 /* exit */
  2166. #define JX9_TKWRD_DIE 14 /* die */
  2167. #define JX9_TKWRD_IMPORT 15 /* import */
  2168. #define JX9_TKWRD_INCLUDE 16 /* include */
  2169. #define JX9_TKWRD_FOR 17 /* for */
  2170. #define JX9_TKWRD_FOREACH 18 /* foreach */
  2171. #define JX9_TKWRD_RETURN 19 /* return */
  2172. #define JX9_TKWRD_BREAK 20 /* break */
  2173. #define JX9_TKWRD_UPLINK 21 /* uplink */
  2174. #define JX9_TKWRD_BOOL 0x8000 /* bool: MUST BE A POWER OF TWO */
  2175. #define JX9_TKWRD_INT 0x10000 /* int: MUST BE A POWER OF TWO */
  2176. #define JX9_TKWRD_FLOAT 0x20000 /* float: MUST BE A POWER OF TWO */
  2177. #define JX9_TKWRD_STRING 0x40000 /* string: MUST BE A POWER OF TWO */
  2178. /* json.c function prototypes */
  2179. JX9_PRIVATE int jx9JsonSerialize(jx9_value *pValue,SyBlob *pOut);
  2180. JX9_PRIVATE int jx9JsonDecode(jx9_context *pCtx,const char *zJSON,int nByte);
  2181. /* memobj.c function prototypes */
  2182. JX9_PRIVATE sxi32 jx9MemObjDump(SyBlob *pOut, jx9_value *pObj);
  2183. JX9_PRIVATE const char * jx9MemObjTypeDump(jx9_value *pVal);
  2184. JX9_PRIVATE sxi32 jx9MemObjAdd(jx9_value *pObj1, jx9_value *pObj2, int bAddStore);
  2185. JX9_PRIVATE sxi32 jx9MemObjCmp(jx9_value *pObj1, jx9_value *pObj2, int bStrict, int iNest);
  2186. JX9_PRIVATE sxi32 jx9MemObjInitFromString(jx9_vm *pVm, jx9_value *pObj, const SyString *pVal);
  2187. JX9_PRIVATE sxi32 jx9MemObjInitFromArray(jx9_vm *pVm, jx9_value *pObj, jx9_hashmap *pArray);
  2188. #if 0
  2189. /* Not used in the current release of the JX9 engine */
  2190. JX9_PRIVATE sxi32 jx9MemObjInitFromReal(jx9_vm *pVm, jx9_value *pObj, jx9_real rVal);
  2191. #endif
  2192. JX9_PRIVATE sxi32 jx9MemObjInitFromInt(jx9_vm *pVm, jx9_value *pObj, sxi64 iVal);
  2193. JX9_PRIVATE sxi32 jx9MemObjInitFromBool(jx9_vm *pVm, jx9_value *pObj, sxi32 iVal);
  2194. JX9_PRIVATE sxi32 jx9MemObjInit(jx9_vm *pVm, jx9_value *pObj);
  2195. JX9_PRIVATE sxi32 jx9MemObjStringAppend(jx9_value *pObj, const char *zData, sxu32 nLen);
  2196. #if 0
  2197. /* Not used in the current release of the JX9 engine */
  2198. JX9_PRIVATE sxi32 jx9MemObjStringFormat(jx9_value *pObj, const char *zFormat, va_list ap);
  2199. #endif
  2200. JX9_PRIVATE sxi32 jx9MemObjStore(jx9_value *pSrc, jx9_value *pDest);
  2201. JX9_PRIVATE sxi32 jx9MemObjLoad(jx9_value *pSrc, jx9_value *pDest);
  2202. JX9_PRIVATE sxi32 jx9MemObjRelease(jx9_value *pObj);
  2203. JX9_PRIVATE sxi32 jx9MemObjToNumeric(jx9_value *pObj);
  2204. JX9_PRIVATE sxi32 jx9MemObjTryInteger(jx9_value *pObj);
  2205. JX9_PRIVATE ProcMemObjCast jx9MemObjCastMethod(sxi32 iFlags);
  2206. JX9_PRIVATE sxi32 jx9MemObjIsNumeric(jx9_value *pObj);
  2207. JX9_PRIVATE sxi32 jx9MemObjIsEmpty(jx9_value *pObj);
  2208. JX9_PRIVATE sxi32 jx9MemObjToHashmap(jx9_value *pObj);
  2209. JX9_PRIVATE sxi32 jx9MemObjToString(jx9_value *pObj);
  2210. JX9_PRIVATE sxi32 jx9MemObjToNull(jx9_value *pObj);
  2211. JX9_PRIVATE sxi32 jx9MemObjToReal(jx9_value *pObj);
  2212. JX9_PRIVATE sxi32 jx9MemObjToInteger(jx9_value *pObj);
  2213. JX9_PRIVATE sxi32 jx9MemObjToBool(jx9_value *pObj);
  2214. JX9_PRIVATE sxi64 jx9TokenValueToInt64(SyString *pData);
  2215. /* lex.c function prototypes */
  2216. JX9_PRIVATE sxi32 jx9Tokenize(const char *zInput, sxu32 nLen, SySet *pOut);
  2217. /* vm.c function prototypes */
  2218. JX9_PRIVATE void jx9VmReleaseContextValue(jx9_context *pCtx, jx9_value *pValue);
  2219. JX9_PRIVATE sxi32 jx9VmInitFuncState(jx9_vm *pVm, jx9_vm_func *pFunc, const char *zName, sxu32 nByte,
  2220. sxi32 iFlags, void *pUserData);
  2221. JX9_PRIVATE sxi32 jx9VmInstallUserFunction(jx9_vm *pVm, jx9_vm_func *pFunc, SyString *pName);
  2222. JX9_PRIVATE sxi32 jx9VmRegisterConstant(jx9_vm *pVm, const SyString *pName, ProcConstant xExpand, void *pUserData);
  2223. JX9_PRIVATE sxi32 jx9VmInstallForeignFunction(jx9_vm *pVm, const SyString *pName, ProcHostFunction xFunc, void *pUserData);
  2224. JX9_PRIVATE sxi32 jx9VmBlobConsumer(const void *pSrc, unsigned int nLen, void *pUserData);
  2225. JX9_PRIVATE jx9_value * jx9VmReserveMemObj(jx9_vm *pVm,sxu32 *pIndex);
  2226. JX9_PRIVATE jx9_value * jx9VmReserveConstObj(jx9_vm *pVm, sxu32 *pIndex);
  2227. JX9_PRIVATE sxi32 jx9VmOutputConsume(jx9_vm *pVm, SyString *pString);
  2228. JX9_PRIVATE sxi32 jx9VmOutputConsumeAp(jx9_vm *pVm, const char *zFormat, va_list ap);
  2229. JX9_PRIVATE sxi32 jx9VmThrowErrorAp(jx9_vm *pVm, SyString *pFuncName, sxi32 iErr, const char *zFormat, va_list ap);
  2230. JX9_PRIVATE sxi32 jx9VmThrowError(jx9_vm *pVm, SyString *pFuncName, sxi32 iErr, const char *zMessage);
  2231. JX9_PRIVATE void jx9VmExpandConstantValue(jx9_value *pVal, void *pUserData);
  2232. JX9_PRIVATE sxi32 jx9VmDump(jx9_vm *pVm, ProcConsumer xConsumer, void *pUserData);
  2233. JX9_PRIVATE sxi32 jx9VmInit(jx9_vm *pVm, jx9 *pEngine);
  2234. JX9_PRIVATE sxi32 jx9VmConfigure(jx9_vm *pVm, sxi32 nOp, va_list ap);
  2235. JX9_PRIVATE sxi32 jx9VmByteCodeExec(jx9_vm *pVm);
  2236. JX9_PRIVATE jx9_value * jx9VmExtractVariable(jx9_vm *pVm,SyString *pVar);
  2237. JX9_PRIVATE sxi32 jx9VmRelease(jx9_vm *pVm);
  2238. JX9_PRIVATE sxi32 jx9VmReset(jx9_vm *pVm);
  2239. JX9_PRIVATE sxi32 jx9VmMakeReady(jx9_vm *pVm);
  2240. JX9_PRIVATE sxu32 jx9VmInstrLength(jx9_vm *pVm);
  2241. JX9_PRIVATE VmInstr * jx9VmPopInstr(jx9_vm *pVm);
  2242. JX9_PRIVATE VmInstr * jx9VmPeekInstr(jx9_vm *pVm);
  2243. JX9_PRIVATE VmInstr *jx9VmGetInstr(jx9_vm *pVm, sxu32 nIndex);
  2244. JX9_PRIVATE SySet * jx9VmGetByteCodeContainer(jx9_vm *pVm);
  2245. JX9_PRIVATE sxi32 jx9VmSetByteCodeContainer(jx9_vm *pVm, SySet *pContainer);
  2246. JX9_PRIVATE sxi32 jx9VmEmitInstr(jx9_vm *pVm, sxi32 iOp, sxi32 iP1, sxu32 iP2, void *p3, sxu32 *pIndex);
  2247. JX9_PRIVATE sxu32 jx9VmRandomNum(jx9_vm *pVm);
  2248. JX9_PRIVATE sxi32 jx9VmCallUserFunction(jx9_vm *pVm, jx9_value *pFunc, int nArg, jx9_value **apArg, jx9_value *pResult);
  2249. JX9_PRIVATE sxi32 jx9VmCallUserFunctionAp(jx9_vm *pVm, jx9_value *pFunc, jx9_value *pResult, ...);
  2250. JX9_PRIVATE sxi32 jx9VmUnsetMemObj(jx9_vm *pVm, sxu32 nObjIdx);
  2251. JX9_PRIVATE void jx9VmRandomString(jx9_vm *pVm, char *zBuf, int nLen);
  2252. JX9_PRIVATE int jx9VmIsCallable(jx9_vm *pVm, jx9_value *pValue);
  2253. JX9_PRIVATE sxi32 jx9VmPushFilePath(jx9_vm *pVm, const char *zPath, int nLen, sxu8 bMain, sxi32 *pNew);
  2254. #ifndef JX9_DISABLE_BUILTIN_FUNC
  2255. JX9_PRIVATE const jx9_io_stream * jx9VmGetStreamDevice(jx9_vm *pVm, const char **pzDevice, int nByte);
  2256. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  2257. JX9_PRIVATE int jx9Utf8Read(
  2258. const unsigned char *z, /* First byte of UTF-8 character */
  2259. const unsigned char *zTerm, /* Pretend this byte is 0x00 */
  2260. const unsigned char **pzNext /* Write first byte past UTF-8 char here */
  2261. );
  2262. /* parse.c function prototypes */
  2263. JX9_PRIVATE int jx9IsLangConstruct(sxu32 nKeyID);
  2264. JX9_PRIVATE sxi32 jx9ExprMakeTree(jx9_gen_state *pGen, SySet *pExprNode, jx9_expr_node **ppRoot);
  2265. JX9_PRIVATE sxi32 jx9GetNextExpr(SyToken *pStart, SyToken *pEnd, SyToken **ppNext);
  2266. JX9_PRIVATE void jx9DelimitNestedTokens(SyToken *pIn, SyToken *pEnd, sxu32 nTokStart, sxu32 nTokEnd, SyToken **ppEnd);
  2267. JX9_PRIVATE const jx9_expr_op * jx9ExprExtractOperator(SyString *pStr, SyToken *pLast);
  2268. JX9_PRIVATE sxi32 jx9ExprFreeTree(jx9_gen_state *pGen, SySet *pNodeSet);
  2269. /* compile.c function prototypes */
  2270. JX9_PRIVATE ProcNodeConstruct jx9GetNodeHandler(sxu32 nNodeType);
  2271. JX9_PRIVATE sxi32 jx9CompileLangConstruct(jx9_gen_state *pGen, sxi32 iCompileFlag);
  2272. JX9_PRIVATE sxi32 jx9CompileJsonArray(jx9_gen_state *pGen, sxi32 iCompileFlag);
  2273. JX9_PRIVATE sxi32 jx9CompileJsonObject(jx9_gen_state *pGen, sxi32 iCompileFlag);
  2274. JX9_PRIVATE sxi32 jx9CompileVariable(jx9_gen_state *pGen, sxi32 iCompileFlag);
  2275. JX9_PRIVATE sxi32 jx9CompileLiteral(jx9_gen_state *pGen, sxi32 iCompileFlag);
  2276. JX9_PRIVATE sxi32 jx9CompileSimpleString(jx9_gen_state *pGen, sxi32 iCompileFlag);
  2277. JX9_PRIVATE sxi32 jx9CompileString(jx9_gen_state *pGen, sxi32 iCompileFlag);
  2278. JX9_PRIVATE sxi32 jx9CompileAnnonFunc(jx9_gen_state *pGen, sxi32 iCompileFlag);
  2279. JX9_PRIVATE sxi32 jx9InitCodeGenerator(jx9_vm *pVm, ProcConsumer xErr, void *pErrData);
  2280. JX9_PRIVATE sxi32 jx9ResetCodeGenerator(jx9_vm *pVm, ProcConsumer xErr, void *pErrData);
  2281. JX9_PRIVATE sxi32 jx9GenCompileError(jx9_gen_state *pGen, sxi32 nErrType, sxu32 nLine, const char *zFormat, ...);
  2282. JX9_PRIVATE sxi32 jx9CompileScript(jx9_vm *pVm, SyString *pScript, sxi32 iFlags);
  2283. /* constant.c function prototypes */
  2284. JX9_PRIVATE void jx9RegisterBuiltInConstant(jx9_vm *pVm);
  2285. /* builtin.c function prototypes */
  2286. JX9_PRIVATE void jx9RegisterBuiltInFunction(jx9_vm *pVm);
  2287. /* hashmap.c function prototypes */
  2288. JX9_PRIVATE jx9_hashmap * jx9NewHashmap(jx9_vm *pVm, sxu32 (*xIntHash)(sxi64), sxu32 (*xBlobHash)(const void *, sxu32));
  2289. JX9_PRIVATE sxi32 jx9HashmapLoadBuiltin(jx9_vm *pVm);
  2290. JX9_PRIVATE sxi32 jx9HashmapRelease(jx9_hashmap *pMap, int FreeDS);
  2291. JX9_PRIVATE void jx9HashmapUnref(jx9_hashmap *pMap);
  2292. JX9_PRIVATE sxi32 jx9HashmapLookup(jx9_hashmap *pMap, jx9_value *pKey, jx9_hashmap_node **ppNode);
  2293. JX9_PRIVATE sxi32 jx9HashmapInsert(jx9_hashmap *pMap, jx9_value *pKey, jx9_value *pVal);
  2294. JX9_PRIVATE sxi32 jx9HashmapUnion(jx9_hashmap *pLeft, jx9_hashmap *pRight);
  2295. JX9_PRIVATE sxi32 jx9HashmapDup(jx9_hashmap *pSrc, jx9_hashmap *pDest);
  2296. JX9_PRIVATE sxi32 jx9HashmapCmp(jx9_hashmap *pLeft, jx9_hashmap *pRight, int bStrict);
  2297. JX9_PRIVATE void jx9HashmapResetLoopCursor(jx9_hashmap *pMap);
  2298. JX9_PRIVATE jx9_hashmap_node * jx9HashmapGetNextEntry(jx9_hashmap *pMap);
  2299. JX9_PRIVATE void jx9HashmapExtractNodeValue(jx9_hashmap_node *pNode, jx9_value *pValue, int bStore);
  2300. JX9_PRIVATE void jx9HashmapExtractNodeKey(jx9_hashmap_node *pNode, jx9_value *pKey);
  2301. JX9_PRIVATE void jx9RegisterHashmapFunctions(jx9_vm *pVm);
  2302. JX9_PRIVATE sxi32 jx9HashmapWalk(jx9_hashmap *pMap, int (*xWalk)(jx9_value *, jx9_value *, void *), void *pUserData);
  2303. #ifndef JX9_DISABLE_BUILTIN_FUNC
  2304. JX9_PRIVATE int jx9HashmapValuesToSet(jx9_hashmap *pMap, SySet *pOut);
  2305. /* builtin.c function prototypes */
  2306. JX9_PRIVATE sxi32 jx9InputFormat(int (*xConsumer)(jx9_context *, const char *, int, void *),
  2307. jx9_context *pCtx, const char *zIn, int nByte, int nArg, jx9_value **apArg, void *pUserData, int vf);
  2308. JX9_PRIVATE sxi32 jx9ProcessCsv(const char *zInput, int nByte, int delim, int encl,
  2309. int escape, sxi32 (*xConsumer)(const char *, int, void *), void *pUserData);
  2310. JX9_PRIVATE sxi32 jx9CsvConsumer(const char *zToken, int nTokenLen, void *pUserData);
  2311. JX9_PRIVATE sxi32 jx9StripTagsFromString(jx9_context *pCtx, const char *zIn, int nByte, const char *zTaglist, int nTaglen);
  2312. JX9_PRIVATE sxi32 jx9ParseIniString(jx9_context *pCtx, const char *zIn, sxu32 nByte, int bProcessSection);
  2313. #endif
  2314. /* vfs.c */
  2315. #ifndef JX9_DISABLE_BUILTIN_FUNC
  2316. JX9_PRIVATE void * jx9StreamOpenHandle(jx9_vm *pVm, const jx9_io_stream *pStream, const char *zFile,
  2317. int iFlags, int use_include, jx9_value *pResource, int bPushInclude, int *pNew);
  2318. JX9_PRIVATE sxi32 jx9StreamReadWholeFile(void *pHandle, const jx9_io_stream *pStream, SyBlob *pOut);
  2319. JX9_PRIVATE void jx9StreamCloseHandle(const jx9_io_stream *pStream, void *pHandle);
  2320. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  2321. JX9_PRIVATE const char * jx9ExtractDirName(const char *zPath, int nByte, int *pLen);
  2322. JX9_PRIVATE sxi32 jx9RegisterIORoutine(jx9_vm *pVm);
  2323. JX9_PRIVATE const jx9_vfs * jx9ExportBuiltinVfs(void);
  2324. JX9_PRIVATE void * jx9ExportStdin(jx9_vm *pVm);
  2325. JX9_PRIVATE void * jx9ExportStdout(jx9_vm *pVm);
  2326. JX9_PRIVATE void * jx9ExportStderr(jx9_vm *pVm);
  2327. /* lib.c function prototypes */
  2328. #ifndef JX9_DISABLE_BUILTIN_FUNC
  2329. JX9_PRIVATE sxi32 SyArchiveInit(SyArchive *pArch, SyMemBackend *pAllocator, ProcHash xHash, ProcRawStrCmp xCmp);
  2330. JX9_PRIVATE sxi32 SyArchiveRelease(SyArchive *pArch);
  2331. JX9_PRIVATE sxi32 SyArchiveResetLoopCursor(SyArchive *pArch);
  2332. JX9_PRIVATE sxi32 SyArchiveGetNextEntry(SyArchive *pArch, SyArchiveEntry **ppEntry);
  2333. JX9_PRIVATE sxi32 SyZipExtractFromBuf(SyArchive *pArch, const char *zBuf, sxu32 nLen);
  2334. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  2335. #ifndef JX9_DISABLE_BUILTIN_FUNC
  2336. JX9_PRIVATE sxi32 SyBinToHexConsumer(const void *pIn, sxu32 nLen, ProcConsumer xConsumer, void *pConsumerData);
  2337. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  2338. #ifndef JX9_DISABLE_BUILTIN_FUNC
  2339. #ifndef JX9_DISABLE_HASH_FUNC
  2340. JX9_PRIVATE sxu32 SyCrc32(const void *pSrc, sxu32 nLen);
  2341. JX9_PRIVATE void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len);
  2342. JX9_PRIVATE void MD5Final(unsigned char digest[16], MD5Context *ctx);
  2343. JX9_PRIVATE sxi32 MD5Init(MD5Context *pCtx);
  2344. JX9_PRIVATE sxi32 SyMD5Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[16]);
  2345. JX9_PRIVATE void SHA1Init(SHA1Context *context);
  2346. JX9_PRIVATE void SHA1Update(SHA1Context *context, const unsigned char *data, unsigned int len);
  2347. JX9_PRIVATE void SHA1Final(SHA1Context *context, unsigned char digest[20]);
  2348. JX9_PRIVATE sxi32 SySha1Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[20]);
  2349. #endif
  2350. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  2351. JX9_PRIVATE sxi32 SyRandomness(SyPRNGCtx *pCtx, void *pBuf, sxu32 nLen);
  2352. JX9_PRIVATE sxi32 SyRandomnessInit(SyPRNGCtx *pCtx, ProcRandomSeed xSeed, void *pUserData);
  2353. JX9_PRIVATE sxu32 SyBufferFormat(char *zBuf, sxu32 nLen, const char *zFormat, ...);
  2354. JX9_PRIVATE sxu32 SyBlobFormatAp(SyBlob *pBlob, const char *zFormat, va_list ap);
  2355. JX9_PRIVATE sxu32 SyBlobFormat(SyBlob *pBlob, const char *zFormat, ...);
  2356. JX9_PRIVATE sxi32 SyProcFormat(ProcConsumer xConsumer, void *pData, const char *zFormat, ...);
  2357. #ifndef JX9_DISABLE_BUILTIN_FUNC
  2358. JX9_PRIVATE const char *SyTimeGetMonth(sxi32 iMonth);
  2359. JX9_PRIVATE const char *SyTimeGetDay(sxi32 iDay);
  2360. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  2361. JX9_PRIVATE sxi32 SyUriDecode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData, int bUTF8);
  2362. #ifndef JX9_DISABLE_BUILTIN_FUNC
  2363. JX9_PRIVATE sxi32 SyUriEncode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData);
  2364. #endif
  2365. JX9_PRIVATE sxi32 SyLexRelease(SyLex *pLex);
  2366. JX9_PRIVATE sxi32 SyLexTokenizeInput(SyLex *pLex, const char *zInput, sxu32 nLen, void *pCtxData, ProcSort xSort, ProcCmp xCmp);
  2367. JX9_PRIVATE sxi32 SyLexInit(SyLex *pLex, SySet *pSet, ProcTokenizer xTokenizer, void *pUserData);
  2368. #ifndef JX9_DISABLE_BUILTIN_FUNC
  2369. JX9_PRIVATE sxi32 SyBase64Decode(const char *zB64, sxu32 nLen, ProcConsumer xConsumer, void *pUserData);
  2370. JX9_PRIVATE sxi32 SyBase64Encode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData);
  2371. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  2372. JX9_PRIVATE sxi32 SyStrToReal(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
  2373. JX9_PRIVATE sxi32 SyBinaryStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
  2374. JX9_PRIVATE sxi32 SyOctalStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
  2375. JX9_PRIVATE sxi32 SyHexStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
  2376. JX9_PRIVATE sxi32 SyHexToint(sxi32 c);
  2377. JX9_PRIVATE sxi32 SyStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
  2378. JX9_PRIVATE sxi32 SyStrToInt32(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest);
  2379. JX9_PRIVATE sxi32 SyStrIsNumeric(const char *zSrc, sxu32 nLen, sxu8 *pReal, const char **pzTail);
  2380. JX9_PRIVATE sxi32 SyHashInsert(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void *pUserData);
  2381. JX9_PRIVATE sxi32 SyHashForEach(SyHash *pHash, sxi32(*xStep)(SyHashEntry *, void *), void *pUserData);
  2382. JX9_PRIVATE sxi32 SyHashDeleteEntry(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void **ppUserData);
  2383. JX9_PRIVATE SyHashEntry *SyHashGet(SyHash *pHash, const void *pKey, sxu32 nKeyLen);
  2384. JX9_PRIVATE sxi32 SyHashRelease(SyHash *pHash);
  2385. JX9_PRIVATE sxi32 SyHashInit(SyHash *pHash, SyMemBackend *pAllocator, ProcHash xHash, ProcCmp xCmp);
  2386. JX9_PRIVATE void *SySetAt(SySet *pSet, sxu32 nIdx);
  2387. JX9_PRIVATE void *SySetPop(SySet *pSet);
  2388. JX9_PRIVATE void *SySetPeek(SySet *pSet);
  2389. JX9_PRIVATE sxi32 SySetRelease(SySet *pSet);
  2390. JX9_PRIVATE sxi32 SySetReset(SySet *pSet);
  2391. JX9_PRIVATE sxi32 SySetResetCursor(SySet *pSet);
  2392. JX9_PRIVATE sxi32 SySetGetNextEntry(SySet *pSet, void **ppEntry);
  2393. JX9_PRIVATE sxi32 SySetAlloc(SySet *pSet, sxi32 nItem);
  2394. JX9_PRIVATE sxi32 SySetPut(SySet *pSet, const void *pItem);
  2395. JX9_PRIVATE sxi32 SySetInit(SySet *pSet, SyMemBackend *pAllocator, sxu32 ElemSize);
  2396. #ifndef JX9_DISABLE_BUILTIN_FUNC
  2397. JX9_PRIVATE sxi32 SyBlobSearch(const void *pBlob, sxu32 nLen, const void *pPattern, sxu32 pLen, sxu32 *pOfft);
  2398. #endif
  2399. JX9_PRIVATE sxi32 SyBlobRelease(SyBlob *pBlob);
  2400. JX9_PRIVATE sxi32 SyBlobReset(SyBlob *pBlob);
  2401. JX9_PRIVATE sxi32 SyBlobDup(SyBlob *pSrc, SyBlob *pDest);
  2402. JX9_PRIVATE sxi32 SyBlobNullAppend(SyBlob *pBlob);
  2403. JX9_PRIVATE sxi32 SyBlobAppend(SyBlob *pBlob, const void *pData, sxu32 nSize);
  2404. JX9_PRIVATE sxi32 SyBlobReadOnly(SyBlob *pBlob, const void *pData, sxu32 nByte);
  2405. JX9_PRIVATE sxi32 SyBlobInit(SyBlob *pBlob, SyMemBackend *pAllocator);
  2406. JX9_PRIVATE sxi32 SyBlobInitFromBuf(SyBlob *pBlob, void *pBuffer, sxu32 nSize);
  2407. JX9_PRIVATE char *SyMemBackendStrDup(SyMemBackend *pBackend, const char *zSrc, sxu32 nSize);
  2408. JX9_PRIVATE void *SyMemBackendDup(SyMemBackend *pBackend, const void *pSrc, sxu32 nSize);
  2409. JX9_PRIVATE sxi32 SyMemBackendRelease(SyMemBackend *pBackend);
  2410. JX9_PRIVATE sxi32 SyMemBackendInitFromOthers(SyMemBackend *pBackend, const SyMemMethods *pMethods, ProcMemError xMemErr, void *pUserData);
  2411. JX9_PRIVATE sxi32 SyMemBackendInit(SyMemBackend *pBackend, ProcMemError xMemErr, void *pUserData);
  2412. JX9_PRIVATE sxi32 SyMemBackendInitFromParent(SyMemBackend *pBackend, SyMemBackend *pParent);
  2413. #if 0
  2414. /* Not used in the current release of the JX9 engine */
  2415. JX9_PRIVATE void *SyMemBackendPoolRealloc(SyMemBackend *pBackend, void *pOld, sxu32 nByte);
  2416. #endif
  2417. JX9_PRIVATE sxi32 SyMemBackendPoolFree(SyMemBackend *pBackend, void *pChunk);
  2418. JX9_PRIVATE void *SyMemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nByte);
  2419. JX9_PRIVATE sxi32 SyMemBackendFree(SyMemBackend *pBackend, void *pChunk);
  2420. JX9_PRIVATE void *SyMemBackendRealloc(SyMemBackend *pBackend, void *pOld, sxu32 nByte);
  2421. JX9_PRIVATE void *SyMemBackendAlloc(SyMemBackend *pBackend, sxu32 nByte);
  2422. #if defined(JX9_ENABLE_THREADS)
  2423. JX9_PRIVATE sxi32 SyMemBackendMakeThreadSafe(SyMemBackend *pBackend, const SyMutexMethods *pMethods);
  2424. JX9_PRIVATE sxi32 SyMemBackendDisbaleMutexing(SyMemBackend *pBackend);
  2425. #endif
  2426. JX9_PRIVATE sxu32 SyMemcpy(const void *pSrc, void *pDest, sxu32 nLen);
  2427. JX9_PRIVATE sxi32 SyMemcmp(const void *pB1, const void *pB2, sxu32 nSize);
  2428. JX9_PRIVATE void SyZero(void *pSrc, sxu32 nSize);
  2429. JX9_PRIVATE sxi32 SyStrnicmp(const char *zLeft, const char *zRight, sxu32 SLen);
  2430. #ifndef JX9_DISABLE_BUILTIN_FUNC
  2431. JX9_PRIVATE sxi32 SyStrncmp(const char *zLeft, const char *zRight, sxu32 nLen);
  2432. #endif
  2433. JX9_PRIVATE sxi32 SyByteListFind(const char *zSrc, sxu32 nLen, const char *zList, sxu32 *pFirstPos);
  2434. #ifndef JX9_DISABLE_BUILTIN_FUNC
  2435. JX9_PRIVATE sxi32 SyByteFind2(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos);
  2436. #endif
  2437. JX9_PRIVATE sxi32 SyByteFind(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos);
  2438. JX9_PRIVATE sxu32 SyStrlen(const char *zSrc);
  2439. #if defined(JX9_ENABLE_THREADS)
  2440. JX9_PRIVATE const SyMutexMethods *SyMutexExportMethods(void);
  2441. JX9_PRIVATE sxi32 SyMemBackendMakeThreadSafe(SyMemBackend *pBackend, const SyMutexMethods *pMethods);
  2442. JX9_PRIVATE sxi32 SyMemBackendDisbaleMutexing(SyMemBackend *pBackend);
  2443. #endif
  2444. #endif /* __JX9INT_H__ */
  2445. /*
  2446. * ----------------------------------------------------------
  2447. * File: vm.c
  2448. * MD5: a0a6393b5981e604af8f6c86d0659845
  2449. * ----------------------------------------------------------
  2450. */
  2451. /*
  2452. * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
  2453. * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
  2454. * Version 1.7.2
  2455. * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  2456. * please contact Symisc Systems via:
  2457. * legal@symisc.net
  2458. * licensing@symisc.net
  2459. * contact@symisc.net
  2460. * or visit:
  2461. * http://jx9.symisc.net/
  2462. */
  2463. /* $SymiscID: lex.c v1.0 FreeBSD 2012-12-09 00:19 stable <chm@symisc.net> $ */
  2464. #ifndef JX9_AMALGAMATION
  2465. #include "jx9Int.h"
  2466. #endif
  2467. /*
  2468. * The code in this file implements execution method of the JX9 Virtual Machine.
  2469. * The JX9 compiler (implemented in 'compiler.c' and 'parse.c') generates a bytecode program
  2470. * which is then executed by the virtual machine implemented here to do the work of the JX9
  2471. * statements.
  2472. * JX9 bytecode programs are similar in form to assembly language. The program consists
  2473. * of a linear sequence of operations .Each operation has an opcode and 3 operands.
  2474. * Operands P1 and P2 are integers where the first is signed while the second is unsigned.
  2475. * Operand P3 is an arbitrary pointer specific to each instruction. The P2 operand is usually
  2476. * the jump destination used by the OP_JMP, OP_JZ, OP_JNZ, ... instructions.
  2477. * Opcodes will typically ignore one or more operands. Many opcodes ignore all three operands.
  2478. * Computation results are stored on a stack. Each entry on the stack is of type jx9_value.
  2479. * JX9 uses the jx9_value object to represent all values that can be stored in a JX9 variable.
  2480. * Since JX9 uses dynamic typing for the values it stores. Values stored in jx9_value objects
  2481. * can be integers, floating point values, strings, arrays, object instances (object in the JX9 jargon)
  2482. * and so on.
  2483. * Internally, the JX9 virtual machine manipulates nearly all values as jx9_values structures.
  2484. * Each jx9_value may cache multiple representations(string, integer etc.) of the same value.
  2485. * An implicit conversion from one type to the other occurs as necessary.
  2486. * Most of the code in this file is taken up by the [VmByteCodeExec()] function which does
  2487. * the work of interpreting a JX9 bytecode program. But other routines are also provided
  2488. * to help in building up a program instruction by instruction.
  2489. */
  2490. /*
  2491. * Each active virtual machine frame is represented by an instance
  2492. * of the following structure.
  2493. * VM Frame hold local variables and other stuff related to function call.
  2494. */
  2495. struct VmFrame
  2496. {
  2497. VmFrame *pParent; /* Parent frame or NULL if global scope */
  2498. void *pUserData; /* Upper layer private data associated with this frame */
  2499. SySet sLocal; /* Local variables container (VmSlot instance) */
  2500. jx9_vm *pVm; /* VM that own this frame */
  2501. SyHash hVar; /* Variable hashtable for fast lookup */
  2502. SySet sArg; /* Function arguments container */
  2503. sxi32 iFlags; /* Frame configuration flags (See below)*/
  2504. sxu32 iExceptionJump; /* Exception jump destination */
  2505. };
  2506. /*
  2507. * When a user defined variable is garbage collected, memory object index
  2508. * is stored in an instance of the following structure and put in the free object
  2509. * table so that it can be reused again without allocating a new memory object.
  2510. */
  2511. typedef struct VmSlot VmSlot;
  2512. struct VmSlot
  2513. {
  2514. sxu32 nIdx; /* Index in pVm->aMemObj[] */
  2515. void *pUserData; /* Upper-layer private data */
  2516. };
  2517. /*
  2518. * Each parsed URI is recorded and stored in an instance of the following structure.
  2519. * This structure and it's related routines are taken verbatim from the xHT project
  2520. * [A modern embeddable HTTP engine implementing all the RFC2616 methods]
  2521. * the xHT project is developed internally by Symisc Systems.
  2522. */
  2523. typedef struct SyhttpUri SyhttpUri;
  2524. struct SyhttpUri
  2525. {
  2526. SyString sHost; /* Hostname or IP address */
  2527. SyString sPort; /* Port number */
  2528. SyString sPath; /* Mandatory resource path passed verbatim (Not decoded) */
  2529. SyString sQuery; /* Query part */
  2530. SyString sFragment; /* Fragment part */
  2531. SyString sScheme; /* Scheme */
  2532. SyString sUser; /* Username */
  2533. SyString sPass; /* Password */
  2534. SyString sRaw; /* Raw URI */
  2535. };
  2536. /*
  2537. * An instance of the following structure is used to record all MIME headers seen
  2538. * during a HTTP interaction.
  2539. * This structure and it's related routines are taken verbatim from the xHT project
  2540. * [A modern embeddable HTTP engine implementing all the RFC2616 methods]
  2541. * the xHT project is developed internally by Symisc Systems.
  2542. */
  2543. typedef struct SyhttpHeader SyhttpHeader;
  2544. struct SyhttpHeader
  2545. {
  2546. SyString sName; /* Header name [i.e:"Content-Type", "Host", "User-Agent"]. NOT NUL TERMINATED */
  2547. SyString sValue; /* Header values [i.e: "text/html"]. NOT NUL TERMINATED */
  2548. };
  2549. /*
  2550. * Supported HTTP methods.
  2551. */
  2552. #define HTTP_METHOD_GET 1 /* GET */
  2553. #define HTTP_METHOD_HEAD 2 /* HEAD */
  2554. #define HTTP_METHOD_POST 3 /* POST */
  2555. #define HTTP_METHOD_PUT 4 /* PUT */
  2556. #define HTTP_METHOD_OTHR 5 /* Other HTTP methods [i.e: DELETE, TRACE, OPTIONS...]*/
  2557. /*
  2558. * Supported HTTP protocol version.
  2559. */
  2560. #define HTTP_PROTO_10 1 /* HTTP/1.0 */
  2561. #define HTTP_PROTO_11 2 /* HTTP/1.1 */
  2562. /*
  2563. * Register a constant and it's associated expansion callback so that
  2564. * it can be expanded from the target JX9 program.
  2565. * The constant expansion mechanism under JX9 is extremely powerful yet
  2566. * simple and work as follows:
  2567. * Each registered constant have a C procedure associated with it.
  2568. * This procedure known as the constant expansion callback is responsible
  2569. * of expanding the invoked constant to the desired value, for example:
  2570. * The C procedure associated with the "__PI__" constant expands to 3.14 (the value of PI).
  2571. * The "__OS__" constant procedure expands to the name of the host Operating Systems
  2572. * (Windows, Linux, ...) and so on.
  2573. * Please refer to the official documentation for additional information.
  2574. */
  2575. JX9_PRIVATE sxi32 jx9VmRegisterConstant(
  2576. jx9_vm *pVm, /* Target VM */
  2577. const SyString *pName, /* Constant name */
  2578. ProcConstant xExpand, /* Constant expansion callback */
  2579. void *pUserData /* Last argument to xExpand() */
  2580. )
  2581. {
  2582. jx9_constant *pCons;
  2583. SyHashEntry *pEntry;
  2584. char *zDupName;
  2585. sxi32 rc;
  2586. pEntry = SyHashGet(&pVm->hConstant, (const void *)pName->zString, pName->nByte);
  2587. if( pEntry ){
  2588. /* Overwrite the old definition and return immediately */
  2589. pCons = (jx9_constant *)pEntry->pUserData;
  2590. pCons->xExpand = xExpand;
  2591. pCons->pUserData = pUserData;
  2592. return SXRET_OK;
  2593. }
  2594. /* Allocate a new constant instance */
  2595. pCons = (jx9_constant *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_constant));
  2596. if( pCons == 0 ){
  2597. return 0;
  2598. }
  2599. /* Duplicate constant name */
  2600. zDupName = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte);
  2601. if( zDupName == 0 ){
  2602. SyMemBackendPoolFree(&pVm->sAllocator, pCons);
  2603. return 0;
  2604. }
  2605. /* Install the constant */
  2606. SyStringInitFromBuf(&pCons->sName, zDupName, pName->nByte);
  2607. pCons->xExpand = xExpand;
  2608. pCons->pUserData = pUserData;
  2609. rc = SyHashInsert(&pVm->hConstant, (const void *)zDupName, SyStringLength(&pCons->sName), pCons);
  2610. if( rc != SXRET_OK ){
  2611. SyMemBackendFree(&pVm->sAllocator, zDupName);
  2612. SyMemBackendPoolFree(&pVm->sAllocator, pCons);
  2613. return rc;
  2614. }
  2615. /* All done, constant can be invoked from JX9 code */
  2616. return SXRET_OK;
  2617. }
  2618. /*
  2619. * Allocate a new foreign function instance.
  2620. * This function return SXRET_OK on success. Any other
  2621. * return value indicates failure.
  2622. * Please refer to the official documentation for an introduction to
  2623. * the foreign function mechanism.
  2624. */
  2625. static sxi32 jx9NewForeignFunction(
  2626. jx9_vm *pVm, /* Target VM */
  2627. const SyString *pName, /* Foreign function name */
  2628. ProcHostFunction xFunc, /* Foreign function implementation */
  2629. void *pUserData, /* Foreign function private data */
  2630. jx9_user_func **ppOut /* OUT: VM image of the foreign function */
  2631. )
  2632. {
  2633. jx9_user_func *pFunc;
  2634. char *zDup;
  2635. /* Allocate a new user function */
  2636. pFunc = (jx9_user_func *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_user_func));
  2637. if( pFunc == 0 ){
  2638. return SXERR_MEM;
  2639. }
  2640. /* Duplicate function name */
  2641. zDup = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte);
  2642. if( zDup == 0 ){
  2643. SyMemBackendPoolFree(&pVm->sAllocator, pFunc);
  2644. return SXERR_MEM;
  2645. }
  2646. /* Zero the structure */
  2647. SyZero(pFunc, sizeof(jx9_user_func));
  2648. /* Initialize structure fields */
  2649. SyStringInitFromBuf(&pFunc->sName, zDup, pName->nByte);
  2650. pFunc->pVm = pVm;
  2651. pFunc->xFunc = xFunc;
  2652. pFunc->pUserData = pUserData;
  2653. SySetInit(&pFunc->aAux, &pVm->sAllocator, sizeof(jx9_aux_data));
  2654. /* Write a pointer to the new function */
  2655. *ppOut = pFunc;
  2656. return SXRET_OK;
  2657. }
  2658. /*
  2659. * Install a foreign function and it's associated callback so that
  2660. * it can be invoked from the target JX9 code.
  2661. * This function return SXRET_OK on successful registration. Any other
  2662. * return value indicates failure.
  2663. * Please refer to the official documentation for an introduction to
  2664. * the foreign function mechanism.
  2665. */
  2666. JX9_PRIVATE sxi32 jx9VmInstallForeignFunction(
  2667. jx9_vm *pVm, /* Target VM */
  2668. const SyString *pName, /* Foreign function name */
  2669. ProcHostFunction xFunc, /* Foreign function implementation */
  2670. void *pUserData /* Foreign function private data */
  2671. )
  2672. {
  2673. jx9_user_func *pFunc;
  2674. SyHashEntry *pEntry;
  2675. sxi32 rc;
  2676. /* Overwrite any previously registered function with the same name */
  2677. pEntry = SyHashGet(&pVm->hHostFunction, pName->zString, pName->nByte);
  2678. if( pEntry ){
  2679. pFunc = (jx9_user_func *)pEntry->pUserData;
  2680. pFunc->pUserData = pUserData;
  2681. pFunc->xFunc = xFunc;
  2682. SySetReset(&pFunc->aAux);
  2683. return SXRET_OK;
  2684. }
  2685. /* Create a new user function */
  2686. rc = jx9NewForeignFunction(&(*pVm), &(*pName), xFunc, pUserData, &pFunc);
  2687. if( rc != SXRET_OK ){
  2688. return rc;
  2689. }
  2690. /* Install the function in the corresponding hashtable */
  2691. rc = SyHashInsert(&pVm->hHostFunction, SyStringData(&pFunc->sName), pName->nByte, pFunc);
  2692. if( rc != SXRET_OK ){
  2693. SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pFunc->sName));
  2694. SyMemBackendPoolFree(&pVm->sAllocator, pFunc);
  2695. return rc;
  2696. }
  2697. /* User function successfully installed */
  2698. return SXRET_OK;
  2699. }
  2700. /*
  2701. * Initialize a VM function.
  2702. */
  2703. JX9_PRIVATE sxi32 jx9VmInitFuncState(
  2704. jx9_vm *pVm, /* Target VM */
  2705. jx9_vm_func *pFunc, /* Target Fucntion */
  2706. const char *zName, /* Function name */
  2707. sxu32 nByte, /* zName length */
  2708. sxi32 iFlags, /* Configuration flags */
  2709. void *pUserData /* Function private data */
  2710. )
  2711. {
  2712. /* Zero the structure */
  2713. SyZero(pFunc, sizeof(jx9_vm_func));
  2714. /* Initialize structure fields */
  2715. /* Arguments container */
  2716. SySetInit(&pFunc->aArgs, &pVm->sAllocator, sizeof(jx9_vm_func_arg));
  2717. /* Static variable container */
  2718. SySetInit(&pFunc->aStatic, &pVm->sAllocator, sizeof(jx9_vm_func_static_var));
  2719. /* Bytecode container */
  2720. SySetInit(&pFunc->aByteCode, &pVm->sAllocator, sizeof(VmInstr));
  2721. /* Preallocate some instruction slots */
  2722. SySetAlloc(&pFunc->aByteCode, 0x10);
  2723. pFunc->iFlags = iFlags;
  2724. pFunc->pUserData = pUserData;
  2725. SyStringInitFromBuf(&pFunc->sName, zName, nByte);
  2726. return SXRET_OK;
  2727. }
  2728. /*
  2729. * Install a user defined function in the corresponding VM container.
  2730. */
  2731. JX9_PRIVATE sxi32 jx9VmInstallUserFunction(
  2732. jx9_vm *pVm, /* Target VM */
  2733. jx9_vm_func *pFunc, /* Target function */
  2734. SyString *pName /* Function name */
  2735. )
  2736. {
  2737. SyHashEntry *pEntry;
  2738. sxi32 rc;
  2739. if( pName == 0 ){
  2740. /* Use the built-in name */
  2741. pName = &pFunc->sName;
  2742. }
  2743. /* Check for duplicates (functions with the same name) first */
  2744. pEntry = SyHashGet(&pVm->hFunction, pName->zString, pName->nByte);
  2745. if( pEntry ){
  2746. jx9_vm_func *pLink = (jx9_vm_func *)pEntry->pUserData;
  2747. if( pLink != pFunc ){
  2748. /* Link */
  2749. pFunc->pNextName = pLink;
  2750. pEntry->pUserData = pFunc;
  2751. }
  2752. return SXRET_OK;
  2753. }
  2754. /* First time seen */
  2755. pFunc->pNextName = 0;
  2756. rc = SyHashInsert(&pVm->hFunction, pName->zString, pName->nByte, pFunc);
  2757. return rc;
  2758. }
  2759. /*
  2760. * Instruction builder interface.
  2761. */
  2762. JX9_PRIVATE sxi32 jx9VmEmitInstr(
  2763. jx9_vm *pVm, /* Target VM */
  2764. sxi32 iOp, /* Operation to perform */
  2765. sxi32 iP1, /* First operand */
  2766. sxu32 iP2, /* Second operand */
  2767. void *p3, /* Third operand */
  2768. sxu32 *pIndex /* Instruction index. NULL otherwise */
  2769. )
  2770. {
  2771. VmInstr sInstr;
  2772. sxi32 rc;
  2773. /* Fill the VM instruction */
  2774. sInstr.iOp = (sxu8)iOp;
  2775. sInstr.iP1 = iP1;
  2776. sInstr.iP2 = iP2;
  2777. sInstr.p3 = p3;
  2778. if( pIndex ){
  2779. /* Instruction index in the bytecode array */
  2780. *pIndex = SySetUsed(pVm->pByteContainer);
  2781. }
  2782. /* Finally, record the instruction */
  2783. rc = SySetPut(pVm->pByteContainer, (const void *)&sInstr);
  2784. if( rc != SXRET_OK ){
  2785. jx9GenCompileError(&pVm->sCodeGen, E_ERROR, 1, "Fatal, Cannot emit instruction due to a memory failure");
  2786. /* Fall throw */
  2787. }
  2788. return rc;
  2789. }
  2790. /*
  2791. * Swap the current bytecode container with the given one.
  2792. */
  2793. JX9_PRIVATE sxi32 jx9VmSetByteCodeContainer(jx9_vm *pVm, SySet *pContainer)
  2794. {
  2795. if( pContainer == 0 ){
  2796. /* Point to the default container */
  2797. pVm->pByteContainer = &pVm->aByteCode;
  2798. }else{
  2799. /* Change container */
  2800. pVm->pByteContainer = &(*pContainer);
  2801. }
  2802. return SXRET_OK;
  2803. }
  2804. /*
  2805. * Return the current bytecode container.
  2806. */
  2807. JX9_PRIVATE SySet * jx9VmGetByteCodeContainer(jx9_vm *pVm)
  2808. {
  2809. return pVm->pByteContainer;
  2810. }
  2811. /*
  2812. * Extract the VM instruction rooted at nIndex.
  2813. */
  2814. JX9_PRIVATE VmInstr * jx9VmGetInstr(jx9_vm *pVm, sxu32 nIndex)
  2815. {
  2816. VmInstr *pInstr;
  2817. pInstr = (VmInstr *)SySetAt(pVm->pByteContainer, nIndex);
  2818. return pInstr;
  2819. }
  2820. /*
  2821. * Return the total number of VM instructions recorded so far.
  2822. */
  2823. JX9_PRIVATE sxu32 jx9VmInstrLength(jx9_vm *pVm)
  2824. {
  2825. return SySetUsed(pVm->pByteContainer);
  2826. }
  2827. /*
  2828. * Pop the last VM instruction.
  2829. */
  2830. JX9_PRIVATE VmInstr * jx9VmPopInstr(jx9_vm *pVm)
  2831. {
  2832. return (VmInstr *)SySetPop(pVm->pByteContainer);
  2833. }
  2834. /*
  2835. * Peek the last VM instruction.
  2836. */
  2837. JX9_PRIVATE VmInstr * jx9VmPeekInstr(jx9_vm *pVm)
  2838. {
  2839. return (VmInstr *)SySetPeek(pVm->pByteContainer);
  2840. }
  2841. /*
  2842. * Allocate a new virtual machine frame.
  2843. */
  2844. static VmFrame * VmNewFrame(
  2845. jx9_vm *pVm, /* Target VM */
  2846. void *pUserData /* Upper-layer private data */
  2847. )
  2848. {
  2849. VmFrame *pFrame;
  2850. /* Allocate a new vm frame */
  2851. pFrame = (VmFrame *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(VmFrame));
  2852. if( pFrame == 0 ){
  2853. return 0;
  2854. }
  2855. /* Zero the structure */
  2856. SyZero(pFrame, sizeof(VmFrame));
  2857. /* Initialize frame fields */
  2858. pFrame->pUserData = pUserData;
  2859. pFrame->pVm = pVm;
  2860. SyHashInit(&pFrame->hVar, &pVm->sAllocator, 0, 0);
  2861. SySetInit(&pFrame->sArg, &pVm->sAllocator, sizeof(VmSlot));
  2862. SySetInit(&pFrame->sLocal, &pVm->sAllocator, sizeof(VmSlot));
  2863. return pFrame;
  2864. }
  2865. /*
  2866. * Enter a VM frame.
  2867. */
  2868. static sxi32 VmEnterFrame(
  2869. jx9_vm *pVm, /* Target VM */
  2870. void *pUserData, /* Upper-layer private data */
  2871. VmFrame **ppFrame /* OUT: Top most active frame */
  2872. )
  2873. {
  2874. VmFrame *pFrame;
  2875. /* Allocate a new frame */
  2876. pFrame = VmNewFrame(&(*pVm), pUserData);
  2877. if( pFrame == 0 ){
  2878. return SXERR_MEM;
  2879. }
  2880. /* Link to the list of active VM frame */
  2881. pFrame->pParent = pVm->pFrame;
  2882. pVm->pFrame = pFrame;
  2883. if( ppFrame ){
  2884. /* Write a pointer to the new VM frame */
  2885. *ppFrame = pFrame;
  2886. }
  2887. return SXRET_OK;
  2888. }
  2889. /*
  2890. * Link a foreign variable with the TOP most active frame.
  2891. * Refer to the JX9_OP_UPLINK instruction implementation for more
  2892. * information.
  2893. */
  2894. static sxi32 VmFrameLink(jx9_vm *pVm,SyString *pName)
  2895. {
  2896. VmFrame *pTarget, *pFrame;
  2897. SyHashEntry *pEntry = 0;
  2898. sxi32 rc;
  2899. /* Point to the upper frame */
  2900. pFrame = pVm->pFrame;
  2901. pTarget = pFrame;
  2902. pFrame = pTarget->pParent;
  2903. while( pFrame ){
  2904. /* Query the current frame */
  2905. pEntry = SyHashGet(&pFrame->hVar, (const void *)pName->zString, pName->nByte);
  2906. if( pEntry ){
  2907. /* Variable found */
  2908. break;
  2909. }
  2910. /* Point to the upper frame */
  2911. pFrame = pFrame->pParent;
  2912. }
  2913. if( pEntry == 0 ){
  2914. /* Inexistant variable */
  2915. return SXERR_NOTFOUND;
  2916. }
  2917. /* Link to the current frame */
  2918. rc = SyHashInsert(&pTarget->hVar, pEntry->pKey, pEntry->nKeyLen, pEntry->pUserData);
  2919. return rc;
  2920. }
  2921. /*
  2922. * Leave the top-most active frame.
  2923. */
  2924. static void VmLeaveFrame(jx9_vm *pVm)
  2925. {
  2926. VmFrame *pFrame = pVm->pFrame;
  2927. if( pFrame ){
  2928. /* Unlink from the list of active VM frame */
  2929. pVm->pFrame = pFrame->pParent;
  2930. if( pFrame->pParent ){
  2931. VmSlot *aSlot;
  2932. sxu32 n;
  2933. /* Restore local variable to the free pool so that they can be reused again */
  2934. aSlot = (VmSlot *)SySetBasePtr(&pFrame->sLocal);
  2935. for(n = 0 ; n < SySetUsed(&pFrame->sLocal) ; ++n ){
  2936. /* Unset the local variable */
  2937. jx9VmUnsetMemObj(&(*pVm), aSlot[n].nIdx);
  2938. }
  2939. }
  2940. /* Release internal containers */
  2941. SyHashRelease(&pFrame->hVar);
  2942. SySetRelease(&pFrame->sArg);
  2943. SySetRelease(&pFrame->sLocal);
  2944. /* Release the whole structure */
  2945. SyMemBackendPoolFree(&pVm->sAllocator, pFrame);
  2946. }
  2947. }
  2948. /*
  2949. * Compare two functions signature and return the comparison result.
  2950. */
  2951. static int VmOverloadCompare(SyString *pFirst, SyString *pSecond)
  2952. {
  2953. const char *zSend = &pSecond->zString[pSecond->nByte];
  2954. const char *zFend = &pFirst->zString[pFirst->nByte];
  2955. const char *zSin = pSecond->zString;
  2956. const char *zFin = pFirst->zString;
  2957. const char *zPtr = zFin;
  2958. for(;;){
  2959. if( zFin >= zFend || zSin >= zSend ){
  2960. break;
  2961. }
  2962. if( zFin[0] != zSin[0] ){
  2963. /* mismatch */
  2964. break;
  2965. }
  2966. zFin++;
  2967. zSin++;
  2968. }
  2969. return (int)(zFin-zPtr);
  2970. }
  2971. /*
  2972. * Select the appropriate VM function for the current call context.
  2973. * This is the implementation of the powerful 'function overloading' feature
  2974. * introduced by the version 2 of the JX9 engine.
  2975. * Refer to the official documentation for more information.
  2976. */
  2977. static jx9_vm_func * VmOverload(
  2978. jx9_vm *pVm, /* Target VM */
  2979. jx9_vm_func *pList, /* Linked list of candidates for overloading */
  2980. jx9_value *aArg, /* Array of passed arguments */
  2981. int nArg /* Total number of passed arguments */
  2982. )
  2983. {
  2984. int iTarget, i, j, iCur, iMax;
  2985. jx9_vm_func *apSet[10]; /* Maximum number of candidates */
  2986. jx9_vm_func *pLink;
  2987. SyString sArgSig;
  2988. SyBlob sSig;
  2989. pLink = pList;
  2990. i = 0;
  2991. /* Put functions expecting the same number of passed arguments */
  2992. while( i < (int)SX_ARRAYSIZE(apSet) ){
  2993. if( pLink == 0 ){
  2994. break;
  2995. }
  2996. if( (int)SySetUsed(&pLink->aArgs) == nArg ){
  2997. /* Candidate for overloading */
  2998. apSet[i++] = pLink;
  2999. }
  3000. /* Point to the next entry */
  3001. pLink = pLink->pNextName;
  3002. }
  3003. if( i < 1 ){
  3004. /* No candidates, return the head of the list */
  3005. return pList;
  3006. }
  3007. if( nArg < 1 || i < 2 ){
  3008. /* Return the only candidate */
  3009. return apSet[0];
  3010. }
  3011. /* Calculate function signature */
  3012. SyBlobInit(&sSig, &pVm->sAllocator);
  3013. for( j = 0 ; j < nArg ; j++ ){
  3014. int c = 'n'; /* null */
  3015. if( aArg[j].iFlags & MEMOBJ_HASHMAP ){
  3016. /* Hashmap */
  3017. c = 'h';
  3018. }else if( aArg[j].iFlags & MEMOBJ_BOOL ){
  3019. /* bool */
  3020. c = 'b';
  3021. }else if( aArg[j].iFlags & MEMOBJ_INT ){
  3022. /* int */
  3023. c = 'i';
  3024. }else if( aArg[j].iFlags & MEMOBJ_STRING ){
  3025. /* String */
  3026. c = 's';
  3027. }else if( aArg[j].iFlags & MEMOBJ_REAL ){
  3028. /* Float */
  3029. c = 'f';
  3030. }
  3031. if( c > 0 ){
  3032. SyBlobAppend(&sSig, (const void *)&c, sizeof(char));
  3033. }
  3034. }
  3035. SyStringInitFromBuf(&sArgSig, SyBlobData(&sSig), SyBlobLength(&sSig));
  3036. iTarget = 0;
  3037. iMax = -1;
  3038. /* Select the appropriate function */
  3039. for( j = 0 ; j < i ; j++ ){
  3040. /* Compare the two signatures */
  3041. iCur = VmOverloadCompare(&sArgSig, &apSet[j]->sSignature);
  3042. if( iCur > iMax ){
  3043. iMax = iCur;
  3044. iTarget = j;
  3045. }
  3046. }
  3047. SyBlobRelease(&sSig);
  3048. /* Appropriate function for the current call context */
  3049. return apSet[iTarget];
  3050. }
  3051. /*
  3052. * Dummy read-only buffer used for slot reservation.
  3053. */
  3054. static const char zDummy[sizeof(jx9_value)] = { 0 }; /* Must be >= sizeof(jx9_value) */
  3055. /*
  3056. * Reserve a constant memory object.
  3057. * Return a pointer to the raw jx9_value on success. NULL on failure.
  3058. */
  3059. JX9_PRIVATE jx9_value * jx9VmReserveConstObj(jx9_vm *pVm, sxu32 *pIndex)
  3060. {
  3061. jx9_value *pObj;
  3062. sxi32 rc;
  3063. if( pIndex ){
  3064. /* Object index in the object table */
  3065. *pIndex = SySetUsed(&pVm->aLitObj);
  3066. }
  3067. /* Reserve a slot for the new object */
  3068. rc = SySetPut(&pVm->aLitObj, (const void *)zDummy);
  3069. if( rc != SXRET_OK ){
  3070. /* If the supplied memory subsystem is so sick that we are unable to allocate
  3071. * a tiny chunk of memory, there is no much we can do here.
  3072. */
  3073. return 0;
  3074. }
  3075. pObj = (jx9_value *)SySetPeek(&pVm->aLitObj);
  3076. return pObj;
  3077. }
  3078. /*
  3079. * Reserve a memory object.
  3080. * Return a pointer to the raw jx9_value on success. NULL on failure.
  3081. */
  3082. static jx9_value * VmReserveMemObj(jx9_vm *pVm, sxu32 *pIndex)
  3083. {
  3084. jx9_value *pObj;
  3085. sxi32 rc;
  3086. if( pIndex ){
  3087. /* Object index in the object table */
  3088. *pIndex = SySetUsed(&pVm->aMemObj);
  3089. }
  3090. /* Reserve a slot for the new object */
  3091. rc = SySetPut(&pVm->aMemObj, (const void *)zDummy);
  3092. if( rc != SXRET_OK ){
  3093. /* If the supplied memory subsystem is so sick that we are unable to allocate
  3094. * a tiny chunk of memory, there is no much we can do here.
  3095. */
  3096. return 0;
  3097. }
  3098. pObj = (jx9_value *)SySetPeek(&pVm->aMemObj);
  3099. return pObj;
  3100. }
  3101. /* Forward declaration */
  3102. static sxi32 VmEvalChunk(jx9_vm *pVm, jx9_context *pCtx, SyString *pChunk, int iFlags, int bTrueReturn);
  3103. /*
  3104. * Built-in functions that cannot be implemented directly as foreign functions.
  3105. */
  3106. #define JX9_BUILTIN_LIB \
  3107. "function scandir(string $directory, int $sort_order = SCANDIR_SORT_ASCENDING)"\
  3108. "{"\
  3109. " if( func_num_args() < 1 ){ return FALSE; }"\
  3110. " $aDir = [];"\
  3111. " $pHandle = opendir($directory);"\
  3112. " if( $pHandle == FALSE ){ return FALSE; }"\
  3113. " while(FALSE !== ($pEntry = readdir($pHandle)) ){"\
  3114. " $aDir[] = $pEntry;"\
  3115. " }"\
  3116. " closedir($pHandle);"\
  3117. " if( $sort_order == SCANDIR_SORT_DESCENDING ){"\
  3118. " rsort($aDir);"\
  3119. " }else if( $sort_order == SCANDIR_SORT_ASCENDING ){"\
  3120. " sort($aDir);"\
  3121. " }"\
  3122. " return $aDir;"\
  3123. "}"\
  3124. "function glob(string $pattern, int $iFlags = 0){"\
  3125. "/* Open the target directory */"\
  3126. "$zDir = dirname($pattern);"\
  3127. "if(!is_string($zDir) ){ $zDir = './'; }"\
  3128. "$pHandle = opendir($zDir);"\
  3129. "if( $pHandle == FALSE ){"\
  3130. " /* IO error while opening the current directory, return FALSE */"\
  3131. " return FALSE;"\
  3132. "}"\
  3133. "$pattern = basename($pattern);"\
  3134. "$pArray = []; /* Empty array */"\
  3135. "/* Loop throw available entries */"\
  3136. "while( FALSE !== ($pEntry = readdir($pHandle)) ){"\
  3137. " /* Use the built-in strglob function which is a Symisc eXtension for wildcard comparison*/"\
  3138. " $rc = strglob($pattern, $pEntry);"\
  3139. " if( $rc ){"\
  3140. " if( is_dir($pEntry) ){"\
  3141. " if( $iFlags & GLOB_MARK ){"\
  3142. " /* Adds a slash to each directory returned */"\
  3143. " $pEntry .= DIRECTORY_SEPARATOR;"\
  3144. " }"\
  3145. " }else if( $iFlags & GLOB_ONLYDIR ){"\
  3146. " /* Not a directory, ignore */"\
  3147. " continue;"\
  3148. " }"\
  3149. " /* Add the entry */"\
  3150. " $pArray[] = $pEntry;"\
  3151. " }"\
  3152. " }"\
  3153. "/* Close the handle */"\
  3154. "closedir($pHandle);"\
  3155. "if( ($iFlags & GLOB_NOSORT) == 0 ){"\
  3156. " /* Sort the array */"\
  3157. " sort($pArray);"\
  3158. "}"\
  3159. "if( ($iFlags & GLOB_NOCHECK) && sizeof($pArray) < 1 ){"\
  3160. " /* Return the search pattern if no files matching were found */"\
  3161. " $pArray[] = $pattern;"\
  3162. "}"\
  3163. "/* Return the created array */"\
  3164. "return $pArray;"\
  3165. "}"\
  3166. "/* Creates a temporary file */"\
  3167. "function tmpfile(){"\
  3168. " /* Extract the temp directory */"\
  3169. " $zTempDir = sys_get_temp_dir();"\
  3170. " if( strlen($zTempDir) < 1 ){"\
  3171. " /* Use the current dir */"\
  3172. " $zTempDir = '.';"\
  3173. " }"\
  3174. " /* Create the file */"\
  3175. " $pHandle = fopen($zTempDir.DIRECTORY_SEPARATOR.'JX9'.rand_str(12), 'w+');"\
  3176. " return $pHandle;"\
  3177. "}"\
  3178. "/* Creates a temporary filename */"\
  3179. "function tempnam(string $zDir = sys_get_temp_dir() /* Symisc eXtension */, string $zPrefix = 'JX9')"\
  3180. "{"\
  3181. " return $zDir.DIRECTORY_SEPARATOR.$zPrefix.rand_str(12);"\
  3182. "}"\
  3183. "function max(){"\
  3184. " $pArgs = func_get_args();"\
  3185. " if( sizeof($pArgs) < 1 ){"\
  3186. " return null;"\
  3187. " }"\
  3188. " if( sizeof($pArgs) < 2 ){"\
  3189. " $pArg = $pArgs[0];"\
  3190. " if( !is_array($pArg) ){"\
  3191. " return $pArg; "\
  3192. " }"\
  3193. " if( sizeof($pArg) < 1 ){"\
  3194. " return null;"\
  3195. " }"\
  3196. " $pArg = array_copy($pArgs[0]);"\
  3197. " reset($pArg);"\
  3198. " $max = current($pArg);"\
  3199. " while( FALSE !== ($val = next($pArg)) ){"\
  3200. " if( $val > $max ){"\
  3201. " $max = $val;"\
  3202. " }"\
  3203. " }"\
  3204. " return $max;"\
  3205. " }"\
  3206. " $max = $pArgs[0];"\
  3207. " for( $i = 1; $i < sizeof($pArgs) ; ++$i ){"\
  3208. " $val = $pArgs[$i];"\
  3209. "if( $val > $max ){"\
  3210. " $max = $val;"\
  3211. "}"\
  3212. " }"\
  3213. " return $max;"\
  3214. "}"\
  3215. "function min(){"\
  3216. " $pArgs = func_get_args();"\
  3217. " if( sizeof($pArgs) < 1 ){"\
  3218. " return null;"\
  3219. " }"\
  3220. " if( sizeof($pArgs) < 2 ){"\
  3221. " $pArg = $pArgs[0];"\
  3222. " if( !is_array($pArg) ){"\
  3223. " return $pArg; "\
  3224. " }"\
  3225. " if( sizeof($pArg) < 1 ){"\
  3226. " return null;"\
  3227. " }"\
  3228. " $pArg = array_copy($pArgs[0]);"\
  3229. " reset($pArg);"\
  3230. " $min = current($pArg);"\
  3231. " while( FALSE !== ($val = next($pArg)) ){"\
  3232. " if( $val < $min ){"\
  3233. " $min = $val;"\
  3234. " }"\
  3235. " }"\
  3236. " return $min;"\
  3237. " }"\
  3238. " $min = $pArgs[0];"\
  3239. " for( $i = 1; $i < sizeof($pArgs) ; ++$i ){"\
  3240. " $val = $pArgs[$i];"\
  3241. "if( $val < $min ){"\
  3242. " $min = $val;"\
  3243. " }"\
  3244. " }"\
  3245. " return $min;"\
  3246. "}"
  3247. /*
  3248. * Initialize a freshly allocated JX9 Virtual Machine so that we can
  3249. * start compiling the target JX9 program.
  3250. */
  3251. JX9_PRIVATE sxi32 jx9VmInit(
  3252. jx9_vm *pVm, /* Initialize this */
  3253. jx9 *pEngine /* Master engine */
  3254. )
  3255. {
  3256. SyString sBuiltin;
  3257. jx9_value *pObj;
  3258. sxi32 rc;
  3259. /* Zero the structure */
  3260. SyZero(pVm, sizeof(jx9_vm));
  3261. /* Initialize VM fields */
  3262. pVm->pEngine = &(*pEngine);
  3263. SyMemBackendInitFromParent(&pVm->sAllocator, &pEngine->sAllocator);
  3264. /* Instructions containers */
  3265. SySetInit(&pVm->aByteCode, &pVm->sAllocator, sizeof(VmInstr));
  3266. SySetAlloc(&pVm->aByteCode, 0xFF);
  3267. pVm->pByteContainer = &pVm->aByteCode;
  3268. /* Object containers */
  3269. SySetInit(&pVm->aMemObj, &pVm->sAllocator, sizeof(jx9_value));
  3270. SySetAlloc(&pVm->aMemObj, 0xFF);
  3271. /* Virtual machine internal containers */
  3272. SyBlobInit(&pVm->sConsumer, &pVm->sAllocator);
  3273. SyBlobInit(&pVm->sWorker, &pVm->sAllocator);
  3274. SyBlobInit(&pVm->sArgv, &pVm->sAllocator);
  3275. SySetInit(&pVm->aLitObj, &pVm->sAllocator, sizeof(jx9_value));
  3276. SySetAlloc(&pVm->aLitObj, 0xFF);
  3277. SyHashInit(&pVm->hHostFunction, &pVm->sAllocator, 0, 0);
  3278. SyHashInit(&pVm->hFunction, &pVm->sAllocator, 0, 0);
  3279. SyHashInit(&pVm->hConstant, &pVm->sAllocator, 0, 0);
  3280. SyHashInit(&pVm->hSuper, &pVm->sAllocator, 0, 0);
  3281. SySetInit(&pVm->aFreeObj, &pVm->sAllocator, sizeof(VmSlot));
  3282. /* Configuration containers */
  3283. SySetInit(&pVm->aFiles, &pVm->sAllocator, sizeof(SyString));
  3284. SySetInit(&pVm->aPaths, &pVm->sAllocator, sizeof(SyString));
  3285. SySetInit(&pVm->aIncluded, &pVm->sAllocator, sizeof(SyString));
  3286. SySetInit(&pVm->aIOstream, &pVm->sAllocator, sizeof(jx9_io_stream *));
  3287. /* Error callbacks containers */
  3288. jx9MemObjInit(&(*pVm), &pVm->sAssertCallback);
  3289. /* Set a default recursion limit */
  3290. #if defined(__WINNT__) || defined(__UNIXES__)
  3291. pVm->nMaxDepth = 32;
  3292. #else
  3293. pVm->nMaxDepth = 16;
  3294. #endif
  3295. /* Default assertion flags */
  3296. pVm->iAssertFlags = JX9_ASSERT_WARNING; /* Issue a warning for each failed assertion */
  3297. /* PRNG context */
  3298. SyRandomnessInit(&pVm->sPrng, 0, 0);
  3299. /* Install the null constant */
  3300. pObj = jx9VmReserveConstObj(&(*pVm), 0);
  3301. if( pObj == 0 ){
  3302. rc = SXERR_MEM;
  3303. goto Err;
  3304. }
  3305. jx9MemObjInit(pVm, pObj);
  3306. /* Install the boolean TRUE constant */
  3307. pObj = jx9VmReserveConstObj(&(*pVm), 0);
  3308. if( pObj == 0 ){
  3309. rc = SXERR_MEM;
  3310. goto Err;
  3311. }
  3312. jx9MemObjInitFromBool(pVm, pObj, 1);
  3313. /* Install the boolean FALSE constant */
  3314. pObj = jx9VmReserveConstObj(&(*pVm), 0);
  3315. if( pObj == 0 ){
  3316. rc = SXERR_MEM;
  3317. goto Err;
  3318. }
  3319. jx9MemObjInitFromBool(pVm, pObj, 0);
  3320. /* Create the global frame */
  3321. rc = VmEnterFrame(&(*pVm), 0, 0);
  3322. if( rc != SXRET_OK ){
  3323. goto Err;
  3324. }
  3325. /* Initialize the code generator */
  3326. rc = jx9InitCodeGenerator(pVm, pEngine->xConf.xErr, pEngine->xConf.pErrData);
  3327. if( rc != SXRET_OK ){
  3328. goto Err;
  3329. }
  3330. /* VM correctly initialized, set the magic number */
  3331. pVm->nMagic = JX9_VM_INIT;
  3332. SyStringInitFromBuf(&sBuiltin,JX9_BUILTIN_LIB, sizeof(JX9_BUILTIN_LIB)-1);
  3333. /* Compile the built-in library */
  3334. VmEvalChunk(&(*pVm), 0, &sBuiltin, 0, FALSE);
  3335. /* Reset the code generator */
  3336. jx9ResetCodeGenerator(&(*pVm), pEngine->xConf.xErr, pEngine->xConf.pErrData);
  3337. return SXRET_OK;
  3338. Err:
  3339. SyMemBackendRelease(&pVm->sAllocator);
  3340. return rc;
  3341. }
  3342. /*
  3343. * Default VM output consumer callback.That is, all VM output is redirected to this
  3344. * routine which store the output in an internal blob.
  3345. * The output can be extracted later after program execution [jx9_vm_exec()] via
  3346. * the [jx9_vm_config()] interface with a configuration verb set to
  3347. * jx9VM_CONFIG_EXTRACT_OUTPUT.
  3348. * Refer to the official docurmentation for additional information.
  3349. * Note that for performance reason it's preferable to install a VM output
  3350. * consumer callback via (jx9VM_CONFIG_OUTPUT) rather than waiting for the VM
  3351. * to finish executing and extracting the output.
  3352. */
  3353. JX9_PRIVATE sxi32 jx9VmBlobConsumer(
  3354. const void *pOut, /* VM Generated output*/
  3355. unsigned int nLen, /* Generated output length */
  3356. void *pUserData /* User private data */
  3357. )
  3358. {
  3359. sxi32 rc;
  3360. /* Store the output in an internal BLOB */
  3361. rc = SyBlobAppend((SyBlob *)pUserData, pOut, nLen);
  3362. return rc;
  3363. }
  3364. #define VM_STACK_GUARD 16
  3365. /*
  3366. * Allocate a new operand stack so that we can start executing
  3367. * our compiled JX9 program.
  3368. * Return a pointer to the operand stack (array of jx9_values)
  3369. * on success. NULL (Fatal error) on failure.
  3370. */
  3371. static jx9_value * VmNewOperandStack(
  3372. jx9_vm *pVm, /* Target VM */
  3373. sxu32 nInstr /* Total numer of generated bytecode instructions */
  3374. )
  3375. {
  3376. jx9_value *pStack;
  3377. /* No instruction ever pushes more than a single element onto the
  3378. ** stack and the stack never grows on successive executions of the
  3379. ** same loop. So the total number of instructions is an upper bound
  3380. ** on the maximum stack depth required.
  3381. **
  3382. ** Allocation all the stack space we will ever need.
  3383. */
  3384. nInstr += VM_STACK_GUARD;
  3385. pStack = (jx9_value *)SyMemBackendAlloc(&pVm->sAllocator, nInstr * sizeof(jx9_value));
  3386. if( pStack == 0 ){
  3387. return 0;
  3388. }
  3389. /* Initialize the operand stack */
  3390. while( nInstr > 0 ){
  3391. jx9MemObjInit(&(*pVm), &pStack[nInstr - 1]);
  3392. --nInstr;
  3393. }
  3394. /* Ready for bytecode execution */
  3395. return pStack;
  3396. }
  3397. /* Forward declaration */
  3398. static sxi32 VmRegisterSpecialFunction(jx9_vm *pVm);
  3399. /*
  3400. * Prepare the Virtual Machine for bytecode execution.
  3401. * This routine gets called by the JX9 engine after
  3402. * successful compilation of the target JX9 program.
  3403. */
  3404. JX9_PRIVATE sxi32 jx9VmMakeReady(
  3405. jx9_vm *pVm /* Target VM */
  3406. )
  3407. {
  3408. sxi32 rc;
  3409. if( pVm->nMagic != JX9_VM_INIT ){
  3410. /* Initialize your VM first */
  3411. return SXERR_CORRUPT;
  3412. }
  3413. /* Mark the VM ready for bytecode execution */
  3414. pVm->nMagic = JX9_VM_RUN;
  3415. /* Release the code generator now we have compiled our program */
  3416. jx9ResetCodeGenerator(pVm, 0, 0);
  3417. /* Emit the DONE instruction */
  3418. rc = jx9VmEmitInstr(&(*pVm), JX9_OP_DONE, 0, 0, 0, 0);
  3419. if( rc != SXRET_OK ){
  3420. return SXERR_MEM;
  3421. }
  3422. /* Script return value */
  3423. jx9MemObjInit(&(*pVm), &pVm->sExec); /* Assume a NULL return value */
  3424. /* Allocate a new operand stack */
  3425. pVm->aOps = VmNewOperandStack(&(*pVm), SySetUsed(pVm->pByteContainer));
  3426. if( pVm->aOps == 0 ){
  3427. return SXERR_MEM;
  3428. }
  3429. /* Set the default VM output consumer callback and it's
  3430. * private data. */
  3431. pVm->sVmConsumer.xConsumer = jx9VmBlobConsumer;
  3432. pVm->sVmConsumer.pUserData = &pVm->sConsumer;
  3433. /* Register special functions first [i.e: print, func_get_args(), die, etc.] */
  3434. rc = VmRegisterSpecialFunction(&(*pVm));
  3435. if( rc != SXRET_OK ){
  3436. /* Don't worry about freeing memory, everything will be released shortly */
  3437. return rc;
  3438. }
  3439. /* Create superglobals [i.e: $GLOBALS, $_GET, $_POST...] */
  3440. rc = jx9HashmapLoadBuiltin(&(*pVm));
  3441. if( rc != SXRET_OK ){
  3442. /* Don't worry about freeing memory, everything will be released shortly */
  3443. return rc;
  3444. }
  3445. /* Register built-in constants [i.e: JX9_EOL, JX9_OS...] */
  3446. jx9RegisterBuiltInConstant(&(*pVm));
  3447. /* Register built-in functions [i.e: is_null(), array_diff(), strlen(), etc.] */
  3448. jx9RegisterBuiltInFunction(&(*pVm));
  3449. /* VM is ready for bytecode execution */
  3450. return SXRET_OK;
  3451. }
  3452. /*
  3453. * Reset a Virtual Machine to it's initial state.
  3454. */
  3455. JX9_PRIVATE sxi32 jx9VmReset(jx9_vm *pVm)
  3456. {
  3457. if( pVm->nMagic != JX9_VM_RUN && pVm->nMagic != JX9_VM_EXEC ){
  3458. return SXERR_CORRUPT;
  3459. }
  3460. /* TICKET 1433-003: As of this version, the VM is automatically reset */
  3461. SyBlobReset(&pVm->sConsumer);
  3462. jx9MemObjRelease(&pVm->sExec);
  3463. /* Set the ready flag */
  3464. pVm->nMagic = JX9_VM_RUN;
  3465. return SXRET_OK;
  3466. }
  3467. /*
  3468. * Release a Virtual Machine.
  3469. * Every virtual machine must be destroyed in order to avoid memory leaks.
  3470. */
  3471. JX9_PRIVATE sxi32 jx9VmRelease(jx9_vm *pVm)
  3472. {
  3473. /* Set the stale magic number */
  3474. pVm->nMagic = JX9_VM_STALE;
  3475. /* Release the private memory subsystem */
  3476. SyMemBackendRelease(&pVm->sAllocator);
  3477. return SXRET_OK;
  3478. }
  3479. /*
  3480. * Initialize a foreign function call context.
  3481. * The context in which a foreign function executes is stored in a jx9_context object.
  3482. * A pointer to a jx9_context object is always first parameter to application-defined foreign
  3483. * functions.
  3484. * The application-defined foreign function implementation will pass this pointer through into
  3485. * calls to dozens of interfaces, these includes jx9_result_int(), jx9_result_string(), jx9_result_value(),
  3486. * jx9_context_new_scalar(), jx9_context_alloc_chunk(), jx9_context_output(), jx9_context_throw_error()
  3487. * and many more. Refer to the C/C++ Interfaces documentation for additional information.
  3488. */
  3489. static sxi32 VmInitCallContext(
  3490. jx9_context *pOut, /* Call Context */
  3491. jx9_vm *pVm, /* Target VM */
  3492. jx9_user_func *pFunc, /* Foreign function to execute shortly */
  3493. jx9_value *pRet, /* Store return value here*/
  3494. sxi32 iFlags /* Control flags */
  3495. )
  3496. {
  3497. pOut->pFunc = pFunc;
  3498. pOut->pVm = pVm;
  3499. SySetInit(&pOut->sVar, &pVm->sAllocator, sizeof(jx9_value *));
  3500. SySetInit(&pOut->sChunk, &pVm->sAllocator, sizeof(jx9_aux_data));
  3501. /* Assume a null return value */
  3502. MemObjSetType(pRet, MEMOBJ_NULL);
  3503. pOut->pRet = pRet;
  3504. pOut->iFlags = iFlags;
  3505. return SXRET_OK;
  3506. }
  3507. /*
  3508. * Release a foreign function call context and cleanup the mess
  3509. * left behind.
  3510. */
  3511. static void VmReleaseCallContext(jx9_context *pCtx)
  3512. {
  3513. sxu32 n;
  3514. if( SySetUsed(&pCtx->sVar) > 0 ){
  3515. jx9_value **apObj = (jx9_value **)SySetBasePtr(&pCtx->sVar);
  3516. for( n = 0 ; n < SySetUsed(&pCtx->sVar) ; ++n ){
  3517. if( apObj[n] == 0 ){
  3518. /* Already released */
  3519. continue;
  3520. }
  3521. jx9MemObjRelease(apObj[n]);
  3522. SyMemBackendPoolFree(&pCtx->pVm->sAllocator, apObj[n]);
  3523. }
  3524. SySetRelease(&pCtx->sVar);
  3525. }
  3526. if( SySetUsed(&pCtx->sChunk) > 0 ){
  3527. jx9_aux_data *aAux;
  3528. void *pChunk;
  3529. /* Automatic release of dynamically allocated chunk
  3530. * using [jx9_context_alloc_chunk()].
  3531. */
  3532. aAux = (jx9_aux_data *)SySetBasePtr(&pCtx->sChunk);
  3533. for( n = 0; n < SySetUsed(&pCtx->sChunk) ; ++n ){
  3534. pChunk = aAux[n].pAuxData;
  3535. /* Release the chunk */
  3536. if( pChunk ){
  3537. SyMemBackendFree(&pCtx->pVm->sAllocator, pChunk);
  3538. }
  3539. }
  3540. SySetRelease(&pCtx->sChunk);
  3541. }
  3542. }
  3543. /*
  3544. * Release a jx9_value allocated from the body of a foreign function.
  3545. * Refer to [jx9_context_release_value()] for additional information.
  3546. */
  3547. JX9_PRIVATE void jx9VmReleaseContextValue(
  3548. jx9_context *pCtx, /* Call context */
  3549. jx9_value *pValue /* Release this value */
  3550. )
  3551. {
  3552. if( pValue == 0 ){
  3553. /* NULL value is a harmless operation */
  3554. return;
  3555. }
  3556. if( SySetUsed(&pCtx->sVar) > 0 ){
  3557. jx9_value **apObj = (jx9_value **)SySetBasePtr(&pCtx->sVar);
  3558. sxu32 n;
  3559. for( n = 0 ; n < SySetUsed(&pCtx->sVar) ; ++n ){
  3560. if( apObj[n] == pValue ){
  3561. jx9MemObjRelease(pValue);
  3562. SyMemBackendPoolFree(&pCtx->pVm->sAllocator, pValue);
  3563. /* Mark as released */
  3564. apObj[n] = 0;
  3565. break;
  3566. }
  3567. }
  3568. }
  3569. }
  3570. /*
  3571. * Pop and release as many memory object from the operand stack.
  3572. */
  3573. static void VmPopOperand(
  3574. jx9_value **ppTos, /* Operand stack */
  3575. sxi32 nPop /* Total number of memory objects to pop */
  3576. )
  3577. {
  3578. jx9_value *pTos = *ppTos;
  3579. while( nPop > 0 ){
  3580. jx9MemObjRelease(pTos);
  3581. pTos--;
  3582. nPop--;
  3583. }
  3584. /* Top of the stack */
  3585. *ppTos = pTos;
  3586. }
  3587. /*
  3588. * Reserve a memory object.
  3589. * Return a pointer to the raw jx9_value on success. NULL on failure.
  3590. */
  3591. JX9_PRIVATE jx9_value * jx9VmReserveMemObj(jx9_vm *pVm,sxu32 *pIdx)
  3592. {
  3593. jx9_value *pObj = 0;
  3594. VmSlot *pSlot;
  3595. sxu32 nIdx;
  3596. /* Check for a free slot */
  3597. nIdx = SXU32_HIGH; /* cc warning */
  3598. pSlot = (VmSlot *)SySetPop(&pVm->aFreeObj);
  3599. if( pSlot ){
  3600. pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pSlot->nIdx);
  3601. nIdx = pSlot->nIdx;
  3602. }
  3603. if( pObj == 0 ){
  3604. /* Reserve a new memory object */
  3605. pObj = VmReserveMemObj(&(*pVm), &nIdx);
  3606. if( pObj == 0 ){
  3607. return 0;
  3608. }
  3609. }
  3610. /* Set a null default value */
  3611. jx9MemObjInit(&(*pVm), pObj);
  3612. if( pIdx ){
  3613. *pIdx = nIdx;
  3614. }
  3615. pObj->nIdx = nIdx;
  3616. return pObj;
  3617. }
  3618. /*
  3619. * Extract a variable value from the top active VM frame.
  3620. * Return a pointer to the variable value on success.
  3621. * NULL otherwise (non-existent variable/Out-of-memory, ...).
  3622. */
  3623. static jx9_value * VmExtractMemObj(
  3624. jx9_vm *pVm, /* Target VM */
  3625. const SyString *pName, /* Variable name */
  3626. int bDup, /* True to duplicate variable name */
  3627. int bCreate /* True to create the variable if non-existent */
  3628. )
  3629. {
  3630. int bNullify = FALSE;
  3631. SyHashEntry *pEntry;
  3632. VmFrame *pFrame;
  3633. jx9_value *pObj;
  3634. sxu32 nIdx;
  3635. sxi32 rc;
  3636. /* Point to the top active frame */
  3637. pFrame = pVm->pFrame;
  3638. /* Perform the lookup */
  3639. if( pName == 0 || pName->nByte < 1 ){
  3640. static const SyString sAnnon = { " " , sizeof(char) };
  3641. pName = &sAnnon;
  3642. /* Always nullify the object */
  3643. bNullify = TRUE;
  3644. bDup = FALSE;
  3645. }
  3646. /* Check the superglobals table first */
  3647. pEntry = SyHashGet(&pVm->hSuper, (const void *)pName->zString, pName->nByte);
  3648. if( pEntry == 0 ){
  3649. /* Query the top active frame */
  3650. pEntry = SyHashGet(&pFrame->hVar, (const void *)pName->zString, pName->nByte);
  3651. if( pEntry == 0 ){
  3652. char *zName = (char *)pName->zString;
  3653. VmSlot sLocal;
  3654. if( !bCreate ){
  3655. /* Do not create the variable, return NULL */
  3656. return 0;
  3657. }
  3658. /* No such variable, automatically create a new one and install
  3659. * it in the current frame.
  3660. */
  3661. pObj = jx9VmReserveMemObj(&(*pVm),&nIdx);
  3662. if( pObj == 0 ){
  3663. return 0;
  3664. }
  3665. if( bDup ){
  3666. /* Duplicate name */
  3667. zName = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte);
  3668. if( zName == 0 ){
  3669. return 0;
  3670. }
  3671. }
  3672. /* Link to the top active VM frame */
  3673. rc = SyHashInsert(&pFrame->hVar, zName, pName->nByte, SX_INT_TO_PTR(nIdx));
  3674. if( rc != SXRET_OK ){
  3675. /* Return the slot to the free pool */
  3676. sLocal.nIdx = nIdx;
  3677. sLocal.pUserData = 0;
  3678. SySetPut(&pVm->aFreeObj, (const void *)&sLocal);
  3679. return 0;
  3680. }
  3681. if( pFrame->pParent != 0 ){
  3682. /* Local variable */
  3683. sLocal.nIdx = nIdx;
  3684. SySetPut(&pFrame->sLocal, (const void *)&sLocal);
  3685. }
  3686. }else{
  3687. /* Extract variable contents */
  3688. nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData);
  3689. pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
  3690. if( bNullify && pObj ){
  3691. jx9MemObjRelease(pObj);
  3692. }
  3693. }
  3694. }else{
  3695. /* Superglobal */
  3696. nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData);
  3697. pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
  3698. }
  3699. return pObj;
  3700. }
  3701. /*
  3702. * Extract a superglobal variable such as $_GET, $_POST, $_HEADERS, ....
  3703. * Return a pointer to the variable value on success.NULL otherwise.
  3704. */
  3705. static jx9_value * VmExtractSuper(
  3706. jx9_vm *pVm, /* Target VM */
  3707. const char *zName, /* Superglobal name: NOT NULL TERMINATED */
  3708. sxu32 nByte /* zName length */
  3709. )
  3710. {
  3711. SyHashEntry *pEntry;
  3712. jx9_value *pValue;
  3713. sxu32 nIdx;
  3714. /* Query the superglobal table */
  3715. pEntry = SyHashGet(&pVm->hSuper, (const void *)zName, nByte);
  3716. if( pEntry == 0 ){
  3717. /* No such entry */
  3718. return 0;
  3719. }
  3720. /* Extract the superglobal index in the global object pool */
  3721. nIdx = SX_PTR_TO_INT(pEntry->pUserData);
  3722. /* Extract the variable value */
  3723. pValue = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
  3724. return pValue;
  3725. }
  3726. /*
  3727. * Perform a raw hashmap insertion.
  3728. * Refer to the [jx9VmConfigure()] implementation for additional information.
  3729. */
  3730. static sxi32 VmHashmapInsert(
  3731. jx9_hashmap *pMap, /* Target hashmap */
  3732. const char *zKey, /* Entry key */
  3733. int nKeylen, /* zKey length*/
  3734. const char *zData, /* Entry data */
  3735. int nLen /* zData length */
  3736. )
  3737. {
  3738. jx9_value sKey,sValue;
  3739. jx9_value *pKey;
  3740. sxi32 rc;
  3741. pKey = 0;
  3742. jx9MemObjInit(pMap->pVm, &sKey);
  3743. jx9MemObjInitFromString(pMap->pVm, &sValue, 0);
  3744. if( zKey ){
  3745. if( nKeylen < 0 ){
  3746. nKeylen = (int)SyStrlen(zKey);
  3747. }
  3748. jx9MemObjStringAppend(&sKey, zKey, (sxu32)nKeylen);
  3749. pKey = &sKey;
  3750. }
  3751. if( zData ){
  3752. if( nLen < 0 ){
  3753. /* Compute length automatically */
  3754. nLen = (int)SyStrlen(zData);
  3755. }
  3756. jx9MemObjStringAppend(&sValue, zData, (sxu32)nLen);
  3757. }
  3758. /* Perform the insertion */
  3759. rc = jx9HashmapInsert(&(*pMap),pKey,&sValue);
  3760. jx9MemObjRelease(&sKey);
  3761. jx9MemObjRelease(&sValue);
  3762. return rc;
  3763. }
  3764. /* Forward declaration */
  3765. static sxi32 VmHttpProcessRequest(jx9_vm *pVm, const char *zRequest, int nByte);
  3766. /*
  3767. * Configure a working virtual machine instance.
  3768. *
  3769. * This routine is used to configure a JX9 virtual machine obtained by a prior
  3770. * successful call to one of the compile interface such as jx9_compile()
  3771. * jx9_compile_v2() or jx9_compile_file().
  3772. * The second argument to this function is an integer configuration option
  3773. * that determines what property of the JX9 virtual machine is to be configured.
  3774. * Subsequent arguments vary depending on the configuration option in the second
  3775. * argument. There are many verbs but the most important are JX9_VM_CONFIG_OUTPUT,
  3776. * JX9_VM_CONFIG_HTTP_REQUEST and JX9_VM_CONFIG_ARGV_ENTRY.
  3777. * Refer to the official documentation for the list of allowed verbs.
  3778. */
  3779. JX9_PRIVATE sxi32 jx9VmConfigure(
  3780. jx9_vm *pVm, /* Target VM */
  3781. sxi32 nOp, /* Configuration verb */
  3782. va_list ap /* Subsequent option arguments */
  3783. )
  3784. {
  3785. sxi32 rc = SXRET_OK;
  3786. switch(nOp){
  3787. case JX9_VM_CONFIG_OUTPUT: {
  3788. ProcConsumer xConsumer = va_arg(ap, ProcConsumer);
  3789. void *pUserData = va_arg(ap, void *);
  3790. /* VM output consumer callback */
  3791. #ifdef UNTRUST
  3792. if( xConsumer == 0 ){
  3793. rc = SXERR_CORRUPT;
  3794. break;
  3795. }
  3796. #endif
  3797. /* Install the output consumer */
  3798. pVm->sVmConsumer.xConsumer = xConsumer;
  3799. pVm->sVmConsumer.pUserData = pUserData;
  3800. break;
  3801. }
  3802. case JX9_VM_CONFIG_IMPORT_PATH: {
  3803. /* Import path */
  3804. const char *zPath;
  3805. SyString sPath;
  3806. zPath = va_arg(ap, const char *);
  3807. #if defined(UNTRUST)
  3808. if( zPath == 0 ){
  3809. rc = SXERR_EMPTY;
  3810. break;
  3811. }
  3812. #endif
  3813. SyStringInitFromBuf(&sPath, zPath, SyStrlen(zPath));
  3814. /* Remove trailing slashes and backslashes */
  3815. #ifdef __WINNT__
  3816. SyStringTrimTrailingChar(&sPath, '\\');
  3817. #endif
  3818. SyStringTrimTrailingChar(&sPath, '/');
  3819. /* Remove leading and trailing white spaces */
  3820. SyStringFullTrim(&sPath);
  3821. if( sPath.nByte > 0 ){
  3822. /* Store the path in the corresponding conatiner */
  3823. rc = SySetPut(&pVm->aPaths, (const void *)&sPath);
  3824. }
  3825. break;
  3826. }
  3827. case JX9_VM_CONFIG_ERR_REPORT:
  3828. /* Run-Time Error report */
  3829. pVm->bErrReport = 1;
  3830. break;
  3831. case JX9_VM_CONFIG_RECURSION_DEPTH:{
  3832. /* Recursion depth */
  3833. int nDepth = va_arg(ap, int);
  3834. if( nDepth > 2 && nDepth < 1024 ){
  3835. pVm->nMaxDepth = nDepth;
  3836. }
  3837. break;
  3838. }
  3839. case JX9_VM_OUTPUT_LENGTH: {
  3840. /* VM output length in bytes */
  3841. sxu32 *pOut = va_arg(ap, sxu32 *);
  3842. #ifdef UNTRUST
  3843. if( pOut == 0 ){
  3844. rc = SXERR_CORRUPT;
  3845. break;
  3846. }
  3847. #endif
  3848. *pOut = pVm->nOutputLen;
  3849. break;
  3850. }
  3851. case JX9_VM_CONFIG_CREATE_VAR: {
  3852. /* Create a new superglobal/global variable */
  3853. const char *zName = va_arg(ap, const char *);
  3854. jx9_value *pValue = va_arg(ap, jx9_value *);
  3855. SyHashEntry *pEntry;
  3856. jx9_value *pObj;
  3857. sxu32 nByte;
  3858. sxu32 nIdx;
  3859. #ifdef UNTRUST
  3860. if( SX_EMPTY_STR(zName) || pValue == 0 ){
  3861. rc = SXERR_CORRUPT;
  3862. break;
  3863. }
  3864. #endif
  3865. nByte = SyStrlen(zName);
  3866. /* Check if the superglobal is already installed */
  3867. pEntry = SyHashGet(&pVm->hSuper, (const void *)zName, nByte);
  3868. if( pEntry ){
  3869. /* Variable already installed */
  3870. nIdx = SX_PTR_TO_INT(pEntry->pUserData);
  3871. /* Extract contents */
  3872. pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
  3873. if( pObj ){
  3874. /* Overwrite old contents */
  3875. jx9MemObjStore(pValue, pObj);
  3876. }
  3877. }else{
  3878. /* Install a new variable */
  3879. pObj = jx9VmReserveMemObj(&(*pVm),&nIdx);
  3880. if( pObj == 0 ){
  3881. rc = SXERR_MEM;
  3882. break;
  3883. }
  3884. /* Copy value */
  3885. jx9MemObjStore(pValue, pObj);
  3886. /* Install the superglobal */
  3887. rc = SyHashInsert(&pVm->hSuper, (const void *)zName, nByte, SX_INT_TO_PTR(nIdx));
  3888. }
  3889. break;
  3890. }
  3891. case JX9_VM_CONFIG_SERVER_ATTR:
  3892. case JX9_VM_CONFIG_ENV_ATTR: {
  3893. const char *zKey = va_arg(ap, const char *);
  3894. const char *zValue = va_arg(ap, const char *);
  3895. int nLen = va_arg(ap, int);
  3896. jx9_hashmap *pMap;
  3897. jx9_value *pValue;
  3898. if( nOp == JX9_VM_CONFIG_ENV_ATTR ){
  3899. /* Extract the $_ENV superglobal */
  3900. pValue = VmExtractSuper(&(*pVm), "_ENV", sizeof("_ENV")-1);
  3901. }else{
  3902. /* Extract the $_SERVER superglobal */
  3903. pValue = VmExtractSuper(&(*pVm), "_SERVER", sizeof("_SERVER")-1);
  3904. }
  3905. if( pValue == 0 || (pValue->iFlags & MEMOBJ_HASHMAP) == 0 ){
  3906. /* No such entry */
  3907. rc = SXERR_NOTFOUND;
  3908. break;
  3909. }
  3910. /* Point to the hashmap */
  3911. pMap = (jx9_hashmap *)pValue->x.pOther;
  3912. /* Perform the insertion */
  3913. rc = VmHashmapInsert(pMap, zKey, -1, zValue, nLen);
  3914. break;
  3915. }
  3916. case JX9_VM_CONFIG_ARGV_ENTRY:{
  3917. /* Script arguments */
  3918. const char *zValue = va_arg(ap, const char *);
  3919. jx9_hashmap *pMap;
  3920. jx9_value *pValue;
  3921. /* Extract the $argv array */
  3922. pValue = VmExtractSuper(&(*pVm), "argv", sizeof("argv")-1);
  3923. if( pValue == 0 || (pValue->iFlags & MEMOBJ_HASHMAP) == 0 ){
  3924. /* No such entry */
  3925. rc = SXERR_NOTFOUND;
  3926. break;
  3927. }
  3928. /* Point to the hashmap */
  3929. pMap = (jx9_hashmap *)pValue->x.pOther;
  3930. /* Perform the insertion */
  3931. rc = VmHashmapInsert(pMap, 0, 0, zValue,-1);
  3932. if( rc == SXRET_OK && zValue && zValue[0] != 0 ){
  3933. if( pMap->nEntry > 1 ){
  3934. /* Append space separator first */
  3935. SyBlobAppend(&pVm->sArgv, (const void *)" ", sizeof(char));
  3936. }
  3937. SyBlobAppend(&pVm->sArgv, (const void *)zValue,SyStrlen(zValue));
  3938. }
  3939. break;
  3940. }
  3941. case JX9_VM_CONFIG_EXEC_VALUE: {
  3942. /* Script return value */
  3943. jx9_value **ppValue = va_arg(ap, jx9_value **);
  3944. #ifdef UNTRUST
  3945. if( ppValue == 0 ){
  3946. rc = SXERR_CORRUPT;
  3947. break;
  3948. }
  3949. #endif
  3950. *ppValue = &pVm->sExec;
  3951. break;
  3952. }
  3953. case JX9_VM_CONFIG_IO_STREAM: {
  3954. /* Register an IO stream device */
  3955. const jx9_io_stream *pStream = va_arg(ap, const jx9_io_stream *);
  3956. /* Make sure we are dealing with a valid IO stream */
  3957. if( pStream == 0 || pStream->zName == 0 || pStream->zName[0] == 0 ||
  3958. pStream->xOpen == 0 || pStream->xRead == 0 ){
  3959. /* Invalid stream */
  3960. rc = SXERR_INVALID;
  3961. break;
  3962. }
  3963. if( pVm->pDefStream == 0 && SyStrnicmp(pStream->zName, "file", sizeof("file")-1) == 0 ){
  3964. /* Make the 'file://' stream the defaut stream device */
  3965. pVm->pDefStream = pStream;
  3966. }
  3967. /* Insert in the appropriate container */
  3968. rc = SySetPut(&pVm->aIOstream, (const void *)&pStream);
  3969. break;
  3970. }
  3971. case JX9_VM_CONFIG_EXTRACT_OUTPUT: {
  3972. /* Point to the VM internal output consumer buffer */
  3973. const void **ppOut = va_arg(ap, const void **);
  3974. unsigned int *pLen = va_arg(ap, unsigned int *);
  3975. #ifdef UNTRUST
  3976. if( ppOut == 0 || pLen == 0 ){
  3977. rc = SXERR_CORRUPT;
  3978. break;
  3979. }
  3980. #endif
  3981. *ppOut = SyBlobData(&pVm->sConsumer);
  3982. *pLen = SyBlobLength(&pVm->sConsumer);
  3983. break;
  3984. }
  3985. case JX9_VM_CONFIG_HTTP_REQUEST:{
  3986. /* Raw HTTP request*/
  3987. const char *zRequest = va_arg(ap, const char *);
  3988. int nByte = va_arg(ap, int);
  3989. if( SX_EMPTY_STR(zRequest) ){
  3990. rc = SXERR_EMPTY;
  3991. break;
  3992. }
  3993. if( nByte < 0 ){
  3994. /* Compute length automatically */
  3995. nByte = (int)SyStrlen(zRequest);
  3996. }
  3997. /* Process the request */
  3998. rc = VmHttpProcessRequest(&(*pVm), zRequest, nByte);
  3999. break;
  4000. }
  4001. default:
  4002. /* Unknown configuration option */
  4003. rc = SXERR_UNKNOWN;
  4004. break;
  4005. }
  4006. return rc;
  4007. }
  4008. /* Forward declaration */
  4009. static const char * VmInstrToString(sxi32 nOp);
  4010. /*
  4011. * This routine is used to dump JX9 bytecode instructions to a human readable
  4012. * format.
  4013. * The dump is redirected to the given consumer callback which is responsible
  4014. * of consuming the generated dump perhaps redirecting it to its standard output
  4015. * (STDOUT).
  4016. */
  4017. static sxi32 VmByteCodeDump(
  4018. SySet *pByteCode, /* Bytecode container */
  4019. ProcConsumer xConsumer, /* Dump consumer callback */
  4020. void *pUserData /* Last argument to xConsumer() */
  4021. )
  4022. {
  4023. static const char zDump[] = {
  4024. "====================================================\n"
  4025. "JX9 VM Dump Copyright (C) 2012-2013 Symisc Systems\n"
  4026. " http://jx9.symisc.net/\n"
  4027. "====================================================\n"
  4028. };
  4029. VmInstr *pInstr, *pEnd;
  4030. sxi32 rc = SXRET_OK;
  4031. sxu32 n;
  4032. /* Point to the JX9 instructions */
  4033. pInstr = (VmInstr *)SySetBasePtr(pByteCode);
  4034. pEnd = &pInstr[SySetUsed(pByteCode)];
  4035. n = 0;
  4036. xConsumer((const void *)zDump, sizeof(zDump)-1, pUserData);
  4037. /* Dump instructions */
  4038. for(;;){
  4039. if( pInstr >= pEnd ){
  4040. /* No more instructions */
  4041. break;
  4042. }
  4043. /* Format and call the consumer callback */
  4044. rc = SyProcFormat(xConsumer, pUserData, "%s %8d %8u %#8x [%u]\n",
  4045. VmInstrToString(pInstr->iOp), pInstr->iP1, pInstr->iP2,
  4046. SX_PTR_TO_INT(pInstr->p3), n);
  4047. if( rc != SXRET_OK ){
  4048. /* Consumer routine request an operation abort */
  4049. return rc;
  4050. }
  4051. ++n;
  4052. pInstr++; /* Next instruction in the stream */
  4053. }
  4054. return rc;
  4055. }
  4056. /*
  4057. * Consume a generated run-time error message by invoking the VM output
  4058. * consumer callback.
  4059. */
  4060. static sxi32 VmCallErrorHandler(jx9_vm *pVm, SyBlob *pMsg)
  4061. {
  4062. jx9_output_consumer *pCons = &pVm->sVmConsumer;
  4063. sxi32 rc = SXRET_OK;
  4064. /* Append a new line */
  4065. #ifdef __WINNT__
  4066. SyBlobAppend(pMsg, "\r\n", sizeof("\r\n")-1);
  4067. #else
  4068. SyBlobAppend(pMsg, "\n", sizeof(char));
  4069. #endif
  4070. /* Invoke the output consumer callback */
  4071. rc = pCons->xConsumer(SyBlobData(pMsg), SyBlobLength(pMsg), pCons->pUserData);
  4072. /* Increment output length */
  4073. pVm->nOutputLen += SyBlobLength(pMsg);
  4074. return rc;
  4075. }
  4076. /*
  4077. * Throw a run-time error and invoke the supplied VM output consumer callback.
  4078. * Refer to the implementation of [jx9_context_throw_error()] for additional
  4079. * information.
  4080. */
  4081. JX9_PRIVATE sxi32 jx9VmThrowError(
  4082. jx9_vm *pVm, /* Target VM */
  4083. SyString *pFuncName, /* Function name. NULL otherwise */
  4084. sxi32 iErr, /* Severity level: [i.e: Error, Warning or Notice]*/
  4085. const char *zMessage /* Null terminated error message */
  4086. )
  4087. {
  4088. SyBlob *pWorker = &pVm->sWorker;
  4089. SyString *pFile;
  4090. char *zErr;
  4091. sxi32 rc;
  4092. if( !pVm->bErrReport ){
  4093. /* Don't bother reporting errors */
  4094. return SXRET_OK;
  4095. }
  4096. /* Reset the working buffer */
  4097. SyBlobReset(pWorker);
  4098. /* Peek the processed file if available */
  4099. pFile = (SyString *)SySetPeek(&pVm->aFiles);
  4100. if( pFile ){
  4101. /* Append file name */
  4102. SyBlobAppend(pWorker, pFile->zString, pFile->nByte);
  4103. SyBlobAppend(pWorker, (const void *)" ", sizeof(char));
  4104. }
  4105. zErr = "Error: ";
  4106. switch(iErr){
  4107. case JX9_CTX_WARNING: zErr = "Warning: "; break;
  4108. case JX9_CTX_NOTICE: zErr = "Notice: "; break;
  4109. default:
  4110. iErr = JX9_CTX_ERR;
  4111. break;
  4112. }
  4113. SyBlobAppend(pWorker, zErr, SyStrlen(zErr));
  4114. if( pFuncName ){
  4115. /* Append function name first */
  4116. SyBlobAppend(pWorker, pFuncName->zString, pFuncName->nByte);
  4117. SyBlobAppend(pWorker, "(): ", sizeof("(): ")-1);
  4118. }
  4119. SyBlobAppend(pWorker, zMessage, SyStrlen(zMessage));
  4120. /* Consume the error message */
  4121. rc = VmCallErrorHandler(&(*pVm), pWorker);
  4122. return rc;
  4123. }
  4124. /*
  4125. * Format and throw a run-time error and invoke the supplied VM output consumer callback.
  4126. * Refer to the implementation of [jx9_context_throw_error_format()] for additional
  4127. * information.
  4128. */
  4129. static sxi32 VmThrowErrorAp(
  4130. jx9_vm *pVm, /* Target VM */
  4131. SyString *pFuncName, /* Function name. NULL otherwise */
  4132. sxi32 iErr, /* Severity level: [i.e: Error, Warning or Notice] */
  4133. const char *zFormat, /* Format message */
  4134. va_list ap /* Variable list of arguments */
  4135. )
  4136. {
  4137. SyBlob *pWorker = &pVm->sWorker;
  4138. SyString *pFile;
  4139. char *zErr;
  4140. sxi32 rc;
  4141. if( !pVm->bErrReport ){
  4142. /* Don't bother reporting errors */
  4143. return SXRET_OK;
  4144. }
  4145. /* Reset the working buffer */
  4146. SyBlobReset(pWorker);
  4147. /* Peek the processed file if available */
  4148. pFile = (SyString *)SySetPeek(&pVm->aFiles);
  4149. if( pFile ){
  4150. /* Append file name */
  4151. SyBlobAppend(pWorker, pFile->zString, pFile->nByte);
  4152. SyBlobAppend(pWorker, (const void *)" ", sizeof(char));
  4153. }
  4154. zErr = "Error: ";
  4155. switch(iErr){
  4156. case JX9_CTX_WARNING: zErr = "Warning: "; break;
  4157. case JX9_CTX_NOTICE: zErr = "Notice: "; break;
  4158. default:
  4159. iErr = JX9_CTX_ERR;
  4160. break;
  4161. }
  4162. SyBlobAppend(pWorker, zErr, SyStrlen(zErr));
  4163. if( pFuncName ){
  4164. /* Append function name first */
  4165. SyBlobAppend(pWorker, pFuncName->zString, pFuncName->nByte);
  4166. SyBlobAppend(pWorker, "(): ", sizeof("(): ")-1);
  4167. }
  4168. SyBlobFormatAp(pWorker, zFormat, ap);
  4169. /* Consume the error message */
  4170. rc = VmCallErrorHandler(&(*pVm), pWorker);
  4171. return rc;
  4172. }
  4173. /*
  4174. * Format and throw a run-time error and invoke the supplied VM output consumer callback.
  4175. * Refer to the implementation of [jx9_context_throw_error_format()] for additional
  4176. * information.
  4177. * ------------------------------------
  4178. * Simple boring wrapper function.
  4179. * ------------------------------------
  4180. */
  4181. static sxi32 VmErrorFormat(jx9_vm *pVm, sxi32 iErr, const char *zFormat, ...)
  4182. {
  4183. va_list ap;
  4184. sxi32 rc;
  4185. va_start(ap, zFormat);
  4186. rc = VmThrowErrorAp(&(*pVm), 0, iErr, zFormat, ap);
  4187. va_end(ap);
  4188. return rc;
  4189. }
  4190. /*
  4191. * Format and throw a run-time error and invoke the supplied VM output consumer callback.
  4192. * Refer to the implementation of [jx9_context_throw_error_format()] for additional
  4193. * information.
  4194. * ------------------------------------
  4195. * Simple boring wrapper function.
  4196. * ------------------------------------
  4197. */
  4198. JX9_PRIVATE sxi32 jx9VmThrowErrorAp(jx9_vm *pVm, SyString *pFuncName, sxi32 iErr, const char *zFormat, va_list ap)
  4199. {
  4200. sxi32 rc;
  4201. rc = VmThrowErrorAp(&(*pVm), &(*pFuncName), iErr, zFormat, ap);
  4202. return rc;
  4203. }
  4204. /* Forward declaration */
  4205. static sxi32 VmLocalExec(jx9_vm *pVm,SySet *pByteCode,jx9_value *pResult);
  4206. /*
  4207. * Execute as much of a JX9 bytecode program as we can then return.
  4208. *
  4209. * [jx9VmMakeReady()] must be called before this routine in order to
  4210. * close the program with a final OP_DONE and to set up the default
  4211. * consumer routines and other stuff. Refer to the implementation
  4212. * of [jx9VmMakeReady()] for additional information.
  4213. * If the installed VM output consumer callback ever returns JX9_ABORT
  4214. * then the program execution is halted.
  4215. * After this routine has finished, [jx9VmRelease()] or [jx9VmReset()]
  4216. * should be used respectively to clean up the mess that was left behind
  4217. * or to reset the VM to it's initial state.
  4218. */
  4219. static sxi32 VmByteCodeExec(
  4220. jx9_vm *pVm, /* Target VM */
  4221. VmInstr *aInstr, /* JX9 bytecode program */
  4222. jx9_value *pStack, /* Operand stack */
  4223. int nTos, /* Top entry in the operand stack (usually -1) */
  4224. jx9_value *pResult /* Store program return value here. NULL otherwise */
  4225. )
  4226. {
  4227. VmInstr *pInstr;
  4228. jx9_value *pTos;
  4229. SySet aArg;
  4230. sxi32 pc;
  4231. sxi32 rc;
  4232. /* Argument container */
  4233. SySetInit(&aArg, &pVm->sAllocator, sizeof(jx9_value *));
  4234. if( nTos < 0 ){
  4235. pTos = &pStack[-1];
  4236. }else{
  4237. pTos = &pStack[nTos];
  4238. }
  4239. pc = 0;
  4240. /* Execute as much as we can */
  4241. for(;;){
  4242. /* Fetch the instruction to execute */
  4243. pInstr = &aInstr[pc];
  4244. rc = SXRET_OK;
  4245. /*
  4246. * What follows here is a massive switch statement where each case implements a
  4247. * separate instruction in the virtual machine. If we follow the usual
  4248. * indentation convention each case should be indented by 6 spaces. But
  4249. * that is a lot of wasted space on the left margin. So the code within
  4250. * the switch statement will break with convention and be flush-left.
  4251. */
  4252. switch(pInstr->iOp){
  4253. /*
  4254. * DONE: P1 * *
  4255. *
  4256. * Program execution completed: Clean up the mess left behind
  4257. * and return immediately.
  4258. */
  4259. case JX9_OP_DONE:
  4260. if( pInstr->iP1 ){
  4261. #ifdef UNTRUST
  4262. if( pTos < pStack ){
  4263. goto Abort;
  4264. }
  4265. #endif
  4266. if( pResult ){
  4267. /* Execution result */
  4268. jx9MemObjStore(pTos, pResult);
  4269. }
  4270. VmPopOperand(&pTos, 1);
  4271. }
  4272. goto Done;
  4273. /*
  4274. * HALT: P1 * *
  4275. *
  4276. * Program execution aborted: Clean up the mess left behind
  4277. * and abort immediately.
  4278. */
  4279. case JX9_OP_HALT:
  4280. if( pInstr->iP1 ){
  4281. #ifdef UNTRUST
  4282. if( pTos < pStack ){
  4283. goto Abort;
  4284. }
  4285. #endif
  4286. if( pTos->iFlags & MEMOBJ_STRING ){
  4287. if( SyBlobLength(&pTos->sBlob) > 0 ){
  4288. /* Output the exit message */
  4289. pVm->sVmConsumer.xConsumer(SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob),
  4290. pVm->sVmConsumer.pUserData);
  4291. /* Increment output length */
  4292. pVm->nOutputLen += SyBlobLength(&pTos->sBlob);
  4293. }
  4294. }else if(pTos->iFlags & MEMOBJ_INT ){
  4295. /* Record exit status */
  4296. pVm->iExitStatus = (sxi32)pTos->x.iVal;
  4297. }
  4298. VmPopOperand(&pTos, 1);
  4299. }
  4300. goto Abort;
  4301. /*
  4302. * JMP: * P2 *
  4303. *
  4304. * Unconditional jump: The next instruction executed will be
  4305. * the one at index P2 from the beginning of the program.
  4306. */
  4307. case JX9_OP_JMP:
  4308. pc = pInstr->iP2 - 1;
  4309. break;
  4310. /*
  4311. * JZ: P1 P2 *
  4312. *
  4313. * Take the jump if the top value is zero (FALSE jump).Pop the top most
  4314. * entry in the stack if P1 is zero.
  4315. */
  4316. case JX9_OP_JZ:
  4317. #ifdef UNTRUST
  4318. if( pTos < pStack ){
  4319. goto Abort;
  4320. }
  4321. #endif
  4322. /* Get a boolean value */
  4323. if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){
  4324. jx9MemObjToBool(pTos);
  4325. }
  4326. if( !pTos->x.iVal ){
  4327. /* Take the jump */
  4328. pc = pInstr->iP2 - 1;
  4329. }
  4330. if( !pInstr->iP1 ){
  4331. VmPopOperand(&pTos, 1);
  4332. }
  4333. break;
  4334. /*
  4335. * JNZ: P1 P2 *
  4336. *
  4337. * Take the jump if the top value is not zero (TRUE jump).Pop the top most
  4338. * entry in the stack if P1 is zero.
  4339. */
  4340. case JX9_OP_JNZ:
  4341. #ifdef UNTRUST
  4342. if( pTos < pStack ){
  4343. goto Abort;
  4344. }
  4345. #endif
  4346. /* Get a boolean value */
  4347. if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){
  4348. jx9MemObjToBool(pTos);
  4349. }
  4350. if( pTos->x.iVal ){
  4351. /* Take the jump */
  4352. pc = pInstr->iP2 - 1;
  4353. }
  4354. if( !pInstr->iP1 ){
  4355. VmPopOperand(&pTos, 1);
  4356. }
  4357. break;
  4358. /*
  4359. * NOOP: * * *
  4360. *
  4361. * Do nothing. This instruction is often useful as a jump
  4362. * destination.
  4363. */
  4364. case JX9_OP_NOOP:
  4365. break;
  4366. /*
  4367. * POP: P1 * *
  4368. *
  4369. * Pop P1 elements from the operand stack.
  4370. */
  4371. case JX9_OP_POP: {
  4372. sxi32 n = pInstr->iP1;
  4373. if( &pTos[-n+1] < pStack ){
  4374. /* TICKET 1433-51 Stack underflow must be handled at run-time */
  4375. n = (sxi32)(pTos - pStack);
  4376. }
  4377. VmPopOperand(&pTos, n);
  4378. break;
  4379. }
  4380. /*
  4381. * CVT_INT: * * *
  4382. *
  4383. * Force the top of the stack to be an integer.
  4384. */
  4385. case JX9_OP_CVT_INT:
  4386. #ifdef UNTRUST
  4387. if( pTos < pStack ){
  4388. goto Abort;
  4389. }
  4390. #endif
  4391. if((pTos->iFlags & MEMOBJ_INT) == 0 ){
  4392. jx9MemObjToInteger(pTos);
  4393. }
  4394. /* Invalidate any prior representation */
  4395. MemObjSetType(pTos, MEMOBJ_INT);
  4396. break;
  4397. /*
  4398. * CVT_REAL: * * *
  4399. *
  4400. * Force the top of the stack to be a real.
  4401. */
  4402. case JX9_OP_CVT_REAL:
  4403. #ifdef UNTRUST
  4404. if( pTos < pStack ){
  4405. goto Abort;
  4406. }
  4407. #endif
  4408. if((pTos->iFlags & MEMOBJ_REAL) == 0 ){
  4409. jx9MemObjToReal(pTos);
  4410. }
  4411. /* Invalidate any prior representation */
  4412. MemObjSetType(pTos, MEMOBJ_REAL);
  4413. break;
  4414. /*
  4415. * CVT_STR: * * *
  4416. *
  4417. * Force the top of the stack to be a string.
  4418. */
  4419. case JX9_OP_CVT_STR:
  4420. #ifdef UNTRUST
  4421. if( pTos < pStack ){
  4422. goto Abort;
  4423. }
  4424. #endif
  4425. if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
  4426. jx9MemObjToString(pTos);
  4427. }
  4428. break;
  4429. /*
  4430. * CVT_BOOL: * * *
  4431. *
  4432. * Force the top of the stack to be a boolean.
  4433. */
  4434. case JX9_OP_CVT_BOOL:
  4435. #ifdef UNTRUST
  4436. if( pTos < pStack ){
  4437. goto Abort;
  4438. }
  4439. #endif
  4440. if( (pTos->iFlags & MEMOBJ_BOOL) == 0 ){
  4441. jx9MemObjToBool(pTos);
  4442. }
  4443. break;
  4444. /*
  4445. * CVT_NULL: * * *
  4446. *
  4447. * Nullify the top of the stack.
  4448. */
  4449. case JX9_OP_CVT_NULL:
  4450. #ifdef UNTRUST
  4451. if( pTos < pStack ){
  4452. goto Abort;
  4453. }
  4454. #endif
  4455. jx9MemObjRelease(pTos);
  4456. break;
  4457. /*
  4458. * CVT_NUMC: * * *
  4459. *
  4460. * Force the top of the stack to be a numeric type (integer, real or both).
  4461. */
  4462. case JX9_OP_CVT_NUMC:
  4463. #ifdef UNTRUST
  4464. if( pTos < pStack ){
  4465. goto Abort;
  4466. }
  4467. #endif
  4468. /* Force a numeric cast */
  4469. jx9MemObjToNumeric(pTos);
  4470. break;
  4471. /*
  4472. * CVT_ARRAY: * * *
  4473. *
  4474. * Force the top of the stack to be a hashmap aka 'array'.
  4475. */
  4476. case JX9_OP_CVT_ARRAY:
  4477. #ifdef UNTRUST
  4478. if( pTos < pStack ){
  4479. goto Abort;
  4480. }
  4481. #endif
  4482. /* Force a hashmap cast */
  4483. rc = jx9MemObjToHashmap(pTos);
  4484. if( rc != SXRET_OK ){
  4485. /* Not so fatal, emit a simple warning */
  4486. jx9VmThrowError(&(*pVm), 0, JX9_CTX_WARNING,
  4487. "JX9 engine is running out of memory while performing an array cast");
  4488. }
  4489. break;
  4490. /*
  4491. * LOADC P1 P2 *
  4492. *
  4493. * Load a constant [i.e: JX9_EOL, JX9_OS, __TIME__, ...] indexed at P2 in the constant pool.
  4494. * If P1 is set, then this constant is candidate for expansion via user installable callbacks.
  4495. */
  4496. case JX9_OP_LOADC: {
  4497. jx9_value *pObj;
  4498. /* Reserve a room */
  4499. pTos++;
  4500. if( (pObj = (jx9_value *)SySetAt(&pVm->aLitObj, pInstr->iP2)) != 0 ){
  4501. if( pInstr->iP1 == 1 && SyBlobLength(&pObj->sBlob) <= 64 ){
  4502. SyHashEntry *pEntry;
  4503. /* Candidate for expansion via user defined callbacks */
  4504. pEntry = SyHashGet(&pVm->hConstant, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
  4505. if( pEntry ){
  4506. jx9_constant *pCons = (jx9_constant *)pEntry->pUserData;
  4507. /* Set a NULL default value */
  4508. MemObjSetType(pTos, MEMOBJ_NULL);
  4509. SyBlobReset(&pTos->sBlob);
  4510. /* Invoke the callback and deal with the expanded value */
  4511. pCons->xExpand(pTos, pCons->pUserData);
  4512. /* Mark as constant */
  4513. pTos->nIdx = SXU32_HIGH;
  4514. break;
  4515. }
  4516. }
  4517. jx9MemObjLoad(pObj, pTos);
  4518. }else{
  4519. /* Set a NULL value */
  4520. MemObjSetType(pTos, MEMOBJ_NULL);
  4521. }
  4522. /* Mark as constant */
  4523. pTos->nIdx = SXU32_HIGH;
  4524. break;
  4525. }
  4526. /*
  4527. * LOAD: P1 * P3
  4528. *
  4529. * Load a variable where it's name is taken from the top of the stack or
  4530. * from the P3 operand.
  4531. * If P1 is set, then perform a lookup only.In other words do not create
  4532. * the variable if non existent and push the NULL constant instead.
  4533. */
  4534. case JX9_OP_LOAD:{
  4535. jx9_value *pObj;
  4536. SyString sName;
  4537. if( pInstr->p3 == 0 ){
  4538. /* Take the variable name from the top of the stack */
  4539. #ifdef UNTRUST
  4540. if( pTos < pStack ){
  4541. goto Abort;
  4542. }
  4543. #endif
  4544. /* Force a string cast */
  4545. if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
  4546. jx9MemObjToString(pTos);
  4547. }
  4548. SyStringInitFromBuf(&sName, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
  4549. }else{
  4550. SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3));
  4551. /* Reserve a room for the target object */
  4552. pTos++;
  4553. }
  4554. /* Extract the requested memory object */
  4555. pObj = VmExtractMemObj(&(*pVm), &sName, pInstr->p3 ? FALSE : TRUE, pInstr->iP1 != 1);
  4556. if( pObj == 0 ){
  4557. if( pInstr->iP1 ){
  4558. /* Variable not found, load NULL */
  4559. if( !pInstr->p3 ){
  4560. jx9MemObjRelease(pTos);
  4561. }else{
  4562. MemObjSetType(pTos, MEMOBJ_NULL);
  4563. }
  4564. pTos->nIdx = SXU32_HIGH; /* Mark as constant */
  4565. break;
  4566. }else{
  4567. /* Fatal error */
  4568. VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Fatal, JX9 engine is running out of memory while loading variable '%z'", &sName);
  4569. goto Abort;
  4570. }
  4571. }
  4572. /* Load variable contents */
  4573. jx9MemObjLoad(pObj, pTos);
  4574. pTos->nIdx = pObj->nIdx;
  4575. break;
  4576. }
  4577. /*
  4578. * LOAD_MAP P1 * *
  4579. *
  4580. * Allocate a new empty hashmap (array in the JX9 jargon) and push it on the stack.
  4581. * If the P1 operand is greater than zero then pop P1 elements from the
  4582. * stack and insert them (key => value pair) in the new hashmap.
  4583. */
  4584. case JX9_OP_LOAD_MAP: {
  4585. jx9_hashmap *pMap;
  4586. int is_json_object; /* TRUE if we are dealing with a JSON object */
  4587. int iIncr = 1;
  4588. /* Allocate a new hashmap instance */
  4589. pMap = jx9NewHashmap(&(*pVm), 0, 0);
  4590. if( pMap == 0 ){
  4591. VmErrorFormat(&(*pVm), JX9_CTX_ERR,
  4592. "Fatal, JX9 engine is running out of memory while loading JSON array/object at instruction #:%d", pc);
  4593. goto Abort;
  4594. }
  4595. is_json_object = 0;
  4596. if( pInstr->iP2 ){
  4597. /* JSON object, record that */
  4598. pMap->iFlags |= HASHMAP_JSON_OBJECT;
  4599. is_json_object = 1;
  4600. iIncr = 2;
  4601. }
  4602. if( pInstr->iP1 > 0 ){
  4603. jx9_value *pEntry = &pTos[-pInstr->iP1+1]; /* Point to the first entry */
  4604. /* Perform the insertion */
  4605. while( pEntry <= pTos ){
  4606. /* Standard insertion */
  4607. jx9HashmapInsert(pMap,
  4608. is_json_object ? pEntry : 0 /* Automatic index assign */,
  4609. is_json_object ? &pEntry[1] : pEntry
  4610. );
  4611. /* Next pair on the stack */
  4612. pEntry += iIncr;
  4613. }
  4614. /* Pop P1 elements */
  4615. VmPopOperand(&pTos, pInstr->iP1);
  4616. }
  4617. /* Push the hashmap */
  4618. pTos++;
  4619. pTos->x.pOther = pMap;
  4620. MemObjSetType(pTos, MEMOBJ_HASHMAP);
  4621. break;
  4622. }
  4623. /*
  4624. * LOAD_IDX: P1 P2 *
  4625. *
  4626. * Load a hasmap entry where it's index (either numeric or string) is taken
  4627. * from the stack.
  4628. * If the index does not refer to a valid element, then push the NULL constant
  4629. * instead.
  4630. */
  4631. case JX9_OP_LOAD_IDX: {
  4632. jx9_hashmap_node *pNode = 0; /* cc warning */
  4633. jx9_hashmap *pMap = 0;
  4634. jx9_value *pIdx;
  4635. pIdx = 0;
  4636. if( pInstr->iP1 == 0 ){
  4637. if( !pInstr->iP2){
  4638. /* No available index, load NULL */
  4639. if( pTos >= pStack ){
  4640. jx9MemObjRelease(pTos);
  4641. }else{
  4642. /* TICKET 1433-020: Empty stack */
  4643. pTos++;
  4644. MemObjSetType(pTos, MEMOBJ_NULL);
  4645. pTos->nIdx = SXU32_HIGH;
  4646. }
  4647. /* Emit a notice */
  4648. jx9VmThrowError(&(*pVm), 0, JX9_CTX_NOTICE,
  4649. "JSON Array/Object: Attempt to access an undefined member, JX9 is loading NULL");
  4650. break;
  4651. }
  4652. }else{
  4653. pIdx = pTos;
  4654. pTos--;
  4655. }
  4656. if( pTos->iFlags & MEMOBJ_STRING ){
  4657. /* String access */
  4658. if( pIdx ){
  4659. sxu32 nOfft;
  4660. if( (pIdx->iFlags & MEMOBJ_INT) == 0 ){
  4661. /* Force an int cast */
  4662. jx9MemObjToInteger(pIdx);
  4663. }
  4664. nOfft = (sxu32)pIdx->x.iVal;
  4665. if( nOfft >= SyBlobLength(&pTos->sBlob) ){
  4666. /* Invalid offset, load null */
  4667. jx9MemObjRelease(pTos);
  4668. }else{
  4669. const char *zData = (const char *)SyBlobData(&pTos->sBlob);
  4670. int c = zData[nOfft];
  4671. jx9MemObjRelease(pTos);
  4672. MemObjSetType(pTos, MEMOBJ_STRING);
  4673. SyBlobAppend(&pTos->sBlob, (const void *)&c, sizeof(char));
  4674. }
  4675. }else{
  4676. /* No available index, load NULL */
  4677. MemObjSetType(pTos, MEMOBJ_NULL);
  4678. }
  4679. break;
  4680. }
  4681. if( pInstr->iP2 && (pTos->iFlags & MEMOBJ_HASHMAP) == 0 ){
  4682. if( pTos->nIdx != SXU32_HIGH ){
  4683. jx9_value *pObj;
  4684. if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
  4685. jx9MemObjToHashmap(pObj);
  4686. jx9MemObjLoad(pObj, pTos);
  4687. }
  4688. }
  4689. }
  4690. rc = SXERR_NOTFOUND; /* Assume the index is invalid */
  4691. if( pTos->iFlags & MEMOBJ_HASHMAP ){
  4692. /* Point to the hashmap */
  4693. pMap = (jx9_hashmap *)pTos->x.pOther;
  4694. if( pIdx ){
  4695. /* Load the desired entry */
  4696. rc = jx9HashmapLookup(pMap, pIdx, &pNode);
  4697. }
  4698. if( rc != SXRET_OK && pInstr->iP2 ){
  4699. /* Create a new empty entry */
  4700. rc = jx9HashmapInsert(pMap, pIdx, 0);
  4701. if( rc == SXRET_OK ){
  4702. /* Point to the last inserted entry */
  4703. pNode = pMap->pLast;
  4704. }
  4705. }
  4706. }
  4707. if( pIdx ){
  4708. jx9MemObjRelease(pIdx);
  4709. }
  4710. if( rc == SXRET_OK ){
  4711. /* Load entry contents */
  4712. if( pMap->iRef < 2 ){
  4713. /* TICKET 1433-42: Array will be deleted shortly, so we will make a copy
  4714. * of the entry value, rather than pointing to it.
  4715. */
  4716. pTos->nIdx = SXU32_HIGH;
  4717. jx9HashmapExtractNodeValue(pNode, pTos, TRUE);
  4718. }else{
  4719. pTos->nIdx = pNode->nValIdx;
  4720. jx9HashmapExtractNodeValue(pNode, pTos, FALSE);
  4721. jx9HashmapUnref(pMap);
  4722. }
  4723. }else{
  4724. /* No such entry, load NULL */
  4725. jx9MemObjRelease(pTos);
  4726. pTos->nIdx = SXU32_HIGH;
  4727. }
  4728. break;
  4729. }
  4730. /*
  4731. * STORE * P2 P3
  4732. *
  4733. * Perform a store (Assignment) operation.
  4734. */
  4735. case JX9_OP_STORE: {
  4736. jx9_value *pObj;
  4737. SyString sName;
  4738. #ifdef UNTRUST
  4739. if( pTos < pStack ){
  4740. goto Abort;
  4741. }
  4742. #endif
  4743. if( pInstr->iP2 ){
  4744. sxu32 nIdx;
  4745. /* Member store operation */
  4746. nIdx = pTos->nIdx;
  4747. VmPopOperand(&pTos, 1);
  4748. if( nIdx == SXU32_HIGH ){
  4749. jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR,
  4750. "Cannot perform assignment on a constant object attribute, JX9 is loading NULL");
  4751. pTos->nIdx = SXU32_HIGH;
  4752. }else{
  4753. /* Point to the desired memory object */
  4754. pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
  4755. if( pObj ){
  4756. /* Perform the store operation */
  4757. jx9MemObjStore(pTos, pObj);
  4758. }
  4759. }
  4760. break;
  4761. }else if( pInstr->p3 == 0 ){
  4762. /* Take the variable name from the next on the stack */
  4763. if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
  4764. /* Force a string cast */
  4765. jx9MemObjToString(pTos);
  4766. }
  4767. SyStringInitFromBuf(&sName, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
  4768. pTos--;
  4769. #ifdef UNTRUST
  4770. if( pTos < pStack ){
  4771. goto Abort;
  4772. }
  4773. #endif
  4774. }else{
  4775. SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3));
  4776. }
  4777. /* Extract the desired variable and if not available dynamically create it */
  4778. pObj = VmExtractMemObj(&(*pVm), &sName, pInstr->p3 ? FALSE : TRUE, TRUE);
  4779. if( pObj == 0 ){
  4780. VmErrorFormat(&(*pVm), JX9_CTX_ERR,
  4781. "Fatal, JX9 engine is running out of memory while loading variable '%z'", &sName);
  4782. goto Abort;
  4783. }
  4784. if( !pInstr->p3 ){
  4785. jx9MemObjRelease(&pTos[1]);
  4786. }
  4787. /* Perform the store operation */
  4788. jx9MemObjStore(pTos, pObj);
  4789. break;
  4790. }
  4791. /*
  4792. * STORE_IDX: P1 * P3
  4793. *
  4794. * Perfrom a store operation an a hashmap entry.
  4795. */
  4796. case JX9_OP_STORE_IDX: {
  4797. jx9_hashmap *pMap = 0; /* cc warning */
  4798. jx9_value *pKey;
  4799. sxu32 nIdx;
  4800. if( pInstr->iP1 ){
  4801. /* Key is next on stack */
  4802. pKey = pTos;
  4803. pTos--;
  4804. }else{
  4805. pKey = 0;
  4806. }
  4807. nIdx = pTos->nIdx;
  4808. if( pTos->iFlags & MEMOBJ_HASHMAP ){
  4809. /* Hashmap already loaded */
  4810. pMap = (jx9_hashmap *)pTos->x.pOther;
  4811. if( pMap->iRef < 2 ){
  4812. /* TICKET 1433-48: Prevent garbage collection */
  4813. pMap->iRef = 2;
  4814. }
  4815. }else{
  4816. jx9_value *pObj;
  4817. pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx);
  4818. if( pObj == 0 ){
  4819. if( pKey ){
  4820. jx9MemObjRelease(pKey);
  4821. }
  4822. VmPopOperand(&pTos, 1);
  4823. break;
  4824. }
  4825. /* Phase#1: Load the array */
  4826. if( (pObj->iFlags & MEMOBJ_STRING) ){
  4827. VmPopOperand(&pTos, 1);
  4828. if( (pTos->iFlags&MEMOBJ_STRING) == 0 ){
  4829. /* Force a string cast */
  4830. jx9MemObjToString(pTos);
  4831. }
  4832. if( pKey == 0 ){
  4833. /* Append string */
  4834. if( SyBlobLength(&pTos->sBlob) > 0 ){
  4835. SyBlobAppend(&pObj->sBlob, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
  4836. }
  4837. }else{
  4838. sxu32 nOfft;
  4839. if((pKey->iFlags & MEMOBJ_INT)){
  4840. /* Force an int cast */
  4841. jx9MemObjToInteger(pKey);
  4842. }
  4843. nOfft = (sxu32)pKey->x.iVal;
  4844. if( nOfft < SyBlobLength(&pObj->sBlob) && SyBlobLength(&pTos->sBlob) > 0 ){
  4845. const char *zBlob = (const char *)SyBlobData(&pTos->sBlob);
  4846. char *zData = (char *)SyBlobData(&pObj->sBlob);
  4847. zData[nOfft] = zBlob[0];
  4848. }else{
  4849. if( SyBlobLength(&pTos->sBlob) >= sizeof(char) ){
  4850. /* Perform an append operation */
  4851. SyBlobAppend(&pObj->sBlob, SyBlobData(&pTos->sBlob), sizeof(char));
  4852. }
  4853. }
  4854. }
  4855. if( pKey ){
  4856. jx9MemObjRelease(pKey);
  4857. }
  4858. break;
  4859. }else if( (pObj->iFlags & MEMOBJ_HASHMAP) == 0 ){
  4860. /* Force a hashmap cast */
  4861. rc = jx9MemObjToHashmap(pObj);
  4862. if( rc != SXRET_OK ){
  4863. VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Fatal, JX9 engine is running out of memory while creating a new array");
  4864. goto Abort;
  4865. }
  4866. }
  4867. pMap = (jx9_hashmap *)pObj->x.pOther;
  4868. }
  4869. VmPopOperand(&pTos, 1);
  4870. /* Phase#2: Perform the insertion */
  4871. jx9HashmapInsert(pMap, pKey, pTos);
  4872. if( pKey ){
  4873. jx9MemObjRelease(pKey);
  4874. }
  4875. break;
  4876. }
  4877. /*
  4878. * INCR: P1 * *
  4879. *
  4880. * Force a numeric cast and increment the top of the stack by 1.
  4881. * If the P1 operand is set then perform a duplication of the top of
  4882. * the stack and increment after that.
  4883. */
  4884. case JX9_OP_INCR:
  4885. #ifdef UNTRUST
  4886. if( pTos < pStack ){
  4887. goto Abort;
  4888. }
  4889. #endif
  4890. if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0 ){
  4891. if( pTos->nIdx != SXU32_HIGH ){
  4892. jx9_value *pObj;
  4893. if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
  4894. /* Force a numeric cast */
  4895. jx9MemObjToNumeric(pObj);
  4896. if( pObj->iFlags & MEMOBJ_REAL ){
  4897. pObj->x.rVal++;
  4898. /* Try to get an integer representation */
  4899. jx9MemObjTryInteger(pTos);
  4900. }else{
  4901. pObj->x.iVal++;
  4902. MemObjSetType(pTos, MEMOBJ_INT);
  4903. }
  4904. if( pInstr->iP1 ){
  4905. /* Pre-icrement */
  4906. jx9MemObjStore(pObj, pTos);
  4907. }
  4908. }
  4909. }else{
  4910. if( pInstr->iP1 ){
  4911. /* Force a numeric cast */
  4912. jx9MemObjToNumeric(pTos);
  4913. /* Pre-increment */
  4914. if( pTos->iFlags & MEMOBJ_REAL ){
  4915. pTos->x.rVal++;
  4916. /* Try to get an integer representation */
  4917. jx9MemObjTryInteger(pTos);
  4918. }else{
  4919. pTos->x.iVal++;
  4920. MemObjSetType(pTos, MEMOBJ_INT);
  4921. }
  4922. }
  4923. }
  4924. }
  4925. break;
  4926. /*
  4927. * DECR: P1 * *
  4928. *
  4929. * Force a numeric cast and decrement the top of the stack by 1.
  4930. * If the P1 operand is set then perform a duplication of the top of the stack
  4931. * and decrement after that.
  4932. */
  4933. case JX9_OP_DECR:
  4934. #ifdef UNTRUST
  4935. if( pTos < pStack ){
  4936. goto Abort;
  4937. }
  4938. #endif
  4939. if( (pTos->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES|MEMOBJ_NULL)) == 0 ){
  4940. /* Force a numeric cast */
  4941. jx9MemObjToNumeric(pTos);
  4942. if( pTos->nIdx != SXU32_HIGH ){
  4943. jx9_value *pObj;
  4944. if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
  4945. /* Force a numeric cast */
  4946. jx9MemObjToNumeric(pObj);
  4947. if( pObj->iFlags & MEMOBJ_REAL ){
  4948. pObj->x.rVal--;
  4949. /* Try to get an integer representation */
  4950. jx9MemObjTryInteger(pTos);
  4951. }else{
  4952. pObj->x.iVal--;
  4953. MemObjSetType(pTos, MEMOBJ_INT);
  4954. }
  4955. if( pInstr->iP1 ){
  4956. /* Pre-icrement */
  4957. jx9MemObjStore(pObj, pTos);
  4958. }
  4959. }
  4960. }else{
  4961. if( pInstr->iP1 ){
  4962. /* Pre-increment */
  4963. if( pTos->iFlags & MEMOBJ_REAL ){
  4964. pTos->x.rVal--;
  4965. /* Try to get an integer representation */
  4966. jx9MemObjTryInteger(pTos);
  4967. }else{
  4968. pTos->x.iVal--;
  4969. MemObjSetType(pTos, MEMOBJ_INT);
  4970. }
  4971. }
  4972. }
  4973. }
  4974. break;
  4975. /*
  4976. * UMINUS: * * *
  4977. *
  4978. * Perform a unary minus operation.
  4979. */
  4980. case JX9_OP_UMINUS:
  4981. #ifdef UNTRUST
  4982. if( pTos < pStack ){
  4983. goto Abort;
  4984. }
  4985. #endif
  4986. /* Force a numeric (integer, real or both) cast */
  4987. jx9MemObjToNumeric(pTos);
  4988. if( pTos->iFlags & MEMOBJ_REAL ){
  4989. pTos->x.rVal = -pTos->x.rVal;
  4990. }
  4991. if( pTos->iFlags & MEMOBJ_INT ){
  4992. pTos->x.iVal = -pTos->x.iVal;
  4993. }
  4994. break;
  4995. /*
  4996. * UPLUS: * * *
  4997. *
  4998. * Perform a unary plus operation.
  4999. */
  5000. case JX9_OP_UPLUS:
  5001. #ifdef UNTRUST
  5002. if( pTos < pStack ){
  5003. goto Abort;
  5004. }
  5005. #endif
  5006. /* Force a numeric (integer, real or both) cast */
  5007. jx9MemObjToNumeric(pTos);
  5008. if( pTos->iFlags & MEMOBJ_REAL ){
  5009. pTos->x.rVal = +pTos->x.rVal;
  5010. }
  5011. if( pTos->iFlags & MEMOBJ_INT ){
  5012. pTos->x.iVal = +pTos->x.iVal;
  5013. }
  5014. break;
  5015. /*
  5016. * OP_LNOT: * * *
  5017. *
  5018. * Interpret the top of the stack as a boolean value. Replace it
  5019. * with its complement.
  5020. */
  5021. case JX9_OP_LNOT:
  5022. #ifdef UNTRUST
  5023. if( pTos < pStack ){
  5024. goto Abort;
  5025. }
  5026. #endif
  5027. /* Force a boolean cast */
  5028. if( (pTos->iFlags & MEMOBJ_BOOL) == 0 ){
  5029. jx9MemObjToBool(pTos);
  5030. }
  5031. pTos->x.iVal = !pTos->x.iVal;
  5032. break;
  5033. /*
  5034. * OP_BITNOT: * * *
  5035. *
  5036. * Interpret the top of the stack as an value.Replace it
  5037. * with its ones-complement.
  5038. */
  5039. case JX9_OP_BITNOT:
  5040. #ifdef UNTRUST
  5041. if( pTos < pStack ){
  5042. goto Abort;
  5043. }
  5044. #endif
  5045. /* Force an integer cast */
  5046. if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
  5047. jx9MemObjToInteger(pTos);
  5048. }
  5049. pTos->x.iVal = ~pTos->x.iVal;
  5050. break;
  5051. /* OP_MUL * * *
  5052. * OP_MUL_STORE * * *
  5053. *
  5054. * Pop the top two elements from the stack, multiply them together,
  5055. * and push the result back onto the stack.
  5056. */
  5057. case JX9_OP_MUL:
  5058. case JX9_OP_MUL_STORE: {
  5059. jx9_value *pNos = &pTos[-1];
  5060. /* Force the operand to be numeric */
  5061. #ifdef UNTRUST
  5062. if( pNos < pStack ){
  5063. goto Abort;
  5064. }
  5065. #endif
  5066. jx9MemObjToNumeric(pTos);
  5067. jx9MemObjToNumeric(pNos);
  5068. /* Perform the requested operation */
  5069. if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){
  5070. /* Floating point arithemic */
  5071. jx9_real a, b, r;
  5072. if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
  5073. jx9MemObjToReal(pTos);
  5074. }
  5075. if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
  5076. jx9MemObjToReal(pNos);
  5077. }
  5078. a = pNos->x.rVal;
  5079. b = pTos->x.rVal;
  5080. r = a * b;
  5081. /* Push the result */
  5082. pNos->x.rVal = r;
  5083. MemObjSetType(pNos, MEMOBJ_REAL);
  5084. /* Try to get an integer representation */
  5085. jx9MemObjTryInteger(pNos);
  5086. }else{
  5087. /* Integer arithmetic */
  5088. sxi64 a, b, r;
  5089. a = pNos->x.iVal;
  5090. b = pTos->x.iVal;
  5091. r = a * b;
  5092. /* Push the result */
  5093. pNos->x.iVal = r;
  5094. MemObjSetType(pNos, MEMOBJ_INT);
  5095. }
  5096. if( pInstr->iOp == JX9_OP_MUL_STORE ){
  5097. jx9_value *pObj;
  5098. if( pTos->nIdx == SXU32_HIGH ){
  5099. jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
  5100. }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
  5101. jx9MemObjStore(pNos, pObj);
  5102. }
  5103. }
  5104. VmPopOperand(&pTos, 1);
  5105. break;
  5106. }
  5107. /* OP_ADD * * *
  5108. *
  5109. * Pop the top two elements from the stack, add them together,
  5110. * and push the result back onto the stack.
  5111. */
  5112. case JX9_OP_ADD:{
  5113. jx9_value *pNos = &pTos[-1];
  5114. #ifdef UNTRUST
  5115. if( pNos < pStack ){
  5116. goto Abort;
  5117. }
  5118. #endif
  5119. /* Perform the addition */
  5120. jx9MemObjAdd(pNos, pTos, FALSE);
  5121. VmPopOperand(&pTos, 1);
  5122. break;
  5123. }
  5124. /*
  5125. * OP_ADD_STORE * * *
  5126. *
  5127. * Pop the top two elements from the stack, add them together,
  5128. * and push the result back onto the stack.
  5129. */
  5130. case JX9_OP_ADD_STORE:{
  5131. jx9_value *pNos = &pTos[-1];
  5132. jx9_value *pObj;
  5133. sxu32 nIdx;
  5134. #ifdef UNTRUST
  5135. if( pNos < pStack ){
  5136. goto Abort;
  5137. }
  5138. #endif
  5139. /* Perform the addition */
  5140. nIdx = pTos->nIdx;
  5141. jx9MemObjAdd(pTos, pNos, TRUE);
  5142. /* Peform the store operation */
  5143. if( nIdx == SXU32_HIGH ){
  5144. jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
  5145. }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nIdx)) != 0 ){
  5146. jx9MemObjStore(pTos, pObj);
  5147. }
  5148. /* Ticket 1433-35: Perform a stack dup */
  5149. jx9MemObjStore(pTos, pNos);
  5150. VmPopOperand(&pTos, 1);
  5151. break;
  5152. }
  5153. /* OP_SUB * * *
  5154. *
  5155. * Pop the top two elements from the stack, subtract the
  5156. * first (what was next on the stack) from the second (the
  5157. * top of the stack) and push the result back onto the stack.
  5158. */
  5159. case JX9_OP_SUB: {
  5160. jx9_value *pNos = &pTos[-1];
  5161. #ifdef UNTRUST
  5162. if( pNos < pStack ){
  5163. goto Abort;
  5164. }
  5165. #endif
  5166. if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){
  5167. /* Floating point arithemic */
  5168. jx9_real a, b, r;
  5169. if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
  5170. jx9MemObjToReal(pTos);
  5171. }
  5172. if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
  5173. jx9MemObjToReal(pNos);
  5174. }
  5175. a = pNos->x.rVal;
  5176. b = pTos->x.rVal;
  5177. r = a - b;
  5178. /* Push the result */
  5179. pNos->x.rVal = r;
  5180. MemObjSetType(pNos, MEMOBJ_REAL);
  5181. /* Try to get an integer representation */
  5182. jx9MemObjTryInteger(pNos);
  5183. }else{
  5184. /* Integer arithmetic */
  5185. sxi64 a, b, r;
  5186. a = pNos->x.iVal;
  5187. b = pTos->x.iVal;
  5188. r = a - b;
  5189. /* Push the result */
  5190. pNos->x.iVal = r;
  5191. MemObjSetType(pNos, MEMOBJ_INT);
  5192. }
  5193. VmPopOperand(&pTos, 1);
  5194. break;
  5195. }
  5196. /* OP_SUB_STORE * * *
  5197. *
  5198. * Pop the top two elements from the stack, subtract the
  5199. * first (what was next on the stack) from the second (the
  5200. * top of the stack) and push the result back onto the stack.
  5201. */
  5202. case JX9_OP_SUB_STORE: {
  5203. jx9_value *pNos = &pTos[-1];
  5204. jx9_value *pObj;
  5205. #ifdef UNTRUST
  5206. if( pNos < pStack ){
  5207. goto Abort;
  5208. }
  5209. #endif
  5210. if( MEMOBJ_REAL & (pTos->iFlags|pNos->iFlags) ){
  5211. /* Floating point arithemic */
  5212. jx9_real a, b, r;
  5213. if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
  5214. jx9MemObjToReal(pTos);
  5215. }
  5216. if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
  5217. jx9MemObjToReal(pNos);
  5218. }
  5219. a = pTos->x.rVal;
  5220. b = pNos->x.rVal;
  5221. r = a - b;
  5222. /* Push the result */
  5223. pNos->x.rVal = r;
  5224. MemObjSetType(pNos, MEMOBJ_REAL);
  5225. /* Try to get an integer representation */
  5226. jx9MemObjTryInteger(pNos);
  5227. }else{
  5228. /* Integer arithmetic */
  5229. sxi64 a, b, r;
  5230. a = pTos->x.iVal;
  5231. b = pNos->x.iVal;
  5232. r = a - b;
  5233. /* Push the result */
  5234. pNos->x.iVal = r;
  5235. MemObjSetType(pNos, MEMOBJ_INT);
  5236. }
  5237. if( pTos->nIdx == SXU32_HIGH ){
  5238. jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
  5239. }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
  5240. jx9MemObjStore(pNos, pObj);
  5241. }
  5242. VmPopOperand(&pTos, 1);
  5243. break;
  5244. }
  5245. /*
  5246. * OP_MOD * * *
  5247. *
  5248. * Pop the top two elements from the stack, divide the
  5249. * first (what was next on the stack) from the second (the
  5250. * top of the stack) and push the remainder after division
  5251. * onto the stack.
  5252. * Note: Only integer arithemtic is allowed.
  5253. */
  5254. case JX9_OP_MOD:{
  5255. jx9_value *pNos = &pTos[-1];
  5256. sxi64 a, b, r;
  5257. #ifdef UNTRUST
  5258. if( pNos < pStack ){
  5259. goto Abort;
  5260. }
  5261. #endif
  5262. /* Force the operands to be integer */
  5263. if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
  5264. jx9MemObjToInteger(pTos);
  5265. }
  5266. if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
  5267. jx9MemObjToInteger(pNos);
  5268. }
  5269. /* Perform the requested operation */
  5270. a = pNos->x.iVal;
  5271. b = pTos->x.iVal;
  5272. if( b == 0 ){
  5273. r = 0;
  5274. VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Division by zero %qd%%0", a);
  5275. /* goto Abort; */
  5276. }else{
  5277. r = a%b;
  5278. }
  5279. /* Push the result */
  5280. pNos->x.iVal = r;
  5281. MemObjSetType(pNos, MEMOBJ_INT);
  5282. VmPopOperand(&pTos, 1);
  5283. break;
  5284. }
  5285. /*
  5286. * OP_MOD_STORE * * *
  5287. *
  5288. * Pop the top two elements from the stack, divide the
  5289. * first (what was next on the stack) from the second (the
  5290. * top of the stack) and push the remainder after division
  5291. * onto the stack.
  5292. * Note: Only integer arithemtic is allowed.
  5293. */
  5294. case JX9_OP_MOD_STORE: {
  5295. jx9_value *pNos = &pTos[-1];
  5296. jx9_value *pObj;
  5297. sxi64 a, b, r;
  5298. #ifdef UNTRUST
  5299. if( pNos < pStack ){
  5300. goto Abort;
  5301. }
  5302. #endif
  5303. /* Force the operands to be integer */
  5304. if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
  5305. jx9MemObjToInteger(pTos);
  5306. }
  5307. if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
  5308. jx9MemObjToInteger(pNos);
  5309. }
  5310. /* Perform the requested operation */
  5311. a = pTos->x.iVal;
  5312. b = pNos->x.iVal;
  5313. if( b == 0 ){
  5314. r = 0;
  5315. VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Division by zero %qd%%0", a);
  5316. /* goto Abort; */
  5317. }else{
  5318. r = a%b;
  5319. }
  5320. /* Push the result */
  5321. pNos->x.iVal = r;
  5322. MemObjSetType(pNos, MEMOBJ_INT);
  5323. if( pTos->nIdx == SXU32_HIGH ){
  5324. jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
  5325. }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
  5326. jx9MemObjStore(pNos, pObj);
  5327. }
  5328. VmPopOperand(&pTos, 1);
  5329. break;
  5330. }
  5331. /*
  5332. * OP_DIV * * *
  5333. *
  5334. * Pop the top two elements from the stack, divide the
  5335. * first (what was next on the stack) from the second (the
  5336. * top of the stack) and push the result onto the stack.
  5337. * Note: Only floating point arithemtic is allowed.
  5338. */
  5339. case JX9_OP_DIV:{
  5340. jx9_value *pNos = &pTos[-1];
  5341. jx9_real a, b, r;
  5342. #ifdef UNTRUST
  5343. if( pNos < pStack ){
  5344. goto Abort;
  5345. }
  5346. #endif
  5347. /* Force the operands to be real */
  5348. if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
  5349. jx9MemObjToReal(pTos);
  5350. }
  5351. if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
  5352. jx9MemObjToReal(pNos);
  5353. }
  5354. /* Perform the requested operation */
  5355. a = pNos->x.rVal;
  5356. b = pTos->x.rVal;
  5357. if( b == 0 ){
  5358. /* Division by zero */
  5359. r = 0;
  5360. jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Division by zero");
  5361. /* goto Abort; */
  5362. }else{
  5363. r = a/b;
  5364. /* Push the result */
  5365. pNos->x.rVal = r;
  5366. MemObjSetType(pNos, MEMOBJ_REAL);
  5367. /* Try to get an integer representation */
  5368. jx9MemObjTryInteger(pNos);
  5369. }
  5370. VmPopOperand(&pTos, 1);
  5371. break;
  5372. }
  5373. /*
  5374. * OP_DIV_STORE * * *
  5375. *
  5376. * Pop the top two elements from the stack, divide the
  5377. * first (what was next on the stack) from the second (the
  5378. * top of the stack) and push the result onto the stack.
  5379. * Note: Only floating point arithemtic is allowed.
  5380. */
  5381. case JX9_OP_DIV_STORE:{
  5382. jx9_value *pNos = &pTos[-1];
  5383. jx9_value *pObj;
  5384. jx9_real a, b, r;
  5385. #ifdef UNTRUST
  5386. if( pNos < pStack ){
  5387. goto Abort;
  5388. }
  5389. #endif
  5390. /* Force the operands to be real */
  5391. if( (pTos->iFlags & MEMOBJ_REAL) == 0 ){
  5392. jx9MemObjToReal(pTos);
  5393. }
  5394. if( (pNos->iFlags & MEMOBJ_REAL) == 0 ){
  5395. jx9MemObjToReal(pNos);
  5396. }
  5397. /* Perform the requested operation */
  5398. a = pTos->x.rVal;
  5399. b = pNos->x.rVal;
  5400. if( b == 0 ){
  5401. /* Division by zero */
  5402. r = 0;
  5403. VmErrorFormat(&(*pVm), JX9_CTX_ERR, "Division by zero %qd/0", a);
  5404. /* goto Abort; */
  5405. }else{
  5406. r = a/b;
  5407. /* Push the result */
  5408. pNos->x.rVal = r;
  5409. MemObjSetType(pNos, MEMOBJ_REAL);
  5410. /* Try to get an integer representation */
  5411. jx9MemObjTryInteger(pNos);
  5412. }
  5413. if( pTos->nIdx == SXU32_HIGH ){
  5414. jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
  5415. }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
  5416. jx9MemObjStore(pNos, pObj);
  5417. }
  5418. VmPopOperand(&pTos, 1);
  5419. break;
  5420. }
  5421. /* OP_BAND * * *
  5422. *
  5423. * Pop the top two elements from the stack. Convert both elements
  5424. * to integers. Push back onto the stack the bit-wise AND of the
  5425. * two elements.
  5426. */
  5427. /* OP_BOR * * *
  5428. *
  5429. * Pop the top two elements from the stack. Convert both elements
  5430. * to integers. Push back onto the stack the bit-wise OR of the
  5431. * two elements.
  5432. */
  5433. /* OP_BXOR * * *
  5434. *
  5435. * Pop the top two elements from the stack. Convert both elements
  5436. * to integers. Push back onto the stack the bit-wise XOR of the
  5437. * two elements.
  5438. */
  5439. case JX9_OP_BAND:
  5440. case JX9_OP_BOR:
  5441. case JX9_OP_BXOR:{
  5442. jx9_value *pNos = &pTos[-1];
  5443. sxi64 a, b, r;
  5444. #ifdef UNTRUST
  5445. if( pNos < pStack ){
  5446. goto Abort;
  5447. }
  5448. #endif
  5449. /* Force the operands to be integer */
  5450. if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
  5451. jx9MemObjToInteger(pTos);
  5452. }
  5453. if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
  5454. jx9MemObjToInteger(pNos);
  5455. }
  5456. /* Perform the requested operation */
  5457. a = pNos->x.iVal;
  5458. b = pTos->x.iVal;
  5459. switch(pInstr->iOp){
  5460. case JX9_OP_BOR_STORE:
  5461. case JX9_OP_BOR: r = a|b; break;
  5462. case JX9_OP_BXOR_STORE:
  5463. case JX9_OP_BXOR: r = a^b; break;
  5464. case JX9_OP_BAND_STORE:
  5465. case JX9_OP_BAND:
  5466. default: r = a&b; break;
  5467. }
  5468. /* Push the result */
  5469. pNos->x.iVal = r;
  5470. MemObjSetType(pNos, MEMOBJ_INT);
  5471. VmPopOperand(&pTos, 1);
  5472. break;
  5473. }
  5474. /* OP_BAND_STORE * * *
  5475. *
  5476. * Pop the top two elements from the stack. Convert both elements
  5477. * to integers. Push back onto the stack the bit-wise AND of the
  5478. * two elements.
  5479. */
  5480. /* OP_BOR_STORE * * *
  5481. *
  5482. * Pop the top two elements from the stack. Convert both elements
  5483. * to integers. Push back onto the stack the bit-wise OR of the
  5484. * two elements.
  5485. */
  5486. /* OP_BXOR_STORE * * *
  5487. *
  5488. * Pop the top two elements from the stack. Convert both elements
  5489. * to integers. Push back onto the stack the bit-wise XOR of the
  5490. * two elements.
  5491. */
  5492. case JX9_OP_BAND_STORE:
  5493. case JX9_OP_BOR_STORE:
  5494. case JX9_OP_BXOR_STORE:{
  5495. jx9_value *pNos = &pTos[-1];
  5496. jx9_value *pObj;
  5497. sxi64 a, b, r;
  5498. #ifdef UNTRUST
  5499. if( pNos < pStack ){
  5500. goto Abort;
  5501. }
  5502. #endif
  5503. /* Force the operands to be integer */
  5504. if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
  5505. jx9MemObjToInteger(pTos);
  5506. }
  5507. if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
  5508. jx9MemObjToInteger(pNos);
  5509. }
  5510. /* Perform the requested operation */
  5511. a = pTos->x.iVal;
  5512. b = pNos->x.iVal;
  5513. switch(pInstr->iOp){
  5514. case JX9_OP_BOR_STORE:
  5515. case JX9_OP_BOR: r = a|b; break;
  5516. case JX9_OP_BXOR_STORE:
  5517. case JX9_OP_BXOR: r = a^b; break;
  5518. case JX9_OP_BAND_STORE:
  5519. case JX9_OP_BAND:
  5520. default: r = a&b; break;
  5521. }
  5522. /* Push the result */
  5523. pNos->x.iVal = r;
  5524. MemObjSetType(pNos, MEMOBJ_INT);
  5525. if( pTos->nIdx == SXU32_HIGH ){
  5526. jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
  5527. }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
  5528. jx9MemObjStore(pNos, pObj);
  5529. }
  5530. VmPopOperand(&pTos, 1);
  5531. break;
  5532. }
  5533. /* OP_SHL * * *
  5534. *
  5535. * Pop the top two elements from the stack. Convert both elements
  5536. * to integers. Push back onto the stack the second element shifted
  5537. * left by N bits where N is the top element on the stack.
  5538. * Note: Only integer arithmetic is allowed.
  5539. */
  5540. /* OP_SHR * * *
  5541. *
  5542. * Pop the top two elements from the stack. Convert both elements
  5543. * to integers. Push back onto the stack the second element shifted
  5544. * right by N bits where N is the top element on the stack.
  5545. * Note: Only integer arithmetic is allowed.
  5546. */
  5547. case JX9_OP_SHL:
  5548. case JX9_OP_SHR: {
  5549. jx9_value *pNos = &pTos[-1];
  5550. sxi64 a, r;
  5551. sxi32 b;
  5552. #ifdef UNTRUST
  5553. if( pNos < pStack ){
  5554. goto Abort;
  5555. }
  5556. #endif
  5557. /* Force the operands to be integer */
  5558. if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
  5559. jx9MemObjToInteger(pTos);
  5560. }
  5561. if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
  5562. jx9MemObjToInteger(pNos);
  5563. }
  5564. /* Perform the requested operation */
  5565. a = pNos->x.iVal;
  5566. b = (sxi32)pTos->x.iVal;
  5567. if( pInstr->iOp == JX9_OP_SHL ){
  5568. r = a << b;
  5569. }else{
  5570. r = a >> b;
  5571. }
  5572. /* Push the result */
  5573. pNos->x.iVal = r;
  5574. MemObjSetType(pNos, MEMOBJ_INT);
  5575. VmPopOperand(&pTos, 1);
  5576. break;
  5577. }
  5578. /* OP_SHL_STORE * * *
  5579. *
  5580. * Pop the top two elements from the stack. Convert both elements
  5581. * to integers. Push back onto the stack the second element shifted
  5582. * left by N bits where N is the top element on the stack.
  5583. * Note: Only integer arithmetic is allowed.
  5584. */
  5585. /* OP_SHR_STORE * * *
  5586. *
  5587. * Pop the top two elements from the stack. Convert both elements
  5588. * to integers. Push back onto the stack the second element shifted
  5589. * right by N bits where N is the top element on the stack.
  5590. * Note: Only integer arithmetic is allowed.
  5591. */
  5592. case JX9_OP_SHL_STORE:
  5593. case JX9_OP_SHR_STORE: {
  5594. jx9_value *pNos = &pTos[-1];
  5595. jx9_value *pObj;
  5596. sxi64 a, r;
  5597. sxi32 b;
  5598. #ifdef UNTRUST
  5599. if( pNos < pStack ){
  5600. goto Abort;
  5601. }
  5602. #endif
  5603. /* Force the operands to be integer */
  5604. if( (pTos->iFlags & MEMOBJ_INT) == 0 ){
  5605. jx9MemObjToInteger(pTos);
  5606. }
  5607. if( (pNos->iFlags & MEMOBJ_INT) == 0 ){
  5608. jx9MemObjToInteger(pNos);
  5609. }
  5610. /* Perform the requested operation */
  5611. a = pTos->x.iVal;
  5612. b = (sxi32)pNos->x.iVal;
  5613. if( pInstr->iOp == JX9_OP_SHL_STORE ){
  5614. r = a << b;
  5615. }else{
  5616. r = a >> b;
  5617. }
  5618. /* Push the result */
  5619. pNos->x.iVal = r;
  5620. MemObjSetType(pNos, MEMOBJ_INT);
  5621. if( pTos->nIdx == SXU32_HIGH ){
  5622. jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
  5623. }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
  5624. jx9MemObjStore(pNos, pObj);
  5625. }
  5626. VmPopOperand(&pTos, 1);
  5627. break;
  5628. }
  5629. /* CAT: P1 * *
  5630. *
  5631. * Pop P1 elements from the stack. Concatenate them togeher and push the result
  5632. * back.
  5633. */
  5634. case JX9_OP_CAT:{
  5635. jx9_value *pNos, *pCur;
  5636. if( pInstr->iP1 < 1 ){
  5637. pNos = &pTos[-1];
  5638. }else{
  5639. pNos = &pTos[-pInstr->iP1+1];
  5640. }
  5641. #ifdef UNTRUST
  5642. if( pNos < pStack ){
  5643. goto Abort;
  5644. }
  5645. #endif
  5646. /* Force a string cast */
  5647. if( (pNos->iFlags & MEMOBJ_STRING) == 0 ){
  5648. jx9MemObjToString(pNos);
  5649. }
  5650. pCur = &pNos[1];
  5651. while( pCur <= pTos ){
  5652. if( (pCur->iFlags & MEMOBJ_STRING) == 0 ){
  5653. jx9MemObjToString(pCur);
  5654. }
  5655. /* Perform the concatenation */
  5656. if( SyBlobLength(&pCur->sBlob) > 0 ){
  5657. jx9MemObjStringAppend(pNos, (const char *)SyBlobData(&pCur->sBlob), SyBlobLength(&pCur->sBlob));
  5658. }
  5659. SyBlobRelease(&pCur->sBlob);
  5660. pCur++;
  5661. }
  5662. pTos = pNos;
  5663. break;
  5664. }
  5665. /* CAT_STORE: * * *
  5666. *
  5667. * Pop two elements from the stack. Concatenate them togeher and push the result
  5668. * back.
  5669. */
  5670. case JX9_OP_CAT_STORE:{
  5671. jx9_value *pNos = &pTos[-1];
  5672. jx9_value *pObj;
  5673. #ifdef UNTRUST
  5674. if( pNos < pStack ){
  5675. goto Abort;
  5676. }
  5677. #endif
  5678. if((pTos->iFlags & MEMOBJ_STRING) == 0 ){
  5679. /* Force a string cast */
  5680. jx9MemObjToString(pTos);
  5681. }
  5682. if((pNos->iFlags & MEMOBJ_STRING) == 0 ){
  5683. /* Force a string cast */
  5684. jx9MemObjToString(pNos);
  5685. }
  5686. /* Perform the concatenation (Reverse order) */
  5687. if( SyBlobLength(&pNos->sBlob) > 0 ){
  5688. jx9MemObjStringAppend(pTos, (const char *)SyBlobData(&pNos->sBlob), SyBlobLength(&pNos->sBlob));
  5689. }
  5690. /* Perform the store operation */
  5691. if( pTos->nIdx == SXU32_HIGH ){
  5692. jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "Cannot perform assignment on a constant object attribute");
  5693. }else if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0 ){
  5694. jx9MemObjStore(pTos, pObj);
  5695. }
  5696. jx9MemObjStore(pTos, pNos);
  5697. VmPopOperand(&pTos, 1);
  5698. break;
  5699. }
  5700. /* OP_AND: * * *
  5701. *
  5702. * Pop two values off the stack. Take the logical AND of the
  5703. * two values and push the resulting boolean value back onto the
  5704. * stack.
  5705. */
  5706. /* OP_OR: * * *
  5707. *
  5708. * Pop two values off the stack. Take the logical OR of the
  5709. * two values and push the resulting boolean value back onto the
  5710. * stack.
  5711. */
  5712. case JX9_OP_LAND:
  5713. case JX9_OP_LOR: {
  5714. jx9_value *pNos = &pTos[-1];
  5715. sxi32 v1, v2; /* 0==TRUE, 1==FALSE, 2==UNKNOWN or NULL */
  5716. #ifdef UNTRUST
  5717. if( pNos < pStack ){
  5718. goto Abort;
  5719. }
  5720. #endif
  5721. /* Force a boolean cast */
  5722. if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){
  5723. jx9MemObjToBool(pTos);
  5724. }
  5725. if((pNos->iFlags & MEMOBJ_BOOL) == 0 ){
  5726. jx9MemObjToBool(pNos);
  5727. }
  5728. v1 = pNos->x.iVal == 0 ? 1 : 0;
  5729. v2 = pTos->x.iVal == 0 ? 1 : 0;
  5730. if( pInstr->iOp == JX9_OP_LAND ){
  5731. static const unsigned char and_logic[] = { 0, 1, 2, 1, 1, 1, 2, 1, 2 };
  5732. v1 = and_logic[v1*3+v2];
  5733. }else{
  5734. static const unsigned char or_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 };
  5735. v1 = or_logic[v1*3+v2];
  5736. }
  5737. if( v1 == 2 ){
  5738. v1 = 1;
  5739. }
  5740. VmPopOperand(&pTos, 1);
  5741. pTos->x.iVal = v1 == 0 ? 1 : 0;
  5742. MemObjSetType(pTos, MEMOBJ_BOOL);
  5743. break;
  5744. }
  5745. /* OP_LXOR: * * *
  5746. *
  5747. * Pop two values off the stack. Take the logical XOR of the
  5748. * two values and push the resulting boolean value back onto the
  5749. * stack.
  5750. * According to the JX9 language reference manual:
  5751. * $a xor $b is evaluated to TRUE if either $a or $b is
  5752. * TRUE, but not both.
  5753. */
  5754. case JX9_OP_LXOR:{
  5755. jx9_value *pNos = &pTos[-1];
  5756. sxi32 v = 0;
  5757. #ifdef UNTRUST
  5758. if( pNos < pStack ){
  5759. goto Abort;
  5760. }
  5761. #endif
  5762. /* Force a boolean cast */
  5763. if((pTos->iFlags & MEMOBJ_BOOL) == 0 ){
  5764. jx9MemObjToBool(pTos);
  5765. }
  5766. if((pNos->iFlags & MEMOBJ_BOOL) == 0 ){
  5767. jx9MemObjToBool(pNos);
  5768. }
  5769. if( (pNos->x.iVal && !pTos->x.iVal) || (pTos->x.iVal && !pNos->x.iVal) ){
  5770. v = 1;
  5771. }
  5772. VmPopOperand(&pTos, 1);
  5773. pTos->x.iVal = v;
  5774. MemObjSetType(pTos, MEMOBJ_BOOL);
  5775. break;
  5776. }
  5777. /* OP_EQ P1 P2 P3
  5778. *
  5779. * Pop the top two elements from the stack. If they are equal, then
  5780. * jump to instruction P2. Otherwise, continue to the next instruction.
  5781. * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the
  5782. * stack if the jump would have been taken, or a 0 (FALSE) if not.
  5783. */
  5784. /* OP_NEQ P1 P2 P3
  5785. *
  5786. * Pop the top two elements from the stack. If they are not equal, then
  5787. * jump to instruction P2. Otherwise, continue to the next instruction.
  5788. * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the
  5789. * stack if the jump would have been taken, or a 0 (FALSE) if not.
  5790. */
  5791. case JX9_OP_EQ:
  5792. case JX9_OP_NEQ: {
  5793. jx9_value *pNos = &pTos[-1];
  5794. /* Perform the comparison and act accordingly */
  5795. #ifdef UNTRUST
  5796. if( pNos < pStack ){
  5797. goto Abort;
  5798. }
  5799. #endif
  5800. rc = jx9MemObjCmp(pNos, pTos, FALSE, 0);
  5801. if( pInstr->iOp == JX9_OP_EQ ){
  5802. rc = rc == 0;
  5803. }else{
  5804. rc = rc != 0;
  5805. }
  5806. VmPopOperand(&pTos, 1);
  5807. if( !pInstr->iP2 ){
  5808. /* Push comparison result without taking the jump */
  5809. jx9MemObjRelease(pTos);
  5810. pTos->x.iVal = rc;
  5811. /* Invalidate any prior representation */
  5812. MemObjSetType(pTos, MEMOBJ_BOOL);
  5813. }else{
  5814. if( rc ){
  5815. /* Jump to the desired location */
  5816. pc = pInstr->iP2 - 1;
  5817. VmPopOperand(&pTos, 1);
  5818. }
  5819. }
  5820. break;
  5821. }
  5822. /* OP_TEQ P1 P2 *
  5823. *
  5824. * Pop the top two elements from the stack. If they have the same type and are equal
  5825. * then jump to instruction P2. Otherwise, continue to the next instruction.
  5826. * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the
  5827. * stack if the jump would have been taken, or a 0 (FALSE) if not.
  5828. */
  5829. case JX9_OP_TEQ: {
  5830. jx9_value *pNos = &pTos[-1];
  5831. /* Perform the comparison and act accordingly */
  5832. #ifdef UNTRUST
  5833. if( pNos < pStack ){
  5834. goto Abort;
  5835. }
  5836. #endif
  5837. rc = jx9MemObjCmp(pNos, pTos, TRUE, 0) == 0;
  5838. VmPopOperand(&pTos, 1);
  5839. if( !pInstr->iP2 ){
  5840. /* Push comparison result without taking the jump */
  5841. jx9MemObjRelease(pTos);
  5842. pTos->x.iVal = rc;
  5843. /* Invalidate any prior representation */
  5844. MemObjSetType(pTos, MEMOBJ_BOOL);
  5845. }else{
  5846. if( rc ){
  5847. /* Jump to the desired location */
  5848. pc = pInstr->iP2 - 1;
  5849. VmPopOperand(&pTos, 1);
  5850. }
  5851. }
  5852. break;
  5853. }
  5854. /* OP_TNE P1 P2 *
  5855. *
  5856. * Pop the top two elements from the stack.If they are not equal an they are not
  5857. * of the same type, then jump to instruction P2. Otherwise, continue to the next
  5858. * instruction.
  5859. * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the
  5860. * stack if the jump would have been taken, or a 0 (FALSE) if not.
  5861. *
  5862. */
  5863. case JX9_OP_TNE: {
  5864. jx9_value *pNos = &pTos[-1];
  5865. /* Perform the comparison and act accordingly */
  5866. #ifdef UNTRUST
  5867. if( pNos < pStack ){
  5868. goto Abort;
  5869. }
  5870. #endif
  5871. rc = jx9MemObjCmp(pNos, pTos, TRUE, 0) != 0;
  5872. VmPopOperand(&pTos, 1);
  5873. if( !pInstr->iP2 ){
  5874. /* Push comparison result without taking the jump */
  5875. jx9MemObjRelease(pTos);
  5876. pTos->x.iVal = rc;
  5877. /* Invalidate any prior representation */
  5878. MemObjSetType(pTos, MEMOBJ_BOOL);
  5879. }else{
  5880. if( rc ){
  5881. /* Jump to the desired location */
  5882. pc = pInstr->iP2 - 1;
  5883. VmPopOperand(&pTos, 1);
  5884. }
  5885. }
  5886. break;
  5887. }
  5888. /* OP_LT P1 P2 P3
  5889. *
  5890. * Pop the top two elements from the stack. If the second element (the top of stack)
  5891. * is less than the first (next on stack), then jump to instruction P2.Otherwise
  5892. * continue to the next instruction. In other words, jump if pNos<pTos.
  5893. * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the
  5894. * stack if the jump would have been taken, or a 0 (FALSE) if not.
  5895. *
  5896. */
  5897. /* OP_LE P1 P2 P3
  5898. *
  5899. * Pop the top two elements from the stack. If the second element (the top of stack)
  5900. * is less than or equal to the first (next on stack), then jump to instruction P2.
  5901. * Otherwise continue to the next instruction. In other words, jump if pNos<pTos.
  5902. * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the
  5903. * stack if the jump would have been taken, or a 0 (FALSE) if not.
  5904. *
  5905. */
  5906. case JX9_OP_LT:
  5907. case JX9_OP_LE: {
  5908. jx9_value *pNos = &pTos[-1];
  5909. /* Perform the comparison and act accordingly */
  5910. #ifdef UNTRUST
  5911. if( pNos < pStack ){
  5912. goto Abort;
  5913. }
  5914. #endif
  5915. rc = jx9MemObjCmp(pNos, pTos, FALSE, 0);
  5916. if( pInstr->iOp == JX9_OP_LE ){
  5917. rc = rc < 1;
  5918. }else{
  5919. rc = rc < 0;
  5920. }
  5921. VmPopOperand(&pTos, 1);
  5922. if( !pInstr->iP2 ){
  5923. /* Push comparison result without taking the jump */
  5924. jx9MemObjRelease(pTos);
  5925. pTos->x.iVal = rc;
  5926. /* Invalidate any prior representation */
  5927. MemObjSetType(pTos, MEMOBJ_BOOL);
  5928. }else{
  5929. if( rc ){
  5930. /* Jump to the desired location */
  5931. pc = pInstr->iP2 - 1;
  5932. VmPopOperand(&pTos, 1);
  5933. }
  5934. }
  5935. break;
  5936. }
  5937. /* OP_GT P1 P2 P3
  5938. *
  5939. * Pop the top two elements from the stack. If the second element (the top of stack)
  5940. * is greater than the first (next on stack), then jump to instruction P2.Otherwise
  5941. * continue to the next instruction. In other words, jump if pNos<pTos.
  5942. * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the
  5943. * stack if the jump would have been taken, or a 0 (FALSE) if not.
  5944. *
  5945. */
  5946. /* OP_GE P1 P2 P3
  5947. *
  5948. * Pop the top two elements from the stack. If the second element (the top of stack)
  5949. * is greater than or equal to the first (next on stack), then jump to instruction P2.
  5950. * Otherwise continue to the next instruction. In other words, jump if pNos<pTos.
  5951. * If P2 is zero, do not jump.Instead, push a boolean 1 (TRUE) onto the
  5952. * stack if the jump would have been taken, or a 0 (FALSE) if not.
  5953. *
  5954. */
  5955. case JX9_OP_GT:
  5956. case JX9_OP_GE: {
  5957. jx9_value *pNos = &pTos[-1];
  5958. /* Perform the comparison and act accordingly */
  5959. #ifdef UNTRUST
  5960. if( pNos < pStack ){
  5961. goto Abort;
  5962. }
  5963. #endif
  5964. rc = jx9MemObjCmp(pNos, pTos, FALSE, 0);
  5965. if( pInstr->iOp == JX9_OP_GE ){
  5966. rc = rc >= 0;
  5967. }else{
  5968. rc = rc > 0;
  5969. }
  5970. VmPopOperand(&pTos, 1);
  5971. if( !pInstr->iP2 ){
  5972. /* Push comparison result without taking the jump */
  5973. jx9MemObjRelease(pTos);
  5974. pTos->x.iVal = rc;
  5975. /* Invalidate any prior representation */
  5976. MemObjSetType(pTos, MEMOBJ_BOOL);
  5977. }else{
  5978. if( rc ){
  5979. /* Jump to the desired location */
  5980. pc = pInstr->iP2 - 1;
  5981. VmPopOperand(&pTos, 1);
  5982. }
  5983. }
  5984. break;
  5985. }
  5986. /*
  5987. * OP_FOREACH_INIT * P2 P3
  5988. * Prepare a foreach step.
  5989. */
  5990. case JX9_OP_FOREACH_INIT: {
  5991. jx9_foreach_info *pInfo = (jx9_foreach_info *)pInstr->p3;
  5992. void *pName;
  5993. #ifdef UNTRUST
  5994. if( pTos < pStack ){
  5995. goto Abort;
  5996. }
  5997. #endif
  5998. if( SyStringLength(&pInfo->sValue) < 1 ){
  5999. /* Take the variable name from the top of the stack */
  6000. if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
  6001. /* Force a string cast */
  6002. jx9MemObjToString(pTos);
  6003. }
  6004. /* Duplicate name */
  6005. if( SyBlobLength(&pTos->sBlob) > 0 ){
  6006. pName = SyMemBackendDup(&pVm->sAllocator, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
  6007. SyStringInitFromBuf(&pInfo->sValue, pName, SyBlobLength(&pTos->sBlob));
  6008. }
  6009. VmPopOperand(&pTos, 1);
  6010. }
  6011. if( (pInfo->iFlags & JX9_4EACH_STEP_KEY) && SyStringLength(&pInfo->sKey) < 1 ){
  6012. if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
  6013. /* Force a string cast */
  6014. jx9MemObjToString(pTos);
  6015. }
  6016. /* Duplicate name */
  6017. if( SyBlobLength(&pTos->sBlob) > 0 ){
  6018. pName = SyMemBackendDup(&pVm->sAllocator, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
  6019. SyStringInitFromBuf(&pInfo->sKey, pName, SyBlobLength(&pTos->sBlob));
  6020. }
  6021. VmPopOperand(&pTos, 1);
  6022. }
  6023. /* Make sure we are dealing with a hashmap [i.e. JSON array or object ]*/
  6024. if( (pTos->iFlags & (MEMOBJ_HASHMAP)) == 0 || SyStringLength(&pInfo->sValue) < 1 ){
  6025. /* Jump out of the loop */
  6026. if( (pTos->iFlags & MEMOBJ_NULL) == 0 ){
  6027. jx9VmThrowError(&(*pVm), 0, JX9_CTX_WARNING,
  6028. "Invalid argument supplied for the foreach statement, expecting JSON array or object instance");
  6029. }
  6030. pc = pInstr->iP2 - 1;
  6031. }else{
  6032. jx9_foreach_step *pStep;
  6033. pStep = (jx9_foreach_step *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_foreach_step));
  6034. if( pStep == 0 ){
  6035. jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "JX9 is running out of memory while preparing the 'foreach' step");
  6036. /* Jump out of the loop */
  6037. pc = pInstr->iP2 - 1;
  6038. }else{
  6039. /* Zero the structure */
  6040. SyZero(pStep, sizeof(jx9_foreach_step));
  6041. /* Prepare the step */
  6042. pStep->iFlags = pInfo->iFlags;
  6043. if( pTos->iFlags & MEMOBJ_HASHMAP ){
  6044. jx9_hashmap *pMap = (jx9_hashmap *)pTos->x.pOther;
  6045. /* Reset the internal loop cursor */
  6046. jx9HashmapResetLoopCursor(pMap);
  6047. /* Mark the step */
  6048. pStep->pMap = pMap;
  6049. pMap->iRef++;
  6050. }
  6051. }
  6052. if( SXRET_OK != SySetPut(&pInfo->aStep, (const void *)&pStep) ){
  6053. jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR, "JX9 is running out of memory while preparing the 'foreach' step");
  6054. SyMemBackendPoolFree(&pVm->sAllocator, pStep);
  6055. /* Jump out of the loop */
  6056. pc = pInstr->iP2 - 1;
  6057. }
  6058. }
  6059. VmPopOperand(&pTos, 1);
  6060. break;
  6061. }
  6062. /*
  6063. * OP_FOREACH_STEP * P2 P3
  6064. * Perform a foreach step. Jump to P2 at the end of the step.
  6065. */
  6066. case JX9_OP_FOREACH_STEP: {
  6067. jx9_foreach_info *pInfo = (jx9_foreach_info *)pInstr->p3;
  6068. jx9_foreach_step **apStep, *pStep;
  6069. jx9_hashmap_node *pNode;
  6070. jx9_hashmap *pMap;
  6071. jx9_value *pValue;
  6072. /* Peek the last step */
  6073. apStep = (jx9_foreach_step **)SySetBasePtr(&pInfo->aStep);
  6074. pStep = apStep[SySetUsed(&pInfo->aStep) - 1];
  6075. pMap = pStep->pMap;
  6076. /* Extract the current node value */
  6077. pNode = jx9HashmapGetNextEntry(pMap);
  6078. if( pNode == 0 ){
  6079. /* No more entry to process */
  6080. pc = pInstr->iP2 - 1; /* Jump to this destination */
  6081. /* Automatically reset the loop cursor */
  6082. jx9HashmapResetLoopCursor(pMap);
  6083. /* Cleanup the mess left behind */
  6084. SyMemBackendPoolFree(&pVm->sAllocator, pStep);
  6085. SySetPop(&pInfo->aStep);
  6086. jx9HashmapUnref(pMap);
  6087. }else{
  6088. if( (pStep->iFlags & JX9_4EACH_STEP_KEY) && SyStringLength(&pInfo->sKey) > 0 ){
  6089. jx9_value *pKey = VmExtractMemObj(&(*pVm), &pInfo->sKey, FALSE, TRUE);
  6090. if( pKey ){
  6091. jx9HashmapExtractNodeKey(pNode, pKey);
  6092. }
  6093. }
  6094. /* Make a copy of the entry value */
  6095. pValue = VmExtractMemObj(&(*pVm), &pInfo->sValue, FALSE, TRUE);
  6096. if( pValue ){
  6097. jx9HashmapExtractNodeValue(pNode, pValue, TRUE);
  6098. }
  6099. }
  6100. break;
  6101. }
  6102. /*
  6103. * OP_MEMBER P1 P2
  6104. * Load JSON object entry on the stack.
  6105. */
  6106. case JX9_OP_MEMBER: {
  6107. jx9_hashmap_node *pNode = 0; /* cc warning */
  6108. jx9_hashmap *pMap = 0;
  6109. jx9_value *pIdx;
  6110. pIdx = pTos;
  6111. pTos--;
  6112. rc = SXERR_NOTFOUND; /* Assume the index is invalid */
  6113. if( pTos->iFlags & MEMOBJ_HASHMAP ){
  6114. /* Point to the hashmap */
  6115. pMap = (jx9_hashmap *)pTos->x.pOther;
  6116. /* Load the desired entry */
  6117. rc = jx9HashmapLookup(pMap, pIdx, &pNode);
  6118. }
  6119. jx9MemObjRelease(pIdx);
  6120. if( rc == SXRET_OK ){
  6121. /* Load entry contents */
  6122. if( pMap->iRef < 2 ){
  6123. /* TICKET 1433-42: Array will be deleted shortly, so we will make a copy
  6124. * of the entry value, rather than pointing to it.
  6125. */
  6126. pTos->nIdx = SXU32_HIGH;
  6127. jx9HashmapExtractNodeValue(pNode, pTos, TRUE);
  6128. }else{
  6129. pTos->nIdx = pNode->nValIdx;
  6130. jx9HashmapExtractNodeValue(pNode, pTos, FALSE);
  6131. jx9HashmapUnref(pMap);
  6132. }
  6133. }else{
  6134. /* No such entry, load NULL */
  6135. jx9MemObjRelease(pTos);
  6136. pTos->nIdx = SXU32_HIGH;
  6137. }
  6138. break;
  6139. }
  6140. /*
  6141. * OP_SWITCH * * P3
  6142. * This is the bytecode implementation of the complex switch() JX9 construct.
  6143. */
  6144. case JX9_OP_SWITCH: {
  6145. jx9_switch *pSwitch = (jx9_switch *)pInstr->p3;
  6146. jx9_case_expr *aCase, *pCase;
  6147. jx9_value sValue, sCaseValue;
  6148. sxu32 n, nEntry;
  6149. #ifdef UNTRUST
  6150. if( pSwitch == 0 || pTos < pStack ){
  6151. goto Abort;
  6152. }
  6153. #endif
  6154. /* Point to the case table */
  6155. aCase = (jx9_case_expr *)SySetBasePtr(&pSwitch->aCaseExpr);
  6156. nEntry = SySetUsed(&pSwitch->aCaseExpr);
  6157. /* Select the appropriate case block to execute */
  6158. jx9MemObjInit(pVm, &sValue);
  6159. jx9MemObjInit(pVm, &sCaseValue);
  6160. for( n = 0 ; n < nEntry ; ++n ){
  6161. pCase = &aCase[n];
  6162. jx9MemObjLoad(pTos, &sValue);
  6163. /* Execute the case expression first */
  6164. VmLocalExec(pVm,&pCase->aByteCode, &sCaseValue);
  6165. /* Compare the two expression */
  6166. rc = jx9MemObjCmp(&sValue, &sCaseValue, FALSE, 0);
  6167. jx9MemObjRelease(&sValue);
  6168. jx9MemObjRelease(&sCaseValue);
  6169. if( rc == 0 ){
  6170. /* Value match, jump to this block */
  6171. pc = pCase->nStart - 1;
  6172. break;
  6173. }
  6174. }
  6175. VmPopOperand(&pTos, 1);
  6176. if( n >= nEntry ){
  6177. /* No approprite case to execute, jump to the default case */
  6178. if( pSwitch->nDefault > 0 ){
  6179. pc = pSwitch->nDefault - 1;
  6180. }else{
  6181. /* No default case, jump out of this switch */
  6182. pc = pSwitch->nOut - 1;
  6183. }
  6184. }
  6185. break;
  6186. }
  6187. /*
  6188. * OP_UPLINK P1 * *
  6189. * Link a variable to the top active VM frame.
  6190. * This is used to implement the 'uplink' JX9 construct.
  6191. */
  6192. case JX9_OP_UPLINK: {
  6193. if( pVm->pFrame->pParent ){
  6194. jx9_value *pLink = &pTos[-pInstr->iP1+1];
  6195. SyString sName;
  6196. /* Perform the link */
  6197. while( pLink <= pTos ){
  6198. if((pLink->iFlags & MEMOBJ_STRING) == 0 ){
  6199. /* Force a string cast */
  6200. jx9MemObjToString(pLink);
  6201. }
  6202. SyStringInitFromBuf(&sName, SyBlobData(&pLink->sBlob), SyBlobLength(&pLink->sBlob));
  6203. if( sName.nByte > 0 ){
  6204. VmFrameLink(&(*pVm), &sName);
  6205. }
  6206. pLink++;
  6207. }
  6208. }
  6209. VmPopOperand(&pTos, pInstr->iP1);
  6210. break;
  6211. }
  6212. /*
  6213. * OP_CALL P1 * *
  6214. * Call a JX9 or a foreign function and push the return value of the called
  6215. * function on the stack.
  6216. */
  6217. case JX9_OP_CALL: {
  6218. jx9_value *pArg = &pTos[-pInstr->iP1];
  6219. SyHashEntry *pEntry;
  6220. SyString sName;
  6221. /* Extract function name */
  6222. if( (pTos->iFlags & MEMOBJ_STRING) == 0 ){
  6223. /* Raise exception: Invalid function name */
  6224. VmErrorFormat(&(*pVm), JX9_CTX_WARNING, "Invalid function name, JX9 is returning NULL.");
  6225. /* Pop given arguments */
  6226. if( pInstr->iP1 > 0 ){
  6227. VmPopOperand(&pTos, pInstr->iP1);
  6228. }
  6229. /* Assume a null return value so that the program continue it's execution normally */
  6230. jx9MemObjRelease(pTos);
  6231. break;
  6232. }
  6233. SyStringInitFromBuf(&sName, SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob));
  6234. /* Check for a compiled function first */
  6235. pEntry = SyHashGet(&pVm->hFunction, (const void *)sName.zString, sName.nByte);
  6236. if( pEntry ){
  6237. jx9_vm_func_arg *aFormalArg;
  6238. jx9_value *pFrameStack;
  6239. jx9_vm_func *pVmFunc;
  6240. VmFrame *pFrame;
  6241. jx9_value *pObj;
  6242. VmSlot sArg;
  6243. sxu32 n;
  6244. pVmFunc = (jx9_vm_func *)pEntry->pUserData;
  6245. /* Check The recursion limit */
  6246. if( pVm->nRecursionDepth > pVm->nMaxDepth ){
  6247. VmErrorFormat(&(*pVm), JX9_CTX_ERR,
  6248. "Recursion limit reached while invoking user function '%z', JX9 will set a NULL return value",
  6249. &pVmFunc->sName);
  6250. /* Pop given arguments */
  6251. if( pInstr->iP1 > 0 ){
  6252. VmPopOperand(&pTos, pInstr->iP1);
  6253. }
  6254. /* Assume a null return value so that the program continue it's execution normally */
  6255. jx9MemObjRelease(pTos);
  6256. break;
  6257. }
  6258. if( pVmFunc->pNextName ){
  6259. /* Function is candidate for overloading, select the appropriate function to call */
  6260. pVmFunc = VmOverload(&(*pVm), pVmFunc, pArg, (int)(pTos-pArg));
  6261. }
  6262. /* Extract the formal argument set */
  6263. aFormalArg = (jx9_vm_func_arg *)SySetBasePtr(&pVmFunc->aArgs);
  6264. /* Create a new VM frame */
  6265. rc = VmEnterFrame(&(*pVm),pVmFunc,&pFrame);
  6266. if( rc != SXRET_OK ){
  6267. /* Raise exception: Out of memory */
  6268. VmErrorFormat(&(*pVm), JX9_CTX_ERR,
  6269. "JX9 is running out of memory while calling function '%z', JX9 is returning NULL.",
  6270. &pVmFunc->sName);
  6271. /* Pop given arguments */
  6272. if( pInstr->iP1 > 0 ){
  6273. VmPopOperand(&pTos, pInstr->iP1);
  6274. }
  6275. /* Assume a null return value so that the program continue it's execution normally */
  6276. jx9MemObjRelease(pTos);
  6277. break;
  6278. }
  6279. if( SySetUsed(&pVmFunc->aStatic) > 0 ){
  6280. jx9_vm_func_static_var *pStatic, *aStatic;
  6281. /* Install static variables */
  6282. aStatic = (jx9_vm_func_static_var *)SySetBasePtr(&pVmFunc->aStatic);
  6283. for( n = 0 ; n < SySetUsed(&pVmFunc->aStatic) ; ++n ){
  6284. pStatic = &aStatic[n];
  6285. if( pStatic->nIdx == SXU32_HIGH ){
  6286. /* Initialize the static variables */
  6287. pObj = VmReserveMemObj(&(*pVm), &pStatic->nIdx);
  6288. if( pObj ){
  6289. /* Assume a NULL initialization value */
  6290. jx9MemObjInit(&(*pVm), pObj);
  6291. if( SySetUsed(&pStatic->aByteCode) > 0 ){
  6292. /* Evaluate initialization expression (Any complex expression) */
  6293. VmLocalExec(&(*pVm), &pStatic->aByteCode, pObj);
  6294. }
  6295. pObj->nIdx = pStatic->nIdx;
  6296. }else{
  6297. continue;
  6298. }
  6299. }
  6300. /* Install in the current frame */
  6301. SyHashInsert(&pFrame->hVar, SyStringData(&pStatic->sName), SyStringLength(&pStatic->sName),
  6302. SX_INT_TO_PTR(pStatic->nIdx));
  6303. }
  6304. }
  6305. /* Push arguments in the local frame */
  6306. n = 0;
  6307. while( pArg < pTos ){
  6308. if( n < SySetUsed(&pVmFunc->aArgs) ){
  6309. if( (pArg->iFlags & MEMOBJ_NULL) && SySetUsed(&aFormalArg[n].aByteCode) > 0 ){
  6310. /* NULL values are redirected to default arguments */
  6311. rc = VmLocalExec(&(*pVm), &aFormalArg[n].aByteCode, pArg);
  6312. if( rc == JX9_ABORT ){
  6313. goto Abort;
  6314. }
  6315. }
  6316. /* Make sure the given arguments are of the correct type */
  6317. if( aFormalArg[n].nType > 0 ){
  6318. if( ((pArg->iFlags & aFormalArg[n].nType) == 0) ){
  6319. ProcMemObjCast xCast = jx9MemObjCastMethod(aFormalArg[n].nType);
  6320. /* Cast to the desired type */
  6321. if( xCast ){
  6322. xCast(pArg);
  6323. }
  6324. }
  6325. }
  6326. /* Pass by value, make a copy of the given argument */
  6327. pObj = VmExtractMemObj(&(*pVm), &aFormalArg[n].sName, FALSE, TRUE);
  6328. }else{
  6329. char zName[32];
  6330. SyString sName;
  6331. /* Set a dummy name */
  6332. sName.nByte = SyBufferFormat(zName, sizeof(zName), "[%u]apArg", n);
  6333. sName.zString = zName;
  6334. /* Annonymous argument */
  6335. pObj = VmExtractMemObj(&(*pVm), &sName, TRUE, TRUE);
  6336. }
  6337. if( pObj ){
  6338. jx9MemObjStore(pArg, pObj);
  6339. /* Insert argument index */
  6340. sArg.nIdx = pObj->nIdx;
  6341. sArg.pUserData = 0;
  6342. SySetPut(&pFrame->sArg, (const void *)&sArg);
  6343. }
  6344. jx9MemObjRelease(pArg);
  6345. pArg++;
  6346. ++n;
  6347. }
  6348. /* Process default values */
  6349. while( n < SySetUsed(&pVmFunc->aArgs) ){
  6350. if( SySetUsed(&aFormalArg[n].aByteCode) > 0 ){
  6351. pObj = VmExtractMemObj(&(*pVm), &aFormalArg[n].sName, FALSE, TRUE);
  6352. if( pObj ){
  6353. /* Evaluate the default value and extract it's result */
  6354. rc = VmLocalExec(&(*pVm), &aFormalArg[n].aByteCode, pObj);
  6355. if( rc == JX9_ABORT ){
  6356. goto Abort;
  6357. }
  6358. /* Insert argument index */
  6359. sArg.nIdx = pObj->nIdx;
  6360. sArg.pUserData = 0;
  6361. SySetPut(&pFrame->sArg, (const void *)&sArg);
  6362. /* Make sure the default argument is of the correct type */
  6363. if( aFormalArg[n].nType > 0 && ((pObj->iFlags & aFormalArg[n].nType) == 0) ){
  6364. ProcMemObjCast xCast = jx9MemObjCastMethod(aFormalArg[n].nType);
  6365. /* Cast to the desired type */
  6366. xCast(pObj);
  6367. }
  6368. }
  6369. }
  6370. ++n;
  6371. }
  6372. /* Pop arguments, function name from the operand stack and assume the function
  6373. * does not return anything.
  6374. */
  6375. jx9MemObjRelease(pTos);
  6376. pTos = &pTos[-pInstr->iP1];
  6377. /* Allocate a new operand stack and evaluate the function body */
  6378. pFrameStack = VmNewOperandStack(&(*pVm), SySetUsed(&pVmFunc->aByteCode));
  6379. if( pFrameStack == 0 ){
  6380. /* Raise exception: Out of memory */
  6381. VmErrorFormat(&(*pVm), JX9_CTX_ERR, "JX9 is running out of memory while calling function '%z', JX9 is returning NULL.",
  6382. &pVmFunc->sName);
  6383. if( pInstr->iP1 > 0 ){
  6384. VmPopOperand(&pTos, pInstr->iP1);
  6385. }
  6386. break;
  6387. }
  6388. /* Increment nesting level */
  6389. pVm->nRecursionDepth++;
  6390. /* Execute function body */
  6391. rc = VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(&pVmFunc->aByteCode), pFrameStack, -1, pTos);
  6392. /* Decrement nesting level */
  6393. pVm->nRecursionDepth--;
  6394. /* Free the operand stack */
  6395. SyMemBackendFree(&pVm->sAllocator, pFrameStack);
  6396. /* Leave the frame */
  6397. VmLeaveFrame(&(*pVm));
  6398. if( rc == JX9_ABORT ){
  6399. /* Abort processing immeditaley */
  6400. goto Abort;
  6401. }
  6402. }else{
  6403. jx9_user_func *pFunc;
  6404. jx9_context sCtx;
  6405. jx9_value sRet;
  6406. /* Look for an installed foreign function */
  6407. pEntry = SyHashGet(&pVm->hHostFunction, (const void *)sName.zString, sName.nByte);
  6408. if( pEntry == 0 ){
  6409. /* Call to undefined function */
  6410. VmErrorFormat(&(*pVm), JX9_CTX_WARNING, "Call to undefined function '%z', JX9 is returning NULL.", &sName);
  6411. /* Pop given arguments */
  6412. if( pInstr->iP1 > 0 ){
  6413. VmPopOperand(&pTos, pInstr->iP1);
  6414. }
  6415. /* Assume a null return value so that the program continue it's execution normally */
  6416. jx9MemObjRelease(pTos);
  6417. break;
  6418. }
  6419. pFunc = (jx9_user_func *)pEntry->pUserData;
  6420. /* Start collecting function arguments */
  6421. SySetReset(&aArg);
  6422. while( pArg < pTos ){
  6423. SySetPut(&aArg, (const void *)&pArg);
  6424. pArg++;
  6425. }
  6426. /* Assume a null return value */
  6427. jx9MemObjInit(&(*pVm), &sRet);
  6428. /* Init the call context */
  6429. VmInitCallContext(&sCtx, &(*pVm), pFunc, &sRet, 0);
  6430. /* Call the foreign function */
  6431. rc = pFunc->xFunc(&sCtx, (int)SySetUsed(&aArg), (jx9_value **)SySetBasePtr(&aArg));
  6432. /* Release the call context */
  6433. VmReleaseCallContext(&sCtx);
  6434. if( rc == JX9_ABORT ){
  6435. goto Abort;
  6436. }
  6437. if( pInstr->iP1 > 0 ){
  6438. /* Pop function name and arguments */
  6439. VmPopOperand(&pTos, pInstr->iP1);
  6440. }
  6441. /* Save foreign function return value */
  6442. jx9MemObjStore(&sRet, pTos);
  6443. jx9MemObjRelease(&sRet);
  6444. }
  6445. break;
  6446. }
  6447. /*
  6448. * OP_CONSUME: P1 * *
  6449. * Consume (Invoke the installed VM output consumer callback) and POP P1 elements from the stack.
  6450. */
  6451. case JX9_OP_CONSUME: {
  6452. jx9_output_consumer *pCons = &pVm->sVmConsumer;
  6453. jx9_value *pCur, *pOut = pTos;
  6454. pOut = &pTos[-pInstr->iP1 + 1];
  6455. pCur = pOut;
  6456. /* Start the consume process */
  6457. while( pOut <= pTos ){
  6458. /* Force a string cast */
  6459. if( (pOut->iFlags & MEMOBJ_STRING) == 0 ){
  6460. jx9MemObjToString(pOut);
  6461. }
  6462. if( SyBlobLength(&pOut->sBlob) > 0 ){
  6463. /*SyBlobNullAppend(&pOut->sBlob);*/
  6464. /* Invoke the output consumer callback */
  6465. rc = pCons->xConsumer(SyBlobData(&pOut->sBlob), SyBlobLength(&pOut->sBlob), pCons->pUserData);
  6466. /* Increment output length */
  6467. pVm->nOutputLen += SyBlobLength(&pOut->sBlob);
  6468. SyBlobRelease(&pOut->sBlob);
  6469. if( rc == SXERR_ABORT ){
  6470. /* Output consumer callback request an operation abort. */
  6471. goto Abort;
  6472. }
  6473. }
  6474. pOut++;
  6475. }
  6476. pTos = &pCur[-1];
  6477. break;
  6478. }
  6479. } /* Switch() */
  6480. pc++; /* Next instruction in the stream */
  6481. } /* For(;;) */
  6482. Done:
  6483. SySetRelease(&aArg);
  6484. return SXRET_OK;
  6485. Abort:
  6486. SySetRelease(&aArg);
  6487. while( pTos >= pStack ){
  6488. jx9MemObjRelease(pTos);
  6489. pTos--;
  6490. }
  6491. return JX9_ABORT;
  6492. }
  6493. /*
  6494. * Execute as much of a local JX9 bytecode program as we can then return.
  6495. * This function is a wrapper around [VmByteCodeExec()].
  6496. * See block-comment on that function for additional information.
  6497. */
  6498. static sxi32 VmLocalExec(jx9_vm *pVm, SySet *pByteCode,jx9_value *pResult)
  6499. {
  6500. jx9_value *pStack;
  6501. sxi32 rc;
  6502. /* Allocate a new operand stack */
  6503. pStack = VmNewOperandStack(&(*pVm), SySetUsed(pByteCode));
  6504. if( pStack == 0 ){
  6505. return SXERR_MEM;
  6506. }
  6507. /* Execute the program */
  6508. rc = VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(pByteCode), pStack, -1, &(*pResult));
  6509. /* Free the operand stack */
  6510. SyMemBackendFree(&pVm->sAllocator, pStack);
  6511. /* Execution result */
  6512. return rc;
  6513. }
  6514. /*
  6515. * Execute as much of a JX9 bytecode program as we can then return.
  6516. * This function is a wrapper around [VmByteCodeExec()].
  6517. * See block-comment on that function for additional information.
  6518. */
  6519. JX9_PRIVATE sxi32 jx9VmByteCodeExec(jx9_vm *pVm)
  6520. {
  6521. /* Make sure we are ready to execute this program */
  6522. if( pVm->nMagic != JX9_VM_RUN ){
  6523. return pVm->nMagic == JX9_VM_EXEC ? SXERR_LOCKED /* Locked VM */ : SXERR_CORRUPT; /* Stale VM */
  6524. }
  6525. /* Set the execution magic number */
  6526. pVm->nMagic = JX9_VM_EXEC;
  6527. /* Execute the program */
  6528. VmByteCodeExec(&(*pVm), (VmInstr *)SySetBasePtr(pVm->pByteContainer), pVm->aOps, -1, &pVm->sExec);
  6529. /*
  6530. * TICKET 1433-100: Do not remove the JX9_VM_EXEC magic number
  6531. * so that any following call to [jx9_vm_exec()] without calling
  6532. * [jx9_vm_reset()] first would fail.
  6533. */
  6534. return SXRET_OK;
  6535. }
  6536. /*
  6537. * Extract a memory object (i.e. a variable) from the running script.
  6538. * This function must be called after calling jx9_vm_exec(). Otherwise
  6539. * NULL is returned.
  6540. */
  6541. JX9_PRIVATE jx9_value * jx9VmExtractVariable(jx9_vm *pVm,SyString *pVar)
  6542. {
  6543. jx9_value *pValue;
  6544. if( pVm->nMagic != JX9_VM_EXEC ){
  6545. /* call jx9_vm_exec() first */
  6546. return 0;
  6547. }
  6548. /* Perform the lookup */
  6549. pValue = VmExtractMemObj(pVm,pVar,FALSE,FALSE);
  6550. /* Lookup result */
  6551. return pValue;
  6552. }
  6553. /*
  6554. * Invoke the installed VM output consumer callback to consume
  6555. * the desired message.
  6556. * Refer to the implementation of [jx9_context_output()] defined
  6557. * in 'api.c' for additional information.
  6558. */
  6559. JX9_PRIVATE sxi32 jx9VmOutputConsume(
  6560. jx9_vm *pVm, /* Target VM */
  6561. SyString *pString /* Message to output */
  6562. )
  6563. {
  6564. jx9_output_consumer *pCons = &pVm->sVmConsumer;
  6565. sxi32 rc = SXRET_OK;
  6566. /* Call the output consumer */
  6567. if( pString->nByte > 0 ){
  6568. rc = pCons->xConsumer((const void *)pString->zString, pString->nByte, pCons->pUserData);
  6569. /* Increment output length */
  6570. pVm->nOutputLen += pString->nByte;
  6571. }
  6572. return rc;
  6573. }
  6574. /*
  6575. * Format a message and invoke the installed VM output consumer
  6576. * callback to consume the formatted message.
  6577. * Refer to the implementation of [jx9_context_output_format()] defined
  6578. * in 'api.c' for additional information.
  6579. */
  6580. JX9_PRIVATE sxi32 jx9VmOutputConsumeAp(
  6581. jx9_vm *pVm, /* Target VM */
  6582. const char *zFormat, /* Formatted message to output */
  6583. va_list ap /* Variable list of arguments */
  6584. )
  6585. {
  6586. jx9_output_consumer *pCons = &pVm->sVmConsumer;
  6587. sxi32 rc = SXRET_OK;
  6588. SyBlob sWorker;
  6589. /* Format the message and call the output consumer */
  6590. SyBlobInit(&sWorker, &pVm->sAllocator);
  6591. SyBlobFormatAp(&sWorker, zFormat, ap);
  6592. if( SyBlobLength(&sWorker) > 0 ){
  6593. /* Consume the formatted message */
  6594. rc = pCons->xConsumer(SyBlobData(&sWorker), SyBlobLength(&sWorker), pCons->pUserData);
  6595. }
  6596. /* Increment output length */
  6597. pVm->nOutputLen += SyBlobLength(&sWorker);
  6598. /* Release the working buffer */
  6599. SyBlobRelease(&sWorker);
  6600. return rc;
  6601. }
  6602. /*
  6603. * Return a string representation of the given JX9 OP code.
  6604. * This function never fail and always return a pointer
  6605. * to a null terminated string.
  6606. */
  6607. static const char * VmInstrToString(sxi32 nOp)
  6608. {
  6609. const char *zOp = "Unknown ";
  6610. switch(nOp){
  6611. case JX9_OP_DONE: zOp = "DONE "; break;
  6612. case JX9_OP_HALT: zOp = "HALT "; break;
  6613. case JX9_OP_LOAD: zOp = "LOAD "; break;
  6614. case JX9_OP_LOADC: zOp = "LOADC "; break;
  6615. case JX9_OP_LOAD_MAP: zOp = "LOAD_MAP "; break;
  6616. case JX9_OP_LOAD_IDX: zOp = "LOAD_IDX "; break;
  6617. case JX9_OP_NOOP: zOp = "NOOP "; break;
  6618. case JX9_OP_JMP: zOp = "JMP "; break;
  6619. case JX9_OP_JZ: zOp = "JZ "; break;
  6620. case JX9_OP_JNZ: zOp = "JNZ "; break;
  6621. case JX9_OP_POP: zOp = "POP "; break;
  6622. case JX9_OP_CAT: zOp = "CAT "; break;
  6623. case JX9_OP_CVT_INT: zOp = "CVT_INT "; break;
  6624. case JX9_OP_CVT_STR: zOp = "CVT_STR "; break;
  6625. case JX9_OP_CVT_REAL: zOp = "CVT_REAL "; break;
  6626. case JX9_OP_CALL: zOp = "CALL "; break;
  6627. case JX9_OP_UMINUS: zOp = "UMINUS "; break;
  6628. case JX9_OP_UPLUS: zOp = "UPLUS "; break;
  6629. case JX9_OP_BITNOT: zOp = "BITNOT "; break;
  6630. case JX9_OP_LNOT: zOp = "LOGNOT "; break;
  6631. case JX9_OP_MUL: zOp = "MUL "; break;
  6632. case JX9_OP_DIV: zOp = "DIV "; break;
  6633. case JX9_OP_MOD: zOp = "MOD "; break;
  6634. case JX9_OP_ADD: zOp = "ADD "; break;
  6635. case JX9_OP_SUB: zOp = "SUB "; break;
  6636. case JX9_OP_SHL: zOp = "SHL "; break;
  6637. case JX9_OP_SHR: zOp = "SHR "; break;
  6638. case JX9_OP_LT: zOp = "LT "; break;
  6639. case JX9_OP_LE: zOp = "LE "; break;
  6640. case JX9_OP_GT: zOp = "GT "; break;
  6641. case JX9_OP_GE: zOp = "GE "; break;
  6642. case JX9_OP_EQ: zOp = "EQ "; break;
  6643. case JX9_OP_NEQ: zOp = "NEQ "; break;
  6644. case JX9_OP_TEQ: zOp = "TEQ "; break;
  6645. case JX9_OP_TNE: zOp = "TNE "; break;
  6646. case JX9_OP_BAND: zOp = "BITAND "; break;
  6647. case JX9_OP_BXOR: zOp = "BITXOR "; break;
  6648. case JX9_OP_BOR: zOp = "BITOR "; break;
  6649. case JX9_OP_LAND: zOp = "LOGAND "; break;
  6650. case JX9_OP_LOR: zOp = "LOGOR "; break;
  6651. case JX9_OP_LXOR: zOp = "LOGXOR "; break;
  6652. case JX9_OP_STORE: zOp = "STORE "; break;
  6653. case JX9_OP_STORE_IDX: zOp = "STORE_IDX "; break;
  6654. case JX9_OP_PULL: zOp = "PULL "; break;
  6655. case JX9_OP_SWAP: zOp = "SWAP "; break;
  6656. case JX9_OP_YIELD: zOp = "YIELD "; break;
  6657. case JX9_OP_CVT_BOOL: zOp = "CVT_BOOL "; break;
  6658. case JX9_OP_CVT_NULL: zOp = "CVT_NULL "; break;
  6659. case JX9_OP_CVT_ARRAY: zOp = "CVT_JSON "; break;
  6660. case JX9_OP_CVT_NUMC: zOp = "CVT_NUMC "; break;
  6661. case JX9_OP_INCR: zOp = "INCR "; break;
  6662. case JX9_OP_DECR: zOp = "DECR "; break;
  6663. case JX9_OP_ADD_STORE: zOp = "ADD_STORE "; break;
  6664. case JX9_OP_SUB_STORE: zOp = "SUB_STORE "; break;
  6665. case JX9_OP_MUL_STORE: zOp = "MUL_STORE "; break;
  6666. case JX9_OP_DIV_STORE: zOp = "DIV_STORE "; break;
  6667. case JX9_OP_MOD_STORE: zOp = "MOD_STORE "; break;
  6668. case JX9_OP_CAT_STORE: zOp = "CAT_STORE "; break;
  6669. case JX9_OP_SHL_STORE: zOp = "SHL_STORE "; break;
  6670. case JX9_OP_SHR_STORE: zOp = "SHR_STORE "; break;
  6671. case JX9_OP_BAND_STORE: zOp = "BAND_STORE "; break;
  6672. case JX9_OP_BOR_STORE: zOp = "BOR_STORE "; break;
  6673. case JX9_OP_BXOR_STORE: zOp = "BXOR_STORE "; break;
  6674. case JX9_OP_CONSUME: zOp = "CONSUME "; break;
  6675. case JX9_OP_MEMBER: zOp = "MEMBER "; break;
  6676. case JX9_OP_UPLINK: zOp = "UPLINK "; break;
  6677. case JX9_OP_SWITCH: zOp = "SWITCH "; break;
  6678. case JX9_OP_FOREACH_INIT:
  6679. zOp = "4EACH_INIT "; break;
  6680. case JX9_OP_FOREACH_STEP:
  6681. zOp = "4EACH_STEP "; break;
  6682. default:
  6683. break;
  6684. }
  6685. return zOp;
  6686. }
  6687. /*
  6688. * Dump JX9 bytecodes instructions to a human readable format.
  6689. * The xConsumer() callback which is an used defined function
  6690. * is responsible of consuming the generated dump.
  6691. */
  6692. JX9_PRIVATE sxi32 jx9VmDump(
  6693. jx9_vm *pVm, /* Target VM */
  6694. ProcConsumer xConsumer, /* Output [i.e: dump] consumer callback */
  6695. void *pUserData /* Last argument to xConsumer() */
  6696. )
  6697. {
  6698. sxi32 rc;
  6699. rc = VmByteCodeDump(pVm->pByteContainer, xConsumer, pUserData);
  6700. return rc;
  6701. }
  6702. /*
  6703. * Default constant expansion callback used by the 'const' statement if used
  6704. * outside a object body [i.e: global or function scope].
  6705. * Refer to the implementation of [JX9_CompileConstant()] defined
  6706. * in 'compile.c' for additional information.
  6707. */
  6708. JX9_PRIVATE void jx9VmExpandConstantValue(jx9_value *pVal, void *pUserData)
  6709. {
  6710. SySet *pByteCode = (SySet *)pUserData;
  6711. /* Evaluate and expand constant value */
  6712. VmLocalExec((jx9_vm *)SySetGetUserData(pByteCode), pByteCode, (jx9_value *)pVal);
  6713. }
  6714. /*
  6715. * Section:
  6716. * Function handling functions.
  6717. * Authors:
  6718. * Symisc Systems, devel@symisc.net.
  6719. * Copyright (C) Symisc Systems, http://jx9.symisc.net
  6720. * Status:
  6721. * Stable.
  6722. */
  6723. /*
  6724. * int func_num_args(void)
  6725. * Returns the number of arguments passed to the function.
  6726. * Parameters
  6727. * None.
  6728. * Return
  6729. * Total number of arguments passed into the current user-defined function
  6730. * or -1 if called from the globe scope.
  6731. */
  6732. static int vm_builtin_func_num_args(jx9_context *pCtx, int nArg, jx9_value **apArg)
  6733. {
  6734. VmFrame *pFrame;
  6735. jx9_vm *pVm;
  6736. /* Point to the target VM */
  6737. pVm = pCtx->pVm;
  6738. /* Current frame */
  6739. pFrame = pVm->pFrame;
  6740. if( pFrame->pParent == 0 ){
  6741. SXUNUSED(nArg);
  6742. SXUNUSED(apArg);
  6743. /* Global frame, return -1 */
  6744. jx9_result_int(pCtx, -1);
  6745. return SXRET_OK;
  6746. }
  6747. /* Total number of arguments passed to the enclosing function */
  6748. nArg = (int)SySetUsed(&pFrame->sArg);
  6749. jx9_result_int(pCtx, nArg);
  6750. return SXRET_OK;
  6751. }
  6752. /*
  6753. * value func_get_arg(int $arg_num)
  6754. * Return an item from the argument list.
  6755. * Parameters
  6756. * Argument number(index start from zero).
  6757. * Return
  6758. * Returns the specified argument or FALSE on error.
  6759. */
  6760. static int vm_builtin_func_get_arg(jx9_context *pCtx, int nArg, jx9_value **apArg)
  6761. {
  6762. jx9_value *pObj = 0;
  6763. VmSlot *pSlot = 0;
  6764. VmFrame *pFrame;
  6765. jx9_vm *pVm;
  6766. /* Point to the target VM */
  6767. pVm = pCtx->pVm;
  6768. /* Current frame */
  6769. pFrame = pVm->pFrame;
  6770. if( nArg < 1 || pFrame->pParent == 0 ){
  6771. /* Global frame or Missing arguments, return FALSE */
  6772. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Called in the global scope");
  6773. jx9_result_bool(pCtx, 0);
  6774. return SXRET_OK;
  6775. }
  6776. /* Extract the desired index */
  6777. nArg = jx9_value_to_int(apArg[0]);
  6778. if( nArg < 0 || nArg >= (int)SySetUsed(&pFrame->sArg) ){
  6779. /* Invalid index, return FALSE */
  6780. jx9_result_bool(pCtx, 0);
  6781. return SXRET_OK;
  6782. }
  6783. /* Extract the desired argument */
  6784. if( (pSlot = (VmSlot *)SySetAt(&pFrame->sArg, (sxu32)nArg)) != 0 ){
  6785. if( (pObj = (jx9_value *)SySetAt(&pVm->aMemObj, pSlot->nIdx)) != 0 ){
  6786. /* Return the desired argument */
  6787. jx9_result_value(pCtx, (jx9_value *)pObj);
  6788. }else{
  6789. /* No such argument, return false */
  6790. jx9_result_bool(pCtx, 0);
  6791. }
  6792. }else{
  6793. /* CAN'T HAPPEN */
  6794. jx9_result_bool(pCtx, 0);
  6795. }
  6796. return SXRET_OK;
  6797. }
  6798. /*
  6799. * array func_get_args(void)
  6800. * Returns an array comprising a copy of function's argument list.
  6801. * Parameters
  6802. * None.
  6803. * Return
  6804. * Returns an array in which each element is a copy of the corresponding
  6805. * member of the current user-defined function's argument list.
  6806. * Otherwise FALSE is returned on failure.
  6807. */
  6808. static int vm_builtin_func_get_args(jx9_context *pCtx, int nArg, jx9_value **apArg)
  6809. {
  6810. jx9_value *pObj = 0;
  6811. jx9_value *pArray;
  6812. VmFrame *pFrame;
  6813. VmSlot *aSlot;
  6814. sxu32 n;
  6815. /* Point to the current frame */
  6816. pFrame = pCtx->pVm->pFrame;
  6817. if( pFrame->pParent == 0 ){
  6818. /* Global frame, return FALSE */
  6819. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Called in the global scope");
  6820. jx9_result_bool(pCtx, 0);
  6821. return SXRET_OK;
  6822. }
  6823. /* Create a new array */
  6824. pArray = jx9_context_new_array(pCtx);
  6825. if( pArray == 0 ){
  6826. SXUNUSED(nArg); /* cc warning */
  6827. SXUNUSED(apArg);
  6828. jx9_result_bool(pCtx, 0);
  6829. return SXRET_OK;
  6830. }
  6831. /* Start filling the array with the given arguments */
  6832. aSlot = (VmSlot *)SySetBasePtr(&pFrame->sArg);
  6833. for( n = 0; n < SySetUsed(&pFrame->sArg) ; n++ ){
  6834. pObj = (jx9_value *)SySetAt(&pCtx->pVm->aMemObj, aSlot[n].nIdx);
  6835. if( pObj ){
  6836. jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pObj);
  6837. }
  6838. }
  6839. /* Return the freshly created array */
  6840. jx9_result_value(pCtx, pArray);
  6841. return SXRET_OK;
  6842. }
  6843. /*
  6844. * bool function_exists(string $name)
  6845. * Return TRUE if the given function has been defined.
  6846. * Parameters
  6847. * The name of the desired function.
  6848. * Return
  6849. * Return TRUE if the given function has been defined.False otherwise
  6850. */
  6851. static int vm_builtin_func_exists(jx9_context *pCtx, int nArg, jx9_value **apArg)
  6852. {
  6853. const char *zName;
  6854. jx9_vm *pVm;
  6855. int nLen;
  6856. int res;
  6857. if( nArg < 1 ){
  6858. /* Missing argument, return FALSE */
  6859. jx9_result_bool(pCtx, 0);
  6860. return SXRET_OK;
  6861. }
  6862. /* Point to the target VM */
  6863. pVm = pCtx->pVm;
  6864. /* Extract the function name */
  6865. zName = jx9_value_to_string(apArg[0], &nLen);
  6866. /* Assume the function is not defined */
  6867. res = 0;
  6868. /* Perform the lookup */
  6869. if( SyHashGet(&pVm->hFunction, (const void *)zName, (sxu32)nLen) != 0 ||
  6870. SyHashGet(&pVm->hHostFunction, (const void *)zName, (sxu32)nLen) != 0 ){
  6871. /* Function is defined */
  6872. res = 1;
  6873. }
  6874. jx9_result_bool(pCtx, res);
  6875. return SXRET_OK;
  6876. }
  6877. /*
  6878. * Verify that the contents of a variable can be called as a function.
  6879. * [i.e: Whether it is callable or not].
  6880. * Return TRUE if callable.FALSE otherwise.
  6881. */
  6882. JX9_PRIVATE int jx9VmIsCallable(jx9_vm *pVm, jx9_value *pValue)
  6883. {
  6884. int res = 0;
  6885. if( pValue->iFlags & MEMOBJ_STRING ){
  6886. const char *zName;
  6887. int nLen;
  6888. /* Extract the name */
  6889. zName = jx9_value_to_string(pValue, &nLen);
  6890. /* Perform the lookup */
  6891. if( SyHashGet(&pVm->hFunction, (const void *)zName, (sxu32)nLen) != 0 ||
  6892. SyHashGet(&pVm->hHostFunction, (const void *)zName, (sxu32)nLen) != 0 ){
  6893. /* Function is callable */
  6894. res = 1;
  6895. }
  6896. }
  6897. return res;
  6898. }
  6899. /*
  6900. * bool is_callable(callable $name[, bool $syntax_only = false])
  6901. * Verify that the contents of a variable can be called as a function.
  6902. * Parameters
  6903. * $name
  6904. * The callback function to check
  6905. * $syntax_only
  6906. * If set to TRUE the function only verifies that name might be a function or method.
  6907. * It will only reject simple variables that are not strings, or an array that does
  6908. * not have a valid structure to be used as a callback. The valid ones are supposed
  6909. * to have only 2 entries, the first of which is an object or a string, and the second
  6910. * a string.
  6911. * Return
  6912. * TRUE if name is callable, FALSE otherwise.
  6913. */
  6914. static int vm_builtin_is_callable(jx9_context *pCtx, int nArg, jx9_value **apArg)
  6915. {
  6916. jx9_vm *pVm;
  6917. int res;
  6918. if( nArg < 1 ){
  6919. /* Missing arguments, return FALSE */
  6920. jx9_result_bool(pCtx, 0);
  6921. return SXRET_OK;
  6922. }
  6923. /* Point to the target VM */
  6924. pVm = pCtx->pVm;
  6925. /* Perform the requested operation */
  6926. res = jx9VmIsCallable(pVm, apArg[0]);
  6927. jx9_result_bool(pCtx, res);
  6928. return SXRET_OK;
  6929. }
  6930. /*
  6931. * Hash walker callback used by the [get_defined_functions()] function
  6932. * defined below.
  6933. */
  6934. static int VmHashFuncStep(SyHashEntry *pEntry, void *pUserData)
  6935. {
  6936. jx9_value *pArray = (jx9_value *)pUserData;
  6937. jx9_value sName;
  6938. sxi32 rc;
  6939. /* Prepare the function name for insertion */
  6940. jx9MemObjInitFromString(pArray->pVm, &sName, 0);
  6941. jx9MemObjStringAppend(&sName, (const char *)pEntry->pKey, pEntry->nKeyLen);
  6942. /* Perform the insertion */
  6943. rc = jx9_array_add_elem(pArray, 0/* Automatic index assign */, &sName); /* Will make it's own copy */
  6944. jx9MemObjRelease(&sName);
  6945. return rc;
  6946. }
  6947. /*
  6948. * array get_defined_functions(void)
  6949. * Returns an array of all defined functions.
  6950. * Parameter
  6951. * None.
  6952. * Return
  6953. * Returns an multidimensional array containing a list of all defined functions
  6954. * both built-in (internal) and user-defined.
  6955. * The internal functions will be accessible via $arr["internal"], and the user
  6956. * defined ones using $arr["user"].
  6957. * Note:
  6958. * NULL is returned on failure.
  6959. */
  6960. static int vm_builtin_get_defined_func(jx9_context *pCtx, int nArg, jx9_value **apArg)
  6961. {
  6962. jx9_value *pArray;
  6963. /* NOTE:
  6964. * Don't worry about freeing memory here, every allocated resource will be released
  6965. * automatically by the engine as soon we return from this foreign function.
  6966. */
  6967. pArray = jx9_context_new_array(pCtx);
  6968. if( pArray == 0 ){
  6969. SXUNUSED(nArg); /* cc warning */
  6970. SXUNUSED(apArg);
  6971. /* Return NULL */
  6972. jx9_result_null(pCtx);
  6973. return SXRET_OK;
  6974. }
  6975. /* Fill with the appropriate information */
  6976. SyHashForEach(&pCtx->pVm->hHostFunction,VmHashFuncStep,pArray);
  6977. /* Fill with the appropriate information */
  6978. SyHashForEach(&pCtx->pVm->hFunction, VmHashFuncStep,pArray);
  6979. /* Return a copy of the array array */
  6980. jx9_result_value(pCtx, pArray);
  6981. return SXRET_OK;
  6982. }
  6983. /*
  6984. * Call a user defined or foreign function where the name of the function
  6985. * is stored in the pFunc parameter and the given arguments are stored
  6986. * in the apArg[] array.
  6987. * Return SXRET_OK if the function was successfuly called.Any other
  6988. * return value indicates failure.
  6989. */
  6990. JX9_PRIVATE sxi32 jx9VmCallUserFunction(
  6991. jx9_vm *pVm, /* Target VM */
  6992. jx9_value *pFunc, /* Callback name */
  6993. int nArg, /* Total number of given arguments */
  6994. jx9_value **apArg, /* Callback arguments */
  6995. jx9_value *pResult /* Store callback return value here. NULL otherwise */
  6996. )
  6997. {
  6998. jx9_value *aStack;
  6999. VmInstr aInstr[2];
  7000. int i;
  7001. if((pFunc->iFlags & (MEMOBJ_STRING)) == 0 ){
  7002. /* Don't bother processing, it's invalid anyway */
  7003. if( pResult ){
  7004. /* Assume a null return value */
  7005. jx9MemObjRelease(pResult);
  7006. }
  7007. return SXERR_INVALID;
  7008. }
  7009. /* Create a new operand stack */
  7010. aStack = VmNewOperandStack(&(*pVm), 1+nArg);
  7011. if( aStack == 0 ){
  7012. jx9VmThrowError(&(*pVm), 0, JX9_CTX_ERR,
  7013. "JX9 is running out of memory while invoking user callback");
  7014. if( pResult ){
  7015. /* Assume a null return value */
  7016. jx9MemObjRelease(pResult);
  7017. }
  7018. return SXERR_MEM;
  7019. }
  7020. /* Fill the operand stack with the given arguments */
  7021. for( i = 0 ; i < nArg ; i++ ){
  7022. jx9MemObjLoad(apArg[i], &aStack[i]);
  7023. aStack[i].nIdx = apArg[i]->nIdx;
  7024. }
  7025. /* Push the function name */
  7026. jx9MemObjLoad(pFunc, &aStack[i]);
  7027. aStack[i].nIdx = SXU32_HIGH; /* Mark as constant */
  7028. /* Emit the CALL istruction */
  7029. aInstr[0].iOp = JX9_OP_CALL;
  7030. aInstr[0].iP1 = nArg; /* Total number of given arguments */
  7031. aInstr[0].iP2 = 0;
  7032. aInstr[0].p3 = 0;
  7033. /* Emit the DONE instruction */
  7034. aInstr[1].iOp = JX9_OP_DONE;
  7035. aInstr[1].iP1 = 1; /* Extract function return value if available */
  7036. aInstr[1].iP2 = 0;
  7037. aInstr[1].p3 = 0;
  7038. /* Execute the function body (if available) */
  7039. VmByteCodeExec(&(*pVm), aInstr, aStack, nArg, pResult);
  7040. /* Clean up the mess left behind */
  7041. SyMemBackendFree(&pVm->sAllocator, aStack);
  7042. return JX9_OK;
  7043. }
  7044. /*
  7045. * Call a user defined or foreign function whith a varibale number
  7046. * of arguments where the name of the function is stored in the pFunc
  7047. * parameter.
  7048. * Return SXRET_OK if the function was successfuly called.Any other
  7049. * return value indicates failure.
  7050. */
  7051. JX9_PRIVATE sxi32 jx9VmCallUserFunctionAp(
  7052. jx9_vm *pVm, /* Target VM */
  7053. jx9_value *pFunc, /* Callback name */
  7054. jx9_value *pResult, /* Store callback return value here. NULL otherwise */
  7055. ... /* 0 (Zero) or more Callback arguments */
  7056. )
  7057. {
  7058. jx9_value *pArg;
  7059. SySet aArg;
  7060. va_list ap;
  7061. sxi32 rc;
  7062. SySetInit(&aArg, &pVm->sAllocator, sizeof(jx9_value *));
  7063. /* Copy arguments one after one */
  7064. va_start(ap, pResult);
  7065. for(;;){
  7066. pArg = va_arg(ap, jx9_value *);
  7067. if( pArg == 0 ){
  7068. break;
  7069. }
  7070. SySetPut(&aArg, (const void *)&pArg);
  7071. }
  7072. /* Call the core routine */
  7073. rc = jx9VmCallUserFunction(&(*pVm), pFunc, (int)SySetUsed(&aArg), (jx9_value **)SySetBasePtr(&aArg), pResult);
  7074. /* Cleanup */
  7075. SySetRelease(&aArg);
  7076. return rc;
  7077. }
  7078. /*
  7079. * bool defined(string $name)
  7080. * Checks whether a given named constant exists.
  7081. * Parameter:
  7082. * Name of the desired constant.
  7083. * Return
  7084. * TRUE if the given constant exists.FALSE otherwise.
  7085. */
  7086. static int vm_builtin_defined(jx9_context *pCtx, int nArg, jx9_value **apArg)
  7087. {
  7088. const char *zName;
  7089. int nLen = 0;
  7090. int res = 0;
  7091. if( nArg < 1 ){
  7092. /* Missing constant name, return FALSE */
  7093. jx9_context_throw_error(pCtx,JX9_CTX_NOTICE,"Missing constant name");
  7094. jx9_result_bool(pCtx, 0);
  7095. return SXRET_OK;
  7096. }
  7097. /* Extract constant name */
  7098. zName = jx9_value_to_string(apArg[0], &nLen);
  7099. /* Perform the lookup */
  7100. if( nLen > 0 && SyHashGet(&pCtx->pVm->hConstant, (const void *)zName, (sxu32)nLen) != 0 ){
  7101. /* Already defined */
  7102. res = 1;
  7103. }
  7104. jx9_result_bool(pCtx, res);
  7105. return SXRET_OK;
  7106. }
  7107. /*
  7108. * Hash walker callback used by the [get_defined_constants()] function
  7109. * defined below.
  7110. */
  7111. static int VmHashConstStep(SyHashEntry *pEntry, void *pUserData)
  7112. {
  7113. jx9_value *pArray = (jx9_value *)pUserData;
  7114. jx9_value sName;
  7115. sxi32 rc;
  7116. /* Prepare the constant name for insertion */
  7117. jx9MemObjInitFromString(pArray->pVm, &sName, 0);
  7118. jx9MemObjStringAppend(&sName, (const char *)pEntry->pKey, pEntry->nKeyLen);
  7119. /* Perform the insertion */
  7120. rc = jx9_array_add_elem(pArray, 0, &sName); /* Will make it's own copy */
  7121. jx9MemObjRelease(&sName);
  7122. return rc;
  7123. }
  7124. /*
  7125. * array get_defined_constants(void)
  7126. * Returns an associative array with the names of all defined
  7127. * constants.
  7128. * Parameters
  7129. * NONE.
  7130. * Returns
  7131. * Returns the names of all the constants currently defined.
  7132. */
  7133. static int vm_builtin_get_defined_constants(jx9_context *pCtx, int nArg, jx9_value **apArg)
  7134. {
  7135. jx9_value *pArray;
  7136. /* Create the array first*/
  7137. pArray = jx9_context_new_array(pCtx);
  7138. if( pArray == 0 ){
  7139. SXUNUSED(nArg); /* cc warning */
  7140. SXUNUSED(apArg);
  7141. /* Return NULL */
  7142. jx9_result_null(pCtx);
  7143. return SXRET_OK;
  7144. }
  7145. /* Fill the array with the defined constants */
  7146. SyHashForEach(&pCtx->pVm->hConstant, VmHashConstStep, pArray);
  7147. /* Return the created array */
  7148. jx9_result_value(pCtx, pArray);
  7149. return SXRET_OK;
  7150. }
  7151. /*
  7152. * Section:
  7153. * Random numbers/string generators.
  7154. * Authors:
  7155. * Symisc Systems, devel@symisc.net.
  7156. * Copyright (C) Symisc Systems, http://jx9.symisc.net
  7157. * Status:
  7158. * Stable.
  7159. */
  7160. /*
  7161. * Generate a random 32-bit unsigned integer.
  7162. * JX9 use it's own private PRNG which is based on the one
  7163. * used by te SQLite3 library.
  7164. */
  7165. JX9_PRIVATE sxu32 jx9VmRandomNum(jx9_vm *pVm)
  7166. {
  7167. sxu32 iNum;
  7168. SyRandomness(&pVm->sPrng, (void *)&iNum, sizeof(sxu32));
  7169. return iNum;
  7170. }
  7171. /*
  7172. * Generate a random string (English Alphabet) of length nLen.
  7173. * Note that the generated string is NOT null terminated.
  7174. * JX9 use it's own private PRNG which is based on the one used
  7175. * by te SQLite3 library.
  7176. */
  7177. JX9_PRIVATE void jx9VmRandomString(jx9_vm *pVm, char *zBuf, int nLen)
  7178. {
  7179. static const char zBase[] = {"abcdefghijklmnopqrstuvwxyz"}; /* English Alphabet */
  7180. int i;
  7181. /* Generate a binary string first */
  7182. SyRandomness(&pVm->sPrng, zBuf, (sxu32)nLen);
  7183. /* Turn the binary string into english based alphabet */
  7184. for( i = 0 ; i < nLen ; ++i ){
  7185. zBuf[i] = zBase[zBuf[i] % (sizeof(zBase)-1)];
  7186. }
  7187. }
  7188. /*
  7189. * int rand()
  7190. * Generate a random (unsigned 32-bit) integer.
  7191. * Parameter
  7192. * $min
  7193. * The lowest value to return (default: 0)
  7194. * $max
  7195. * The highest value to return (default: getrandmax())
  7196. * Return
  7197. * A pseudo random value between min (or 0) and max (or getrandmax(), inclusive).
  7198. * Note:
  7199. * JX9 use it's own private PRNG which is based on the one used
  7200. * by te SQLite3 library.
  7201. */
  7202. static int vm_builtin_rand(jx9_context *pCtx, int nArg, jx9_value **apArg)
  7203. {
  7204. sxu32 iNum;
  7205. /* Generate the random number */
  7206. iNum = jx9VmRandomNum(pCtx->pVm);
  7207. if( nArg > 1 ){
  7208. sxu32 iMin, iMax;
  7209. iMin = (sxu32)jx9_value_to_int(apArg[0]);
  7210. iMax = (sxu32)jx9_value_to_int(apArg[1]);
  7211. if( iMin < iMax ){
  7212. sxu32 iDiv = iMax+1-iMin;
  7213. if( iDiv > 0 ){
  7214. iNum = (iNum % iDiv)+iMin;
  7215. }
  7216. }else if(iMax > 0 ){
  7217. iNum %= iMax;
  7218. }
  7219. }
  7220. /* Return the number */
  7221. jx9_result_int64(pCtx, (jx9_int64)iNum);
  7222. return SXRET_OK;
  7223. }
  7224. /*
  7225. * int getrandmax(void)
  7226. * Show largest possible random value
  7227. * Return
  7228. * The largest possible random value returned by rand() which is in
  7229. * this implementation 0xFFFFFFFF.
  7230. * Note:
  7231. * JX9 use it's own private PRNG which is based on the one used
  7232. * by te SQLite3 library.
  7233. */
  7234. static int vm_builtin_getrandmax(jx9_context *pCtx, int nArg, jx9_value **apArg)
  7235. {
  7236. SXUNUSED(nArg); /* cc warning */
  7237. SXUNUSED(apArg);
  7238. jx9_result_int64(pCtx, SXU32_HIGH);
  7239. return SXRET_OK;
  7240. }
  7241. /*
  7242. * string rand_str()
  7243. * string rand_str(int $len)
  7244. * Generate a random string (English alphabet).
  7245. * Parameter
  7246. * $len
  7247. * Length of the desired string (default: 16, Min: 1, Max: 1024)
  7248. * Return
  7249. * A pseudo random string.
  7250. * Note:
  7251. * JX9 use it's own private PRNG which is based on the one used
  7252. * by te SQLite3 library.
  7253. */
  7254. static int vm_builtin_rand_str(jx9_context *pCtx, int nArg, jx9_value **apArg)
  7255. {
  7256. char zString[1024];
  7257. int iLen = 0x10;
  7258. if( nArg > 0 ){
  7259. /* Get the desired length */
  7260. iLen = jx9_value_to_int(apArg[0]);
  7261. if( iLen < 1 || iLen > 1024 ){
  7262. /* Default length */
  7263. iLen = 0x10;
  7264. }
  7265. }
  7266. /* Generate the random string */
  7267. jx9VmRandomString(pCtx->pVm, zString, iLen);
  7268. /* Return the generated string */
  7269. jx9_result_string(pCtx, zString, iLen); /* Will make it's own copy */
  7270. return SXRET_OK;
  7271. }
  7272. /*
  7273. * Section:
  7274. * Language construct implementation as foreign functions.
  7275. * Authors:
  7276. * Symisc Systems, devel@symisc.net.
  7277. * Copyright (C) Symisc Systems, http://jx9.symisc.net
  7278. * Status:
  7279. * Stable.
  7280. */
  7281. /*
  7282. * void print($string...)
  7283. * Output one or more messages.
  7284. * Parameters
  7285. * $string
  7286. * Message to output.
  7287. * Return
  7288. * NULL.
  7289. */
  7290. static int vm_builtin_print(jx9_context *pCtx, int nArg,jx9_value **apArg)
  7291. {
  7292. const char *zData;
  7293. int nDataLen = 0;
  7294. jx9_vm *pVm;
  7295. int i, rc;
  7296. /* Point to the target VM */
  7297. pVm = pCtx->pVm;
  7298. /* Output */
  7299. for( i = 0 ; i < nArg ; ++i ){
  7300. zData = jx9_value_to_string(apArg[i], &nDataLen);
  7301. if( nDataLen > 0 ){
  7302. rc = pVm->sVmConsumer.xConsumer((const void *)zData, (unsigned int)nDataLen, pVm->sVmConsumer.pUserData);
  7303. /* Increment output length */
  7304. pVm->nOutputLen += nDataLen;
  7305. if( rc == SXERR_ABORT ){
  7306. /* Output consumer callback request an operation abort */
  7307. return JX9_ABORT;
  7308. }
  7309. }
  7310. }
  7311. return SXRET_OK;
  7312. }
  7313. /*
  7314. * void exit(string $msg)
  7315. * void exit(int $status)
  7316. * void die(string $ms)
  7317. * void die(int $status)
  7318. * Output a message and terminate program execution.
  7319. * Parameter
  7320. * If status is a string, this function prints the status just before exiting.
  7321. * If status is an integer, that value will be used as the exit status
  7322. * and not printed
  7323. * Return
  7324. * NULL
  7325. */
  7326. static int vm_builtin_exit(jx9_context *pCtx, int nArg, jx9_value **apArg)
  7327. {
  7328. if( nArg > 0 ){
  7329. if( jx9_value_is_string(apArg[0]) ){
  7330. const char *zData;
  7331. int iLen = 0;
  7332. /* Print exit message */
  7333. zData = jx9_value_to_string(apArg[0], &iLen);
  7334. jx9_context_output(pCtx, zData, iLen);
  7335. }else if(jx9_value_is_int(apArg[0]) ){
  7336. sxi32 iExitStatus;
  7337. /* Record exit status code */
  7338. iExitStatus = jx9_value_to_int(apArg[0]);
  7339. pCtx->pVm->iExitStatus = iExitStatus;
  7340. }
  7341. }
  7342. /* Abort processing immediately */
  7343. return JX9_ABORT;
  7344. }
  7345. /*
  7346. * Unset a memory object [i.e: a jx9_value].
  7347. */
  7348. JX9_PRIVATE sxi32 jx9VmUnsetMemObj(jx9_vm *pVm,sxu32 nObjIdx)
  7349. {
  7350. jx9_value *pObj;
  7351. pObj = (jx9_value *)SySetAt(&pVm->aMemObj, nObjIdx);
  7352. if( pObj ){
  7353. VmSlot sFree;
  7354. /* Release the object */
  7355. jx9MemObjRelease(pObj);
  7356. /* Restore to the free list */
  7357. sFree.nIdx = nObjIdx;
  7358. sFree.pUserData = 0;
  7359. SySetPut(&pVm->aFreeObj, (const void *)&sFree);
  7360. }
  7361. return SXRET_OK;
  7362. }
  7363. /*
  7364. * string gettype($var)
  7365. * Get the type of a variable
  7366. * Parameters
  7367. * $var
  7368. * The variable being type checked.
  7369. * Return
  7370. * String representation of the given variable type.
  7371. */
  7372. static int vm_builtin_gettype(jx9_context *pCtx, int nArg, jx9_value **apArg)
  7373. {
  7374. const char *zType = "null";
  7375. if( nArg > 0 ){
  7376. zType = jx9MemObjTypeDump(apArg[0]);
  7377. }
  7378. /* Return the variable type */
  7379. jx9_result_string(pCtx, zType, -1/*Compute length automatically*/);
  7380. return SXRET_OK;
  7381. }
  7382. /*
  7383. * string get_resource_type(resource $handle)
  7384. * This function gets the type of the given resource.
  7385. * Parameters
  7386. * $handle
  7387. * The evaluated resource handle.
  7388. * Return
  7389. * If the given handle is a resource, this function will return a string
  7390. * representing its type. If the type is not identified by this function
  7391. * the return value will be the string Unknown.
  7392. * This function will return FALSE and generate an error if handle
  7393. * is not a resource.
  7394. */
  7395. static int vm_builtin_get_resource_type(jx9_context *pCtx, int nArg, jx9_value **apArg)
  7396. {
  7397. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  7398. /* Missing/Invalid arguments, return FALSE*/
  7399. jx9_result_bool(pCtx, 0);
  7400. return SXRET_OK;
  7401. }
  7402. jx9_result_string_format(pCtx, "resID_%#x", apArg[0]->x.pOther);
  7403. return SXRET_OK;
  7404. }
  7405. /*
  7406. * void dump(expression, ....)
  7407. * dump &#x2014; Dumps information about a variable
  7408. * Parameters
  7409. * One or more expression to dump.
  7410. * Returns
  7411. * Nothing.
  7412. */
  7413. static int vm_builtin_dump(jx9_context *pCtx, int nArg, jx9_value **apArg)
  7414. {
  7415. SyBlob sDump; /* Generated dump is stored here */
  7416. int i;
  7417. SyBlobInit(&sDump,&pCtx->pVm->sAllocator);
  7418. /* Dump one or more expressions */
  7419. for( i = 0 ; i < nArg ; i++ ){
  7420. jx9_value *pObj = apArg[i];
  7421. /* Reset the working buffer */
  7422. SyBlobReset(&sDump);
  7423. /* Dump the given expression */
  7424. jx9MemObjDump(&sDump,pObj);
  7425. /* Output */
  7426. if( SyBlobLength(&sDump) > 0 ){
  7427. jx9_context_output(pCtx, (const char *)SyBlobData(&sDump), (int)SyBlobLength(&sDump));
  7428. }
  7429. }
  7430. /* Release the working buffer */
  7431. SyBlobRelease(&sDump);
  7432. return SXRET_OK;
  7433. }
  7434. /*
  7435. * Section:
  7436. * Version, Credits and Copyright related functions.
  7437. * Authors:
  7438. * Symisc Systems, devel@symisc.net.
  7439. * Copyright (C) Symisc Systems, http://jx9.symisc.net
  7440. * Stable.
  7441. */
  7442. /*
  7443. * string jx9_version(void)
  7444. * string jx9_credits(void)
  7445. * Returns the running version of the jx9 version.
  7446. * Parameters
  7447. * None
  7448. * Return
  7449. * Current jx9 version.
  7450. */
  7451. static int vm_builtin_jx9_version(jx9_context *pCtx, int nArg, jx9_value **apArg)
  7452. {
  7453. SXUNUSED(nArg);
  7454. SXUNUSED(apArg); /* cc warning */
  7455. /* Current engine version, signature and cipyright notice */
  7456. jx9_result_string_format(pCtx,"%s %s, %s",JX9_VERSION,JX9_SIG,JX9_COPYRIGHT);
  7457. return JX9_OK;
  7458. }
  7459. /*
  7460. * Section:
  7461. * URL related routines.
  7462. * Authors:
  7463. * Symisc Systems, devel@symisc.net.
  7464. * Copyright (C) Symisc Systems, http://jx9.symisc.net
  7465. * Status:
  7466. * Stable.
  7467. */
  7468. /* Forward declaration */
  7469. static sxi32 VmHttpSplitURI(SyhttpUri *pOut, const char *zUri, sxu32 nLen);
  7470. /*
  7471. * value parse_url(string $url [, int $component = -1 ])
  7472. * Parse a URL and return its fields.
  7473. * Parameters
  7474. * $url
  7475. * The URL to parse.
  7476. * $component
  7477. * Specify one of JX9_URL_SCHEME, JX9_URL_HOST, JX9_URL_PORT, JX9_URL_USER
  7478. * JX9_URL_PASS, JX9_URL_PATH, JX9_URL_QUERY or JX9_URL_FRAGMENT to retrieve
  7479. * just a specific URL component as a string (except when JX9_URL_PORT is given
  7480. * in which case the return value will be an integer).
  7481. * Return
  7482. * If the component parameter is omitted, an associative array is returned.
  7483. * At least one element will be present within the array. Potential keys within
  7484. * this array are:
  7485. * scheme - e.g. http
  7486. * host
  7487. * port
  7488. * user
  7489. * pass
  7490. * path
  7491. * query - after the question mark ?
  7492. * fragment - after the hashmark #
  7493. * Note:
  7494. * FALSE is returned on failure.
  7495. * This function work with relative URL unlike the one shipped
  7496. * with the standard JX9 engine.
  7497. */
  7498. static int vm_builtin_parse_url(jx9_context *pCtx, int nArg, jx9_value **apArg)
  7499. {
  7500. const char *zStr; /* Input string */
  7501. SyString *pComp; /* Pointer to the URI component */
  7502. SyhttpUri sURI; /* Parse of the given URI */
  7503. int nLen;
  7504. sxi32 rc;
  7505. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  7506. /* Missing/Invalid arguments, return FALSE */
  7507. jx9_result_bool(pCtx, 0);
  7508. return JX9_OK;
  7509. }
  7510. /* Extract the given URI */
  7511. zStr = jx9_value_to_string(apArg[0], &nLen);
  7512. if( nLen < 1 ){
  7513. /* Nothing to process, return FALSE */
  7514. jx9_result_bool(pCtx, 0);
  7515. return JX9_OK;
  7516. }
  7517. /* Get a parse */
  7518. rc = VmHttpSplitURI(&sURI, zStr, (sxu32)nLen);
  7519. if( rc != SXRET_OK ){
  7520. /* Malformed input, return FALSE */
  7521. jx9_result_bool(pCtx, 0);
  7522. return JX9_OK;
  7523. }
  7524. if( nArg > 1 ){
  7525. int nComponent = jx9_value_to_int(apArg[1]);
  7526. /* Refer to constant.c for constants values */
  7527. switch(nComponent){
  7528. case 1: /* JX9_URL_SCHEME */
  7529. pComp = &sURI.sScheme;
  7530. if( pComp->nByte < 1 ){
  7531. /* No available value, return NULL */
  7532. jx9_result_null(pCtx);
  7533. }else{
  7534. jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
  7535. }
  7536. break;
  7537. case 2: /* JX9_URL_HOST */
  7538. pComp = &sURI.sHost;
  7539. if( pComp->nByte < 1 ){
  7540. /* No available value, return NULL */
  7541. jx9_result_null(pCtx);
  7542. }else{
  7543. jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
  7544. }
  7545. break;
  7546. case 3: /* JX9_URL_PORT */
  7547. pComp = &sURI.sPort;
  7548. if( pComp->nByte < 1 ){
  7549. /* No available value, return NULL */
  7550. jx9_result_null(pCtx);
  7551. }else{
  7552. int iPort = 0;
  7553. /* Cast the value to integer */
  7554. SyStrToInt32(pComp->zString, pComp->nByte, (void *)&iPort, 0);
  7555. jx9_result_int(pCtx, iPort);
  7556. }
  7557. break;
  7558. case 4: /* JX9_URL_USER */
  7559. pComp = &sURI.sUser;
  7560. if( pComp->nByte < 1 ){
  7561. /* No available value, return NULL */
  7562. jx9_result_null(pCtx);
  7563. }else{
  7564. jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
  7565. }
  7566. break;
  7567. case 5: /* JX9_URL_PASS */
  7568. pComp = &sURI.sPass;
  7569. if( pComp->nByte < 1 ){
  7570. /* No available value, return NULL */
  7571. jx9_result_null(pCtx);
  7572. }else{
  7573. jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
  7574. }
  7575. break;
  7576. case 7: /* JX9_URL_QUERY */
  7577. pComp = &sURI.sQuery;
  7578. if( pComp->nByte < 1 ){
  7579. /* No available value, return NULL */
  7580. jx9_result_null(pCtx);
  7581. }else{
  7582. jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
  7583. }
  7584. break;
  7585. case 8: /* JX9_URL_FRAGMENT */
  7586. pComp = &sURI.sFragment;
  7587. if( pComp->nByte < 1 ){
  7588. /* No available value, return NULL */
  7589. jx9_result_null(pCtx);
  7590. }else{
  7591. jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
  7592. }
  7593. break;
  7594. case 6: /* JX9_URL_PATH */
  7595. pComp = &sURI.sPath;
  7596. if( pComp->nByte < 1 ){
  7597. /* No available value, return NULL */
  7598. jx9_result_null(pCtx);
  7599. }else{
  7600. jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
  7601. }
  7602. break;
  7603. default:
  7604. /* No such entry, return NULL */
  7605. jx9_result_null(pCtx);
  7606. break;
  7607. }
  7608. }else{
  7609. jx9_value *pArray, *pValue;
  7610. /* Return an associative array */
  7611. pArray = jx9_context_new_array(pCtx); /* Empty array */
  7612. pValue = jx9_context_new_scalar(pCtx); /* Array value */
  7613. if( pArray == 0 || pValue == 0 ){
  7614. /* Out of memory */
  7615. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "jx9 engine is running out of memory");
  7616. /* Return false */
  7617. jx9_result_bool(pCtx, 0);
  7618. return JX9_OK;
  7619. }
  7620. /* Fill the array */
  7621. pComp = &sURI.sScheme;
  7622. if( pComp->nByte > 0 ){
  7623. jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
  7624. jx9_array_add_strkey_elem(pArray, "scheme", pValue); /* Will make it's own copy */
  7625. }
  7626. /* Reset the string cursor */
  7627. jx9_value_reset_string_cursor(pValue);
  7628. pComp = &sURI.sHost;
  7629. if( pComp->nByte > 0 ){
  7630. jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
  7631. jx9_array_add_strkey_elem(pArray, "host", pValue); /* Will make it's own copy */
  7632. }
  7633. /* Reset the string cursor */
  7634. jx9_value_reset_string_cursor(pValue);
  7635. pComp = &sURI.sPort;
  7636. if( pComp->nByte > 0 ){
  7637. int iPort = 0;/* cc warning */
  7638. /* Convert to integer */
  7639. SyStrToInt32(pComp->zString, pComp->nByte, (void *)&iPort, 0);
  7640. jx9_value_int(pValue, iPort);
  7641. jx9_array_add_strkey_elem(pArray, "port", pValue); /* Will make it's own copy */
  7642. }
  7643. /* Reset the string cursor */
  7644. jx9_value_reset_string_cursor(pValue);
  7645. pComp = &sURI.sUser;
  7646. if( pComp->nByte > 0 ){
  7647. jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
  7648. jx9_array_add_strkey_elem(pArray, "user", pValue); /* Will make it's own copy */
  7649. }
  7650. /* Reset the string cursor */
  7651. jx9_value_reset_string_cursor(pValue);
  7652. pComp = &sURI.sPass;
  7653. if( pComp->nByte > 0 ){
  7654. jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
  7655. jx9_array_add_strkey_elem(pArray, "pass", pValue); /* Will make it's own copy */
  7656. }
  7657. /* Reset the string cursor */
  7658. jx9_value_reset_string_cursor(pValue);
  7659. pComp = &sURI.sPath;
  7660. if( pComp->nByte > 0 ){
  7661. jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
  7662. jx9_array_add_strkey_elem(pArray, "path", pValue); /* Will make it's own copy */
  7663. }
  7664. /* Reset the string cursor */
  7665. jx9_value_reset_string_cursor(pValue);
  7666. pComp = &sURI.sQuery;
  7667. if( pComp->nByte > 0 ){
  7668. jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
  7669. jx9_array_add_strkey_elem(pArray, "query", pValue); /* Will make it's own copy */
  7670. }
  7671. /* Reset the string cursor */
  7672. jx9_value_reset_string_cursor(pValue);
  7673. pComp = &sURI.sFragment;
  7674. if( pComp->nByte > 0 ){
  7675. jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
  7676. jx9_array_add_strkey_elem(pArray, "fragment", pValue); /* Will make it's own copy */
  7677. }
  7678. /* Return the created array */
  7679. jx9_result_value(pCtx, pArray);
  7680. /* NOTE:
  7681. * Don't worry about freeing 'pValue', everything will be released
  7682. * automatically as soon we return from this function.
  7683. */
  7684. }
  7685. /* All done */
  7686. return JX9_OK;
  7687. }
  7688. /*
  7689. * Section:
  7690. * Array related routines.
  7691. * Authors:
  7692. * Symisc Systems, devel@symisc.net.
  7693. * Copyright (C) Symisc Systems, http://jx9.symisc.net
  7694. * Status:
  7695. * Stable.
  7696. * Note 2012-5-21 01:04:15:
  7697. * Array related functions that need access to the underlying
  7698. * virtual machine are implemented here rather than 'hashmap.c'
  7699. */
  7700. /*
  7701. * The [extract()] function store it's state information in an instance
  7702. * of the following structure.
  7703. */
  7704. typedef struct extract_aux_data extract_aux_data;
  7705. struct extract_aux_data
  7706. {
  7707. jx9_vm *pVm; /* VM that own this instance */
  7708. int iCount; /* Number of variables successfully imported */
  7709. const char *zPrefix; /* Prefix name */
  7710. int Prefixlen; /* Prefix length */
  7711. int iFlags; /* Control flags */
  7712. char zWorker[1024]; /* Working buffer */
  7713. };
  7714. /* Forward declaration */
  7715. static int VmExtractCallback(jx9_value *pKey, jx9_value *pValue, void *pUserData);
  7716. /*
  7717. * int extract(array $var_array[, int $extract_type = EXTR_OVERWRITE[, string $prefix = NULL ]])
  7718. * Import variables into the current symbol table from an array.
  7719. * Parameters
  7720. * $var_array
  7721. * An associative array. This function treats keys as variable names and values
  7722. * as variable values. For each key/value pair it will create a variable in the current symbol
  7723. * table, subject to extract_type and prefix parameters.
  7724. * You must use an associative array; a numerically indexed array will not produce results
  7725. * unless you use EXTR_PREFIX_ALL or EXTR_PREFIX_INVALID.
  7726. * $extract_type
  7727. * The way invalid/numeric keys and collisions are treated is determined by the extract_type.
  7728. * It can be one of the following values:
  7729. * EXTR_OVERWRITE
  7730. * If there is a collision, overwrite the existing variable.
  7731. * EXTR_SKIP
  7732. * If there is a collision, don't overwrite the existing variable.
  7733. * EXTR_PREFIX_SAME
  7734. * If there is a collision, prefix the variable name with prefix.
  7735. * EXTR_PREFIX_ALL
  7736. * Prefix all variable names with prefix.
  7737. * EXTR_PREFIX_INVALID
  7738. * Only prefix invalid/numeric variable names with prefix.
  7739. * EXTR_IF_EXISTS
  7740. * Only overwrite the variable if it already exists in the current symbol table
  7741. * otherwise do nothing.
  7742. * This is useful for defining a list of valid variables and then extracting only those
  7743. * variables you have defined out of $_REQUEST, for example.
  7744. * EXTR_PREFIX_IF_EXISTS
  7745. * Only create prefixed variable names if the non-prefixed version of the same variable exists in
  7746. * the current symbol table.
  7747. * $prefix
  7748. * Note that prefix is only required if extract_type is EXTR_PREFIX_SAME, EXTR_PREFIX_ALL
  7749. * EXTR_PREFIX_INVALID or EXTR_PREFIX_IF_EXISTS. If the prefixed result is not a valid variable name
  7750. * it is not imported into the symbol table. Prefixes are automatically separated from the array key by an
  7751. * underscore character.
  7752. * Return
  7753. * Returns the number of variables successfully imported into the symbol table.
  7754. */
  7755. static int vm_builtin_extract(jx9_context *pCtx, int nArg, jx9_value **apArg)
  7756. {
  7757. extract_aux_data sAux;
  7758. jx9_hashmap *pMap;
  7759. if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
  7760. /* Missing/Invalid arguments, return 0 */
  7761. jx9_result_int(pCtx, 0);
  7762. return JX9_OK;
  7763. }
  7764. /* Point to the target hashmap */
  7765. pMap = (jx9_hashmap *)apArg[0]->x.pOther;
  7766. if( pMap->nEntry < 1 ){
  7767. /* Empty map, return 0 */
  7768. jx9_result_int(pCtx, 0);
  7769. return JX9_OK;
  7770. }
  7771. /* Prepare the aux data */
  7772. SyZero(&sAux, sizeof(extract_aux_data)-sizeof(sAux.zWorker));
  7773. if( nArg > 1 ){
  7774. sAux.iFlags = jx9_value_to_int(apArg[1]);
  7775. if( nArg > 2 ){
  7776. sAux.zPrefix = jx9_value_to_string(apArg[2], &sAux.Prefixlen);
  7777. }
  7778. }
  7779. sAux.pVm = pCtx->pVm;
  7780. /* Invoke the worker callback */
  7781. jx9HashmapWalk(pMap, VmExtractCallback, &sAux);
  7782. /* Number of variables successfully imported */
  7783. jx9_result_int(pCtx, sAux.iCount);
  7784. return JX9_OK;
  7785. }
  7786. /*
  7787. * Worker callback for the [extract()] function defined
  7788. * below.
  7789. */
  7790. static int VmExtractCallback(jx9_value *pKey, jx9_value *pValue, void *pUserData)
  7791. {
  7792. extract_aux_data *pAux = (extract_aux_data *)pUserData;
  7793. int iFlags = pAux->iFlags;
  7794. jx9_vm *pVm = pAux->pVm;
  7795. jx9_value *pObj;
  7796. SyString sVar;
  7797. if( (iFlags & 0x10/* EXTR_PREFIX_INVALID */) && (pKey->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL|MEMOBJ_REAL))){
  7798. iFlags |= 0x08; /*EXTR_PREFIX_ALL*/
  7799. }
  7800. /* Perform a string cast */
  7801. jx9MemObjToString(pKey);
  7802. if( SyBlobLength(&pKey->sBlob) < 1 ){
  7803. /* Unavailable variable name */
  7804. return SXRET_OK;
  7805. }
  7806. sVar.nByte = 0; /* cc warning */
  7807. if( (iFlags & 0x08/*EXTR_PREFIX_ALL*/ ) && pAux->Prefixlen > 0 ){
  7808. sVar.nByte = (sxu32)SyBufferFormat(pAux->zWorker, sizeof(pAux->zWorker), "%.*s_%.*s",
  7809. pAux->Prefixlen, pAux->zPrefix,
  7810. SyBlobLength(&pKey->sBlob), SyBlobData(&pKey->sBlob)
  7811. );
  7812. }else{
  7813. sVar.nByte = (sxu32) SyMemcpy(SyBlobData(&pKey->sBlob), pAux->zWorker,
  7814. SXMIN(SyBlobLength(&pKey->sBlob), sizeof(pAux->zWorker)));
  7815. }
  7816. sVar.zString = pAux->zWorker;
  7817. /* Try to extract the variable */
  7818. pObj = VmExtractMemObj(pVm, &sVar, TRUE, FALSE);
  7819. if( pObj ){
  7820. /* Collision */
  7821. if( iFlags & 0x02 /* EXTR_SKIP */ ){
  7822. return SXRET_OK;
  7823. }
  7824. if( iFlags & 0x04 /* EXTR_PREFIX_SAME */ ){
  7825. if( (iFlags & 0x08/*EXTR_PREFIX_ALL*/) || pAux->Prefixlen < 1){
  7826. /* Already prefixed */
  7827. return SXRET_OK;
  7828. }
  7829. sVar.nByte = SyBufferFormat(
  7830. pAux->zWorker, sizeof(pAux->zWorker),
  7831. "%.*s_%.*s",
  7832. pAux->Prefixlen, pAux->zPrefix,
  7833. SyBlobLength(&pKey->sBlob), SyBlobData(&pKey->sBlob)
  7834. );
  7835. pObj = VmExtractMemObj(pVm, &sVar, TRUE, TRUE);
  7836. }
  7837. }else{
  7838. /* Create the variable */
  7839. pObj = VmExtractMemObj(pVm, &sVar, TRUE, TRUE);
  7840. }
  7841. if( pObj ){
  7842. /* Overwrite the old value */
  7843. jx9MemObjStore(pValue, pObj);
  7844. /* Increment counter */
  7845. pAux->iCount++;
  7846. }
  7847. return SXRET_OK;
  7848. }
  7849. /*
  7850. * Compile and evaluate a JX9 chunk at run-time.
  7851. * Refer to the include language construct implementation for more
  7852. * information.
  7853. */
  7854. static sxi32 VmEvalChunk(
  7855. jx9_vm *pVm, /* Underlying Virtual Machine */
  7856. jx9_context *pCtx, /* Call Context */
  7857. SyString *pChunk, /* JX9 chunk to evaluate */
  7858. int iFlags, /* Compile flag */
  7859. int bTrueReturn /* TRUE to return execution result */
  7860. )
  7861. {
  7862. SySet *pByteCode, aByteCode;
  7863. ProcConsumer xErr = 0;
  7864. void *pErrData = 0;
  7865. /* Initialize bytecode container */
  7866. SySetInit(&aByteCode, &pVm->sAllocator, sizeof(VmInstr));
  7867. SySetAlloc(&aByteCode, 0x20);
  7868. /* Reset the code generator */
  7869. if( bTrueReturn ){
  7870. /* Included file, log compile-time errors */
  7871. xErr = pVm->pEngine->xConf.xErr;
  7872. pErrData = pVm->pEngine->xConf.pErrData;
  7873. }
  7874. jx9ResetCodeGenerator(pVm, xErr, pErrData);
  7875. /* Swap bytecode container */
  7876. pByteCode = pVm->pByteContainer;
  7877. pVm->pByteContainer = &aByteCode;
  7878. /* Compile the chunk */
  7879. jx9CompileScript(pVm, pChunk, iFlags);
  7880. if( pVm->sCodeGen.nErr > 0 ){
  7881. /* Compilation error, return false */
  7882. if( pCtx ){
  7883. jx9_result_bool(pCtx, 0);
  7884. }
  7885. }else{
  7886. jx9_value sResult; /* Return value */
  7887. if( SXRET_OK != jx9VmEmitInstr(pVm, JX9_OP_DONE, 0, 0, 0, 0) ){
  7888. /* Out of memory */
  7889. if( pCtx ){
  7890. jx9_result_bool(pCtx, 0);
  7891. }
  7892. goto Cleanup;
  7893. }
  7894. if( bTrueReturn ){
  7895. /* Assume a boolean true return value */
  7896. jx9MemObjInitFromBool(pVm, &sResult, 1);
  7897. }else{
  7898. /* Assume a null return value */
  7899. jx9MemObjInit(pVm, &sResult);
  7900. }
  7901. /* Execute the compiled chunk */
  7902. VmLocalExec(pVm, &aByteCode, &sResult);
  7903. if( pCtx ){
  7904. /* Set the execution result */
  7905. jx9_result_value(pCtx, &sResult);
  7906. }
  7907. jx9MemObjRelease(&sResult);
  7908. }
  7909. Cleanup:
  7910. /* Cleanup the mess left behind */
  7911. pVm->pByteContainer = pByteCode;
  7912. SySetRelease(&aByteCode);
  7913. return SXRET_OK;
  7914. }
  7915. /*
  7916. * Check if a file path is already included.
  7917. */
  7918. static int VmIsIncludedFile(jx9_vm *pVm, SyString *pFile)
  7919. {
  7920. SyString *aEntries;
  7921. sxu32 n;
  7922. aEntries = (SyString *)SySetBasePtr(&pVm->aIncluded);
  7923. /* Perform a linear search */
  7924. for( n = 0 ; n < SySetUsed(&pVm->aIncluded) ; ++n ){
  7925. if( SyStringCmp(pFile, &aEntries[n], SyMemcmp) == 0 ){
  7926. /* Already included */
  7927. return TRUE;
  7928. }
  7929. }
  7930. return FALSE;
  7931. }
  7932. /*
  7933. * Push a file path in the appropriate VM container.
  7934. */
  7935. JX9_PRIVATE sxi32 jx9VmPushFilePath(jx9_vm *pVm, const char *zPath, int nLen, sxu8 bMain, sxi32 *pNew)
  7936. {
  7937. SyString sPath;
  7938. char *zDup;
  7939. #ifdef __WINNT__
  7940. char *zCur;
  7941. #endif
  7942. sxi32 rc;
  7943. if( nLen < 0 ){
  7944. nLen = SyStrlen(zPath);
  7945. }
  7946. /* Duplicate the file path first */
  7947. zDup = SyMemBackendStrDup(&pVm->sAllocator, zPath, nLen);
  7948. if( zDup == 0 ){
  7949. return SXERR_MEM;
  7950. }
  7951. #ifdef __WINNT__
  7952. /* Normalize path on windows
  7953. * Example:
  7954. * Path/To/File.jx9
  7955. * becomes
  7956. * path\to\file.jx9
  7957. */
  7958. zCur = zDup;
  7959. while( zCur[0] != 0 ){
  7960. if( zCur[0] == '/' ){
  7961. zCur[0] = '\\';
  7962. }else if( (unsigned char)zCur[0] < 0xc0 && SyisUpper(zCur[0]) ){
  7963. int c = SyToLower(zCur[0]);
  7964. zCur[0] = (char)c; /* MSVC stupidity */
  7965. }
  7966. zCur++;
  7967. }
  7968. #endif
  7969. /* Install the file path */
  7970. SyStringInitFromBuf(&sPath, zDup, nLen);
  7971. if( !bMain ){
  7972. if( VmIsIncludedFile(&(*pVm), &sPath) ){
  7973. /* Already included */
  7974. *pNew = 0;
  7975. }else{
  7976. /* Insert in the corresponding container */
  7977. rc = SySetPut(&pVm->aIncluded, (const void *)&sPath);
  7978. if( rc != SXRET_OK ){
  7979. SyMemBackendFree(&pVm->sAllocator, zDup);
  7980. return rc;
  7981. }
  7982. *pNew = 1;
  7983. }
  7984. }
  7985. SySetPut(&pVm->aFiles, (const void *)&sPath);
  7986. return SXRET_OK;
  7987. }
  7988. /*
  7989. * Compile and Execute a JX9 script at run-time.
  7990. * SXRET_OK is returned on sucessful evaluation.Any other return values
  7991. * indicates failure.
  7992. * Note that the JX9 script to evaluate can be a local or remote file.In
  7993. * either cases the [jx9StreamReadWholeFile()] function handle all the underlying
  7994. * operations.
  7995. * If the [jJX9_DISABLE_BUILTIN_FUNC] compile-time directive is defined, then
  7996. * this function is a no-op.
  7997. * Refer to the implementation of the include(), import() language
  7998. * constructs for more information.
  7999. */
  8000. static sxi32 VmExecIncludedFile(
  8001. jx9_context *pCtx, /* Call Context */
  8002. SyString *pPath, /* Script path or URL*/
  8003. int IncludeOnce /* TRUE if called from import() or require_once() */
  8004. )
  8005. {
  8006. sxi32 rc;
  8007. #ifndef JX9_DISABLE_BUILTIN_FUNC
  8008. const jx9_io_stream *pStream;
  8009. SyBlob sContents;
  8010. void *pHandle;
  8011. jx9_vm *pVm;
  8012. int isNew;
  8013. /* Initialize fields */
  8014. pVm = pCtx->pVm;
  8015. SyBlobInit(&sContents, &pVm->sAllocator);
  8016. isNew = 0;
  8017. /* Extract the associated stream */
  8018. pStream = jx9VmGetStreamDevice(pVm, &pPath->zString, pPath->nByte);
  8019. /*
  8020. * Open the file or the URL [i.e: http://jx9.symisc.net/example/hello.jx9.txt"]
  8021. * in a read-only mode.
  8022. */
  8023. pHandle = jx9StreamOpenHandle(pVm, pStream,pPath->zString, JX9_IO_OPEN_RDONLY, TRUE, 0, TRUE, &isNew);
  8024. if( pHandle == 0 ){
  8025. return SXERR_IO;
  8026. }
  8027. rc = SXRET_OK; /* Stupid cc warning */
  8028. if( IncludeOnce && !isNew ){
  8029. /* Already included */
  8030. rc = SXERR_EXISTS;
  8031. }else{
  8032. /* Read the whole file contents */
  8033. rc = jx9StreamReadWholeFile(pHandle, pStream, &sContents);
  8034. if( rc == SXRET_OK ){
  8035. SyString sScript;
  8036. /* Compile and execute the script */
  8037. SyStringInitFromBuf(&sScript, SyBlobData(&sContents), SyBlobLength(&sContents));
  8038. VmEvalChunk(pCtx->pVm, &(*pCtx), &sScript, 0, TRUE);
  8039. }
  8040. }
  8041. /* Pop from the set of included file */
  8042. (void)SySetPop(&pVm->aFiles);
  8043. /* Close the handle */
  8044. jx9StreamCloseHandle(pStream, pHandle);
  8045. /* Release the working buffer */
  8046. SyBlobRelease(&sContents);
  8047. #else
  8048. pCtx = 0; /* cc warning */
  8049. pPath = 0;
  8050. IncludeOnce = 0;
  8051. rc = SXERR_IO;
  8052. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  8053. return rc;
  8054. }
  8055. /* * include:
  8056. * According to the JX9 reference manual.
  8057. * The include() function includes and evaluates the specified file.
  8058. * Files are included based on the file path given or, if none is given
  8059. * the include_path specified.If the file isn't found in the include_path
  8060. * include() will finally check in the calling script's own directory
  8061. * and the current working directory before failing. The include()
  8062. * construct will emit a warning if it cannot find a file; this is different
  8063. * behavior from require(), which will emit a fatal error.
  8064. * If a path is defined &#x2014; whether absolute (starting with a drive letter
  8065. * or \ on Windows, or / on Unix/Linux systems) or relative to the current
  8066. * directory (starting with . or ..) &#x2014; the include_path will be ignored altogether.
  8067. * For example, if a filename begins with ../, the parser will look in the parent
  8068. * directory to find the requested file.
  8069. * When a file is included, the code it contains inherits the variable scope
  8070. * of the line on which the include occurs. Any variables available at that line
  8071. * in the calling file will be available within the called file, from that point forward.
  8072. * However, all functions and objectes defined in the included file have the global scope.
  8073. */
  8074. static int vm_builtin_include(jx9_context *pCtx, int nArg, jx9_value **apArg)
  8075. {
  8076. SyString sFile;
  8077. sxi32 rc;
  8078. if( nArg < 1 ){
  8079. /* Nothing to evaluate, return NULL */
  8080. jx9_result_null(pCtx);
  8081. return SXRET_OK;
  8082. }
  8083. /* File to include */
  8084. sFile.zString = jx9_value_to_string(apArg[0], (int *)&sFile.nByte);
  8085. if( sFile.nByte < 1 ){
  8086. /* Empty string, return NULL */
  8087. jx9_result_null(pCtx);
  8088. return SXRET_OK;
  8089. }
  8090. /* Open, compile and execute the desired script */
  8091. rc = VmExecIncludedFile(&(*pCtx), &sFile, FALSE);
  8092. if( rc != SXRET_OK ){
  8093. /* Emit a warning and return false */
  8094. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, "IO error while importing: '%z'", &sFile);
  8095. jx9_result_bool(pCtx, 0);
  8096. }
  8097. return SXRET_OK;
  8098. }
  8099. /*
  8100. * import:
  8101. * According to the JX9 reference manual.
  8102. * The import() statement includes and evaluates the specified file during
  8103. * the execution of the script. This is a behavior similar to the include()
  8104. * statement, with the only difference being that if the code from a file has already
  8105. * been included, it will not be included again. As the name suggests, it will be included
  8106. * just once.
  8107. */
  8108. static int vm_builtin_import(jx9_context *pCtx, int nArg, jx9_value **apArg)
  8109. {
  8110. SyString sFile;
  8111. sxi32 rc;
  8112. if( nArg < 1 ){
  8113. /* Nothing to evaluate, return NULL */
  8114. jx9_result_null(pCtx);
  8115. return SXRET_OK;
  8116. }
  8117. /* File to include */
  8118. sFile.zString = jx9_value_to_string(apArg[0], (int *)&sFile.nByte);
  8119. if( sFile.nByte < 1 ){
  8120. /* Empty string, return NULL */
  8121. jx9_result_null(pCtx);
  8122. return SXRET_OK;
  8123. }
  8124. /* Open, compile and execute the desired script */
  8125. rc = VmExecIncludedFile(&(*pCtx), &sFile, TRUE);
  8126. if( rc == SXERR_EXISTS ){
  8127. /* File already included, return TRUE */
  8128. jx9_result_bool(pCtx, 1);
  8129. return SXRET_OK;
  8130. }
  8131. if( rc != SXRET_OK ){
  8132. /* Emit a warning and return false */
  8133. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, "IO error while importing: '%z'", &sFile);
  8134. jx9_result_bool(pCtx, 0);
  8135. }
  8136. return SXRET_OK;
  8137. }
  8138. /*
  8139. * Section:
  8140. * Command line arguments processing.
  8141. * Authors:
  8142. * Symisc Systems, devel@symisc.net.
  8143. * Copyright (C) Symisc Systems, http://jx9.symisc.net
  8144. * Status:
  8145. * Stable.
  8146. */
  8147. /*
  8148. * Check if a short option argument [i.e: -c] is available in the command
  8149. * line string. Return a pointer to the start of the stream on success.
  8150. * NULL otherwise.
  8151. */
  8152. static const char * VmFindShortOpt(int c, const char *zIn, const char *zEnd)
  8153. {
  8154. while( zIn < zEnd ){
  8155. if( zIn[0] == '-' && &zIn[1] < zEnd && (int)zIn[1] == c ){
  8156. /* Got one */
  8157. return &zIn[1];
  8158. }
  8159. /* Advance the cursor */
  8160. zIn++;
  8161. }
  8162. /* No such option */
  8163. return 0;
  8164. }
  8165. /*
  8166. * Check if a long option argument [i.e: --opt] is available in the command
  8167. * line string. Return a pointer to the start of the stream on success.
  8168. * NULL otherwise.
  8169. */
  8170. static const char * VmFindLongOpt(const char *zLong, int nByte, const char *zIn, const char *zEnd)
  8171. {
  8172. const char *zOpt;
  8173. while( zIn < zEnd ){
  8174. if( zIn[0] == '-' && &zIn[1] < zEnd && (int)zIn[1] == '-' ){
  8175. zIn += 2;
  8176. zOpt = zIn;
  8177. while( zIn < zEnd && !SyisSpace(zIn[0]) ){
  8178. if( zIn[0] == '=' /* --opt=val */){
  8179. break;
  8180. }
  8181. zIn++;
  8182. }
  8183. /* Test */
  8184. if( (int)(zIn-zOpt) == nByte && SyMemcmp(zOpt, zLong, nByte) == 0 ){
  8185. /* Got one, return it's value */
  8186. return zIn;
  8187. }
  8188. }else{
  8189. zIn++;
  8190. }
  8191. }
  8192. /* No such option */
  8193. return 0;
  8194. }
  8195. /*
  8196. * Long option [i.e: --opt] arguments private data structure.
  8197. */
  8198. struct getopt_long_opt
  8199. {
  8200. const char *zArgIn, *zArgEnd; /* Command line arguments */
  8201. jx9_value *pWorker; /* Worker variable*/
  8202. jx9_value *pArray; /* getopt() return value */
  8203. jx9_context *pCtx; /* Call Context */
  8204. };
  8205. /* Forward declaration */
  8206. static int VmProcessLongOpt(jx9_value *pKey, jx9_value *pValue, void *pUserData);
  8207. /*
  8208. * Extract short or long argument option values.
  8209. */
  8210. static void VmExtractOptArgValue(
  8211. jx9_value *pArray, /* getopt() return value */
  8212. jx9_value *pWorker, /* Worker variable */
  8213. const char *zArg, /* Argument stream */
  8214. const char *zArgEnd, /* End of the argument stream */
  8215. int need_val, /* TRUE to fetch option argument */
  8216. jx9_context *pCtx, /* Call Context */
  8217. const char *zName /* Option name */)
  8218. {
  8219. jx9_value_bool(pWorker, 0);
  8220. if( !need_val ){
  8221. /*
  8222. * Option does not need arguments.
  8223. * Insert the option name and a boolean FALSE.
  8224. */
  8225. jx9_array_add_strkey_elem(pArray, (const char *)zName, pWorker); /* Will make it's own copy */
  8226. }else{
  8227. const char *zCur;
  8228. /* Extract option argument */
  8229. zArg++;
  8230. if( zArg < zArgEnd && zArg[0] == '=' ){
  8231. zArg++;
  8232. }
  8233. while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){
  8234. zArg++;
  8235. }
  8236. if( zArg >= zArgEnd || zArg[0] == '-' ){
  8237. /*
  8238. * Argument not found.
  8239. * Insert the option name and a boolean FALSE.
  8240. */
  8241. jx9_array_add_strkey_elem(pArray, (const char *)zName, pWorker); /* Will make it's own copy */
  8242. return;
  8243. }
  8244. /* Delimit the value */
  8245. zCur = zArg;
  8246. if( zArg[0] == '\'' || zArg[0] == '"' ){
  8247. int d = zArg[0];
  8248. /* Delimt the argument */
  8249. zArg++;
  8250. zCur = zArg;
  8251. while( zArg < zArgEnd ){
  8252. if( zArg[0] == d && zArg[-1] != '\\' ){
  8253. /* Delimiter found, exit the loop */
  8254. break;
  8255. }
  8256. zArg++;
  8257. }
  8258. /* Save the value */
  8259. jx9_value_string(pWorker, zCur, (int)(zArg-zCur));
  8260. if( zArg < zArgEnd ){ zArg++; }
  8261. }else{
  8262. while( zArg < zArgEnd && !SyisSpace(zArg[0]) ){
  8263. zArg++;
  8264. }
  8265. /* Save the value */
  8266. jx9_value_string(pWorker, zCur, (int)(zArg-zCur));
  8267. }
  8268. /*
  8269. * Check if we are dealing with multiple values.
  8270. * If so, create an array to hold them, rather than a scalar variable.
  8271. */
  8272. while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){
  8273. zArg++;
  8274. }
  8275. if( zArg < zArgEnd && zArg[0] != '-' ){
  8276. jx9_value *pOptArg; /* Array of option arguments */
  8277. pOptArg = jx9_context_new_array(pCtx);
  8278. if( pOptArg == 0 ){
  8279. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
  8280. }else{
  8281. /* Insert the first value */
  8282. jx9_array_add_elem(pOptArg, 0, pWorker); /* Will make it's own copy */
  8283. for(;;){
  8284. if( zArg >= zArgEnd || zArg[0] == '-' ){
  8285. /* No more value */
  8286. break;
  8287. }
  8288. /* Delimit the value */
  8289. zCur = zArg;
  8290. if( zArg < zArgEnd && zArg[0] == '\\' ){
  8291. zArg++;
  8292. zCur = zArg;
  8293. }
  8294. while( zArg < zArgEnd && !SyisSpace(zArg[0]) ){
  8295. zArg++;
  8296. }
  8297. /* Reset the string cursor */
  8298. jx9_value_reset_string_cursor(pWorker);
  8299. /* Save the value */
  8300. jx9_value_string(pWorker, zCur, (int)(zArg-zCur));
  8301. /* Insert */
  8302. jx9_array_add_elem(pOptArg, 0, pWorker); /* Will make it's own copy */
  8303. /* Jump trailing white spaces */
  8304. while( zArg < zArgEnd && (unsigned char)zArg[0] < 0xc0 && SyisSpace(zArg[0]) ){
  8305. zArg++;
  8306. }
  8307. }
  8308. /* Insert the option arg array */
  8309. jx9_array_add_strkey_elem(pArray, (const char *)zName, pOptArg); /* Will make it's own copy */
  8310. /* Safely release */
  8311. jx9_context_release_value(pCtx, pOptArg);
  8312. }
  8313. }else{
  8314. /* Single value */
  8315. jx9_array_add_strkey_elem(pArray, (const char *)zName, pWorker); /* Will make it's own copy */
  8316. }
  8317. }
  8318. }
  8319. /*
  8320. * array getopt(string $options[, array $longopts ])
  8321. * Gets options from the command line argument list.
  8322. * Parameters
  8323. * $options
  8324. * Each character in this string will be used as option characters
  8325. * and matched against options passed to the script starting with
  8326. * a single hyphen (-). For example, an option string "x" recognizes
  8327. * an option -x. Only a-z, A-Z and 0-9 are allowed.
  8328. * $longopts
  8329. * An array of options. Each element in this array will be used as option
  8330. * strings and matched against options passed to the script starting with
  8331. * two hyphens (--). For example, an longopts element "opt" recognizes an
  8332. * option --opt.
  8333. * Return
  8334. * This function will return an array of option / argument pairs or FALSE
  8335. * on failure.
  8336. */
  8337. static int vm_builtin_getopt(jx9_context *pCtx, int nArg, jx9_value **apArg)
  8338. {
  8339. const char *zIn, *zEnd, *zArg, *zArgIn, *zArgEnd;
  8340. struct getopt_long_opt sLong;
  8341. jx9_value *pArray, *pWorker;
  8342. SyBlob *pArg;
  8343. int nByte;
  8344. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  8345. /* Missing/Invalid arguments, return FALSE */
  8346. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Missing/Invalid option arguments");
  8347. jx9_result_bool(pCtx, 0);
  8348. return JX9_OK;
  8349. }
  8350. /* Extract option arguments */
  8351. zIn = jx9_value_to_string(apArg[0], &nByte);
  8352. zEnd = &zIn[nByte];
  8353. /* Point to the string representation of the $argv[] array */
  8354. pArg = &pCtx->pVm->sArgv;
  8355. /* Create a new empty array and a worker variable */
  8356. pArray = jx9_context_new_array(pCtx);
  8357. pWorker = jx9_context_new_scalar(pCtx);
  8358. if( pArray == 0 || pWorker == 0 ){
  8359. jx9_context_throw_error(pCtx,JX9_CTX_ERR, "JX9 is running out of memory");
  8360. jx9_result_bool(pCtx, 0);
  8361. return JX9_OK;
  8362. }
  8363. if( SyBlobLength(pArg) < 1 ){
  8364. /* Empty command line, return the empty array*/
  8365. jx9_result_value(pCtx, pArray);
  8366. /* Everything will be released automatically when we return
  8367. * from this function.
  8368. */
  8369. return JX9_OK;
  8370. }
  8371. zArgIn = (const char *)SyBlobData(pArg);
  8372. zArgEnd = &zArgIn[SyBlobLength(pArg)];
  8373. /* Fill the long option structure */
  8374. sLong.pArray = pArray;
  8375. sLong.pWorker = pWorker;
  8376. sLong.zArgIn = zArgIn;
  8377. sLong.zArgEnd = zArgEnd;
  8378. sLong.pCtx = pCtx;
  8379. /* Start processing */
  8380. while( zIn < zEnd ){
  8381. int c = zIn[0];
  8382. int need_val = 0;
  8383. /* Advance the stream cursor */
  8384. zIn++;
  8385. /* Ignore non-alphanum characters */
  8386. if( !SyisAlphaNum(c) ){
  8387. continue;
  8388. }
  8389. if( zIn < zEnd && zIn[0] == ':' ){
  8390. zIn++;
  8391. need_val = 1;
  8392. if( zIn < zEnd && zIn[0] == ':' ){
  8393. zIn++;
  8394. }
  8395. }
  8396. /* Find option */
  8397. zArg = VmFindShortOpt(c, zArgIn, zArgEnd);
  8398. if( zArg == 0 ){
  8399. /* No such option */
  8400. continue;
  8401. }
  8402. /* Extract option argument value */
  8403. VmExtractOptArgValue(pArray, pWorker, zArg, zArgEnd, need_val, pCtx, (const char *)&c);
  8404. }
  8405. if( nArg > 1 && jx9_value_is_json_array(apArg[1]) && jx9_array_count(apArg[1]) > 0 ){
  8406. /* Process long options */
  8407. jx9_array_walk(apArg[1], VmProcessLongOpt, &sLong);
  8408. }
  8409. /* Return the option array */
  8410. jx9_result_value(pCtx, pArray);
  8411. /*
  8412. * Don't worry about freeing memory, everything will be released
  8413. * automatically as soon we return from this foreign function.
  8414. */
  8415. return JX9_OK;
  8416. }
  8417. /*
  8418. * Array walker callback used for processing long options values.
  8419. */
  8420. static int VmProcessLongOpt(jx9_value *pKey, jx9_value *pValue, void *pUserData)
  8421. {
  8422. struct getopt_long_opt *pOpt = (struct getopt_long_opt *)pUserData;
  8423. const char *zArg, *zOpt, *zEnd;
  8424. int need_value = 0;
  8425. int nByte;
  8426. /* Value must be of type string */
  8427. if( !jx9_value_is_string(pValue) ){
  8428. /* Simply ignore */
  8429. return JX9_OK;
  8430. }
  8431. zOpt = jx9_value_to_string(pValue, &nByte);
  8432. if( nByte < 1 ){
  8433. /* Empty string, ignore */
  8434. return JX9_OK;
  8435. }
  8436. zEnd = &zOpt[nByte - 1];
  8437. if( zEnd[0] == ':' ){
  8438. char *zTerm;
  8439. /* Try to extract a value */
  8440. need_value = 1;
  8441. while( zEnd >= zOpt && zEnd[0] == ':' ){
  8442. zEnd--;
  8443. }
  8444. if( zOpt >= zEnd ){
  8445. /* Empty string, ignore */
  8446. SXUNUSED(pKey);
  8447. return JX9_OK;
  8448. }
  8449. zEnd++;
  8450. zTerm = (char *)zEnd;
  8451. zTerm[0] = 0;
  8452. }else{
  8453. zEnd = &zOpt[nByte];
  8454. }
  8455. /* Find the option */
  8456. zArg = VmFindLongOpt(zOpt, (int)(zEnd-zOpt), pOpt->zArgIn, pOpt->zArgEnd);
  8457. if( zArg == 0 ){
  8458. /* No such option, return immediately */
  8459. return JX9_OK;
  8460. }
  8461. /* Try to extract a value */
  8462. VmExtractOptArgValue(pOpt->pArray, pOpt->pWorker, zArg, pOpt->zArgEnd, need_value, pOpt->pCtx, zOpt);
  8463. return JX9_OK;
  8464. }
  8465. /*
  8466. * int utf8_encode(string $input)
  8467. * UTF-8 encoding.
  8468. * This function encodes the string data to UTF-8, and returns the encoded version.
  8469. * UTF-8 is a standard mechanism used by Unicode for encoding wide character values
  8470. * into a byte stream. UTF-8 is transparent to plain ASCII characters, is self-synchronized
  8471. * (meaning it is possible for a program to figure out where in the bytestream characters start)
  8472. * and can be used with normal string comparison functions for sorting and such.
  8473. * Notes on UTF-8 (According to SQLite3 authors):
  8474. * Byte-0 Byte-1 Byte-2 Byte-3 Value
  8475. * 0xxxxxxx 00000000 00000000 0xxxxxxx
  8476. * 110yyyyy 10xxxxxx 00000000 00000yyy yyxxxxxx
  8477. * 1110zzzz 10yyyyyy 10xxxxxx 00000000 zzzzyyyy yyxxxxxx
  8478. * 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 000uuuuu zzzzyyyy yyxxxxxx
  8479. * Parameters
  8480. * $input
  8481. * String to encode or NULL on failure.
  8482. * Return
  8483. * An UTF-8 encoded string.
  8484. */
  8485. static int vm_builtin_utf8_encode(jx9_context *pCtx, int nArg, jx9_value **apArg)
  8486. {
  8487. const unsigned char *zIn, *zEnd;
  8488. int nByte, c, e;
  8489. if( nArg < 1 ){
  8490. /* Missing arguments, return null */
  8491. jx9_result_null(pCtx);
  8492. return JX9_OK;
  8493. }
  8494. /* Extract the target string */
  8495. zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nByte);
  8496. if( nByte < 1 ){
  8497. /* Empty string, return null */
  8498. jx9_result_null(pCtx);
  8499. return JX9_OK;
  8500. }
  8501. zEnd = &zIn[nByte];
  8502. /* Start the encoding process */
  8503. for(;;){
  8504. if( zIn >= zEnd ){
  8505. /* End of input */
  8506. break;
  8507. }
  8508. c = zIn[0];
  8509. /* Advance the stream cursor */
  8510. zIn++;
  8511. /* Encode */
  8512. if( c<0x00080 ){
  8513. e = (c&0xFF);
  8514. jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
  8515. }else if( c<0x00800 ){
  8516. e = 0xC0 + ((c>>6)&0x1F);
  8517. jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
  8518. e = 0x80 + (c & 0x3F);
  8519. jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
  8520. }else if( c<0x10000 ){
  8521. e = 0xE0 + ((c>>12)&0x0F);
  8522. jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
  8523. e = 0x80 + ((c>>6) & 0x3F);
  8524. jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
  8525. e = 0x80 + (c & 0x3F);
  8526. jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
  8527. }else{
  8528. e = 0xF0 + ((c>>18) & 0x07);
  8529. jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
  8530. e = 0x80 + ((c>>12) & 0x3F);
  8531. jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
  8532. e = 0x80 + ((c>>6) & 0x3F);
  8533. jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
  8534. e = 0x80 + (c & 0x3F);
  8535. jx9_result_string(pCtx, (const char *)&e, (int)sizeof(char));
  8536. }
  8537. }
  8538. /* All done */
  8539. return JX9_OK;
  8540. }
  8541. /*
  8542. * UTF-8 decoding routine extracted from the sqlite3 source tree.
  8543. * Original author: D. Richard Hipp (http://www.sqlite.org)
  8544. * Status: Public Domain
  8545. */
  8546. /*
  8547. ** This lookup table is used to help decode the first byte of
  8548. ** a multi-byte UTF8 character.
  8549. */
  8550. static const unsigned char UtfTrans1[] = {
  8551. 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
  8552. 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
  8553. 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
  8554. 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
  8555. 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
  8556. 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
  8557. 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
  8558. 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
  8559. };
  8560. /*
  8561. ** Translate a single UTF-8 character. Return the unicode value.
  8562. **
  8563. ** During translation, assume that the byte that zTerm points
  8564. ** is a 0x00.
  8565. **
  8566. ** Write a pointer to the next unread byte back into *pzNext.
  8567. **
  8568. ** Notes On Invalid UTF-8:
  8569. **
  8570. ** * This routine never allows a 7-bit character (0x00 through 0x7f) to
  8571. ** be encoded as a multi-byte character. Any multi-byte character that
  8572. ** attempts to encode a value between 0x00 and 0x7f is rendered as 0xfffd.
  8573. **
  8574. ** * This routine never allows a UTF16 surrogate value to be encoded.
  8575. ** If a multi-byte character attempts to encode a value between
  8576. ** 0xd800 and 0xe000 then it is rendered as 0xfffd.
  8577. **
  8578. ** * Bytes in the range of 0x80 through 0xbf which occur as the first
  8579. ** byte of a character are interpreted as single-byte characters
  8580. ** and rendered as themselves even though they are technically
  8581. ** invalid characters.
  8582. **
  8583. ** * This routine accepts an infinite number of different UTF8 encodings
  8584. ** for unicode values 0x80 and greater. It do not change over-length
  8585. ** encodings to 0xfffd as some systems recommend.
  8586. */
  8587. #define READ_UTF8(zIn, zTerm, c) \
  8588. c = *(zIn++); \
  8589. if( c>=0xc0 ){ \
  8590. c = UtfTrans1[c-0xc0]; \
  8591. while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \
  8592. c = (c<<6) + (0x3f & *(zIn++)); \
  8593. } \
  8594. if( c<0x80 \
  8595. || (c&0xFFFFF800)==0xD800 \
  8596. || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
  8597. }
  8598. JX9_PRIVATE int jx9Utf8Read(
  8599. const unsigned char *z, /* First byte of UTF-8 character */
  8600. const unsigned char *zTerm, /* Pretend this byte is 0x00 */
  8601. const unsigned char **pzNext /* Write first byte past UTF-8 char here */
  8602. ){
  8603. int c;
  8604. READ_UTF8(z, zTerm, c);
  8605. *pzNext = z;
  8606. return c;
  8607. }
  8608. /*
  8609. * string utf8_decode(string $data)
  8610. * This function decodes data, assumed to be UTF-8 encoded, to unicode.
  8611. * Parameters
  8612. * data
  8613. * An UTF-8 encoded string.
  8614. * Return
  8615. * Unicode decoded string or NULL on failure.
  8616. */
  8617. static int vm_builtin_utf8_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
  8618. {
  8619. const unsigned char *zIn, *zEnd;
  8620. int nByte, c;
  8621. if( nArg < 1 ){
  8622. /* Missing arguments, return null */
  8623. jx9_result_null(pCtx);
  8624. return JX9_OK;
  8625. }
  8626. /* Extract the target string */
  8627. zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nByte);
  8628. if( nByte < 1 ){
  8629. /* Empty string, return null */
  8630. jx9_result_null(pCtx);
  8631. return JX9_OK;
  8632. }
  8633. zEnd = &zIn[nByte];
  8634. /* Start the decoding process */
  8635. while( zIn < zEnd ){
  8636. c = jx9Utf8Read(zIn, zEnd, &zIn);
  8637. if( c == 0x0 ){
  8638. break;
  8639. }
  8640. jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
  8641. }
  8642. return JX9_OK;
  8643. }
  8644. /*
  8645. * string json_encode(mixed $value)
  8646. * Returns a string containing the JSON representation of value.
  8647. * Parameters
  8648. * $value
  8649. * The value being encoded. Can be any type except a resource.
  8650. * Return
  8651. * Returns a JSON encoded string on success. FALSE otherwise
  8652. */
  8653. static int vm_builtin_json_encode(jx9_context *pCtx,int nArg,jx9_value **apArg)
  8654. {
  8655. SyBlob sBlob;
  8656. if( nArg < 1 ){
  8657. /* Missing arguments, return FALSE */
  8658. jx9_result_bool(pCtx, 0);
  8659. return JX9_OK;
  8660. }
  8661. /* Init the working buffer */
  8662. SyBlobInit(&sBlob,&pCtx->pVm->sAllocator);
  8663. /* Perform the encoding operation */
  8664. jx9JsonSerialize(apArg[0],&sBlob);
  8665. /* Return the serialized value */
  8666. jx9_result_string(pCtx,(const char *)SyBlobData(&sBlob),(int)SyBlobLength(&sBlob));
  8667. /* Cleanup */
  8668. SyBlobRelease(&sBlob);
  8669. /* All done */
  8670. return JX9_OK;
  8671. }
  8672. /*
  8673. * mixed json_decode(string $json)
  8674. * Takes a JSON encoded string and converts it into a JX9 variable.
  8675. * Parameters
  8676. * $json
  8677. * The json string being decoded.
  8678. * Return
  8679. * The value encoded in json in appropriate JX9 type. Values true, false and null (case-insensitive)
  8680. * are returned as TRUE, FALSE and NULL respectively. NULL is returned if the json cannot be decoded
  8681. * or if the encoded data is deeper than the recursion limit.
  8682. */
  8683. static int vm_builtin_json_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
  8684. {
  8685. const char *zJSON;
  8686. int nByte;
  8687. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  8688. /* Missing/Invalid arguments, return NULL */
  8689. jx9_result_null(pCtx);
  8690. return JX9_OK;
  8691. }
  8692. /* Extract the JSON string */
  8693. zJSON = jx9_value_to_string(apArg[0], &nByte);
  8694. if( nByte < 1 ){
  8695. /* Empty string, return NULL */
  8696. jx9_result_null(pCtx);
  8697. return JX9_OK;
  8698. }
  8699. /* Decode the raw JSON */
  8700. jx9JsonDecode(pCtx,zJSON,nByte);
  8701. return JX9_OK;
  8702. }
  8703. /* Table of built-in VM functions. */
  8704. static const jx9_builtin_func aVmFunc[] = {
  8705. /* JSON Encoding/Decoding */
  8706. { "json_encode", vm_builtin_json_encode },
  8707. { "json_decode", vm_builtin_json_decode },
  8708. /* Functions calls */
  8709. { "func_num_args" , vm_builtin_func_num_args },
  8710. { "func_get_arg" , vm_builtin_func_get_arg },
  8711. { "func_get_args" , vm_builtin_func_get_args },
  8712. { "function_exists", vm_builtin_func_exists },
  8713. { "is_callable" , vm_builtin_is_callable },
  8714. { "get_defined_functions", vm_builtin_get_defined_func },
  8715. /* Constants management */
  8716. { "defined", vm_builtin_defined },
  8717. { "get_defined_constants", vm_builtin_get_defined_constants },
  8718. /* Random numbers/strings generators */
  8719. { "rand", vm_builtin_rand },
  8720. { "rand_str", vm_builtin_rand_str },
  8721. { "getrandmax", vm_builtin_getrandmax },
  8722. /* Language constructs functions */
  8723. { "print", vm_builtin_print },
  8724. { "exit", vm_builtin_exit },
  8725. { "die", vm_builtin_exit },
  8726. /* Variable handling functions */
  8727. { "gettype", vm_builtin_gettype },
  8728. { "get_resource_type", vm_builtin_get_resource_type},
  8729. /* Variable dumping */
  8730. { "dump", vm_builtin_dump },
  8731. /* Release info */
  8732. {"jx9_version", vm_builtin_jx9_version },
  8733. {"jx9_credits", vm_builtin_jx9_version },
  8734. {"jx9_info", vm_builtin_jx9_version },
  8735. {"jx9_copyright", vm_builtin_jx9_version },
  8736. /* hashmap */
  8737. {"extract", vm_builtin_extract },
  8738. /* URL related function */
  8739. {"parse_url", vm_builtin_parse_url },
  8740. /* UTF-8 encoding/decoding */
  8741. {"utf8_encode", vm_builtin_utf8_encode},
  8742. {"utf8_decode", vm_builtin_utf8_decode},
  8743. /* Command line processing */
  8744. {"getopt", vm_builtin_getopt },
  8745. /* Files/URI inclusion facility */
  8746. { "include", vm_builtin_include },
  8747. { "import", vm_builtin_import }
  8748. };
  8749. /*
  8750. * Register the built-in VM functions defined above.
  8751. */
  8752. static sxi32 VmRegisterSpecialFunction(jx9_vm *pVm)
  8753. {
  8754. sxi32 rc;
  8755. sxu32 n;
  8756. for( n = 0 ; n < SX_ARRAYSIZE(aVmFunc) ; ++n ){
  8757. /* Note that these special functions have access
  8758. * to the underlying virtual machine as their
  8759. * private data.
  8760. */
  8761. rc = jx9_create_function(&(*pVm), aVmFunc[n].zName, aVmFunc[n].xFunc, &(*pVm));
  8762. if( rc != SXRET_OK ){
  8763. return rc;
  8764. }
  8765. }
  8766. return SXRET_OK;
  8767. }
  8768. #ifndef JX9_DISABLE_BUILTIN_FUNC
  8769. /*
  8770. * Extract the IO stream device associated with a given scheme.
  8771. * Return a pointer to an instance of jx9_io_stream when the scheme
  8772. * have an associated IO stream registered with it. NULL otherwise.
  8773. * If no scheme:// is avalilable then the file:// scheme is assumed.
  8774. * For more information on how to register IO stream devices, please
  8775. * refer to the official documentation.
  8776. */
  8777. JX9_PRIVATE const jx9_io_stream * jx9VmGetStreamDevice(
  8778. jx9_vm *pVm, /* Target VM */
  8779. const char **pzDevice, /* Full path, URI, ... */
  8780. int nByte /* *pzDevice length*/
  8781. )
  8782. {
  8783. const char *zIn, *zEnd, *zCur, *zNext;
  8784. jx9_io_stream **apStream, *pStream;
  8785. SyString sDev, sCur;
  8786. sxu32 n, nEntry;
  8787. int rc;
  8788. /* Check if a scheme [i.e: file://, http://, zip://...] is available */
  8789. zNext = zCur = zIn = *pzDevice;
  8790. zEnd = &zIn[nByte];
  8791. while( zIn < zEnd ){
  8792. if( zIn < &zEnd[-3]/*://*/ && zIn[0] == ':' && zIn[1] == '/' && zIn[2] == '/' ){
  8793. /* Got one */
  8794. zNext = &zIn[sizeof("://")-1];
  8795. break;
  8796. }
  8797. /* Advance the cursor */
  8798. zIn++;
  8799. }
  8800. if( zIn >= zEnd ){
  8801. /* No such scheme, return the default stream */
  8802. return pVm->pDefStream;
  8803. }
  8804. SyStringInitFromBuf(&sDev, zCur, zIn-zCur);
  8805. /* Remove leading and trailing white spaces */
  8806. SyStringFullTrim(&sDev);
  8807. /* Perform a linear lookup on the installed stream devices */
  8808. apStream = (jx9_io_stream **)SySetBasePtr(&pVm->aIOstream);
  8809. nEntry = SySetUsed(&pVm->aIOstream);
  8810. for( n = 0 ; n < nEntry ; n++ ){
  8811. pStream = apStream[n];
  8812. SyStringInitFromBuf(&sCur, pStream->zName, SyStrlen(pStream->zName));
  8813. /* Perfrom a case-insensitive comparison */
  8814. rc = SyStringCmp(&sDev, &sCur, SyStrnicmp);
  8815. if( rc == 0 ){
  8816. /* Stream device found */
  8817. *pzDevice = zNext;
  8818. return pStream;
  8819. }
  8820. }
  8821. /* No such stream, return NULL */
  8822. return 0;
  8823. }
  8824. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  8825. /*
  8826. * Section:
  8827. * HTTP/URI related routines.
  8828. * Authors:
  8829. * Symisc Systems, devel@symisc.net.
  8830. * Copyright (C) Symisc Systems, http://jx9.symisc.net
  8831. * Status:
  8832. * Stable.
  8833. */
  8834. /*
  8835. * URI Parser: Split an URI into components [i.e: Host, Path, Query, ...].
  8836. * URI syntax: [method:/][/[user[:pwd]@]host[:port]/][document]
  8837. * This almost, but not quite, RFC1738 URI syntax.
  8838. * This routine is not a validator, it does not check for validity
  8839. * nor decode URI parts, the only thing this routine does is splitting
  8840. * the input to its fields.
  8841. * Upper layer are responsible of decoding and validating URI parts.
  8842. * On success, this function populate the "SyhttpUri" structure passed
  8843. * as the first argument. Otherwise SXERR_* is returned when a malformed
  8844. * input is encountered.
  8845. */
  8846. static sxi32 VmHttpSplitURI(SyhttpUri *pOut, const char *zUri, sxu32 nLen)
  8847. {
  8848. const char *zEnd = &zUri[nLen];
  8849. sxu8 bHostOnly = FALSE;
  8850. sxu8 bIPv6 = FALSE ;
  8851. const char *zCur;
  8852. SyString *pComp;
  8853. sxu32 nPos = 0;
  8854. sxi32 rc;
  8855. /* Zero the structure first */
  8856. SyZero(pOut, sizeof(SyhttpUri));
  8857. /* Remove leading and trailing white spaces */
  8858. SyStringInitFromBuf(&pOut->sRaw, zUri, nLen);
  8859. SyStringFullTrim(&pOut->sRaw);
  8860. /* Find the first '/' separator */
  8861. rc = SyByteFind(zUri, (sxu32)(zEnd - zUri), '/', &nPos);
  8862. if( rc != SXRET_OK ){
  8863. /* Assume a host name only */
  8864. zCur = zEnd;
  8865. bHostOnly = TRUE;
  8866. goto ProcessHost;
  8867. }
  8868. zCur = &zUri[nPos];
  8869. if( zUri != zCur && zCur[-1] == ':' ){
  8870. /* Extract a scheme:
  8871. * Not that we can get an invalid scheme here.
  8872. * Fortunately the caller can discard any URI by comparing this scheme with its
  8873. * registered schemes and will report the error as soon as his comparison function
  8874. * fail.
  8875. */
  8876. pComp = &pOut->sScheme;
  8877. SyStringInitFromBuf(pComp, zUri, (sxu32)(zCur - zUri - 1));
  8878. SyStringLeftTrim(pComp);
  8879. }
  8880. if( zCur[1] != '/' ){
  8881. if( zCur == zUri || zCur[-1] == ':' ){
  8882. /* No authority */
  8883. goto PathSplit;
  8884. }
  8885. /* There is something here , we will assume its an authority
  8886. * and someone has forgot the two prefix slashes "//",
  8887. * sooner or later we will detect if we are dealing with a malicious
  8888. * user or not, but now assume we are dealing with an authority
  8889. * and let the caller handle all the validation process.
  8890. */
  8891. goto ProcessHost;
  8892. }
  8893. zUri = &zCur[2];
  8894. zCur = zEnd;
  8895. rc = SyByteFind(zUri, (sxu32)(zEnd - zUri), '/', &nPos);
  8896. if( rc == SXRET_OK ){
  8897. zCur = &zUri[nPos];
  8898. }
  8899. ProcessHost:
  8900. /* Extract user information if present */
  8901. rc = SyByteFind(zUri, (sxu32)(zCur - zUri), '@', &nPos);
  8902. if( rc == SXRET_OK ){
  8903. if( nPos > 0 ){
  8904. sxu32 nPassOfft; /* Password offset */
  8905. pComp = &pOut->sUser;
  8906. SyStringInitFromBuf(pComp, zUri, nPos);
  8907. /* Extract the password if available */
  8908. rc = SyByteFind(zUri, (sxu32)(zCur - zUri), ':', &nPassOfft);
  8909. if( rc == SXRET_OK && nPassOfft < nPos){
  8910. pComp->nByte = nPassOfft;
  8911. pComp = &pOut->sPass;
  8912. pComp->zString = &zUri[nPassOfft+sizeof(char)];
  8913. pComp->nByte = nPos - nPassOfft - 1;
  8914. }
  8915. /* Update the cursor */
  8916. zUri = &zUri[nPos+1];
  8917. }else{
  8918. zUri++;
  8919. }
  8920. }
  8921. pComp = &pOut->sHost;
  8922. while( zUri < zCur && SyisSpace(zUri[0])){
  8923. zUri++;
  8924. }
  8925. SyStringInitFromBuf(pComp, zUri, (sxu32)(zCur - zUri));
  8926. if( pComp->zString[0] == '[' ){
  8927. /* An IPv6 Address: Make a simple naive test
  8928. */
  8929. zUri++; pComp->zString++; pComp->nByte = 0;
  8930. while( ((unsigned char)zUri[0] < 0xc0 && SyisHex(zUri[0])) || zUri[0] == ':' ){
  8931. zUri++; pComp->nByte++;
  8932. }
  8933. if( zUri[0] != ']' ){
  8934. return SXERR_CORRUPT; /* Malformed IPv6 address */
  8935. }
  8936. zUri++;
  8937. bIPv6 = TRUE;
  8938. }
  8939. /* Extract a port number if available */
  8940. rc = SyByteFind(zUri, (sxu32)(zCur - zUri), ':', &nPos);
  8941. if( rc == SXRET_OK ){
  8942. if( bIPv6 == FALSE ){
  8943. pComp->nByte = (sxu32)(&zUri[nPos] - zUri);
  8944. }
  8945. pComp = &pOut->sPort;
  8946. SyStringInitFromBuf(pComp, &zUri[nPos+1], (sxu32)(zCur - &zUri[nPos+1]));
  8947. }
  8948. if( bHostOnly == TRUE ){
  8949. return SXRET_OK;
  8950. }
  8951. PathSplit:
  8952. zUri = zCur;
  8953. pComp = &pOut->sPath;
  8954. SyStringInitFromBuf(pComp, zUri, (sxu32)(zEnd-zUri));
  8955. if( pComp->nByte == 0 ){
  8956. return SXRET_OK; /* Empty path */
  8957. }
  8958. if( SXRET_OK == SyByteFind(zUri, (sxu32)(zEnd-zUri), '?', &nPos) ){
  8959. pComp->nByte = nPos; /* Update path length */
  8960. pComp = &pOut->sQuery;
  8961. SyStringInitFromBuf(pComp, &zUri[nPos+1], (sxu32)(zEnd-&zUri[nPos+1]));
  8962. }
  8963. if( SXRET_OK == SyByteFind(zUri, (sxu32)(zEnd-zUri), '#', &nPos) ){
  8964. /* Update path or query length */
  8965. if( pComp == &pOut->sPath ){
  8966. pComp->nByte = nPos;
  8967. }else{
  8968. if( &zUri[nPos] < (char *)SyStringData(pComp) ){
  8969. /* Malformed syntax : Query must be present before fragment */
  8970. return SXERR_SYNTAX;
  8971. }
  8972. pComp->nByte -= (sxu32)(zEnd - &zUri[nPos]);
  8973. }
  8974. pComp = &pOut->sFragment;
  8975. SyStringInitFromBuf(pComp, &zUri[nPos+1], (sxu32)(zEnd-&zUri[nPos+1]))
  8976. }
  8977. return SXRET_OK;
  8978. }
  8979. /*
  8980. * Extract a single line from a raw HTTP request.
  8981. * Return SXRET_OK on success, SXERR_EOF when end of input
  8982. * and SXERR_MORE when more input is needed.
  8983. */
  8984. static sxi32 VmGetNextLine(SyString *pCursor, SyString *pCurrent)
  8985. {
  8986. const char *zIn;
  8987. sxu32 nPos;
  8988. /* Jump leading white spaces */
  8989. SyStringLeftTrim(pCursor);
  8990. if( pCursor->nByte < 1 ){
  8991. SyStringInitFromBuf(pCurrent, 0, 0);
  8992. return SXERR_EOF; /* End of input */
  8993. }
  8994. zIn = SyStringData(pCursor);
  8995. if( SXRET_OK != SyByteListFind(pCursor->zString, pCursor->nByte, "\r\n", &nPos) ){
  8996. /* Line not found, tell the caller to read more input from source */
  8997. SyStringDupPtr(pCurrent, pCursor);
  8998. return SXERR_MORE;
  8999. }
  9000. pCurrent->zString = zIn;
  9001. pCurrent->nByte = nPos;
  9002. /* advance the cursor so we can call this routine again */
  9003. pCursor->zString = &zIn[nPos];
  9004. pCursor->nByte -= nPos;
  9005. return SXRET_OK;
  9006. }
  9007. /*
  9008. * Split a single MIME header into a name value pair.
  9009. * This function return SXRET_OK, SXERR_CONTINUE on success.
  9010. * Otherwise SXERR_NEXT is returned when a malformed header
  9011. * is encountered.
  9012. * Note: This function handle also mult-line headers.
  9013. */
  9014. static sxi32 VmHttpProcessOneHeader(SyhttpHeader *pHdr, SyhttpHeader *pLast, const char *zLine, sxu32 nLen)
  9015. {
  9016. SyString *pName;
  9017. sxu32 nPos;
  9018. sxi32 rc;
  9019. if( nLen < 1 ){
  9020. return SXERR_NEXT;
  9021. }
  9022. /* Check for multi-line header */
  9023. if( pLast && (zLine[-1] == ' ' || zLine[-1] == '\t') ){
  9024. SyString *pTmp = &pLast->sValue;
  9025. SyStringFullTrim(pTmp);
  9026. if( pTmp->nByte == 0 ){
  9027. SyStringInitFromBuf(pTmp, zLine, nLen);
  9028. }else{
  9029. /* Update header value length */
  9030. pTmp->nByte = (sxu32)(&zLine[nLen] - pTmp->zString);
  9031. }
  9032. /* Simply tell the caller to reset its states and get another line */
  9033. return SXERR_CONTINUE;
  9034. }
  9035. /* Split the header */
  9036. pName = &pHdr->sName;
  9037. rc = SyByteFind(zLine, nLen, ':', &nPos);
  9038. if(rc != SXRET_OK ){
  9039. return SXERR_NEXT; /* Malformed header;Check the next entry */
  9040. }
  9041. SyStringInitFromBuf(pName, zLine, nPos);
  9042. SyStringFullTrim(pName);
  9043. /* Extract a header value */
  9044. SyStringInitFromBuf(&pHdr->sValue, &zLine[nPos + 1], nLen - nPos - 1);
  9045. /* Remove leading and trailing whitespaces */
  9046. SyStringFullTrim(&pHdr->sValue);
  9047. return SXRET_OK;
  9048. }
  9049. /*
  9050. * Extract all MIME headers associated with a HTTP request.
  9051. * After processing the first line of a HTTP request, the following
  9052. * routine is called in order to extract MIME headers.
  9053. * This function return SXRET_OK on success, SXERR_MORE when it needs
  9054. * more inputs.
  9055. * Note: Any malformed header is simply discarded.
  9056. */
  9057. static sxi32 VmHttpExtractHeaders(SyString *pRequest, SySet *pOut)
  9058. {
  9059. SyhttpHeader *pLast = 0;
  9060. SyString sCurrent;
  9061. SyhttpHeader sHdr;
  9062. sxu8 bEol;
  9063. sxi32 rc;
  9064. if( SySetUsed(pOut) > 0 ){
  9065. pLast = (SyhttpHeader *)SySetAt(pOut, SySetUsed(pOut)-1);
  9066. }
  9067. bEol = FALSE;
  9068. for(;;){
  9069. SyZero(&sHdr, sizeof(SyhttpHeader));
  9070. /* Extract a single line from the raw HTTP request */
  9071. rc = VmGetNextLine(pRequest, &sCurrent);
  9072. if(rc != SXRET_OK ){
  9073. if( sCurrent.nByte < 1 ){
  9074. break;
  9075. }
  9076. bEol = TRUE;
  9077. }
  9078. /* Process the header */
  9079. if( SXRET_OK == VmHttpProcessOneHeader(&sHdr, pLast, sCurrent.zString, sCurrent.nByte)){
  9080. if( SXRET_OK != SySetPut(pOut, (const void *)&sHdr) ){
  9081. break;
  9082. }
  9083. /* Retrieve the last parsed header so we can handle multi-line header
  9084. * in case we face one of them.
  9085. */
  9086. pLast = (SyhttpHeader *)SySetPeek(pOut);
  9087. }
  9088. if( bEol ){
  9089. break;
  9090. }
  9091. } /* for(;;) */
  9092. return SXRET_OK;
  9093. }
  9094. /*
  9095. * Process the first line of a HTTP request.
  9096. * This routine perform the following operations
  9097. * 1) Extract the HTTP method.
  9098. * 2) Split the request URI to it's fields [ie: host, path, query, ...].
  9099. * 3) Extract the HTTP protocol version.
  9100. */
  9101. static sxi32 VmHttpProcessFirstLine(
  9102. SyString *pRequest, /* Raw HTTP request */
  9103. sxi32 *pMethod, /* OUT: HTTP method */
  9104. SyhttpUri *pUri, /* OUT: Parse of the URI */
  9105. sxi32 *pProto /* OUT: HTTP protocol */
  9106. )
  9107. {
  9108. static const char *azMethods[] = { "get", "post", "head", "put"};
  9109. static const sxi32 aMethods[] = { HTTP_METHOD_GET, HTTP_METHOD_POST, HTTP_METHOD_HEAD, HTTP_METHOD_PUT};
  9110. const char *zIn, *zEnd, *zPtr;
  9111. SyString sLine;
  9112. sxu32 nLen;
  9113. sxi32 rc;
  9114. /* Extract the first line and update the pointer */
  9115. rc = VmGetNextLine(pRequest, &sLine);
  9116. if( rc != SXRET_OK ){
  9117. return rc;
  9118. }
  9119. if ( sLine.nByte < 1 ){
  9120. /* Empty HTTP request */
  9121. return SXERR_EMPTY;
  9122. }
  9123. /* Delimit the line and ignore trailing and leading white spaces */
  9124. zIn = sLine.zString;
  9125. zEnd = &zIn[sLine.nByte];
  9126. while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
  9127. zIn++;
  9128. }
  9129. /* Extract the HTTP method */
  9130. zPtr = zIn;
  9131. while( zIn < zEnd && !SyisSpace(zIn[0]) ){
  9132. zIn++;
  9133. }
  9134. *pMethod = HTTP_METHOD_OTHR;
  9135. if( zIn > zPtr ){
  9136. sxu32 i;
  9137. nLen = (sxu32)(zIn-zPtr);
  9138. for( i = 0 ; i < SX_ARRAYSIZE(azMethods) ; ++i ){
  9139. if( SyStrnicmp(azMethods[i], zPtr, nLen) == 0 ){
  9140. *pMethod = aMethods[i];
  9141. break;
  9142. }
  9143. }
  9144. }
  9145. /* Jump trailing white spaces */
  9146. while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
  9147. zIn++;
  9148. }
  9149. /* Extract the request URI */
  9150. zPtr = zIn;
  9151. while( zIn < zEnd && !SyisSpace(zIn[0]) ){
  9152. zIn++;
  9153. }
  9154. if( zIn > zPtr ){
  9155. nLen = (sxu32)(zIn-zPtr);
  9156. /* Split raw URI to it's fields */
  9157. VmHttpSplitURI(pUri, zPtr, nLen);
  9158. }
  9159. /* Jump trailing white spaces */
  9160. while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
  9161. zIn++;
  9162. }
  9163. /* Extract the HTTP version */
  9164. zPtr = zIn;
  9165. while( zIn < zEnd && !SyisSpace(zIn[0]) ){
  9166. zIn++;
  9167. }
  9168. *pProto = HTTP_PROTO_11; /* HTTP/1.1 */
  9169. rc = 1;
  9170. if( zIn > zPtr ){
  9171. rc = SyStrnicmp(zPtr, "http/1.0", (sxu32)(zIn-zPtr));
  9172. }
  9173. if( !rc ){
  9174. *pProto = HTTP_PROTO_10; /* HTTP/1.0 */
  9175. }
  9176. return SXRET_OK;
  9177. }
  9178. /*
  9179. * Tokenize, decode and split a raw query encoded as: "x-www-form-urlencoded"
  9180. * into a name value pair.
  9181. * Note that this encoding is implicit in GET based requests.
  9182. * After the tokenization process, register the decoded queries
  9183. * in the $_GET/$_POST/$_REQUEST superglobals arrays.
  9184. */
  9185. static sxi32 VmHttpSplitEncodedQuery(
  9186. jx9_vm *pVm, /* Target VM */
  9187. SyString *pQuery, /* Raw query to decode */
  9188. SyBlob *pWorker, /* Working buffer */
  9189. int is_post /* TRUE if we are dealing with a POST request */
  9190. )
  9191. {
  9192. const char *zEnd = &pQuery->zString[pQuery->nByte];
  9193. const char *zIn = pQuery->zString;
  9194. jx9_value *pGet, *pRequest;
  9195. SyString sName, sValue;
  9196. const char *zPtr;
  9197. sxu32 nBlobOfft;
  9198. /* Extract superglobals */
  9199. if( is_post ){
  9200. /* $_POST superglobal */
  9201. pGet = VmExtractSuper(&(*pVm), "_POST", sizeof("_POST")-1);
  9202. }else{
  9203. /* $_GET superglobal */
  9204. pGet = VmExtractSuper(&(*pVm), "_GET", sizeof("_GET")-1);
  9205. }
  9206. pRequest = VmExtractSuper(&(*pVm), "_REQUEST", sizeof("_REQUEST")-1);
  9207. /* Split up the raw query */
  9208. for(;;){
  9209. /* Jump leading white spaces */
  9210. while(zIn < zEnd && SyisSpace(zIn[0]) ){
  9211. zIn++;
  9212. }
  9213. if( zIn >= zEnd ){
  9214. break;
  9215. }
  9216. zPtr = zIn;
  9217. while( zPtr < zEnd && zPtr[0] != '=' && zPtr[0] != '&' && zPtr[0] != ';' ){
  9218. zPtr++;
  9219. }
  9220. /* Reset the working buffer */
  9221. SyBlobReset(pWorker);
  9222. /* Decode the entry */
  9223. SyUriDecode(zIn, (sxu32)(zPtr-zIn), jx9VmBlobConsumer, pWorker, TRUE);
  9224. /* Save the entry */
  9225. sName.nByte = SyBlobLength(pWorker);
  9226. sValue.zString = 0;
  9227. sValue.nByte = 0;
  9228. if( zPtr < zEnd && zPtr[0] == '=' ){
  9229. zPtr++;
  9230. zIn = zPtr;
  9231. /* Store field value */
  9232. while( zPtr < zEnd && zPtr[0] != '&' && zPtr[0] != ';' ){
  9233. zPtr++;
  9234. }
  9235. if( zPtr > zIn ){
  9236. /* Decode the value */
  9237. nBlobOfft = SyBlobLength(pWorker);
  9238. SyUriDecode(zIn, (sxu32)(zPtr-zIn), jx9VmBlobConsumer, pWorker, TRUE);
  9239. sValue.zString = (const char *)SyBlobDataAt(pWorker, nBlobOfft);
  9240. sValue.nByte = SyBlobLength(pWorker) - nBlobOfft;
  9241. }
  9242. /* Synchronize pointers */
  9243. zIn = zPtr;
  9244. }
  9245. sName.zString = (const char *)SyBlobData(pWorker);
  9246. /* Install the decoded query in the $_GET/$_REQUEST array */
  9247. if( pGet && (pGet->iFlags & MEMOBJ_HASHMAP) ){
  9248. VmHashmapInsert((jx9_hashmap *)pGet->x.pOther,
  9249. sName.zString, (int)sName.nByte,
  9250. sValue.zString, (int)sValue.nByte
  9251. );
  9252. }
  9253. if( pRequest && (pRequest->iFlags & MEMOBJ_HASHMAP) ){
  9254. VmHashmapInsert((jx9_hashmap *)pRequest->x.pOther,
  9255. sName.zString, (int)sName.nByte,
  9256. sValue.zString, (int)sValue.nByte
  9257. );
  9258. }
  9259. /* Advance the pointer */
  9260. zIn = &zPtr[1];
  9261. }
  9262. /* All done*/
  9263. return SXRET_OK;
  9264. }
  9265. /*
  9266. * Extract MIME header value from the given set.
  9267. * Return header value on success. NULL otherwise.
  9268. */
  9269. static SyString * VmHttpExtractHeaderValue(SySet *pSet, const char *zMime, sxu32 nByte)
  9270. {
  9271. SyhttpHeader *aMime, *pMime;
  9272. SyString sMime;
  9273. sxu32 n;
  9274. SyStringInitFromBuf(&sMime, zMime, nByte);
  9275. /* Point to the MIME entries */
  9276. aMime = (SyhttpHeader *)SySetBasePtr(pSet);
  9277. /* Perform the lookup */
  9278. for( n = 0 ; n < SySetUsed(pSet) ; ++n ){
  9279. pMime = &aMime[n];
  9280. if( SyStringCmp(&sMime, &pMime->sName, SyStrnicmp) == 0 ){
  9281. /* Header found, return it's associated value */
  9282. return &pMime->sValue;
  9283. }
  9284. }
  9285. /* No such MIME header */
  9286. return 0;
  9287. }
  9288. /*
  9289. * Tokenize and decode a raw "Cookie:" MIME header into a name value pair
  9290. * and insert it's fields [i.e name, value] in the $_COOKIE superglobal.
  9291. */
  9292. static sxi32 VmHttpPorcessCookie(jx9_vm *pVm, SyBlob *pWorker, const char *zIn, sxu32 nByte)
  9293. {
  9294. const char *zPtr, *zDelimiter, *zEnd = &zIn[nByte];
  9295. SyString sName, sValue;
  9296. jx9_value *pCookie;
  9297. sxu32 nOfft;
  9298. /* Make sure the $_COOKIE superglobal is available */
  9299. pCookie = VmExtractSuper(&(*pVm), "_COOKIE", sizeof("_COOKIE")-1);
  9300. if( pCookie == 0 || (pCookie->iFlags & MEMOBJ_HASHMAP) == 0 ){
  9301. /* $_COOKIE superglobal not available */
  9302. return SXERR_NOTFOUND;
  9303. }
  9304. for(;;){
  9305. /* Jump leading white spaces */
  9306. while( zIn < zEnd && SyisSpace(zIn[0]) ){
  9307. zIn++;
  9308. }
  9309. if( zIn >= zEnd ){
  9310. break;
  9311. }
  9312. /* Reset the working buffer */
  9313. SyBlobReset(pWorker);
  9314. zDelimiter = zIn;
  9315. /* Delimit the name[=value]; pair */
  9316. while( zDelimiter < zEnd && zDelimiter[0] != ';' ){
  9317. zDelimiter++;
  9318. }
  9319. zPtr = zIn;
  9320. while( zPtr < zDelimiter && zPtr[0] != '=' ){
  9321. zPtr++;
  9322. }
  9323. /* Decode the cookie */
  9324. SyUriDecode(zIn, (sxu32)(zPtr-zIn), jx9VmBlobConsumer, pWorker, TRUE);
  9325. sName.nByte = SyBlobLength(pWorker);
  9326. zPtr++;
  9327. sValue.zString = 0;
  9328. sValue.nByte = 0;
  9329. if( zPtr < zDelimiter ){
  9330. /* Got a Cookie value */
  9331. nOfft = SyBlobLength(pWorker);
  9332. SyUriDecode(zPtr, (sxu32)(zDelimiter-zPtr), jx9VmBlobConsumer, pWorker, TRUE);
  9333. SyStringInitFromBuf(&sValue, SyBlobDataAt(pWorker, nOfft), SyBlobLength(pWorker)-nOfft);
  9334. }
  9335. /* Synchronize pointers */
  9336. zIn = &zDelimiter[1];
  9337. /* Perform the insertion */
  9338. sName.zString = (const char *)SyBlobData(pWorker);
  9339. VmHashmapInsert((jx9_hashmap *)pCookie->x.pOther,
  9340. sName.zString, (int)sName.nByte,
  9341. sValue.zString, (int)sValue.nByte
  9342. );
  9343. }
  9344. return SXRET_OK;
  9345. }
  9346. /*
  9347. * Process a full HTTP request and populate the appropriate arrays
  9348. * such as $_SERVER, $_GET, $_POST, $_COOKIE, $_REQUEST, ... with the information
  9349. * extracted from the raw HTTP request. As an extension Symisc introduced
  9350. * the $_HEADER array which hold a copy of the processed HTTP MIME headers
  9351. * and their associated values. [i.e: $_HEADER['Server'], $_HEADER['User-Agent'], ...].
  9352. * This function return SXRET_OK on success. Any other return value indicates
  9353. * a malformed HTTP request.
  9354. */
  9355. static sxi32 VmHttpProcessRequest(jx9_vm *pVm, const char *zRequest, int nByte)
  9356. {
  9357. SyString *pName, *pValue, sRequest; /* Raw HTTP request */
  9358. jx9_value *pHeaderArray; /* $_HEADER superglobal (Symisc eXtension to the JX9 specification)*/
  9359. SyhttpHeader *pHeader; /* MIME header */
  9360. SyhttpUri sUri; /* Parse of the raw URI*/
  9361. SyBlob sWorker; /* General purpose working buffer */
  9362. SySet sHeader; /* MIME headers set */
  9363. sxi32 iMethod; /* HTTP method [i.e: GET, POST, HEAD...]*/
  9364. sxi32 iVer; /* HTTP protocol version */
  9365. sxi32 rc;
  9366. SyStringInitFromBuf(&sRequest, zRequest, nByte);
  9367. SySetInit(&sHeader, &pVm->sAllocator, sizeof(SyhttpHeader));
  9368. SyBlobInit(&sWorker, &pVm->sAllocator);
  9369. /* Ignore leading and trailing white spaces*/
  9370. SyStringFullTrim(&sRequest);
  9371. /* Process the first line */
  9372. rc = VmHttpProcessFirstLine(&sRequest, &iMethod, &sUri, &iVer);
  9373. if( rc != SXRET_OK ){
  9374. return rc;
  9375. }
  9376. /* Process MIME headers */
  9377. VmHttpExtractHeaders(&sRequest, &sHeader);
  9378. /*
  9379. * Setup $_SERVER environments
  9380. */
  9381. /* 'SERVER_PROTOCOL': Name and revision of the information protocol via which the page was requested */
  9382. jx9_vm_config(pVm,
  9383. JX9_VM_CONFIG_SERVER_ATTR,
  9384. "SERVER_PROTOCOL",
  9385. iVer == HTTP_PROTO_10 ? "HTTP/1.0" : "HTTP/1.1",
  9386. sizeof("HTTP/1.1")-1
  9387. );
  9388. /* 'REQUEST_METHOD': Which request method was used to access the page */
  9389. jx9_vm_config(pVm,
  9390. JX9_VM_CONFIG_SERVER_ATTR,
  9391. "REQUEST_METHOD",
  9392. iMethod == HTTP_METHOD_GET ? "GET" :
  9393. (iMethod == HTTP_METHOD_POST ? "POST":
  9394. (iMethod == HTTP_METHOD_PUT ? "PUT" :
  9395. (iMethod == HTTP_METHOD_HEAD ? "HEAD" : "OTHER"))),
  9396. -1 /* Compute attribute length automatically */
  9397. );
  9398. if( SyStringLength(&sUri.sQuery) > 0 && iMethod == HTTP_METHOD_GET ){
  9399. pValue = &sUri.sQuery;
  9400. /* 'QUERY_STRING': The query string, if any, via which the page was accessed */
  9401. jx9_vm_config(pVm,
  9402. JX9_VM_CONFIG_SERVER_ATTR,
  9403. "QUERY_STRING",
  9404. pValue->zString,
  9405. pValue->nByte
  9406. );
  9407. /* Decoded the raw query */
  9408. VmHttpSplitEncodedQuery(&(*pVm), pValue, &sWorker, FALSE);
  9409. }
  9410. /* REQUEST_URI: The URI which was given in order to access this page; for instance, '/index.html' */
  9411. pValue = &sUri.sRaw;
  9412. jx9_vm_config(pVm,
  9413. JX9_VM_CONFIG_SERVER_ATTR,
  9414. "REQUEST_URI",
  9415. pValue->zString,
  9416. pValue->nByte
  9417. );
  9418. /*
  9419. * 'PATH_INFO'
  9420. * 'ORIG_PATH_INFO'
  9421. * Contains any client-provided pathname information trailing the actual script filename but preceding
  9422. * the query string, if available. For instance, if the current script was accessed via the URL
  9423. * http://www.example.com/jx9/path_info.jx9/some/stuff?foo=bar, then $_SERVER['PATH_INFO'] would contain
  9424. * /some/stuff.
  9425. */
  9426. pValue = &sUri.sPath;
  9427. jx9_vm_config(pVm,
  9428. JX9_VM_CONFIG_SERVER_ATTR,
  9429. "PATH_INFO",
  9430. pValue->zString,
  9431. pValue->nByte
  9432. );
  9433. jx9_vm_config(pVm,
  9434. JX9_VM_CONFIG_SERVER_ATTR,
  9435. "ORIG_PATH_INFO",
  9436. pValue->zString,
  9437. pValue->nByte
  9438. );
  9439. /* 'HTTP_ACCEPT': Contents of the Accept: header from the current request, if there is one */
  9440. pValue = VmHttpExtractHeaderValue(&sHeader, "Accept", sizeof("Accept")-1);
  9441. if( pValue ){
  9442. jx9_vm_config(pVm,
  9443. JX9_VM_CONFIG_SERVER_ATTR,
  9444. "HTTP_ACCEPT",
  9445. pValue->zString,
  9446. pValue->nByte
  9447. );
  9448. }
  9449. /* 'HTTP_ACCEPT_CHARSET': Contents of the Accept-Charset: header from the current request, if there is one. */
  9450. pValue = VmHttpExtractHeaderValue(&sHeader, "Accept-Charset", sizeof("Accept-Charset")-1);
  9451. if( pValue ){
  9452. jx9_vm_config(pVm,
  9453. JX9_VM_CONFIG_SERVER_ATTR,
  9454. "HTTP_ACCEPT_CHARSET",
  9455. pValue->zString,
  9456. pValue->nByte
  9457. );
  9458. }
  9459. /* 'HTTP_ACCEPT_ENCODING': Contents of the Accept-Encoding: header from the current request, if there is one. */
  9460. pValue = VmHttpExtractHeaderValue(&sHeader, "Accept-Encoding", sizeof("Accept-Encoding")-1);
  9461. if( pValue ){
  9462. jx9_vm_config(pVm,
  9463. JX9_VM_CONFIG_SERVER_ATTR,
  9464. "HTTP_ACCEPT_ENCODING",
  9465. pValue->zString,
  9466. pValue->nByte
  9467. );
  9468. }
  9469. /* 'HTTP_ACCEPT_LANGUAGE': Contents of the Accept-Language: header from the current request, if there is one */
  9470. pValue = VmHttpExtractHeaderValue(&sHeader, "Accept-Language", sizeof("Accept-Language")-1);
  9471. if( pValue ){
  9472. jx9_vm_config(pVm,
  9473. JX9_VM_CONFIG_SERVER_ATTR,
  9474. "HTTP_ACCEPT_LANGUAGE",
  9475. pValue->zString,
  9476. pValue->nByte
  9477. );
  9478. }
  9479. /* 'HTTP_CONNECTION': Contents of the Connection: header from the current request, if there is one. */
  9480. pValue = VmHttpExtractHeaderValue(&sHeader, "Connection", sizeof("Connection")-1);
  9481. if( pValue ){
  9482. jx9_vm_config(pVm,
  9483. JX9_VM_CONFIG_SERVER_ATTR,
  9484. "HTTP_CONNECTION",
  9485. pValue->zString,
  9486. pValue->nByte
  9487. );
  9488. }
  9489. /* 'HTTP_HOST': Contents of the Host: header from the current request, if there is one. */
  9490. pValue = VmHttpExtractHeaderValue(&sHeader, "Host", sizeof("Host")-1);
  9491. if( pValue ){
  9492. jx9_vm_config(pVm,
  9493. JX9_VM_CONFIG_SERVER_ATTR,
  9494. "HTTP_HOST",
  9495. pValue->zString,
  9496. pValue->nByte
  9497. );
  9498. }
  9499. /* 'HTTP_REFERER': Contents of the Referer: header from the current request, if there is one. */
  9500. pValue = VmHttpExtractHeaderValue(&sHeader, "Referer", sizeof("Referer")-1);
  9501. if( pValue ){
  9502. jx9_vm_config(pVm,
  9503. JX9_VM_CONFIG_SERVER_ATTR,
  9504. "HTTP_REFERER",
  9505. pValue->zString,
  9506. pValue->nByte
  9507. );
  9508. }
  9509. /* 'HTTP_USER_AGENT': Contents of the Referer: header from the current request, if there is one. */
  9510. pValue = VmHttpExtractHeaderValue(&sHeader, "User-Agent", sizeof("User-Agent")-1);
  9511. if( pValue ){
  9512. jx9_vm_config(pVm,
  9513. JX9_VM_CONFIG_SERVER_ATTR,
  9514. "HTTP_USER_AGENT",
  9515. pValue->zString,
  9516. pValue->nByte
  9517. );
  9518. }
  9519. /* 'JX9_AUTH_DIGEST': When doing Digest HTTP authentication this variable is set to the 'Authorization'
  9520. * header sent by the client (which you should then use to make the appropriate validation).
  9521. */
  9522. pValue = VmHttpExtractHeaderValue(&sHeader, "Authorization", sizeof("Authorization")-1);
  9523. if( pValue ){
  9524. jx9_vm_config(pVm,
  9525. JX9_VM_CONFIG_SERVER_ATTR,
  9526. "JX9_AUTH_DIGEST",
  9527. pValue->zString,
  9528. pValue->nByte
  9529. );
  9530. jx9_vm_config(pVm,
  9531. JX9_VM_CONFIG_SERVER_ATTR,
  9532. "JX9_AUTH",
  9533. pValue->zString,
  9534. pValue->nByte
  9535. );
  9536. }
  9537. /* Install all clients HTTP headers in the $_HEADER superglobal */
  9538. pHeaderArray = VmExtractSuper(&(*pVm), "_HEADER", sizeof("_HEADER")-1);
  9539. /* Iterate throw the available MIME headers*/
  9540. SySetResetCursor(&sHeader);
  9541. pHeader = 0; /* stupid cc warning */
  9542. while( SXRET_OK == SySetGetNextEntry(&sHeader, (void **)&pHeader) ){
  9543. pName = &pHeader->sName;
  9544. pValue = &pHeader->sValue;
  9545. if( pHeaderArray && (pHeaderArray->iFlags & MEMOBJ_HASHMAP)){
  9546. /* Insert the MIME header and it's associated value */
  9547. VmHashmapInsert((jx9_hashmap *)pHeaderArray->x.pOther,
  9548. pName->zString, (int)pName->nByte,
  9549. pValue->zString, (int)pValue->nByte
  9550. );
  9551. }
  9552. if( pName->nByte == sizeof("Cookie")-1 && SyStrnicmp(pName->zString, "Cookie", sizeof("Cookie")-1) == 0
  9553. && pValue->nByte > 0){
  9554. /* Process the name=value pair and insert them in the $_COOKIE superglobal array */
  9555. VmHttpPorcessCookie(&(*pVm), &sWorker, pValue->zString, pValue->nByte);
  9556. }
  9557. }
  9558. if( iMethod == HTTP_METHOD_POST ){
  9559. /* Extract raw POST data */
  9560. pValue = VmHttpExtractHeaderValue(&sHeader, "Content-Type", sizeof("Content-Type") - 1);
  9561. if( pValue && pValue->nByte >= sizeof("application/x-www-form-urlencoded") - 1 &&
  9562. SyMemcmp("application/x-www-form-urlencoded", pValue->zString, pValue->nByte) == 0 ){
  9563. /* Extract POST data length */
  9564. pValue = VmHttpExtractHeaderValue(&sHeader, "Content-Length", sizeof("Content-Length") - 1);
  9565. if( pValue ){
  9566. sxi32 iLen = 0; /* POST data length */
  9567. SyStrToInt32(pValue->zString, pValue->nByte, (void *)&iLen, 0);
  9568. if( iLen > 0 ){
  9569. /* Remove leading and trailing white spaces */
  9570. SyStringFullTrim(&sRequest);
  9571. if( (int)sRequest.nByte > iLen ){
  9572. sRequest.nByte = (sxu32)iLen;
  9573. }
  9574. /* Decode POST data now */
  9575. VmHttpSplitEncodedQuery(&(*pVm), &sRequest, &sWorker, TRUE);
  9576. }
  9577. }
  9578. }
  9579. }
  9580. /* All done, clean-up the mess left behind */
  9581. SySetRelease(&sHeader);
  9582. SyBlobRelease(&sWorker);
  9583. return SXRET_OK;
  9584. }
  9585. /*
  9586. * ----------------------------------------------------------
  9587. * File: vfs.c
  9588. * MD5: 6042c53b2e870f191bd3e1a2a6ae032e
  9589. * ----------------------------------------------------------
  9590. */
  9591. /*
  9592. * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
  9593. * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
  9594. * Version 1.7.2
  9595. * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  9596. * please contact Symisc Systems via:
  9597. * legal@symisc.net
  9598. * licensing@symisc.net
  9599. * contact@symisc.net
  9600. * or visit:
  9601. * http://jx9.symisc.net/
  9602. */
  9603. /* $SymiscID: vfs.c v2.1 Ubuntu 2012-12-13 00:013 stable <chm@symisc.net> $ */
  9604. #ifndef JX9_AMALGAMATION
  9605. #include "jx9Int.h"
  9606. #endif
  9607. /*
  9608. * This file implement a virtual file systems (VFS) for the JX9 engine.
  9609. */
  9610. /*
  9611. * Given a string containing the path of a file or directory, this function
  9612. * return the parent directory's path.
  9613. */
  9614. JX9_PRIVATE const char * jx9ExtractDirName(const char *zPath, int nByte, int *pLen)
  9615. {
  9616. const char *zEnd = &zPath[nByte - 1];
  9617. int c, d;
  9618. c = d = '/';
  9619. #ifdef __WINNT__
  9620. d = '\\';
  9621. #endif
  9622. while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){
  9623. zEnd--;
  9624. }
  9625. *pLen = (int)(zEnd-zPath);
  9626. #ifdef __WINNT__
  9627. if( (*pLen) == (int)sizeof(char) && zPath[0] == '/' ){
  9628. /* Normalize path on windows */
  9629. return "\\";
  9630. }
  9631. #endif
  9632. if( zEnd == zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d) ){
  9633. /* No separator, return "." as the current directory */
  9634. *pLen = sizeof(char);
  9635. return ".";
  9636. }
  9637. if( (*pLen) == 0 ){
  9638. *pLen = sizeof(char);
  9639. #ifdef __WINNT__
  9640. return "\\";
  9641. #else
  9642. return "/";
  9643. #endif
  9644. }
  9645. return zPath;
  9646. }
  9647. /*
  9648. * Omit the vfs layer implementation from the built if the JX9_DISABLE_BUILTIN_FUNC directive is defined.
  9649. */
  9650. #ifndef JX9_DISABLE_BUILTIN_FUNC
  9651. /*
  9652. * bool chdir(string $directory)
  9653. * Change the current directory.
  9654. * Parameters
  9655. * $directory
  9656. * The new current directory
  9657. * Return
  9658. * TRUE on success or FALSE on failure.
  9659. */
  9660. static int jx9Vfs_chdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9661. {
  9662. const char *zPath;
  9663. jx9_vfs *pVfs;
  9664. int rc;
  9665. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  9666. /* Missing/Invalid argument, return FALSE */
  9667. jx9_result_bool(pCtx, 0);
  9668. return JX9_OK;
  9669. }
  9670. /* Point to the underlying vfs */
  9671. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  9672. if( pVfs == 0 || pVfs->xChdir == 0 ){
  9673. /* IO routine not implemented, return NULL */
  9674. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  9675. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  9676. jx9_function_name(pCtx)
  9677. );
  9678. jx9_result_bool(pCtx, 0);
  9679. return JX9_OK;
  9680. }
  9681. /* Point to the desired directory */
  9682. zPath = jx9_value_to_string(apArg[0], 0);
  9683. /* Perform the requested operation */
  9684. rc = pVfs->xChdir(zPath);
  9685. /* IO return value */
  9686. jx9_result_bool(pCtx, rc == JX9_OK);
  9687. return JX9_OK;
  9688. }
  9689. /*
  9690. * bool chroot(string $directory)
  9691. * Change the root directory.
  9692. * Parameters
  9693. * $directory
  9694. * The path to change the root directory to
  9695. * Return
  9696. * TRUE on success or FALSE on failure.
  9697. */
  9698. static int jx9Vfs_chroot(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9699. {
  9700. const char *zPath;
  9701. jx9_vfs *pVfs;
  9702. int rc;
  9703. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  9704. /* Missing/Invalid argument, return FALSE */
  9705. jx9_result_bool(pCtx, 0);
  9706. return JX9_OK;
  9707. }
  9708. /* Point to the underlying vfs */
  9709. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  9710. if( pVfs == 0 || pVfs->xChroot == 0 ){
  9711. /* IO routine not implemented, return NULL */
  9712. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  9713. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  9714. jx9_function_name(pCtx)
  9715. );
  9716. jx9_result_bool(pCtx, 0);
  9717. return JX9_OK;
  9718. }
  9719. /* Point to the desired directory */
  9720. zPath = jx9_value_to_string(apArg[0], 0);
  9721. /* Perform the requested operation */
  9722. rc = pVfs->xChroot(zPath);
  9723. /* IO return value */
  9724. jx9_result_bool(pCtx, rc == JX9_OK);
  9725. return JX9_OK;
  9726. }
  9727. /*
  9728. * string getcwd(void)
  9729. * Gets the current working directory.
  9730. * Parameters
  9731. * None
  9732. * Return
  9733. * Returns the current working directory on success, or FALSE on failure.
  9734. */
  9735. static int jx9Vfs_getcwd(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9736. {
  9737. jx9_vfs *pVfs;
  9738. int rc;
  9739. /* Point to the underlying vfs */
  9740. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  9741. if( pVfs == 0 || pVfs->xGetcwd == 0 ){
  9742. SXUNUSED(nArg); /* cc warning */
  9743. SXUNUSED(apArg);
  9744. /* IO routine not implemented, return NULL */
  9745. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  9746. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  9747. jx9_function_name(pCtx)
  9748. );
  9749. jx9_result_bool(pCtx, 0);
  9750. return JX9_OK;
  9751. }
  9752. jx9_result_string(pCtx, "", 0);
  9753. /* Perform the requested operation */
  9754. rc = pVfs->xGetcwd(pCtx);
  9755. if( rc != JX9_OK ){
  9756. /* Error, return FALSE */
  9757. jx9_result_bool(pCtx, 0);
  9758. }
  9759. return JX9_OK;
  9760. }
  9761. /*
  9762. * bool rmdir(string $directory)
  9763. * Removes directory.
  9764. * Parameters
  9765. * $directory
  9766. * The path to the directory
  9767. * Return
  9768. * TRUE on success or FALSE on failure.
  9769. */
  9770. static int jx9Vfs_rmdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9771. {
  9772. const char *zPath;
  9773. jx9_vfs *pVfs;
  9774. int rc;
  9775. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  9776. /* Missing/Invalid argument, return FALSE */
  9777. jx9_result_bool(pCtx, 0);
  9778. return JX9_OK;
  9779. }
  9780. /* Point to the underlying vfs */
  9781. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  9782. if( pVfs == 0 || pVfs->xRmdir == 0 ){
  9783. /* IO routine not implemented, return NULL */
  9784. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  9785. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  9786. jx9_function_name(pCtx)
  9787. );
  9788. jx9_result_bool(pCtx, 0);
  9789. return JX9_OK;
  9790. }
  9791. /* Point to the desired directory */
  9792. zPath = jx9_value_to_string(apArg[0], 0);
  9793. /* Perform the requested operation */
  9794. rc = pVfs->xRmdir(zPath);
  9795. /* IO return value */
  9796. jx9_result_bool(pCtx, rc == JX9_OK);
  9797. return JX9_OK;
  9798. }
  9799. /*
  9800. * bool is_dir(string $filename)
  9801. * Tells whether the given filename is a directory.
  9802. * Parameters
  9803. * $filename
  9804. * Path to the file.
  9805. * Return
  9806. * TRUE on success or FALSE on failure.
  9807. */
  9808. static int jx9Vfs_is_dir(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9809. {
  9810. const char *zPath;
  9811. jx9_vfs *pVfs;
  9812. int rc;
  9813. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  9814. /* Missing/Invalid argument, return FALSE */
  9815. jx9_result_bool(pCtx, 0);
  9816. return JX9_OK;
  9817. }
  9818. /* Point to the underlying vfs */
  9819. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  9820. if( pVfs == 0 || pVfs->xIsdir == 0 ){
  9821. /* IO routine not implemented, return NULL */
  9822. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  9823. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  9824. jx9_function_name(pCtx)
  9825. );
  9826. jx9_result_bool(pCtx, 0);
  9827. return JX9_OK;
  9828. }
  9829. /* Point to the desired directory */
  9830. zPath = jx9_value_to_string(apArg[0], 0);
  9831. /* Perform the requested operation */
  9832. rc = pVfs->xIsdir(zPath);
  9833. /* IO return value */
  9834. jx9_result_bool(pCtx, rc == JX9_OK);
  9835. return JX9_OK;
  9836. }
  9837. /*
  9838. * bool mkdir(string $pathname[, int $mode = 0777])
  9839. * Make a directory.
  9840. * Parameters
  9841. * $pathname
  9842. * The directory path.
  9843. * $mode
  9844. * The mode is 0777 by default, which means the widest possible access.
  9845. * Note:
  9846. * mode is ignored on Windows.
  9847. * Note that you probably want to specify the mode as an octal number, which means
  9848. * it should have a leading zero. The mode is also modified by the current umask
  9849. * which you can change using umask().
  9850. * Return
  9851. * TRUE on success or FALSE on failure.
  9852. */
  9853. static int jx9Vfs_mkdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9854. {
  9855. int iRecursive = 0;
  9856. const char *zPath;
  9857. jx9_vfs *pVfs;
  9858. int iMode, rc;
  9859. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  9860. /* Missing/Invalid argument, return FALSE */
  9861. jx9_result_bool(pCtx, 0);
  9862. return JX9_OK;
  9863. }
  9864. /* Point to the underlying vfs */
  9865. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  9866. if( pVfs == 0 || pVfs->xMkdir == 0 ){
  9867. /* IO routine not implemented, return NULL */
  9868. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  9869. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  9870. jx9_function_name(pCtx)
  9871. );
  9872. jx9_result_bool(pCtx, 0);
  9873. return JX9_OK;
  9874. }
  9875. /* Point to the desired directory */
  9876. zPath = jx9_value_to_string(apArg[0], 0);
  9877. #ifdef __WINNT__
  9878. iMode = 0;
  9879. #else
  9880. /* Assume UNIX */
  9881. iMode = 0777;
  9882. #endif
  9883. if( nArg > 1 ){
  9884. iMode = jx9_value_to_int(apArg[1]);
  9885. if( nArg > 2 ){
  9886. iRecursive = jx9_value_to_bool(apArg[2]);
  9887. }
  9888. }
  9889. /* Perform the requested operation */
  9890. rc = pVfs->xMkdir(zPath, iMode, iRecursive);
  9891. /* IO return value */
  9892. jx9_result_bool(pCtx, rc == JX9_OK);
  9893. return JX9_OK;
  9894. }
  9895. /*
  9896. * bool rename(string $oldname, string $newname)
  9897. * Attempts to rename oldname to newname.
  9898. * Parameters
  9899. * $oldname
  9900. * Old name.
  9901. * $newname
  9902. * New name.
  9903. * Return
  9904. * TRUE on success or FALSE on failure.
  9905. */
  9906. static int jx9Vfs_rename(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9907. {
  9908. const char *zOld, *zNew;
  9909. jx9_vfs *pVfs;
  9910. int rc;
  9911. if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
  9912. /* Missing/Invalid arguments, return FALSE */
  9913. jx9_result_bool(pCtx, 0);
  9914. return JX9_OK;
  9915. }
  9916. /* Point to the underlying vfs */
  9917. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  9918. if( pVfs == 0 || pVfs->xRename == 0 ){
  9919. /* IO routine not implemented, return NULL */
  9920. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  9921. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  9922. jx9_function_name(pCtx)
  9923. );
  9924. jx9_result_bool(pCtx, 0);
  9925. return JX9_OK;
  9926. }
  9927. /* Perform the requested operation */
  9928. zOld = jx9_value_to_string(apArg[0], 0);
  9929. zNew = jx9_value_to_string(apArg[1], 0);
  9930. rc = pVfs->xRename(zOld, zNew);
  9931. /* IO result */
  9932. jx9_result_bool(pCtx, rc == JX9_OK );
  9933. return JX9_OK;
  9934. }
  9935. /*
  9936. * string realpath(string $path)
  9937. * Returns canonicalized absolute pathname.
  9938. * Parameters
  9939. * $path
  9940. * Target path.
  9941. * Return
  9942. * Canonicalized absolute pathname on success. or FALSE on failure.
  9943. */
  9944. static int jx9Vfs_realpath(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9945. {
  9946. const char *zPath;
  9947. jx9_vfs *pVfs;
  9948. int rc;
  9949. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  9950. /* Missing/Invalid argument, return FALSE */
  9951. jx9_result_bool(pCtx, 0);
  9952. return JX9_OK;
  9953. }
  9954. /* Point to the underlying vfs */
  9955. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  9956. if( pVfs == 0 || pVfs->xRealpath == 0 ){
  9957. /* IO routine not implemented, return NULL */
  9958. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  9959. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  9960. jx9_function_name(pCtx)
  9961. );
  9962. jx9_result_bool(pCtx, 0);
  9963. return JX9_OK;
  9964. }
  9965. /* Set an empty string untnil the underlying OS interface change that */
  9966. jx9_result_string(pCtx, "", 0);
  9967. /* Perform the requested operation */
  9968. zPath = jx9_value_to_string(apArg[0], 0);
  9969. rc = pVfs->xRealpath(zPath, pCtx);
  9970. if( rc != JX9_OK ){
  9971. jx9_result_bool(pCtx, 0);
  9972. }
  9973. return JX9_OK;
  9974. }
  9975. /*
  9976. * int sleep(int $seconds)
  9977. * Delays the program execution for the given number of seconds.
  9978. * Parameters
  9979. * $seconds
  9980. * Halt time in seconds.
  9981. * Return
  9982. * Zero on success or FALSE on failure.
  9983. */
  9984. static int jx9Vfs_sleep(jx9_context *pCtx, int nArg, jx9_value **apArg)
  9985. {
  9986. jx9_vfs *pVfs;
  9987. int rc, nSleep;
  9988. if( nArg < 1 || !jx9_value_is_int(apArg[0]) ){
  9989. /* Missing/Invalid argument, return FALSE */
  9990. jx9_result_bool(pCtx, 0);
  9991. return JX9_OK;
  9992. }
  9993. /* Point to the underlying vfs */
  9994. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  9995. if( pVfs == 0 || pVfs->xSleep == 0 ){
  9996. /* IO routine not implemented, return NULL */
  9997. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  9998. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  9999. jx9_function_name(pCtx)
  10000. );
  10001. jx9_result_bool(pCtx, 0);
  10002. return JX9_OK;
  10003. }
  10004. /* Amount to sleep */
  10005. nSleep = jx9_value_to_int(apArg[0]);
  10006. if( nSleep < 0 ){
  10007. /* Invalid value, return FALSE */
  10008. jx9_result_bool(pCtx, 0);
  10009. return JX9_OK;
  10010. }
  10011. /* Perform the requested operation (Microseconds) */
  10012. rc = pVfs->xSleep((unsigned int)(nSleep * SX_USEC_PER_SEC));
  10013. if( rc != JX9_OK ){
  10014. /* Return FALSE */
  10015. jx9_result_bool(pCtx, 0);
  10016. }else{
  10017. /* Return zero */
  10018. jx9_result_int(pCtx, 0);
  10019. }
  10020. return JX9_OK;
  10021. }
  10022. /*
  10023. * void usleep(int $micro_seconds)
  10024. * Delays program execution for the given number of micro seconds.
  10025. * Parameters
  10026. * $micro_seconds
  10027. * Halt time in micro seconds. A micro second is one millionth of a second.
  10028. * Return
  10029. * None.
  10030. */
  10031. static int jx9Vfs_usleep(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10032. {
  10033. jx9_vfs *pVfs;
  10034. int nSleep;
  10035. if( nArg < 1 || !jx9_value_is_int(apArg[0]) ){
  10036. /* Missing/Invalid argument, return immediately */
  10037. return JX9_OK;
  10038. }
  10039. /* Point to the underlying vfs */
  10040. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10041. if( pVfs == 0 || pVfs->xSleep == 0 ){
  10042. /* IO routine not implemented, return NULL */
  10043. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  10044. "IO routine(%s) not implemented in the underlying VFS",
  10045. jx9_function_name(pCtx)
  10046. );
  10047. return JX9_OK;
  10048. }
  10049. /* Amount to sleep */
  10050. nSleep = jx9_value_to_int(apArg[0]);
  10051. if( nSleep < 0 ){
  10052. /* Invalid value, return immediately */
  10053. return JX9_OK;
  10054. }
  10055. /* Perform the requested operation (Microseconds) */
  10056. pVfs->xSleep((unsigned int)nSleep);
  10057. return JX9_OK;
  10058. }
  10059. /*
  10060. * bool unlink (string $filename)
  10061. * Delete a file.
  10062. * Parameters
  10063. * $filename
  10064. * Path to the file.
  10065. * Return
  10066. * TRUE on success or FALSE on failure.
  10067. */
  10068. static int jx9Vfs_unlink(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10069. {
  10070. const char *zPath;
  10071. jx9_vfs *pVfs;
  10072. int rc;
  10073. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  10074. /* Missing/Invalid argument, return FALSE */
  10075. jx9_result_bool(pCtx, 0);
  10076. return JX9_OK;
  10077. }
  10078. /* Point to the underlying vfs */
  10079. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10080. if( pVfs == 0 || pVfs->xUnlink == 0 ){
  10081. /* IO routine not implemented, return NULL */
  10082. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  10083. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  10084. jx9_function_name(pCtx)
  10085. );
  10086. jx9_result_bool(pCtx, 0);
  10087. return JX9_OK;
  10088. }
  10089. /* Point to the desired directory */
  10090. zPath = jx9_value_to_string(apArg[0], 0);
  10091. /* Perform the requested operation */
  10092. rc = pVfs->xUnlink(zPath);
  10093. /* IO return value */
  10094. jx9_result_bool(pCtx, rc == JX9_OK);
  10095. return JX9_OK;
  10096. }
  10097. /*
  10098. * bool chmod(string $filename, int $mode)
  10099. * Attempts to change the mode of the specified file to that given in mode.
  10100. * Parameters
  10101. * $filename
  10102. * Path to the file.
  10103. * $mode
  10104. * Mode (Must be an integer)
  10105. * Return
  10106. * TRUE on success or FALSE on failure.
  10107. */
  10108. static int jx9Vfs_chmod(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10109. {
  10110. const char *zPath;
  10111. jx9_vfs *pVfs;
  10112. int iMode;
  10113. int rc;
  10114. if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
  10115. /* Missing/Invalid argument, return FALSE */
  10116. jx9_result_bool(pCtx, 0);
  10117. return JX9_OK;
  10118. }
  10119. /* Point to the underlying vfs */
  10120. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10121. if( pVfs == 0 || pVfs->xChmod == 0 ){
  10122. /* IO routine not implemented, return NULL */
  10123. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  10124. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  10125. jx9_function_name(pCtx)
  10126. );
  10127. jx9_result_bool(pCtx, 0);
  10128. return JX9_OK;
  10129. }
  10130. /* Point to the desired directory */
  10131. zPath = jx9_value_to_string(apArg[0], 0);
  10132. /* Extract the mode */
  10133. iMode = jx9_value_to_int(apArg[1]);
  10134. /* Perform the requested operation */
  10135. rc = pVfs->xChmod(zPath, iMode);
  10136. /* IO return value */
  10137. jx9_result_bool(pCtx, rc == JX9_OK);
  10138. return JX9_OK;
  10139. }
  10140. /*
  10141. * bool chown(string $filename, string $user)
  10142. * Attempts to change the owner of the file filename to user user.
  10143. * Parameters
  10144. * $filename
  10145. * Path to the file.
  10146. * $user
  10147. * Username.
  10148. * Return
  10149. * TRUE on success or FALSE on failure.
  10150. */
  10151. static int jx9Vfs_chown(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10152. {
  10153. const char *zPath, *zUser;
  10154. jx9_vfs *pVfs;
  10155. int rc;
  10156. if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
  10157. /* Missing/Invalid arguments, return FALSE */
  10158. jx9_result_bool(pCtx, 0);
  10159. return JX9_OK;
  10160. }
  10161. /* Point to the underlying vfs */
  10162. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10163. if( pVfs == 0 || pVfs->xChown == 0 ){
  10164. /* IO routine not implemented, return NULL */
  10165. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  10166. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  10167. jx9_function_name(pCtx)
  10168. );
  10169. jx9_result_bool(pCtx, 0);
  10170. return JX9_OK;
  10171. }
  10172. /* Point to the desired directory */
  10173. zPath = jx9_value_to_string(apArg[0], 0);
  10174. /* Extract the user */
  10175. zUser = jx9_value_to_string(apArg[1], 0);
  10176. /* Perform the requested operation */
  10177. rc = pVfs->xChown(zPath, zUser);
  10178. /* IO return value */
  10179. jx9_result_bool(pCtx, rc == JX9_OK);
  10180. return JX9_OK;
  10181. }
  10182. /*
  10183. * bool chgrp(string $filename, string $group)
  10184. * Attempts to change the group of the file filename to group.
  10185. * Parameters
  10186. * $filename
  10187. * Path to the file.
  10188. * $group
  10189. * groupname.
  10190. * Return
  10191. * TRUE on success or FALSE on failure.
  10192. */
  10193. static int jx9Vfs_chgrp(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10194. {
  10195. const char *zPath, *zGroup;
  10196. jx9_vfs *pVfs;
  10197. int rc;
  10198. if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
  10199. /* Missing/Invalid arguments, return FALSE */
  10200. jx9_result_bool(pCtx, 0);
  10201. return JX9_OK;
  10202. }
  10203. /* Point to the underlying vfs */
  10204. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10205. if( pVfs == 0 || pVfs->xChgrp == 0 ){
  10206. /* IO routine not implemented, return NULL */
  10207. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  10208. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  10209. jx9_function_name(pCtx)
  10210. );
  10211. jx9_result_bool(pCtx, 0);
  10212. return JX9_OK;
  10213. }
  10214. /* Point to the desired directory */
  10215. zPath = jx9_value_to_string(apArg[0], 0);
  10216. /* Extract the user */
  10217. zGroup = jx9_value_to_string(apArg[1], 0);
  10218. /* Perform the requested operation */
  10219. rc = pVfs->xChgrp(zPath, zGroup);
  10220. /* IO return value */
  10221. jx9_result_bool(pCtx, rc == JX9_OK);
  10222. return JX9_OK;
  10223. }
  10224. /*
  10225. * int64 disk_free_space(string $directory)
  10226. * Returns available space on filesystem or disk partition.
  10227. * Parameters
  10228. * $directory
  10229. * A directory of the filesystem or disk partition.
  10230. * Return
  10231. * Returns the number of available bytes as a 64-bit integer or FALSE on failure.
  10232. */
  10233. static int jx9Vfs_disk_free_space(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10234. {
  10235. const char *zPath;
  10236. jx9_int64 iSize;
  10237. jx9_vfs *pVfs;
  10238. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  10239. /* Missing/Invalid argument, return FALSE */
  10240. jx9_result_bool(pCtx, 0);
  10241. return JX9_OK;
  10242. }
  10243. /* Point to the underlying vfs */
  10244. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10245. if( pVfs == 0 || pVfs->xFreeSpace == 0 ){
  10246. /* IO routine not implemented, return NULL */
  10247. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  10248. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  10249. jx9_function_name(pCtx)
  10250. );
  10251. jx9_result_bool(pCtx, 0);
  10252. return JX9_OK;
  10253. }
  10254. /* Point to the desired directory */
  10255. zPath = jx9_value_to_string(apArg[0], 0);
  10256. /* Perform the requested operation */
  10257. iSize = pVfs->xFreeSpace(zPath);
  10258. /* IO return value */
  10259. jx9_result_int64(pCtx, iSize);
  10260. return JX9_OK;
  10261. }
  10262. /*
  10263. * int64 disk_total_space(string $directory)
  10264. * Returns the total size of a filesystem or disk partition.
  10265. * Parameters
  10266. * $directory
  10267. * A directory of the filesystem or disk partition.
  10268. * Return
  10269. * Returns the number of available bytes as a 64-bit integer or FALSE on failure.
  10270. */
  10271. static int jx9Vfs_disk_total_space(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10272. {
  10273. const char *zPath;
  10274. jx9_int64 iSize;
  10275. jx9_vfs *pVfs;
  10276. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  10277. /* Missing/Invalid argument, return FALSE */
  10278. jx9_result_bool(pCtx, 0);
  10279. return JX9_OK;
  10280. }
  10281. /* Point to the underlying vfs */
  10282. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10283. if( pVfs == 0 || pVfs->xTotalSpace == 0 ){
  10284. /* IO routine not implemented, return NULL */
  10285. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  10286. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  10287. jx9_function_name(pCtx)
  10288. );
  10289. jx9_result_bool(pCtx, 0);
  10290. return JX9_OK;
  10291. }
  10292. /* Point to the desired directory */
  10293. zPath = jx9_value_to_string(apArg[0], 0);
  10294. /* Perform the requested operation */
  10295. iSize = pVfs->xTotalSpace(zPath);
  10296. /* IO return value */
  10297. jx9_result_int64(pCtx, iSize);
  10298. return JX9_OK;
  10299. }
  10300. /*
  10301. * bool file_exists(string $filename)
  10302. * Checks whether a file or directory exists.
  10303. * Parameters
  10304. * $filename
  10305. * Path to the file.
  10306. * Return
  10307. * TRUE on success or FALSE on failure.
  10308. */
  10309. static int jx9Vfs_file_exists(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10310. {
  10311. const char *zPath;
  10312. jx9_vfs *pVfs;
  10313. int rc;
  10314. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  10315. /* Missing/Invalid argument, return FALSE */
  10316. jx9_result_bool(pCtx, 0);
  10317. return JX9_OK;
  10318. }
  10319. /* Point to the underlying vfs */
  10320. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10321. if( pVfs == 0 || pVfs->xFileExists == 0 ){
  10322. /* IO routine not implemented, return NULL */
  10323. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  10324. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  10325. jx9_function_name(pCtx)
  10326. );
  10327. jx9_result_bool(pCtx, 0);
  10328. return JX9_OK;
  10329. }
  10330. /* Point to the desired directory */
  10331. zPath = jx9_value_to_string(apArg[0], 0);
  10332. /* Perform the requested operation */
  10333. rc = pVfs->xFileExists(zPath);
  10334. /* IO return value */
  10335. jx9_result_bool(pCtx, rc == JX9_OK);
  10336. return JX9_OK;
  10337. }
  10338. /*
  10339. * int64 file_size(string $filename)
  10340. * Gets the size for the given file.
  10341. * Parameters
  10342. * $filename
  10343. * Path to the file.
  10344. * Return
  10345. * File size on success or FALSE on failure.
  10346. */
  10347. static int jx9Vfs_file_size(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10348. {
  10349. const char *zPath;
  10350. jx9_int64 iSize;
  10351. jx9_vfs *pVfs;
  10352. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  10353. /* Missing/Invalid argument, return FALSE */
  10354. jx9_result_bool(pCtx, 0);
  10355. return JX9_OK;
  10356. }
  10357. /* Point to the underlying vfs */
  10358. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10359. if( pVfs == 0 || pVfs->xFileSize == 0 ){
  10360. /* IO routine not implemented, return NULL */
  10361. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  10362. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  10363. jx9_function_name(pCtx)
  10364. );
  10365. jx9_result_bool(pCtx, 0);
  10366. return JX9_OK;
  10367. }
  10368. /* Point to the desired directory */
  10369. zPath = jx9_value_to_string(apArg[0], 0);
  10370. /* Perform the requested operation */
  10371. iSize = pVfs->xFileSize(zPath);
  10372. /* IO return value */
  10373. jx9_result_int64(pCtx, iSize);
  10374. return JX9_OK;
  10375. }
  10376. /*
  10377. * int64 fileatime(string $filename)
  10378. * Gets the last access time of the given file.
  10379. * Parameters
  10380. * $filename
  10381. * Path to the file.
  10382. * Return
  10383. * File atime on success or FALSE on failure.
  10384. */
  10385. static int jx9Vfs_file_atime(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10386. {
  10387. const char *zPath;
  10388. jx9_int64 iTime;
  10389. jx9_vfs *pVfs;
  10390. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  10391. /* Missing/Invalid argument, return FALSE */
  10392. jx9_result_bool(pCtx, 0);
  10393. return JX9_OK;
  10394. }
  10395. /* Point to the underlying vfs */
  10396. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10397. if( pVfs == 0 || pVfs->xFileAtime == 0 ){
  10398. /* IO routine not implemented, return NULL */
  10399. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  10400. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  10401. jx9_function_name(pCtx)
  10402. );
  10403. jx9_result_bool(pCtx, 0);
  10404. return JX9_OK;
  10405. }
  10406. /* Point to the desired directory */
  10407. zPath = jx9_value_to_string(apArg[0], 0);
  10408. /* Perform the requested operation */
  10409. iTime = pVfs->xFileAtime(zPath);
  10410. /* IO return value */
  10411. jx9_result_int64(pCtx, iTime);
  10412. return JX9_OK;
  10413. }
  10414. /*
  10415. * int64 filemtime(string $filename)
  10416. * Gets file modification time.
  10417. * Parameters
  10418. * $filename
  10419. * Path to the file.
  10420. * Return
  10421. * File mtime on success or FALSE on failure.
  10422. */
  10423. static int jx9Vfs_file_mtime(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10424. {
  10425. const char *zPath;
  10426. jx9_int64 iTime;
  10427. jx9_vfs *pVfs;
  10428. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  10429. /* Missing/Invalid argument, return FALSE */
  10430. jx9_result_bool(pCtx, 0);
  10431. return JX9_OK;
  10432. }
  10433. /* Point to the underlying vfs */
  10434. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10435. if( pVfs == 0 || pVfs->xFileMtime == 0 ){
  10436. /* IO routine not implemented, return NULL */
  10437. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  10438. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  10439. jx9_function_name(pCtx)
  10440. );
  10441. jx9_result_bool(pCtx, 0);
  10442. return JX9_OK;
  10443. }
  10444. /* Point to the desired directory */
  10445. zPath = jx9_value_to_string(apArg[0], 0);
  10446. /* Perform the requested operation */
  10447. iTime = pVfs->xFileMtime(zPath);
  10448. /* IO return value */
  10449. jx9_result_int64(pCtx, iTime);
  10450. return JX9_OK;
  10451. }
  10452. /*
  10453. * int64 filectime(string $filename)
  10454. * Gets inode change time of file.
  10455. * Parameters
  10456. * $filename
  10457. * Path to the file.
  10458. * Return
  10459. * File ctime on success or FALSE on failure.
  10460. */
  10461. static int jx9Vfs_file_ctime(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10462. {
  10463. const char *zPath;
  10464. jx9_int64 iTime;
  10465. jx9_vfs *pVfs;
  10466. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  10467. /* Missing/Invalid argument, return FALSE */
  10468. jx9_result_bool(pCtx, 0);
  10469. return JX9_OK;
  10470. }
  10471. /* Point to the underlying vfs */
  10472. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10473. if( pVfs == 0 || pVfs->xFileCtime == 0 ){
  10474. /* IO routine not implemented, return NULL */
  10475. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  10476. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  10477. jx9_function_name(pCtx)
  10478. );
  10479. jx9_result_bool(pCtx, 0);
  10480. return JX9_OK;
  10481. }
  10482. /* Point to the desired directory */
  10483. zPath = jx9_value_to_string(apArg[0], 0);
  10484. /* Perform the requested operation */
  10485. iTime = pVfs->xFileCtime(zPath);
  10486. /* IO return value */
  10487. jx9_result_int64(pCtx, iTime);
  10488. return JX9_OK;
  10489. }
  10490. /*
  10491. * bool is_file(string $filename)
  10492. * Tells whether the filename is a regular file.
  10493. * Parameters
  10494. * $filename
  10495. * Path to the file.
  10496. * Return
  10497. * TRUE on success or FALSE on failure.
  10498. */
  10499. static int jx9Vfs_is_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10500. {
  10501. const char *zPath;
  10502. jx9_vfs *pVfs;
  10503. int rc;
  10504. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  10505. /* Missing/Invalid argument, return FALSE */
  10506. jx9_result_bool(pCtx, 0);
  10507. return JX9_OK;
  10508. }
  10509. /* Point to the underlying vfs */
  10510. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10511. if( pVfs == 0 || pVfs->xIsfile == 0 ){
  10512. /* IO routine not implemented, return NULL */
  10513. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  10514. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  10515. jx9_function_name(pCtx)
  10516. );
  10517. jx9_result_bool(pCtx, 0);
  10518. return JX9_OK;
  10519. }
  10520. /* Point to the desired directory */
  10521. zPath = jx9_value_to_string(apArg[0], 0);
  10522. /* Perform the requested operation */
  10523. rc = pVfs->xIsfile(zPath);
  10524. /* IO return value */
  10525. jx9_result_bool(pCtx, rc == JX9_OK);
  10526. return JX9_OK;
  10527. }
  10528. /*
  10529. * bool is_link(string $filename)
  10530. * Tells whether the filename is a symbolic link.
  10531. * Parameters
  10532. * $filename
  10533. * Path to the file.
  10534. * Return
  10535. * TRUE on success or FALSE on failure.
  10536. */
  10537. static int jx9Vfs_is_link(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10538. {
  10539. const char *zPath;
  10540. jx9_vfs *pVfs;
  10541. int rc;
  10542. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  10543. /* Missing/Invalid argument, return FALSE */
  10544. jx9_result_bool(pCtx, 0);
  10545. return JX9_OK;
  10546. }
  10547. /* Point to the underlying vfs */
  10548. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10549. if( pVfs == 0 || pVfs->xIslink == 0 ){
  10550. /* IO routine not implemented, return NULL */
  10551. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  10552. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  10553. jx9_function_name(pCtx)
  10554. );
  10555. jx9_result_bool(pCtx, 0);
  10556. return JX9_OK;
  10557. }
  10558. /* Point to the desired directory */
  10559. zPath = jx9_value_to_string(apArg[0], 0);
  10560. /* Perform the requested operation */
  10561. rc = pVfs->xIslink(zPath);
  10562. /* IO return value */
  10563. jx9_result_bool(pCtx, rc == JX9_OK);
  10564. return JX9_OK;
  10565. }
  10566. /*
  10567. * bool is_readable(string $filename)
  10568. * Tells whether a file exists and is readable.
  10569. * Parameters
  10570. * $filename
  10571. * Path to the file.
  10572. * Return
  10573. * TRUE on success or FALSE on failure.
  10574. */
  10575. static int jx9Vfs_is_readable(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10576. {
  10577. const char *zPath;
  10578. jx9_vfs *pVfs;
  10579. int rc;
  10580. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  10581. /* Missing/Invalid argument, return FALSE */
  10582. jx9_result_bool(pCtx, 0);
  10583. return JX9_OK;
  10584. }
  10585. /* Point to the underlying vfs */
  10586. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10587. if( pVfs == 0 || pVfs->xReadable == 0 ){
  10588. /* IO routine not implemented, return NULL */
  10589. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  10590. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  10591. jx9_function_name(pCtx)
  10592. );
  10593. jx9_result_bool(pCtx, 0);
  10594. return JX9_OK;
  10595. }
  10596. /* Point to the desired directory */
  10597. zPath = jx9_value_to_string(apArg[0], 0);
  10598. /* Perform the requested operation */
  10599. rc = pVfs->xReadable(zPath);
  10600. /* IO return value */
  10601. jx9_result_bool(pCtx, rc == JX9_OK);
  10602. return JX9_OK;
  10603. }
  10604. /*
  10605. * bool is_writable(string $filename)
  10606. * Tells whether the filename is writable.
  10607. * Parameters
  10608. * $filename
  10609. * Path to the file.
  10610. * Return
  10611. * TRUE on success or FALSE on failure.
  10612. */
  10613. static int jx9Vfs_is_writable(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10614. {
  10615. const char *zPath;
  10616. jx9_vfs *pVfs;
  10617. int rc;
  10618. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  10619. /* Missing/Invalid argument, return FALSE */
  10620. jx9_result_bool(pCtx, 0);
  10621. return JX9_OK;
  10622. }
  10623. /* Point to the underlying vfs */
  10624. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10625. if( pVfs == 0 || pVfs->xWritable == 0 ){
  10626. /* IO routine not implemented, return NULL */
  10627. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  10628. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  10629. jx9_function_name(pCtx)
  10630. );
  10631. jx9_result_bool(pCtx, 0);
  10632. return JX9_OK;
  10633. }
  10634. /* Point to the desired directory */
  10635. zPath = jx9_value_to_string(apArg[0], 0);
  10636. /* Perform the requested operation */
  10637. rc = pVfs->xWritable(zPath);
  10638. /* IO return value */
  10639. jx9_result_bool(pCtx, rc == JX9_OK);
  10640. return JX9_OK;
  10641. }
  10642. /*
  10643. * bool is_executable(string $filename)
  10644. * Tells whether the filename is executable.
  10645. * Parameters
  10646. * $filename
  10647. * Path to the file.
  10648. * Return
  10649. * TRUE on success or FALSE on failure.
  10650. */
  10651. static int jx9Vfs_is_executable(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10652. {
  10653. const char *zPath;
  10654. jx9_vfs *pVfs;
  10655. int rc;
  10656. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  10657. /* Missing/Invalid argument, return FALSE */
  10658. jx9_result_bool(pCtx, 0);
  10659. return JX9_OK;
  10660. }
  10661. /* Point to the underlying vfs */
  10662. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10663. if( pVfs == 0 || pVfs->xExecutable == 0 ){
  10664. /* IO routine not implemented, return NULL */
  10665. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  10666. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  10667. jx9_function_name(pCtx)
  10668. );
  10669. jx9_result_bool(pCtx, 0);
  10670. return JX9_OK;
  10671. }
  10672. /* Point to the desired directory */
  10673. zPath = jx9_value_to_string(apArg[0], 0);
  10674. /* Perform the requested operation */
  10675. rc = pVfs->xExecutable(zPath);
  10676. /* IO return value */
  10677. jx9_result_bool(pCtx, rc == JX9_OK);
  10678. return JX9_OK;
  10679. }
  10680. /*
  10681. * string filetype(string $filename)
  10682. * Gets file type.
  10683. * Parameters
  10684. * $filename
  10685. * Path to the file.
  10686. * Return
  10687. * The type of the file. Possible values are fifo, char, dir, block, link
  10688. * file, socket and unknown.
  10689. */
  10690. static int jx9Vfs_filetype(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10691. {
  10692. const char *zPath;
  10693. jx9_vfs *pVfs;
  10694. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  10695. /* Missing/Invalid argument, return 'unknown' */
  10696. jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
  10697. return JX9_OK;
  10698. }
  10699. /* Point to the underlying vfs */
  10700. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10701. if( pVfs == 0 || pVfs->xFiletype == 0 ){
  10702. /* IO routine not implemented, return NULL */
  10703. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  10704. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  10705. jx9_function_name(pCtx)
  10706. );
  10707. jx9_result_bool(pCtx, 0);
  10708. return JX9_OK;
  10709. }
  10710. /* Point to the desired directory */
  10711. zPath = jx9_value_to_string(apArg[0], 0);
  10712. /* Set the empty string as the default return value */
  10713. jx9_result_string(pCtx, "", 0);
  10714. /* Perform the requested operation */
  10715. pVfs->xFiletype(zPath, pCtx);
  10716. return JX9_OK;
  10717. }
  10718. /*
  10719. * array stat(string $filename)
  10720. * Gives information about a file.
  10721. * Parameters
  10722. * $filename
  10723. * Path to the file.
  10724. * Return
  10725. * An associative array on success holding the following entries on success
  10726. * 0 dev device number
  10727. * 1 ino inode number (zero on windows)
  10728. * 2 mode inode protection mode
  10729. * 3 nlink number of links
  10730. * 4 uid userid of owner (zero on windows)
  10731. * 5 gid groupid of owner (zero on windows)
  10732. * 6 rdev device type, if inode device
  10733. * 7 size size in bytes
  10734. * 8 atime time of last access (Unix timestamp)
  10735. * 9 mtime time of last modification (Unix timestamp)
  10736. * 10 ctime time of last inode change (Unix timestamp)
  10737. * 11 blksize blocksize of filesystem IO (zero on windows)
  10738. * 12 blocks number of 512-byte blocks allocated.
  10739. * Note:
  10740. * FALSE is returned on failure.
  10741. */
  10742. static int jx9Vfs_stat(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10743. {
  10744. jx9_value *pArray, *pValue;
  10745. const char *zPath;
  10746. jx9_vfs *pVfs;
  10747. int rc;
  10748. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  10749. /* Missing/Invalid argument, return FALSE */
  10750. jx9_result_bool(pCtx, 0);
  10751. return JX9_OK;
  10752. }
  10753. /* Point to the underlying vfs */
  10754. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10755. if( pVfs == 0 || pVfs->xStat == 0 ){
  10756. /* IO routine not implemented, return NULL */
  10757. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  10758. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  10759. jx9_function_name(pCtx)
  10760. );
  10761. jx9_result_bool(pCtx, 0);
  10762. return JX9_OK;
  10763. }
  10764. /* Create the array and the working value */
  10765. pArray = jx9_context_new_array(pCtx);
  10766. pValue = jx9_context_new_scalar(pCtx);
  10767. if( pArray == 0 || pValue == 0 ){
  10768. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
  10769. jx9_result_bool(pCtx, 0);
  10770. return JX9_OK;
  10771. }
  10772. /* Extract the file path */
  10773. zPath = jx9_value_to_string(apArg[0], 0);
  10774. /* Perform the requested operation */
  10775. rc = pVfs->xStat(zPath, pArray, pValue);
  10776. if( rc != JX9_OK ){
  10777. /* IO error, return FALSE */
  10778. jx9_result_bool(pCtx, 0);
  10779. }else{
  10780. /* Return the associative array */
  10781. jx9_result_value(pCtx, pArray);
  10782. }
  10783. /* Don't worry about freeing memory here, everything will be released
  10784. * automatically as soon we return from this function. */
  10785. return JX9_OK;
  10786. }
  10787. /*
  10788. * array lstat(string $filename)
  10789. * Gives information about a file or symbolic link.
  10790. * Parameters
  10791. * $filename
  10792. * Path to the file.
  10793. * Return
  10794. * An associative array on success holding the following entries on success
  10795. * 0 dev device number
  10796. * 1 ino inode number (zero on windows)
  10797. * 2 mode inode protection mode
  10798. * 3 nlink number of links
  10799. * 4 uid userid of owner (zero on windows)
  10800. * 5 gid groupid of owner (zero on windows)
  10801. * 6 rdev device type, if inode device
  10802. * 7 size size in bytes
  10803. * 8 atime time of last access (Unix timestamp)
  10804. * 9 mtime time of last modification (Unix timestamp)
  10805. * 10 ctime time of last inode change (Unix timestamp)
  10806. * 11 blksize blocksize of filesystem IO (zero on windows)
  10807. * 12 blocks number of 512-byte blocks allocated.
  10808. * Note:
  10809. * FALSE is returned on failure.
  10810. */
  10811. static int jx9Vfs_lstat(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10812. {
  10813. jx9_value *pArray, *pValue;
  10814. const char *zPath;
  10815. jx9_vfs *pVfs;
  10816. int rc;
  10817. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  10818. /* Missing/Invalid argument, return FALSE */
  10819. jx9_result_bool(pCtx, 0);
  10820. return JX9_OK;
  10821. }
  10822. /* Point to the underlying vfs */
  10823. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10824. if( pVfs == 0 || pVfs->xlStat == 0 ){
  10825. /* IO routine not implemented, return NULL */
  10826. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  10827. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  10828. jx9_function_name(pCtx)
  10829. );
  10830. jx9_result_bool(pCtx, 0);
  10831. return JX9_OK;
  10832. }
  10833. /* Create the array and the working value */
  10834. pArray = jx9_context_new_array(pCtx);
  10835. pValue = jx9_context_new_scalar(pCtx);
  10836. if( pArray == 0 || pValue == 0 ){
  10837. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
  10838. jx9_result_bool(pCtx, 0);
  10839. return JX9_OK;
  10840. }
  10841. /* Extract the file path */
  10842. zPath = jx9_value_to_string(apArg[0], 0);
  10843. /* Perform the requested operation */
  10844. rc = pVfs->xlStat(zPath, pArray, pValue);
  10845. if( rc != JX9_OK ){
  10846. /* IO error, return FALSE */
  10847. jx9_result_bool(pCtx, 0);
  10848. }else{
  10849. /* Return the associative array */
  10850. jx9_result_value(pCtx, pArray);
  10851. }
  10852. /* Don't worry about freeing memory here, everything will be released
  10853. * automatically as soon we return from this function. */
  10854. return JX9_OK;
  10855. }
  10856. /*
  10857. * string getenv(string $varname)
  10858. * Gets the value of an environment variable.
  10859. * Parameters
  10860. * $varname
  10861. * The variable name.
  10862. * Return
  10863. * Returns the value of the environment variable varname, or FALSE if the environment
  10864. * variable varname does not exist.
  10865. */
  10866. static int jx9Vfs_getenv(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10867. {
  10868. const char *zEnv;
  10869. jx9_vfs *pVfs;
  10870. int iLen;
  10871. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  10872. /* Missing/Invalid argument, return FALSE */
  10873. jx9_result_bool(pCtx, 0);
  10874. return JX9_OK;
  10875. }
  10876. /* Point to the underlying vfs */
  10877. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10878. if( pVfs == 0 || pVfs->xGetenv == 0 ){
  10879. /* IO routine not implemented, return NULL */
  10880. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  10881. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  10882. jx9_function_name(pCtx)
  10883. );
  10884. jx9_result_bool(pCtx, 0);
  10885. return JX9_OK;
  10886. }
  10887. /* Extract the environment variable */
  10888. zEnv = jx9_value_to_string(apArg[0], &iLen);
  10889. /* Set a boolean FALSE as the default return value */
  10890. jx9_result_bool(pCtx, 0);
  10891. if( iLen < 1 ){
  10892. /* Empty string */
  10893. return JX9_OK;
  10894. }
  10895. /* Perform the requested operation */
  10896. pVfs->xGetenv(zEnv, pCtx);
  10897. return JX9_OK;
  10898. }
  10899. /*
  10900. * bool putenv(string $settings)
  10901. * Set the value of an environment variable.
  10902. * Parameters
  10903. * $setting
  10904. * The setting, like "FOO=BAR"
  10905. * Return
  10906. * TRUE on success or FALSE on failure.
  10907. */
  10908. static int jx9Vfs_putenv(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10909. {
  10910. const char *zName, *zValue;
  10911. char *zSettings, *zEnd;
  10912. jx9_vfs *pVfs;
  10913. int iLen, rc;
  10914. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  10915. /* Missing/Invalid argument, return FALSE */
  10916. jx9_result_bool(pCtx, 0);
  10917. return JX9_OK;
  10918. }
  10919. /* Extract the setting variable */
  10920. zSettings = (char *)jx9_value_to_string(apArg[0], &iLen);
  10921. if( iLen < 1 ){
  10922. /* Empty string, return FALSE */
  10923. jx9_result_bool(pCtx, 0);
  10924. return JX9_OK;
  10925. }
  10926. /* Parse the setting */
  10927. zEnd = &zSettings[iLen];
  10928. zValue = 0;
  10929. zName = zSettings;
  10930. while( zSettings < zEnd ){
  10931. if( zSettings[0] == '=' ){
  10932. /* Null terminate the name */
  10933. zSettings[0] = 0;
  10934. zValue = &zSettings[1];
  10935. break;
  10936. }
  10937. zSettings++;
  10938. }
  10939. /* Install the environment variable in the $_Env array */
  10940. if( zValue == 0 || zName[0] == 0 || zValue >= zEnd || zName >= zValue ){
  10941. /* Invalid settings, retun FALSE */
  10942. jx9_result_bool(pCtx, 0);
  10943. if( zSettings < zEnd ){
  10944. zSettings[0] = '=';
  10945. }
  10946. return JX9_OK;
  10947. }
  10948. jx9_vm_config(pCtx->pVm, JX9_VM_CONFIG_ENV_ATTR, zName, zValue, (int)(zEnd-zValue));
  10949. /* Point to the underlying vfs */
  10950. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10951. if( pVfs == 0 || pVfs->xSetenv == 0 ){
  10952. /* IO routine not implemented, return NULL */
  10953. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  10954. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  10955. jx9_function_name(pCtx)
  10956. );
  10957. jx9_result_bool(pCtx, 0);
  10958. zSettings[0] = '=';
  10959. return JX9_OK;
  10960. }
  10961. /* Perform the requested operation */
  10962. rc = pVfs->xSetenv(zName, zValue);
  10963. jx9_result_bool(pCtx, rc == JX9_OK );
  10964. zSettings[0] = '=';
  10965. return JX9_OK;
  10966. }
  10967. /*
  10968. * bool touch(string $filename[, int64 $time = time()[, int64 $atime]])
  10969. * Sets access and modification time of file.
  10970. * Note: On windows
  10971. * If the file does not exists, it will not be created.
  10972. * Parameters
  10973. * $filename
  10974. * The name of the file being touched.
  10975. * $time
  10976. * The touch time. If time is not supplied, the current system time is used.
  10977. * $atime
  10978. * If present, the access time of the given filename is set to the value of atime.
  10979. * Otherwise, it is set to the value passed to the time parameter. If neither are
  10980. * present, the current system time is used.
  10981. * Return
  10982. * TRUE on success or FALSE on failure.
  10983. */
  10984. static int jx9Vfs_touch(jx9_context *pCtx, int nArg, jx9_value **apArg)
  10985. {
  10986. jx9_int64 nTime, nAccess;
  10987. const char *zFile;
  10988. jx9_vfs *pVfs;
  10989. int rc;
  10990. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  10991. /* Missing/Invalid argument, return FALSE */
  10992. jx9_result_bool(pCtx, 0);
  10993. return JX9_OK;
  10994. }
  10995. /* Point to the underlying vfs */
  10996. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  10997. if( pVfs == 0 || pVfs->xTouch == 0 ){
  10998. /* IO routine not implemented, return NULL */
  10999. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  11000. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  11001. jx9_function_name(pCtx)
  11002. );
  11003. jx9_result_bool(pCtx, 0);
  11004. return JX9_OK;
  11005. }
  11006. /* Perform the requested operation */
  11007. nTime = nAccess = -1;
  11008. zFile = jx9_value_to_string(apArg[0], 0);
  11009. if( nArg > 1 ){
  11010. nTime = jx9_value_to_int64(apArg[1]);
  11011. if( nArg > 2 ){
  11012. nAccess = jx9_value_to_int64(apArg[1]);
  11013. }else{
  11014. nAccess = nTime;
  11015. }
  11016. }
  11017. rc = pVfs->xTouch(zFile, nTime, nAccess);
  11018. /* IO result */
  11019. jx9_result_bool(pCtx, rc == JX9_OK);
  11020. return JX9_OK;
  11021. }
  11022. /*
  11023. * Path processing functions that do not need access to the VFS layer
  11024. * Authors:
  11025. * Symisc Systems, devel@symisc.net.
  11026. * Copyright (C) Symisc Systems, http://jx9.symisc.net
  11027. * Status:
  11028. * Stable.
  11029. */
  11030. /*
  11031. * string dirname(string $path)
  11032. * Returns parent directory's path.
  11033. * Parameters
  11034. * $path
  11035. * Target path.
  11036. * On Windows, both slash (/) and backslash (\) are used as directory separator character.
  11037. * In other environments, it is the forward slash (/).
  11038. * Return
  11039. * The path of the parent directory. If there are no slashes in path, a dot ('.')
  11040. * is returned, indicating the current directory.
  11041. */
  11042. static int jx9Builtin_dirname(jx9_context *pCtx, int nArg, jx9_value **apArg)
  11043. {
  11044. const char *zPath, *zDir;
  11045. int iLen, iDirlen;
  11046. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  11047. /* Missing/Invalid arguments, return the empty string */
  11048. jx9_result_string(pCtx, "", 0);
  11049. return JX9_OK;
  11050. }
  11051. /* Point to the target path */
  11052. zPath = jx9_value_to_string(apArg[0], &iLen);
  11053. if( iLen < 1 ){
  11054. /* Reuturn "." */
  11055. jx9_result_string(pCtx, ".", sizeof(char));
  11056. return JX9_OK;
  11057. }
  11058. /* Perform the requested operation */
  11059. zDir = jx9ExtractDirName(zPath, iLen, &iDirlen);
  11060. /* Return directory name */
  11061. jx9_result_string(pCtx, zDir, iDirlen);
  11062. return JX9_OK;
  11063. }
  11064. /*
  11065. * string basename(string $path[, string $suffix ])
  11066. * Returns trailing name component of path.
  11067. * Parameters
  11068. * $path
  11069. * Target path.
  11070. * On Windows, both slash (/) and backslash (\) are used as directory separator character.
  11071. * In other environments, it is the forward slash (/).
  11072. * $suffix
  11073. * If the name component ends in suffix this will also be cut off.
  11074. * Return
  11075. * The base name of the given path.
  11076. */
  11077. static int jx9Builtin_basename(jx9_context *pCtx, int nArg, jx9_value **apArg)
  11078. {
  11079. const char *zPath, *zBase, *zEnd;
  11080. int c, d, iLen;
  11081. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  11082. /* Missing/Invalid argument, return the empty string */
  11083. jx9_result_string(pCtx, "", 0);
  11084. return JX9_OK;
  11085. }
  11086. c = d = '/';
  11087. #ifdef __WINNT__
  11088. d = '\\';
  11089. #endif
  11090. /* Point to the target path */
  11091. zPath = jx9_value_to_string(apArg[0], &iLen);
  11092. if( iLen < 1 ){
  11093. /* Empty string */
  11094. jx9_result_string(pCtx, "", 0);
  11095. return JX9_OK;
  11096. }
  11097. /* Perform the requested operation */
  11098. zEnd = &zPath[iLen - 1];
  11099. /* Ignore trailing '/' */
  11100. while( zEnd > zPath && ( (int)zEnd[0] == c || (int)zEnd[0] == d ) ){
  11101. zEnd--;
  11102. }
  11103. iLen = (int)(&zEnd[1]-zPath);
  11104. while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){
  11105. zEnd--;
  11106. }
  11107. zBase = (zEnd > zPath) ? &zEnd[1] : zPath;
  11108. zEnd = &zPath[iLen];
  11109. if( nArg > 1 && jx9_value_is_string(apArg[1]) ){
  11110. const char *zSuffix;
  11111. int nSuffix;
  11112. /* Strip suffix */
  11113. zSuffix = jx9_value_to_string(apArg[1], &nSuffix);
  11114. if( nSuffix > 0 && nSuffix < iLen && SyMemcmp(&zEnd[-nSuffix], zSuffix, nSuffix) == 0 ){
  11115. zEnd -= nSuffix;
  11116. }
  11117. }
  11118. /* Store the basename */
  11119. jx9_result_string(pCtx, zBase, (int)(zEnd-zBase));
  11120. return JX9_OK;
  11121. }
  11122. /*
  11123. * value pathinfo(string $path [, int $options = PATHINFO_DIRNAME | PATHINFO_BASENAME | PATHINFO_EXTENSION | PATHINFO_FILENAME ])
  11124. * Returns information about a file path.
  11125. * Parameter
  11126. * $path
  11127. * The path to be parsed.
  11128. * $options
  11129. * If present, specifies a specific element to be returned; one of
  11130. * PATHINFO_DIRNAME, PATHINFO_BASENAME, PATHINFO_EXTENSION or PATHINFO_FILENAME.
  11131. * Return
  11132. * If the options parameter is not passed, an associative array containing the following
  11133. * elements is returned: dirname, basename, extension (if any), and filename.
  11134. * If options is present, returns a string containing the requested element.
  11135. */
  11136. typedef struct path_info path_info;
  11137. struct path_info
  11138. {
  11139. SyString sDir; /* Directory [i.e: /var/www] */
  11140. SyString sBasename; /* Basename [i.e httpd.conf] */
  11141. SyString sExtension; /* File extension [i.e xml, pdf..] */
  11142. SyString sFilename; /* Filename */
  11143. };
  11144. /*
  11145. * Extract path fields.
  11146. */
  11147. static sxi32 ExtractPathInfo(const char *zPath, int nByte, path_info *pOut)
  11148. {
  11149. const char *zPtr, *zEnd = &zPath[nByte - 1];
  11150. SyString *pCur;
  11151. int c, d;
  11152. c = d = '/';
  11153. #ifdef __WINNT__
  11154. d = '\\';
  11155. #endif
  11156. /* Zero the structure */
  11157. SyZero(pOut, sizeof(path_info));
  11158. /* Handle special case */
  11159. if( nByte == sizeof(char) && ( (int)zPath[0] == c || (int)zPath[0] == d ) ){
  11160. #ifdef __WINNT__
  11161. SyStringInitFromBuf(&pOut->sDir, "\\", sizeof(char));
  11162. #else
  11163. SyStringInitFromBuf(&pOut->sDir, "/", sizeof(char));
  11164. #endif
  11165. return SXRET_OK;
  11166. }
  11167. /* Extract the basename */
  11168. while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){
  11169. zEnd--;
  11170. }
  11171. zPtr = (zEnd > zPath) ? &zEnd[1] : zPath;
  11172. zEnd = &zPath[nByte];
  11173. /* dirname */
  11174. pCur = &pOut->sDir;
  11175. SyStringInitFromBuf(pCur, zPath, zPtr-zPath);
  11176. if( pCur->nByte > 1 ){
  11177. SyStringTrimTrailingChar(pCur, '/');
  11178. #ifdef __WINNT__
  11179. SyStringTrimTrailingChar(pCur, '\\');
  11180. #endif
  11181. }else if( (int)zPath[0] == c || (int)zPath[0] == d ){
  11182. #ifdef __WINNT__
  11183. SyStringInitFromBuf(&pOut->sDir, "\\", sizeof(char));
  11184. #else
  11185. SyStringInitFromBuf(&pOut->sDir, "/", sizeof(char));
  11186. #endif
  11187. }
  11188. /* basename/filename */
  11189. pCur = &pOut->sBasename;
  11190. SyStringInitFromBuf(pCur, zPtr, zEnd-zPtr);
  11191. SyStringTrimLeadingChar(pCur, '/');
  11192. #ifdef __WINNT__
  11193. SyStringTrimLeadingChar(pCur, '\\');
  11194. #endif
  11195. SyStringDupPtr(&pOut->sFilename, pCur);
  11196. if( pCur->nByte > 0 ){
  11197. /* extension */
  11198. zEnd--;
  11199. while( zEnd > pCur->zString /*basename*/ && zEnd[0] != '.' ){
  11200. zEnd--;
  11201. }
  11202. if( zEnd > pCur->zString ){
  11203. zEnd++; /* Jump leading dot */
  11204. SyStringInitFromBuf(&pOut->sExtension, zEnd, &zPath[nByte]-zEnd);
  11205. /* Fix filename */
  11206. pCur = &pOut->sFilename;
  11207. if( pCur->nByte > SyStringLength(&pOut->sExtension) ){
  11208. pCur->nByte -= 1 + SyStringLength(&pOut->sExtension);
  11209. }
  11210. }
  11211. }
  11212. return SXRET_OK;
  11213. }
  11214. /*
  11215. * value pathinfo(string $path [, int $options = PATHINFO_DIRNAME | PATHINFO_BASENAME | PATHINFO_EXTENSION | PATHINFO_FILENAME ])
  11216. * See block comment above.
  11217. */
  11218. static int jx9Builtin_pathinfo(jx9_context *pCtx, int nArg, jx9_value **apArg)
  11219. {
  11220. const char *zPath;
  11221. path_info sInfo;
  11222. SyString *pComp;
  11223. int iLen;
  11224. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  11225. /* Missing/Invalid argument, return the empty string */
  11226. jx9_result_string(pCtx, "", 0);
  11227. return JX9_OK;
  11228. }
  11229. /* Point to the target path */
  11230. zPath = jx9_value_to_string(apArg[0], &iLen);
  11231. if( iLen < 1 ){
  11232. /* Empty string */
  11233. jx9_result_string(pCtx, "", 0);
  11234. return JX9_OK;
  11235. }
  11236. /* Extract path info */
  11237. ExtractPathInfo(zPath, iLen, &sInfo);
  11238. if( nArg > 1 && jx9_value_is_int(apArg[1]) ){
  11239. /* Return path component */
  11240. int nComp = jx9_value_to_int(apArg[1]);
  11241. switch(nComp){
  11242. case 1: /* PATHINFO_DIRNAME */
  11243. pComp = &sInfo.sDir;
  11244. if( pComp->nByte > 0 ){
  11245. jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
  11246. }else{
  11247. /* Expand the empty string */
  11248. jx9_result_string(pCtx, "", 0);
  11249. }
  11250. break;
  11251. case 2: /*PATHINFO_BASENAME*/
  11252. pComp = &sInfo.sBasename;
  11253. if( pComp->nByte > 0 ){
  11254. jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
  11255. }else{
  11256. /* Expand the empty string */
  11257. jx9_result_string(pCtx, "", 0);
  11258. }
  11259. break;
  11260. case 3: /*PATHINFO_EXTENSION*/
  11261. pComp = &sInfo.sExtension;
  11262. if( pComp->nByte > 0 ){
  11263. jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
  11264. }else{
  11265. /* Expand the empty string */
  11266. jx9_result_string(pCtx, "", 0);
  11267. }
  11268. break;
  11269. case 4: /*PATHINFO_FILENAME*/
  11270. pComp = &sInfo.sFilename;
  11271. if( pComp->nByte > 0 ){
  11272. jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
  11273. }else{
  11274. /* Expand the empty string */
  11275. jx9_result_string(pCtx, "", 0);
  11276. }
  11277. break;
  11278. default:
  11279. /* Expand the empty string */
  11280. jx9_result_string(pCtx, "", 0);
  11281. break;
  11282. }
  11283. }else{
  11284. /* Return an associative array */
  11285. jx9_value *pArray, *pValue;
  11286. pArray = jx9_context_new_array(pCtx);
  11287. pValue = jx9_context_new_scalar(pCtx);
  11288. if( pArray == 0 || pValue == 0 ){
  11289. /* Out of mem, return NULL */
  11290. jx9_result_bool(pCtx, 0);
  11291. return JX9_OK;
  11292. }
  11293. /* dirname */
  11294. pComp = &sInfo.sDir;
  11295. if( pComp->nByte > 0 ){
  11296. jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
  11297. /* Perform the insertion */
  11298. jx9_array_add_strkey_elem(pArray, "dirname", pValue); /* Will make it's own copy */
  11299. }
  11300. /* Reset the string cursor */
  11301. jx9_value_reset_string_cursor(pValue);
  11302. /* basername */
  11303. pComp = &sInfo.sBasename;
  11304. if( pComp->nByte > 0 ){
  11305. jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
  11306. /* Perform the insertion */
  11307. jx9_array_add_strkey_elem(pArray, "basename", pValue); /* Will make it's own copy */
  11308. }
  11309. /* Reset the string cursor */
  11310. jx9_value_reset_string_cursor(pValue);
  11311. /* extension */
  11312. pComp = &sInfo.sExtension;
  11313. if( pComp->nByte > 0 ){
  11314. jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
  11315. /* Perform the insertion */
  11316. jx9_array_add_strkey_elem(pArray, "extension", pValue); /* Will make it's own copy */
  11317. }
  11318. /* Reset the string cursor */
  11319. jx9_value_reset_string_cursor(pValue);
  11320. /* filename */
  11321. pComp = &sInfo.sFilename;
  11322. if( pComp->nByte > 0 ){
  11323. jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
  11324. /* Perform the insertion */
  11325. jx9_array_add_strkey_elem(pArray, "filename", pValue); /* Will make it's own copy */
  11326. }
  11327. /* Return the created array */
  11328. jx9_result_value(pCtx, pArray);
  11329. /* Don't worry about freeing memory, everything will be released
  11330. * automatically as soon we return from this foreign function.
  11331. */
  11332. }
  11333. return JX9_OK;
  11334. }
  11335. /*
  11336. * Globbing implementation extracted from the sqlite3 source tree.
  11337. * Original author: D. Richard Hipp (http://www.sqlite.org)
  11338. * Status: Public Domain
  11339. */
  11340. typedef unsigned char u8;
  11341. /* An array to map all upper-case characters into their corresponding
  11342. ** lower-case character.
  11343. **
  11344. ** SQLite only considers US-ASCII (or EBCDIC) characters. We do not
  11345. ** handle case conversions for the UTF character set since the tables
  11346. ** involved are nearly as big or bigger than SQLite itself.
  11347. */
  11348. static const unsigned char sqlite3UpperToLower[] = {
  11349. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
  11350. 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
  11351. 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
  11352. 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103,
  11353. 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
  11354. 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
  11355. 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
  11356. 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
  11357. 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161,
  11358. 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
  11359. 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197,
  11360. 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215,
  11361. 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233,
  11362. 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251,
  11363. 252, 253, 254, 255
  11364. };
  11365. #define GlogUpperToLower(A) if( A<0x80 ){ A = sqlite3UpperToLower[A]; }
  11366. /*
  11367. ** Assuming zIn points to the first byte of a UTF-8 character,
  11368. ** advance zIn to point to the first byte of the next UTF-8 character.
  11369. */
  11370. #define SQLITE_SKIP_UTF8(zIn) { \
  11371. if( (*(zIn++))>=0xc0 ){ \
  11372. while( (*zIn & 0xc0)==0x80 ){ zIn++; } \
  11373. } \
  11374. }
  11375. /*
  11376. ** Compare two UTF-8 strings for equality where the first string can
  11377. ** potentially be a "glob" expression. Return true (1) if they
  11378. ** are the same and false (0) if they are different.
  11379. **
  11380. ** Globbing rules:
  11381. **
  11382. ** '*' Matches any sequence of zero or more characters.
  11383. **
  11384. ** '?' Matches exactly one character.
  11385. **
  11386. ** [...] Matches one character from the enclosed list of
  11387. ** characters.
  11388. **
  11389. ** [^...] Matches one character not in the enclosed list.
  11390. **
  11391. ** With the [...] and [^...] matching, a ']' character can be included
  11392. ** in the list by making it the first character after '[' or '^'. A
  11393. ** range of characters can be specified using '-'. Example:
  11394. ** "[a-z]" matches any single lower-case letter. To match a '-', make
  11395. ** it the last character in the list.
  11396. **
  11397. ** This routine is usually quick, but can be N**2 in the worst case.
  11398. **
  11399. ** Hints: to match '*' or '?', put them in "[]". Like this:
  11400. **
  11401. ** abc[*]xyz Matches "abc*xyz" only
  11402. */
  11403. static int patternCompare(
  11404. const u8 *zPattern, /* The glob pattern */
  11405. const u8 *zString, /* The string to compare against the glob */
  11406. const int esc, /* The escape character */
  11407. int noCase
  11408. ){
  11409. int c, c2;
  11410. int invert;
  11411. int seen;
  11412. u8 matchOne = '?';
  11413. u8 matchAll = '*';
  11414. u8 matchSet = '[';
  11415. int prevEscape = 0; /* True if the previous character was 'escape' */
  11416. if( !zPattern || !zString ) return 0;
  11417. while( (c = jx9Utf8Read(zPattern, 0, &zPattern))!=0 ){
  11418. if( !prevEscape && c==matchAll ){
  11419. while( (c= jx9Utf8Read(zPattern, 0, &zPattern)) == matchAll
  11420. || c == matchOne ){
  11421. if( c==matchOne && jx9Utf8Read(zString, 0, &zString)==0 ){
  11422. return 0;
  11423. }
  11424. }
  11425. if( c==0 ){
  11426. return 1;
  11427. }else if( c==esc ){
  11428. c = jx9Utf8Read(zPattern, 0, &zPattern);
  11429. if( c==0 ){
  11430. return 0;
  11431. }
  11432. }else if( c==matchSet ){
  11433. if( (esc==0) || (matchSet<0x80) ) return 0;
  11434. while( *zString && patternCompare(&zPattern[-1], zString, esc, noCase)==0 ){
  11435. SQLITE_SKIP_UTF8(zString);
  11436. }
  11437. return *zString!=0;
  11438. }
  11439. while( (c2 = jx9Utf8Read(zString, 0, &zString))!=0 ){
  11440. if( noCase ){
  11441. GlogUpperToLower(c2);
  11442. GlogUpperToLower(c);
  11443. while( c2 != 0 && c2 != c ){
  11444. c2 = jx9Utf8Read(zString, 0, &zString);
  11445. GlogUpperToLower(c2);
  11446. }
  11447. }else{
  11448. while( c2 != 0 && c2 != c ){
  11449. c2 = jx9Utf8Read(zString, 0, &zString);
  11450. }
  11451. }
  11452. if( c2==0 ) return 0;
  11453. if( patternCompare(zPattern, zString, esc, noCase) ) return 1;
  11454. }
  11455. return 0;
  11456. }else if( !prevEscape && c==matchOne ){
  11457. if( jx9Utf8Read(zString, 0, &zString)==0 ){
  11458. return 0;
  11459. }
  11460. }else if( c==matchSet ){
  11461. int prior_c = 0;
  11462. if( esc == 0 ) return 0;
  11463. seen = 0;
  11464. invert = 0;
  11465. c = jx9Utf8Read(zString, 0, &zString);
  11466. if( c==0 ) return 0;
  11467. c2 = jx9Utf8Read(zPattern, 0, &zPattern);
  11468. if( c2=='^' ){
  11469. invert = 1;
  11470. c2 = jx9Utf8Read(zPattern, 0, &zPattern);
  11471. }
  11472. if( c2==']' ){
  11473. if( c==']' ) seen = 1;
  11474. c2 = jx9Utf8Read(zPattern, 0, &zPattern);
  11475. }
  11476. while( c2 && c2!=']' ){
  11477. if( c2=='-' && zPattern[0]!=']' && zPattern[0]!=0 && prior_c>0 ){
  11478. c2 = jx9Utf8Read(zPattern, 0, &zPattern);
  11479. if( c>=prior_c && c<=c2 ) seen = 1;
  11480. prior_c = 0;
  11481. }else{
  11482. if( c==c2 ){
  11483. seen = 1;
  11484. }
  11485. prior_c = c2;
  11486. }
  11487. c2 = jx9Utf8Read(zPattern, 0, &zPattern);
  11488. }
  11489. if( c2==0 || (seen ^ invert)==0 ){
  11490. return 0;
  11491. }
  11492. }else if( esc==c && !prevEscape ){
  11493. prevEscape = 1;
  11494. }else{
  11495. c2 = jx9Utf8Read(zString, 0, &zString);
  11496. if( noCase ){
  11497. GlogUpperToLower(c);
  11498. GlogUpperToLower(c2);
  11499. }
  11500. if( c!=c2 ){
  11501. return 0;
  11502. }
  11503. prevEscape = 0;
  11504. }
  11505. }
  11506. return *zString==0;
  11507. }
  11508. /*
  11509. * Wrapper around patternCompare() defined above.
  11510. * See block comment above for more information.
  11511. */
  11512. static int Glob(const unsigned char *zPattern, const unsigned char *zString, int iEsc, int CaseCompare)
  11513. {
  11514. int rc;
  11515. if( iEsc < 0 ){
  11516. iEsc = '\\';
  11517. }
  11518. rc = patternCompare(zPattern, zString, iEsc, CaseCompare);
  11519. return rc;
  11520. }
  11521. /*
  11522. * bool fnmatch(string $pattern, string $string[, int $flags = 0 ])
  11523. * Match filename against a pattern.
  11524. * Parameters
  11525. * $pattern
  11526. * The shell wildcard pattern.
  11527. * $string
  11528. * The tested string.
  11529. * $flags
  11530. * A list of possible flags:
  11531. * FNM_NOESCAPE Disable backslash escaping.
  11532. * FNM_PATHNAME Slash in string only matches slash in the given pattern.
  11533. * FNM_PERIOD Leading period in string must be exactly matched by period in the given pattern.
  11534. * FNM_CASEFOLD Caseless match.
  11535. * Return
  11536. * TRUE if there is a match, FALSE otherwise.
  11537. */
  11538. static int jx9Builtin_fnmatch(jx9_context *pCtx, int nArg, jx9_value **apArg)
  11539. {
  11540. const char *zString, *zPattern;
  11541. int iEsc = '\\';
  11542. int noCase = 0;
  11543. int rc;
  11544. if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
  11545. /* Missing/Invalid arguments, return FALSE */
  11546. jx9_result_bool(pCtx, 0);
  11547. return JX9_OK;
  11548. }
  11549. /* Extract the pattern and the string */
  11550. zPattern = jx9_value_to_string(apArg[0], 0);
  11551. zString = jx9_value_to_string(apArg[1], 0);
  11552. /* Extract the flags if avaialble */
  11553. if( nArg > 2 && jx9_value_is_int(apArg[2]) ){
  11554. rc = jx9_value_to_int(apArg[2]);
  11555. if( rc & 0x01 /*FNM_NOESCAPE*/){
  11556. iEsc = 0;
  11557. }
  11558. if( rc & 0x08 /*FNM_CASEFOLD*/){
  11559. noCase = 1;
  11560. }
  11561. }
  11562. /* Go globbing */
  11563. rc = Glob((const unsigned char *)zPattern, (const unsigned char *)zString, iEsc, noCase);
  11564. /* Globbing result */
  11565. jx9_result_bool(pCtx, rc);
  11566. return JX9_OK;
  11567. }
  11568. /*
  11569. * bool strglob(string $pattern, string $string)
  11570. * Match string against a pattern.
  11571. * Parameters
  11572. * $pattern
  11573. * The shell wildcard pattern.
  11574. * $string
  11575. * The tested string.
  11576. * Return
  11577. * TRUE if there is a match, FALSE otherwise.
  11578. */
  11579. static int jx9Builtin_strglob(jx9_context *pCtx, int nArg, jx9_value **apArg)
  11580. {
  11581. const char *zString, *zPattern;
  11582. int iEsc = '\\';
  11583. int rc;
  11584. if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
  11585. /* Missing/Invalid arguments, return FALSE */
  11586. jx9_result_bool(pCtx, 0);
  11587. return JX9_OK;
  11588. }
  11589. /* Extract the pattern and the string */
  11590. zPattern = jx9_value_to_string(apArg[0], 0);
  11591. zString = jx9_value_to_string(apArg[1], 0);
  11592. /* Go globbing */
  11593. rc = Glob((const unsigned char *)zPattern, (const unsigned char *)zString, iEsc, 0);
  11594. /* Globbing result */
  11595. jx9_result_bool(pCtx, rc);
  11596. return JX9_OK;
  11597. }
  11598. /*
  11599. * bool link(string $target, string $link)
  11600. * Create a hard link.
  11601. * Parameters
  11602. * $target
  11603. * Target of the link.
  11604. * $link
  11605. * The link name.
  11606. * Return
  11607. * TRUE on success or FALSE on failure.
  11608. */
  11609. static int jx9Vfs_link(jx9_context *pCtx, int nArg, jx9_value **apArg)
  11610. {
  11611. const char *zTarget, *zLink;
  11612. jx9_vfs *pVfs;
  11613. int rc;
  11614. if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
  11615. /* Missing/Invalid arguments, return FALSE */
  11616. jx9_result_bool(pCtx, 0);
  11617. return JX9_OK;
  11618. }
  11619. /* Point to the underlying vfs */
  11620. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  11621. if( pVfs == 0 || pVfs->xLink == 0 ){
  11622. /* IO routine not implemented, return NULL */
  11623. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  11624. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  11625. jx9_function_name(pCtx)
  11626. );
  11627. jx9_result_bool(pCtx, 0);
  11628. return JX9_OK;
  11629. }
  11630. /* Extract the given arguments */
  11631. zTarget = jx9_value_to_string(apArg[0], 0);
  11632. zLink = jx9_value_to_string(apArg[1], 0);
  11633. /* Perform the requested operation */
  11634. rc = pVfs->xLink(zTarget, zLink, 0/*Not a symbolic link */);
  11635. /* IO result */
  11636. jx9_result_bool(pCtx, rc == JX9_OK );
  11637. return JX9_OK;
  11638. }
  11639. /*
  11640. * bool symlink(string $target, string $link)
  11641. * Creates a symbolic link.
  11642. * Parameters
  11643. * $target
  11644. * Target of the link.
  11645. * $link
  11646. * The link name.
  11647. * Return
  11648. * TRUE on success or FALSE on failure.
  11649. */
  11650. static int jx9Vfs_symlink(jx9_context *pCtx, int nArg, jx9_value **apArg)
  11651. {
  11652. const char *zTarget, *zLink;
  11653. jx9_vfs *pVfs;
  11654. int rc;
  11655. if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
  11656. /* Missing/Invalid arguments, return FALSE */
  11657. jx9_result_bool(pCtx, 0);
  11658. return JX9_OK;
  11659. }
  11660. /* Point to the underlying vfs */
  11661. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  11662. if( pVfs == 0 || pVfs->xLink == 0 ){
  11663. /* IO routine not implemented, return NULL */
  11664. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  11665. "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
  11666. jx9_function_name(pCtx)
  11667. );
  11668. jx9_result_bool(pCtx, 0);
  11669. return JX9_OK;
  11670. }
  11671. /* Extract the given arguments */
  11672. zTarget = jx9_value_to_string(apArg[0], 0);
  11673. zLink = jx9_value_to_string(apArg[1], 0);
  11674. /* Perform the requested operation */
  11675. rc = pVfs->xLink(zTarget, zLink, 1/*A symbolic link */);
  11676. /* IO result */
  11677. jx9_result_bool(pCtx, rc == JX9_OK );
  11678. return JX9_OK;
  11679. }
  11680. /*
  11681. * int umask([ int $mask ])
  11682. * Changes the current umask.
  11683. * Parameters
  11684. * $mask
  11685. * The new umask.
  11686. * Return
  11687. * umask() without arguments simply returns the current umask.
  11688. * Otherwise the old umask is returned.
  11689. */
  11690. static int jx9Vfs_umask(jx9_context *pCtx, int nArg, jx9_value **apArg)
  11691. {
  11692. int iOld, iNew;
  11693. jx9_vfs *pVfs;
  11694. /* Point to the underlying vfs */
  11695. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  11696. if( pVfs == 0 || pVfs->xUmask == 0 ){
  11697. /* IO routine not implemented, return -1 */
  11698. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  11699. "IO routine(%s) not implemented in the underlying VFS",
  11700. jx9_function_name(pCtx)
  11701. );
  11702. jx9_result_int(pCtx, 0);
  11703. return JX9_OK;
  11704. }
  11705. iNew = 0;
  11706. if( nArg > 0 ){
  11707. iNew = jx9_value_to_int(apArg[0]);
  11708. }
  11709. /* Perform the requested operation */
  11710. iOld = pVfs->xUmask(iNew);
  11711. /* Old mask */
  11712. jx9_result_int(pCtx, iOld);
  11713. return JX9_OK;
  11714. }
  11715. /*
  11716. * string sys_get_temp_dir()
  11717. * Returns directory path used for temporary files.
  11718. * Parameters
  11719. * None
  11720. * Return
  11721. * Returns the path of the temporary directory.
  11722. */
  11723. static int jx9Vfs_sys_get_temp_dir(jx9_context *pCtx, int nArg, jx9_value **apArg)
  11724. {
  11725. jx9_vfs *pVfs;
  11726. /* Set the empty string as the default return value */
  11727. jx9_result_string(pCtx, "", 0);
  11728. /* Point to the underlying vfs */
  11729. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  11730. if( pVfs == 0 || pVfs->xTempDir == 0 ){
  11731. SXUNUSED(nArg); /* cc warning */
  11732. SXUNUSED(apArg);
  11733. /* IO routine not implemented, return "" */
  11734. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  11735. "IO routine(%s) not implemented in the underlying VFS",
  11736. jx9_function_name(pCtx)
  11737. );
  11738. return JX9_OK;
  11739. }
  11740. /* Perform the requested operation */
  11741. pVfs->xTempDir(pCtx);
  11742. return JX9_OK;
  11743. }
  11744. /*
  11745. * string get_current_user()
  11746. * Returns the name of the current working user.
  11747. * Parameters
  11748. * None
  11749. * Return
  11750. * Returns the name of the current working user.
  11751. */
  11752. static int jx9Vfs_get_current_user(jx9_context *pCtx, int nArg, jx9_value **apArg)
  11753. {
  11754. jx9_vfs *pVfs;
  11755. /* Point to the underlying vfs */
  11756. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  11757. if( pVfs == 0 || pVfs->xUsername == 0 ){
  11758. SXUNUSED(nArg); /* cc warning */
  11759. SXUNUSED(apArg);
  11760. /* IO routine not implemented */
  11761. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  11762. "IO routine(%s) not implemented in the underlying VFS",
  11763. jx9_function_name(pCtx)
  11764. );
  11765. /* Set a dummy username */
  11766. jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
  11767. return JX9_OK;
  11768. }
  11769. /* Perform the requested operation */
  11770. pVfs->xUsername(pCtx);
  11771. return JX9_OK;
  11772. }
  11773. /*
  11774. * int64 getmypid()
  11775. * Gets process ID.
  11776. * Parameters
  11777. * None
  11778. * Return
  11779. * Returns the process ID.
  11780. */
  11781. static int jx9Vfs_getmypid(jx9_context *pCtx, int nArg, jx9_value **apArg)
  11782. {
  11783. jx9_int64 nProcessId;
  11784. jx9_vfs *pVfs;
  11785. /* Point to the underlying vfs */
  11786. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  11787. if( pVfs == 0 || pVfs->xProcessId == 0 ){
  11788. SXUNUSED(nArg); /* cc warning */
  11789. SXUNUSED(apArg);
  11790. /* IO routine not implemented, return -1 */
  11791. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  11792. "IO routine(%s) not implemented in the underlying VFS",
  11793. jx9_function_name(pCtx)
  11794. );
  11795. jx9_result_int(pCtx, -1);
  11796. return JX9_OK;
  11797. }
  11798. /* Perform the requested operation */
  11799. nProcessId = (jx9_int64)pVfs->xProcessId();
  11800. /* Set the result */
  11801. jx9_result_int64(pCtx, nProcessId);
  11802. return JX9_OK;
  11803. }
  11804. /*
  11805. * int getmyuid()
  11806. * Get user ID.
  11807. * Parameters
  11808. * None
  11809. * Return
  11810. * Returns the user ID.
  11811. */
  11812. static int jx9Vfs_getmyuid(jx9_context *pCtx, int nArg, jx9_value **apArg)
  11813. {
  11814. jx9_vfs *pVfs;
  11815. int nUid;
  11816. /* Point to the underlying vfs */
  11817. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  11818. if( pVfs == 0 || pVfs->xUid == 0 ){
  11819. SXUNUSED(nArg); /* cc warning */
  11820. SXUNUSED(apArg);
  11821. /* IO routine not implemented, return -1 */
  11822. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  11823. "IO routine(%s) not implemented in the underlying VFS",
  11824. jx9_function_name(pCtx)
  11825. );
  11826. jx9_result_int(pCtx, -1);
  11827. return JX9_OK;
  11828. }
  11829. /* Perform the requested operation */
  11830. nUid = pVfs->xUid();
  11831. /* Set the result */
  11832. jx9_result_int(pCtx, nUid);
  11833. return JX9_OK;
  11834. }
  11835. /*
  11836. * int getmygid()
  11837. * Get group ID.
  11838. * Parameters
  11839. * None
  11840. * Return
  11841. * Returns the group ID.
  11842. */
  11843. static int jx9Vfs_getmygid(jx9_context *pCtx, int nArg, jx9_value **apArg)
  11844. {
  11845. jx9_vfs *pVfs;
  11846. int nGid;
  11847. /* Point to the underlying vfs */
  11848. pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
  11849. if( pVfs == 0 || pVfs->xGid == 0 ){
  11850. SXUNUSED(nArg); /* cc warning */
  11851. SXUNUSED(apArg);
  11852. /* IO routine not implemented, return -1 */
  11853. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  11854. "IO routine(%s) not implemented in the underlying VFS",
  11855. jx9_function_name(pCtx)
  11856. );
  11857. jx9_result_int(pCtx, -1);
  11858. return JX9_OK;
  11859. }
  11860. /* Perform the requested operation */
  11861. nGid = pVfs->xGid();
  11862. /* Set the result */
  11863. jx9_result_int(pCtx, nGid);
  11864. return JX9_OK;
  11865. }
  11866. #ifdef __WINNT__
  11867. #include <Windows.h>
  11868. #elif defined(__UNIXES__)
  11869. #include <sys/utsname.h>
  11870. #endif
  11871. /*
  11872. * string uname([ string $mode = "a" ])
  11873. * Returns information about the host operating system.
  11874. * Parameters
  11875. * $mode
  11876. * mode is a single character that defines what information is returned:
  11877. * 'a': This is the default. Contains all modes in the sequence "s n r v m".
  11878. * 's': Operating system name. eg. FreeBSD.
  11879. * 'n': Host name. eg. localhost.example.com.
  11880. * 'r': Release name. eg. 5.1.2-RELEASE.
  11881. * 'v': Version information. Varies a lot between operating systems.
  11882. * 'm': Machine type. eg. i386.
  11883. * Return
  11884. * OS description as a string.
  11885. */
  11886. static int jx9Vfs_uname(jx9_context *pCtx, int nArg, jx9_value **apArg)
  11887. {
  11888. #if defined(__WINNT__)
  11889. const char *zName = "Microsoft Windows";
  11890. OSVERSIONINFOW sVer;
  11891. #elif defined(__UNIXES__)
  11892. struct utsname sName;
  11893. #endif
  11894. const char *zMode = "a";
  11895. if( nArg > 0 && jx9_value_is_string(apArg[0]) ){
  11896. /* Extract the desired mode */
  11897. zMode = jx9_value_to_string(apArg[0], 0);
  11898. }
  11899. #if defined(__WINNT__)
  11900. sVer.dwOSVersionInfoSize = sizeof(sVer);
  11901. if( TRUE != GetVersionExW(&sVer)){
  11902. jx9_result_string(pCtx, zName, -1);
  11903. return JX9_OK;
  11904. }
  11905. if( sVer.dwPlatformId == VER_PLATFORM_WIN32_NT ){
  11906. if( sVer.dwMajorVersion <= 4 ){
  11907. zName = "Microsoft Windows NT";
  11908. }else if( sVer.dwMajorVersion == 5 ){
  11909. switch(sVer.dwMinorVersion){
  11910. case 0: zName = "Microsoft Windows 2000"; break;
  11911. case 1: zName = "Microsoft Windows XP"; break;
  11912. case 2: zName = "Microsoft Windows Server 2003"; break;
  11913. }
  11914. }else if( sVer.dwMajorVersion == 6){
  11915. switch(sVer.dwMinorVersion){
  11916. case 0: zName = "Microsoft Windows Vista"; break;
  11917. case 1: zName = "Microsoft Windows 7"; break;
  11918. case 2: zName = "Microsoft Windows 8"; break;
  11919. default: break;
  11920. }
  11921. }
  11922. }
  11923. switch(zMode[0]){
  11924. case 's':
  11925. /* Operating system name */
  11926. jx9_result_string(pCtx, zName, -1/* Compute length automatically*/);
  11927. break;
  11928. case 'n':
  11929. /* Host name */
  11930. jx9_result_string(pCtx, "localhost", (int)sizeof("localhost")-1);
  11931. break;
  11932. case 'r':
  11933. case 'v':
  11934. /* Version information. */
  11935. jx9_result_string_format(pCtx, "%u.%u build %u",
  11936. sVer.dwMajorVersion, sVer.dwMinorVersion, sVer.dwBuildNumber
  11937. );
  11938. break;
  11939. case 'm':
  11940. /* Machine name */
  11941. jx9_result_string(pCtx, "x86", (int)sizeof("x86")-1);
  11942. break;
  11943. default:
  11944. jx9_result_string_format(pCtx, "%s localhost %u.%u build %u x86",
  11945. zName,
  11946. sVer.dwMajorVersion, sVer.dwMinorVersion, sVer.dwBuildNumber
  11947. );
  11948. break;
  11949. }
  11950. #elif defined(__UNIXES__)
  11951. if( uname(&sName) != 0 ){
  11952. jx9_result_string(pCtx, "Unix", (int)sizeof("Unix")-1);
  11953. return JX9_OK;
  11954. }
  11955. switch(zMode[0]){
  11956. case 's':
  11957. /* Operating system name */
  11958. jx9_result_string(pCtx, sName.sysname, -1/* Compute length automatically*/);
  11959. break;
  11960. case 'n':
  11961. /* Host name */
  11962. jx9_result_string(pCtx, sName.nodename, -1/* Compute length automatically*/);
  11963. break;
  11964. case 'r':
  11965. /* Release information */
  11966. jx9_result_string(pCtx, sName.release, -1/* Compute length automatically*/);
  11967. break;
  11968. case 'v':
  11969. /* Version information. */
  11970. jx9_result_string(pCtx, sName.version, -1/* Compute length automatically*/);
  11971. break;
  11972. case 'm':
  11973. /* Machine name */
  11974. jx9_result_string(pCtx, sName.machine, -1/* Compute length automatically*/);
  11975. break;
  11976. default:
  11977. jx9_result_string_format(pCtx,
  11978. "%s %s %s %s %s",
  11979. sName.sysname,
  11980. sName.release,
  11981. sName.version,
  11982. sName.nodename,
  11983. sName.machine
  11984. );
  11985. break;
  11986. }
  11987. #else
  11988. jx9_result_string(pCtx, "Host Operating System/localhost", (int)sizeof("Host Operating System/localhost")-1);
  11989. #endif
  11990. return JX9_OK;
  11991. }
  11992. /*
  11993. * Section:
  11994. * IO stream implementation.
  11995. * Authors:
  11996. * Symisc Systems, devel@symisc.net.
  11997. * Copyright (C) Symisc Systems, http://jx9.symisc.net
  11998. * Status:
  11999. * Stable.
  12000. */
  12001. typedef struct io_private io_private;
  12002. struct io_private
  12003. {
  12004. const jx9_io_stream *pStream; /* Underlying IO device */
  12005. void *pHandle; /* IO handle */
  12006. /* Unbuffered IO */
  12007. SyBlob sBuffer; /* Working buffer */
  12008. sxu32 nOfft; /* Current read offset */
  12009. sxu32 iMagic; /* Sanity check to avoid misuse */
  12010. };
  12011. #define IO_PRIVATE_MAGIC 0xFEAC14
  12012. /* Make sure we are dealing with a valid io_private instance */
  12013. #define IO_PRIVATE_INVALID(IO) ( IO == 0 || IO->iMagic != IO_PRIVATE_MAGIC )
  12014. /* Forward declaration */
  12015. static void ResetIOPrivate(io_private *pDev);
  12016. /*
  12017. * bool ftruncate(resource $handle, int64 $size)
  12018. * Truncates a file to a given length.
  12019. * Parameters
  12020. * $handle
  12021. * The file pointer.
  12022. * Note:
  12023. * The handle must be open for writing.
  12024. * $size
  12025. * The size to truncate to.
  12026. * Return
  12027. * TRUE on success or FALSE on failure.
  12028. */
  12029. static int jx9Builtin_ftruncate(jx9_context *pCtx, int nArg, jx9_value **apArg)
  12030. {
  12031. const jx9_io_stream *pStream;
  12032. io_private *pDev;
  12033. int rc;
  12034. if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
  12035. /* Missing/Invalid arguments, return FALSE */
  12036. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12037. jx9_result_bool(pCtx, 0);
  12038. return JX9_OK;
  12039. }
  12040. /* Extract our private data */
  12041. pDev = (io_private *)jx9_value_to_resource(apArg[0]);
  12042. /* Make sure we are dealing with a valid io_private instance */
  12043. if( IO_PRIVATE_INVALID(pDev) ){
  12044. /*Expecting an IO handle */
  12045. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12046. jx9_result_bool(pCtx, 0);
  12047. return JX9_OK;
  12048. }
  12049. /* Point to the target IO stream device */
  12050. pStream = pDev->pStream;
  12051. if( pStream == 0 || pStream->xTrunc == 0){
  12052. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  12053. "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
  12054. jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
  12055. );
  12056. jx9_result_bool(pCtx, 0);
  12057. return JX9_OK;
  12058. }
  12059. /* Perform the requested operation */
  12060. rc = pStream->xTrunc(pDev->pHandle, jx9_value_to_int64(apArg[1]));
  12061. if( rc == JX9_OK ){
  12062. /* Discard buffered data */
  12063. ResetIOPrivate(pDev);
  12064. }
  12065. /* IO result */
  12066. jx9_result_bool(pCtx, rc == JX9_OK);
  12067. return JX9_OK;
  12068. }
  12069. /*
  12070. * int fseek(resource $handle, int $offset[, int $whence = SEEK_SET ])
  12071. * Seeks on a file pointer.
  12072. * Parameters
  12073. * $handle
  12074. * A file system pointer resource that is typically created using fopen().
  12075. * $offset
  12076. * The offset.
  12077. * To move to a position before the end-of-file, you need to pass a negative
  12078. * value in offset and set whence to SEEK_END.
  12079. * whence
  12080. * whence values are:
  12081. * SEEK_SET - Set position equal to offset bytes.
  12082. * SEEK_CUR - Set position to current location plus offset.
  12083. * SEEK_END - Set position to end-of-file plus offset.
  12084. * Return
  12085. * 0 on success, -1 on failure
  12086. */
  12087. static int jx9Builtin_fseek(jx9_context *pCtx, int nArg, jx9_value **apArg)
  12088. {
  12089. const jx9_io_stream *pStream;
  12090. io_private *pDev;
  12091. jx9_int64 iOfft;
  12092. int whence;
  12093. int rc;
  12094. if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
  12095. /* Missing/Invalid arguments, return FALSE */
  12096. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12097. jx9_result_int(pCtx, -1);
  12098. return JX9_OK;
  12099. }
  12100. /* Extract our private data */
  12101. pDev = (io_private *)jx9_value_to_resource(apArg[0]);
  12102. /* Make sure we are dealing with a valid io_private instance */
  12103. if( IO_PRIVATE_INVALID(pDev) ){
  12104. /*Expecting an IO handle */
  12105. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12106. jx9_result_int(pCtx, -1);
  12107. return JX9_OK;
  12108. }
  12109. /* Point to the target IO stream device */
  12110. pStream = pDev->pStream;
  12111. if( pStream == 0 || pStream->xSeek == 0){
  12112. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  12113. "IO routine(%s) not implemented in the underlying stream(%s) device",
  12114. jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
  12115. );
  12116. jx9_result_int(pCtx, -1);
  12117. return JX9_OK;
  12118. }
  12119. /* Extract the offset */
  12120. iOfft = jx9_value_to_int64(apArg[1]);
  12121. whence = 0;/* SEEK_SET */
  12122. if( nArg > 2 && jx9_value_is_int(apArg[2]) ){
  12123. whence = jx9_value_to_int(apArg[2]);
  12124. }
  12125. /* Perform the requested operation */
  12126. rc = pStream->xSeek(pDev->pHandle, iOfft, whence);
  12127. if( rc == JX9_OK ){
  12128. /* Ignore buffered data */
  12129. ResetIOPrivate(pDev);
  12130. }
  12131. /* IO result */
  12132. jx9_result_int(pCtx, rc == JX9_OK ? 0 : - 1);
  12133. return JX9_OK;
  12134. }
  12135. /*
  12136. * int64 ftell(resource $handle)
  12137. * Returns the current position of the file read/write pointer.
  12138. * Parameters
  12139. * $handle
  12140. * The file pointer.
  12141. * Return
  12142. * Returns the position of the file pointer referenced by handle
  12143. * as an integer; i.e., its offset into the file stream.
  12144. * FALSE is returned on failure.
  12145. */
  12146. static int jx9Builtin_ftell(jx9_context *pCtx, int nArg, jx9_value **apArg)
  12147. {
  12148. const jx9_io_stream *pStream;
  12149. io_private *pDev;
  12150. jx9_int64 iOfft;
  12151. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  12152. /* Missing/Invalid arguments, return FALSE */
  12153. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12154. jx9_result_bool(pCtx, 0);
  12155. return JX9_OK;
  12156. }
  12157. /* Extract our private data */
  12158. pDev = (io_private *)jx9_value_to_resource(apArg[0]);
  12159. /* Make sure we are dealing with a valid io_private instance */
  12160. if( IO_PRIVATE_INVALID(pDev) ){
  12161. /*Expecting an IO handle */
  12162. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12163. jx9_result_bool(pCtx, 0);
  12164. return JX9_OK;
  12165. }
  12166. /* Point to the target IO stream device */
  12167. pStream = pDev->pStream;
  12168. if( pStream == 0 || pStream->xTell == 0){
  12169. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  12170. "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
  12171. jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
  12172. );
  12173. jx9_result_bool(pCtx, 0);
  12174. return JX9_OK;
  12175. }
  12176. /* Perform the requested operation */
  12177. iOfft = pStream->xTell(pDev->pHandle);
  12178. /* IO result */
  12179. jx9_result_int64(pCtx, iOfft);
  12180. return JX9_OK;
  12181. }
  12182. /*
  12183. * bool rewind(resource $handle)
  12184. * Rewind the position of a file pointer.
  12185. * Parameters
  12186. * $handle
  12187. * The file pointer.
  12188. * Return
  12189. * TRUE on success or FALSE on failure.
  12190. */
  12191. static int jx9Builtin_rewind(jx9_context *pCtx, int nArg, jx9_value **apArg)
  12192. {
  12193. const jx9_io_stream *pStream;
  12194. io_private *pDev;
  12195. int rc;
  12196. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  12197. /* Missing/Invalid arguments, return FALSE */
  12198. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12199. jx9_result_bool(pCtx, 0);
  12200. return JX9_OK;
  12201. }
  12202. /* Extract our private data */
  12203. pDev = (io_private *)jx9_value_to_resource(apArg[0]);
  12204. /* Make sure we are dealing with a valid io_private instance */
  12205. if( IO_PRIVATE_INVALID(pDev) ){
  12206. /*Expecting an IO handle */
  12207. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12208. jx9_result_bool(pCtx, 0);
  12209. return JX9_OK;
  12210. }
  12211. /* Point to the target IO stream device */
  12212. pStream = pDev->pStream;
  12213. if( pStream == 0 || pStream->xSeek == 0){
  12214. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  12215. "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
  12216. jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
  12217. );
  12218. jx9_result_bool(pCtx, 0);
  12219. return JX9_OK;
  12220. }
  12221. /* Perform the requested operation */
  12222. rc = pStream->xSeek(pDev->pHandle, 0, 0/*SEEK_SET*/);
  12223. if( rc == JX9_OK ){
  12224. /* Ignore buffered data */
  12225. ResetIOPrivate(pDev);
  12226. }
  12227. /* IO result */
  12228. jx9_result_bool(pCtx, rc == JX9_OK);
  12229. return JX9_OK;
  12230. }
  12231. /*
  12232. * bool fflush(resource $handle)
  12233. * Flushes the output to a file.
  12234. * Parameters
  12235. * $handle
  12236. * The file pointer.
  12237. * Return
  12238. * TRUE on success or FALSE on failure.
  12239. */
  12240. static int jx9Builtin_fflush(jx9_context *pCtx, int nArg, jx9_value **apArg)
  12241. {
  12242. const jx9_io_stream *pStream;
  12243. io_private *pDev;
  12244. int rc;
  12245. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  12246. /* Missing/Invalid arguments, return FALSE */
  12247. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12248. jx9_result_bool(pCtx, 0);
  12249. return JX9_OK;
  12250. }
  12251. /* Extract our private data */
  12252. pDev = (io_private *)jx9_value_to_resource(apArg[0]);
  12253. /* Make sure we are dealing with a valid io_private instance */
  12254. if( IO_PRIVATE_INVALID(pDev) ){
  12255. /*Expecting an IO handle */
  12256. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12257. jx9_result_bool(pCtx, 0);
  12258. return JX9_OK;
  12259. }
  12260. /* Point to the target IO stream device */
  12261. pStream = pDev->pStream;
  12262. if( pStream == 0 || pStream->xSync == 0){
  12263. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  12264. "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
  12265. jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
  12266. );
  12267. jx9_result_bool(pCtx, 0);
  12268. return JX9_OK;
  12269. }
  12270. /* Perform the requested operation */
  12271. rc = pStream->xSync(pDev->pHandle);
  12272. /* IO result */
  12273. jx9_result_bool(pCtx, rc == JX9_OK);
  12274. return JX9_OK;
  12275. }
  12276. /*
  12277. * bool feof(resource $handle)
  12278. * Tests for end-of-file on a file pointer.
  12279. * Parameters
  12280. * $handle
  12281. * The file pointer.
  12282. * Return
  12283. * Returns TRUE if the file pointer is at EOF.FALSE otherwise
  12284. */
  12285. static int jx9Builtin_feof(jx9_context *pCtx, int nArg, jx9_value **apArg)
  12286. {
  12287. const jx9_io_stream *pStream;
  12288. io_private *pDev;
  12289. int rc;
  12290. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  12291. /* Missing/Invalid arguments */
  12292. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12293. jx9_result_bool(pCtx, 1);
  12294. return JX9_OK;
  12295. }
  12296. /* Extract our private data */
  12297. pDev = (io_private *)jx9_value_to_resource(apArg[0]);
  12298. /* Make sure we are dealing with a valid io_private instance */
  12299. if( IO_PRIVATE_INVALID(pDev) ){
  12300. /*Expecting an IO handle */
  12301. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12302. jx9_result_bool(pCtx, 1);
  12303. return JX9_OK;
  12304. }
  12305. /* Point to the target IO stream device */
  12306. pStream = pDev->pStream;
  12307. if( pStream == 0 ){
  12308. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  12309. "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
  12310. jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
  12311. );
  12312. jx9_result_bool(pCtx, 1);
  12313. return JX9_OK;
  12314. }
  12315. rc = SXERR_EOF;
  12316. /* Perform the requested operation */
  12317. if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){
  12318. /* Data is available */
  12319. rc = JX9_OK;
  12320. }else{
  12321. char zBuf[4096];
  12322. jx9_int64 n;
  12323. /* Perform a buffered read */
  12324. n = pStream->xRead(pDev->pHandle, zBuf, sizeof(zBuf));
  12325. if( n > 0 ){
  12326. /* Copy buffered data */
  12327. SyBlobAppend(&pDev->sBuffer, zBuf, (sxu32)n);
  12328. rc = JX9_OK;
  12329. }
  12330. }
  12331. /* EOF or not */
  12332. jx9_result_bool(pCtx, rc == SXERR_EOF);
  12333. return JX9_OK;
  12334. }
  12335. /*
  12336. * Read n bytes from the underlying IO stream device.
  12337. * Return total numbers of bytes readen on success. A number < 1 on failure
  12338. * [i.e: IO error ] or EOF.
  12339. */
  12340. static jx9_int64 StreamRead(io_private *pDev, void *pBuf, jx9_int64 nLen)
  12341. {
  12342. const jx9_io_stream *pStream = pDev->pStream;
  12343. char *zBuf = (char *)pBuf;
  12344. jx9_int64 n, nRead;
  12345. n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft;
  12346. if( n > 0 ){
  12347. if( n > nLen ){
  12348. n = nLen;
  12349. }
  12350. /* Copy the buffered data */
  12351. SyMemcpy(SyBlobDataAt(&pDev->sBuffer, pDev->nOfft), pBuf, (sxu32)n);
  12352. /* Update the read offset */
  12353. pDev->nOfft += (sxu32)n;
  12354. if( pDev->nOfft >= SyBlobLength(&pDev->sBuffer) ){
  12355. /* Reset the working buffer so that we avoid excessive memory allocation */
  12356. SyBlobReset(&pDev->sBuffer);
  12357. pDev->nOfft = 0;
  12358. }
  12359. nLen -= n;
  12360. if( nLen < 1 ){
  12361. /* All done */
  12362. return n;
  12363. }
  12364. /* Advance the cursor */
  12365. zBuf += n;
  12366. }
  12367. /* Read without buffering */
  12368. nRead = pStream->xRead(pDev->pHandle, zBuf, nLen);
  12369. if( nRead > 0 ){
  12370. n += nRead;
  12371. }else if( n < 1 ){
  12372. /* EOF or IO error */
  12373. return nRead;
  12374. }
  12375. return n;
  12376. }
  12377. /*
  12378. * Extract a single line from the buffered input.
  12379. */
  12380. static sxi32 GetLine(io_private *pDev, jx9_int64 *pLen, const char **pzLine)
  12381. {
  12382. const char *zIn, *zEnd, *zPtr;
  12383. zIn = (const char *)SyBlobDataAt(&pDev->sBuffer, pDev->nOfft);
  12384. zEnd = &zIn[SyBlobLength(&pDev->sBuffer)-pDev->nOfft];
  12385. zPtr = zIn;
  12386. while( zIn < zEnd ){
  12387. if( zIn[0] == '\n' ){
  12388. /* Line found */
  12389. zIn++; /* Include the line ending as requested by the JX9 specification */
  12390. *pLen = (jx9_int64)(zIn-zPtr);
  12391. *pzLine = zPtr;
  12392. return SXRET_OK;
  12393. }
  12394. zIn++;
  12395. }
  12396. /* No line were found */
  12397. return SXERR_NOTFOUND;
  12398. }
  12399. /*
  12400. * Read a single line from the underlying IO stream device.
  12401. */
  12402. static jx9_int64 StreamReadLine(io_private *pDev, const char **pzData, jx9_int64 nMaxLen)
  12403. {
  12404. const jx9_io_stream *pStream = pDev->pStream;
  12405. char zBuf[8192];
  12406. jx9_int64 n;
  12407. sxi32 rc;
  12408. n = 0;
  12409. if( pDev->nOfft >= SyBlobLength(&pDev->sBuffer) ){
  12410. /* Reset the working buffer so that we avoid excessive memory allocation */
  12411. SyBlobReset(&pDev->sBuffer);
  12412. pDev->nOfft = 0;
  12413. }
  12414. if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){
  12415. /* Check if there is a line */
  12416. rc = GetLine(pDev, &n, pzData);
  12417. if( rc == SXRET_OK ){
  12418. /* Got line, update the cursor */
  12419. pDev->nOfft += (sxu32)n;
  12420. return n;
  12421. }
  12422. }
  12423. /* Perform the read operation until a new line is extracted or length
  12424. * limit is reached.
  12425. */
  12426. for(;;){
  12427. n = pStream->xRead(pDev->pHandle, zBuf, (nMaxLen > 0 && nMaxLen < sizeof(zBuf)) ? nMaxLen : sizeof(zBuf));
  12428. if( n < 1 ){
  12429. /* EOF or IO error */
  12430. break;
  12431. }
  12432. /* Append the data just read */
  12433. SyBlobAppend(&pDev->sBuffer, zBuf, (sxu32)n);
  12434. /* Try to extract a line */
  12435. rc = GetLine(pDev, &n, pzData);
  12436. if( rc == SXRET_OK ){
  12437. /* Got one, return immediately */
  12438. pDev->nOfft += (sxu32)n;
  12439. return n;
  12440. }
  12441. if( nMaxLen > 0 && (SyBlobLength(&pDev->sBuffer) - pDev->nOfft >= nMaxLen) ){
  12442. /* Read limit reached, return the available data */
  12443. *pzData = (const char *)SyBlobDataAt(&pDev->sBuffer, pDev->nOfft);
  12444. n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft;
  12445. /* Reset the working buffer */
  12446. SyBlobReset(&pDev->sBuffer);
  12447. pDev->nOfft = 0;
  12448. return n;
  12449. }
  12450. }
  12451. if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){
  12452. /* Read limit reached, return the available data */
  12453. *pzData = (const char *)SyBlobDataAt(&pDev->sBuffer, pDev->nOfft);
  12454. n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft;
  12455. /* Reset the working buffer */
  12456. SyBlobReset(&pDev->sBuffer);
  12457. pDev->nOfft = 0;
  12458. }
  12459. return n;
  12460. }
  12461. /*
  12462. * Open an IO stream handle.
  12463. * Notes on stream:
  12464. * According to the JX9 reference manual.
  12465. * In its simplest definition, a stream is a resource object which exhibits streamable behavior.
  12466. * That is, it can be read from or written to in a linear fashion, and may be able to fseek()
  12467. * to an arbitrary locations within the stream.
  12468. * A wrapper is additional code which tells the stream how to handle specific protocols/encodings.
  12469. * For example, the http wrapper knows how to translate a URL into an HTTP/1.0 request for a file
  12470. * on a remote server.
  12471. * A stream is referenced as: scheme://target
  12472. * scheme(string) - The name of the wrapper to be used. Examples include: file, http...
  12473. * If no wrapper is specified, the function default is used (typically file://).
  12474. * target - Depends on the wrapper used. For filesystem related streams this is typically a path
  12475. * and filename of the desired file. For network related streams this is typically a hostname, often
  12476. * with a path appended.
  12477. *
  12478. * Note that JX9 IO streams looks like JX9 streams but their implementation differ greately.
  12479. * Please refer to the official documentation for a full discussion.
  12480. * This function return a handle on success. Otherwise null.
  12481. */
  12482. JX9_PRIVATE void * jx9StreamOpenHandle(jx9_vm *pVm, const jx9_io_stream *pStream, const char *zFile,
  12483. int iFlags, int use_include, jx9_value *pResource, int bPushInclude, int *pNew)
  12484. {
  12485. void *pHandle = 0; /* cc warning */
  12486. SyString sFile;
  12487. int rc;
  12488. if( pStream == 0 ){
  12489. /* No such stream device */
  12490. return 0;
  12491. }
  12492. SyStringInitFromBuf(&sFile, zFile, SyStrlen(zFile));
  12493. if( use_include ){
  12494. if( sFile.zString[0] == '/' ||
  12495. #ifdef __WINNT__
  12496. (sFile.nByte > 2 && sFile.zString[1] == ':' && (sFile.zString[2] == '\\' || sFile.zString[2] == '/') ) ||
  12497. #endif
  12498. (sFile.nByte > 1 && sFile.zString[0] == '.' && sFile.zString[1] == '/') ||
  12499. (sFile.nByte > 2 && sFile.zString[0] == '.' && sFile.zString[1] == '.' && sFile.zString[2] == '/') ){
  12500. /* Open the file directly */
  12501. rc = pStream->xOpen(zFile, iFlags, pResource, &pHandle);
  12502. }else{
  12503. SyString *pPath;
  12504. SyBlob sWorker;
  12505. #ifdef __WINNT__
  12506. static const int c = '\\';
  12507. #else
  12508. static const int c = '/';
  12509. #endif
  12510. /* Init the path builder working buffer */
  12511. SyBlobInit(&sWorker, &pVm->sAllocator);
  12512. /* Build a path from the set of include path */
  12513. SySetResetCursor(&pVm->aPaths);
  12514. rc = SXERR_IO;
  12515. while( SXRET_OK == SySetGetNextEntry(&pVm->aPaths, (void **)&pPath) ){
  12516. /* Build full path */
  12517. SyBlobFormat(&sWorker, "%z%c%z", pPath, c, &sFile);
  12518. /* Append null terminator */
  12519. if( SXRET_OK != SyBlobNullAppend(&sWorker) ){
  12520. continue;
  12521. }
  12522. /* Try to open the file */
  12523. rc = pStream->xOpen((const char *)SyBlobData(&sWorker), iFlags, pResource, &pHandle);
  12524. if( rc == JX9_OK ){
  12525. if( bPushInclude ){
  12526. /* Mark as included */
  12527. jx9VmPushFilePath(pVm, (const char *)SyBlobData(&sWorker), SyBlobLength(&sWorker), FALSE, pNew);
  12528. }
  12529. break;
  12530. }
  12531. /* Reset the working buffer */
  12532. SyBlobReset(&sWorker);
  12533. /* Check the next path */
  12534. }
  12535. SyBlobRelease(&sWorker);
  12536. }
  12537. if( rc == JX9_OK ){
  12538. if( bPushInclude ){
  12539. /* Mark as included */
  12540. jx9VmPushFilePath(pVm, sFile.zString, sFile.nByte, FALSE, pNew);
  12541. }
  12542. }
  12543. }else{
  12544. /* Open the URI direcly */
  12545. rc = pStream->xOpen(zFile, iFlags, pResource, &pHandle);
  12546. }
  12547. if( rc != JX9_OK ){
  12548. /* IO error */
  12549. return 0;
  12550. }
  12551. /* Return the file handle */
  12552. return pHandle;
  12553. }
  12554. /*
  12555. * Read the whole contents of an open IO stream handle [i.e local file/URL..]
  12556. * Store the read data in the given BLOB (last argument).
  12557. * The read operation is stopped when he hit the EOF or an IO error occurs.
  12558. */
  12559. JX9_PRIVATE sxi32 jx9StreamReadWholeFile(void *pHandle, const jx9_io_stream *pStream, SyBlob *pOut)
  12560. {
  12561. jx9_int64 nRead;
  12562. char zBuf[8192]; /* 8K */
  12563. int rc;
  12564. /* Perform the requested operation */
  12565. for(;;){
  12566. nRead = pStream->xRead(pHandle, zBuf, sizeof(zBuf));
  12567. if( nRead < 1 ){
  12568. /* EOF or IO error */
  12569. break;
  12570. }
  12571. /* Append contents */
  12572. rc = SyBlobAppend(pOut, zBuf, (sxu32)nRead);
  12573. if( rc != SXRET_OK ){
  12574. break;
  12575. }
  12576. }
  12577. return SyBlobLength(pOut) > 0 ? SXRET_OK : -1;
  12578. }
  12579. /*
  12580. * Close an open IO stream handle [i.e local file/URI..].
  12581. */
  12582. JX9_PRIVATE void jx9StreamCloseHandle(const jx9_io_stream *pStream, void *pHandle)
  12583. {
  12584. if( pStream->xClose ){
  12585. pStream->xClose(pHandle);
  12586. }
  12587. }
  12588. /*
  12589. * string fgetc(resource $handle)
  12590. * Gets a character from the given file pointer.
  12591. * Parameters
  12592. * $handle
  12593. * The file pointer.
  12594. * Return
  12595. * Returns a string containing a single character read from the file
  12596. * pointed to by handle. Returns FALSE on EOF.
  12597. * WARNING
  12598. * This operation is extremely slow.Avoid using it.
  12599. */
  12600. static int jx9Builtin_fgetc(jx9_context *pCtx, int nArg, jx9_value **apArg)
  12601. {
  12602. const jx9_io_stream *pStream;
  12603. io_private *pDev;
  12604. int c, n;
  12605. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  12606. /* Missing/Invalid arguments, return FALSE */
  12607. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12608. jx9_result_bool(pCtx, 0);
  12609. return JX9_OK;
  12610. }
  12611. /* Extract our private data */
  12612. pDev = (io_private *)jx9_value_to_resource(apArg[0]);
  12613. /* Make sure we are dealing with a valid io_private instance */
  12614. if( IO_PRIVATE_INVALID(pDev) ){
  12615. /*Expecting an IO handle */
  12616. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12617. jx9_result_bool(pCtx, 0);
  12618. return JX9_OK;
  12619. }
  12620. /* Point to the target IO stream device */
  12621. pStream = pDev->pStream;
  12622. if( pStream == 0 ){
  12623. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  12624. "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
  12625. jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
  12626. );
  12627. jx9_result_bool(pCtx, 0);
  12628. return JX9_OK;
  12629. }
  12630. /* Perform the requested operation */
  12631. n = (int)StreamRead(pDev, (void *)&c, sizeof(char));
  12632. /* IO result */
  12633. if( n < 1 ){
  12634. /* EOF or error, return FALSE */
  12635. jx9_result_bool(pCtx, 0);
  12636. }else{
  12637. /* Return the string holding the character */
  12638. jx9_result_string(pCtx, (const char *)&c, sizeof(char));
  12639. }
  12640. return JX9_OK;
  12641. }
  12642. /*
  12643. * string fgets(resource $handle[, int64 $length ])
  12644. * Gets line from file pointer.
  12645. * Parameters
  12646. * $handle
  12647. * The file pointer.
  12648. * $length
  12649. * Reading ends when length - 1 bytes have been read, on a newline
  12650. * (which is included in the return value), or on EOF (whichever comes first).
  12651. * If no length is specified, it will keep reading from the stream until it reaches
  12652. * the end of the line.
  12653. * Return
  12654. * Returns a string of up to length - 1 bytes read from the file pointed to by handle.
  12655. * If there is no more data to read in the file pointer, then FALSE is returned.
  12656. * If an error occurs, FALSE is returned.
  12657. */
  12658. static int jx9Builtin_fgets(jx9_context *pCtx, int nArg, jx9_value **apArg)
  12659. {
  12660. const jx9_io_stream *pStream;
  12661. const char *zLine;
  12662. io_private *pDev;
  12663. jx9_int64 n, nLen;
  12664. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  12665. /* Missing/Invalid arguments, return FALSE */
  12666. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12667. jx9_result_bool(pCtx, 0);
  12668. return JX9_OK;
  12669. }
  12670. /* Extract our private data */
  12671. pDev = (io_private *)jx9_value_to_resource(apArg[0]);
  12672. /* Make sure we are dealing with a valid io_private instance */
  12673. if( IO_PRIVATE_INVALID(pDev) ){
  12674. /*Expecting an IO handle */
  12675. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12676. jx9_result_bool(pCtx, 0);
  12677. return JX9_OK;
  12678. }
  12679. /* Point to the target IO stream device */
  12680. pStream = pDev->pStream;
  12681. if( pStream == 0 ){
  12682. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  12683. "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
  12684. jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
  12685. );
  12686. jx9_result_bool(pCtx, 0);
  12687. return JX9_OK;
  12688. }
  12689. nLen = -1;
  12690. if( nArg > 1 ){
  12691. /* Maximum data to read */
  12692. nLen = jx9_value_to_int64(apArg[1]);
  12693. }
  12694. /* Perform the requested operation */
  12695. n = StreamReadLine(pDev, &zLine, nLen);
  12696. if( n < 1 ){
  12697. /* EOF or IO error, return FALSE */
  12698. jx9_result_bool(pCtx, 0);
  12699. }else{
  12700. /* Return the freshly extracted line */
  12701. jx9_result_string(pCtx, zLine, (int)n);
  12702. }
  12703. return JX9_OK;
  12704. }
  12705. /*
  12706. * string fread(resource $handle, int64 $length)
  12707. * Binary-safe file read.
  12708. * Parameters
  12709. * $handle
  12710. * The file pointer.
  12711. * $length
  12712. * Up to length number of bytes read.
  12713. * Return
  12714. * The data readen on success or FALSE on failure.
  12715. */
  12716. static int jx9Builtin_fread(jx9_context *pCtx, int nArg, jx9_value **apArg)
  12717. {
  12718. const jx9_io_stream *pStream;
  12719. io_private *pDev;
  12720. jx9_int64 nRead;
  12721. void *pBuf;
  12722. int nLen;
  12723. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  12724. /* Missing/Invalid arguments, return FALSE */
  12725. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12726. jx9_result_bool(pCtx, 0);
  12727. return JX9_OK;
  12728. }
  12729. /* Extract our private data */
  12730. pDev = (io_private *)jx9_value_to_resource(apArg[0]);
  12731. /* Make sure we are dealing with a valid io_private instance */
  12732. if( IO_PRIVATE_INVALID(pDev) ){
  12733. /*Expecting an IO handle */
  12734. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12735. jx9_result_bool(pCtx, 0);
  12736. return JX9_OK;
  12737. }
  12738. /* Point to the target IO stream device */
  12739. pStream = pDev->pStream;
  12740. if( pStream == 0 ){
  12741. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  12742. "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
  12743. jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
  12744. );
  12745. jx9_result_bool(pCtx, 0);
  12746. return JX9_OK;
  12747. }
  12748. nLen = 4096;
  12749. if( nArg > 1 ){
  12750. nLen = jx9_value_to_int(apArg[1]);
  12751. if( nLen < 1 ){
  12752. /* Invalid length, set a default length */
  12753. nLen = 4096;
  12754. }
  12755. }
  12756. /* Allocate enough buffer */
  12757. pBuf = jx9_context_alloc_chunk(pCtx, (unsigned int)nLen, FALSE, FALSE);
  12758. if( pBuf == 0 ){
  12759. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
  12760. jx9_result_bool(pCtx, 0);
  12761. return JX9_OK;
  12762. }
  12763. /* Perform the requested operation */
  12764. nRead = StreamRead(pDev, pBuf, (jx9_int64)nLen);
  12765. if( nRead < 1 ){
  12766. /* Nothing read, return FALSE */
  12767. jx9_result_bool(pCtx, 0);
  12768. }else{
  12769. /* Make a copy of the data just read */
  12770. jx9_result_string(pCtx, (const char *)pBuf, (int)nRead);
  12771. }
  12772. /* Release the buffer */
  12773. jx9_context_free_chunk(pCtx, pBuf);
  12774. return JX9_OK;
  12775. }
  12776. /*
  12777. * array fgetcsv(resource $handle [, int $length = 0
  12778. * [, string $delimiter = ', '[, string $enclosure = '"'[, string $escape='\\']]]])
  12779. * Gets line from file pointer and parse for CSV fields.
  12780. * Parameters
  12781. * $handle
  12782. * The file pointer.
  12783. * $length
  12784. * Reading ends when length - 1 bytes have been read, on a newline
  12785. * (which is included in the return value), or on EOF (whichever comes first).
  12786. * If no length is specified, it will keep reading from the stream until it reaches
  12787. * the end of the line.
  12788. * $delimiter
  12789. * Set the field delimiter (one character only).
  12790. * $enclosure
  12791. * Set the field enclosure character (one character only).
  12792. * $escape
  12793. * Set the escape character (one character only). Defaults as a backslash (\)
  12794. * Return
  12795. * Returns a string of up to length - 1 bytes read from the file pointed to by handle.
  12796. * If there is no more data to read in the file pointer, then FALSE is returned.
  12797. * If an error occurs, FALSE is returned.
  12798. */
  12799. static int jx9Builtin_fgetcsv(jx9_context *pCtx, int nArg, jx9_value **apArg)
  12800. {
  12801. const jx9_io_stream *pStream;
  12802. const char *zLine;
  12803. io_private *pDev;
  12804. jx9_int64 n, nLen;
  12805. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  12806. /* Missing/Invalid arguments, return FALSE */
  12807. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12808. jx9_result_bool(pCtx, 0);
  12809. return JX9_OK;
  12810. }
  12811. /* Extract our private data */
  12812. pDev = (io_private *)jx9_value_to_resource(apArg[0]);
  12813. /* Make sure we are dealing with a valid io_private instance */
  12814. if( IO_PRIVATE_INVALID(pDev) ){
  12815. /*Expecting an IO handle */
  12816. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12817. jx9_result_bool(pCtx, 0);
  12818. return JX9_OK;
  12819. }
  12820. /* Point to the target IO stream device */
  12821. pStream = pDev->pStream;
  12822. if( pStream == 0 ){
  12823. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  12824. "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
  12825. jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
  12826. );
  12827. jx9_result_bool(pCtx, 0);
  12828. return JX9_OK;
  12829. }
  12830. nLen = -1;
  12831. if( nArg > 1 ){
  12832. /* Maximum data to read */
  12833. nLen = jx9_value_to_int64(apArg[1]);
  12834. }
  12835. /* Perform the requested operation */
  12836. n = StreamReadLine(pDev, &zLine, nLen);
  12837. if( n < 1 ){
  12838. /* EOF or IO error, return FALSE */
  12839. jx9_result_bool(pCtx, 0);
  12840. }else{
  12841. jx9_value *pArray;
  12842. int delim = ','; /* Delimiter */
  12843. int encl = '"' ; /* Enclosure */
  12844. int escape = '\\'; /* Escape character */
  12845. if( nArg > 2 ){
  12846. const char *zPtr;
  12847. int i;
  12848. if( jx9_value_is_string(apArg[2]) ){
  12849. /* Extract the delimiter */
  12850. zPtr = jx9_value_to_string(apArg[2], &i);
  12851. if( i > 0 ){
  12852. delim = zPtr[0];
  12853. }
  12854. }
  12855. if( nArg > 3 ){
  12856. if( jx9_value_is_string(apArg[3]) ){
  12857. /* Extract the enclosure */
  12858. zPtr = jx9_value_to_string(apArg[3], &i);
  12859. if( i > 0 ){
  12860. encl = zPtr[0];
  12861. }
  12862. }
  12863. if( nArg > 4 ){
  12864. if( jx9_value_is_string(apArg[4]) ){
  12865. /* Extract the escape character */
  12866. zPtr = jx9_value_to_string(apArg[4], &i);
  12867. if( i > 0 ){
  12868. escape = zPtr[0];
  12869. }
  12870. }
  12871. }
  12872. }
  12873. }
  12874. /* Create our array */
  12875. pArray = jx9_context_new_array(pCtx);
  12876. if( pArray == 0 ){
  12877. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
  12878. jx9_result_null(pCtx);
  12879. return JX9_OK;
  12880. }
  12881. /* Parse the raw input */
  12882. jx9ProcessCsv(zLine, (int)n, delim, encl, escape, jx9CsvConsumer, pArray);
  12883. /* Return the freshly created array */
  12884. jx9_result_value(pCtx, pArray);
  12885. }
  12886. return JX9_OK;
  12887. }
  12888. /*
  12889. * string fgetss(resource $handle [, int $length [, string $allowable_tags ]])
  12890. * Gets line from file pointer and strip HTML tags.
  12891. * Parameters
  12892. * $handle
  12893. * The file pointer.
  12894. * $length
  12895. * Reading ends when length - 1 bytes have been read, on a newline
  12896. * (which is included in the return value), or on EOF (whichever comes first).
  12897. * If no length is specified, it will keep reading from the stream until it reaches
  12898. * the end of the line.
  12899. * $allowable_tags
  12900. * You can use the optional second parameter to specify tags which should not be stripped.
  12901. * Return
  12902. * Returns a string of up to length - 1 bytes read from the file pointed to by
  12903. * handle, with all HTML and JX9 code stripped. If an error occurs, returns FALSE.
  12904. */
  12905. static int jx9Builtin_fgetss(jx9_context *pCtx, int nArg, jx9_value **apArg)
  12906. {
  12907. const jx9_io_stream *pStream;
  12908. const char *zLine;
  12909. io_private *pDev;
  12910. jx9_int64 n, nLen;
  12911. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  12912. /* Missing/Invalid arguments, return FALSE */
  12913. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12914. jx9_result_bool(pCtx, 0);
  12915. return JX9_OK;
  12916. }
  12917. /* Extract our private data */
  12918. pDev = (io_private *)jx9_value_to_resource(apArg[0]);
  12919. /* Make sure we are dealing with a valid io_private instance */
  12920. if( IO_PRIVATE_INVALID(pDev) ){
  12921. /*Expecting an IO handle */
  12922. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12923. jx9_result_bool(pCtx, 0);
  12924. return JX9_OK;
  12925. }
  12926. /* Point to the target IO stream device */
  12927. pStream = pDev->pStream;
  12928. if( pStream == 0 ){
  12929. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  12930. "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
  12931. jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
  12932. );
  12933. jx9_result_bool(pCtx, 0);
  12934. return JX9_OK;
  12935. }
  12936. nLen = -1;
  12937. if( nArg > 1 ){
  12938. /* Maximum data to read */
  12939. nLen = jx9_value_to_int64(apArg[1]);
  12940. }
  12941. /* Perform the requested operation */
  12942. n = StreamReadLine(pDev, &zLine, nLen);
  12943. if( n < 1 ){
  12944. /* EOF or IO error, return FALSE */
  12945. jx9_result_bool(pCtx, 0);
  12946. }else{
  12947. const char *zTaglist = 0;
  12948. int nTaglen = 0;
  12949. if( nArg > 2 && jx9_value_is_string(apArg[2]) ){
  12950. /* Allowed tag */
  12951. zTaglist = jx9_value_to_string(apArg[2], &nTaglen);
  12952. }
  12953. /* Process data just read */
  12954. jx9StripTagsFromString(pCtx, zLine, (int)n, zTaglist, nTaglen);
  12955. }
  12956. return JX9_OK;
  12957. }
  12958. /*
  12959. * string readdir(resource $dir_handle)
  12960. * Read entry from directory handle.
  12961. * Parameter
  12962. * $dir_handle
  12963. * The directory handle resource previously opened with opendir().
  12964. * Return
  12965. * Returns the filename on success or FALSE on failure.
  12966. */
  12967. static int jx9Builtin_readdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
  12968. {
  12969. const jx9_io_stream *pStream;
  12970. io_private *pDev;
  12971. int rc;
  12972. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  12973. /* Missing/Invalid arguments, return FALSE */
  12974. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12975. jx9_result_bool(pCtx, 0);
  12976. return JX9_OK;
  12977. }
  12978. /* Extract our private data */
  12979. pDev = (io_private *)jx9_value_to_resource(apArg[0]);
  12980. /* Make sure we are dealing with a valid io_private instance */
  12981. if( IO_PRIVATE_INVALID(pDev) ){
  12982. /*Expecting an IO handle */
  12983. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  12984. jx9_result_bool(pCtx, 0);
  12985. return JX9_OK;
  12986. }
  12987. /* Point to the target IO stream device */
  12988. pStream = pDev->pStream;
  12989. if( pStream == 0 || pStream->xReadDir == 0 ){
  12990. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  12991. "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
  12992. jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
  12993. );
  12994. jx9_result_bool(pCtx, 0);
  12995. return JX9_OK;
  12996. }
  12997. jx9_result_bool(pCtx, 0);
  12998. /* Perform the requested operation */
  12999. rc = pStream->xReadDir(pDev->pHandle, pCtx);
  13000. if( rc != JX9_OK ){
  13001. /* Return FALSE */
  13002. jx9_result_bool(pCtx, 0);
  13003. }
  13004. return JX9_OK;
  13005. }
  13006. /*
  13007. * void rewinddir(resource $dir_handle)
  13008. * Rewind directory handle.
  13009. * Parameter
  13010. * $dir_handle
  13011. * The directory handle resource previously opened with opendir().
  13012. * Return
  13013. * FALSE on failure.
  13014. */
  13015. static int jx9Builtin_rewinddir(jx9_context *pCtx, int nArg, jx9_value **apArg)
  13016. {
  13017. const jx9_io_stream *pStream;
  13018. io_private *pDev;
  13019. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  13020. /* Missing/Invalid arguments, return FALSE */
  13021. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  13022. jx9_result_bool(pCtx, 0);
  13023. return JX9_OK;
  13024. }
  13025. /* Extract our private data */
  13026. pDev = (io_private *)jx9_value_to_resource(apArg[0]);
  13027. /* Make sure we are dealing with a valid io_private instance */
  13028. if( IO_PRIVATE_INVALID(pDev) ){
  13029. /*Expecting an IO handle */
  13030. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  13031. jx9_result_bool(pCtx, 0);
  13032. return JX9_OK;
  13033. }
  13034. /* Point to the target IO stream device */
  13035. pStream = pDev->pStream;
  13036. if( pStream == 0 || pStream->xRewindDir == 0 ){
  13037. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  13038. "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
  13039. jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
  13040. );
  13041. jx9_result_bool(pCtx, 0);
  13042. return JX9_OK;
  13043. }
  13044. /* Perform the requested operation */
  13045. pStream->xRewindDir(pDev->pHandle);
  13046. return JX9_OK;
  13047. }
  13048. /* Forward declaration */
  13049. static void InitIOPrivate(jx9_vm *pVm, const jx9_io_stream *pStream, io_private *pOut);
  13050. static void ReleaseIOPrivate(jx9_context *pCtx, io_private *pDev);
  13051. /*
  13052. * void closedir(resource $dir_handle)
  13053. * Close directory handle.
  13054. * Parameter
  13055. * $dir_handle
  13056. * The directory handle resource previously opened with opendir().
  13057. * Return
  13058. * FALSE on failure.
  13059. */
  13060. static int jx9Builtin_closedir(jx9_context *pCtx, int nArg, jx9_value **apArg)
  13061. {
  13062. const jx9_io_stream *pStream;
  13063. io_private *pDev;
  13064. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  13065. /* Missing/Invalid arguments, return FALSE */
  13066. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  13067. jx9_result_bool(pCtx, 0);
  13068. return JX9_OK;
  13069. }
  13070. /* Extract our private data */
  13071. pDev = (io_private *)jx9_value_to_resource(apArg[0]);
  13072. /* Make sure we are dealing with a valid io_private instance */
  13073. if( IO_PRIVATE_INVALID(pDev) ){
  13074. /*Expecting an IO handle */
  13075. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  13076. jx9_result_bool(pCtx, 0);
  13077. return JX9_OK;
  13078. }
  13079. /* Point to the target IO stream device */
  13080. pStream = pDev->pStream;
  13081. if( pStream == 0 || pStream->xCloseDir == 0 ){
  13082. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  13083. "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
  13084. jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
  13085. );
  13086. jx9_result_bool(pCtx, 0);
  13087. return JX9_OK;
  13088. }
  13089. /* Perform the requested operation */
  13090. pStream->xCloseDir(pDev->pHandle);
  13091. /* Release the private stucture */
  13092. ReleaseIOPrivate(pCtx, pDev);
  13093. jx9MemObjRelease(apArg[0]);
  13094. return JX9_OK;
  13095. }
  13096. /*
  13097. * resource opendir(string $path[, resource $context])
  13098. * Open directory handle.
  13099. * Parameters
  13100. * $path
  13101. * The directory path that is to be opened.
  13102. * $context
  13103. * A context stream resource.
  13104. * Return
  13105. * A directory handle resource on success, or FALSE on failure.
  13106. */
  13107. static int jx9Builtin_opendir(jx9_context *pCtx, int nArg, jx9_value **apArg)
  13108. {
  13109. const jx9_io_stream *pStream;
  13110. const char *zPath;
  13111. io_private *pDev;
  13112. int iLen, rc;
  13113. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  13114. /* Missing/Invalid arguments, return FALSE */
  13115. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a directory path");
  13116. jx9_result_bool(pCtx, 0);
  13117. return JX9_OK;
  13118. }
  13119. /* Extract the target path */
  13120. zPath = jx9_value_to_string(apArg[0], &iLen);
  13121. /* Try to extract a stream */
  13122. pStream = jx9VmGetStreamDevice(pCtx->pVm, &zPath, iLen);
  13123. if( pStream == 0 ){
  13124. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  13125. "No stream device is associated with the given path(%s)", zPath);
  13126. jx9_result_bool(pCtx, 0);
  13127. return JX9_OK;
  13128. }
  13129. if( pStream->xOpenDir == 0 ){
  13130. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  13131. "IO routine(%s) not implemented in the underlying stream(%s) device",
  13132. jx9_function_name(pCtx), pStream->zName
  13133. );
  13134. jx9_result_bool(pCtx, 0);
  13135. return JX9_OK;
  13136. }
  13137. /* Allocate a new IO private instance */
  13138. pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE);
  13139. if( pDev == 0 ){
  13140. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
  13141. jx9_result_bool(pCtx, 0);
  13142. return JX9_OK;
  13143. }
  13144. /* Initialize the structure */
  13145. InitIOPrivate(pCtx->pVm, pStream, pDev);
  13146. /* Open the target directory */
  13147. rc = pStream->xOpenDir(zPath, nArg > 1 ? apArg[1] : 0, &pDev->pHandle);
  13148. if( rc != JX9_OK ){
  13149. /* IO error, return FALSE */
  13150. ReleaseIOPrivate(pCtx, pDev);
  13151. jx9_result_bool(pCtx, 0);
  13152. }else{
  13153. /* Return the handle as a resource */
  13154. jx9_result_resource(pCtx, pDev);
  13155. }
  13156. return JX9_OK;
  13157. }
  13158. /*
  13159. * int readfile(string $filename[, bool $use_include_path = false [, resource $context ]])
  13160. * Reads a file and writes it to the output buffer.
  13161. * Parameters
  13162. * $filename
  13163. * The filename being read.
  13164. * $use_include_path
  13165. * You can use the optional second parameter and set it to
  13166. * TRUE, if you want to search for the file in the include_path, too.
  13167. * $context
  13168. * A context stream resource.
  13169. * Return
  13170. * The number of bytes read from the file on success or FALSE on failure.
  13171. */
  13172. static int jx9Builtin_readfile(jx9_context *pCtx, int nArg, jx9_value **apArg)
  13173. {
  13174. int use_include = FALSE;
  13175. const jx9_io_stream *pStream;
  13176. jx9_int64 n, nRead;
  13177. const char *zFile;
  13178. char zBuf[8192];
  13179. void *pHandle;
  13180. int rc, nLen;
  13181. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  13182. /* Missing/Invalid arguments, return FALSE */
  13183. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
  13184. jx9_result_bool(pCtx, 0);
  13185. return JX9_OK;
  13186. }
  13187. /* Extract the file path */
  13188. zFile = jx9_value_to_string(apArg[0], &nLen);
  13189. /* Point to the target IO stream device */
  13190. pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
  13191. if( pStream == 0 ){
  13192. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
  13193. jx9_result_bool(pCtx, 0);
  13194. return JX9_OK;
  13195. }
  13196. if( nArg > 1 ){
  13197. use_include = jx9_value_to_bool(apArg[1]);
  13198. }
  13199. /* Try to open the file in read-only mode */
  13200. pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY,
  13201. use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0);
  13202. if( pHandle == 0 ){
  13203. jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
  13204. jx9_result_bool(pCtx, 0);
  13205. return JX9_OK;
  13206. }
  13207. /* Perform the requested operation */
  13208. nRead = 0;
  13209. for(;;){
  13210. n = pStream->xRead(pHandle, zBuf, sizeof(zBuf));
  13211. if( n < 1 ){
  13212. /* EOF or IO error, break immediately */
  13213. break;
  13214. }
  13215. /* Output data */
  13216. rc = jx9_context_output(pCtx, zBuf, (int)n);
  13217. if( rc == JX9_ABORT ){
  13218. break;
  13219. }
  13220. /* Increment counter */
  13221. nRead += n;
  13222. }
  13223. /* Close the stream */
  13224. jx9StreamCloseHandle(pStream, pHandle);
  13225. /* Total number of bytes readen */
  13226. jx9_result_int64(pCtx, nRead);
  13227. return JX9_OK;
  13228. }
  13229. /*
  13230. * string file_get_contents(string $filename[, bool $use_include_path = false
  13231. * [, resource $context [, int $offset = -1 [, int $maxlen ]]]])
  13232. * Reads entire file into a string.
  13233. * Parameters
  13234. * $filename
  13235. * The filename being read.
  13236. * $use_include_path
  13237. * You can use the optional second parameter and set it to
  13238. * TRUE, if you want to search for the file in the include_path, too.
  13239. * $context
  13240. * A context stream resource.
  13241. * $offset
  13242. * The offset where the reading starts on the original stream.
  13243. * $maxlen
  13244. * Maximum length of data read. The default is to read until end of file
  13245. * is reached. Note that this parameter is applied to the stream processed by the filters.
  13246. * Return
  13247. * The function returns the read data or FALSE on failure.
  13248. */
  13249. static int jx9Builtin_file_get_contents(jx9_context *pCtx, int nArg, jx9_value **apArg)
  13250. {
  13251. const jx9_io_stream *pStream;
  13252. jx9_int64 n, nRead, nMaxlen;
  13253. int use_include = FALSE;
  13254. const char *zFile;
  13255. char zBuf[8192];
  13256. void *pHandle;
  13257. int nLen;
  13258. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  13259. /* Missing/Invalid arguments, return FALSE */
  13260. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
  13261. jx9_result_bool(pCtx, 0);
  13262. return JX9_OK;
  13263. }
  13264. /* Extract the file path */
  13265. zFile = jx9_value_to_string(apArg[0], &nLen);
  13266. /* Point to the target IO stream device */
  13267. pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
  13268. if( pStream == 0 ){
  13269. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
  13270. jx9_result_bool(pCtx, 0);
  13271. return JX9_OK;
  13272. }
  13273. nMaxlen = -1;
  13274. if( nArg > 1 ){
  13275. use_include = jx9_value_to_bool(apArg[1]);
  13276. }
  13277. /* Try to open the file in read-only mode */
  13278. pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0);
  13279. if( pHandle == 0 ){
  13280. jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
  13281. jx9_result_bool(pCtx, 0);
  13282. return JX9_OK;
  13283. }
  13284. if( nArg > 3 ){
  13285. /* Extract the offset */
  13286. n = jx9_value_to_int64(apArg[3]);
  13287. if( n > 0 ){
  13288. if( pStream->xSeek ){
  13289. /* Seek to the desired offset */
  13290. pStream->xSeek(pHandle, n, 0/*SEEK_SET*/);
  13291. }
  13292. }
  13293. if( nArg > 4 ){
  13294. /* Maximum data to read */
  13295. nMaxlen = jx9_value_to_int64(apArg[4]);
  13296. }
  13297. }
  13298. /* Perform the requested operation */
  13299. nRead = 0;
  13300. for(;;){
  13301. n = pStream->xRead(pHandle, zBuf,
  13302. (nMaxlen > 0 && (nMaxlen < sizeof(zBuf))) ? nMaxlen : sizeof(zBuf));
  13303. if( n < 1 ){
  13304. /* EOF or IO error, break immediately */
  13305. break;
  13306. }
  13307. /* Append data */
  13308. jx9_result_string(pCtx, zBuf, (int)n);
  13309. /* Increment read counter */
  13310. nRead += n;
  13311. if( nMaxlen > 0 && nRead >= nMaxlen ){
  13312. /* Read limit reached */
  13313. break;
  13314. }
  13315. }
  13316. /* Close the stream */
  13317. jx9StreamCloseHandle(pStream, pHandle);
  13318. /* Check if we have read something */
  13319. if( jx9_context_result_buf_length(pCtx) < 1 ){
  13320. /* Nothing read, return FALSE */
  13321. jx9_result_bool(pCtx, 0);
  13322. }
  13323. return JX9_OK;
  13324. }
  13325. /*
  13326. * int file_put_contents(string $filename, mixed $data[, int $flags = 0[, resource $context]])
  13327. * Write a string to a file.
  13328. * Parameters
  13329. * $filename
  13330. * Path to the file where to write the data.
  13331. * $data
  13332. * The data to write(Must be a string).
  13333. * $flags
  13334. * The value of flags can be any combination of the following
  13335. * flags, joined with the binary OR (|) operator.
  13336. * FILE_USE_INCLUDE_PATH Search for filename in the include directory. See include_path for more information.
  13337. * FILE_APPEND If file filename already exists, append the data to the file instead of overwriting it.
  13338. * LOCK_EX Acquire an exclusive lock on the file while proceeding to the writing.
  13339. * context
  13340. * A context stream resource.
  13341. * Return
  13342. * The function returns the number of bytes that were written to the file, or FALSE on failure.
  13343. */
  13344. static int jx9Builtin_file_put_contents(jx9_context *pCtx, int nArg, jx9_value **apArg)
  13345. {
  13346. int use_include = FALSE;
  13347. const jx9_io_stream *pStream;
  13348. const char *zFile;
  13349. const char *zData;
  13350. int iOpenFlags;
  13351. void *pHandle;
  13352. int iFlags;
  13353. int nLen;
  13354. if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
  13355. /* Missing/Invalid arguments, return FALSE */
  13356. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
  13357. jx9_result_bool(pCtx, 0);
  13358. return JX9_OK;
  13359. }
  13360. /* Extract the file path */
  13361. zFile = jx9_value_to_string(apArg[0], &nLen);
  13362. /* Point to the target IO stream device */
  13363. pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
  13364. if( pStream == 0 ){
  13365. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
  13366. jx9_result_bool(pCtx, 0);
  13367. return JX9_OK;
  13368. }
  13369. /* Data to write */
  13370. zData = jx9_value_to_string(apArg[1], &nLen);
  13371. if( nLen < 1 ){
  13372. /* Nothing to write, return immediately */
  13373. jx9_result_bool(pCtx, 0);
  13374. return JX9_OK;
  13375. }
  13376. /* Try to open the file in read-write mode */
  13377. iOpenFlags = JX9_IO_OPEN_CREATE|JX9_IO_OPEN_RDWR|JX9_IO_OPEN_TRUNC;
  13378. /* Extract the flags */
  13379. iFlags = 0;
  13380. if( nArg > 2 ){
  13381. iFlags = jx9_value_to_int(apArg[2]);
  13382. if( iFlags & 0x01 /*FILE_USE_INCLUDE_PATH*/){
  13383. use_include = TRUE;
  13384. }
  13385. if( iFlags & 0x08 /* FILE_APPEND */){
  13386. /* If the file already exists, append the data to the file
  13387. * instead of overwriting it.
  13388. */
  13389. iOpenFlags &= ~JX9_IO_OPEN_TRUNC;
  13390. /* Append mode */
  13391. iOpenFlags |= JX9_IO_OPEN_APPEND;
  13392. }
  13393. }
  13394. pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, iOpenFlags, use_include,
  13395. nArg > 3 ? apArg[3] : 0, FALSE, FALSE);
  13396. if( pHandle == 0 ){
  13397. jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
  13398. jx9_result_bool(pCtx, 0);
  13399. return JX9_OK;
  13400. }
  13401. if( pStream->xWrite ){
  13402. jx9_int64 n;
  13403. if( (iFlags & 0x01/* LOCK_EX */) && pStream->xLock ){
  13404. /* Try to acquire an exclusive lock */
  13405. pStream->xLock(pHandle, 1/* LOCK_EX */);
  13406. }
  13407. /* Perform the write operation */
  13408. n = pStream->xWrite(pHandle, (const void *)zData, nLen);
  13409. if( n < 1 ){
  13410. /* IO error, return FALSE */
  13411. jx9_result_bool(pCtx, 0);
  13412. }else{
  13413. /* Total number of bytes written */
  13414. jx9_result_int64(pCtx, n);
  13415. }
  13416. }else{
  13417. /* Read-only stream */
  13418. jx9_context_throw_error_format(pCtx, JX9_CTX_ERR,
  13419. "Read-only stream(%s): Cannot perform write operation",
  13420. pStream ? pStream->zName : "null_stream"
  13421. );
  13422. jx9_result_bool(pCtx, 0);
  13423. }
  13424. /* Close the handle */
  13425. jx9StreamCloseHandle(pStream, pHandle);
  13426. return JX9_OK;
  13427. }
  13428. /*
  13429. * array file(string $filename[, int $flags = 0[, resource $context]])
  13430. * Reads entire file into an array.
  13431. * Parameters
  13432. * $filename
  13433. * The filename being read.
  13434. * $flags
  13435. * The optional parameter flags can be one, or more, of the following constants:
  13436. * FILE_USE_INCLUDE_PATH
  13437. * Search for the file in the include_path.
  13438. * FILE_IGNORE_NEW_LINES
  13439. * Do not add newline at the end of each array element
  13440. * FILE_SKIP_EMPTY_LINES
  13441. * Skip empty lines
  13442. * $context
  13443. * A context stream resource.
  13444. * Return
  13445. * The function returns the read data or FALSE on failure.
  13446. */
  13447. static int jx9Builtin_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
  13448. {
  13449. const char *zFile, *zPtr, *zEnd, *zBuf;
  13450. jx9_value *pArray, *pLine;
  13451. const jx9_io_stream *pStream;
  13452. int use_include = 0;
  13453. io_private *pDev;
  13454. jx9_int64 n;
  13455. int iFlags;
  13456. int nLen;
  13457. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  13458. /* Missing/Invalid arguments, return FALSE */
  13459. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
  13460. jx9_result_bool(pCtx, 0);
  13461. return JX9_OK;
  13462. }
  13463. /* Extract the file path */
  13464. zFile = jx9_value_to_string(apArg[0], &nLen);
  13465. /* Point to the target IO stream device */
  13466. pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
  13467. if( pStream == 0 ){
  13468. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
  13469. jx9_result_bool(pCtx, 0);
  13470. return JX9_OK;
  13471. }
  13472. /* Allocate a new IO private instance */
  13473. pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE);
  13474. if( pDev == 0 ){
  13475. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
  13476. jx9_result_bool(pCtx, 0);
  13477. return JX9_OK;
  13478. }
  13479. /* Initialize the structure */
  13480. InitIOPrivate(pCtx->pVm, pStream, pDev);
  13481. iFlags = 0;
  13482. if( nArg > 1 ){
  13483. iFlags = jx9_value_to_int(apArg[1]);
  13484. }
  13485. if( iFlags & 0x01 /*FILE_USE_INCLUDE_PATH*/ ){
  13486. use_include = TRUE;
  13487. }
  13488. /* Create the array and the working value */
  13489. pArray = jx9_context_new_array(pCtx);
  13490. pLine = jx9_context_new_scalar(pCtx);
  13491. if( pArray == 0 || pLine == 0 ){
  13492. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
  13493. jx9_result_bool(pCtx, 0);
  13494. return JX9_OK;
  13495. }
  13496. /* Try to open the file in read-only mode */
  13497. pDev->pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0);
  13498. if( pDev->pHandle == 0 ){
  13499. jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
  13500. jx9_result_bool(pCtx, 0);
  13501. /* Don't worry about freeing memory, everything will be released automatically
  13502. * as soon we return from this function.
  13503. */
  13504. return JX9_OK;
  13505. }
  13506. /* Perform the requested operation */
  13507. for(;;){
  13508. /* Try to extract a line */
  13509. n = StreamReadLine(pDev, &zBuf, -1);
  13510. if( n < 1 ){
  13511. /* EOF or IO error */
  13512. break;
  13513. }
  13514. /* Reset the cursor */
  13515. jx9_value_reset_string_cursor(pLine);
  13516. /* Remove line ending if requested by the caller */
  13517. zPtr = zBuf;
  13518. zEnd = &zBuf[n];
  13519. if( iFlags & 0x02 /* FILE_IGNORE_NEW_LINES */ ){
  13520. /* Ignore trailig lines */
  13521. while( zPtr < zEnd && (zEnd[-1] == '\n'
  13522. #ifdef __WINNT__
  13523. || zEnd[-1] == '\r'
  13524. #endif
  13525. )){
  13526. n--;
  13527. zEnd--;
  13528. }
  13529. }
  13530. if( iFlags & 0x04 /* FILE_SKIP_EMPTY_LINES */ ){
  13531. /* Ignore empty lines */
  13532. while( zPtr < zEnd && (unsigned char)zPtr[0] < 0xc0 && SyisSpace(zPtr[0]) ){
  13533. zPtr++;
  13534. }
  13535. if( zPtr >= zEnd ){
  13536. /* Empty line */
  13537. continue;
  13538. }
  13539. }
  13540. jx9_value_string(pLine, zBuf, (int)(zEnd-zBuf));
  13541. /* Insert line */
  13542. jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pLine);
  13543. }
  13544. /* Close the stream */
  13545. jx9StreamCloseHandle(pStream, pDev->pHandle);
  13546. /* Release the io_private instance */
  13547. ReleaseIOPrivate(pCtx, pDev);
  13548. /* Return the created array */
  13549. jx9_result_value(pCtx, pArray);
  13550. return JX9_OK;
  13551. }
  13552. /*
  13553. * bool copy(string $source, string $dest[, resource $context ] )
  13554. * Makes a copy of the file source to dest.
  13555. * Parameters
  13556. * $source
  13557. * Path to the source file.
  13558. * $dest
  13559. * The destination path. If dest is a URL, the copy operation
  13560. * may fail if the wrapper does not support overwriting of existing files.
  13561. * $context
  13562. * A context stream resource.
  13563. * Return
  13564. * TRUE on success or FALSE on failure.
  13565. */
  13566. static int jx9Builtin_copy(jx9_context *pCtx, int nArg, jx9_value **apArg)
  13567. {
  13568. const jx9_io_stream *pSin, *pSout;
  13569. const char *zFile;
  13570. char zBuf[8192];
  13571. void *pIn, *pOut;
  13572. jx9_int64 n;
  13573. int nLen;
  13574. if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1])){
  13575. /* Missing/Invalid arguments, return FALSE */
  13576. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a source and a destination path");
  13577. jx9_result_bool(pCtx, 0);
  13578. return JX9_OK;
  13579. }
  13580. /* Extract the source name */
  13581. zFile = jx9_value_to_string(apArg[0], &nLen);
  13582. /* Point to the target IO stream device */
  13583. pSin = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
  13584. if( pSin == 0 ){
  13585. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
  13586. jx9_result_bool(pCtx, 0);
  13587. return JX9_OK;
  13588. }
  13589. /* Try to open the source file in a read-only mode */
  13590. pIn = jx9StreamOpenHandle(pCtx->pVm, pSin, zFile, JX9_IO_OPEN_RDONLY, FALSE, nArg > 2 ? apArg[2] : 0, FALSE, 0);
  13591. if( pIn == 0 ){
  13592. jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening source: '%s'", zFile);
  13593. jx9_result_bool(pCtx, 0);
  13594. return JX9_OK;
  13595. }
  13596. /* Extract the destination name */
  13597. zFile = jx9_value_to_string(apArg[1], &nLen);
  13598. /* Point to the target IO stream device */
  13599. pSout = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
  13600. if( pSout == 0 ){
  13601. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
  13602. jx9_result_bool(pCtx, 0);
  13603. jx9StreamCloseHandle(pSin, pIn);
  13604. return JX9_OK;
  13605. }
  13606. if( pSout->xWrite == 0 ){
  13607. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  13608. "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
  13609. jx9_function_name(pCtx), pSin->zName
  13610. );
  13611. jx9_result_bool(pCtx, 0);
  13612. jx9StreamCloseHandle(pSin, pIn);
  13613. return JX9_OK;
  13614. }
  13615. /* Try to open the destination file in a read-write mode */
  13616. pOut = jx9StreamOpenHandle(pCtx->pVm, pSout, zFile,
  13617. JX9_IO_OPEN_CREATE|JX9_IO_OPEN_TRUNC|JX9_IO_OPEN_RDWR, FALSE, nArg > 2 ? apArg[2] : 0, FALSE, 0);
  13618. if( pOut == 0 ){
  13619. jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening destination: '%s'", zFile);
  13620. jx9_result_bool(pCtx, 0);
  13621. jx9StreamCloseHandle(pSin, pIn);
  13622. return JX9_OK;
  13623. }
  13624. /* Perform the requested operation */
  13625. for(;;){
  13626. /* Read from source */
  13627. n = pSin->xRead(pIn, zBuf, sizeof(zBuf));
  13628. if( n < 1 ){
  13629. /* EOF or IO error, break immediately */
  13630. break;
  13631. }
  13632. /* Write to dest */
  13633. n = pSout->xWrite(pOut, zBuf, n);
  13634. if( n < 1 ){
  13635. /* IO error, break immediately */
  13636. break;
  13637. }
  13638. }
  13639. /* Close the streams */
  13640. jx9StreamCloseHandle(pSin, pIn);
  13641. jx9StreamCloseHandle(pSout, pOut);
  13642. /* Return TRUE */
  13643. jx9_result_bool(pCtx, 1);
  13644. return JX9_OK;
  13645. }
  13646. /*
  13647. * array fstat(resource $handle)
  13648. * Gets information about a file using an open file pointer.
  13649. * Parameters
  13650. * $handle
  13651. * The file pointer.
  13652. * Return
  13653. * Returns an array with the statistics of the file or FALSE on failure.
  13654. */
  13655. static int jx9Builtin_fstat(jx9_context *pCtx, int nArg, jx9_value **apArg)
  13656. {
  13657. jx9_value *pArray, *pValue;
  13658. const jx9_io_stream *pStream;
  13659. io_private *pDev;
  13660. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  13661. /* Missing/Invalid arguments, return FALSE */
  13662. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  13663. jx9_result_bool(pCtx, 0);
  13664. return JX9_OK;
  13665. }
  13666. /* Extract our private data */
  13667. pDev = (io_private *)jx9_value_to_resource(apArg[0]);
  13668. /* Make sure we are dealing with a valid io_private instance */
  13669. if( IO_PRIVATE_INVALID(pDev) ){
  13670. /* Expecting an IO handle */
  13671. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  13672. jx9_result_bool(pCtx, 0);
  13673. return JX9_OK;
  13674. }
  13675. /* Point to the target IO stream device */
  13676. pStream = pDev->pStream;
  13677. if( pStream == 0 || pStream->xStat == 0){
  13678. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  13679. "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
  13680. jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
  13681. );
  13682. jx9_result_bool(pCtx, 0);
  13683. return JX9_OK;
  13684. }
  13685. /* Create the array and the working value */
  13686. pArray = jx9_context_new_array(pCtx);
  13687. pValue = jx9_context_new_scalar(pCtx);
  13688. if( pArray == 0 || pValue == 0 ){
  13689. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
  13690. jx9_result_bool(pCtx, 0);
  13691. return JX9_OK;
  13692. }
  13693. /* Perform the requested operation */
  13694. pStream->xStat(pDev->pHandle, pArray, pValue);
  13695. /* Return the freshly created array */
  13696. jx9_result_value(pCtx, pArray);
  13697. /* Don't worry about freeing memory here, everything will be
  13698. * released automatically as soon we return from this function.
  13699. */
  13700. return JX9_OK;
  13701. }
  13702. /*
  13703. * int fwrite(resource $handle, string $string[, int $length])
  13704. * Writes the contents of string to the file stream pointed to by handle.
  13705. * Parameters
  13706. * $handle
  13707. * The file pointer.
  13708. * $string
  13709. * The string that is to be written.
  13710. * $length
  13711. * If the length argument is given, writing will stop after length bytes have been written
  13712. * or the end of string is reached, whichever comes first.
  13713. * Return
  13714. * Returns the number of bytes written, or FALSE on error.
  13715. */
  13716. static int jx9Builtin_fwrite(jx9_context *pCtx, int nArg, jx9_value **apArg)
  13717. {
  13718. const jx9_io_stream *pStream;
  13719. const char *zString;
  13720. io_private *pDev;
  13721. int nLen, n;
  13722. if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
  13723. /* Missing/Invalid arguments, return FALSE */
  13724. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  13725. jx9_result_bool(pCtx, 0);
  13726. return JX9_OK;
  13727. }
  13728. /* Extract our private data */
  13729. pDev = (io_private *)jx9_value_to_resource(apArg[0]);
  13730. /* Make sure we are dealing with a valid io_private instance */
  13731. if( IO_PRIVATE_INVALID(pDev) ){
  13732. /* Expecting an IO handle */
  13733. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  13734. jx9_result_bool(pCtx, 0);
  13735. return JX9_OK;
  13736. }
  13737. /* Point to the target IO stream device */
  13738. pStream = pDev->pStream;
  13739. if( pStream == 0 || pStream->xWrite == 0){
  13740. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  13741. "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
  13742. jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
  13743. );
  13744. jx9_result_bool(pCtx, 0);
  13745. return JX9_OK;
  13746. }
  13747. /* Extract the data to write */
  13748. zString = jx9_value_to_string(apArg[1], &nLen);
  13749. if( nArg > 2 ){
  13750. /* Maximum data length to write */
  13751. n = jx9_value_to_int(apArg[2]);
  13752. if( n >= 0 && n < nLen ){
  13753. nLen = n;
  13754. }
  13755. }
  13756. if( nLen < 1 ){
  13757. /* Nothing to write */
  13758. jx9_result_int(pCtx, 0);
  13759. return JX9_OK;
  13760. }
  13761. /* Perform the requested operation */
  13762. n = (int)pStream->xWrite(pDev->pHandle, (const void *)zString, nLen);
  13763. if( n < 0 ){
  13764. /* IO error, return FALSE */
  13765. jx9_result_bool(pCtx, 0);
  13766. }else{
  13767. /* #Bytes written */
  13768. jx9_result_int(pCtx, n);
  13769. }
  13770. return JX9_OK;
  13771. }
  13772. /*
  13773. * bool flock(resource $handle, int $operation)
  13774. * Portable advisory file locking.
  13775. * Parameters
  13776. * $handle
  13777. * The file pointer.
  13778. * $operation
  13779. * operation is one of the following:
  13780. * LOCK_SH to acquire a shared lock (reader).
  13781. * LOCK_EX to acquire an exclusive lock (writer).
  13782. * LOCK_UN to release a lock (shared or exclusive).
  13783. * Return
  13784. * Returns TRUE on success or FALSE on failure.
  13785. */
  13786. static int jx9Builtin_flock(jx9_context *pCtx, int nArg, jx9_value **apArg)
  13787. {
  13788. const jx9_io_stream *pStream;
  13789. io_private *pDev;
  13790. int nLock;
  13791. int rc;
  13792. if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
  13793. /* Missing/Invalid arguments, return FALSE */
  13794. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  13795. jx9_result_bool(pCtx, 0);
  13796. return JX9_OK;
  13797. }
  13798. /* Extract our private data */
  13799. pDev = (io_private *)jx9_value_to_resource(apArg[0]);
  13800. /* Make sure we are dealing with a valid io_private instance */
  13801. if( IO_PRIVATE_INVALID(pDev) ){
  13802. /*Expecting an IO handle */
  13803. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  13804. jx9_result_bool(pCtx, 0);
  13805. return JX9_OK;
  13806. }
  13807. /* Point to the target IO stream device */
  13808. pStream = pDev->pStream;
  13809. if( pStream == 0 || pStream->xLock == 0){
  13810. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  13811. "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
  13812. jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
  13813. );
  13814. jx9_result_bool(pCtx, 0);
  13815. return JX9_OK;
  13816. }
  13817. /* Requested lock operation */
  13818. nLock = jx9_value_to_int(apArg[1]);
  13819. /* Lock operation */
  13820. rc = pStream->xLock(pDev->pHandle, nLock);
  13821. /* IO result */
  13822. jx9_result_bool(pCtx, rc == JX9_OK);
  13823. return JX9_OK;
  13824. }
  13825. /*
  13826. * int fpassthru(resource $handle)
  13827. * Output all remaining data on a file pointer.
  13828. * Parameters
  13829. * $handle
  13830. * The file pointer.
  13831. * Return
  13832. * Total number of characters read from handle and passed through
  13833. * to the output on success or FALSE on failure.
  13834. */
  13835. static int jx9Builtin_fpassthru(jx9_context *pCtx, int nArg, jx9_value **apArg)
  13836. {
  13837. const jx9_io_stream *pStream;
  13838. io_private *pDev;
  13839. jx9_int64 n, nRead;
  13840. char zBuf[8192];
  13841. int rc;
  13842. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  13843. /* Missing/Invalid arguments, return FALSE */
  13844. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  13845. jx9_result_bool(pCtx, 0);
  13846. return JX9_OK;
  13847. }
  13848. /* Extract our private data */
  13849. pDev = (io_private *)jx9_value_to_resource(apArg[0]);
  13850. /* Make sure we are dealing with a valid io_private instance */
  13851. if( IO_PRIVATE_INVALID(pDev) ){
  13852. /*Expecting an IO handle */
  13853. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  13854. jx9_result_bool(pCtx, 0);
  13855. return JX9_OK;
  13856. }
  13857. /* Point to the target IO stream device */
  13858. pStream = pDev->pStream;
  13859. if( pStream == 0 ){
  13860. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  13861. "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
  13862. jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
  13863. );
  13864. jx9_result_bool(pCtx, 0);
  13865. return JX9_OK;
  13866. }
  13867. /* Perform the requested operation */
  13868. nRead = 0;
  13869. for(;;){
  13870. n = StreamRead(pDev, zBuf, sizeof(zBuf));
  13871. if( n < 1 ){
  13872. /* Error or EOF */
  13873. break;
  13874. }
  13875. /* Increment the read counter */
  13876. nRead += n;
  13877. /* Output data */
  13878. rc = jx9_context_output(pCtx, zBuf, (int)nRead /* FIXME: 64-bit issues */);
  13879. if( rc == JX9_ABORT ){
  13880. /* Consumer callback request an operation abort */
  13881. break;
  13882. }
  13883. }
  13884. /* Total number of bytes readen */
  13885. jx9_result_int64(pCtx, nRead);
  13886. return JX9_OK;
  13887. }
  13888. /* CSV reader/writer private data */
  13889. struct csv_data
  13890. {
  13891. int delimiter; /* Delimiter. Default ', ' */
  13892. int enclosure; /* Enclosure. Default '"'*/
  13893. io_private *pDev; /* Open stream handle */
  13894. int iCount; /* Counter */
  13895. };
  13896. /*
  13897. * The following callback is used by the fputcsv() function inorder to iterate
  13898. * throw array entries and output CSV data based on the current key and it's
  13899. * associated data.
  13900. */
  13901. static int csv_write_callback(jx9_value *pKey, jx9_value *pValue, void *pUserData)
  13902. {
  13903. struct csv_data *pData = (struct csv_data *)pUserData;
  13904. const char *zData;
  13905. int nLen, c2;
  13906. sxu32 n;
  13907. /* Point to the raw data */
  13908. zData = jx9_value_to_string(pValue, &nLen);
  13909. if( nLen < 1 ){
  13910. /* Nothing to write */
  13911. return JX9_OK;
  13912. }
  13913. if( pData->iCount > 0 ){
  13914. /* Write the delimiter */
  13915. pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->delimiter, sizeof(char));
  13916. }
  13917. n = 1;
  13918. c2 = 0;
  13919. if( SyByteFind(zData, (sxu32)nLen, pData->delimiter, 0) == SXRET_OK ||
  13920. SyByteFind(zData, (sxu32)nLen, pData->enclosure, &n) == SXRET_OK ){
  13921. c2 = 1;
  13922. if( n == 0 ){
  13923. c2 = 2;
  13924. }
  13925. /* Write the enclosure */
  13926. pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char));
  13927. if( c2 > 1 ){
  13928. pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char));
  13929. }
  13930. }
  13931. /* Write the data */
  13932. if( pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)zData, (jx9_int64)nLen) < 1 ){
  13933. SXUNUSED(pKey); /* cc warning */
  13934. return JX9_ABORT;
  13935. }
  13936. if( c2 > 0 ){
  13937. /* Write the enclosure */
  13938. pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char));
  13939. if( c2 > 1 ){
  13940. pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char));
  13941. }
  13942. }
  13943. pData->iCount++;
  13944. return JX9_OK;
  13945. }
  13946. /*
  13947. * int fputcsv(resource $handle, array $fields[, string $delimiter = ', '[, string $enclosure = '"' ]])
  13948. * Format line as CSV and write to file pointer.
  13949. * Parameters
  13950. * $handle
  13951. * Open file handle.
  13952. * $fields
  13953. * An array of values.
  13954. * $delimiter
  13955. * The optional delimiter parameter sets the field delimiter (one character only).
  13956. * $enclosure
  13957. * The optional enclosure parameter sets the field enclosure (one character only).
  13958. */
  13959. static int jx9Builtin_fputcsv(jx9_context *pCtx, int nArg, jx9_value **apArg)
  13960. {
  13961. const jx9_io_stream *pStream;
  13962. struct csv_data sCsv;
  13963. io_private *pDev;
  13964. char *zEol;
  13965. int eolen;
  13966. if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
  13967. /* Missing/Invalid arguments, return FALSE */
  13968. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Missing/Invalid arguments");
  13969. jx9_result_bool(pCtx, 0);
  13970. return JX9_OK;
  13971. }
  13972. /* Extract our private data */
  13973. pDev = (io_private *)jx9_value_to_resource(apArg[0]);
  13974. /* Make sure we are dealing with a valid io_private instance */
  13975. if( IO_PRIVATE_INVALID(pDev) ){
  13976. /*Expecting an IO handle */
  13977. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  13978. jx9_result_bool(pCtx, 0);
  13979. return JX9_OK;
  13980. }
  13981. /* Point to the target IO stream device */
  13982. pStream = pDev->pStream;
  13983. if( pStream == 0 || pStream->xWrite == 0){
  13984. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  13985. "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
  13986. jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
  13987. );
  13988. jx9_result_bool(pCtx, 0);
  13989. return JX9_OK;
  13990. }
  13991. /* Set default csv separator */
  13992. sCsv.delimiter = ',';
  13993. sCsv.enclosure = '"';
  13994. sCsv.pDev = pDev;
  13995. sCsv.iCount = 0;
  13996. if( nArg > 2 ){
  13997. /* User delimiter */
  13998. const char *z;
  13999. int n;
  14000. z = jx9_value_to_string(apArg[2], &n);
  14001. if( n > 0 ){
  14002. sCsv.delimiter = z[0];
  14003. }
  14004. if( nArg > 3 ){
  14005. z = jx9_value_to_string(apArg[3], &n);
  14006. if( n > 0 ){
  14007. sCsv.enclosure = z[0];
  14008. }
  14009. }
  14010. }
  14011. /* Iterate throw array entries and write csv data */
  14012. jx9_array_walk(apArg[1], csv_write_callback, &sCsv);
  14013. /* Write a line ending */
  14014. #ifdef __WINNT__
  14015. zEol = "\r\n";
  14016. eolen = (int)sizeof("\r\n")-1;
  14017. #else
  14018. /* Assume UNIX LF */
  14019. zEol = "\n";
  14020. eolen = (int)sizeof(char);
  14021. #endif
  14022. pDev->pStream->xWrite(pDev->pHandle, (const void *)zEol, eolen);
  14023. return JX9_OK;
  14024. }
  14025. /*
  14026. * fprintf, vfprintf private data.
  14027. * An instance of the following structure is passed to the formatted
  14028. * input consumer callback defined below.
  14029. */
  14030. typedef struct fprintf_data fprintf_data;
  14031. struct fprintf_data
  14032. {
  14033. io_private *pIO; /* IO stream */
  14034. jx9_int64 nCount; /* Total number of bytes written */
  14035. };
  14036. /*
  14037. * Callback [i.e: Formatted input consumer] for the fprintf function.
  14038. */
  14039. static int fprintfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData)
  14040. {
  14041. fprintf_data *pFdata = (fprintf_data *)pUserData;
  14042. jx9_int64 n;
  14043. /* Write the formatted data */
  14044. n = pFdata->pIO->pStream->xWrite(pFdata->pIO->pHandle, (const void *)zInput, nLen);
  14045. if( n < 1 ){
  14046. SXUNUSED(pCtx); /* cc warning */
  14047. /* IO error, abort immediately */
  14048. return SXERR_ABORT;
  14049. }
  14050. /* Increment counter */
  14051. pFdata->nCount += n;
  14052. return JX9_OK;
  14053. }
  14054. /*
  14055. * int fprintf(resource $handle, string $format[, mixed $args [, mixed $... ]])
  14056. * Write a formatted string to a stream.
  14057. * Parameters
  14058. * $handle
  14059. * The file pointer.
  14060. * $format
  14061. * String format (see sprintf()).
  14062. * Return
  14063. * The length of the written string.
  14064. */
  14065. static int jx9Builtin_fprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
  14066. {
  14067. fprintf_data sFdata;
  14068. const char *zFormat;
  14069. io_private *pDev;
  14070. int nLen;
  14071. if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
  14072. /* Missing/Invalid arguments, return zero */
  14073. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Invalid arguments");
  14074. jx9_result_int(pCtx, 0);
  14075. return JX9_OK;
  14076. }
  14077. /* Extract our private data */
  14078. pDev = (io_private *)jx9_value_to_resource(apArg[0]);
  14079. /* Make sure we are dealing with a valid io_private instance */
  14080. if( IO_PRIVATE_INVALID(pDev) ){
  14081. /*Expecting an IO handle */
  14082. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  14083. jx9_result_int(pCtx, 0);
  14084. return JX9_OK;
  14085. }
  14086. /* Point to the target IO stream device */
  14087. if( pDev->pStream == 0 || pDev->pStream->xWrite == 0 ){
  14088. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  14089. "IO routine(%s) not implemented in the underlying stream(%s) device",
  14090. jx9_function_name(pCtx), pDev->pStream ? pDev->pStream->zName : "null_stream"
  14091. );
  14092. jx9_result_int(pCtx, 0);
  14093. return JX9_OK;
  14094. }
  14095. /* Extract the string format */
  14096. zFormat = jx9_value_to_string(apArg[1], &nLen);
  14097. if( nLen < 1 ){
  14098. /* Empty string, return zero */
  14099. jx9_result_int(pCtx, 0);
  14100. return JX9_OK;
  14101. }
  14102. /* Prepare our private data */
  14103. sFdata.nCount = 0;
  14104. sFdata.pIO = pDev;
  14105. /* Format the string */
  14106. jx9InputFormat(fprintfConsumer, pCtx, zFormat, nLen, nArg - 1, &apArg[1], (void *)&sFdata, FALSE);
  14107. /* Return total number of bytes written */
  14108. jx9_result_int64(pCtx, sFdata.nCount);
  14109. return JX9_OK;
  14110. }
  14111. /*
  14112. * int vfprintf(resource $handle, string $format, array $args)
  14113. * Write a formatted string to a stream.
  14114. * Parameters
  14115. * $handle
  14116. * The file pointer.
  14117. * $format
  14118. * String format (see sprintf()).
  14119. * $args
  14120. * User arguments.
  14121. * Return
  14122. * The length of the written string.
  14123. */
  14124. static int jx9Builtin_vfprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
  14125. {
  14126. fprintf_data sFdata;
  14127. const char *zFormat;
  14128. jx9_hashmap *pMap;
  14129. io_private *pDev;
  14130. SySet sArg;
  14131. int n, nLen;
  14132. if( nArg < 3 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_string(apArg[1]) || !jx9_value_is_json_array(apArg[2]) ){
  14133. /* Missing/Invalid arguments, return zero */
  14134. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Invalid arguments");
  14135. jx9_result_int(pCtx, 0);
  14136. return JX9_OK;
  14137. }
  14138. /* Extract our private data */
  14139. pDev = (io_private *)jx9_value_to_resource(apArg[0]);
  14140. /* Make sure we are dealing with a valid io_private instance */
  14141. if( IO_PRIVATE_INVALID(pDev) ){
  14142. /*Expecting an IO handle */
  14143. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  14144. jx9_result_int(pCtx, 0);
  14145. return JX9_OK;
  14146. }
  14147. /* Point to the target IO stream device */
  14148. if( pDev->pStream == 0 || pDev->pStream->xWrite == 0 ){
  14149. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  14150. "IO routine(%s) not implemented in the underlying stream(%s) device",
  14151. jx9_function_name(pCtx), pDev->pStream ? pDev->pStream->zName : "null_stream"
  14152. );
  14153. jx9_result_int(pCtx, 0);
  14154. return JX9_OK;
  14155. }
  14156. /* Extract the string format */
  14157. zFormat = jx9_value_to_string(apArg[1], &nLen);
  14158. if( nLen < 1 ){
  14159. /* Empty string, return zero */
  14160. jx9_result_int(pCtx, 0);
  14161. return JX9_OK;
  14162. }
  14163. /* Point to hashmap */
  14164. pMap = (jx9_hashmap *)apArg[2]->x.pOther;
  14165. /* Extract arguments from the hashmap */
  14166. n = jx9HashmapValuesToSet(pMap, &sArg);
  14167. /* Prepare our private data */
  14168. sFdata.nCount = 0;
  14169. sFdata.pIO = pDev;
  14170. /* Format the string */
  14171. jx9InputFormat(fprintfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), (void *)&sFdata, TRUE);
  14172. /* Return total number of bytes written*/
  14173. jx9_result_int64(pCtx, sFdata.nCount);
  14174. SySetRelease(&sArg);
  14175. return JX9_OK;
  14176. }
  14177. /*
  14178. * Convert open modes (string passed to the fopen() function) [i.e: 'r', 'w+', 'a', ...] into JX9 flags.
  14179. * According to the JX9 reference manual:
  14180. * The mode parameter specifies the type of access you require to the stream. It may be any of the following
  14181. * 'r' Open for reading only; place the file pointer at the beginning of the file.
  14182. * 'r+' Open for reading and writing; place the file pointer at the beginning of the file.
  14183. * 'w' Open for writing only; place the file pointer at the beginning of the file and truncate the file
  14184. * to zero length. If the file does not exist, attempt to create it.
  14185. * 'w+' Open for reading and writing; place the file pointer at the beginning of the file and truncate
  14186. * the file to zero length. If the file does not exist, attempt to create it.
  14187. * 'a' Open for writing only; place the file pointer at the end of the file. If the file does not
  14188. * exist, attempt to create it.
  14189. * 'a+' Open for reading and writing; place the file pointer at the end of the file. If the file does
  14190. * not exist, attempt to create it.
  14191. * 'x' Create and open for writing only; place the file pointer at the beginning of the file. If the file
  14192. * already exists,
  14193. * the fopen() call will fail by returning FALSE and generating an error of level E_WARNING. If the file
  14194. * does not exist attempt to create it. This is equivalent to specifying O_EXCL|O_CREAT flags for
  14195. * the underlying open(2) system call.
  14196. * 'x+' Create and open for reading and writing; otherwise it has the same behavior as 'x'.
  14197. * 'c' Open the file for writing only. If the file does not exist, it is created. If it exists, it is neither truncated
  14198. * (as opposed to 'w'), nor the call to this function fails (as is the case with 'x'). The file pointer
  14199. * is positioned on the beginning of the file.
  14200. * This may be useful if it's desired to get an advisory lock (see flock()) before attempting to modify the file
  14201. * as using 'w' could truncate the file before the lock was obtained (if truncation is desired, ftruncate() can
  14202. * be used after the lock is requested).
  14203. * 'c+' Open the file for reading and writing; otherwise it has the same behavior as 'c'.
  14204. */
  14205. static int StrModeToFlags(jx9_context *pCtx, const char *zMode, int nLen)
  14206. {
  14207. const char *zEnd = &zMode[nLen];
  14208. int iFlag = 0;
  14209. int c;
  14210. if( nLen < 1 ){
  14211. /* Open in a read-only mode */
  14212. return JX9_IO_OPEN_RDONLY;
  14213. }
  14214. c = zMode[0];
  14215. if( c == 'r' || c == 'R' ){
  14216. /* Read-only access */
  14217. iFlag = JX9_IO_OPEN_RDONLY;
  14218. zMode++; /* Advance */
  14219. if( zMode < zEnd ){
  14220. c = zMode[0];
  14221. if( c == '+' || c == 'w' || c == 'W' ){
  14222. /* Read+Write access */
  14223. iFlag = JX9_IO_OPEN_RDWR;
  14224. }
  14225. }
  14226. }else if( c == 'w' || c == 'W' ){
  14227. /* Overwrite mode.
  14228. * If the file does not exists, try to create it
  14229. */
  14230. iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_TRUNC|JX9_IO_OPEN_CREATE;
  14231. zMode++; /* Advance */
  14232. if( zMode < zEnd ){
  14233. c = zMode[0];
  14234. if( c == '+' || c == 'r' || c == 'R' ){
  14235. /* Read+Write access */
  14236. iFlag &= ~JX9_IO_OPEN_WRONLY;
  14237. iFlag |= JX9_IO_OPEN_RDWR;
  14238. }
  14239. }
  14240. }else if( c == 'a' || c == 'A' ){
  14241. /* Append mode (place the file pointer at the end of the file).
  14242. * Create the file if it does not exists.
  14243. */
  14244. iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_APPEND|JX9_IO_OPEN_CREATE;
  14245. zMode++; /* Advance */
  14246. if( zMode < zEnd ){
  14247. c = zMode[0];
  14248. if( c == '+' ){
  14249. /* Read-Write access */
  14250. iFlag &= ~JX9_IO_OPEN_WRONLY;
  14251. iFlag |= JX9_IO_OPEN_RDWR;
  14252. }
  14253. }
  14254. }else if( c == 'x' || c == 'X' ){
  14255. /* Exclusive access.
  14256. * If the file already exists, return immediately with a failure code.
  14257. * Otherwise create a new file.
  14258. */
  14259. iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_EXCL;
  14260. zMode++; /* Advance */
  14261. if( zMode < zEnd ){
  14262. c = zMode[0];
  14263. if( c == '+' || c == 'r' || c == 'R' ){
  14264. /* Read-Write access */
  14265. iFlag &= ~JX9_IO_OPEN_WRONLY;
  14266. iFlag |= JX9_IO_OPEN_RDWR;
  14267. }
  14268. }
  14269. }else if( c == 'c' || c == 'C' ){
  14270. /* Overwrite mode.Create the file if it does not exists.*/
  14271. iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_CREATE;
  14272. zMode++; /* Advance */
  14273. if( zMode < zEnd ){
  14274. c = zMode[0];
  14275. if( c == '+' ){
  14276. /* Read-Write access */
  14277. iFlag &= ~JX9_IO_OPEN_WRONLY;
  14278. iFlag |= JX9_IO_OPEN_RDWR;
  14279. }
  14280. }
  14281. }else{
  14282. /* Invalid mode. Assume a read only open */
  14283. jx9_context_throw_error(pCtx, JX9_CTX_NOTICE, "Invalid open mode, JX9 is assuming a Read-Only open");
  14284. iFlag = JX9_IO_OPEN_RDONLY;
  14285. }
  14286. while( zMode < zEnd ){
  14287. c = zMode[0];
  14288. if( c == 'b' || c == 'B' ){
  14289. iFlag &= ~JX9_IO_OPEN_TEXT;
  14290. iFlag |= JX9_IO_OPEN_BINARY;
  14291. }else if( c == 't' || c == 'T' ){
  14292. iFlag &= ~JX9_IO_OPEN_BINARY;
  14293. iFlag |= JX9_IO_OPEN_TEXT;
  14294. }
  14295. zMode++;
  14296. }
  14297. return iFlag;
  14298. }
  14299. /*
  14300. * Initialize the IO private structure.
  14301. */
  14302. static void InitIOPrivate(jx9_vm *pVm, const jx9_io_stream *pStream, io_private *pOut)
  14303. {
  14304. pOut->pStream = pStream;
  14305. SyBlobInit(&pOut->sBuffer, &pVm->sAllocator);
  14306. pOut->nOfft = 0;
  14307. /* Set the magic number */
  14308. pOut->iMagic = IO_PRIVATE_MAGIC;
  14309. }
  14310. /*
  14311. * Release the IO private structure.
  14312. */
  14313. static void ReleaseIOPrivate(jx9_context *pCtx, io_private *pDev)
  14314. {
  14315. SyBlobRelease(&pDev->sBuffer);
  14316. pDev->iMagic = 0x2126; /* Invalid magic number so we can detetct misuse */
  14317. /* Release the whole structure */
  14318. jx9_context_free_chunk(pCtx, pDev);
  14319. }
  14320. /*
  14321. * Reset the IO private structure.
  14322. */
  14323. static void ResetIOPrivate(io_private *pDev)
  14324. {
  14325. SyBlobReset(&pDev->sBuffer);
  14326. pDev->nOfft = 0;
  14327. }
  14328. /* Forward declaration */
  14329. static int is_jx9_stream(const jx9_io_stream *pStream);
  14330. /*
  14331. * resource fopen(string $filename, string $mode [, bool $use_include_path = false[, resource $context ]])
  14332. * Open a file, a URL or any other IO stream.
  14333. * Parameters
  14334. * $filename
  14335. * If filename is of the form "scheme://...", it is assumed to be a URL and JX9 will search
  14336. * for a protocol handler (also known as a wrapper) for that scheme. If no scheme is given
  14337. * then a regular file is assumed.
  14338. * $mode
  14339. * The mode parameter specifies the type of access you require to the stream
  14340. * See the block comment associated with the StrModeToFlags() for the supported
  14341. * modes.
  14342. * $use_include_path
  14343. * You can use the optional second parameter and set it to
  14344. * TRUE, if you want to search for the file in the include_path, too.
  14345. * $context
  14346. * A context stream resource.
  14347. * Return
  14348. * File handle on success or FALSE on failure.
  14349. */
  14350. static int jx9Builtin_fopen(jx9_context *pCtx, int nArg, jx9_value **apArg)
  14351. {
  14352. const jx9_io_stream *pStream;
  14353. const char *zUri, *zMode;
  14354. jx9_value *pResource;
  14355. io_private *pDev;
  14356. int iLen, imLen;
  14357. int iOpenFlags;
  14358. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  14359. /* Missing/Invalid arguments, return FALSE */
  14360. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path or URL");
  14361. jx9_result_bool(pCtx, 0);
  14362. return JX9_OK;
  14363. }
  14364. /* Extract the URI and the desired access mode */
  14365. zUri = jx9_value_to_string(apArg[0], &iLen);
  14366. if( nArg > 1 ){
  14367. zMode = jx9_value_to_string(apArg[1], &imLen);
  14368. }else{
  14369. /* Set a default read-only mode */
  14370. zMode = "r";
  14371. imLen = (int)sizeof(char);
  14372. }
  14373. /* Try to extract a stream */
  14374. pStream = jx9VmGetStreamDevice(pCtx->pVm, &zUri, iLen);
  14375. if( pStream == 0 ){
  14376. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  14377. "No stream device is associated with the given URI(%s)", zUri);
  14378. jx9_result_bool(pCtx, 0);
  14379. return JX9_OK;
  14380. }
  14381. /* Allocate a new IO private instance */
  14382. pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE);
  14383. if( pDev == 0 ){
  14384. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
  14385. jx9_result_bool(pCtx, 0);
  14386. return JX9_OK;
  14387. }
  14388. pResource = 0;
  14389. if( nArg > 3 ){
  14390. pResource = apArg[3];
  14391. }else if( is_jx9_stream(pStream) ){
  14392. /* TICKET 1433-80: The jx9:// stream need a jx9_value to access the underlying
  14393. * virtual machine.
  14394. */
  14395. pResource = apArg[0];
  14396. }
  14397. /* Initialize the structure */
  14398. InitIOPrivate(pCtx->pVm, pStream, pDev);
  14399. /* Convert open mode to JX9 flags */
  14400. iOpenFlags = StrModeToFlags(pCtx, zMode, imLen);
  14401. /* Try to get a handle */
  14402. pDev->pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zUri, iOpenFlags,
  14403. nArg > 2 ? jx9_value_to_bool(apArg[2]) : FALSE, pResource, FALSE, 0);
  14404. if( pDev->pHandle == 0 ){
  14405. jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zUri);
  14406. jx9_result_bool(pCtx, 0);
  14407. jx9_context_free_chunk(pCtx, pDev);
  14408. return JX9_OK;
  14409. }
  14410. /* All done, return the io_private instance as a resource */
  14411. jx9_result_resource(pCtx, pDev);
  14412. return JX9_OK;
  14413. }
  14414. /*
  14415. * bool fclose(resource $handle)
  14416. * Closes an open file pointer
  14417. * Parameters
  14418. * $handle
  14419. * The file pointer.
  14420. * Return
  14421. * TRUE on success or FALSE on failure.
  14422. */
  14423. static int jx9Builtin_fclose(jx9_context *pCtx, int nArg, jx9_value **apArg)
  14424. {
  14425. const jx9_io_stream *pStream;
  14426. io_private *pDev;
  14427. jx9_vm *pVm;
  14428. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  14429. /* Missing/Invalid arguments, return FALSE */
  14430. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  14431. jx9_result_bool(pCtx, 0);
  14432. return JX9_OK;
  14433. }
  14434. /* Extract our private data */
  14435. pDev = (io_private *)jx9_value_to_resource(apArg[0]);
  14436. /* Make sure we are dealing with a valid io_private instance */
  14437. if( IO_PRIVATE_INVALID(pDev) ){
  14438. /*Expecting an IO handle */
  14439. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
  14440. jx9_result_bool(pCtx, 0);
  14441. return JX9_OK;
  14442. }
  14443. /* Point to the target IO stream device */
  14444. pStream = pDev->pStream;
  14445. if( pStream == 0 ){
  14446. jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
  14447. "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
  14448. jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
  14449. );
  14450. jx9_result_bool(pCtx, 0);
  14451. return JX9_OK;
  14452. }
  14453. /* Point to the VM that own this context */
  14454. pVm = pCtx->pVm;
  14455. /* TICKET 1433-62: Keep the STDIN/STDOUT/STDERR handles open */
  14456. if( pDev != pVm->pStdin && pDev != pVm->pStdout && pDev != pVm->pStderr ){
  14457. /* Perform the requested operation */
  14458. jx9StreamCloseHandle(pStream, pDev->pHandle);
  14459. /* Release the IO private structure */
  14460. ReleaseIOPrivate(pCtx, pDev);
  14461. /* Invalidate the resource handle */
  14462. jx9_value_release(apArg[0]);
  14463. }
  14464. /* Return TRUE */
  14465. jx9_result_bool(pCtx, 1);
  14466. return JX9_OK;
  14467. }
  14468. #if !defined(JX9_DISABLE_HASH_FUNC)
  14469. /*
  14470. * MD5/SHA1 digest consumer.
  14471. */
  14472. static int vfsHashConsumer(const void *pData, unsigned int nLen, void *pUserData)
  14473. {
  14474. /* Append hex chunk verbatim */
  14475. jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen);
  14476. return SXRET_OK;
  14477. }
  14478. /*
  14479. * string md5_file(string $uri[, bool $raw_output = false ])
  14480. * Calculates the md5 hash of a given file.
  14481. * Parameters
  14482. * $uri
  14483. * Target URI (file(/path/to/something) or URL(http://www.symisc.net/))
  14484. * $raw_output
  14485. * When TRUE, returns the digest in raw binary format with a length of 16.
  14486. * Return
  14487. * Return the MD5 digest on success or FALSE on failure.
  14488. */
  14489. static int jx9Builtin_md5_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
  14490. {
  14491. const jx9_io_stream *pStream;
  14492. unsigned char zDigest[16];
  14493. int raw_output = FALSE;
  14494. const char *zFile;
  14495. MD5Context sCtx;
  14496. char zBuf[8192];
  14497. void *pHandle;
  14498. jx9_int64 n;
  14499. int nLen;
  14500. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  14501. /* Missing/Invalid arguments, return FALSE */
  14502. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
  14503. jx9_result_bool(pCtx, 0);
  14504. return JX9_OK;
  14505. }
  14506. /* Extract the file path */
  14507. zFile = jx9_value_to_string(apArg[0], &nLen);
  14508. /* Point to the target IO stream device */
  14509. pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
  14510. if( pStream == 0 ){
  14511. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
  14512. jx9_result_bool(pCtx, 0);
  14513. return JX9_OK;
  14514. }
  14515. if( nArg > 1 ){
  14516. raw_output = jx9_value_to_bool(apArg[1]);
  14517. }
  14518. /* Try to open the file in read-only mode */
  14519. pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
  14520. if( pHandle == 0 ){
  14521. jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
  14522. jx9_result_bool(pCtx, 0);
  14523. return JX9_OK;
  14524. }
  14525. /* Init the MD5 context */
  14526. MD5Init(&sCtx);
  14527. /* Perform the requested operation */
  14528. for(;;){
  14529. n = pStream->xRead(pHandle, zBuf, sizeof(zBuf));
  14530. if( n < 1 ){
  14531. /* EOF or IO error, break immediately */
  14532. break;
  14533. }
  14534. MD5Update(&sCtx, (const unsigned char *)zBuf, (unsigned int)n);
  14535. }
  14536. /* Close the stream */
  14537. jx9StreamCloseHandle(pStream, pHandle);
  14538. /* Extract the digest */
  14539. MD5Final(zDigest, &sCtx);
  14540. if( raw_output ){
  14541. /* Output raw digest */
  14542. jx9_result_string(pCtx, (const char *)zDigest, sizeof(zDigest));
  14543. }else{
  14544. /* Perform a binary to hex conversion */
  14545. SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), vfsHashConsumer, pCtx);
  14546. }
  14547. return JX9_OK;
  14548. }
  14549. /*
  14550. * string sha1_file(string $uri[, bool $raw_output = false ])
  14551. * Calculates the SHA1 hash of a given file.
  14552. * Parameters
  14553. * $uri
  14554. * Target URI (file(/path/to/something) or URL(http://www.symisc.net/))
  14555. * $raw_output
  14556. * When TRUE, returns the digest in raw binary format with a length of 20.
  14557. * Return
  14558. * Return the SHA1 digest on success or FALSE on failure.
  14559. */
  14560. static int jx9Builtin_sha1_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
  14561. {
  14562. const jx9_io_stream *pStream;
  14563. unsigned char zDigest[20];
  14564. int raw_output = FALSE;
  14565. const char *zFile;
  14566. SHA1Context sCtx;
  14567. char zBuf[8192];
  14568. void *pHandle;
  14569. jx9_int64 n;
  14570. int nLen;
  14571. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  14572. /* Missing/Invalid arguments, return FALSE */
  14573. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
  14574. jx9_result_bool(pCtx, 0);
  14575. return JX9_OK;
  14576. }
  14577. /* Extract the file path */
  14578. zFile = jx9_value_to_string(apArg[0], &nLen);
  14579. /* Point to the target IO stream device */
  14580. pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
  14581. if( pStream == 0 ){
  14582. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
  14583. jx9_result_bool(pCtx, 0);
  14584. return JX9_OK;
  14585. }
  14586. if( nArg > 1 ){
  14587. raw_output = jx9_value_to_bool(apArg[1]);
  14588. }
  14589. /* Try to open the file in read-only mode */
  14590. pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
  14591. if( pHandle == 0 ){
  14592. jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
  14593. jx9_result_bool(pCtx, 0);
  14594. return JX9_OK;
  14595. }
  14596. /* Init the SHA1 context */
  14597. SHA1Init(&sCtx);
  14598. /* Perform the requested operation */
  14599. for(;;){
  14600. n = pStream->xRead(pHandle, zBuf, sizeof(zBuf));
  14601. if( n < 1 ){
  14602. /* EOF or IO error, break immediately */
  14603. break;
  14604. }
  14605. SHA1Update(&sCtx, (const unsigned char *)zBuf, (unsigned int)n);
  14606. }
  14607. /* Close the stream */
  14608. jx9StreamCloseHandle(pStream, pHandle);
  14609. /* Extract the digest */
  14610. SHA1Final(&sCtx, zDigest);
  14611. if( raw_output ){
  14612. /* Output raw digest */
  14613. jx9_result_string(pCtx, (const char *)zDigest, sizeof(zDigest));
  14614. }else{
  14615. /* Perform a binary to hex conversion */
  14616. SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), vfsHashConsumer, pCtx);
  14617. }
  14618. return JX9_OK;
  14619. }
  14620. #endif /* JX9_DISABLE_HASH_FUNC */
  14621. /*
  14622. * array parse_ini_file(string $filename[, bool $process_sections = false [, int $scanner_mode = INI_SCANNER_NORMAL ]] )
  14623. * Parse a configuration file.
  14624. * Parameters
  14625. * $filename
  14626. * The filename of the ini file being parsed.
  14627. * $process_sections
  14628. * By setting the process_sections parameter to TRUE, you get a multidimensional array
  14629. * with the section names and settings included.
  14630. * The default for process_sections is FALSE.
  14631. * $scanner_mode
  14632. * Can either be INI_SCANNER_NORMAL (default) or INI_SCANNER_RAW.
  14633. * If INI_SCANNER_RAW is supplied, then option values will not be parsed.
  14634. * Return
  14635. * The settings are returned as an associative array on success.
  14636. * Otherwise is returned.
  14637. */
  14638. static int jx9Builtin_parse_ini_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
  14639. {
  14640. const jx9_io_stream *pStream;
  14641. const char *zFile;
  14642. SyBlob sContents;
  14643. void *pHandle;
  14644. int nLen;
  14645. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  14646. /* Missing/Invalid arguments, return FALSE */
  14647. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
  14648. jx9_result_bool(pCtx, 0);
  14649. return JX9_OK;
  14650. }
  14651. /* Extract the file path */
  14652. zFile = jx9_value_to_string(apArg[0], &nLen);
  14653. /* Point to the target IO stream device */
  14654. pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
  14655. if( pStream == 0 ){
  14656. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
  14657. jx9_result_bool(pCtx, 0);
  14658. return JX9_OK;
  14659. }
  14660. /* Try to open the file in read-only mode */
  14661. pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
  14662. if( pHandle == 0 ){
  14663. jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
  14664. jx9_result_bool(pCtx, 0);
  14665. return JX9_OK;
  14666. }
  14667. SyBlobInit(&sContents, &pCtx->pVm->sAllocator);
  14668. /* Read the whole file */
  14669. jx9StreamReadWholeFile(pHandle, pStream, &sContents);
  14670. if( SyBlobLength(&sContents) < 1 ){
  14671. /* Empty buffer, return FALSE */
  14672. jx9_result_bool(pCtx, 0);
  14673. }else{
  14674. /* Process the raw INI buffer */
  14675. jx9ParseIniString(pCtx, (const char *)SyBlobData(&sContents), SyBlobLength(&sContents),
  14676. nArg > 1 ? jx9_value_to_bool(apArg[1]) : 0);
  14677. }
  14678. /* Close the stream */
  14679. jx9StreamCloseHandle(pStream, pHandle);
  14680. /* Release the working buffer */
  14681. SyBlobRelease(&sContents);
  14682. return JX9_OK;
  14683. }
  14684. /*
  14685. * Section:
  14686. * ZIP archive processing.
  14687. * Authors:
  14688. * Symisc Systems, devel@symisc.net.
  14689. * Copyright (C) Symisc Systems, http://jx9.symisc.net
  14690. * Status:
  14691. * Stable.
  14692. */
  14693. typedef struct zip_raw_data zip_raw_data;
  14694. struct zip_raw_data
  14695. {
  14696. int iType; /* Where the raw data is stored */
  14697. union raw_data{
  14698. struct mmap_data{
  14699. void *pMap; /* Memory mapped data */
  14700. jx9_int64 nSize; /* Map size */
  14701. const jx9_vfs *pVfs; /* Underlying vfs */
  14702. }mmap;
  14703. SyBlob sBlob; /* Memory buffer */
  14704. }raw;
  14705. };
  14706. #define ZIP_RAW_DATA_MMAPED 1 /* Memory mapped ZIP raw data */
  14707. #define ZIP_RAW_DATA_MEMBUF 2 /* ZIP raw data stored in a dynamically
  14708. * allocated memory chunk.
  14709. */
  14710. /*
  14711. * mixed zip_open(string $filename)
  14712. * Opens a new zip archive for reading.
  14713. * Parameters
  14714. * $filename
  14715. * The file name of the ZIP archive to open.
  14716. * Return
  14717. * A resource handle for later use with zip_read() and zip_close() or FALSE on failure.
  14718. */
  14719. static int jx9Builtin_zip_open(jx9_context *pCtx, int nArg, jx9_value **apArg)
  14720. {
  14721. const jx9_io_stream *pStream;
  14722. SyArchive *pArchive;
  14723. zip_raw_data *pRaw;
  14724. const char *zFile;
  14725. SyBlob *pContents;
  14726. void *pHandle;
  14727. int nLen;
  14728. sxi32 rc;
  14729. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  14730. /* Missing/Invalid arguments, return FALSE */
  14731. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
  14732. jx9_result_bool(pCtx, 0);
  14733. return JX9_OK;
  14734. }
  14735. /* Extract the file path */
  14736. zFile = jx9_value_to_string(apArg[0], &nLen);
  14737. /* Point to the target IO stream device */
  14738. pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
  14739. if( pStream == 0 ){
  14740. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
  14741. jx9_result_bool(pCtx, 0);
  14742. return JX9_OK;
  14743. }
  14744. /* Create an in-memory archive */
  14745. pArchive = (SyArchive *)jx9_context_alloc_chunk(pCtx, sizeof(SyArchive)+sizeof(zip_raw_data), TRUE, FALSE);
  14746. if( pArchive == 0 ){
  14747. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "JX9 is running out of memory");
  14748. jx9_result_bool(pCtx, 0);
  14749. return JX9_OK;
  14750. }
  14751. pRaw = (zip_raw_data *)&pArchive[1];
  14752. /* Initialize the archive */
  14753. SyArchiveInit(pArchive, &pCtx->pVm->sAllocator, 0, 0);
  14754. /* Extract the default stream */
  14755. if( pStream == pCtx->pVm->pDefStream /* file:// stream*/){
  14756. const jx9_vfs *pVfs;
  14757. /* Try to get a memory view of the whole file since ZIP files
  14758. * tends to be very big this days, this is a huge performance win.
  14759. */
  14760. pVfs = jx9ExportBuiltinVfs();
  14761. if( pVfs && pVfs->xMmap ){
  14762. rc = pVfs->xMmap(zFile, &pRaw->raw.mmap.pMap, &pRaw->raw.mmap.nSize);
  14763. if( rc == JX9_OK ){
  14764. /* Nice, Extract the whole archive */
  14765. rc = SyZipExtractFromBuf(pArchive, (const char *)pRaw->raw.mmap.pMap, (sxu32)pRaw->raw.mmap.nSize);
  14766. if( rc != SXRET_OK ){
  14767. if( pVfs->xUnmap ){
  14768. pVfs->xUnmap(pRaw->raw.mmap.pMap, pRaw->raw.mmap.nSize);
  14769. }
  14770. /* Release the allocated chunk */
  14771. jx9_context_free_chunk(pCtx, pArchive);
  14772. /* Something goes wrong with this ZIP archive, return FALSE */
  14773. jx9_result_bool(pCtx, 0);
  14774. return JX9_OK;
  14775. }
  14776. /* Archive successfully opened */
  14777. pRaw->iType = ZIP_RAW_DATA_MMAPED;
  14778. pRaw->raw.mmap.pVfs = pVfs;
  14779. goto success;
  14780. }
  14781. }
  14782. /* FALL THROUGH */
  14783. }
  14784. /* Try to open the file in read-only mode */
  14785. pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
  14786. if( pHandle == 0 ){
  14787. jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
  14788. jx9_result_bool(pCtx, 0);
  14789. return JX9_OK;
  14790. }
  14791. pContents = &pRaw->raw.sBlob;
  14792. SyBlobInit(pContents, &pCtx->pVm->sAllocator);
  14793. /* Read the whole file */
  14794. jx9StreamReadWholeFile(pHandle, pStream, pContents);
  14795. /* Assume an invalid ZIP file */
  14796. rc = SXERR_INVALID;
  14797. if( SyBlobLength(pContents) > 0 ){
  14798. /* Extract archive entries */
  14799. rc = SyZipExtractFromBuf(pArchive, (const char *)SyBlobData(pContents), SyBlobLength(pContents));
  14800. }
  14801. pRaw->iType = ZIP_RAW_DATA_MEMBUF;
  14802. /* Close the stream */
  14803. jx9StreamCloseHandle(pStream, pHandle);
  14804. if( rc != SXRET_OK ){
  14805. /* Release the working buffer */
  14806. SyBlobRelease(pContents);
  14807. /* Release the allocated chunk */
  14808. jx9_context_free_chunk(pCtx, pArchive);
  14809. /* Something goes wrong with this ZIP archive, return FALSE */
  14810. jx9_result_bool(pCtx, 0);
  14811. return JX9_OK;
  14812. }
  14813. success:
  14814. /* Reset the loop cursor */
  14815. SyArchiveResetLoopCursor(pArchive);
  14816. /* Return the in-memory archive as a resource handle */
  14817. jx9_result_resource(pCtx, pArchive);
  14818. return JX9_OK;
  14819. }
  14820. /*
  14821. * void zip_close(resource $zip)
  14822. * Close an in-memory ZIP archive.
  14823. * Parameters
  14824. * $zip
  14825. * A ZIP file previously opened with zip_open().
  14826. * Return
  14827. * null.
  14828. */
  14829. static int jx9Builtin_zip_close(jx9_context *pCtx, int nArg, jx9_value **apArg)
  14830. {
  14831. SyArchive *pArchive;
  14832. zip_raw_data *pRaw;
  14833. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  14834. /* Missing/Invalid arguments */
  14835. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
  14836. return JX9_OK;
  14837. }
  14838. /* Point to the in-memory archive */
  14839. pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]);
  14840. /* Make sure we are dealing with a valid ZIP archive */
  14841. if( SXARCH_INVALID(pArchive) ){
  14842. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
  14843. return JX9_OK;
  14844. }
  14845. /* Release the archive */
  14846. SyArchiveRelease(pArchive);
  14847. pRaw = (zip_raw_data *)&pArchive[1];
  14848. if( pRaw->iType == ZIP_RAW_DATA_MEMBUF ){
  14849. SyBlobRelease(&pRaw->raw.sBlob);
  14850. }else{
  14851. const jx9_vfs *pVfs = pRaw->raw.mmap.pVfs;
  14852. if( pVfs->xUnmap ){
  14853. /* Unmap the memory view */
  14854. pVfs->xUnmap(pRaw->raw.mmap.pMap, pRaw->raw.mmap.nSize);
  14855. }
  14856. }
  14857. /* Release the memory chunk */
  14858. jx9_context_free_chunk(pCtx, pArchive);
  14859. return JX9_OK;
  14860. }
  14861. /*
  14862. * mixed zip_read(resource $zip)
  14863. * Reads the next entry from an in-memory ZIP archive.
  14864. * Parameters
  14865. * $zip
  14866. * A ZIP file previously opened with zip_open().
  14867. * Return
  14868. * A directory entry resource for later use with the zip_entry_... functions
  14869. * or FALSE if there are no more entries to read, or an error code if an error occurred.
  14870. */
  14871. static int jx9Builtin_zip_read(jx9_context *pCtx, int nArg, jx9_value **apArg)
  14872. {
  14873. SyArchiveEntry *pNext = 0; /* cc warning */
  14874. SyArchive *pArchive;
  14875. sxi32 rc;
  14876. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  14877. /* Missing/Invalid arguments */
  14878. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
  14879. /* return FALSE */
  14880. jx9_result_bool(pCtx, 0);
  14881. return JX9_OK;
  14882. }
  14883. /* Point to the in-memory archive */
  14884. pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]);
  14885. /* Make sure we are dealing with a valid ZIP archive */
  14886. if( SXARCH_INVALID(pArchive) ){
  14887. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
  14888. /* return FALSE */
  14889. jx9_result_bool(pCtx, 0);
  14890. return JX9_OK;
  14891. }
  14892. /* Extract the next entry */
  14893. rc = SyArchiveGetNextEntry(pArchive, &pNext);
  14894. if( rc != SXRET_OK ){
  14895. /* No more entries in the central directory, return FALSE */
  14896. jx9_result_bool(pCtx, 0);
  14897. }else{
  14898. /* Return as a resource handle */
  14899. jx9_result_resource(pCtx, pNext);
  14900. /* Point to the ZIP raw data */
  14901. pNext->pUserData = (void *)&pArchive[1];
  14902. }
  14903. return JX9_OK;
  14904. }
  14905. /*
  14906. * bool zip_entry_open(resource $zip, resource $zip_entry[, string $mode ])
  14907. * Open a directory entry for reading
  14908. * Parameters
  14909. * $zip
  14910. * A ZIP file previously opened with zip_open().
  14911. * $zip_entry
  14912. * A directory entry returned by zip_read().
  14913. * $mode
  14914. * Not used
  14915. * Return
  14916. * A directory entry resource for later use with the zip_entry_... functions
  14917. * or FALSE if there are no more entries to read, or an error code if an error occurred.
  14918. */
  14919. static int jx9Builtin_zip_entry_open(jx9_context *pCtx, int nArg, jx9_value **apArg)
  14920. {
  14921. SyArchiveEntry *pEntry;
  14922. SyArchive *pArchive;
  14923. if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_resource(apArg[1]) ){
  14924. /* Missing/Invalid arguments */
  14925. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
  14926. /* return FALSE */
  14927. jx9_result_bool(pCtx, 0);
  14928. return JX9_OK;
  14929. }
  14930. /* Point to the in-memory archive */
  14931. pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]);
  14932. /* Make sure we are dealing with a valid ZIP archive */
  14933. if( SXARCH_INVALID(pArchive) ){
  14934. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
  14935. /* return FALSE */
  14936. jx9_result_bool(pCtx, 0);
  14937. return JX9_OK;
  14938. }
  14939. /* Make sure we are dealing with a valid ZIP archive entry */
  14940. pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[1]);
  14941. if( SXARCH_ENTRY_INVALID(pEntry) ){
  14942. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
  14943. /* return FALSE */
  14944. jx9_result_bool(pCtx, 0);
  14945. return JX9_OK;
  14946. }
  14947. /* All done. Actually this function is a no-op, return TRUE */
  14948. jx9_result_bool(pCtx, 1);
  14949. return JX9_OK;
  14950. }
  14951. /*
  14952. * bool zip_entry_close(resource $zip_entry)
  14953. * Close a directory entry.
  14954. * Parameters
  14955. * $zip_entry
  14956. * A directory entry returned by zip_read().
  14957. * Return
  14958. * Returns TRUE on success or FALSE on failure.
  14959. */
  14960. static int jx9Builtin_zip_entry_close(jx9_context *pCtx, int nArg, jx9_value **apArg)
  14961. {
  14962. SyArchiveEntry *pEntry;
  14963. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  14964. /* Missing/Invalid arguments */
  14965. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
  14966. /* return FALSE */
  14967. jx9_result_bool(pCtx, 0);
  14968. return JX9_OK;
  14969. }
  14970. /* Make sure we are dealing with a valid ZIP archive entry */
  14971. pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
  14972. if( SXARCH_ENTRY_INVALID(pEntry) ){
  14973. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
  14974. /* return FALSE */
  14975. jx9_result_bool(pCtx, 0);
  14976. return JX9_OK;
  14977. }
  14978. /* Reset the read cursor */
  14979. pEntry->nReadCount = 0;
  14980. /*All done. Actually this function is a no-op, return TRUE */
  14981. jx9_result_bool(pCtx, 1);
  14982. return JX9_OK;
  14983. }
  14984. /*
  14985. * string zip_entry_name(resource $zip_entry)
  14986. * Retrieve the name of a directory entry.
  14987. * Parameters
  14988. * $zip_entry
  14989. * A directory entry returned by zip_read().
  14990. * Return
  14991. * The name of the directory entry.
  14992. */
  14993. static int jx9Builtin_zip_entry_name(jx9_context *pCtx, int nArg, jx9_value **apArg)
  14994. {
  14995. SyArchiveEntry *pEntry;
  14996. SyString *pName;
  14997. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  14998. /* Missing/Invalid arguments */
  14999. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
  15000. /* return FALSE */
  15001. jx9_result_bool(pCtx, 0);
  15002. return JX9_OK;
  15003. }
  15004. /* Make sure we are dealing with a valid ZIP archive entry */
  15005. pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
  15006. if( SXARCH_ENTRY_INVALID(pEntry) ){
  15007. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
  15008. /* return FALSE */
  15009. jx9_result_bool(pCtx, 0);
  15010. return JX9_OK;
  15011. }
  15012. /* Return entry name */
  15013. pName = &pEntry->sFileName;
  15014. jx9_result_string(pCtx, pName->zString, (int)pName->nByte);
  15015. return JX9_OK;
  15016. }
  15017. /*
  15018. * int64 zip_entry_filesize(resource $zip_entry)
  15019. * Retrieve the actual file size of a directory entry.
  15020. * Parameters
  15021. * $zip_entry
  15022. * A directory entry returned by zip_read().
  15023. * Return
  15024. * The size of the directory entry.
  15025. */
  15026. static int jx9Builtin_zip_entry_filesize(jx9_context *pCtx, int nArg, jx9_value **apArg)
  15027. {
  15028. SyArchiveEntry *pEntry;
  15029. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  15030. /* Missing/Invalid arguments */
  15031. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
  15032. /* return FALSE */
  15033. jx9_result_bool(pCtx, 0);
  15034. return JX9_OK;
  15035. }
  15036. /* Make sure we are dealing with a valid ZIP archive entry */
  15037. pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
  15038. if( SXARCH_ENTRY_INVALID(pEntry) ){
  15039. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
  15040. /* return FALSE */
  15041. jx9_result_bool(pCtx, 0);
  15042. return JX9_OK;
  15043. }
  15044. /* Return entry size */
  15045. jx9_result_int64(pCtx, (jx9_int64)pEntry->nByte);
  15046. return JX9_OK;
  15047. }
  15048. /*
  15049. * int64 zip_entry_compressedsize(resource $zip_entry)
  15050. * Retrieve the compressed size of a directory entry.
  15051. * Parameters
  15052. * $zip_entry
  15053. * A directory entry returned by zip_read().
  15054. * Return
  15055. * The compressed size.
  15056. */
  15057. static int jx9Builtin_zip_entry_compressedsize(jx9_context *pCtx, int nArg, jx9_value **apArg)
  15058. {
  15059. SyArchiveEntry *pEntry;
  15060. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  15061. /* Missing/Invalid arguments */
  15062. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
  15063. /* return FALSE */
  15064. jx9_result_bool(pCtx, 0);
  15065. return JX9_OK;
  15066. }
  15067. /* Make sure we are dealing with a valid ZIP archive entry */
  15068. pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
  15069. if( SXARCH_ENTRY_INVALID(pEntry) ){
  15070. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
  15071. /* return FALSE */
  15072. jx9_result_bool(pCtx, 0);
  15073. return JX9_OK;
  15074. }
  15075. /* Return entry compressed size */
  15076. jx9_result_int64(pCtx, (jx9_int64)pEntry->nByteCompr);
  15077. return JX9_OK;
  15078. }
  15079. /*
  15080. * string zip_entry_read(resource $zip_entry[, int $length])
  15081. * Reads from an open directory entry.
  15082. * Parameters
  15083. * $zip_entry
  15084. * A directory entry returned by zip_read().
  15085. * $length
  15086. * The number of bytes to return. If not specified, this function
  15087. * will attempt to read 1024 bytes.
  15088. * Return
  15089. * Returns the data read, or FALSE if the end of the file is reached.
  15090. */
  15091. static int jx9Builtin_zip_entry_read(jx9_context *pCtx, int nArg, jx9_value **apArg)
  15092. {
  15093. SyArchiveEntry *pEntry;
  15094. zip_raw_data *pRaw;
  15095. const char *zData;
  15096. int iLength;
  15097. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  15098. /* Missing/Invalid arguments */
  15099. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
  15100. /* return FALSE */
  15101. jx9_result_bool(pCtx, 0);
  15102. return JX9_OK;
  15103. }
  15104. /* Make sure we are dealing with a valid ZIP archive entry */
  15105. pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
  15106. if( SXARCH_ENTRY_INVALID(pEntry) ){
  15107. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
  15108. /* return FALSE */
  15109. jx9_result_bool(pCtx, 0);
  15110. return JX9_OK;
  15111. }
  15112. zData = 0;
  15113. if( pEntry->nReadCount >= pEntry->nByteCompr ){
  15114. /* No more data to read, return FALSE */
  15115. jx9_result_bool(pCtx, 0);
  15116. return JX9_OK;
  15117. }
  15118. /* Set a default read length */
  15119. iLength = 1024;
  15120. if( nArg > 1 ){
  15121. iLength = jx9_value_to_int(apArg[1]);
  15122. if( iLength < 1 ){
  15123. iLength = 1024;
  15124. }
  15125. }
  15126. if( (sxu32)iLength > pEntry->nByteCompr - pEntry->nReadCount ){
  15127. iLength = (int)(pEntry->nByteCompr - pEntry->nReadCount);
  15128. }
  15129. /* Return the entry contents */
  15130. pRaw = (zip_raw_data *)pEntry->pUserData;
  15131. if( pRaw->iType == ZIP_RAW_DATA_MEMBUF ){
  15132. zData = (const char *)SyBlobDataAt(&pRaw->raw.sBlob, (pEntry->nOfft+pEntry->nReadCount));
  15133. }else{
  15134. const char *zMap = (const char *)pRaw->raw.mmap.pMap;
  15135. /* Memory mmaped chunk */
  15136. zData = &zMap[pEntry->nOfft+pEntry->nReadCount];
  15137. }
  15138. /* Increment the read counter */
  15139. pEntry->nReadCount += iLength;
  15140. /* Return the raw data */
  15141. jx9_result_string(pCtx, zData, iLength);
  15142. return JX9_OK;
  15143. }
  15144. /*
  15145. * bool zip_entry_reset_read_cursor(resource $zip_entry)
  15146. * Reset the read cursor of an open directory entry.
  15147. * Parameters
  15148. * $zip_entry
  15149. * A directory entry returned by zip_read().
  15150. * Return
  15151. * TRUE on success, FALSE on failure.
  15152. */
  15153. static int jx9Builtin_zip_entry_reset_read_cursor(jx9_context *pCtx, int nArg, jx9_value **apArg)
  15154. {
  15155. SyArchiveEntry *pEntry;
  15156. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  15157. /* Missing/Invalid arguments */
  15158. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
  15159. /* return FALSE */
  15160. jx9_result_bool(pCtx, 0);
  15161. return JX9_OK;
  15162. }
  15163. /* Make sure we are dealing with a valid ZIP archive entry */
  15164. pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
  15165. if( SXARCH_ENTRY_INVALID(pEntry) ){
  15166. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
  15167. /* return FALSE */
  15168. jx9_result_bool(pCtx, 0);
  15169. return JX9_OK;
  15170. }
  15171. /* Reset the cursor */
  15172. pEntry->nReadCount = 0;
  15173. /* Return TRUE */
  15174. jx9_result_bool(pCtx, 1);
  15175. return JX9_OK;
  15176. }
  15177. /*
  15178. * string zip_entry_compressionmethod(resource $zip_entry)
  15179. * Retrieve the compression method of a directory entry.
  15180. * Parameters
  15181. * $zip_entry
  15182. * A directory entry returned by zip_read().
  15183. * Return
  15184. * The compression method on success or FALSE on failure.
  15185. */
  15186. static int jx9Builtin_zip_entry_compressionmethod(jx9_context *pCtx, int nArg, jx9_value **apArg)
  15187. {
  15188. SyArchiveEntry *pEntry;
  15189. if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
  15190. /* Missing/Invalid arguments */
  15191. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
  15192. /* return FALSE */
  15193. jx9_result_bool(pCtx, 0);
  15194. return JX9_OK;
  15195. }
  15196. /* Make sure we are dealing with a valid ZIP archive entry */
  15197. pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
  15198. if( SXARCH_ENTRY_INVALID(pEntry) ){
  15199. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
  15200. /* return FALSE */
  15201. jx9_result_bool(pCtx, 0);
  15202. return JX9_OK;
  15203. }
  15204. switch(pEntry->nComprMeth){
  15205. case 0:
  15206. /* No compression;entry is stored */
  15207. jx9_result_string(pCtx, "stored", (int)sizeof("stored")-1);
  15208. break;
  15209. case 8:
  15210. /* Entry is deflated (Default compression algorithm) */
  15211. jx9_result_string(pCtx, "deflate", (int)sizeof("deflate")-1);
  15212. break;
  15213. /* Exotic compression algorithms */
  15214. case 1:
  15215. jx9_result_string(pCtx, "shrunk", (int)sizeof("shrunk")-1);
  15216. break;
  15217. case 2:
  15218. case 3:
  15219. case 4:
  15220. case 5:
  15221. /* Entry is reduced */
  15222. jx9_result_string(pCtx, "reduced", (int)sizeof("reduced")-1);
  15223. break;
  15224. case 6:
  15225. /* Entry is imploded */
  15226. jx9_result_string(pCtx, "implode", (int)sizeof("implode")-1);
  15227. break;
  15228. default:
  15229. jx9_result_string(pCtx, "unknown", (int)sizeof("unknown")-1);
  15230. break;
  15231. }
  15232. return JX9_OK;
  15233. }
  15234. #endif /* #ifndef JX9_DISABLE_BUILTIN_FUNC*/
  15235. /* NULL VFS [i.e: a no-op VFS]*/
  15236. static const jx9_vfs null_vfs = {
  15237. "null_vfs",
  15238. JX9_VFS_VERSION,
  15239. 0, /* int (*xChdir)(const char *) */
  15240. 0, /* int (*xChroot)(const char *); */
  15241. 0, /* int (*xGetcwd)(jx9_context *) */
  15242. 0, /* int (*xMkdir)(const char *, int, int) */
  15243. 0, /* int (*xRmdir)(const char *) */
  15244. 0, /* int (*xIsdir)(const char *) */
  15245. 0, /* int (*xRename)(const char *, const char *) */
  15246. 0, /*int (*xRealpath)(const char *, jx9_context *)*/
  15247. 0, /* int (*xSleep)(unsigned int) */
  15248. 0, /* int (*xUnlink)(const char *) */
  15249. 0, /* int (*xFileExists)(const char *) */
  15250. 0, /*int (*xChmod)(const char *, int)*/
  15251. 0, /*int (*xChown)(const char *, const char *)*/
  15252. 0, /*int (*xChgrp)(const char *, const char *)*/
  15253. 0, /* jx9_int64 (*xFreeSpace)(const char *) */
  15254. 0, /* jx9_int64 (*xTotalSpace)(const char *) */
  15255. 0, /* jx9_int64 (*xFileSize)(const char *) */
  15256. 0, /* jx9_int64 (*xFileAtime)(const char *) */
  15257. 0, /* jx9_int64 (*xFileMtime)(const char *) */
  15258. 0, /* jx9_int64 (*xFileCtime)(const char *) */
  15259. 0, /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
  15260. 0, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
  15261. 0, /* int (*xIsfile)(const char *) */
  15262. 0, /* int (*xIslink)(const char *) */
  15263. 0, /* int (*xReadable)(const char *) */
  15264. 0, /* int (*xWritable)(const char *) */
  15265. 0, /* int (*xExecutable)(const char *) */
  15266. 0, /* int (*xFiletype)(const char *, jx9_context *) */
  15267. 0, /* int (*xGetenv)(const char *, jx9_context *) */
  15268. 0, /* int (*xSetenv)(const char *, const char *) */
  15269. 0, /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
  15270. 0, /* int (*xMmap)(const char *, void **, jx9_int64 *) */
  15271. 0, /* void (*xUnmap)(void *, jx9_int64); */
  15272. 0, /* int (*xLink)(const char *, const char *, int) */
  15273. 0, /* int (*xUmask)(int) */
  15274. 0, /* void (*xTempDir)(jx9_context *) */
  15275. 0, /* unsigned int (*xProcessId)(void) */
  15276. 0, /* int (*xUid)(void) */
  15277. 0, /* int (*xGid)(void) */
  15278. 0, /* void (*xUsername)(jx9_context *) */
  15279. 0 /* int (*xExec)(const char *, jx9_context *) */
  15280. };
  15281. #ifndef JX9_DISABLE_BUILTIN_FUNC
  15282. #ifndef JX9_DISABLE_DISK_IO
  15283. #ifdef __WINNT__
  15284. /*
  15285. * Windows VFS implementation for the JX9 engine.
  15286. * Authors:
  15287. * Symisc Systems, devel@symisc.net.
  15288. * Copyright (C) Symisc Systems, http://jx9.symisc.net
  15289. * Status:
  15290. * Stable.
  15291. */
  15292. /* What follows here is code that is specific to windows systems. */
  15293. #include <Windows.h>
  15294. /*
  15295. ** Convert a UTF-8 string to microsoft unicode (UTF-16?).
  15296. **
  15297. ** Space to hold the returned string is obtained from HeapAlloc().
  15298. ** Taken from the sqlite3 source tree
  15299. ** status: Public Domain
  15300. */
  15301. static WCHAR *utf8ToUnicode(const char *zFilename){
  15302. int nChar;
  15303. WCHAR *zWideFilename;
  15304. nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, 0, 0);
  15305. zWideFilename = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, nChar*sizeof(zWideFilename[0]));
  15306. if( zWideFilename == 0 ){
  15307. return 0;
  15308. }
  15309. nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar);
  15310. if( nChar==0 ){
  15311. HeapFree(GetProcessHeap(), 0, zWideFilename);
  15312. return 0;
  15313. }
  15314. return zWideFilename;
  15315. }
  15316. /*
  15317. ** Convert a UTF-8 filename into whatever form the underlying
  15318. ** operating system wants filenames in.Space to hold the result
  15319. ** is obtained from HeapAlloc() and must be freed by the calling
  15320. ** function.
  15321. ** Taken from the sqlite3 source tree
  15322. ** status: Public Domain
  15323. */
  15324. static void *convertUtf8Filename(const char *zFilename){
  15325. void *zConverted;
  15326. zConverted = utf8ToUnicode(zFilename);
  15327. return zConverted;
  15328. }
  15329. /*
  15330. ** Convert microsoft unicode to UTF-8. Space to hold the returned string is
  15331. ** obtained from HeapAlloc().
  15332. ** Taken from the sqlite3 source tree
  15333. ** status: Public Domain
  15334. */
  15335. static char *unicodeToUtf8(const WCHAR *zWideFilename){
  15336. char *zFilename;
  15337. int nByte;
  15338. nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0);
  15339. zFilename = (char *)HeapAlloc(GetProcessHeap(), 0, nByte);
  15340. if( zFilename == 0 ){
  15341. return 0;
  15342. }
  15343. nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, 0, 0);
  15344. if( nByte == 0 ){
  15345. HeapFree(GetProcessHeap(), 0, zFilename);
  15346. return 0;
  15347. }
  15348. return zFilename;
  15349. }
  15350. /* int (*xchdir)(const char *) */
  15351. static int WinVfs_chdir(const char *zPath)
  15352. {
  15353. void * pConverted;
  15354. BOOL rc;
  15355. pConverted = convertUtf8Filename(zPath);
  15356. if( pConverted == 0 ){
  15357. return -1;
  15358. }
  15359. rc = SetCurrentDirectoryW((LPCWSTR)pConverted);
  15360. HeapFree(GetProcessHeap(), 0, pConverted);
  15361. return rc ? JX9_OK : -1;
  15362. }
  15363. /* int (*xGetcwd)(jx9_context *) */
  15364. static int WinVfs_getcwd(jx9_context *pCtx)
  15365. {
  15366. WCHAR zDir[2048];
  15367. char *zConverted;
  15368. DWORD rc;
  15369. /* Get the current directory */
  15370. rc = GetCurrentDirectoryW(sizeof(zDir), zDir);
  15371. if( rc < 1 ){
  15372. return -1;
  15373. }
  15374. zConverted = unicodeToUtf8(zDir);
  15375. if( zConverted == 0 ){
  15376. return -1;
  15377. }
  15378. jx9_result_string(pCtx, zConverted, -1/*Compute length automatically*/); /* Will make it's own copy */
  15379. HeapFree(GetProcessHeap(), 0, zConverted);
  15380. return JX9_OK;
  15381. }
  15382. /* int (*xMkdir)(const char *, int, int) */
  15383. static int WinVfs_mkdir(const char *zPath, int mode, int recursive)
  15384. {
  15385. void * pConverted;
  15386. BOOL rc;
  15387. pConverted = convertUtf8Filename(zPath);
  15388. if( pConverted == 0 ){
  15389. return -1;
  15390. }
  15391. mode= 0; /* MSVC warning */
  15392. recursive = 0;
  15393. rc = CreateDirectoryW((LPCWSTR)pConverted, 0);
  15394. HeapFree(GetProcessHeap(), 0, pConverted);
  15395. return rc ? JX9_OK : -1;
  15396. }
  15397. /* int (*xRmdir)(const char *) */
  15398. static int WinVfs_rmdir(const char *zPath)
  15399. {
  15400. void * pConverted;
  15401. BOOL rc;
  15402. pConverted = convertUtf8Filename(zPath);
  15403. if( pConverted == 0 ){
  15404. return -1;
  15405. }
  15406. rc = RemoveDirectoryW((LPCWSTR)pConverted);
  15407. HeapFree(GetProcessHeap(), 0, pConverted);
  15408. return rc ? JX9_OK : -1;
  15409. }
  15410. /* int (*xIsdir)(const char *) */
  15411. static int WinVfs_isdir(const char *zPath)
  15412. {
  15413. void * pConverted;
  15414. DWORD dwAttr;
  15415. pConverted = convertUtf8Filename(zPath);
  15416. if( pConverted == 0 ){
  15417. return -1;
  15418. }
  15419. dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
  15420. HeapFree(GetProcessHeap(), 0, pConverted);
  15421. if( dwAttr == INVALID_FILE_ATTRIBUTES ){
  15422. return -1;
  15423. }
  15424. return (dwAttr & FILE_ATTRIBUTE_DIRECTORY) ? JX9_OK : -1;
  15425. }
  15426. /* int (*xRename)(const char *, const char *) */
  15427. static int WinVfs_Rename(const char *zOld, const char *zNew)
  15428. {
  15429. void *pOld, *pNew;
  15430. BOOL rc = 0;
  15431. pOld = convertUtf8Filename(zOld);
  15432. if( pOld == 0 ){
  15433. return -1;
  15434. }
  15435. pNew = convertUtf8Filename(zNew);
  15436. if( pNew ){
  15437. rc = MoveFileW((LPCWSTR)pOld, (LPCWSTR)pNew);
  15438. }
  15439. HeapFree(GetProcessHeap(), 0, pOld);
  15440. if( pNew ){
  15441. HeapFree(GetProcessHeap(), 0, pNew);
  15442. }
  15443. return rc ? JX9_OK : - 1;
  15444. }
  15445. /* int (*xRealpath)(const char *, jx9_context *) */
  15446. static int WinVfs_Realpath(const char *zPath, jx9_context *pCtx)
  15447. {
  15448. WCHAR zTemp[2048];
  15449. void *pPath;
  15450. char *zReal;
  15451. DWORD n;
  15452. pPath = convertUtf8Filename(zPath);
  15453. if( pPath == 0 ){
  15454. return -1;
  15455. }
  15456. n = GetFullPathNameW((LPCWSTR)pPath, 0, 0, 0);
  15457. if( n > 0 ){
  15458. if( n >= sizeof(zTemp) ){
  15459. n = sizeof(zTemp) - 1;
  15460. }
  15461. GetFullPathNameW((LPCWSTR)pPath, n, zTemp, 0);
  15462. }
  15463. HeapFree(GetProcessHeap(), 0, pPath);
  15464. if( !n ){
  15465. return -1;
  15466. }
  15467. zReal = unicodeToUtf8(zTemp);
  15468. if( zReal == 0 ){
  15469. return -1;
  15470. }
  15471. jx9_result_string(pCtx, zReal, -1); /* Will make it's own copy */
  15472. HeapFree(GetProcessHeap(), 0, zReal);
  15473. return JX9_OK;
  15474. }
  15475. /* int (*xSleep)(unsigned int) */
  15476. static int WinVfs_Sleep(unsigned int uSec)
  15477. {
  15478. Sleep(uSec/1000/*uSec per Millisec */);
  15479. return JX9_OK;
  15480. }
  15481. /* int (*xUnlink)(const char *) */
  15482. static int WinVfs_unlink(const char *zPath)
  15483. {
  15484. void * pConverted;
  15485. BOOL rc;
  15486. pConverted = convertUtf8Filename(zPath);
  15487. if( pConverted == 0 ){
  15488. return -1;
  15489. }
  15490. rc = DeleteFileW((LPCWSTR)pConverted);
  15491. HeapFree(GetProcessHeap(), 0, pConverted);
  15492. return rc ? JX9_OK : - 1;
  15493. }
  15494. /* jx9_int64 (*xFreeSpace)(const char *) */
  15495. static jx9_int64 WinVfs_DiskFreeSpace(const char *zPath)
  15496. {
  15497. #ifdef _WIN32_WCE
  15498. /* GetDiskFreeSpace is not supported under WINCE */
  15499. SXUNUSED(zPath);
  15500. return 0;
  15501. #else
  15502. DWORD dwSectPerClust, dwBytesPerSect, dwFreeClusters, dwTotalClusters;
  15503. void * pConverted;
  15504. WCHAR *p;
  15505. BOOL rc;
  15506. pConverted = convertUtf8Filename(zPath);
  15507. if( pConverted == 0 ){
  15508. return 0;
  15509. }
  15510. p = (WCHAR *)pConverted;
  15511. for(;*p;p++){
  15512. if( *p == '\\' || *p == '/'){
  15513. *p = '\0';
  15514. break;
  15515. }
  15516. }
  15517. rc = GetDiskFreeSpaceW((LPCWSTR)pConverted, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters);
  15518. if( !rc ){
  15519. return 0;
  15520. }
  15521. return (jx9_int64)dwFreeClusters * dwSectPerClust * dwBytesPerSect;
  15522. #endif
  15523. }
  15524. /* jx9_int64 (*xTotalSpace)(const char *) */
  15525. static jx9_int64 WinVfs_DiskTotalSpace(const char *zPath)
  15526. {
  15527. #ifdef _WIN32_WCE
  15528. /* GetDiskFreeSpace is not supported under WINCE */
  15529. SXUNUSED(zPath);
  15530. return 0;
  15531. #else
  15532. DWORD dwSectPerClust, dwBytesPerSect, dwFreeClusters, dwTotalClusters;
  15533. void * pConverted;
  15534. WCHAR *p;
  15535. BOOL rc;
  15536. pConverted = convertUtf8Filename(zPath);
  15537. if( pConverted == 0 ){
  15538. return 0;
  15539. }
  15540. p = (WCHAR *)pConverted;
  15541. for(;*p;p++){
  15542. if( *p == '\\' || *p == '/'){
  15543. *p = '\0';
  15544. break;
  15545. }
  15546. }
  15547. rc = GetDiskFreeSpaceW((LPCWSTR)pConverted, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters);
  15548. if( !rc ){
  15549. return 0;
  15550. }
  15551. return (jx9_int64)dwTotalClusters * dwSectPerClust * dwBytesPerSect;
  15552. #endif
  15553. }
  15554. /* int (*xFileExists)(const char *) */
  15555. static int WinVfs_FileExists(const char *zPath)
  15556. {
  15557. void * pConverted;
  15558. DWORD dwAttr;
  15559. pConverted = convertUtf8Filename(zPath);
  15560. if( pConverted == 0 ){
  15561. return -1;
  15562. }
  15563. dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
  15564. HeapFree(GetProcessHeap(), 0, pConverted);
  15565. if( dwAttr == INVALID_FILE_ATTRIBUTES ){
  15566. return -1;
  15567. }
  15568. return JX9_OK;
  15569. }
  15570. /* Open a file in a read-only mode */
  15571. static HANDLE OpenReadOnly(LPCWSTR pPath)
  15572. {
  15573. DWORD dwType = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
  15574. DWORD dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE;
  15575. DWORD dwAccess = GENERIC_READ;
  15576. DWORD dwCreate = OPEN_EXISTING;
  15577. HANDLE pHandle;
  15578. pHandle = CreateFileW(pPath, dwAccess, dwShare, 0, dwCreate, dwType, 0);
  15579. if( pHandle == INVALID_HANDLE_VALUE){
  15580. return 0;
  15581. }
  15582. return pHandle;
  15583. }
  15584. /* jx9_int64 (*xFileSize)(const char *) */
  15585. static jx9_int64 WinVfs_FileSize(const char *zPath)
  15586. {
  15587. DWORD dwLow, dwHigh;
  15588. void * pConverted;
  15589. jx9_int64 nSize;
  15590. HANDLE pHandle;
  15591. pConverted = convertUtf8Filename(zPath);
  15592. if( pConverted == 0 ){
  15593. return -1;
  15594. }
  15595. /* Open the file in read-only mode */
  15596. pHandle = OpenReadOnly((LPCWSTR)pConverted);
  15597. HeapFree(GetProcessHeap(), 0, pConverted);
  15598. if( pHandle ){
  15599. dwLow = GetFileSize(pHandle, &dwHigh);
  15600. nSize = dwHigh;
  15601. nSize <<= 32;
  15602. nSize += dwLow;
  15603. CloseHandle(pHandle);
  15604. }else{
  15605. nSize = -1;
  15606. }
  15607. return nSize;
  15608. }
  15609. #define TICKS_PER_SECOND 10000000
  15610. #define EPOCH_DIFFERENCE 11644473600LL
  15611. /* Convert Windows timestamp to UNIX timestamp */
  15612. static jx9_int64 convertWindowsTimeToUnixTime(LPFILETIME pTime)
  15613. {
  15614. jx9_int64 input, temp;
  15615. input = pTime->dwHighDateTime;
  15616. input <<= 32;
  15617. input += pTime->dwLowDateTime;
  15618. temp = input / TICKS_PER_SECOND; /*convert from 100ns intervals to seconds*/
  15619. temp = temp - EPOCH_DIFFERENCE; /*subtract number of seconds between epochs*/
  15620. return temp;
  15621. }
  15622. /* Convert UNIX timestamp to Windows timestamp */
  15623. static void convertUnixTimeToWindowsTime(jx9_int64 nUnixtime, LPFILETIME pOut)
  15624. {
  15625. jx9_int64 result = EPOCH_DIFFERENCE;
  15626. result += nUnixtime;
  15627. result *= 10000000LL;
  15628. pOut->dwHighDateTime = (DWORD)(nUnixtime>>32);
  15629. pOut->dwLowDateTime = (DWORD)nUnixtime;
  15630. }
  15631. /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
  15632. static int WinVfs_Touch(const char *zPath, jx9_int64 touch_time, jx9_int64 access_time)
  15633. {
  15634. FILETIME sTouch, sAccess;
  15635. void *pConverted;
  15636. void *pHandle;
  15637. BOOL rc = 0;
  15638. pConverted = convertUtf8Filename(zPath);
  15639. if( pConverted == 0 ){
  15640. return -1;
  15641. }
  15642. pHandle = OpenReadOnly((LPCWSTR)pConverted);
  15643. if( pHandle ){
  15644. if( touch_time < 0 ){
  15645. GetSystemTimeAsFileTime(&sTouch);
  15646. }else{
  15647. convertUnixTimeToWindowsTime(touch_time, &sTouch);
  15648. }
  15649. if( access_time < 0 ){
  15650. /* Use the touch time */
  15651. sAccess = sTouch; /* Structure assignment */
  15652. }else{
  15653. convertUnixTimeToWindowsTime(access_time, &sAccess);
  15654. }
  15655. rc = SetFileTime(pHandle, &sTouch, &sAccess, 0);
  15656. /* Close the handle */
  15657. CloseHandle(pHandle);
  15658. }
  15659. HeapFree(GetProcessHeap(), 0, pConverted);
  15660. return rc ? JX9_OK : -1;
  15661. }
  15662. /* jx9_int64 (*xFileAtime)(const char *) */
  15663. static jx9_int64 WinVfs_FileAtime(const char *zPath)
  15664. {
  15665. BY_HANDLE_FILE_INFORMATION sInfo;
  15666. void * pConverted;
  15667. jx9_int64 atime;
  15668. HANDLE pHandle;
  15669. pConverted = convertUtf8Filename(zPath);
  15670. if( pConverted == 0 ){
  15671. return -1;
  15672. }
  15673. /* Open the file in read-only mode */
  15674. pHandle = OpenReadOnly((LPCWSTR)pConverted);
  15675. if( pHandle ){
  15676. BOOL rc;
  15677. rc = GetFileInformationByHandle(pHandle, &sInfo);
  15678. if( rc ){
  15679. atime = convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime);
  15680. }else{
  15681. atime = -1;
  15682. }
  15683. CloseHandle(pHandle);
  15684. }else{
  15685. atime = -1;
  15686. }
  15687. HeapFree(GetProcessHeap(), 0, pConverted);
  15688. return atime;
  15689. }
  15690. /* jx9_int64 (*xFileMtime)(const char *) */
  15691. static jx9_int64 WinVfs_FileMtime(const char *zPath)
  15692. {
  15693. BY_HANDLE_FILE_INFORMATION sInfo;
  15694. void * pConverted;
  15695. jx9_int64 mtime;
  15696. HANDLE pHandle;
  15697. pConverted = convertUtf8Filename(zPath);
  15698. if( pConverted == 0 ){
  15699. return -1;
  15700. }
  15701. /* Open the file in read-only mode */
  15702. pHandle = OpenReadOnly((LPCWSTR)pConverted);
  15703. if( pHandle ){
  15704. BOOL rc;
  15705. rc = GetFileInformationByHandle(pHandle, &sInfo);
  15706. if( rc ){
  15707. mtime = convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime);
  15708. }else{
  15709. mtime = -1;
  15710. }
  15711. CloseHandle(pHandle);
  15712. }else{
  15713. mtime = -1;
  15714. }
  15715. HeapFree(GetProcessHeap(), 0, pConverted);
  15716. return mtime;
  15717. }
  15718. /* jx9_int64 (*xFileCtime)(const char *) */
  15719. static jx9_int64 WinVfs_FileCtime(const char *zPath)
  15720. {
  15721. BY_HANDLE_FILE_INFORMATION sInfo;
  15722. void * pConverted;
  15723. jx9_int64 ctime;
  15724. HANDLE pHandle;
  15725. pConverted = convertUtf8Filename(zPath);
  15726. if( pConverted == 0 ){
  15727. return -1;
  15728. }
  15729. /* Open the file in read-only mode */
  15730. pHandle = OpenReadOnly((LPCWSTR)pConverted);
  15731. if( pHandle ){
  15732. BOOL rc;
  15733. rc = GetFileInformationByHandle(pHandle, &sInfo);
  15734. if( rc ){
  15735. ctime = convertWindowsTimeToUnixTime(&sInfo.ftCreationTime);
  15736. }else{
  15737. ctime = -1;
  15738. }
  15739. CloseHandle(pHandle);
  15740. }else{
  15741. ctime = -1;
  15742. }
  15743. HeapFree(GetProcessHeap(), 0, pConverted);
  15744. return ctime;
  15745. }
  15746. /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
  15747. /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
  15748. static int WinVfs_Stat(const char *zPath, jx9_value *pArray, jx9_value *pWorker)
  15749. {
  15750. BY_HANDLE_FILE_INFORMATION sInfo;
  15751. void *pConverted;
  15752. HANDLE pHandle;
  15753. BOOL rc;
  15754. pConverted = convertUtf8Filename(zPath);
  15755. if( pConverted == 0 ){
  15756. return -1;
  15757. }
  15758. /* Open the file in read-only mode */
  15759. pHandle = OpenReadOnly((LPCWSTR)pConverted);
  15760. HeapFree(GetProcessHeap(), 0, pConverted);
  15761. if( pHandle == 0 ){
  15762. return -1;
  15763. }
  15764. rc = GetFileInformationByHandle(pHandle, &sInfo);
  15765. CloseHandle(pHandle);
  15766. if( !rc ){
  15767. return -1;
  15768. }
  15769. /* dev */
  15770. jx9_value_int64(pWorker, (jx9_int64)sInfo.dwVolumeSerialNumber);
  15771. jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
  15772. /* ino */
  15773. jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileIndexHigh << 32) | sInfo.nFileIndexLow));
  15774. jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
  15775. /* mode */
  15776. jx9_value_int(pWorker, 0);
  15777. jx9_array_add_strkey_elem(pArray, "mode", pWorker);
  15778. /* nlink */
  15779. jx9_value_int(pWorker, (int)sInfo.nNumberOfLinks);
  15780. jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
  15781. /* uid, gid, rdev */
  15782. jx9_value_int(pWorker, 0);
  15783. jx9_array_add_strkey_elem(pArray, "uid", pWorker);
  15784. jx9_array_add_strkey_elem(pArray, "gid", pWorker);
  15785. jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
  15786. /* size */
  15787. jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileSizeHigh << 32) | sInfo.nFileSizeLow));
  15788. jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
  15789. /* atime */
  15790. jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime));
  15791. jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
  15792. /* mtime */
  15793. jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime));
  15794. jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
  15795. /* ctime */
  15796. jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftCreationTime));
  15797. jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
  15798. /* blksize, blocks */
  15799. jx9_value_int(pWorker, 0);
  15800. jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
  15801. jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
  15802. return JX9_OK;
  15803. }
  15804. /* int (*xIsfile)(const char *) */
  15805. static int WinVfs_isfile(const char *zPath)
  15806. {
  15807. void * pConverted;
  15808. DWORD dwAttr;
  15809. pConverted = convertUtf8Filename(zPath);
  15810. if( pConverted == 0 ){
  15811. return -1;
  15812. }
  15813. dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
  15814. HeapFree(GetProcessHeap(), 0, pConverted);
  15815. if( dwAttr == INVALID_FILE_ATTRIBUTES ){
  15816. return -1;
  15817. }
  15818. return (dwAttr & (FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE)) ? JX9_OK : -1;
  15819. }
  15820. /* int (*xIslink)(const char *) */
  15821. static int WinVfs_islink(const char *zPath)
  15822. {
  15823. void * pConverted;
  15824. DWORD dwAttr;
  15825. pConverted = convertUtf8Filename(zPath);
  15826. if( pConverted == 0 ){
  15827. return -1;
  15828. }
  15829. dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
  15830. HeapFree(GetProcessHeap(), 0, pConverted);
  15831. if( dwAttr == INVALID_FILE_ATTRIBUTES ){
  15832. return -1;
  15833. }
  15834. return (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT) ? JX9_OK : -1;
  15835. }
  15836. /* int (*xWritable)(const char *) */
  15837. static int WinVfs_iswritable(const char *zPath)
  15838. {
  15839. void * pConverted;
  15840. DWORD dwAttr;
  15841. pConverted = convertUtf8Filename(zPath);
  15842. if( pConverted == 0 ){
  15843. return -1;
  15844. }
  15845. dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
  15846. HeapFree(GetProcessHeap(), 0, pConverted);
  15847. if( dwAttr == INVALID_FILE_ATTRIBUTES ){
  15848. return -1;
  15849. }
  15850. if( (dwAttr & (FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_NORMAL)) == 0 ){
  15851. /* Not a regular file */
  15852. return -1;
  15853. }
  15854. if( dwAttr & FILE_ATTRIBUTE_READONLY ){
  15855. /* Read-only file */
  15856. return -1;
  15857. }
  15858. /* File is writable */
  15859. return JX9_OK;
  15860. }
  15861. /* int (*xExecutable)(const char *) */
  15862. static int WinVfs_isexecutable(const char *zPath)
  15863. {
  15864. void * pConverted;
  15865. DWORD dwAttr;
  15866. pConverted = convertUtf8Filename(zPath);
  15867. if( pConverted == 0 ){
  15868. return -1;
  15869. }
  15870. dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
  15871. HeapFree(GetProcessHeap(), 0, pConverted);
  15872. if( dwAttr == INVALID_FILE_ATTRIBUTES ){
  15873. return -1;
  15874. }
  15875. if( (dwAttr & FILE_ATTRIBUTE_NORMAL) == 0 ){
  15876. /* Not a regular file */
  15877. return -1;
  15878. }
  15879. /* File is executable */
  15880. return JX9_OK;
  15881. }
  15882. /* int (*xFiletype)(const char *, jx9_context *) */
  15883. static int WinVfs_Filetype(const char *zPath, jx9_context *pCtx)
  15884. {
  15885. void * pConverted;
  15886. DWORD dwAttr;
  15887. pConverted = convertUtf8Filename(zPath);
  15888. if( pConverted == 0 ){
  15889. /* Expand 'unknown' */
  15890. jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
  15891. return -1;
  15892. }
  15893. dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
  15894. HeapFree(GetProcessHeap(), 0, pConverted);
  15895. if( dwAttr == INVALID_FILE_ATTRIBUTES ){
  15896. /* Expand 'unknown' */
  15897. jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
  15898. return -1;
  15899. }
  15900. if(dwAttr & (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE) ){
  15901. jx9_result_string(pCtx, "file", sizeof("file")-1);
  15902. }else if(dwAttr & FILE_ATTRIBUTE_DIRECTORY){
  15903. jx9_result_string(pCtx, "dir", sizeof("dir")-1);
  15904. }else if(dwAttr & FILE_ATTRIBUTE_REPARSE_POINT){
  15905. jx9_result_string(pCtx, "link", sizeof("link")-1);
  15906. }else if(dwAttr & (FILE_ATTRIBUTE_DEVICE)){
  15907. jx9_result_string(pCtx, "block", sizeof("block")-1);
  15908. }else{
  15909. jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
  15910. }
  15911. return JX9_OK;
  15912. }
  15913. /* int (*xGetenv)(const char *, jx9_context *) */
  15914. static int WinVfs_Getenv(const char *zVar, jx9_context *pCtx)
  15915. {
  15916. char zValue[1024];
  15917. DWORD n;
  15918. /*
  15919. * According to MSDN
  15920. * If lpBuffer is not large enough to hold the data, the return
  15921. * value is the buffer size, in characters, required to hold the
  15922. * string and its terminating null character and the contents
  15923. * of lpBuffer are undefined.
  15924. */
  15925. n = sizeof(zValue);
  15926. SyMemcpy("Undefined", zValue, sizeof("Undefined")-1);
  15927. /* Extract the environment value */
  15928. n = GetEnvironmentVariableA(zVar, zValue, sizeof(zValue));
  15929. if( !n ){
  15930. /* No such variable*/
  15931. return -1;
  15932. }
  15933. jx9_result_string(pCtx, zValue, (int)n);
  15934. return JX9_OK;
  15935. }
  15936. /* int (*xSetenv)(const char *, const char *) */
  15937. static int WinVfs_Setenv(const char *zName, const char *zValue)
  15938. {
  15939. BOOL rc;
  15940. rc = SetEnvironmentVariableA(zName, zValue);
  15941. return rc ? JX9_OK : -1;
  15942. }
  15943. /* int (*xMmap)(const char *, void **, jx9_int64 *) */
  15944. static int WinVfs_Mmap(const char *zPath, void **ppMap, jx9_int64 *pSize)
  15945. {
  15946. DWORD dwSizeLow, dwSizeHigh;
  15947. HANDLE pHandle, pMapHandle;
  15948. void *pConverted, *pView;
  15949. pConverted = convertUtf8Filename(zPath);
  15950. if( pConverted == 0 ){
  15951. return -1;
  15952. }
  15953. pHandle = OpenReadOnly((LPCWSTR)pConverted);
  15954. HeapFree(GetProcessHeap(), 0, pConverted);
  15955. if( pHandle == 0 ){
  15956. return -1;
  15957. }
  15958. /* Get the file size */
  15959. dwSizeLow = GetFileSize(pHandle, &dwSizeHigh);
  15960. /* Create the mapping */
  15961. pMapHandle = CreateFileMappingW(pHandle, 0, PAGE_READONLY, dwSizeHigh, dwSizeLow, 0);
  15962. if( pMapHandle == 0 ){
  15963. CloseHandle(pHandle);
  15964. return -1;
  15965. }
  15966. *pSize = ((jx9_int64)dwSizeHigh << 32) | dwSizeLow;
  15967. /* Obtain the view */
  15968. pView = MapViewOfFile(pMapHandle, FILE_MAP_READ, 0, 0, (SIZE_T)(*pSize));
  15969. if( pView ){
  15970. /* Let the upper layer point to the view */
  15971. *ppMap = pView;
  15972. }
  15973. /* Close the handle
  15974. * According to MSDN it's OK the close the HANDLES.
  15975. */
  15976. CloseHandle(pMapHandle);
  15977. CloseHandle(pHandle);
  15978. return pView ? JX9_OK : -1;
  15979. }
  15980. /* void (*xUnmap)(void *, jx9_int64) */
  15981. static void WinVfs_Unmap(void *pView, jx9_int64 nSize)
  15982. {
  15983. nSize = 0; /* Compiler warning */
  15984. UnmapViewOfFile(pView);
  15985. }
  15986. /* void (*xTempDir)(jx9_context *) */
  15987. static void WinVfs_TempDir(jx9_context *pCtx)
  15988. {
  15989. CHAR zTemp[1024];
  15990. DWORD n;
  15991. n = GetTempPathA(sizeof(zTemp), zTemp);
  15992. if( n < 1 ){
  15993. /* Assume the default windows temp directory */
  15994. jx9_result_string(pCtx, "C:\\Windows\\Temp", -1/*Compute length automatically*/);
  15995. }else{
  15996. jx9_result_string(pCtx, zTemp, (int)n);
  15997. }
  15998. }
  15999. /* unsigned int (*xProcessId)(void) */
  16000. static unsigned int WinVfs_ProcessId(void)
  16001. {
  16002. DWORD nID = 0;
  16003. #ifndef __MINGW32__
  16004. nID = GetProcessId(GetCurrentProcess());
  16005. #endif /* __MINGW32__ */
  16006. return (unsigned int)nID;
  16007. }
  16008. /* void (*xUsername)(jx9_context *) */
  16009. static void WinVfs_Username(jx9_context *pCtx)
  16010. {
  16011. WCHAR zUser[1024];
  16012. DWORD nByte;
  16013. BOOL rc;
  16014. nByte = sizeof(zUser);
  16015. rc = GetUserNameW(zUser, &nByte);
  16016. if( !rc ){
  16017. /* Set a dummy name */
  16018. jx9_result_string(pCtx, "Unknown", sizeof("Unknown")-1);
  16019. }else{
  16020. char *zName;
  16021. zName = unicodeToUtf8(zUser);
  16022. if( zName == 0 ){
  16023. jx9_result_string(pCtx, "Unknown", sizeof("Unknown")-1);
  16024. }else{
  16025. jx9_result_string(pCtx, zName, -1/*Compute length automatically*/); /* Will make it's own copy */
  16026. HeapFree(GetProcessHeap(), 0, zName);
  16027. }
  16028. }
  16029. }
  16030. /* Export the windows vfs */
  16031. static const jx9_vfs sWinVfs = {
  16032. "Windows_vfs",
  16033. JX9_VFS_VERSION,
  16034. WinVfs_chdir, /* int (*xChdir)(const char *) */
  16035. 0, /* int (*xChroot)(const char *); */
  16036. WinVfs_getcwd, /* int (*xGetcwd)(jx9_context *) */
  16037. WinVfs_mkdir, /* int (*xMkdir)(const char *, int, int) */
  16038. WinVfs_rmdir, /* int (*xRmdir)(const char *) */
  16039. WinVfs_isdir, /* int (*xIsdir)(const char *) */
  16040. WinVfs_Rename, /* int (*xRename)(const char *, const char *) */
  16041. WinVfs_Realpath, /*int (*xRealpath)(const char *, jx9_context *)*/
  16042. WinVfs_Sleep, /* int (*xSleep)(unsigned int) */
  16043. WinVfs_unlink, /* int (*xUnlink)(const char *) */
  16044. WinVfs_FileExists, /* int (*xFileExists)(const char *) */
  16045. 0, /*int (*xChmod)(const char *, int)*/
  16046. 0, /*int (*xChown)(const char *, const char *)*/
  16047. 0, /*int (*xChgrp)(const char *, const char *)*/
  16048. WinVfs_DiskFreeSpace, /* jx9_int64 (*xFreeSpace)(const char *) */
  16049. WinVfs_DiskTotalSpace, /* jx9_int64 (*xTotalSpace)(const char *) */
  16050. WinVfs_FileSize, /* jx9_int64 (*xFileSize)(const char *) */
  16051. WinVfs_FileAtime, /* jx9_int64 (*xFileAtime)(const char *) */
  16052. WinVfs_FileMtime, /* jx9_int64 (*xFileMtime)(const char *) */
  16053. WinVfs_FileCtime, /* jx9_int64 (*xFileCtime)(const char *) */
  16054. WinVfs_Stat, /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
  16055. WinVfs_Stat, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
  16056. WinVfs_isfile, /* int (*xIsfile)(const char *) */
  16057. WinVfs_islink, /* int (*xIslink)(const char *) */
  16058. WinVfs_isfile, /* int (*xReadable)(const char *) */
  16059. WinVfs_iswritable, /* int (*xWritable)(const char *) */
  16060. WinVfs_isexecutable, /* int (*xExecutable)(const char *) */
  16061. WinVfs_Filetype, /* int (*xFiletype)(const char *, jx9_context *) */
  16062. WinVfs_Getenv, /* int (*xGetenv)(const char *, jx9_context *) */
  16063. WinVfs_Setenv, /* int (*xSetenv)(const char *, const char *) */
  16064. WinVfs_Touch, /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
  16065. WinVfs_Mmap, /* int (*xMmap)(const char *, void **, jx9_int64 *) */
  16066. WinVfs_Unmap, /* void (*xUnmap)(void *, jx9_int64); */
  16067. 0, /* int (*xLink)(const char *, const char *, int) */
  16068. 0, /* int (*xUmask)(int) */
  16069. WinVfs_TempDir, /* void (*xTempDir)(jx9_context *) */
  16070. WinVfs_ProcessId, /* unsigned int (*xProcessId)(void) */
  16071. 0, /* int (*xUid)(void) */
  16072. 0, /* int (*xGid)(void) */
  16073. WinVfs_Username, /* void (*xUsername)(jx9_context *) */
  16074. 0 /* int (*xExec)(const char *, jx9_context *) */
  16075. };
  16076. /* Windows file IO */
  16077. #ifndef INVALID_SET_FILE_POINTER
  16078. # define INVALID_SET_FILE_POINTER ((DWORD)-1)
  16079. #endif
  16080. /* int (*xOpen)(const char *, int, jx9_value *, void **) */
  16081. static int WinFile_Open(const char *zPath, int iOpenMode, jx9_value *pResource, void **ppHandle)
  16082. {
  16083. DWORD dwType = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
  16084. DWORD dwAccess = GENERIC_READ;
  16085. DWORD dwShare, dwCreate;
  16086. void *pConverted;
  16087. HANDLE pHandle;
  16088. pConverted = convertUtf8Filename(zPath);
  16089. if( pConverted == 0 ){
  16090. return -1;
  16091. }
  16092. /* Set the desired flags according to the open mode */
  16093. if( iOpenMode & JX9_IO_OPEN_CREATE ){
  16094. /* Open existing file, or create if it doesn't exist */
  16095. dwCreate = OPEN_ALWAYS;
  16096. if( iOpenMode & JX9_IO_OPEN_TRUNC ){
  16097. /* If the specified file exists and is writable, the function overwrites the file */
  16098. dwCreate = CREATE_ALWAYS;
  16099. }
  16100. }else if( iOpenMode & JX9_IO_OPEN_EXCL ){
  16101. /* Creates a new file, only if it does not already exist.
  16102. * If the file exists, it fails.
  16103. */
  16104. dwCreate = CREATE_NEW;
  16105. }else if( iOpenMode & JX9_IO_OPEN_TRUNC ){
  16106. /* Opens a file and truncates it so that its size is zero bytes
  16107. * The file must exist.
  16108. */
  16109. dwCreate = TRUNCATE_EXISTING;
  16110. }else{
  16111. /* Opens a file, only if it exists. */
  16112. dwCreate = OPEN_EXISTING;
  16113. }
  16114. if( iOpenMode & JX9_IO_OPEN_RDWR ){
  16115. /* Read+Write access */
  16116. dwAccess |= GENERIC_WRITE;
  16117. }else if( iOpenMode & JX9_IO_OPEN_WRONLY ){
  16118. /* Write only access */
  16119. dwAccess = GENERIC_WRITE;
  16120. }
  16121. if( iOpenMode & JX9_IO_OPEN_APPEND ){
  16122. /* Append mode */
  16123. dwAccess = FILE_APPEND_DATA;
  16124. }
  16125. if( iOpenMode & JX9_IO_OPEN_TEMP ){
  16126. /* File is temporary */
  16127. dwType = FILE_ATTRIBUTE_TEMPORARY;
  16128. }
  16129. dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE;
  16130. pHandle = CreateFileW((LPCWSTR)pConverted, dwAccess, dwShare, 0, dwCreate, dwType, 0);
  16131. HeapFree(GetProcessHeap(), 0, pConverted);
  16132. if( pHandle == INVALID_HANDLE_VALUE){
  16133. SXUNUSED(pResource); /* MSVC warning */
  16134. return -1;
  16135. }
  16136. /* Make the handle accessible to the upper layer */
  16137. *ppHandle = (void *)pHandle;
  16138. return JX9_OK;
  16139. }
  16140. /* An instance of the following structure is used to record state information
  16141. * while iterating throw directory entries.
  16142. */
  16143. typedef struct WinDir_Info WinDir_Info;
  16144. struct WinDir_Info
  16145. {
  16146. HANDLE pDirHandle;
  16147. void *pPath;
  16148. WIN32_FIND_DATAW sInfo;
  16149. int rc;
  16150. };
  16151. /* int (*xOpenDir)(const char *, jx9_value *, void **) */
  16152. static int WinDir_Open(const char *zPath, jx9_value *pResource, void **ppHandle)
  16153. {
  16154. WinDir_Info *pDirInfo;
  16155. void *pConverted;
  16156. char *zPrep;
  16157. sxu32 n;
  16158. /* Prepare the path */
  16159. n = SyStrlen(zPath);
  16160. zPrep = (char *)HeapAlloc(GetProcessHeap(), 0, n+sizeof("\\*")+4);
  16161. if( zPrep == 0 ){
  16162. return -1;
  16163. }
  16164. SyMemcpy((const void *)zPath, zPrep, n);
  16165. zPrep[n] = '\\';
  16166. zPrep[n+1] = '*';
  16167. zPrep[n+2] = 0;
  16168. pConverted = convertUtf8Filename(zPrep);
  16169. HeapFree(GetProcessHeap(), 0, zPrep);
  16170. if( pConverted == 0 ){
  16171. return -1;
  16172. }
  16173. /* Allocate a new instance */
  16174. pDirInfo = (WinDir_Info *)HeapAlloc(GetProcessHeap(), 0, sizeof(WinDir_Info));
  16175. if( pDirInfo == 0 ){
  16176. pResource = 0; /* Compiler warning */
  16177. return -1;
  16178. }
  16179. pDirInfo->rc = SXRET_OK;
  16180. pDirInfo->pDirHandle = FindFirstFileW((LPCWSTR)pConverted, &pDirInfo->sInfo);
  16181. if( pDirInfo->pDirHandle == INVALID_HANDLE_VALUE ){
  16182. /* Cannot open directory */
  16183. HeapFree(GetProcessHeap(), 0, pConverted);
  16184. HeapFree(GetProcessHeap(), 0, pDirInfo);
  16185. return -1;
  16186. }
  16187. /* Save the path */
  16188. pDirInfo->pPath = pConverted;
  16189. /* Save our structure */
  16190. *ppHandle = pDirInfo;
  16191. return JX9_OK;
  16192. }
  16193. /* void (*xCloseDir)(void *) */
  16194. static void WinDir_Close(void *pUserData)
  16195. {
  16196. WinDir_Info *pDirInfo = (WinDir_Info *)pUserData;
  16197. if( pDirInfo->pDirHandle != INVALID_HANDLE_VALUE ){
  16198. FindClose(pDirInfo->pDirHandle);
  16199. }
  16200. HeapFree(GetProcessHeap(), 0, pDirInfo->pPath);
  16201. HeapFree(GetProcessHeap(), 0, pDirInfo);
  16202. }
  16203. /* void (*xClose)(void *); */
  16204. static void WinFile_Close(void *pUserData)
  16205. {
  16206. HANDLE pHandle = (HANDLE)pUserData;
  16207. CloseHandle(pHandle);
  16208. }
  16209. /* int (*xReadDir)(void *, jx9_context *) */
  16210. static int WinDir_Read(void *pUserData, jx9_context *pCtx)
  16211. {
  16212. WinDir_Info *pDirInfo = (WinDir_Info *)pUserData;
  16213. LPWIN32_FIND_DATAW pData;
  16214. char *zName;
  16215. BOOL rc;
  16216. sxu32 n;
  16217. if( pDirInfo->rc != SXRET_OK ){
  16218. /* No more entry to process */
  16219. return -1;
  16220. }
  16221. pData = &pDirInfo->sInfo;
  16222. for(;;){
  16223. zName = unicodeToUtf8(pData->cFileName);
  16224. if( zName == 0 ){
  16225. /* Out of memory */
  16226. return -1;
  16227. }
  16228. n = SyStrlen(zName);
  16229. /* Ignore '.' && '..' */
  16230. if( n > sizeof("..")-1 || zName[0] != '.' || ( n == sizeof("..")-1 && zName[1] != '.') ){
  16231. break;
  16232. }
  16233. HeapFree(GetProcessHeap(), 0, zName);
  16234. rc = FindNextFileW(pDirInfo->pDirHandle, &pDirInfo->sInfo);
  16235. if( !rc ){
  16236. return -1;
  16237. }
  16238. }
  16239. /* Return the current file name */
  16240. jx9_result_string(pCtx, zName, -1);
  16241. HeapFree(GetProcessHeap(), 0, zName);
  16242. /* Point to the next entry */
  16243. rc = FindNextFileW(pDirInfo->pDirHandle, &pDirInfo->sInfo);
  16244. if( !rc ){
  16245. pDirInfo->rc = SXERR_EOF;
  16246. }
  16247. return JX9_OK;
  16248. }
  16249. /* void (*xRewindDir)(void *) */
  16250. static void WinDir_RewindDir(void *pUserData)
  16251. {
  16252. WinDir_Info *pDirInfo = (WinDir_Info *)pUserData;
  16253. FindClose(pDirInfo->pDirHandle);
  16254. pDirInfo->pDirHandle = FindFirstFileW((LPCWSTR)pDirInfo->pPath, &pDirInfo->sInfo);
  16255. if( pDirInfo->pDirHandle == INVALID_HANDLE_VALUE ){
  16256. pDirInfo->rc = SXERR_EOF;
  16257. }else{
  16258. pDirInfo->rc = SXRET_OK;
  16259. }
  16260. }
  16261. /* jx9_int64 (*xRead)(void *, void *, jx9_int64); */
  16262. static jx9_int64 WinFile_Read(void *pOS, void *pBuffer, jx9_int64 nDatatoRead)
  16263. {
  16264. HANDLE pHandle = (HANDLE)pOS;
  16265. DWORD nRd;
  16266. BOOL rc;
  16267. rc = ReadFile(pHandle, pBuffer, (DWORD)nDatatoRead, &nRd, 0);
  16268. if( !rc ){
  16269. /* EOF or IO error */
  16270. return -1;
  16271. }
  16272. return (jx9_int64)nRd;
  16273. }
  16274. /* jx9_int64 (*xWrite)(void *, const void *, jx9_int64); */
  16275. static jx9_int64 WinFile_Write(void *pOS, const void *pBuffer, jx9_int64 nWrite)
  16276. {
  16277. const char *zData = (const char *)pBuffer;
  16278. HANDLE pHandle = (HANDLE)pOS;
  16279. jx9_int64 nCount;
  16280. DWORD nWr;
  16281. BOOL rc;
  16282. nWr = 0;
  16283. nCount = 0;
  16284. for(;;){
  16285. if( nWrite < 1 ){
  16286. break;
  16287. }
  16288. rc = WriteFile(pHandle, zData, (DWORD)nWrite, &nWr, 0);
  16289. if( !rc ){
  16290. /* IO error */
  16291. break;
  16292. }
  16293. nWrite -= nWr;
  16294. nCount += nWr;
  16295. zData += nWr;
  16296. }
  16297. if( nWrite > 0 ){
  16298. return -1;
  16299. }
  16300. return nCount;
  16301. }
  16302. /* int (*xSeek)(void *, jx9_int64, int) */
  16303. static int WinFile_Seek(void *pUserData, jx9_int64 iOfft, int whence)
  16304. {
  16305. HANDLE pHandle = (HANDLE)pUserData;
  16306. DWORD dwMove, dwNew;
  16307. LONG nHighOfft;
  16308. switch(whence){
  16309. case 1:/*SEEK_CUR*/
  16310. dwMove = FILE_CURRENT;
  16311. break;
  16312. case 2: /* SEEK_END */
  16313. dwMove = FILE_END;
  16314. break;
  16315. case 0: /* SEEK_SET */
  16316. default:
  16317. dwMove = FILE_BEGIN;
  16318. break;
  16319. }
  16320. nHighOfft = (LONG)(iOfft >> 32);
  16321. dwNew = SetFilePointer(pHandle, (LONG)iOfft, &nHighOfft, dwMove);
  16322. if( dwNew == INVALID_SET_FILE_POINTER ){
  16323. return -1;
  16324. }
  16325. return JX9_OK;
  16326. }
  16327. /* int (*xLock)(void *, int) */
  16328. static int WinFile_Lock(void *pUserData, int lock_type)
  16329. {
  16330. HANDLE pHandle = (HANDLE)pUserData;
  16331. static DWORD dwLo = 0, dwHi = 0; /* xx: MT-SAFE */
  16332. OVERLAPPED sDummy;
  16333. BOOL rc;
  16334. SyZero(&sDummy, sizeof(sDummy));
  16335. /* Get the file size */
  16336. if( lock_type < 1 ){
  16337. /* Unlock the file */
  16338. rc = UnlockFileEx(pHandle, 0, dwLo, dwHi, &sDummy);
  16339. }else{
  16340. DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY; /* Shared non-blocking lock by default*/
  16341. /* Lock the file */
  16342. if( lock_type == 1 /* LOCK_EXCL */ ){
  16343. dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
  16344. }
  16345. dwLo = GetFileSize(pHandle, &dwHi);
  16346. rc = LockFileEx(pHandle, dwFlags, 0, dwLo, dwHi, &sDummy);
  16347. }
  16348. return rc ? JX9_OK : -1 /* Lock error */;
  16349. }
  16350. /* jx9_int64 (*xTell)(void *) */
  16351. static jx9_int64 WinFile_Tell(void *pUserData)
  16352. {
  16353. HANDLE pHandle = (HANDLE)pUserData;
  16354. DWORD dwNew;
  16355. dwNew = SetFilePointer(pHandle, 0, 0, FILE_CURRENT/* SEEK_CUR */);
  16356. if( dwNew == INVALID_SET_FILE_POINTER ){
  16357. return -1;
  16358. }
  16359. return (jx9_int64)dwNew;
  16360. }
  16361. /* int (*xTrunc)(void *, jx9_int64) */
  16362. static int WinFile_Trunc(void *pUserData, jx9_int64 nOfft)
  16363. {
  16364. HANDLE pHandle = (HANDLE)pUserData;
  16365. LONG HighOfft;
  16366. DWORD dwNew;
  16367. BOOL rc;
  16368. HighOfft = (LONG)(nOfft >> 32);
  16369. dwNew = SetFilePointer(pHandle, (LONG)nOfft, &HighOfft, FILE_BEGIN);
  16370. if( dwNew == INVALID_SET_FILE_POINTER ){
  16371. return -1;
  16372. }
  16373. rc = SetEndOfFile(pHandle);
  16374. return rc ? JX9_OK : -1;
  16375. }
  16376. /* int (*xSync)(void *); */
  16377. static int WinFile_Sync(void *pUserData)
  16378. {
  16379. HANDLE pHandle = (HANDLE)pUserData;
  16380. BOOL rc;
  16381. rc = FlushFileBuffers(pHandle);
  16382. return rc ? JX9_OK : - 1;
  16383. }
  16384. /* int (*xStat)(void *, jx9_value *, jx9_value *) */
  16385. static int WinFile_Stat(void *pUserData, jx9_value *pArray, jx9_value *pWorker)
  16386. {
  16387. BY_HANDLE_FILE_INFORMATION sInfo;
  16388. HANDLE pHandle = (HANDLE)pUserData;
  16389. BOOL rc;
  16390. rc = GetFileInformationByHandle(pHandle, &sInfo);
  16391. if( !rc ){
  16392. return -1;
  16393. }
  16394. /* dev */
  16395. jx9_value_int64(pWorker, (jx9_int64)sInfo.dwVolumeSerialNumber);
  16396. jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
  16397. /* ino */
  16398. jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileIndexHigh << 32) | sInfo.nFileIndexLow));
  16399. jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
  16400. /* mode */
  16401. jx9_value_int(pWorker, 0);
  16402. jx9_array_add_strkey_elem(pArray, "mode", pWorker);
  16403. /* nlink */
  16404. jx9_value_int(pWorker, (int)sInfo.nNumberOfLinks);
  16405. jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
  16406. /* uid, gid, rdev */
  16407. jx9_value_int(pWorker, 0);
  16408. jx9_array_add_strkey_elem(pArray, "uid", pWorker);
  16409. jx9_array_add_strkey_elem(pArray, "gid", pWorker);
  16410. jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
  16411. /* size */
  16412. jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileSizeHigh << 32) | sInfo.nFileSizeLow));
  16413. jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
  16414. /* atime */
  16415. jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime));
  16416. jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
  16417. /* mtime */
  16418. jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime));
  16419. jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
  16420. /* ctime */
  16421. jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftCreationTime));
  16422. jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
  16423. /* blksize, blocks */
  16424. jx9_value_int(pWorker, 0);
  16425. jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
  16426. jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
  16427. return JX9_OK;
  16428. }
  16429. /* Export the file:// stream */
  16430. static const jx9_io_stream sWinFileStream = {
  16431. "file", /* Stream name */
  16432. JX9_IO_STREAM_VERSION,
  16433. WinFile_Open, /* xOpen */
  16434. WinDir_Open, /* xOpenDir */
  16435. WinFile_Close, /* xClose */
  16436. WinDir_Close, /* xCloseDir */
  16437. WinFile_Read, /* xRead */
  16438. WinDir_Read, /* xReadDir */
  16439. WinFile_Write, /* xWrite */
  16440. WinFile_Seek, /* xSeek */
  16441. WinFile_Lock, /* xLock */
  16442. WinDir_RewindDir, /* xRewindDir */
  16443. WinFile_Tell, /* xTell */
  16444. WinFile_Trunc, /* xTrunc */
  16445. WinFile_Sync, /* xSeek */
  16446. WinFile_Stat /* xStat */
  16447. };
  16448. #elif defined(__UNIXES__)
  16449. /*
  16450. * UNIX VFS implementation for the JX9 engine.
  16451. * Authors:
  16452. * Symisc Systems, devel@symisc.net.
  16453. * Copyright (C) Symisc Systems, http://jx9.symisc.net
  16454. * Status:
  16455. * Stable.
  16456. */
  16457. #include <sys/types.h>
  16458. #include <limits.h>
  16459. #include <fcntl.h>
  16460. #include <unistd.h>
  16461. #include <sys/uio.h>
  16462. #include <sys/stat.h>
  16463. #include <sys/mman.h>
  16464. #include <sys/file.h>
  16465. #include <pwd.h>
  16466. #include <grp.h>
  16467. #include <dirent.h>
  16468. #include <utime.h>
  16469. #include <stdio.h>
  16470. #include <stdlib.h>
  16471. /* int (*xchdir)(const char *) */
  16472. static int UnixVfs_chdir(const char *zPath)
  16473. {
  16474. int rc;
  16475. rc = chdir(zPath);
  16476. return rc == 0 ? JX9_OK : -1;
  16477. }
  16478. /* int (*xGetcwd)(jx9_context *) */
  16479. static int UnixVfs_getcwd(jx9_context *pCtx)
  16480. {
  16481. char zBuf[4096];
  16482. char *zDir;
  16483. /* Get the current directory */
  16484. zDir = getcwd(zBuf, sizeof(zBuf));
  16485. if( zDir == 0 ){
  16486. return -1;
  16487. }
  16488. jx9_result_string(pCtx, zDir, -1/*Compute length automatically*/);
  16489. return JX9_OK;
  16490. }
  16491. /* int (*xMkdir)(const char *, int, int) */
  16492. static int UnixVfs_mkdir(const char *zPath, int mode, int recursive)
  16493. {
  16494. int rc;
  16495. rc = mkdir(zPath, mode);
  16496. recursive = 0; /* cc warning */
  16497. return rc == 0 ? JX9_OK : -1;
  16498. }
  16499. /* int (*xRmdir)(const char *) */
  16500. static int UnixVfs_rmdir(const char *zPath)
  16501. {
  16502. int rc;
  16503. rc = rmdir(zPath);
  16504. return rc == 0 ? JX9_OK : -1;
  16505. }
  16506. /* int (*xIsdir)(const char *) */
  16507. static int UnixVfs_isdir(const char *zPath)
  16508. {
  16509. struct stat st;
  16510. int rc;
  16511. rc = stat(zPath, &st);
  16512. if( rc != 0 ){
  16513. return -1;
  16514. }
  16515. rc = S_ISDIR(st.st_mode);
  16516. return rc ? JX9_OK : -1 ;
  16517. }
  16518. /* int (*xRename)(const char *, const char *) */
  16519. static int UnixVfs_Rename(const char *zOld, const char *zNew)
  16520. {
  16521. int rc;
  16522. rc = rename(zOld, zNew);
  16523. return rc == 0 ? JX9_OK : -1;
  16524. }
  16525. /* int (*xRealpath)(const char *, jx9_context *) */
  16526. static int UnixVfs_Realpath(const char *zPath, jx9_context *pCtx)
  16527. {
  16528. #ifndef JX9_UNIX_OLD_LIBC
  16529. char *zReal;
  16530. zReal = realpath(zPath, 0);
  16531. if( zReal == 0 ){
  16532. return -1;
  16533. }
  16534. jx9_result_string(pCtx, zReal, -1/*Compute length automatically*/);
  16535. /* Release the allocated buffer */
  16536. free(zReal);
  16537. return JX9_OK;
  16538. #else
  16539. zPath = 0; /* cc warning */
  16540. pCtx = 0;
  16541. return -1;
  16542. #endif
  16543. }
  16544. /* int (*xSleep)(unsigned int) */
  16545. static int UnixVfs_Sleep(unsigned int uSec)
  16546. {
  16547. usleep(uSec);
  16548. return JX9_OK;
  16549. }
  16550. /* int (*xUnlink)(const char *) */
  16551. static int UnixVfs_unlink(const char *zPath)
  16552. {
  16553. int rc;
  16554. rc = unlink(zPath);
  16555. return rc == 0 ? JX9_OK : -1 ;
  16556. }
  16557. /* int (*xFileExists)(const char *) */
  16558. static int UnixVfs_FileExists(const char *zPath)
  16559. {
  16560. int rc;
  16561. rc = access(zPath, F_OK);
  16562. return rc == 0 ? JX9_OK : -1;
  16563. }
  16564. /* jx9_int64 (*xFileSize)(const char *) */
  16565. static jx9_int64 UnixVfs_FileSize(const char *zPath)
  16566. {
  16567. struct stat st;
  16568. int rc;
  16569. rc = stat(zPath, &st);
  16570. if( rc != 0 ){
  16571. return -1;
  16572. }
  16573. return (jx9_int64)st.st_size;
  16574. }
  16575. /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
  16576. static int UnixVfs_Touch(const char *zPath, jx9_int64 touch_time, jx9_int64 access_time)
  16577. {
  16578. struct utimbuf ut;
  16579. int rc;
  16580. ut.actime = (time_t)access_time;
  16581. ut.modtime = (time_t)touch_time;
  16582. rc = utime(zPath, &ut);
  16583. if( rc != 0 ){
  16584. return -1;
  16585. }
  16586. return JX9_OK;
  16587. }
  16588. /* jx9_int64 (*xFileAtime)(const char *) */
  16589. static jx9_int64 UnixVfs_FileAtime(const char *zPath)
  16590. {
  16591. struct stat st;
  16592. int rc;
  16593. rc = stat(zPath, &st);
  16594. if( rc != 0 ){
  16595. return -1;
  16596. }
  16597. return (jx9_int64)st.st_atime;
  16598. }
  16599. /* jx9_int64 (*xFileMtime)(const char *) */
  16600. static jx9_int64 UnixVfs_FileMtime(const char *zPath)
  16601. {
  16602. struct stat st;
  16603. int rc;
  16604. rc = stat(zPath, &st);
  16605. if( rc != 0 ){
  16606. return -1;
  16607. }
  16608. return (jx9_int64)st.st_mtime;
  16609. }
  16610. /* jx9_int64 (*xFileCtime)(const char *) */
  16611. static jx9_int64 UnixVfs_FileCtime(const char *zPath)
  16612. {
  16613. struct stat st;
  16614. int rc;
  16615. rc = stat(zPath, &st);
  16616. if( rc != 0 ){
  16617. return -1;
  16618. }
  16619. return (jx9_int64)st.st_ctime;
  16620. }
  16621. /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
  16622. static int UnixVfs_Stat(const char *zPath, jx9_value *pArray, jx9_value *pWorker)
  16623. {
  16624. struct stat st;
  16625. int rc;
  16626. rc = stat(zPath, &st);
  16627. if( rc != 0 ){
  16628. return -1;
  16629. }
  16630. /* dev */
  16631. jx9_value_int64(pWorker, (jx9_int64)st.st_dev);
  16632. jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
  16633. /* ino */
  16634. jx9_value_int64(pWorker, (jx9_int64)st.st_ino);
  16635. jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
  16636. /* mode */
  16637. jx9_value_int(pWorker, (int)st.st_mode);
  16638. jx9_array_add_strkey_elem(pArray, "mode", pWorker);
  16639. /* nlink */
  16640. jx9_value_int(pWorker, (int)st.st_nlink);
  16641. jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
  16642. /* uid, gid, rdev */
  16643. jx9_value_int(pWorker, (int)st.st_uid);
  16644. jx9_array_add_strkey_elem(pArray, "uid", pWorker);
  16645. jx9_value_int(pWorker, (int)st.st_gid);
  16646. jx9_array_add_strkey_elem(pArray, "gid", pWorker);
  16647. jx9_value_int(pWorker, (int)st.st_rdev);
  16648. jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
  16649. /* size */
  16650. jx9_value_int64(pWorker, (jx9_int64)st.st_size);
  16651. jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
  16652. /* atime */
  16653. jx9_value_int64(pWorker, (jx9_int64)st.st_atime);
  16654. jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
  16655. /* mtime */
  16656. jx9_value_int64(pWorker, (jx9_int64)st.st_mtime);
  16657. jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
  16658. /* ctime */
  16659. jx9_value_int64(pWorker, (jx9_int64)st.st_ctime);
  16660. jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
  16661. /* blksize, blocks */
  16662. jx9_value_int(pWorker, (int)st.st_blksize);
  16663. jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
  16664. jx9_value_int(pWorker, (int)st.st_blocks);
  16665. jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
  16666. return JX9_OK;
  16667. }
  16668. /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
  16669. static int UnixVfs_lStat(const char *zPath, jx9_value *pArray, jx9_value *pWorker)
  16670. {
  16671. struct stat st;
  16672. int rc;
  16673. rc = lstat(zPath, &st);
  16674. if( rc != 0 ){
  16675. return -1;
  16676. }
  16677. /* dev */
  16678. jx9_value_int64(pWorker, (jx9_int64)st.st_dev);
  16679. jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
  16680. /* ino */
  16681. jx9_value_int64(pWorker, (jx9_int64)st.st_ino);
  16682. jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
  16683. /* mode */
  16684. jx9_value_int(pWorker, (int)st.st_mode);
  16685. jx9_array_add_strkey_elem(pArray, "mode", pWorker);
  16686. /* nlink */
  16687. jx9_value_int(pWorker, (int)st.st_nlink);
  16688. jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
  16689. /* uid, gid, rdev */
  16690. jx9_value_int(pWorker, (int)st.st_uid);
  16691. jx9_array_add_strkey_elem(pArray, "uid", pWorker);
  16692. jx9_value_int(pWorker, (int)st.st_gid);
  16693. jx9_array_add_strkey_elem(pArray, "gid", pWorker);
  16694. jx9_value_int(pWorker, (int)st.st_rdev);
  16695. jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
  16696. /* size */
  16697. jx9_value_int64(pWorker, (jx9_int64)st.st_size);
  16698. jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
  16699. /* atime */
  16700. jx9_value_int64(pWorker, (jx9_int64)st.st_atime);
  16701. jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
  16702. /* mtime */
  16703. jx9_value_int64(pWorker, (jx9_int64)st.st_mtime);
  16704. jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
  16705. /* ctime */
  16706. jx9_value_int64(pWorker, (jx9_int64)st.st_ctime);
  16707. jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
  16708. /* blksize, blocks */
  16709. jx9_value_int(pWorker, (int)st.st_blksize);
  16710. jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
  16711. jx9_value_int(pWorker, (int)st.st_blocks);
  16712. jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
  16713. return JX9_OK;
  16714. }
  16715. /* int (*xChmod)(const char *, int) */
  16716. static int UnixVfs_Chmod(const char *zPath, int mode)
  16717. {
  16718. int rc;
  16719. rc = chmod(zPath, (mode_t)mode);
  16720. return rc == 0 ? JX9_OK : - 1;
  16721. }
  16722. /* int (*xChown)(const char *, const char *) */
  16723. static int UnixVfs_Chown(const char *zPath, const char *zUser)
  16724. {
  16725. #ifndef JX9_UNIX_STATIC_BUILD
  16726. struct passwd *pwd;
  16727. uid_t uid;
  16728. int rc;
  16729. pwd = getpwnam(zUser); /* Try getting UID for username */
  16730. if (pwd == 0) {
  16731. return -1;
  16732. }
  16733. uid = pwd->pw_uid;
  16734. rc = chown(zPath, uid, -1);
  16735. return rc == 0 ? JX9_OK : -1;
  16736. #else
  16737. SXUNUSED(zPath);
  16738. SXUNUSED(zUser);
  16739. return -1;
  16740. #endif /* JX9_UNIX_STATIC_BUILD */
  16741. }
  16742. /* int (*xChgrp)(const char *, const char *) */
  16743. static int UnixVfs_Chgrp(const char *zPath, const char *zGroup)
  16744. {
  16745. #ifndef JX9_UNIX_STATIC_BUILD
  16746. struct group *group;
  16747. gid_t gid;
  16748. int rc;
  16749. group = getgrnam(zGroup);
  16750. if (group == 0) {
  16751. return -1;
  16752. }
  16753. gid = group->gr_gid;
  16754. rc = chown(zPath, -1, gid);
  16755. return rc == 0 ? JX9_OK : -1;
  16756. #else
  16757. SXUNUSED(zPath);
  16758. SXUNUSED(zGroup);
  16759. return -1;
  16760. #endif /* JX9_UNIX_STATIC_BUILD */
  16761. }
  16762. /* int (*xIsfile)(const char *) */
  16763. static int UnixVfs_isfile(const char *zPath)
  16764. {
  16765. struct stat st;
  16766. int rc;
  16767. rc = stat(zPath, &st);
  16768. if( rc != 0 ){
  16769. return -1;
  16770. }
  16771. rc = S_ISREG(st.st_mode);
  16772. return rc ? JX9_OK : -1 ;
  16773. }
  16774. /* int (*xIslink)(const char *) */
  16775. static int UnixVfs_islink(const char *zPath)
  16776. {
  16777. struct stat st;
  16778. int rc;
  16779. rc = stat(zPath, &st);
  16780. if( rc != 0 ){
  16781. return -1;
  16782. }
  16783. rc = S_ISLNK(st.st_mode);
  16784. return rc ? JX9_OK : -1 ;
  16785. }
  16786. /* int (*xReadable)(const char *) */
  16787. static int UnixVfs_isreadable(const char *zPath)
  16788. {
  16789. int rc;
  16790. rc = access(zPath, R_OK);
  16791. return rc == 0 ? JX9_OK : -1;
  16792. }
  16793. /* int (*xWritable)(const char *) */
  16794. static int UnixVfs_iswritable(const char *zPath)
  16795. {
  16796. int rc;
  16797. rc = access(zPath, W_OK);
  16798. return rc == 0 ? JX9_OK : -1;
  16799. }
  16800. /* int (*xExecutable)(const char *) */
  16801. static int UnixVfs_isexecutable(const char *zPath)
  16802. {
  16803. int rc;
  16804. rc = access(zPath, X_OK);
  16805. return rc == 0 ? JX9_OK : -1;
  16806. }
  16807. /* int (*xFiletype)(const char *, jx9_context *) */
  16808. static int UnixVfs_Filetype(const char *zPath, jx9_context *pCtx)
  16809. {
  16810. struct stat st;
  16811. int rc;
  16812. rc = stat(zPath, &st);
  16813. if( rc != 0 ){
  16814. /* Expand 'unknown' */
  16815. jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
  16816. return -1;
  16817. }
  16818. if(S_ISREG(st.st_mode) ){
  16819. jx9_result_string(pCtx, "file", sizeof("file")-1);
  16820. }else if(S_ISDIR(st.st_mode)){
  16821. jx9_result_string(pCtx, "dir", sizeof("dir")-1);
  16822. }else if(S_ISLNK(st.st_mode)){
  16823. jx9_result_string(pCtx, "link", sizeof("link")-1);
  16824. }else if(S_ISBLK(st.st_mode)){
  16825. jx9_result_string(pCtx, "block", sizeof("block")-1);
  16826. }else if(S_ISSOCK(st.st_mode)){
  16827. jx9_result_string(pCtx, "socket", sizeof("socket")-1);
  16828. }else if(S_ISFIFO(st.st_mode)){
  16829. jx9_result_string(pCtx, "fifo", sizeof("fifo")-1);
  16830. }else{
  16831. jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
  16832. }
  16833. return JX9_OK;
  16834. }
  16835. /* int (*xGetenv)(const char *, jx9_context *) */
  16836. static int UnixVfs_Getenv(const char *zVar, jx9_context *pCtx)
  16837. {
  16838. char *zEnv;
  16839. zEnv = getenv(zVar);
  16840. if( zEnv == 0 ){
  16841. return -1;
  16842. }
  16843. jx9_result_string(pCtx, zEnv, -1/*Compute length automatically*/);
  16844. return JX9_OK;
  16845. }
  16846. /* int (*xSetenv)(const char *, const char *) */
  16847. static int UnixVfs_Setenv(const char *zName, const char *zValue)
  16848. {
  16849. int rc;
  16850. rc = setenv(zName, zValue, 1);
  16851. return rc == 0 ? JX9_OK : -1;
  16852. }
  16853. /* int (*xMmap)(const char *, void **, jx9_int64 *) */
  16854. static int UnixVfs_Mmap(const char *zPath, void **ppMap, jx9_int64 *pSize)
  16855. {
  16856. struct stat st;
  16857. void *pMap;
  16858. int fd;
  16859. int rc;
  16860. /* Open the file in a read-only mode */
  16861. fd = open(zPath, O_RDONLY);
  16862. if( fd < 0 ){
  16863. return -1;
  16864. }
  16865. /* stat the handle */
  16866. fstat(fd, &st);
  16867. /* Obtain a memory view of the whole file */
  16868. pMap = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE|MAP_FILE, fd, 0);
  16869. rc = JX9_OK;
  16870. if( pMap == MAP_FAILED ){
  16871. rc = -1;
  16872. }else{
  16873. /* Point to the memory view */
  16874. *ppMap = pMap;
  16875. *pSize = (jx9_int64)st.st_size;
  16876. }
  16877. close(fd);
  16878. return rc;
  16879. }
  16880. /* void (*xUnmap)(void *, jx9_int64) */
  16881. static void UnixVfs_Unmap(void *pView, jx9_int64 nSize)
  16882. {
  16883. munmap(pView, (size_t)nSize);
  16884. }
  16885. /* void (*xTempDir)(jx9_context *) */
  16886. static void UnixVfs_TempDir(jx9_context *pCtx)
  16887. {
  16888. static const char *azDirs[] = {
  16889. "/var/tmp",
  16890. "/usr/tmp",
  16891. "/usr/local/tmp"
  16892. };
  16893. unsigned int i;
  16894. struct stat buf;
  16895. const char *zDir;
  16896. zDir = getenv("TMPDIR");
  16897. if( zDir && zDir[0] != 0 && !access(zDir, 07) ){
  16898. jx9_result_string(pCtx, zDir, -1);
  16899. return;
  16900. }
  16901. for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){
  16902. zDir=azDirs[i];
  16903. if( zDir==0 ) continue;
  16904. if( stat(zDir, &buf) ) continue;
  16905. if( !S_ISDIR(buf.st_mode) ) continue;
  16906. if( access(zDir, 07) ) continue;
  16907. /* Got one */
  16908. jx9_result_string(pCtx, zDir, -1);
  16909. return;
  16910. }
  16911. /* Default temp dir */
  16912. jx9_result_string(pCtx, "/tmp", (int)sizeof("/tmp")-1);
  16913. }
  16914. /* unsigned int (*xProcessId)(void) */
  16915. static unsigned int UnixVfs_ProcessId(void)
  16916. {
  16917. return (unsigned int)getpid();
  16918. }
  16919. /* int (*xUid)(void) */
  16920. static int UnixVfs_uid(void)
  16921. {
  16922. return (int)getuid();
  16923. }
  16924. /* int (*xGid)(void) */
  16925. static int UnixVfs_gid(void)
  16926. {
  16927. return (int)getgid();
  16928. }
  16929. /* int (*xUmask)(int) */
  16930. static int UnixVfs_Umask(int new_mask)
  16931. {
  16932. int old_mask;
  16933. old_mask = umask(new_mask);
  16934. return old_mask;
  16935. }
  16936. /* void (*xUsername)(jx9_context *) */
  16937. static void UnixVfs_Username(jx9_context *pCtx)
  16938. {
  16939. #ifndef JX9_UNIX_STATIC_BUILD
  16940. struct passwd *pwd;
  16941. uid_t uid;
  16942. uid = getuid();
  16943. pwd = getpwuid(uid); /* Try getting UID for username */
  16944. if (pwd == 0) {
  16945. return;
  16946. }
  16947. /* Return the username */
  16948. jx9_result_string(pCtx, pwd->pw_name, -1);
  16949. #else
  16950. jx9_result_string(pCtx, "Unknown", -1);
  16951. #endif /* JX9_UNIX_STATIC_BUILD */
  16952. return;
  16953. }
  16954. /* int (*xLink)(const char *, const char *, int) */
  16955. static int UnixVfs_link(const char *zSrc, const char *zTarget, int is_sym)
  16956. {
  16957. int rc;
  16958. if( is_sym ){
  16959. /* Symbolic link */
  16960. rc = symlink(zSrc, zTarget);
  16961. }else{
  16962. /* Hard link */
  16963. rc = link(zSrc, zTarget);
  16964. }
  16965. return rc == 0 ? JX9_OK : -1;
  16966. }
  16967. /* int (*xChroot)(const char *) */
  16968. static int UnixVfs_chroot(const char *zRootDir)
  16969. {
  16970. int rc;
  16971. rc = chroot(zRootDir);
  16972. return rc == 0 ? JX9_OK : -1;
  16973. }
  16974. /* Export the UNIX vfs */
  16975. static const jx9_vfs sUnixVfs = {
  16976. "Unix_vfs",
  16977. JX9_VFS_VERSION,
  16978. UnixVfs_chdir, /* int (*xChdir)(const char *) */
  16979. UnixVfs_chroot, /* int (*xChroot)(const char *); */
  16980. UnixVfs_getcwd, /* int (*xGetcwd)(jx9_context *) */
  16981. UnixVfs_mkdir, /* int (*xMkdir)(const char *, int, int) */
  16982. UnixVfs_rmdir, /* int (*xRmdir)(const char *) */
  16983. UnixVfs_isdir, /* int (*xIsdir)(const char *) */
  16984. UnixVfs_Rename, /* int (*xRename)(const char *, const char *) */
  16985. UnixVfs_Realpath, /*int (*xRealpath)(const char *, jx9_context *)*/
  16986. UnixVfs_Sleep, /* int (*xSleep)(unsigned int) */
  16987. UnixVfs_unlink, /* int (*xUnlink)(const char *) */
  16988. UnixVfs_FileExists, /* int (*xFileExists)(const char *) */
  16989. UnixVfs_Chmod, /*int (*xChmod)(const char *, int)*/
  16990. UnixVfs_Chown, /*int (*xChown)(const char *, const char *)*/
  16991. UnixVfs_Chgrp, /*int (*xChgrp)(const char *, const char *)*/
  16992. 0, /* jx9_int64 (*xFreeSpace)(const char *) */
  16993. 0, /* jx9_int64 (*xTotalSpace)(const char *) */
  16994. UnixVfs_FileSize, /* jx9_int64 (*xFileSize)(const char *) */
  16995. UnixVfs_FileAtime, /* jx9_int64 (*xFileAtime)(const char *) */
  16996. UnixVfs_FileMtime, /* jx9_int64 (*xFileMtime)(const char *) */
  16997. UnixVfs_FileCtime, /* jx9_int64 (*xFileCtime)(const char *) */
  16998. UnixVfs_Stat, /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
  16999. UnixVfs_lStat, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
  17000. UnixVfs_isfile, /* int (*xIsfile)(const char *) */
  17001. UnixVfs_islink, /* int (*xIslink)(const char *) */
  17002. UnixVfs_isreadable, /* int (*xReadable)(const char *) */
  17003. UnixVfs_iswritable, /* int (*xWritable)(const char *) */
  17004. UnixVfs_isexecutable, /* int (*xExecutable)(const char *) */
  17005. UnixVfs_Filetype, /* int (*xFiletype)(const char *, jx9_context *) */
  17006. UnixVfs_Getenv, /* int (*xGetenv)(const char *, jx9_context *) */
  17007. UnixVfs_Setenv, /* int (*xSetenv)(const char *, const char *) */
  17008. UnixVfs_Touch, /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
  17009. UnixVfs_Mmap, /* int (*xMmap)(const char *, void **, jx9_int64 *) */
  17010. UnixVfs_Unmap, /* void (*xUnmap)(void *, jx9_int64); */
  17011. UnixVfs_link, /* int (*xLink)(const char *, const char *, int) */
  17012. UnixVfs_Umask, /* int (*xUmask)(int) */
  17013. UnixVfs_TempDir, /* void (*xTempDir)(jx9_context *) */
  17014. UnixVfs_ProcessId, /* unsigned int (*xProcessId)(void) */
  17015. UnixVfs_uid, /* int (*xUid)(void) */
  17016. UnixVfs_gid, /* int (*xGid)(void) */
  17017. UnixVfs_Username, /* void (*xUsername)(jx9_context *) */
  17018. 0 /* int (*xExec)(const char *, jx9_context *) */
  17019. };
  17020. /* UNIX File IO */
  17021. #define JX9_UNIX_OPEN_MODE 0640 /* Default open mode */
  17022. /* int (*xOpen)(const char *, int, jx9_value *, void **) */
  17023. static int UnixFile_Open(const char *zPath, int iOpenMode, jx9_value *pResource, void **ppHandle)
  17024. {
  17025. int iOpen = O_RDONLY;
  17026. int fd;
  17027. /* Set the desired flags according to the open mode */
  17028. if( iOpenMode & JX9_IO_OPEN_CREATE ){
  17029. /* Open existing file, or create if it doesn't exist */
  17030. iOpen = O_CREAT;
  17031. if( iOpenMode & JX9_IO_OPEN_TRUNC ){
  17032. /* If the specified file exists and is writable, the function overwrites the file */
  17033. iOpen |= O_TRUNC;
  17034. SXUNUSED(pResource); /* cc warning */
  17035. }
  17036. }else if( iOpenMode & JX9_IO_OPEN_EXCL ){
  17037. /* Creates a new file, only if it does not already exist.
  17038. * If the file exists, it fails.
  17039. */
  17040. iOpen = O_CREAT|O_EXCL;
  17041. }else if( iOpenMode & JX9_IO_OPEN_TRUNC ){
  17042. /* Opens a file and truncates it so that its size is zero bytes
  17043. * The file must exist.
  17044. */
  17045. iOpen = O_RDWR|O_TRUNC;
  17046. }
  17047. if( iOpenMode & JX9_IO_OPEN_RDWR ){
  17048. /* Read+Write access */
  17049. iOpen &= ~O_RDONLY;
  17050. iOpen |= O_RDWR;
  17051. }else if( iOpenMode & JX9_IO_OPEN_WRONLY ){
  17052. /* Write only access */
  17053. iOpen &= ~O_RDONLY;
  17054. iOpen |= O_WRONLY;
  17055. }
  17056. if( iOpenMode & JX9_IO_OPEN_APPEND ){
  17057. /* Append mode */
  17058. iOpen |= O_APPEND;
  17059. }
  17060. #ifdef O_TEMP
  17061. if( iOpenMode & JX9_IO_OPEN_TEMP ){
  17062. /* File is temporary */
  17063. iOpen |= O_TEMP;
  17064. }
  17065. #endif
  17066. /* Open the file now */
  17067. fd = open(zPath, iOpen, JX9_UNIX_OPEN_MODE);
  17068. if( fd < 0 ){
  17069. /* IO error */
  17070. return -1;
  17071. }
  17072. /* Save the handle */
  17073. *ppHandle = SX_INT_TO_PTR(fd);
  17074. return JX9_OK;
  17075. }
  17076. /* int (*xOpenDir)(const char *, jx9_value *, void **) */
  17077. static int UnixDir_Open(const char *zPath, jx9_value *pResource, void **ppHandle)
  17078. {
  17079. DIR *pDir;
  17080. /* Open the target directory */
  17081. pDir = opendir(zPath);
  17082. if( pDir == 0 ){
  17083. pResource = 0; /* Compiler warning */
  17084. return -1;
  17085. }
  17086. /* Save our structure */
  17087. *ppHandle = pDir;
  17088. return JX9_OK;
  17089. }
  17090. /* void (*xCloseDir)(void *) */
  17091. static void UnixDir_Close(void *pUserData)
  17092. {
  17093. closedir((DIR *)pUserData);
  17094. }
  17095. /* void (*xClose)(void *); */
  17096. static void UnixFile_Close(void *pUserData)
  17097. {
  17098. close(SX_PTR_TO_INT(pUserData));
  17099. }
  17100. /* int (*xReadDir)(void *, jx9_context *) */
  17101. static int UnixDir_Read(void *pUserData, jx9_context *pCtx)
  17102. {
  17103. DIR *pDir = (DIR *)pUserData;
  17104. struct dirent *pEntry;
  17105. char *zName = 0; /* cc warning */
  17106. sxu32 n = 0;
  17107. for(;;){
  17108. pEntry = readdir(pDir);
  17109. if( pEntry == 0 ){
  17110. /* No more entries to process */
  17111. return -1;
  17112. }
  17113. zName = pEntry->d_name;
  17114. n = SyStrlen(zName);
  17115. /* Ignore '.' && '..' */
  17116. if( n > sizeof("..")-1 || zName[0] != '.' || ( n == sizeof("..")-1 && zName[1] != '.') ){
  17117. break;
  17118. }
  17119. /* Next entry */
  17120. }
  17121. /* Return the current file name */
  17122. jx9_result_string(pCtx, zName, (int)n);
  17123. return JX9_OK;
  17124. }
  17125. /* void (*xRewindDir)(void *) */
  17126. static void UnixDir_Rewind(void *pUserData)
  17127. {
  17128. rewinddir((DIR *)pUserData);
  17129. }
  17130. /* jx9_int64 (*xRead)(void *, void *, jx9_int64); */
  17131. static jx9_int64 UnixFile_Read(void *pUserData, void *pBuffer, jx9_int64 nDatatoRead)
  17132. {
  17133. ssize_t nRd;
  17134. nRd = read(SX_PTR_TO_INT(pUserData), pBuffer, (size_t)nDatatoRead);
  17135. if( nRd < 1 ){
  17136. /* EOF or IO error */
  17137. return -1;
  17138. }
  17139. return (jx9_int64)nRd;
  17140. }
  17141. /* jx9_int64 (*xWrite)(void *, const void *, jx9_int64); */
  17142. static jx9_int64 UnixFile_Write(void *pUserData, const void *pBuffer, jx9_int64 nWrite)
  17143. {
  17144. const char *zData = (const char *)pBuffer;
  17145. int fd = SX_PTR_TO_INT(pUserData);
  17146. jx9_int64 nCount;
  17147. ssize_t nWr;
  17148. nCount = 0;
  17149. for(;;){
  17150. if( nWrite < 1 ){
  17151. break;
  17152. }
  17153. nWr = write(fd, zData, (size_t)nWrite);
  17154. if( nWr < 1 ){
  17155. /* IO error */
  17156. break;
  17157. }
  17158. nWrite -= nWr;
  17159. nCount += nWr;
  17160. zData += nWr;
  17161. }
  17162. if( nWrite > 0 ){
  17163. return -1;
  17164. }
  17165. return nCount;
  17166. }
  17167. /* int (*xSeek)(void *, jx9_int64, int) */
  17168. static int UnixFile_Seek(void *pUserData, jx9_int64 iOfft, int whence)
  17169. {
  17170. off_t iNew;
  17171. switch(whence){
  17172. case 1:/*SEEK_CUR*/
  17173. whence = SEEK_CUR;
  17174. break;
  17175. case 2: /* SEEK_END */
  17176. whence = SEEK_END;
  17177. break;
  17178. case 0: /* SEEK_SET */
  17179. default:
  17180. whence = SEEK_SET;
  17181. break;
  17182. }
  17183. iNew = lseek(SX_PTR_TO_INT(pUserData), (off_t)iOfft, whence);
  17184. if( iNew < 0 ){
  17185. return -1;
  17186. }
  17187. return JX9_OK;
  17188. }
  17189. /* int (*xLock)(void *, int) */
  17190. static int UnixFile_Lock(void *pUserData, int lock_type)
  17191. {
  17192. int fd = SX_PTR_TO_INT(pUserData);
  17193. int rc = JX9_OK; /* cc warning */
  17194. if( lock_type < 0 ){
  17195. /* Unlock the file */
  17196. rc = flock(fd, LOCK_UN);
  17197. }else{
  17198. if( lock_type == 1 ){
  17199. /* Exculsive lock */
  17200. rc = flock(fd, LOCK_EX);
  17201. }else{
  17202. /* Shared lock */
  17203. rc = flock(fd, LOCK_SH);
  17204. }
  17205. }
  17206. return !rc ? JX9_OK : -1;
  17207. }
  17208. /* jx9_int64 (*xTell)(void *) */
  17209. static jx9_int64 UnixFile_Tell(void *pUserData)
  17210. {
  17211. off_t iNew;
  17212. iNew = lseek(SX_PTR_TO_INT(pUserData), 0, SEEK_CUR);
  17213. return (jx9_int64)iNew;
  17214. }
  17215. /* int (*xTrunc)(void *, jx9_int64) */
  17216. static int UnixFile_Trunc(void *pUserData, jx9_int64 nOfft)
  17217. {
  17218. int rc;
  17219. rc = ftruncate(SX_PTR_TO_INT(pUserData), (off_t)nOfft);
  17220. if( rc != 0 ){
  17221. return -1;
  17222. }
  17223. return JX9_OK;
  17224. }
  17225. /* int (*xSync)(void *); */
  17226. static int UnixFile_Sync(void *pUserData)
  17227. {
  17228. int rc;
  17229. rc = fsync(SX_PTR_TO_INT(pUserData));
  17230. return rc == 0 ? JX9_OK : - 1;
  17231. }
  17232. /* int (*xStat)(void *, jx9_value *, jx9_value *) */
  17233. static int UnixFile_Stat(void *pUserData, jx9_value *pArray, jx9_value *pWorker)
  17234. {
  17235. struct stat st;
  17236. int rc;
  17237. rc = fstat(SX_PTR_TO_INT(pUserData), &st);
  17238. if( rc != 0 ){
  17239. return -1;
  17240. }
  17241. /* dev */
  17242. jx9_value_int64(pWorker, (jx9_int64)st.st_dev);
  17243. jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
  17244. /* ino */
  17245. jx9_value_int64(pWorker, (jx9_int64)st.st_ino);
  17246. jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
  17247. /* mode */
  17248. jx9_value_int(pWorker, (int)st.st_mode);
  17249. jx9_array_add_strkey_elem(pArray, "mode", pWorker);
  17250. /* nlink */
  17251. jx9_value_int(pWorker, (int)st.st_nlink);
  17252. jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
  17253. /* uid, gid, rdev */
  17254. jx9_value_int(pWorker, (int)st.st_uid);
  17255. jx9_array_add_strkey_elem(pArray, "uid", pWorker);
  17256. jx9_value_int(pWorker, (int)st.st_gid);
  17257. jx9_array_add_strkey_elem(pArray, "gid", pWorker);
  17258. jx9_value_int(pWorker, (int)st.st_rdev);
  17259. jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
  17260. /* size */
  17261. jx9_value_int64(pWorker, (jx9_int64)st.st_size);
  17262. jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
  17263. /* atime */
  17264. jx9_value_int64(pWorker, (jx9_int64)st.st_atime);
  17265. jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
  17266. /* mtime */
  17267. jx9_value_int64(pWorker, (jx9_int64)st.st_mtime);
  17268. jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
  17269. /* ctime */
  17270. jx9_value_int64(pWorker, (jx9_int64)st.st_ctime);
  17271. jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
  17272. /* blksize, blocks */
  17273. jx9_value_int(pWorker, (int)st.st_blksize);
  17274. jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
  17275. jx9_value_int(pWorker, (int)st.st_blocks);
  17276. jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
  17277. return JX9_OK;
  17278. }
  17279. /* Export the file:// stream */
  17280. static const jx9_io_stream sUnixFileStream = {
  17281. "file", /* Stream name */
  17282. JX9_IO_STREAM_VERSION,
  17283. UnixFile_Open, /* xOpen */
  17284. UnixDir_Open, /* xOpenDir */
  17285. UnixFile_Close, /* xClose */
  17286. UnixDir_Close, /* xCloseDir */
  17287. UnixFile_Read, /* xRead */
  17288. UnixDir_Read, /* xReadDir */
  17289. UnixFile_Write, /* xWrite */
  17290. UnixFile_Seek, /* xSeek */
  17291. UnixFile_Lock, /* xLock */
  17292. UnixDir_Rewind, /* xRewindDir */
  17293. UnixFile_Tell, /* xTell */
  17294. UnixFile_Trunc, /* xTrunc */
  17295. UnixFile_Sync, /* xSeek */
  17296. UnixFile_Stat /* xStat */
  17297. };
  17298. #endif /* __WINNT__/__UNIXES__ */
  17299. #endif /* JX9_DISABLE_DISK_IO */
  17300. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  17301. /*
  17302. * Export the builtin vfs.
  17303. * Return a pointer to the builtin vfs if available.
  17304. * Otherwise return the null_vfs [i.e: a no-op vfs] instead.
  17305. * Note:
  17306. * The built-in vfs is always available for Windows/UNIX systems.
  17307. * Note:
  17308. * If the engine is compiled with the JX9_DISABLE_DISK_IO/JX9_DISABLE_BUILTIN_FUNC
  17309. * directives defined then this function return the null_vfs instead.
  17310. */
  17311. JX9_PRIVATE const jx9_vfs * jx9ExportBuiltinVfs(void)
  17312. {
  17313. #ifndef JX9_DISABLE_BUILTIN_FUNC
  17314. #ifdef JX9_DISABLE_DISK_IO
  17315. return &null_vfs;
  17316. #else
  17317. #ifdef __WINNT__
  17318. return &sWinVfs;
  17319. #elif defined(__UNIXES__)
  17320. return &sUnixVfs;
  17321. #else
  17322. return &null_vfs;
  17323. #endif /* __WINNT__/__UNIXES__ */
  17324. #endif /*JX9_DISABLE_DISK_IO*/
  17325. #else
  17326. return &null_vfs;
  17327. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  17328. }
  17329. #ifndef JX9_DISABLE_BUILTIN_FUNC
  17330. #ifndef JX9_DISABLE_DISK_IO
  17331. /*
  17332. * The following defines are mostly used by the UNIX built and have
  17333. * no particular meaning on windows.
  17334. */
  17335. #ifndef STDIN_FILENO
  17336. #define STDIN_FILENO 0
  17337. #endif
  17338. #ifndef STDOUT_FILENO
  17339. #define STDOUT_FILENO 1
  17340. #endif
  17341. #ifndef STDERR_FILENO
  17342. #define STDERR_FILENO 2
  17343. #endif
  17344. /*
  17345. * jx9:// Accessing various I/O streams
  17346. * According to the JX9 langage reference manual
  17347. * JX9 provides a number of miscellaneous I/O streams that allow access to JX9's own input
  17348. * and output streams, the standard input, output and error file descriptors.
  17349. * jx9://stdin, jx9://stdout and jx9://stderr:
  17350. * Allow direct access to the corresponding input or output stream of the JX9 process.
  17351. * The stream references a duplicate file descriptor, so if you open jx9://stdin and later
  17352. * close it, you close only your copy of the descriptor-the actual stream referenced by STDIN is unaffected.
  17353. * jx9://stdin is read-only, whereas jx9://stdout and jx9://stderr are write-only.
  17354. * jx9://output
  17355. * jx9://output is a write-only stream that allows you to write to the output buffer
  17356. * mechanism in the same way as print and print.
  17357. */
  17358. typedef struct jx9_stream_data jx9_stream_data;
  17359. /* Supported IO streams */
  17360. #define JX9_IO_STREAM_STDIN 1 /* jx9://stdin */
  17361. #define JX9_IO_STREAM_STDOUT 2 /* jx9://stdout */
  17362. #define JX9_IO_STREAM_STDERR 3 /* jx9://stderr */
  17363. #define JX9_IO_STREAM_OUTPUT 4 /* jx9://output */
  17364. /* The following structure is the private data associated with the jx9:// stream */
  17365. struct jx9_stream_data
  17366. {
  17367. jx9_vm *pVm; /* VM that own this instance */
  17368. int iType; /* Stream type */
  17369. union{
  17370. void *pHandle; /* Stream handle */
  17371. jx9_output_consumer sConsumer; /* VM output consumer */
  17372. }x;
  17373. };
  17374. /*
  17375. * Allocate a new instance of the jx9_stream_data structure.
  17376. */
  17377. static jx9_stream_data * JX9StreamDataInit(jx9_vm *pVm, int iType)
  17378. {
  17379. jx9_stream_data *pData;
  17380. if( pVm == 0 ){
  17381. return 0;
  17382. }
  17383. /* Allocate a new instance */
  17384. pData = (jx9_stream_data *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(jx9_stream_data));
  17385. if( pData == 0 ){
  17386. return 0;
  17387. }
  17388. /* Zero the structure */
  17389. SyZero(pData, sizeof(jx9_stream_data));
  17390. /* Initialize fields */
  17391. pData->iType = iType;
  17392. if( iType == JX9_IO_STREAM_OUTPUT ){
  17393. /* Point to the default VM consumer routine. */
  17394. pData->x.sConsumer = pVm->sVmConsumer;
  17395. }else{
  17396. #ifdef __WINNT__
  17397. DWORD nChannel;
  17398. switch(iType){
  17399. case JX9_IO_STREAM_STDOUT: nChannel = STD_OUTPUT_HANDLE; break;
  17400. case JX9_IO_STREAM_STDERR: nChannel = STD_ERROR_HANDLE; break;
  17401. default:
  17402. nChannel = STD_INPUT_HANDLE;
  17403. break;
  17404. }
  17405. pData->x.pHandle = GetStdHandle(nChannel);
  17406. #else
  17407. /* Assume an UNIX system */
  17408. int ifd = STDIN_FILENO;
  17409. switch(iType){
  17410. case JX9_IO_STREAM_STDOUT: ifd = STDOUT_FILENO; break;
  17411. case JX9_IO_STREAM_STDERR: ifd = STDERR_FILENO; break;
  17412. default:
  17413. break;
  17414. }
  17415. pData->x.pHandle = SX_INT_TO_PTR(ifd);
  17416. #endif
  17417. }
  17418. pData->pVm = pVm;
  17419. return pData;
  17420. }
  17421. /*
  17422. * Implementation of the jx9:// IO streams routines
  17423. * Authors:
  17424. * Symisc Systems, devel@symisc.net.
  17425. * Copyright (C) Symisc Systems, http://jx9.symisc.net
  17426. * Status:
  17427. * Stable.
  17428. */
  17429. /* int (*xOpen)(const char *, int, jx9_value *, void **) */
  17430. static int JX9StreamData_Open(const char *zName, int iMode, jx9_value *pResource, void ** ppHandle)
  17431. {
  17432. jx9_stream_data *pData;
  17433. SyString sStream;
  17434. SyStringInitFromBuf(&sStream, zName, SyStrlen(zName));
  17435. /* Trim leading and trailing white spaces */
  17436. SyStringFullTrim(&sStream);
  17437. /* Stream to open */
  17438. if( SyStrnicmp(sStream.zString, "stdin", sizeof("stdin")-1) == 0 ){
  17439. iMode = JX9_IO_STREAM_STDIN;
  17440. }else if( SyStrnicmp(sStream.zString, "output", sizeof("output")-1) == 0 ){
  17441. iMode = JX9_IO_STREAM_OUTPUT;
  17442. }else if( SyStrnicmp(sStream.zString, "stdout", sizeof("stdout")-1) == 0 ){
  17443. iMode = JX9_IO_STREAM_STDOUT;
  17444. }else if( SyStrnicmp(sStream.zString, "stderr", sizeof("stderr")-1) == 0 ){
  17445. iMode = JX9_IO_STREAM_STDERR;
  17446. }else{
  17447. /* unknown stream name */
  17448. return -1;
  17449. }
  17450. /* Create our handle */
  17451. pData = JX9StreamDataInit(pResource?pResource->pVm:0, iMode);
  17452. if( pData == 0 ){
  17453. return -1;
  17454. }
  17455. /* Make the handle public */
  17456. *ppHandle = (void *)pData;
  17457. return JX9_OK;
  17458. }
  17459. /* jx9_int64 (*xRead)(void *, void *, jx9_int64) */
  17460. static jx9_int64 JX9StreamData_Read(void *pHandle, void *pBuffer, jx9_int64 nDatatoRead)
  17461. {
  17462. jx9_stream_data *pData = (jx9_stream_data *)pHandle;
  17463. if( pData == 0 ){
  17464. return -1;
  17465. }
  17466. if( pData->iType != JX9_IO_STREAM_STDIN ){
  17467. /* Forbidden */
  17468. return -1;
  17469. }
  17470. #ifdef __WINNT__
  17471. {
  17472. DWORD nRd;
  17473. BOOL rc;
  17474. rc = ReadFile(pData->x.pHandle, pBuffer, (DWORD)nDatatoRead, &nRd, 0);
  17475. if( !rc ){
  17476. /* IO error */
  17477. return -1;
  17478. }
  17479. return (jx9_int64)nRd;
  17480. }
  17481. #elif defined(__UNIXES__)
  17482. {
  17483. ssize_t nRd;
  17484. int fd;
  17485. fd = SX_PTR_TO_INT(pData->x.pHandle);
  17486. nRd = read(fd, pBuffer, (size_t)nDatatoRead);
  17487. if( nRd < 1 ){
  17488. return -1;
  17489. }
  17490. return (jx9_int64)nRd;
  17491. }
  17492. #else
  17493. return -1;
  17494. #endif
  17495. }
  17496. /* jx9_int64 (*xWrite)(void *, const void *, jx9_int64) */
  17497. static jx9_int64 JX9StreamData_Write(void *pHandle, const void *pBuf, jx9_int64 nWrite)
  17498. {
  17499. jx9_stream_data *pData = (jx9_stream_data *)pHandle;
  17500. if( pData == 0 ){
  17501. return -1;
  17502. }
  17503. if( pData->iType == JX9_IO_STREAM_STDIN ){
  17504. /* Forbidden */
  17505. return -1;
  17506. }else if( pData->iType == JX9_IO_STREAM_OUTPUT ){
  17507. jx9_output_consumer *pCons = &pData->x.sConsumer;
  17508. int rc;
  17509. /* Call the vm output consumer */
  17510. rc = pCons->xConsumer(pBuf, (unsigned int)nWrite, pCons->pUserData);
  17511. if( rc == JX9_ABORT ){
  17512. return -1;
  17513. }
  17514. return nWrite;
  17515. }
  17516. #ifdef __WINNT__
  17517. {
  17518. DWORD nWr;
  17519. BOOL rc;
  17520. rc = WriteFile(pData->x.pHandle, pBuf, (DWORD)nWrite, &nWr, 0);
  17521. if( !rc ){
  17522. /* IO error */
  17523. return -1;
  17524. }
  17525. return (jx9_int64)nWr;
  17526. }
  17527. #elif defined(__UNIXES__)
  17528. {
  17529. ssize_t nWr;
  17530. int fd;
  17531. fd = SX_PTR_TO_INT(pData->x.pHandle);
  17532. nWr = write(fd, pBuf, (size_t)nWrite);
  17533. if( nWr < 1 ){
  17534. return -1;
  17535. }
  17536. return (jx9_int64)nWr;
  17537. }
  17538. #else
  17539. return -1;
  17540. #endif
  17541. }
  17542. /* void (*xClose)(void *) */
  17543. static void JX9StreamData_Close(void *pHandle)
  17544. {
  17545. jx9_stream_data *pData = (jx9_stream_data *)pHandle;
  17546. jx9_vm *pVm;
  17547. if( pData == 0 ){
  17548. return;
  17549. }
  17550. pVm = pData->pVm;
  17551. /* Free the instance */
  17552. SyMemBackendFree(&pVm->sAllocator, pData);
  17553. }
  17554. /* Export the jx9:// stream */
  17555. static const jx9_io_stream sjx9Stream = {
  17556. "jx9",
  17557. JX9_IO_STREAM_VERSION,
  17558. JX9StreamData_Open, /* xOpen */
  17559. 0, /* xOpenDir */
  17560. JX9StreamData_Close, /* xClose */
  17561. 0, /* xCloseDir */
  17562. JX9StreamData_Read, /* xRead */
  17563. 0, /* xReadDir */
  17564. JX9StreamData_Write, /* xWrite */
  17565. 0, /* xSeek */
  17566. 0, /* xLock */
  17567. 0, /* xRewindDir */
  17568. 0, /* xTell */
  17569. 0, /* xTrunc */
  17570. 0, /* xSeek */
  17571. 0 /* xStat */
  17572. };
  17573. #endif /* JX9_DISABLE_DISK_IO */
  17574. /*
  17575. * Return TRUE if we are dealing with the jx9:// stream.
  17576. * FALSE otherwise.
  17577. */
  17578. static int is_jx9_stream(const jx9_io_stream *pStream)
  17579. {
  17580. #ifndef JX9_DISABLE_DISK_IO
  17581. return pStream == &sjx9Stream;
  17582. #else
  17583. SXUNUSED(pStream); /* cc warning */
  17584. return 0;
  17585. #endif /* JX9_DISABLE_DISK_IO */
  17586. }
  17587. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  17588. /*
  17589. * Export the IO routines defined above and the built-in IO streams
  17590. * [i.e: file://, jx9://].
  17591. * Note:
  17592. * If the engine is compiled with the JX9_DISABLE_BUILTIN_FUNC directive
  17593. * defined then this function is a no-op.
  17594. */
  17595. JX9_PRIVATE sxi32 jx9RegisterIORoutine(jx9_vm *pVm)
  17596. {
  17597. #ifndef JX9_DISABLE_BUILTIN_FUNC
  17598. /* VFS functions */
  17599. static const jx9_builtin_func aVfsFunc[] = {
  17600. {"chdir", jx9Vfs_chdir },
  17601. {"chroot", jx9Vfs_chroot },
  17602. {"getcwd", jx9Vfs_getcwd },
  17603. {"rmdir", jx9Vfs_rmdir },
  17604. {"is_dir", jx9Vfs_is_dir },
  17605. {"mkdir", jx9Vfs_mkdir },
  17606. {"rename", jx9Vfs_rename },
  17607. {"realpath", jx9Vfs_realpath},
  17608. {"sleep", jx9Vfs_sleep },
  17609. {"usleep", jx9Vfs_usleep },
  17610. {"unlink", jx9Vfs_unlink },
  17611. {"delete", jx9Vfs_unlink },
  17612. {"chmod", jx9Vfs_chmod },
  17613. {"chown", jx9Vfs_chown },
  17614. {"chgrp", jx9Vfs_chgrp },
  17615. {"disk_free_space", jx9Vfs_disk_free_space },
  17616. {"disk_total_space", jx9Vfs_disk_total_space},
  17617. {"file_exists", jx9Vfs_file_exists },
  17618. {"filesize", jx9Vfs_file_size },
  17619. {"fileatime", jx9Vfs_file_atime },
  17620. {"filemtime", jx9Vfs_file_mtime },
  17621. {"filectime", jx9Vfs_file_ctime },
  17622. {"is_file", jx9Vfs_is_file },
  17623. {"is_link", jx9Vfs_is_link },
  17624. {"is_readable", jx9Vfs_is_readable },
  17625. {"is_writable", jx9Vfs_is_writable },
  17626. {"is_executable", jx9Vfs_is_executable},
  17627. {"filetype", jx9Vfs_filetype },
  17628. {"stat", jx9Vfs_stat },
  17629. {"lstat", jx9Vfs_lstat },
  17630. {"getenv", jx9Vfs_getenv },
  17631. {"setenv", jx9Vfs_putenv },
  17632. {"putenv", jx9Vfs_putenv },
  17633. {"touch", jx9Vfs_touch },
  17634. {"link", jx9Vfs_link },
  17635. {"symlink", jx9Vfs_symlink },
  17636. {"umask", jx9Vfs_umask },
  17637. {"sys_get_temp_dir", jx9Vfs_sys_get_temp_dir },
  17638. {"get_current_user", jx9Vfs_get_current_user },
  17639. {"getpid", jx9Vfs_getmypid },
  17640. {"getuid", jx9Vfs_getmyuid },
  17641. {"getgid", jx9Vfs_getmygid },
  17642. {"uname", jx9Vfs_uname},
  17643. /* Path processing */
  17644. {"dirname", jx9Builtin_dirname },
  17645. {"basename", jx9Builtin_basename },
  17646. {"pathinfo", jx9Builtin_pathinfo },
  17647. {"strglob", jx9Builtin_strglob },
  17648. {"fnmatch", jx9Builtin_fnmatch },
  17649. /* ZIP processing */
  17650. {"zip_open", jx9Builtin_zip_open },
  17651. {"zip_close", jx9Builtin_zip_close},
  17652. {"zip_read", jx9Builtin_zip_read },
  17653. {"zip_entry_open", jx9Builtin_zip_entry_open },
  17654. {"zip_entry_close", jx9Builtin_zip_entry_close},
  17655. {"zip_entry_name", jx9Builtin_zip_entry_name },
  17656. {"zip_entry_filesize", jx9Builtin_zip_entry_filesize },
  17657. {"zip_entry_compressedsize", jx9Builtin_zip_entry_compressedsize },
  17658. {"zip_entry_read", jx9Builtin_zip_entry_read },
  17659. {"zip_entry_reset_read_cursor", jx9Builtin_zip_entry_reset_read_cursor},
  17660. {"zip_entry_compressionmethod", jx9Builtin_zip_entry_compressionmethod}
  17661. };
  17662. /* IO stream functions */
  17663. static const jx9_builtin_func aIOFunc[] = {
  17664. {"ftruncate", jx9Builtin_ftruncate },
  17665. {"fseek", jx9Builtin_fseek },
  17666. {"ftell", jx9Builtin_ftell },
  17667. {"rewind", jx9Builtin_rewind },
  17668. {"fflush", jx9Builtin_fflush },
  17669. {"feof", jx9Builtin_feof },
  17670. {"fgetc", jx9Builtin_fgetc },
  17671. {"fgets", jx9Builtin_fgets },
  17672. {"fread", jx9Builtin_fread },
  17673. {"fgetcsv", jx9Builtin_fgetcsv},
  17674. {"fgetss", jx9Builtin_fgetss },
  17675. {"readdir", jx9Builtin_readdir},
  17676. {"rewinddir", jx9Builtin_rewinddir },
  17677. {"closedir", jx9Builtin_closedir},
  17678. {"opendir", jx9Builtin_opendir },
  17679. {"readfile", jx9Builtin_readfile},
  17680. {"file_get_contents", jx9Builtin_file_get_contents},
  17681. {"file_put_contents", jx9Builtin_file_put_contents},
  17682. {"file", jx9Builtin_file },
  17683. {"copy", jx9Builtin_copy },
  17684. {"fstat", jx9Builtin_fstat },
  17685. {"fwrite", jx9Builtin_fwrite },
  17686. {"fputs", jx9Builtin_fwrite },
  17687. {"flock", jx9Builtin_flock },
  17688. {"fclose", jx9Builtin_fclose },
  17689. {"fopen", jx9Builtin_fopen },
  17690. {"fpassthru", jx9Builtin_fpassthru },
  17691. {"fputcsv", jx9Builtin_fputcsv },
  17692. {"fprintf", jx9Builtin_fprintf },
  17693. #if !defined(JX9_DISABLE_HASH_FUNC)
  17694. {"md5_file", jx9Builtin_md5_file},
  17695. {"sha1_file", jx9Builtin_sha1_file},
  17696. #endif /* JX9_DISABLE_HASH_FUNC */
  17697. {"parse_ini_file", jx9Builtin_parse_ini_file},
  17698. {"vfprintf", jx9Builtin_vfprintf}
  17699. };
  17700. const jx9_io_stream *pFileStream = 0;
  17701. sxu32 n = 0;
  17702. /* Register the functions defined above */
  17703. for( n = 0 ; n < SX_ARRAYSIZE(aVfsFunc) ; ++n ){
  17704. jx9_create_function(&(*pVm), aVfsFunc[n].zName, aVfsFunc[n].xFunc, (void *)pVm->pEngine->pVfs);
  17705. }
  17706. for( n = 0 ; n < SX_ARRAYSIZE(aIOFunc) ; ++n ){
  17707. jx9_create_function(&(*pVm), aIOFunc[n].zName, aIOFunc[n].xFunc, pVm);
  17708. }
  17709. #ifndef JX9_DISABLE_DISK_IO
  17710. /* Register the file stream if available */
  17711. #ifdef __WINNT__
  17712. pFileStream = &sWinFileStream;
  17713. #elif defined(__UNIXES__)
  17714. pFileStream = &sUnixFileStream;
  17715. #endif
  17716. /* Install the jx9:// stream */
  17717. jx9_vm_config(pVm, JX9_VM_CONFIG_IO_STREAM, &sjx9Stream);
  17718. #endif /* JX9_DISABLE_DISK_IO */
  17719. if( pFileStream ){
  17720. /* Install the file:// stream */
  17721. jx9_vm_config(pVm, JX9_VM_CONFIG_IO_STREAM, pFileStream);
  17722. }
  17723. #else
  17724. SXUNUSED(pVm); /* cc warning */
  17725. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  17726. return SXRET_OK;
  17727. }
  17728. /*
  17729. * Export the STDIN handle.
  17730. */
  17731. JX9_PRIVATE void * jx9ExportStdin(jx9_vm *pVm)
  17732. {
  17733. #ifndef JX9_DISABLE_BUILTIN_FUNC
  17734. #ifndef JX9_DISABLE_DISK_IO
  17735. if( pVm->pStdin == 0 ){
  17736. io_private *pIn;
  17737. /* Allocate an IO private instance */
  17738. pIn = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private));
  17739. if( pIn == 0 ){
  17740. return 0;
  17741. }
  17742. InitIOPrivate(pVm, &sjx9Stream, pIn);
  17743. /* Initialize the handle */
  17744. pIn->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDIN);
  17745. /* Install the STDIN stream */
  17746. pVm->pStdin = pIn;
  17747. return pIn;
  17748. }else{
  17749. /* NULL or STDIN */
  17750. return pVm->pStdin;
  17751. }
  17752. #else
  17753. return 0;
  17754. #endif
  17755. #else
  17756. SXUNUSED(pVm); /* cc warning */
  17757. return 0;
  17758. #endif
  17759. }
  17760. /*
  17761. * Export the STDOUT handle.
  17762. */
  17763. JX9_PRIVATE void * jx9ExportStdout(jx9_vm *pVm)
  17764. {
  17765. #ifndef JX9_DISABLE_BUILTIN_FUNC
  17766. #ifndef JX9_DISABLE_DISK_IO
  17767. if( pVm->pStdout == 0 ){
  17768. io_private *pOut;
  17769. /* Allocate an IO private instance */
  17770. pOut = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private));
  17771. if( pOut == 0 ){
  17772. return 0;
  17773. }
  17774. InitIOPrivate(pVm, &sjx9Stream, pOut);
  17775. /* Initialize the handle */
  17776. pOut->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDOUT);
  17777. /* Install the STDOUT stream */
  17778. pVm->pStdout = pOut;
  17779. return pOut;
  17780. }else{
  17781. /* NULL or STDOUT */
  17782. return pVm->pStdout;
  17783. }
  17784. #else
  17785. return 0;
  17786. #endif
  17787. #else
  17788. SXUNUSED(pVm); /* cc warning */
  17789. return 0;
  17790. #endif
  17791. }
  17792. /*
  17793. * Export the STDERR handle.
  17794. */
  17795. JX9_PRIVATE void * jx9ExportStderr(jx9_vm *pVm)
  17796. {
  17797. #ifndef JX9_DISABLE_BUILTIN_FUNC
  17798. #ifndef JX9_DISABLE_DISK_IO
  17799. if( pVm->pStderr == 0 ){
  17800. io_private *pErr;
  17801. /* Allocate an IO private instance */
  17802. pErr = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private));
  17803. if( pErr == 0 ){
  17804. return 0;
  17805. }
  17806. InitIOPrivate(pVm, &sjx9Stream, pErr);
  17807. /* Initialize the handle */
  17808. pErr->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDERR);
  17809. /* Install the STDERR stream */
  17810. pVm->pStderr = pErr;
  17811. return pErr;
  17812. }else{
  17813. /* NULL or STDERR */
  17814. return pVm->pStderr;
  17815. }
  17816. #else
  17817. return 0;
  17818. #endif
  17819. #else
  17820. SXUNUSED(pVm); /* cc warning */
  17821. return 0;
  17822. #endif
  17823. }
  17824. /*
  17825. * ----------------------------------------------------------
  17826. * File: parse.c
  17827. * MD5: 3380d9f6f1c0b39d30c7a1fe19319f32
  17828. * ----------------------------------------------------------
  17829. */
  17830. /*
  17831. * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
  17832. * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
  17833. * Version 1.7.2
  17834. * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  17835. * please contact Symisc Systems via:
  17836. * legal@symisc.net
  17837. * licensing@symisc.net
  17838. * contact@symisc.net
  17839. * or visit:
  17840. * http://jx9.symisc.net/
  17841. */
  17842. /* $SymiscID: parse.c v1.2 FreeBSD 2012-12-11 00:46 stable <chm@symisc.net> $ */
  17843. #ifndef JX9_AMALGAMATION
  17844. #include "jx9Int.h"
  17845. #endif
  17846. /* Expression parser for the unQL programming language */
  17847. /* Operators associativity */
  17848. #define EXPR_OP_ASSOC_LEFT 0x01 /* Left associative operator */
  17849. #define EXPR_OP_ASSOC_RIGHT 0x02 /* Right associative operator */
  17850. #define EXPR_OP_NON_ASSOC 0x04 /* Non-associative operator */
  17851. /*
  17852. * Operators table
  17853. * This table is sorted by operators priority (highest to lowest) according
  17854. * the JX9 language reference manual.
  17855. * JX9 implements all the 60 JX9 operators and have introduced the eq and ne operators.
  17856. * The operators precedence table have been improved dramatically so that you can do same
  17857. * amazing things now such as array dereferencing, on the fly function call, anonymous function
  17858. * as array values, object member access on instantiation and so on.
  17859. * Refer to the following page for a full discussion on these improvements:
  17860. * http://jx9.symisc.net/features.html
  17861. */
  17862. static const jx9_expr_op aOpTable[] = {
  17863. /* Postfix operators */
  17864. /* Precedence 2(Highest), left-associative */
  17865. { {".", sizeof(char)}, EXPR_OP_DOT, 2, EXPR_OP_ASSOC_LEFT , JX9_OP_MEMBER },
  17866. { {"[", sizeof(char)}, EXPR_OP_SUBSCRIPT, 2, EXPR_OP_ASSOC_LEFT , JX9_OP_LOAD_IDX},
  17867. /* Precedence 3, non-associative */
  17868. { {"++", sizeof(char)*2}, EXPR_OP_INCR, 3, EXPR_OP_NON_ASSOC , JX9_OP_INCR},
  17869. { {"--", sizeof(char)*2}, EXPR_OP_DECR, 3, EXPR_OP_NON_ASSOC , JX9_OP_DECR},
  17870. /* Unary operators */
  17871. /* Precedence 4, right-associative */
  17872. { {"-", sizeof(char)}, EXPR_OP_UMINUS, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_UMINUS },
  17873. { {"+", sizeof(char)}, EXPR_OP_UPLUS, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_UPLUS },
  17874. { {"~", sizeof(char)}, EXPR_OP_BITNOT, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_BITNOT },
  17875. { {"!", sizeof(char)}, EXPR_OP_LOGNOT, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_LNOT },
  17876. /* Cast operators */
  17877. { {"(int)", sizeof("(int)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_INT },
  17878. { {"(bool)", sizeof("(bool)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_BOOL },
  17879. { {"(string)", sizeof("(string)")-1}, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_STR },
  17880. { {"(float)", sizeof("(float)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_REAL },
  17881. { {"(array)", sizeof("(array)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_ARRAY }, /* Not used, but reserved for future use */
  17882. { {"(object)", sizeof("(object)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_ARRAY }, /* Not used, but reserved for future use */
  17883. /* Binary operators */
  17884. /* Precedence 7, left-associative */
  17885. { {"*", sizeof(char)}, EXPR_OP_MUL, 7, EXPR_OP_ASSOC_LEFT , JX9_OP_MUL},
  17886. { {"/", sizeof(char)}, EXPR_OP_DIV, 7, EXPR_OP_ASSOC_LEFT , JX9_OP_DIV},
  17887. { {"%", sizeof(char)}, EXPR_OP_MOD, 7, EXPR_OP_ASSOC_LEFT , JX9_OP_MOD},
  17888. /* Precedence 8, left-associative */
  17889. { {"+", sizeof(char)}, EXPR_OP_ADD, 8, EXPR_OP_ASSOC_LEFT, JX9_OP_ADD},
  17890. { {"-", sizeof(char)}, EXPR_OP_SUB, 8, EXPR_OP_ASSOC_LEFT, JX9_OP_SUB},
  17891. { {"..", sizeof(char)*2},EXPR_OP_DDOT, 8, EXPR_OP_ASSOC_LEFT, JX9_OP_CAT},
  17892. /* Precedence 9, left-associative */
  17893. { {"<<", sizeof(char)*2}, EXPR_OP_SHL, 9, EXPR_OP_ASSOC_LEFT, JX9_OP_SHL},
  17894. { {">>", sizeof(char)*2}, EXPR_OP_SHR, 9, EXPR_OP_ASSOC_LEFT, JX9_OP_SHR},
  17895. /* Precedence 10, non-associative */
  17896. { {"<", sizeof(char)}, EXPR_OP_LT, 10, EXPR_OP_NON_ASSOC, JX9_OP_LT},
  17897. { {">", sizeof(char)}, EXPR_OP_GT, 10, EXPR_OP_NON_ASSOC, JX9_OP_GT},
  17898. { {"<=", sizeof(char)*2}, EXPR_OP_LE, 10, EXPR_OP_NON_ASSOC, JX9_OP_LE},
  17899. { {">=", sizeof(char)*2}, EXPR_OP_GE, 10, EXPR_OP_NON_ASSOC, JX9_OP_GE},
  17900. { {"<>", sizeof(char)*2}, EXPR_OP_NE, 10, EXPR_OP_NON_ASSOC, JX9_OP_NEQ},
  17901. /* Precedence 11, non-associative */
  17902. { {"==", sizeof(char)*2}, EXPR_OP_EQ, 11, EXPR_OP_NON_ASSOC, JX9_OP_EQ},
  17903. { {"!=", sizeof(char)*2}, EXPR_OP_NE, 11, EXPR_OP_NON_ASSOC, JX9_OP_NEQ},
  17904. { {"===", sizeof(char)*3}, EXPR_OP_TEQ, 11, EXPR_OP_NON_ASSOC, JX9_OP_TEQ},
  17905. { {"!==", sizeof(char)*3}, EXPR_OP_TNE, 11, EXPR_OP_NON_ASSOC, JX9_OP_TNE},
  17906. /* Precedence 12, left-associative */
  17907. { {"&", sizeof(char)}, EXPR_OP_BAND, 12, EXPR_OP_ASSOC_LEFT, JX9_OP_BAND},
  17908. /* Binary operators */
  17909. /* Precedence 13, left-associative */
  17910. { {"^", sizeof(char)}, EXPR_OP_XOR, 13, EXPR_OP_ASSOC_LEFT, JX9_OP_BXOR},
  17911. /* Precedence 14, left-associative */
  17912. { {"|", sizeof(char)}, EXPR_OP_BOR, 14, EXPR_OP_ASSOC_LEFT, JX9_OP_BOR},
  17913. /* Precedence 15, left-associative */
  17914. { {"&&", sizeof(char)*2}, EXPR_OP_LAND, 15, EXPR_OP_ASSOC_LEFT, JX9_OP_LAND},
  17915. /* Precedence 16, left-associative */
  17916. { {"||", sizeof(char)*2}, EXPR_OP_LOR, 16, EXPR_OP_ASSOC_LEFT, JX9_OP_LOR},
  17917. /* Ternary operator */
  17918. /* Precedence 17, left-associative */
  17919. { {"?", sizeof(char)}, EXPR_OP_QUESTY, 17, EXPR_OP_ASSOC_LEFT, 0},
  17920. /* Combined binary operators */
  17921. /* Precedence 18, right-associative */
  17922. { {"=", sizeof(char)}, EXPR_OP_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_STORE},
  17923. { {"+=", sizeof(char)*2}, EXPR_OP_ADD_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_ADD_STORE },
  17924. { {"-=", sizeof(char)*2}, EXPR_OP_SUB_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_SUB_STORE },
  17925. { {".=", sizeof(char)*2}, EXPR_OP_DOT_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_CAT_STORE },
  17926. { {"*=", sizeof(char)*2}, EXPR_OP_MUL_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_MUL_STORE },
  17927. { {"/=", sizeof(char)*2}, EXPR_OP_DIV_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_DIV_STORE },
  17928. { {"%=", sizeof(char)*2}, EXPR_OP_MOD_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_MOD_STORE },
  17929. { {"&=", sizeof(char)*2}, EXPR_OP_AND_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_BAND_STORE },
  17930. { {"|=", sizeof(char)*2}, EXPR_OP_OR_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_BOR_STORE },
  17931. { {"^=", sizeof(char)*2}, EXPR_OP_XOR_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_BXOR_STORE },
  17932. { {"<<=", sizeof(char)*3}, EXPR_OP_SHL_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_SHL_STORE },
  17933. { {">>=", sizeof(char)*3}, EXPR_OP_SHR_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_SHR_STORE },
  17934. /* Precedence 22, left-associative [Lowest operator] */
  17935. { {",", sizeof(char)}, EXPR_OP_COMMA, 22, EXPR_OP_ASSOC_LEFT, 0}, /* IMP-0139-COMMA: Symisc eXtension */
  17936. };
  17937. /* Function call operator need special handling */
  17938. static const jx9_expr_op sFCallOp = {{"(", sizeof(char)}, EXPR_OP_FUNC_CALL, 2, EXPR_OP_ASSOC_LEFT , JX9_OP_CALL};
  17939. /*
  17940. * Check if the given token is a potential operator or not.
  17941. * This function is called by the lexer each time it extract a token that may
  17942. * look like an operator.
  17943. * Return a structure [i.e: jx9_expr_op instnace ] that describe the operator on success.
  17944. * Otherwise NULL.
  17945. * Note that the function take care of handling ambiguity [i.e: whether we are dealing with
  17946. * a binary minus or unary minus.]
  17947. */
  17948. JX9_PRIVATE const jx9_expr_op * jx9ExprExtractOperator(SyString *pStr, SyToken *pLast)
  17949. {
  17950. sxu32 n = 0;
  17951. sxi32 rc;
  17952. /* Do a linear lookup on the operators table */
  17953. for(;;){
  17954. if( n >= SX_ARRAYSIZE(aOpTable) ){
  17955. break;
  17956. }
  17957. rc = SyStringCmp(pStr, &aOpTable[n].sOp, SyMemcmp);
  17958. if( rc == 0 ){
  17959. if( aOpTable[n].sOp.nByte != sizeof(char) || (aOpTable[n].iOp != EXPR_OP_UMINUS && aOpTable[n].iOp != EXPR_OP_UPLUS) || pLast == 0 ){
  17960. if( aOpTable[n].iOp == EXPR_OP_SUBSCRIPT && (pLast == 0 || (pLast->nType & (JX9_TK_ID|JX9_TK_CSB/*]*/|JX9_TK_RPAREN/*)*/)) == 0) ){
  17961. /* JSON Array not subscripting, return NULL */
  17962. return 0;
  17963. }
  17964. /* There is no ambiguity here, simply return the first operator seen */
  17965. return &aOpTable[n];
  17966. }
  17967. /* Handle ambiguity */
  17968. if( pLast->nType & (JX9_TK_LPAREN/*'('*/|JX9_TK_OCB/*'{'*/|JX9_TK_OSB/*'['*/|JX9_TK_COLON/*:*/|JX9_TK_COMMA/*, '*/) ){
  17969. /* Unary opertors have prcedence here over binary operators */
  17970. return &aOpTable[n];
  17971. }
  17972. if( pLast->nType & JX9_TK_OP ){
  17973. const jx9_expr_op *pOp = (const jx9_expr_op *)pLast->pUserData;
  17974. /* Ticket 1433-31: Handle the '++', '--' operators case */
  17975. if( pOp->iOp != EXPR_OP_INCR && pOp->iOp != EXPR_OP_DECR ){
  17976. /* Unary opertors have prcedence here over binary operators */
  17977. return &aOpTable[n];
  17978. }
  17979. }
  17980. }
  17981. ++n; /* Next operator in the table */
  17982. }
  17983. /* No such operator */
  17984. return 0;
  17985. }
  17986. /*
  17987. * Delimit a set of token stream.
  17988. * This function take care of handling the nesting level and stops when it hit
  17989. * the end of the input or the ending token is found and the nesting level is zero.
  17990. */
  17991. JX9_PRIVATE void jx9DelimitNestedTokens(SyToken *pIn,SyToken *pEnd,sxu32 nTokStart,sxu32 nTokEnd,SyToken **ppEnd)
  17992. {
  17993. SyToken *pCur = pIn;
  17994. sxi32 iNest = 1;
  17995. for(;;){
  17996. if( pCur >= pEnd ){
  17997. break;
  17998. }
  17999. if( pCur->nType & nTokStart ){
  18000. /* Increment nesting level */
  18001. iNest++;
  18002. }else if( pCur->nType & nTokEnd ){
  18003. /* Decrement nesting level */
  18004. iNest--;
  18005. if( iNest <= 0 ){
  18006. break;
  18007. }
  18008. }
  18009. /* Advance cursor */
  18010. pCur++;
  18011. }
  18012. /* Point to the end of the chunk */
  18013. *ppEnd = pCur;
  18014. }
  18015. /*
  18016. * Retrun TRUE if the given ID represent a language construct [i.e: print, print..]. FALSE otherwise.
  18017. * Note on reserved keywords.
  18018. * According to the JX9 language reference manual:
  18019. * These words have special meaning in JX9. Some of them represent things which look like
  18020. * functions, some look like constants, and so on--but they're not, really: they are language
  18021. * constructs. You cannot use any of the following words as constants, object names, function
  18022. * or method names. Using them as variable names is generally OK, but could lead to confusion.
  18023. */
  18024. JX9_PRIVATE int jx9IsLangConstruct(sxu32 nKeyID)
  18025. {
  18026. if( nKeyID == JX9_TKWRD_PRINT || nKeyID == JX9_TKWRD_EXIT || nKeyID == JX9_TKWRD_DIE
  18027. || nKeyID == JX9_TKWRD_INCLUDE|| nKeyID == JX9_TKWRD_IMPORT ){
  18028. return TRUE;
  18029. }
  18030. /* Not a language construct */
  18031. return FALSE;
  18032. }
  18033. /*
  18034. * Point to the next expression that should be evaluated shortly.
  18035. * The cursor stops when it hit a comma ', ' or a semi-colon and the nesting
  18036. * level is zero.
  18037. */
  18038. JX9_PRIVATE sxi32 jx9GetNextExpr(SyToken *pStart,SyToken *pEnd,SyToken **ppNext)
  18039. {
  18040. SyToken *pCur = pStart;
  18041. sxi32 iNest = 0;
  18042. if( pCur >= pEnd || (pCur->nType & JX9_TK_SEMI/*';'*/) ){
  18043. /* Last expression */
  18044. return SXERR_EOF;
  18045. }
  18046. while( pCur < pEnd ){
  18047. if( (pCur->nType & (JX9_TK_COMMA/*','*/|JX9_TK_SEMI/*';'*/)) && iNest <= 0){
  18048. break;
  18049. }
  18050. if( pCur->nType & (JX9_TK_LPAREN/*'('*/|JX9_TK_OSB/*'['*/|JX9_TK_OCB/*'{'*/) ){
  18051. iNest++;
  18052. }else if( pCur->nType & (JX9_TK_RPAREN/*')'*/|JX9_TK_CSB/*']'*/|JX9_TK_CCB/*'}*/) ){
  18053. iNest--;
  18054. }
  18055. pCur++;
  18056. }
  18057. *ppNext = pCur;
  18058. return SXRET_OK;
  18059. }
  18060. /*
  18061. * Collect and assemble tokens holding annonymous functions/closure body.
  18062. * When errors, JX9 take care of generating the appropriate error message.
  18063. * Note on annonymous functions.
  18064. * According to the JX9 language reference manual:
  18065. * Anonymous functions, also known as closures, allow the creation of functions
  18066. * which have no specified name. They are most useful as the value of callback
  18067. * parameters, but they have many other uses.
  18068. * Closures may also inherit variables from the parent scope. Any such variables
  18069. * must be declared in the function header. Inheriting variables from the parent
  18070. * scope is not the same as using global variables. Global variables exist in the global scope
  18071. * which is the same no matter what function is executing. The parent scope of a closure is the
  18072. * function in which the closure was declared (not necessarily the function it was called from).
  18073. *
  18074. * Some example:
  18075. * $greet = function($name)
  18076. * {
  18077. * printf("Hello %s\r\n", $name);
  18078. * };
  18079. * $greet('World');
  18080. * $greet('JX9');
  18081. *
  18082. * $double = function($a) {
  18083. * return $a * 2;
  18084. * };
  18085. * // This is our range of numbers
  18086. * $numbers = range(1, 5);
  18087. * // Use the Annonymous function as a callback here to
  18088. * // double the size of each element in our
  18089. * // range
  18090. * $new_numbers = array_map($double, $numbers);
  18091. * print implode(' ', $new_numbers);
  18092. */
  18093. static sxi32 ExprAssembleAnnon(jx9_gen_state *pGen,SyToken **ppCur, SyToken *pEnd)
  18094. {
  18095. SyToken *pIn = *ppCur;
  18096. sxu32 nLine;
  18097. sxi32 rc;
  18098. /* Jump the 'function' keyword */
  18099. nLine = pIn->nLine;
  18100. pIn++;
  18101. if( pIn < pEnd && (pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD)) ){
  18102. pIn++;
  18103. }
  18104. if( pIn >= pEnd || (pIn->nType & JX9_TK_LPAREN) == 0 ){
  18105. /* Syntax error */
  18106. rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Missing opening parenthesis '(' while declaring annonymous function");
  18107. if( rc != SXERR_ABORT ){
  18108. rc = SXERR_SYNTAX;
  18109. }
  18110. goto Synchronize;
  18111. }
  18112. pIn++; /* Jump the leading parenthesis '(' */
  18113. jx9DelimitNestedTokens(pIn, pEnd, JX9_TK_LPAREN/*'('*/, JX9_TK_RPAREN/*')'*/, &pIn);
  18114. if( pIn >= pEnd || &pIn[1] >= pEnd ){
  18115. /* Syntax error */
  18116. rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Syntax error while declaring annonymous function");
  18117. if( rc != SXERR_ABORT ){
  18118. rc = SXERR_SYNTAX;
  18119. }
  18120. goto Synchronize;
  18121. }
  18122. pIn++; /* Jump the trailing parenthesis */
  18123. if( pIn->nType & JX9_TK_OCB /*'{'*/ ){
  18124. pIn++; /* Jump the leading curly '{' */
  18125. jx9DelimitNestedTokens(pIn, pEnd, JX9_TK_OCB/*'{'*/, JX9_TK_CCB/*'}'*/, &pIn);
  18126. if( pIn < pEnd ){
  18127. pIn++;
  18128. }
  18129. }else{
  18130. /* Syntax error */
  18131. rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Syntax error while declaring annonymous function, missing '{'");
  18132. if( rc == SXERR_ABORT ){
  18133. return SXERR_ABORT;
  18134. }
  18135. }
  18136. rc = SXRET_OK;
  18137. Synchronize:
  18138. /* Synchronize pointers */
  18139. *ppCur = pIn;
  18140. return rc;
  18141. }
  18142. /*
  18143. * Make sure we are dealing with a valid expression tree.
  18144. * This function check for balanced parenthesis, braces, brackets and so on.
  18145. * When errors, JX9 take care of generating the appropriate error message.
  18146. * Return SXRET_OK on success. Any other return value indicates syntax error.
  18147. */
  18148. static sxi32 ExprVerifyNodes(jx9_gen_state *pGen, jx9_expr_node **apNode, sxi32 nNode)
  18149. {
  18150. sxi32 iParen, iSquare, iBraces;
  18151. sxi32 i, rc;
  18152. if( nNode > 0 && apNode[0]->pOp && (apNode[0]->pOp->iOp == EXPR_OP_ADD || apNode[0]->pOp->iOp == EXPR_OP_SUB) ){
  18153. /* Fix and mark as an unary not binary plus/minus operator */
  18154. apNode[0]->pOp = jx9ExprExtractOperator(&apNode[0]->pStart->sData, 0);
  18155. apNode[0]->pStart->pUserData = (void *)apNode[0]->pOp;
  18156. }
  18157. iParen = iSquare = iBraces = 0;
  18158. for( i = 0 ; i < nNode ; ++i ){
  18159. if( apNode[i]->pStart->nType & JX9_TK_LPAREN /*'('*/){
  18160. if( i > 0 && ( apNode[i-1]->xCode == jx9CompileVariable || apNode[i-1]->xCode == jx9CompileLiteral ||
  18161. (apNode[i - 1]->pStart->nType & (JX9_TK_ID|JX9_TK_KEYWORD|JX9_TK_SSTR|JX9_TK_DSTR|JX9_TK_RPAREN/*')'*/|JX9_TK_CSB/*]*/))) ){
  18162. /* Ticket 1433-033: Take care to ignore alpha-stream [i.e: or, xor] operators followed by an opening parenthesis */
  18163. if( (apNode[i - 1]->pStart->nType & JX9_TK_OP) == 0 ){
  18164. /* We are dealing with a postfix [i.e: function call] operator
  18165. * not a simple left parenthesis. Mark the node.
  18166. */
  18167. apNode[i]->pStart->nType |= JX9_TK_OP;
  18168. apNode[i]->pStart->pUserData = (void *)&sFCallOp; /* Function call operator */
  18169. apNode[i]->pOp = &sFCallOp;
  18170. }
  18171. }
  18172. iParen++;
  18173. }else if( apNode[i]->pStart->nType & JX9_TK_RPAREN/*')*/){
  18174. if( iParen <= 0 ){
  18175. rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token ')'");
  18176. if( rc != SXERR_ABORT ){
  18177. rc = SXERR_SYNTAX;
  18178. }
  18179. return rc;
  18180. }
  18181. iParen--;
  18182. }else if( apNode[i]->pStart->nType & JX9_TK_OSB /*'['*/ && apNode[i]->xCode == 0 ){
  18183. iSquare++;
  18184. }else if (apNode[i]->pStart->nType & JX9_TK_CSB /*']'*/){
  18185. if( iSquare <= 0 ){
  18186. rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token ']'");
  18187. if( rc != SXERR_ABORT ){
  18188. rc = SXERR_SYNTAX;
  18189. }
  18190. return rc;
  18191. }
  18192. iSquare--;
  18193. }else if( apNode[i]->pStart->nType & JX9_TK_OCB /*'{'*/ && apNode[i]->xCode == 0 ){
  18194. iBraces++;
  18195. }else if (apNode[i]->pStart->nType & JX9_TK_CCB /*'}'*/){
  18196. if( iBraces <= 0 ){
  18197. rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token '}'");
  18198. if( rc != SXERR_ABORT ){
  18199. rc = SXERR_SYNTAX;
  18200. }
  18201. return rc;
  18202. }
  18203. iBraces--;
  18204. }else if( apNode[i]->pStart->nType & JX9_TK_OP ){
  18205. const jx9_expr_op *pOp = (const jx9_expr_op *)apNode[i]->pOp;
  18206. if( i > 0 && (pOp->iOp == EXPR_OP_UMINUS || pOp->iOp == EXPR_OP_UPLUS)){
  18207. if( apNode[i-1]->xCode == jx9CompileVariable || apNode[i-1]->xCode == jx9CompileLiteral ){
  18208. sxi32 iExprOp = EXPR_OP_SUB; /* Binary minus */
  18209. sxu32 n = 0;
  18210. if( pOp->iOp == EXPR_OP_UPLUS ){
  18211. iExprOp = EXPR_OP_ADD; /* Binary plus */
  18212. }
  18213. /*
  18214. * TICKET 1433-013: This is a fix around an obscure bug when the user uses
  18215. * a variable name which is an alpha-stream operator [i.e: $and, $xor, $eq..].
  18216. */
  18217. while( n < SX_ARRAYSIZE(aOpTable) && aOpTable[n].iOp != iExprOp ){
  18218. ++n;
  18219. }
  18220. pOp = &aOpTable[n];
  18221. /* Mark as binary '+' or '-', not an unary */
  18222. apNode[i]->pOp = pOp;
  18223. apNode[i]->pStart->pUserData = (void *)pOp;
  18224. }
  18225. }
  18226. }
  18227. }
  18228. if( iParen != 0 || iSquare != 0 || iBraces != 0){
  18229. rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[0]->pStart->nLine, "Syntax error, mismatched '(', '[' or '{'");
  18230. if( rc != SXERR_ABORT ){
  18231. rc = SXERR_SYNTAX;
  18232. }
  18233. return rc;
  18234. }
  18235. return SXRET_OK;
  18236. }
  18237. /*
  18238. * Extract a single expression node from the input.
  18239. * On success store the freshly extractd node in ppNode.
  18240. * When errors, JX9 take care of generating the appropriate error message.
  18241. * An expression node can be a variable [i.e: $var], an operator [i.e: ++]
  18242. * an annonymous function [i.e: function(){ return "Hello"; }, a double/single
  18243. * quoted string, a heredoc/nowdoc, a literal [i.e: JX9_EOL], a namespace path
  18244. * [i.e: namespaces\path\to..], a array/list [i.e: array(4, 5, 6)] and so on.
  18245. */
  18246. static sxi32 ExprExtractNode(jx9_gen_state *pGen, jx9_expr_node **ppNode)
  18247. {
  18248. jx9_expr_node *pNode;
  18249. SyToken *pCur;
  18250. sxi32 rc;
  18251. /* Allocate a new node */
  18252. pNode = (jx9_expr_node *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(jx9_expr_node));
  18253. if( pNode == 0 ){
  18254. /* If the supplied memory subsystem is so sick that we are unable to allocate
  18255. * a tiny chunk of memory, there is no much we can do here.
  18256. */
  18257. return SXERR_MEM;
  18258. }
  18259. /* Zero the structure */
  18260. SyZero(pNode, sizeof(jx9_expr_node));
  18261. SySetInit(&pNode->aNodeArgs, &pGen->pVm->sAllocator, sizeof(jx9_expr_node **));
  18262. /* Point to the head of the token stream */
  18263. pCur = pNode->pStart = pGen->pIn;
  18264. /* Start collecting tokens */
  18265. if( pCur->nType & JX9_TK_OP ){
  18266. /* Point to the instance that describe this operator */
  18267. pNode->pOp = (const jx9_expr_op *)pCur->pUserData;
  18268. /* Advance the stream cursor */
  18269. pCur++;
  18270. }else if( pCur->nType & JX9_TK_DOLLAR ){
  18271. /* Isolate variable */
  18272. pCur++; /* Jump the dollar sign */
  18273. if( pCur >= pGen->pEnd ){
  18274. /* Syntax error */
  18275. rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"Invalid variable name");
  18276. if( rc != SXERR_ABORT ){
  18277. rc = SXERR_SYNTAX;
  18278. }
  18279. SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
  18280. return rc;
  18281. }
  18282. pCur++; /* Jump the variable name */
  18283. pNode->xCode = jx9CompileVariable;
  18284. }else if( pCur->nType & JX9_TK_OCB /* '{' */ ){
  18285. /* JSON Object, assemble tokens */
  18286. pCur++;
  18287. jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_OCB /* '[' */, JX9_TK_CCB /* ']' */, &pCur);
  18288. if( pCur < pGen->pEnd ){
  18289. pCur++;
  18290. }else{
  18291. /* Syntax error */
  18292. rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"JSON Object: Missing closing braces '}'");
  18293. if( rc != SXERR_ABORT ){
  18294. rc = SXERR_SYNTAX;
  18295. }
  18296. SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
  18297. return rc;
  18298. }
  18299. pNode->xCode = jx9CompileJsonObject;
  18300. }else if( pCur->nType & JX9_TK_OSB /* '[' */ && !(pCur->nType & JX9_TK_OP) ){
  18301. /* JSON Array, assemble tokens */
  18302. pCur++;
  18303. jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_OSB /* '[' */, JX9_TK_CSB /* ']' */, &pCur);
  18304. if( pCur < pGen->pEnd ){
  18305. pCur++;
  18306. }else{
  18307. /* Syntax error */
  18308. rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"JSON Array: Missing closing square bracket ']'");
  18309. if( rc != SXERR_ABORT ){
  18310. rc = SXERR_SYNTAX;
  18311. }
  18312. SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
  18313. return rc;
  18314. }
  18315. pNode->xCode = jx9CompileJsonArray;
  18316. }else if( pCur->nType & JX9_TK_KEYWORD ){
  18317. int nKeyword = SX_PTR_TO_INT(pCur->pUserData);
  18318. if( nKeyword == JX9_TKWRD_FUNCTION ){
  18319. /* Annonymous function */
  18320. if( &pCur[1] >= pGen->pEnd ){
  18321. /* Assume a literal */
  18322. pCur++;
  18323. pNode->xCode = jx9CompileLiteral;
  18324. }else{
  18325. /* Assemble annonymous functions body */
  18326. rc = ExprAssembleAnnon(&(*pGen), &pCur, pGen->pEnd);
  18327. if( rc != SXRET_OK ){
  18328. SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
  18329. return rc;
  18330. }
  18331. pNode->xCode = jx9CompileAnnonFunc;
  18332. }
  18333. }else if( jx9IsLangConstruct(nKeyword) && &pCur[1] < pGen->pEnd ){
  18334. /* Language constructs [i.e: print,die...] require special handling */
  18335. jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_LPAREN|JX9_TK_OCB|JX9_TK_OSB, JX9_TK_RPAREN|JX9_TK_CCB|JX9_TK_CSB, &pCur);
  18336. pNode->xCode = jx9CompileLangConstruct;
  18337. }else{
  18338. /* Assume a literal */
  18339. pCur++;
  18340. pNode->xCode = jx9CompileLiteral;
  18341. }
  18342. }else if( pCur->nType & (JX9_TK_ID) ){
  18343. /* Constants, function name, namespace path, object name... */
  18344. pCur++;
  18345. pNode->xCode = jx9CompileLiteral;
  18346. }else{
  18347. 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 ){
  18348. /* Point to the code generator routine */
  18349. pNode->xCode = jx9GetNodeHandler(pCur->nType);
  18350. if( pNode->xCode == 0 ){
  18351. rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Syntax error: Unexpected token '%z'", &pNode->pStart->sData);
  18352. if( rc != SXERR_ABORT ){
  18353. rc = SXERR_SYNTAX;
  18354. }
  18355. SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
  18356. return rc;
  18357. }
  18358. }
  18359. /* Advance the stream cursor */
  18360. pCur++;
  18361. }
  18362. /* Point to the end of the token stream */
  18363. pNode->pEnd = pCur;
  18364. /* Save the node for later processing */
  18365. *ppNode = pNode;
  18366. /* Synchronize cursors */
  18367. pGen->pIn = pCur;
  18368. return SXRET_OK;
  18369. }
  18370. /*
  18371. * Free an expression tree.
  18372. */
  18373. static void ExprFreeTree(jx9_gen_state *pGen, jx9_expr_node *pNode)
  18374. {
  18375. if( pNode->pLeft ){
  18376. /* Release the left tree */
  18377. ExprFreeTree(&(*pGen), pNode->pLeft);
  18378. }
  18379. if( pNode->pRight ){
  18380. /* Release the right tree */
  18381. ExprFreeTree(&(*pGen), pNode->pRight);
  18382. }
  18383. if( pNode->pCond ){
  18384. /* Release the conditional tree used by the ternary operator */
  18385. ExprFreeTree(&(*pGen), pNode->pCond);
  18386. }
  18387. if( SySetUsed(&pNode->aNodeArgs) > 0 ){
  18388. jx9_expr_node **apArg;
  18389. sxu32 n;
  18390. /* Release node arguments */
  18391. apArg = (jx9_expr_node **)SySetBasePtr(&pNode->aNodeArgs);
  18392. for( n = 0 ; n < SySetUsed(&pNode->aNodeArgs) ; ++n ){
  18393. ExprFreeTree(&(*pGen), apArg[n]);
  18394. }
  18395. SySetRelease(&pNode->aNodeArgs);
  18396. }
  18397. /* Finally, release this node */
  18398. SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
  18399. }
  18400. /*
  18401. * Free an expression tree.
  18402. * This function is a wrapper around ExprFreeTree() defined above.
  18403. */
  18404. JX9_PRIVATE sxi32 jx9ExprFreeTree(jx9_gen_state *pGen, SySet *pNodeSet)
  18405. {
  18406. jx9_expr_node **apNode;
  18407. sxu32 n;
  18408. apNode = (jx9_expr_node **)SySetBasePtr(pNodeSet);
  18409. for( n = 0 ; n < SySetUsed(pNodeSet) ; ++n ){
  18410. if( apNode[n] ){
  18411. ExprFreeTree(&(*pGen), apNode[n]);
  18412. }
  18413. }
  18414. return SXRET_OK;
  18415. }
  18416. /*
  18417. * Check if the given node is a modifialbe l/r-value.
  18418. * Return TRUE if modifiable.FALSE otherwise.
  18419. */
  18420. static int ExprIsModifiableValue(jx9_expr_node *pNode)
  18421. {
  18422. sxi32 iExprOp;
  18423. if( pNode->pOp == 0 ){
  18424. return pNode->xCode == jx9CompileVariable ? TRUE : FALSE;
  18425. }
  18426. iExprOp = pNode->pOp->iOp;
  18427. if( iExprOp == EXPR_OP_DOT /*'.' */ ){
  18428. return TRUE;
  18429. }
  18430. if( iExprOp == EXPR_OP_SUBSCRIPT/*'[]'*/ ){
  18431. if( pNode->pLeft->pOp ) {
  18432. if( pNode->pLeft->pOp->iOp != EXPR_OP_SUBSCRIPT /*'['*/ && pNode->pLeft->pOp->iOp != EXPR_OP_DOT /*'.'*/){
  18433. return FALSE;
  18434. }
  18435. }else if( pNode->pLeft->xCode != jx9CompileVariable ){
  18436. return FALSE;
  18437. }
  18438. return TRUE;
  18439. }
  18440. /* Not a modifiable l or r-value */
  18441. return FALSE;
  18442. }
  18443. /* Forward declaration */
  18444. static sxi32 ExprMakeTree(jx9_gen_state *pGen, jx9_expr_node **apNode, sxi32 nToken);
  18445. /* Macro to check if the given node is a terminal */
  18446. #define NODE_ISTERM(NODE) (apNode[NODE] && (!apNode[NODE]->pOp || apNode[NODE]->pLeft ))
  18447. /*
  18448. * Buid an expression tree for each given function argument.
  18449. * When errors, JX9 take care of generating the appropriate error message.
  18450. */
  18451. static sxi32 ExprProcessFuncArguments(jx9_gen_state *pGen, jx9_expr_node *pOp, jx9_expr_node **apNode, sxi32 nToken)
  18452. {
  18453. sxi32 iNest, iCur, iNode;
  18454. sxi32 rc;
  18455. /* Process function arguments from left to right */
  18456. iCur = 0;
  18457. for(;;){
  18458. if( iCur >= nToken ){
  18459. /* No more arguments to process */
  18460. break;
  18461. }
  18462. iNode = iCur;
  18463. iNest = 0;
  18464. while( iCur < nToken ){
  18465. if( apNode[iCur] ){
  18466. if( (apNode[iCur]->pStart->nType & JX9_TK_COMMA) && apNode[iCur]->pLeft == 0 && iNest <= 0 ){
  18467. break;
  18468. }else if( apNode[iCur]->pStart->nType & (JX9_TK_LPAREN|JX9_TK_OSB|JX9_TK_OCB) ){
  18469. iNest++;
  18470. }else if( apNode[iCur]->pStart->nType & (JX9_TK_RPAREN|JX9_TK_CCB|JX9_TK_CSB) ){
  18471. iNest--;
  18472. }
  18473. }
  18474. iCur++;
  18475. }
  18476. if( iCur > iNode ){
  18477. ExprMakeTree(&(*pGen), &apNode[iNode], iCur-iNode);
  18478. if( apNode[iNode] ){
  18479. /* Put a pointer to the root of the tree in the arguments set */
  18480. SySetPut(&pOp->aNodeArgs, (const void *)&apNode[iNode]);
  18481. }else{
  18482. /* Empty function argument */
  18483. rc = jx9GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Empty function argument");
  18484. if( rc != SXERR_ABORT ){
  18485. rc = SXERR_SYNTAX;
  18486. }
  18487. return rc;
  18488. }
  18489. }else{
  18490. rc = jx9GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Missing function argument");
  18491. if( rc != SXERR_ABORT ){
  18492. rc = SXERR_SYNTAX;
  18493. }
  18494. return rc;
  18495. }
  18496. /* Jump trailing comma */
  18497. if( iCur < nToken && apNode[iCur] && (apNode[iCur]->pStart->nType & JX9_TK_COMMA) ){
  18498. iCur++;
  18499. if( iCur >= nToken ){
  18500. /* missing function argument */
  18501. rc = jx9GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Missing function argument");
  18502. if( rc != SXERR_ABORT ){
  18503. rc = SXERR_SYNTAX;
  18504. }
  18505. return rc;
  18506. }
  18507. }
  18508. }
  18509. return SXRET_OK;
  18510. }
  18511. /*
  18512. * Create an expression tree from an array of tokens.
  18513. * If successful, the root of the tree is stored in apNode[0].
  18514. * When errors, JX9 take care of generating the appropriate error message.
  18515. */
  18516. static sxi32 ExprMakeTree(jx9_gen_state *pGen, jx9_expr_node **apNode, sxi32 nToken)
  18517. {
  18518. sxi32 i, iLeft, iRight;
  18519. jx9_expr_node *pNode;
  18520. sxi32 iCur;
  18521. sxi32 rc;
  18522. if( nToken <= 0 || (nToken == 1 && apNode[0]->xCode) ){
  18523. /* TICKET 1433-17: self evaluating node */
  18524. return SXRET_OK;
  18525. }
  18526. /* Process expressions enclosed in parenthesis first */
  18527. for( iCur = 0 ; iCur < nToken ; ++iCur ){
  18528. sxi32 iNest;
  18529. /* Note that, we use strict comparison here '!=' instead of the bitwise and '&' operator
  18530. * since the LPAREN token can also be an operator [i.e: Function call].
  18531. */
  18532. if( apNode[iCur] == 0 || apNode[iCur]->pStart->nType != JX9_TK_LPAREN ){
  18533. continue;
  18534. }
  18535. iNest = 1;
  18536. iLeft = iCur;
  18537. /* Find the closing parenthesis */
  18538. iCur++;
  18539. while( iCur < nToken ){
  18540. if( apNode[iCur] ){
  18541. if( apNode[iCur]->pStart->nType & JX9_TK_RPAREN /* ')' */){
  18542. /* Decrement nesting level */
  18543. iNest--;
  18544. if( iNest <= 0 ){
  18545. break;
  18546. }
  18547. }else if( apNode[iCur]->pStart->nType & JX9_TK_LPAREN /* '(' */ ){
  18548. /* Increment nesting level */
  18549. iNest++;
  18550. }
  18551. }
  18552. iCur++;
  18553. }
  18554. if( iCur - iLeft > 1 ){
  18555. /* Recurse and process this expression */
  18556. rc = ExprMakeTree(&(*pGen), &apNode[iLeft + 1], iCur - iLeft - 1);
  18557. if( rc != SXRET_OK ){
  18558. return rc;
  18559. }
  18560. }
  18561. /* Free the left and right nodes */
  18562. ExprFreeTree(&(*pGen), apNode[iLeft]);
  18563. ExprFreeTree(&(*pGen), apNode[iCur]);
  18564. apNode[iLeft] = 0;
  18565. apNode[iCur] = 0;
  18566. }
  18567. /* Handle postfix [i.e: function call, member access] operators with precedence 2 */
  18568. iLeft = -1;
  18569. for( iCur = 0 ; iCur < nToken ; ++iCur ){
  18570. if( apNode[iCur] == 0 ){
  18571. continue;
  18572. }
  18573. pNode = apNode[iCur];
  18574. if( pNode->pOp && pNode->pOp->iPrec == 2 && pNode->pLeft == 0 ){
  18575. if( pNode->pOp->iOp == EXPR_OP_FUNC_CALL ){
  18576. /* Collect function arguments */
  18577. sxi32 iPtr = 0;
  18578. sxi32 nFuncTok = 0;
  18579. while( nFuncTok + iCur < nToken ){
  18580. if( apNode[nFuncTok+iCur] ){
  18581. if( apNode[nFuncTok+iCur]->pStart->nType & JX9_TK_LPAREN /*'('*/ ){
  18582. iPtr++;
  18583. }else if ( apNode[nFuncTok+iCur]->pStart->nType & JX9_TK_RPAREN /*')'*/){
  18584. iPtr--;
  18585. if( iPtr <= 0 ){
  18586. break;
  18587. }
  18588. }
  18589. }
  18590. nFuncTok++;
  18591. }
  18592. if( nFuncTok + iCur >= nToken ){
  18593. /* Syntax error */
  18594. rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Missing right parenthesis ')'");
  18595. if( rc != SXERR_ABORT ){
  18596. rc = SXERR_SYNTAX;
  18597. }
  18598. return rc;
  18599. }
  18600. if( iLeft < 0 || !NODE_ISTERM(iLeft) /*|| ( apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec != 2)*/ ){
  18601. /* Syntax error */
  18602. rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Invalid function name");
  18603. if( rc != SXERR_ABORT ){
  18604. rc = SXERR_SYNTAX;
  18605. }
  18606. return rc;
  18607. }
  18608. if( nFuncTok > 1 ){
  18609. /* Process function arguments */
  18610. rc = ExprProcessFuncArguments(&(*pGen), pNode, &apNode[iCur+1], nFuncTok-1);
  18611. if( rc != SXRET_OK ){
  18612. return rc;
  18613. }
  18614. }
  18615. /* Link the node to the tree */
  18616. pNode->pLeft = apNode[iLeft];
  18617. apNode[iLeft] = 0;
  18618. for( iPtr = 1; iPtr <= nFuncTok ; iPtr++ ){
  18619. apNode[iCur+iPtr] = 0;
  18620. }
  18621. }else if (pNode->pOp->iOp == EXPR_OP_SUBSCRIPT ){
  18622. /* Subscripting */
  18623. sxi32 iArrTok = iCur + 1;
  18624. sxi32 iNest = 1;
  18625. if( iLeft >= 0 && (apNode[iLeft]->xCode == jx9CompileVariable || (apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec == 2 /* postfix */) ) ){
  18626. /* Collect index tokens */
  18627. while( iArrTok < nToken ){
  18628. if( apNode[iArrTok] ){
  18629. if( apNode[iArrTok]->pStart->nType & JX9_TK_OSB /*'['*/){
  18630. /* Increment nesting level */
  18631. iNest++;
  18632. }else if( apNode[iArrTok]->pStart->nType & JX9_TK_CSB /*']'*/){
  18633. /* Decrement nesting level */
  18634. iNest--;
  18635. if( iNest <= 0 ){
  18636. break;
  18637. }
  18638. }
  18639. }
  18640. ++iArrTok;
  18641. }
  18642. if( iArrTok > iCur + 1 ){
  18643. /* Recurse and process this expression */
  18644. rc = ExprMakeTree(&(*pGen), &apNode[iCur+1], iArrTok - iCur - 1);
  18645. if( rc != SXRET_OK ){
  18646. return rc;
  18647. }
  18648. /* Link the node to it's index */
  18649. SySetPut(&pNode->aNodeArgs, (const void *)&apNode[iCur+1]);
  18650. }
  18651. /* Link the node to the tree */
  18652. pNode->pLeft = apNode[iLeft];
  18653. pNode->pRight = 0;
  18654. apNode[iLeft] = 0;
  18655. for( iNest = iCur + 1 ; iNest <= iArrTok ; ++iNest ){
  18656. apNode[iNest] = 0;
  18657. }
  18658. }
  18659. }else{
  18660. /* Member access operators [i.e: '.' ] */
  18661. iRight = iCur + 1;
  18662. while( iRight < nToken && apNode[iRight] == 0 ){
  18663. iRight++;
  18664. }
  18665. if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){
  18666. /* Syntax error */
  18667. rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid member name", &pNode->pOp->sOp);
  18668. if( rc != SXERR_ABORT ){
  18669. rc = SXERR_SYNTAX;
  18670. }
  18671. return rc;
  18672. }
  18673. /* Link the node to the tree */
  18674. pNode->pLeft = apNode[iLeft];
  18675. if( pNode->pLeft->pOp == 0 && pNode->pLeft->xCode != jx9CompileVariable ){
  18676. /* Syntax error */
  18677. rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,
  18678. "'%z': Expecting a variable as left operand", &pNode->pOp->sOp);
  18679. if( rc != SXERR_ABORT ){
  18680. rc = SXERR_SYNTAX;
  18681. }
  18682. return rc;
  18683. }
  18684. pNode->pRight = apNode[iRight];
  18685. apNode[iLeft] = apNode[iRight] = 0;
  18686. }
  18687. }
  18688. iLeft = iCur;
  18689. }
  18690. /* Handle post/pre icrement/decrement [i.e: ++/--] operators with precedence 3 */
  18691. iLeft = -1;
  18692. for( iCur = 0 ; iCur < nToken ; ++iCur ){
  18693. if( apNode[iCur] == 0 ){
  18694. continue;
  18695. }
  18696. pNode = apNode[iCur];
  18697. if( pNode->pOp && pNode->pOp->iPrec == 3 && pNode->pLeft == 0){
  18698. if( iLeft >= 0 && ((apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec == 2 /* Postfix */)
  18699. || apNode[iLeft]->xCode == jx9CompileVariable) ){
  18700. /* Link the node to the tree */
  18701. pNode->pLeft = apNode[iLeft];
  18702. apNode[iLeft] = 0;
  18703. }
  18704. }
  18705. iLeft = iCur;
  18706. }
  18707. iLeft = -1;
  18708. for( iCur = nToken - 1 ; iCur >= 0 ; iCur-- ){
  18709. if( apNode[iCur] == 0 ){
  18710. continue;
  18711. }
  18712. pNode = apNode[iCur];
  18713. if( pNode->pOp && pNode->pOp->iPrec == 3 && pNode->pLeft == 0){
  18714. if( iLeft < 0 || (apNode[iLeft]->pOp == 0 && apNode[iLeft]->xCode != jx9CompileVariable)
  18715. || ( apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec != 2 /* Postfix */) ){
  18716. /* Syntax error */
  18717. rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z' operator needs l-value", &pNode->pOp->sOp);
  18718. if( rc != SXERR_ABORT ){
  18719. rc = SXERR_SYNTAX;
  18720. }
  18721. return rc;
  18722. }
  18723. /* Link the node to the tree */
  18724. pNode->pLeft = apNode[iLeft];
  18725. apNode[iLeft] = 0;
  18726. /* Mark as pre-increment/decrement node */
  18727. pNode->iFlags |= EXPR_NODE_PRE_INCR;
  18728. }
  18729. iLeft = iCur;
  18730. }
  18731. /* Handle right associative unary and cast operators [i.e: !, (string), ~...] with precedence 4 */
  18732. iLeft = 0;
  18733. for( iCur = nToken - 1 ; iCur >= 0 ; iCur-- ){
  18734. if( apNode[iCur] ){
  18735. pNode = apNode[iCur];
  18736. if( pNode->pOp && pNode->pOp->iPrec == 4 && pNode->pLeft == 0){
  18737. if( iLeft > 0 ){
  18738. /* Link the node to the tree */
  18739. pNode->pLeft = apNode[iLeft];
  18740. apNode[iLeft] = 0;
  18741. if( pNode->pLeft && pNode->pLeft->pOp && pNode->pLeft->pOp->iPrec > 4 ){
  18742. if( pNode->pLeft->pLeft == 0 || pNode->pLeft->pRight == 0 ){
  18743. /* Syntax error */
  18744. rc = jx9GenCompileError(pGen, E_ERROR, pNode->pLeft->pStart->nLine, "'%z': Missing operand", &pNode->pLeft->pOp->sOp);
  18745. if( rc != SXERR_ABORT ){
  18746. rc = SXERR_SYNTAX;
  18747. }
  18748. return rc;
  18749. }
  18750. }
  18751. }else{
  18752. /* Syntax error */
  18753. rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing operand", &pNode->pOp->sOp);
  18754. if( rc != SXERR_ABORT ){
  18755. rc = SXERR_SYNTAX;
  18756. }
  18757. return rc;
  18758. }
  18759. }
  18760. /* Save terminal position */
  18761. iLeft = iCur;
  18762. }
  18763. }
  18764. /* Process left and non-associative binary operators [i.e: *, /, &&, ||...]*/
  18765. for( i = 7 ; i < 17 ; i++ ){
  18766. iLeft = -1;
  18767. for( iCur = 0 ; iCur < nToken ; ++iCur ){
  18768. if( apNode[iCur] == 0 ){
  18769. continue;
  18770. }
  18771. pNode = apNode[iCur];
  18772. if( pNode->pOp && pNode->pOp->iPrec == i && pNode->pLeft == 0 ){
  18773. /* Get the right node */
  18774. iRight = iCur + 1;
  18775. while( iRight < nToken && apNode[iRight] == 0 ){
  18776. iRight++;
  18777. }
  18778. if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){
  18779. /* Syntax error */
  18780. rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp);
  18781. if( rc != SXERR_ABORT ){
  18782. rc = SXERR_SYNTAX;
  18783. }
  18784. return rc;
  18785. }
  18786. /* Link the node to the tree */
  18787. pNode->pLeft = apNode[iLeft];
  18788. pNode->pRight = apNode[iRight];
  18789. apNode[iLeft] = apNode[iRight] = 0;
  18790. }
  18791. iLeft = iCur;
  18792. }
  18793. }
  18794. /* Handle the ternary operator. (expr1) ? (expr2) : (expr3)
  18795. * Note that we do not need a precedence loop here since
  18796. * we are dealing with a single operator.
  18797. */
  18798. iLeft = -1;
  18799. for( iCur = 0 ; iCur < nToken ; ++iCur ){
  18800. if( apNode[iCur] == 0 ){
  18801. continue;
  18802. }
  18803. pNode = apNode[iCur];
  18804. if( pNode->pOp && pNode->pOp->iOp == EXPR_OP_QUESTY && pNode->pLeft == 0 ){
  18805. sxi32 iNest = 1;
  18806. if( iLeft < 0 || !NODE_ISTERM(iLeft) ){
  18807. /* Missing condition */
  18808. rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Syntax error", &pNode->pOp->sOp);
  18809. if( rc != SXERR_ABORT ){
  18810. rc = SXERR_SYNTAX;
  18811. }
  18812. return rc;
  18813. }
  18814. /* Get the right node */
  18815. iRight = iCur + 1;
  18816. while( iRight < nToken ){
  18817. if( apNode[iRight] ){
  18818. if( apNode[iRight]->pOp && apNode[iRight]->pOp->iOp == EXPR_OP_QUESTY && apNode[iRight]->pCond == 0){
  18819. /* Increment nesting level */
  18820. ++iNest;
  18821. }else if( apNode[iRight]->pStart->nType & JX9_TK_COLON /*:*/ ){
  18822. /* Decrement nesting level */
  18823. --iNest;
  18824. if( iNest <= 0 ){
  18825. break;
  18826. }
  18827. }
  18828. }
  18829. iRight++;
  18830. }
  18831. if( iRight > iCur + 1 ){
  18832. /* Recurse and process the then expression */
  18833. rc = ExprMakeTree(&(*pGen), &apNode[iCur + 1], iRight - iCur - 1);
  18834. if( rc != SXRET_OK ){
  18835. return rc;
  18836. }
  18837. /* Link the node to the tree */
  18838. pNode->pLeft = apNode[iCur + 1];
  18839. }else{
  18840. rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing 'then' expression", &pNode->pOp->sOp);
  18841. if( rc != SXERR_ABORT ){
  18842. rc = SXERR_SYNTAX;
  18843. }
  18844. return rc;
  18845. }
  18846. apNode[iCur + 1] = 0;
  18847. if( iRight + 1 < nToken ){
  18848. /* Recurse and process the else expression */
  18849. rc = ExprMakeTree(&(*pGen), &apNode[iRight + 1], nToken - iRight - 1);
  18850. if( rc != SXRET_OK ){
  18851. return rc;
  18852. }
  18853. /* Link the node to the tree */
  18854. pNode->pRight = apNode[iRight + 1];
  18855. apNode[iRight + 1] = apNode[iRight] = 0;
  18856. }else{
  18857. rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing 'else' expression", &pNode->pOp->sOp);
  18858. if( rc != SXERR_ABORT ){
  18859. rc = SXERR_SYNTAX;
  18860. }
  18861. return rc;
  18862. }
  18863. /* Point to the condition */
  18864. pNode->pCond = apNode[iLeft];
  18865. apNode[iLeft] = 0;
  18866. break;
  18867. }
  18868. iLeft = iCur;
  18869. }
  18870. /* Process right associative binary operators [i.e: '=', '+=', '/=']
  18871. * Note: All right associative binary operators have precedence 18
  18872. * so there is no need for a precedence loop here.
  18873. */
  18874. iRight = -1;
  18875. for( iCur = nToken - 1 ; iCur >= 0 ; iCur--){
  18876. if( apNode[iCur] == 0 ){
  18877. continue;
  18878. }
  18879. pNode = apNode[iCur];
  18880. if( pNode->pOp && pNode->pOp->iPrec == 18 && pNode->pLeft == 0 ){
  18881. /* Get the left node */
  18882. iLeft = iCur - 1;
  18883. while( iLeft >= 0 && apNode[iLeft] == 0 ){
  18884. iLeft--;
  18885. }
  18886. if( iLeft < 0 || iRight < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){
  18887. /* Syntax error */
  18888. rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp);
  18889. if( rc != SXERR_ABORT ){
  18890. rc = SXERR_SYNTAX;
  18891. }
  18892. return rc;
  18893. }
  18894. if( ExprIsModifiableValue(apNode[iLeft]) == FALSE ){
  18895. if( pNode->pOp->iVmOp != JX9_OP_STORE ){
  18896. /* Left operand must be a modifiable l-value */
  18897. rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,
  18898. "'%z': Left operand must be a modifiable l-value", &pNode->pOp->sOp);
  18899. if( rc != SXERR_ABORT ){
  18900. rc = SXERR_SYNTAX;
  18901. }
  18902. return rc;
  18903. }
  18904. }
  18905. /* Link the node to the tree (Reverse) */
  18906. pNode->pLeft = apNode[iRight];
  18907. pNode->pRight = apNode[iLeft];
  18908. apNode[iLeft] = apNode[iRight] = 0;
  18909. }
  18910. iRight = iCur;
  18911. }
  18912. /* Process the lowest precedence operator (22, comma) */
  18913. iLeft = -1;
  18914. for( iCur = 0 ; iCur < nToken ; ++iCur ){
  18915. if( apNode[iCur] == 0 ){
  18916. continue;
  18917. }
  18918. pNode = apNode[iCur];
  18919. if( pNode->pOp && pNode->pOp->iPrec == 22 /* ',' */ && pNode->pLeft == 0 ){
  18920. /* Get the right node */
  18921. iRight = iCur + 1;
  18922. while( iRight < nToken && apNode[iRight] == 0 ){
  18923. iRight++;
  18924. }
  18925. if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){
  18926. /* Syntax error */
  18927. rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp);
  18928. if( rc != SXERR_ABORT ){
  18929. rc = SXERR_SYNTAX;
  18930. }
  18931. return rc;
  18932. }
  18933. /* Link the node to the tree */
  18934. pNode->pLeft = apNode[iLeft];
  18935. pNode->pRight = apNode[iRight];
  18936. apNode[iLeft] = apNode[iRight] = 0;
  18937. }
  18938. iLeft = iCur;
  18939. }
  18940. /* Point to the root of the expression tree */
  18941. for( iCur = 1 ; iCur < nToken ; ++iCur ){
  18942. if( apNode[iCur] ){
  18943. if( (apNode[iCur]->pOp || apNode[iCur]->xCode ) && apNode[0] != 0){
  18944. rc = jx9GenCompileError(pGen, E_ERROR, apNode[iCur]->pStart->nLine, "Unexpected token '%z'", &apNode[iCur]->pStart->sData);
  18945. if( rc != SXERR_ABORT ){
  18946. rc = SXERR_SYNTAX;
  18947. }
  18948. return rc;
  18949. }
  18950. apNode[0] = apNode[iCur];
  18951. apNode[iCur] = 0;
  18952. }
  18953. }
  18954. return SXRET_OK;
  18955. }
  18956. /*
  18957. * Build an expression tree from the freshly extracted raw tokens.
  18958. * If successful, the root of the tree is stored in ppRoot.
  18959. * When errors, JX9 take care of generating the appropriate error message.
  18960. * This is the public interface used by the most code generator routines.
  18961. */
  18962. JX9_PRIVATE sxi32 jx9ExprMakeTree(jx9_gen_state *pGen, SySet *pExprNode, jx9_expr_node **ppRoot)
  18963. {
  18964. jx9_expr_node **apNode;
  18965. jx9_expr_node *pNode;
  18966. sxi32 rc;
  18967. /* Reset node container */
  18968. SySetReset(pExprNode);
  18969. pNode = 0; /* Prevent compiler warning */
  18970. /* Extract nodes one after one until we hit the end of the input */
  18971. while( pGen->pIn < pGen->pEnd ){
  18972. rc = ExprExtractNode(&(*pGen), &pNode);
  18973. if( rc != SXRET_OK ){
  18974. return rc;
  18975. }
  18976. /* Save the extracted node */
  18977. SySetPut(pExprNode, (const void *)&pNode);
  18978. }
  18979. if( SySetUsed(pExprNode) < 1 ){
  18980. /* Empty expression [i.e: A semi-colon;] */
  18981. *ppRoot = 0;
  18982. return SXRET_OK;
  18983. }
  18984. apNode = (jx9_expr_node **)SySetBasePtr(pExprNode);
  18985. /* Make sure we are dealing with valid nodes */
  18986. rc = ExprVerifyNodes(&(*pGen), apNode, (sxi32)SySetUsed(pExprNode));
  18987. if( rc != SXRET_OK ){
  18988. /* Don't worry about freeing memory, upper layer will
  18989. * cleanup the mess left behind.
  18990. */
  18991. *ppRoot = 0;
  18992. return rc;
  18993. }
  18994. /* Build the tree */
  18995. rc = ExprMakeTree(&(*pGen), apNode, (sxi32)SySetUsed(pExprNode));
  18996. if( rc != SXRET_OK ){
  18997. /* Something goes wrong [i.e: Syntax error] */
  18998. *ppRoot = 0;
  18999. return rc;
  19000. }
  19001. /* Point to the root of the tree */
  19002. *ppRoot = apNode[0];
  19003. return SXRET_OK;
  19004. }
  19005. /*
  19006. * ----------------------------------------------------------
  19007. * File: memobj.c
  19008. * MD5: 8692d7f4cb297c0946066b4a9034c637
  19009. * ----------------------------------------------------------
  19010. */
  19011. /*
  19012. * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
  19013. * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
  19014. * Version 1.7.2
  19015. * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  19016. * please contact Symisc Systems via:
  19017. * legal@symisc.net
  19018. * licensing@symisc.net
  19019. * contact@symisc.net
  19020. * or visit:
  19021. * http://jx9.symisc.net/
  19022. */
  19023. /* $SymiscID: memobj.c v2.7 FreeBSD 2012-08-09 03:40 stable <chm@symisc.net> $ */
  19024. #ifndef JX9_AMALGAMATION
  19025. #include "jx9Int.h"
  19026. #endif
  19027. /* This file manage low-level stuff related to indexed memory objects [i.e: jx9_value] */
  19028. /*
  19029. * Notes on memory objects [i.e: jx9_value].
  19030. * Internally, the JX9 virtual machine manipulates nearly all JX9 values
  19031. * [i.e: string, int, float, resource, object, bool, null..] as jx9_values structures.
  19032. * Each jx9_values struct may cache multiple representations (string,
  19033. * integer etc.) of the same value.
  19034. */
  19035. /*
  19036. * Convert a 64-bit IEEE double into a 64-bit signed integer.
  19037. * If the double is too large, return 0x8000000000000000.
  19038. *
  19039. * Most systems appear to do this simply by assigning ariables and without
  19040. * the extra range tests.
  19041. * But there are reports that windows throws an expection if the floating
  19042. * point value is out of range.
  19043. */
  19044. static sxi64 MemObjRealToInt(jx9_value *pObj)
  19045. {
  19046. #ifdef JX9_OMIT_FLOATING_POINT
  19047. /* Real and 64bit integer are the same when floating point arithmetic
  19048. * is omitted from the build.
  19049. */
  19050. return pObj->x.rVal;
  19051. #else
  19052. /*
  19053. ** Many compilers we encounter do not define constants for the
  19054. ** minimum and maximum 64-bit integers, or they define them
  19055. ** inconsistently. And many do not understand the "LL" notation.
  19056. ** So we define our own static constants here using nothing
  19057. ** larger than a 32-bit integer constant.
  19058. */
  19059. static const sxi64 maxInt = LARGEST_INT64;
  19060. static const sxi64 minInt = SMALLEST_INT64;
  19061. jx9_real r = pObj->x.rVal;
  19062. if( r<(jx9_real)minInt ){
  19063. return minInt;
  19064. }else if( r>(jx9_real)maxInt ){
  19065. /* minInt is correct here - not maxInt. It turns out that assigning
  19066. ** a very large positive number to an integer results in a very large
  19067. ** negative integer. This makes no sense, but it is what x86 hardware
  19068. ** does so for compatibility we will do the same in software. */
  19069. return minInt;
  19070. }else{
  19071. return (sxi64)r;
  19072. }
  19073. #endif
  19074. }
  19075. /*
  19076. * Convert a raw token value typically a stream of digit [i.e: hex, octal, binary or decimal]
  19077. * to a 64-bit integer.
  19078. */
  19079. JX9_PRIVATE sxi64 jx9TokenValueToInt64(SyString *pVal)
  19080. {
  19081. sxi64 iVal = 0;
  19082. if( pVal->nByte <= 0 ){
  19083. return 0;
  19084. }
  19085. if( pVal->zString[0] == '0' ){
  19086. sxi32 c;
  19087. if( pVal->nByte == sizeof(char) ){
  19088. return 0;
  19089. }
  19090. c = pVal->zString[1];
  19091. if( c == 'x' || c == 'X' ){
  19092. /* Hex digit stream */
  19093. SyHexStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0);
  19094. }else if( c == 'b' || c == 'B' ){
  19095. /* Binary digit stream */
  19096. SyBinaryStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0);
  19097. }else{
  19098. /* Octal digit stream */
  19099. SyOctalStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0);
  19100. }
  19101. }else{
  19102. /* Decimal digit stream */
  19103. SyStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0);
  19104. }
  19105. return iVal;
  19106. }
  19107. /*
  19108. * Return some kind of 64-bit integer value which is the best we can
  19109. * do at representing the value that pObj describes as a string
  19110. * representation.
  19111. */
  19112. static sxi64 MemObjStringToInt(jx9_value *pObj)
  19113. {
  19114. SyString sVal;
  19115. SyStringInitFromBuf(&sVal, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
  19116. return jx9TokenValueToInt64(&sVal);
  19117. }
  19118. /*
  19119. * Return some kind of integer value which is the best we can
  19120. * do at representing the value that pObj describes as an integer.
  19121. * If pObj is an integer, then the value is exact. If pObj is
  19122. * a floating-point then the value returned is the integer part.
  19123. * If pObj is a string, then we make an attempt to convert it into
  19124. * a integer and return that.
  19125. * If pObj represents a NULL value, return 0.
  19126. */
  19127. static sxi64 MemObjIntValue(jx9_value *pObj)
  19128. {
  19129. sxi32 iFlags;
  19130. iFlags = pObj->iFlags;
  19131. if (iFlags & MEMOBJ_REAL ){
  19132. return MemObjRealToInt(&(*pObj));
  19133. }else if( iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
  19134. return pObj->x.iVal;
  19135. }else if (iFlags & MEMOBJ_STRING) {
  19136. return MemObjStringToInt(&(*pObj));
  19137. }else if( iFlags & MEMOBJ_NULL ){
  19138. return 0;
  19139. }else if( iFlags & MEMOBJ_HASHMAP ){
  19140. jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
  19141. sxu32 n = pMap->nEntry;
  19142. jx9HashmapUnref(pMap);
  19143. /* Return total number of entries in the hashmap */
  19144. return n;
  19145. }else if(iFlags & MEMOBJ_RES ){
  19146. return pObj->x.pOther != 0;
  19147. }
  19148. /* CANT HAPPEN */
  19149. return 0;
  19150. }
  19151. /*
  19152. * Return some kind of real value which is the best we can
  19153. * do at representing the value that pObj describes as a real.
  19154. * If pObj is a real, then the value is exact.If pObj is an
  19155. * integer then the integer is promoted to real and that value
  19156. * is returned.
  19157. * If pObj is a string, then we make an attempt to convert it
  19158. * into a real and return that.
  19159. * If pObj represents a NULL value, return 0.0
  19160. */
  19161. static jx9_real MemObjRealValue(jx9_value *pObj)
  19162. {
  19163. sxi32 iFlags;
  19164. iFlags = pObj->iFlags;
  19165. if( iFlags & MEMOBJ_REAL ){
  19166. return pObj->x.rVal;
  19167. }else if (iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
  19168. return (jx9_real)pObj->x.iVal;
  19169. }else if (iFlags & MEMOBJ_STRING){
  19170. SyString sString;
  19171. #ifdef JX9_OMIT_FLOATING_POINT
  19172. jx9_real rVal = 0;
  19173. #else
  19174. jx9_real rVal = 0.0;
  19175. #endif
  19176. SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
  19177. if( SyBlobLength(&pObj->sBlob) > 0 ){
  19178. /* Convert as much as we can */
  19179. #ifdef JX9_OMIT_FLOATING_POINT
  19180. rVal = MemObjStringToInt(&(*pObj));
  19181. #else
  19182. SyStrToReal(sString.zString, sString.nByte, (void *)&rVal, 0);
  19183. #endif
  19184. }
  19185. return rVal;
  19186. }else if( iFlags & MEMOBJ_NULL ){
  19187. #ifdef JX9_OMIT_FLOATING_POINT
  19188. return 0;
  19189. #else
  19190. return 0.0;
  19191. #endif
  19192. }else if( iFlags & MEMOBJ_HASHMAP ){
  19193. /* Return the total number of entries in the hashmap */
  19194. jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
  19195. jx9_real n = (jx9_real)pMap->nEntry;
  19196. jx9HashmapUnref(pMap);
  19197. return n;
  19198. }else if(iFlags & MEMOBJ_RES ){
  19199. return (jx9_real)(pObj->x.pOther != 0);
  19200. }
  19201. /* NOT REACHED */
  19202. return 0;
  19203. }
  19204. /*
  19205. * Return the string representation of a given jx9_value.
  19206. * This function never fail and always return SXRET_OK.
  19207. */
  19208. static sxi32 MemObjStringValue(SyBlob *pOut,jx9_value *pObj)
  19209. {
  19210. if( pObj->iFlags & MEMOBJ_REAL ){
  19211. SyBlobFormat(&(*pOut), "%.15g", pObj->x.rVal);
  19212. }else if( pObj->iFlags & MEMOBJ_INT ){
  19213. SyBlobFormat(&(*pOut), "%qd", pObj->x.iVal);
  19214. /* %qd (BSD quad) is equivalent to %lld in the libc printf */
  19215. }else if( pObj->iFlags & MEMOBJ_BOOL ){
  19216. if( pObj->x.iVal ){
  19217. SyBlobAppend(&(*pOut),"true", sizeof("true")-1);
  19218. }else{
  19219. SyBlobAppend(&(*pOut),"false", sizeof("false")-1);
  19220. }
  19221. }else if( pObj->iFlags & MEMOBJ_HASHMAP ){
  19222. /* Serialize JSON object or array */
  19223. jx9JsonSerialize(pObj,pOut);
  19224. jx9HashmapUnref((jx9_hashmap *)pObj->x.pOther);
  19225. }else if(pObj->iFlags & MEMOBJ_RES ){
  19226. SyBlobFormat(&(*pOut), "ResourceID_%#x", pObj->x.pOther);
  19227. }
  19228. return SXRET_OK;
  19229. }
  19230. /*
  19231. * Return some kind of boolean value which is the best we can do
  19232. * at representing the value that pObj describes as a boolean.
  19233. * When converting to boolean, the following values are considered FALSE:
  19234. * NULL
  19235. * the boolean FALSE itself.
  19236. * the integer 0 (zero).
  19237. * the real 0.0 (zero).
  19238. * the empty string, a stream of zero [i.e: "0", "00", "000", ...] and the string
  19239. * "false".
  19240. * an array with zero elements.
  19241. */
  19242. static sxi32 MemObjBooleanValue(jx9_value *pObj)
  19243. {
  19244. sxi32 iFlags;
  19245. iFlags = pObj->iFlags;
  19246. if (iFlags & MEMOBJ_REAL ){
  19247. #ifdef JX9_OMIT_FLOATING_POINT
  19248. return pObj->x.rVal ? 1 : 0;
  19249. #else
  19250. return pObj->x.rVal != 0.0 ? 1 : 0;
  19251. #endif
  19252. }else if( iFlags & MEMOBJ_INT ){
  19253. return pObj->x.iVal ? 1 : 0;
  19254. }else if (iFlags & MEMOBJ_STRING) {
  19255. SyString sString;
  19256. SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
  19257. if( sString.nByte == 0 ){
  19258. /* Empty string */
  19259. return 0;
  19260. }else if( (sString.nByte == sizeof("true") - 1 && SyStrnicmp(sString.zString, "true", sizeof("true")-1) == 0) ||
  19261. (sString.nByte == sizeof("on") - 1 && SyStrnicmp(sString.zString, "on", sizeof("on")-1) == 0) ||
  19262. (sString.nByte == sizeof("yes") - 1 && SyStrnicmp(sString.zString, "yes", sizeof("yes")-1) == 0) ){
  19263. return 1;
  19264. }else if( sString.nByte == sizeof("false") - 1 && SyStrnicmp(sString.zString, "false", sizeof("false")-1) == 0 ){
  19265. return 0;
  19266. }else{
  19267. const char *zIn, *zEnd;
  19268. zIn = sString.zString;
  19269. zEnd = &zIn[sString.nByte];
  19270. while( zIn < zEnd && zIn[0] == '0' ){
  19271. zIn++;
  19272. }
  19273. return zIn >= zEnd ? 0 : 1;
  19274. }
  19275. }else if( iFlags & MEMOBJ_NULL ){
  19276. return 0;
  19277. }else if( iFlags & MEMOBJ_HASHMAP ){
  19278. jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
  19279. sxu32 n = pMap->nEntry;
  19280. jx9HashmapUnref(pMap);
  19281. return n > 0 ? TRUE : FALSE;
  19282. }else if(iFlags & MEMOBJ_RES ){
  19283. return pObj->x.pOther != 0;
  19284. }
  19285. /* NOT REACHED */
  19286. return 0;
  19287. }
  19288. /*
  19289. * If the jx9_value is of type real, try to make it an integer also.
  19290. */
  19291. static sxi32 MemObjTryIntger(jx9_value *pObj)
  19292. {
  19293. sxi64 iVal = MemObjRealToInt(&(*pObj));
  19294. /* Only mark the value as an integer if
  19295. **
  19296. ** (1) the round-trip conversion real->int->real is a no-op, and
  19297. ** (2) The integer is neither the largest nor the smallest
  19298. ** possible integer
  19299. **
  19300. ** The second and third terms in the following conditional enforces
  19301. ** the second condition under the assumption that addition overflow causes
  19302. ** values to wrap around. On x86 hardware, the third term is always
  19303. ** true and could be omitted. But we leave it in because other
  19304. ** architectures might behave differently.
  19305. */
  19306. if( pObj->x.rVal ==(jx9_real)iVal && iVal>SMALLEST_INT64 && iVal<LARGEST_INT64 ){
  19307. pObj->x.iVal = iVal;
  19308. pObj->iFlags = MEMOBJ_INT;
  19309. }
  19310. return SXRET_OK;
  19311. }
  19312. /*
  19313. * Convert a jx9_value to type integer.Invalidate any prior representations.
  19314. */
  19315. JX9_PRIVATE sxi32 jx9MemObjToInteger(jx9_value *pObj)
  19316. {
  19317. if( (pObj->iFlags & MEMOBJ_INT) == 0 ){
  19318. /* Preform the conversion */
  19319. pObj->x.iVal = MemObjIntValue(&(*pObj));
  19320. /* Invalidate any prior representations */
  19321. SyBlobRelease(&pObj->sBlob);
  19322. MemObjSetType(pObj, MEMOBJ_INT);
  19323. }
  19324. return SXRET_OK;
  19325. }
  19326. /*
  19327. * Convert a jx9_value to type real (Try to get an integer representation also).
  19328. * Invalidate any prior representations
  19329. */
  19330. JX9_PRIVATE sxi32 jx9MemObjToReal(jx9_value *pObj)
  19331. {
  19332. if((pObj->iFlags & MEMOBJ_REAL) == 0 ){
  19333. /* Preform the conversion */
  19334. pObj->x.rVal = MemObjRealValue(&(*pObj));
  19335. /* Invalidate any prior representations */
  19336. SyBlobRelease(&pObj->sBlob);
  19337. MemObjSetType(pObj, MEMOBJ_REAL);
  19338. }
  19339. return SXRET_OK;
  19340. }
  19341. /*
  19342. * Convert a jx9_value to type boolean.Invalidate any prior representations.
  19343. */
  19344. JX9_PRIVATE sxi32 jx9MemObjToBool(jx9_value *pObj)
  19345. {
  19346. if( (pObj->iFlags & MEMOBJ_BOOL) == 0 ){
  19347. /* Preform the conversion */
  19348. pObj->x.iVal = MemObjBooleanValue(&(*pObj));
  19349. /* Invalidate any prior representations */
  19350. SyBlobRelease(&pObj->sBlob);
  19351. MemObjSetType(pObj, MEMOBJ_BOOL);
  19352. }
  19353. return SXRET_OK;
  19354. }
  19355. /*
  19356. * Convert a jx9_value to type string.Prior representations are NOT invalidated.
  19357. */
  19358. JX9_PRIVATE sxi32 jx9MemObjToString(jx9_value *pObj)
  19359. {
  19360. sxi32 rc = SXRET_OK;
  19361. if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){
  19362. /* Perform the conversion */
  19363. SyBlobReset(&pObj->sBlob); /* Reset the internal buffer */
  19364. rc = MemObjStringValue(&pObj->sBlob, &(*pObj));
  19365. MemObjSetType(pObj, MEMOBJ_STRING);
  19366. }
  19367. return rc;
  19368. }
  19369. /*
  19370. * Nullify a jx9_value.In other words invalidate any prior
  19371. * representation.
  19372. */
  19373. JX9_PRIVATE sxi32 jx9MemObjToNull(jx9_value *pObj)
  19374. {
  19375. return jx9MemObjRelease(pObj);
  19376. }
  19377. /*
  19378. * Convert a jx9_value to type array.Invalidate any prior representations.
  19379. * According to the JX9 language reference manual.
  19380. * For any of the types: integer, float, string, boolean converting a value
  19381. * to an array results in an array with a single element with index zero
  19382. * and the value of the scalar which was converted.
  19383. */
  19384. JX9_PRIVATE sxi32 jx9MemObjToHashmap(jx9_value *pObj)
  19385. {
  19386. if( (pObj->iFlags & MEMOBJ_HASHMAP) == 0 ){
  19387. jx9_hashmap *pMap;
  19388. /* Allocate a new hashmap instance */
  19389. pMap = jx9NewHashmap(pObj->pVm, 0, 0);
  19390. if( pMap == 0 ){
  19391. return SXERR_MEM;
  19392. }
  19393. if( (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_RES)) == 0 ){
  19394. /*
  19395. * According to the JX9 language reference manual.
  19396. * For any of the types: integer, float, string, boolean converting a value
  19397. * to an array results in an array with a single element with index zero
  19398. * and the value of the scalar which was converted.
  19399. */
  19400. /* Insert a single element */
  19401. jx9HashmapInsert(pMap, 0/* Automatic index assign */, &(*pObj));
  19402. SyBlobRelease(&pObj->sBlob);
  19403. }
  19404. /* Invalidate any prior representation */
  19405. MemObjSetType(pObj, MEMOBJ_HASHMAP);
  19406. pObj->x.pOther = pMap;
  19407. }
  19408. return SXRET_OK;
  19409. }
  19410. /*
  19411. * Return a pointer to the appropriate convertion method associated
  19412. * with the given type.
  19413. * Note on type juggling.
  19414. * Accoding to the JX9 language reference manual
  19415. * JX9 does not require (or support) explicit type definition in variable
  19416. * declaration; a variable's type is determined by the context in which
  19417. * the variable is used. That is to say, if a string value is assigned
  19418. * to variable $var, $var becomes a string. If an integer value is then
  19419. * assigned to $var, it becomes an integer.
  19420. */
  19421. JX9_PRIVATE ProcMemObjCast jx9MemObjCastMethod(sxi32 iFlags)
  19422. {
  19423. if( iFlags & MEMOBJ_STRING ){
  19424. return jx9MemObjToString;
  19425. }else if( iFlags & MEMOBJ_INT ){
  19426. return jx9MemObjToInteger;
  19427. }else if( iFlags & MEMOBJ_REAL ){
  19428. return jx9MemObjToReal;
  19429. }else if( iFlags & MEMOBJ_BOOL ){
  19430. return jx9MemObjToBool;
  19431. }else if( iFlags & MEMOBJ_HASHMAP ){
  19432. return jx9MemObjToHashmap;
  19433. }
  19434. /* NULL cast */
  19435. return jx9MemObjToNull;
  19436. }
  19437. /*
  19438. * Check whether the jx9_value is numeric [i.e: int/float/bool] or looks
  19439. * like a numeric number [i.e: if the jx9_value is of type string.].
  19440. * Return TRUE if numeric.FALSE otherwise.
  19441. */
  19442. JX9_PRIVATE sxi32 jx9MemObjIsNumeric(jx9_value *pObj)
  19443. {
  19444. if( pObj->iFlags & ( MEMOBJ_BOOL|MEMOBJ_INT|MEMOBJ_REAL) ){
  19445. return TRUE;
  19446. }else if( pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES) ){
  19447. return FALSE;
  19448. }else if( pObj->iFlags & MEMOBJ_STRING ){
  19449. SyString sStr;
  19450. sxi32 rc;
  19451. SyStringInitFromBuf(&sStr, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
  19452. if( sStr.nByte <= 0 ){
  19453. /* Empty string */
  19454. return FALSE;
  19455. }
  19456. /* Check if the string representation looks like a numeric number */
  19457. rc = SyStrIsNumeric(sStr.zString, sStr.nByte, 0, 0);
  19458. return rc == SXRET_OK ? TRUE : FALSE;
  19459. }
  19460. /* NOT REACHED */
  19461. return FALSE;
  19462. }
  19463. /*
  19464. * Check whether the jx9_value is empty.Return TRUE if empty.
  19465. * FALSE otherwise.
  19466. * An jx9_value is considered empty if the following are true:
  19467. * NULL value.
  19468. * Boolean FALSE.
  19469. * Integer/Float with a 0 (zero) value.
  19470. * An empty string or a stream of 0 (zero) [i.e: "0", "00", "000", ...].
  19471. * An empty array.
  19472. * NOTE
  19473. * OBJECT VALUE MUST NOT BE MODIFIED.
  19474. */
  19475. JX9_PRIVATE sxi32 jx9MemObjIsEmpty(jx9_value *pObj)
  19476. {
  19477. if( pObj->iFlags & MEMOBJ_NULL ){
  19478. return TRUE;
  19479. }else if( pObj->iFlags & MEMOBJ_INT ){
  19480. return pObj->x.iVal == 0 ? TRUE : FALSE;
  19481. }else if( pObj->iFlags & MEMOBJ_REAL ){
  19482. return pObj->x.rVal == (jx9_real)0 ? TRUE : FALSE;
  19483. }else if( pObj->iFlags & MEMOBJ_BOOL ){
  19484. return !pObj->x.iVal;
  19485. }else if( pObj->iFlags & MEMOBJ_STRING ){
  19486. if( SyBlobLength(&pObj->sBlob) <= 0 ){
  19487. return TRUE;
  19488. }else{
  19489. const char *zIn, *zEnd;
  19490. zIn = (const char *)SyBlobData(&pObj->sBlob);
  19491. zEnd = &zIn[SyBlobLength(&pObj->sBlob)];
  19492. while( zIn < zEnd ){
  19493. if( zIn[0] != '0' ){
  19494. break;
  19495. }
  19496. zIn++;
  19497. }
  19498. return zIn >= zEnd ? TRUE : FALSE;
  19499. }
  19500. }else if( pObj->iFlags & MEMOBJ_HASHMAP ){
  19501. jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
  19502. return pMap->nEntry == 0 ? TRUE : FALSE;
  19503. }else if ( pObj->iFlags & (MEMOBJ_RES) ){
  19504. return FALSE;
  19505. }
  19506. /* Assume empty by default */
  19507. return TRUE;
  19508. }
  19509. /*
  19510. * Convert a jx9_value so that it has types MEMOBJ_REAL or MEMOBJ_INT
  19511. * or both.
  19512. * Invalidate any prior representations. Every effort is made to force
  19513. * the conversion, even if the input is a string that does not look
  19514. * completely like a number.Convert as much of the string as we can
  19515. * and ignore the rest.
  19516. */
  19517. JX9_PRIVATE sxi32 jx9MemObjToNumeric(jx9_value *pObj)
  19518. {
  19519. if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL) ){
  19520. if( pObj->iFlags & (MEMOBJ_BOOL|MEMOBJ_NULL) ){
  19521. if( pObj->iFlags & MEMOBJ_NULL ){
  19522. pObj->x.iVal = 0;
  19523. }
  19524. MemObjSetType(pObj, MEMOBJ_INT);
  19525. }
  19526. /* Already numeric */
  19527. return SXRET_OK;
  19528. }
  19529. if( pObj->iFlags & MEMOBJ_STRING ){
  19530. sxi32 rc = SXERR_INVALID;
  19531. sxu8 bReal = FALSE;
  19532. SyString sString;
  19533. SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
  19534. /* Check if the given string looks like a numeric number */
  19535. if( sString.nByte > 0 ){
  19536. rc = SyStrIsNumeric(sString.zString, sString.nByte, &bReal, 0);
  19537. }
  19538. if( bReal ){
  19539. jx9MemObjToReal(&(*pObj));
  19540. }else{
  19541. if( rc != SXRET_OK ){
  19542. /* The input does not look at all like a number, set the value to 0 */
  19543. pObj->x.iVal = 0;
  19544. }else{
  19545. /* Convert as much as we can */
  19546. pObj->x.iVal = MemObjStringToInt(&(*pObj));
  19547. }
  19548. MemObjSetType(pObj, MEMOBJ_INT);
  19549. SyBlobRelease(&pObj->sBlob);
  19550. }
  19551. }else if(pObj->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES)){
  19552. jx9MemObjToInteger(pObj);
  19553. }else{
  19554. /* Perform a blind cast */
  19555. jx9MemObjToReal(&(*pObj));
  19556. }
  19557. return SXRET_OK;
  19558. }
  19559. /*
  19560. * Try a get an integer representation of the given jx9_value.
  19561. * If the jx9_value is not of type real, this function is a no-op.
  19562. */
  19563. JX9_PRIVATE sxi32 jx9MemObjTryInteger(jx9_value *pObj)
  19564. {
  19565. if( pObj->iFlags & MEMOBJ_REAL ){
  19566. /* Work only with reals */
  19567. MemObjTryIntger(&(*pObj));
  19568. }
  19569. return SXRET_OK;
  19570. }
  19571. /*
  19572. * Initialize a jx9_value to the null type.
  19573. */
  19574. JX9_PRIVATE sxi32 jx9MemObjInit(jx9_vm *pVm, jx9_value *pObj)
  19575. {
  19576. /* Zero the structure */
  19577. SyZero(pObj, sizeof(jx9_value));
  19578. /* Initialize fields */
  19579. pObj->pVm = pVm;
  19580. SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
  19581. /* Set the NULL type */
  19582. pObj->iFlags = MEMOBJ_NULL;
  19583. return SXRET_OK;
  19584. }
  19585. /*
  19586. * Initialize a jx9_value to the integer type.
  19587. */
  19588. JX9_PRIVATE sxi32 jx9MemObjInitFromInt(jx9_vm *pVm, jx9_value *pObj, sxi64 iVal)
  19589. {
  19590. /* Zero the structure */
  19591. SyZero(pObj, sizeof(jx9_value));
  19592. /* Initialize fields */
  19593. pObj->pVm = pVm;
  19594. SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
  19595. /* Set the desired type */
  19596. pObj->x.iVal = iVal;
  19597. pObj->iFlags = MEMOBJ_INT;
  19598. return SXRET_OK;
  19599. }
  19600. /*
  19601. * Initialize a jx9_value to the boolean type.
  19602. */
  19603. JX9_PRIVATE sxi32 jx9MemObjInitFromBool(jx9_vm *pVm, jx9_value *pObj, sxi32 iVal)
  19604. {
  19605. /* Zero the structure */
  19606. SyZero(pObj, sizeof(jx9_value));
  19607. /* Initialize fields */
  19608. pObj->pVm = pVm;
  19609. SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
  19610. /* Set the desired type */
  19611. pObj->x.iVal = iVal ? 1 : 0;
  19612. pObj->iFlags = MEMOBJ_BOOL;
  19613. return SXRET_OK;
  19614. }
  19615. #if 0
  19616. /*
  19617. * Initialize a jx9_value to the real type.
  19618. */
  19619. JX9_PRIVATE sxi32 jx9MemObjInitFromReal(jx9_vm *pVm, jx9_value *pObj, jx9_real rVal)
  19620. {
  19621. /* Zero the structure */
  19622. SyZero(pObj, sizeof(jx9_value));
  19623. /* Initialize fields */
  19624. pObj->pVm = pVm;
  19625. SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
  19626. /* Set the desired type */
  19627. pObj->x.rVal = rVal;
  19628. pObj->iFlags = MEMOBJ_REAL;
  19629. return SXRET_OK;
  19630. }
  19631. #endif
  19632. /*
  19633. * Initialize a jx9_value to the array type.
  19634. */
  19635. JX9_PRIVATE sxi32 jx9MemObjInitFromArray(jx9_vm *pVm, jx9_value *pObj, jx9_hashmap *pArray)
  19636. {
  19637. /* Zero the structure */
  19638. SyZero(pObj, sizeof(jx9_value));
  19639. /* Initialize fields */
  19640. pObj->pVm = pVm;
  19641. SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
  19642. /* Set the desired type */
  19643. pObj->iFlags = MEMOBJ_HASHMAP;
  19644. pObj->x.pOther = pArray;
  19645. return SXRET_OK;
  19646. }
  19647. /*
  19648. * Initialize a jx9_value to the string type.
  19649. */
  19650. JX9_PRIVATE sxi32 jx9MemObjInitFromString(jx9_vm *pVm, jx9_value *pObj, const SyString *pVal)
  19651. {
  19652. /* Zero the structure */
  19653. SyZero(pObj, sizeof(jx9_value));
  19654. /* Initialize fields */
  19655. pObj->pVm = pVm;
  19656. SyBlobInit(&pObj->sBlob, &pVm->sAllocator);
  19657. if( pVal ){
  19658. /* Append contents */
  19659. SyBlobAppend(&pObj->sBlob, (const void *)pVal->zString, pVal->nByte);
  19660. }
  19661. /* Set the desired type */
  19662. pObj->iFlags = MEMOBJ_STRING;
  19663. return SXRET_OK;
  19664. }
  19665. /*
  19666. * Append some contents to the internal buffer of a given jx9_value.
  19667. * If the given jx9_value is not of type string, this function
  19668. * invalidate any prior representation and set the string type.
  19669. * Then a simple append operation is performed.
  19670. */
  19671. JX9_PRIVATE sxi32 jx9MemObjStringAppend(jx9_value *pObj, const char *zData, sxu32 nLen)
  19672. {
  19673. sxi32 rc;
  19674. if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){
  19675. /* Invalidate any prior representation */
  19676. jx9MemObjRelease(pObj);
  19677. MemObjSetType(pObj, MEMOBJ_STRING);
  19678. }
  19679. /* Append contents */
  19680. rc = SyBlobAppend(&pObj->sBlob, zData, nLen);
  19681. return rc;
  19682. }
  19683. #if 0
  19684. /*
  19685. * Format and append some contents to the internal buffer of a given jx9_value.
  19686. * If the given jx9_value is not of type string, this function invalidate
  19687. * any prior representation and set the string type.
  19688. * Then a simple format and append operation is performed.
  19689. */
  19690. JX9_PRIVATE sxi32 jx9MemObjStringFormat(jx9_value *pObj, const char *zFormat, va_list ap)
  19691. {
  19692. sxi32 rc;
  19693. if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){
  19694. /* Invalidate any prior representation */
  19695. jx9MemObjRelease(pObj);
  19696. MemObjSetType(pObj, MEMOBJ_STRING);
  19697. }
  19698. /* Format and append contents */
  19699. rc = SyBlobFormatAp(&pObj->sBlob, zFormat, ap);
  19700. return rc;
  19701. }
  19702. #endif
  19703. /*
  19704. * Duplicate the contents of a jx9_value.
  19705. */
  19706. JX9_PRIVATE sxi32 jx9MemObjStore(jx9_value *pSrc, jx9_value *pDest)
  19707. {
  19708. jx9_hashmap *pMap = 0;
  19709. sxi32 rc;
  19710. if( pSrc->iFlags & MEMOBJ_HASHMAP ){
  19711. /* Increment reference count */
  19712. ((jx9_hashmap *)pSrc->x.pOther)->iRef++;
  19713. }
  19714. if( pDest->iFlags & MEMOBJ_HASHMAP ){
  19715. pMap = (jx9_hashmap *)pDest->x.pOther;
  19716. }
  19717. SyMemcpy((const void *)&(*pSrc), &(*pDest), sizeof(jx9_value)-(sizeof(jx9_vm *)+sizeof(SyBlob)+sizeof(sxu32)));
  19718. rc = SXRET_OK;
  19719. if( SyBlobLength(&pSrc->sBlob) > 0 ){
  19720. SyBlobReset(&pDest->sBlob);
  19721. rc = SyBlobDup(&pSrc->sBlob, &pDest->sBlob);
  19722. }else{
  19723. if( SyBlobLength(&pDest->sBlob) > 0 ){
  19724. SyBlobRelease(&pDest->sBlob);
  19725. }
  19726. }
  19727. if( pMap ){
  19728. jx9HashmapUnref(pMap);
  19729. }
  19730. return rc;
  19731. }
  19732. /*
  19733. * Duplicate the contents of a jx9_value but do not copy internal
  19734. * buffer contents, simply point to it.
  19735. */
  19736. JX9_PRIVATE sxi32 jx9MemObjLoad(jx9_value *pSrc, jx9_value *pDest)
  19737. {
  19738. SyMemcpy((const void *)&(*pSrc), &(*pDest),
  19739. sizeof(jx9_value)-(sizeof(jx9_vm *)+sizeof(SyBlob)+sizeof(sxu32)));
  19740. if( pSrc->iFlags & MEMOBJ_HASHMAP ){
  19741. /* Increment reference count */
  19742. ((jx9_hashmap *)pSrc->x.pOther)->iRef++;
  19743. }
  19744. if( SyBlobLength(&pDest->sBlob) > 0 ){
  19745. SyBlobRelease(&pDest->sBlob);
  19746. }
  19747. if( SyBlobLength(&pSrc->sBlob) > 0 ){
  19748. SyBlobReadOnly(&pDest->sBlob, SyBlobData(&pSrc->sBlob), SyBlobLength(&pSrc->sBlob));
  19749. }
  19750. return SXRET_OK;
  19751. }
  19752. /*
  19753. * Invalidate any prior representation of a given jx9_value.
  19754. */
  19755. JX9_PRIVATE sxi32 jx9MemObjRelease(jx9_value *pObj)
  19756. {
  19757. if( (pObj->iFlags & MEMOBJ_NULL) == 0 ){
  19758. if( pObj->iFlags & MEMOBJ_HASHMAP ){
  19759. jx9HashmapUnref((jx9_hashmap *)pObj->x.pOther);
  19760. }
  19761. /* Release the internal buffer */
  19762. SyBlobRelease(&pObj->sBlob);
  19763. /* Invalidate any prior representation */
  19764. pObj->iFlags = MEMOBJ_NULL;
  19765. }
  19766. return SXRET_OK;
  19767. }
  19768. /*
  19769. * Compare two jx9_values.
  19770. * Return 0 if the values are equals, > 0 if pObj1 is greater than pObj2
  19771. * or < 0 if pObj2 is greater than pObj1.
  19772. * Type comparison table taken from the JX9 language reference manual.
  19773. * Comparisons of $x with JX9 functions Expression
  19774. * gettype() empty() is_null() isset() boolean : if($x)
  19775. * $x = ""; string TRUE FALSE TRUE FALSE
  19776. * $x = null NULL TRUE TRUE FALSE FALSE
  19777. * var $x; NULL TRUE TRUE FALSE FALSE
  19778. * $x is undefined NULL TRUE TRUE FALSE FALSE
  19779. * $x = array(); array TRUE FALSE TRUE FALSE
  19780. * $x = false; boolean TRUE FALSE TRUE FALSE
  19781. * $x = true; boolean FALSE FALSE TRUE TRUE
  19782. * $x = 1; integer FALSE FALSE TRUE TRUE
  19783. * $x = 42; integer FALSE FALSE TRUE TRUE
  19784. * $x = 0; integer TRUE FALSE TRUE FALSE
  19785. * $x = -1; integer FALSE FALSE TRUE TRUE
  19786. * $x = "1"; string FALSE FALSE TRUE TRUE
  19787. * $x = "0"; string TRUE FALSE TRUE FALSE
  19788. * $x = "-1"; string FALSE FALSE TRUE TRUE
  19789. * $x = "jx9"; string FALSE FALSE TRUE TRUE
  19790. * $x = "true"; string FALSE FALSE TRUE TRUE
  19791. * $x = "false"; string FALSE FALSE TRUE TRUE
  19792. * Loose comparisons with ==
  19793. * TRUE FALSE 1 0 -1 "1" "0" "-1" NULL array() "jx9" ""
  19794. * TRUE TRUE FALSE TRUE FALSE TRUE TRUE FALSE TRUE FALSE FALSE TRUE FALSE
  19795. * FALSE FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE TRUE TRUE FALSE TRUE
  19796. * 1 TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
  19797. * 0 FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE TRUE FALSE TRUE TRUE
  19798. * -1 TRUE FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
  19799. * "1" TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
  19800. * "0" FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
  19801. * "-1" TRUE FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
  19802. * NULL FALSE TRUE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE FALSE TRUE
  19803. * array() FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE
  19804. * "jx9" TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
  19805. * "" FALSE TRUE FALSE TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE TRUE
  19806. * Strict comparisons with ===
  19807. * TRUE FALSE 1 0 -1 "1" "0" "-1" NULL array() "jx9" ""
  19808. * TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
  19809. * FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
  19810. * 1 FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
  19811. * 0 FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
  19812. * -1 FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
  19813. * "1" FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
  19814. * "0" FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
  19815. * "-1" FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
  19816. * NULL FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE
  19817. * array() FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE
  19818. * "jx9" FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
  19819. * "" FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE
  19820. */
  19821. JX9_PRIVATE sxi32 jx9MemObjCmp(jx9_value *pObj1, jx9_value *pObj2, int bStrict, int iNest)
  19822. {
  19823. sxi32 iComb;
  19824. sxi32 rc;
  19825. if( bStrict ){
  19826. sxi32 iF1, iF2;
  19827. /* Strict comparisons with === */
  19828. iF1 = pObj1->iFlags;
  19829. iF2 = pObj2->iFlags;
  19830. if( iF1 != iF2 ){
  19831. /* Not of the same type */
  19832. return 1;
  19833. }
  19834. }
  19835. /* Combine flag together */
  19836. iComb = pObj1->iFlags|pObj2->iFlags;
  19837. if( iComb & (MEMOBJ_NULL|MEMOBJ_RES|MEMOBJ_BOOL) ){
  19838. /* Convert to boolean: Keep in mind FALSE < TRUE */
  19839. if( (pObj1->iFlags & MEMOBJ_BOOL) == 0 ){
  19840. jx9MemObjToBool(pObj1);
  19841. }
  19842. if( (pObj2->iFlags & MEMOBJ_BOOL) == 0 ){
  19843. jx9MemObjToBool(pObj2);
  19844. }
  19845. return (sxi32)((pObj1->x.iVal != 0) - (pObj2->x.iVal != 0));
  19846. }else if ( iComb & MEMOBJ_HASHMAP ){
  19847. /* Hashmap aka 'array' comparison */
  19848. if( (pObj1->iFlags & MEMOBJ_HASHMAP) == 0 ){
  19849. /* Array is always greater */
  19850. return -1;
  19851. }
  19852. if( (pObj2->iFlags & MEMOBJ_HASHMAP) == 0 ){
  19853. /* Array is always greater */
  19854. return 1;
  19855. }
  19856. /* Perform the comparison */
  19857. rc = jx9HashmapCmp((jx9_hashmap *)pObj1->x.pOther, (jx9_hashmap *)pObj2->x.pOther, bStrict);
  19858. return rc;
  19859. }else if ( iComb & MEMOBJ_STRING ){
  19860. SyString s1, s2;
  19861. /* Perform a strict string comparison.*/
  19862. if( (pObj1->iFlags&MEMOBJ_STRING) == 0 ){
  19863. jx9MemObjToString(pObj1);
  19864. }
  19865. if( (pObj2->iFlags&MEMOBJ_STRING) == 0 ){
  19866. jx9MemObjToString(pObj2);
  19867. }
  19868. SyStringInitFromBuf(&s1, SyBlobData(&pObj1->sBlob), SyBlobLength(&pObj1->sBlob));
  19869. SyStringInitFromBuf(&s2, SyBlobData(&pObj2->sBlob), SyBlobLength(&pObj2->sBlob));
  19870. /*
  19871. * Strings are compared using memcmp(). If one value is an exact prefix of the
  19872. * other, then the shorter value is less than the longer value.
  19873. */
  19874. rc = SyMemcmp((const void *)s1.zString, (const void *)s2.zString, SXMIN(s1.nByte, s2.nByte));
  19875. if( rc == 0 ){
  19876. if( s1.nByte != s2.nByte ){
  19877. rc = s1.nByte < s2.nByte ? -1 : 1;
  19878. }
  19879. }
  19880. return rc;
  19881. }else if( iComb & (MEMOBJ_INT|MEMOBJ_REAL) ){
  19882. /* Perform a numeric comparison if one of the operand is numeric(integer or real) */
  19883. if( (pObj1->iFlags & (MEMOBJ_INT|MEMOBJ_REAL)) == 0 ){
  19884. jx9MemObjToNumeric(pObj1);
  19885. }
  19886. if( (pObj2->iFlags & (MEMOBJ_INT|MEMOBJ_REAL)) == 0 ){
  19887. jx9MemObjToNumeric(pObj2);
  19888. }
  19889. if( (pObj1->iFlags & pObj2->iFlags & MEMOBJ_INT) == 0) {
  19890. jx9_real r1, r2;
  19891. /* Compare as reals */
  19892. if( (pObj1->iFlags & MEMOBJ_REAL) == 0 ){
  19893. jx9MemObjToReal(pObj1);
  19894. }
  19895. r1 = pObj1->x.rVal;
  19896. if( (pObj2->iFlags & MEMOBJ_REAL) == 0 ){
  19897. jx9MemObjToReal(pObj2);
  19898. }
  19899. r2 = pObj2->x.rVal;
  19900. if( r1 > r2 ){
  19901. return 1;
  19902. }else if( r1 < r2 ){
  19903. return -1;
  19904. }
  19905. return 0;
  19906. }else{
  19907. /* Integer comparison */
  19908. if( pObj1->x.iVal > pObj2->x.iVal ){
  19909. return 1;
  19910. }else if( pObj1->x.iVal < pObj2->x.iVal ){
  19911. return -1;
  19912. }
  19913. return 0;
  19914. }
  19915. }
  19916. /* NOT REACHED */
  19917. SXUNUSED(iNest);
  19918. return 0;
  19919. }
  19920. /*
  19921. * Perform an addition operation of two jx9_values.
  19922. * The reason this function is implemented here rather than 'vm.c'
  19923. * is that the '+' operator is overloaded.
  19924. * That is, the '+' operator is used for arithmetic operation and also
  19925. * used for operation on arrays [i.e: union]. When used with an array
  19926. * The + operator returns the right-hand array appended to the left-hand array.
  19927. * For keys that exist in both arrays, the elements from the left-hand array
  19928. * will be used, and the matching elements from the right-hand array will
  19929. * be ignored.
  19930. * This function take care of handling all the scenarios.
  19931. */
  19932. JX9_PRIVATE sxi32 jx9MemObjAdd(jx9_value *pObj1, jx9_value *pObj2, int bAddStore)
  19933. {
  19934. if( ((pObj1->iFlags|pObj2->iFlags) & MEMOBJ_HASHMAP) == 0 ){
  19935. /* Arithemtic operation */
  19936. jx9MemObjToNumeric(pObj1);
  19937. jx9MemObjToNumeric(pObj2);
  19938. if( (pObj1->iFlags|pObj2->iFlags) & MEMOBJ_REAL ){
  19939. /* Floating point arithmetic */
  19940. jx9_real a, b;
  19941. if( (pObj1->iFlags & MEMOBJ_REAL) == 0 ){
  19942. jx9MemObjToReal(pObj1);
  19943. }
  19944. if( (pObj2->iFlags & MEMOBJ_REAL) == 0 ){
  19945. jx9MemObjToReal(pObj2);
  19946. }
  19947. a = pObj1->x.rVal;
  19948. b = pObj2->x.rVal;
  19949. pObj1->x.rVal = a+b;
  19950. MemObjSetType(pObj1, MEMOBJ_REAL);
  19951. /* Try to get an integer representation also */
  19952. MemObjTryIntger(&(*pObj1));
  19953. }else{
  19954. /* Integer arithmetic */
  19955. sxi64 a, b;
  19956. a = pObj1->x.iVal;
  19957. b = pObj2->x.iVal;
  19958. pObj1->x.iVal = a+b;
  19959. MemObjSetType(pObj1, MEMOBJ_INT);
  19960. }
  19961. }else{
  19962. if( (pObj1->iFlags|pObj2->iFlags) & MEMOBJ_HASHMAP ){
  19963. jx9_hashmap *pMap;
  19964. sxi32 rc;
  19965. if( bAddStore ){
  19966. /* Do not duplicate the hashmap, use the left one since its an add&store operation.
  19967. */
  19968. if( (pObj1->iFlags & MEMOBJ_HASHMAP) == 0 ){
  19969. /* Force a hashmap cast */
  19970. rc = jx9MemObjToHashmap(pObj1);
  19971. if( rc != SXRET_OK ){
  19972. jx9VmThrowError(pObj1->pVm, 0, JX9_CTX_ERR, "JX9 is running out of memory while creating array");
  19973. return rc;
  19974. }
  19975. }
  19976. /* Point to the structure that describe the hashmap */
  19977. pMap = (jx9_hashmap *)pObj1->x.pOther;
  19978. }else{
  19979. /* Create a new hashmap */
  19980. pMap = jx9NewHashmap(pObj1->pVm, 0, 0);
  19981. if( pMap == 0){
  19982. jx9VmThrowError(pObj1->pVm, 0, JX9_CTX_ERR, "JX9 is running out of memory while creating array");
  19983. return SXERR_MEM;
  19984. }
  19985. }
  19986. if( !bAddStore ){
  19987. if(pObj1->iFlags & MEMOBJ_HASHMAP ){
  19988. /* Perform a hashmap duplication */
  19989. jx9HashmapDup((jx9_hashmap *)pObj1->x.pOther, pMap);
  19990. }else{
  19991. if((pObj1->iFlags & MEMOBJ_NULL) == 0 ){
  19992. /* Simple insertion */
  19993. jx9HashmapInsert(pMap, 0, pObj1);
  19994. }
  19995. }
  19996. }
  19997. /* Perform the union */
  19998. if(pObj2->iFlags & MEMOBJ_HASHMAP ){
  19999. jx9HashmapUnion(pMap, (jx9_hashmap *)pObj2->x.pOther);
  20000. }else{
  20001. if((pObj2->iFlags & MEMOBJ_NULL) == 0 ){
  20002. /* Simple insertion */
  20003. jx9HashmapInsert(pMap, 0, pObj2);
  20004. }
  20005. }
  20006. /* Reflect the change */
  20007. if( pObj1->iFlags & MEMOBJ_STRING ){
  20008. SyBlobRelease(&pObj1->sBlob);
  20009. }
  20010. pObj1->x.pOther = pMap;
  20011. MemObjSetType(pObj1, MEMOBJ_HASHMAP);
  20012. }
  20013. }
  20014. return SXRET_OK;
  20015. }
  20016. /*
  20017. * Return a printable representation of the type of a given
  20018. * jx9_value.
  20019. */
  20020. JX9_PRIVATE const char * jx9MemObjTypeDump(jx9_value *pVal)
  20021. {
  20022. const char *zType = "";
  20023. if( pVal->iFlags & MEMOBJ_NULL ){
  20024. zType = "null";
  20025. }else if( pVal->iFlags & MEMOBJ_INT ){
  20026. zType = "int";
  20027. }else if( pVal->iFlags & MEMOBJ_REAL ){
  20028. zType = "float";
  20029. }else if( pVal->iFlags & MEMOBJ_STRING ){
  20030. zType = "string";
  20031. }else if( pVal->iFlags & MEMOBJ_BOOL ){
  20032. zType = "bool";
  20033. }else if( pVal->iFlags & MEMOBJ_HASHMAP ){
  20034. jx9_hashmap *pMap = (jx9_hashmap *)pVal->x.pOther;
  20035. if( pMap->iFlags & HASHMAP_JSON_OBJECT ){
  20036. zType = "JSON Object";
  20037. }else{
  20038. zType = "JSON Array";
  20039. }
  20040. }else if( pVal->iFlags & MEMOBJ_RES ){
  20041. zType = "resource";
  20042. }
  20043. return zType;
  20044. }
  20045. /*
  20046. * Dump a jx9_value [i.e: get a printable representation of it's type and contents.].
  20047. * Store the dump in the given blob.
  20048. */
  20049. JX9_PRIVATE sxi32 jx9MemObjDump(
  20050. SyBlob *pOut, /* Store the dump here */
  20051. jx9_value *pObj /* Dump this */
  20052. )
  20053. {
  20054. sxi32 rc = SXRET_OK;
  20055. const char *zType;
  20056. /* Get value type first */
  20057. zType = jx9MemObjTypeDump(pObj);
  20058. SyBlobAppend(&(*pOut), zType, SyStrlen(zType));
  20059. if((pObj->iFlags & MEMOBJ_NULL) == 0 ){
  20060. SyBlobAppend(&(*pOut), "(", sizeof(char));
  20061. if( pObj->iFlags & MEMOBJ_HASHMAP ){
  20062. jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther;
  20063. SyBlobFormat(pOut,"%u ",pMap->nEntry);
  20064. /* Dump hashmap entries */
  20065. rc = jx9JsonSerialize(pObj,pOut);
  20066. }else{
  20067. SyBlob *pContents = &pObj->sBlob;
  20068. /* Get a printable representation of the contents */
  20069. if((pObj->iFlags & MEMOBJ_STRING) == 0 ){
  20070. MemObjStringValue(&(*pOut), &(*pObj));
  20071. }else{
  20072. /* Append length first */
  20073. SyBlobFormat(&(*pOut), "%u '", SyBlobLength(&pObj->sBlob));
  20074. if( SyBlobLength(pContents) > 0 ){
  20075. SyBlobAppend(&(*pOut), SyBlobData(pContents), SyBlobLength(pContents));
  20076. }
  20077. SyBlobAppend(&(*pOut), "'", sizeof(char));
  20078. }
  20079. }
  20080. SyBlobAppend(&(*pOut), ")", sizeof(char));
  20081. }
  20082. #ifdef __WINNT__
  20083. SyBlobAppend(&(*pOut), "\r\n", sizeof("\r\n")-1);
  20084. #else
  20085. SyBlobAppend(&(*pOut), "\n", sizeof(char));
  20086. #endif
  20087. return rc;
  20088. }
  20089. /*
  20090. * ----------------------------------------------------------
  20091. * File: lib.c
  20092. * MD5: 64a1a197d42ecd449d2e99b3b183b75a
  20093. * ----------------------------------------------------------
  20094. */
  20095. /*
  20096. * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
  20097. * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
  20098. * Version 1.7.2
  20099. * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  20100. * please contact Symisc Systems via:
  20101. * legal@symisc.net
  20102. * licensing@symisc.net
  20103. * contact@symisc.net
  20104. * or visit:
  20105. * http://jx9.symisc.net/
  20106. */
  20107. /* $SymiscID: lib.c v5.1 Win7 2012-08-08 04:19 stable <chm@symisc.net> $ */
  20108. /*
  20109. * Symisc Run-Time API: A modern thread safe replacement of the standard libc
  20110. * Copyright (C) Symisc Systems 2007-2012, http://www.symisc.net/
  20111. *
  20112. * The Symisc Run-Time API is an independent project developed by symisc systems
  20113. * internally as a secure replacement of the standard libc.
  20114. * The library is re-entrant, thread-safe and platform independent.
  20115. */
  20116. #ifndef JX9_AMALGAMATION
  20117. #include "jx9Int.h"
  20118. #endif
  20119. #if defined(__WINNT__)
  20120. #include <Windows.h>
  20121. #else
  20122. #include <stdlib.h>
  20123. #endif
  20124. #if defined(JX9_ENABLE_THREADS)
  20125. /* SyRunTimeApi: sxmutex.c */
  20126. #if defined(__WINNT__)
  20127. struct SyMutex
  20128. {
  20129. CRITICAL_SECTION sMutex;
  20130. sxu32 nType; /* Mutex type, one of SXMUTEX_TYPE_* */
  20131. };
  20132. /* Preallocated static mutex */
  20133. static SyMutex aStaticMutexes[] = {
  20134. {{0}, SXMUTEX_TYPE_STATIC_1},
  20135. {{0}, SXMUTEX_TYPE_STATIC_2},
  20136. {{0}, SXMUTEX_TYPE_STATIC_3},
  20137. {{0}, SXMUTEX_TYPE_STATIC_4},
  20138. {{0}, SXMUTEX_TYPE_STATIC_5},
  20139. {{0}, SXMUTEX_TYPE_STATIC_6}
  20140. };
  20141. static BOOL winMutexInit = FALSE;
  20142. static LONG winMutexLock = 0;
  20143. static sxi32 WinMutexGlobaInit(void)
  20144. {
  20145. LONG rc;
  20146. rc = InterlockedCompareExchange(&winMutexLock, 1, 0);
  20147. if ( rc == 0 ){
  20148. sxu32 n;
  20149. for( n = 0 ; n < SX_ARRAYSIZE(aStaticMutexes) ; ++n ){
  20150. InitializeCriticalSection(&aStaticMutexes[n].sMutex);
  20151. }
  20152. winMutexInit = TRUE;
  20153. }else{
  20154. /* Someone else is doing this for us */
  20155. while( winMutexInit == FALSE ){
  20156. Sleep(1);
  20157. }
  20158. }
  20159. return SXRET_OK;
  20160. }
  20161. static void WinMutexGlobalRelease(void)
  20162. {
  20163. LONG rc;
  20164. rc = InterlockedCompareExchange(&winMutexLock, 0, 1);
  20165. if( rc == 1 ){
  20166. /* The first to decrement to zero does the actual global release */
  20167. if( winMutexInit == TRUE ){
  20168. sxu32 n;
  20169. for( n = 0 ; n < SX_ARRAYSIZE(aStaticMutexes) ; ++n ){
  20170. DeleteCriticalSection(&aStaticMutexes[n].sMutex);
  20171. }
  20172. winMutexInit = FALSE;
  20173. }
  20174. }
  20175. }
  20176. static SyMutex * WinMutexNew(int nType)
  20177. {
  20178. SyMutex *pMutex = 0;
  20179. if( nType == SXMUTEX_TYPE_FAST || nType == SXMUTEX_TYPE_RECURSIVE ){
  20180. /* Allocate a new mutex */
  20181. pMutex = (SyMutex *)HeapAlloc(GetProcessHeap(), 0, sizeof(SyMutex));
  20182. if( pMutex == 0 ){
  20183. return 0;
  20184. }
  20185. InitializeCriticalSection(&pMutex->sMutex);
  20186. }else{
  20187. /* Use a pre-allocated static mutex */
  20188. if( nType > SXMUTEX_TYPE_STATIC_6 ){
  20189. nType = SXMUTEX_TYPE_STATIC_6;
  20190. }
  20191. pMutex = &aStaticMutexes[nType - 3];
  20192. }
  20193. pMutex->nType = nType;
  20194. return pMutex;
  20195. }
  20196. static void WinMutexRelease(SyMutex *pMutex)
  20197. {
  20198. if( pMutex->nType == SXMUTEX_TYPE_FAST || pMutex->nType == SXMUTEX_TYPE_RECURSIVE ){
  20199. DeleteCriticalSection(&pMutex->sMutex);
  20200. HeapFree(GetProcessHeap(), 0, pMutex);
  20201. }
  20202. }
  20203. static void WinMutexEnter(SyMutex *pMutex)
  20204. {
  20205. EnterCriticalSection(&pMutex->sMutex);
  20206. }
  20207. static sxi32 WinMutexTryEnter(SyMutex *pMutex)
  20208. {
  20209. #ifdef _WIN32_WINNT
  20210. BOOL rc;
  20211. /* Only WindowsNT platforms */
  20212. rc = TryEnterCriticalSection(&pMutex->sMutex);
  20213. if( rc ){
  20214. return SXRET_OK;
  20215. }else{
  20216. return SXERR_BUSY;
  20217. }
  20218. #else
  20219. return SXERR_NOTIMPLEMENTED;
  20220. #endif
  20221. }
  20222. static void WinMutexLeave(SyMutex *pMutex)
  20223. {
  20224. LeaveCriticalSection(&pMutex->sMutex);
  20225. }
  20226. /* Export Windows mutex interfaces */
  20227. static const SyMutexMethods sWinMutexMethods = {
  20228. WinMutexGlobaInit, /* xGlobalInit() */
  20229. WinMutexGlobalRelease, /* xGlobalRelease() */
  20230. WinMutexNew, /* xNew() */
  20231. WinMutexRelease, /* xRelease() */
  20232. WinMutexEnter, /* xEnter() */
  20233. WinMutexTryEnter, /* xTryEnter() */
  20234. WinMutexLeave /* xLeave() */
  20235. };
  20236. JX9_PRIVATE const SyMutexMethods * SyMutexExportMethods(void)
  20237. {
  20238. return &sWinMutexMethods;
  20239. }
  20240. #elif defined(__UNIXES__)
  20241. #include <pthread.h>
  20242. struct SyMutex
  20243. {
  20244. pthread_mutex_t sMutex;
  20245. sxu32 nType;
  20246. };
  20247. static SyMutex * UnixMutexNew(int nType)
  20248. {
  20249. static SyMutex aStaticMutexes[] = {
  20250. {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_1},
  20251. {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_2},
  20252. {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_3},
  20253. {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_4},
  20254. {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_5},
  20255. {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_6}
  20256. };
  20257. SyMutex *pMutex;
  20258. if( nType == SXMUTEX_TYPE_FAST || nType == SXMUTEX_TYPE_RECURSIVE ){
  20259. pthread_mutexattr_t sRecursiveAttr;
  20260. /* Allocate a new mutex */
  20261. pMutex = (SyMutex *)malloc(sizeof(SyMutex));
  20262. if( pMutex == 0 ){
  20263. return 0;
  20264. }
  20265. if( nType == SXMUTEX_TYPE_RECURSIVE ){
  20266. pthread_mutexattr_init(&sRecursiveAttr);
  20267. pthread_mutexattr_settype(&sRecursiveAttr, PTHREAD_MUTEX_RECURSIVE);
  20268. }
  20269. pthread_mutex_init(&pMutex->sMutex, nType == SXMUTEX_TYPE_RECURSIVE ? &sRecursiveAttr : 0 );
  20270. if( nType == SXMUTEX_TYPE_RECURSIVE ){
  20271. pthread_mutexattr_destroy(&sRecursiveAttr);
  20272. }
  20273. }else{
  20274. /* Use a pre-allocated static mutex */
  20275. if( nType > SXMUTEX_TYPE_STATIC_6 ){
  20276. nType = SXMUTEX_TYPE_STATIC_6;
  20277. }
  20278. pMutex = &aStaticMutexes[nType - 3];
  20279. }
  20280. pMutex->nType = nType;
  20281. return pMutex;
  20282. }
  20283. static void UnixMutexRelease(SyMutex *pMutex)
  20284. {
  20285. if( pMutex->nType == SXMUTEX_TYPE_FAST || pMutex->nType == SXMUTEX_TYPE_RECURSIVE ){
  20286. pthread_mutex_destroy(&pMutex->sMutex);
  20287. free(pMutex);
  20288. }
  20289. }
  20290. static void UnixMutexEnter(SyMutex *pMutex)
  20291. {
  20292. pthread_mutex_lock(&pMutex->sMutex);
  20293. }
  20294. static void UnixMutexLeave(SyMutex *pMutex)
  20295. {
  20296. pthread_mutex_unlock(&pMutex->sMutex);
  20297. }
  20298. /* Export pthread mutex interfaces */
  20299. static const SyMutexMethods sPthreadMutexMethods = {
  20300. 0, /* xGlobalInit() */
  20301. 0, /* xGlobalRelease() */
  20302. UnixMutexNew, /* xNew() */
  20303. UnixMutexRelease, /* xRelease() */
  20304. UnixMutexEnter, /* xEnter() */
  20305. 0, /* xTryEnter() */
  20306. UnixMutexLeave /* xLeave() */
  20307. };
  20308. JX9_PRIVATE const SyMutexMethods * SyMutexExportMethods(void)
  20309. {
  20310. return &sPthreadMutexMethods;
  20311. }
  20312. #else
  20313. /* Host application must register their own mutex subsystem if the target
  20314. * platform is not an UNIX-like or windows systems.
  20315. */
  20316. struct SyMutex
  20317. {
  20318. sxu32 nType;
  20319. };
  20320. static SyMutex * DummyMutexNew(int nType)
  20321. {
  20322. static SyMutex sMutex;
  20323. SXUNUSED(nType);
  20324. return &sMutex;
  20325. }
  20326. static void DummyMutexRelease(SyMutex *pMutex)
  20327. {
  20328. SXUNUSED(pMutex);
  20329. }
  20330. static void DummyMutexEnter(SyMutex *pMutex)
  20331. {
  20332. SXUNUSED(pMutex);
  20333. }
  20334. static void DummyMutexLeave(SyMutex *pMutex)
  20335. {
  20336. SXUNUSED(pMutex);
  20337. }
  20338. /* Export the dummy mutex interfaces */
  20339. static const SyMutexMethods sDummyMutexMethods = {
  20340. 0, /* xGlobalInit() */
  20341. 0, /* xGlobalRelease() */
  20342. DummyMutexNew, /* xNew() */
  20343. DummyMutexRelease, /* xRelease() */
  20344. DummyMutexEnter, /* xEnter() */
  20345. 0, /* xTryEnter() */
  20346. DummyMutexLeave /* xLeave() */
  20347. };
  20348. JX9_PRIVATE const SyMutexMethods * SyMutexExportMethods(void)
  20349. {
  20350. return &sDummyMutexMethods;
  20351. }
  20352. #endif /* __WINNT__ */
  20353. #endif /* JX9_ENABLE_THREADS */
  20354. static void * SyOSHeapAlloc(sxu32 nByte)
  20355. {
  20356. void *pNew;
  20357. #if defined(__WINNT__)
  20358. pNew = HeapAlloc(GetProcessHeap(), 0, nByte);
  20359. #else
  20360. pNew = malloc((size_t)nByte);
  20361. #endif
  20362. return pNew;
  20363. }
  20364. static void * SyOSHeapRealloc(void *pOld, sxu32 nByte)
  20365. {
  20366. void *pNew;
  20367. #if defined(__WINNT__)
  20368. pNew = HeapReAlloc(GetProcessHeap(), 0, pOld, nByte);
  20369. #else
  20370. pNew = realloc(pOld, (size_t)nByte);
  20371. #endif
  20372. return pNew;
  20373. }
  20374. static void SyOSHeapFree(void *pPtr)
  20375. {
  20376. #if defined(__WINNT__)
  20377. HeapFree(GetProcessHeap(), 0, pPtr);
  20378. #else
  20379. free(pPtr);
  20380. #endif
  20381. }
  20382. /* SyRunTimeApi:sxstr.c */
  20383. JX9_PRIVATE sxu32 SyStrlen(const char *zSrc)
  20384. {
  20385. register const char *zIn = zSrc;
  20386. #if defined(UNTRUST)
  20387. if( zIn == 0 ){
  20388. return 0;
  20389. }
  20390. #endif
  20391. for(;;){
  20392. if( !zIn[0] ){ break; } zIn++;
  20393. if( !zIn[0] ){ break; } zIn++;
  20394. if( !zIn[0] ){ break; } zIn++;
  20395. if( !zIn[0] ){ break; } zIn++;
  20396. }
  20397. return (sxu32)(zIn - zSrc);
  20398. }
  20399. JX9_PRIVATE sxi32 SyByteFind(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos)
  20400. {
  20401. const char *zIn = zStr;
  20402. const char *zEnd;
  20403. zEnd = &zIn[nLen];
  20404. for(;;){
  20405. if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++;
  20406. if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++;
  20407. if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++;
  20408. if( zIn >= zEnd ){ break; }if( zIn[0] == c ){ if( pPos ){ *pPos = (sxu32)(zIn - zStr); } return SXRET_OK; } zIn++;
  20409. }
  20410. return SXERR_NOTFOUND;
  20411. }
  20412. #ifndef JX9_DISABLE_BUILTIN_FUNC
  20413. JX9_PRIVATE sxi32 SyByteFind2(const char *zStr, sxu32 nLen, sxi32 c, sxu32 *pPos)
  20414. {
  20415. const char *zIn = zStr;
  20416. const char *zEnd;
  20417. zEnd = &zIn[nLen - 1];
  20418. for( ;; ){
  20419. if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos = (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--;
  20420. if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos = (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--;
  20421. if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos = (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--;
  20422. if( zEnd < zIn ){ break; } if( zEnd[0] == c ){ if( pPos ){ *pPos = (sxu32)(zEnd - zIn);} return SXRET_OK; } zEnd--;
  20423. }
  20424. return SXERR_NOTFOUND;
  20425. }
  20426. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  20427. JX9_PRIVATE sxi32 SyByteListFind(const char *zSrc, sxu32 nLen, const char *zList, sxu32 *pFirstPos)
  20428. {
  20429. const char *zIn = zSrc;
  20430. const char *zPtr;
  20431. const char *zEnd;
  20432. sxi32 c;
  20433. zEnd = &zSrc[nLen];
  20434. for(;;){
  20435. 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++;
  20436. 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++;
  20437. 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++;
  20438. 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++;
  20439. }
  20440. return SXERR_NOTFOUND;
  20441. }
  20442. #ifndef JX9_DISABLE_BUILTIN_FUNC
  20443. JX9_PRIVATE sxi32 SyStrncmp(const char *zLeft, const char *zRight, sxu32 nLen)
  20444. {
  20445. const unsigned char *zP = (const unsigned char *)zLeft;
  20446. const unsigned char *zQ = (const unsigned char *)zRight;
  20447. if( SX_EMPTY_STR(zP) || SX_EMPTY_STR(zQ) ){
  20448. return SX_EMPTY_STR(zP) ? (SX_EMPTY_STR(zQ) ? 0 : -1) :1;
  20449. }
  20450. if( nLen <= 0 ){
  20451. return 0;
  20452. }
  20453. for(;;){
  20454. if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--;
  20455. if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--;
  20456. if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--;
  20457. if( nLen <= 0 ){ return 0; } if( zP[0] == 0 || zQ[0] == 0 || zP[0] != zQ[0] ){ break; } zP++; zQ++; nLen--;
  20458. }
  20459. return (sxi32)(zP[0] - zQ[0]);
  20460. }
  20461. #endif
  20462. JX9_PRIVATE sxi32 SyStrnicmp(const char *zLeft, const char *zRight, sxu32 SLen)
  20463. {
  20464. register unsigned char *p = (unsigned char *)zLeft;
  20465. register unsigned char *q = (unsigned char *)zRight;
  20466. if( SX_EMPTY_STR(p) || SX_EMPTY_STR(q) ){
  20467. return SX_EMPTY_STR(p)? SX_EMPTY_STR(q) ? 0 : -1 :1;
  20468. }
  20469. for(;;){
  20470. if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen;
  20471. if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen;
  20472. if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen;
  20473. if( !SLen ){ return 0; }if( !*p || !*q || SyCharToLower(*p) != SyCharToLower(*q) ){ break; }p++;q++;--SLen;
  20474. }
  20475. return (sxi32)(SyCharToLower(p[0]) - SyCharToLower(q[0]));
  20476. }
  20477. static sxu32 Systrcpy(char *zDest, sxu32 nDestLen, const char *zSrc, sxu32 nLen)
  20478. {
  20479. unsigned char *zBuf = (unsigned char *)zDest;
  20480. unsigned char *zIn = (unsigned char *)zSrc;
  20481. unsigned char *zEnd;
  20482. #if defined(UNTRUST)
  20483. if( zSrc == (const char *)zDest ){
  20484. return 0;
  20485. }
  20486. #endif
  20487. if( nLen <= 0 ){
  20488. nLen = SyStrlen(zSrc);
  20489. }
  20490. zEnd = &zBuf[nDestLen - 1]; /* reserve a room for the null terminator */
  20491. for(;;){
  20492. if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--;
  20493. if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--;
  20494. if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--;
  20495. if( zBuf >= zEnd || nLen == 0 ){ break;} zBuf[0] = zIn[0]; zIn++; zBuf++; nLen--;
  20496. }
  20497. zBuf[0] = 0;
  20498. return (sxu32)(zBuf-(unsigned char *)zDest);
  20499. }
  20500. /* SyRunTimeApi:sxmem.c */
  20501. JX9_PRIVATE void SyZero(void *pSrc, sxu32 nSize)
  20502. {
  20503. register unsigned char *zSrc = (unsigned char *)pSrc;
  20504. unsigned char *zEnd;
  20505. #if defined(UNTRUST)
  20506. if( zSrc == 0 || nSize <= 0 ){
  20507. return ;
  20508. }
  20509. #endif
  20510. zEnd = &zSrc[nSize];
  20511. for(;;){
  20512. if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++;
  20513. if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++;
  20514. if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++;
  20515. if( zSrc >= zEnd ){break;} zSrc[0] = 0; zSrc++;
  20516. }
  20517. }
  20518. JX9_PRIVATE sxi32 SyMemcmp(const void *pB1, const void *pB2, sxu32 nSize)
  20519. {
  20520. sxi32 rc;
  20521. if( nSize <= 0 ){
  20522. return 0;
  20523. }
  20524. if( pB1 == 0 || pB2 == 0 ){
  20525. return pB1 != 0 ? 1 : (pB2 == 0 ? 0 : -1);
  20526. }
  20527. SX_MACRO_FAST_CMP(pB1, pB2, nSize, rc);
  20528. return rc;
  20529. }
  20530. JX9_PRIVATE sxu32 SyMemcpy(const void *pSrc, void *pDest, sxu32 nLen)
  20531. {
  20532. #if defined(UNTRUST)
  20533. if( pSrc == 0 || pDest == 0 ){
  20534. return 0;
  20535. }
  20536. #endif
  20537. if( pSrc == (const void *)pDest ){
  20538. return nLen;
  20539. }
  20540. SX_MACRO_FAST_MEMCPY(pSrc, pDest, nLen);
  20541. return nLen;
  20542. }
  20543. static void * MemOSAlloc(sxu32 nBytes)
  20544. {
  20545. sxu32 *pChunk;
  20546. pChunk = (sxu32 *)SyOSHeapAlloc(nBytes + sizeof(sxu32));
  20547. if( pChunk == 0 ){
  20548. return 0;
  20549. }
  20550. pChunk[0] = nBytes;
  20551. return (void *)&pChunk[1];
  20552. }
  20553. static void * MemOSRealloc(void *pOld, sxu32 nBytes)
  20554. {
  20555. sxu32 *pOldChunk;
  20556. sxu32 *pChunk;
  20557. pOldChunk = (sxu32 *)(((char *)pOld)-sizeof(sxu32));
  20558. if( pOldChunk[0] >= nBytes ){
  20559. return pOld;
  20560. }
  20561. pChunk = (sxu32 *)SyOSHeapRealloc(pOldChunk, nBytes + sizeof(sxu32));
  20562. if( pChunk == 0 ){
  20563. return 0;
  20564. }
  20565. pChunk[0] = nBytes;
  20566. return (void *)&pChunk[1];
  20567. }
  20568. static void MemOSFree(void *pBlock)
  20569. {
  20570. void *pChunk;
  20571. pChunk = (void *)(((char *)pBlock)-sizeof(sxu32));
  20572. SyOSHeapFree(pChunk);
  20573. }
  20574. static sxu32 MemOSChunkSize(void *pBlock)
  20575. {
  20576. sxu32 *pChunk;
  20577. pChunk = (sxu32 *)(((char *)pBlock)-sizeof(sxu32));
  20578. return pChunk[0];
  20579. }
  20580. /* Export OS allocation methods */
  20581. static const SyMemMethods sOSAllocMethods = {
  20582. MemOSAlloc,
  20583. MemOSRealloc,
  20584. MemOSFree,
  20585. MemOSChunkSize,
  20586. 0,
  20587. 0,
  20588. 0
  20589. };
  20590. static void * MemBackendAlloc(SyMemBackend *pBackend, sxu32 nByte)
  20591. {
  20592. SyMemBlock *pBlock;
  20593. sxi32 nRetry = 0;
  20594. /* Append an extra block so we can tracks allocated chunks and avoid memory
  20595. * leaks.
  20596. */
  20597. nByte += sizeof(SyMemBlock);
  20598. for(;;){
  20599. pBlock = (SyMemBlock *)pBackend->pMethods->xAlloc(nByte);
  20600. if( pBlock != 0 || pBackend->xMemError == 0 || nRetry > SXMEM_BACKEND_RETRY
  20601. || SXERR_RETRY != pBackend->xMemError(pBackend->pUserData) ){
  20602. break;
  20603. }
  20604. nRetry++;
  20605. }
  20606. if( pBlock == 0 ){
  20607. return 0;
  20608. }
  20609. pBlock->pNext = pBlock->pPrev = 0;
  20610. /* Link to the list of already tracked blocks */
  20611. MACRO_LD_PUSH(pBackend->pBlocks, pBlock);
  20612. #if defined(UNTRUST)
  20613. pBlock->nGuard = SXMEM_BACKEND_MAGIC;
  20614. #endif
  20615. pBackend->nBlock++;
  20616. return (void *)&pBlock[1];
  20617. }
  20618. JX9_PRIVATE void * SyMemBackendAlloc(SyMemBackend *pBackend, sxu32 nByte)
  20619. {
  20620. void *pChunk;
  20621. #if defined(UNTRUST)
  20622. if( SXMEM_BACKEND_CORRUPT(pBackend) ){
  20623. return 0;
  20624. }
  20625. #endif
  20626. if( pBackend->pMutexMethods ){
  20627. SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
  20628. }
  20629. pChunk = MemBackendAlloc(&(*pBackend), nByte);
  20630. if( pBackend->pMutexMethods ){
  20631. SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
  20632. }
  20633. return pChunk;
  20634. }
  20635. static void * MemBackendRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte)
  20636. {
  20637. SyMemBlock *pBlock, *pNew, *pPrev, *pNext;
  20638. sxu32 nRetry = 0;
  20639. if( pOld == 0 ){
  20640. return MemBackendAlloc(&(*pBackend), nByte);
  20641. }
  20642. pBlock = (SyMemBlock *)(((char *)pOld) - sizeof(SyMemBlock));
  20643. #if defined(UNTRUST)
  20644. if( pBlock->nGuard != SXMEM_BACKEND_MAGIC ){
  20645. return 0;
  20646. }
  20647. #endif
  20648. nByte += sizeof(SyMemBlock);
  20649. pPrev = pBlock->pPrev;
  20650. pNext = pBlock->pNext;
  20651. for(;;){
  20652. pNew = (SyMemBlock *)pBackend->pMethods->xRealloc(pBlock, nByte);
  20653. if( pNew != 0 || pBackend->xMemError == 0 || nRetry > SXMEM_BACKEND_RETRY ||
  20654. SXERR_RETRY != pBackend->xMemError(pBackend->pUserData) ){
  20655. break;
  20656. }
  20657. nRetry++;
  20658. }
  20659. if( pNew == 0 ){
  20660. return 0;
  20661. }
  20662. if( pNew != pBlock ){
  20663. if( pPrev == 0 ){
  20664. pBackend->pBlocks = pNew;
  20665. }else{
  20666. pPrev->pNext = pNew;
  20667. }
  20668. if( pNext ){
  20669. pNext->pPrev = pNew;
  20670. }
  20671. #if defined(UNTRUST)
  20672. pNew->nGuard = SXMEM_BACKEND_MAGIC;
  20673. #endif
  20674. }
  20675. return (void *)&pNew[1];
  20676. }
  20677. JX9_PRIVATE void * SyMemBackendRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte)
  20678. {
  20679. void *pChunk;
  20680. #if defined(UNTRUST)
  20681. if( SXMEM_BACKEND_CORRUPT(pBackend) ){
  20682. return 0;
  20683. }
  20684. #endif
  20685. if( pBackend->pMutexMethods ){
  20686. SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
  20687. }
  20688. pChunk = MemBackendRealloc(&(*pBackend), pOld, nByte);
  20689. if( pBackend->pMutexMethods ){
  20690. SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
  20691. }
  20692. return pChunk;
  20693. }
  20694. static sxi32 MemBackendFree(SyMemBackend *pBackend, void * pChunk)
  20695. {
  20696. SyMemBlock *pBlock;
  20697. pBlock = (SyMemBlock *)(((char *)pChunk) - sizeof(SyMemBlock));
  20698. #if defined(UNTRUST)
  20699. if( pBlock->nGuard != SXMEM_BACKEND_MAGIC ){
  20700. return SXERR_CORRUPT;
  20701. }
  20702. #endif
  20703. /* Unlink from the list of active blocks */
  20704. if( pBackend->nBlock > 0 ){
  20705. /* Release the block */
  20706. #if defined(UNTRUST)
  20707. /* Mark as stale block */
  20708. pBlock->nGuard = 0x635B;
  20709. #endif
  20710. MACRO_LD_REMOVE(pBackend->pBlocks, pBlock);
  20711. pBackend->nBlock--;
  20712. pBackend->pMethods->xFree(pBlock);
  20713. }
  20714. return SXRET_OK;
  20715. }
  20716. JX9_PRIVATE sxi32 SyMemBackendFree(SyMemBackend *pBackend, void * pChunk)
  20717. {
  20718. sxi32 rc;
  20719. #if defined(UNTRUST)
  20720. if( SXMEM_BACKEND_CORRUPT(pBackend) ){
  20721. return SXERR_CORRUPT;
  20722. }
  20723. #endif
  20724. if( pChunk == 0 ){
  20725. return SXRET_OK;
  20726. }
  20727. if( pBackend->pMutexMethods ){
  20728. SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
  20729. }
  20730. rc = MemBackendFree(&(*pBackend), pChunk);
  20731. if( pBackend->pMutexMethods ){
  20732. SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
  20733. }
  20734. return rc;
  20735. }
  20736. #if defined(JX9_ENABLE_THREADS)
  20737. JX9_PRIVATE sxi32 SyMemBackendMakeThreadSafe(SyMemBackend *pBackend, const SyMutexMethods *pMethods)
  20738. {
  20739. SyMutex *pMutex;
  20740. #if defined(UNTRUST)
  20741. if( SXMEM_BACKEND_CORRUPT(pBackend) || pMethods == 0 || pMethods->xNew == 0){
  20742. return SXERR_CORRUPT;
  20743. }
  20744. #endif
  20745. pMutex = pMethods->xNew(SXMUTEX_TYPE_FAST);
  20746. if( pMutex == 0 ){
  20747. return SXERR_OS;
  20748. }
  20749. /* Attach the mutex to the memory backend */
  20750. pBackend->pMutex = pMutex;
  20751. pBackend->pMutexMethods = pMethods;
  20752. return SXRET_OK;
  20753. }
  20754. JX9_PRIVATE sxi32 SyMemBackendDisbaleMutexing(SyMemBackend *pBackend)
  20755. {
  20756. #if defined(UNTRUST)
  20757. if( SXMEM_BACKEND_CORRUPT(pBackend) ){
  20758. return SXERR_CORRUPT;
  20759. }
  20760. #endif
  20761. if( pBackend->pMutex == 0 ){
  20762. /* There is no mutex subsystem at all */
  20763. return SXRET_OK;
  20764. }
  20765. SyMutexRelease(pBackend->pMutexMethods, pBackend->pMutex);
  20766. pBackend->pMutexMethods = 0;
  20767. pBackend->pMutex = 0;
  20768. return SXRET_OK;
  20769. }
  20770. #endif
  20771. /*
  20772. * Memory pool allocator
  20773. */
  20774. #define SXMEM_POOL_MAGIC 0xDEAD
  20775. #define SXMEM_POOL_MAXALLOC (1<<(SXMEM_POOL_NBUCKETS+SXMEM_POOL_INCR))
  20776. #define SXMEM_POOL_MINALLOC (1<<(SXMEM_POOL_INCR))
  20777. static sxi32 MemPoolBucketAlloc(SyMemBackend *pBackend, sxu32 nBucket)
  20778. {
  20779. char *zBucket, *zBucketEnd;
  20780. SyMemHeader *pHeader;
  20781. sxu32 nBucketSize;
  20782. /* Allocate one big block first */
  20783. zBucket = (char *)MemBackendAlloc(&(*pBackend), SXMEM_POOL_MAXALLOC);
  20784. if( zBucket == 0 ){
  20785. return SXERR_MEM;
  20786. }
  20787. zBucketEnd = &zBucket[SXMEM_POOL_MAXALLOC];
  20788. /* Divide the big block into mini bucket pool */
  20789. nBucketSize = 1 << (nBucket + SXMEM_POOL_INCR);
  20790. pBackend->apPool[nBucket] = pHeader = (SyMemHeader *)zBucket;
  20791. for(;;){
  20792. if( &zBucket[nBucketSize] >= zBucketEnd ){
  20793. break;
  20794. }
  20795. pHeader->pNext = (SyMemHeader *)&zBucket[nBucketSize];
  20796. /* Advance the cursor to the next available chunk */
  20797. pHeader = pHeader->pNext;
  20798. zBucket += nBucketSize;
  20799. }
  20800. pHeader->pNext = 0;
  20801. return SXRET_OK;
  20802. }
  20803. static void * MemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nByte)
  20804. {
  20805. SyMemHeader *pBucket, *pNext;
  20806. sxu32 nBucketSize;
  20807. sxu32 nBucket;
  20808. if( nByte + sizeof(SyMemHeader) >= SXMEM_POOL_MAXALLOC ){
  20809. /* Allocate a big chunk directly */
  20810. pBucket = (SyMemHeader *)MemBackendAlloc(&(*pBackend), nByte+sizeof(SyMemHeader));
  20811. if( pBucket == 0 ){
  20812. return 0;
  20813. }
  20814. /* Record as big block */
  20815. pBucket->nBucket = (sxu32)(SXMEM_POOL_MAGIC << 16) | SXU16_HIGH;
  20816. return (void *)(pBucket+1);
  20817. }
  20818. /* Locate the appropriate bucket */
  20819. nBucket = 0;
  20820. nBucketSize = SXMEM_POOL_MINALLOC;
  20821. while( nByte + sizeof(SyMemHeader) > nBucketSize ){
  20822. nBucketSize <<= 1;
  20823. nBucket++;
  20824. }
  20825. pBucket = pBackend->apPool[nBucket];
  20826. if( pBucket == 0 ){
  20827. sxi32 rc;
  20828. rc = MemPoolBucketAlloc(&(*pBackend), nBucket);
  20829. if( rc != SXRET_OK ){
  20830. return 0;
  20831. }
  20832. pBucket = pBackend->apPool[nBucket];
  20833. }
  20834. /* Remove from the free list */
  20835. pNext = pBucket->pNext;
  20836. pBackend->apPool[nBucket] = pNext;
  20837. /* Record bucket&magic number */
  20838. pBucket->nBucket = (SXMEM_POOL_MAGIC << 16) | nBucket;
  20839. return (void *)&pBucket[1];
  20840. }
  20841. JX9_PRIVATE void * SyMemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nByte)
  20842. {
  20843. void *pChunk;
  20844. #if defined(UNTRUST)
  20845. if( SXMEM_BACKEND_CORRUPT(pBackend) ){
  20846. return 0;
  20847. }
  20848. #endif
  20849. if( pBackend->pMutexMethods ){
  20850. SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
  20851. }
  20852. pChunk = MemBackendPoolAlloc(&(*pBackend), nByte);
  20853. if( pBackend->pMutexMethods ){
  20854. SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
  20855. }
  20856. return pChunk;
  20857. }
  20858. static sxi32 MemBackendPoolFree(SyMemBackend *pBackend, void * pChunk)
  20859. {
  20860. SyMemHeader *pHeader;
  20861. sxu32 nBucket;
  20862. /* Get the corresponding bucket */
  20863. pHeader = (SyMemHeader *)(((char *)pChunk) - sizeof(SyMemHeader));
  20864. /* Sanity check to avoid misuse */
  20865. if( (pHeader->nBucket >> 16) != SXMEM_POOL_MAGIC ){
  20866. return SXERR_CORRUPT;
  20867. }
  20868. nBucket = pHeader->nBucket & 0xFFFF;
  20869. if( nBucket == SXU16_HIGH ){
  20870. /* Free the big block */
  20871. MemBackendFree(&(*pBackend), pHeader);
  20872. }else{
  20873. /* Return to the free list */
  20874. pHeader->pNext = pBackend->apPool[nBucket & 0x0f];
  20875. pBackend->apPool[nBucket & 0x0f] = pHeader;
  20876. }
  20877. return SXRET_OK;
  20878. }
  20879. JX9_PRIVATE sxi32 SyMemBackendPoolFree(SyMemBackend *pBackend, void * pChunk)
  20880. {
  20881. sxi32 rc;
  20882. #if defined(UNTRUST)
  20883. if( SXMEM_BACKEND_CORRUPT(pBackend) || pChunk == 0 ){
  20884. return SXERR_CORRUPT;
  20885. }
  20886. #endif
  20887. if( pBackend->pMutexMethods ){
  20888. SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
  20889. }
  20890. rc = MemBackendPoolFree(&(*pBackend), pChunk);
  20891. if( pBackend->pMutexMethods ){
  20892. SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
  20893. }
  20894. return rc;
  20895. }
  20896. #if 0
  20897. static void * MemBackendPoolRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte)
  20898. {
  20899. sxu32 nBucket, nBucketSize;
  20900. SyMemHeader *pHeader;
  20901. void * pNew;
  20902. if( pOld == 0 ){
  20903. /* Allocate a new pool */
  20904. pNew = MemBackendPoolAlloc(&(*pBackend), nByte);
  20905. return pNew;
  20906. }
  20907. /* Get the corresponding bucket */
  20908. pHeader = (SyMemHeader *)(((char *)pOld) - sizeof(SyMemHeader));
  20909. /* Sanity check to avoid misuse */
  20910. if( (pHeader->nBucket >> 16) != SXMEM_POOL_MAGIC ){
  20911. return 0;
  20912. }
  20913. nBucket = pHeader->nBucket & 0xFFFF;
  20914. if( nBucket == SXU16_HIGH ){
  20915. /* Big block */
  20916. return MemBackendRealloc(&(*pBackend), pHeader, nByte);
  20917. }
  20918. nBucketSize = 1 << (nBucket + SXMEM_POOL_INCR);
  20919. if( nBucketSize >= nByte + sizeof(SyMemHeader) ){
  20920. /* The old bucket can honor the requested size */
  20921. return pOld;
  20922. }
  20923. /* Allocate a new pool */
  20924. pNew = MemBackendPoolAlloc(&(*pBackend), nByte);
  20925. if( pNew == 0 ){
  20926. return 0;
  20927. }
  20928. /* Copy the old data into the new block */
  20929. SyMemcpy(pOld, pNew, nBucketSize);
  20930. /* Free the stale block */
  20931. MemBackendPoolFree(&(*pBackend), pOld);
  20932. return pNew;
  20933. }
  20934. JX9_PRIVATE void * SyMemBackendPoolRealloc(SyMemBackend *pBackend, void * pOld, sxu32 nByte)
  20935. {
  20936. void *pChunk;
  20937. #if defined(UNTRUST)
  20938. if( SXMEM_BACKEND_CORRUPT(pBackend) ){
  20939. return 0;
  20940. }
  20941. #endif
  20942. if( pBackend->pMutexMethods ){
  20943. SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
  20944. }
  20945. pChunk = MemBackendPoolRealloc(&(*pBackend), pOld, nByte);
  20946. if( pBackend->pMutexMethods ){
  20947. SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
  20948. }
  20949. return pChunk;
  20950. }
  20951. #endif
  20952. JX9_PRIVATE sxi32 SyMemBackendInit(SyMemBackend *pBackend, ProcMemError xMemErr, void * pUserData)
  20953. {
  20954. #if defined(UNTRUST)
  20955. if( pBackend == 0 ){
  20956. return SXERR_EMPTY;
  20957. }
  20958. #endif
  20959. /* Zero the allocator first */
  20960. SyZero(&(*pBackend), sizeof(SyMemBackend));
  20961. pBackend->xMemError = xMemErr;
  20962. pBackend->pUserData = pUserData;
  20963. /* Switch to the OS memory allocator */
  20964. pBackend->pMethods = &sOSAllocMethods;
  20965. if( pBackend->pMethods->xInit ){
  20966. /* Initialize the backend */
  20967. if( SXRET_OK != pBackend->pMethods->xInit(pBackend->pMethods->pUserData) ){
  20968. return SXERR_ABORT;
  20969. }
  20970. }
  20971. #if defined(UNTRUST)
  20972. pBackend->nMagic = SXMEM_BACKEND_MAGIC;
  20973. #endif
  20974. return SXRET_OK;
  20975. }
  20976. JX9_PRIVATE sxi32 SyMemBackendInitFromOthers(SyMemBackend *pBackend, const SyMemMethods *pMethods, ProcMemError xMemErr, void * pUserData)
  20977. {
  20978. #if defined(UNTRUST)
  20979. if( pBackend == 0 || pMethods == 0){
  20980. return SXERR_EMPTY;
  20981. }
  20982. #endif
  20983. if( pMethods->xAlloc == 0 || pMethods->xRealloc == 0 || pMethods->xFree == 0 || pMethods->xChunkSize == 0 ){
  20984. /* mandatory methods are missing */
  20985. return SXERR_INVALID;
  20986. }
  20987. /* Zero the allocator first */
  20988. SyZero(&(*pBackend), sizeof(SyMemBackend));
  20989. pBackend->xMemError = xMemErr;
  20990. pBackend->pUserData = pUserData;
  20991. /* Switch to the host application memory allocator */
  20992. pBackend->pMethods = pMethods;
  20993. if( pBackend->pMethods->xInit ){
  20994. /* Initialize the backend */
  20995. if( SXRET_OK != pBackend->pMethods->xInit(pBackend->pMethods->pUserData) ){
  20996. return SXERR_ABORT;
  20997. }
  20998. }
  20999. #if defined(UNTRUST)
  21000. pBackend->nMagic = SXMEM_BACKEND_MAGIC;
  21001. #endif
  21002. return SXRET_OK;
  21003. }
  21004. JX9_PRIVATE sxi32 SyMemBackendInitFromParent(SyMemBackend *pBackend, SyMemBackend *pParent)
  21005. {
  21006. sxu8 bInheritMutex;
  21007. #if defined(UNTRUST)
  21008. if( pBackend == 0 || SXMEM_BACKEND_CORRUPT(pParent) ){
  21009. return SXERR_CORRUPT;
  21010. }
  21011. #endif
  21012. /* Zero the allocator first */
  21013. SyZero(&(*pBackend), sizeof(SyMemBackend));
  21014. pBackend->pMethods = pParent->pMethods;
  21015. pBackend->xMemError = pParent->xMemError;
  21016. pBackend->pUserData = pParent->pUserData;
  21017. bInheritMutex = pParent->pMutexMethods ? TRUE : FALSE;
  21018. if( bInheritMutex ){
  21019. pBackend->pMutexMethods = pParent->pMutexMethods;
  21020. /* Create a private mutex */
  21021. pBackend->pMutex = pBackend->pMutexMethods->xNew(SXMUTEX_TYPE_FAST);
  21022. if( pBackend->pMutex == 0){
  21023. return SXERR_OS;
  21024. }
  21025. }
  21026. #if defined(UNTRUST)
  21027. pBackend->nMagic = SXMEM_BACKEND_MAGIC;
  21028. #endif
  21029. return SXRET_OK;
  21030. }
  21031. static sxi32 MemBackendRelease(SyMemBackend *pBackend)
  21032. {
  21033. SyMemBlock *pBlock, *pNext;
  21034. pBlock = pBackend->pBlocks;
  21035. for(;;){
  21036. if( pBackend->nBlock == 0 ){
  21037. break;
  21038. }
  21039. pNext = pBlock->pNext;
  21040. pBackend->pMethods->xFree(pBlock);
  21041. pBlock = pNext;
  21042. pBackend->nBlock--;
  21043. /* LOOP ONE */
  21044. if( pBackend->nBlock == 0 ){
  21045. break;
  21046. }
  21047. pNext = pBlock->pNext;
  21048. pBackend->pMethods->xFree(pBlock);
  21049. pBlock = pNext;
  21050. pBackend->nBlock--;
  21051. /* LOOP TWO */
  21052. if( pBackend->nBlock == 0 ){
  21053. break;
  21054. }
  21055. pNext = pBlock->pNext;
  21056. pBackend->pMethods->xFree(pBlock);
  21057. pBlock = pNext;
  21058. pBackend->nBlock--;
  21059. /* LOOP THREE */
  21060. if( pBackend->nBlock == 0 ){
  21061. break;
  21062. }
  21063. pNext = pBlock->pNext;
  21064. pBackend->pMethods->xFree(pBlock);
  21065. pBlock = pNext;
  21066. pBackend->nBlock--;
  21067. /* LOOP FOUR */
  21068. }
  21069. if( pBackend->pMethods->xRelease ){
  21070. pBackend->pMethods->xRelease(pBackend->pMethods->pUserData);
  21071. }
  21072. pBackend->pMethods = 0;
  21073. pBackend->pBlocks = 0;
  21074. #if defined(UNTRUST)
  21075. pBackend->nMagic = 0x2626;
  21076. #endif
  21077. return SXRET_OK;
  21078. }
  21079. JX9_PRIVATE sxi32 SyMemBackendRelease(SyMemBackend *pBackend)
  21080. {
  21081. sxi32 rc;
  21082. #if defined(UNTRUST)
  21083. if( SXMEM_BACKEND_CORRUPT(pBackend) ){
  21084. return SXERR_INVALID;
  21085. }
  21086. #endif
  21087. if( pBackend->pMutexMethods ){
  21088. SyMutexEnter(pBackend->pMutexMethods, pBackend->pMutex);
  21089. }
  21090. rc = MemBackendRelease(&(*pBackend));
  21091. if( pBackend->pMutexMethods ){
  21092. SyMutexLeave(pBackend->pMutexMethods, pBackend->pMutex);
  21093. SyMutexRelease(pBackend->pMutexMethods, pBackend->pMutex);
  21094. }
  21095. return SXRET_OK;
  21096. }
  21097. JX9_PRIVATE void * SyMemBackendDup(SyMemBackend *pBackend, const void *pSrc, sxu32 nSize)
  21098. {
  21099. void *pNew;
  21100. #if defined(UNTRUST)
  21101. if( pSrc == 0 || nSize <= 0 ){
  21102. return 0;
  21103. }
  21104. #endif
  21105. pNew = SyMemBackendAlloc(&(*pBackend), nSize);
  21106. if( pNew ){
  21107. SyMemcpy(pSrc, pNew, nSize);
  21108. }
  21109. return pNew;
  21110. }
  21111. JX9_PRIVATE char * SyMemBackendStrDup(SyMemBackend *pBackend, const char *zSrc, sxu32 nSize)
  21112. {
  21113. char *zDest;
  21114. zDest = (char *)SyMemBackendAlloc(&(*pBackend), nSize + 1);
  21115. if( zDest ){
  21116. Systrcpy(zDest, nSize+1, zSrc, nSize);
  21117. }
  21118. return zDest;
  21119. }
  21120. JX9_PRIVATE sxi32 SyBlobInitFromBuf(SyBlob *pBlob, void *pBuffer, sxu32 nSize)
  21121. {
  21122. #if defined(UNTRUST)
  21123. if( pBlob == 0 || pBuffer == 0 || nSize < 1 ){
  21124. return SXERR_EMPTY;
  21125. }
  21126. #endif
  21127. pBlob->pBlob = pBuffer;
  21128. pBlob->mByte = nSize;
  21129. pBlob->nByte = 0;
  21130. pBlob->pAllocator = 0;
  21131. pBlob->nFlags = SXBLOB_LOCKED|SXBLOB_STATIC;
  21132. return SXRET_OK;
  21133. }
  21134. JX9_PRIVATE sxi32 SyBlobInit(SyBlob *pBlob, SyMemBackend *pAllocator)
  21135. {
  21136. #if defined(UNTRUST)
  21137. if( pBlob == 0 ){
  21138. return SXERR_EMPTY;
  21139. }
  21140. #endif
  21141. pBlob->pBlob = 0;
  21142. pBlob->mByte = pBlob->nByte = 0;
  21143. pBlob->pAllocator = &(*pAllocator);
  21144. pBlob->nFlags = 0;
  21145. return SXRET_OK;
  21146. }
  21147. JX9_PRIVATE sxi32 SyBlobReadOnly(SyBlob *pBlob, const void *pData, sxu32 nByte)
  21148. {
  21149. #if defined(UNTRUST)
  21150. if( pBlob == 0 ){
  21151. return SXERR_EMPTY;
  21152. }
  21153. #endif
  21154. pBlob->pBlob = (void *)pData;
  21155. pBlob->nByte = nByte;
  21156. pBlob->mByte = 0;
  21157. pBlob->nFlags |= SXBLOB_RDONLY;
  21158. return SXRET_OK;
  21159. }
  21160. #ifndef SXBLOB_MIN_GROWTH
  21161. #define SXBLOB_MIN_GROWTH 16
  21162. #endif
  21163. static sxi32 BlobPrepareGrow(SyBlob *pBlob, sxu32 *pByte)
  21164. {
  21165. sxu32 nByte;
  21166. void *pNew;
  21167. nByte = *pByte;
  21168. if( pBlob->nFlags & (SXBLOB_LOCKED|SXBLOB_STATIC) ){
  21169. if ( SyBlobFreeSpace(pBlob) < nByte ){
  21170. *pByte = SyBlobFreeSpace(pBlob);
  21171. if( (*pByte) == 0 ){
  21172. return SXERR_SHORT;
  21173. }
  21174. }
  21175. return SXRET_OK;
  21176. }
  21177. if( pBlob->nFlags & SXBLOB_RDONLY ){
  21178. /* Make a copy of the read-only item */
  21179. if( pBlob->nByte > 0 ){
  21180. pNew = SyMemBackendDup(pBlob->pAllocator, pBlob->pBlob, pBlob->nByte);
  21181. if( pNew == 0 ){
  21182. return SXERR_MEM;
  21183. }
  21184. pBlob->pBlob = pNew;
  21185. pBlob->mByte = pBlob->nByte;
  21186. }else{
  21187. pBlob->pBlob = 0;
  21188. pBlob->mByte = 0;
  21189. }
  21190. /* Remove the read-only flag */
  21191. pBlob->nFlags &= ~SXBLOB_RDONLY;
  21192. }
  21193. if( SyBlobFreeSpace(pBlob) >= nByte ){
  21194. return SXRET_OK;
  21195. }
  21196. if( pBlob->mByte > 0 ){
  21197. nByte = nByte + pBlob->mByte * 2 + SXBLOB_MIN_GROWTH;
  21198. }else if ( nByte < SXBLOB_MIN_GROWTH ){
  21199. nByte = SXBLOB_MIN_GROWTH;
  21200. }
  21201. pNew = SyMemBackendRealloc(pBlob->pAllocator, pBlob->pBlob, nByte);
  21202. if( pNew == 0 ){
  21203. return SXERR_MEM;
  21204. }
  21205. pBlob->pBlob = pNew;
  21206. pBlob->mByte = nByte;
  21207. return SXRET_OK;
  21208. }
  21209. JX9_PRIVATE sxi32 SyBlobAppend(SyBlob *pBlob, const void *pData, sxu32 nSize)
  21210. {
  21211. sxu8 *zBlob;
  21212. sxi32 rc;
  21213. if( nSize < 1 ){
  21214. return SXRET_OK;
  21215. }
  21216. rc = BlobPrepareGrow(&(*pBlob), &nSize);
  21217. if( SXRET_OK != rc ){
  21218. return rc;
  21219. }
  21220. if( pData ){
  21221. zBlob = (sxu8 *)pBlob->pBlob ;
  21222. zBlob = &zBlob[pBlob->nByte];
  21223. pBlob->nByte += nSize;
  21224. SX_MACRO_FAST_MEMCPY(pData, zBlob, nSize);
  21225. }
  21226. return SXRET_OK;
  21227. }
  21228. JX9_PRIVATE sxi32 SyBlobNullAppend(SyBlob *pBlob)
  21229. {
  21230. sxi32 rc;
  21231. sxu32 n;
  21232. n = pBlob->nByte;
  21233. rc = SyBlobAppend(&(*pBlob), (const void *)"\0", sizeof(char));
  21234. if (rc == SXRET_OK ){
  21235. pBlob->nByte = n;
  21236. }
  21237. return rc;
  21238. }
  21239. JX9_PRIVATE sxi32 SyBlobDup(SyBlob *pSrc, SyBlob *pDest)
  21240. {
  21241. sxi32 rc = SXRET_OK;
  21242. #ifdef UNTRUST
  21243. if( pSrc == 0 || pDest == 0 ){
  21244. return SXERR_EMPTY;
  21245. }
  21246. #endif
  21247. if( pSrc->nByte > 0 ){
  21248. rc = SyBlobAppend(&(*pDest), pSrc->pBlob, pSrc->nByte);
  21249. }
  21250. return rc;
  21251. }
  21252. JX9_PRIVATE sxi32 SyBlobReset(SyBlob *pBlob)
  21253. {
  21254. pBlob->nByte = 0;
  21255. if( pBlob->nFlags & SXBLOB_RDONLY ){
  21256. pBlob->pBlob = 0;
  21257. pBlob->mByte = 0;
  21258. pBlob->nFlags &= ~SXBLOB_RDONLY;
  21259. }
  21260. return SXRET_OK;
  21261. }
  21262. JX9_PRIVATE sxi32 SyBlobRelease(SyBlob *pBlob)
  21263. {
  21264. if( (pBlob->nFlags & (SXBLOB_STATIC|SXBLOB_RDONLY)) == 0 && pBlob->mByte > 0 ){
  21265. SyMemBackendFree(pBlob->pAllocator, pBlob->pBlob);
  21266. }
  21267. pBlob->pBlob = 0;
  21268. pBlob->nByte = pBlob->mByte = 0;
  21269. pBlob->nFlags = 0;
  21270. return SXRET_OK;
  21271. }
  21272. #ifndef JX9_DISABLE_BUILTIN_FUNC
  21273. JX9_PRIVATE sxi32 SyBlobSearch(const void *pBlob, sxu32 nLen, const void *pPattern, sxu32 pLen, sxu32 *pOfft)
  21274. {
  21275. const char *zIn = (const char *)pBlob;
  21276. const char *zEnd;
  21277. sxi32 rc;
  21278. if( pLen > nLen ){
  21279. return SXERR_NOTFOUND;
  21280. }
  21281. zEnd = &zIn[nLen-pLen];
  21282. for(;;){
  21283. 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++;
  21284. 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++;
  21285. 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++;
  21286. 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++;
  21287. }
  21288. return SXERR_NOTFOUND;
  21289. }
  21290. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  21291. /* SyRunTimeApi:sxds.c */
  21292. JX9_PRIVATE sxi32 SySetInit(SySet *pSet, SyMemBackend *pAllocator, sxu32 ElemSize)
  21293. {
  21294. pSet->nSize = 0 ;
  21295. pSet->nUsed = 0;
  21296. pSet->nCursor = 0;
  21297. pSet->eSize = ElemSize;
  21298. pSet->pAllocator = pAllocator;
  21299. pSet->pBase = 0;
  21300. pSet->pUserData = 0;
  21301. return SXRET_OK;
  21302. }
  21303. JX9_PRIVATE sxi32 SySetPut(SySet *pSet, const void *pItem)
  21304. {
  21305. unsigned char *zbase;
  21306. if( pSet->nUsed >= pSet->nSize ){
  21307. void *pNew;
  21308. if( pSet->pAllocator == 0 ){
  21309. return SXERR_LOCKED;
  21310. }
  21311. if( pSet->nSize <= 0 ){
  21312. pSet->nSize = 4;
  21313. }
  21314. pNew = SyMemBackendRealloc(pSet->pAllocator, pSet->pBase, pSet->eSize * pSet->nSize * 2);
  21315. if( pNew == 0 ){
  21316. return SXERR_MEM;
  21317. }
  21318. pSet->pBase = pNew;
  21319. pSet->nSize <<= 1;
  21320. }
  21321. zbase = (unsigned char *)pSet->pBase;
  21322. SX_MACRO_FAST_MEMCPY(pItem, &zbase[pSet->nUsed * pSet->eSize], pSet->eSize);
  21323. pSet->nUsed++;
  21324. return SXRET_OK;
  21325. }
  21326. JX9_PRIVATE sxi32 SySetAlloc(SySet *pSet, sxi32 nItem)
  21327. {
  21328. if( pSet->nSize > 0 ){
  21329. return SXERR_LOCKED;
  21330. }
  21331. if( nItem < 8 ){
  21332. nItem = 8;
  21333. }
  21334. pSet->pBase = SyMemBackendAlloc(pSet->pAllocator, pSet->eSize * nItem);
  21335. if( pSet->pBase == 0 ){
  21336. return SXERR_MEM;
  21337. }
  21338. pSet->nSize = nItem;
  21339. return SXRET_OK;
  21340. }
  21341. JX9_PRIVATE sxi32 SySetReset(SySet *pSet)
  21342. {
  21343. pSet->nUsed = 0;
  21344. pSet->nCursor = 0;
  21345. return SXRET_OK;
  21346. }
  21347. JX9_PRIVATE sxi32 SySetResetCursor(SySet *pSet)
  21348. {
  21349. pSet->nCursor = 0;
  21350. return SXRET_OK;
  21351. }
  21352. JX9_PRIVATE sxi32 SySetGetNextEntry(SySet *pSet, void **ppEntry)
  21353. {
  21354. register unsigned char *zSrc;
  21355. if( pSet->nCursor >= pSet->nUsed ){
  21356. /* Reset cursor */
  21357. pSet->nCursor = 0;
  21358. return SXERR_EOF;
  21359. }
  21360. zSrc = (unsigned char *)SySetBasePtr(pSet);
  21361. if( ppEntry ){
  21362. *ppEntry = (void *)&zSrc[pSet->nCursor * pSet->eSize];
  21363. }
  21364. pSet->nCursor++;
  21365. return SXRET_OK;
  21366. }
  21367. JX9_PRIVATE sxi32 SySetRelease(SySet *pSet)
  21368. {
  21369. sxi32 rc = SXRET_OK;
  21370. if( pSet->pAllocator && pSet->pBase ){
  21371. rc = SyMemBackendFree(pSet->pAllocator, pSet->pBase);
  21372. }
  21373. pSet->pBase = 0;
  21374. pSet->nUsed = 0;
  21375. pSet->nCursor = 0;
  21376. return rc;
  21377. }
  21378. JX9_PRIVATE void * SySetPeek(SySet *pSet)
  21379. {
  21380. const char *zBase;
  21381. if( pSet->nUsed <= 0 ){
  21382. return 0;
  21383. }
  21384. zBase = (const char *)pSet->pBase;
  21385. return (void *)&zBase[(pSet->nUsed - 1) * pSet->eSize];
  21386. }
  21387. JX9_PRIVATE void * SySetPop(SySet *pSet)
  21388. {
  21389. const char *zBase;
  21390. void *pData;
  21391. if( pSet->nUsed <= 0 ){
  21392. return 0;
  21393. }
  21394. zBase = (const char *)pSet->pBase;
  21395. pSet->nUsed--;
  21396. pData = (void *)&zBase[pSet->nUsed * pSet->eSize];
  21397. return pData;
  21398. }
  21399. JX9_PRIVATE void * SySetAt(SySet *pSet, sxu32 nIdx)
  21400. {
  21401. const char *zBase;
  21402. if( nIdx >= pSet->nUsed ){
  21403. /* Out of range */
  21404. return 0;
  21405. }
  21406. zBase = (const char *)pSet->pBase;
  21407. return (void *)&zBase[nIdx * pSet->eSize];
  21408. }
  21409. /* Private hash entry */
  21410. struct SyHashEntry_Pr
  21411. {
  21412. const void *pKey; /* Hash key */
  21413. sxu32 nKeyLen; /* Key length */
  21414. void *pUserData; /* User private data */
  21415. /* Private fields */
  21416. sxu32 nHash;
  21417. SyHash *pHash;
  21418. SyHashEntry_Pr *pNext, *pPrev; /* Next and previous entry in the list */
  21419. SyHashEntry_Pr *pNextCollide, *pPrevCollide; /* Collision list */
  21420. };
  21421. #define INVALID_HASH(H) ((H)->apBucket == 0)
  21422. /* Forward declarartion */
  21423. static sxu32 SyBinHash(const void *pSrc, sxu32 nLen);
  21424. JX9_PRIVATE sxi32 SyHashInit(SyHash *pHash, SyMemBackend *pAllocator, ProcHash xHash, ProcCmp xCmp)
  21425. {
  21426. SyHashEntry_Pr **apNew;
  21427. #if defined(UNTRUST)
  21428. if( pHash == 0 ){
  21429. return SXERR_EMPTY;
  21430. }
  21431. #endif
  21432. /* Allocate a new table */
  21433. apNew = (SyHashEntry_Pr **)SyMemBackendAlloc(&(*pAllocator), sizeof(SyHashEntry_Pr *) * SXHASH_BUCKET_SIZE);
  21434. if( apNew == 0 ){
  21435. return SXERR_MEM;
  21436. }
  21437. SyZero((void *)apNew, sizeof(SyHashEntry_Pr *) * SXHASH_BUCKET_SIZE);
  21438. pHash->pAllocator = &(*pAllocator);
  21439. pHash->xHash = xHash ? xHash : SyBinHash;
  21440. pHash->xCmp = xCmp ? xCmp : SyMemcmp;
  21441. pHash->pCurrent = pHash->pList = 0;
  21442. pHash->nEntry = 0;
  21443. pHash->apBucket = apNew;
  21444. pHash->nBucketSize = SXHASH_BUCKET_SIZE;
  21445. return SXRET_OK;
  21446. }
  21447. JX9_PRIVATE sxi32 SyHashRelease(SyHash *pHash)
  21448. {
  21449. SyHashEntry_Pr *pEntry, *pNext;
  21450. #if defined(UNTRUST)
  21451. if( INVALID_HASH(pHash) ){
  21452. return SXERR_EMPTY;
  21453. }
  21454. #endif
  21455. pEntry = pHash->pList;
  21456. for(;;){
  21457. if( pHash->nEntry == 0 ){
  21458. break;
  21459. }
  21460. pNext = pEntry->pNext;
  21461. SyMemBackendPoolFree(pHash->pAllocator, pEntry);
  21462. pEntry = pNext;
  21463. pHash->nEntry--;
  21464. }
  21465. if( pHash->apBucket ){
  21466. SyMemBackendFree(pHash->pAllocator, (void *)pHash->apBucket);
  21467. }
  21468. pHash->apBucket = 0;
  21469. pHash->nBucketSize = 0;
  21470. pHash->pAllocator = 0;
  21471. return SXRET_OK;
  21472. }
  21473. static SyHashEntry_Pr * HashGetEntry(SyHash *pHash, const void *pKey, sxu32 nKeyLen)
  21474. {
  21475. SyHashEntry_Pr *pEntry;
  21476. sxu32 nHash;
  21477. nHash = pHash->xHash(pKey, nKeyLen);
  21478. pEntry = pHash->apBucket[nHash & (pHash->nBucketSize - 1)];
  21479. for(;;){
  21480. if( pEntry == 0 ){
  21481. break;
  21482. }
  21483. if( pEntry->nHash == nHash && pEntry->nKeyLen == nKeyLen &&
  21484. pHash->xCmp(pEntry->pKey, pKey, nKeyLen) == 0 ){
  21485. return pEntry;
  21486. }
  21487. pEntry = pEntry->pNextCollide;
  21488. }
  21489. /* Entry not found */
  21490. return 0;
  21491. }
  21492. JX9_PRIVATE SyHashEntry * SyHashGet(SyHash *pHash, const void *pKey, sxu32 nKeyLen)
  21493. {
  21494. SyHashEntry_Pr *pEntry;
  21495. #if defined(UNTRUST)
  21496. if( INVALID_HASH(pHash) ){
  21497. return 0;
  21498. }
  21499. #endif
  21500. if( pHash->nEntry < 1 || nKeyLen < 1 ){
  21501. /* Don't bother hashing, return immediately */
  21502. return 0;
  21503. }
  21504. pEntry = HashGetEntry(&(*pHash), pKey, nKeyLen);
  21505. if( pEntry == 0 ){
  21506. return 0;
  21507. }
  21508. return (SyHashEntry *)pEntry;
  21509. }
  21510. static sxi32 HashDeleteEntry(SyHash *pHash, SyHashEntry_Pr *pEntry, void **ppUserData)
  21511. {
  21512. sxi32 rc;
  21513. if( pEntry->pPrevCollide == 0 ){
  21514. pHash->apBucket[pEntry->nHash & (pHash->nBucketSize - 1)] = pEntry->pNextCollide;
  21515. }else{
  21516. pEntry->pPrevCollide->pNextCollide = pEntry->pNextCollide;
  21517. }
  21518. if( pEntry->pNextCollide ){
  21519. pEntry->pNextCollide->pPrevCollide = pEntry->pPrevCollide;
  21520. }
  21521. MACRO_LD_REMOVE(pHash->pList, pEntry);
  21522. pHash->nEntry--;
  21523. if( ppUserData ){
  21524. /* Write a pointer to the user data */
  21525. *ppUserData = pEntry->pUserData;
  21526. }
  21527. /* Release the entry */
  21528. rc = SyMemBackendPoolFree(pHash->pAllocator, pEntry);
  21529. return rc;
  21530. }
  21531. JX9_PRIVATE sxi32 SyHashDeleteEntry(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void **ppUserData)
  21532. {
  21533. SyHashEntry_Pr *pEntry;
  21534. sxi32 rc;
  21535. #if defined(UNTRUST)
  21536. if( INVALID_HASH(pHash) ){
  21537. return SXERR_CORRUPT;
  21538. }
  21539. #endif
  21540. pEntry = HashGetEntry(&(*pHash), pKey, nKeyLen);
  21541. if( pEntry == 0 ){
  21542. return SXERR_NOTFOUND;
  21543. }
  21544. rc = HashDeleteEntry(&(*pHash), pEntry, ppUserData);
  21545. return rc;
  21546. }
  21547. JX9_PRIVATE sxi32 SyHashForEach(SyHash *pHash, sxi32 (*xStep)(SyHashEntry *, void *), void *pUserData)
  21548. {
  21549. SyHashEntry_Pr *pEntry;
  21550. sxi32 rc;
  21551. sxu32 n;
  21552. #if defined(UNTRUST)
  21553. if( INVALID_HASH(pHash) || xStep == 0){
  21554. return 0;
  21555. }
  21556. #endif
  21557. pEntry = pHash->pList;
  21558. for( n = 0 ; n < pHash->nEntry ; n++ ){
  21559. /* Invoke the callback */
  21560. rc = xStep((SyHashEntry *)pEntry, pUserData);
  21561. if( rc != SXRET_OK ){
  21562. return rc;
  21563. }
  21564. /* Point to the next entry */
  21565. pEntry = pEntry->pNext;
  21566. }
  21567. return SXRET_OK;
  21568. }
  21569. static sxi32 HashGrowTable(SyHash *pHash)
  21570. {
  21571. sxu32 nNewSize = pHash->nBucketSize * 2;
  21572. SyHashEntry_Pr *pEntry;
  21573. SyHashEntry_Pr **apNew;
  21574. sxu32 n, iBucket;
  21575. /* Allocate a new larger table */
  21576. apNew = (SyHashEntry_Pr **)SyMemBackendAlloc(pHash->pAllocator, nNewSize * sizeof(SyHashEntry_Pr *));
  21577. if( apNew == 0 ){
  21578. /* Not so fatal, simply a performance hit */
  21579. return SXRET_OK;
  21580. }
  21581. /* Zero the new table */
  21582. SyZero((void *)apNew, nNewSize * sizeof(SyHashEntry_Pr *));
  21583. /* Rehash all entries */
  21584. for( n = 0, pEntry = pHash->pList; n < pHash->nEntry ; n++ ){
  21585. pEntry->pNextCollide = pEntry->pPrevCollide = 0;
  21586. /* Install in the new bucket */
  21587. iBucket = pEntry->nHash & (nNewSize - 1);
  21588. pEntry->pNextCollide = apNew[iBucket];
  21589. if( apNew[iBucket] != 0 ){
  21590. apNew[iBucket]->pPrevCollide = pEntry;
  21591. }
  21592. apNew[iBucket] = pEntry;
  21593. /* Point to the next entry */
  21594. pEntry = pEntry->pNext;
  21595. }
  21596. /* Release the old table and reflect the change */
  21597. SyMemBackendFree(pHash->pAllocator, (void *)pHash->apBucket);
  21598. pHash->apBucket = apNew;
  21599. pHash->nBucketSize = nNewSize;
  21600. return SXRET_OK;
  21601. }
  21602. static sxi32 HashInsert(SyHash *pHash, SyHashEntry_Pr *pEntry)
  21603. {
  21604. sxu32 iBucket = pEntry->nHash & (pHash->nBucketSize - 1);
  21605. /* Insert the entry in its corresponding bcuket */
  21606. pEntry->pNextCollide = pHash->apBucket[iBucket];
  21607. if( pHash->apBucket[iBucket] != 0 ){
  21608. pHash->apBucket[iBucket]->pPrevCollide = pEntry;
  21609. }
  21610. pHash->apBucket[iBucket] = pEntry;
  21611. /* Link to the entry list */
  21612. MACRO_LD_PUSH(pHash->pList, pEntry);
  21613. if( pHash->nEntry == 0 ){
  21614. pHash->pCurrent = pHash->pList;
  21615. }
  21616. pHash->nEntry++;
  21617. return SXRET_OK;
  21618. }
  21619. JX9_PRIVATE sxi32 SyHashInsert(SyHash *pHash, const void *pKey, sxu32 nKeyLen, void *pUserData)
  21620. {
  21621. SyHashEntry_Pr *pEntry;
  21622. sxi32 rc;
  21623. #if defined(UNTRUST)
  21624. if( INVALID_HASH(pHash) || pKey == 0 ){
  21625. return SXERR_CORRUPT;
  21626. }
  21627. #endif
  21628. if( pHash->nEntry >= pHash->nBucketSize * SXHASH_FILL_FACTOR ){
  21629. rc = HashGrowTable(&(*pHash));
  21630. if( rc != SXRET_OK ){
  21631. return rc;
  21632. }
  21633. }
  21634. /* Allocate a new hash entry */
  21635. pEntry = (SyHashEntry_Pr *)SyMemBackendPoolAlloc(pHash->pAllocator, sizeof(SyHashEntry_Pr));
  21636. if( pEntry == 0 ){
  21637. return SXERR_MEM;
  21638. }
  21639. /* Zero the entry */
  21640. SyZero(pEntry, sizeof(SyHashEntry_Pr));
  21641. pEntry->pHash = pHash;
  21642. pEntry->pKey = pKey;
  21643. pEntry->nKeyLen = nKeyLen;
  21644. pEntry->pUserData = pUserData;
  21645. pEntry->nHash = pHash->xHash(pEntry->pKey, pEntry->nKeyLen);
  21646. /* Finally insert the entry in its corresponding bucket */
  21647. rc = HashInsert(&(*pHash), pEntry);
  21648. return rc;
  21649. }
  21650. /* SyRunTimeApi:sxutils.c */
  21651. JX9_PRIVATE sxi32 SyStrIsNumeric(const char *zSrc, sxu32 nLen, sxu8 *pReal, const char **pzTail)
  21652. {
  21653. const char *zCur, *zEnd;
  21654. #ifdef UNTRUST
  21655. if( SX_EMPTY_STR(zSrc) ){
  21656. return SXERR_EMPTY;
  21657. }
  21658. #endif
  21659. zEnd = &zSrc[nLen];
  21660. /* Jump leading white spaces */
  21661. while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisSpace(zSrc[0]) ){
  21662. zSrc++;
  21663. }
  21664. if( zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-') ){
  21665. zSrc++;
  21666. }
  21667. zCur = zSrc;
  21668. if( pReal ){
  21669. *pReal = FALSE;
  21670. }
  21671. for(;;){
  21672. if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++;
  21673. if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++;
  21674. if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++;
  21675. if( zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0]) ){ break; } zSrc++;
  21676. };
  21677. if( zSrc < zEnd && zSrc > zCur ){
  21678. int c = zSrc[0];
  21679. if( c == '.' ){
  21680. zSrc++;
  21681. if( pReal ){
  21682. *pReal = TRUE;
  21683. }
  21684. if( pzTail ){
  21685. while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0]) ){
  21686. zSrc++;
  21687. }
  21688. if( zSrc < zEnd && (zSrc[0] == 'e' || zSrc[0] == 'E') ){
  21689. zSrc++;
  21690. if( zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-') ){
  21691. zSrc++;
  21692. }
  21693. while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0]) ){
  21694. zSrc++;
  21695. }
  21696. }
  21697. }
  21698. }else if( c == 'e' || c == 'E' ){
  21699. zSrc++;
  21700. if( pReal ){
  21701. *pReal = TRUE;
  21702. }
  21703. if( pzTail ){
  21704. if( zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-') ){
  21705. zSrc++;
  21706. }
  21707. while( zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0]) ){
  21708. zSrc++;
  21709. }
  21710. }
  21711. }
  21712. }
  21713. if( pzTail ){
  21714. /* Point to the non numeric part */
  21715. *pzTail = zSrc;
  21716. }
  21717. return zSrc > zCur ? SXRET_OK /* String prefix is numeric */ : SXERR_INVALID /* Not a digit stream */;
  21718. }
  21719. #define SXINT32_MIN_STR "2147483648"
  21720. #define SXINT32_MAX_STR "2147483647"
  21721. #define SXINT64_MIN_STR "9223372036854775808"
  21722. #define SXINT64_MAX_STR "9223372036854775807"
  21723. JX9_PRIVATE sxi32 SyStrToInt32(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
  21724. {
  21725. int isNeg = FALSE;
  21726. const char *zEnd;
  21727. sxi32 nVal = 0;
  21728. sxi16 i;
  21729. #if defined(UNTRUST)
  21730. if( SX_EMPTY_STR(zSrc) ){
  21731. if( pOutVal ){
  21732. *(sxi32 *)pOutVal = 0;
  21733. }
  21734. return SXERR_EMPTY;
  21735. }
  21736. #endif
  21737. zEnd = &zSrc[nLen];
  21738. while(zSrc < zEnd && SyisSpace(zSrc[0]) ){
  21739. zSrc++;
  21740. }
  21741. if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){
  21742. isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
  21743. zSrc++;
  21744. }
  21745. /* Skip leading zero */
  21746. while(zSrc < zEnd && zSrc[0] == '0' ){
  21747. zSrc++;
  21748. }
  21749. i = 10;
  21750. if( (sxu32)(zEnd-zSrc) >= 10 ){
  21751. /* Handle overflow */
  21752. i = SyMemcmp(zSrc, (isNeg == TRUE) ? SXINT32_MIN_STR : SXINT32_MAX_STR, nLen) <= 0 ? 10 : 9;
  21753. }
  21754. for(;;){
  21755. if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
  21756. if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
  21757. if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
  21758. if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
  21759. }
  21760. /* Skip trailing spaces */
  21761. while(zSrc < zEnd && SyisSpace(zSrc[0])){
  21762. zSrc++;
  21763. }
  21764. if( zRest ){
  21765. *zRest = (char *)zSrc;
  21766. }
  21767. if( pOutVal ){
  21768. if( isNeg == TRUE && nVal != 0 ){
  21769. nVal = -nVal;
  21770. }
  21771. *(sxi32 *)pOutVal = nVal;
  21772. }
  21773. return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
  21774. }
  21775. JX9_PRIVATE sxi32 SyStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
  21776. {
  21777. int isNeg = FALSE;
  21778. const char *zEnd;
  21779. sxi64 nVal;
  21780. sxi16 i;
  21781. #if defined(UNTRUST)
  21782. if( SX_EMPTY_STR(zSrc) ){
  21783. if( pOutVal ){
  21784. *(sxi32 *)pOutVal = 0;
  21785. }
  21786. return SXERR_EMPTY;
  21787. }
  21788. #endif
  21789. zEnd = &zSrc[nLen];
  21790. while(zSrc < zEnd && SyisSpace(zSrc[0]) ){
  21791. zSrc++;
  21792. }
  21793. if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){
  21794. isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
  21795. zSrc++;
  21796. }
  21797. /* Skip leading zero */
  21798. while(zSrc < zEnd && zSrc[0] == '0' ){
  21799. zSrc++;
  21800. }
  21801. i = 19;
  21802. if( (sxu32)(zEnd-zSrc) >= 19 ){
  21803. i = SyMemcmp(zSrc, isNeg ? SXINT64_MIN_STR : SXINT64_MAX_STR, 19) <= 0 ? 19 : 18 ;
  21804. }
  21805. nVal = 0;
  21806. for(;;){
  21807. if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
  21808. if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
  21809. if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
  21810. if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])){ break; } nVal = nVal * 10 + ( zSrc[0] - '0' ) ; --i ; zSrc++;
  21811. }
  21812. /* Skip trailing spaces */
  21813. while(zSrc < zEnd && SyisSpace(zSrc[0])){
  21814. zSrc++;
  21815. }
  21816. if( zRest ){
  21817. *zRest = (char *)zSrc;
  21818. }
  21819. if( pOutVal ){
  21820. if( isNeg == TRUE && nVal != 0 ){
  21821. nVal = -nVal;
  21822. }
  21823. *(sxi64 *)pOutVal = nVal;
  21824. }
  21825. return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
  21826. }
  21827. JX9_PRIVATE sxi32 SyHexToint(sxi32 c)
  21828. {
  21829. switch(c){
  21830. case '0': return 0;
  21831. case '1': return 1;
  21832. case '2': return 2;
  21833. case '3': return 3;
  21834. case '4': return 4;
  21835. case '5': return 5;
  21836. case '6': return 6;
  21837. case '7': return 7;
  21838. case '8': return 8;
  21839. case '9': return 9;
  21840. case 'A': case 'a': return 10;
  21841. case 'B': case 'b': return 11;
  21842. case 'C': case 'c': return 12;
  21843. case 'D': case 'd': return 13;
  21844. case 'E': case 'e': return 14;
  21845. case 'F': case 'f': return 15;
  21846. }
  21847. return -1;
  21848. }
  21849. JX9_PRIVATE sxi32 SyHexStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
  21850. {
  21851. const char *zIn, *zEnd;
  21852. int isNeg = FALSE;
  21853. sxi64 nVal = 0;
  21854. #if defined(UNTRUST)
  21855. if( SX_EMPTY_STR(zSrc) ){
  21856. if( pOutVal ){
  21857. *(sxi32 *)pOutVal = 0;
  21858. }
  21859. return SXERR_EMPTY;
  21860. }
  21861. #endif
  21862. zEnd = &zSrc[nLen];
  21863. while( zSrc < zEnd && SyisSpace(zSrc[0]) ){
  21864. zSrc++;
  21865. }
  21866. if( zSrc < zEnd && ( *zSrc == '-' || *zSrc == '+' ) ){
  21867. isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
  21868. zSrc++;
  21869. }
  21870. if( zSrc < &zEnd[-2] && zSrc[0] == '0' && (zSrc[1] == 'x' || zSrc[1] == 'X') ){
  21871. /* Bypass hex prefix */
  21872. zSrc += sizeof(char) * 2;
  21873. }
  21874. /* Skip leading zero */
  21875. while(zSrc < zEnd && zSrc[0] == '0' ){
  21876. zSrc++;
  21877. }
  21878. zIn = zSrc;
  21879. for(;;){
  21880. if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]); zSrc++ ;
  21881. if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]); zSrc++ ;
  21882. if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]); zSrc++ ;
  21883. if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc-zIn) > 15) break; nVal = nVal * 16 + SyHexToint(zSrc[0]); zSrc++ ;
  21884. }
  21885. while( zSrc < zEnd && SyisSpace(zSrc[0]) ){
  21886. zSrc++;
  21887. }
  21888. if( zRest ){
  21889. *zRest = zSrc;
  21890. }
  21891. if( pOutVal ){
  21892. if( isNeg == TRUE && nVal != 0 ){
  21893. nVal = -nVal;
  21894. }
  21895. *(sxi64 *)pOutVal = nVal;
  21896. }
  21897. return zSrc >= zEnd ? SXRET_OK : SXERR_SYNTAX;
  21898. }
  21899. JX9_PRIVATE sxi32 SyOctalStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
  21900. {
  21901. const char *zIn, *zEnd;
  21902. int isNeg = FALSE;
  21903. sxi64 nVal = 0;
  21904. int c;
  21905. #if defined(UNTRUST)
  21906. if( SX_EMPTY_STR(zSrc) ){
  21907. if( pOutVal ){
  21908. *(sxi32 *)pOutVal = 0;
  21909. }
  21910. return SXERR_EMPTY;
  21911. }
  21912. #endif
  21913. zEnd = &zSrc[nLen];
  21914. while(zSrc < zEnd && SyisSpace(zSrc[0]) ){
  21915. zSrc++;
  21916. }
  21917. if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){
  21918. isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
  21919. zSrc++;
  21920. }
  21921. /* Skip leading zero */
  21922. while(zSrc < zEnd && zSrc[0] == '0' ){
  21923. zSrc++;
  21924. }
  21925. zIn = zSrc;
  21926. for(;;){
  21927. if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 + c; zSrc++;
  21928. if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 + c; zSrc++;
  21929. if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 + c; zSrc++;
  21930. if(zSrc >= zEnd || !SyisDigit(zSrc[0])){ break; } if( (c=zSrc[0]-'0') > 7 || (int)(zSrc-zIn) > 20){ break;} nVal = nVal * 8 + c; zSrc++;
  21931. }
  21932. /* Skip trailing spaces */
  21933. while(zSrc < zEnd && SyisSpace(zSrc[0])){
  21934. zSrc++;
  21935. }
  21936. if( zRest ){
  21937. *zRest = zSrc;
  21938. }
  21939. if( pOutVal ){
  21940. if( isNeg == TRUE && nVal != 0 ){
  21941. nVal = -nVal;
  21942. }
  21943. *(sxi64 *)pOutVal = nVal;
  21944. }
  21945. return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
  21946. }
  21947. JX9_PRIVATE sxi32 SyBinaryStrToInt64(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
  21948. {
  21949. const char *zIn, *zEnd;
  21950. int isNeg = FALSE;
  21951. sxi64 nVal = 0;
  21952. int c;
  21953. #if defined(UNTRUST)
  21954. if( SX_EMPTY_STR(zSrc) ){
  21955. if( pOutVal ){
  21956. *(sxi32 *)pOutVal = 0;
  21957. }
  21958. return SXERR_EMPTY;
  21959. }
  21960. #endif
  21961. zEnd = &zSrc[nLen];
  21962. while(zSrc < zEnd && SyisSpace(zSrc[0]) ){
  21963. zSrc++;
  21964. }
  21965. if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+' ) ){
  21966. isNeg = (zSrc[0] == '-') ? TRUE :FALSE;
  21967. zSrc++;
  21968. }
  21969. if( zSrc < &zEnd[-2] && zSrc[0] == '0' && (zSrc[1] == 'b' || zSrc[1] == 'B') ){
  21970. /* Bypass binary prefix */
  21971. zSrc += sizeof(char) * 2;
  21972. }
  21973. /* Skip leading zero */
  21974. while(zSrc < zEnd && zSrc[0] == '0' ){
  21975. zSrc++;
  21976. }
  21977. zIn = zSrc;
  21978. for(;;){
  21979. if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++;
  21980. if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++;
  21981. if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++;
  21982. if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc-zIn) > 62){ break; } c = zSrc[0] - '0'; nVal = (nVal << 1) + c; zSrc++;
  21983. }
  21984. /* Skip trailing spaces */
  21985. while(zSrc < zEnd && SyisSpace(zSrc[0])){
  21986. zSrc++;
  21987. }
  21988. if( zRest ){
  21989. *zRest = zSrc;
  21990. }
  21991. if( pOutVal ){
  21992. if( isNeg == TRUE && nVal != 0 ){
  21993. nVal = -nVal;
  21994. }
  21995. *(sxi64 *)pOutVal = nVal;
  21996. }
  21997. return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
  21998. }
  21999. JX9_PRIVATE sxi32 SyStrToReal(const char *zSrc, sxu32 nLen, void * pOutVal, const char **zRest)
  22000. {
  22001. #define SXDBL_DIG 15
  22002. #define SXDBL_MAX_EXP 308
  22003. #define SXDBL_MIN_EXP_PLUS 307
  22004. static const sxreal aTab[] = {
  22005. 10,
  22006. 1.0e2,
  22007. 1.0e4,
  22008. 1.0e8,
  22009. 1.0e16,
  22010. 1.0e32,
  22011. 1.0e64,
  22012. 1.0e128,
  22013. 1.0e256
  22014. };
  22015. sxu8 neg = FALSE;
  22016. sxreal Val = 0.0;
  22017. const char *zEnd;
  22018. sxi32 Lim, exp;
  22019. sxreal *p = 0;
  22020. #ifdef UNTRUST
  22021. if( SX_EMPTY_STR(zSrc) ){
  22022. if( pOutVal ){
  22023. *(sxreal *)pOutVal = 0.0;
  22024. }
  22025. return SXERR_EMPTY;
  22026. }
  22027. #endif
  22028. zEnd = &zSrc[nLen];
  22029. while( zSrc < zEnd && SyisSpace(zSrc[0]) ){
  22030. zSrc++;
  22031. }
  22032. if( zSrc < zEnd && (zSrc[0] == '-' || zSrc[0] == '+' ) ){
  22033. neg = zSrc[0] == '-' ? TRUE : FALSE ;
  22034. zSrc++;
  22035. }
  22036. Lim = SXDBL_DIG ;
  22037. for(;;){
  22038. if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim;
  22039. if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim;
  22040. if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim;
  22041. if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; zSrc++ ; --Lim;
  22042. }
  22043. if( zSrc < zEnd && ( zSrc[0] == '.' || zSrc[0] == ',' ) ){
  22044. sxreal dec = 1.0;
  22045. zSrc++;
  22046. for(;;){
  22047. if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim;
  22048. if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim;
  22049. if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim;
  22050. if(zSrc >= zEnd||!Lim||!SyisDigit(zSrc[0])) break ; Val = Val * 10.0 + (zSrc[0] - '0') ; dec *= 10.0; zSrc++ ;--Lim;
  22051. }
  22052. Val /= dec;
  22053. }
  22054. if( neg == TRUE && Val != 0.0 ) {
  22055. Val = -Val ;
  22056. }
  22057. if( Lim <= 0 ){
  22058. /* jump overflow digit */
  22059. while( zSrc < zEnd ){
  22060. if( zSrc[0] == 'e' || zSrc[0] == 'E' ){
  22061. break;
  22062. }
  22063. zSrc++;
  22064. }
  22065. }
  22066. neg = FALSE;
  22067. if( zSrc < zEnd && ( zSrc[0] == 'e' || zSrc[0] == 'E' ) ){
  22068. zSrc++;
  22069. if( zSrc < zEnd && ( zSrc[0] == '-' || zSrc[0] == '+') ){
  22070. neg = zSrc[0] == '-' ? TRUE : FALSE ;
  22071. zSrc++;
  22072. }
  22073. exp = 0;
  22074. while( zSrc < zEnd && SyisDigit(zSrc[0]) && exp < SXDBL_MAX_EXP ){
  22075. exp = exp * 10 + (zSrc[0] - '0');
  22076. zSrc++;
  22077. }
  22078. if( neg ){
  22079. if( exp > SXDBL_MIN_EXP_PLUS ) exp = SXDBL_MIN_EXP_PLUS ;
  22080. }else if ( exp > SXDBL_MAX_EXP ){
  22081. exp = SXDBL_MAX_EXP;
  22082. }
  22083. for( p = (sxreal *)aTab ; exp ; exp >>= 1 , p++ ){
  22084. if( exp & 01 ){
  22085. if( neg ){
  22086. Val /= *p ;
  22087. }else{
  22088. Val *= *p;
  22089. }
  22090. }
  22091. }
  22092. }
  22093. while( zSrc < zEnd && SyisSpace(zSrc[0]) ){
  22094. zSrc++;
  22095. }
  22096. if( zRest ){
  22097. *zRest = zSrc;
  22098. }
  22099. if( pOutVal ){
  22100. *(sxreal *)pOutVal = Val;
  22101. }
  22102. return zSrc >= zEnd ? SXRET_OK : SXERR_SYNTAX;
  22103. }
  22104. /* SyRunTimeApi:sxlib.c */
  22105. static sxu32 SyBinHash(const void *pSrc, sxu32 nLen)
  22106. {
  22107. register unsigned char *zIn = (unsigned char *)pSrc;
  22108. unsigned char *zEnd;
  22109. sxu32 nH = 5381;
  22110. zEnd = &zIn[nLen];
  22111. for(;;){
  22112. if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
  22113. if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
  22114. if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
  22115. if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
  22116. }
  22117. return nH;
  22118. }
  22119. #ifndef JX9_DISABLE_BUILTIN_FUNC
  22120. JX9_PRIVATE sxi32 SyBase64Encode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData)
  22121. {
  22122. static const unsigned char zBase64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  22123. unsigned char *zIn = (unsigned char *)zSrc;
  22124. unsigned char z64[4];
  22125. sxu32 i;
  22126. sxi32 rc;
  22127. #if defined(UNTRUST)
  22128. if( SX_EMPTY_STR(zSrc) || xConsumer == 0){
  22129. return SXERR_EMPTY;
  22130. }
  22131. #endif
  22132. for(i = 0; i + 2 < nLen; i += 3){
  22133. z64[0] = zBase64[(zIn[i] >> 2) & 0x3F];
  22134. z64[1] = zBase64[( ((zIn[i] & 0x03) << 4) | (zIn[i+1] >> 4)) & 0x3F];
  22135. z64[2] = zBase64[( ((zIn[i+1] & 0x0F) << 2) | (zIn[i + 2] >> 6) ) & 0x3F];
  22136. z64[3] = zBase64[ zIn[i + 2] & 0x3F];
  22137. rc = xConsumer((const void *)z64, sizeof(z64), pUserData);
  22138. if( rc != SXRET_OK ){return SXERR_ABORT;}
  22139. }
  22140. if ( i+1 < nLen ){
  22141. z64[0] = zBase64[(zIn[i] >> 2) & 0x3F];
  22142. z64[1] = zBase64[( ((zIn[i] & 0x03) << 4) | (zIn[i+1] >> 4)) & 0x3F];
  22143. z64[2] = zBase64[(zIn[i+1] & 0x0F) << 2 ];
  22144. z64[3] = '=';
  22145. rc = xConsumer((const void *)z64, sizeof(z64), pUserData);
  22146. if( rc != SXRET_OK ){return SXERR_ABORT;}
  22147. }else if( i < nLen ){
  22148. z64[0] = zBase64[(zIn[i] >> 2) & 0x3F];
  22149. z64[1] = zBase64[(zIn[i] & 0x03) << 4];
  22150. z64[2] = '=';
  22151. z64[3] = '=';
  22152. rc = xConsumer((const void *)z64, sizeof(z64), pUserData);
  22153. if( rc != SXRET_OK ){return SXERR_ABORT;}
  22154. }
  22155. return SXRET_OK;
  22156. }
  22157. JX9_PRIVATE sxi32 SyBase64Decode(const char *zB64, sxu32 nLen, ProcConsumer xConsumer, void *pUserData)
  22158. {
  22159. static const sxu32 aBase64Trans[] = {
  22160. 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,
  22161. 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,
  22162. 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,
  22163. 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,
  22164. 0, 0, 0
  22165. };
  22166. sxu32 n, w, x, y, z;
  22167. sxi32 rc;
  22168. unsigned char zOut[10];
  22169. #if defined(UNTRUST)
  22170. if( SX_EMPTY_STR(zB64) || xConsumer == 0 ){
  22171. return SXERR_EMPTY;
  22172. }
  22173. #endif
  22174. while(nLen > 0 && zB64[nLen - 1] == '=' ){
  22175. nLen--;
  22176. }
  22177. for( n = 0 ; n+3<nLen ; n += 4){
  22178. w = aBase64Trans[zB64[n] & 0x7F];
  22179. x = aBase64Trans[zB64[n+1] & 0x7F];
  22180. y = aBase64Trans[zB64[n+2] & 0x7F];
  22181. z = aBase64Trans[zB64[n+3] & 0x7F];
  22182. zOut[0] = ((w<<2) & 0xFC) | ((x>>4) & 0x03);
  22183. zOut[1] = ((x<<4) & 0xF0) | ((y>>2) & 0x0F);
  22184. zOut[2] = ((y<<6) & 0xC0) | (z & 0x3F);
  22185. rc = xConsumer((const void *)zOut, sizeof(unsigned char)*3, pUserData);
  22186. if( rc != SXRET_OK ){ return SXERR_ABORT;}
  22187. }
  22188. if( n+2 < nLen ){
  22189. w = aBase64Trans[zB64[n] & 0x7F];
  22190. x = aBase64Trans[zB64[n+1] & 0x7F];
  22191. y = aBase64Trans[zB64[n+2] & 0x7F];
  22192. zOut[0] = ((w<<2) & 0xFC) | ((x>>4) & 0x03);
  22193. zOut[1] = ((x<<4) & 0xF0) | ((y>>2) & 0x0F);
  22194. rc = xConsumer((const void *)zOut, sizeof(unsigned char)*2, pUserData);
  22195. if( rc != SXRET_OK ){ return SXERR_ABORT;}
  22196. }else if( n+1 < nLen ){
  22197. w = aBase64Trans[zB64[n] & 0x7F];
  22198. x = aBase64Trans[zB64[n+1] & 0x7F];
  22199. zOut[0] = ((w<<2) & 0xFC) | ((x>>4) & 0x03);
  22200. rc = xConsumer((const void *)zOut, sizeof(unsigned char)*1, pUserData);
  22201. if( rc != SXRET_OK ){ return SXERR_ABORT;}
  22202. }
  22203. return SXRET_OK;
  22204. }
  22205. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  22206. #define INVALID_LEXER(LEX) ( LEX == 0 || LEX->xTokenizer == 0 )
  22207. JX9_PRIVATE sxi32 SyLexInit(SyLex *pLex, SySet *pSet, ProcTokenizer xTokenizer, void *pUserData)
  22208. {
  22209. SyStream *pStream;
  22210. #if defined (UNTRUST)
  22211. if ( pLex == 0 || xTokenizer == 0 ){
  22212. return SXERR_CORRUPT;
  22213. }
  22214. #endif
  22215. pLex->pTokenSet = 0;
  22216. /* Initialize lexer fields */
  22217. if( pSet ){
  22218. if ( SySetElemSize(pSet) != sizeof(SyToken) ){
  22219. return SXERR_INVALID;
  22220. }
  22221. pLex->pTokenSet = pSet;
  22222. }
  22223. pStream = &pLex->sStream;
  22224. pLex->xTokenizer = xTokenizer;
  22225. pLex->pUserData = pUserData;
  22226. pStream->nLine = 1;
  22227. pStream->nIgn = 0;
  22228. pStream->zText = pStream->zEnd = 0;
  22229. pStream->pSet = pSet;
  22230. return SXRET_OK;
  22231. }
  22232. JX9_PRIVATE sxi32 SyLexTokenizeInput(SyLex *pLex, const char *zInput, sxu32 nLen, void *pCtxData, ProcSort xSort, ProcCmp xCmp)
  22233. {
  22234. const unsigned char *zCur;
  22235. SyStream *pStream;
  22236. SyToken sToken;
  22237. sxi32 rc;
  22238. #if defined (UNTRUST)
  22239. if ( INVALID_LEXER(pLex) || zInput == 0 ){
  22240. return SXERR_CORRUPT;
  22241. }
  22242. #endif
  22243. pStream = &pLex->sStream;
  22244. /* Point to the head of the input */
  22245. pStream->zText = pStream->zInput = (const unsigned char *)zInput;
  22246. /* Point to the end of the input */
  22247. pStream->zEnd = &pStream->zInput[nLen];
  22248. for(;;){
  22249. if( pStream->zText >= pStream->zEnd ){
  22250. /* End of the input reached */
  22251. break;
  22252. }
  22253. zCur = pStream->zText;
  22254. /* Call the tokenizer callback */
  22255. rc = pLex->xTokenizer(pStream, &sToken, pLex->pUserData, pCtxData);
  22256. if( rc != SXRET_OK && rc != SXERR_CONTINUE ){
  22257. /* Tokenizer callback request an operation abort */
  22258. if( rc == SXERR_ABORT ){
  22259. return SXERR_ABORT;
  22260. }
  22261. break;
  22262. }
  22263. if( rc == SXERR_CONTINUE ){
  22264. /* Request to ignore this token */
  22265. pStream->nIgn++;
  22266. }else if( pLex->pTokenSet ){
  22267. /* Put the token in the set */
  22268. rc = SySetPut(pLex->pTokenSet, (const void *)&sToken);
  22269. if( rc != SXRET_OK ){
  22270. break;
  22271. }
  22272. }
  22273. if( zCur >= pStream->zText ){
  22274. /* Automatic advance of the stream cursor */
  22275. pStream->zText = &zCur[1];
  22276. }
  22277. }
  22278. if( xSort && pLex->pTokenSet ){
  22279. SyToken *aToken = (SyToken *)SySetBasePtr(pLex->pTokenSet);
  22280. /* Sort the extrated tokens */
  22281. if( xCmp == 0 ){
  22282. /* Use a default comparison function */
  22283. xCmp = SyMemcmp;
  22284. }
  22285. xSort(aToken, SySetUsed(pLex->pTokenSet), sizeof(SyToken), xCmp);
  22286. }
  22287. return SXRET_OK;
  22288. }
  22289. JX9_PRIVATE sxi32 SyLexRelease(SyLex *pLex)
  22290. {
  22291. sxi32 rc = SXRET_OK;
  22292. #if defined (UNTRUST)
  22293. if ( INVALID_LEXER(pLex) ){
  22294. return SXERR_CORRUPT;
  22295. }
  22296. #else
  22297. SXUNUSED(pLex); /* Prevent compiler warning */
  22298. #endif
  22299. return rc;
  22300. }
  22301. #ifndef JX9_DISABLE_BUILTIN_FUNC
  22302. #define SAFE_HTTP(C) (SyisAlphaNum(c) || c == '_' || c == '-' || c == '$' || c == '.' )
  22303. JX9_PRIVATE sxi32 SyUriEncode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData)
  22304. {
  22305. unsigned char *zIn = (unsigned char *)zSrc;
  22306. unsigned char zHex[3] = { '%', 0, 0 };
  22307. unsigned char zOut[2];
  22308. unsigned char *zCur, *zEnd;
  22309. sxi32 c;
  22310. sxi32 rc;
  22311. #ifdef UNTRUST
  22312. if( SX_EMPTY_STR(zSrc) || xConsumer == 0 ){
  22313. return SXERR_EMPTY;
  22314. }
  22315. #endif
  22316. rc = SXRET_OK;
  22317. zEnd = &zIn[nLen]; zCur = zIn;
  22318. for(;;){
  22319. if( zCur >= zEnd ){
  22320. if( zCur != zIn ){
  22321. rc = xConsumer(zIn, (sxu32)(zCur-zIn), pUserData);
  22322. }
  22323. break;
  22324. }
  22325. c = zCur[0];
  22326. if( SAFE_HTTP(c) ){
  22327. zCur++; continue;
  22328. }
  22329. if( zCur != zIn && SXRET_OK != (rc = xConsumer(zIn, (sxu32)(zCur-zIn), pUserData))){
  22330. break;
  22331. }
  22332. if( c == ' ' ){
  22333. zOut[0] = '+';
  22334. rc = xConsumer((const void *)zOut, sizeof(unsigned char), pUserData);
  22335. }else{
  22336. zHex[1] = "0123456789ABCDEF"[(c >> 4) & 0x0F];
  22337. zHex[2] = "0123456789ABCDEF"[c & 0x0F];
  22338. rc = xConsumer(zHex, sizeof(zHex), pUserData);
  22339. }
  22340. if( SXRET_OK != rc ){
  22341. break;
  22342. }
  22343. zIn = &zCur[1]; zCur = zIn ;
  22344. }
  22345. return rc == SXRET_OK ? SXRET_OK : SXERR_ABORT;
  22346. }
  22347. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  22348. static sxi32 SyAsciiToHex(sxi32 c)
  22349. {
  22350. if( c >= 'a' && c <= 'f' ){
  22351. c += 10 - 'a';
  22352. return c;
  22353. }
  22354. if( c >= '0' && c <= '9' ){
  22355. c -= '0';
  22356. return c;
  22357. }
  22358. if( c >= 'A' && c <= 'F') {
  22359. c += 10 - 'A';
  22360. return c;
  22361. }
  22362. return 0;
  22363. }
  22364. JX9_PRIVATE sxi32 SyUriDecode(const char *zSrc, sxu32 nLen, ProcConsumer xConsumer, void *pUserData, int bUTF8)
  22365. {
  22366. static const sxu8 Utf8Trans[] = {
  22367. 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
  22368. 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
  22369. 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
  22370. 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
  22371. 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
  22372. 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
  22373. 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
  22374. 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00
  22375. };
  22376. const char *zIn = zSrc;
  22377. const char *zEnd;
  22378. const char *zCur;
  22379. sxu8 *zOutPtr;
  22380. sxu8 zOut[10];
  22381. sxi32 c, d;
  22382. sxi32 rc;
  22383. #if defined(UNTRUST)
  22384. if( SX_EMPTY_STR(zSrc) || xConsumer == 0 ){
  22385. return SXERR_EMPTY;
  22386. }
  22387. #endif
  22388. rc = SXRET_OK;
  22389. zEnd = &zSrc[nLen];
  22390. zCur = zIn;
  22391. for(;;){
  22392. while(zCur < zEnd && zCur[0] != '%' && zCur[0] != '+' ){
  22393. zCur++;
  22394. }
  22395. if( zCur != zIn ){
  22396. /* Consume input */
  22397. rc = xConsumer(zIn, (unsigned int)(zCur-zIn), pUserData);
  22398. if( rc != SXRET_OK ){
  22399. /* User consumer routine request an operation abort */
  22400. break;
  22401. }
  22402. }
  22403. if( zCur >= zEnd ){
  22404. rc = SXRET_OK;
  22405. break;
  22406. }
  22407. /* Decode unsafe HTTP characters */
  22408. zOutPtr = zOut;
  22409. if( zCur[0] == '+' ){
  22410. *zOutPtr++ = ' ';
  22411. zCur++;
  22412. }else{
  22413. if( &zCur[2] >= zEnd ){
  22414. rc = SXERR_OVERFLOW;
  22415. break;
  22416. }
  22417. c = (SyAsciiToHex(zCur[1]) <<4) | SyAsciiToHex(zCur[2]);
  22418. zCur += 3;
  22419. if( c < 0x000C0 ){
  22420. *zOutPtr++ = (sxu8)c;
  22421. }else{
  22422. c = Utf8Trans[c-0xC0];
  22423. while( zCur[0] == '%' ){
  22424. d = (SyAsciiToHex(zCur[1]) <<4) | SyAsciiToHex(zCur[2]);
  22425. if( (d&0xC0) != 0x80 ){
  22426. break;
  22427. }
  22428. c = (c<<6) + (0x3f & d);
  22429. zCur += 3;
  22430. }
  22431. if( bUTF8 == FALSE ){
  22432. *zOutPtr++ = (sxu8)c;
  22433. }else{
  22434. SX_WRITE_UTF8(zOutPtr, c);
  22435. }
  22436. }
  22437. }
  22438. /* Consume the decoded characters */
  22439. rc = xConsumer((const void *)zOut, (unsigned int)(zOutPtr-zOut), pUserData);
  22440. if( rc != SXRET_OK ){
  22441. break;
  22442. }
  22443. /* Synchronize pointers */
  22444. zIn = zCur;
  22445. }
  22446. return rc;
  22447. }
  22448. #ifndef JX9_DISABLE_BUILTIN_FUNC
  22449. static const char *zEngDay[] = {
  22450. "Sunday", "Monday", "Tuesday", "Wednesday",
  22451. "Thursday", "Friday", "Saturday"
  22452. };
  22453. static const char *zEngMonth[] = {
  22454. "January", "February", "March", "April",
  22455. "May", "June", "July", "August",
  22456. "September", "October", "November", "December"
  22457. };
  22458. static const char * GetDay(sxi32 i)
  22459. {
  22460. return zEngDay[ i % 7 ];
  22461. }
  22462. static const char * GetMonth(sxi32 i)
  22463. {
  22464. return zEngMonth[ i % 12 ];
  22465. }
  22466. JX9_PRIVATE const char * SyTimeGetDay(sxi32 iDay)
  22467. {
  22468. return GetDay(iDay);
  22469. }
  22470. JX9_PRIVATE const char * SyTimeGetMonth(sxi32 iMonth)
  22471. {
  22472. return GetMonth(iMonth);
  22473. }
  22474. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  22475. /* SyRunTimeApi: sxfmt.c */
  22476. #define SXFMT_BUFSIZ 1024 /* Conversion buffer size */
  22477. /*
  22478. ** Conversion types fall into various categories as defined by the
  22479. ** following enumeration.
  22480. */
  22481. #define SXFMT_RADIX 1 /* Integer types.%d, %x, %o, and so forth */
  22482. #define SXFMT_FLOAT 2 /* Floating point.%f */
  22483. #define SXFMT_EXP 3 /* Exponentional notation.%e and %E */
  22484. #define SXFMT_GENERIC 4 /* Floating or exponential, depending on exponent.%g */
  22485. #define SXFMT_SIZE 5 /* Total number of characters processed so far.%n */
  22486. #define SXFMT_STRING 6 /* Strings.%s */
  22487. #define SXFMT_PERCENT 7 /* Percent symbol.%% */
  22488. #define SXFMT_CHARX 8 /* Characters.%c */
  22489. #define SXFMT_ERROR 9 /* Used to indicate no such conversion type */
  22490. /* Extension by Symisc Systems */
  22491. #define SXFMT_RAWSTR 13 /* %z Pointer to raw string (SyString *) */
  22492. #define SXFMT_UNUSED 15
  22493. /*
  22494. ** Allowed values for SyFmtInfo.flags
  22495. */
  22496. #define SXFLAG_SIGNED 0x01
  22497. #define SXFLAG_UNSIGNED 0x02
  22498. /* Allowed values for SyFmtConsumer.nType */
  22499. #define SXFMT_CONS_PROC 1 /* Consumer is a procedure */
  22500. #define SXFMT_CONS_STR 2 /* Consumer is a managed string */
  22501. #define SXFMT_CONS_FILE 5 /* Consumer is an open File */
  22502. #define SXFMT_CONS_BLOB 6 /* Consumer is a BLOB */
  22503. /*
  22504. ** Each builtin conversion character (ex: the 'd' in "%d") is described
  22505. ** by an instance of the following structure
  22506. */
  22507. typedef struct SyFmtInfo SyFmtInfo;
  22508. struct SyFmtInfo
  22509. {
  22510. char fmttype; /* The format field code letter [i.e: 'd', 's', 'x'] */
  22511. sxu8 base; /* The base for radix conversion */
  22512. int flags; /* One or more of SXFLAG_ constants below */
  22513. sxu8 type; /* Conversion paradigm */
  22514. char *charset; /* The character set for conversion */
  22515. char *prefix; /* Prefix on non-zero values in alt format */
  22516. };
  22517. typedef struct SyFmtConsumer SyFmtConsumer;
  22518. struct SyFmtConsumer
  22519. {
  22520. sxu32 nLen; /* Total output length */
  22521. sxi32 nType; /* Type of the consumer see below */
  22522. sxi32 rc; /* Consumer return value;Abort processing if rc != SXRET_OK */
  22523. union{
  22524. struct{
  22525. ProcConsumer xUserConsumer;
  22526. void *pUserData;
  22527. }sFunc;
  22528. SyBlob *pBlob;
  22529. }uConsumer;
  22530. };
  22531. #ifndef SX_OMIT_FLOATINGPOINT
  22532. static int getdigit(sxlongreal *val, int *cnt)
  22533. {
  22534. sxlongreal d;
  22535. int digit;
  22536. if( (*cnt)++ >= 16 ){
  22537. return '0';
  22538. }
  22539. digit = (int)*val;
  22540. d = digit;
  22541. *val = (*val - d)*10.0;
  22542. return digit + '0' ;
  22543. }
  22544. #endif /* SX_OMIT_FLOATINGPOINT */
  22545. /*
  22546. * The following routine was taken from the SQLITE2 source tree and was
  22547. * extended by Symisc Systems to fit its need.
  22548. * Status: Public Domain
  22549. */
  22550. static sxi32 InternFormat(ProcConsumer xConsumer, void *pUserData, const char *zFormat, va_list ap)
  22551. {
  22552. /*
  22553. * The following table is searched linearly, so it is good to put the most frequently
  22554. * used conversion types first.
  22555. */
  22556. static const SyFmtInfo aFmt[] = {
  22557. { 'd', 10, SXFLAG_SIGNED, SXFMT_RADIX, "0123456789", 0 },
  22558. { 's', 0, 0, SXFMT_STRING, 0, 0 },
  22559. { 'c', 0, 0, SXFMT_CHARX, 0, 0 },
  22560. { 'x', 16, 0, SXFMT_RADIX, "0123456789abcdef", "x0" },
  22561. { 'X', 16, 0, SXFMT_RADIX, "0123456789ABCDEF", "X0" },
  22562. /* -- Extensions by Symisc Systems -- */
  22563. { 'z', 0, 0, SXFMT_RAWSTR, 0, 0 }, /* Pointer to a raw string (SyString *) */
  22564. { 'B', 2, 0, SXFMT_RADIX, "01", "b0"},
  22565. /* -- End of Extensions -- */
  22566. { 'o', 8, 0, SXFMT_RADIX, "01234567", "0" },
  22567. { 'u', 10, 0, SXFMT_RADIX, "0123456789", 0 },
  22568. #ifndef SX_OMIT_FLOATINGPOINT
  22569. { 'f', 0, SXFLAG_SIGNED, SXFMT_FLOAT, 0, 0 },
  22570. { 'e', 0, SXFLAG_SIGNED, SXFMT_EXP, "e", 0 },
  22571. { 'E', 0, SXFLAG_SIGNED, SXFMT_EXP, "E", 0 },
  22572. { 'g', 0, SXFLAG_SIGNED, SXFMT_GENERIC, "e", 0 },
  22573. { 'G', 0, SXFLAG_SIGNED, SXFMT_GENERIC, "E", 0 },
  22574. #endif
  22575. { 'i', 10, SXFLAG_SIGNED, SXFMT_RADIX, "0123456789", 0 },
  22576. { 'n', 0, 0, SXFMT_SIZE, 0, 0 },
  22577. { '%', 0, 0, SXFMT_PERCENT, 0, 0 },
  22578. { 'p', 10, 0, SXFMT_RADIX, "0123456789", 0 }
  22579. };
  22580. int c; /* Next character in the format string */
  22581. char *bufpt; /* Pointer to the conversion buffer */
  22582. int precision; /* Precision of the current field */
  22583. int length; /* Length of the field */
  22584. int idx; /* A general purpose loop counter */
  22585. int width; /* Width of the current field */
  22586. sxu8 flag_leftjustify; /* True if "-" flag is present */
  22587. sxu8 flag_plussign; /* True if "+" flag is present */
  22588. sxu8 flag_blanksign; /* True if " " flag is present */
  22589. sxu8 flag_alternateform; /* True if "#" flag is present */
  22590. sxu8 flag_zeropad; /* True if field width constant starts with zero */
  22591. sxu8 flag_long; /* True if "l" flag is present */
  22592. sxi64 longvalue; /* Value for integer types */
  22593. const SyFmtInfo *infop; /* Pointer to the appropriate info structure */
  22594. char buf[SXFMT_BUFSIZ]; /* Conversion buffer */
  22595. char prefix; /* Prefix character."+" or "-" or " " or '\0'.*/
  22596. sxu8 errorflag = 0; /* True if an error is encountered */
  22597. sxu8 xtype; /* Conversion paradigm */
  22598. char *zExtra;
  22599. static char spaces[] = " ";
  22600. #define etSPACESIZE ((int)sizeof(spaces)-1)
  22601. #ifndef SX_OMIT_FLOATINGPOINT
  22602. sxlongreal realvalue; /* Value for real types */
  22603. int exp; /* exponent of real numbers */
  22604. double rounder; /* Used for rounding floating point values */
  22605. sxu8 flag_dp; /* True if decimal point should be shown */
  22606. sxu8 flag_rtz; /* True if trailing zeros should be removed */
  22607. sxu8 flag_exp; /* True to force display of the exponent */
  22608. int nsd; /* Number of significant digits returned */
  22609. #endif
  22610. int rc;
  22611. length = 0;
  22612. bufpt = 0;
  22613. for(; (c=(*zFormat))!=0; ++zFormat){
  22614. if( c!='%' ){
  22615. unsigned int amt;
  22616. bufpt = (char *)zFormat;
  22617. amt = 1;
  22618. while( (c=(*++zFormat))!='%' && c!=0 ) amt++;
  22619. rc = xConsumer((const void *)bufpt, amt, pUserData);
  22620. if( rc != SXRET_OK ){
  22621. return SXERR_ABORT; /* Consumer routine request an operation abort */
  22622. }
  22623. if( c==0 ){
  22624. return errorflag > 0 ? SXERR_FORMAT : SXRET_OK;
  22625. }
  22626. }
  22627. if( (c=(*++zFormat))==0 ){
  22628. errorflag = 1;
  22629. rc = xConsumer("%", sizeof("%")-1, pUserData);
  22630. if( rc != SXRET_OK ){
  22631. return SXERR_ABORT; /* Consumer routine request an operation abort */
  22632. }
  22633. return errorflag > 0 ? SXERR_FORMAT : SXRET_OK;
  22634. }
  22635. /* Find out what flags are present */
  22636. flag_leftjustify = flag_plussign = flag_blanksign =
  22637. flag_alternateform = flag_zeropad = 0;
  22638. do{
  22639. switch( c ){
  22640. case '-': flag_leftjustify = 1; c = 0; break;
  22641. case '+': flag_plussign = 1; c = 0; break;
  22642. case ' ': flag_blanksign = 1; c = 0; break;
  22643. case '#': flag_alternateform = 1; c = 0; break;
  22644. case '0': flag_zeropad = 1; c = 0; break;
  22645. default: break;
  22646. }
  22647. }while( c==0 && (c=(*++zFormat))!=0 );
  22648. /* Get the field width */
  22649. width = 0;
  22650. if( c=='*' ){
  22651. width = va_arg(ap, int);
  22652. if( width<0 ){
  22653. flag_leftjustify = 1;
  22654. width = -width;
  22655. }
  22656. c = *++zFormat;
  22657. }else{
  22658. while( c>='0' && c<='9' ){
  22659. width = width*10 + c - '0';
  22660. c = *++zFormat;
  22661. }
  22662. }
  22663. if( width > SXFMT_BUFSIZ-10 ){
  22664. width = SXFMT_BUFSIZ-10;
  22665. }
  22666. /* Get the precision */
  22667. precision = -1;
  22668. if( c=='.' ){
  22669. precision = 0;
  22670. c = *++zFormat;
  22671. if( c=='*' ){
  22672. precision = va_arg(ap, int);
  22673. if( precision<0 ) precision = -precision;
  22674. c = *++zFormat;
  22675. }else{
  22676. while( c>='0' && c<='9' ){
  22677. precision = precision*10 + c - '0';
  22678. c = *++zFormat;
  22679. }
  22680. }
  22681. }
  22682. /* Get the conversion type modifier */
  22683. flag_long = 0;
  22684. if( c=='l' || c == 'q' /* BSD quad (expect a 64-bit integer) */ ){
  22685. flag_long = (c == 'q') ? 2 : 1;
  22686. c = *++zFormat;
  22687. if( c == 'l' ){
  22688. /* Standard printf emulation 'lld' (expect a 64bit integer) */
  22689. flag_long = 2;
  22690. }
  22691. }
  22692. /* Fetch the info entry for the field */
  22693. infop = 0;
  22694. xtype = SXFMT_ERROR;
  22695. for(idx=0; idx< (int)SX_ARRAYSIZE(aFmt); idx++){
  22696. if( c==aFmt[idx].fmttype ){
  22697. infop = &aFmt[idx];
  22698. xtype = infop->type;
  22699. break;
  22700. }
  22701. }
  22702. zExtra = 0;
  22703. /*
  22704. ** At this point, variables are initialized as follows:
  22705. **
  22706. ** flag_alternateform TRUE if a '#' is present.
  22707. ** flag_plussign TRUE if a '+' is present.
  22708. ** flag_leftjustify TRUE if a '-' is present or if the
  22709. ** field width was negative.
  22710. ** flag_zeropad TRUE if the width began with 0.
  22711. ** flag_long TRUE if the letter 'l' (ell) or 'q'(BSD quad) prefixed
  22712. ** the conversion character.
  22713. ** flag_blanksign TRUE if a ' ' is present.
  22714. ** width The specified field width.This is
  22715. ** always non-negative.Zero is the default.
  22716. ** precision The specified precision.The default
  22717. ** is -1.
  22718. ** xtype The object of the conversion.
  22719. ** infop Pointer to the appropriate info struct.
  22720. */
  22721. switch( xtype ){
  22722. case SXFMT_RADIX:
  22723. if( flag_long > 0 ){
  22724. if( flag_long > 1 ){
  22725. /* BSD quad: expect a 64-bit integer */
  22726. longvalue = va_arg(ap, sxi64);
  22727. }else{
  22728. longvalue = va_arg(ap, sxlong);
  22729. }
  22730. }else{
  22731. if( infop->flags & SXFLAG_SIGNED ){
  22732. longvalue = va_arg(ap, sxi32);
  22733. }else{
  22734. longvalue = va_arg(ap, sxu32);
  22735. }
  22736. }
  22737. /* Limit the precision to prevent overflowing buf[] during conversion */
  22738. if( precision>SXFMT_BUFSIZ-40 ) precision = SXFMT_BUFSIZ-40;
  22739. #if 1
  22740. /* For the format %#x, the value zero is printed "0" not "0x0".
  22741. ** I think this is stupid.*/
  22742. if( longvalue==0 ) flag_alternateform = 0;
  22743. #else
  22744. /* More sensible: turn off the prefix for octal (to prevent "00"),
  22745. ** but leave the prefix for hex.*/
  22746. if( longvalue==0 && infop->base==8 ) flag_alternateform = 0;
  22747. #endif
  22748. if( infop->flags & SXFLAG_SIGNED ){
  22749. if( longvalue<0 ){
  22750. longvalue = -longvalue;
  22751. /* Ticket 1433-003 */
  22752. if( longvalue < 0 ){
  22753. /* Overflow */
  22754. longvalue= 0x7FFFFFFFFFFFFFFF;
  22755. }
  22756. prefix = '-';
  22757. }else if( flag_plussign ) prefix = '+';
  22758. else if( flag_blanksign ) prefix = ' ';
  22759. else prefix = 0;
  22760. }else{
  22761. if( longvalue<0 ){
  22762. longvalue = -longvalue;
  22763. /* Ticket 1433-003 */
  22764. if( longvalue < 0 ){
  22765. /* Overflow */
  22766. longvalue= 0x7FFFFFFFFFFFFFFF;
  22767. }
  22768. }
  22769. prefix = 0;
  22770. }
  22771. if( flag_zeropad && precision<width-(prefix!=0) ){
  22772. precision = width-(prefix!=0);
  22773. }
  22774. bufpt = &buf[SXFMT_BUFSIZ-1];
  22775. {
  22776. register char *cset; /* Use registers for speed */
  22777. register int base;
  22778. cset = infop->charset;
  22779. base = infop->base;
  22780. do{ /* Convert to ascii */
  22781. *(--bufpt) = cset[longvalue%base];
  22782. longvalue = longvalue/base;
  22783. }while( longvalue>0 );
  22784. }
  22785. length = &buf[SXFMT_BUFSIZ-1]-bufpt;
  22786. for(idx=precision-length; idx>0; idx--){
  22787. *(--bufpt) = '0'; /* Zero pad */
  22788. }
  22789. if( prefix ) *(--bufpt) = prefix; /* Add sign */
  22790. if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */
  22791. char *pre, x;
  22792. pre = infop->prefix;
  22793. if( *bufpt!=pre[0] ){
  22794. for(pre=infop->prefix; (x=(*pre))!=0; pre++) *(--bufpt) = x;
  22795. }
  22796. }
  22797. length = &buf[SXFMT_BUFSIZ-1]-bufpt;
  22798. break;
  22799. case SXFMT_FLOAT:
  22800. case SXFMT_EXP:
  22801. case SXFMT_GENERIC:
  22802. #ifndef SX_OMIT_FLOATINGPOINT
  22803. realvalue = va_arg(ap, double);
  22804. if( precision<0 ) precision = 6; /* Set default precision */
  22805. if( precision>SXFMT_BUFSIZ-40) precision = SXFMT_BUFSIZ-40;
  22806. if( realvalue<0.0 ){
  22807. realvalue = -realvalue;
  22808. prefix = '-';
  22809. }else{
  22810. if( flag_plussign ) prefix = '+';
  22811. else if( flag_blanksign ) prefix = ' ';
  22812. else prefix = 0;
  22813. }
  22814. if( infop->type==SXFMT_GENERIC && precision>0 ) precision--;
  22815. rounder = 0.0;
  22816. #if 0
  22817. /* Rounding works like BSD when the constant 0.4999 is used.Wierd! */
  22818. for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
  22819. #else
  22820. /* It makes more sense to use 0.5 */
  22821. for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1);
  22822. #endif
  22823. if( infop->type==SXFMT_FLOAT ) realvalue += rounder;
  22824. /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
  22825. exp = 0;
  22826. if( realvalue>0.0 ){
  22827. while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
  22828. while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
  22829. while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; }
  22830. while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; }
  22831. if( exp>350 || exp<-350 ){
  22832. bufpt = "NaN";
  22833. length = 3;
  22834. break;
  22835. }
  22836. }
  22837. bufpt = buf;
  22838. /*
  22839. ** If the field type is etGENERIC, then convert to either etEXP
  22840. ** or etFLOAT, as appropriate.
  22841. */
  22842. flag_exp = xtype==SXFMT_EXP;
  22843. if( xtype!=SXFMT_FLOAT ){
  22844. realvalue += rounder;
  22845. if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
  22846. }
  22847. if( xtype==SXFMT_GENERIC ){
  22848. flag_rtz = !flag_alternateform;
  22849. if( exp<-4 || exp>precision ){
  22850. xtype = SXFMT_EXP;
  22851. }else{
  22852. precision = precision - exp;
  22853. xtype = SXFMT_FLOAT;
  22854. }
  22855. }else{
  22856. flag_rtz = 0;
  22857. }
  22858. /*
  22859. ** The "exp+precision" test causes output to be of type etEXP if
  22860. ** the precision is too large to fit in buf[].
  22861. */
  22862. nsd = 0;
  22863. if( xtype==SXFMT_FLOAT && exp+precision<SXFMT_BUFSIZ-30 ){
  22864. flag_dp = (precision>0 || flag_alternateform);
  22865. if( prefix ) *(bufpt++) = prefix; /* Sign */
  22866. if( exp<0 ) *(bufpt++) = '0'; /* Digits before "." */
  22867. else for(; exp>=0; exp--) *(bufpt++) = (char)getdigit(&realvalue, &nsd);
  22868. if( flag_dp ) *(bufpt++) = '.'; /* The decimal point */
  22869. for(exp++; exp<0 && precision>0; precision--, exp++){
  22870. *(bufpt++) = '0';
  22871. }
  22872. while( (precision--)>0 ) *(bufpt++) = (char)getdigit(&realvalue, &nsd);
  22873. *(bufpt--) = 0; /* Null terminate */
  22874. if( flag_rtz && flag_dp ){ /* Remove trailing zeros and "." */
  22875. while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0;
  22876. if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0;
  22877. }
  22878. bufpt++; /* point to next free slot */
  22879. }else{ /* etEXP or etGENERIC */
  22880. flag_dp = (precision>0 || flag_alternateform);
  22881. if( prefix ) *(bufpt++) = prefix; /* Sign */
  22882. *(bufpt++) = (char)getdigit(&realvalue, &nsd); /* First digit */
  22883. if( flag_dp ) *(bufpt++) = '.'; /* Decimal point */
  22884. while( (precision--)>0 ) *(bufpt++) = (char)getdigit(&realvalue, &nsd);
  22885. bufpt--; /* point to last digit */
  22886. if( flag_rtz && flag_dp ){ /* Remove tail zeros */
  22887. while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0;
  22888. if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0;
  22889. }
  22890. bufpt++; /* point to next free slot */
  22891. if( exp || flag_exp ){
  22892. *(bufpt++) = infop->charset[0];
  22893. if( exp<0 ){ *(bufpt++) = '-'; exp = -exp; } /* sign of exp */
  22894. else { *(bufpt++) = '+'; }
  22895. if( exp>=100 ){
  22896. *(bufpt++) = (char)((exp/100)+'0'); /* 100's digit */
  22897. exp %= 100;
  22898. }
  22899. *(bufpt++) = (char)(exp/10+'0'); /* 10's digit */
  22900. *(bufpt++) = (char)(exp%10+'0'); /* 1's digit */
  22901. }
  22902. }
  22903. /* The converted number is in buf[] and zero terminated.Output it.
  22904. ** Note that the number is in the usual order, not reversed as with
  22905. ** integer conversions.*/
  22906. length = bufpt-buf;
  22907. bufpt = buf;
  22908. /* Special case: Add leading zeros if the flag_zeropad flag is
  22909. ** set and we are not left justified */
  22910. if( flag_zeropad && !flag_leftjustify && length < width){
  22911. int i;
  22912. int nPad = width - length;
  22913. for(i=width; i>=nPad; i--){
  22914. bufpt[i] = bufpt[i-nPad];
  22915. }
  22916. i = prefix!=0;
  22917. while( nPad-- ) bufpt[i++] = '0';
  22918. length = width;
  22919. }
  22920. #else
  22921. bufpt = " ";
  22922. length = (int)sizeof(" ") - 1;
  22923. #endif /* SX_OMIT_FLOATINGPOINT */
  22924. break;
  22925. case SXFMT_SIZE:{
  22926. int *pSize = va_arg(ap, int *);
  22927. *pSize = ((SyFmtConsumer *)pUserData)->nLen;
  22928. length = width = 0;
  22929. }
  22930. break;
  22931. case SXFMT_PERCENT:
  22932. buf[0] = '%';
  22933. bufpt = buf;
  22934. length = 1;
  22935. break;
  22936. case SXFMT_CHARX:
  22937. c = va_arg(ap, int);
  22938. buf[0] = (char)c;
  22939. /* Limit the precision to prevent overflowing buf[] during conversion */
  22940. if( precision>SXFMT_BUFSIZ-40 ) precision = SXFMT_BUFSIZ-40;
  22941. if( precision>=0 ){
  22942. for(idx=1; idx<precision; idx++) buf[idx] = (char)c;
  22943. length = precision;
  22944. }else{
  22945. length =1;
  22946. }
  22947. bufpt = buf;
  22948. break;
  22949. case SXFMT_STRING:
  22950. bufpt = va_arg(ap, char*);
  22951. if( bufpt==0 ){
  22952. bufpt = " ";
  22953. length = (int)sizeof(" ")-1;
  22954. break;
  22955. }
  22956. length = precision;
  22957. if( precision < 0 ){
  22958. /* Symisc extension */
  22959. length = (int)SyStrlen(bufpt);
  22960. }
  22961. if( precision>=0 && precision<length ) length = precision;
  22962. break;
  22963. case SXFMT_RAWSTR:{
  22964. /* Symisc extension */
  22965. SyString *pStr = va_arg(ap, SyString *);
  22966. if( pStr == 0 || pStr->zString == 0 ){
  22967. bufpt = " ";
  22968. length = (int)sizeof(char);
  22969. break;
  22970. }
  22971. bufpt = (char *)pStr->zString;
  22972. length = (int)pStr->nByte;
  22973. break;
  22974. }
  22975. case SXFMT_ERROR:
  22976. buf[0] = '?';
  22977. bufpt = buf;
  22978. length = (int)sizeof(char);
  22979. if( c==0 ) zFormat--;
  22980. break;
  22981. }/* End switch over the format type */
  22982. /*
  22983. ** The text of the conversion is pointed to by "bufpt" and is
  22984. ** "length" characters long.The field width is "width".Do
  22985. ** the output.
  22986. */
  22987. if( !flag_leftjustify ){
  22988. register int nspace;
  22989. nspace = width-length;
  22990. if( nspace>0 ){
  22991. while( nspace>=etSPACESIZE ){
  22992. rc = xConsumer(spaces, etSPACESIZE, pUserData);
  22993. if( rc != SXRET_OK ){
  22994. return SXERR_ABORT; /* Consumer routine request an operation abort */
  22995. }
  22996. nspace -= etSPACESIZE;
  22997. }
  22998. if( nspace>0 ){
  22999. rc = xConsumer(spaces, (unsigned int)nspace, pUserData);
  23000. if( rc != SXRET_OK ){
  23001. return SXERR_ABORT; /* Consumer routine request an operation abort */
  23002. }
  23003. }
  23004. }
  23005. }
  23006. if( length>0 ){
  23007. rc = xConsumer(bufpt, (unsigned int)length, pUserData);
  23008. if( rc != SXRET_OK ){
  23009. return SXERR_ABORT; /* Consumer routine request an operation abort */
  23010. }
  23011. }
  23012. if( flag_leftjustify ){
  23013. register int nspace;
  23014. nspace = width-length;
  23015. if( nspace>0 ){
  23016. while( nspace>=etSPACESIZE ){
  23017. rc = xConsumer(spaces, etSPACESIZE, pUserData);
  23018. if( rc != SXRET_OK ){
  23019. return SXERR_ABORT; /* Consumer routine request an operation abort */
  23020. }
  23021. nspace -= etSPACESIZE;
  23022. }
  23023. if( nspace>0 ){
  23024. rc = xConsumer(spaces, (unsigned int)nspace, pUserData);
  23025. if( rc != SXRET_OK ){
  23026. return SXERR_ABORT; /* Consumer routine request an operation abort */
  23027. }
  23028. }
  23029. }
  23030. }
  23031. }/* End for loop over the format string */
  23032. return errorflag ? SXERR_FORMAT : SXRET_OK;
  23033. }
  23034. static sxi32 FormatConsumer(const void *pSrc, unsigned int nLen, void *pData)
  23035. {
  23036. SyFmtConsumer *pConsumer = (SyFmtConsumer *)pData;
  23037. sxi32 rc = SXERR_ABORT;
  23038. switch(pConsumer->nType){
  23039. case SXFMT_CONS_PROC:
  23040. /* User callback */
  23041. rc = pConsumer->uConsumer.sFunc.xUserConsumer(pSrc, nLen, pConsumer->uConsumer.sFunc.pUserData);
  23042. break;
  23043. case SXFMT_CONS_BLOB:
  23044. /* Blob consumer */
  23045. rc = SyBlobAppend(pConsumer->uConsumer.pBlob, pSrc, (sxu32)nLen);
  23046. break;
  23047. default:
  23048. /* Unknown consumer */
  23049. break;
  23050. }
  23051. /* Update total number of bytes consumed so far */
  23052. pConsumer->nLen += nLen;
  23053. pConsumer->rc = rc;
  23054. return rc;
  23055. }
  23056. static sxi32 FormatMount(sxi32 nType, void *pConsumer, ProcConsumer xUserCons, void *pUserData, sxu32 *pOutLen, const char *zFormat, va_list ap)
  23057. {
  23058. SyFmtConsumer sCons;
  23059. sCons.nType = nType;
  23060. sCons.rc = SXRET_OK;
  23061. sCons.nLen = 0;
  23062. if( pOutLen ){
  23063. *pOutLen = 0;
  23064. }
  23065. switch(nType){
  23066. case SXFMT_CONS_PROC:
  23067. #if defined(UNTRUST)
  23068. if( xUserCons == 0 ){
  23069. return SXERR_EMPTY;
  23070. }
  23071. #endif
  23072. sCons.uConsumer.sFunc.xUserConsumer = xUserCons;
  23073. sCons.uConsumer.sFunc.pUserData = pUserData;
  23074. break;
  23075. case SXFMT_CONS_BLOB:
  23076. sCons.uConsumer.pBlob = (SyBlob *)pConsumer;
  23077. break;
  23078. default:
  23079. return SXERR_UNKNOWN;
  23080. }
  23081. InternFormat(FormatConsumer, &sCons, zFormat, ap);
  23082. if( pOutLen ){
  23083. *pOutLen = sCons.nLen;
  23084. }
  23085. return sCons.rc;
  23086. }
  23087. JX9_PRIVATE sxi32 SyProcFormat(ProcConsumer xConsumer, void *pData, const char *zFormat, ...)
  23088. {
  23089. va_list ap;
  23090. sxi32 rc;
  23091. #if defined(UNTRUST)
  23092. if( SX_EMPTY_STR(zFormat) ){
  23093. return SXERR_EMPTY;
  23094. }
  23095. #endif
  23096. va_start(ap, zFormat);
  23097. rc = FormatMount(SXFMT_CONS_PROC, 0, xConsumer, pData, 0, zFormat, ap);
  23098. va_end(ap);
  23099. return rc;
  23100. }
  23101. JX9_PRIVATE sxu32 SyBlobFormat(SyBlob *pBlob, const char *zFormat, ...)
  23102. {
  23103. va_list ap;
  23104. sxu32 n;
  23105. #if defined(UNTRUST)
  23106. if( SX_EMPTY_STR(zFormat) ){
  23107. return 0;
  23108. }
  23109. #endif
  23110. va_start(ap, zFormat);
  23111. FormatMount(SXFMT_CONS_BLOB, &(*pBlob), 0, 0, &n, zFormat, ap);
  23112. va_end(ap);
  23113. return n;
  23114. }
  23115. JX9_PRIVATE sxu32 SyBlobFormatAp(SyBlob *pBlob, const char *zFormat, va_list ap)
  23116. {
  23117. sxu32 n = 0; /* cc warning */
  23118. #if defined(UNTRUST)
  23119. if( SX_EMPTY_STR(zFormat) ){
  23120. return 0;
  23121. }
  23122. #endif
  23123. FormatMount(SXFMT_CONS_BLOB, &(*pBlob), 0, 0, &n, zFormat, ap);
  23124. return n;
  23125. }
  23126. JX9_PRIVATE sxu32 SyBufferFormat(char *zBuf, sxu32 nLen, const char *zFormat, ...)
  23127. {
  23128. SyBlob sBlob;
  23129. va_list ap;
  23130. sxu32 n;
  23131. #if defined(UNTRUST)
  23132. if( SX_EMPTY_STR(zFormat) ){
  23133. return 0;
  23134. }
  23135. #endif
  23136. if( SXRET_OK != SyBlobInitFromBuf(&sBlob, zBuf, nLen - 1) ){
  23137. return 0;
  23138. }
  23139. va_start(ap, zFormat);
  23140. FormatMount(SXFMT_CONS_BLOB, &sBlob, 0, 0, 0, zFormat, ap);
  23141. va_end(ap);
  23142. n = SyBlobLength(&sBlob);
  23143. /* Append the null terminator */
  23144. sBlob.mByte++;
  23145. SyBlobAppend(&sBlob, "\0", sizeof(char));
  23146. return n;
  23147. }
  23148. #ifndef JX9_DISABLE_BUILTIN_FUNC
  23149. /*
  23150. * Zip File Format:
  23151. *
  23152. * Byte order: Little-endian
  23153. *
  23154. * [Local file header + Compressed data [+ Extended local header]?]*
  23155. * [Central directory]*
  23156. * [End of central directory record]
  23157. *
  23158. * Local file header:*
  23159. * Offset Length Contents
  23160. * 0 4 bytes Local file header signature (0x04034b50)
  23161. * 4 2 bytes Version needed to extract
  23162. * 6 2 bytes General purpose bit flag
  23163. * 8 2 bytes Compression method
  23164. * 10 2 bytes Last mod file time
  23165. * 12 2 bytes Last mod file date
  23166. * 14 4 bytes CRC-32
  23167. * 18 4 bytes Compressed size (n)
  23168. * 22 4 bytes Uncompressed size
  23169. * 26 2 bytes Filename length (f)
  23170. * 28 2 bytes Extra field length (e)
  23171. * 30 (f)bytes Filename
  23172. * (e)bytes Extra field
  23173. * (n)bytes Compressed data
  23174. *
  23175. * Extended local header:*
  23176. * Offset Length Contents
  23177. * 0 4 bytes Extended Local file header signature (0x08074b50)
  23178. * 4 4 bytes CRC-32
  23179. * 8 4 bytes Compressed size
  23180. * 12 4 bytes Uncompressed size
  23181. *
  23182. * Extra field:?(if any)
  23183. * Offset Length Contents
  23184. * 0 2 bytes Header ID (0x001 until 0xfb4a) see extended appnote from Info-zip
  23185. * 2 2 bytes Data size (g)
  23186. * (g) bytes (g) bytes of extra field
  23187. *
  23188. * Central directory:*
  23189. * Offset Length Contents
  23190. * 0 4 bytes Central file header signature (0x02014b50)
  23191. * 4 2 bytes Version made by
  23192. * 6 2 bytes Version needed to extract
  23193. * 8 2 bytes General purpose bit flag
  23194. * 10 2 bytes Compression method
  23195. * 12 2 bytes Last mod file time
  23196. * 14 2 bytes Last mod file date
  23197. * 16 4 bytes CRC-32
  23198. * 20 4 bytes Compressed size
  23199. * 24 4 bytes Uncompressed size
  23200. * 28 2 bytes Filename length (f)
  23201. * 30 2 bytes Extra field length (e)
  23202. * 32 2 bytes File comment length (c)
  23203. * 34 2 bytes Disk number start
  23204. * 36 2 bytes Internal file attributes
  23205. * 38 4 bytes External file attributes
  23206. * 42 4 bytes Relative offset of local header
  23207. * 46 (f)bytes Filename
  23208. * (e)bytes Extra field
  23209. * (c)bytes File comment
  23210. *
  23211. * End of central directory record:
  23212. * Offset Length Contents
  23213. * 0 4 bytes End of central dir signature (0x06054b50)
  23214. * 4 2 bytes Number of this disk
  23215. * 6 2 bytes Number of the disk with the start of the central directory
  23216. * 8 2 bytes Total number of entries in the central dir on this disk
  23217. * 10 2 bytes Total number of entries in the central dir
  23218. * 12 4 bytes Size of the central directory
  23219. * 16 4 bytes Offset of start of central directory with respect to the starting disk number
  23220. * 20 2 bytes zipfile comment length (c)
  23221. * 22 (c)bytes zipfile comment
  23222. *
  23223. * compression method: (2 bytes)
  23224. * 0 - The file is stored (no compression)
  23225. * 1 - The file is Shrunk
  23226. * 2 - The file is Reduced with compression factor 1
  23227. * 3 - The file is Reduced with compression factor 2
  23228. * 4 - The file is Reduced with compression factor 3
  23229. * 5 - The file is Reduced with compression factor 4
  23230. * 6 - The file is Imploded
  23231. * 7 - Reserved for Tokenizing compression algorithm
  23232. * 8 - The file is Deflated
  23233. */
  23234. #define SXMAKE_ZIP_WORKBUF (SXU16_HIGH/2) /* 32KB Initial working buffer size */
  23235. #define SXMAKE_ZIP_EXTRACT_VER 0x000a /* Version needed to extract */
  23236. #define SXMAKE_ZIP_VER 0x003 /* Version made by */
  23237. #define SXZIP_CENTRAL_MAGIC 0x02014b50
  23238. #define SXZIP_END_CENTRAL_MAGIC 0x06054b50
  23239. #define SXZIP_LOCAL_MAGIC 0x04034b50
  23240. /*#define SXZIP_CRC32_START 0xdebb20e3*/
  23241. #define SXZIP_LOCAL_HDRSZ 30 /* Local header size */
  23242. #define SXZIP_LOCAL_EXT_HDRZ 16 /* Extended local header(footer) size */
  23243. #define SXZIP_CENTRAL_HDRSZ 46 /* Central directory header size */
  23244. #define SXZIP_END_CENTRAL_HDRSZ 22 /* End of central directory header size */
  23245. #define SXARCHIVE_HASH_SIZE 64 /* Starting hash table size(MUST BE POWER OF 2)*/
  23246. static sxi32 SyLittleEndianUnpack32(sxu32 *uNB, const unsigned char *buf, sxu32 Len)
  23247. {
  23248. if( Len < sizeof(sxu32) ){
  23249. return SXERR_SHORT;
  23250. }
  23251. *uNB = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
  23252. return SXRET_OK;
  23253. }
  23254. static sxi32 SyLittleEndianUnpack16(sxu16 *pOut, const unsigned char *zBuf, sxu32 nLen)
  23255. {
  23256. if( nLen < sizeof(sxu16) ){
  23257. return SXERR_SHORT;
  23258. }
  23259. *pOut = zBuf[0] + (zBuf[1] <<8);
  23260. return SXRET_OK;
  23261. }
  23262. static sxi32 SyDosTimeFormat(sxu32 nDosDate, Sytm *pOut)
  23263. {
  23264. sxu16 nDate;
  23265. sxu16 nTime;
  23266. nDate = nDosDate >> 16;
  23267. nTime = nDosDate & 0xFFFF;
  23268. pOut->tm_isdst = 0;
  23269. pOut->tm_year = 1980 + (nDate >> 9);
  23270. pOut->tm_mon = (nDate % (1<<9))>>5;
  23271. pOut->tm_mday = (nDate % (1<<9))&0x1F;
  23272. pOut->tm_hour = nTime >> 11;
  23273. pOut->tm_min = (nTime % (1<<11)) >> 5;
  23274. pOut->tm_sec = ((nTime % (1<<11))& 0x1F )<<1;
  23275. return SXRET_OK;
  23276. }
  23277. /*
  23278. * Archive hashtable manager
  23279. */
  23280. static sxi32 ArchiveHashGetEntry(SyArchive *pArch, const char *zName, sxu32 nLen, SyArchiveEntry **ppEntry)
  23281. {
  23282. SyArchiveEntry *pBucketEntry;
  23283. SyString sEntry;
  23284. sxu32 nHash;
  23285. nHash = pArch->xHash(zName, nLen);
  23286. pBucketEntry = pArch->apHash[nHash & (pArch->nSize - 1)];
  23287. SyStringInitFromBuf(&sEntry, zName, nLen);
  23288. for(;;){
  23289. if( pBucketEntry == 0 ){
  23290. break;
  23291. }
  23292. if( nHash == pBucketEntry->nHash && pArch->xCmp(&sEntry, &pBucketEntry->sFileName) == 0 ){
  23293. if( ppEntry ){
  23294. *ppEntry = pBucketEntry;
  23295. }
  23296. return SXRET_OK;
  23297. }
  23298. pBucketEntry = pBucketEntry->pNextHash;
  23299. }
  23300. return SXERR_NOTFOUND;
  23301. }
  23302. static void ArchiveHashBucketInstall(SyArchiveEntry **apTable, sxu32 nBucket, SyArchiveEntry *pEntry)
  23303. {
  23304. pEntry->pNextHash = apTable[nBucket];
  23305. if( apTable[nBucket] != 0 ){
  23306. apTable[nBucket]->pPrevHash = pEntry;
  23307. }
  23308. apTable[nBucket] = pEntry;
  23309. }
  23310. static sxi32 ArchiveHashGrowTable(SyArchive *pArch)
  23311. {
  23312. sxu32 nNewSize = pArch->nSize * 2;
  23313. SyArchiveEntry **apNew;
  23314. SyArchiveEntry *pEntry;
  23315. sxu32 n;
  23316. /* Allocate a new table */
  23317. apNew = (SyArchiveEntry **)SyMemBackendAlloc(pArch->pAllocator, nNewSize * sizeof(SyArchiveEntry *));
  23318. if( apNew == 0 ){
  23319. return SXRET_OK; /* Not so fatal, simply a performance hit */
  23320. }
  23321. SyZero(apNew, nNewSize * sizeof(SyArchiveEntry *));
  23322. /* Rehash old entries */
  23323. for( n = 0 , pEntry = pArch->pList ; n < pArch->nLoaded ; n++ , pEntry = pEntry->pNext ){
  23324. pEntry->pNextHash = pEntry->pPrevHash = 0;
  23325. ArchiveHashBucketInstall(apNew, pEntry->nHash & (nNewSize - 1), pEntry);
  23326. }
  23327. /* Release the old table */
  23328. SyMemBackendFree(pArch->pAllocator, pArch->apHash);
  23329. pArch->apHash = apNew;
  23330. pArch->nSize = nNewSize;
  23331. return SXRET_OK;
  23332. }
  23333. static sxi32 ArchiveHashInstallEntry(SyArchive *pArch, SyArchiveEntry *pEntry)
  23334. {
  23335. if( pArch->nLoaded > pArch->nSize * 3 ){
  23336. ArchiveHashGrowTable(&(*pArch));
  23337. }
  23338. pEntry->nHash = pArch->xHash(SyStringData(&pEntry->sFileName), SyStringLength(&pEntry->sFileName));
  23339. /* Install the entry in its bucket */
  23340. ArchiveHashBucketInstall(pArch->apHash, pEntry->nHash & (pArch->nSize - 1), pEntry);
  23341. MACRO_LD_PUSH(pArch->pList, pEntry);
  23342. pArch->nLoaded++;
  23343. return SXRET_OK;
  23344. }
  23345. /*
  23346. * Parse the End of central directory and report status
  23347. */
  23348. static sxi32 ParseEndOfCentralDirectory(SyArchive *pArch, const unsigned char *zBuf)
  23349. {
  23350. sxu32 nMagic = 0; /* cc -O6 warning */
  23351. sxi32 rc;
  23352. /* Sanity check */
  23353. rc = SyLittleEndianUnpack32(&nMagic, zBuf, sizeof(sxu32));
  23354. if( /* rc != SXRET_OK || */nMagic != SXZIP_END_CENTRAL_MAGIC ){
  23355. return SXERR_CORRUPT;
  23356. }
  23357. /* # of entries */
  23358. rc = SyLittleEndianUnpack16((sxu16 *)&pArch->nEntry, &zBuf[8], sizeof(sxu16));
  23359. if( /* rc != SXRET_OK || */ pArch->nEntry > SXI16_HIGH /* SXU16_HIGH */ ){
  23360. return SXERR_CORRUPT;
  23361. }
  23362. /* Size of central directory */
  23363. rc = SyLittleEndianUnpack32(&pArch->nCentralSize, &zBuf[12], sizeof(sxu32));
  23364. if( /*rc != SXRET_OK ||*/ pArch->nCentralSize > SXI32_HIGH ){
  23365. return SXERR_CORRUPT;
  23366. }
  23367. /* Starting offset of central directory */
  23368. rc = SyLittleEndianUnpack32(&pArch->nCentralOfft, &zBuf[16], sizeof(sxu32));
  23369. if( /*rc != SXRET_OK ||*/ pArch->nCentralSize > SXI32_HIGH ){
  23370. return SXERR_CORRUPT;
  23371. }
  23372. return SXRET_OK;
  23373. }
  23374. /*
  23375. * Fill the zip entry with the appropriate information from the central directory
  23376. */
  23377. static sxi32 GetCentralDirectoryEntry(SyArchive *pArch, SyArchiveEntry *pEntry, const unsigned char *zCentral, sxu32 *pNextOffset)
  23378. {
  23379. SyString *pName = &pEntry->sFileName; /* File name */
  23380. sxu16 nDosDate, nDosTime;
  23381. sxu16 nComment = 0 ;
  23382. sxu32 nMagic = 0; /* cc -O6 warning */
  23383. sxi32 rc;
  23384. nDosDate = nDosTime = 0; /* cc -O6 warning */
  23385. SXUNUSED(pArch);
  23386. /* Sanity check */
  23387. rc = SyLittleEndianUnpack32(&nMagic, zCentral, sizeof(sxu32));
  23388. if( /* rc != SXRET_OK || */ nMagic != SXZIP_CENTRAL_MAGIC ){
  23389. rc = SXERR_CORRUPT;
  23390. /*
  23391. * Try to recover by examing the next central directory record.
  23392. * Dont worry here, there is no risk of an infinite loop since
  23393. * the buffer size is delimited.
  23394. */
  23395. /* pName->nByte = 0; nComment = 0; pName->nExtra = 0 */
  23396. goto update;
  23397. }
  23398. /*
  23399. * entry name length
  23400. */
  23401. SyLittleEndianUnpack16((sxu16 *)&pName->nByte, &zCentral[28], sizeof(sxu16));
  23402. if( pName->nByte > SXI16_HIGH /* SXU16_HIGH */){
  23403. rc = SXERR_BIG;
  23404. goto update;
  23405. }
  23406. /* Extra information */
  23407. SyLittleEndianUnpack16(&pEntry->nExtra, &zCentral[30], sizeof(sxu16));
  23408. /* Comment length */
  23409. SyLittleEndianUnpack16(&nComment, &zCentral[32], sizeof(sxu16));
  23410. /* Compression method 0 == stored / 8 == deflated */
  23411. rc = SyLittleEndianUnpack16(&pEntry->nComprMeth, &zCentral[10], sizeof(sxu16));
  23412. /* DOS Timestamp */
  23413. SyLittleEndianUnpack16(&nDosTime, &zCentral[12], sizeof(sxu16));
  23414. SyLittleEndianUnpack16(&nDosDate, &zCentral[14], sizeof(sxu16));
  23415. SyDosTimeFormat((nDosDate << 16 | nDosTime), &pEntry->sFmt);
  23416. /* Little hack to fix month index */
  23417. pEntry->sFmt.tm_mon--;
  23418. /* CRC32 */
  23419. rc = SyLittleEndianUnpack32(&pEntry->nCrc, &zCentral[16], sizeof(sxu32));
  23420. /* Content size before compression */
  23421. rc = SyLittleEndianUnpack32(&pEntry->nByte, &zCentral[24], sizeof(sxu32));
  23422. if( pEntry->nByte > SXI32_HIGH ){
  23423. rc = SXERR_BIG;
  23424. goto update;
  23425. }
  23426. /*
  23427. * Content size after compression.
  23428. * Note that if the file is stored pEntry->nByte should be equal to pEntry->nByteCompr
  23429. */
  23430. rc = SyLittleEndianUnpack32(&pEntry->nByteCompr, &zCentral[20], sizeof(sxu32));
  23431. if( pEntry->nByteCompr > SXI32_HIGH ){
  23432. rc = SXERR_BIG;
  23433. goto update;
  23434. }
  23435. /* Finally grab the contents offset */
  23436. SyLittleEndianUnpack32(&pEntry->nOfft, &zCentral[42], sizeof(sxu32));
  23437. if( pEntry->nOfft > SXI32_HIGH ){
  23438. rc = SXERR_BIG;
  23439. goto update;
  23440. }
  23441. rc = SXRET_OK;
  23442. update:
  23443. /* Update the offset to point to the next central directory record */
  23444. *pNextOffset = SXZIP_CENTRAL_HDRSZ + pName->nByte + pEntry->nExtra + nComment;
  23445. return rc; /* Report failure or success */
  23446. }
  23447. static sxi32 ZipFixOffset(SyArchiveEntry *pEntry, void *pSrc)
  23448. {
  23449. sxu16 nExtra, nNameLen;
  23450. unsigned char *zHdr;
  23451. nExtra = nNameLen = 0;
  23452. zHdr = (unsigned char *)pSrc;
  23453. zHdr = &zHdr[pEntry->nOfft];
  23454. if( SyMemcmp(zHdr, "PK\003\004", sizeof(sxu32)) != 0 ){
  23455. return SXERR_CORRUPT;
  23456. }
  23457. SyLittleEndianUnpack16(&nNameLen, &zHdr[26], sizeof(sxu16));
  23458. SyLittleEndianUnpack16(&nExtra, &zHdr[28], sizeof(sxu16));
  23459. /* Fix contents offset */
  23460. pEntry->nOfft += SXZIP_LOCAL_HDRSZ + nExtra + nNameLen;
  23461. return SXRET_OK;
  23462. }
  23463. /*
  23464. * Extract all valid entries from the central directory
  23465. */
  23466. static sxi32 ZipExtract(SyArchive *pArch, const unsigned char *zCentral, sxu32 nLen, void *pSrc)
  23467. {
  23468. SyArchiveEntry *pEntry, *pDup;
  23469. const unsigned char *zEnd ; /* End of central directory */
  23470. sxu32 nIncr, nOfft; /* Central Offset */
  23471. SyString *pName; /* Entry name */
  23472. char *zName;
  23473. sxi32 rc;
  23474. nOfft = nIncr = 0;
  23475. zEnd = &zCentral[nLen];
  23476. for(;;){
  23477. if( &zCentral[nOfft] >= zEnd ){
  23478. break;
  23479. }
  23480. /* Add a new entry */
  23481. pEntry = (SyArchiveEntry *)SyMemBackendPoolAlloc(pArch->pAllocator, sizeof(SyArchiveEntry));
  23482. if( pEntry == 0 ){
  23483. break;
  23484. }
  23485. SyZero(pEntry, sizeof(SyArchiveEntry));
  23486. pEntry->nMagic = SXARCH_MAGIC;
  23487. nIncr = 0;
  23488. rc = GetCentralDirectoryEntry(&(*pArch), pEntry, &zCentral[nOfft], &nIncr);
  23489. if( rc == SXRET_OK ){
  23490. /* Fix the starting record offset so we can access entry contents correctly */
  23491. rc = ZipFixOffset(pEntry, pSrc);
  23492. }
  23493. if(rc != SXRET_OK ){
  23494. sxu32 nJmp = 0;
  23495. SyMemBackendPoolFree(pArch->pAllocator, pEntry);
  23496. /* Try to recover by brute-forcing for a valid central directory record */
  23497. if( SXRET_OK == SyBlobSearch((const void *)&zCentral[nOfft + nIncr], (sxu32)(zEnd - &zCentral[nOfft + nIncr]),
  23498. (const void *)"PK\001\002", sizeof(sxu32), &nJmp)){
  23499. nOfft += nIncr + nJmp; /* Check next entry */
  23500. continue;
  23501. }
  23502. break; /* Giving up, archive is hopelessly corrupted */
  23503. }
  23504. pName = &pEntry->sFileName;
  23505. pName->zString = (const char *)&zCentral[nOfft + SXZIP_CENTRAL_HDRSZ];
  23506. if( pName->nByte <= 0 || ( pEntry->nByte <= 0 && pName->zString[pName->nByte - 1] != '/') ){
  23507. /* Ignore zero length records (except folders) and records without names */
  23508. SyMemBackendPoolFree(pArch->pAllocator, pEntry);
  23509. nOfft += nIncr; /* Check next entry */
  23510. continue;
  23511. }
  23512. zName = SyMemBackendStrDup(pArch->pAllocator, pName->zString, pName->nByte);
  23513. if( zName == 0 ){
  23514. SyMemBackendPoolFree(pArch->pAllocator, pEntry);
  23515. nOfft += nIncr; /* Check next entry */
  23516. continue;
  23517. }
  23518. pName->zString = (const char *)zName;
  23519. /* Check for duplicates */
  23520. rc = ArchiveHashGetEntry(&(*pArch), pName->zString, pName->nByte, &pDup);
  23521. if( rc == SXRET_OK ){
  23522. /* Another entry with the same name exists ; link them together */
  23523. pEntry->pNextName = pDup->pNextName;
  23524. pDup->pNextName = pEntry;
  23525. pDup->nDup++;
  23526. }else{
  23527. /* Insert in hashtable */
  23528. ArchiveHashInstallEntry(pArch, pEntry);
  23529. }
  23530. nOfft += nIncr; /* Check next record */
  23531. }
  23532. pArch->pCursor = pArch->pList;
  23533. return pArch->nLoaded > 0 ? SXRET_OK : SXERR_EMPTY;
  23534. }
  23535. JX9_PRIVATE sxi32 SyZipExtractFromBuf(SyArchive *pArch, const char *zBuf, sxu32 nLen)
  23536. {
  23537. const unsigned char *zCentral, *zEnd;
  23538. sxi32 rc;
  23539. #if defined(UNTRUST)
  23540. if( SXARCH_INVALID(pArch) || zBuf == 0 ){
  23541. return SXERR_INVALID;
  23542. }
  23543. #endif
  23544. /* The miminal size of a zip archive:
  23545. * LOCAL_HDR_SZ + CENTRAL_HDR_SZ + END_OF_CENTRAL_HDR_SZ
  23546. * 30 46 22
  23547. */
  23548. if( nLen < SXZIP_LOCAL_HDRSZ + SXZIP_CENTRAL_HDRSZ + SXZIP_END_CENTRAL_HDRSZ ){
  23549. return SXERR_CORRUPT; /* Don't bother processing return immediately */
  23550. }
  23551. zEnd = (unsigned char *)&zBuf[nLen - SXZIP_END_CENTRAL_HDRSZ];
  23552. /* Find the end of central directory */
  23553. while( ((sxu32)((unsigned char *)&zBuf[nLen] - zEnd) < (SXZIP_END_CENTRAL_HDRSZ + SXI16_HIGH)) &&
  23554. zEnd > (unsigned char *)zBuf && SyMemcmp(zEnd, "PK\005\006", sizeof(sxu32)) != 0 ){
  23555. zEnd--;
  23556. }
  23557. /* Parse the end of central directory */
  23558. rc = ParseEndOfCentralDirectory(&(*pArch), zEnd);
  23559. if( rc != SXRET_OK ){
  23560. return rc;
  23561. }
  23562. /* Find the starting offset of the central directory */
  23563. zCentral = &zEnd[-(sxi32)pArch->nCentralSize];
  23564. if( zCentral <= (unsigned char *)zBuf || SyMemcmp(zCentral, "PK\001\002", sizeof(sxu32)) != 0 ){
  23565. if( pArch->nCentralOfft >= nLen ){
  23566. /* Corrupted central directory offset */
  23567. return SXERR_CORRUPT;
  23568. }
  23569. zCentral = (unsigned char *)&zBuf[pArch->nCentralOfft];
  23570. if( SyMemcmp(zCentral, "PK\001\002", sizeof(sxu32)) != 0 ){
  23571. /* Corrupted zip archive */
  23572. return SXERR_CORRUPT;
  23573. }
  23574. /* Fall thru and extract all valid entries from the central directory */
  23575. }
  23576. rc = ZipExtract(&(*pArch), zCentral, (sxu32)(zEnd - zCentral), (void *)zBuf);
  23577. return rc;
  23578. }
  23579. /*
  23580. * Default comparison function.
  23581. */
  23582. static sxi32 ArchiveHashCmp(const SyString *pStr1, const SyString *pStr2)
  23583. {
  23584. sxi32 rc;
  23585. rc = SyStringCmp(pStr1, pStr2, SyMemcmp);
  23586. return rc;
  23587. }
  23588. JX9_PRIVATE sxi32 SyArchiveInit(SyArchive *pArch, SyMemBackend *pAllocator, ProcHash xHash, ProcRawStrCmp xCmp)
  23589. {
  23590. SyArchiveEntry **apHash;
  23591. #if defined(UNTRUST)
  23592. if( pArch == 0 ){
  23593. return SXERR_EMPTY;
  23594. }
  23595. #endif
  23596. SyZero(pArch, sizeof(SyArchive));
  23597. /* Allocate a new hashtable */
  23598. apHash = (SyArchiveEntry **)SyMemBackendAlloc(&(*pAllocator), SXARCHIVE_HASH_SIZE * sizeof(SyArchiveEntry *));
  23599. if( apHash == 0){
  23600. return SXERR_MEM;
  23601. }
  23602. SyZero(apHash, SXARCHIVE_HASH_SIZE * sizeof(SyArchiveEntry *));
  23603. pArch->apHash = apHash;
  23604. pArch->xHash = xHash ? xHash : SyBinHash;
  23605. pArch->xCmp = xCmp ? xCmp : ArchiveHashCmp;
  23606. pArch->nSize = SXARCHIVE_HASH_SIZE;
  23607. pArch->pAllocator = &(*pAllocator);
  23608. pArch->nMagic = SXARCH_MAGIC;
  23609. return SXRET_OK;
  23610. }
  23611. static sxi32 ArchiveReleaseEntry(SyMemBackend *pAllocator, SyArchiveEntry *pEntry)
  23612. {
  23613. SyArchiveEntry *pDup = pEntry->pNextName;
  23614. SyArchiveEntry *pNextDup;
  23615. /* Release duplicates first since there are not stored in the hashtable */
  23616. for(;;){
  23617. if( pEntry->nDup == 0 ){
  23618. break;
  23619. }
  23620. pNextDup = pDup->pNextName;
  23621. pDup->nMagic = 0x2661;
  23622. SyMemBackendFree(pAllocator, (void *)SyStringData(&pDup->sFileName));
  23623. SyMemBackendPoolFree(pAllocator, pDup);
  23624. pDup = pNextDup;
  23625. pEntry->nDup--;
  23626. }
  23627. pEntry->nMagic = 0x2661;
  23628. SyMemBackendFree(pAllocator, (void *)SyStringData(&pEntry->sFileName));
  23629. SyMemBackendPoolFree(pAllocator, pEntry);
  23630. return SXRET_OK;
  23631. }
  23632. JX9_PRIVATE sxi32 SyArchiveRelease(SyArchive *pArch)
  23633. {
  23634. SyArchiveEntry *pEntry, *pNext;
  23635. pEntry = pArch->pList;
  23636. for(;;){
  23637. if( pArch->nLoaded < 1 ){
  23638. break;
  23639. }
  23640. pNext = pEntry->pNext;
  23641. MACRO_LD_REMOVE(pArch->pList, pEntry);
  23642. ArchiveReleaseEntry(pArch->pAllocator, pEntry);
  23643. pEntry = pNext;
  23644. pArch->nLoaded--;
  23645. }
  23646. SyMemBackendFree(pArch->pAllocator, pArch->apHash);
  23647. pArch->pCursor = 0;
  23648. pArch->nMagic = 0x2626;
  23649. return SXRET_OK;
  23650. }
  23651. JX9_PRIVATE sxi32 SyArchiveResetLoopCursor(SyArchive *pArch)
  23652. {
  23653. pArch->pCursor = pArch->pList;
  23654. return SXRET_OK;
  23655. }
  23656. JX9_PRIVATE sxi32 SyArchiveGetNextEntry(SyArchive *pArch, SyArchiveEntry **ppEntry)
  23657. {
  23658. SyArchiveEntry *pNext;
  23659. if( pArch->pCursor == 0 ){
  23660. /* Rewind the cursor */
  23661. pArch->pCursor = pArch->pList;
  23662. return SXERR_EOF;
  23663. }
  23664. *ppEntry = pArch->pCursor;
  23665. pNext = pArch->pCursor->pNext;
  23666. /* Advance the cursor to the next entry */
  23667. pArch->pCursor = pNext;
  23668. return SXRET_OK;
  23669. }
  23670. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  23671. /*
  23672. * Psuedo Random Number Generator (PRNG)
  23673. * @authors: SQLite authors <http://www.sqlite.org/>
  23674. * @status: Public Domain
  23675. * NOTE:
  23676. * Nothing in this file or anywhere else in the library does any kind of
  23677. * encryption.The RC4 algorithm is being used as a PRNG (pseudo-random
  23678. * number generator) not as an encryption device.
  23679. */
  23680. #define SXPRNG_MAGIC 0x13C4
  23681. #ifdef __UNIXES__
  23682. #include <sys/types.h>
  23683. #include <sys/stat.h>
  23684. #include <fcntl.h>
  23685. #include <unistd.h>
  23686. #include <errno.h>
  23687. #include <time.h>
  23688. #include <sys/time.h>
  23689. #endif
  23690. static sxi32 SyOSUtilRandomSeed(void *pBuf, sxu32 nLen, void *pUnused)
  23691. {
  23692. char *zBuf = (char *)pBuf;
  23693. #ifdef __WINNT__
  23694. DWORD nProcessID; /* Yes, keep it uninitialized when compiling using the MinGW32 builds tools */
  23695. #elif defined(__UNIXES__)
  23696. pid_t pid;
  23697. int fd;
  23698. #else
  23699. char zGarbage[128]; /* Yes, keep this buffer uninitialized */
  23700. #endif
  23701. SXUNUSED(pUnused);
  23702. #ifdef __WINNT__
  23703. #ifndef __MINGW32__
  23704. nProcessID = GetProcessId(GetCurrentProcess());
  23705. #endif
  23706. SyMemcpy((const void *)&nProcessID, zBuf, SXMIN(nLen, sizeof(DWORD)));
  23707. if( (sxu32)(&zBuf[nLen] - &zBuf[sizeof(DWORD)]) >= sizeof(SYSTEMTIME) ){
  23708. GetSystemTime((LPSYSTEMTIME)&zBuf[sizeof(DWORD)]);
  23709. }
  23710. #elif defined(__UNIXES__)
  23711. fd = open("/dev/urandom", O_RDONLY);
  23712. if (fd >= 0 ){
  23713. if( read(fd, zBuf, nLen) > 0 ){
  23714. return SXRET_OK;
  23715. }
  23716. /* FALL THRU */
  23717. }
  23718. pid = getpid();
  23719. SyMemcpy((const void *)&pid, zBuf, SXMIN(nLen, sizeof(pid_t)));
  23720. if( &zBuf[nLen] - &zBuf[sizeof(pid_t)] >= (int)sizeof(struct timeval) ){
  23721. gettimeofday((struct timeval *)&zBuf[sizeof(pid_t)], 0);
  23722. }
  23723. #else
  23724. /* Fill with uninitialized data */
  23725. SyMemcpy(zGarbage, zBuf, SXMIN(nLen, sizeof(zGarbage)));
  23726. #endif
  23727. return SXRET_OK;
  23728. }
  23729. JX9_PRIVATE sxi32 SyRandomnessInit(SyPRNGCtx *pCtx, ProcRandomSeed xSeed, void * pUserData)
  23730. {
  23731. char zSeed[256];
  23732. sxu8 t;
  23733. sxi32 rc;
  23734. sxu32 i;
  23735. if( pCtx->nMagic == SXPRNG_MAGIC ){
  23736. return SXRET_OK; /* Already initialized */
  23737. }
  23738. /* Initialize the state of the random number generator once,
  23739. ** the first time this routine is called.The seed value does
  23740. ** not need to contain a lot of randomness since we are not
  23741. ** trying to do secure encryption or anything like that...
  23742. */
  23743. if( xSeed == 0 ){
  23744. xSeed = SyOSUtilRandomSeed;
  23745. }
  23746. rc = xSeed(zSeed, sizeof(zSeed), pUserData);
  23747. if( rc != SXRET_OK ){
  23748. return rc;
  23749. }
  23750. pCtx->i = pCtx->j = 0;
  23751. for(i=0; i < SX_ARRAYSIZE(pCtx->s) ; i++){
  23752. pCtx->s[i] = (unsigned char)i;
  23753. }
  23754. for(i=0; i < sizeof(zSeed) ; i++){
  23755. pCtx->j += pCtx->s[i] + zSeed[i];
  23756. t = pCtx->s[pCtx->j];
  23757. pCtx->s[pCtx->j] = pCtx->s[i];
  23758. pCtx->s[i] = t;
  23759. }
  23760. pCtx->nMagic = SXPRNG_MAGIC;
  23761. return SXRET_OK;
  23762. }
  23763. /*
  23764. * Get a single 8-bit random value using the RC4 PRNG.
  23765. */
  23766. static sxu8 randomByte(SyPRNGCtx *pCtx)
  23767. {
  23768. sxu8 t;
  23769. /* Generate and return single random byte */
  23770. pCtx->i++;
  23771. t = pCtx->s[pCtx->i];
  23772. pCtx->j += t;
  23773. pCtx->s[pCtx->i] = pCtx->s[pCtx->j];
  23774. pCtx->s[pCtx->j] = t;
  23775. t += pCtx->s[pCtx->i];
  23776. return pCtx->s[t];
  23777. }
  23778. JX9_PRIVATE sxi32 SyRandomness(SyPRNGCtx *pCtx, void *pBuf, sxu32 nLen)
  23779. {
  23780. unsigned char *zBuf = (unsigned char *)pBuf;
  23781. unsigned char *zEnd = &zBuf[nLen];
  23782. #if defined(UNTRUST)
  23783. if( pCtx == 0 || pBuf == 0 || nLen <= 0 ){
  23784. return SXERR_EMPTY;
  23785. }
  23786. #endif
  23787. if(pCtx->nMagic != SXPRNG_MAGIC ){
  23788. return SXERR_CORRUPT;
  23789. }
  23790. for(;;){
  23791. if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++;
  23792. if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++;
  23793. if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++;
  23794. if( zBuf >= zEnd ){break;} zBuf[0] = randomByte(pCtx); zBuf++;
  23795. }
  23796. return SXRET_OK;
  23797. }
  23798. #ifndef JX9_DISABLE_BUILTIN_FUNC
  23799. #ifndef JX9_DISABLE_HASH_FUNC
  23800. /* SyRunTimeApi: sxhash.c */
  23801. /*
  23802. * This code implements the MD5 message-digest algorithm.
  23803. * The algorithm is due to Ron Rivest.This code was
  23804. * written by Colin Plumb in 1993, no copyright is claimed.
  23805. * This code is in the public domain; do with it what you wish.
  23806. *
  23807. * Equivalent code is available from RSA Data Security, Inc.
  23808. * This code has been tested against that, and is equivalent,
  23809. * except that you don't need to include two pages of legalese
  23810. * with every copy.
  23811. *
  23812. * To compute the message digest of a chunk of bytes, declare an
  23813. * MD5Context structure, pass it to MD5Init, call MD5Update as
  23814. * needed on buffers full of bytes, and then call MD5Final, which
  23815. * will fill a supplied 16-byte array with the digest.
  23816. */
  23817. #define SX_MD5_BINSZ 16
  23818. #define SX_MD5_HEXSZ 32
  23819. /*
  23820. * Note: this code is harmless on little-endian machines.
  23821. */
  23822. static void byteReverse (unsigned char *buf, unsigned longs)
  23823. {
  23824. sxu32 t;
  23825. do {
  23826. t = (sxu32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
  23827. ((unsigned)buf[1]<<8 | buf[0]);
  23828. *(sxu32*)buf = t;
  23829. buf += 4;
  23830. } while (--longs);
  23831. }
  23832. /* The four core functions - F1 is optimized somewhat */
  23833. /* #define F1(x, y, z) (x & y | ~x & z) */
  23834. #ifdef F1
  23835. #undef F1
  23836. #endif
  23837. #ifdef F2
  23838. #undef F2
  23839. #endif
  23840. #ifdef F3
  23841. #undef F3
  23842. #endif
  23843. #ifdef F4
  23844. #undef F4
  23845. #endif
  23846. #define F1(x, y, z) (z ^ (x & (y ^ z)))
  23847. #define F2(x, y, z) F1(z, x, y)
  23848. #define F3(x, y, z) (x ^ y ^ z)
  23849. #define F4(x, y, z) (y ^ (x | ~z))
  23850. /* This is the central step in the MD5 algorithm.*/
  23851. #define SX_MD5STEP(f, w, x, y, z, data, s) \
  23852. ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
  23853. /*
  23854. * The core of the MD5 algorithm, this alters an existing MD5 hash to
  23855. * reflect the addition of 16 longwords of new data.MD5Update blocks
  23856. * the data and converts bytes into longwords for this routine.
  23857. */
  23858. static void MD5Transform(sxu32 buf[4], const sxu32 in[16])
  23859. {
  23860. register sxu32 a, b, c, d;
  23861. a = buf[0];
  23862. b = buf[1];
  23863. c = buf[2];
  23864. d = buf[3];
  23865. SX_MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7);
  23866. SX_MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
  23867. SX_MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
  23868. SX_MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
  23869. SX_MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7);
  23870. SX_MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
  23871. SX_MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
  23872. SX_MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
  23873. SX_MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7);
  23874. SX_MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
  23875. SX_MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
  23876. SX_MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
  23877. SX_MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7);
  23878. SX_MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
  23879. SX_MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
  23880. SX_MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
  23881. SX_MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5);
  23882. SX_MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9);
  23883. SX_MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
  23884. SX_MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
  23885. SX_MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5);
  23886. SX_MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9);
  23887. SX_MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
  23888. SX_MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
  23889. SX_MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5);
  23890. SX_MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9);
  23891. SX_MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
  23892. SX_MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
  23893. SX_MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5);
  23894. SX_MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9);
  23895. SX_MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
  23896. SX_MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
  23897. SX_MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4);
  23898. SX_MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
  23899. SX_MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
  23900. SX_MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
  23901. SX_MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4);
  23902. SX_MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
  23903. SX_MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
  23904. SX_MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
  23905. SX_MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4);
  23906. SX_MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
  23907. SX_MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
  23908. SX_MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
  23909. SX_MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4);
  23910. SX_MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
  23911. SX_MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
  23912. SX_MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
  23913. SX_MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6);
  23914. SX_MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
  23915. SX_MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
  23916. SX_MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
  23917. SX_MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6);
  23918. SX_MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
  23919. SX_MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
  23920. SX_MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
  23921. SX_MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6);
  23922. SX_MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
  23923. SX_MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
  23924. SX_MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
  23925. SX_MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6);
  23926. SX_MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
  23927. SX_MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
  23928. SX_MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
  23929. buf[0] += a;
  23930. buf[1] += b;
  23931. buf[2] += c;
  23932. buf[3] += d;
  23933. }
  23934. /*
  23935. * Update context to reflect the concatenation of another buffer full
  23936. * of bytes.
  23937. */
  23938. JX9_PRIVATE void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len)
  23939. {
  23940. sxu32 t;
  23941. /* Update bitcount */
  23942. t = ctx->bits[0];
  23943. if ((ctx->bits[0] = t + ((sxu32)len << 3)) < t)
  23944. ctx->bits[1]++; /* Carry from low to high */
  23945. ctx->bits[1] += len >> 29;
  23946. t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
  23947. /* Handle any leading odd-sized chunks */
  23948. if ( t ) {
  23949. unsigned char *p = (unsigned char *)ctx->in + t;
  23950. t = 64-t;
  23951. if (len < t) {
  23952. SyMemcpy(buf, p, len);
  23953. return;
  23954. }
  23955. SyMemcpy(buf, p, t);
  23956. byteReverse(ctx->in, 16);
  23957. MD5Transform(ctx->buf, (sxu32*)ctx->in);
  23958. buf += t;
  23959. len -= t;
  23960. }
  23961. /* Process data in 64-byte chunks */
  23962. while (len >= 64) {
  23963. SyMemcpy(buf, ctx->in, 64);
  23964. byteReverse(ctx->in, 16);
  23965. MD5Transform(ctx->buf, (sxu32*)ctx->in);
  23966. buf += 64;
  23967. len -= 64;
  23968. }
  23969. /* Handle any remaining bytes of data.*/
  23970. SyMemcpy(buf, ctx->in, len);
  23971. }
  23972. /*
  23973. * Final wrapup - pad to 64-byte boundary with the bit pattern
  23974. * 1 0* (64-bit count of bits processed, MSB-first)
  23975. */
  23976. JX9_PRIVATE void MD5Final(unsigned char digest[16], MD5Context *ctx){
  23977. unsigned count;
  23978. unsigned char *p;
  23979. /* Compute number of bytes mod 64 */
  23980. count = (ctx->bits[0] >> 3) & 0x3F;
  23981. /* Set the first char of padding to 0x80.This is safe since there is
  23982. always at least one byte free */
  23983. p = ctx->in + count;
  23984. *p++ = 0x80;
  23985. /* Bytes of padding needed to make 64 bytes */
  23986. count = 64 - 1 - count;
  23987. /* Pad out to 56 mod 64 */
  23988. if (count < 8) {
  23989. /* Two lots of padding: Pad the first block to 64 bytes */
  23990. SyZero(p, count);
  23991. byteReverse(ctx->in, 16);
  23992. MD5Transform(ctx->buf, (sxu32*)ctx->in);
  23993. /* Now fill the next block with 56 bytes */
  23994. SyZero(ctx->in, 56);
  23995. } else {
  23996. /* Pad block to 56 bytes */
  23997. SyZero(p, count-8);
  23998. }
  23999. byteReverse(ctx->in, 14);
  24000. /* Append length in bits and transform */
  24001. ((sxu32*)ctx->in)[ 14 ] = ctx->bits[0];
  24002. ((sxu32*)ctx->in)[ 15 ] = ctx->bits[1];
  24003. MD5Transform(ctx->buf, (sxu32*)ctx->in);
  24004. byteReverse((unsigned char *)ctx->buf, 4);
  24005. SyMemcpy(ctx->buf, digest, 0x10);
  24006. SyZero(ctx, sizeof(ctx)); /* In case it's sensitive */
  24007. }
  24008. #undef F1
  24009. #undef F2
  24010. #undef F3
  24011. #undef F4
  24012. JX9_PRIVATE sxi32 MD5Init(MD5Context *pCtx)
  24013. {
  24014. pCtx->buf[0] = 0x67452301;
  24015. pCtx->buf[1] = 0xefcdab89;
  24016. pCtx->buf[2] = 0x98badcfe;
  24017. pCtx->buf[3] = 0x10325476;
  24018. pCtx->bits[0] = 0;
  24019. pCtx->bits[1] = 0;
  24020. return SXRET_OK;
  24021. }
  24022. JX9_PRIVATE sxi32 SyMD5Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[16])
  24023. {
  24024. MD5Context sCtx;
  24025. MD5Init(&sCtx);
  24026. MD5Update(&sCtx, (const unsigned char *)pIn, nLen);
  24027. MD5Final(zDigest, &sCtx);
  24028. return SXRET_OK;
  24029. }
  24030. /*
  24031. * SHA-1 in C
  24032. * By Steve Reid <steve@edmweb.com>
  24033. * Status: Public Domain
  24034. */
  24035. /*
  24036. * blk0() and blk() perform the initial expand.
  24037. * I got the idea of expanding during the round function from SSLeay
  24038. *
  24039. * blk0le() for little-endian and blk0be() for big-endian.
  24040. */
  24041. #if __GNUC__ && (defined(__i386__) || defined(__x86_64__))
  24042. /*
  24043. * GCC by itself only generates left rotates. Use right rotates if
  24044. * possible to be kinder to dinky implementations with iterative rotate
  24045. * instructions.
  24046. */
  24047. #define SHA_ROT(op, x, k) \
  24048. ({ unsigned int y; asm(op " %1, %0" : "=r" (y) : "I" (k), "0" (x)); y; })
  24049. #define rol(x, k) SHA_ROT("roll", x, k)
  24050. #define ror(x, k) SHA_ROT("rorl", x, k)
  24051. #else
  24052. /* Generic C equivalent */
  24053. #define SHA_ROT(x, l, r) ((x) << (l) | (x) >> (r))
  24054. #define rol(x, k) SHA_ROT(x, k, 32-(k))
  24055. #define ror(x, k) SHA_ROT(x, 32-(k), k)
  24056. #endif
  24057. #define blk0le(i) (block[i] = (ror(block[i], 8)&0xFF00FF00) \
  24058. |(rol(block[i], 8)&0x00FF00FF))
  24059. #define blk0be(i) block[i]
  24060. #define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \
  24061. ^block[(i+2)&15]^block[i&15], 1))
  24062. /*
  24063. * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
  24064. *
  24065. * Rl0() for little-endian and Rb0() for big-endian. Endianness is
  24066. * determined at run-time.
  24067. */
  24068. #define Rl0(v, w, x, y, z, i) \
  24069. z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v, 5);w=ror(w, 2);
  24070. #define Rb0(v, w, x, y, z, i) \
  24071. z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v, 5);w=ror(w, 2);
  24072. #define R1(v, w, x, y, z, i) \
  24073. z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v, 5);w=ror(w, 2);
  24074. #define R2(v, w, x, y, z, i) \
  24075. z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v, 5);w=ror(w, 2);
  24076. #define R3(v, w, x, y, z, i) \
  24077. z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v, 5);w=ror(w, 2);
  24078. #define R4(v, w, x, y, z, i) \
  24079. z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v, 5);w=ror(w, 2);
  24080. /*
  24081. * Hash a single 512-bit block. This is the core of the algorithm.
  24082. */
  24083. #define a qq[0]
  24084. #define b qq[1]
  24085. #define c qq[2]
  24086. #define d qq[3]
  24087. #define e qq[4]
  24088. static void SHA1Transform(unsigned int state[5], const unsigned char buffer[64])
  24089. {
  24090. unsigned int qq[5]; /* a, b, c, d, e; */
  24091. static int one = 1;
  24092. unsigned int block[16];
  24093. SyMemcpy(buffer, (void *)block, 64);
  24094. SyMemcpy(state, qq, 5*sizeof(unsigned int));
  24095. /* Copy context->state[] to working vars */
  24096. /*
  24097. a = state[0];
  24098. b = state[1];
  24099. c = state[2];
  24100. d = state[3];
  24101. e = state[4];
  24102. */
  24103. /* 4 rounds of 20 operations each. Loop unrolled. */
  24104. if( 1 == *(unsigned char*)&one ){
  24105. 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);
  24106. 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);
  24107. 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);
  24108. 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);
  24109. }else{
  24110. 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);
  24111. 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);
  24112. 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);
  24113. 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);
  24114. }
  24115. 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);
  24116. 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);
  24117. 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);
  24118. 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);
  24119. 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);
  24120. 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);
  24121. 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);
  24122. 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);
  24123. 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);
  24124. 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);
  24125. 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);
  24126. 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);
  24127. 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);
  24128. 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);
  24129. 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);
  24130. 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);
  24131. /* Add the working vars back into context.state[] */
  24132. state[0] += a;
  24133. state[1] += b;
  24134. state[2] += c;
  24135. state[3] += d;
  24136. state[4] += e;
  24137. }
  24138. #undef a
  24139. #undef b
  24140. #undef c
  24141. #undef d
  24142. #undef e
  24143. /*
  24144. * SHA1Init - Initialize new context
  24145. */
  24146. JX9_PRIVATE void SHA1Init(SHA1Context *context){
  24147. /* SHA1 initialization constants */
  24148. context->state[0] = 0x67452301;
  24149. context->state[1] = 0xEFCDAB89;
  24150. context->state[2] = 0x98BADCFE;
  24151. context->state[3] = 0x10325476;
  24152. context->state[4] = 0xC3D2E1F0;
  24153. context->count[0] = context->count[1] = 0;
  24154. }
  24155. /*
  24156. * Run your data through this.
  24157. */
  24158. JX9_PRIVATE void SHA1Update(SHA1Context *context, const unsigned char *data, unsigned int len){
  24159. unsigned int i, j;
  24160. j = context->count[0];
  24161. if ((context->count[0] += len << 3) < j)
  24162. context->count[1] += (len>>29)+1;
  24163. j = (j >> 3) & 63;
  24164. if ((j + len) > 63) {
  24165. (void)SyMemcpy(data, &context->buffer[j], (i = 64-j));
  24166. SHA1Transform(context->state, context->buffer);
  24167. for ( ; i + 63 < len; i += 64)
  24168. SHA1Transform(context->state, &data[i]);
  24169. j = 0;
  24170. } else {
  24171. i = 0;
  24172. }
  24173. (void)SyMemcpy(&data[i], &context->buffer[j], len - i);
  24174. }
  24175. /*
  24176. * Add padding and return the message digest.
  24177. */
  24178. JX9_PRIVATE void SHA1Final(SHA1Context *context, unsigned char digest[20]){
  24179. unsigned int i;
  24180. unsigned char finalcount[8];
  24181. for (i = 0; i < 8; i++) {
  24182. finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
  24183. >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
  24184. }
  24185. SHA1Update(context, (const unsigned char *)"\200", 1);
  24186. while ((context->count[0] & 504) != 448)
  24187. SHA1Update(context, (const unsigned char *)"\0", 1);
  24188. SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
  24189. if (digest) {
  24190. for (i = 0; i < 20; i++)
  24191. digest[i] = (unsigned char)
  24192. ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
  24193. }
  24194. }
  24195. #undef Rl0
  24196. #undef Rb0
  24197. #undef R1
  24198. #undef R2
  24199. #undef R3
  24200. #undef R4
  24201. JX9_PRIVATE sxi32 SySha1Compute(const void *pIn, sxu32 nLen, unsigned char zDigest[20])
  24202. {
  24203. SHA1Context sCtx;
  24204. SHA1Init(&sCtx);
  24205. SHA1Update(&sCtx, (const unsigned char *)pIn, nLen);
  24206. SHA1Final(&sCtx, zDigest);
  24207. return SXRET_OK;
  24208. }
  24209. static const sxu32 crc32_table[] = {
  24210. 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
  24211. 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
  24212. 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
  24213. 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
  24214. 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
  24215. 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
  24216. 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
  24217. 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
  24218. 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
  24219. 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
  24220. 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
  24221. 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
  24222. 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
  24223. 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
  24224. 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
  24225. 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
  24226. 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
  24227. 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
  24228. 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
  24229. 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
  24230. 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
  24231. 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
  24232. 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
  24233. 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
  24234. 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
  24235. 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
  24236. 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
  24237. 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
  24238. 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
  24239. 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
  24240. 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
  24241. 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
  24242. 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
  24243. 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
  24244. 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
  24245. 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
  24246. 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
  24247. 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
  24248. 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
  24249. 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
  24250. 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
  24251. 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
  24252. 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
  24253. 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
  24254. 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
  24255. 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
  24256. 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
  24257. 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
  24258. 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
  24259. 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
  24260. 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
  24261. 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
  24262. 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
  24263. 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
  24264. 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
  24265. 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
  24266. 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
  24267. 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
  24268. 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
  24269. 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
  24270. 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
  24271. 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
  24272. 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
  24273. 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
  24274. };
  24275. #define CRC32C(c, d) (c = ( crc32_table[(c ^ (d)) & 0xFF] ^ (c>>8) ) )
  24276. static sxu32 SyCrc32Update(sxu32 crc32, const void *pSrc, sxu32 nLen)
  24277. {
  24278. register unsigned char *zIn = (unsigned char *)pSrc;
  24279. unsigned char *zEnd;
  24280. if( zIn == 0 ){
  24281. return crc32;
  24282. }
  24283. zEnd = &zIn[nLen];
  24284. for(;;){
  24285. if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++;
  24286. if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++;
  24287. if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++;
  24288. if(zIn >= zEnd ){ break; } CRC32C(crc32, zIn[0]); zIn++;
  24289. }
  24290. return crc32;
  24291. }
  24292. JX9_PRIVATE sxu32 SyCrc32(const void *pSrc, sxu32 nLen)
  24293. {
  24294. return SyCrc32Update(SXU32_HIGH, pSrc, nLen);
  24295. }
  24296. #endif /* JX9_DISABLE_HASH_FUNC */
  24297. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  24298. #ifndef JX9_DISABLE_BUILTIN_FUNC
  24299. JX9_PRIVATE sxi32 SyBinToHexConsumer(const void *pIn, sxu32 nLen, ProcConsumer xConsumer, void *pConsumerData)
  24300. {
  24301. static const unsigned char zHexTab[] = "0123456789abcdef";
  24302. const unsigned char *zIn, *zEnd;
  24303. unsigned char zOut[3];
  24304. sxi32 rc;
  24305. #if defined(UNTRUST)
  24306. if( pIn == 0 || xConsumer == 0 ){
  24307. return SXERR_EMPTY;
  24308. }
  24309. #endif
  24310. zIn = (const unsigned char *)pIn;
  24311. zEnd = &zIn[nLen];
  24312. for(;;){
  24313. if( zIn >= zEnd ){
  24314. break;
  24315. }
  24316. zOut[0] = zHexTab[zIn[0] >> 4]; zOut[1] = zHexTab[zIn[0] & 0x0F];
  24317. rc = xConsumer((const void *)zOut, sizeof(char)*2, pConsumerData);
  24318. if( rc != SXRET_OK ){
  24319. return rc;
  24320. }
  24321. zIn++;
  24322. }
  24323. return SXRET_OK;
  24324. }
  24325. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  24326. /*
  24327. * ----------------------------------------------------------
  24328. * File: lex.c
  24329. * MD5: 170223620630c661afed749eeae4c9c1
  24330. * ----------------------------------------------------------
  24331. */
  24332. /*
  24333. * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
  24334. * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
  24335. * Version 1.7.2
  24336. * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  24337. * please contact Symisc Systems via:
  24338. * legal@symisc.net
  24339. * licensing@symisc.net
  24340. * contact@symisc.net
  24341. * or visit:
  24342. * http://jx9.symisc.net/
  24343. */
  24344. /* $SymiscID: lex.c v1.0 FreeBSD 2012-12-09 00:19 stable <chm@symisc.net> $ */
  24345. #ifndef JX9_AMALGAMATION
  24346. #include "jx9Int.h"
  24347. #endif
  24348. /* This file implements a thread-safe and full reentrant lexical analyzer for the unQL programming language */
  24349. /* Forward declarations */
  24350. static sxu32 keywordCode(const char *z,int n);
  24351. static sxi32 LexExtractNowdoc(SyStream *pStream,SyToken *pToken);
  24352. /*
  24353. * Tokenize a raw jx9 input.
  24354. * Get a single low-level token from the input file. Update the stream pointer so that
  24355. * it points to the first character beyond the extracted token.
  24356. */
  24357. static sxi32 jx9TokenizeInput(SyStream *pStream,SyToken *pToken,void *pUserData,void *pCtxData)
  24358. {
  24359. SyString *pStr;
  24360. sxi32 rc;
  24361. /* Ignore leading white spaces */
  24362. while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisSpace(pStream->zText[0]) ){
  24363. /* Advance the stream cursor */
  24364. if( pStream->zText[0] == '\n' ){
  24365. /* Update line counter */
  24366. pStream->nLine++;
  24367. }
  24368. pStream->zText++;
  24369. }
  24370. if( pStream->zText >= pStream->zEnd ){
  24371. /* End of input reached */
  24372. return SXERR_EOF;
  24373. }
  24374. /* Record token starting position and line */
  24375. pToken->nLine = pStream->nLine;
  24376. pToken->pUserData = 0;
  24377. pStr = &pToken->sData;
  24378. SyStringInitFromBuf(pStr, pStream->zText, 0);
  24379. if( pStream->zText[0] >= 0xc0 || SyisAlpha(pStream->zText[0]) || pStream->zText[0] == '_' ){
  24380. /* The following code fragment is taken verbatim from the xPP source tree.
  24381. * xPP is a modern embeddable macro processor with advanced features useful for
  24382. * application seeking for a production quality, ready to use macro processor.
  24383. * xPP is a widely used library developed and maintened by Symisc Systems.
  24384. * You can reach the xPP home page by following this link:
  24385. * http://xpp.symisc.net/
  24386. */
  24387. const unsigned char *zIn;
  24388. sxu32 nKeyword;
  24389. /* Isolate UTF-8 or alphanumeric stream */
  24390. if( pStream->zText[0] < 0xc0 ){
  24391. pStream->zText++;
  24392. }
  24393. for(;;){
  24394. zIn = pStream->zText;
  24395. if( zIn[0] >= 0xc0 ){
  24396. zIn++;
  24397. /* UTF-8 stream */
  24398. while( zIn < pStream->zEnd && ((zIn[0] & 0xc0) == 0x80) ){
  24399. zIn++;
  24400. }
  24401. }
  24402. /* Skip alphanumeric stream */
  24403. while( zIn < pStream->zEnd && zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_') ){
  24404. zIn++;
  24405. }
  24406. if( zIn == pStream->zText ){
  24407. /* Not an UTF-8 or alphanumeric stream */
  24408. break;
  24409. }
  24410. /* Synchronize pointers */
  24411. pStream->zText = zIn;
  24412. }
  24413. /* Record token length */
  24414. pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
  24415. nKeyword = keywordCode(pStr->zString, (int)pStr->nByte);
  24416. if( nKeyword != JX9_TK_ID ){
  24417. /* We are dealing with a keyword [i.e: if, function, CREATE, ...], save the keyword ID */
  24418. pToken->nType = JX9_TK_KEYWORD;
  24419. pToken->pUserData = SX_INT_TO_PTR(nKeyword);
  24420. }else{
  24421. /* A simple identifier */
  24422. pToken->nType = JX9_TK_ID;
  24423. }
  24424. }else{
  24425. sxi32 c;
  24426. /* Non-alpha stream */
  24427. if( pStream->zText[0] == '#' ||
  24428. ( pStream->zText[0] == '/' && &pStream->zText[1] < pStream->zEnd && pStream->zText[1] == '/') ){
  24429. pStream->zText++;
  24430. /* Inline comments */
  24431. while( pStream->zText < pStream->zEnd && pStream->zText[0] != '\n' ){
  24432. pStream->zText++;
  24433. }
  24434. /* Tell the upper-layer to ignore this token */
  24435. return SXERR_CONTINUE;
  24436. }else if( pStream->zText[0] == '/' && &pStream->zText[1] < pStream->zEnd && pStream->zText[1] == '*' ){
  24437. pStream->zText += 2;
  24438. /* Block comment */
  24439. while( pStream->zText < pStream->zEnd ){
  24440. if( pStream->zText[0] == '*' ){
  24441. if( &pStream->zText[1] >= pStream->zEnd || pStream->zText[1] == '/' ){
  24442. break;
  24443. }
  24444. }
  24445. if( pStream->zText[0] == '\n' ){
  24446. pStream->nLine++;
  24447. }
  24448. pStream->zText++;
  24449. }
  24450. pStream->zText += 2;
  24451. /* Tell the upper-layer to ignore this token */
  24452. return SXERR_CONTINUE;
  24453. }else if( SyisDigit(pStream->zText[0]) ){
  24454. pStream->zText++;
  24455. /* Decimal digit stream */
  24456. while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
  24457. pStream->zText++;
  24458. }
  24459. /* Mark the token as integer until we encounter a real number */
  24460. pToken->nType = JX9_TK_INTEGER;
  24461. if( pStream->zText < pStream->zEnd ){
  24462. c = pStream->zText[0];
  24463. if( c == '.' ){
  24464. /* Real number */
  24465. pStream->zText++;
  24466. while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
  24467. pStream->zText++;
  24468. }
  24469. if( pStream->zText < pStream->zEnd ){
  24470. c = pStream->zText[0];
  24471. if( c=='e' || c=='E' ){
  24472. pStream->zText++;
  24473. if( pStream->zText < pStream->zEnd ){
  24474. c = pStream->zText[0];
  24475. if( (c =='+' || c=='-') && &pStream->zText[1] < pStream->zEnd &&
  24476. pStream->zText[1] < 0xc0 && SyisDigit(pStream->zText[1]) ){
  24477. pStream->zText++;
  24478. }
  24479. while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
  24480. pStream->zText++;
  24481. }
  24482. }
  24483. }
  24484. }
  24485. pToken->nType = JX9_TK_REAL;
  24486. }else if( c=='e' || c=='E' ){
  24487. SXUNUSED(pUserData); /* Prevent compiler warning */
  24488. SXUNUSED(pCtxData);
  24489. pStream->zText++;
  24490. if( pStream->zText < pStream->zEnd ){
  24491. c = pStream->zText[0];
  24492. if( (c =='+' || c=='-') && &pStream->zText[1] < pStream->zEnd &&
  24493. pStream->zText[1] < 0xc0 && SyisDigit(pStream->zText[1]) ){
  24494. pStream->zText++;
  24495. }
  24496. while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
  24497. pStream->zText++;
  24498. }
  24499. }
  24500. pToken->nType = JX9_TK_REAL;
  24501. }else if( c == 'x' || c == 'X' ){
  24502. /* Hex digit stream */
  24503. pStream->zText++;
  24504. while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisHex(pStream->zText[0]) ){
  24505. pStream->zText++;
  24506. }
  24507. }else if(c == 'b' || c == 'B' ){
  24508. /* Binary digit stream */
  24509. pStream->zText++;
  24510. while( pStream->zText < pStream->zEnd && (pStream->zText[0] == '0' || pStream->zText[0] == '1') ){
  24511. pStream->zText++;
  24512. }
  24513. }
  24514. }
  24515. /* Record token length */
  24516. pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
  24517. return SXRET_OK;
  24518. }
  24519. c = pStream->zText[0];
  24520. pStream->zText++; /* Advance the stream cursor */
  24521. /* Assume we are dealing with an operator*/
  24522. pToken->nType = JX9_TK_OP;
  24523. switch(c){
  24524. case '$': pToken->nType = JX9_TK_DOLLAR; break;
  24525. case '{': pToken->nType = JX9_TK_OCB; break;
  24526. case '}': pToken->nType = JX9_TK_CCB; break;
  24527. case '(': pToken->nType = JX9_TK_LPAREN; break;
  24528. case '[': pToken->nType |= JX9_TK_OSB; break; /* Bitwise operation here, since the square bracket token '['
  24529. * is a potential operator [i.e: subscripting] */
  24530. case ']': pToken->nType = JX9_TK_CSB; break;
  24531. case ')': {
  24532. SySet *pTokSet = pStream->pSet;
  24533. /* Assemble type cast operators [i.e: (int), (float), (bool)...] */
  24534. if( pTokSet->nUsed >= 2 ){
  24535. SyToken *pTmp;
  24536. /* Peek the last recongnized token */
  24537. pTmp = (SyToken *)SySetPeek(pTokSet);
  24538. if( pTmp->nType & JX9_TK_KEYWORD ){
  24539. sxi32 nID = SX_PTR_TO_INT(pTmp->pUserData);
  24540. if( (sxu32)nID & (JX9_TKWRD_INT|JX9_TKWRD_FLOAT|JX9_TKWRD_STRING|JX9_TKWRD_BOOL) ){
  24541. pTmp = (SyToken *)SySetAt(pTokSet, pTokSet->nUsed - 2);
  24542. if( pTmp->nType & JX9_TK_LPAREN ){
  24543. /* Merge the three tokens '(' 'TYPE' ')' into a single one */
  24544. const char * zTypeCast = "(int)";
  24545. if( nID & JX9_TKWRD_FLOAT ){
  24546. zTypeCast = "(float)";
  24547. }else if( nID & JX9_TKWRD_BOOL ){
  24548. zTypeCast = "(bool)";
  24549. }else if( nID & JX9_TKWRD_STRING ){
  24550. zTypeCast = "(string)";
  24551. }
  24552. /* Reflect the change */
  24553. pToken->nType = JX9_TK_OP;
  24554. SyStringInitFromBuf(&pToken->sData, zTypeCast, SyStrlen(zTypeCast));
  24555. /* Save the instance associated with the type cast operator */
  24556. pToken->pUserData = (void *)jx9ExprExtractOperator(&pToken->sData, 0);
  24557. /* Remove the two previous tokens */
  24558. pTokSet->nUsed -= 2;
  24559. return SXRET_OK;
  24560. }
  24561. }
  24562. }
  24563. }
  24564. pToken->nType = JX9_TK_RPAREN;
  24565. break;
  24566. }
  24567. case '\'':{
  24568. /* Single quoted string */
  24569. pStr->zString++;
  24570. while( pStream->zText < pStream->zEnd ){
  24571. if( pStream->zText[0] == '\'' ){
  24572. if( pStream->zText[-1] != '\\' ){
  24573. break;
  24574. }else{
  24575. const unsigned char *zPtr = &pStream->zText[-2];
  24576. sxi32 i = 1;
  24577. while( zPtr > pStream->zInput && zPtr[0] == '\\' ){
  24578. zPtr--;
  24579. i++;
  24580. }
  24581. if((i&1)==0){
  24582. break;
  24583. }
  24584. }
  24585. }
  24586. if( pStream->zText[0] == '\n' ){
  24587. pStream->nLine++;
  24588. }
  24589. pStream->zText++;
  24590. }
  24591. /* Record token length and type */
  24592. pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
  24593. pToken->nType = JX9_TK_SSTR;
  24594. /* Jump the trailing single quote */
  24595. pStream->zText++;
  24596. return SXRET_OK;
  24597. }
  24598. case '"':{
  24599. sxi32 iNest;
  24600. /* Double quoted string */
  24601. pStr->zString++;
  24602. while( pStream->zText < pStream->zEnd ){
  24603. if( pStream->zText[0] == '{' && &pStream->zText[1] < pStream->zEnd && pStream->zText[1] == '$'){
  24604. iNest = 1;
  24605. pStream->zText++;
  24606. /* TICKET 1433-40: Hnadle braces'{}' in double quoted string where everything is allowed */
  24607. while(pStream->zText < pStream->zEnd ){
  24608. if( pStream->zText[0] == '{' ){
  24609. iNest++;
  24610. }else if (pStream->zText[0] == '}' ){
  24611. iNest--;
  24612. if( iNest <= 0 ){
  24613. pStream->zText++;
  24614. break;
  24615. }
  24616. }else if( pStream->zText[0] == '\n' ){
  24617. pStream->nLine++;
  24618. }
  24619. pStream->zText++;
  24620. }
  24621. if( pStream->zText >= pStream->zEnd ){
  24622. break;
  24623. }
  24624. }
  24625. if( pStream->zText[0] == '"' ){
  24626. if( pStream->zText[-1] != '\\' ){
  24627. break;
  24628. }else{
  24629. const unsigned char *zPtr = &pStream->zText[-2];
  24630. sxi32 i = 1;
  24631. while( zPtr > pStream->zInput && zPtr[0] == '\\' ){
  24632. zPtr--;
  24633. i++;
  24634. }
  24635. if((i&1)==0){
  24636. break;
  24637. }
  24638. }
  24639. }
  24640. if( pStream->zText[0] == '\n' ){
  24641. pStream->nLine++;
  24642. }
  24643. pStream->zText++;
  24644. }
  24645. /* Record token length and type */
  24646. pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
  24647. pToken->nType = JX9_TK_DSTR;
  24648. /* Jump the trailing quote */
  24649. pStream->zText++;
  24650. return SXRET_OK;
  24651. }
  24652. case ':':
  24653. pToken->nType = JX9_TK_COLON; /* Single colon */
  24654. break;
  24655. case ',': pToken->nType |= JX9_TK_COMMA; break; /* Comma is also an operator */
  24656. case ';': pToken->nType = JX9_TK_SEMI; break;
  24657. /* Handle combined operators [i.e: +=, ===, !=== ...] */
  24658. case '=':
  24659. pToken->nType |= JX9_TK_EQUAL;
  24660. if( pStream->zText < pStream->zEnd ){
  24661. if( pStream->zText[0] == '=' ){
  24662. pToken->nType &= ~JX9_TK_EQUAL;
  24663. /* Current operator: == */
  24664. pStream->zText++;
  24665. if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
  24666. /* Current operator: === */
  24667. pStream->zText++;
  24668. }
  24669. }
  24670. }
  24671. break;
  24672. case '!':
  24673. if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
  24674. /* Current operator: != */
  24675. pStream->zText++;
  24676. if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
  24677. /* Current operator: !== */
  24678. pStream->zText++;
  24679. }
  24680. }
  24681. break;
  24682. case '&':
  24683. pToken->nType |= JX9_TK_AMPER;
  24684. if( pStream->zText < pStream->zEnd ){
  24685. if( pStream->zText[0] == '&' ){
  24686. pToken->nType &= ~JX9_TK_AMPER;
  24687. /* Current operator: && */
  24688. pStream->zText++;
  24689. }else if( pStream->zText[0] == '=' ){
  24690. pToken->nType &= ~JX9_TK_AMPER;
  24691. /* Current operator: &= */
  24692. pStream->zText++;
  24693. }
  24694. }
  24695. case '.':
  24696. if( pStream->zText < pStream->zEnd && (pStream->zText[0] == '.' || pStream->zText[0] == '=') ){
  24697. /* Concatenation operator: '..' or '.=' */
  24698. pStream->zText++;
  24699. }
  24700. break;
  24701. case '|':
  24702. if( pStream->zText < pStream->zEnd ){
  24703. if( pStream->zText[0] == '|' ){
  24704. /* Current operator: || */
  24705. pStream->zText++;
  24706. }else if( pStream->zText[0] == '=' ){
  24707. /* Current operator: |= */
  24708. pStream->zText++;
  24709. }
  24710. }
  24711. break;
  24712. case '+':
  24713. if( pStream->zText < pStream->zEnd ){
  24714. if( pStream->zText[0] == '+' ){
  24715. /* Current operator: ++ */
  24716. pStream->zText++;
  24717. }else if( pStream->zText[0] == '=' ){
  24718. /* Current operator: += */
  24719. pStream->zText++;
  24720. }
  24721. }
  24722. break;
  24723. case '-':
  24724. if( pStream->zText < pStream->zEnd ){
  24725. if( pStream->zText[0] == '-' ){
  24726. /* Current operator: -- */
  24727. pStream->zText++;
  24728. }else if( pStream->zText[0] == '=' ){
  24729. /* Current operator: -= */
  24730. pStream->zText++;
  24731. }else if( pStream->zText[0] == '>' ){
  24732. /* Current operator: -> */
  24733. pStream->zText++;
  24734. }
  24735. }
  24736. break;
  24737. case '*':
  24738. if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
  24739. /* Current operator: *= */
  24740. pStream->zText++;
  24741. }
  24742. break;
  24743. case '/':
  24744. if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
  24745. /* Current operator: /= */
  24746. pStream->zText++;
  24747. }
  24748. break;
  24749. case '%':
  24750. if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
  24751. /* Current operator: %= */
  24752. pStream->zText++;
  24753. }
  24754. break;
  24755. case '^':
  24756. if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
  24757. /* Current operator: ^= */
  24758. pStream->zText++;
  24759. }
  24760. break;
  24761. case '<':
  24762. if( pStream->zText < pStream->zEnd ){
  24763. if( pStream->zText[0] == '<' ){
  24764. /* Current operator: << */
  24765. pStream->zText++;
  24766. if( pStream->zText < pStream->zEnd ){
  24767. if( pStream->zText[0] == '=' ){
  24768. /* Current operator: <<= */
  24769. pStream->zText++;
  24770. }else if( pStream->zText[0] == '<' ){
  24771. /* Current Token: <<< */
  24772. pStream->zText++;
  24773. /* This may be the beginning of a Heredoc/Nowdoc string, try to delimit it */
  24774. rc = LexExtractNowdoc(&(*pStream), &(*pToken));
  24775. if( rc == SXRET_OK ){
  24776. /* Here/Now doc successfuly extracted */
  24777. return SXRET_OK;
  24778. }
  24779. }
  24780. }
  24781. }else if( pStream->zText[0] == '>' ){
  24782. /* Current operator: <> */
  24783. pStream->zText++;
  24784. }else if( pStream->zText[0] == '=' ){
  24785. /* Current operator: <= */
  24786. pStream->zText++;
  24787. }
  24788. }
  24789. break;
  24790. case '>':
  24791. if( pStream->zText < pStream->zEnd ){
  24792. if( pStream->zText[0] == '>' ){
  24793. /* Current operator: >> */
  24794. pStream->zText++;
  24795. if( pStream->zText < pStream->zEnd && pStream->zText[0] == '=' ){
  24796. /* Current operator: >>= */
  24797. pStream->zText++;
  24798. }
  24799. }else if( pStream->zText[0] == '=' ){
  24800. /* Current operator: >= */
  24801. pStream->zText++;
  24802. }
  24803. }
  24804. break;
  24805. default:
  24806. break;
  24807. }
  24808. if( pStr->nByte <= 0 ){
  24809. /* Record token length */
  24810. pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
  24811. }
  24812. if( pToken->nType & JX9_TK_OP ){
  24813. const jx9_expr_op *pOp;
  24814. /* Check if the extracted token is an operator */
  24815. pOp = jx9ExprExtractOperator(pStr, (SyToken *)SySetPeek(pStream->pSet));
  24816. if( pOp == 0 ){
  24817. /* Not an operator */
  24818. pToken->nType &= ~JX9_TK_OP;
  24819. if( pToken->nType <= 0 ){
  24820. pToken->nType = JX9_TK_OTHER;
  24821. }
  24822. }else{
  24823. /* Save the instance associated with this operator for later processing */
  24824. pToken->pUserData = (void *)pOp;
  24825. }
  24826. }
  24827. }
  24828. /* Tell the upper-layer to save the extracted token for later processing */
  24829. return SXRET_OK;
  24830. }
  24831. /***** This file contains automatically generated code ******
  24832. **
  24833. ** The code in this file has been automatically generated by
  24834. **
  24835. ** $Header: /sqlite/sqlite/tool/mkkeywordhash.c,v 1.38 2011/12/21 01:00:46 <chm@symisc.net> $
  24836. **
  24837. ** The code in this file implements a function that determines whether
  24838. ** or not a given identifier is really a JX9 keyword. The same thing
  24839. ** might be implemented more directly using a hand-written hash table.
  24840. ** But by using this automatically generated code, the size of the code
  24841. ** is substantially reduced. This is important for embedded applications
  24842. ** on platforms with limited memory.
  24843. */
  24844. /* Hash score: 35 */
  24845. static sxu32 keywordCode(const char *z, int n)
  24846. {
  24847. /* zText[] encodes 188 bytes of keywords in 128 bytes */
  24848. /* printegereturnconstaticaselseifloatincludefaultDIEXITcontinue */
  24849. /* diewhileASPRINTbooleanbreakforeachfunctionimportstringswitch */
  24850. /* uplink */
  24851. static const char zText[127] = {
  24852. 'p','r','i','n','t','e','g','e','r','e','t','u','r','n','c','o','n','s',
  24853. 't','a','t','i','c','a','s','e','l','s','e','i','f','l','o','a','t','i',
  24854. 'n','c','l','u','d','e','f','a','u','l','t','D','I','E','X','I','T','c',
  24855. 'o','n','t','i','n','u','e','d','i','e','w','h','i','l','e','A','S','P',
  24856. 'R','I','N','T','b','o','o','l','e','a','n','b','r','e','a','k','f','o',
  24857. 'r','e','a','c','h','f','u','n','c','t','i','o','n','i','m','p','o','r',
  24858. 't','s','t','r','i','n','g','s','w','i','t','c','h','u','p','l','i','n',
  24859. 'k',
  24860. };
  24861. static const unsigned char aHash[59] = {
  24862. 0, 0, 0, 0, 15, 0, 30, 0, 0, 2, 19, 18, 0,
  24863. 0, 10, 3, 12, 0, 28, 29, 23, 0, 13, 22, 0, 0,
  24864. 14, 24, 25, 31, 11, 0, 0, 0, 0, 1, 5, 0, 0,
  24865. 20, 0, 27, 9, 0, 0, 0, 8, 0, 0, 26, 6, 0,
  24866. 0, 17, 0, 0, 0, 0, 0,
  24867. };
  24868. static const unsigned char aNext[31] = {
  24869. 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0,
  24870. 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 21, 7,
  24871. 0, 0, 0, 0, 0,
  24872. };
  24873. static const unsigned char aLen[31] = {
  24874. 5, 7, 3, 6, 5, 6, 4, 2, 6, 4, 2, 5, 7,
  24875. 7, 3, 4, 8, 3, 5, 2, 5, 4, 7, 5, 3, 7,
  24876. 8, 6, 6, 6, 6,
  24877. };
  24878. static const sxu16 aOffset[31] = {
  24879. 0, 2, 2, 8, 14, 17, 22, 23, 25, 25, 29, 30, 35,
  24880. 40, 47, 49, 53, 61, 64, 69, 71, 76, 76, 83, 88, 88,
  24881. 95, 103, 109, 115, 121,
  24882. };
  24883. static const sxu32 aCode[31] = {
  24884. JX9_TKWRD_PRINT, JX9_TKWRD_INT, JX9_TKWRD_INT, JX9_TKWRD_RETURN, JX9_TKWRD_CONST,
  24885. JX9_TKWRD_STATIC, JX9_TKWRD_CASE, JX9_TKWRD_AS, JX9_TKWRD_ELIF, JX9_TKWRD_ELSE,
  24886. JX9_TKWRD_IF, JX9_TKWRD_FLOAT, JX9_TKWRD_INCLUDE, JX9_TKWRD_DEFAULT, JX9_TKWRD_DIE,
  24887. JX9_TKWRD_EXIT, JX9_TKWRD_CONTINUE, JX9_TKWRD_DIE, JX9_TKWRD_WHILE, JX9_TKWRD_AS,
  24888. JX9_TKWRD_PRINT, JX9_TKWRD_BOOL, JX9_TKWRD_BOOL, JX9_TKWRD_BREAK, JX9_TKWRD_FOR,
  24889. JX9_TKWRD_FOREACH, JX9_TKWRD_FUNCTION, JX9_TKWRD_IMPORT, JX9_TKWRD_STRING, JX9_TKWRD_SWITCH,
  24890. JX9_TKWRD_UPLINK,
  24891. };
  24892. int h, i;
  24893. if( n<2 ) return JX9_TK_ID;
  24894. h = (((int)z[0]*4) ^ ((int)z[n-1]*3) ^ n) % 59;
  24895. for(i=((int)aHash[h])-1; i>=0; i=((int)aNext[i])-1){
  24896. if( (int)aLen[i]==n && SyMemcmp(&zText[aOffset[i]],z,n)==0 ){
  24897. /* JX9_TKWRD_PRINT */
  24898. /* JX9_TKWRD_INT */
  24899. /* JX9_TKWRD_INT */
  24900. /* JX9_TKWRD_RETURN */
  24901. /* JX9_TKWRD_CONST */
  24902. /* JX9_TKWRD_STATIC */
  24903. /* JX9_TKWRD_CASE */
  24904. /* JX9_TKWRD_AS */
  24905. /* JX9_TKWRD_ELIF */
  24906. /* JX9_TKWRD_ELSE */
  24907. /* JX9_TKWRD_IF */
  24908. /* JX9_TKWRD_FLOAT */
  24909. /* JX9_TKWRD_INCLUDE */
  24910. /* JX9_TKWRD_DEFAULT */
  24911. /* JX9_TKWRD_DIE */
  24912. /* JX9_TKWRD_EXIT */
  24913. /* JX9_TKWRD_CONTINUE */
  24914. /* JX9_TKWRD_DIE */
  24915. /* JX9_TKWRD_WHILE */
  24916. /* JX9_TKWRD_AS */
  24917. /* JX9_TKWRD_PRINT */
  24918. /* JX9_TKWRD_BOOL */
  24919. /* JX9_TKWRD_BOOL */
  24920. /* JX9_TKWRD_BREAK */
  24921. /* JX9_TKWRD_FOR */
  24922. /* JX9_TKWRD_FOREACH */
  24923. /* JX9_TKWRD_FUNCTION */
  24924. /* JX9_TKWRD_IMPORT */
  24925. /* JX9_TKWRD_STRING */
  24926. /* JX9_TKWRD_SWITCH */
  24927. /* JX9_TKWRD_UPLINK */
  24928. return aCode[i];
  24929. }
  24930. }
  24931. return JX9_TK_ID;
  24932. }
  24933. /*
  24934. * Extract a heredoc/nowdoc text from a raw JX9 input.
  24935. * According to the JX9 language reference manual:
  24936. * A third way to delimit strings is the heredoc syntax: <<<. After this operator, an identifier
  24937. * is provided, then a newline. The string itself follows, and then the same identifier again
  24938. * to close the quotation.
  24939. * The closing identifier must begin in the first column of the line. Also, the identifier must
  24940. * follow the same naming rules as any other label in JX9: it must contain only alphanumeric
  24941. * characters and underscores, and must start with a non-digit character or underscore.
  24942. * Heredoc text behaves just like a double-quoted string, without the double quotes.
  24943. * This means that quotes in a heredoc do not need to be escaped, but the escape codes listed
  24944. * above can still be used. Variables are expanded, but the same care must be taken when expressing
  24945. * complex variables inside a heredoc as with strings.
  24946. * Nowdocs are to single-quoted strings what heredocs are to double-quoted strings.
  24947. * A nowdoc is specified similarly to a heredoc, but no parsing is done inside a nowdoc.
  24948. * The construct is ideal for embedding JX9 code or other large blocks of text without the need
  24949. * for escaping. It shares some features in common with the SGML <![CDATA[ ]]> construct, in that
  24950. * it declares a block of text which is not for parsing.
  24951. * A nowdoc is identified with the same <<< sequence used for heredocs, but the identifier which follows
  24952. * is enclosed in single quotes, e.g. <<<'EOT'. All the rules for heredoc identifiers also apply to nowdoc
  24953. * identifiers, especially those regarding the appearance of the closing identifier.
  24954. */
  24955. static sxi32 LexExtractNowdoc(SyStream *pStream, SyToken *pToken)
  24956. {
  24957. const unsigned char *zIn = pStream->zText;
  24958. const unsigned char *zEnd = pStream->zEnd;
  24959. const unsigned char *zPtr;
  24960. SyString sDelim;
  24961. SyString sStr;
  24962. /* Jump leading white spaces */
  24963. while( zIn < zEnd && zIn[0] < 0xc0 && SyisSpace(zIn[0]) && zIn[0] != '\n' ){
  24964. zIn++;
  24965. }
  24966. if( zIn >= zEnd ){
  24967. /* A simple symbol, return immediately */
  24968. return SXERR_CONTINUE;
  24969. }
  24970. if( zIn[0] == '\'' || zIn[0] == '"' ){
  24971. zIn++;
  24972. }
  24973. if( zIn[0] < 0xc0 && !SyisAlphaNum(zIn[0]) && zIn[0] != '_' ){
  24974. /* Invalid delimiter, return immediately */
  24975. return SXERR_CONTINUE;
  24976. }
  24977. /* Isolate the identifier */
  24978. sDelim.zString = (const char *)zIn;
  24979. for(;;){
  24980. zPtr = zIn;
  24981. /* Skip alphanumeric stream */
  24982. while( zPtr < zEnd && zPtr[0] < 0xc0 && (SyisAlphaNum(zPtr[0]) || zPtr[0] == '_') ){
  24983. zPtr++;
  24984. }
  24985. if( zPtr < zEnd && zPtr[0] >= 0xc0 ){
  24986. zPtr++;
  24987. /* UTF-8 stream */
  24988. while( zPtr < zEnd && ((zPtr[0] & 0xc0) == 0x80) ){
  24989. zPtr++;
  24990. }
  24991. }
  24992. if( zPtr == zIn ){
  24993. /* Not an UTF-8 or alphanumeric stream */
  24994. break;
  24995. }
  24996. /* Synchronize pointers */
  24997. zIn = zPtr;
  24998. }
  24999. /* Get the identifier length */
  25000. sDelim.nByte = (sxu32)((const char *)zIn-sDelim.zString);
  25001. if( zIn[0] == '"' || zIn[0] == '\'' ){
  25002. /* Jump the trailing single quote */
  25003. zIn++;
  25004. }
  25005. /* Jump trailing white spaces */
  25006. while( zIn < zEnd && zIn[0] < 0xc0 && SyisSpace(zIn[0]) && zIn[0] != '\n' ){
  25007. zIn++;
  25008. }
  25009. if( sDelim.nByte <= 0 || zIn >= zEnd || zIn[0] != '\n' ){
  25010. /* Invalid syntax */
  25011. return SXERR_CONTINUE;
  25012. }
  25013. pStream->nLine++; /* Increment line counter */
  25014. zIn++;
  25015. /* Isolate the delimited string */
  25016. sStr.zString = (const char *)zIn;
  25017. /* Go and found the closing delimiter */
  25018. for(;;){
  25019. /* Synchronize with the next line */
  25020. while( zIn < zEnd && zIn[0] != '\n' ){
  25021. zIn++;
  25022. }
  25023. if( zIn >= zEnd ){
  25024. /* End of the input reached, break immediately */
  25025. pStream->zText = pStream->zEnd;
  25026. break;
  25027. }
  25028. pStream->nLine++; /* Increment line counter */
  25029. zIn++;
  25030. if( (sxu32)(zEnd - zIn) >= sDelim.nByte && SyMemcmp((const void *)sDelim.zString, (const void *)zIn, sDelim.nByte) == 0 ){
  25031. zPtr = &zIn[sDelim.nByte];
  25032. while( zPtr < zEnd && zPtr[0] < 0xc0 && SyisSpace(zPtr[0]) && zPtr[0] != '\n' ){
  25033. zPtr++;
  25034. }
  25035. if( zPtr >= zEnd ){
  25036. /* End of input */
  25037. pStream->zText = zPtr;
  25038. break;
  25039. }
  25040. if( zPtr[0] == ';' ){
  25041. const unsigned char *zCur = zPtr;
  25042. zPtr++;
  25043. while( zPtr < zEnd && zPtr[0] < 0xc0 && SyisSpace(zPtr[0]) && zPtr[0] != '\n' ){
  25044. zPtr++;
  25045. }
  25046. if( zPtr >= zEnd || zPtr[0] == '\n' ){
  25047. /* Closing delimiter found, break immediately */
  25048. pStream->zText = zCur; /* Keep the semi-colon */
  25049. break;
  25050. }
  25051. }else if( zPtr[0] == '\n' ){
  25052. /* Closing delimiter found, break immediately */
  25053. pStream->zText = zPtr; /* Synchronize with the stream cursor */
  25054. break;
  25055. }
  25056. /* Synchronize pointers and continue searching */
  25057. zIn = zPtr;
  25058. }
  25059. } /* For(;;) */
  25060. /* Get the delimited string length */
  25061. sStr.nByte = (sxu32)((const char *)zIn-sStr.zString);
  25062. /* Record token type and length */
  25063. pToken->nType = JX9_TK_NOWDOC;
  25064. SyStringDupPtr(&pToken->sData, &sStr);
  25065. /* Remove trailing white spaces */
  25066. SyStringRightTrim(&pToken->sData);
  25067. /* All done */
  25068. return SXRET_OK;
  25069. }
  25070. /*
  25071. * Tokenize a raw jx9 input.
  25072. * This is the public tokenizer called by most code generator routines.
  25073. */
  25074. JX9_PRIVATE sxi32 jx9Tokenize(const char *zInput,sxu32 nLen,SySet *pOut)
  25075. {
  25076. SyLex sLexer;
  25077. sxi32 rc;
  25078. /* Initialize the lexer */
  25079. rc = SyLexInit(&sLexer, &(*pOut),jx9TokenizeInput,0);
  25080. if( rc != SXRET_OK ){
  25081. return rc;
  25082. }
  25083. /* Tokenize input */
  25084. rc = SyLexTokenizeInput(&sLexer, zInput, nLen, 0, 0, 0);
  25085. /* Release the lexer */
  25086. SyLexRelease(&sLexer);
  25087. /* Tokenization result */
  25088. return rc;
  25089. }
  25090. /*
  25091. * ----------------------------------------------------------
  25092. * File: json.c
  25093. * MD5: 31a27f8797418de511c669feed763341
  25094. * ----------------------------------------------------------
  25095. */
  25096. /*
  25097. * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
  25098. * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
  25099. * Version 1.7.2
  25100. * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  25101. * please contact Symisc Systems via:
  25102. * legal@symisc.net
  25103. * licensing@symisc.net
  25104. * contact@symisc.net
  25105. * or visit:
  25106. * http://jx9.symisc.net/
  25107. */
  25108. /* $SymiscID: json.c v1.0 FreeBSD 2012-12-16 00:28 stable <chm@symisc.net> $ */
  25109. #ifndef JX9_AMALGAMATION
  25110. #include "jx9Int.h"
  25111. #endif
  25112. /* This file deals with JSON serialization, decoding and stuff like that. */
  25113. /*
  25114. * Section:
  25115. * JSON encoding/decoding routines.
  25116. * Authors:
  25117. * Symisc Systems, devel@symisc.net.
  25118. * Copyright (C) Symisc Systems, http://jx9.symisc.net
  25119. * Status:
  25120. * Devel.
  25121. */
  25122. /* Forward reference */
  25123. static int VmJsonArrayEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData);
  25124. static int VmJsonObjectEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData);
  25125. /*
  25126. * JSON encoder state is stored in an instance
  25127. * of the following structure.
  25128. */
  25129. typedef struct json_private_data json_private_data;
  25130. struct json_private_data
  25131. {
  25132. SyBlob *pOut; /* Output consumer buffer */
  25133. int isFirst; /* True if first encoded entry */
  25134. int iFlags; /* JSON encoding flags */
  25135. int nRecCount; /* Recursion count */
  25136. };
  25137. /*
  25138. * Returns the JSON representation of a value.In other word perform a JSON encoding operation.
  25139. * According to wikipedia
  25140. * JSON's basic types are:
  25141. * Number (double precision floating-point format in JavaScript, generally depends on implementation)
  25142. * String (double-quoted Unicode, with backslash escaping)
  25143. * Boolean (true or false)
  25144. * Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values
  25145. * do not need to be of the same type)
  25146. * Object (an unordered collection of key:value pairs with the ':' character separating the key
  25147. * and the value, comma-separated and enclosed in curly braces; the keys must be strings and should
  25148. * be distinct from each other)
  25149. * null (empty)
  25150. * Non-significant white space may be added freely around the "structural characters"
  25151. * (i.e. the brackets "[{]}", colon ":" and comma ",").
  25152. */
  25153. static sxi32 VmJsonEncode(
  25154. jx9_value *pIn, /* Encode this value */
  25155. json_private_data *pData /* Context data */
  25156. ){
  25157. SyBlob *pOut = pData->pOut;
  25158. int nByte;
  25159. if( jx9_value_is_null(pIn) || jx9_value_is_resource(pIn)){
  25160. /* null */
  25161. SyBlobAppend(pOut, "null", sizeof("null")-1);
  25162. }else if( jx9_value_is_bool(pIn) ){
  25163. int iBool = jx9_value_to_bool(pIn);
  25164. sxu32 iLen;
  25165. /* true/false */
  25166. iLen = iBool ? sizeof("true") : sizeof("false");
  25167. SyBlobAppend(pOut, iBool ? "true" : "false", iLen-1);
  25168. }else if( jx9_value_is_numeric(pIn) && !jx9_value_is_string(pIn) ){
  25169. const char *zNum;
  25170. /* Get a string representation of the number */
  25171. zNum = jx9_value_to_string(pIn, &nByte);
  25172. SyBlobAppend(pOut,zNum,nByte);
  25173. }else if( jx9_value_is_string(pIn) ){
  25174. const char *zIn, *zEnd;
  25175. int c;
  25176. /* Encode the string */
  25177. zIn = jx9_value_to_string(pIn, &nByte);
  25178. zEnd = &zIn[nByte];
  25179. /* Append the double quote */
  25180. SyBlobAppend(pOut,"\"", sizeof(char));
  25181. for(;;){
  25182. if( zIn >= zEnd ){
  25183. /* No more input to process */
  25184. break;
  25185. }
  25186. c = zIn[0];
  25187. /* Advance the stream cursor */
  25188. zIn++;
  25189. if( c == '"' || c == '\\' ){
  25190. /* Unescape the character */
  25191. SyBlobAppend(pOut,"\\", sizeof(char));
  25192. }
  25193. /* Append character verbatim */
  25194. SyBlobAppend(pOut,(const char *)&c,sizeof(char));
  25195. }
  25196. /* Append the double quote */
  25197. SyBlobAppend(pOut,"\"",sizeof(char));
  25198. }else if( jx9_value_is_json_array(pIn) ){
  25199. /* Encode the array/object */
  25200. pData->isFirst = 1;
  25201. if( jx9_value_is_json_object(pIn) ){
  25202. /* Encode the object instance */
  25203. pData->isFirst = 1;
  25204. /* Append the curly braces */
  25205. SyBlobAppend(pOut, "{",sizeof(char));
  25206. /* Iterate throw object attribute */
  25207. jx9_array_walk(pIn,VmJsonObjectEncode, pData);
  25208. /* Append the closing curly braces */
  25209. SyBlobAppend(pOut, "}",sizeof(char));
  25210. }else{
  25211. /* Append the square bracket or curly braces */
  25212. SyBlobAppend(pOut,"[",sizeof(char));
  25213. /* Iterate throw array entries */
  25214. jx9_array_walk(pIn, VmJsonArrayEncode, pData);
  25215. /* Append the closing square bracket or curly braces */
  25216. SyBlobAppend(pOut,"]",sizeof(char));
  25217. }
  25218. }else{
  25219. /* Can't happen */
  25220. SyBlobAppend(pOut,"null",sizeof("null")-1);
  25221. }
  25222. /* All done */
  25223. return JX9_OK;
  25224. }
  25225. /*
  25226. * The following walker callback is invoked each time we need
  25227. * to encode an array to JSON.
  25228. */
  25229. static int VmJsonArrayEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData)
  25230. {
  25231. json_private_data *pJson = (json_private_data *)pUserData;
  25232. if( pJson->nRecCount > 31 ){
  25233. /* Recursion limit reached, return immediately */
  25234. SXUNUSED(pKey); /* cc warning */
  25235. return JX9_OK;
  25236. }
  25237. if( !pJson->isFirst ){
  25238. /* Append the colon first */
  25239. SyBlobAppend(pJson->pOut,",",(int)sizeof(char));
  25240. }
  25241. /* Encode the value */
  25242. pJson->nRecCount++;
  25243. VmJsonEncode(pValue, pJson);
  25244. pJson->nRecCount--;
  25245. pJson->isFirst = 0;
  25246. return JX9_OK;
  25247. }
  25248. /*
  25249. * The following walker callback is invoked each time we need to encode
  25250. * a object instance [i.e: Object in the JX9 jargon] to JSON.
  25251. */
  25252. static int VmJsonObjectEncode(jx9_value *pKey,jx9_value *pValue,void *pUserData)
  25253. {
  25254. json_private_data *pJson = (json_private_data *)pUserData;
  25255. const char *zKey;
  25256. int nByte;
  25257. if( pJson->nRecCount > 31 ){
  25258. /* Recursion limit reached, return immediately */
  25259. return JX9_OK;
  25260. }
  25261. if( !pJson->isFirst ){
  25262. /* Append the colon first */
  25263. SyBlobAppend(pJson->pOut,",",sizeof(char));
  25264. }
  25265. /* Extract a string representation of the key */
  25266. zKey = jx9_value_to_string(pKey, &nByte);
  25267. /* Append the key and the double colon */
  25268. if( nByte > 0 ){
  25269. SyBlobAppend(pJson->pOut,"\"",sizeof(char));
  25270. SyBlobAppend(pJson->pOut,zKey,(sxu32)nByte);
  25271. SyBlobAppend(pJson->pOut,"\"",sizeof(char));
  25272. }else{
  25273. /* Can't happen */
  25274. SyBlobAppend(pJson->pOut,"null",sizeof("null")-1);
  25275. }
  25276. SyBlobAppend(pJson->pOut,":",sizeof(char));
  25277. /* Encode the value */
  25278. pJson->nRecCount++;
  25279. VmJsonEncode(pValue, pJson);
  25280. pJson->nRecCount--;
  25281. pJson->isFirst = 0;
  25282. return JX9_OK;
  25283. }
  25284. /*
  25285. * Returns a string containing the JSON representation of value.
  25286. * In other words, perform the serialization of the given JSON object.
  25287. */
  25288. JX9_PRIVATE int jx9JsonSerialize(jx9_value *pValue,SyBlob *pOut)
  25289. {
  25290. json_private_data sJson;
  25291. /* Prepare the JSON data */
  25292. sJson.nRecCount = 0;
  25293. sJson.pOut = pOut;
  25294. sJson.isFirst = 1;
  25295. sJson.iFlags = 0;
  25296. /* Perform the encoding operation */
  25297. VmJsonEncode(pValue, &sJson);
  25298. /* All done */
  25299. return JX9_OK;
  25300. }
  25301. /* Possible tokens from the JSON tokenization process */
  25302. #define JSON_TK_TRUE 0x001 /* Boolean true */
  25303. #define JSON_TK_FALSE 0x002 /* Boolean false */
  25304. #define JSON_TK_STR 0x004 /* String enclosed in double quotes */
  25305. #define JSON_TK_NULL 0x008 /* null */
  25306. #define JSON_TK_NUM 0x010 /* Numeric */
  25307. #define JSON_TK_OCB 0x020 /* Open curly braces '{' */
  25308. #define JSON_TK_CCB 0x040 /* Closing curly braces '}' */
  25309. #define JSON_TK_OSB 0x080 /* Open square bracke '[' */
  25310. #define JSON_TK_CSB 0x100 /* Closing square bracket ']' */
  25311. #define JSON_TK_COLON 0x200 /* Single colon ':' */
  25312. #define JSON_TK_COMMA 0x400 /* Single comma ',' */
  25313. #define JSON_TK_ID 0x800 /* ID */
  25314. #define JSON_TK_INVALID 0x1000 /* Unexpected token */
  25315. /*
  25316. * Tokenize an entire JSON input.
  25317. * Get a single low-level token from the input file.
  25318. * Update the stream pointer so that it points to the first
  25319. * character beyond the extracted token.
  25320. */
  25321. static sxi32 VmJsonTokenize(SyStream *pStream, SyToken *pToken, void *pUserData, void *pCtxData)
  25322. {
  25323. int *pJsonErr = (int *)pUserData;
  25324. SyString *pStr;
  25325. int c;
  25326. /* Ignore leading white spaces */
  25327. while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisSpace(pStream->zText[0]) ){
  25328. /* Advance the stream cursor */
  25329. if( pStream->zText[0] == '\n' ){
  25330. /* Update line counter */
  25331. pStream->nLine++;
  25332. }
  25333. pStream->zText++;
  25334. }
  25335. if( pStream->zText >= pStream->zEnd ){
  25336. /* End of input reached */
  25337. SXUNUSED(pCtxData); /* cc warning */
  25338. return SXERR_EOF;
  25339. }
  25340. /* Record token starting position and line */
  25341. pToken->nLine = pStream->nLine;
  25342. pToken->pUserData = 0;
  25343. pStr = &pToken->sData;
  25344. SyStringInitFromBuf(pStr, pStream->zText, 0);
  25345. if( pStream->zText[0] >= 0xc0 || SyisAlpha(pStream->zText[0]) || pStream->zText[0] == '_' ){
  25346. /* The following code fragment is taken verbatim from the xPP source tree.
  25347. * xPP is a modern embeddable macro processor with advanced features useful for
  25348. * application seeking for a production quality, ready to use macro processor.
  25349. * xPP is a widely used library developed and maintened by Symisc Systems.
  25350. * You can reach the xPP home page by following this link:
  25351. * http://xpp.symisc.net/
  25352. */
  25353. const unsigned char *zIn;
  25354. /* Isolate UTF-8 or alphanumeric stream */
  25355. if( pStream->zText[0] < 0xc0 ){
  25356. pStream->zText++;
  25357. }
  25358. for(;;){
  25359. zIn = pStream->zText;
  25360. if( zIn[0] >= 0xc0 ){
  25361. zIn++;
  25362. /* UTF-8 stream */
  25363. while( zIn < pStream->zEnd && ((zIn[0] & 0xc0) == 0x80) ){
  25364. zIn++;
  25365. }
  25366. }
  25367. /* Skip alphanumeric stream */
  25368. while( zIn < pStream->zEnd && zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_') ){
  25369. zIn++;
  25370. }
  25371. if( zIn == pStream->zText ){
  25372. /* Not an UTF-8 or alphanumeric stream */
  25373. break;
  25374. }
  25375. /* Synchronize pointers */
  25376. pStream->zText = zIn;
  25377. }
  25378. /* Record token length */
  25379. pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
  25380. /* A simple identifier */
  25381. pToken->nType = JSON_TK_ID;
  25382. if( pStr->nByte == sizeof("true") -1 && SyStrnicmp(pStr->zString, "true", sizeof("true")-1) == 0 ){
  25383. /* boolean true */
  25384. pToken->nType = JSON_TK_TRUE;
  25385. }else if( pStr->nByte == sizeof("false") -1 && SyStrnicmp(pStr->zString,"false", sizeof("false")-1) == 0 ){
  25386. /* boolean false */
  25387. pToken->nType = JSON_TK_FALSE;
  25388. }else if( pStr->nByte == sizeof("null") -1 && SyStrnicmp(pStr->zString,"null", sizeof("null")-1) == 0 ){
  25389. /* NULL */
  25390. pToken->nType = JSON_TK_NULL;
  25391. }
  25392. return SXRET_OK;
  25393. }
  25394. if( pStream->zText[0] == '{' || pStream->zText[0] == '[' || pStream->zText[0] == '}' || pStream->zText[0] == ']'
  25395. || pStream->zText[0] == ':' || pStream->zText[0] == ',' ){
  25396. /* Single character */
  25397. c = pStream->zText[0];
  25398. /* Set token type */
  25399. switch(c){
  25400. case '[': pToken->nType = JSON_TK_OSB; break;
  25401. case '{': pToken->nType = JSON_TK_OCB; break;
  25402. case '}': pToken->nType = JSON_TK_CCB; break;
  25403. case ']': pToken->nType = JSON_TK_CSB; break;
  25404. case ':': pToken->nType = JSON_TK_COLON; break;
  25405. case ',': pToken->nType = JSON_TK_COMMA; break;
  25406. default:
  25407. break;
  25408. }
  25409. /* Advance the stream cursor */
  25410. pStream->zText++;
  25411. }else if( pStream->zText[0] == '"') {
  25412. /* JSON string */
  25413. pStream->zText++;
  25414. pStr->zString++;
  25415. /* Delimit the string */
  25416. while( pStream->zText < pStream->zEnd ){
  25417. if( pStream->zText[0] == '"' && pStream->zText[-1] != '\\' ){
  25418. break;
  25419. }
  25420. if( pStream->zText[0] == '\n' ){
  25421. /* Update line counter */
  25422. pStream->nLine++;
  25423. }
  25424. pStream->zText++;
  25425. }
  25426. if( pStream->zText >= pStream->zEnd ){
  25427. /* Missing closing '"' */
  25428. pToken->nType = JSON_TK_INVALID;
  25429. *pJsonErr = SXERR_SYNTAX;
  25430. }else{
  25431. pToken->nType = JSON_TK_STR;
  25432. pStream->zText++; /* Jump the closing double quotes */
  25433. }
  25434. }else if( pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
  25435. /* Number */
  25436. pStream->zText++;
  25437. pToken->nType = JSON_TK_NUM;
  25438. while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
  25439. pStream->zText++;
  25440. }
  25441. if( pStream->zText < pStream->zEnd ){
  25442. c = pStream->zText[0];
  25443. if( c == '.' ){
  25444. /* Real number */
  25445. pStream->zText++;
  25446. while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
  25447. pStream->zText++;
  25448. }
  25449. if( pStream->zText < pStream->zEnd ){
  25450. c = pStream->zText[0];
  25451. if( c=='e' || c=='E' ){
  25452. pStream->zText++;
  25453. if( pStream->zText < pStream->zEnd ){
  25454. c = pStream->zText[0];
  25455. if( c =='+' || c=='-' ){
  25456. pStream->zText++;
  25457. }
  25458. while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
  25459. pStream->zText++;
  25460. }
  25461. }
  25462. }
  25463. }
  25464. }else if( c=='e' || c=='E' ){
  25465. /* Real number */
  25466. pStream->zText++;
  25467. if( pStream->zText < pStream->zEnd ){
  25468. c = pStream->zText[0];
  25469. if( c =='+' || c=='-' ){
  25470. pStream->zText++;
  25471. }
  25472. while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
  25473. pStream->zText++;
  25474. }
  25475. }
  25476. }
  25477. }
  25478. }else{
  25479. /* Unexpected token */
  25480. pToken->nType = JSON_TK_INVALID;
  25481. /* Advance the stream cursor */
  25482. pStream->zText++;
  25483. *pJsonErr = SXERR_SYNTAX;
  25484. /* Abort processing immediatley */
  25485. return SXERR_ABORT;
  25486. }
  25487. /* record token length */
  25488. pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
  25489. if( pToken->nType == JSON_TK_STR ){
  25490. pStr->nByte--;
  25491. }
  25492. /* Return to the lexer */
  25493. return SXRET_OK;
  25494. }
  25495. /*
  25496. * JSON decoded input consumer callback signature.
  25497. */
  25498. typedef int (*ProcJSONConsumer)(jx9_context *, jx9_value *, jx9_value *, void *);
  25499. /*
  25500. * JSON decoder state is kept in the following structure.
  25501. */
  25502. typedef struct json_decoder json_decoder;
  25503. struct json_decoder
  25504. {
  25505. jx9_context *pCtx; /* Call context */
  25506. ProcJSONConsumer xConsumer; /* Consumer callback */
  25507. void *pUserData; /* Last argument to xConsumer() */
  25508. int iFlags; /* Configuration flags */
  25509. SyToken *pIn; /* Token stream */
  25510. SyToken *pEnd; /* End of the token stream */
  25511. int rec_count; /* Current nesting level */
  25512. int *pErr; /* JSON decoding error if any */
  25513. };
  25514. /* Forward declaration */
  25515. static int VmJsonArrayDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData);
  25516. /*
  25517. * Dequote [i.e: Resolve all backslash escapes ] a JSON string and store
  25518. * the result in the given jx9_value.
  25519. */
  25520. static void VmJsonDequoteString(const SyString *pStr, jx9_value *pWorker)
  25521. {
  25522. const char *zIn = pStr->zString;
  25523. const char *zEnd = &pStr->zString[pStr->nByte];
  25524. const char *zCur;
  25525. int c;
  25526. /* Mark the value as a string */
  25527. jx9_value_string(pWorker, "", 0); /* Empty string */
  25528. for(;;){
  25529. zCur = zIn;
  25530. while( zIn < zEnd && zIn[0] != '\\' ){
  25531. zIn++;
  25532. }
  25533. if( zIn > zCur ){
  25534. /* Append chunk verbatim */
  25535. jx9_value_string(pWorker, zCur, (int)(zIn-zCur));
  25536. }
  25537. zIn++;
  25538. if( zIn >= zEnd ){
  25539. /* End of the input reached */
  25540. break;
  25541. }
  25542. c = zIn[0];
  25543. /* Unescape the character */
  25544. switch(c){
  25545. case '"': jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char)); break;
  25546. case '\\': jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char)); break;
  25547. case 'n': jx9_value_string(pWorker, "\n", (int)sizeof(char)); break;
  25548. case 'r': jx9_value_string(pWorker, "\r", (int)sizeof(char)); break;
  25549. case 't': jx9_value_string(pWorker, "\t", (int)sizeof(char)); break;
  25550. case 'f': jx9_value_string(pWorker, "\f", (int)sizeof(char)); break;
  25551. default:
  25552. jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char));
  25553. break;
  25554. }
  25555. /* Advance the stream cursor */
  25556. zIn++;
  25557. }
  25558. }
  25559. /*
  25560. * Returns a jx9_value holding the image of a JSON string. In other word perform a JSON decoding operation.
  25561. * According to wikipedia
  25562. * JSON's basic types are:
  25563. * Number (double precision floating-point format in JavaScript, generally depends on implementation)
  25564. * String (double-quoted Unicode, with backslash escaping)
  25565. * Boolean (true or false)
  25566. * Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values
  25567. * do not need to be of the same type)
  25568. * Object (an unordered collection of key:value pairs with the ':' character separating the key
  25569. * and the value, comma-separated and enclosed in curly braces; the keys must be strings and should
  25570. * be distinct from each other)
  25571. * null (empty)
  25572. * Non-significant white space may be added freely around the "structural characters" (i.e. the brackets "[{]}", colon ":" and comma ", ").
  25573. */
  25574. static sxi32 VmJsonDecode(
  25575. json_decoder *pDecoder, /* JSON decoder */
  25576. jx9_value *pArrayKey /* Key for the decoded array */
  25577. ){
  25578. jx9_value *pWorker; /* Worker variable */
  25579. sxi32 rc;
  25580. /* Check if we do not nest to much */
  25581. if( pDecoder->rec_count > 31 ){
  25582. /* Nesting limit reached, abort decoding immediately */
  25583. return SXERR_ABORT;
  25584. }
  25585. if( pDecoder->pIn->nType & (JSON_TK_STR|JSON_TK_ID|JSON_TK_TRUE|JSON_TK_FALSE|JSON_TK_NULL|JSON_TK_NUM) ){
  25586. /* Scalar value */
  25587. pWorker = jx9_context_new_scalar(pDecoder->pCtx);
  25588. if( pWorker == 0 ){
  25589. jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
  25590. /* Abort the decoding operation immediately */
  25591. return SXERR_ABORT;
  25592. }
  25593. /* Reflect the JSON image */
  25594. if( pDecoder->pIn->nType & JSON_TK_NULL ){
  25595. /* Nullify the value.*/
  25596. jx9_value_null(pWorker);
  25597. }else if( pDecoder->pIn->nType & (JSON_TK_TRUE|JSON_TK_FALSE) ){
  25598. /* Boolean value */
  25599. jx9_value_bool(pWorker, (pDecoder->pIn->nType & JSON_TK_TRUE) ? 1 : 0 );
  25600. }else if( pDecoder->pIn->nType & JSON_TK_NUM ){
  25601. SyString *pStr = &pDecoder->pIn->sData;
  25602. /*
  25603. * Numeric value.
  25604. * Get a string representation first then try to get a numeric
  25605. * value.
  25606. */
  25607. jx9_value_string(pWorker, pStr->zString, (int)pStr->nByte);
  25608. /* Obtain a numeric representation */
  25609. jx9MemObjToNumeric(pWorker);
  25610. }else if( pDecoder->pIn->nType & JSON_TK_ID ){
  25611. SyString *pStr = &pDecoder->pIn->sData;
  25612. jx9_value_string(pWorker, pStr->zString, (int)pStr->nByte);
  25613. }else{
  25614. /* Dequote the string */
  25615. VmJsonDequoteString(&pDecoder->pIn->sData, pWorker);
  25616. }
  25617. /* Invoke the consumer callback */
  25618. rc = pDecoder->xConsumer(pDecoder->pCtx, pArrayKey, pWorker, pDecoder->pUserData);
  25619. if( rc == SXERR_ABORT ){
  25620. return SXERR_ABORT;
  25621. }
  25622. /* All done, advance the stream cursor */
  25623. pDecoder->pIn++;
  25624. }else if( pDecoder->pIn->nType & JSON_TK_OSB /*'[' */) {
  25625. ProcJSONConsumer xOld;
  25626. void *pOld;
  25627. /* Array representation*/
  25628. pDecoder->pIn++;
  25629. /* Create a working array */
  25630. pWorker = jx9_context_new_array(pDecoder->pCtx);
  25631. if( pWorker == 0 ){
  25632. jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
  25633. /* Abort the decoding operation immediately */
  25634. return SXERR_ABORT;
  25635. }
  25636. /* Save the old consumer */
  25637. xOld = pDecoder->xConsumer;
  25638. pOld = pDecoder->pUserData;
  25639. /* Set the new consumer */
  25640. pDecoder->xConsumer = VmJsonArrayDecoder;
  25641. pDecoder->pUserData = pWorker;
  25642. /* Decode the array */
  25643. for(;;){
  25644. /* Jump trailing comma. Note that the standard JX9 engine will not let you
  25645. * do this.
  25646. */
  25647. while( (pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA) ){
  25648. pDecoder->pIn++;
  25649. }
  25650. if( pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CSB) /*']'*/ ){
  25651. if( pDecoder->pIn < pDecoder->pEnd ){
  25652. pDecoder->pIn++; /* Jump the trailing ']' */
  25653. }
  25654. break;
  25655. }
  25656. /* Recurse and decode the entry */
  25657. pDecoder->rec_count++;
  25658. rc = VmJsonDecode(pDecoder, 0);
  25659. pDecoder->rec_count--;
  25660. if( rc == SXERR_ABORT ){
  25661. /* Abort processing immediately */
  25662. return SXERR_ABORT;
  25663. }
  25664. /*The cursor is automatically advanced by the VmJsonDecode() function */
  25665. if( (pDecoder->pIn < pDecoder->pEnd) &&
  25666. ((pDecoder->pIn->nType & (JSON_TK_CSB/*']'*/|JSON_TK_COMMA/*','*/))==0) ){
  25667. /* Unexpected token, abort immediatley */
  25668. *pDecoder->pErr = SXERR_SYNTAX;
  25669. return SXERR_ABORT;
  25670. }
  25671. }
  25672. /* Restore the old consumer */
  25673. pDecoder->xConsumer = xOld;
  25674. pDecoder->pUserData = pOld;
  25675. /* Invoke the old consumer on the decoded array */
  25676. xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld);
  25677. }else if( pDecoder->pIn->nType & JSON_TK_OCB /*'{' */) {
  25678. ProcJSONConsumer xOld;
  25679. jx9_value *pKey;
  25680. void *pOld;
  25681. /* Object representation*/
  25682. pDecoder->pIn++;
  25683. /* Create a working array */
  25684. pWorker = jx9_context_new_array(pDecoder->pCtx);
  25685. pKey = jx9_context_new_scalar(pDecoder->pCtx);
  25686. if( pWorker == 0 || pKey == 0){
  25687. jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
  25688. /* Abort the decoding operation immediately */
  25689. return SXERR_ABORT;
  25690. }
  25691. /* Save the old consumer */
  25692. xOld = pDecoder->xConsumer;
  25693. pOld = pDecoder->pUserData;
  25694. /* Set the new consumer */
  25695. pDecoder->xConsumer = VmJsonArrayDecoder;
  25696. pDecoder->pUserData = pWorker;
  25697. /* Decode the object */
  25698. for(;;){
  25699. /* Jump trailing comma. Note that the standard JX9 engine will not let you
  25700. * do this.
  25701. */
  25702. while( (pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA) ){
  25703. pDecoder->pIn++;
  25704. }
  25705. if( pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CCB) /*'}'*/ ){
  25706. if( pDecoder->pIn < pDecoder->pEnd ){
  25707. pDecoder->pIn++; /* Jump the trailing ']' */
  25708. }
  25709. break;
  25710. }
  25711. if( (pDecoder->pIn->nType & (JSON_TK_ID|JSON_TK_STR)) == 0 || &pDecoder->pIn[1] >= pDecoder->pEnd
  25712. || (pDecoder->pIn[1].nType & JSON_TK_COLON) == 0){
  25713. /* Syntax error, return immediately */
  25714. *pDecoder->pErr = SXERR_SYNTAX;
  25715. return SXERR_ABORT;
  25716. }
  25717. if( pDecoder->pIn->nType & JSON_TK_ID ){
  25718. SyString *pStr = &pDecoder->pIn->sData;
  25719. jx9_value_string(pKey, pStr->zString, (int)pStr->nByte);
  25720. }else{
  25721. /* Dequote the key */
  25722. VmJsonDequoteString(&pDecoder->pIn->sData, pKey);
  25723. }
  25724. /* Jump the key and the colon */
  25725. pDecoder->pIn += 2;
  25726. /* Recurse and decode the value */
  25727. pDecoder->rec_count++;
  25728. rc = VmJsonDecode(pDecoder, pKey);
  25729. pDecoder->rec_count--;
  25730. if( rc == SXERR_ABORT ){
  25731. /* Abort processing immediately */
  25732. return SXERR_ABORT;
  25733. }
  25734. /* Reset the internal buffer of the key */
  25735. jx9_value_reset_string_cursor(pKey);
  25736. /*The cursor is automatically advanced by the VmJsonDecode() function */
  25737. }
  25738. /* Restore the old consumer */
  25739. pDecoder->xConsumer = xOld;
  25740. pDecoder->pUserData = pOld;
  25741. /* Invoke the old consumer on the decoded object*/
  25742. xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld);
  25743. /* Release the key */
  25744. jx9_context_release_value(pDecoder->pCtx, pKey);
  25745. }else{
  25746. /* Unexpected token */
  25747. return SXERR_ABORT; /* Abort immediately */
  25748. }
  25749. /* Release the worker variable */
  25750. jx9_context_release_value(pDecoder->pCtx, pWorker);
  25751. return SXRET_OK;
  25752. }
  25753. /*
  25754. * The following JSON decoder callback is invoked each time
  25755. * a JSON array representation [i.e: [15, "hello", FALSE] ]
  25756. * is being decoded.
  25757. */
  25758. static int VmJsonArrayDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData)
  25759. {
  25760. jx9_value *pArray = (jx9_value *)pUserData;
  25761. /* Insert the entry */
  25762. jx9_array_add_elem(pArray, pKey, pWorker); /* Will make it's own copy */
  25763. SXUNUSED(pCtx); /* cc warning */
  25764. /* All done */
  25765. return SXRET_OK;
  25766. }
  25767. /*
  25768. * Standard JSON decoder callback.
  25769. */
  25770. static int VmJsonDefaultDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData)
  25771. {
  25772. /* Return the value directly */
  25773. jx9_result_value(pCtx, pWorker); /* Will make it's own copy */
  25774. SXUNUSED(pKey); /* cc warning */
  25775. SXUNUSED(pUserData);
  25776. /* All done */
  25777. return SXRET_OK;
  25778. }
  25779. /*
  25780. * Exported JSON decoding interface
  25781. */
  25782. JX9_PRIVATE int jx9JsonDecode(jx9_context *pCtx,const char *zJSON,int nByte)
  25783. {
  25784. jx9_vm *pVm = pCtx->pVm;
  25785. json_decoder sDecoder;
  25786. SySet sToken;
  25787. SyLex sLex;
  25788. sxi32 rc;
  25789. /* Tokenize the input */
  25790. SySetInit(&sToken, &pVm->sAllocator, sizeof(SyToken));
  25791. rc = SXRET_OK;
  25792. SyLexInit(&sLex, &sToken, VmJsonTokenize, &rc);
  25793. SyLexTokenizeInput(&sLex,zJSON,(sxu32)nByte, 0, 0, 0);
  25794. if( rc != SXRET_OK ){
  25795. /* Something goes wrong while tokenizing input. [i.e: Unexpected token] */
  25796. SyLexRelease(&sLex);
  25797. SySetRelease(&sToken);
  25798. /* return NULL */
  25799. jx9_result_null(pCtx);
  25800. return JX9_OK;
  25801. }
  25802. /* Fill the decoder */
  25803. sDecoder.pCtx = pCtx;
  25804. sDecoder.pErr = &rc;
  25805. sDecoder.pIn = (SyToken *)SySetBasePtr(&sToken);
  25806. sDecoder.pEnd = &sDecoder.pIn[SySetUsed(&sToken)];
  25807. sDecoder.iFlags = 0;
  25808. sDecoder.rec_count = 0;
  25809. /* Set a default consumer */
  25810. sDecoder.xConsumer = VmJsonDefaultDecoder;
  25811. sDecoder.pUserData = 0;
  25812. /* Decode the raw JSON input */
  25813. rc = VmJsonDecode(&sDecoder, 0);
  25814. if( rc == SXERR_ABORT ){
  25815. /*
  25816. * Something goes wrong while decoding JSON input.Return NULL.
  25817. */
  25818. jx9_result_null(pCtx);
  25819. }
  25820. /* Clean-up the mess left behind */
  25821. SyLexRelease(&sLex);
  25822. SySetRelease(&sToken);
  25823. /* All done */
  25824. return JX9_OK;
  25825. }
  25826. /*
  25827. * ----------------------------------------------------------
  25828. * File: hashmap.c
  25829. * MD5: 428857d599e2b9adb9dc5a9fefcbd2a9
  25830. * ----------------------------------------------------------
  25831. */
  25832. /*
  25833. * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
  25834. * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
  25835. * Version 1.7.2
  25836. * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  25837. * please contact Symisc Systems via:
  25838. * legal@symisc.net
  25839. * licensing@symisc.net
  25840. * contact@symisc.net
  25841. * or visit:
  25842. * http://jx9.symisc.net/
  25843. */
  25844. /* $SymiscID: hashmap.c v2.6 Win7 2012-12-11 00:50 stable <chm@symisc.net> $ */
  25845. #ifndef JX9_AMALGAMATION
  25846. #include "jx9Int.h"
  25847. #endif
  25848. /* This file implement generic hashmaps used to represent JSON arrays and objects */
  25849. /* Allowed node types */
  25850. #define HASHMAP_INT_NODE 1 /* Node with an int [i.e: 64-bit integer] key */
  25851. #define HASHMAP_BLOB_NODE 2 /* Node with a string/BLOB key */
  25852. /*
  25853. * Default hash function for int [i.e; 64-bit integer] keys.
  25854. */
  25855. static sxu32 IntHash(sxi64 iKey)
  25856. {
  25857. return (sxu32)(iKey ^ (iKey << 8) ^ (iKey >> 8));
  25858. }
  25859. /*
  25860. * Default hash function for string/BLOB keys.
  25861. */
  25862. static sxu32 BinHash(const void *pSrc, sxu32 nLen)
  25863. {
  25864. register unsigned char *zIn = (unsigned char *)pSrc;
  25865. unsigned char *zEnd;
  25866. sxu32 nH = 5381;
  25867. zEnd = &zIn[nLen];
  25868. for(;;){
  25869. if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
  25870. if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
  25871. if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
  25872. if( zIn >= zEnd ){ break; } nH = nH * 33 + zIn[0] ; zIn++;
  25873. }
  25874. return nH;
  25875. }
  25876. /*
  25877. * Return the total number of entries in a given hashmap.
  25878. * If bRecurisve is set to TRUE then recurse on hashmap entries.
  25879. * If the nesting limit is reached, this function abort immediately.
  25880. */
  25881. static sxi64 HashmapCount(jx9_hashmap *pMap, int bRecursive, int iRecCount)
  25882. {
  25883. sxi64 iCount = 0;
  25884. if( !bRecursive ){
  25885. iCount = pMap->nEntry;
  25886. }else{
  25887. /* Recursive hashmap walk */
  25888. jx9_hashmap_node *pEntry = pMap->pLast;
  25889. jx9_value *pElem;
  25890. sxu32 n = 0;
  25891. for(;;){
  25892. if( n >= pMap->nEntry ){
  25893. break;
  25894. }
  25895. /* Point to the element value */
  25896. pElem = (jx9_value *)SySetAt(&pMap->pVm->aMemObj, pEntry->nValIdx);
  25897. if( pElem ){
  25898. if( pElem->iFlags & MEMOBJ_HASHMAP ){
  25899. if( iRecCount > 31 ){
  25900. /* Nesting limit reached */
  25901. return iCount;
  25902. }
  25903. /* Recurse */
  25904. iRecCount++;
  25905. iCount += HashmapCount((jx9_hashmap *)pElem->x.pOther, TRUE, iRecCount);
  25906. iRecCount--;
  25907. }
  25908. }
  25909. /* Point to the next entry */
  25910. pEntry = pEntry->pNext;
  25911. ++n;
  25912. }
  25913. /* Update count */
  25914. iCount += pMap->nEntry;
  25915. }
  25916. return iCount;
  25917. }
  25918. /*
  25919. * Allocate a new hashmap node with a 64-bit integer key.
  25920. * If something goes wrong [i.e: out of memory], this function return NULL.
  25921. * Otherwise a fresh [jx9_hashmap_node] instance is returned.
  25922. */
  25923. static jx9_hashmap_node * HashmapNewIntNode(jx9_hashmap *pMap, sxi64 iKey, sxu32 nHash, sxu32 nValIdx)
  25924. {
  25925. jx9_hashmap_node *pNode;
  25926. /* Allocate a new node */
  25927. pNode = (jx9_hashmap_node *)SyMemBackendPoolAlloc(&pMap->pVm->sAllocator, sizeof(jx9_hashmap_node));
  25928. if( pNode == 0 ){
  25929. return 0;
  25930. }
  25931. /* Zero the stucture */
  25932. SyZero(pNode, sizeof(jx9_hashmap_node));
  25933. /* Fill in the structure */
  25934. pNode->pMap = &(*pMap);
  25935. pNode->iType = HASHMAP_INT_NODE;
  25936. pNode->nHash = nHash;
  25937. pNode->xKey.iKey = iKey;
  25938. pNode->nValIdx = nValIdx;
  25939. return pNode;
  25940. }
  25941. /*
  25942. * Allocate a new hashmap node with a BLOB key.
  25943. * If something goes wrong [i.e: out of memory], this function return NULL.
  25944. * Otherwise a fresh [jx9_hashmap_node] instance is returned.
  25945. */
  25946. static jx9_hashmap_node * HashmapNewBlobNode(jx9_hashmap *pMap, const void *pKey, sxu32 nKeyLen, sxu32 nHash, sxu32 nValIdx)
  25947. {
  25948. jx9_hashmap_node *pNode;
  25949. /* Allocate a new node */
  25950. pNode = (jx9_hashmap_node *)SyMemBackendPoolAlloc(&pMap->pVm->sAllocator, sizeof(jx9_hashmap_node));
  25951. if( pNode == 0 ){
  25952. return 0;
  25953. }
  25954. /* Zero the stucture */
  25955. SyZero(pNode, sizeof(jx9_hashmap_node));
  25956. /* Fill in the structure */
  25957. pNode->pMap = &(*pMap);
  25958. pNode->iType = HASHMAP_BLOB_NODE;
  25959. pNode->nHash = nHash;
  25960. SyBlobInit(&pNode->xKey.sKey, &pMap->pVm->sAllocator);
  25961. SyBlobAppend(&pNode->xKey.sKey, pKey, nKeyLen);
  25962. pNode->nValIdx = nValIdx;
  25963. return pNode;
  25964. }
  25965. /*
  25966. * link a hashmap node to the given bucket index (last argument to this function).
  25967. */
  25968. static void HashmapNodeLink(jx9_hashmap *pMap, jx9_hashmap_node *pNode, sxu32 nBucketIdx)
  25969. {
  25970. /* Link */
  25971. if( pMap->apBucket[nBucketIdx] != 0 ){
  25972. pNode->pNextCollide = pMap->apBucket[nBucketIdx];
  25973. pMap->apBucket[nBucketIdx]->pPrevCollide = pNode;
  25974. }
  25975. pMap->apBucket[nBucketIdx] = pNode;
  25976. /* Link to the map list */
  25977. if( pMap->pFirst == 0 ){
  25978. pMap->pFirst = pMap->pLast = pNode;
  25979. /* Point to the first inserted node */
  25980. pMap->pCur = pNode;
  25981. }else{
  25982. MACRO_LD_PUSH(pMap->pLast, pNode);
  25983. }
  25984. ++pMap->nEntry;
  25985. }
  25986. /*
  25987. * Unlink a node from the hashmap.
  25988. * If the node count reaches zero then release the whole hash-bucket.
  25989. */
  25990. static void jx9HashmapUnlinkNode(jx9_hashmap_node *pNode)
  25991. {
  25992. jx9_hashmap *pMap = pNode->pMap;
  25993. jx9_vm *pVm = pMap->pVm;
  25994. /* Unlink from the corresponding bucket */
  25995. if( pNode->pPrevCollide == 0 ){
  25996. pMap->apBucket[pNode->nHash & (pMap->nSize - 1)] = pNode->pNextCollide;
  25997. }else{
  25998. pNode->pPrevCollide->pNextCollide = pNode->pNextCollide;
  25999. }
  26000. if( pNode->pNextCollide ){
  26001. pNode->pNextCollide->pPrevCollide = pNode->pPrevCollide;
  26002. }
  26003. if( pMap->pFirst == pNode ){
  26004. pMap->pFirst = pNode->pPrev;
  26005. }
  26006. if( pMap->pCur == pNode ){
  26007. /* Advance the node cursor */
  26008. pMap->pCur = pMap->pCur->pPrev; /* Reverse link */
  26009. }
  26010. /* Unlink from the map list */
  26011. MACRO_LD_REMOVE(pMap->pLast, pNode);
  26012. /* Restore to the free list */
  26013. jx9VmUnsetMemObj(pVm, pNode->nValIdx);
  26014. if( pNode->iType == HASHMAP_BLOB_NODE ){
  26015. SyBlobRelease(&pNode->xKey.sKey);
  26016. }
  26017. SyMemBackendPoolFree(&pVm->sAllocator, pNode);
  26018. pMap->nEntry--;
  26019. if( pMap->nEntry < 1 ){
  26020. /* Free the hash-bucket */
  26021. SyMemBackendFree(&pVm->sAllocator, pMap->apBucket);
  26022. pMap->apBucket = 0;
  26023. pMap->nSize = 0;
  26024. pMap->pFirst = pMap->pLast = pMap->pCur = 0;
  26025. }
  26026. }
  26027. #define HASHMAP_FILL_FACTOR 3
  26028. /*
  26029. * Grow the hash-table and rehash all entries.
  26030. */
  26031. static sxi32 HashmapGrowBucket(jx9_hashmap *pMap)
  26032. {
  26033. if( pMap->nEntry >= pMap->nSize * HASHMAP_FILL_FACTOR ){
  26034. jx9_hashmap_node **apOld = pMap->apBucket;
  26035. jx9_hashmap_node *pEntry, **apNew;
  26036. sxu32 nNew = pMap->nSize << 1;
  26037. sxu32 nBucket;
  26038. sxu32 n;
  26039. if( nNew < 1 ){
  26040. nNew = 16;
  26041. }
  26042. /* Allocate a new bucket */
  26043. apNew = (jx9_hashmap_node **)SyMemBackendAlloc(&pMap->pVm->sAllocator, nNew * sizeof(jx9_hashmap_node *));
  26044. if( apNew == 0 ){
  26045. if( pMap->nSize < 1 ){
  26046. return SXERR_MEM; /* Fatal */
  26047. }
  26048. /* Not so fatal here, simply a performance hit */
  26049. return SXRET_OK;
  26050. }
  26051. /* Zero the table */
  26052. SyZero((void *)apNew, nNew * sizeof(jx9_hashmap_node *));
  26053. /* Reflect the change */
  26054. pMap->apBucket = apNew;
  26055. pMap->nSize = nNew;
  26056. if( apOld == 0 ){
  26057. /* First allocated table [i.e: no entry], return immediately */
  26058. return SXRET_OK;
  26059. }
  26060. /* Rehash old entries */
  26061. pEntry = pMap->pFirst;
  26062. n = 0;
  26063. for( ;; ){
  26064. if( n >= pMap->nEntry ){
  26065. break;
  26066. }
  26067. /* Clear the old collision link */
  26068. pEntry->pNextCollide = pEntry->pPrevCollide = 0;
  26069. /* Link to the new bucket */
  26070. nBucket = pEntry->nHash & (nNew - 1);
  26071. if( pMap->apBucket[nBucket] != 0 ){
  26072. pEntry->pNextCollide = pMap->apBucket[nBucket];
  26073. pMap->apBucket[nBucket]->pPrevCollide = pEntry;
  26074. }
  26075. pMap->apBucket[nBucket] = pEntry;
  26076. /* Point to the next entry */
  26077. pEntry = pEntry->pPrev; /* Reverse link */
  26078. n++;
  26079. }
  26080. /* Free the old table */
  26081. SyMemBackendFree(&pMap->pVm->sAllocator, (void *)apOld);
  26082. }
  26083. return SXRET_OK;
  26084. }
  26085. /*
  26086. * Insert a 64-bit integer key and it's associated value (if any) in the given
  26087. * hashmap.
  26088. */
  26089. static sxi32 HashmapInsertIntKey(jx9_hashmap *pMap,sxi64 iKey,jx9_value *pValue)
  26090. {
  26091. jx9_hashmap_node *pNode;
  26092. jx9_value *pObj;
  26093. sxu32 nIdx;
  26094. sxu32 nHash;
  26095. sxi32 rc;
  26096. /* Reserve a jx9_value for the value */
  26097. pObj = jx9VmReserveMemObj(pMap->pVm,&nIdx);
  26098. if( pObj == 0 ){
  26099. return SXERR_MEM;
  26100. }
  26101. if( pValue ){
  26102. /* Duplicate the value */
  26103. jx9MemObjStore(pValue, pObj);
  26104. }
  26105. /* Hash the key */
  26106. nHash = pMap->xIntHash(iKey);
  26107. /* Allocate a new int node */
  26108. pNode = HashmapNewIntNode(&(*pMap), iKey, nHash, nIdx);
  26109. if( pNode == 0 ){
  26110. return SXERR_MEM;
  26111. }
  26112. /* Make sure the bucket is big enough to hold the new entry */
  26113. rc = HashmapGrowBucket(&(*pMap));
  26114. if( rc != SXRET_OK ){
  26115. SyMemBackendPoolFree(&pMap->pVm->sAllocator, pNode);
  26116. return rc;
  26117. }
  26118. /* Perform the insertion */
  26119. HashmapNodeLink(&(*pMap), pNode, nHash & (pMap->nSize - 1));
  26120. /* All done */
  26121. return SXRET_OK;
  26122. }
  26123. /*
  26124. * Insert a BLOB key and it's associated value (if any) in the given
  26125. * hashmap.
  26126. */
  26127. static sxi32 HashmapInsertBlobKey(jx9_hashmap *pMap,const void *pKey,sxu32 nKeyLen,jx9_value *pValue)
  26128. {
  26129. jx9_hashmap_node *pNode;
  26130. jx9_value *pObj;
  26131. sxu32 nHash;
  26132. sxu32 nIdx;
  26133. sxi32 rc;
  26134. /* Reserve a jx9_value for the value */
  26135. pObj = jx9VmReserveMemObj(pMap->pVm,&nIdx);
  26136. if( pObj == 0 ){
  26137. return SXERR_MEM;
  26138. }
  26139. if( pValue ){
  26140. /* Duplicate the value */
  26141. jx9MemObjStore(pValue, pObj);
  26142. }
  26143. /* Hash the key */
  26144. nHash = pMap->xBlobHash(pKey, nKeyLen);
  26145. /* Allocate a new blob node */
  26146. pNode = HashmapNewBlobNode(&(*pMap), pKey, nKeyLen, nHash, nIdx);
  26147. if( pNode == 0 ){
  26148. return SXERR_MEM;
  26149. }
  26150. /* Make sure the bucket is big enough to hold the new entry */
  26151. rc = HashmapGrowBucket(&(*pMap));
  26152. if( rc != SXRET_OK ){
  26153. SyMemBackendPoolFree(&pMap->pVm->sAllocator, pNode);
  26154. return rc;
  26155. }
  26156. /* Perform the insertion */
  26157. HashmapNodeLink(&(*pMap), pNode, nHash & (pMap->nSize - 1));
  26158. /* All done */
  26159. return SXRET_OK;
  26160. }
  26161. /*
  26162. * Check if a given 64-bit integer key exists in the given hashmap.
  26163. * Write a pointer to the target node on success. Otherwise
  26164. * SXERR_NOTFOUND is returned on failure.
  26165. */
  26166. static sxi32 HashmapLookupIntKey(
  26167. jx9_hashmap *pMap, /* Target hashmap */
  26168. sxi64 iKey, /* lookup key */
  26169. jx9_hashmap_node **ppNode /* OUT: target node on success */
  26170. )
  26171. {
  26172. jx9_hashmap_node *pNode;
  26173. sxu32 nHash;
  26174. if( pMap->nEntry < 1 ){
  26175. /* Don't bother hashing, there is no entry anyway */
  26176. return SXERR_NOTFOUND;
  26177. }
  26178. /* Hash the key first */
  26179. nHash = pMap->xIntHash(iKey);
  26180. /* Point to the appropriate bucket */
  26181. pNode = pMap->apBucket[nHash & (pMap->nSize - 1)];
  26182. /* Perform the lookup */
  26183. for(;;){
  26184. if( pNode == 0 ){
  26185. break;
  26186. }
  26187. if( pNode->iType == HASHMAP_INT_NODE
  26188. && pNode->nHash == nHash
  26189. && pNode->xKey.iKey == iKey ){
  26190. /* Node found */
  26191. if( ppNode ){
  26192. *ppNode = pNode;
  26193. }
  26194. return SXRET_OK;
  26195. }
  26196. /* Follow the collision link */
  26197. pNode = pNode->pNextCollide;
  26198. }
  26199. /* No such entry */
  26200. return SXERR_NOTFOUND;
  26201. }
  26202. /*
  26203. * Check if a given BLOB key exists in the given hashmap.
  26204. * Write a pointer to the target node on success. Otherwise
  26205. * SXERR_NOTFOUND is returned on failure.
  26206. */
  26207. static sxi32 HashmapLookupBlobKey(
  26208. jx9_hashmap *pMap, /* Target hashmap */
  26209. const void *pKey, /* Lookup key */
  26210. sxu32 nKeyLen, /* Key length in bytes */
  26211. jx9_hashmap_node **ppNode /* OUT: target node on success */
  26212. )
  26213. {
  26214. jx9_hashmap_node *pNode;
  26215. sxu32 nHash;
  26216. if( pMap->nEntry < 1 ){
  26217. /* Don't bother hashing, there is no entry anyway */
  26218. return SXERR_NOTFOUND;
  26219. }
  26220. /* Hash the key first */
  26221. nHash = pMap->xBlobHash(pKey, nKeyLen);
  26222. /* Point to the appropriate bucket */
  26223. pNode = pMap->apBucket[nHash & (pMap->nSize - 1)];
  26224. /* Perform the lookup */
  26225. for(;;){
  26226. if( pNode == 0 ){
  26227. break;
  26228. }
  26229. if( pNode->iType == HASHMAP_BLOB_NODE
  26230. && pNode->nHash == nHash
  26231. && SyBlobLength(&pNode->xKey.sKey) == nKeyLen
  26232. && SyMemcmp(SyBlobData(&pNode->xKey.sKey), pKey, nKeyLen) == 0 ){
  26233. /* Node found */
  26234. if( ppNode ){
  26235. *ppNode = pNode;
  26236. }
  26237. return SXRET_OK;
  26238. }
  26239. /* Follow the collision link */
  26240. pNode = pNode->pNextCollide;
  26241. }
  26242. /* No such entry */
  26243. return SXERR_NOTFOUND;
  26244. }
  26245. /*
  26246. * Check if the given BLOB key looks like a decimal number.
  26247. * Retrurn TRUE on success.FALSE otherwise.
  26248. */
  26249. static int HashmapIsIntKey(SyBlob *pKey)
  26250. {
  26251. const char *zIn = (const char *)SyBlobData(pKey);
  26252. const char *zEnd = &zIn[SyBlobLength(pKey)];
  26253. if( (int)(zEnd-zIn) > 1 && zIn[0] == '0' ){
  26254. /* Octal not decimal number */
  26255. return FALSE;
  26256. }
  26257. if( (zIn[0] == '-' || zIn[0] == '+') && &zIn[1] < zEnd ){
  26258. zIn++;
  26259. }
  26260. for(;;){
  26261. if( zIn >= zEnd ){
  26262. return TRUE;
  26263. }
  26264. if( (unsigned char)zIn[0] >= 0xc0 /* UTF-8 stream */ || !SyisDigit(zIn[0]) ){
  26265. break;
  26266. }
  26267. zIn++;
  26268. }
  26269. /* Key does not look like a decimal number */
  26270. return FALSE;
  26271. }
  26272. /*
  26273. * Check if a given key exists in the given hashmap.
  26274. * Write a pointer to the target node on success.
  26275. * Otherwise SXERR_NOTFOUND is returned on failure.
  26276. */
  26277. static sxi32 HashmapLookup(
  26278. jx9_hashmap *pMap, /* Target hashmap */
  26279. jx9_value *pKey, /* Lookup key */
  26280. jx9_hashmap_node **ppNode /* OUT: target node on success */
  26281. )
  26282. {
  26283. jx9_hashmap_node *pNode = 0; /* cc -O6 warning */
  26284. sxi32 rc;
  26285. if( pKey->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP|MEMOBJ_RES) ){
  26286. if( (pKey->iFlags & MEMOBJ_STRING) == 0 ){
  26287. /* Force a string cast */
  26288. jx9MemObjToString(&(*pKey));
  26289. }
  26290. if( SyBlobLength(&pKey->sBlob) > 0 ){
  26291. /* Perform a blob lookup */
  26292. rc = HashmapLookupBlobKey(&(*pMap), SyBlobData(&pKey->sBlob), SyBlobLength(&pKey->sBlob), &pNode);
  26293. goto result;
  26294. }
  26295. }
  26296. /* Perform an int lookup */
  26297. if((pKey->iFlags & MEMOBJ_INT) == 0 ){
  26298. /* Force an integer cast */
  26299. jx9MemObjToInteger(pKey);
  26300. }
  26301. /* Perform an int lookup */
  26302. rc = HashmapLookupIntKey(&(*pMap), pKey->x.iVal, &pNode);
  26303. result:
  26304. if( rc == SXRET_OK ){
  26305. /* Node found */
  26306. if( ppNode ){
  26307. *ppNode = pNode;
  26308. }
  26309. return SXRET_OK;
  26310. }
  26311. /* No such entry */
  26312. return SXERR_NOTFOUND;
  26313. }
  26314. /*
  26315. * Insert a given key and it's associated value (if any) in the given
  26316. * hashmap.
  26317. * If a node with the given key already exists in the database
  26318. * then this function overwrite the old value.
  26319. */
  26320. static sxi32 HashmapInsert(
  26321. jx9_hashmap *pMap, /* Target hashmap */
  26322. jx9_value *pKey, /* Lookup key */
  26323. jx9_value *pVal /* Node value */
  26324. )
  26325. {
  26326. jx9_hashmap_node *pNode = 0;
  26327. sxi32 rc = SXRET_OK;
  26328. if( pMap->nEntry < 1 && pKey && (pKey->iFlags & MEMOBJ_STRING) ){
  26329. pMap->iFlags |= HASHMAP_JSON_OBJECT;
  26330. }
  26331. if( pKey && (pKey->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP|MEMOBJ_RES)) ){
  26332. if( (pKey->iFlags & MEMOBJ_STRING) == 0 ){
  26333. /* Force a string cast */
  26334. jx9MemObjToString(&(*pKey));
  26335. }
  26336. if( SyBlobLength(&pKey->sBlob) < 1 || HashmapIsIntKey(&pKey->sBlob) ){
  26337. if(SyBlobLength(&pKey->sBlob) < 1){
  26338. /* Automatic index assign */
  26339. pKey = 0;
  26340. }
  26341. goto IntKey;
  26342. }
  26343. if( SXRET_OK == HashmapLookupBlobKey(&(*pMap), SyBlobData(&pKey->sBlob),
  26344. SyBlobLength(&pKey->sBlob), &pNode) ){
  26345. /* Overwrite the old value */
  26346. jx9_value *pElem;
  26347. pElem = (jx9_value *)SySetAt(&pMap->pVm->aMemObj, pNode->nValIdx);
  26348. if( pElem ){
  26349. if( pVal ){
  26350. jx9MemObjStore(pVal, pElem);
  26351. }else{
  26352. /* Nullify the entry */
  26353. jx9MemObjToNull(pElem);
  26354. }
  26355. }
  26356. return SXRET_OK;
  26357. }
  26358. /* Perform a blob-key insertion */
  26359. rc = HashmapInsertBlobKey(&(*pMap),SyBlobData(&pKey->sBlob),SyBlobLength(&pKey->sBlob),&(*pVal));
  26360. return rc;
  26361. }
  26362. IntKey:
  26363. if( pKey ){
  26364. if((pKey->iFlags & MEMOBJ_INT) == 0 ){
  26365. /* Force an integer cast */
  26366. jx9MemObjToInteger(pKey);
  26367. }
  26368. if( SXRET_OK == HashmapLookupIntKey(&(*pMap), pKey->x.iVal, &pNode) ){
  26369. /* Overwrite the old value */
  26370. jx9_value *pElem;
  26371. pElem = (jx9_value *)SySetAt(&pMap->pVm->aMemObj, pNode->nValIdx);
  26372. if( pElem ){
  26373. if( pVal ){
  26374. jx9MemObjStore(pVal, pElem);
  26375. }else{
  26376. /* Nullify the entry */
  26377. jx9MemObjToNull(pElem);
  26378. }
  26379. }
  26380. return SXRET_OK;
  26381. }
  26382. /* Perform a 64-bit-int-key insertion */
  26383. rc = HashmapInsertIntKey(&(*pMap), pKey->x.iVal, &(*pVal));
  26384. if( rc == SXRET_OK ){
  26385. if( pKey->x.iVal >= pMap->iNextIdx ){
  26386. /* Increment the automatic index */
  26387. pMap->iNextIdx = pKey->x.iVal + 1;
  26388. /* Make sure the automatic index is not reserved */
  26389. while( SXRET_OK == HashmapLookupIntKey(&(*pMap), pMap->iNextIdx, 0) ){
  26390. pMap->iNextIdx++;
  26391. }
  26392. }
  26393. }
  26394. }else{
  26395. /* Assign an automatic index */
  26396. rc = HashmapInsertIntKey(&(*pMap),pMap->iNextIdx,&(*pVal));
  26397. if( rc == SXRET_OK ){
  26398. ++pMap->iNextIdx;
  26399. }
  26400. }
  26401. /* Insertion result */
  26402. return rc;
  26403. }
  26404. /*
  26405. * Extract node value.
  26406. */
  26407. static jx9_value * HashmapExtractNodeValue(jx9_hashmap_node *pNode)
  26408. {
  26409. /* Point to the desired object */
  26410. jx9_value *pObj;
  26411. pObj = (jx9_value *)SySetAt(&pNode->pMap->pVm->aMemObj, pNode->nValIdx);
  26412. return pObj;
  26413. }
  26414. /*
  26415. * Insert a node in the given hashmap.
  26416. * If a node with the given key already exists in the database
  26417. * then this function overwrite the old value.
  26418. */
  26419. static sxi32 HashmapInsertNode(jx9_hashmap *pMap, jx9_hashmap_node *pNode, int bPreserve)
  26420. {
  26421. jx9_value *pObj;
  26422. sxi32 rc;
  26423. /* Extract the node value */
  26424. pObj = HashmapExtractNodeValue(&(*pNode));
  26425. if( pObj == 0 ){
  26426. return SXERR_EMPTY;
  26427. }
  26428. /* Preserve key */
  26429. if( pNode->iType == HASHMAP_INT_NODE){
  26430. /* Int64 key */
  26431. if( !bPreserve ){
  26432. /* Assign an automatic index */
  26433. rc = HashmapInsert(&(*pMap), 0, pObj);
  26434. }else{
  26435. rc = HashmapInsertIntKey(&(*pMap), pNode->xKey.iKey, pObj);
  26436. }
  26437. }else{
  26438. /* Blob key */
  26439. rc = HashmapInsertBlobKey(&(*pMap), SyBlobData(&pNode->xKey.sKey),
  26440. SyBlobLength(&pNode->xKey.sKey), pObj);
  26441. }
  26442. return rc;
  26443. }
  26444. /*
  26445. * Compare two node values.
  26446. * Return 0 if the node values are equals, > 0 if pLeft is greater than pRight
  26447. * or < 0 if pRight is greater than pLeft.
  26448. * For a full description on jx9_values comparison, refer to the implementation
  26449. * of the [jx9MemObjCmp()] function defined in memobj.c or the official
  26450. * documenation.
  26451. */
  26452. static sxi32 HashmapNodeCmp(jx9_hashmap_node *pLeft, jx9_hashmap_node *pRight, int bStrict)
  26453. {
  26454. jx9_value sObj1, sObj2;
  26455. sxi32 rc;
  26456. if( pLeft == pRight ){
  26457. /*
  26458. * Same node.Refer to the sort() implementation defined
  26459. * below for more information on this sceanario.
  26460. */
  26461. return 0;
  26462. }
  26463. /* Do the comparison */
  26464. jx9MemObjInit(pLeft->pMap->pVm, &sObj1);
  26465. jx9MemObjInit(pLeft->pMap->pVm, &sObj2);
  26466. jx9HashmapExtractNodeValue(pLeft, &sObj1, FALSE);
  26467. jx9HashmapExtractNodeValue(pRight, &sObj2, FALSE);
  26468. rc = jx9MemObjCmp(&sObj1, &sObj2, bStrict, 0);
  26469. jx9MemObjRelease(&sObj1);
  26470. jx9MemObjRelease(&sObj2);
  26471. return rc;
  26472. }
  26473. /*
  26474. * Rehash a node with a 64-bit integer key.
  26475. * Refer to [merge_sort(), array_shift()] implementations for more information.
  26476. */
  26477. static void HashmapRehashIntNode(jx9_hashmap_node *pEntry)
  26478. {
  26479. jx9_hashmap *pMap = pEntry->pMap;
  26480. sxu32 nBucket;
  26481. /* Remove old collision links */
  26482. if( pEntry->pPrevCollide ){
  26483. pEntry->pPrevCollide->pNextCollide = pEntry->pNextCollide;
  26484. }else{
  26485. pMap->apBucket[pEntry->nHash & (pMap->nSize - 1)] = pEntry->pNextCollide;
  26486. }
  26487. if( pEntry->pNextCollide ){
  26488. pEntry->pNextCollide->pPrevCollide = pEntry->pPrevCollide;
  26489. }
  26490. pEntry->pNextCollide = pEntry->pPrevCollide = 0;
  26491. /* Compute the new hash */
  26492. pEntry->nHash = pMap->xIntHash(pMap->iNextIdx);
  26493. pEntry->xKey.iKey = pMap->iNextIdx;
  26494. nBucket = pEntry->nHash & (pMap->nSize - 1);
  26495. /* Link to the new bucket */
  26496. pEntry->pNextCollide = pMap->apBucket[nBucket];
  26497. if( pMap->apBucket[nBucket] ){
  26498. pMap->apBucket[nBucket]->pPrevCollide = pEntry;
  26499. }
  26500. pEntry->pNextCollide = pMap->apBucket[nBucket];
  26501. pMap->apBucket[nBucket] = pEntry;
  26502. /* Increment the automatic index */
  26503. pMap->iNextIdx++;
  26504. }
  26505. /*
  26506. * Perform a linear search on a given hashmap.
  26507. * Write a pointer to the target node on success.
  26508. * Otherwise SXERR_NOTFOUND is returned on failure.
  26509. * Refer to [array_intersect(), array_diff(), in_array(), ...] implementations
  26510. * for more information.
  26511. */
  26512. static int HashmapFindValue(
  26513. jx9_hashmap *pMap, /* Target hashmap */
  26514. jx9_value *pNeedle, /* Lookup key */
  26515. jx9_hashmap_node **ppNode, /* OUT: target node on success */
  26516. int bStrict /* TRUE for strict comparison */
  26517. )
  26518. {
  26519. jx9_hashmap_node *pEntry;
  26520. jx9_value sVal, *pVal;
  26521. jx9_value sNeedle;
  26522. sxi32 rc;
  26523. sxu32 n;
  26524. /* Perform a linear search since we cannot sort the hashmap based on values */
  26525. pEntry = pMap->pFirst;
  26526. n = pMap->nEntry;
  26527. jx9MemObjInit(pMap->pVm, &sVal);
  26528. jx9MemObjInit(pMap->pVm, &sNeedle);
  26529. for(;;){
  26530. if( n < 1 ){
  26531. break;
  26532. }
  26533. /* Extract node value */
  26534. pVal = HashmapExtractNodeValue(pEntry);
  26535. if( pVal ){
  26536. if( (pVal->iFlags|pNeedle->iFlags) & MEMOBJ_NULL ){
  26537. sxi32 iF1 = pVal->iFlags;
  26538. sxi32 iF2 = pNeedle->iFlags;
  26539. if( iF1 == iF2 ){
  26540. /* NULL values are equals */
  26541. if( ppNode ){
  26542. *ppNode = pEntry;
  26543. }
  26544. return SXRET_OK;
  26545. }
  26546. }else{
  26547. /* Duplicate value */
  26548. jx9MemObjLoad(pVal, &sVal);
  26549. jx9MemObjLoad(pNeedle, &sNeedle);
  26550. rc = jx9MemObjCmp(&sNeedle, &sVal, bStrict, 0);
  26551. jx9MemObjRelease(&sVal);
  26552. jx9MemObjRelease(&sNeedle);
  26553. if( rc == 0 ){
  26554. if( ppNode ){
  26555. *ppNode = pEntry;
  26556. }
  26557. /* Match found*/
  26558. return SXRET_OK;
  26559. }
  26560. }
  26561. }
  26562. /* Point to the next entry */
  26563. pEntry = pEntry->pPrev; /* Reverse link */
  26564. n--;
  26565. }
  26566. /* No such entry */
  26567. return SXERR_NOTFOUND;
  26568. }
  26569. /*
  26570. * Compare two hashmaps.
  26571. * Return 0 if the hashmaps are equals.Any other value indicates inequality.
  26572. * Note on array comparison operators.
  26573. * According to the JX9 language reference manual.
  26574. * Array Operators Example Name Result
  26575. * $a + $b Union Union of $a and $b.
  26576. * $a == $b Equality TRUE if $a and $b have the same key/value pairs.
  26577. * $a === $b Identity TRUE if $a and $b have the same key/value pairs in the same
  26578. * order and of the same types.
  26579. * $a != $b Inequality TRUE if $a is not equal to $b.
  26580. * $a <> $b Inequality TRUE if $a is not equal to $b.
  26581. * $a !== $b Non-identity TRUE if $a is not identical to $b.
  26582. * The + operator returns the right-hand array appended to the left-hand array;
  26583. * For keys that exist in both arrays, the elements from the left-hand array will be used
  26584. * and the matching elements from the right-hand array will be ignored.
  26585. * <?jx9
  26586. * $a = array("a" => "apple", "b" => "banana");
  26587. * $b = array("a" => "pear", "b" => "strawberry", "c" => "cherry");
  26588. * $c = $a + $b; // Union of $a and $b
  26589. * print "Union of \$a and \$b: \n";
  26590. * dump($c);
  26591. * $c = $b + $a; // Union of $b and $a
  26592. * print "Union of \$b and \$a: \n";
  26593. * dump($c);
  26594. * ?>
  26595. * When executed, this script will print the following:
  26596. * Union of $a and $b:
  26597. * array(3) {
  26598. * ["a"]=>
  26599. * string(5) "apple"
  26600. * ["b"]=>
  26601. * string(6) "banana"
  26602. * ["c"]=>
  26603. * string(6) "cherry"
  26604. * }
  26605. * Union of $b and $a:
  26606. * array(3) {
  26607. * ["a"]=>
  26608. * string(4) "pear"
  26609. * ["b"]=>
  26610. * string(10) "strawberry"
  26611. * ["c"]=>
  26612. * string(6) "cherry"
  26613. * }
  26614. * Elements of arrays are equal for the comparison if they have the same key and value.
  26615. */
  26616. JX9_PRIVATE sxi32 jx9HashmapCmp(
  26617. jx9_hashmap *pLeft, /* Left hashmap */
  26618. jx9_hashmap *pRight, /* Right hashmap */
  26619. int bStrict /* TRUE for strict comparison */
  26620. )
  26621. {
  26622. jx9_hashmap_node *pLe, *pRe;
  26623. sxi32 rc;
  26624. sxu32 n;
  26625. if( pLeft == pRight ){
  26626. /* Same hashmap instance. This can easily happen since hashmaps are passed by reference.
  26627. * Unlike the engine.
  26628. */
  26629. return 0;
  26630. }
  26631. if( pLeft->nEntry != pRight->nEntry ){
  26632. /* Must have the same number of entries */
  26633. return pLeft->nEntry > pRight->nEntry ? 1 : -1;
  26634. }
  26635. /* Point to the first inserted entry of the left hashmap */
  26636. pLe = pLeft->pFirst;
  26637. pRe = 0; /* cc warning */
  26638. /* Perform the comparison */
  26639. n = pLeft->nEntry;
  26640. for(;;){
  26641. if( n < 1 ){
  26642. break;
  26643. }
  26644. if( pLe->iType == HASHMAP_INT_NODE){
  26645. /* Int key */
  26646. rc = HashmapLookupIntKey(&(*pRight), pLe->xKey.iKey, &pRe);
  26647. }else{
  26648. SyBlob *pKey = &pLe->xKey.sKey;
  26649. /* Blob key */
  26650. rc = HashmapLookupBlobKey(&(*pRight), SyBlobData(pKey), SyBlobLength(pKey), &pRe);
  26651. }
  26652. if( rc != SXRET_OK ){
  26653. /* No such entry in the right side */
  26654. return 1;
  26655. }
  26656. rc = 0;
  26657. if( bStrict ){
  26658. /* Make sure, the keys are of the same type */
  26659. if( pLe->iType != pRe->iType ){
  26660. rc = 1;
  26661. }
  26662. }
  26663. if( !rc ){
  26664. /* Compare nodes */
  26665. rc = HashmapNodeCmp(pLe, pRe, bStrict);
  26666. }
  26667. if( rc != 0 ){
  26668. /* Nodes key/value differ */
  26669. return rc;
  26670. }
  26671. /* Point to the next entry */
  26672. pLe = pLe->pPrev; /* Reverse link */
  26673. n--;
  26674. }
  26675. return 0; /* Hashmaps are equals */
  26676. }
  26677. /*
  26678. * Merge two hashmaps.
  26679. * Note on the merge process
  26680. * According to the JX9 language reference manual.
  26681. * Merges the elements of two arrays together so that the values of one are appended
  26682. * to the end of the previous one. It returns the resulting array (pDest).
  26683. * If the input arrays have the same string keys, then the later value for that key
  26684. * will overwrite the previous one. If, however, the arrays contain numeric keys
  26685. * the later value will not overwrite the original value, but will be appended.
  26686. * Values in the input array with numeric keys will be renumbered with incrementing
  26687. * keys starting from zero in the result array.
  26688. */
  26689. static sxi32 HashmapMerge(jx9_hashmap *pSrc, jx9_hashmap *pDest)
  26690. {
  26691. jx9_hashmap_node *pEntry;
  26692. jx9_value sKey, *pVal;
  26693. sxi32 rc;
  26694. sxu32 n;
  26695. if( pSrc == pDest ){
  26696. /* Same map. This can easily happen since hashmaps are passed by reference.
  26697. * Unlike the engine.
  26698. */
  26699. return SXRET_OK;
  26700. }
  26701. /* Point to the first inserted entry in the source */
  26702. pEntry = pSrc->pFirst;
  26703. /* Perform the merge */
  26704. for( n = 0 ; n < pSrc->nEntry ; ++n ){
  26705. /* Extract the node value */
  26706. pVal = HashmapExtractNodeValue(pEntry);
  26707. if( pEntry->iType == HASHMAP_BLOB_NODE ){
  26708. /* Blob key insertion */
  26709. jx9MemObjInitFromString(pDest->pVm, &sKey, 0);
  26710. jx9MemObjStringAppend(&sKey, (const char *)SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey));
  26711. rc = jx9HashmapInsert(&(*pDest), &sKey, pVal);
  26712. jx9MemObjRelease(&sKey);
  26713. }else{
  26714. rc = HashmapInsert(&(*pDest), 0/* Automatic index assign */, pVal);
  26715. }
  26716. if( rc != SXRET_OK ){
  26717. return rc;
  26718. }
  26719. /* Point to the next entry */
  26720. pEntry = pEntry->pPrev; /* Reverse link */
  26721. }
  26722. return SXRET_OK;
  26723. }
  26724. /*
  26725. * Duplicate the contents of a hashmap. Store the copy in pDest.
  26726. * Refer to the [array_pad(), array_copy(), ...] implementation for more information.
  26727. */
  26728. JX9_PRIVATE sxi32 jx9HashmapDup(jx9_hashmap *pSrc, jx9_hashmap *pDest)
  26729. {
  26730. jx9_hashmap_node *pEntry;
  26731. jx9_value sKey, *pVal;
  26732. sxi32 rc;
  26733. sxu32 n;
  26734. if( pSrc == pDest ){
  26735. /* Same map. This can easily happen since hashmaps are passed by reference.
  26736. * Unlike the engine.
  26737. */
  26738. return SXRET_OK;
  26739. }
  26740. /* Point to the first inserted entry in the source */
  26741. pEntry = pSrc->pFirst;
  26742. /* Perform the duplication */
  26743. for( n = 0 ; n < pSrc->nEntry ; ++n ){
  26744. /* Extract the node value */
  26745. pVal = HashmapExtractNodeValue(pEntry);
  26746. if( pEntry->iType == HASHMAP_BLOB_NODE ){
  26747. /* Blob key insertion */
  26748. jx9MemObjInitFromString(pDest->pVm, &sKey, 0);
  26749. jx9MemObjStringAppend(&sKey, (const char *)SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey));
  26750. rc = jx9HashmapInsert(&(*pDest), &sKey, pVal);
  26751. jx9MemObjRelease(&sKey);
  26752. }else{
  26753. /* Int key insertion */
  26754. rc = HashmapInsertIntKey(&(*pDest), pEntry->xKey.iKey, pVal);
  26755. }
  26756. if( rc != SXRET_OK ){
  26757. return rc;
  26758. }
  26759. /* Point to the next entry */
  26760. pEntry = pEntry->pPrev; /* Reverse link */
  26761. }
  26762. return SXRET_OK;
  26763. }
  26764. /*
  26765. * Perform the union of two hashmaps.
  26766. * This operation is performed only if the user uses the '+' operator
  26767. * with a variable holding an array as follows:
  26768. * <?jx9
  26769. * $a = array("a" => "apple", "b" => "banana");
  26770. * $b = array("a" => "pear", "b" => "strawberry", "c" => "cherry");
  26771. * $c = $a + $b; // Union of $a and $b
  26772. * print "Union of \$a and \$b: \n";
  26773. * dump($c);
  26774. * $c = $b + $a; // Union of $b and $a
  26775. * print "Union of \$b and \$a: \n";
  26776. * dump($c);
  26777. * ?>
  26778. * When executed, this script will print the following:
  26779. * Union of $a and $b:
  26780. * array(3) {
  26781. * ["a"]=>
  26782. * string(5) "apple"
  26783. * ["b"]=>
  26784. * string(6) "banana"
  26785. * ["c"]=>
  26786. * string(6) "cherry"
  26787. * }
  26788. * Union of $b and $a:
  26789. * array(3) {
  26790. * ["a"]=>
  26791. * string(4) "pear"
  26792. * ["b"]=>
  26793. * string(10) "strawberry"
  26794. * ["c"]=>
  26795. * string(6) "cherry"
  26796. * }
  26797. * The + operator returns the right-hand array appended to the left-hand array;
  26798. * For keys that exist in both arrays, the elements from the left-hand array will be used
  26799. * and the matching elements from the right-hand array will be ignored.
  26800. */
  26801. JX9_PRIVATE sxi32 jx9HashmapUnion(jx9_hashmap *pLeft, jx9_hashmap *pRight)
  26802. {
  26803. jx9_hashmap_node *pEntry;
  26804. sxi32 rc = SXRET_OK;
  26805. jx9_value *pObj;
  26806. sxu32 n;
  26807. if( pLeft == pRight ){
  26808. /* Same map. This can easily happen since hashmaps are passed by reference.
  26809. * Unlike the engine.
  26810. */
  26811. return SXRET_OK;
  26812. }
  26813. /* Perform the union */
  26814. pEntry = pRight->pFirst;
  26815. for(n = 0 ; n < pRight->nEntry ; ++n ){
  26816. /* Make sure the given key does not exists in the left array */
  26817. if( pEntry->iType == HASHMAP_BLOB_NODE ){
  26818. /* BLOB key */
  26819. if( SXRET_OK !=
  26820. HashmapLookupBlobKey(&(*pLeft), SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey), 0) ){
  26821. pObj = HashmapExtractNodeValue(pEntry);
  26822. if( pObj ){
  26823. /* Perform the insertion */
  26824. rc = HashmapInsertBlobKey(&(*pLeft), SyBlobData(&pEntry->xKey.sKey),
  26825. SyBlobLength(&pEntry->xKey.sKey),pObj);
  26826. if( rc != SXRET_OK ){
  26827. return rc;
  26828. }
  26829. }
  26830. }
  26831. }else{
  26832. /* INT key */
  26833. if( SXRET_OK != HashmapLookupIntKey(&(*pLeft), pEntry->xKey.iKey, 0) ){
  26834. pObj = HashmapExtractNodeValue(pEntry);
  26835. if( pObj ){
  26836. /* Perform the insertion */
  26837. rc = HashmapInsertIntKey(&(*pLeft), pEntry->xKey.iKey, pObj);
  26838. if( rc != SXRET_OK ){
  26839. return rc;
  26840. }
  26841. }
  26842. }
  26843. }
  26844. /* Point to the next entry */
  26845. pEntry = pEntry->pPrev; /* Reverse link */
  26846. }
  26847. return SXRET_OK;
  26848. }
  26849. /*
  26850. * Allocate a new hashmap.
  26851. * Return a pointer to the freshly allocated hashmap on success.NULL otherwise.
  26852. */
  26853. JX9_PRIVATE jx9_hashmap * jx9NewHashmap(
  26854. jx9_vm *pVm, /* VM that trigger the hashmap creation */
  26855. sxu32 (*xIntHash)(sxi64), /* Hash function for int keys.NULL otherwise*/
  26856. sxu32 (*xBlobHash)(const void *, sxu32) /* Hash function for BLOB keys.NULL otherwise */
  26857. )
  26858. {
  26859. jx9_hashmap *pMap;
  26860. /* Allocate a new instance */
  26861. pMap = (jx9_hashmap *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_hashmap));
  26862. if( pMap == 0 ){
  26863. return 0;
  26864. }
  26865. /* Zero the structure */
  26866. SyZero(pMap, sizeof(jx9_hashmap));
  26867. /* Fill in the structure */
  26868. pMap->pVm = &(*pVm);
  26869. pMap->iRef = 1;
  26870. /* pMap->iFlags = 0; */
  26871. /* Default hash functions */
  26872. pMap->xIntHash = xIntHash ? xIntHash : IntHash;
  26873. pMap->xBlobHash = xBlobHash ? xBlobHash : BinHash;
  26874. return pMap;
  26875. }
  26876. /*
  26877. * Install superglobals in the given virtual machine.
  26878. * Note on superglobals.
  26879. * According to the JX9 language reference manual.
  26880. * Superglobals are built-in variables that are always available in all scopes.
  26881. * Description
  26882. * All predefined variables in JX9 are "superglobals", which means they
  26883. * are available in all scopes throughout a script.
  26884. * These variables are:
  26885. * $_SERVER
  26886. * $_GET
  26887. * $_POST
  26888. * $_FILES
  26889. * $_REQUEST
  26890. * $_ENV
  26891. */
  26892. JX9_PRIVATE sxi32 jx9HashmapLoadBuiltin(jx9_vm *pVm)
  26893. {
  26894. static const char * azSuper[] = {
  26895. "_SERVER", /* $_SERVER */
  26896. "_GET", /* $_GET */
  26897. "_POST", /* $_POST */
  26898. "_FILES", /* $_FILES */
  26899. "_REQUEST", /* $_REQUEST */
  26900. "_COOKIE", /* $_COOKIE */
  26901. "_ENV", /* $_ENV */
  26902. "_HEADER", /* $_HEADER */
  26903. "argv" /* $argv */
  26904. };
  26905. SyString *pFile;
  26906. sxi32 rc;
  26907. sxu32 n;
  26908. /* Install globals variable now */
  26909. for( n = 0 ; n < SX_ARRAYSIZE(azSuper) ; n++ ){
  26910. jx9_value *pSuper;
  26911. /* Request an empty array */
  26912. pSuper = jx9_new_array(&(*pVm));
  26913. if( pSuper == 0 ){
  26914. return SXERR_MEM;
  26915. }
  26916. /* Install */
  26917. rc = jx9_vm_config(&(*pVm),JX9_VM_CONFIG_CREATE_VAR, azSuper[n]/* Super-global name*/, pSuper/* Super-global value */);
  26918. if( rc != SXRET_OK ){
  26919. return rc;
  26920. }
  26921. /* Release the value now it have been installed */
  26922. jx9_release_value(&(*pVm), pSuper);
  26923. }
  26924. /* Set some $_SERVER entries */
  26925. pFile = (SyString *)SySetPeek(&pVm->aFiles);
  26926. /*
  26927. * 'SCRIPT_FILENAME'
  26928. * The absolute pathname of the currently executing script.
  26929. */
  26930. jx9_vm_config(pVm, JX9_VM_CONFIG_SERVER_ATTR,
  26931. "SCRIPT_FILENAME",
  26932. pFile ? pFile->zString : ":Memory:",
  26933. pFile ? pFile->nByte : sizeof(":Memory:") - 1
  26934. );
  26935. /* All done, all global variables are installed now */
  26936. return SXRET_OK;
  26937. }
  26938. /*
  26939. * Release a hashmap.
  26940. */
  26941. JX9_PRIVATE sxi32 jx9HashmapRelease(jx9_hashmap *pMap, int FreeDS)
  26942. {
  26943. jx9_hashmap_node *pEntry, *pNext;
  26944. jx9_vm *pVm = pMap->pVm;
  26945. sxu32 n;
  26946. /* Start the release process */
  26947. n = 0;
  26948. pEntry = pMap->pFirst;
  26949. for(;;){
  26950. if( n >= pMap->nEntry ){
  26951. break;
  26952. }
  26953. pNext = pEntry->pPrev; /* Reverse link */
  26954. /* Restore the jx9_value to the free list */
  26955. jx9VmUnsetMemObj(pVm, pEntry->nValIdx);
  26956. /* Release the node */
  26957. if( pEntry->iType == HASHMAP_BLOB_NODE ){
  26958. SyBlobRelease(&pEntry->xKey.sKey);
  26959. }
  26960. SyMemBackendPoolFree(&pVm->sAllocator, pEntry);
  26961. /* Point to the next entry */
  26962. pEntry = pNext;
  26963. n++;
  26964. }
  26965. if( pMap->nEntry > 0 ){
  26966. /* Release the hash bucket */
  26967. SyMemBackendFree(&pVm->sAllocator, pMap->apBucket);
  26968. }
  26969. if( FreeDS ){
  26970. /* Free the whole instance */
  26971. SyMemBackendPoolFree(&pVm->sAllocator, pMap);
  26972. }else{
  26973. /* Keep the instance but reset it's fields */
  26974. pMap->apBucket = 0;
  26975. pMap->iNextIdx = 0;
  26976. pMap->nEntry = pMap->nSize = 0;
  26977. pMap->pFirst = pMap->pLast = pMap->pCur = 0;
  26978. }
  26979. return SXRET_OK;
  26980. }
  26981. /*
  26982. * Decrement the reference count of a given hashmap.
  26983. * If the count reaches zero which mean no more variables
  26984. * are pointing to this hashmap, then release the whole instance.
  26985. */
  26986. JX9_PRIVATE void jx9HashmapUnref(jx9_hashmap *pMap)
  26987. {
  26988. pMap->iRef--;
  26989. if( pMap->iRef < 1 ){
  26990. jx9HashmapRelease(pMap, TRUE);
  26991. }
  26992. }
  26993. /*
  26994. * Check if a given key exists in the given hashmap.
  26995. * Write a pointer to the target node on success.
  26996. * Otherwise SXERR_NOTFOUND is returned on failure.
  26997. */
  26998. JX9_PRIVATE sxi32 jx9HashmapLookup(
  26999. jx9_hashmap *pMap, /* Target hashmap */
  27000. jx9_value *pKey, /* Lookup key */
  27001. jx9_hashmap_node **ppNode /* OUT: Target node on success */
  27002. )
  27003. {
  27004. sxi32 rc;
  27005. if( pMap->nEntry < 1 ){
  27006. /* TICKET 1433-25: Don't bother hashing, the hashmap is empty anyway.
  27007. */
  27008. return SXERR_NOTFOUND;
  27009. }
  27010. rc = HashmapLookup(&(*pMap), &(*pKey), ppNode);
  27011. return rc;
  27012. }
  27013. /*
  27014. * Insert a given key and it's associated value (if any) in the given
  27015. * hashmap.
  27016. * If a node with the given key already exists in the database
  27017. * then this function overwrite the old value.
  27018. */
  27019. JX9_PRIVATE sxi32 jx9HashmapInsert(
  27020. jx9_hashmap *pMap, /* Target hashmap */
  27021. jx9_value *pKey, /* Lookup key */
  27022. jx9_value *pVal /* Node value.NULL otherwise */
  27023. )
  27024. {
  27025. sxi32 rc;
  27026. rc = HashmapInsert(&(*pMap), &(*pKey), &(*pVal));
  27027. return rc;
  27028. }
  27029. /*
  27030. * Reset the node cursor of a given hashmap.
  27031. */
  27032. JX9_PRIVATE void jx9HashmapResetLoopCursor(jx9_hashmap *pMap)
  27033. {
  27034. /* Reset the loop cursor */
  27035. pMap->pCur = pMap->pFirst;
  27036. }
  27037. /*
  27038. * Return a pointer to the node currently pointed by the node cursor.
  27039. * If the cursor reaches the end of the list, then this function
  27040. * return NULL.
  27041. * Note that the node cursor is automatically advanced by this function.
  27042. */
  27043. JX9_PRIVATE jx9_hashmap_node * jx9HashmapGetNextEntry(jx9_hashmap *pMap)
  27044. {
  27045. jx9_hashmap_node *pCur = pMap->pCur;
  27046. if( pCur == 0 ){
  27047. /* End of the list, return null */
  27048. return 0;
  27049. }
  27050. /* Advance the node cursor */
  27051. pMap->pCur = pCur->pPrev; /* Reverse link */
  27052. return pCur;
  27053. }
  27054. /*
  27055. * Extract a node value.
  27056. */
  27057. JX9_PRIVATE void jx9HashmapExtractNodeValue(jx9_hashmap_node *pNode, jx9_value *pValue, int bStore)
  27058. {
  27059. jx9_value *pEntry = HashmapExtractNodeValue(pNode);
  27060. if( pEntry ){
  27061. if( bStore ){
  27062. jx9MemObjStore(pEntry, pValue);
  27063. }else{
  27064. jx9MemObjLoad(pEntry, pValue);
  27065. }
  27066. }else{
  27067. jx9MemObjRelease(pValue);
  27068. }
  27069. }
  27070. /*
  27071. * Extract a node key.
  27072. */
  27073. JX9_PRIVATE void jx9HashmapExtractNodeKey(jx9_hashmap_node *pNode,jx9_value *pKey)
  27074. {
  27075. /* Fill with the current key */
  27076. if( pNode->iType == HASHMAP_INT_NODE ){
  27077. if( SyBlobLength(&pKey->sBlob) > 0 ){
  27078. SyBlobRelease(&pKey->sBlob);
  27079. }
  27080. pKey->x.iVal = pNode->xKey.iKey;
  27081. MemObjSetType(pKey, MEMOBJ_INT);
  27082. }else{
  27083. SyBlobReset(&pKey->sBlob);
  27084. SyBlobAppend(&pKey->sBlob, SyBlobData(&pNode->xKey.sKey), SyBlobLength(&pNode->xKey.sKey));
  27085. MemObjSetType(pKey, MEMOBJ_STRING);
  27086. }
  27087. }
  27088. #ifndef JX9_DISABLE_BUILTIN_FUNC
  27089. /*
  27090. * Store the address of nodes value in the given container.
  27091. * Refer to the [vfprintf(), vprintf(), vsprintf()] implementations
  27092. * defined in 'builtin.c' for more information.
  27093. */
  27094. JX9_PRIVATE int jx9HashmapValuesToSet(jx9_hashmap *pMap, SySet *pOut)
  27095. {
  27096. jx9_hashmap_node *pEntry = pMap->pFirst;
  27097. jx9_value *pValue;
  27098. sxu32 n;
  27099. /* Initialize the container */
  27100. SySetInit(pOut, &pMap->pVm->sAllocator, sizeof(jx9_value *));
  27101. for(n = 0 ; n < pMap->nEntry ; n++ ){
  27102. /* Extract node value */
  27103. pValue = HashmapExtractNodeValue(pEntry);
  27104. if( pValue ){
  27105. SySetPut(pOut, (const void *)&pValue);
  27106. }
  27107. /* Point to the next entry */
  27108. pEntry = pEntry->pPrev; /* Reverse link */
  27109. }
  27110. /* Total inserted entries */
  27111. return (int)SySetUsed(pOut);
  27112. }
  27113. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  27114. /*
  27115. * Merge sort.
  27116. * The merge sort implementation is based on the one found in the SQLite3 source tree.
  27117. * Status: Public domain
  27118. */
  27119. /* Node comparison callback signature */
  27120. typedef sxi32 (*ProcNodeCmp)(jx9_hashmap_node *, jx9_hashmap_node *, void *);
  27121. /*
  27122. ** Inputs:
  27123. ** a: A sorted, null-terminated linked list. (May be null).
  27124. ** b: A sorted, null-terminated linked list. (May be null).
  27125. ** cmp: A pointer to the comparison function.
  27126. **
  27127. ** Return Value:
  27128. ** A pointer to the head of a sorted list containing the elements
  27129. ** of both a and b.
  27130. **
  27131. ** Side effects:
  27132. ** The "next", "prev" pointers for elements in the lists a and b are
  27133. ** changed.
  27134. */
  27135. static jx9_hashmap_node * HashmapNodeMerge(jx9_hashmap_node *pA, jx9_hashmap_node *pB, ProcNodeCmp xCmp, void *pCmpData)
  27136. {
  27137. jx9_hashmap_node result, *pTail;
  27138. /* Prevent compiler warning */
  27139. result.pNext = result.pPrev = 0;
  27140. pTail = &result;
  27141. while( pA && pB ){
  27142. if( xCmp(pA, pB, pCmpData) < 0 ){
  27143. pTail->pPrev = pA;
  27144. pA->pNext = pTail;
  27145. pTail = pA;
  27146. pA = pA->pPrev;
  27147. }else{
  27148. pTail->pPrev = pB;
  27149. pB->pNext = pTail;
  27150. pTail = pB;
  27151. pB = pB->pPrev;
  27152. }
  27153. }
  27154. if( pA ){
  27155. pTail->pPrev = pA;
  27156. pA->pNext = pTail;
  27157. }else if( pB ){
  27158. pTail->pPrev = pB;
  27159. pB->pNext = pTail;
  27160. }else{
  27161. pTail->pPrev = pTail->pNext = 0;
  27162. }
  27163. return result.pPrev;
  27164. }
  27165. /*
  27166. ** Inputs:
  27167. ** Map: Input hashmap
  27168. ** cmp: A comparison function.
  27169. **
  27170. ** Return Value:
  27171. ** Sorted hashmap.
  27172. **
  27173. ** Side effects:
  27174. ** The "next" pointers for elements in list are changed.
  27175. */
  27176. #define N_SORT_BUCKET 32
  27177. static sxi32 HashmapMergeSort(jx9_hashmap *pMap, ProcNodeCmp xCmp, void *pCmpData)
  27178. {
  27179. jx9_hashmap_node *a[N_SORT_BUCKET], *p, *pIn;
  27180. sxu32 i;
  27181. SyZero(a, sizeof(a));
  27182. /* Point to the first inserted entry */
  27183. pIn = pMap->pFirst;
  27184. while( pIn ){
  27185. p = pIn;
  27186. pIn = p->pPrev;
  27187. p->pPrev = 0;
  27188. for(i=0; i<N_SORT_BUCKET-1; i++){
  27189. if( a[i]==0 ){
  27190. a[i] = p;
  27191. break;
  27192. }else{
  27193. p = HashmapNodeMerge(a[i], p, xCmp, pCmpData);
  27194. a[i] = 0;
  27195. }
  27196. }
  27197. if( i==N_SORT_BUCKET-1 ){
  27198. /* To get here, there need to be 2^(N_SORT_BUCKET) elements in he input list.
  27199. * But that is impossible.
  27200. */
  27201. a[i] = HashmapNodeMerge(a[i], p, xCmp, pCmpData);
  27202. }
  27203. }
  27204. p = a[0];
  27205. for(i=1; i<N_SORT_BUCKET; i++){
  27206. p = HashmapNodeMerge(p, a[i], xCmp, pCmpData);
  27207. }
  27208. p->pNext = 0;
  27209. /* Reflect the change */
  27210. pMap->pFirst = p;
  27211. /* Reset the loop cursor */
  27212. pMap->pCur = pMap->pFirst;
  27213. return SXRET_OK;
  27214. }
  27215. /*
  27216. * Node comparison callback.
  27217. * used-by: [sort(), asort(), ...]
  27218. */
  27219. static sxi32 HashmapCmpCallback1(jx9_hashmap_node *pA, jx9_hashmap_node *pB, void *pCmpData)
  27220. {
  27221. jx9_value sA, sB;
  27222. sxi32 iFlags;
  27223. int rc;
  27224. if( pCmpData == 0 ){
  27225. /* Perform a standard comparison */
  27226. rc = HashmapNodeCmp(pA, pB, FALSE);
  27227. return rc;
  27228. }
  27229. iFlags = SX_PTR_TO_INT(pCmpData);
  27230. /* Duplicate node values */
  27231. jx9MemObjInit(pA->pMap->pVm, &sA);
  27232. jx9MemObjInit(pA->pMap->pVm, &sB);
  27233. jx9HashmapExtractNodeValue(pA, &sA, FALSE);
  27234. jx9HashmapExtractNodeValue(pB, &sB, FALSE);
  27235. if( iFlags == 5 ){
  27236. /* String cast */
  27237. if( (sA.iFlags & MEMOBJ_STRING) == 0 ){
  27238. jx9MemObjToString(&sA);
  27239. }
  27240. if( (sB.iFlags & MEMOBJ_STRING) == 0 ){
  27241. jx9MemObjToString(&sB);
  27242. }
  27243. }else{
  27244. /* Numeric cast */
  27245. jx9MemObjToNumeric(&sA);
  27246. jx9MemObjToNumeric(&sB);
  27247. }
  27248. /* Perform the comparison */
  27249. rc = jx9MemObjCmp(&sA, &sB, FALSE, 0);
  27250. jx9MemObjRelease(&sA);
  27251. jx9MemObjRelease(&sB);
  27252. return rc;
  27253. }
  27254. /*
  27255. * Node comparison callback.
  27256. * Used by: [rsort(), arsort()];
  27257. */
  27258. static sxi32 HashmapCmpCallback3(jx9_hashmap_node *pA, jx9_hashmap_node *pB, void *pCmpData)
  27259. {
  27260. jx9_value sA, sB;
  27261. sxi32 iFlags;
  27262. int rc;
  27263. if( pCmpData == 0 ){
  27264. /* Perform a standard comparison */
  27265. rc = HashmapNodeCmp(pA, pB, FALSE);
  27266. return -rc;
  27267. }
  27268. iFlags = SX_PTR_TO_INT(pCmpData);
  27269. /* Duplicate node values */
  27270. jx9MemObjInit(pA->pMap->pVm, &sA);
  27271. jx9MemObjInit(pA->pMap->pVm, &sB);
  27272. jx9HashmapExtractNodeValue(pA, &sA, FALSE);
  27273. jx9HashmapExtractNodeValue(pB, &sB, FALSE);
  27274. if( iFlags == 5 ){
  27275. /* String cast */
  27276. if( (sA.iFlags & MEMOBJ_STRING) == 0 ){
  27277. jx9MemObjToString(&sA);
  27278. }
  27279. if( (sB.iFlags & MEMOBJ_STRING) == 0 ){
  27280. jx9MemObjToString(&sB);
  27281. }
  27282. }else{
  27283. /* Numeric cast */
  27284. jx9MemObjToNumeric(&sA);
  27285. jx9MemObjToNumeric(&sB);
  27286. }
  27287. /* Perform the comparison */
  27288. rc = jx9MemObjCmp(&sA, &sB, FALSE, 0);
  27289. jx9MemObjRelease(&sA);
  27290. jx9MemObjRelease(&sB);
  27291. return -rc;
  27292. }
  27293. /*
  27294. * Node comparison callback: Invoke an user-defined callback for the purpose of node comparison.
  27295. * used-by: [usort(), uasort()]
  27296. */
  27297. static sxi32 HashmapCmpCallback4(jx9_hashmap_node *pA, jx9_hashmap_node *pB, void *pCmpData)
  27298. {
  27299. jx9_value sResult, *pCallback;
  27300. jx9_value *pV1, *pV2;
  27301. jx9_value *apArg[2]; /* Callback arguments */
  27302. sxi32 rc;
  27303. /* Point to the desired callback */
  27304. pCallback = (jx9_value *)pCmpData;
  27305. /* initialize the result value */
  27306. jx9MemObjInit(pA->pMap->pVm, &sResult);
  27307. /* Extract nodes values */
  27308. pV1 = HashmapExtractNodeValue(pA);
  27309. pV2 = HashmapExtractNodeValue(pB);
  27310. apArg[0] = pV1;
  27311. apArg[1] = pV2;
  27312. /* Invoke the callback */
  27313. rc = jx9VmCallUserFunction(pA->pMap->pVm, pCallback, 2, apArg, &sResult);
  27314. if( rc != SXRET_OK ){
  27315. /* An error occured while calling user defined function [i.e: not defined] */
  27316. rc = -1; /* Set a dummy result */
  27317. }else{
  27318. /* Extract callback result */
  27319. if((sResult.iFlags & MEMOBJ_INT) == 0 ){
  27320. /* Perform an int cast */
  27321. jx9MemObjToInteger(&sResult);
  27322. }
  27323. rc = (sxi32)sResult.x.iVal;
  27324. }
  27325. jx9MemObjRelease(&sResult);
  27326. /* Callback result */
  27327. return rc;
  27328. }
  27329. /*
  27330. * Rehash all nodes keys after a merge-sort have been applied.
  27331. * Used by [sort(), usort() and rsort()].
  27332. */
  27333. static void HashmapSortRehash(jx9_hashmap *pMap)
  27334. {
  27335. jx9_hashmap_node *p, *pLast;
  27336. sxu32 i;
  27337. /* Rehash all entries */
  27338. pLast = p = pMap->pFirst;
  27339. pMap->iNextIdx = 0; /* Reset the automatic index */
  27340. i = 0;
  27341. for( ;; ){
  27342. if( i >= pMap->nEntry ){
  27343. pMap->pLast = pLast; /* Fix the last link broken by the merge-sort */
  27344. break;
  27345. }
  27346. if( p->iType == HASHMAP_BLOB_NODE ){
  27347. /* Do not maintain index association as requested by the JX9 specification */
  27348. SyBlobRelease(&p->xKey.sKey);
  27349. /* Change key type */
  27350. p->iType = HASHMAP_INT_NODE;
  27351. }
  27352. HashmapRehashIntNode(p);
  27353. /* Point to the next entry */
  27354. i++;
  27355. pLast = p;
  27356. p = p->pPrev; /* Reverse link */
  27357. }
  27358. }
  27359. /*
  27360. * Array functions implementation.
  27361. * Authors:
  27362. * Symisc Systems, devel@symisc.net.
  27363. * Copyright (C) Symisc Systems, http://jx9.symisc.net
  27364. * Status:
  27365. * Stable.
  27366. */
  27367. /*
  27368. * bool sort(array &$array[, int $sort_flags = SORT_REGULAR ] )
  27369. * Sort an array.
  27370. * Parameters
  27371. * $array
  27372. * The input array.
  27373. * $sort_flags
  27374. * The optional second parameter sort_flags may be used to modify the sorting behavior using these values:
  27375. * Sorting type flags:
  27376. * SORT_REGULAR - compare items normally (don't change types)
  27377. * SORT_NUMERIC - compare items numerically
  27378. * SORT_STRING - compare items as strings
  27379. * Return
  27380. * TRUE on success or FALSE on failure.
  27381. *
  27382. */
  27383. static int jx9_hashmap_sort(jx9_context *pCtx, int nArg, jx9_value **apArg)
  27384. {
  27385. jx9_hashmap *pMap;
  27386. /* Make sure we are dealing with a valid hashmap */
  27387. if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
  27388. /* Missing/Invalid arguments, return FALSE */
  27389. jx9_result_bool(pCtx, 0);
  27390. return JX9_OK;
  27391. }
  27392. /* Point to the internal representation of the input hashmap */
  27393. pMap = (jx9_hashmap *)apArg[0]->x.pOther;
  27394. if( pMap->nEntry > 1 ){
  27395. sxi32 iCmpFlags = 0;
  27396. if( nArg > 1 ){
  27397. /* Extract comparison flags */
  27398. iCmpFlags = jx9_value_to_int(apArg[1]);
  27399. if( iCmpFlags == 3 /* SORT_REGULAR */ ){
  27400. iCmpFlags = 0; /* Standard comparison */
  27401. }
  27402. }
  27403. /* Do the merge sort */
  27404. HashmapMergeSort(pMap, HashmapCmpCallback1, SX_INT_TO_PTR(iCmpFlags));
  27405. /* Rehash [Do not maintain index association as requested by the JX9 specification] */
  27406. HashmapSortRehash(pMap);
  27407. }
  27408. /* All done, return TRUE */
  27409. jx9_result_bool(pCtx, 1);
  27410. return JX9_OK;
  27411. }
  27412. /*
  27413. * bool rsort(array &$array[, int $sort_flags = SORT_REGULAR ] )
  27414. * Sort an array in reverse order.
  27415. * Parameters
  27416. * $array
  27417. * The input array.
  27418. * $sort_flags
  27419. * The optional second parameter sort_flags may be used to modify the sorting behavior using these values:
  27420. * Sorting type flags:
  27421. * SORT_REGULAR - compare items normally (don't change types)
  27422. * SORT_NUMERIC - compare items numerically
  27423. * SORT_STRING - compare items as strings
  27424. * Return
  27425. * TRUE on success or FALSE on failure.
  27426. */
  27427. static int jx9_hashmap_rsort(jx9_context *pCtx, int nArg, jx9_value **apArg)
  27428. {
  27429. jx9_hashmap *pMap;
  27430. /* Make sure we are dealing with a valid hashmap */
  27431. if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
  27432. /* Missing/Invalid arguments, return FALSE */
  27433. jx9_result_bool(pCtx, 0);
  27434. return JX9_OK;
  27435. }
  27436. /* Point to the internal representation of the input hashmap */
  27437. pMap = (jx9_hashmap *)apArg[0]->x.pOther;
  27438. if( pMap->nEntry > 1 ){
  27439. sxi32 iCmpFlags = 0;
  27440. if( nArg > 1 ){
  27441. /* Extract comparison flags */
  27442. iCmpFlags = jx9_value_to_int(apArg[1]);
  27443. if( iCmpFlags == 3 /* SORT_REGULAR */ ){
  27444. iCmpFlags = 0; /* Standard comparison */
  27445. }
  27446. }
  27447. /* Do the merge sort */
  27448. HashmapMergeSort(pMap, HashmapCmpCallback3, SX_INT_TO_PTR(iCmpFlags));
  27449. /* Rehash [Do not maintain index association as requested by the JX9 specification] */
  27450. HashmapSortRehash(pMap);
  27451. }
  27452. /* All done, return TRUE */
  27453. jx9_result_bool(pCtx, 1);
  27454. return JX9_OK;
  27455. }
  27456. /*
  27457. * bool usort(array &$array, callable $cmp_function)
  27458. * Sort an array by values using a user-defined comparison function.
  27459. * Parameters
  27460. * $array
  27461. * The input array.
  27462. * $cmp_function
  27463. * The comparison function must return an integer less than, equal to, or greater
  27464. * than zero if the first argument is considered to be respectively less than, equal
  27465. * to, or greater than the second.
  27466. * int callback ( mixed $a, mixed $b )
  27467. * Return
  27468. * TRUE on success or FALSE on failure.
  27469. */
  27470. static int jx9_hashmap_usort(jx9_context *pCtx, int nArg, jx9_value **apArg)
  27471. {
  27472. jx9_hashmap *pMap;
  27473. /* Make sure we are dealing with a valid hashmap */
  27474. if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
  27475. /* Missing/Invalid arguments, return FALSE */
  27476. jx9_result_bool(pCtx, 0);
  27477. return JX9_OK;
  27478. }
  27479. /* Point to the internal representation of the input hashmap */
  27480. pMap = (jx9_hashmap *)apArg[0]->x.pOther;
  27481. if( pMap->nEntry > 1 ){
  27482. jx9_value *pCallback = 0;
  27483. ProcNodeCmp xCmp;
  27484. xCmp = HashmapCmpCallback4; /* User-defined function as the comparison callback */
  27485. if( nArg > 1 && jx9_value_is_callable(apArg[1]) ){
  27486. /* Point to the desired callback */
  27487. pCallback = apArg[1];
  27488. }else{
  27489. /* Use the default comparison function */
  27490. xCmp = HashmapCmpCallback1;
  27491. }
  27492. /* Do the merge sort */
  27493. HashmapMergeSort(pMap, xCmp, pCallback);
  27494. /* Rehash [Do not maintain index association as requested by the JX9 specification] */
  27495. HashmapSortRehash(pMap);
  27496. }
  27497. /* All done, return TRUE */
  27498. jx9_result_bool(pCtx, 1);
  27499. return JX9_OK;
  27500. }
  27501. /*
  27502. * int count(array $var [, int $mode = COUNT_NORMAL ])
  27503. * Count all elements in an array, or something in an object.
  27504. * Parameters
  27505. * $var
  27506. * The array or the object.
  27507. * $mode
  27508. * If the optional mode parameter is set to COUNT_RECURSIVE (or 1), count()
  27509. * will recursively count the array. This is particularly useful for counting
  27510. * all the elements of a multidimensional array. count() does not detect infinite
  27511. * recursion.
  27512. * Return
  27513. * Returns the number of elements in the array.
  27514. */
  27515. static int jx9_hashmap_count(jx9_context *pCtx, int nArg, jx9_value **apArg)
  27516. {
  27517. int bRecursive = FALSE;
  27518. sxi64 iCount;
  27519. if( nArg < 1 ){
  27520. /* Missing arguments, return 0 */
  27521. jx9_result_int(pCtx, 0);
  27522. return JX9_OK;
  27523. }
  27524. if( !jx9_value_is_json_array(apArg[0]) ){
  27525. /* TICKET 1433-19: Handle objects */
  27526. int res = !jx9_value_is_null(apArg[0]);
  27527. jx9_result_int(pCtx, res);
  27528. return JX9_OK;
  27529. }
  27530. if( nArg > 1 ){
  27531. /* Recursive count? */
  27532. bRecursive = jx9_value_to_int(apArg[1]) == 1 /* COUNT_RECURSIVE */;
  27533. }
  27534. /* Count */
  27535. iCount = HashmapCount((jx9_hashmap *)apArg[0]->x.pOther, bRecursive, 0);
  27536. jx9_result_int64(pCtx, iCount);
  27537. return JX9_OK;
  27538. }
  27539. /*
  27540. * bool array_key_exists(value $key, array $search)
  27541. * Checks if the given key or index exists in the array.
  27542. * Parameters
  27543. * $key
  27544. * Value to check.
  27545. * $search
  27546. * An array with keys to check.
  27547. * Return
  27548. * TRUE on success or FALSE on failure.
  27549. */
  27550. static int jx9_hashmap_key_exists(jx9_context *pCtx, int nArg, jx9_value **apArg)
  27551. {
  27552. sxi32 rc;
  27553. if( nArg < 2 ){
  27554. /* Missing arguments, return FALSE */
  27555. jx9_result_bool(pCtx, 0);
  27556. return JX9_OK;
  27557. }
  27558. /* Make sure we are dealing with a valid hashmap */
  27559. if( !jx9_value_is_json_array(apArg[1]) ){
  27560. /* Invalid argument, return FALSE */
  27561. jx9_result_bool(pCtx, 0);
  27562. return JX9_OK;
  27563. }
  27564. /* Perform the lookup */
  27565. rc = jx9HashmapLookup((jx9_hashmap *)apArg[1]->x.pOther, apArg[0], 0);
  27566. /* lookup result */
  27567. jx9_result_bool(pCtx, rc == SXRET_OK ? 1 : 0);
  27568. return JX9_OK;
  27569. }
  27570. /*
  27571. * value array_pop(array $array)
  27572. * POP the last inserted element from the array.
  27573. * Parameter
  27574. * The array to get the value from.
  27575. * Return
  27576. * Poped value or NULL on failure.
  27577. */
  27578. static int jx9_hashmap_pop(jx9_context *pCtx, int nArg, jx9_value **apArg)
  27579. {
  27580. jx9_hashmap *pMap;
  27581. if( nArg < 1 ){
  27582. /* Missing arguments, return null */
  27583. jx9_result_null(pCtx);
  27584. return JX9_OK;
  27585. }
  27586. /* Make sure we are dealing with a valid hashmap */
  27587. if( !jx9_value_is_json_array(apArg[0]) ){
  27588. /* Invalid argument, return null */
  27589. jx9_result_null(pCtx);
  27590. return JX9_OK;
  27591. }
  27592. pMap = (jx9_hashmap *)apArg[0]->x.pOther;
  27593. if( pMap->nEntry < 1 ){
  27594. /* Noting to pop, return NULL */
  27595. jx9_result_null(pCtx);
  27596. }else{
  27597. jx9_hashmap_node *pLast = pMap->pLast;
  27598. jx9_value *pObj;
  27599. pObj = HashmapExtractNodeValue(pLast);
  27600. if( pObj ){
  27601. /* Node value */
  27602. jx9_result_value(pCtx, pObj);
  27603. /* Unlink the node */
  27604. jx9HashmapUnlinkNode(pLast);
  27605. }else{
  27606. jx9_result_null(pCtx);
  27607. }
  27608. /* Reset the cursor */
  27609. pMap->pCur = pMap->pFirst;
  27610. }
  27611. return JX9_OK;
  27612. }
  27613. /*
  27614. * int array_push($array, $var, ...)
  27615. * Push one or more elements onto the end of array. (Stack insertion)
  27616. * Parameters
  27617. * array
  27618. * The input array.
  27619. * var
  27620. * On or more value to push.
  27621. * Return
  27622. * New array count (including old items).
  27623. */
  27624. static int jx9_hashmap_push(jx9_context *pCtx, int nArg, jx9_value **apArg)
  27625. {
  27626. jx9_hashmap *pMap;
  27627. sxi32 rc;
  27628. int i;
  27629. if( nArg < 1 ){
  27630. /* Missing arguments, return 0 */
  27631. jx9_result_int(pCtx, 0);
  27632. return JX9_OK;
  27633. }
  27634. /* Make sure we are dealing with a valid hashmap */
  27635. if( !jx9_value_is_json_array(apArg[0]) ){
  27636. /* Invalid argument, return 0 */
  27637. jx9_result_int(pCtx, 0);
  27638. return JX9_OK;
  27639. }
  27640. /* Point to the internal representation of the input hashmap */
  27641. pMap = (jx9_hashmap *)apArg[0]->x.pOther;
  27642. /* Start pushing given values */
  27643. for( i = 1 ; i < nArg ; ++i ){
  27644. rc = jx9HashmapInsert(pMap, 0, apArg[i]);
  27645. if( rc != SXRET_OK ){
  27646. break;
  27647. }
  27648. }
  27649. /* Return the new count */
  27650. jx9_result_int64(pCtx, (sxi64)pMap->nEntry);
  27651. return JX9_OK;
  27652. }
  27653. /*
  27654. * value array_shift(array $array)
  27655. * Shift an element off the beginning of array.
  27656. * Parameter
  27657. * The array to get the value from.
  27658. * Return
  27659. * Shifted value or NULL on failure.
  27660. */
  27661. static int jx9_hashmap_shift(jx9_context *pCtx, int nArg, jx9_value **apArg)
  27662. {
  27663. jx9_hashmap *pMap;
  27664. if( nArg < 1 ){
  27665. /* Missing arguments, return null */
  27666. jx9_result_null(pCtx);
  27667. return JX9_OK;
  27668. }
  27669. /* Make sure we are dealing with a valid hashmap */
  27670. if( !jx9_value_is_json_array(apArg[0]) ){
  27671. /* Invalid argument, return null */
  27672. jx9_result_null(pCtx);
  27673. return JX9_OK;
  27674. }
  27675. /* Point to the internal representation of the hashmap */
  27676. pMap = (jx9_hashmap *)apArg[0]->x.pOther;
  27677. if( pMap->nEntry < 1 ){
  27678. /* Empty hashmap, return NULL */
  27679. jx9_result_null(pCtx);
  27680. }else{
  27681. jx9_hashmap_node *pEntry = pMap->pFirst;
  27682. jx9_value *pObj;
  27683. sxu32 n;
  27684. pObj = HashmapExtractNodeValue(pEntry);
  27685. if( pObj ){
  27686. /* Node value */
  27687. jx9_result_value(pCtx, pObj);
  27688. /* Unlink the first node */
  27689. jx9HashmapUnlinkNode(pEntry);
  27690. }else{
  27691. jx9_result_null(pCtx);
  27692. }
  27693. /* Rehash all int keys */
  27694. n = pMap->nEntry;
  27695. pEntry = pMap->pFirst;
  27696. pMap->iNextIdx = 0; /* Reset the automatic index */
  27697. for(;;){
  27698. if( n < 1 ){
  27699. break;
  27700. }
  27701. if( pEntry->iType == HASHMAP_INT_NODE ){
  27702. HashmapRehashIntNode(pEntry);
  27703. }
  27704. /* Point to the next entry */
  27705. pEntry = pEntry->pPrev; /* Reverse link */
  27706. n--;
  27707. }
  27708. /* Reset the cursor */
  27709. pMap->pCur = pMap->pFirst;
  27710. }
  27711. return JX9_OK;
  27712. }
  27713. /*
  27714. * Extract the node cursor value.
  27715. */
  27716. static sxi32 HashmapCurrentValue(jx9_context *pCtx, jx9_hashmap *pMap, int iDirection)
  27717. {
  27718. jx9_hashmap_node *pCur = pMap->pCur;
  27719. jx9_value *pVal;
  27720. if( pCur == 0 ){
  27721. /* Cursor does not point to anything, return FALSE */
  27722. jx9_result_bool(pCtx, 0);
  27723. return JX9_OK;
  27724. }
  27725. if( iDirection != 0 ){
  27726. if( iDirection > 0 ){
  27727. /* Point to the next entry */
  27728. pMap->pCur = pCur->pPrev; /* Reverse link */
  27729. pCur = pMap->pCur;
  27730. }else{
  27731. /* Point to the previous entry */
  27732. pMap->pCur = pCur->pNext; /* Reverse link */
  27733. pCur = pMap->pCur;
  27734. }
  27735. if( pCur == 0 ){
  27736. /* End of input reached, return FALSE */
  27737. jx9_result_bool(pCtx, 0);
  27738. return JX9_OK;
  27739. }
  27740. }
  27741. /* Point to the desired element */
  27742. pVal = HashmapExtractNodeValue(pCur);
  27743. if( pVal ){
  27744. jx9_result_value(pCtx, pVal);
  27745. }else{
  27746. jx9_result_bool(pCtx, 0);
  27747. }
  27748. return JX9_OK;
  27749. }
  27750. /*
  27751. * value current(array $array)
  27752. * Return the current element in an array.
  27753. * Parameter
  27754. * $input: The input array.
  27755. * Return
  27756. * The current() function simply returns the value of the array element that's currently
  27757. * being pointed to by the internal pointer. It does not move the pointer in any way.
  27758. * If the internal pointer points beyond the end of the elements list or the array
  27759. * is empty, current() returns FALSE.
  27760. */
  27761. static int jx9_hashmap_current(jx9_context *pCtx, int nArg, jx9_value **apArg)
  27762. {
  27763. if( nArg < 1 ){
  27764. /* Missing arguments, return FALSE */
  27765. jx9_result_bool(pCtx, 0);
  27766. return JX9_OK;
  27767. }
  27768. /* Make sure we are dealing with a valid hashmap */
  27769. if( !jx9_value_is_json_array(apArg[0]) ){
  27770. /* Invalid argument, return FALSE */
  27771. jx9_result_bool(pCtx, 0);
  27772. return JX9_OK;
  27773. }
  27774. HashmapCurrentValue(&(*pCtx), (jx9_hashmap *)apArg[0]->x.pOther, 0);
  27775. return JX9_OK;
  27776. }
  27777. /*
  27778. * value next(array $input)
  27779. * Advance the internal array pointer of an array.
  27780. * Parameter
  27781. * $input: The input array.
  27782. * Return
  27783. * next() behaves like current(), with one difference. It advances the internal array
  27784. * pointer one place forward before returning the element value. That means it returns
  27785. * the next array value and advances the internal array pointer by one.
  27786. */
  27787. static int jx9_hashmap_next(jx9_context *pCtx, int nArg, jx9_value **apArg)
  27788. {
  27789. if( nArg < 1 ){
  27790. /* Missing arguments, return FALSE */
  27791. jx9_result_bool(pCtx, 0);
  27792. return JX9_OK;
  27793. }
  27794. /* Make sure we are dealing with a valid hashmap */
  27795. if( !jx9_value_is_json_array(apArg[0]) ){
  27796. /* Invalid argument, return FALSE */
  27797. jx9_result_bool(pCtx, 0);
  27798. return JX9_OK;
  27799. }
  27800. HashmapCurrentValue(&(*pCtx), (jx9_hashmap *)apArg[0]->x.pOther, 1);
  27801. return JX9_OK;
  27802. }
  27803. /*
  27804. * value prev(array $input)
  27805. * Rewind the internal array pointer.
  27806. * Parameter
  27807. * $input: The input array.
  27808. * Return
  27809. * Returns the array value in the previous place that's pointed
  27810. * to by the internal array pointer, or FALSE if there are no more
  27811. * elements.
  27812. */
  27813. static int jx9_hashmap_prev(jx9_context *pCtx, int nArg, jx9_value **apArg)
  27814. {
  27815. if( nArg < 1 ){
  27816. /* Missing arguments, return FALSE */
  27817. jx9_result_bool(pCtx, 0);
  27818. return JX9_OK;
  27819. }
  27820. /* Make sure we are dealing with a valid hashmap */
  27821. if( !jx9_value_is_json_array(apArg[0]) ){
  27822. /* Invalid argument, return FALSE */
  27823. jx9_result_bool(pCtx, 0);
  27824. return JX9_OK;
  27825. }
  27826. HashmapCurrentValue(&(*pCtx), (jx9_hashmap *)apArg[0]->x.pOther, -1);
  27827. return JX9_OK;
  27828. }
  27829. /*
  27830. * value end(array $input)
  27831. * Set the internal pointer of an array to its last element.
  27832. * Parameter
  27833. * $input: The input array.
  27834. * Return
  27835. * Returns the value of the last element or FALSE for empty array.
  27836. */
  27837. static int jx9_hashmap_end(jx9_context *pCtx, int nArg, jx9_value **apArg)
  27838. {
  27839. jx9_hashmap *pMap;
  27840. if( nArg < 1 ){
  27841. /* Missing arguments, return FALSE */
  27842. jx9_result_bool(pCtx, 0);
  27843. return JX9_OK;
  27844. }
  27845. /* Make sure we are dealing with a valid hashmap */
  27846. if( !jx9_value_is_json_array(apArg[0]) ){
  27847. /* Invalid argument, return FALSE */
  27848. jx9_result_bool(pCtx, 0);
  27849. return JX9_OK;
  27850. }
  27851. /* Point to the internal representation of the input hashmap */
  27852. pMap = (jx9_hashmap *)apArg[0]->x.pOther;
  27853. /* Point to the last node */
  27854. pMap->pCur = pMap->pLast;
  27855. /* Return the last node value */
  27856. HashmapCurrentValue(&(*pCtx), pMap, 0);
  27857. return JX9_OK;
  27858. }
  27859. /*
  27860. * value reset(array $array )
  27861. * Set the internal pointer of an array to its first element.
  27862. * Parameter
  27863. * $input: The input array.
  27864. * Return
  27865. * Returns the value of the first array element, or FALSE if the array is empty.
  27866. */
  27867. static int jx9_hashmap_reset(jx9_context *pCtx, int nArg, jx9_value **apArg)
  27868. {
  27869. jx9_hashmap *pMap;
  27870. if( nArg < 1 ){
  27871. /* Missing arguments, return FALSE */
  27872. jx9_result_bool(pCtx, 0);
  27873. return JX9_OK;
  27874. }
  27875. /* Make sure we are dealing with a valid hashmap */
  27876. if( !jx9_value_is_json_array(apArg[0]) ){
  27877. /* Invalid argument, return FALSE */
  27878. jx9_result_bool(pCtx, 0);
  27879. return JX9_OK;
  27880. }
  27881. /* Point to the internal representation of the input hashmap */
  27882. pMap = (jx9_hashmap *)apArg[0]->x.pOther;
  27883. /* Point to the first node */
  27884. pMap->pCur = pMap->pFirst;
  27885. /* Return the last node value if available */
  27886. HashmapCurrentValue(&(*pCtx), pMap, 0);
  27887. return JX9_OK;
  27888. }
  27889. /*
  27890. * value key(array $array)
  27891. * Fetch a key from an array
  27892. * Parameter
  27893. * $input
  27894. * The input array.
  27895. * Return
  27896. * The key() function simply returns the key of the array element that's currently
  27897. * being pointed to by the internal pointer. It does not move the pointer in any way.
  27898. * If the internal pointer points beyond the end of the elements list or the array
  27899. * is empty, key() returns NULL.
  27900. */
  27901. static int jx9_hashmap_simple_key(jx9_context *pCtx, int nArg, jx9_value **apArg)
  27902. {
  27903. jx9_hashmap_node *pCur;
  27904. jx9_hashmap *pMap;
  27905. if( nArg < 1 ){
  27906. /* Missing arguments, return NULL */
  27907. jx9_result_null(pCtx);
  27908. return JX9_OK;
  27909. }
  27910. /* Make sure we are dealing with a valid hashmap */
  27911. if( !jx9_value_is_json_array(apArg[0]) ){
  27912. /* Invalid argument, return NULL */
  27913. jx9_result_null(pCtx);
  27914. return JX9_OK;
  27915. }
  27916. pMap = (jx9_hashmap *)apArg[0]->x.pOther;
  27917. pCur = pMap->pCur;
  27918. if( pCur == 0 ){
  27919. /* Cursor does not point to anything, return NULL */
  27920. jx9_result_null(pCtx);
  27921. return JX9_OK;
  27922. }
  27923. if( pCur->iType == HASHMAP_INT_NODE){
  27924. /* Key is integer */
  27925. jx9_result_int64(pCtx, pCur->xKey.iKey);
  27926. }else{
  27927. /* Key is blob */
  27928. jx9_result_string(pCtx,
  27929. (const char *)SyBlobData(&pCur->xKey.sKey), (int)SyBlobLength(&pCur->xKey.sKey));
  27930. }
  27931. return JX9_OK;
  27932. }
  27933. /*
  27934. * array each(array $input)
  27935. * Return the current key and value pair from an array and advance the array cursor.
  27936. * Parameter
  27937. * $input
  27938. * The input array.
  27939. * Return
  27940. * Returns the current key and value pair from the array array. This pair is returned
  27941. * in a four-element array, with the keys 0, 1, key, and value. Elements 0 and key
  27942. * contain the key name of the array element, and 1 and value contain the data.
  27943. * If the internal pointer for the array points past the end of the array contents
  27944. * each() returns FALSE.
  27945. */
  27946. static int jx9_hashmap_each(jx9_context *pCtx, int nArg, jx9_value **apArg)
  27947. {
  27948. jx9_hashmap_node *pCur;
  27949. jx9_hashmap *pMap;
  27950. jx9_value *pArray;
  27951. jx9_value *pVal;
  27952. jx9_value sKey;
  27953. if( nArg < 1 ){
  27954. /* Missing arguments, return FALSE */
  27955. jx9_result_bool(pCtx, 0);
  27956. return JX9_OK;
  27957. }
  27958. /* Make sure we are dealing with a valid hashmap */
  27959. if( !jx9_value_is_json_array(apArg[0]) ){
  27960. /* Invalid argument, return FALSE */
  27961. jx9_result_bool(pCtx, 0);
  27962. return JX9_OK;
  27963. }
  27964. /* Point to the internal representation that describe the input hashmap */
  27965. pMap = (jx9_hashmap *)apArg[0]->x.pOther;
  27966. if( pMap->pCur == 0 ){
  27967. /* Cursor does not point to anything, return FALSE */
  27968. jx9_result_bool(pCtx, 0);
  27969. return JX9_OK;
  27970. }
  27971. pCur = pMap->pCur;
  27972. /* Create a new array */
  27973. pArray = jx9_context_new_array(pCtx);
  27974. if( pArray == 0 ){
  27975. jx9_result_bool(pCtx, 0);
  27976. return JX9_OK;
  27977. }
  27978. pVal = HashmapExtractNodeValue(pCur);
  27979. /* Insert the current value */
  27980. jx9_array_add_strkey_elem(pArray, "1", pVal);
  27981. jx9_array_add_strkey_elem(pArray, "value", pVal);
  27982. /* Make the key */
  27983. if( pCur->iType == HASHMAP_INT_NODE ){
  27984. jx9MemObjInitFromInt(pMap->pVm, &sKey, pCur->xKey.iKey);
  27985. }else{
  27986. jx9MemObjInitFromString(pMap->pVm, &sKey, 0);
  27987. jx9MemObjStringAppend(&sKey, (const char *)SyBlobData(&pCur->xKey.sKey), SyBlobLength(&pCur->xKey.sKey));
  27988. }
  27989. /* Insert the current key */
  27990. jx9_array_add_elem(pArray, 0, &sKey);
  27991. jx9_array_add_strkey_elem(pArray, "key", &sKey);
  27992. jx9MemObjRelease(&sKey);
  27993. /* Advance the cursor */
  27994. pMap->pCur = pCur->pPrev; /* Reverse link */
  27995. /* Return the current entry */
  27996. jx9_result_value(pCtx, pArray);
  27997. return JX9_OK;
  27998. }
  27999. /*
  28000. * array array_values(array $input)
  28001. * Returns all the values from the input array and indexes numerically the array.
  28002. * Parameters
  28003. * input: The input array.
  28004. * Return
  28005. * An indexed array of values or NULL on failure.
  28006. */
  28007. static int jx9_hashmap_values(jx9_context *pCtx, int nArg, jx9_value **apArg)
  28008. {
  28009. jx9_hashmap_node *pNode;
  28010. jx9_hashmap *pMap;
  28011. jx9_value *pArray;
  28012. jx9_value *pObj;
  28013. sxu32 n;
  28014. if( nArg < 1 ){
  28015. /* Missing arguments, return NULL */
  28016. jx9_result_null(pCtx);
  28017. return JX9_OK;
  28018. }
  28019. /* Make sure we are dealing with a valid hashmap */
  28020. if( !jx9_value_is_json_array(apArg[0]) ){
  28021. /* Invalid argument, return NULL */
  28022. jx9_result_null(pCtx);
  28023. return JX9_OK;
  28024. }
  28025. /* Point to the internal representation that describe the input hashmap */
  28026. pMap = (jx9_hashmap *)apArg[0]->x.pOther;
  28027. /* Create a new array */
  28028. pArray = jx9_context_new_array(pCtx);
  28029. if( pArray == 0 ){
  28030. jx9_result_null(pCtx);
  28031. return JX9_OK;
  28032. }
  28033. /* Perform the requested operation */
  28034. pNode = pMap->pFirst;
  28035. for( n = 0 ; n < pMap->nEntry ; ++n ){
  28036. pObj = HashmapExtractNodeValue(pNode);
  28037. if( pObj ){
  28038. /* perform the insertion */
  28039. jx9_array_add_elem(pArray, 0/* Automatic index assign */, pObj);
  28040. }
  28041. /* Point to the next entry */
  28042. pNode = pNode->pPrev; /* Reverse link */
  28043. }
  28044. /* return the new array */
  28045. jx9_result_value(pCtx, pArray);
  28046. return JX9_OK;
  28047. }
  28048. /*
  28049. * bool array_same(array $arr1, array $arr2)
  28050. * Return TRUE if the given arrays are the same instance.
  28051. * This function is useful under JX9 since arrays are passed
  28052. * by reference unlike the engine which use pass by values.
  28053. * Parameters
  28054. * $arr1
  28055. * First array
  28056. * $arr2
  28057. * Second array
  28058. * Return
  28059. * TRUE if the arrays are the same instance.FALSE otherwise.
  28060. * Note
  28061. * This function is a symisc eXtension.
  28062. */
  28063. static int jx9_hashmap_same(jx9_context *pCtx, int nArg, jx9_value **apArg)
  28064. {
  28065. jx9_hashmap *p1, *p2;
  28066. int rc;
  28067. if( nArg < 2 || !jx9_value_is_json_array(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
  28068. /* Missing or invalid arguments, return FALSE*/
  28069. jx9_result_bool(pCtx, 0);
  28070. return JX9_OK;
  28071. }
  28072. /* Point to the hashmaps */
  28073. p1 = (jx9_hashmap *)apArg[0]->x.pOther;
  28074. p2 = (jx9_hashmap *)apArg[1]->x.pOther;
  28075. rc = (p1 == p2);
  28076. /* Same instance? */
  28077. jx9_result_bool(pCtx, rc);
  28078. return JX9_OK;
  28079. }
  28080. /*
  28081. * array array_merge(array $array1, ...)
  28082. * Merge one or more arrays.
  28083. * Parameters
  28084. * $array1
  28085. * Initial array to merge.
  28086. * ...
  28087. * More array to merge.
  28088. * Return
  28089. * The resulting array.
  28090. */
  28091. static int jx9_hashmap_merge(jx9_context *pCtx, int nArg, jx9_value **apArg)
  28092. {
  28093. jx9_hashmap *pMap, *pSrc;
  28094. jx9_value *pArray;
  28095. int i;
  28096. if( nArg < 1 ){
  28097. /* Missing arguments, return NULL */
  28098. jx9_result_null(pCtx);
  28099. return JX9_OK;
  28100. }
  28101. /* Create a new array */
  28102. pArray = jx9_context_new_array(pCtx);
  28103. if( pArray == 0 ){
  28104. jx9_result_null(pCtx);
  28105. return JX9_OK;
  28106. }
  28107. /* Point to the internal representation of the hashmap */
  28108. pMap = (jx9_hashmap *)pArray->x.pOther;
  28109. /* Start merging */
  28110. for( i = 0 ; i < nArg ; i++ ){
  28111. /* Make sure we are dealing with a valid hashmap */
  28112. if( !jx9_value_is_json_array(apArg[i]) ){
  28113. /* Insert scalar value */
  28114. jx9_array_add_elem(pArray, 0, apArg[i]);
  28115. }else{
  28116. pSrc = (jx9_hashmap *)apArg[i]->x.pOther;
  28117. /* Merge the two hashmaps */
  28118. HashmapMerge(pSrc, pMap);
  28119. }
  28120. }
  28121. /* Return the freshly created array */
  28122. jx9_result_value(pCtx, pArray);
  28123. return JX9_OK;
  28124. }
  28125. /*
  28126. * bool in_array(value $needle, array $haystack[, bool $strict = FALSE ])
  28127. * Checks if a value exists in an array.
  28128. * Parameters
  28129. * $needle
  28130. * The searched value.
  28131. * Note:
  28132. * If needle is a string, the comparison is done in a case-sensitive manner.
  28133. * $haystack
  28134. * The target array.
  28135. * $strict
  28136. * If the third parameter strict is set to TRUE then the in_array() function
  28137. * will also check the types of the needle in the haystack.
  28138. */
  28139. static int jx9_hashmap_in_array(jx9_context *pCtx, int nArg, jx9_value **apArg)
  28140. {
  28141. jx9_value *pNeedle;
  28142. int bStrict;
  28143. int rc;
  28144. if( nArg < 2 ){
  28145. /* Missing argument, return FALSE */
  28146. jx9_result_bool(pCtx, 0);
  28147. return JX9_OK;
  28148. }
  28149. pNeedle = apArg[0];
  28150. bStrict = 0;
  28151. if( nArg > 2 ){
  28152. bStrict = jx9_value_to_bool(apArg[2]);
  28153. }
  28154. if( !jx9_value_is_json_array(apArg[1]) ){
  28155. /* haystack must be an array, perform a standard comparison */
  28156. rc = jx9_value_compare(pNeedle, apArg[1], bStrict);
  28157. /* Set the comparison result */
  28158. jx9_result_bool(pCtx, rc == 0);
  28159. return JX9_OK;
  28160. }
  28161. /* Perform the lookup */
  28162. rc = HashmapFindValue((jx9_hashmap *)apArg[1]->x.pOther, pNeedle, 0, bStrict);
  28163. /* Lookup result */
  28164. jx9_result_bool(pCtx, rc == SXRET_OK);
  28165. return JX9_OK;
  28166. }
  28167. /*
  28168. * array array_copy(array $source)
  28169. * Make a blind copy of the target array.
  28170. * Parameters
  28171. * $source
  28172. * Target array
  28173. * Return
  28174. * Copy of the target array on success.NULL otherwise.
  28175. * Note
  28176. * This function is a symisc eXtension.
  28177. */
  28178. static int jx9_hashmap_copy(jx9_context *pCtx, int nArg, jx9_value **apArg)
  28179. {
  28180. jx9_hashmap *pMap;
  28181. jx9_value *pArray;
  28182. if( nArg < 1 ){
  28183. /* Missing arguments, return NULL */
  28184. jx9_result_null(pCtx);
  28185. return JX9_OK;
  28186. }
  28187. /* Create a new array */
  28188. pArray = jx9_context_new_array(pCtx);
  28189. if( pArray == 0 ){
  28190. jx9_result_null(pCtx);
  28191. return JX9_OK;
  28192. }
  28193. /* Point to the internal representation of the hashmap */
  28194. pMap = (jx9_hashmap *)pArray->x.pOther;
  28195. if( jx9_value_is_json_array(apArg[0])){
  28196. /* Point to the internal representation of the source */
  28197. jx9_hashmap *pSrc = (jx9_hashmap *)apArg[0]->x.pOther;
  28198. /* Perform the copy */
  28199. jx9HashmapDup(pSrc, pMap);
  28200. }else{
  28201. /* Simple insertion */
  28202. jx9HashmapInsert(pMap, 0/* Automatic index assign*/, apArg[0]);
  28203. }
  28204. /* Return the duplicated array */
  28205. jx9_result_value(pCtx, pArray);
  28206. return JX9_OK;
  28207. }
  28208. /*
  28209. * bool array_erase(array $source)
  28210. * Remove all elements from a given array.
  28211. * Parameters
  28212. * $source
  28213. * Target array
  28214. * Return
  28215. * TRUE on success.FALSE otherwise.
  28216. * Note
  28217. * This function is a symisc eXtension.
  28218. */
  28219. static int jx9_hashmap_erase(jx9_context *pCtx, int nArg, jx9_value **apArg)
  28220. {
  28221. jx9_hashmap *pMap;
  28222. if( nArg < 1 ){
  28223. /* Missing arguments */
  28224. jx9_result_bool(pCtx, 0);
  28225. return JX9_OK;
  28226. }
  28227. /* Point to the target hashmap */
  28228. pMap = (jx9_hashmap *)apArg[0]->x.pOther;
  28229. /* Erase */
  28230. jx9HashmapRelease(pMap, FALSE);
  28231. return JX9_OK;
  28232. }
  28233. /*
  28234. * array array_diff(array $array1, array $array2, ...)
  28235. * Computes the difference of arrays.
  28236. * Parameters
  28237. * $array1
  28238. * The array to compare from
  28239. * $array2
  28240. * An array to compare against
  28241. * $...
  28242. * More arrays to compare against
  28243. * Return
  28244. * Returns an array containing all the entries from array1 that
  28245. * are not present in any of the other arrays.
  28246. */
  28247. static int jx9_hashmap_diff(jx9_context *pCtx, int nArg, jx9_value **apArg)
  28248. {
  28249. jx9_hashmap_node *pEntry;
  28250. jx9_hashmap *pSrc, *pMap;
  28251. jx9_value *pArray;
  28252. jx9_value *pVal;
  28253. sxi32 rc;
  28254. sxu32 n;
  28255. int i;
  28256. if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
  28257. /* Missing arguments, return NULL */
  28258. jx9_result_null(pCtx);
  28259. return JX9_OK;
  28260. }
  28261. if( nArg == 1 ){
  28262. /* Return the first array since we cannot perform a diff */
  28263. jx9_result_value(pCtx, apArg[0]);
  28264. return JX9_OK;
  28265. }
  28266. /* Create a new array */
  28267. pArray = jx9_context_new_array(pCtx);
  28268. if( pArray == 0 ){
  28269. jx9_result_null(pCtx);
  28270. return JX9_OK;
  28271. }
  28272. /* Point to the internal representation of the source hashmap */
  28273. pSrc = (jx9_hashmap *)apArg[0]->x.pOther;
  28274. /* Perform the diff */
  28275. pEntry = pSrc->pFirst;
  28276. n = pSrc->nEntry;
  28277. for(;;){
  28278. if( n < 1 ){
  28279. break;
  28280. }
  28281. /* Extract the node value */
  28282. pVal = HashmapExtractNodeValue(pEntry);
  28283. if( pVal ){
  28284. for( i = 1 ; i < nArg ; i++ ){
  28285. if( !jx9_value_is_json_array(apArg[i])) {
  28286. /* ignore */
  28287. continue;
  28288. }
  28289. /* Point to the internal representation of the hashmap */
  28290. pMap = (jx9_hashmap *)apArg[i]->x.pOther;
  28291. /* Perform the lookup */
  28292. rc = HashmapFindValue(pMap, pVal, 0, TRUE);
  28293. if( rc == SXRET_OK ){
  28294. /* Value exist */
  28295. break;
  28296. }
  28297. }
  28298. if( i >= nArg ){
  28299. /* Perform the insertion */
  28300. HashmapInsertNode((jx9_hashmap *)pArray->x.pOther, pEntry, TRUE);
  28301. }
  28302. }
  28303. /* Point to the next entry */
  28304. pEntry = pEntry->pPrev; /* Reverse link */
  28305. n--;
  28306. }
  28307. /* Return the freshly created array */
  28308. jx9_result_value(pCtx, pArray);
  28309. return JX9_OK;
  28310. }
  28311. /*
  28312. * array array_intersect(array $array1 , array $array2, ...)
  28313. * Computes the intersection of arrays.
  28314. * Parameters
  28315. * $array1
  28316. * The array to compare from
  28317. * $array2
  28318. * An array to compare against
  28319. * $...
  28320. * More arrays to compare against
  28321. * Return
  28322. * Returns an array containing all of the values in array1 whose values exist
  28323. * in all of the parameters. .
  28324. * Note that NULL is returned on failure.
  28325. */
  28326. static int jx9_hashmap_intersect(jx9_context *pCtx, int nArg, jx9_value **apArg)
  28327. {
  28328. jx9_hashmap_node *pEntry;
  28329. jx9_hashmap *pSrc, *pMap;
  28330. jx9_value *pArray;
  28331. jx9_value *pVal;
  28332. sxi32 rc;
  28333. sxu32 n;
  28334. int i;
  28335. if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){
  28336. /* Missing arguments, return NULL */
  28337. jx9_result_null(pCtx);
  28338. return JX9_OK;
  28339. }
  28340. if( nArg == 1 ){
  28341. /* Return the first array since we cannot perform a diff */
  28342. jx9_result_value(pCtx, apArg[0]);
  28343. return JX9_OK;
  28344. }
  28345. /* Create a new array */
  28346. pArray = jx9_context_new_array(pCtx);
  28347. if( pArray == 0 ){
  28348. jx9_result_null(pCtx);
  28349. return JX9_OK;
  28350. }
  28351. /* Point to the internal representation of the source hashmap */
  28352. pSrc = (jx9_hashmap *)apArg[0]->x.pOther;
  28353. /* Perform the intersection */
  28354. pEntry = pSrc->pFirst;
  28355. n = pSrc->nEntry;
  28356. for(;;){
  28357. if( n < 1 ){
  28358. break;
  28359. }
  28360. /* Extract the node value */
  28361. pVal = HashmapExtractNodeValue(pEntry);
  28362. if( pVal ){
  28363. for( i = 1 ; i < nArg ; i++ ){
  28364. if( !jx9_value_is_json_array(apArg[i])) {
  28365. /* ignore */
  28366. continue;
  28367. }
  28368. /* Point to the internal representation of the hashmap */
  28369. pMap = (jx9_hashmap *)apArg[i]->x.pOther;
  28370. /* Perform the lookup */
  28371. rc = HashmapFindValue(pMap, pVal, 0, TRUE);
  28372. if( rc != SXRET_OK ){
  28373. /* Value does not exist */
  28374. break;
  28375. }
  28376. }
  28377. if( i >= nArg ){
  28378. /* Perform the insertion */
  28379. HashmapInsertNode((jx9_hashmap *)pArray->x.pOther, pEntry, TRUE);
  28380. }
  28381. }
  28382. /* Point to the next entry */
  28383. pEntry = pEntry->pPrev; /* Reverse link */
  28384. n--;
  28385. }
  28386. /* Return the freshly created array */
  28387. jx9_result_value(pCtx, pArray);
  28388. return JX9_OK;
  28389. }
  28390. /*
  28391. * number array_sum(array $array )
  28392. * Calculate the sum of values in an array.
  28393. * Parameters
  28394. * $array: The input array.
  28395. * Return
  28396. * Returns the sum of values as an integer or float.
  28397. */
  28398. static void DoubleSum(jx9_context *pCtx, jx9_hashmap *pMap)
  28399. {
  28400. jx9_hashmap_node *pEntry;
  28401. jx9_value *pObj;
  28402. double dSum = 0;
  28403. sxu32 n;
  28404. pEntry = pMap->pFirst;
  28405. for( n = 0 ; n < pMap->nEntry ; n++ ){
  28406. pObj = HashmapExtractNodeValue(pEntry);
  28407. if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){
  28408. if( pObj->iFlags & MEMOBJ_REAL ){
  28409. dSum += pObj->x.rVal;
  28410. }else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
  28411. dSum += (double)pObj->x.iVal;
  28412. }else if( pObj->iFlags & MEMOBJ_STRING ){
  28413. if( SyBlobLength(&pObj->sBlob) > 0 ){
  28414. double dv = 0;
  28415. SyStrToReal((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&dv, 0);
  28416. dSum += dv;
  28417. }
  28418. }
  28419. }
  28420. /* Point to the next entry */
  28421. pEntry = pEntry->pPrev; /* Reverse link */
  28422. }
  28423. /* Return sum */
  28424. jx9_result_double(pCtx, dSum);
  28425. }
  28426. static void Int64Sum(jx9_context *pCtx, jx9_hashmap *pMap)
  28427. {
  28428. jx9_hashmap_node *pEntry;
  28429. jx9_value *pObj;
  28430. sxi64 nSum = 0;
  28431. sxu32 n;
  28432. pEntry = pMap->pFirst;
  28433. for( n = 0 ; n < pMap->nEntry ; n++ ){
  28434. pObj = HashmapExtractNodeValue(pEntry);
  28435. if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){
  28436. if( pObj->iFlags & MEMOBJ_REAL ){
  28437. nSum += (sxi64)pObj->x.rVal;
  28438. }else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
  28439. nSum += pObj->x.iVal;
  28440. }else if( pObj->iFlags & MEMOBJ_STRING ){
  28441. if( SyBlobLength(&pObj->sBlob) > 0 ){
  28442. sxi64 nv = 0;
  28443. SyStrToInt64((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&nv, 0);
  28444. nSum += nv;
  28445. }
  28446. }
  28447. }
  28448. /* Point to the next entry */
  28449. pEntry = pEntry->pPrev; /* Reverse link */
  28450. }
  28451. /* Return sum */
  28452. jx9_result_int64(pCtx, nSum);
  28453. }
  28454. /* number array_sum(array $array )
  28455. * (See block-coment above)
  28456. */
  28457. static int jx9_hashmap_sum(jx9_context *pCtx, int nArg, jx9_value **apArg)
  28458. {
  28459. jx9_hashmap *pMap;
  28460. jx9_value *pObj;
  28461. if( nArg < 1 ){
  28462. /* Missing arguments, return 0 */
  28463. jx9_result_int(pCtx, 0);
  28464. return JX9_OK;
  28465. }
  28466. /* Make sure we are dealing with a valid hashmap */
  28467. if( !jx9_value_is_json_array(apArg[0]) ){
  28468. /* Invalid argument, return 0 */
  28469. jx9_result_int(pCtx, 0);
  28470. return JX9_OK;
  28471. }
  28472. pMap = (jx9_hashmap *)apArg[0]->x.pOther;
  28473. if( pMap->nEntry < 1 ){
  28474. /* Nothing to compute, return 0 */
  28475. jx9_result_int(pCtx, 0);
  28476. return JX9_OK;
  28477. }
  28478. /* If the first element is of type float, then perform floating
  28479. * point computaion.Otherwise switch to int64 computaion.
  28480. */
  28481. pObj = HashmapExtractNodeValue(pMap->pFirst);
  28482. if( pObj == 0 ){
  28483. jx9_result_int(pCtx, 0);
  28484. return JX9_OK;
  28485. }
  28486. if( pObj->iFlags & MEMOBJ_REAL ){
  28487. DoubleSum(pCtx, pMap);
  28488. }else{
  28489. Int64Sum(pCtx, pMap);
  28490. }
  28491. return JX9_OK;
  28492. }
  28493. /*
  28494. * number array_product(array $array )
  28495. * Calculate the product of values in an array.
  28496. * Parameters
  28497. * $array: The input array.
  28498. * Return
  28499. * Returns the product of values as an integer or float.
  28500. */
  28501. static void DoubleProd(jx9_context *pCtx, jx9_hashmap *pMap)
  28502. {
  28503. jx9_hashmap_node *pEntry;
  28504. jx9_value *pObj;
  28505. double dProd;
  28506. sxu32 n;
  28507. pEntry = pMap->pFirst;
  28508. dProd = 1;
  28509. for( n = 0 ; n < pMap->nEntry ; n++ ){
  28510. pObj = HashmapExtractNodeValue(pEntry);
  28511. if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){
  28512. if( pObj->iFlags & MEMOBJ_REAL ){
  28513. dProd *= pObj->x.rVal;
  28514. }else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
  28515. dProd *= (double)pObj->x.iVal;
  28516. }else if( pObj->iFlags & MEMOBJ_STRING ){
  28517. if( SyBlobLength(&pObj->sBlob) > 0 ){
  28518. double dv = 0;
  28519. SyStrToReal((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&dv, 0);
  28520. dProd *= dv;
  28521. }
  28522. }
  28523. }
  28524. /* Point to the next entry */
  28525. pEntry = pEntry->pPrev; /* Reverse link */
  28526. }
  28527. /* Return product */
  28528. jx9_result_double(pCtx, dProd);
  28529. }
  28530. static void Int64Prod(jx9_context *pCtx, jx9_hashmap *pMap)
  28531. {
  28532. jx9_hashmap_node *pEntry;
  28533. jx9_value *pObj;
  28534. sxi64 nProd;
  28535. sxu32 n;
  28536. pEntry = pMap->pFirst;
  28537. nProd = 1;
  28538. for( n = 0 ; n < pMap->nEntry ; n++ ){
  28539. pObj = HashmapExtractNodeValue(pEntry);
  28540. if( pObj && (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES)) == 0){
  28541. if( pObj->iFlags & MEMOBJ_REAL ){
  28542. nProd *= (sxi64)pObj->x.rVal;
  28543. }else if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){
  28544. nProd *= pObj->x.iVal;
  28545. }else if( pObj->iFlags & MEMOBJ_STRING ){
  28546. if( SyBlobLength(&pObj->sBlob) > 0 ){
  28547. sxi64 nv = 0;
  28548. SyStrToInt64((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&nv, 0);
  28549. nProd *= nv;
  28550. }
  28551. }
  28552. }
  28553. /* Point to the next entry */
  28554. pEntry = pEntry->pPrev; /* Reverse link */
  28555. }
  28556. /* Return product */
  28557. jx9_result_int64(pCtx, nProd);
  28558. }
  28559. /* number array_product(array $array )
  28560. * (See block-block comment above)
  28561. */
  28562. static int jx9_hashmap_product(jx9_context *pCtx, int nArg, jx9_value **apArg)
  28563. {
  28564. jx9_hashmap *pMap;
  28565. jx9_value *pObj;
  28566. if( nArg < 1 ){
  28567. /* Missing arguments, return 0 */
  28568. jx9_result_int(pCtx, 0);
  28569. return JX9_OK;
  28570. }
  28571. /* Make sure we are dealing with a valid hashmap */
  28572. if( !jx9_value_is_json_array(apArg[0]) ){
  28573. /* Invalid argument, return 0 */
  28574. jx9_result_int(pCtx, 0);
  28575. return JX9_OK;
  28576. }
  28577. pMap = (jx9_hashmap *)apArg[0]->x.pOther;
  28578. if( pMap->nEntry < 1 ){
  28579. /* Nothing to compute, return 0 */
  28580. jx9_result_int(pCtx, 0);
  28581. return JX9_OK;
  28582. }
  28583. /* If the first element is of type float, then perform floating
  28584. * point computaion.Otherwise switch to int64 computaion.
  28585. */
  28586. pObj = HashmapExtractNodeValue(pMap->pFirst);
  28587. if( pObj == 0 ){
  28588. jx9_result_int(pCtx, 0);
  28589. return JX9_OK;
  28590. }
  28591. if( pObj->iFlags & MEMOBJ_REAL ){
  28592. DoubleProd(pCtx, pMap);
  28593. }else{
  28594. Int64Prod(pCtx, pMap);
  28595. }
  28596. return JX9_OK;
  28597. }
  28598. /*
  28599. * array array_map(callback $callback, array $arr1)
  28600. * Applies the callback to the elements of the given arrays.
  28601. * Parameters
  28602. * $callback
  28603. * Callback function to run for each element in each array.
  28604. * $arr1
  28605. * An array to run through the callback function.
  28606. * Return
  28607. * Returns an array containing all the elements of arr1 after applying
  28608. * the callback function to each one.
  28609. * NOTE:
  28610. * array_map() passes only a single value to the callback.
  28611. */
  28612. static int jx9_hashmap_map(jx9_context *pCtx, int nArg, jx9_value **apArg)
  28613. {
  28614. jx9_value *pArray, *pValue, sKey, sResult;
  28615. jx9_hashmap_node *pEntry;
  28616. jx9_hashmap *pMap;
  28617. sxu32 n;
  28618. if( nArg < 2 || !jx9_value_is_json_array(apArg[1]) ){
  28619. /* Invalid arguments, return NULL */
  28620. jx9_result_null(pCtx);
  28621. return JX9_OK;
  28622. }
  28623. /* Create a new array */
  28624. pArray = jx9_context_new_array(pCtx);
  28625. if( pArray == 0 ){
  28626. jx9_result_null(pCtx);
  28627. return JX9_OK;
  28628. }
  28629. /* Point to the internal representation of the input hashmap */
  28630. pMap = (jx9_hashmap *)apArg[1]->x.pOther;
  28631. jx9MemObjInit(pMap->pVm, &sResult);
  28632. jx9MemObjInit(pMap->pVm, &sKey);
  28633. /* Perform the requested operation */
  28634. pEntry = pMap->pFirst;
  28635. for( n = 0 ; n < pMap->nEntry ; n++ ){
  28636. /* Extrcat the node value */
  28637. pValue = HashmapExtractNodeValue(pEntry);
  28638. if( pValue ){
  28639. sxi32 rc;
  28640. /* Invoke the supplied callback */
  28641. rc = jx9VmCallUserFunction(pMap->pVm, apArg[0], 1, &pValue, &sResult);
  28642. /* Extract the node key */
  28643. jx9HashmapExtractNodeKey(pEntry, &sKey);
  28644. if( rc != SXRET_OK ){
  28645. /* An error occured while invoking the supplied callback [i.e: not defined] */
  28646. jx9_array_add_elem(pArray, &sKey, pValue); /* Keep the same value */
  28647. }else{
  28648. /* Insert the callback return value */
  28649. jx9_array_add_elem(pArray, &sKey, &sResult);
  28650. }
  28651. jx9MemObjRelease(&sKey);
  28652. jx9MemObjRelease(&sResult);
  28653. }
  28654. /* Point to the next entry */
  28655. pEntry = pEntry->pPrev; /* Reverse link */
  28656. }
  28657. jx9_result_value(pCtx, pArray);
  28658. return JX9_OK;
  28659. }
  28660. /*
  28661. * bool array_walk(array &$array, callback $funcname [, value $userdata ] )
  28662. * Apply a user function to every member of an array.
  28663. * Parameters
  28664. * $array
  28665. * The input array.
  28666. * $funcname
  28667. * Typically, funcname takes on two parameters.The array parameter's value being
  28668. * the first, and the key/index second.
  28669. * Note:
  28670. * If funcname needs to be working with the actual values of the array, specify the first
  28671. * parameter of funcname as a reference. Then, any changes made to those elements will
  28672. * be made in the original array itself.
  28673. * $userdata
  28674. * If the optional userdata parameter is supplied, it will be passed as the third parameter
  28675. * to the callback funcname.
  28676. * Return
  28677. * Returns TRUE on success or FALSE on failure.
  28678. */
  28679. static int jx9_hashmap_walk(jx9_context *pCtx, int nArg, jx9_value **apArg)
  28680. {
  28681. jx9_value *pValue, *pUserData, sKey;
  28682. jx9_hashmap_node *pEntry;
  28683. jx9_hashmap *pMap;
  28684. sxi32 rc;
  28685. sxu32 n;
  28686. if( nArg < 2 || !jx9_value_is_json_array(apArg[0]) ){
  28687. /* Invalid/Missing arguments, return FALSE */
  28688. jx9_result_bool(pCtx, 0);
  28689. return JX9_OK;
  28690. }
  28691. pUserData = nArg > 2 ? apArg[2] : 0;
  28692. /* Point to the internal representation of the input hashmap */
  28693. pMap = (jx9_hashmap *)apArg[0]->x.pOther;
  28694. jx9MemObjInit(pMap->pVm, &sKey);
  28695. /* Perform the desired operation */
  28696. pEntry = pMap->pFirst;
  28697. for( n = 0 ; n < pMap->nEntry ; n++ ){
  28698. /* Extract the node value */
  28699. pValue = HashmapExtractNodeValue(pEntry);
  28700. if( pValue ){
  28701. /* Extract the entry key */
  28702. jx9HashmapExtractNodeKey(pEntry, &sKey);
  28703. /* Invoke the supplied callback */
  28704. rc = jx9VmCallUserFunctionAp(pMap->pVm, apArg[1], 0, pValue, &sKey, pUserData, 0);
  28705. jx9MemObjRelease(&sKey);
  28706. if( rc != SXRET_OK ){
  28707. /* An error occured while invoking the supplied callback [i.e: not defined] */
  28708. jx9_result_bool(pCtx, 0); /* return FALSE */
  28709. return JX9_OK;
  28710. }
  28711. }
  28712. /* Point to the next entry */
  28713. pEntry = pEntry->pPrev; /* Reverse link */
  28714. }
  28715. /* All done, return TRUE */
  28716. jx9_result_bool(pCtx, 1);
  28717. return JX9_OK;
  28718. }
  28719. /*
  28720. * Table of built-in hashmap functions.
  28721. */
  28722. static const jx9_builtin_func aHashmapFunc[] = {
  28723. {"count", jx9_hashmap_count },
  28724. {"sizeof", jx9_hashmap_count },
  28725. {"array_key_exists", jx9_hashmap_key_exists },
  28726. {"array_pop", jx9_hashmap_pop },
  28727. {"array_push", jx9_hashmap_push },
  28728. {"array_shift", jx9_hashmap_shift },
  28729. {"array_product", jx9_hashmap_product },
  28730. {"array_sum", jx9_hashmap_sum },
  28731. {"array_values", jx9_hashmap_values },
  28732. {"array_same", jx9_hashmap_same },
  28733. {"array_merge", jx9_hashmap_merge },
  28734. {"array_diff", jx9_hashmap_diff },
  28735. {"array_intersect", jx9_hashmap_intersect},
  28736. {"in_array", jx9_hashmap_in_array },
  28737. {"array_copy", jx9_hashmap_copy },
  28738. {"array_erase", jx9_hashmap_erase },
  28739. {"array_map", jx9_hashmap_map },
  28740. {"array_walk", jx9_hashmap_walk },
  28741. {"sort", jx9_hashmap_sort },
  28742. {"rsort", jx9_hashmap_rsort },
  28743. {"usort", jx9_hashmap_usort },
  28744. {"current", jx9_hashmap_current },
  28745. {"each", jx9_hashmap_each },
  28746. {"pos", jx9_hashmap_current },
  28747. {"next", jx9_hashmap_next },
  28748. {"prev", jx9_hashmap_prev },
  28749. {"end", jx9_hashmap_end },
  28750. {"reset", jx9_hashmap_reset },
  28751. {"key", jx9_hashmap_simple_key }
  28752. };
  28753. /*
  28754. * Register the built-in hashmap functions defined above.
  28755. */
  28756. JX9_PRIVATE void jx9RegisterHashmapFunctions(jx9_vm *pVm)
  28757. {
  28758. sxu32 n;
  28759. for( n = 0 ; n < SX_ARRAYSIZE(aHashmapFunc) ; n++ ){
  28760. jx9_create_function(&(*pVm), aHashmapFunc[n].zName, aHashmapFunc[n].xFunc, 0);
  28761. }
  28762. }
  28763. /*
  28764. * Iterate throw hashmap entries and invoke the given callback [i.e: xWalk()] for each
  28765. * retrieved entry.
  28766. * Note that argument are passed to the callback by copy. That is, any modification to
  28767. * the entry value in the callback body will not alter the real value.
  28768. * If the callback wishes to abort processing [i.e: it's invocation] it must return
  28769. * a value different from JX9_OK.
  28770. * Refer to [jx9_array_walk()] for more information.
  28771. */
  28772. JX9_PRIVATE sxi32 jx9HashmapWalk(
  28773. jx9_hashmap *pMap, /* Target hashmap */
  28774. int (*xWalk)(jx9_value *, jx9_value *, void *), /* Walker callback */
  28775. void *pUserData /* Last argument to xWalk() */
  28776. )
  28777. {
  28778. jx9_hashmap_node *pEntry;
  28779. jx9_value sKey, sValue;
  28780. sxi32 rc;
  28781. sxu32 n;
  28782. /* Initialize walker parameter */
  28783. rc = SXRET_OK;
  28784. jx9MemObjInit(pMap->pVm, &sKey);
  28785. jx9MemObjInit(pMap->pVm, &sValue);
  28786. n = pMap->nEntry;
  28787. pEntry = pMap->pFirst;
  28788. /* Start the iteration process */
  28789. for(;;){
  28790. if( n < 1 ){
  28791. break;
  28792. }
  28793. /* Extract a copy of the key and a copy the current value */
  28794. jx9HashmapExtractNodeKey(pEntry, &sKey);
  28795. jx9HashmapExtractNodeValue(pEntry, &sValue, FALSE);
  28796. /* Invoke the user callback */
  28797. rc = xWalk(&sKey, &sValue, pUserData);
  28798. /* Release the copy of the key and the value */
  28799. jx9MemObjRelease(&sKey);
  28800. jx9MemObjRelease(&sValue);
  28801. if( rc != JX9_OK ){
  28802. /* Callback request an operation abort */
  28803. return SXERR_ABORT;
  28804. }
  28805. /* Point to the next entry */
  28806. pEntry = pEntry->pPrev; /* Reverse link */
  28807. n--;
  28808. }
  28809. /* All done */
  28810. return SXRET_OK;
  28811. }
  28812. /*
  28813. * ----------------------------------------------------------
  28814. * File: const.c
  28815. * MD5: f3980b00dd1eda0bb2b749424a8dfffe
  28816. * ----------------------------------------------------------
  28817. */
  28818. /*
  28819. * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
  28820. * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
  28821. * Version 1.7.2
  28822. * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  28823. * please contact Symisc Systems via:
  28824. * legal@symisc.net
  28825. * licensing@symisc.net
  28826. * contact@symisc.net
  28827. * or visit:
  28828. * http://jx9.symisc.net/
  28829. */
  28830. /* $SymiscID: const.c v1.7 Win7 2012-12-13 00:01 stable <chm@symisc.net> $ */
  28831. #ifndef JX9_AMALGAMATION
  28832. #include "jx9Int.h"
  28833. #endif
  28834. /* This file implement built-in constants for the JX9 engine. */
  28835. /*
  28836. * JX9_VERSION
  28837. * __JX9__
  28838. * Expand the current version of the JX9 engine.
  28839. */
  28840. static void JX9_VER_Const(jx9_value *pVal, void *pUnused)
  28841. {
  28842. SXUNUSED(pUnused);
  28843. jx9_value_string(pVal, jx9_lib_signature(), -1/*Compute length automatically*/);
  28844. }
  28845. #ifdef __WINNT__
  28846. #include <Windows.h>
  28847. #elif defined(__UNIXES__)
  28848. #include <sys/utsname.h>
  28849. #endif
  28850. /*
  28851. * JX9_OS
  28852. * __OS__
  28853. * Expand the name of the host Operating System.
  28854. */
  28855. static void JX9_OS_Const(jx9_value *pVal, void *pUnused)
  28856. {
  28857. #if defined(__WINNT__)
  28858. jx9_value_string(pVal, "WinNT", (int)sizeof("WinNT")-1);
  28859. #elif defined(__UNIXES__)
  28860. struct utsname sInfo;
  28861. if( uname(&sInfo) != 0 ){
  28862. jx9_value_string(pVal, "Unix", (int)sizeof("Unix")-1);
  28863. }else{
  28864. jx9_value_string(pVal, sInfo.sysname, -1);
  28865. }
  28866. #else
  28867. jx9_value_string(pVal,"Host OS", (int)sizeof("Host OS")-1);
  28868. #endif
  28869. SXUNUSED(pUnused);
  28870. }
  28871. /*
  28872. * JX9_EOL
  28873. * Expand the correct 'End Of Line' symbol for this platform.
  28874. */
  28875. static void JX9_EOL_Const(jx9_value *pVal, void *pUnused)
  28876. {
  28877. SXUNUSED(pUnused);
  28878. #ifdef __WINNT__
  28879. jx9_value_string(pVal, "\r\n", (int)sizeof("\r\n")-1);
  28880. #else
  28881. jx9_value_string(pVal, "\n", (int)sizeof(char));
  28882. #endif
  28883. }
  28884. /*
  28885. * JX9_INT_MAX
  28886. * Expand the largest integer supported.
  28887. * Note that JX9 deals with 64-bit integer for all platforms.
  28888. */
  28889. static void JX9_INTMAX_Const(jx9_value *pVal, void *pUnused)
  28890. {
  28891. SXUNUSED(pUnused);
  28892. jx9_value_int64(pVal, SXI64_HIGH);
  28893. }
  28894. /*
  28895. * JX9_INT_SIZE
  28896. * Expand the size in bytes of a 64-bit integer.
  28897. */
  28898. static void JX9_INTSIZE_Const(jx9_value *pVal, void *pUnused)
  28899. {
  28900. SXUNUSED(pUnused);
  28901. jx9_value_int64(pVal, sizeof(sxi64));
  28902. }
  28903. /*
  28904. * DIRECTORY_SEPARATOR.
  28905. * Expand the directory separator character.
  28906. */
  28907. static void JX9_DIRSEP_Const(jx9_value *pVal, void *pUnused)
  28908. {
  28909. SXUNUSED(pUnused);
  28910. #ifdef __WINNT__
  28911. jx9_value_string(pVal, "\\", (int)sizeof(char));
  28912. #else
  28913. jx9_value_string(pVal, "/", (int)sizeof(char));
  28914. #endif
  28915. }
  28916. /*
  28917. * PATH_SEPARATOR.
  28918. * Expand the path separator character.
  28919. */
  28920. static void JX9_PATHSEP_Const(jx9_value *pVal, void *pUnused)
  28921. {
  28922. SXUNUSED(pUnused);
  28923. #ifdef __WINNT__
  28924. jx9_value_string(pVal, ";", (int)sizeof(char));
  28925. #else
  28926. jx9_value_string(pVal, ":", (int)sizeof(char));
  28927. #endif
  28928. }
  28929. #ifndef __WINNT__
  28930. #include <time.h>
  28931. #endif
  28932. /*
  28933. * __TIME__
  28934. * Expand the current time (GMT).
  28935. */
  28936. static void JX9_TIME_Const(jx9_value *pVal, void *pUnused)
  28937. {
  28938. Sytm sTm;
  28939. #ifdef __WINNT__
  28940. SYSTEMTIME sOS;
  28941. GetSystemTime(&sOS);
  28942. SYSTEMTIME_TO_SYTM(&sOS, &sTm);
  28943. #else
  28944. struct tm *pTm;
  28945. time_t t;
  28946. time(&t);
  28947. pTm = gmtime(&t);
  28948. STRUCT_TM_TO_SYTM(pTm, &sTm);
  28949. #endif
  28950. SXUNUSED(pUnused); /* cc warning */
  28951. /* Expand */
  28952. jx9_value_string_format(pVal, "%02d:%02d:%02d", sTm.tm_hour, sTm.tm_min, sTm.tm_sec);
  28953. }
  28954. /*
  28955. * __DATE__
  28956. * Expand the current date in the ISO-8601 format.
  28957. */
  28958. static void JX9_DATE_Const(jx9_value *pVal, void *pUnused)
  28959. {
  28960. Sytm sTm;
  28961. #ifdef __WINNT__
  28962. SYSTEMTIME sOS;
  28963. GetSystemTime(&sOS);
  28964. SYSTEMTIME_TO_SYTM(&sOS, &sTm);
  28965. #else
  28966. struct tm *pTm;
  28967. time_t t;
  28968. time(&t);
  28969. pTm = gmtime(&t);
  28970. STRUCT_TM_TO_SYTM(pTm, &sTm);
  28971. #endif
  28972. SXUNUSED(pUnused); /* cc warning */
  28973. /* Expand */
  28974. jx9_value_string_format(pVal, "%04d-%02d-%02d", sTm.tm_year, sTm.tm_mon+1, sTm.tm_mday);
  28975. }
  28976. /*
  28977. * __FILE__
  28978. * Path of the processed script.
  28979. */
  28980. static void JX9_FILE_Const(jx9_value *pVal, void *pUserData)
  28981. {
  28982. jx9_vm *pVm = (jx9_vm *)pUserData;
  28983. SyString *pFile;
  28984. /* Peek the top entry */
  28985. pFile = (SyString *)SySetPeek(&pVm->aFiles);
  28986. if( pFile == 0 ){
  28987. /* Expand the magic word: ":MEMORY:" */
  28988. jx9_value_string(pVal, ":MEMORY:", (int)sizeof(":MEMORY:")-1);
  28989. }else{
  28990. jx9_value_string(pVal, pFile->zString, pFile->nByte);
  28991. }
  28992. }
  28993. /*
  28994. * __DIR__
  28995. * Directory holding the processed script.
  28996. */
  28997. static void JX9_DIR_Const(jx9_value *pVal, void *pUserData)
  28998. {
  28999. jx9_vm *pVm = (jx9_vm *)pUserData;
  29000. SyString *pFile;
  29001. /* Peek the top entry */
  29002. pFile = (SyString *)SySetPeek(&pVm->aFiles);
  29003. if( pFile == 0 ){
  29004. /* Expand the magic word: ":MEMORY:" */
  29005. jx9_value_string(pVal, ":MEMORY:", (int)sizeof(":MEMORY:")-1);
  29006. }else{
  29007. if( pFile->nByte > 0 ){
  29008. const char *zDir;
  29009. int nLen;
  29010. zDir = jx9ExtractDirName(pFile->zString, (int)pFile->nByte, &nLen);
  29011. jx9_value_string(pVal, zDir, nLen);
  29012. }else{
  29013. /* Expand '.' as the current directory*/
  29014. jx9_value_string(pVal, ".", (int)sizeof(char));
  29015. }
  29016. }
  29017. }
  29018. /*
  29019. * E_ERROR
  29020. * Expands 1
  29021. */
  29022. static void JX9_E_ERROR_Const(jx9_value *pVal, void *pUserData)
  29023. {
  29024. jx9_value_int(pVal, 1);
  29025. SXUNUSED(pUserData);
  29026. }
  29027. /*
  29028. * E_WARNING
  29029. * Expands 2
  29030. */
  29031. static void JX9_E_WARNING_Const(jx9_value *pVal, void *pUserData)
  29032. {
  29033. jx9_value_int(pVal, 2);
  29034. SXUNUSED(pUserData);
  29035. }
  29036. /*
  29037. * E_PARSE
  29038. * Expands 4
  29039. */
  29040. static void JX9_E_PARSE_Const(jx9_value *pVal, void *pUserData)
  29041. {
  29042. jx9_value_int(pVal, 4);
  29043. SXUNUSED(pUserData);
  29044. }
  29045. /*
  29046. * E_NOTICE
  29047. * Expands 8
  29048. */
  29049. static void JX9_E_NOTICE_Const(jx9_value *pVal, void *pUserData)
  29050. {
  29051. jx9_value_int(pVal, 8);
  29052. SXUNUSED(pUserData);
  29053. }
  29054. /*
  29055. * CASE_LOWER
  29056. * Expands 0.
  29057. */
  29058. static void JX9_CASE_LOWER_Const(jx9_value *pVal, void *pUserData)
  29059. {
  29060. jx9_value_int(pVal, 0);
  29061. SXUNUSED(pUserData);
  29062. }
  29063. /*
  29064. * CASE_UPPER
  29065. * Expands 1.
  29066. */
  29067. static void JX9_CASE_UPPER_Const(jx9_value *pVal, void *pUserData)
  29068. {
  29069. jx9_value_int(pVal, 1);
  29070. SXUNUSED(pUserData);
  29071. }
  29072. /*
  29073. * STR_PAD_LEFT
  29074. * Expands 0.
  29075. */
  29076. static void JX9_STR_PAD_LEFT_Const(jx9_value *pVal, void *pUserData)
  29077. {
  29078. jx9_value_int(pVal, 0);
  29079. SXUNUSED(pUserData);
  29080. }
  29081. /*
  29082. * STR_PAD_RIGHT
  29083. * Expands 1.
  29084. */
  29085. static void JX9_STR_PAD_RIGHT_Const(jx9_value *pVal, void *pUserData)
  29086. {
  29087. jx9_value_int(pVal, 1);
  29088. SXUNUSED(pUserData);
  29089. }
  29090. /*
  29091. * STR_PAD_BOTH
  29092. * Expands 2.
  29093. */
  29094. static void JX9_STR_PAD_BOTH_Const(jx9_value *pVal, void *pUserData)
  29095. {
  29096. jx9_value_int(pVal, 2);
  29097. SXUNUSED(pUserData);
  29098. }
  29099. /*
  29100. * COUNT_NORMAL
  29101. * Expands 0
  29102. */
  29103. static void JX9_COUNT_NORMAL_Const(jx9_value *pVal, void *pUserData)
  29104. {
  29105. jx9_value_int(pVal, 0);
  29106. SXUNUSED(pUserData);
  29107. }
  29108. /*
  29109. * COUNT_RECURSIVE
  29110. * Expands 1.
  29111. */
  29112. static void JX9_COUNT_RECURSIVE_Const(jx9_value *pVal, void *pUserData)
  29113. {
  29114. jx9_value_int(pVal, 1);
  29115. SXUNUSED(pUserData);
  29116. }
  29117. /*
  29118. * SORT_ASC
  29119. * Expands 1.
  29120. */
  29121. static void JX9_SORT_ASC_Const(jx9_value *pVal, void *pUserData)
  29122. {
  29123. jx9_value_int(pVal, 1);
  29124. SXUNUSED(pUserData);
  29125. }
  29126. /*
  29127. * SORT_DESC
  29128. * Expands 2.
  29129. */
  29130. static void JX9_SORT_DESC_Const(jx9_value *pVal, void *pUserData)
  29131. {
  29132. jx9_value_int(pVal, 2);
  29133. SXUNUSED(pUserData);
  29134. }
  29135. /*
  29136. * SORT_REGULAR
  29137. * Expands 3.
  29138. */
  29139. static void JX9_SORT_REG_Const(jx9_value *pVal, void *pUserData)
  29140. {
  29141. jx9_value_int(pVal, 3);
  29142. SXUNUSED(pUserData);
  29143. }
  29144. /*
  29145. * SORT_NUMERIC
  29146. * Expands 4.
  29147. */
  29148. static void JX9_SORT_NUMERIC_Const(jx9_value *pVal, void *pUserData)
  29149. {
  29150. jx9_value_int(pVal, 4);
  29151. SXUNUSED(pUserData);
  29152. }
  29153. /*
  29154. * SORT_STRING
  29155. * Expands 5.
  29156. */
  29157. static void JX9_SORT_STRING_Const(jx9_value *pVal, void *pUserData)
  29158. {
  29159. jx9_value_int(pVal, 5);
  29160. SXUNUSED(pUserData);
  29161. }
  29162. /*
  29163. * JX9_ROUND_HALF_UP
  29164. * Expands 1.
  29165. */
  29166. static void JX9_JX9_ROUND_HALF_UP_Const(jx9_value *pVal, void *pUserData)
  29167. {
  29168. jx9_value_int(pVal, 1);
  29169. SXUNUSED(pUserData);
  29170. }
  29171. /*
  29172. * SJX9_ROUND_HALF_DOWN
  29173. * Expands 2.
  29174. */
  29175. static void JX9_JX9_ROUND_HALF_DOWN_Const(jx9_value *pVal, void *pUserData)
  29176. {
  29177. jx9_value_int(pVal, 2);
  29178. SXUNUSED(pUserData);
  29179. }
  29180. /*
  29181. * JX9_ROUND_HALF_EVEN
  29182. * Expands 3.
  29183. */
  29184. static void JX9_JX9_ROUND_HALF_EVEN_Const(jx9_value *pVal, void *pUserData)
  29185. {
  29186. jx9_value_int(pVal, 3);
  29187. SXUNUSED(pUserData);
  29188. }
  29189. /*
  29190. * JX9_ROUND_HALF_ODD
  29191. * Expands 4.
  29192. */
  29193. static void JX9_JX9_ROUND_HALF_ODD_Const(jx9_value *pVal, void *pUserData)
  29194. {
  29195. jx9_value_int(pVal, 4);
  29196. SXUNUSED(pUserData);
  29197. }
  29198. #ifdef JX9_ENABLE_MATH_FUNC
  29199. /*
  29200. * PI
  29201. * Expand the value of pi.
  29202. */
  29203. static void JX9_M_PI_Const(jx9_value *pVal, void *pUserData)
  29204. {
  29205. SXUNUSED(pUserData); /* cc warning */
  29206. jx9_value_double(pVal, JX9_PI);
  29207. }
  29208. /*
  29209. * M_E
  29210. * Expand 2.7182818284590452354
  29211. */
  29212. static void JX9_M_E_Const(jx9_value *pVal, void *pUserData)
  29213. {
  29214. SXUNUSED(pUserData); /* cc warning */
  29215. jx9_value_double(pVal, 2.7182818284590452354);
  29216. }
  29217. /*
  29218. * M_LOG2E
  29219. * Expand 2.7182818284590452354
  29220. */
  29221. static void JX9_M_LOG2E_Const(jx9_value *pVal, void *pUserData)
  29222. {
  29223. SXUNUSED(pUserData); /* cc warning */
  29224. jx9_value_double(pVal, 1.4426950408889634074);
  29225. }
  29226. /*
  29227. * M_LOG10E
  29228. * Expand 0.4342944819032518276
  29229. */
  29230. static void JX9_M_LOG10E_Const(jx9_value *pVal, void *pUserData)
  29231. {
  29232. SXUNUSED(pUserData); /* cc warning */
  29233. jx9_value_double(pVal, 0.4342944819032518276);
  29234. }
  29235. /*
  29236. * M_LN2
  29237. * Expand 0.69314718055994530942
  29238. */
  29239. static void JX9_M_LN2_Const(jx9_value *pVal, void *pUserData)
  29240. {
  29241. SXUNUSED(pUserData); /* cc warning */
  29242. jx9_value_double(pVal, 0.69314718055994530942);
  29243. }
  29244. /*
  29245. * M_LN10
  29246. * Expand 2.30258509299404568402
  29247. */
  29248. static void JX9_M_LN10_Const(jx9_value *pVal, void *pUserData)
  29249. {
  29250. SXUNUSED(pUserData); /* cc warning */
  29251. jx9_value_double(pVal, 2.30258509299404568402);
  29252. }
  29253. /*
  29254. * M_PI_2
  29255. * Expand 1.57079632679489661923
  29256. */
  29257. static void JX9_M_PI_2_Const(jx9_value *pVal, void *pUserData)
  29258. {
  29259. SXUNUSED(pUserData); /* cc warning */
  29260. jx9_value_double(pVal, 1.57079632679489661923);
  29261. }
  29262. /*
  29263. * M_PI_4
  29264. * Expand 0.78539816339744830962
  29265. */
  29266. static void JX9_M_PI_4_Const(jx9_value *pVal, void *pUserData)
  29267. {
  29268. SXUNUSED(pUserData); /* cc warning */
  29269. jx9_value_double(pVal, 0.78539816339744830962);
  29270. }
  29271. /*
  29272. * M_1_PI
  29273. * Expand 0.31830988618379067154
  29274. */
  29275. static void JX9_M_1_PI_Const(jx9_value *pVal, void *pUserData)
  29276. {
  29277. SXUNUSED(pUserData); /* cc warning */
  29278. jx9_value_double(pVal, 0.31830988618379067154);
  29279. }
  29280. /*
  29281. * M_2_PI
  29282. * Expand 0.63661977236758134308
  29283. */
  29284. static void JX9_M_2_PI_Const(jx9_value *pVal, void *pUserData)
  29285. {
  29286. SXUNUSED(pUserData); /* cc warning */
  29287. jx9_value_double(pVal, 0.63661977236758134308);
  29288. }
  29289. /*
  29290. * M_SQRTPI
  29291. * Expand 1.77245385090551602729
  29292. */
  29293. static void JX9_M_SQRTPI_Const(jx9_value *pVal, void *pUserData)
  29294. {
  29295. SXUNUSED(pUserData); /* cc warning */
  29296. jx9_value_double(pVal, 1.77245385090551602729);
  29297. }
  29298. /*
  29299. * M_2_SQRTPI
  29300. * Expand 1.12837916709551257390
  29301. */
  29302. static void JX9_M_2_SQRTPI_Const(jx9_value *pVal, void *pUserData)
  29303. {
  29304. SXUNUSED(pUserData); /* cc warning */
  29305. jx9_value_double(pVal, 1.12837916709551257390);
  29306. }
  29307. /*
  29308. * M_SQRT2
  29309. * Expand 1.41421356237309504880
  29310. */
  29311. static void JX9_M_SQRT2_Const(jx9_value *pVal, void *pUserData)
  29312. {
  29313. SXUNUSED(pUserData); /* cc warning */
  29314. jx9_value_double(pVal, 1.41421356237309504880);
  29315. }
  29316. /*
  29317. * M_SQRT3
  29318. * Expand 1.73205080756887729352
  29319. */
  29320. static void JX9_M_SQRT3_Const(jx9_value *pVal, void *pUserData)
  29321. {
  29322. SXUNUSED(pUserData); /* cc warning */
  29323. jx9_value_double(pVal, 1.73205080756887729352);
  29324. }
  29325. /*
  29326. * M_SQRT1_2
  29327. * Expand 0.70710678118654752440
  29328. */
  29329. static void JX9_M_SQRT1_2_Const(jx9_value *pVal, void *pUserData)
  29330. {
  29331. SXUNUSED(pUserData); /* cc warning */
  29332. jx9_value_double(pVal, 0.70710678118654752440);
  29333. }
  29334. /*
  29335. * M_LNPI
  29336. * Expand 1.14472988584940017414
  29337. */
  29338. static void JX9_M_LNPI_Const(jx9_value *pVal, void *pUserData)
  29339. {
  29340. SXUNUSED(pUserData); /* cc warning */
  29341. jx9_value_double(pVal, 1.14472988584940017414);
  29342. }
  29343. /*
  29344. * M_EULER
  29345. * Expand 0.57721566490153286061
  29346. */
  29347. static void JX9_M_EULER_Const(jx9_value *pVal, void *pUserData)
  29348. {
  29349. SXUNUSED(pUserData); /* cc warning */
  29350. jx9_value_double(pVal, 0.57721566490153286061);
  29351. }
  29352. #endif /* JX9_DISABLE_BUILTIN_MATH */
  29353. /*
  29354. * DATE_ATOM
  29355. * Expand Atom (example: 2005-08-15T15:52:01+00:00)
  29356. */
  29357. static void JX9_DATE_ATOM_Const(jx9_value *pVal, void *pUserData)
  29358. {
  29359. SXUNUSED(pUserData); /* cc warning */
  29360. jx9_value_string(pVal, "Y-m-d\\TH:i:sP", -1/*Compute length automatically*/);
  29361. }
  29362. /*
  29363. * DATE_COOKIE
  29364. * HTTP Cookies (example: Monday, 15-Aug-05 15:52:01 UTC)
  29365. */
  29366. static void JX9_DATE_COOKIE_Const(jx9_value *pVal, void *pUserData)
  29367. {
  29368. SXUNUSED(pUserData); /* cc warning */
  29369. jx9_value_string(pVal, "l, d-M-y H:i:s T", -1/*Compute length automatically*/);
  29370. }
  29371. /*
  29372. * DATE_ISO8601
  29373. * ISO-8601 (example: 2005-08-15T15:52:01+0000)
  29374. */
  29375. static void JX9_DATE_ISO8601_Const(jx9_value *pVal, void *pUserData)
  29376. {
  29377. SXUNUSED(pUserData); /* cc warning */
  29378. jx9_value_string(pVal, "Y-m-d\\TH:i:sO", -1/*Compute length automatically*/);
  29379. }
  29380. /*
  29381. * DATE_RFC822
  29382. * RFC 822 (example: Mon, 15 Aug 05 15:52:01 +0000)
  29383. */
  29384. static void JX9_DATE_RFC822_Const(jx9_value *pVal, void *pUserData)
  29385. {
  29386. SXUNUSED(pUserData); /* cc warning */
  29387. jx9_value_string(pVal, "D, d M y H:i:s O", -1/*Compute length automatically*/);
  29388. }
  29389. /*
  29390. * DATE_RFC850
  29391. * RFC 850 (example: Monday, 15-Aug-05 15:52:01 UTC)
  29392. */
  29393. static void JX9_DATE_RFC850_Const(jx9_value *pVal, void *pUserData)
  29394. {
  29395. SXUNUSED(pUserData); /* cc warning */
  29396. jx9_value_string(pVal, "l, d-M-y H:i:s T", -1/*Compute length automatically*/);
  29397. }
  29398. /*
  29399. * DATE_RFC1036
  29400. * RFC 1123 (example: Mon, 15 Aug 2005 15:52:01 +0000)
  29401. */
  29402. static void JX9_DATE_RFC1036_Const(jx9_value *pVal, void *pUserData)
  29403. {
  29404. SXUNUSED(pUserData); /* cc warning */
  29405. jx9_value_string(pVal, "D, d M y H:i:s O", -1/*Compute length automatically*/);
  29406. }
  29407. /*
  29408. * DATE_RFC1123
  29409. * RFC 1123 (example: Mon, 15 Aug 2005 15:52:01 +0000)
  29410. */
  29411. static void JX9_DATE_RFC1123_Const(jx9_value *pVal, void *pUserData)
  29412. {
  29413. SXUNUSED(pUserData); /* cc warning */
  29414. jx9_value_string(pVal, "D, d M Y H:i:s O", -1/*Compute length automatically*/);
  29415. }
  29416. /*
  29417. * DATE_RFC2822
  29418. * RFC 2822 (Mon, 15 Aug 2005 15:52:01 +0000)
  29419. */
  29420. static void JX9_DATE_RFC2822_Const(jx9_value *pVal, void *pUserData)
  29421. {
  29422. SXUNUSED(pUserData); /* cc warning */
  29423. jx9_value_string(pVal, "D, d M Y H:i:s O", -1/*Compute length automatically*/);
  29424. }
  29425. /*
  29426. * DATE_RSS
  29427. * RSS (Mon, 15 Aug 2005 15:52:01 +0000)
  29428. */
  29429. static void JX9_DATE_RSS_Const(jx9_value *pVal, void *pUserData)
  29430. {
  29431. SXUNUSED(pUserData); /* cc warning */
  29432. jx9_value_string(pVal, "D, d M Y H:i:s O", -1/*Compute length automatically*/);
  29433. }
  29434. /*
  29435. * DATE_W3C
  29436. * World Wide Web Consortium (example: 2005-08-15T15:52:01+00:00)
  29437. */
  29438. static void JX9_DATE_W3C_Const(jx9_value *pVal, void *pUserData)
  29439. {
  29440. SXUNUSED(pUserData); /* cc warning */
  29441. jx9_value_string(pVal, "Y-m-d\\TH:i:sP", -1/*Compute length automatically*/);
  29442. }
  29443. /*
  29444. * ENT_COMPAT
  29445. * Expand 0x01 (Must be a power of two)
  29446. */
  29447. static void JX9_ENT_COMPAT_Const(jx9_value *pVal, void *pUserData)
  29448. {
  29449. SXUNUSED(pUserData); /* cc warning */
  29450. jx9_value_int(pVal, 0x01);
  29451. }
  29452. /*
  29453. * ENT_QUOTES
  29454. * Expand 0x02 (Must be a power of two)
  29455. */
  29456. static void JX9_ENT_QUOTES_Const(jx9_value *pVal, void *pUserData)
  29457. {
  29458. SXUNUSED(pUserData); /* cc warning */
  29459. jx9_value_int(pVal, 0x02);
  29460. }
  29461. /*
  29462. * ENT_NOQUOTES
  29463. * Expand 0x04 (Must be a power of two)
  29464. */
  29465. static void JX9_ENT_NOQUOTES_Const(jx9_value *pVal, void *pUserData)
  29466. {
  29467. SXUNUSED(pUserData); /* cc warning */
  29468. jx9_value_int(pVal, 0x04);
  29469. }
  29470. /*
  29471. * ENT_IGNORE
  29472. * Expand 0x08 (Must be a power of two)
  29473. */
  29474. static void JX9_ENT_IGNORE_Const(jx9_value *pVal, void *pUserData)
  29475. {
  29476. SXUNUSED(pUserData); /* cc warning */
  29477. jx9_value_int(pVal, 0x08);
  29478. }
  29479. /*
  29480. * ENT_SUBSTITUTE
  29481. * Expand 0x10 (Must be a power of two)
  29482. */
  29483. static void JX9_ENT_SUBSTITUTE_Const(jx9_value *pVal, void *pUserData)
  29484. {
  29485. SXUNUSED(pUserData); /* cc warning */
  29486. jx9_value_int(pVal, 0x10);
  29487. }
  29488. /*
  29489. * ENT_DISALLOWED
  29490. * Expand 0x20 (Must be a power of two)
  29491. */
  29492. static void JX9_ENT_DISALLOWED_Const(jx9_value *pVal, void *pUserData)
  29493. {
  29494. SXUNUSED(pUserData); /* cc warning */
  29495. jx9_value_int(pVal, 0x20);
  29496. }
  29497. /*
  29498. * ENT_HTML401
  29499. * Expand 0x40 (Must be a power of two)
  29500. */
  29501. static void JX9_ENT_HTML401_Const(jx9_value *pVal, void *pUserData)
  29502. {
  29503. SXUNUSED(pUserData); /* cc warning */
  29504. jx9_value_int(pVal, 0x40);
  29505. }
  29506. /*
  29507. * ENT_XML1
  29508. * Expand 0x80 (Must be a power of two)
  29509. */
  29510. static void JX9_ENT_XML1_Const(jx9_value *pVal, void *pUserData)
  29511. {
  29512. SXUNUSED(pUserData); /* cc warning */
  29513. jx9_value_int(pVal, 0x80);
  29514. }
  29515. /*
  29516. * ENT_XHTML
  29517. * Expand 0x100 (Must be a power of two)
  29518. */
  29519. static void JX9_ENT_XHTML_Const(jx9_value *pVal, void *pUserData)
  29520. {
  29521. SXUNUSED(pUserData); /* cc warning */
  29522. jx9_value_int(pVal, 0x100);
  29523. }
  29524. /*
  29525. * ENT_HTML5
  29526. * Expand 0x200 (Must be a power of two)
  29527. */
  29528. static void JX9_ENT_HTML5_Const(jx9_value *pVal, void *pUserData)
  29529. {
  29530. SXUNUSED(pUserData); /* cc warning */
  29531. jx9_value_int(pVal, 0x200);
  29532. }
  29533. /*
  29534. * ISO-8859-1
  29535. * ISO_8859_1
  29536. * Expand 1
  29537. */
  29538. static void JX9_ISO88591_Const(jx9_value *pVal, void *pUserData)
  29539. {
  29540. SXUNUSED(pUserData); /* cc warning */
  29541. jx9_value_int(pVal, 1);
  29542. }
  29543. /*
  29544. * UTF-8
  29545. * UTF8
  29546. * Expand 2
  29547. */
  29548. static void JX9_UTF8_Const(jx9_value *pVal, void *pUserData)
  29549. {
  29550. SXUNUSED(pUserData); /* cc warning */
  29551. jx9_value_int(pVal, 1);
  29552. }
  29553. /*
  29554. * HTML_ENTITIES
  29555. * Expand 1
  29556. */
  29557. static void JX9_HTML_ENTITIES_Const(jx9_value *pVal, void *pUserData)
  29558. {
  29559. SXUNUSED(pUserData); /* cc warning */
  29560. jx9_value_int(pVal, 1);
  29561. }
  29562. /*
  29563. * HTML_SPECIALCHARS
  29564. * Expand 2
  29565. */
  29566. static void JX9_HTML_SPECIALCHARS_Const(jx9_value *pVal, void *pUserData)
  29567. {
  29568. SXUNUSED(pUserData); /* cc warning */
  29569. jx9_value_int(pVal, 2);
  29570. }
  29571. /*
  29572. * JX9_URL_SCHEME.
  29573. * Expand 1
  29574. */
  29575. static void JX9_JX9_URL_SCHEME_Const(jx9_value *pVal, void *pUserData)
  29576. {
  29577. SXUNUSED(pUserData); /* cc warning */
  29578. jx9_value_int(pVal, 1);
  29579. }
  29580. /*
  29581. * JX9_URL_HOST.
  29582. * Expand 2
  29583. */
  29584. static void JX9_JX9_URL_HOST_Const(jx9_value *pVal, void *pUserData)
  29585. {
  29586. SXUNUSED(pUserData); /* cc warning */
  29587. jx9_value_int(pVal, 2);
  29588. }
  29589. /*
  29590. * JX9_URL_PORT.
  29591. * Expand 3
  29592. */
  29593. static void JX9_JX9_URL_PORT_Const(jx9_value *pVal, void *pUserData)
  29594. {
  29595. SXUNUSED(pUserData); /* cc warning */
  29596. jx9_value_int(pVal, 3);
  29597. }
  29598. /*
  29599. * JX9_URL_USER.
  29600. * Expand 4
  29601. */
  29602. static void JX9_JX9_URL_USER_Const(jx9_value *pVal, void *pUserData)
  29603. {
  29604. SXUNUSED(pUserData); /* cc warning */
  29605. jx9_value_int(pVal, 4);
  29606. }
  29607. /*
  29608. * JX9_URL_PASS.
  29609. * Expand 5
  29610. */
  29611. static void JX9_JX9_URL_PASS_Const(jx9_value *pVal, void *pUserData)
  29612. {
  29613. SXUNUSED(pUserData); /* cc warning */
  29614. jx9_value_int(pVal, 5);
  29615. }
  29616. /*
  29617. * JX9_URL_PATH.
  29618. * Expand 6
  29619. */
  29620. static void JX9_JX9_URL_PATH_Const(jx9_value *pVal, void *pUserData)
  29621. {
  29622. SXUNUSED(pUserData); /* cc warning */
  29623. jx9_value_int(pVal, 6);
  29624. }
  29625. /*
  29626. * JX9_URL_QUERY.
  29627. * Expand 7
  29628. */
  29629. static void JX9_JX9_URL_QUERY_Const(jx9_value *pVal, void *pUserData)
  29630. {
  29631. SXUNUSED(pUserData); /* cc warning */
  29632. jx9_value_int(pVal, 7);
  29633. }
  29634. /*
  29635. * JX9_URL_FRAGMENT.
  29636. * Expand 8
  29637. */
  29638. static void JX9_JX9_URL_FRAGMENT_Const(jx9_value *pVal, void *pUserData)
  29639. {
  29640. SXUNUSED(pUserData); /* cc warning */
  29641. jx9_value_int(pVal, 8);
  29642. }
  29643. /*
  29644. * JX9_QUERY_RFC1738
  29645. * Expand 1
  29646. */
  29647. static void JX9_JX9_QUERY_RFC1738_Const(jx9_value *pVal, void *pUserData)
  29648. {
  29649. SXUNUSED(pUserData); /* cc warning */
  29650. jx9_value_int(pVal, 1);
  29651. }
  29652. /*
  29653. * JX9_QUERY_RFC3986
  29654. * Expand 1
  29655. */
  29656. static void JX9_JX9_QUERY_RFC3986_Const(jx9_value *pVal, void *pUserData)
  29657. {
  29658. SXUNUSED(pUserData); /* cc warning */
  29659. jx9_value_int(pVal, 2);
  29660. }
  29661. /*
  29662. * FNM_NOESCAPE
  29663. * Expand 0x01 (Must be a power of two)
  29664. */
  29665. static void JX9_FNM_NOESCAPE_Const(jx9_value *pVal, void *pUserData)
  29666. {
  29667. SXUNUSED(pUserData); /* cc warning */
  29668. jx9_value_int(pVal, 0x01);
  29669. }
  29670. /*
  29671. * FNM_PATHNAME
  29672. * Expand 0x02 (Must be a power of two)
  29673. */
  29674. static void JX9_FNM_PATHNAME_Const(jx9_value *pVal, void *pUserData)
  29675. {
  29676. SXUNUSED(pUserData); /* cc warning */
  29677. jx9_value_int(pVal, 0x02);
  29678. }
  29679. /*
  29680. * FNM_PERIOD
  29681. * Expand 0x04 (Must be a power of two)
  29682. */
  29683. static void JX9_FNM_PERIOD_Const(jx9_value *pVal, void *pUserData)
  29684. {
  29685. SXUNUSED(pUserData); /* cc warning */
  29686. jx9_value_int(pVal, 0x04);
  29687. }
  29688. /*
  29689. * FNM_CASEFOLD
  29690. * Expand 0x08 (Must be a power of two)
  29691. */
  29692. static void JX9_FNM_CASEFOLD_Const(jx9_value *pVal, void *pUserData)
  29693. {
  29694. SXUNUSED(pUserData); /* cc warning */
  29695. jx9_value_int(pVal, 0x08);
  29696. }
  29697. /*
  29698. * PATHINFO_DIRNAME
  29699. * Expand 1.
  29700. */
  29701. static void JX9_PATHINFO_DIRNAME_Const(jx9_value *pVal, void *pUserData)
  29702. {
  29703. SXUNUSED(pUserData); /* cc warning */
  29704. jx9_value_int(pVal, 1);
  29705. }
  29706. /*
  29707. * PATHINFO_BASENAME
  29708. * Expand 2.
  29709. */
  29710. static void JX9_PATHINFO_BASENAME_Const(jx9_value *pVal, void *pUserData)
  29711. {
  29712. SXUNUSED(pUserData); /* cc warning */
  29713. jx9_value_int(pVal, 2);
  29714. }
  29715. /*
  29716. * PATHINFO_EXTENSION
  29717. * Expand 3.
  29718. */
  29719. static void JX9_PATHINFO_EXTENSION_Const(jx9_value *pVal, void *pUserData)
  29720. {
  29721. SXUNUSED(pUserData); /* cc warning */
  29722. jx9_value_int(pVal, 3);
  29723. }
  29724. /*
  29725. * PATHINFO_FILENAME
  29726. * Expand 4.
  29727. */
  29728. static void JX9_PATHINFO_FILENAME_Const(jx9_value *pVal, void *pUserData)
  29729. {
  29730. SXUNUSED(pUserData); /* cc warning */
  29731. jx9_value_int(pVal, 4);
  29732. }
  29733. /*
  29734. * ASSERT_ACTIVE.
  29735. * Expand the value of JX9_ASSERT_ACTIVE defined in jx9Int.h
  29736. */
  29737. static void JX9_ASSERT_ACTIVE_Const(jx9_value *pVal, void *pUserData)
  29738. {
  29739. SXUNUSED(pUserData); /* cc warning */
  29740. jx9_value_int(pVal, JX9_ASSERT_DISABLE);
  29741. }
  29742. /*
  29743. * ASSERT_WARNING.
  29744. * Expand the value of JX9_ASSERT_WARNING defined in jx9Int.h
  29745. */
  29746. static void JX9_ASSERT_WARNING_Const(jx9_value *pVal, void *pUserData)
  29747. {
  29748. SXUNUSED(pUserData); /* cc warning */
  29749. jx9_value_int(pVal, JX9_ASSERT_WARNING);
  29750. }
  29751. /*
  29752. * ASSERT_BAIL.
  29753. * Expand the value of JX9_ASSERT_BAIL defined in jx9Int.h
  29754. */
  29755. static void JX9_ASSERT_BAIL_Const(jx9_value *pVal, void *pUserData)
  29756. {
  29757. SXUNUSED(pUserData); /* cc warning */
  29758. jx9_value_int(pVal, JX9_ASSERT_BAIL);
  29759. }
  29760. /*
  29761. * ASSERT_QUIET_EVAL.
  29762. * Expand the value of JX9_ASSERT_QUIET_EVAL defined in jx9Int.h
  29763. */
  29764. static void JX9_ASSERT_QUIET_EVAL_Const(jx9_value *pVal, void *pUserData)
  29765. {
  29766. SXUNUSED(pUserData); /* cc warning */
  29767. jx9_value_int(pVal, JX9_ASSERT_QUIET_EVAL);
  29768. }
  29769. /*
  29770. * ASSERT_CALLBACK.
  29771. * Expand the value of JX9_ASSERT_CALLBACK defined in jx9Int.h
  29772. */
  29773. static void JX9_ASSERT_CALLBACK_Const(jx9_value *pVal, void *pUserData)
  29774. {
  29775. SXUNUSED(pUserData); /* cc warning */
  29776. jx9_value_int(pVal, JX9_ASSERT_CALLBACK);
  29777. }
  29778. /*
  29779. * SEEK_SET.
  29780. * Expand 0
  29781. */
  29782. static void JX9_SEEK_SET_Const(jx9_value *pVal, void *pUserData)
  29783. {
  29784. SXUNUSED(pUserData); /* cc warning */
  29785. jx9_value_int(pVal, 0);
  29786. }
  29787. /*
  29788. * SEEK_CUR.
  29789. * Expand 1
  29790. */
  29791. static void JX9_SEEK_CUR_Const(jx9_value *pVal, void *pUserData)
  29792. {
  29793. SXUNUSED(pUserData); /* cc warning */
  29794. jx9_value_int(pVal, 1);
  29795. }
  29796. /*
  29797. * SEEK_END.
  29798. * Expand 2
  29799. */
  29800. static void JX9_SEEK_END_Const(jx9_value *pVal, void *pUserData)
  29801. {
  29802. SXUNUSED(pUserData); /* cc warning */
  29803. jx9_value_int(pVal, 2);
  29804. }
  29805. /*
  29806. * LOCK_SH.
  29807. * Expand 2
  29808. */
  29809. static void JX9_LOCK_SH_Const(jx9_value *pVal, void *pUserData)
  29810. {
  29811. SXUNUSED(pUserData); /* cc warning */
  29812. jx9_value_int(pVal, 1);
  29813. }
  29814. /*
  29815. * LOCK_NB.
  29816. * Expand 5
  29817. */
  29818. static void JX9_LOCK_NB_Const(jx9_value *pVal, void *pUserData)
  29819. {
  29820. SXUNUSED(pUserData); /* cc warning */
  29821. jx9_value_int(pVal, 5);
  29822. }
  29823. /*
  29824. * LOCK_EX.
  29825. * Expand 0x01 (MUST BE A POWER OF TWO)
  29826. */
  29827. static void JX9_LOCK_EX_Const(jx9_value *pVal, void *pUserData)
  29828. {
  29829. SXUNUSED(pUserData); /* cc warning */
  29830. jx9_value_int(pVal, 0x01);
  29831. }
  29832. /*
  29833. * LOCK_UN.
  29834. * Expand 0
  29835. */
  29836. static void JX9_LOCK_UN_Const(jx9_value *pVal, void *pUserData)
  29837. {
  29838. SXUNUSED(pUserData); /* cc warning */
  29839. jx9_value_int(pVal, 0);
  29840. }
  29841. /*
  29842. * FILE_USE_INC_PATH
  29843. * Expand 0x01 (Must be a power of two)
  29844. */
  29845. static void JX9_FILE_USE_INCLUDE_PATH_Const(jx9_value *pVal, void *pUserData)
  29846. {
  29847. SXUNUSED(pUserData); /* cc warning */
  29848. jx9_value_int(pVal, 0x1);
  29849. }
  29850. /*
  29851. * FILE_IGN_NL
  29852. * Expand 0x02 (Must be a power of two)
  29853. */
  29854. static void JX9_FILE_IGNORE_NEW_LINES_Const(jx9_value *pVal, void *pUserData)
  29855. {
  29856. SXUNUSED(pUserData); /* cc warning */
  29857. jx9_value_int(pVal, 0x2);
  29858. }
  29859. /*
  29860. * FILE_SKIP_EL
  29861. * Expand 0x04 (Must be a power of two)
  29862. */
  29863. static void JX9_FILE_SKIP_EMPTY_LINES_Const(jx9_value *pVal, void *pUserData)
  29864. {
  29865. SXUNUSED(pUserData); /* cc warning */
  29866. jx9_value_int(pVal, 0x4);
  29867. }
  29868. /*
  29869. * FILE_APPEND
  29870. * Expand 0x08 (Must be a power of two)
  29871. */
  29872. static void JX9_FILE_APPEND_Const(jx9_value *pVal, void *pUserData)
  29873. {
  29874. SXUNUSED(pUserData); /* cc warning */
  29875. jx9_value_int(pVal, 0x08);
  29876. }
  29877. /*
  29878. * SCANDIR_SORT_ASCENDING
  29879. * Expand 0
  29880. */
  29881. static void JX9_SCANDIR_SORT_ASCENDING_Const(jx9_value *pVal, void *pUserData)
  29882. {
  29883. SXUNUSED(pUserData); /* cc warning */
  29884. jx9_value_int(pVal, 0);
  29885. }
  29886. /*
  29887. * SCANDIR_SORT_DESCENDING
  29888. * Expand 1
  29889. */
  29890. static void JX9_SCANDIR_SORT_DESCENDING_Const(jx9_value *pVal, void *pUserData)
  29891. {
  29892. SXUNUSED(pUserData); /* cc warning */
  29893. jx9_value_int(pVal, 1);
  29894. }
  29895. /*
  29896. * SCANDIR_SORT_NONE
  29897. * Expand 2
  29898. */
  29899. static void JX9_SCANDIR_SORT_NONE_Const(jx9_value *pVal, void *pUserData)
  29900. {
  29901. SXUNUSED(pUserData); /* cc warning */
  29902. jx9_value_int(pVal, 2);
  29903. }
  29904. /*
  29905. * GLOB_MARK
  29906. * Expand 0x01 (must be a power of two)
  29907. */
  29908. static void JX9_GLOB_MARK_Const(jx9_value *pVal, void *pUserData)
  29909. {
  29910. SXUNUSED(pUserData); /* cc warning */
  29911. jx9_value_int(pVal, 0x01);
  29912. }
  29913. /*
  29914. * GLOB_NOSORT
  29915. * Expand 0x02 (must be a power of two)
  29916. */
  29917. static void JX9_GLOB_NOSORT_Const(jx9_value *pVal, void *pUserData)
  29918. {
  29919. SXUNUSED(pUserData); /* cc warning */
  29920. jx9_value_int(pVal, 0x02);
  29921. }
  29922. /*
  29923. * GLOB_NOCHECK
  29924. * Expand 0x04 (must be a power of two)
  29925. */
  29926. static void JX9_GLOB_NOCHECK_Const(jx9_value *pVal, void *pUserData)
  29927. {
  29928. SXUNUSED(pUserData); /* cc warning */
  29929. jx9_value_int(pVal, 0x04);
  29930. }
  29931. /*
  29932. * GLOB_NOESCAPE
  29933. * Expand 0x08 (must be a power of two)
  29934. */
  29935. static void JX9_GLOB_NOESCAPE_Const(jx9_value *pVal, void *pUserData)
  29936. {
  29937. SXUNUSED(pUserData); /* cc warning */
  29938. jx9_value_int(pVal, 0x08);
  29939. }
  29940. /*
  29941. * GLOB_BRACE
  29942. * Expand 0x10 (must be a power of two)
  29943. */
  29944. static void JX9_GLOB_BRACE_Const(jx9_value *pVal, void *pUserData)
  29945. {
  29946. SXUNUSED(pUserData); /* cc warning */
  29947. jx9_value_int(pVal, 0x10);
  29948. }
  29949. /*
  29950. * GLOB_ONLYDIR
  29951. * Expand 0x20 (must be a power of two)
  29952. */
  29953. static void JX9_GLOB_ONLYDIR_Const(jx9_value *pVal, void *pUserData)
  29954. {
  29955. SXUNUSED(pUserData); /* cc warning */
  29956. jx9_value_int(pVal, 0x20);
  29957. }
  29958. /*
  29959. * GLOB_ERR
  29960. * Expand 0x40 (must be a power of two)
  29961. */
  29962. static void JX9_GLOB_ERR_Const(jx9_value *pVal, void *pUserData)
  29963. {
  29964. SXUNUSED(pUserData); /* cc warning */
  29965. jx9_value_int(pVal, 0x40);
  29966. }
  29967. /*
  29968. * STDIN
  29969. * Expand the STDIN handle as a resource.
  29970. */
  29971. static void JX9_STDIN_Const(jx9_value *pVal, void *pUserData)
  29972. {
  29973. jx9_vm *pVm = (jx9_vm *)pUserData;
  29974. void *pResource;
  29975. pResource = jx9ExportStdin(pVm);
  29976. jx9_value_resource(pVal, pResource);
  29977. }
  29978. /*
  29979. * STDOUT
  29980. * Expand the STDOUT handle as a resource.
  29981. */
  29982. static void JX9_STDOUT_Const(jx9_value *pVal, void *pUserData)
  29983. {
  29984. jx9_vm *pVm = (jx9_vm *)pUserData;
  29985. void *pResource;
  29986. pResource = jx9ExportStdout(pVm);
  29987. jx9_value_resource(pVal, pResource);
  29988. }
  29989. /*
  29990. * STDERR
  29991. * Expand the STDERR handle as a resource.
  29992. */
  29993. static void JX9_STDERR_Const(jx9_value *pVal, void *pUserData)
  29994. {
  29995. jx9_vm *pVm = (jx9_vm *)pUserData;
  29996. void *pResource;
  29997. pResource = jx9ExportStderr(pVm);
  29998. jx9_value_resource(pVal, pResource);
  29999. }
  30000. /*
  30001. * INI_SCANNER_NORMAL
  30002. * Expand 1
  30003. */
  30004. static void JX9_INI_SCANNER_NORMAL_Const(jx9_value *pVal, void *pUserData)
  30005. {
  30006. SXUNUSED(pUserData); /* cc warning */
  30007. jx9_value_int(pVal, 1);
  30008. }
  30009. /*
  30010. * INI_SCANNER_RAW
  30011. * Expand 2
  30012. */
  30013. static void JX9_INI_SCANNER_RAW_Const(jx9_value *pVal, void *pUserData)
  30014. {
  30015. SXUNUSED(pUserData); /* cc warning */
  30016. jx9_value_int(pVal, 2);
  30017. }
  30018. /*
  30019. * EXTR_OVERWRITE
  30020. * Expand 0x01 (Must be a power of two)
  30021. */
  30022. static void JX9_EXTR_OVERWRITE_Const(jx9_value *pVal, void *pUserData)
  30023. {
  30024. SXUNUSED(pUserData); /* cc warning */
  30025. jx9_value_int(pVal, 0x1);
  30026. }
  30027. /*
  30028. * EXTR_SKIP
  30029. * Expand 0x02 (Must be a power of two)
  30030. */
  30031. static void JX9_EXTR_SKIP_Const(jx9_value *pVal, void *pUserData)
  30032. {
  30033. SXUNUSED(pUserData); /* cc warning */
  30034. jx9_value_int(pVal, 0x2);
  30035. }
  30036. /*
  30037. * EXTR_PREFIX_SAME
  30038. * Expand 0x04 (Must be a power of two)
  30039. */
  30040. static void JX9_EXTR_PREFIX_SAME_Const(jx9_value *pVal, void *pUserData)
  30041. {
  30042. SXUNUSED(pUserData); /* cc warning */
  30043. jx9_value_int(pVal, 0x4);
  30044. }
  30045. /*
  30046. * EXTR_PREFIX_ALL
  30047. * Expand 0x08 (Must be a power of two)
  30048. */
  30049. static void JX9_EXTR_PREFIX_ALL_Const(jx9_value *pVal, void *pUserData)
  30050. {
  30051. SXUNUSED(pUserData); /* cc warning */
  30052. jx9_value_int(pVal, 0x8);
  30053. }
  30054. /*
  30055. * EXTR_PREFIX_INVALID
  30056. * Expand 0x10 (Must be a power of two)
  30057. */
  30058. static void JX9_EXTR_PREFIX_INVALID_Const(jx9_value *pVal, void *pUserData)
  30059. {
  30060. SXUNUSED(pUserData); /* cc warning */
  30061. jx9_value_int(pVal, 0x10);
  30062. }
  30063. /*
  30064. * EXTR_IF_EXISTS
  30065. * Expand 0x20 (Must be a power of two)
  30066. */
  30067. static void JX9_EXTR_IF_EXISTS_Const(jx9_value *pVal, void *pUserData)
  30068. {
  30069. SXUNUSED(pUserData); /* cc warning */
  30070. jx9_value_int(pVal, 0x20);
  30071. }
  30072. /*
  30073. * EXTR_PREFIX_IF_EXISTS
  30074. * Expand 0x40 (Must be a power of two)
  30075. */
  30076. static void JX9_EXTR_PREFIX_IF_EXISTS_Const(jx9_value *pVal, void *pUserData)
  30077. {
  30078. SXUNUSED(pUserData); /* cc warning */
  30079. jx9_value_int(pVal, 0x40);
  30080. }
  30081. /*
  30082. * Table of built-in constants.
  30083. */
  30084. static const jx9_builtin_constant aBuiltIn[] = {
  30085. {"JX9_VERSION", JX9_VER_Const },
  30086. {"JX9_ENGINE", JX9_VER_Const },
  30087. {"__JX9__", JX9_VER_Const },
  30088. {"JX9_OS", JX9_OS_Const },
  30089. {"__OS__", JX9_OS_Const },
  30090. {"JX9_EOL", JX9_EOL_Const },
  30091. {"JX9_INT_MAX", JX9_INTMAX_Const },
  30092. {"MAXINT", JX9_INTMAX_Const },
  30093. {"JX9_INT_SIZE", JX9_INTSIZE_Const },
  30094. {"PATH_SEPARATOR", JX9_PATHSEP_Const },
  30095. {"DIRECTORY_SEPARATOR", JX9_DIRSEP_Const },
  30096. {"DIR_SEP", JX9_DIRSEP_Const },
  30097. {"__TIME__", JX9_TIME_Const },
  30098. {"__DATE__", JX9_DATE_Const },
  30099. {"__FILE__", JX9_FILE_Const },
  30100. {"__DIR__", JX9_DIR_Const },
  30101. {"E_ERROR", JX9_E_ERROR_Const },
  30102. {"E_WARNING", JX9_E_WARNING_Const},
  30103. {"E_PARSE", JX9_E_PARSE_Const },
  30104. {"E_NOTICE", JX9_E_NOTICE_Const },
  30105. {"CASE_LOWER", JX9_CASE_LOWER_Const },
  30106. {"CASE_UPPER", JX9_CASE_UPPER_Const },
  30107. {"STR_PAD_LEFT", JX9_STR_PAD_LEFT_Const },
  30108. {"STR_PAD_RIGHT", JX9_STR_PAD_RIGHT_Const},
  30109. {"STR_PAD_BOTH", JX9_STR_PAD_BOTH_Const },
  30110. {"COUNT_NORMAL", JX9_COUNT_NORMAL_Const },
  30111. {"COUNT_RECURSIVE", JX9_COUNT_RECURSIVE_Const },
  30112. {"SORT_ASC", JX9_SORT_ASC_Const },
  30113. {"SORT_DESC", JX9_SORT_DESC_Const },
  30114. {"SORT_REGULAR", JX9_SORT_REG_Const },
  30115. {"SORT_NUMERIC", JX9_SORT_NUMERIC_Const },
  30116. {"SORT_STRING", JX9_SORT_STRING_Const },
  30117. {"JX9_ROUND_HALF_DOWN", JX9_JX9_ROUND_HALF_DOWN_Const },
  30118. {"JX9_ROUND_HALF_EVEN", JX9_JX9_ROUND_HALF_EVEN_Const },
  30119. {"JX9_ROUND_HALF_UP", JX9_JX9_ROUND_HALF_UP_Const },
  30120. {"JX9_ROUND_HALF_ODD", JX9_JX9_ROUND_HALF_ODD_Const },
  30121. #ifdef JX9_ENABLE_MATH_FUNC
  30122. {"PI", JX9_M_PI_Const },
  30123. {"M_E", JX9_M_E_Const },
  30124. {"M_LOG2E", JX9_M_LOG2E_Const },
  30125. {"M_LOG10E", JX9_M_LOG10E_Const },
  30126. {"M_LN2", JX9_M_LN2_Const },
  30127. {"M_LN10", JX9_M_LN10_Const },
  30128. {"M_PI_2", JX9_M_PI_2_Const },
  30129. {"M_PI_4", JX9_M_PI_4_Const },
  30130. {"M_1_PI", JX9_M_1_PI_Const },
  30131. {"M_2_PI", JX9_M_2_PI_Const },
  30132. {"M_SQRTPI", JX9_M_SQRTPI_Const },
  30133. {"M_2_SQRTPI", JX9_M_2_SQRTPI_Const },
  30134. {"M_SQRT2", JX9_M_SQRT2_Const },
  30135. {"M_SQRT3", JX9_M_SQRT3_Const },
  30136. {"M_SQRT1_2", JX9_M_SQRT1_2_Const },
  30137. {"M_LNPI", JX9_M_LNPI_Const },
  30138. {"M_EULER", JX9_M_EULER_Const },
  30139. #endif /* JX9_ENABLE_MATH_FUNC */
  30140. {"DATE_ATOM", JX9_DATE_ATOM_Const },
  30141. {"DATE_COOKIE", JX9_DATE_COOKIE_Const },
  30142. {"DATE_ISO8601", JX9_DATE_ISO8601_Const },
  30143. {"DATE_RFC822", JX9_DATE_RFC822_Const },
  30144. {"DATE_RFC850", JX9_DATE_RFC850_Const },
  30145. {"DATE_RFC1036", JX9_DATE_RFC1036_Const },
  30146. {"DATE_RFC1123", JX9_DATE_RFC1123_Const },
  30147. {"DATE_RFC2822", JX9_DATE_RFC2822_Const },
  30148. {"DATE_RFC3339", JX9_DATE_ATOM_Const },
  30149. {"DATE_RSS", JX9_DATE_RSS_Const },
  30150. {"DATE_W3C", JX9_DATE_W3C_Const },
  30151. {"ENT_COMPAT", JX9_ENT_COMPAT_Const },
  30152. {"ENT_QUOTES", JX9_ENT_QUOTES_Const },
  30153. {"ENT_NOQUOTES", JX9_ENT_NOQUOTES_Const },
  30154. {"ENT_IGNORE", JX9_ENT_IGNORE_Const },
  30155. {"ENT_SUBSTITUTE", JX9_ENT_SUBSTITUTE_Const},
  30156. {"ENT_DISALLOWED", JX9_ENT_DISALLOWED_Const},
  30157. {"ENT_HTML401", JX9_ENT_HTML401_Const },
  30158. {"ENT_XML1", JX9_ENT_XML1_Const },
  30159. {"ENT_XHTML", JX9_ENT_XHTML_Const },
  30160. {"ENT_HTML5", JX9_ENT_HTML5_Const },
  30161. {"ISO-8859-1", JX9_ISO88591_Const },
  30162. {"ISO_8859_1", JX9_ISO88591_Const },
  30163. {"UTF-8", JX9_UTF8_Const },
  30164. {"UTF8", JX9_UTF8_Const },
  30165. {"HTML_ENTITIES", JX9_HTML_ENTITIES_Const},
  30166. {"HTML_SPECIALCHARS", JX9_HTML_SPECIALCHARS_Const },
  30167. {"JX9_URL_SCHEME", JX9_JX9_URL_SCHEME_Const},
  30168. {"JX9_URL_HOST", JX9_JX9_URL_HOST_Const},
  30169. {"JX9_URL_PORT", JX9_JX9_URL_PORT_Const},
  30170. {"JX9_URL_USER", JX9_JX9_URL_USER_Const},
  30171. {"JX9_URL_PASS", JX9_JX9_URL_PASS_Const},
  30172. {"JX9_URL_PATH", JX9_JX9_URL_PATH_Const},
  30173. {"JX9_URL_QUERY", JX9_JX9_URL_QUERY_Const},
  30174. {"JX9_URL_FRAGMENT", JX9_JX9_URL_FRAGMENT_Const},
  30175. {"JX9_QUERY_RFC1738", JX9_JX9_QUERY_RFC1738_Const},
  30176. {"JX9_QUERY_RFC3986", JX9_JX9_QUERY_RFC3986_Const},
  30177. {"FNM_NOESCAPE", JX9_FNM_NOESCAPE_Const },
  30178. {"FNM_PATHNAME", JX9_FNM_PATHNAME_Const },
  30179. {"FNM_PERIOD", JX9_FNM_PERIOD_Const },
  30180. {"FNM_CASEFOLD", JX9_FNM_CASEFOLD_Const },
  30181. {"PATHINFO_DIRNAME", JX9_PATHINFO_DIRNAME_Const },
  30182. {"PATHINFO_BASENAME", JX9_PATHINFO_BASENAME_Const },
  30183. {"PATHINFO_EXTENSION", JX9_PATHINFO_EXTENSION_Const},
  30184. {"PATHINFO_FILENAME", JX9_PATHINFO_FILENAME_Const },
  30185. {"ASSERT_ACTIVE", JX9_ASSERT_ACTIVE_Const },
  30186. {"ASSERT_WARNING", JX9_ASSERT_WARNING_Const },
  30187. {"ASSERT_BAIL", JX9_ASSERT_BAIL_Const },
  30188. {"ASSERT_QUIET_EVAL", JX9_ASSERT_QUIET_EVAL_Const },
  30189. {"ASSERT_CALLBACK", JX9_ASSERT_CALLBACK_Const },
  30190. {"SEEK_SET", JX9_SEEK_SET_Const },
  30191. {"SEEK_CUR", JX9_SEEK_CUR_Const },
  30192. {"SEEK_END", JX9_SEEK_END_Const },
  30193. {"LOCK_EX", JX9_LOCK_EX_Const },
  30194. {"LOCK_SH", JX9_LOCK_SH_Const },
  30195. {"LOCK_NB", JX9_LOCK_NB_Const },
  30196. {"LOCK_UN", JX9_LOCK_UN_Const },
  30197. {"FILE_USE_INC_PATH", JX9_FILE_USE_INCLUDE_PATH_Const},
  30198. {"FILE_IGN_NL", JX9_FILE_IGNORE_NEW_LINES_Const},
  30199. {"FILE_SKIP_EL", JX9_FILE_SKIP_EMPTY_LINES_Const},
  30200. {"FILE_APPEND", JX9_FILE_APPEND_Const },
  30201. {"SCANDIR_SORT_ASC", JX9_SCANDIR_SORT_ASCENDING_Const },
  30202. {"SCANDIR_SORT_DESC", JX9_SCANDIR_SORT_DESCENDING_Const },
  30203. {"SCANDIR_SORT_NONE", JX9_SCANDIR_SORT_NONE_Const },
  30204. {"GLOB_MARK", JX9_GLOB_MARK_Const },
  30205. {"GLOB_NOSORT", JX9_GLOB_NOSORT_Const },
  30206. {"GLOB_NOCHECK", JX9_GLOB_NOCHECK_Const },
  30207. {"GLOB_NOESCAPE", JX9_GLOB_NOESCAPE_Const},
  30208. {"GLOB_BRACE", JX9_GLOB_BRACE_Const },
  30209. {"GLOB_ONLYDIR", JX9_GLOB_ONLYDIR_Const },
  30210. {"GLOB_ERR", JX9_GLOB_ERR_Const },
  30211. {"STDIN", JX9_STDIN_Const },
  30212. {"stdin", JX9_STDIN_Const },
  30213. {"STDOUT", JX9_STDOUT_Const },
  30214. {"stdout", JX9_STDOUT_Const },
  30215. {"STDERR", JX9_STDERR_Const },
  30216. {"stderr", JX9_STDERR_Const },
  30217. {"INI_SCANNER_NORMAL", JX9_INI_SCANNER_NORMAL_Const },
  30218. {"INI_SCANNER_RAW", JX9_INI_SCANNER_RAW_Const },
  30219. {"EXTR_OVERWRITE", JX9_EXTR_OVERWRITE_Const },
  30220. {"EXTR_SKIP", JX9_EXTR_SKIP_Const },
  30221. {"EXTR_PREFIX_SAME", JX9_EXTR_PREFIX_SAME_Const },
  30222. {"EXTR_PREFIX_ALL", JX9_EXTR_PREFIX_ALL_Const },
  30223. {"EXTR_PREFIX_INVALID", JX9_EXTR_PREFIX_INVALID_Const },
  30224. {"EXTR_IF_EXISTS", JX9_EXTR_IF_EXISTS_Const },
  30225. {"EXTR_PREFIX_IF_EXISTS", JX9_EXTR_PREFIX_IF_EXISTS_Const}
  30226. };
  30227. /*
  30228. * Register the built-in constants defined above.
  30229. */
  30230. JX9_PRIVATE void jx9RegisterBuiltInConstant(jx9_vm *pVm)
  30231. {
  30232. sxu32 n;
  30233. /*
  30234. * Note that all built-in constants have access to the jx9 virtual machine
  30235. * that trigger the constant invocation as their private data.
  30236. */
  30237. for( n = 0 ; n < SX_ARRAYSIZE(aBuiltIn) ; ++n ){
  30238. jx9_create_constant(&(*pVm), aBuiltIn[n].zName, aBuiltIn[n].xExpand, &(*pVm));
  30239. }
  30240. }
  30241. /*
  30242. * ----------------------------------------------------------
  30243. * File: compile.c
  30244. * MD5: 77e4fa80ff2a3599fe588f7f3bb2eb1b
  30245. * ----------------------------------------------------------
  30246. */
  30247. /*
  30248. * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
  30249. * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
  30250. * Version 1.7.2
  30251. * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  30252. * please contact Symisc Systems via:
  30253. * legal@symisc.net
  30254. * licensing@symisc.net
  30255. * contact@symisc.net
  30256. * or visit:
  30257. * http://jx9.symisc.net/
  30258. */
  30259. /* $SymiscID: compile.c v1.7 FreeBSD 2012-12-11 21:46 stable <chm@symisc.net> $ */
  30260. #ifndef JX9_AMALGAMATION
  30261. #include "jx9Int.h"
  30262. #endif
  30263. /*
  30264. * This file implement a thread-safe and full-reentrant compiler for the JX9 engine.
  30265. * That is, routines defined in this file takes a stream of tokens and output
  30266. * JX9 bytecode instructions.
  30267. */
  30268. /* Forward declaration */
  30269. typedef struct LangConstruct LangConstruct;
  30270. typedef struct JumpFixup JumpFixup;
  30271. /* Block [i.e: set of statements] control flags */
  30272. #define GEN_BLOCK_LOOP 0x001 /* Loop block [i.e: for, while, ...] */
  30273. #define GEN_BLOCK_PROTECTED 0x002 /* Protected block */
  30274. #define GEN_BLOCK_COND 0x004 /* Conditional block [i.e: if(condition){} ]*/
  30275. #define GEN_BLOCK_FUNC 0x008 /* Function body */
  30276. #define GEN_BLOCK_GLOBAL 0x010 /* Global block (always set)*/
  30277. #define GEN_BLOC_NESTED_FUNC 0x020 /* Nested function body */
  30278. #define GEN_BLOCK_EXPR 0x040 /* Expression */
  30279. #define GEN_BLOCK_STD 0x080 /* Standard block */
  30280. #define GEN_BLOCK_SWITCH 0x100 /* Switch statement */
  30281. /*
  30282. * Compilation of some JX9 constructs such as if, for, while, the logical or
  30283. * (||) and logical and (&&) operators in expressions requires the
  30284. * generation of forward jumps.
  30285. * Since the destination PC target of these jumps isn't known when the jumps
  30286. * are emitted, we record each forward jump in an instance of the following
  30287. * structure. Those jumps are fixed later when the jump destination is resolved.
  30288. */
  30289. struct JumpFixup
  30290. {
  30291. sxi32 nJumpType; /* Jump type. Either TRUE jump, FALSE jump or Unconditional jump */
  30292. sxu32 nInstrIdx; /* Instruction index to fix later when the jump destination is resolved. */
  30293. };
  30294. /*
  30295. * Each language construct is represented by an instance
  30296. * of the following structure.
  30297. */
  30298. struct LangConstruct
  30299. {
  30300. sxu32 nID; /* Language construct ID [i.e: JX9_TKWRD_WHILE, JX9_TKWRD_FOR, JX9_TKWRD_IF...] */
  30301. ProcLangConstruct xConstruct; /* C function implementing the language construct */
  30302. };
  30303. /* Compilation flags */
  30304. #define JX9_COMPILE_SINGLE_STMT 0x001 /* Compile a single statement */
  30305. /* Token stream synchronization macros */
  30306. #define SWAP_TOKEN_STREAM(GEN, START, END)\
  30307. pTmp = GEN->pEnd;\
  30308. pGen->pIn = START;\
  30309. pGen->pEnd = END
  30310. #define UPDATE_TOKEN_STREAM(GEN)\
  30311. if( GEN->pIn < pTmp ){\
  30312. GEN->pIn++;\
  30313. }\
  30314. GEN->pEnd = pTmp
  30315. #define SWAP_DELIMITER(GEN, START, END)\
  30316. pTmpIn = GEN->pIn;\
  30317. pTmpEnd = GEN->pEnd;\
  30318. GEN->pIn = START;\
  30319. GEN->pEnd = END
  30320. #define RE_SWAP_DELIMITER(GEN)\
  30321. GEN->pIn = pTmpIn;\
  30322. GEN->pEnd = pTmpEnd
  30323. /* Flags related to expression compilation */
  30324. #define EXPR_FLAG_LOAD_IDX_STORE 0x001 /* Set the iP2 flag when dealing with the LOAD_IDX instruction */
  30325. #define EXPR_FLAG_RDONLY_LOAD 0x002 /* Read-only load, refer to the 'JX9_OP_LOAD' VM instruction for more information */
  30326. #define EXPR_FLAG_COMMA_STATEMENT 0x004 /* Treat comma expression as a single statement (used by object attributes) */
  30327. /* Forward declaration */
  30328. static sxi32 jx9CompileExpr(
  30329. jx9_gen_state *pGen, /* Code generator state */
  30330. sxi32 iFlags, /* Control flags */
  30331. sxi32 (*xTreeValidator)(jx9_gen_state *, jx9_expr_node *) /* Node validator callback.NULL otherwise */
  30332. );
  30333. /*
  30334. * Recover from a compile-time error. In other words synchronize
  30335. * the token stream cursor with the first semi-colon seen.
  30336. */
  30337. static sxi32 jx9ErrorRecover(jx9_gen_state *pGen)
  30338. {
  30339. /* Synchronize with the next-semi-colon and avoid compiling this erroneous statement */
  30340. while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI /*';'*/) == 0){
  30341. pGen->pIn++;
  30342. }
  30343. return SXRET_OK;
  30344. }
  30345. /*
  30346. * Check if the given identifier name is reserved or not.
  30347. * Return TRUE if reserved.FALSE otherwise.
  30348. */
  30349. static int GenStateIsReservedID(SyString *pName)
  30350. {
  30351. if( pName->nByte == sizeof("null") - 1 ){
  30352. if( SyStrnicmp(pName->zString, "null", sizeof("null")-1) == 0 ){
  30353. return TRUE;
  30354. }else if( SyStrnicmp(pName->zString, "true", sizeof("true")-1) == 0 ){
  30355. return TRUE;
  30356. }
  30357. }else if( pName->nByte == sizeof("false") - 1 ){
  30358. if( SyStrnicmp(pName->zString, "false", sizeof("false")-1) == 0 ){
  30359. return TRUE;
  30360. }
  30361. }
  30362. /* Not a reserved constant */
  30363. return FALSE;
  30364. }
  30365. /*
  30366. * Check if a given token value is installed in the literal table.
  30367. */
  30368. static sxi32 GenStateFindLiteral(jx9_gen_state *pGen, const SyString *pValue, sxu32 *pIdx)
  30369. {
  30370. SyHashEntry *pEntry;
  30371. pEntry = SyHashGet(&pGen->hLiteral, (const void *)pValue->zString, pValue->nByte);
  30372. if( pEntry == 0 ){
  30373. return SXERR_NOTFOUND;
  30374. }
  30375. *pIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData);
  30376. return SXRET_OK;
  30377. }
  30378. /*
  30379. * Install a given constant index in the literal table.
  30380. * In order to be installed, the jx9_value must be of type string.
  30381. */
  30382. static sxi32 GenStateInstallLiteral(jx9_gen_state *pGen,jx9_value *pObj, sxu32 nIdx)
  30383. {
  30384. if( SyBlobLength(&pObj->sBlob) > 0 ){
  30385. SyHashInsert(&pGen->hLiteral, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), SX_INT_TO_PTR(nIdx));
  30386. }
  30387. return SXRET_OK;
  30388. }
  30389. /*
  30390. * Generate a fatal error.
  30391. */
  30392. static sxi32 GenStateOutOfMem(jx9_gen_state *pGen)
  30393. {
  30394. jx9GenCompileError(pGen,E_ERROR,1,"Fatal, Jx9 compiler is running out of memory");
  30395. /* Abort compilation immediately */
  30396. return SXERR_ABORT;
  30397. }
  30398. /*
  30399. * Fetch a block that correspond to the given criteria from the stack of
  30400. * compiled blocks.
  30401. * Return a pointer to that block on success. NULL otherwise.
  30402. */
  30403. static GenBlock * GenStateFetchBlock(GenBlock *pCurrent, sxi32 iBlockType, sxi32 iCount)
  30404. {
  30405. GenBlock *pBlock = pCurrent;
  30406. for(;;){
  30407. if( pBlock->iFlags & iBlockType ){
  30408. iCount--; /* Decrement nesting level */
  30409. if( iCount < 1 ){
  30410. /* Block meet with the desired criteria */
  30411. return pBlock;
  30412. }
  30413. }
  30414. /* Point to the upper block */
  30415. pBlock = pBlock->pParent;
  30416. if( pBlock == 0 || (pBlock->iFlags & (GEN_BLOCK_PROTECTED|GEN_BLOCK_FUNC)) ){
  30417. /* Forbidden */
  30418. break;
  30419. }
  30420. }
  30421. /* No such block */
  30422. return 0;
  30423. }
  30424. /*
  30425. * Initialize a freshly allocated block instance.
  30426. */
  30427. static void GenStateInitBlock(
  30428. jx9_gen_state *pGen, /* Code generator state */
  30429. GenBlock *pBlock, /* Target block */
  30430. sxi32 iType, /* Block type [i.e: loop, conditional, function body, etc.]*/
  30431. sxu32 nFirstInstr, /* First instruction to compile */
  30432. void *pUserData /* Upper layer private data */
  30433. )
  30434. {
  30435. /* Initialize block fields */
  30436. pBlock->nFirstInstr = nFirstInstr;
  30437. pBlock->pUserData = pUserData;
  30438. pBlock->pGen = pGen;
  30439. pBlock->iFlags = iType;
  30440. pBlock->pParent = 0;
  30441. pBlock->bPostContinue = 0;
  30442. SySetInit(&pBlock->aJumpFix, &pGen->pVm->sAllocator, sizeof(JumpFixup));
  30443. SySetInit(&pBlock->aPostContFix, &pGen->pVm->sAllocator, sizeof(JumpFixup));
  30444. }
  30445. /*
  30446. * Allocate a new block instance.
  30447. * Return SXRET_OK and write a pointer to the new instantiated block
  30448. * on success.Otherwise generate a compile-time error and abort
  30449. * processing on failure.
  30450. */
  30451. static sxi32 GenStateEnterBlock(
  30452. jx9_gen_state *pGen, /* Code generator state */
  30453. sxi32 iType, /* Block type [i.e: loop, conditional, function body, etc.]*/
  30454. sxu32 nFirstInstr, /* First instruction to compile */
  30455. void *pUserData, /* Upper layer private data */
  30456. GenBlock **ppBlock /* OUT: instantiated block */
  30457. )
  30458. {
  30459. GenBlock *pBlock;
  30460. /* Allocate a new block instance */
  30461. pBlock = (GenBlock *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(GenBlock));
  30462. if( pBlock == 0 ){
  30463. /* If the supplied memory subsystem is so sick that we are unable to allocate
  30464. * a tiny chunk of memory, there is no much we can do here.
  30465. */
  30466. return GenStateOutOfMem(pGen);
  30467. }
  30468. /* Zero the structure */
  30469. SyZero(pBlock, sizeof(GenBlock));
  30470. GenStateInitBlock(&(*pGen), pBlock, iType, nFirstInstr, pUserData);
  30471. /* Link to the parent block */
  30472. pBlock->pParent = pGen->pCurrent;
  30473. /* Mark as the current block */
  30474. pGen->pCurrent = pBlock;
  30475. if( ppBlock ){
  30476. /* Write a pointer to the new instance */
  30477. *ppBlock = pBlock;
  30478. }
  30479. return SXRET_OK;
  30480. }
  30481. /*
  30482. * Release block fields without freeing the whole instance.
  30483. */
  30484. static void GenStateReleaseBlock(GenBlock *pBlock)
  30485. {
  30486. SySetRelease(&pBlock->aPostContFix);
  30487. SySetRelease(&pBlock->aJumpFix);
  30488. }
  30489. /*
  30490. * Release a block.
  30491. */
  30492. static void GenStateFreeBlock(GenBlock *pBlock)
  30493. {
  30494. jx9_gen_state *pGen = pBlock->pGen;
  30495. GenStateReleaseBlock(&(*pBlock));
  30496. /* Free the instance */
  30497. SyMemBackendPoolFree(&pGen->pVm->sAllocator, pBlock);
  30498. }
  30499. /*
  30500. * POP and release a block from the stack of compiled blocks.
  30501. */
  30502. static sxi32 GenStateLeaveBlock(jx9_gen_state *pGen, GenBlock **ppBlock)
  30503. {
  30504. GenBlock *pBlock = pGen->pCurrent;
  30505. if( pBlock == 0 ){
  30506. /* No more block to pop */
  30507. return SXERR_EMPTY;
  30508. }
  30509. /* Point to the upper block */
  30510. pGen->pCurrent = pBlock->pParent;
  30511. if( ppBlock ){
  30512. /* Write a pointer to the popped block */
  30513. *ppBlock = pBlock;
  30514. }else{
  30515. /* Safely release the block */
  30516. GenStateFreeBlock(&(*pBlock));
  30517. }
  30518. return SXRET_OK;
  30519. }
  30520. /*
  30521. * Emit a forward jump.
  30522. * Notes on forward jumps
  30523. * Compilation of some JX9 constructs such as if, for, while and the logical or
  30524. * (||) and logical and (&&) operators in expressions requires the
  30525. * generation of forward jumps.
  30526. * Since the destination PC target of these jumps isn't known when the jumps
  30527. * are emitted, we record each forward jump in an instance of the following
  30528. * structure. Those jumps are fixed later when the jump destination is resolved.
  30529. */
  30530. static sxi32 GenStateNewJumpFixup(GenBlock *pBlock, sxi32 nJumpType, sxu32 nInstrIdx)
  30531. {
  30532. JumpFixup sJumpFix;
  30533. sxi32 rc;
  30534. /* Init the JumpFixup structure */
  30535. sJumpFix.nJumpType = nJumpType;
  30536. sJumpFix.nInstrIdx = nInstrIdx;
  30537. /* Insert in the jump fixup table */
  30538. rc = SySetPut(&pBlock->aJumpFix,(const void *)&sJumpFix);
  30539. return rc;
  30540. }
  30541. /*
  30542. * Fix a forward jump now the jump destination is resolved.
  30543. * Return the total number of fixed jumps.
  30544. * Notes on forward jumps:
  30545. * Compilation of some JX9 constructs such as if, for, while and the logical or
  30546. * (||) and logical and (&&) operators in expressions requires the
  30547. * generation of forward jumps.
  30548. * Since the destination PC target of these jumps isn't known when the jumps
  30549. * are emitted, we record each forward jump in an instance of the following
  30550. * structure.Those jumps are fixed later when the jump destination is resolved.
  30551. */
  30552. static sxu32 GenStateFixJumps(GenBlock *pBlock, sxi32 nJumpType, sxu32 nJumpDest)
  30553. {
  30554. JumpFixup *aFix;
  30555. VmInstr *pInstr;
  30556. sxu32 nFixed;
  30557. sxu32 n;
  30558. /* Point to the jump fixup table */
  30559. aFix = (JumpFixup *)SySetBasePtr(&pBlock->aJumpFix);
  30560. /* Fix the desired jumps */
  30561. for( nFixed = n = 0 ; n < SySetUsed(&pBlock->aJumpFix) ; ++n ){
  30562. if( aFix[n].nJumpType < 0 ){
  30563. /* Already fixed */
  30564. continue;
  30565. }
  30566. if( nJumpType > 0 && aFix[n].nJumpType != nJumpType ){
  30567. /* Not of our interest */
  30568. continue;
  30569. }
  30570. /* Point to the instruction to fix */
  30571. pInstr = jx9VmGetInstr(pBlock->pGen->pVm, aFix[n].nInstrIdx);
  30572. if( pInstr ){
  30573. pInstr->iP2 = nJumpDest;
  30574. nFixed++;
  30575. /* Mark as fixed */
  30576. aFix[n].nJumpType = -1;
  30577. }
  30578. }
  30579. /* Total number of fixed jumps */
  30580. return nFixed;
  30581. }
  30582. /*
  30583. * Reserve a room for a numeric constant [i.e: 64-bit integer or real number]
  30584. * in the constant table.
  30585. */
  30586. static jx9_value * GenStateInstallNumLiteral(jx9_gen_state *pGen, sxu32 *pIdx)
  30587. {
  30588. jx9_value *pObj;
  30589. sxu32 nIdx = 0; /* cc warning */
  30590. /* Reserve a new constant */
  30591. pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
  30592. if( pObj == 0 ){
  30593. GenStateOutOfMem(pGen);
  30594. return 0;
  30595. }
  30596. *pIdx = nIdx;
  30597. /* TODO(chems): Create a numeric table (64bit int keys) same as
  30598. * the constant string iterals table [optimization purposes].
  30599. */
  30600. return pObj;
  30601. }
  30602. /*
  30603. * Compile a numeric [i.e: integer or real] literal.
  30604. * Notes on the integer type.
  30605. * According to the JX9 language reference manual
  30606. * Integers can be specified in decimal (base 10), hexadecimal (base 16), octal (base 8)
  30607. * or binary (base 2) notation, optionally preceded by a sign (- or +).
  30608. * To use octal notation, precede the number with a 0 (zero). To use hexadecimal
  30609. * notation precede the number with 0x. To use binary notation precede the number with 0b.
  30610. */
  30611. static sxi32 jx9CompileNumLiteral(jx9_gen_state *pGen,sxi32 iCompileFlag)
  30612. {
  30613. SyToken *pToken = pGen->pIn; /* Raw token */
  30614. sxu32 nIdx = 0;
  30615. if( pToken->nType & JX9_TK_INTEGER ){
  30616. jx9_value *pObj;
  30617. sxi64 iValue;
  30618. iValue = jx9TokenValueToInt64(&pToken->sData);
  30619. pObj = GenStateInstallNumLiteral(&(*pGen), &nIdx);
  30620. if( pObj == 0 ){
  30621. SXUNUSED(iCompileFlag); /* cc warning */
  30622. return SXERR_ABORT;
  30623. }
  30624. jx9MemObjInitFromInt(pGen->pVm, pObj, iValue);
  30625. }else{
  30626. /* Real number */
  30627. jx9_value *pObj;
  30628. /* Reserve a new constant */
  30629. pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
  30630. if( pObj == 0 ){
  30631. return GenStateOutOfMem(pGen);
  30632. }
  30633. jx9MemObjInitFromString(pGen->pVm, pObj, &pToken->sData);
  30634. jx9MemObjToReal(pObj);
  30635. }
  30636. /* Emit the load constant instruction */
  30637. jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
  30638. /* Node successfully compiled */
  30639. return SXRET_OK;
  30640. }
  30641. /*
  30642. * Compile a nowdoc string.
  30643. * According to the JX9 language reference manual:
  30644. *
  30645. * Nowdocs are to single-quoted strings what heredocs are to double-quoted strings.
  30646. * A nowdoc is specified similarly to a heredoc, but no parsing is done inside a nowdoc.
  30647. * The construct is ideal for embedding JX9 code or other large blocks of text without the
  30648. * need for escaping. It shares some features in common with the SGML <![CDATA[ ]]>
  30649. * construct, in that it declares a block of text which is not for parsing.
  30650. * A nowdoc is identified with the same <<< sequence used for heredocs, but the identifier
  30651. * which follows is enclosed in single quotes, e.g. <<<'EOT'. All the rules for heredoc
  30652. * identifiers also apply to nowdoc identifiers, especially those regarding the appearance
  30653. * of the closing identifier.
  30654. */
  30655. static sxi32 jx9CompileNowdoc(jx9_gen_state *pGen,sxi32 iCompileFlag)
  30656. {
  30657. SyString *pStr = &pGen->pIn->sData; /* Constant string literal */
  30658. jx9_value *pObj;
  30659. sxu32 nIdx;
  30660. nIdx = 0; /* Prevent compiler warning */
  30661. if( pStr->nByte <= 0 ){
  30662. /* Empty string, load NULL */
  30663. jx9VmEmitInstr(pGen->pVm,JX9_OP_LOADC, 0, 0, 0, 0);
  30664. return SXRET_OK;
  30665. }
  30666. /* Reserve a new constant */
  30667. pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
  30668. if( pObj == 0 ){
  30669. jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "JX9 engine is running out of memory");
  30670. SXUNUSED(iCompileFlag); /* cc warning */
  30671. return SXERR_ABORT;
  30672. }
  30673. /* No processing is done here, simply a memcpy() operation */
  30674. jx9MemObjInitFromString(pGen->pVm, pObj, pStr);
  30675. /* Emit the load constant instruction */
  30676. jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
  30677. /* Node successfully compiled */
  30678. return SXRET_OK;
  30679. }
  30680. /*
  30681. * Compile a single quoted string.
  30682. * According to the JX9 language reference manual:
  30683. *
  30684. * The simplest way to specify a string is to enclose it in single quotes (the character ' ).
  30685. * To specify a literal single quote, escape it with a backslash (\). To specify a literal
  30686. * backslash, double it (\\). All other instances of backslash will be treated as a literal
  30687. * backslash: this means that the other escape sequences you might be used to, such as \r
  30688. * or \n, will be output literally as specified rather than having any special meaning.
  30689. *
  30690. */
  30691. JX9_PRIVATE sxi32 jx9CompileSimpleString(jx9_gen_state *pGen, sxi32 iCompileFlag)
  30692. {
  30693. SyString *pStr = &pGen->pIn->sData; /* Constant string literal */
  30694. const char *zIn, *zCur, *zEnd;
  30695. jx9_value *pObj;
  30696. sxu32 nIdx;
  30697. nIdx = 0; /* Prevent compiler warning */
  30698. /* Delimit the string */
  30699. zIn = pStr->zString;
  30700. zEnd = &zIn[pStr->nByte];
  30701. if( zIn >= zEnd ){
  30702. /* Empty string, load NULL */
  30703. jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
  30704. return SXRET_OK;
  30705. }
  30706. if( SXRET_OK == GenStateFindLiteral(&(*pGen), pStr, &nIdx) ){
  30707. /* Already processed, emit the load constant instruction
  30708. * and return.
  30709. */
  30710. jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
  30711. return SXRET_OK;
  30712. }
  30713. /* Reserve a new constant */
  30714. pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
  30715. if( pObj == 0 ){
  30716. jx9GenCompileError(&(*pGen), E_ERROR, 1, "JX9 engine is running out of memory");
  30717. SXUNUSED(iCompileFlag); /* cc warning */
  30718. return SXERR_ABORT;
  30719. }
  30720. jx9MemObjInitFromString(pGen->pVm, pObj, 0);
  30721. /* Compile the node */
  30722. for(;;){
  30723. if( zIn >= zEnd ){
  30724. /* End of input */
  30725. break;
  30726. }
  30727. zCur = zIn;
  30728. while( zIn < zEnd && zIn[0] != '\\' ){
  30729. zIn++;
  30730. }
  30731. if( zIn > zCur ){
  30732. /* Append raw contents*/
  30733. jx9MemObjStringAppend(pObj, zCur, (sxu32)(zIn-zCur));
  30734. }
  30735. zIn++;
  30736. if( zIn < zEnd ){
  30737. if( zIn[0] == '\\' ){
  30738. /* A literal backslash */
  30739. jx9MemObjStringAppend(pObj, "\\", sizeof(char));
  30740. }else if( zIn[0] == '\'' ){
  30741. /* A single quote */
  30742. jx9MemObjStringAppend(pObj, "'", sizeof(char));
  30743. }else{
  30744. /* verbatim copy */
  30745. zIn--;
  30746. jx9MemObjStringAppend(pObj, zIn, sizeof(char)*2);
  30747. zIn++;
  30748. }
  30749. }
  30750. /* Advance the stream cursor */
  30751. zIn++;
  30752. }
  30753. /* Emit the load constant instruction */
  30754. jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
  30755. if( pStr->nByte < 1024 ){
  30756. /* Install in the literal table */
  30757. GenStateInstallLiteral(pGen, pObj, nIdx);
  30758. }
  30759. /* Node successfully compiled */
  30760. return SXRET_OK;
  30761. }
  30762. /*
  30763. * Process variable expression [i.e: "$var", "${var}"] embedded in a double quoted/heredoc string.
  30764. * According to the JX9 language reference manual
  30765. * When a string is specified in double quotes or with heredoc, variables are parsed within it.
  30766. * There are two types of syntax: a simple one and a complex one. The simple syntax is the most
  30767. * common and convenient. It provides a way to embed a variable, an array value, or an object
  30768. * property in a string with a minimum of effort.
  30769. * Simple syntax
  30770. * If a dollar sign ($) is encountered, the parser will greedily take as many tokens as possible
  30771. * to form a valid variable name. Enclose the variable name in curly braces to explicitly specify
  30772. * the end of the name.
  30773. * Similarly, an array index or an object property can be parsed. With array indices, the closing
  30774. * square bracket (]) marks the end of the index. The same rules apply to object properties
  30775. * as to simple variables.
  30776. * Complex (curly) syntax
  30777. * This isn't called complex because the syntax is complex, but because it allows for the use
  30778. * of complex expressions.
  30779. * Any scalar variable, array element or object property with a string representation can be
  30780. * included via this syntax. Simply write the expression the same way as it would appear outside
  30781. * the string, and then wrap it in { and }. Since { can not be escaped, this syntax will only
  30782. * be recognised when the $ immediately follows the {. Use {\$ to get a literal {$
  30783. */
  30784. static sxi32 GenStateProcessStringExpression(
  30785. jx9_gen_state *pGen, /* Code generator state */
  30786. const char *zIn, /* Raw expression */
  30787. const char *zEnd /* End of the expression */
  30788. )
  30789. {
  30790. SyToken *pTmpIn, *pTmpEnd;
  30791. SySet sToken;
  30792. sxi32 rc;
  30793. /* Initialize the token set */
  30794. SySetInit(&sToken, &pGen->pVm->sAllocator, sizeof(SyToken));
  30795. /* Preallocate some slots */
  30796. SySetAlloc(&sToken, 0x08);
  30797. /* Tokenize the text */
  30798. jx9Tokenize(zIn,(sxu32)(zEnd-zIn),&sToken);
  30799. /* Swap delimiter */
  30800. pTmpIn = pGen->pIn;
  30801. pTmpEnd = pGen->pEnd;
  30802. pGen->pIn = (SyToken *)SySetBasePtr(&sToken);
  30803. pGen->pEnd = &pGen->pIn[SySetUsed(&sToken)];
  30804. /* Compile the expression */
  30805. rc = jx9CompileExpr(&(*pGen), 0, 0);
  30806. /* Restore token stream */
  30807. pGen->pIn = pTmpIn;
  30808. pGen->pEnd = pTmpEnd;
  30809. /* Release the token set */
  30810. SySetRelease(&sToken);
  30811. /* Compilation result */
  30812. return rc;
  30813. }
  30814. /*
  30815. * Reserve a new constant for a double quoted/heredoc string.
  30816. */
  30817. static jx9_value * GenStateNewStrObj(jx9_gen_state *pGen,sxi32 *pCount)
  30818. {
  30819. jx9_value *pConstObj;
  30820. sxu32 nIdx = 0;
  30821. /* Reserve a new constant */
  30822. pConstObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
  30823. if( pConstObj == 0 ){
  30824. GenStateOutOfMem(&(*pGen));
  30825. return 0;
  30826. }
  30827. (*pCount)++;
  30828. jx9MemObjInitFromString(pGen->pVm, pConstObj, 0);
  30829. /* Emit the load constant instruction */
  30830. jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
  30831. return pConstObj;
  30832. }
  30833. /*
  30834. * Compile a double quoted/heredoc string.
  30835. * According to the JX9 language reference manual
  30836. * Heredoc
  30837. * A third way to delimit strings is the heredoc syntax: <<<. After this operator, an identifier
  30838. * is provided, then a newline. The string itself follows, and then the same identifier again
  30839. * to close the quotation.
  30840. * The closing identifier must begin in the first column of the line. Also, the identifier must
  30841. * follow the same naming rules as any other label in JX9: it must contain only alphanumeric
  30842. * characters and underscores, and must start with a non-digit character or underscore.
  30843. * Warning
  30844. * It is very important to note that the line with the closing identifier must contain
  30845. * no other characters, except possibly a semicolon (;). That means especially that the identifier
  30846. * may not be indented, and there may not be any spaces or tabs before or after the semicolon.
  30847. * It's also important to realize that the first character before the closing identifier must
  30848. * be a newline as defined by the local operating system. This is \n on UNIX systems, including Mac OS X.
  30849. * The closing delimiter (possibly followed by a semicolon) must also be followed by a newline.
  30850. * If this rule is broken and the closing identifier is not "clean", it will not be considered a closing
  30851. * identifier, and JX9 will continue looking for one. If a proper closing identifier is not found before
  30852. * the end of the current file, a parse error will result at the last line.
  30853. * Heredocs can not be used for initializing object properties.
  30854. * Double quoted
  30855. * If the string is enclosed in double-quotes ("), JX9 will interpret more escape sequences for special characters:
  30856. * Escaped characters Sequence Meaning
  30857. * \n linefeed (LF or 0x0A (10) in ASCII)
  30858. * \r carriage return (CR or 0x0D (13) in ASCII)
  30859. * \t horizontal tab (HT or 0x09 (9) in ASCII)
  30860. * \v vertical tab (VT or 0x0B (11) in ASCII)
  30861. * \f form feed (FF or 0x0C (12) in ASCII)
  30862. * \\ backslash
  30863. * \$ dollar sign
  30864. * \" double-quote
  30865. * \[0-7]{1, 3} the sequence of characters matching the regular expression is a character in octal notation
  30866. * \x[0-9A-Fa-f]{1, 2} the sequence of characters matching the regular expression is a character in hexadecimal notation
  30867. * As in single quoted strings, escaping any other character will result in the backslash being printed too.
  30868. * The most important feature of double-quoted strings is the fact that variable names will be expanded.
  30869. * See string parsing for details.
  30870. */
  30871. static sxi32 GenStateCompileString(jx9_gen_state *pGen)
  30872. {
  30873. SyString *pStr = &pGen->pIn->sData; /* Raw token value */
  30874. const char *zIn, *zCur, *zEnd;
  30875. jx9_value *pObj = 0;
  30876. sxi32 iCons;
  30877. sxi32 rc;
  30878. /* Delimit the string */
  30879. zIn = pStr->zString;
  30880. zEnd = &zIn[pStr->nByte];
  30881. if( zIn >= zEnd ){
  30882. /* Empty string, load NULL */
  30883. jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
  30884. return SXRET_OK;
  30885. }
  30886. zCur = 0;
  30887. /* Compile the node */
  30888. iCons = 0;
  30889. for(;;){
  30890. zCur = zIn;
  30891. while( zIn < zEnd && zIn[0] != '\\' ){
  30892. if(zIn[0] == '$' && &zIn[1] < zEnd &&
  30893. (((unsigned char)zIn[1] >= 0xc0 || SyisAlpha(zIn[1]) || zIn[1] == '_')) ){
  30894. break;
  30895. }
  30896. zIn++;
  30897. }
  30898. if( zIn > zCur ){
  30899. if( pObj == 0 ){
  30900. pObj = GenStateNewStrObj(&(*pGen), &iCons);
  30901. if( pObj == 0 ){
  30902. return SXERR_ABORT;
  30903. }
  30904. }
  30905. jx9MemObjStringAppend(pObj, zCur, (sxu32)(zIn-zCur));
  30906. }
  30907. if( zIn >= zEnd ){
  30908. break;
  30909. }
  30910. if( zIn[0] == '\\' ){
  30911. const char *zPtr = 0;
  30912. sxu32 n;
  30913. zIn++;
  30914. if( zIn >= zEnd ){
  30915. break;
  30916. }
  30917. if( pObj == 0 ){
  30918. pObj = GenStateNewStrObj(&(*pGen), &iCons);
  30919. if( pObj == 0 ){
  30920. return SXERR_ABORT;
  30921. }
  30922. }
  30923. n = sizeof(char); /* size of conversion */
  30924. switch( zIn[0] ){
  30925. case '$':
  30926. /* Dollar sign */
  30927. jx9MemObjStringAppend(pObj, "$", sizeof(char));
  30928. break;
  30929. case '\\':
  30930. /* A literal backslash */
  30931. jx9MemObjStringAppend(pObj, "\\", sizeof(char));
  30932. break;
  30933. case 'a':
  30934. /* The "alert" character (BEL)[ctrl+g] ASCII code 7 */
  30935. jx9MemObjStringAppend(pObj, "\a", sizeof(char));
  30936. break;
  30937. case 'b':
  30938. /* Backspace (BS)[ctrl+h] ASCII code 8 */
  30939. jx9MemObjStringAppend(pObj, "\b", sizeof(char));
  30940. break;
  30941. case 'f':
  30942. /* Form-feed (FF)[ctrl+l] ASCII code 12 */
  30943. jx9MemObjStringAppend(pObj, "\f", sizeof(char));
  30944. break;
  30945. case 'n':
  30946. /* Line feed(new line) (LF)[ctrl+j] ASCII code 10 */
  30947. jx9MemObjStringAppend(pObj, "\n", sizeof(char));
  30948. break;
  30949. case 'r':
  30950. /* Carriage return (CR)[ctrl+m] ASCII code 13 */
  30951. jx9MemObjStringAppend(pObj, "\r", sizeof(char));
  30952. break;
  30953. case 't':
  30954. /* Horizontal tab (HT)[ctrl+i] ASCII code 9 */
  30955. jx9MemObjStringAppend(pObj, "\t", sizeof(char));
  30956. break;
  30957. case 'v':
  30958. /* Vertical tab(VT)[ctrl+k] ASCII code 11 */
  30959. jx9MemObjStringAppend(pObj, "\v", sizeof(char));
  30960. break;
  30961. case '\'':
  30962. /* Single quote */
  30963. jx9MemObjStringAppend(pObj, "'", sizeof(char));
  30964. break;
  30965. case '"':
  30966. /* Double quote */
  30967. jx9MemObjStringAppend(pObj, "\"", sizeof(char));
  30968. break;
  30969. case '0':
  30970. /* NUL byte */
  30971. jx9MemObjStringAppend(pObj, "\0", sizeof(char));
  30972. break;
  30973. case 'x':
  30974. if((unsigned char)zIn[1] < 0xc0 && SyisHex(zIn[1]) ){
  30975. int c;
  30976. /* Hex digit */
  30977. c = SyHexToint(zIn[1]) << 4;
  30978. if( &zIn[2] < zEnd ){
  30979. c += SyHexToint(zIn[2]);
  30980. }
  30981. /* Output char */
  30982. jx9MemObjStringAppend(pObj, (const char *)&c, sizeof(char));
  30983. n += sizeof(char) * 2;
  30984. }else{
  30985. /* Output literal character */
  30986. jx9MemObjStringAppend(pObj, "x", sizeof(char));
  30987. }
  30988. break;
  30989. case 'o':
  30990. if( &zIn[1] < zEnd && (unsigned char)zIn[1] < 0xc0 && SyisDigit(zIn[1]) && (zIn[1] - '0') < 8 ){
  30991. /* Octal digit stream */
  30992. int c;
  30993. c = 0;
  30994. zIn++;
  30995. for( zPtr = zIn ; zPtr < &zIn[3*sizeof(char)] ; zPtr++ ){
  30996. if( zPtr >= zEnd || (unsigned char)zPtr[0] >= 0xc0 || !SyisDigit(zPtr[0]) || (zPtr[0] - '0') > 7 ){
  30997. break;
  30998. }
  30999. c = c * 8 + (zPtr[0] - '0');
  31000. }
  31001. if ( c > 0 ){
  31002. jx9MemObjStringAppend(pObj, (const char *)&c, sizeof(char));
  31003. }
  31004. n = (sxu32)(zPtr-zIn);
  31005. }else{
  31006. /* Output literal character */
  31007. jx9MemObjStringAppend(pObj, "o", sizeof(char));
  31008. }
  31009. break;
  31010. default:
  31011. /* Output without a slash */
  31012. jx9MemObjStringAppend(pObj, zIn, sizeof(char));
  31013. break;
  31014. }
  31015. /* Advance the stream cursor */
  31016. zIn += n;
  31017. continue;
  31018. }
  31019. if( zIn[0] == '{' ){
  31020. /* Curly syntax */
  31021. const char *zExpr;
  31022. sxi32 iNest = 1;
  31023. zIn++;
  31024. zExpr = zIn;
  31025. /* Synchronize with the next closing curly braces */
  31026. while( zIn < zEnd ){
  31027. if( zIn[0] == '{' ){
  31028. /* Increment nesting level */
  31029. iNest++;
  31030. }else if(zIn[0] == '}' ){
  31031. /* Decrement nesting level */
  31032. iNest--;
  31033. if( iNest <= 0 ){
  31034. break;
  31035. }
  31036. }
  31037. zIn++;
  31038. }
  31039. /* Process the expression */
  31040. rc = GenStateProcessStringExpression(&(*pGen),zExpr,zIn);
  31041. if( rc == SXERR_ABORT ){
  31042. return SXERR_ABORT;
  31043. }
  31044. if( rc != SXERR_EMPTY ){
  31045. ++iCons;
  31046. }
  31047. if( zIn < zEnd ){
  31048. /* Jump the trailing curly */
  31049. zIn++;
  31050. }
  31051. }else{
  31052. /* Simple syntax */
  31053. const char *zExpr = zIn;
  31054. /* Assemble variable name */
  31055. for(;;){
  31056. /* Jump leading dollars */
  31057. while( zIn < zEnd && zIn[0] == '$' ){
  31058. zIn++;
  31059. }
  31060. for(;;){
  31061. while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_' ) ){
  31062. zIn++;
  31063. }
  31064. if((unsigned char)zIn[0] >= 0xc0 ){
  31065. /* UTF-8 stream */
  31066. zIn++;
  31067. while( zIn < zEnd && (((unsigned char)zIn[0] & 0xc0) == 0x80) ){
  31068. zIn++;
  31069. }
  31070. continue;
  31071. }
  31072. break;
  31073. }
  31074. if( zIn >= zEnd ){
  31075. break;
  31076. }
  31077. if( zIn[0] == '[' ){
  31078. sxi32 iSquare = 1;
  31079. zIn++;
  31080. while( zIn < zEnd ){
  31081. if( zIn[0] == '[' ){
  31082. iSquare++;
  31083. }else if (zIn[0] == ']' ){
  31084. iSquare--;
  31085. if( iSquare <= 0 ){
  31086. break;
  31087. }
  31088. }
  31089. zIn++;
  31090. }
  31091. if( zIn < zEnd ){
  31092. zIn++;
  31093. }
  31094. break;
  31095. }else if( zIn[0] == '.' ){
  31096. /* Member access operator '.' */
  31097. zIn++;
  31098. }else{
  31099. break;
  31100. }
  31101. }
  31102. /* Process the expression */
  31103. rc = GenStateProcessStringExpression(&(*pGen),zExpr, zIn);
  31104. if( rc == SXERR_ABORT ){
  31105. return SXERR_ABORT;
  31106. }
  31107. if( rc != SXERR_EMPTY ){
  31108. ++iCons;
  31109. }
  31110. }
  31111. /* Invalidate the previously used constant */
  31112. pObj = 0;
  31113. }/*for(;;)*/
  31114. if( iCons > 1 ){
  31115. /* Concatenate all compiled constants */
  31116. jx9VmEmitInstr(pGen->pVm, JX9_OP_CAT, iCons, 0, 0, 0);
  31117. }
  31118. /* Node successfully compiled */
  31119. return SXRET_OK;
  31120. }
  31121. /*
  31122. * Compile a double quoted string.
  31123. * See the block-comment above for more information.
  31124. */
  31125. JX9_PRIVATE sxi32 jx9CompileString(jx9_gen_state *pGen, sxi32 iCompileFlag)
  31126. {
  31127. sxi32 rc;
  31128. rc = GenStateCompileString(&(*pGen));
  31129. SXUNUSED(iCompileFlag); /* cc warning */
  31130. /* Compilation result */
  31131. return rc;
  31132. }
  31133. /*
  31134. * Compile a literal which is an identifier(name) for simple values.
  31135. */
  31136. JX9_PRIVATE sxi32 jx9CompileLiteral(jx9_gen_state *pGen,sxi32 iCompileFlag)
  31137. {
  31138. SyToken *pToken = pGen->pIn;
  31139. jx9_value *pObj;
  31140. SyString *pStr;
  31141. sxu32 nIdx;
  31142. /* Extract token value */
  31143. pStr = &pToken->sData;
  31144. /* Deal with the reserved literals [i.e: null, false, true, ...] first */
  31145. if( pStr->nByte == sizeof("NULL") - 1 ){
  31146. if( SyStrnicmp(pStr->zString, "null", sizeof("NULL")-1) == 0 ){
  31147. /* NULL constant are always indexed at 0 */
  31148. jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
  31149. return SXRET_OK;
  31150. }else if( SyStrnicmp(pStr->zString, "true", sizeof("TRUE")-1) == 0 ){
  31151. /* TRUE constant are always indexed at 1 */
  31152. jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 1, 0, 0);
  31153. return SXRET_OK;
  31154. }
  31155. }else if (pStr->nByte == sizeof("FALSE") - 1 &&
  31156. SyStrnicmp(pStr->zString, "false", sizeof("FALSE")-1) == 0 ){
  31157. /* FALSE constant are always indexed at 2 */
  31158. jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 2, 0, 0);
  31159. return SXRET_OK;
  31160. }else if(pStr->nByte == sizeof("__LINE__") - 1 &&
  31161. SyMemcmp(pStr->zString, "__LINE__", sizeof("__LINE__")-1) == 0 ){
  31162. /* TICKET 1433-004: __LINE__ constant must be resolved at compile time, not run time */
  31163. pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
  31164. if( pObj == 0 ){
  31165. SXUNUSED(iCompileFlag); /* cc warning */
  31166. return GenStateOutOfMem(pGen);
  31167. }
  31168. jx9MemObjInitFromInt(pGen->pVm, pObj, pToken->nLine);
  31169. /* Emit the load constant instruction */
  31170. jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
  31171. return SXRET_OK;
  31172. }else if( pStr->nByte == sizeof("__FUNCTION__") - 1 &&
  31173. SyMemcmp(pStr->zString, "__FUNCTION__", sizeof("__FUNCTION__")-1) == 0 ){
  31174. GenBlock *pBlock = pGen->pCurrent;
  31175. /* TICKET 1433-004: __FUNCTION__/__METHOD__ constants must be resolved at compile time, not run time */
  31176. while( pBlock && (pBlock->iFlags & GEN_BLOCK_FUNC) == 0 ){
  31177. /* Point to the upper block */
  31178. pBlock = pBlock->pParent;
  31179. }
  31180. if( pBlock == 0 ){
  31181. /* Called in the global scope, load NULL */
  31182. jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
  31183. }else{
  31184. /* Extract the target function/method */
  31185. jx9_vm_func *pFunc = (jx9_vm_func *)pBlock->pUserData;
  31186. pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
  31187. if( pObj == 0 ){
  31188. return GenStateOutOfMem(pGen);
  31189. }
  31190. jx9MemObjInitFromString(pGen->pVm, pObj, &pFunc->sName);
  31191. /* Emit the load constant instruction */
  31192. jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
  31193. }
  31194. return SXRET_OK;
  31195. }
  31196. /* Query literal table */
  31197. if( SXRET_OK != GenStateFindLiteral(&(*pGen), &pToken->sData, &nIdx) ){
  31198. jx9_value *pObj;
  31199. /* Unknown literal, install it in the literal table */
  31200. pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
  31201. if( pObj == 0 ){
  31202. return GenStateOutOfMem(pGen);
  31203. }
  31204. jx9MemObjInitFromString(pGen->pVm, pObj, &pToken->sData);
  31205. GenStateInstallLiteral(&(*pGen), pObj, nIdx);
  31206. }
  31207. /* Emit the load constant instruction */
  31208. jx9VmEmitInstr(pGen->pVm,JX9_OP_LOADC,1,nIdx, 0, 0);
  31209. /* Node successfully compiled */
  31210. return SXRET_OK;
  31211. }
  31212. /*
  31213. * Compile an array entry whether it is a key or a value.
  31214. */
  31215. static sxi32 GenStateCompileJSONEntry(
  31216. jx9_gen_state *pGen, /* Code generator state */
  31217. SyToken *pIn, /* Token stream */
  31218. SyToken *pEnd, /* End of the token stream */
  31219. sxi32 iFlags, /* Compilation flags */
  31220. sxi32 (*xValidator)(jx9_gen_state *,jx9_expr_node *) /* Expression tree validator callback */
  31221. )
  31222. {
  31223. SyToken *pTmpIn, *pTmpEnd;
  31224. sxi32 rc;
  31225. /* Swap token stream */
  31226. SWAP_DELIMITER(pGen, pIn, pEnd);
  31227. /* Compile the expression*/
  31228. rc = jx9CompileExpr(&(*pGen), iFlags, xValidator);
  31229. /* Restore token stream */
  31230. RE_SWAP_DELIMITER(pGen);
  31231. return rc;
  31232. }
  31233. /*
  31234. * Compile a Jx9 JSON Array.
  31235. */
  31236. JX9_PRIVATE sxi32 jx9CompileJsonArray(jx9_gen_state *pGen, sxi32 iCompileFlag)
  31237. {
  31238. sxi32 nPair = 0;
  31239. SyToken *pCur;
  31240. sxi32 rc;
  31241. pGen->pIn++; /* Jump the open square bracket '['*/
  31242. pGen->pEnd--;
  31243. SXUNUSED(iCompileFlag); /* cc warning */
  31244. for(;;){
  31245. /* Jump leading commas */
  31246. while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_COMMA) ){
  31247. pGen->pIn++;
  31248. }
  31249. pCur = pGen->pIn;
  31250. if( SXRET_OK != jx9GetNextExpr(pGen->pIn, pGen->pEnd, &pGen->pIn) ){
  31251. /* No more entry to process */
  31252. break;
  31253. }
  31254. /* Compile entry */
  31255. rc = GenStateCompileJSONEntry(&(*pGen),pCur,pGen->pIn,EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if inexistant*/,0);
  31256. if( rc == SXERR_ABORT ){
  31257. return SXERR_ABORT;
  31258. }
  31259. nPair++;
  31260. }
  31261. /* Emit the load map instruction */
  31262. jx9VmEmitInstr(pGen->pVm, JX9_OP_LOAD_MAP,nPair,0,0,0);
  31263. /* Node successfully compiled */
  31264. return SXRET_OK;
  31265. }
  31266. /*
  31267. * Node validator for a given JSON key.
  31268. */
  31269. static sxi32 GenStateJSONObjectKeyNodeValidator(jx9_gen_state *pGen,jx9_expr_node *pRoot)
  31270. {
  31271. sxi32 rc = SXRET_OK;
  31272. if( pRoot->xCode != jx9CompileVariable && pRoot->xCode != jx9CompileString
  31273. && pRoot->xCode != jx9CompileSimpleString && pRoot->xCode != jx9CompileLiteral ){
  31274. /* Unexpected expression */
  31275. rc = jx9GenCompileError(&(*pGen), E_ERROR, pRoot->pStart? pRoot->pStart->nLine : 0,
  31276. "JSON Object: Unexpected expression, key must be of type string, literal or simple variable");
  31277. if( rc != SXERR_ABORT ){
  31278. rc = SXERR_INVALID;
  31279. }
  31280. }
  31281. return rc;
  31282. }
  31283. /*
  31284. * Compile a Jx9 JSON Object
  31285. */
  31286. JX9_PRIVATE sxi32 jx9CompileJsonObject(jx9_gen_state *pGen, sxi32 iCompileFlag)
  31287. {
  31288. SyToken *pKey, *pCur;
  31289. sxi32 nPair = 0;
  31290. sxi32 rc;
  31291. pGen->pIn++; /* Jump the open querly braces '{'*/
  31292. pGen->pEnd--;
  31293. SXUNUSED(iCompileFlag); /* cc warning */
  31294. for(;;){
  31295. /* Jump leading commas */
  31296. while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_COMMA) ){
  31297. pGen->pIn++;
  31298. }
  31299. pCur = pGen->pIn;
  31300. if( SXRET_OK != jx9GetNextExpr(pGen->pIn, pGen->pEnd, &pGen->pIn) ){
  31301. /* No more entry to process */
  31302. break;
  31303. }
  31304. /* Compile the key */
  31305. pKey = pCur;
  31306. while( pCur < pGen->pIn ){
  31307. if( pCur->nType & JX9_TK_COLON /*':'*/ ){
  31308. break;
  31309. }
  31310. pCur++;
  31311. }
  31312. rc = SXERR_EMPTY;
  31313. if( pCur < pGen->pIn ){
  31314. if( &pCur[1] >= pGen->pIn ){
  31315. /* Missing value */
  31316. rc = jx9GenCompileError(&(*pGen), E_ERROR, pCur->nLine, "JSON Object: Missing entry value");
  31317. if( rc == SXERR_ABORT ){
  31318. return SXERR_ABORT;
  31319. }
  31320. return SXRET_OK;
  31321. }
  31322. /* Compile the expression holding the key */
  31323. rc = GenStateCompileJSONEntry(&(*pGen), pKey, pCur,
  31324. EXPR_FLAG_RDONLY_LOAD /* Do not create the variable if inexistant */,
  31325. GenStateJSONObjectKeyNodeValidator /* Node validator callback */
  31326. );
  31327. if( rc == SXERR_ABORT ){
  31328. return SXERR_ABORT;
  31329. }
  31330. pCur++; /* Jump the double colon ':' */
  31331. }else if( pKey == pCur ){
  31332. /* Key is omitted, emit an error */
  31333. jx9GenCompileError(&(*pGen),E_ERROR, pCur->nLine, "JSON Object: Missing entry key");
  31334. pCur++; /* Jump the double colon ':' */
  31335. }else{
  31336. /* Reset back the cursor and point to the entry value */
  31337. pCur = pKey;
  31338. }
  31339. /* Compile indice value */
  31340. rc = GenStateCompileJSONEntry(&(*pGen), pCur, pGen->pIn, EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if inexistant*/,0);
  31341. if( rc == SXERR_ABORT ){
  31342. return SXERR_ABORT;
  31343. }
  31344. nPair++;
  31345. }
  31346. /* Emit the load map instruction */
  31347. jx9VmEmitInstr(pGen->pVm, JX9_OP_LOAD_MAP, nPair * 2, 1, 0, 0);
  31348. /* Node successfully compiled */
  31349. return SXRET_OK;
  31350. }
  31351. /*
  31352. * Compile a function [i.e: print, exit(), include(), ...] which is a langauge
  31353. * construct.
  31354. */
  31355. JX9_PRIVATE sxi32 jx9CompileLangConstruct(jx9_gen_state *pGen,sxi32 iCompileFlag)
  31356. {
  31357. SyString *pName;
  31358. sxu32 nKeyID;
  31359. sxi32 rc;
  31360. /* Name of the language construct [i.e: print, die...]*/
  31361. pName = &pGen->pIn->sData;
  31362. nKeyID = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData);
  31363. pGen->pIn++; /* Jump the language construct keyword */
  31364. if( nKeyID == JX9_TKWRD_PRINT ){
  31365. SyToken *pTmp, *pNext = 0;
  31366. /* Compile arguments one after one */
  31367. pTmp = pGen->pEnd;
  31368. jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 1 /* Boolean true index */, 0, 0);
  31369. while( SXRET_OK == jx9GetNextExpr(pGen->pIn, pTmp, &pNext) ){
  31370. if( pGen->pIn < pNext ){
  31371. pGen->pEnd = pNext;
  31372. rc = jx9CompileExpr(&(*pGen), EXPR_FLAG_RDONLY_LOAD/* Do not create variable if inexistant */, 0);
  31373. if( rc == SXERR_ABORT ){
  31374. return SXERR_ABORT;
  31375. }
  31376. if( rc != SXERR_EMPTY ){
  31377. /* Ticket 1433-008: Optimization #1: Consume input directly
  31378. * without the overhead of a function call.
  31379. * This is a very powerful optimization that improve
  31380. * performance greatly.
  31381. */
  31382. jx9VmEmitInstr(pGen->pVm,JX9_OP_CONSUME,1,0,0,0);
  31383. }
  31384. }
  31385. /* Jump trailing commas */
  31386. while( pNext < pTmp && (pNext->nType & JX9_TK_COMMA) ){
  31387. pNext++;
  31388. }
  31389. pGen->pIn = pNext;
  31390. }
  31391. /* Restore token stream */
  31392. pGen->pEnd = pTmp;
  31393. }else{
  31394. sxi32 nArg = 0;
  31395. sxu32 nIdx = 0;
  31396. rc = jx9CompileExpr(&(*pGen), EXPR_FLAG_RDONLY_LOAD/* Do not create variable if inexistant */, 0);
  31397. if( rc == SXERR_ABORT ){
  31398. return SXERR_ABORT;
  31399. }else if(rc != SXERR_EMPTY ){
  31400. nArg = 1;
  31401. }
  31402. if( SXRET_OK != GenStateFindLiteral(&(*pGen), pName, &nIdx) ){
  31403. jx9_value *pObj;
  31404. /* Emit the call instruction */
  31405. pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
  31406. if( pObj == 0 ){
  31407. SXUNUSED(iCompileFlag); /* cc warning */
  31408. return GenStateOutOfMem(pGen);
  31409. }
  31410. jx9MemObjInitFromString(pGen->pVm, pObj, pName);
  31411. /* Install in the literal table */
  31412. GenStateInstallLiteral(&(*pGen), pObj, nIdx);
  31413. }
  31414. /* Emit the call instruction */
  31415. jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
  31416. jx9VmEmitInstr(pGen->pVm, JX9_OP_CALL, nArg, 0, 0, 0);
  31417. }
  31418. /* Node successfully compiled */
  31419. return SXRET_OK;
  31420. }
  31421. /*
  31422. * Compile a node holding a variable declaration.
  31423. * According to the J9X language reference
  31424. * Variables in JX9 are represented by a dollar sign followed by the name of the variable.
  31425. * The variable name is case-sensitive.
  31426. * Variable names follow the same rules as other labels in JX9. A valid variable name
  31427. * starts with a letter, underscore or any UTF-8 stream, followed by any number of letters
  31428. * numbers, or underscores.
  31429. * By default, variables are always assigned by value unless the target value is a JSON
  31430. * array or a JSON object which is passed by reference.
  31431. */
  31432. JX9_PRIVATE sxi32 jx9CompileVariable(jx9_gen_state *pGen,sxi32 iCompileFlag)
  31433. {
  31434. sxu32 nLine = pGen->pIn->nLine;
  31435. SyHashEntry *pEntry;
  31436. SyString *pName;
  31437. char *zName = 0;
  31438. sxi32 iP1;
  31439. void *p3;
  31440. sxi32 rc;
  31441. pGen->pIn++; /* Jump the dollar sign '$' */
  31442. if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
  31443. /* Invalid variable name */
  31444. rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Invalid variable name");
  31445. if( rc == SXERR_ABORT ){
  31446. /* Error count limit reached, abort immediately */
  31447. return SXERR_ABORT;
  31448. }
  31449. return SXRET_OK;
  31450. }
  31451. /* Extract variable name */
  31452. pName = &pGen->pIn->sData;
  31453. /* Advance the stream cursor */
  31454. pGen->pIn++;
  31455. pEntry = SyHashGet(&pGen->hVar, (const void *)pName->zString, pName->nByte);
  31456. if( pEntry == 0 ){
  31457. /* Duplicate name */
  31458. zName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
  31459. if( zName == 0 ){
  31460. return GenStateOutOfMem(pGen);
  31461. }
  31462. /* Install in the hashtable */
  31463. SyHashInsert(&pGen->hVar, zName, pName->nByte, zName);
  31464. }else{
  31465. /* Name already available */
  31466. zName = (char *)pEntry->pUserData;
  31467. }
  31468. p3 = (void *)zName;
  31469. iP1 = 0;
  31470. if( iCompileFlag & EXPR_FLAG_RDONLY_LOAD ){
  31471. if( (iCompileFlag & EXPR_FLAG_LOAD_IDX_STORE) == 0 ){
  31472. /* Read-only load.In other words do not create the variable if inexistant */
  31473. iP1 = 1;
  31474. }
  31475. }
  31476. /* Emit the load instruction */
  31477. jx9VmEmitInstr(pGen->pVm, JX9_OP_LOAD, iP1, 0, p3, 0);
  31478. /* Node successfully compiled */
  31479. return SXRET_OK;
  31480. }
  31481. /* Forward declaration */
  31482. static sxi32 GenStateCompileFunc(jx9_gen_state *pGen,SyString *pName,sxi32 iFlags,jx9_vm_func **ppFunc);
  31483. /*
  31484. * Compile an annoynmous function or a closure.
  31485. * According to the JX9 language reference
  31486. * Anonymous functions, also known as closures, allow the creation of functions
  31487. * which have no specified name. They are most useful as the value of callback
  31488. * parameters, but they have many other uses. Closures can also be used as
  31489. * the values of variables; Assigning a closure to a variable uses the same
  31490. * syntax as any other assignment, including the trailing semicolon:
  31491. * Example Anonymous function variable assignment example
  31492. * $greet = function($name)
  31493. * {
  31494. * printf("Hello %s\r\n", $name);
  31495. * };
  31496. * $greet('World');
  31497. * $greet('JX9');
  31498. * Note that the implementation of annoynmous function and closure under
  31499. * JX9 is completely different from the one used by the engine.
  31500. */
  31501. JX9_PRIVATE sxi32 jx9CompileAnnonFunc(jx9_gen_state *pGen,sxi32 iCompileFlag)
  31502. {
  31503. jx9_vm_func *pAnnonFunc; /* Annonymous function body */
  31504. char zName[512]; /* Unique lambda name */
  31505. static int iCnt = 1; /* There is no worry about thread-safety here, because only
  31506. * one thread is allowed to compile the script.
  31507. */
  31508. jx9_value *pObj;
  31509. SyString sName;
  31510. sxu32 nIdx;
  31511. sxu32 nLen;
  31512. sxi32 rc;
  31513. pGen->pIn++; /* Jump the 'function' keyword */
  31514. if( pGen->pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD) ){
  31515. pGen->pIn++;
  31516. }
  31517. /* Reserve a constant for the lambda */
  31518. pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
  31519. if( pObj == 0 ){
  31520. GenStateOutOfMem(pGen);
  31521. SXUNUSED(iCompileFlag); /* cc warning */
  31522. return SXERR_ABORT;
  31523. }
  31524. /* Generate a unique name */
  31525. nLen = SyBufferFormat(zName, sizeof(zName), "[lambda_%d]", iCnt++);
  31526. /* Make sure the generated name is unique */
  31527. while( SyHashGet(&pGen->pVm->hFunction, zName, nLen) != 0 && nLen < sizeof(zName) - 2 ){
  31528. nLen = SyBufferFormat(zName, sizeof(zName), "[lambda_%d]", iCnt++);
  31529. }
  31530. SyStringInitFromBuf(&sName, zName, nLen);
  31531. jx9MemObjInitFromString(pGen->pVm, pObj, &sName);
  31532. /* Compile the lambda body */
  31533. rc = GenStateCompileFunc(&(*pGen),&sName,0,&pAnnonFunc);
  31534. if( rc == SXERR_ABORT ){
  31535. return SXERR_ABORT;
  31536. }
  31537. /* Emit the load constant instruction */
  31538. jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
  31539. /* Node successfully compiled */
  31540. return SXRET_OK;
  31541. }
  31542. /*
  31543. * Compile the 'continue' statement.
  31544. * According to the JX9 language reference
  31545. * continue is used within looping structures to skip the rest of the current loop iteration
  31546. * and continue execution at the condition evaluation and then the beginning of the next
  31547. * iteration.
  31548. * Note: Note that in JX9 the switch statement is considered a looping structure for
  31549. * the purposes of continue.
  31550. * continue accepts an optional numeric argument which tells it how many levels
  31551. * of enclosing loops it should skip to the end of.
  31552. * Note:
  31553. * continue 0; and continue 1; is the same as running continue;.
  31554. */
  31555. static sxi32 jx9CompileContinue(jx9_gen_state *pGen)
  31556. {
  31557. GenBlock *pLoop; /* Target loop */
  31558. sxi32 iLevel; /* How many nesting loop to skip */
  31559. sxu32 nLine;
  31560. sxi32 rc;
  31561. nLine = pGen->pIn->nLine;
  31562. iLevel = 0;
  31563. /* Jump the 'continue' keyword */
  31564. pGen->pIn++;
  31565. if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_NUM) ){
  31566. /* optional numeric argument which tells us how many levels
  31567. * of enclosing loops we should skip to the end of.
  31568. */
  31569. iLevel = (sxi32)jx9TokenValueToInt64(&pGen->pIn->sData);
  31570. if( iLevel < 2 ){
  31571. iLevel = 0;
  31572. }
  31573. pGen->pIn++; /* Jump the optional numeric argument */
  31574. }
  31575. /* Point to the target loop */
  31576. pLoop = GenStateFetchBlock(pGen->pCurrent, GEN_BLOCK_LOOP, iLevel);
  31577. if( pLoop == 0 ){
  31578. /* Illegal continue */
  31579. rc = jx9GenCompileError(pGen, E_ERROR, nLine, "A 'continue' statement may only be used within a loop or switch");
  31580. if( rc == SXERR_ABORT ){
  31581. /* Error count limit reached, abort immediately */
  31582. return SXERR_ABORT;
  31583. }
  31584. }else{
  31585. sxu32 nInstrIdx = 0;
  31586. /* Emit the unconditional jump to the beginning of the target loop */
  31587. jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pLoop->nFirstInstr, 0, &nInstrIdx);
  31588. if( pLoop->bPostContinue == TRUE ){
  31589. JumpFixup sJumpFix;
  31590. /* Post-continue */
  31591. sJumpFix.nJumpType = JX9_OP_JMP;
  31592. sJumpFix.nInstrIdx = nInstrIdx;
  31593. SySetPut(&pLoop->aPostContFix, (const void *)&sJumpFix);
  31594. }
  31595. }
  31596. if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
  31597. /* Not so fatal, emit a warning only */
  31598. jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Expected semi-colon ';' after 'continue' statement");
  31599. }
  31600. /* Statement successfully compiled */
  31601. return SXRET_OK;
  31602. }
  31603. /*
  31604. * Compile the 'break' statement.
  31605. * According to the JX9 language reference
  31606. * break ends execution of the current for, foreach, while, do-while or switch
  31607. * structure.
  31608. * break accepts an optional numeric argument which tells it how many nested
  31609. * enclosing structures are to be broken out of.
  31610. */
  31611. static sxi32 jx9CompileBreak(jx9_gen_state *pGen)
  31612. {
  31613. GenBlock *pLoop; /* Target loop */
  31614. sxi32 iLevel; /* How many nesting loop to skip */
  31615. sxu32 nLine;
  31616. sxi32 rc;
  31617. nLine = pGen->pIn->nLine;
  31618. iLevel = 0;
  31619. /* Jump the 'break' keyword */
  31620. pGen->pIn++;
  31621. if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_NUM) ){
  31622. /* optional numeric argument which tells us how many levels
  31623. * of enclosing loops we should skip to the end of.
  31624. */
  31625. iLevel = (sxi32)jx9TokenValueToInt64(&pGen->pIn->sData);
  31626. if( iLevel < 2 ){
  31627. iLevel = 0;
  31628. }
  31629. pGen->pIn++; /* Jump the optional numeric argument */
  31630. }
  31631. /* Extract the target loop */
  31632. pLoop = GenStateFetchBlock(pGen->pCurrent, GEN_BLOCK_LOOP, iLevel);
  31633. if( pLoop == 0 ){
  31634. /* Illegal break */
  31635. rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "A 'break' statement may only be used within a loop or switch");
  31636. if( rc == SXERR_ABORT ){
  31637. /* Error count limit reached, abort immediately */
  31638. return SXERR_ABORT;
  31639. }
  31640. }else{
  31641. sxu32 nInstrIdx;
  31642. rc = jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, 0, 0, &nInstrIdx);
  31643. if( rc == SXRET_OK ){
  31644. /* Fix the jump later when the jump destination is resolved */
  31645. GenStateNewJumpFixup(pLoop, JX9_OP_JMP, nInstrIdx);
  31646. }
  31647. }
  31648. if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
  31649. /* Not so fatal, emit a warning only */
  31650. jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Expected semi-colon ';' after 'break' statement");
  31651. }
  31652. /* Statement successfully compiled */
  31653. return SXRET_OK;
  31654. }
  31655. /* Forward declaration */
  31656. static sxi32 GenStateCompileChunk(jx9_gen_state *pGen,sxi32 iFlags);
  31657. /*
  31658. * Compile a JX9 block.
  31659. * A block is simply one or more JX9 statements and expressions to compile
  31660. * optionally delimited by braces {}.
  31661. * Return SXRET_OK on success. Any other return value indicates failure
  31662. * and this function takes care of generating the appropriate error
  31663. * message.
  31664. */
  31665. static sxi32 jx9CompileBlock(
  31666. jx9_gen_state *pGen /* Code generator state */
  31667. )
  31668. {
  31669. sxi32 rc;
  31670. if( pGen->pIn->nType & JX9_TK_OCB /* '{' */ ){
  31671. sxu32 nLine = pGen->pIn->nLine;
  31672. rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_STD, jx9VmInstrLength(pGen->pVm), 0, 0);
  31673. if( rc != SXRET_OK ){
  31674. return SXERR_ABORT;
  31675. }
  31676. pGen->pIn++;
  31677. /* Compile until we hit the closing braces '}' */
  31678. for(;;){
  31679. if( pGen->pIn >= pGen->pEnd ){
  31680. /* No more token to process. Missing closing braces */
  31681. jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Missing closing braces '}'");
  31682. break;
  31683. }
  31684. if( pGen->pIn->nType & JX9_TK_CCB/*'}'*/ ){
  31685. /* Closing braces found, break immediately*/
  31686. pGen->pIn++;
  31687. break;
  31688. }
  31689. /* Compile a single statement */
  31690. rc = GenStateCompileChunk(&(*pGen),JX9_COMPILE_SINGLE_STMT);
  31691. if( rc == SXERR_ABORT ){
  31692. return SXERR_ABORT;
  31693. }
  31694. }
  31695. GenStateLeaveBlock(&(*pGen), 0);
  31696. }else{
  31697. /* Compile a single statement */
  31698. rc = GenStateCompileChunk(&(*pGen),JX9_COMPILE_SINGLE_STMT);
  31699. if( rc == SXERR_ABORT ){
  31700. return SXERR_ABORT;
  31701. }
  31702. }
  31703. /* Jump trailing semi-colons ';' */
  31704. while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) ){
  31705. pGen->pIn++;
  31706. }
  31707. return SXRET_OK;
  31708. }
  31709. /*
  31710. * Compile the gentle 'while' statement.
  31711. * According to the JX9 language reference
  31712. * while loops are the simplest type of loop in JX9.They behave just like their C counterparts.
  31713. * The basic form of a while statement is:
  31714. * while (expr)
  31715. * statement
  31716. * The meaning of a while statement is simple. It tells JX9 to execute the nested statement(s)
  31717. * repeatedly, as long as the while expression evaluates to TRUE. The value of the expression
  31718. * is checked each time at the beginning of the loop, so even if this value changes during
  31719. * the execution of the nested statement(s), execution will not stop until the end of the iteration
  31720. * (each time JX9 runs the statements in the loop is one iteration). Sometimes, if the while
  31721. * expression evaluates to FALSE from the very beginning, the nested statement(s) won't even be run once.
  31722. * Like with the if statement, you can group multiple statements within the same while loop by surrounding
  31723. * a group of statements with curly braces, or by using the alternate syntax:
  31724. * while (expr):
  31725. * statement
  31726. * endwhile;
  31727. */
  31728. static sxi32 jx9CompileWhile(jx9_gen_state *pGen)
  31729. {
  31730. GenBlock *pWhileBlock = 0;
  31731. SyToken *pTmp, *pEnd = 0;
  31732. sxu32 nFalseJump;
  31733. sxu32 nLine;
  31734. sxi32 rc;
  31735. nLine = pGen->pIn->nLine;
  31736. /* Jump the 'while' keyword */
  31737. pGen->pIn++;
  31738. if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
  31739. /* Syntax error */
  31740. rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'while' keyword");
  31741. if( rc == SXERR_ABORT ){
  31742. /* Error count limit reached, abort immediately */
  31743. return SXERR_ABORT;
  31744. }
  31745. goto Synchronize;
  31746. }
  31747. /* Jump the left parenthesis '(' */
  31748. pGen->pIn++;
  31749. /* Create the loop block */
  31750. rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, jx9VmInstrLength(pGen->pVm), 0, &pWhileBlock);
  31751. if( rc != SXRET_OK ){
  31752. return SXERR_ABORT;
  31753. }
  31754. /* Delimit the condition */
  31755. jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
  31756. if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
  31757. /* Empty expression */
  31758. rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected expression after 'while' keyword");
  31759. if( rc == SXERR_ABORT ){
  31760. /* Error count limit reached, abort immediately */
  31761. return SXERR_ABORT;
  31762. }
  31763. }
  31764. /* Swap token streams */
  31765. pTmp = pGen->pEnd;
  31766. pGen->pEnd = pEnd;
  31767. /* Compile the expression */
  31768. rc = jx9CompileExpr(&(*pGen), 0, 0);
  31769. if( rc == SXERR_ABORT ){
  31770. /* Expression handler request an operation abort [i.e: Out-of-memory] */
  31771. return SXERR_ABORT;
  31772. }
  31773. /* Update token stream */
  31774. while(pGen->pIn < pEnd ){
  31775. rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData);
  31776. if( rc == SXERR_ABORT ){
  31777. return SXERR_ABORT;
  31778. }
  31779. pGen->pIn++;
  31780. }
  31781. /* Synchronize pointers */
  31782. pGen->pIn = &pEnd[1];
  31783. pGen->pEnd = pTmp;
  31784. /* Emit the false jump */
  31785. jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nFalseJump);
  31786. /* Save the instruction index so we can fix it later when the jump destination is resolved */
  31787. GenStateNewJumpFixup(pWhileBlock, JX9_OP_JZ, nFalseJump);
  31788. /* Compile the loop body */
  31789. rc = jx9CompileBlock(&(*pGen));
  31790. if( rc == SXERR_ABORT ){
  31791. return SXERR_ABORT;
  31792. }
  31793. /* Emit the unconditional jump to the start of the loop */
  31794. jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pWhileBlock->nFirstInstr, 0, 0);
  31795. /* Fix all jumps now the destination is resolved */
  31796. GenStateFixJumps(pWhileBlock, -1, jx9VmInstrLength(pGen->pVm));
  31797. /* Release the loop block */
  31798. GenStateLeaveBlock(pGen, 0);
  31799. /* Statement successfully compiled */
  31800. return SXRET_OK;
  31801. Synchronize:
  31802. /* Synchronize with the first semi-colon ';' so we can avoid
  31803. * compiling this erroneous block.
  31804. */
  31805. while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
  31806. pGen->pIn++;
  31807. }
  31808. return SXRET_OK;
  31809. }
  31810. /*
  31811. * Compile the complex and powerful 'for' statement.
  31812. * According to the JX9 language reference
  31813. * for loops are the most complex loops in JX9. They behave like their C counterparts.
  31814. * The syntax of a for loop is:
  31815. * for (expr1; expr2; expr3)
  31816. * statement
  31817. * The first expression (expr1) is evaluated (executed) once unconditionally at
  31818. * the beginning of the loop.
  31819. * In the beginning of each iteration, expr2 is evaluated. If it evaluates to
  31820. * TRUE, the loop continues and the nested statement(s) are executed. If it evaluates
  31821. * to FALSE, the execution of the loop ends.
  31822. * At the end of each iteration, expr3 is evaluated (executed).
  31823. * Each of the expressions can be empty or contain multiple expressions separated by commas.
  31824. * In expr2, all expressions separated by a comma are evaluated but the result is taken
  31825. * from the last part. expr2 being empty means the loop should be run indefinitely
  31826. * (JX9 implicitly considers it as TRUE, like C). This may not be as useless as you might
  31827. * think, since often you'd want to end the loop using a conditional break statement instead
  31828. * of using the for truth expression.
  31829. */
  31830. static sxi32 jx9CompileFor(jx9_gen_state *pGen)
  31831. {
  31832. SyToken *pTmp, *pPostStart, *pEnd = 0;
  31833. GenBlock *pForBlock = 0;
  31834. sxu32 nFalseJump;
  31835. sxu32 nLine;
  31836. sxi32 rc;
  31837. nLine = pGen->pIn->nLine;
  31838. /* Jump the 'for' keyword */
  31839. pGen->pIn++;
  31840. if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
  31841. /* Syntax error */
  31842. rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'for' keyword");
  31843. if( rc == SXERR_ABORT ){
  31844. /* Error count limit reached, abort immediately */
  31845. return SXERR_ABORT;
  31846. }
  31847. return SXRET_OK;
  31848. }
  31849. /* Jump the left parenthesis '(' */
  31850. pGen->pIn++;
  31851. /* Delimit the init-expr;condition;post-expr */
  31852. jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
  31853. if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
  31854. /* Empty expression */
  31855. rc = jx9GenCompileError(pGen, E_ERROR, nLine, "for: Invalid expression");
  31856. if( rc == SXERR_ABORT ){
  31857. /* Error count limit reached, abort immediately */
  31858. return SXERR_ABORT;
  31859. }
  31860. /* Synchronize */
  31861. pGen->pIn = pEnd;
  31862. if( pGen->pIn < pGen->pEnd ){
  31863. pGen->pIn++;
  31864. }
  31865. return SXRET_OK;
  31866. }
  31867. /* Swap token streams */
  31868. pTmp = pGen->pEnd;
  31869. pGen->pEnd = pEnd;
  31870. /* Compile initialization expressions if available */
  31871. rc = jx9CompileExpr(&(*pGen), 0, 0);
  31872. /* Pop operand lvalues */
  31873. if( rc == SXERR_ABORT ){
  31874. /* Expression handler request an operation abort [i.e: Out-of-memory] */
  31875. return SXERR_ABORT;
  31876. }else if( rc != SXERR_EMPTY ){
  31877. jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
  31878. }
  31879. if( (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
  31880. /* Syntax error */
  31881. rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
  31882. "for: Expected ';' after initialization expressions");
  31883. if( rc == SXERR_ABORT ){
  31884. /* Error count limit reached, abort immediately */
  31885. return SXERR_ABORT;
  31886. }
  31887. return SXRET_OK;
  31888. }
  31889. /* Jump the trailing ';' */
  31890. pGen->pIn++;
  31891. /* Create the loop block */
  31892. rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, jx9VmInstrLength(pGen->pVm), 0, &pForBlock);
  31893. if( rc != SXRET_OK ){
  31894. return SXERR_ABORT;
  31895. }
  31896. /* Deffer continue jumps */
  31897. pForBlock->bPostContinue = TRUE;
  31898. /* Compile the condition */
  31899. rc = jx9CompileExpr(&(*pGen), 0, 0);
  31900. if( rc == SXERR_ABORT ){
  31901. /* Expression handler request an operation abort [i.e: Out-of-memory] */
  31902. return SXERR_ABORT;
  31903. }else if( rc != SXERR_EMPTY ){
  31904. /* Emit the false jump */
  31905. jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nFalseJump);
  31906. /* Save the instruction index so we can fix it later when the jump destination is resolved */
  31907. GenStateNewJumpFixup(pForBlock, JX9_OP_JZ, nFalseJump);
  31908. }
  31909. if( (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
  31910. /* Syntax error */
  31911. rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
  31912. "for: Expected ';' after conditionals expressions");
  31913. if( rc == SXERR_ABORT ){
  31914. /* Error count limit reached, abort immediately */
  31915. return SXERR_ABORT;
  31916. }
  31917. return SXRET_OK;
  31918. }
  31919. /* Jump the trailing ';' */
  31920. pGen->pIn++;
  31921. /* Save the post condition stream */
  31922. pPostStart = pGen->pIn;
  31923. /* Compile the loop body */
  31924. pGen->pIn = &pEnd[1]; /* Jump the trailing parenthesis ')' */
  31925. pGen->pEnd = pTmp;
  31926. rc = jx9CompileBlock(&(*pGen));
  31927. if( rc == SXERR_ABORT ){
  31928. return SXERR_ABORT;
  31929. }
  31930. /* Fix post-continue jumps */
  31931. if( SySetUsed(&pForBlock->aPostContFix) > 0 ){
  31932. JumpFixup *aPost;
  31933. VmInstr *pInstr;
  31934. sxu32 nJumpDest;
  31935. sxu32 n;
  31936. aPost = (JumpFixup *)SySetBasePtr(&pForBlock->aPostContFix);
  31937. nJumpDest = jx9VmInstrLength(pGen->pVm);
  31938. for( n = 0 ; n < SySetUsed(&pForBlock->aPostContFix) ; ++n ){
  31939. pInstr = jx9VmGetInstr(pGen->pVm, aPost[n].nInstrIdx);
  31940. if( pInstr ){
  31941. /* Fix jump */
  31942. pInstr->iP2 = nJumpDest;
  31943. }
  31944. }
  31945. }
  31946. /* compile the post-expressions if available */
  31947. while( pPostStart < pEnd && (pPostStart->nType & JX9_TK_SEMI) ){
  31948. pPostStart++;
  31949. }
  31950. if( pPostStart < pEnd ){
  31951. SyToken *pTmpIn, *pTmpEnd;
  31952. SWAP_DELIMITER(pGen, pPostStart, pEnd);
  31953. rc = jx9CompileExpr(&(*pGen), 0, 0);
  31954. if( pGen->pIn < pGen->pEnd ){
  31955. /* Syntax error */
  31956. rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "for: Expected ')' after post-expressions");
  31957. if( rc == SXERR_ABORT ){
  31958. /* Error count limit reached, abort immediately */
  31959. return SXERR_ABORT;
  31960. }
  31961. return SXRET_OK;
  31962. }
  31963. RE_SWAP_DELIMITER(pGen);
  31964. if( rc == SXERR_ABORT ){
  31965. /* Expression handler request an operation abort [i.e: Out-of-memory] */
  31966. return SXERR_ABORT;
  31967. }else if( rc != SXERR_EMPTY){
  31968. /* Pop operand lvalue */
  31969. jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
  31970. }
  31971. }
  31972. /* Emit the unconditional jump to the start of the loop */
  31973. jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pForBlock->nFirstInstr, 0, 0);
  31974. /* Fix all jumps now the destination is resolved */
  31975. GenStateFixJumps(pForBlock, -1, jx9VmInstrLength(pGen->pVm));
  31976. /* Release the loop block */
  31977. GenStateLeaveBlock(pGen, 0);
  31978. /* Statement successfully compiled */
  31979. return SXRET_OK;
  31980. }
  31981. /* Expression tree validator callback used by the 'foreach' statement.
  31982. * Note that only variable expression [i.e: $x; ${'My'.'Var'}; ${$a['key]};...]
  31983. * are allowed.
  31984. */
  31985. static sxi32 GenStateForEachNodeValidator(jx9_gen_state *pGen,jx9_expr_node *pRoot)
  31986. {
  31987. sxi32 rc = SXRET_OK; /* Assume a valid expression tree */
  31988. if( pRoot->xCode != jx9CompileVariable ){
  31989. /* Unexpected expression */
  31990. rc = jx9GenCompileError(&(*pGen),
  31991. E_ERROR,
  31992. pRoot->pStart? pRoot->pStart->nLine : 0,
  31993. "foreach: Expecting a variable name"
  31994. );
  31995. if( rc != SXERR_ABORT ){
  31996. rc = SXERR_INVALID;
  31997. }
  31998. }
  31999. return rc;
  32000. }
  32001. /*
  32002. * Compile the 'foreach' statement.
  32003. * According to the JX9 language reference
  32004. * The foreach construct simply gives an easy way to iterate over arrays. foreach works
  32005. * only on arrays (and objects), and will issue an error when you try to use it on a variable
  32006. * with a different data type or an uninitialized variable. There are two syntaxes; the second
  32007. * is a minor but useful extension of the first:
  32008. * foreach (json_array_json_object as $value)
  32009. * statement
  32010. * foreach (json_array_json_objec as $key,$value)
  32011. * statement
  32012. * The first form loops over the array given by array_expression. On each loop, the value
  32013. * of the current element is assigned to $value and the internal array pointer is advanced
  32014. * by one (so on the next loop, you'll be looking at the next element).
  32015. * The second form does the same thing, except that the current element's key will be assigned
  32016. * to the variable $key on each loop.
  32017. * Note:
  32018. * When foreach first starts executing, the internal array pointer is automatically reset to the
  32019. * first element of the array. This means that you do not need to call reset() before a foreach loop.
  32020. */
  32021. static sxi32 jx9CompileForeach(jx9_gen_state *pGen)
  32022. {
  32023. SyToken *pCur, *pTmp, *pEnd = 0;
  32024. GenBlock *pForeachBlock = 0;
  32025. jx9_foreach_info *pInfo;
  32026. sxu32 nFalseJump;
  32027. VmInstr *pInstr;
  32028. sxu32 nLine;
  32029. sxi32 rc;
  32030. nLine = pGen->pIn->nLine;
  32031. /* Jump the 'foreach' keyword */
  32032. pGen->pIn++;
  32033. if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
  32034. /* Syntax error */
  32035. rc = jx9GenCompileError(pGen, E_ERROR, nLine, "foreach: Expected '('");
  32036. if( rc == SXERR_ABORT ){
  32037. /* Error count limit reached, abort immediately */
  32038. return SXERR_ABORT;
  32039. }
  32040. goto Synchronize;
  32041. }
  32042. /* Jump the left parenthesis '(' */
  32043. pGen->pIn++;
  32044. /* Create the loop block */
  32045. rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, jx9VmInstrLength(pGen->pVm), 0, &pForeachBlock);
  32046. if( rc != SXRET_OK ){
  32047. return SXERR_ABORT;
  32048. }
  32049. /* Delimit the expression */
  32050. jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
  32051. if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
  32052. /* Empty expression */
  32053. rc = jx9GenCompileError(pGen, E_ERROR, nLine, "foreach: Missing expression");
  32054. if( rc == SXERR_ABORT ){
  32055. /* Error count limit reached, abort immediately */
  32056. return SXERR_ABORT;
  32057. }
  32058. /* Synchronize */
  32059. pGen->pIn = pEnd;
  32060. if( pGen->pIn < pGen->pEnd ){
  32061. pGen->pIn++;
  32062. }
  32063. return SXRET_OK;
  32064. }
  32065. /* Compile the array expression */
  32066. pCur = pGen->pIn;
  32067. while( pCur < pEnd ){
  32068. if( pCur->nType & JX9_TK_KEYWORD ){
  32069. sxi32 nKeywrd = SX_PTR_TO_INT(pCur->pUserData);
  32070. if( nKeywrd == JX9_TKWRD_AS ){
  32071. /* Break with the first 'as' found */
  32072. break;
  32073. }
  32074. }
  32075. /* Advance the stream cursor */
  32076. pCur++;
  32077. }
  32078. if( pCur <= pGen->pIn ){
  32079. rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine,
  32080. "foreach: Missing array/object expression");
  32081. if( rc == SXERR_ABORT ){
  32082. /* Don't worry about freeing memory, everything will be released shortly */
  32083. return SXERR_ABORT;
  32084. }
  32085. goto Synchronize;
  32086. }
  32087. /* Swap token streams */
  32088. pTmp = pGen->pEnd;
  32089. pGen->pEnd = pCur;
  32090. rc = jx9CompileExpr(&(*pGen), 0, 0);
  32091. if( rc == SXERR_ABORT ){
  32092. /* Expression handler request an operation abort [i.e: Out-of-memory] */
  32093. return SXERR_ABORT;
  32094. }
  32095. /* Update token stream */
  32096. while(pGen->pIn < pCur ){
  32097. rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Unexpected token '%z'", &pGen->pIn->sData);
  32098. if( rc == SXERR_ABORT ){
  32099. /* Don't worry about freeing memory, everything will be released shortly */
  32100. return SXERR_ABORT;
  32101. }
  32102. pGen->pIn++;
  32103. }
  32104. pCur++; /* Jump the 'as' keyword */
  32105. pGen->pIn = pCur;
  32106. if( pGen->pIn >= pEnd ){
  32107. rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $key => $value pair");
  32108. if( rc == SXERR_ABORT ){
  32109. return SXERR_ABORT;
  32110. }
  32111. }
  32112. /* Create the foreach context */
  32113. pInfo = (jx9_foreach_info *)SyMemBackendAlloc(&pGen->pVm->sAllocator, sizeof(jx9_foreach_info));
  32114. if( pInfo == 0 ){
  32115. jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Fatal, JX9 engine is running out-of-memory");
  32116. return SXERR_ABORT;
  32117. }
  32118. /* Zero the structure */
  32119. SyZero(pInfo, sizeof(jx9_foreach_info));
  32120. /* Initialize structure fields */
  32121. SySetInit(&pInfo->aStep, &pGen->pVm->sAllocator, sizeof(jx9_foreach_step *));
  32122. /* Check if we have a key field */
  32123. while( pCur < pEnd && (pCur->nType & JX9_TK_COMMA) == 0 ){
  32124. pCur++;
  32125. }
  32126. if( pCur < pEnd ){
  32127. /* Compile the expression holding the key name */
  32128. if( pGen->pIn >= pCur ){
  32129. rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $key");
  32130. if( rc == SXERR_ABORT ){
  32131. /* Don't worry about freeing memory, everything will be released shortly */
  32132. return SXERR_ABORT;
  32133. }
  32134. }else{
  32135. pGen->pEnd = pCur;
  32136. rc = jx9CompileExpr(&(*pGen), 0, GenStateForEachNodeValidator);
  32137. if( rc == SXERR_ABORT ){
  32138. /* Don't worry about freeing memory, everything will be released shortly */
  32139. return SXERR_ABORT;
  32140. }
  32141. pInstr = jx9VmPopInstr(pGen->pVm);
  32142. if( pInstr->p3 ){
  32143. /* Record key name */
  32144. SyStringInitFromBuf(&pInfo->sKey, pInstr->p3, SyStrlen((const char *)pInstr->p3));
  32145. }
  32146. pInfo->iFlags |= JX9_4EACH_STEP_KEY;
  32147. }
  32148. pGen->pIn = &pCur[1]; /* Jump the arrow */
  32149. }
  32150. pGen->pEnd = pEnd;
  32151. if( pGen->pIn >= pEnd ){
  32152. rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $value");
  32153. if( rc == SXERR_ABORT ){
  32154. /* Don't worry about freeing memory, everything will be released shortly */
  32155. return SXERR_ABORT;
  32156. }
  32157. goto Synchronize;
  32158. }
  32159. /* Compile the expression holding the value name */
  32160. rc = jx9CompileExpr(&(*pGen), 0, GenStateForEachNodeValidator);
  32161. if( rc == SXERR_ABORT ){
  32162. /* Don't worry about freeing memory, everything will be released shortly */
  32163. return SXERR_ABORT;
  32164. }
  32165. pInstr = jx9VmPopInstr(pGen->pVm);
  32166. if( pInstr->p3 ){
  32167. /* Record value name */
  32168. SyStringInitFromBuf(&pInfo->sValue, pInstr->p3, SyStrlen((const char *)pInstr->p3));
  32169. }
  32170. /* Emit the 'FOREACH_INIT' instruction */
  32171. jx9VmEmitInstr(pGen->pVm, JX9_OP_FOREACH_INIT, 0, 0, pInfo, &nFalseJump);
  32172. /* Save the instruction index so we can fix it later when the jump destination is resolved */
  32173. GenStateNewJumpFixup(pForeachBlock, JX9_OP_FOREACH_INIT, nFalseJump);
  32174. /* Record the first instruction to execute */
  32175. pForeachBlock->nFirstInstr = jx9VmInstrLength(pGen->pVm);
  32176. /* Emit the FOREACH_STEP instruction */
  32177. jx9VmEmitInstr(pGen->pVm, JX9_OP_FOREACH_STEP, 0, 0, pInfo, &nFalseJump);
  32178. /* Save the instruction index so we can fix it later when the jump destination is resolved */
  32179. GenStateNewJumpFixup(pForeachBlock, JX9_OP_FOREACH_STEP, nFalseJump);
  32180. /* Compile the loop body */
  32181. pGen->pIn = &pEnd[1];
  32182. pGen->pEnd = pTmp;
  32183. rc = jx9CompileBlock(&(*pGen));
  32184. if( rc == SXERR_ABORT ){
  32185. /* Don't worry about freeing memory, everything will be released shortly */
  32186. return SXERR_ABORT;
  32187. }
  32188. /* Emit the unconditional jump to the start of the loop */
  32189. jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pForeachBlock->nFirstInstr, 0, 0);
  32190. /* Fix all jumps now the destination is resolved */
  32191. GenStateFixJumps(pForeachBlock, -1,jx9VmInstrLength(pGen->pVm));
  32192. /* Release the loop block */
  32193. GenStateLeaveBlock(pGen, 0);
  32194. /* Statement successfully compiled */
  32195. return SXRET_OK;
  32196. Synchronize:
  32197. /* Synchronize with the first semi-colon ';' so we can avoid
  32198. * compiling this erroneous block.
  32199. */
  32200. while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
  32201. pGen->pIn++;
  32202. }
  32203. return SXRET_OK;
  32204. }
  32205. /*
  32206. * Compile the infamous if/elseif/else if/else statements.
  32207. * According to the JX9 language reference
  32208. * The if construct is one of the most important features of many languages JX9 included.
  32209. * It allows for conditional execution of code fragments. JX9 features an if structure
  32210. * that is similar to that of C:
  32211. * if (expr)
  32212. * statement
  32213. * else construct:
  32214. * Often you'd want to execute a statement if a certain condition is met, and a different
  32215. * statement if the condition is not met. This is what else is for. else extends an if statement
  32216. * to execute a statement in case the expression in the if statement evaluates to FALSE.
  32217. * For example, the following code would display a is greater than b if $a is greater than
  32218. * $b, and a is NOT greater than b otherwise.
  32219. * The else statement is only executed if the if expression evaluated to FALSE, and if there
  32220. * were any elseif expressions - only if they evaluated to FALSE as well
  32221. * elseif
  32222. * elseif, as its name suggests, is a combination of if and else. Like else, it extends
  32223. * an if statement to execute a different statement in case the original if expression evaluates
  32224. * to FALSE. However, unlike else, it will execute that alternative expression only if the elseif
  32225. * conditional expression evaluates to TRUE. For example, the following code would display a is bigger
  32226. * than b, a equal to b or a is smaller than b:
  32227. * if ($a > $b) {
  32228. * print "a is bigger than b";
  32229. * } elseif ($a == $b) {
  32230. * print "a is equal to b";
  32231. * } else {
  32232. * print "a is smaller than b";
  32233. * }
  32234. */
  32235. static sxi32 jx9CompileIf(jx9_gen_state *pGen)
  32236. {
  32237. SyToken *pToken, *pTmp, *pEnd = 0;
  32238. GenBlock *pCondBlock = 0;
  32239. sxu32 nJumpIdx;
  32240. sxu32 nKeyID;
  32241. sxi32 rc;
  32242. /* Jump the 'if' keyword */
  32243. pGen->pIn++;
  32244. pToken = pGen->pIn;
  32245. /* Create the conditional block */
  32246. rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_COND, jx9VmInstrLength(pGen->pVm), 0, &pCondBlock);
  32247. if( rc != SXRET_OK ){
  32248. return SXERR_ABORT;
  32249. }
  32250. /* Process as many [if/else if/elseif/else] blocks as we can */
  32251. for(;;){
  32252. if( pToken >= pGen->pEnd || (pToken->nType & JX9_TK_LPAREN) == 0 ){
  32253. /* Syntax error */
  32254. if( pToken >= pGen->pEnd ){
  32255. pToken--;
  32256. }
  32257. rc = jx9GenCompileError(pGen, E_ERROR, pToken->nLine, "if/else/elseif: Missing '('");
  32258. if( rc == SXERR_ABORT ){
  32259. /* Error count limit reached, abort immediately */
  32260. return SXERR_ABORT;
  32261. }
  32262. goto Synchronize;
  32263. }
  32264. /* Jump the left parenthesis '(' */
  32265. pToken++;
  32266. /* Delimit the condition */
  32267. jx9DelimitNestedTokens(pToken, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
  32268. if( pToken >= pEnd || (pEnd->nType & JX9_TK_RPAREN) == 0 ){
  32269. /* Syntax error */
  32270. if( pToken >= pGen->pEnd ){
  32271. pToken--;
  32272. }
  32273. rc = jx9GenCompileError(pGen, E_ERROR, pToken->nLine, "if/else/elseif: Missing ')'");
  32274. if( rc == SXERR_ABORT ){
  32275. /* Error count limit reached, abort immediately */
  32276. return SXERR_ABORT;
  32277. }
  32278. goto Synchronize;
  32279. }
  32280. /* Swap token streams */
  32281. SWAP_TOKEN_STREAM(pGen, pToken, pEnd);
  32282. /* Compile the condition */
  32283. rc = jx9CompileExpr(&(*pGen), 0, 0);
  32284. /* Update token stream */
  32285. while(pGen->pIn < pEnd ){
  32286. jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData);
  32287. pGen->pIn++;
  32288. }
  32289. pGen->pIn = &pEnd[1];
  32290. pGen->pEnd = pTmp;
  32291. if( rc == SXERR_ABORT ){
  32292. /* Expression handler request an operation abort [i.e: Out-of-memory] */
  32293. return SXERR_ABORT;
  32294. }
  32295. /* Emit the false jump */
  32296. jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nJumpIdx);
  32297. /* Save the instruction index so we can fix it later when the jump destination is resolved */
  32298. GenStateNewJumpFixup(pCondBlock, JX9_OP_JZ, nJumpIdx);
  32299. /* Compile the body */
  32300. rc = jx9CompileBlock(&(*pGen));
  32301. if( rc == SXERR_ABORT ){
  32302. return SXERR_ABORT;
  32303. }
  32304. if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_KEYWORD) == 0 ){
  32305. break;
  32306. }
  32307. /* Ensure that the keyword ID is 'else if' or 'else' */
  32308. nKeyID = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData);
  32309. if( (nKeyID & (JX9_TKWRD_ELSE|JX9_TKWRD_ELIF)) == 0 ){
  32310. break;
  32311. }
  32312. /* Emit the unconditional jump */
  32313. jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, 0, 0, &nJumpIdx);
  32314. /* Save the instruction index so we can fix it later when the jump destination is resolved */
  32315. GenStateNewJumpFixup(pCondBlock, JX9_OP_JMP, nJumpIdx);
  32316. if( nKeyID & JX9_TKWRD_ELSE ){
  32317. pToken = &pGen->pIn[1];
  32318. if( pToken >= pGen->pEnd || (pToken->nType & JX9_TK_KEYWORD) == 0 ||
  32319. SX_PTR_TO_INT(pToken->pUserData) != JX9_TKWRD_IF ){
  32320. break;
  32321. }
  32322. pGen->pIn++; /* Jump the 'else' keyword */
  32323. }
  32324. pGen->pIn++; /* Jump the 'elseif/if' keyword */
  32325. /* Synchronize cursors */
  32326. pToken = pGen->pIn;
  32327. /* Fix the false jump */
  32328. GenStateFixJumps(pCondBlock, JX9_OP_JZ, jx9VmInstrLength(pGen->pVm));
  32329. } /* For(;;) */
  32330. /* Fix the false jump */
  32331. GenStateFixJumps(pCondBlock, JX9_OP_JZ, jx9VmInstrLength(pGen->pVm));
  32332. if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_KEYWORD) &&
  32333. (SX_PTR_TO_INT(pGen->pIn->pUserData) & JX9_TKWRD_ELSE) ){
  32334. /* Compile the else block */
  32335. pGen->pIn++;
  32336. rc = jx9CompileBlock(&(*pGen));
  32337. if( rc == SXERR_ABORT ){
  32338. return SXERR_ABORT;
  32339. }
  32340. }
  32341. nJumpIdx = jx9VmInstrLength(pGen->pVm);
  32342. /* Fix all unconditional jumps now the destination is resolved */
  32343. GenStateFixJumps(pCondBlock, JX9_OP_JMP, nJumpIdx);
  32344. /* Release the conditional block */
  32345. GenStateLeaveBlock(pGen, 0);
  32346. /* Statement successfully compiled */
  32347. return SXRET_OK;
  32348. Synchronize:
  32349. /* Synchronize with the first semi-colon ';' so we can avoid compiling this erroneous block.
  32350. */
  32351. while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
  32352. pGen->pIn++;
  32353. }
  32354. return SXRET_OK;
  32355. }
  32356. /*
  32357. * Compile the return statement.
  32358. * According to the JX9 language reference
  32359. * If called from within a function, the return() statement immediately ends execution
  32360. * of the current function, and returns its argument as the value of the function call.
  32361. * return() will also end the execution of an eval() statement or script file.
  32362. * If called from the global scope, then execution of the current script file is ended.
  32363. * If the current script file was include()ed or require()ed, then control is passed back
  32364. * to the calling file. Furthermore, if the current script file was include()ed, then the value
  32365. * given to return() will be returned as the value of the include() call. If return() is called
  32366. * from within the main script file, then script execution end.
  32367. * Note that since return() is a language construct and not a function, the parentheses
  32368. * surrounding its arguments are not required. It is common to leave them out, and you actually
  32369. * should do so as JX9 has less work to do in this case.
  32370. * Note: If no parameter is supplied, then the parentheses must be omitted and JX9 is returning NULL instead..
  32371. */
  32372. static sxi32 jx9CompileReturn(jx9_gen_state *pGen)
  32373. {
  32374. sxi32 nRet = 0; /* TRUE if there is a return value */
  32375. sxi32 rc;
  32376. /* Jump the 'return' keyword */
  32377. pGen->pIn++;
  32378. if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
  32379. /* Compile the expression */
  32380. rc = jx9CompileExpr(&(*pGen), 0, 0);
  32381. if( rc == SXERR_ABORT ){
  32382. return SXERR_ABORT;
  32383. }else if(rc != SXERR_EMPTY ){
  32384. nRet = 1;
  32385. }
  32386. }
  32387. /* Emit the done instruction */
  32388. jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, nRet, 0, 0, 0);
  32389. return SXRET_OK;
  32390. }
  32391. /*
  32392. * Compile the die/exit language construct.
  32393. * The role of these constructs is to terminate execution of the script.
  32394. * Shutdown functions will always be executed even if exit() is called.
  32395. */
  32396. static sxi32 jx9CompileHalt(jx9_gen_state *pGen)
  32397. {
  32398. sxi32 nExpr = 0;
  32399. sxi32 rc;
  32400. /* Jump the die/exit keyword */
  32401. pGen->pIn++;
  32402. if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
  32403. /* Compile the expression */
  32404. rc = jx9CompileExpr(&(*pGen), 0, 0);
  32405. if( rc == SXERR_ABORT ){
  32406. return SXERR_ABORT;
  32407. }else if(rc != SXERR_EMPTY ){
  32408. nExpr = 1;
  32409. }
  32410. }
  32411. /* Emit the HALT instruction */
  32412. jx9VmEmitInstr(pGen->pVm, JX9_OP_HALT, nExpr, 0, 0, 0);
  32413. return SXRET_OK;
  32414. }
  32415. /*
  32416. * Compile the static statement.
  32417. * According to the JX9 language reference
  32418. * Another important feature of variable scoping is the static variable.
  32419. * A static variable exists only in a local function scope, but it does not lose its value
  32420. * when program execution leaves this scope.
  32421. * Static variables also provide one way to deal with recursive functions.
  32422. */
  32423. static sxi32 jx9CompileStatic(jx9_gen_state *pGen)
  32424. {
  32425. jx9_vm_func_static_var sStatic; /* Structure describing the static variable */
  32426. jx9_vm_func *pFunc; /* Enclosing function */
  32427. GenBlock *pBlock;
  32428. SyString *pName;
  32429. char *zDup;
  32430. sxu32 nLine;
  32431. sxi32 rc;
  32432. /* Jump the static keyword */
  32433. nLine = pGen->pIn->nLine;
  32434. pGen->pIn++;
  32435. /* Extract the enclosing function if any */
  32436. pBlock = pGen->pCurrent;
  32437. while( pBlock ){
  32438. if( pBlock->iFlags & GEN_BLOCK_FUNC){
  32439. break;
  32440. }
  32441. /* Point to the upper block */
  32442. pBlock = pBlock->pParent;
  32443. }
  32444. if( pBlock == 0 ){
  32445. /* Static statement, called outside of a function body, treat it as a simple variable. */
  32446. if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_DOLLAR) == 0 ){
  32447. rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Expected variable after 'static' keyword");
  32448. if( rc == SXERR_ABORT ){
  32449. return SXERR_ABORT;
  32450. }
  32451. goto Synchronize;
  32452. }
  32453. /* Compile the expression holding the variable */
  32454. rc = jx9CompileExpr(&(*pGen), 0, 0);
  32455. if( rc == SXERR_ABORT ){
  32456. return SXERR_ABORT;
  32457. }else if( rc != SXERR_EMPTY ){
  32458. /* Emit the POP instruction */
  32459. jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
  32460. }
  32461. return SXRET_OK;
  32462. }
  32463. pFunc = (jx9_vm_func *)pBlock->pUserData;
  32464. /* Make sure we are dealing with a valid statement */
  32465. if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_DOLLAR) == 0 || &pGen->pIn[1] >= pGen->pEnd ||
  32466. (pGen->pIn[1].nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
  32467. rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Expected variable after 'static' keyword");
  32468. if( rc == SXERR_ABORT ){
  32469. return SXERR_ABORT;
  32470. }
  32471. goto Synchronize;
  32472. }
  32473. pGen->pIn++;
  32474. /* Extract variable name */
  32475. pName = &pGen->pIn->sData;
  32476. pGen->pIn++; /* Jump the var name */
  32477. if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI/*';'*/|JX9_TK_EQUAL/*'='*/)) == 0 ){
  32478. rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "static: Unexpected token '%z'", &pGen->pIn->sData);
  32479. goto Synchronize;
  32480. }
  32481. /* Initialize the structure describing the static variable */
  32482. SySetInit(&sStatic.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
  32483. sStatic.nIdx = SXU32_HIGH; /* Not yet created */
  32484. /* Duplicate variable name */
  32485. zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
  32486. if( zDup == 0 ){
  32487. jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Fatal, JX9 engine is running out of memory");
  32488. return SXERR_ABORT;
  32489. }
  32490. SyStringInitFromBuf(&sStatic.sName, zDup, pName->nByte);
  32491. /* Check if we have an expression to compile */
  32492. if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_EQUAL) ){
  32493. SySet *pInstrContainer;
  32494. pGen->pIn++; /* Jump the equal '=' sign */
  32495. /* Swap bytecode container */
  32496. pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
  32497. jx9VmSetByteCodeContainer(pGen->pVm, &sStatic.aByteCode);
  32498. /* Compile the expression */
  32499. rc = jx9CompileExpr(&(*pGen), 0, 0);
  32500. /* Emit the done instruction */
  32501. jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
  32502. /* Restore default bytecode container */
  32503. jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
  32504. }
  32505. /* Finally save the compiled static variable in the appropriate container */
  32506. SySetPut(&pFunc->aStatic, (const void *)&sStatic);
  32507. return SXRET_OK;
  32508. Synchronize:
  32509. /* Synchronize with the first semi-colon ';', so we can avoid compiling this erroneous
  32510. * statement.
  32511. */
  32512. while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
  32513. pGen->pIn++;
  32514. }
  32515. return SXRET_OK;
  32516. }
  32517. /*
  32518. * Compile the 'const' statement.
  32519. * According to the JX9 language reference
  32520. * A constant is an identifier (name) for a simple value. As the name suggests, that value
  32521. * cannot change during the execution of the script (except for magic constants, which aren't actually constants).
  32522. * A constant is case-sensitive by default. By convention, constant identifiers are always uppercase.
  32523. * The name of a constant follows the same rules as any label in JX9. A valid constant name starts
  32524. * with a letter or underscore, followed by any number of letters, numbers, or underscores.
  32525. * As a regular expression it would be expressed thusly: [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*
  32526. * Syntax
  32527. * You can define a constant by using the define()-function or by using the const keyword outside
  32528. * a object definition. Once a constant is defined, it can never be changed or undefined.
  32529. * You can get the value of a constant by simply specifying its name. Unlike with variables
  32530. * you should not prepend a constant with a $. You can also use the function constant() to read
  32531. * a constant's value if you wish to obtain the constant's name dynamically. Use get_defined_constants()
  32532. * to get a list of all defined constants.
  32533. */
  32534. static sxi32 jx9CompileConstant(jx9_gen_state *pGen)
  32535. {
  32536. SySet *pConsCode, *pInstrContainer;
  32537. sxu32 nLine = pGen->pIn->nLine;
  32538. SyString *pName;
  32539. sxi32 rc;
  32540. pGen->pIn++; /* Jump the 'const' keyword */
  32541. if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (JX9_TK_SSTR|JX9_TK_DSTR|JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
  32542. /* Invalid constant name */
  32543. rc = jx9GenCompileError(pGen, E_ERROR, nLine, "const: Invalid constant name");
  32544. if( rc == SXERR_ABORT ){
  32545. /* Error count limit reached, abort immediately */
  32546. return SXERR_ABORT;
  32547. }
  32548. goto Synchronize;
  32549. }
  32550. /* Peek constant name */
  32551. pName = &pGen->pIn->sData;
  32552. /* Make sure the constant name isn't reserved */
  32553. if( GenStateIsReservedID(pName) ){
  32554. /* Reserved constant */
  32555. rc = jx9GenCompileError(pGen, E_ERROR, nLine, "const: Cannot redeclare a reserved constant '%z'", pName);
  32556. if( rc == SXERR_ABORT ){
  32557. /* Error count limit reached, abort immediately */
  32558. return SXERR_ABORT;
  32559. }
  32560. goto Synchronize;
  32561. }
  32562. pGen->pIn++;
  32563. if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_EQUAL /* '=' */) == 0 ){
  32564. /* Invalid statement*/
  32565. rc = jx9GenCompileError(pGen, E_ERROR, nLine, "const: Expected '=' after constant name");
  32566. if( rc == SXERR_ABORT ){
  32567. /* Error count limit reached, abort immediately */
  32568. return SXERR_ABORT;
  32569. }
  32570. goto Synchronize;
  32571. }
  32572. pGen->pIn++; /*Jump the equal sign */
  32573. /* Allocate a new constant value container */
  32574. pConsCode = (SySet *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(SySet));
  32575. if( pConsCode == 0 ){
  32576. return GenStateOutOfMem(pGen);
  32577. }
  32578. SySetInit(pConsCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
  32579. /* Swap bytecode container */
  32580. pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
  32581. jx9VmSetByteCodeContainer(pGen->pVm, pConsCode);
  32582. /* Compile constant value */
  32583. rc = jx9CompileExpr(&(*pGen), 0, 0);
  32584. /* Emit the done instruction */
  32585. jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
  32586. jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
  32587. if( rc == SXERR_ABORT ){
  32588. /* Don't worry about freeing memory, everything will be released shortly */
  32589. return SXERR_ABORT;
  32590. }
  32591. SySetSetUserData(pConsCode, pGen->pVm);
  32592. /* Register the constant */
  32593. rc = jx9VmRegisterConstant(pGen->pVm, pName, jx9VmExpandConstantValue, pConsCode);
  32594. if( rc != SXRET_OK ){
  32595. SySetRelease(pConsCode);
  32596. SyMemBackendPoolFree(&pGen->pVm->sAllocator, pConsCode);
  32597. }
  32598. return SXRET_OK;
  32599. Synchronize:
  32600. /* Synchronize with the next-semi-colon and avoid compiling this erroneous statement */
  32601. while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
  32602. pGen->pIn++;
  32603. }
  32604. return SXRET_OK;
  32605. }
  32606. /*
  32607. * Compile the uplink construct.
  32608. * According to the JX9 language reference
  32609. * In JX9 global variables must be declared uplink inside a function if they are going
  32610. * to be used in that function.
  32611. * Example #1 Using global
  32612. * $a = 1;
  32613. * $b = 2;
  32614. * function Sum()
  32615. * {
  32616. * uplink $a, $b;
  32617. * $b = $a + $b;
  32618. * }
  32619. * Sum();
  32620. * print $b;
  32621. * ?>
  32622. * The above script will output 3. By declaring $a and $b global within the function
  32623. * all references to either variable will refer to the global version. There is no limit
  32624. * to the number of global variables that can be manipulated by a function.
  32625. */
  32626. static sxi32 jx9CompileUplink(jx9_gen_state *pGen)
  32627. {
  32628. SyToken *pTmp, *pNext = 0;
  32629. sxi32 nExpr;
  32630. sxi32 rc;
  32631. /* Jump the 'uplink' keyword */
  32632. pGen->pIn++;
  32633. if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_SEMI) ){
  32634. /* Nothing to process */
  32635. return SXRET_OK;
  32636. }
  32637. pTmp = pGen->pEnd;
  32638. nExpr = 0;
  32639. while( SXRET_OK == jx9GetNextExpr(pGen->pIn, pTmp, &pNext) ){
  32640. if( pGen->pIn < pNext ){
  32641. pGen->pEnd = pNext;
  32642. if( (pGen->pIn->nType & JX9_TK_DOLLAR) == 0 ){
  32643. rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "uplink: Expected variable name");
  32644. if( rc == SXERR_ABORT ){
  32645. return SXERR_ABORT;
  32646. }
  32647. }else{
  32648. pGen->pIn++;
  32649. if( pGen->pIn >= pGen->pEnd ){
  32650. /* Emit a warning */
  32651. jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn[-1].nLine, "uplink: Empty variable name");
  32652. }else{
  32653. rc = jx9CompileExpr(&(*pGen), 0, 0);
  32654. if( rc == SXERR_ABORT ){
  32655. return SXERR_ABORT;
  32656. }else if(rc != SXERR_EMPTY ){
  32657. nExpr++;
  32658. }
  32659. }
  32660. }
  32661. }
  32662. /* Next expression in the stream */
  32663. pGen->pIn = pNext;
  32664. /* Jump trailing commas */
  32665. while( pGen->pIn < pTmp && (pGen->pIn->nType & JX9_TK_COMMA) ){
  32666. pGen->pIn++;
  32667. }
  32668. }
  32669. /* Restore token stream */
  32670. pGen->pEnd = pTmp;
  32671. if( nExpr > 0 ){
  32672. /* Emit the uplink instruction */
  32673. jx9VmEmitInstr(pGen->pVm, JX9_OP_UPLINK, nExpr, 0, 0, 0);
  32674. }
  32675. return SXRET_OK;
  32676. }
  32677. /*
  32678. * Compile a switch block.
  32679. * (See block-comment below for more information)
  32680. */
  32681. static sxi32 GenStateCompileSwitchBlock(jx9_gen_state *pGen,sxu32 *pBlockStart)
  32682. {
  32683. sxi32 rc = SXRET_OK;
  32684. while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI/*';'*/|JX9_TK_COLON/*':'*/)) == 0 ){
  32685. /* Unexpected token */
  32686. rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData);
  32687. if( rc == SXERR_ABORT ){
  32688. return SXERR_ABORT;
  32689. }
  32690. pGen->pIn++;
  32691. }
  32692. pGen->pIn++;
  32693. /* First instruction to execute in this block. */
  32694. *pBlockStart = jx9VmInstrLength(pGen->pVm);
  32695. /* Compile the block until we hit a case/default/endswitch keyword
  32696. * or the '}' token */
  32697. for(;;){
  32698. if( pGen->pIn >= pGen->pEnd ){
  32699. /* No more input to process */
  32700. break;
  32701. }
  32702. rc = SXRET_OK;
  32703. if( (pGen->pIn->nType & JX9_TK_KEYWORD) == 0 ){
  32704. if( pGen->pIn->nType & JX9_TK_CCB /*'}' */ ){
  32705. rc = SXERR_EOF;
  32706. break;
  32707. }
  32708. }else{
  32709. sxi32 nKwrd;
  32710. /* Extract the keyword */
  32711. nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
  32712. if( nKwrd == JX9_TKWRD_CASE || nKwrd == JX9_TKWRD_DEFAULT ){
  32713. break;
  32714. }
  32715. }
  32716. /* Compile block */
  32717. rc = jx9CompileBlock(&(*pGen));
  32718. if( rc == SXERR_ABORT ){
  32719. return SXERR_ABORT;
  32720. }
  32721. }
  32722. return rc;
  32723. }
  32724. /*
  32725. * Compile a case eXpression.
  32726. * (See block-comment below for more information)
  32727. */
  32728. static sxi32 GenStateCompileCaseExpr(jx9_gen_state *pGen, jx9_case_expr *pExpr)
  32729. {
  32730. SySet *pInstrContainer;
  32731. SyToken *pEnd, *pTmp;
  32732. sxi32 iNest = 0;
  32733. sxi32 rc;
  32734. /* Delimit the expression */
  32735. pEnd = pGen->pIn;
  32736. while( pEnd < pGen->pEnd ){
  32737. if( pEnd->nType & JX9_TK_LPAREN /*(*/ ){
  32738. /* Increment nesting level */
  32739. iNest++;
  32740. }else if( pEnd->nType & JX9_TK_RPAREN /*)*/ ){
  32741. /* Decrement nesting level */
  32742. iNest--;
  32743. }else if( pEnd->nType & (JX9_TK_SEMI/*';'*/|JX9_TK_COLON/*;'*/) && iNest < 1 ){
  32744. break;
  32745. }
  32746. pEnd++;
  32747. }
  32748. if( pGen->pIn >= pEnd ){
  32749. rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Empty case expression");
  32750. if( rc == SXERR_ABORT ){
  32751. /* Error count limit reached, abort immediately */
  32752. return SXERR_ABORT;
  32753. }
  32754. }
  32755. /* Swap token stream */
  32756. pTmp = pGen->pEnd;
  32757. pGen->pEnd = pEnd;
  32758. pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
  32759. jx9VmSetByteCodeContainer(pGen->pVm, &pExpr->aByteCode);
  32760. rc = jx9CompileExpr(&(*pGen), 0, 0);
  32761. /* Emit the done instruction */
  32762. jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
  32763. jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
  32764. /* Update token stream */
  32765. pGen->pIn = pEnd;
  32766. pGen->pEnd = pTmp;
  32767. if( rc == SXERR_ABORT ){
  32768. return SXERR_ABORT;
  32769. }
  32770. return SXRET_OK;
  32771. }
  32772. /*
  32773. * Compile the smart switch statement.
  32774. * According to the JX9 language reference manual
  32775. * The switch statement is similar to a series of IF statements on the same expression.
  32776. * In many occasions, you may want to compare the same variable (or expression) with many
  32777. * different values, and execute a different piece of code depending on which value it equals to.
  32778. * This is exactly what the switch statement is for.
  32779. * Note: Note that unlike some other languages, the continue statement applies to switch and acts
  32780. * similar to break. If you have a switch inside a loop and wish to continue to the next iteration
  32781. * of the outer loop, use continue 2.
  32782. * Note that switch/case does loose comparision.
  32783. * It is important to understand how the switch statement is executed in order to avoid mistakes.
  32784. * The switch statement executes line by line (actually, statement by statement).
  32785. * In the beginning, no code is executed. Only when a case statement is found with a value that
  32786. * matches the value of the switch expression does JX9 begin to execute the statements.
  32787. * JX9 continues to execute the statements until the end of the switch block, or the first time
  32788. * it sees a break statement. If you don't write a break statement at the end of a case's statement list.
  32789. * In a switch statement, the condition is evaluated only once and the result is compared to each
  32790. * case statement. In an elseif statement, the condition is evaluated again. If your condition
  32791. * is more complicated than a simple compare and/or is in a tight loop, a switch may be faster.
  32792. * The statement list for a case can also be empty, which simply passes control into the statement
  32793. * list for the next case.
  32794. * The case expression may be any expression that evaluates to a simple type, that is, integer
  32795. * or floating-point numbers and strings.
  32796. */
  32797. static sxi32 jx9CompileSwitch(jx9_gen_state *pGen)
  32798. {
  32799. GenBlock *pSwitchBlock;
  32800. SyToken *pTmp, *pEnd;
  32801. jx9_switch *pSwitch;
  32802. sxu32 nLine;
  32803. sxi32 rc;
  32804. nLine = pGen->pIn->nLine;
  32805. /* Jump the 'switch' keyword */
  32806. pGen->pIn++;
  32807. if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
  32808. /* Syntax error */
  32809. rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'switch' keyword");
  32810. if( rc == SXERR_ABORT ){
  32811. /* Error count limit reached, abort immediately */
  32812. return SXERR_ABORT;
  32813. }
  32814. goto Synchronize;
  32815. }
  32816. /* Jump the left parenthesis '(' */
  32817. pGen->pIn++;
  32818. pEnd = 0; /* cc warning */
  32819. /* Create the loop block */
  32820. rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP|GEN_BLOCK_SWITCH,
  32821. jx9VmInstrLength(pGen->pVm), 0, &pSwitchBlock);
  32822. if( rc != SXRET_OK ){
  32823. return SXERR_ABORT;
  32824. }
  32825. /* Delimit the condition */
  32826. jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
  32827. if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
  32828. /* Empty expression */
  32829. rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected expression after 'switch' keyword");
  32830. if( rc == SXERR_ABORT ){
  32831. /* Error count limit reached, abort immediately */
  32832. return SXERR_ABORT;
  32833. }
  32834. }
  32835. /* Swap token streams */
  32836. pTmp = pGen->pEnd;
  32837. pGen->pEnd = pEnd;
  32838. /* Compile the expression */
  32839. rc = jx9CompileExpr(&(*pGen), 0, 0);
  32840. if( rc == SXERR_ABORT ){
  32841. /* Expression handler request an operation abort [i.e: Out-of-memory] */
  32842. return SXERR_ABORT;
  32843. }
  32844. /* Update token stream */
  32845. while(pGen->pIn < pEnd ){
  32846. rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine,
  32847. "Switch: Unexpected token '%z'", &pGen->pIn->sData);
  32848. if( rc == SXERR_ABORT ){
  32849. return SXERR_ABORT;
  32850. }
  32851. pGen->pIn++;
  32852. }
  32853. pGen->pIn = &pEnd[1];
  32854. pGen->pEnd = pTmp;
  32855. if( pGen->pIn >= pGen->pEnd || &pGen->pIn[1] >= pGen->pEnd ||
  32856. (pGen->pIn->nType & (JX9_TK_OCB/*'{'*/|JX9_TK_COLON/*:*/)) == 0 ){
  32857. pTmp = pGen->pIn;
  32858. if( pTmp >= pGen->pEnd ){
  32859. pTmp--;
  32860. }
  32861. /* Unexpected token */
  32862. rc = jx9GenCompileError(&(*pGen), E_ERROR, pTmp->nLine, "Switch: Unexpected token '%z'", &pTmp->sData);
  32863. if( rc == SXERR_ABORT ){
  32864. return SXERR_ABORT;
  32865. }
  32866. goto Synchronize;
  32867. }
  32868. pGen->pIn++; /* Jump the leading curly braces/colons */
  32869. /* Create the switch blocks container */
  32870. pSwitch = (jx9_switch *)SyMemBackendAlloc(&pGen->pVm->sAllocator, sizeof(jx9_switch));
  32871. if( pSwitch == 0 ){
  32872. /* Abort compilation */
  32873. return GenStateOutOfMem(pGen);
  32874. }
  32875. /* Zero the structure */
  32876. SyZero(pSwitch, sizeof(jx9_switch));
  32877. /* Initialize fields */
  32878. SySetInit(&pSwitch->aCaseExpr, &pGen->pVm->sAllocator, sizeof(jx9_case_expr));
  32879. /* Emit the switch instruction */
  32880. jx9VmEmitInstr(pGen->pVm, JX9_OP_SWITCH, 0, 0, pSwitch, 0);
  32881. /* Compile case blocks */
  32882. for(;;){
  32883. sxu32 nKwrd;
  32884. if( pGen->pIn >= pGen->pEnd ){
  32885. /* No more input to process */
  32886. break;
  32887. }
  32888. if( (pGen->pIn->nType & JX9_TK_KEYWORD) == 0 ){
  32889. if( (pGen->pIn->nType & JX9_TK_CCB /*}*/) == 0 ){
  32890. /* Unexpected token */
  32891. rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Switch: Unexpected token '%z'",
  32892. &pGen->pIn->sData);
  32893. if( rc == SXERR_ABORT ){
  32894. return SXERR_ABORT;
  32895. }
  32896. /* FALL THROUGH */
  32897. }
  32898. /* Block compiled */
  32899. break;
  32900. }
  32901. /* Extract the keyword */
  32902. nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
  32903. if( nKwrd == JX9_TKWRD_DEFAULT ){
  32904. /*
  32905. * Accroding to the JX9 language reference manual
  32906. * A special case is the default case. This case matches anything
  32907. * that wasn't matched by the other cases.
  32908. */
  32909. if( pSwitch->nDefault > 0 ){
  32910. /* Default case already compiled */
  32911. rc = jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Switch: 'default' case already compiled");
  32912. if( rc == SXERR_ABORT ){
  32913. return SXERR_ABORT;
  32914. }
  32915. }
  32916. pGen->pIn++; /* Jump the 'default' keyword */
  32917. /* Compile the default block */
  32918. rc = GenStateCompileSwitchBlock(pGen,&pSwitch->nDefault);
  32919. if( rc == SXERR_ABORT){
  32920. return SXERR_ABORT;
  32921. }else if( rc == SXERR_EOF ){
  32922. break;
  32923. }
  32924. }else if( nKwrd == JX9_TKWRD_CASE ){
  32925. jx9_case_expr sCase;
  32926. /* Standard case block */
  32927. pGen->pIn++; /* Jump the 'case' keyword */
  32928. /* initialize the structure */
  32929. SySetInit(&sCase.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
  32930. /* Compile the case expression */
  32931. rc = GenStateCompileCaseExpr(pGen, &sCase);
  32932. if( rc == SXERR_ABORT ){
  32933. return SXERR_ABORT;
  32934. }
  32935. /* Compile the case block */
  32936. rc = GenStateCompileSwitchBlock(pGen,&sCase.nStart);
  32937. /* Insert in the switch container */
  32938. SySetPut(&pSwitch->aCaseExpr, (const void *)&sCase);
  32939. if( rc == SXERR_ABORT){
  32940. return SXERR_ABORT;
  32941. }else if( rc == SXERR_EOF ){
  32942. break;
  32943. }
  32944. }else{
  32945. /* Unexpected token */
  32946. rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Switch: Unexpected token '%z'",
  32947. &pGen->pIn->sData);
  32948. if( rc == SXERR_ABORT ){
  32949. return SXERR_ABORT;
  32950. }
  32951. break;
  32952. }
  32953. }
  32954. /* Fix all jumps now the destination is resolved */
  32955. pSwitch->nOut = jx9VmInstrLength(pGen->pVm);
  32956. GenStateFixJumps(pSwitchBlock, -1, jx9VmInstrLength(pGen->pVm));
  32957. /* Release the loop block */
  32958. GenStateLeaveBlock(pGen, 0);
  32959. if( pGen->pIn < pGen->pEnd ){
  32960. /* Jump the trailing curly braces */
  32961. pGen->pIn++;
  32962. }
  32963. /* Statement successfully compiled */
  32964. return SXRET_OK;
  32965. Synchronize:
  32966. /* Synchronize with the first semi-colon */
  32967. while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
  32968. pGen->pIn++;
  32969. }
  32970. return SXRET_OK;
  32971. }
  32972. /*
  32973. * Process default argument values. That is, a function may define C++-style default value
  32974. * as follows:
  32975. * function makecoffee($type = "cappuccino")
  32976. * {
  32977. * return "Making a cup of $type.\n";
  32978. * }
  32979. * Some features:
  32980. * 1 -) Default arguments value can be any complex expression [i.e: function call, annynoymous
  32981. * functions, array member, ..]
  32982. * 2 -) Full type hinting: (Arguments are automatically casted to the desired type)
  32983. * Example:
  32984. * function a(string $a){} function b(int $a, string $c, float $d){}
  32985. * 3 -) Function overloading!!
  32986. * Example:
  32987. * function foo($a) {
  32988. * return $a.JX9_EOL;
  32989. * }
  32990. * function foo($a, $b) {
  32991. * return $a + $b;
  32992. * }
  32993. * print foo(5); // Prints "5"
  32994. * print foo(5, 2); // Prints "7"
  32995. * // Same arg
  32996. * function foo(string $a)
  32997. * {
  32998. * print "a is a string\n";
  32999. * dump($a);
  33000. * }
  33001. * function foo(int $a)
  33002. * {
  33003. * print "a is integer\n";
  33004. * dump($a);
  33005. * }
  33006. * function foo(array $a)
  33007. * {
  33008. * print "a is an array\n";
  33009. * dump($a);
  33010. * }
  33011. * foo('This is a great feature'); // a is a string [first foo]
  33012. * foo(52); // a is integer [second foo]
  33013. * foo(array(14, __TIME__, __DATE__)); // a is an array [third foo]
  33014. * Please refer to the official documentation for more information on the powerful extension
  33015. * introduced by the JX9 engine.
  33016. */
  33017. static sxi32 GenStateProcessArgValue(jx9_gen_state *pGen, jx9_vm_func_arg *pArg, SyToken *pIn, SyToken *pEnd)
  33018. {
  33019. SyToken *pTmpIn, *pTmpEnd;
  33020. SySet *pInstrContainer;
  33021. sxi32 rc;
  33022. /* Swap token stream */
  33023. SWAP_DELIMITER(pGen, pIn, pEnd);
  33024. pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
  33025. jx9VmSetByteCodeContainer(pGen->pVm, &pArg->aByteCode);
  33026. /* Compile the expression holding the argument value */
  33027. rc = jx9CompileExpr(&(*pGen), 0, 0);
  33028. /* Emit the done instruction */
  33029. jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
  33030. jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
  33031. RE_SWAP_DELIMITER(pGen);
  33032. if( rc == SXERR_ABORT ){
  33033. return SXERR_ABORT;
  33034. }
  33035. return SXRET_OK;
  33036. }
  33037. /*
  33038. * Collect function arguments one after one.
  33039. * According to the JX9 language reference manual.
  33040. * Information may be passed to functions via the argument list, which is a comma-delimited
  33041. * list of expressions.
  33042. * JX9 supports passing arguments by value (the default), passing by reference
  33043. * and default argument values. Variable-length argument lists are also supported,
  33044. * see also the function references for func_num_args(), func_get_arg(), and func_get_args()
  33045. * for more information.
  33046. * Example #1 Passing arrays to functions
  33047. * <?jx9
  33048. * function takes_array($input)
  33049. * {
  33050. * print "$input[0] + $input[1] = ", $input[0]+$input[1];
  33051. * }
  33052. * ?>
  33053. * Making arguments be passed by reference
  33054. * By default, function arguments are passed by value (so that if the value of the argument
  33055. * within the function is changed, it does not get changed outside of the function).
  33056. * To allow a function to modify its arguments, they must be passed by reference.
  33057. * To have an argument to a function always passed by reference, prepend an ampersand (&)
  33058. * to the argument name in the function definition:
  33059. * Example #2 Passing function parameters by reference
  33060. * <?jx9
  33061. * function add_some_extra(&$string)
  33062. * {
  33063. * $string .= 'and something extra.';
  33064. * }
  33065. * $str = 'This is a string, ';
  33066. * add_some_extra($str);
  33067. * print $str; // outputs 'This is a string, and something extra.'
  33068. * ?>
  33069. *
  33070. * JX9 have introduced powerful extension including full type hinting, function overloading
  33071. * complex agrument values.Please refer to the official documentation for more information
  33072. * on these extension.
  33073. */
  33074. static sxi32 GenStateCollectFuncArgs(jx9_vm_func *pFunc, jx9_gen_state *pGen, SyToken *pEnd)
  33075. {
  33076. jx9_vm_func_arg sArg; /* Current processed argument */
  33077. SyToken *pCur, *pIn; /* Token stream */
  33078. SyBlob sSig; /* Function signature */
  33079. char *zDup; /* Copy of argument name */
  33080. sxi32 rc;
  33081. pIn = pGen->pIn;
  33082. pCur = 0;
  33083. SyBlobInit(&sSig, &pGen->pVm->sAllocator);
  33084. /* Process arguments one after one */
  33085. for(;;){
  33086. if( pIn >= pEnd ){
  33087. /* No more arguments to process */
  33088. break;
  33089. }
  33090. SyZero(&sArg, sizeof(jx9_vm_func_arg));
  33091. SySetInit(&sArg.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
  33092. if( pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD) ){
  33093. if( pIn->nType & JX9_TK_KEYWORD ){
  33094. sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pIn->pUserData));
  33095. if( nKey & JX9_TKWRD_BOOL ){
  33096. sArg.nType = MEMOBJ_BOOL;
  33097. }else if( nKey & JX9_TKWRD_INT ){
  33098. sArg.nType = MEMOBJ_INT;
  33099. }else if( nKey & JX9_TKWRD_STRING ){
  33100. sArg.nType = MEMOBJ_STRING;
  33101. }else if( nKey & JX9_TKWRD_FLOAT ){
  33102. sArg.nType = MEMOBJ_REAL;
  33103. }else{
  33104. jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine,
  33105. "Invalid argument type '%z', Automatic cast will not be performed",
  33106. &pIn->sData);
  33107. }
  33108. }
  33109. pIn++;
  33110. }
  33111. if( pIn >= pEnd ){
  33112. rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Missing argument name");
  33113. return rc;
  33114. }
  33115. if( pIn >= pEnd || (pIn->nType & JX9_TK_DOLLAR) == 0 || &pIn[1] >= pEnd || (pIn[1].nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
  33116. /* Invalid argument */
  33117. rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Invalid argument name");
  33118. return rc;
  33119. }
  33120. pIn++; /* Jump the dollar sign */
  33121. /* Copy argument name */
  33122. zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, SyStringData(&pIn->sData), SyStringLength(&pIn->sData));
  33123. if( zDup == 0 ){
  33124. return GenStateOutOfMem(pGen);
  33125. }
  33126. SyStringInitFromBuf(&sArg.sName, zDup, SyStringLength(&pIn->sData));
  33127. pIn++;
  33128. if( pIn < pEnd ){
  33129. if( pIn->nType & JX9_TK_EQUAL ){
  33130. SyToken *pDefend;
  33131. sxi32 iNest = 0;
  33132. pIn++; /* Jump the equal sign */
  33133. pDefend = pIn;
  33134. /* Process the default value associated with this argument */
  33135. while( pDefend < pEnd ){
  33136. if( (pDefend->nType & JX9_TK_COMMA) && iNest <= 0 ){
  33137. break;
  33138. }
  33139. if( pDefend->nType & (JX9_TK_LPAREN/*'('*/|JX9_TK_OCB/*'{'*/|JX9_TK_OSB/*[*/) ){
  33140. /* Increment nesting level */
  33141. iNest++;
  33142. }else if( pDefend->nType & (JX9_TK_RPAREN/*')'*/|JX9_TK_CCB/*'}'*/|JX9_TK_CSB/*]*/) ){
  33143. /* Decrement nesting level */
  33144. iNest--;
  33145. }
  33146. pDefend++;
  33147. }
  33148. if( pIn >= pDefend ){
  33149. rc = jx9GenCompileError(&(*pGen), E_ERROR, pIn->nLine, "Missing argument default value");
  33150. return rc;
  33151. }
  33152. /* Process default value */
  33153. rc = GenStateProcessArgValue(&(*pGen), &sArg, pIn, pDefend);
  33154. if( rc != SXRET_OK ){
  33155. return rc;
  33156. }
  33157. /* Point beyond the default value */
  33158. pIn = pDefend;
  33159. }
  33160. if( pIn < pEnd && (pIn->nType & JX9_TK_COMMA) == 0 ){
  33161. rc = jx9GenCompileError(&(*pGen), E_ERROR, pIn->nLine, "Unexpected token '%z'", &pIn->sData);
  33162. return rc;
  33163. }
  33164. pIn++; /* Jump the trailing comma */
  33165. }
  33166. /* Append argument signature */
  33167. if( sArg.nType > 0 ){
  33168. int c;
  33169. c = 'n'; /* cc warning */
  33170. /* Type leading character */
  33171. switch(sArg.nType){
  33172. case MEMOBJ_HASHMAP:
  33173. /* Hashmap aka 'array' */
  33174. c = 'h';
  33175. break;
  33176. case MEMOBJ_INT:
  33177. /* Integer */
  33178. c = 'i';
  33179. break;
  33180. case MEMOBJ_BOOL:
  33181. /* Bool */
  33182. c = 'b';
  33183. break;
  33184. case MEMOBJ_REAL:
  33185. /* Float */
  33186. c = 'f';
  33187. break;
  33188. case MEMOBJ_STRING:
  33189. /* String */
  33190. c = 's';
  33191. break;
  33192. default:
  33193. break;
  33194. }
  33195. SyBlobAppend(&sSig, (const void *)&c, sizeof(char));
  33196. }
  33197. /* Save in the argument set */
  33198. SySetPut(&pFunc->aArgs, (const void *)&sArg);
  33199. }
  33200. if( SyBlobLength(&sSig) > 0 ){
  33201. /* Save function signature */
  33202. SyStringInitFromBuf(&pFunc->sSignature, SyBlobData(&sSig), SyBlobLength(&sSig));
  33203. }
  33204. return SXRET_OK;
  33205. }
  33206. /*
  33207. * Compile function [i.e: standard function, annonymous function or closure ] body.
  33208. * Return SXRET_OK on success. Any other return value indicates failure
  33209. * and this routine takes care of generating the appropriate error message.
  33210. */
  33211. static sxi32 GenStateCompileFuncBody(
  33212. jx9_gen_state *pGen, /* Code generator state */
  33213. jx9_vm_func *pFunc /* Function state */
  33214. )
  33215. {
  33216. SySet *pInstrContainer; /* Instruction container */
  33217. GenBlock *pBlock;
  33218. sxi32 rc;
  33219. /* Attach the new function */
  33220. rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_PROTECTED|GEN_BLOCK_FUNC,jx9VmInstrLength(pGen->pVm), pFunc, &pBlock);
  33221. if( rc != SXRET_OK ){
  33222. return GenStateOutOfMem(pGen);
  33223. }
  33224. /* Swap bytecode containers */
  33225. pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
  33226. jx9VmSetByteCodeContainer(pGen->pVm, &pFunc->aByteCode);
  33227. /* Compile the body */
  33228. jx9CompileBlock(&(*pGen));
  33229. /* Emit the final return if not yet done */
  33230. jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, 0, 0, 0, 0);
  33231. /* Restore the default container */
  33232. jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
  33233. /* Leave function block */
  33234. GenStateLeaveBlock(&(*pGen), 0);
  33235. if( rc == SXERR_ABORT ){
  33236. /* Don't worry about freeing memory, everything will be released shortly */
  33237. return SXERR_ABORT;
  33238. }
  33239. /* All done, function body compiled */
  33240. return SXRET_OK;
  33241. }
  33242. /*
  33243. * Compile a JX9 function whether is a Standard or Annonymous function.
  33244. * According to the JX9 language reference manual.
  33245. * Function names follow the same rules as other labels in JX9. A valid function name
  33246. * starts with a letter or underscore, followed by any number of letters, numbers, or
  33247. * underscores. As a regular expression, it would be expressed thus:
  33248. * [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*.
  33249. * Functions need not be defined before they are referenced.
  33250. * All functions and objectes in JX9 have the global scope - they can be called outside
  33251. * a function even if they were defined inside and vice versa.
  33252. * It is possible to call recursive functions in JX9. However avoid recursive function/method
  33253. * calls with over 32-64 recursion levels.
  33254. *
  33255. * JX9 have introduced powerful extension including full type hinting, function overloading,
  33256. * complex agrument values and more. Please refer to the official documentation for more information
  33257. * on these extension.
  33258. */
  33259. static sxi32 GenStateCompileFunc(
  33260. jx9_gen_state *pGen, /* Code generator state */
  33261. SyString *pName, /* Function name. NULL otherwise */
  33262. sxi32 iFlags, /* Control flags */
  33263. jx9_vm_func **ppFunc /* OUT: function state */
  33264. )
  33265. {
  33266. jx9_vm_func *pFunc;
  33267. SyToken *pEnd;
  33268. sxu32 nLine;
  33269. char *zName;
  33270. sxi32 rc;
  33271. /* Extract line number */
  33272. nLine = pGen->pIn->nLine;
  33273. /* Jump the left parenthesis '(' */
  33274. pGen->pIn++;
  33275. /* Delimit the function signature */
  33276. jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
  33277. if( pEnd >= pGen->pEnd ){
  33278. /* Syntax error */
  33279. rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Missing ')' after function '%z' signature", pName);
  33280. if( rc == SXERR_ABORT ){
  33281. /* Error count limit reached, abort immediately */
  33282. return SXERR_ABORT;
  33283. }
  33284. pGen->pIn = pGen->pEnd;
  33285. return SXRET_OK;
  33286. }
  33287. /* Create the function state */
  33288. pFunc = (jx9_vm_func *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(jx9_vm_func));
  33289. if( pFunc == 0 ){
  33290. goto OutOfMem;
  33291. }
  33292. /* function ID */
  33293. zName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
  33294. if( zName == 0 ){
  33295. /* Don't worry about freeing memory, everything will be released shortly */
  33296. goto OutOfMem;
  33297. }
  33298. /* Initialize the function state */
  33299. jx9VmInitFuncState(pGen->pVm, pFunc, zName, pName->nByte, iFlags, 0);
  33300. if( pGen->pIn < pEnd ){
  33301. /* Collect function arguments */
  33302. rc = GenStateCollectFuncArgs(pFunc, &(*pGen), pEnd);
  33303. if( rc == SXERR_ABORT ){
  33304. /* Don't worry about freeing memory, everything will be released shortly */
  33305. return SXERR_ABORT;
  33306. }
  33307. }
  33308. /* Compile function body */
  33309. pGen->pIn = &pEnd[1];
  33310. /* Compile the body */
  33311. rc = GenStateCompileFuncBody(&(*pGen), pFunc);
  33312. if( rc == SXERR_ABORT ){
  33313. return SXERR_ABORT;
  33314. }
  33315. if( ppFunc ){
  33316. *ppFunc = pFunc;
  33317. }
  33318. /* Finally register the function */
  33319. rc = jx9VmInstallUserFunction(pGen->pVm, pFunc, 0);
  33320. return rc;
  33321. /* Fall through if something goes wrong */
  33322. OutOfMem:
  33323. /* If the supplied memory subsystem is so sick that we are unable to allocate
  33324. * a tiny chunk of memory, there is no much we can do here.
  33325. */
  33326. return GenStateOutOfMem(pGen);
  33327. }
  33328. /*
  33329. * Compile a standard JX9 function.
  33330. * Refer to the block-comment above for more information.
  33331. */
  33332. static sxi32 jx9CompileFunction(jx9_gen_state *pGen)
  33333. {
  33334. SyString *pName;
  33335. sxi32 iFlags;
  33336. sxu32 nLine;
  33337. sxi32 rc;
  33338. nLine = pGen->pIn->nLine;
  33339. pGen->pIn++; /* Jump the 'function' keyword */
  33340. iFlags = 0;
  33341. if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
  33342. /* Invalid function name */
  33343. rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Invalid function name");
  33344. if( rc == SXERR_ABORT ){
  33345. return SXERR_ABORT;
  33346. }
  33347. /* Sychronize with the next semi-colon or braces*/
  33348. while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
  33349. pGen->pIn++;
  33350. }
  33351. return SXRET_OK;
  33352. }
  33353. pName = &pGen->pIn->sData;
  33354. nLine = pGen->pIn->nLine;
  33355. /* Jump the function name */
  33356. pGen->pIn++;
  33357. if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
  33358. /* Syntax error */
  33359. rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after function name '%z'", pName);
  33360. if( rc == SXERR_ABORT ){
  33361. /* Error count limit reached, abort immediately */
  33362. return SXERR_ABORT;
  33363. }
  33364. /* Sychronize with the next semi-colon or '{' */
  33365. while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
  33366. pGen->pIn++;
  33367. }
  33368. return SXRET_OK;
  33369. }
  33370. /* Compile function body */
  33371. rc = GenStateCompileFunc(&(*pGen),pName,iFlags,0);
  33372. return rc;
  33373. }
  33374. /*
  33375. * Generate bytecode for a given expression tree.
  33376. * If something goes wrong while generating bytecode
  33377. * for the expression tree (A very unlikely scenario)
  33378. * this function takes care of generating the appropriate
  33379. * error message.
  33380. */
  33381. static sxi32 GenStateEmitExprCode(
  33382. jx9_gen_state *pGen, /* Code generator state */
  33383. jx9_expr_node *pNode, /* Root of the expression tree */
  33384. sxi32 iFlags /* Control flags */
  33385. )
  33386. {
  33387. VmInstr *pInstr;
  33388. sxu32 nJmpIdx;
  33389. sxi32 iP1 = 0;
  33390. sxu32 iP2 = 0;
  33391. void *p3 = 0;
  33392. sxi32 iVmOp;
  33393. sxi32 rc;
  33394. if( pNode->xCode ){
  33395. SyToken *pTmpIn, *pTmpEnd;
  33396. /* Compile node */
  33397. SWAP_DELIMITER(pGen, pNode->pStart, pNode->pEnd);
  33398. rc = pNode->xCode(&(*pGen), iFlags);
  33399. RE_SWAP_DELIMITER(pGen);
  33400. return rc;
  33401. }
  33402. if( pNode->pOp == 0 ){
  33403. jx9GenCompileError(&(*pGen), E_ERROR, pNode->pStart->nLine,
  33404. "Invalid expression node, JX9 is aborting compilation");
  33405. return SXERR_ABORT;
  33406. }
  33407. iVmOp = pNode->pOp->iVmOp;
  33408. if( pNode->pOp->iOp == EXPR_OP_QUESTY ){
  33409. sxu32 nJz, nJmp;
  33410. /* Ternary operator require special handling */
  33411. /* Phase#1: Compile the condition */
  33412. rc = GenStateEmitExprCode(&(*pGen), pNode->pCond, iFlags);
  33413. if( rc != SXRET_OK ){
  33414. return rc;
  33415. }
  33416. nJz = nJmp = 0; /* cc -O6 warning */
  33417. /* Phase#2: Emit the false jump */
  33418. jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nJz);
  33419. if( pNode->pLeft ){
  33420. /* Phase#3: Compile the 'then' expression */
  33421. rc = GenStateEmitExprCode(&(*pGen), pNode->pLeft, iFlags);
  33422. if( rc != SXRET_OK ){
  33423. return rc;
  33424. }
  33425. }
  33426. /* Phase#4: Emit the unconditional jump */
  33427. jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, 0, 0, &nJmp);
  33428. /* Phase#5: Fix the false jump now the jump destination is resolved. */
  33429. pInstr = jx9VmGetInstr(pGen->pVm, nJz);
  33430. if( pInstr ){
  33431. pInstr->iP2 = jx9VmInstrLength(pGen->pVm);
  33432. }
  33433. /* Phase#6: Compile the 'else' expression */
  33434. if( pNode->pRight ){
  33435. rc = GenStateEmitExprCode(&(*pGen), pNode->pRight, iFlags);
  33436. if( rc != SXRET_OK ){
  33437. return rc;
  33438. }
  33439. }
  33440. if( nJmp > 0 ){
  33441. /* Phase#7: Fix the unconditional jump */
  33442. pInstr = jx9VmGetInstr(pGen->pVm, nJmp);
  33443. if( pInstr ){
  33444. pInstr->iP2 = jx9VmInstrLength(pGen->pVm);
  33445. }
  33446. }
  33447. /* All done */
  33448. return SXRET_OK;
  33449. }
  33450. /* Generate code for the left tree */
  33451. if( pNode->pLeft ){
  33452. if( iVmOp == JX9_OP_CALL ){
  33453. jx9_expr_node **apNode;
  33454. sxi32 n;
  33455. /* Recurse and generate bytecodes for function arguments */
  33456. apNode = (jx9_expr_node **)SySetBasePtr(&pNode->aNodeArgs);
  33457. /* Read-only load */
  33458. iFlags |= EXPR_FLAG_RDONLY_LOAD;
  33459. for( n = 0 ; n < (sxi32)SySetUsed(&pNode->aNodeArgs) ; ++n ){
  33460. rc = GenStateEmitExprCode(&(*pGen), apNode[n], iFlags&~EXPR_FLAG_LOAD_IDX_STORE);
  33461. if( rc != SXRET_OK ){
  33462. return rc;
  33463. }
  33464. }
  33465. /* Total number of given arguments */
  33466. iP1 = (sxi32)SySetUsed(&pNode->aNodeArgs);
  33467. /* Remove stale flags now */
  33468. iFlags &= ~EXPR_FLAG_RDONLY_LOAD;
  33469. }
  33470. rc = GenStateEmitExprCode(&(*pGen), pNode->pLeft, iFlags);
  33471. if( rc != SXRET_OK ){
  33472. return rc;
  33473. }
  33474. if( iVmOp == JX9_OP_CALL ){
  33475. pInstr = jx9VmPeekInstr(pGen->pVm);
  33476. if( pInstr ){
  33477. if ( pInstr->iOp == JX9_OP_LOADC ){
  33478. /* Prevent constant expansion */
  33479. pInstr->iP1 = 0;
  33480. }else if( pInstr->iOp == JX9_OP_MEMBER /* $a.b(1, 2, 3) */ ){
  33481. /* Annonymous function call, flag that */
  33482. pInstr->iP2 = 1;
  33483. }
  33484. }
  33485. }else if( iVmOp == JX9_OP_LOAD_IDX ){
  33486. jx9_expr_node **apNode;
  33487. sxi32 n;
  33488. /* Recurse and generate bytecodes for array index */
  33489. apNode = (jx9_expr_node **)SySetBasePtr(&pNode->aNodeArgs);
  33490. for( n = 0 ; n < (sxi32)SySetUsed(&pNode->aNodeArgs) ; ++n ){
  33491. rc = GenStateEmitExprCode(&(*pGen), apNode[n], iFlags&~EXPR_FLAG_LOAD_IDX_STORE);
  33492. if( rc != SXRET_OK ){
  33493. return rc;
  33494. }
  33495. }
  33496. if( SySetUsed(&pNode->aNodeArgs) > 0 ){
  33497. iP1 = 1; /* Node have an index associated with it */
  33498. }
  33499. if( iFlags & EXPR_FLAG_LOAD_IDX_STORE ){
  33500. /* Create an empty entry when the desired index is not found */
  33501. iP2 = 1;
  33502. }
  33503. }else if( pNode->pOp->iOp == EXPR_OP_COMMA ){
  33504. /* POP the left node */
  33505. jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
  33506. }
  33507. }
  33508. rc = SXRET_OK;
  33509. nJmpIdx = 0;
  33510. /* Generate code for the right tree */
  33511. if( pNode->pRight ){
  33512. if( iVmOp == JX9_OP_LAND ){
  33513. /* Emit the false jump so we can short-circuit the logical and */
  33514. jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 1/* Keep the value on the stack */, 0, 0, &nJmpIdx);
  33515. }else if (iVmOp == JX9_OP_LOR ){
  33516. /* Emit the true jump so we can short-circuit the logical or*/
  33517. jx9VmEmitInstr(pGen->pVm, JX9_OP_JNZ, 1/* Keep the value on the stack */, 0, 0, &nJmpIdx);
  33518. }else if( pNode->pOp->iPrec == 18 /* Combined binary operators [i.e: =, '.=', '+=', *=' ...] precedence */ ){
  33519. iFlags |= EXPR_FLAG_LOAD_IDX_STORE;
  33520. }
  33521. rc = GenStateEmitExprCode(&(*pGen), pNode->pRight, iFlags);
  33522. if( iVmOp == JX9_OP_STORE ){
  33523. pInstr = jx9VmPeekInstr(pGen->pVm);
  33524. if( pInstr ){
  33525. if(pInstr->iOp == JX9_OP_MEMBER ){
  33526. /* Perform a member store operation [i.e: $this.x = 50] */
  33527. iP2 = 1;
  33528. }else{
  33529. if( pInstr->iOp == JX9_OP_LOAD_IDX ){
  33530. /* Transform the STORE instruction to STORE_IDX instruction */
  33531. iVmOp = JX9_OP_STORE_IDX;
  33532. iP1 = pInstr->iP1;
  33533. }else{
  33534. p3 = pInstr->p3;
  33535. }
  33536. /* POP the last dynamic load instruction */
  33537. (void)jx9VmPopInstr(pGen->pVm);
  33538. }
  33539. }
  33540. }
  33541. }
  33542. if( iVmOp > 0 ){
  33543. if( iVmOp == JX9_OP_INCR || iVmOp == JX9_OP_DECR ){
  33544. if( pNode->iFlags & EXPR_NODE_PRE_INCR ){
  33545. /* Pre-increment/decrement operator [i.e: ++$i, --$j ] */
  33546. iP1 = 1;
  33547. }
  33548. }
  33549. /* Finally, emit the VM instruction associated with this operator */
  33550. jx9VmEmitInstr(pGen->pVm, iVmOp, iP1, iP2, p3, 0);
  33551. if( nJmpIdx > 0 ){
  33552. /* Fix short-circuited jumps now the destination is resolved */
  33553. pInstr = jx9VmGetInstr(pGen->pVm, nJmpIdx);
  33554. if( pInstr ){
  33555. pInstr->iP2 = jx9VmInstrLength(pGen->pVm);
  33556. }
  33557. }
  33558. }
  33559. return rc;
  33560. }
  33561. /*
  33562. * Compile a JX9 expression.
  33563. * According to the JX9 language reference manual:
  33564. * Expressions are the most important building stones of JX9.
  33565. * In JX9, almost anything you write is an expression.
  33566. * The simplest yet most accurate way to define an expression
  33567. * is "anything that has a value".
  33568. * If something goes wrong while compiling the expression, this
  33569. * function takes care of generating the appropriate error
  33570. * message.
  33571. */
  33572. static sxi32 jx9CompileExpr(
  33573. jx9_gen_state *pGen, /* Code generator state */
  33574. sxi32 iFlags, /* Control flags */
  33575. sxi32 (*xTreeValidator)(jx9_gen_state *, jx9_expr_node *) /* Node validator callback.NULL otherwise */
  33576. )
  33577. {
  33578. jx9_expr_node *pRoot;
  33579. SySet sExprNode;
  33580. SyToken *pEnd;
  33581. sxi32 nExpr;
  33582. sxi32 iNest;
  33583. sxi32 rc;
  33584. /* Initialize worker variables */
  33585. nExpr = 0;
  33586. pRoot = 0;
  33587. SySetInit(&sExprNode, &pGen->pVm->sAllocator, sizeof(jx9_expr_node *));
  33588. SySetAlloc(&sExprNode, 0x10);
  33589. rc = SXRET_OK;
  33590. /* Delimit the expression */
  33591. pEnd = pGen->pIn;
  33592. iNest = 0;
  33593. while( pEnd < pGen->pEnd ){
  33594. if( pEnd->nType & JX9_TK_OCB /* '{' */ ){
  33595. /* Ticket 1433-30: Annonymous/Closure functions body */
  33596. iNest++;
  33597. }else if(pEnd->nType & JX9_TK_CCB /* '}' */ ){
  33598. iNest--;
  33599. }else if( pEnd->nType & JX9_TK_SEMI /* ';' */ ){
  33600. if( iNest <= 0 ){
  33601. break;
  33602. }
  33603. }
  33604. pEnd++;
  33605. }
  33606. if( iFlags & EXPR_FLAG_COMMA_STATEMENT ){
  33607. SyToken *pEnd2 = pGen->pIn;
  33608. iNest = 0;
  33609. /* Stop at the first comma */
  33610. while( pEnd2 < pEnd ){
  33611. if( pEnd2->nType & (JX9_TK_OCB/*'{'*/|JX9_TK_OSB/*'['*/|JX9_TK_LPAREN/*'('*/) ){
  33612. iNest++;
  33613. }else if(pEnd2->nType & (JX9_TK_CCB/*'}'*/|JX9_TK_CSB/*']'*/|JX9_TK_RPAREN/*')'*/)){
  33614. iNest--;
  33615. }else if( pEnd2->nType & JX9_TK_COMMA /*','*/ ){
  33616. if( iNest <= 0 ){
  33617. break;
  33618. }
  33619. }
  33620. pEnd2++;
  33621. }
  33622. if( pEnd2 <pEnd ){
  33623. pEnd = pEnd2;
  33624. }
  33625. }
  33626. if( pEnd > pGen->pIn ){
  33627. SyToken *pTmp = pGen->pEnd;
  33628. /* Swap delimiter */
  33629. pGen->pEnd = pEnd;
  33630. /* Try to get an expression tree */
  33631. rc = jx9ExprMakeTree(&(*pGen), &sExprNode, &pRoot);
  33632. if( rc == SXRET_OK && pRoot ){
  33633. rc = SXRET_OK;
  33634. if( xTreeValidator ){
  33635. /* Call the upper layer validator callback */
  33636. rc = xTreeValidator(&(*pGen), pRoot);
  33637. }
  33638. if( rc != SXERR_ABORT ){
  33639. /* Generate code for the given tree */
  33640. rc = GenStateEmitExprCode(&(*pGen), pRoot, iFlags);
  33641. }
  33642. nExpr = 1;
  33643. }
  33644. /* Release the whole tree */
  33645. jx9ExprFreeTree(&(*pGen), &sExprNode);
  33646. /* Synchronize token stream */
  33647. pGen->pEnd = pTmp;
  33648. pGen->pIn = pEnd;
  33649. if( rc == SXERR_ABORT ){
  33650. SySetRelease(&sExprNode);
  33651. return SXERR_ABORT;
  33652. }
  33653. }
  33654. SySetRelease(&sExprNode);
  33655. return nExpr > 0 ? SXRET_OK : SXERR_EMPTY;
  33656. }
  33657. /*
  33658. * Return a pointer to the node construct handler associated
  33659. * with a given node type [i.e: string, integer, float, ...].
  33660. */
  33661. JX9_PRIVATE ProcNodeConstruct jx9GetNodeHandler(sxu32 nNodeType)
  33662. {
  33663. if( nNodeType & JX9_TK_NUM ){
  33664. /* Numeric literal: Either real or integer */
  33665. return jx9CompileNumLiteral;
  33666. }else if( nNodeType & JX9_TK_DSTR ){
  33667. /* Double quoted string */
  33668. return jx9CompileString;
  33669. }else if( nNodeType & JX9_TK_SSTR ){
  33670. /* Single quoted string */
  33671. return jx9CompileSimpleString;
  33672. }else if( nNodeType & JX9_TK_NOWDOC ){
  33673. /* Nowdoc */
  33674. return jx9CompileNowdoc;
  33675. }
  33676. return 0;
  33677. }
  33678. /*
  33679. * Jx9 Language construct table.
  33680. */
  33681. static const LangConstruct aLangConstruct[] = {
  33682. { JX9_TKWRD_IF, jx9CompileIf },
  33683. { JX9_TKWRD_FUNCTION, jx9CompileFunction },
  33684. { JX9_TKWRD_FOREACH, jx9CompileForeach },
  33685. { JX9_TKWRD_WHILE, jx9CompileWhile },
  33686. { JX9_TKWRD_FOR, jx9CompileFor },
  33687. { JX9_TKWRD_SWITCH, jx9CompileSwitch },
  33688. { JX9_TKWRD_DIE, jx9CompileHalt },
  33689. { JX9_TKWRD_EXIT, jx9CompileHalt },
  33690. { JX9_TKWRD_RETURN, jx9CompileReturn },
  33691. { JX9_TKWRD_BREAK, jx9CompileBreak },
  33692. { JX9_TKWRD_CONTINUE, jx9CompileContinue },
  33693. { JX9_TKWRD_STATIC, jx9CompileStatic },
  33694. { JX9_TKWRD_UPLINK, jx9CompileUplink },
  33695. { JX9_TKWRD_CONST, jx9CompileConstant },
  33696. };
  33697. /*
  33698. * Return a pointer to the statement handler routine associated
  33699. * with a given JX9 keyword [i.e: if, for, while, ...].
  33700. */
  33701. static ProcLangConstruct GenStateGetStatementHandler(
  33702. sxu32 nKeywordID /* Keyword ID*/
  33703. )
  33704. {
  33705. sxu32 n = 0;
  33706. for(;;){
  33707. if( n >= SX_ARRAYSIZE(aLangConstruct) ){
  33708. break;
  33709. }
  33710. if( aLangConstruct[n].nID == nKeywordID ){
  33711. /* Return a pointer to the handler.
  33712. */
  33713. return aLangConstruct[n].xConstruct;
  33714. }
  33715. n++;
  33716. }
  33717. /* Not a language construct */
  33718. return 0;
  33719. }
  33720. /*
  33721. * Compile a jx9 program.
  33722. * If something goes wrong while compiling the unQL chunk, this function
  33723. * takes care of generating the appropriate error message.
  33724. */
  33725. static sxi32 GenStateCompileChunk(
  33726. jx9_gen_state *pGen, /* Code generator state */
  33727. sxi32 iFlags /* Compile flags */
  33728. )
  33729. {
  33730. ProcLangConstruct xCons;
  33731. sxi32 rc;
  33732. rc = SXRET_OK; /* Prevent compiler warning */
  33733. for(;;){
  33734. if( pGen->pIn >= pGen->pEnd ){
  33735. /* No more input to process */
  33736. break;
  33737. }
  33738. xCons = 0;
  33739. if( pGen->pIn->nType & JX9_TK_KEYWORD ){
  33740. sxu32 nKeyword = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData);
  33741. /* Try to extract a language construct handler */
  33742. xCons = GenStateGetStatementHandler(nKeyword);
  33743. if( xCons == 0 && !jx9IsLangConstruct(nKeyword) ){
  33744. rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
  33745. "Syntax error: Unexpected keyword '%z'",
  33746. &pGen->pIn->sData);
  33747. if( rc == SXERR_ABORT ){
  33748. break;
  33749. }
  33750. /* Synchronize with the first semi-colon and avoid compiling
  33751. * this erroneous statement.
  33752. */
  33753. xCons = jx9ErrorRecover;
  33754. }
  33755. }
  33756. if( xCons == 0 ){
  33757. /* Assume an expression an try to compile it */
  33758. rc = jx9CompileExpr(&(*pGen), 0, 0);
  33759. if( rc != SXERR_EMPTY ){
  33760. /* Pop l-value */
  33761. jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
  33762. }
  33763. }else{
  33764. /* Go compile the sucker */
  33765. rc = xCons(&(*pGen));
  33766. }
  33767. if( rc == SXERR_ABORT ){
  33768. /* Request to abort compilation */
  33769. break;
  33770. }
  33771. /* Ignore trailing semi-colons ';' */
  33772. while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) ){
  33773. pGen->pIn++;
  33774. }
  33775. if( iFlags & JX9_COMPILE_SINGLE_STMT ){
  33776. /* Compile a single statement and return */
  33777. break;
  33778. }
  33779. /* LOOP ONE */
  33780. /* LOOP TWO */
  33781. /* LOOP THREE */
  33782. /* LOOP FOUR */
  33783. }
  33784. /* Return compilation status */
  33785. return rc;
  33786. }
  33787. /*
  33788. * Compile a raw chunk. The raw chunk can contain JX9 code embedded
  33789. * in HTML, XML and so on. This function handle all the stuff.
  33790. * This is the only compile interface exported from this file.
  33791. */
  33792. JX9_PRIVATE sxi32 jx9CompileScript(
  33793. jx9_vm *pVm, /* Generate JX9 bytecodes for this Virtual Machine */
  33794. SyString *pScript, /* Script to compile */
  33795. sxi32 iFlags /* Compile flags */
  33796. )
  33797. {
  33798. jx9_gen_state *pGen;
  33799. SySet aToken;
  33800. sxi32 rc;
  33801. if( pScript->nByte < 1 ){
  33802. /* Nothing to compile */
  33803. return JX9_OK;
  33804. }
  33805. /* Initialize the tokens containers */
  33806. SySetInit(&aToken, &pVm->sAllocator, sizeof(SyToken));
  33807. SySetAlloc(&aToken, 0xc0);
  33808. pGen = &pVm->sCodeGen;
  33809. rc = JX9_OK;
  33810. /* Tokenize the JX9 chunk first */
  33811. jx9Tokenize(pScript->zString,pScript->nByte,&aToken);
  33812. if( SySetUsed(&aToken) < 1 ){
  33813. return SXERR_EMPTY;
  33814. }
  33815. /* Point to the head and tail of the token stream. */
  33816. pGen->pIn = (SyToken *)SySetBasePtr(&aToken);
  33817. pGen->pEnd = &pGen->pIn[SySetUsed(&aToken)];
  33818. /* Compile the chunk */
  33819. rc = GenStateCompileChunk(pGen,iFlags);
  33820. /* Cleanup */
  33821. SySetRelease(&aToken);
  33822. return rc;
  33823. }
  33824. /*
  33825. * Utility routines.Initialize the code generator.
  33826. */
  33827. JX9_PRIVATE sxi32 jx9InitCodeGenerator(
  33828. jx9_vm *pVm, /* Target VM */
  33829. ProcConsumer xErr, /* Error log consumer callabck */
  33830. void *pErrData /* Last argument to xErr() */
  33831. )
  33832. {
  33833. jx9_gen_state *pGen = &pVm->sCodeGen;
  33834. /* Zero the structure */
  33835. SyZero(pGen, sizeof(jx9_gen_state));
  33836. /* Initial state */
  33837. pGen->pVm = &(*pVm);
  33838. pGen->xErr = xErr;
  33839. pGen->pErrData = pErrData;
  33840. SyHashInit(&pGen->hLiteral, &pVm->sAllocator, 0, 0);
  33841. SyHashInit(&pGen->hVar, &pVm->sAllocator, 0, 0);
  33842. /* Create the global scope */
  33843. GenStateInitBlock(pGen, &pGen->sGlobal,GEN_BLOCK_GLOBAL,jx9VmInstrLength(&(*pVm)), 0);
  33844. /* Point to the global scope */
  33845. pGen->pCurrent = &pGen->sGlobal;
  33846. return SXRET_OK;
  33847. }
  33848. /*
  33849. * Utility routines. Reset the code generator to it's initial state.
  33850. */
  33851. JX9_PRIVATE sxi32 jx9ResetCodeGenerator(
  33852. jx9_vm *pVm, /* Target VM */
  33853. ProcConsumer xErr, /* Error log consumer callabck */
  33854. void *pErrData /* Last argument to xErr() */
  33855. )
  33856. {
  33857. jx9_gen_state *pGen = &pVm->sCodeGen;
  33858. GenBlock *pBlock, *pParent;
  33859. /* Point to the global scope */
  33860. pBlock = pGen->pCurrent;
  33861. while( pBlock->pParent != 0 ){
  33862. pParent = pBlock->pParent;
  33863. GenStateFreeBlock(pBlock);
  33864. pBlock = pParent;
  33865. }
  33866. pGen->xErr = xErr;
  33867. pGen->pErrData = pErrData;
  33868. pGen->pCurrent = &pGen->sGlobal;
  33869. pGen->pIn = pGen->pEnd = 0;
  33870. pGen->nErr = 0;
  33871. return SXRET_OK;
  33872. }
  33873. /*
  33874. * Generate a compile-time error message.
  33875. * If the error count limit is reached (usually 15 error message)
  33876. * this function return SXERR_ABORT.In that case upper-layers must
  33877. * abort compilation immediately.
  33878. */
  33879. JX9_PRIVATE sxi32 jx9GenCompileError(jx9_gen_state *pGen,sxi32 nErrType,sxu32 nLine,const char *zFormat,...)
  33880. {
  33881. SyBlob *pWorker = &pGen->pVm->pEngine->xConf.sErrConsumer;
  33882. const char *zErr = "Error";
  33883. va_list ap;
  33884. if( nErrType == E_ERROR ){
  33885. /* Increment the error counter */
  33886. pGen->nErr++;
  33887. if( pGen->nErr > 15 ){
  33888. /* Error count limit reached */
  33889. SyBlobFormat(pWorker, "%u Error count limit reached, JX9 is aborting compilation\n", nLine);
  33890. /* Abort immediately */
  33891. return SXERR_ABORT;
  33892. }
  33893. }
  33894. switch(nErrType){
  33895. case E_WARNING: zErr = "Warning"; break;
  33896. case E_PARSE: zErr = "Parse error"; break;
  33897. case E_NOTICE: zErr = "Notice"; break;
  33898. default:
  33899. break;
  33900. }
  33901. /* Format the error message */
  33902. SyBlobFormat(pWorker, "%u %s: ", nLine, zErr);
  33903. va_start(ap, zFormat);
  33904. SyBlobFormatAp(pWorker, zFormat, ap);
  33905. va_end(ap);
  33906. /* Append a new line */
  33907. SyBlobAppend(pWorker, (const void *)"\n", sizeof(char));
  33908. return JX9_OK;
  33909. }
  33910. /*
  33911. * ----------------------------------------------------------
  33912. * File: builtin.c
  33913. * MD5: 97ae6ddf8ded9fe14634060675e12f80
  33914. * ----------------------------------------------------------
  33915. */
  33916. /*
  33917. * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
  33918. * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
  33919. * Version 1.7.2
  33920. * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  33921. * please contact Symisc Systems via:
  33922. * legal@symisc.net
  33923. * licensing@symisc.net
  33924. * contact@symisc.net
  33925. * or visit:
  33926. * http://jx9.symisc.net/
  33927. */
  33928. /* $SymiscID: builtin.c v1.7 Win7 2012-12-13 00:01 stable <chm@symisc.net> $ */
  33929. #ifndef JX9_AMALGAMATION
  33930. #include "jx9Int.h"
  33931. #endif
  33932. /* This file implement built-in 'foreign' functions for the JX9 engine */
  33933. /*
  33934. * Section:
  33935. * Variable handling Functions.
  33936. * Authors:
  33937. * Symisc Systems, devel@symisc.net.
  33938. * Copyright (C) Symisc Systems, http://jx9.symisc.net
  33939. * Status:
  33940. * Stable.
  33941. */
  33942. /*
  33943. * bool is_bool($var)
  33944. * Finds out whether a variable is a boolean.
  33945. * Parameters
  33946. * $var: The variable being evaluated.
  33947. * Return
  33948. * TRUE if var is a boolean. False otherwise.
  33949. */
  33950. static int jx9Builtin_is_bool(jx9_context *pCtx, int nArg, jx9_value **apArg)
  33951. {
  33952. int res = 0; /* Assume false by default */
  33953. if( nArg > 0 ){
  33954. res = jx9_value_is_bool(apArg[0]);
  33955. }
  33956. /* Query result */
  33957. jx9_result_bool(pCtx, res);
  33958. return JX9_OK;
  33959. }
  33960. /*
  33961. * bool is_float($var)
  33962. * bool is_real($var)
  33963. * bool is_double($var)
  33964. * Finds out whether a variable is a float.
  33965. * Parameters
  33966. * $var: The variable being evaluated.
  33967. * Return
  33968. * TRUE if var is a float. False otherwise.
  33969. */
  33970. static int jx9Builtin_is_float(jx9_context *pCtx, int nArg, jx9_value **apArg)
  33971. {
  33972. int res = 0; /* Assume false by default */
  33973. if( nArg > 0 ){
  33974. res = jx9_value_is_float(apArg[0]);
  33975. }
  33976. /* Query result */
  33977. jx9_result_bool(pCtx, res);
  33978. return JX9_OK;
  33979. }
  33980. /*
  33981. * bool is_int($var)
  33982. * bool is_integer($var)
  33983. * bool is_long($var)
  33984. * Finds out whether a variable is an integer.
  33985. * Parameters
  33986. * $var: The variable being evaluated.
  33987. * Return
  33988. * TRUE if var is an integer. False otherwise.
  33989. */
  33990. static int jx9Builtin_is_int(jx9_context *pCtx, int nArg, jx9_value **apArg)
  33991. {
  33992. int res = 0; /* Assume false by default */
  33993. if( nArg > 0 ){
  33994. res = jx9_value_is_int(apArg[0]);
  33995. }
  33996. /* Query result */
  33997. jx9_result_bool(pCtx, res);
  33998. return JX9_OK;
  33999. }
  34000. /*
  34001. * bool is_string($var)
  34002. * Finds out whether a variable is a string.
  34003. * Parameters
  34004. * $var: The variable being evaluated.
  34005. * Return
  34006. * TRUE if var is string. False otherwise.
  34007. */
  34008. static int jx9Builtin_is_string(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34009. {
  34010. int res = 0; /* Assume false by default */
  34011. if( nArg > 0 ){
  34012. res = jx9_value_is_string(apArg[0]);
  34013. }
  34014. /* Query result */
  34015. jx9_result_bool(pCtx, res);
  34016. return JX9_OK;
  34017. }
  34018. /*
  34019. * bool is_null($var)
  34020. * Finds out whether a variable is NULL.
  34021. * Parameters
  34022. * $var: The variable being evaluated.
  34023. * Return
  34024. * TRUE if var is NULL. False otherwise.
  34025. */
  34026. static int jx9Builtin_is_null(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34027. {
  34028. int res = 0; /* Assume false by default */
  34029. if( nArg > 0 ){
  34030. res = jx9_value_is_null(apArg[0]);
  34031. }
  34032. /* Query result */
  34033. jx9_result_bool(pCtx, res);
  34034. return JX9_OK;
  34035. }
  34036. /*
  34037. * bool is_numeric($var)
  34038. * Find out whether a variable is NULL.
  34039. * Parameters
  34040. * $var: The variable being evaluated.
  34041. * Return
  34042. * True if var is numeric. False otherwise.
  34043. */
  34044. static int jx9Builtin_is_numeric(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34045. {
  34046. int res = 0; /* Assume false by default */
  34047. if( nArg > 0 ){
  34048. res = jx9_value_is_numeric(apArg[0]);
  34049. }
  34050. /* Query result */
  34051. jx9_result_bool(pCtx, res);
  34052. return JX9_OK;
  34053. }
  34054. /*
  34055. * bool is_scalar($var)
  34056. * Find out whether a variable is a scalar.
  34057. * Parameters
  34058. * $var: The variable being evaluated.
  34059. * Return
  34060. * True if var is scalar. False otherwise.
  34061. */
  34062. static int jx9Builtin_is_scalar(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34063. {
  34064. int res = 0; /* Assume false by default */
  34065. if( nArg > 0 ){
  34066. res = jx9_value_is_scalar(apArg[0]);
  34067. }
  34068. /* Query result */
  34069. jx9_result_bool(pCtx, res);
  34070. return JX9_OK;
  34071. }
  34072. /*
  34073. * bool is_array($var)
  34074. * Find out whether a variable is an array.
  34075. * Parameters
  34076. * $var: The variable being evaluated.
  34077. * Return
  34078. * True if var is an array. False otherwise.
  34079. */
  34080. static int jx9Builtin_is_array(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34081. {
  34082. int res = 0; /* Assume false by default */
  34083. if( nArg > 0 ){
  34084. res = jx9_value_is_json_array(apArg[0]);
  34085. }
  34086. /* Query result */
  34087. jx9_result_bool(pCtx, res);
  34088. return JX9_OK;
  34089. }
  34090. /*
  34091. * bool is_object($var)
  34092. * Find out whether a variable is an object.
  34093. * Parameters
  34094. * $var: The variable being evaluated.
  34095. * Return
  34096. * True if var is an object. False otherwise.
  34097. */
  34098. static int jx9Builtin_is_object(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34099. {
  34100. int res = 0; /* Assume false by default */
  34101. if( nArg > 0 ){
  34102. res = jx9_value_is_json_object(apArg[0]);
  34103. }
  34104. /* Query result */
  34105. jx9_result_bool(pCtx, res);
  34106. return JX9_OK;
  34107. }
  34108. /*
  34109. * bool is_resource($var)
  34110. * Find out whether a variable is a resource.
  34111. * Parameters
  34112. * $var: The variable being evaluated.
  34113. * Return
  34114. * True if a resource. False otherwise.
  34115. */
  34116. static int jx9Builtin_is_resource(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34117. {
  34118. int res = 0; /* Assume false by default */
  34119. if( nArg > 0 ){
  34120. res = jx9_value_is_resource(apArg[0]);
  34121. }
  34122. jx9_result_bool(pCtx, res);
  34123. return JX9_OK;
  34124. }
  34125. /*
  34126. * float floatval($var)
  34127. * Get float value of a variable.
  34128. * Parameter
  34129. * $var: The variable being processed.
  34130. * Return
  34131. * the float value of a variable.
  34132. */
  34133. static int jx9Builtin_floatval(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34134. {
  34135. if( nArg < 1 ){
  34136. /* return 0.0 */
  34137. jx9_result_double(pCtx, 0);
  34138. }else{
  34139. double dval;
  34140. /* Perform the cast */
  34141. dval = jx9_value_to_double(apArg[0]);
  34142. jx9_result_double(pCtx, dval);
  34143. }
  34144. return JX9_OK;
  34145. }
  34146. /*
  34147. * int intval($var)
  34148. * Get integer value of a variable.
  34149. * Parameter
  34150. * $var: The variable being processed.
  34151. * Return
  34152. * the int value of a variable.
  34153. */
  34154. static int jx9Builtin_intval(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34155. {
  34156. if( nArg < 1 ){
  34157. /* return 0 */
  34158. jx9_result_int(pCtx, 0);
  34159. }else{
  34160. sxi64 iVal;
  34161. /* Perform the cast */
  34162. iVal = jx9_value_to_int64(apArg[0]);
  34163. jx9_result_int64(pCtx, iVal);
  34164. }
  34165. return JX9_OK;
  34166. }
  34167. /*
  34168. * string strval($var)
  34169. * Get the string representation of a variable.
  34170. * Parameter
  34171. * $var: The variable being processed.
  34172. * Return
  34173. * the string value of a variable.
  34174. */
  34175. static int jx9Builtin_strval(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34176. {
  34177. if( nArg < 1 ){
  34178. /* return NULL */
  34179. jx9_result_null(pCtx);
  34180. }else{
  34181. const char *zVal;
  34182. int iLen = 0; /* cc -O6 warning */
  34183. /* Perform the cast */
  34184. zVal = jx9_value_to_string(apArg[0], &iLen);
  34185. jx9_result_string(pCtx, zVal, iLen);
  34186. }
  34187. return JX9_OK;
  34188. }
  34189. /*
  34190. * bool empty($var)
  34191. * Determine whether a variable is empty.
  34192. * Parameters
  34193. * $var: The variable being checked.
  34194. * Return
  34195. * 0 if var has a non-empty and non-zero value.1 otherwise.
  34196. */
  34197. static int jx9Builtin_empty(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34198. {
  34199. int res = 1; /* Assume empty by default */
  34200. if( nArg > 0 ){
  34201. res = jx9_value_is_empty(apArg[0]);
  34202. }
  34203. jx9_result_bool(pCtx, res);
  34204. return JX9_OK;
  34205. }
  34206. #ifndef JX9_DISABLE_BUILTIN_FUNC
  34207. #ifdef JX9_ENABLE_MATH_FUNC
  34208. /*
  34209. * Section:
  34210. * Math Functions.
  34211. * Authors:
  34212. * Symisc Systems, devel@symisc.net.
  34213. * Copyright (C) Symisc Systems, http://jx9.symisc.net
  34214. * Status:
  34215. * Stable.
  34216. */
  34217. #include <stdlib.h> /* abs */
  34218. #include <math.h>
  34219. /*
  34220. * float sqrt(float $arg )
  34221. * Square root of the given number.
  34222. * Parameter
  34223. * The number to process.
  34224. * Return
  34225. * The square root of arg or the special value Nan of failure.
  34226. */
  34227. static int jx9Builtin_sqrt(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34228. {
  34229. double r, x;
  34230. if( nArg < 1 ){
  34231. /* Missing argument, return 0 */
  34232. jx9_result_int(pCtx, 0);
  34233. return JX9_OK;
  34234. }
  34235. x = jx9_value_to_double(apArg[0]);
  34236. /* Perform the requested operation */
  34237. r = sqrt(x);
  34238. /* store the result back */
  34239. jx9_result_double(pCtx, r);
  34240. return JX9_OK;
  34241. }
  34242. /*
  34243. * float exp(float $arg )
  34244. * Calculates the exponent of e.
  34245. * Parameter
  34246. * The number to process.
  34247. * Return
  34248. * 'e' raised to the power of arg.
  34249. */
  34250. static int jx9Builtin_exp(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34251. {
  34252. double r, x;
  34253. if( nArg < 1 ){
  34254. /* Missing argument, return 0 */
  34255. jx9_result_int(pCtx, 0);
  34256. return JX9_OK;
  34257. }
  34258. x = jx9_value_to_double(apArg[0]);
  34259. /* Perform the requested operation */
  34260. r = exp(x);
  34261. /* store the result back */
  34262. jx9_result_double(pCtx, r);
  34263. return JX9_OK;
  34264. }
  34265. /*
  34266. * float floor(float $arg )
  34267. * Round fractions down.
  34268. * Parameter
  34269. * The number to process.
  34270. * Return
  34271. * Returns the next lowest integer value by rounding down value if necessary.
  34272. */
  34273. static int jx9Builtin_floor(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34274. {
  34275. double r, x;
  34276. if( nArg < 1 ){
  34277. /* Missing argument, return 0 */
  34278. jx9_result_int(pCtx, 0);
  34279. return JX9_OK;
  34280. }
  34281. x = jx9_value_to_double(apArg[0]);
  34282. /* Perform the requested operation */
  34283. r = floor(x);
  34284. /* store the result back */
  34285. jx9_result_double(pCtx, r);
  34286. return JX9_OK;
  34287. }
  34288. /*
  34289. * float cos(float $arg )
  34290. * Cosine.
  34291. * Parameter
  34292. * The number to process.
  34293. * Return
  34294. * The cosine of arg.
  34295. */
  34296. static int jx9Builtin_cos(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34297. {
  34298. double r, x;
  34299. if( nArg < 1 ){
  34300. /* Missing argument, return 0 */
  34301. jx9_result_int(pCtx, 0);
  34302. return JX9_OK;
  34303. }
  34304. x = jx9_value_to_double(apArg[0]);
  34305. /* Perform the requested operation */
  34306. r = cos(x);
  34307. /* store the result back */
  34308. jx9_result_double(pCtx, r);
  34309. return JX9_OK;
  34310. }
  34311. /*
  34312. * float acos(float $arg )
  34313. * Arc cosine.
  34314. * Parameter
  34315. * The number to process.
  34316. * Return
  34317. * The arc cosine of arg.
  34318. */
  34319. static int jx9Builtin_acos(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34320. {
  34321. double r, x;
  34322. if( nArg < 1 ){
  34323. /* Missing argument, return 0 */
  34324. jx9_result_int(pCtx, 0);
  34325. return JX9_OK;
  34326. }
  34327. x = jx9_value_to_double(apArg[0]);
  34328. /* Perform the requested operation */
  34329. r = acos(x);
  34330. /* store the result back */
  34331. jx9_result_double(pCtx, r);
  34332. return JX9_OK;
  34333. }
  34334. /*
  34335. * float cosh(float $arg )
  34336. * Hyperbolic cosine.
  34337. * Parameter
  34338. * The number to process.
  34339. * Return
  34340. * The hyperbolic cosine of arg.
  34341. */
  34342. static int jx9Builtin_cosh(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34343. {
  34344. double r, x;
  34345. if( nArg < 1 ){
  34346. /* Missing argument, return 0 */
  34347. jx9_result_int(pCtx, 0);
  34348. return JX9_OK;
  34349. }
  34350. x = jx9_value_to_double(apArg[0]);
  34351. /* Perform the requested operation */
  34352. r = cosh(x);
  34353. /* store the result back */
  34354. jx9_result_double(pCtx, r);
  34355. return JX9_OK;
  34356. }
  34357. /*
  34358. * float sin(float $arg )
  34359. * Sine.
  34360. * Parameter
  34361. * The number to process.
  34362. * Return
  34363. * The sine of arg.
  34364. */
  34365. static int jx9Builtin_sin(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34366. {
  34367. double r, x;
  34368. if( nArg < 1 ){
  34369. /* Missing argument, return 0 */
  34370. jx9_result_int(pCtx, 0);
  34371. return JX9_OK;
  34372. }
  34373. x = jx9_value_to_double(apArg[0]);
  34374. /* Perform the requested operation */
  34375. r = sin(x);
  34376. /* store the result back */
  34377. jx9_result_double(pCtx, r);
  34378. return JX9_OK;
  34379. }
  34380. /*
  34381. * float asin(float $arg )
  34382. * Arc sine.
  34383. * Parameter
  34384. * The number to process.
  34385. * Return
  34386. * The arc sine of arg.
  34387. */
  34388. static int jx9Builtin_asin(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34389. {
  34390. double r, x;
  34391. if( nArg < 1 ){
  34392. /* Missing argument, return 0 */
  34393. jx9_result_int(pCtx, 0);
  34394. return JX9_OK;
  34395. }
  34396. x = jx9_value_to_double(apArg[0]);
  34397. /* Perform the requested operation */
  34398. r = asin(x);
  34399. /* store the result back */
  34400. jx9_result_double(pCtx, r);
  34401. return JX9_OK;
  34402. }
  34403. /*
  34404. * float sinh(float $arg )
  34405. * Hyperbolic sine.
  34406. * Parameter
  34407. * The number to process.
  34408. * Return
  34409. * The hyperbolic sine of arg.
  34410. */
  34411. static int jx9Builtin_sinh(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34412. {
  34413. double r, x;
  34414. if( nArg < 1 ){
  34415. /* Missing argument, return 0 */
  34416. jx9_result_int(pCtx, 0);
  34417. return JX9_OK;
  34418. }
  34419. x = jx9_value_to_double(apArg[0]);
  34420. /* Perform the requested operation */
  34421. r = sinh(x);
  34422. /* store the result back */
  34423. jx9_result_double(pCtx, r);
  34424. return JX9_OK;
  34425. }
  34426. /*
  34427. * float ceil(float $arg )
  34428. * Round fractions up.
  34429. * Parameter
  34430. * The number to process.
  34431. * Return
  34432. * The next highest integer value by rounding up value if necessary.
  34433. */
  34434. static int jx9Builtin_ceil(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34435. {
  34436. double r, x;
  34437. if( nArg < 1 ){
  34438. /* Missing argument, return 0 */
  34439. jx9_result_int(pCtx, 0);
  34440. return JX9_OK;
  34441. }
  34442. x = jx9_value_to_double(apArg[0]);
  34443. /* Perform the requested operation */
  34444. r = ceil(x);
  34445. /* store the result back */
  34446. jx9_result_double(pCtx, r);
  34447. return JX9_OK;
  34448. }
  34449. /*
  34450. * float tan(float $arg )
  34451. * Tangent.
  34452. * Parameter
  34453. * The number to process.
  34454. * Return
  34455. * The tangent of arg.
  34456. */
  34457. static int jx9Builtin_tan(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34458. {
  34459. double r, x;
  34460. if( nArg < 1 ){
  34461. /* Missing argument, return 0 */
  34462. jx9_result_int(pCtx, 0);
  34463. return JX9_OK;
  34464. }
  34465. x = jx9_value_to_double(apArg[0]);
  34466. /* Perform the requested operation */
  34467. r = tan(x);
  34468. /* store the result back */
  34469. jx9_result_double(pCtx, r);
  34470. return JX9_OK;
  34471. }
  34472. /*
  34473. * float atan(float $arg )
  34474. * Arc tangent.
  34475. * Parameter
  34476. * The number to process.
  34477. * Return
  34478. * The arc tangent of arg.
  34479. */
  34480. static int jx9Builtin_atan(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34481. {
  34482. double r, x;
  34483. if( nArg < 1 ){
  34484. /* Missing argument, return 0 */
  34485. jx9_result_int(pCtx, 0);
  34486. return JX9_OK;
  34487. }
  34488. x = jx9_value_to_double(apArg[0]);
  34489. /* Perform the requested operation */
  34490. r = atan(x);
  34491. /* store the result back */
  34492. jx9_result_double(pCtx, r);
  34493. return JX9_OK;
  34494. }
  34495. /*
  34496. * float tanh(float $arg )
  34497. * Hyperbolic tangent.
  34498. * Parameter
  34499. * The number to process.
  34500. * Return
  34501. * The Hyperbolic tangent of arg.
  34502. */
  34503. static int jx9Builtin_tanh(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34504. {
  34505. double r, x;
  34506. if( nArg < 1 ){
  34507. /* Missing argument, return 0 */
  34508. jx9_result_int(pCtx, 0);
  34509. return JX9_OK;
  34510. }
  34511. x = jx9_value_to_double(apArg[0]);
  34512. /* Perform the requested operation */
  34513. r = tanh(x);
  34514. /* store the result back */
  34515. jx9_result_double(pCtx, r);
  34516. return JX9_OK;
  34517. }
  34518. /*
  34519. * float atan2(float $y, float $x)
  34520. * Arc tangent of two variable.
  34521. * Parameter
  34522. * $y = Dividend parameter.
  34523. * $x = Divisor parameter.
  34524. * Return
  34525. * The arc tangent of y/x in radian.
  34526. */
  34527. static int jx9Builtin_atan2(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34528. {
  34529. double r, x, y;
  34530. if( nArg < 2 ){
  34531. /* Missing arguments, return 0 */
  34532. jx9_result_int(pCtx, 0);
  34533. return JX9_OK;
  34534. }
  34535. y = jx9_value_to_double(apArg[0]);
  34536. x = jx9_value_to_double(apArg[1]);
  34537. /* Perform the requested operation */
  34538. r = atan2(y, x);
  34539. /* store the result back */
  34540. jx9_result_double(pCtx, r);
  34541. return JX9_OK;
  34542. }
  34543. /*
  34544. * float/int64 abs(float/int64 $arg )
  34545. * Absolute value.
  34546. * Parameter
  34547. * The number to process.
  34548. * Return
  34549. * The absolute value of number.
  34550. */
  34551. static int jx9Builtin_abs(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34552. {
  34553. int is_float;
  34554. if( nArg < 1 ){
  34555. /* Missing argument, return 0 */
  34556. jx9_result_int(pCtx, 0);
  34557. return JX9_OK;
  34558. }
  34559. is_float = jx9_value_is_float(apArg[0]);
  34560. if( is_float ){
  34561. double r, x;
  34562. x = jx9_value_to_double(apArg[0]);
  34563. /* Perform the requested operation */
  34564. r = fabs(x);
  34565. jx9_result_double(pCtx, r);
  34566. }else{
  34567. int r, x;
  34568. x = jx9_value_to_int(apArg[0]);
  34569. /* Perform the requested operation */
  34570. r = abs(x);
  34571. jx9_result_int(pCtx, r);
  34572. }
  34573. return JX9_OK;
  34574. }
  34575. /*
  34576. * float log(float $arg, [int/float $base])
  34577. * Natural logarithm.
  34578. * Parameter
  34579. * $arg: The number to process.
  34580. * $base: The optional logarithmic base to use. (only base-10 is supported)
  34581. * Return
  34582. * The logarithm of arg to base, if given, or the natural logarithm.
  34583. * Note:
  34584. * only Natural log and base-10 log are supported.
  34585. */
  34586. static int jx9Builtin_log(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34587. {
  34588. double r, x;
  34589. if( nArg < 1 ){
  34590. /* Missing argument, return 0 */
  34591. jx9_result_int(pCtx, 0);
  34592. return JX9_OK;
  34593. }
  34594. x = jx9_value_to_double(apArg[0]);
  34595. /* Perform the requested operation */
  34596. if( nArg == 2 && jx9_value_is_numeric(apArg[1]) && jx9_value_to_int(apArg[1]) == 10 ){
  34597. /* Base-10 log */
  34598. r = log10(x);
  34599. }else{
  34600. r = log(x);
  34601. }
  34602. /* store the result back */
  34603. jx9_result_double(pCtx, r);
  34604. return JX9_OK;
  34605. }
  34606. /*
  34607. * float log10(float $arg )
  34608. * Base-10 logarithm.
  34609. * Parameter
  34610. * The number to process.
  34611. * Return
  34612. * The Base-10 logarithm of the given number.
  34613. */
  34614. static int jx9Builtin_log10(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34615. {
  34616. double r, x;
  34617. if( nArg < 1 ){
  34618. /* Missing argument, return 0 */
  34619. jx9_result_int(pCtx, 0);
  34620. return JX9_OK;
  34621. }
  34622. x = jx9_value_to_double(apArg[0]);
  34623. /* Perform the requested operation */
  34624. r = log10(x);
  34625. /* store the result back */
  34626. jx9_result_double(pCtx, r);
  34627. return JX9_OK;
  34628. }
  34629. /*
  34630. * number pow(number $base, number $exp)
  34631. * Exponential expression.
  34632. * Parameter
  34633. * base
  34634. * The base to use.
  34635. * exp
  34636. * The exponent.
  34637. * Return
  34638. * base raised to the power of exp.
  34639. * If the result can be represented as integer it will be returned
  34640. * as type integer, else it will be returned as type float.
  34641. */
  34642. static int jx9Builtin_pow(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34643. {
  34644. double r, x, y;
  34645. if( nArg < 1 ){
  34646. /* Missing argument, return 0 */
  34647. jx9_result_int(pCtx, 0);
  34648. return JX9_OK;
  34649. }
  34650. x = jx9_value_to_double(apArg[0]);
  34651. y = jx9_value_to_double(apArg[1]);
  34652. /* Perform the requested operation */
  34653. r = pow(x, y);
  34654. jx9_result_double(pCtx, r);
  34655. return JX9_OK;
  34656. }
  34657. /*
  34658. * float pi(void)
  34659. * Returns an approximation of pi.
  34660. * Note
  34661. * you can use the M_PI constant which yields identical results to pi().
  34662. * Return
  34663. * The value of pi as float.
  34664. */
  34665. static int jx9Builtin_pi(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34666. {
  34667. SXUNUSED(nArg); /* cc warning */
  34668. SXUNUSED(apArg);
  34669. jx9_result_double(pCtx, JX9_PI);
  34670. return JX9_OK;
  34671. }
  34672. /*
  34673. * float fmod(float $x, float $y)
  34674. * Returns the floating point remainder (modulo) of the division of the arguments.
  34675. * Parameters
  34676. * $x
  34677. * The dividend
  34678. * $y
  34679. * The divisor
  34680. * Return
  34681. * The floating point remainder of x/y.
  34682. */
  34683. static int jx9Builtin_fmod(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34684. {
  34685. double x, y, r;
  34686. if( nArg < 2 ){
  34687. /* Missing arguments */
  34688. jx9_result_double(pCtx, 0);
  34689. return JX9_OK;
  34690. }
  34691. /* Extract given arguments */
  34692. x = jx9_value_to_double(apArg[0]);
  34693. y = jx9_value_to_double(apArg[1]);
  34694. /* Perform the requested operation */
  34695. r = fmod(x, y);
  34696. /* Processing result */
  34697. jx9_result_double(pCtx, r);
  34698. return JX9_OK;
  34699. }
  34700. /*
  34701. * float hypot(float $x, float $y)
  34702. * Calculate the length of the hypotenuse of a right-angle triangle .
  34703. * Parameters
  34704. * $x
  34705. * Length of first side
  34706. * $y
  34707. * Length of first side
  34708. * Return
  34709. * Calculated length of the hypotenuse.
  34710. */
  34711. static int jx9Builtin_hypot(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34712. {
  34713. double x, y, r;
  34714. if( nArg < 2 ){
  34715. /* Missing arguments */
  34716. jx9_result_double(pCtx, 0);
  34717. return JX9_OK;
  34718. }
  34719. /* Extract given arguments */
  34720. x = jx9_value_to_double(apArg[0]);
  34721. y = jx9_value_to_double(apArg[1]);
  34722. /* Perform the requested operation */
  34723. r = hypot(x, y);
  34724. /* Processing result */
  34725. jx9_result_double(pCtx, r);
  34726. return JX9_OK;
  34727. }
  34728. #endif /* JX9_ENABLE_MATH_FUNC */
  34729. /*
  34730. * float round ( float $val [, int $precision = 0 [, int $mode = JX9_ROUND_HALF_UP ]] )
  34731. * Exponential expression.
  34732. * Parameter
  34733. * $val
  34734. * The value to round.
  34735. * $precision
  34736. * The optional number of decimal digits to round to.
  34737. * $mode
  34738. * One of JX9_ROUND_HALF_UP, JX9_ROUND_HALF_DOWN, JX9_ROUND_HALF_EVEN, or JX9_ROUND_HALF_ODD.
  34739. * (not supported).
  34740. * Return
  34741. * The rounded value.
  34742. */
  34743. static int jx9Builtin_round(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34744. {
  34745. int n = 0;
  34746. double r;
  34747. if( nArg < 1 ){
  34748. /* Missing argument, return 0 */
  34749. jx9_result_int(pCtx, 0);
  34750. return JX9_OK;
  34751. }
  34752. /* Extract the precision if available */
  34753. if( nArg > 1 ){
  34754. n = jx9_value_to_int(apArg[1]);
  34755. if( n>30 ){
  34756. n = 30;
  34757. }
  34758. if( n<0 ){
  34759. n = 0;
  34760. }
  34761. }
  34762. r = jx9_value_to_double(apArg[0]);
  34763. /* If Y==0 and X will fit in a 64-bit int,
  34764. * handle the rounding directly.Otherwise
  34765. * use our own cutsom printf [i.e:SyBufferFormat()].
  34766. */
  34767. if( n==0 && r>=0 && r<LARGEST_INT64-1 ){
  34768. r = (double)((jx9_int64)(r+0.5));
  34769. }else if( n==0 && r<0 && (-r)<LARGEST_INT64-1 ){
  34770. r = -(double)((jx9_int64)((-r)+0.5));
  34771. }else{
  34772. char zBuf[256];
  34773. sxu32 nLen;
  34774. nLen = SyBufferFormat(zBuf, sizeof(zBuf), "%.*f", n, r);
  34775. /* Convert the string to real number */
  34776. SyStrToReal(zBuf, nLen, (void *)&r, 0);
  34777. }
  34778. /* Return thr rounded value */
  34779. jx9_result_double(pCtx, r);
  34780. return JX9_OK;
  34781. }
  34782. /*
  34783. * string dechex(int $number)
  34784. * Decimal to hexadecimal.
  34785. * Parameters
  34786. * $number
  34787. * Decimal value to convert
  34788. * Return
  34789. * Hexadecimal string representation of number
  34790. */
  34791. static int jx9Builtin_dechex(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34792. {
  34793. int iVal;
  34794. if( nArg < 1 ){
  34795. /* Missing arguments, return null */
  34796. jx9_result_null(pCtx);
  34797. return JX9_OK;
  34798. }
  34799. /* Extract the given number */
  34800. iVal = jx9_value_to_int(apArg[0]);
  34801. /* Format */
  34802. jx9_result_string_format(pCtx, "%x", iVal);
  34803. return JX9_OK;
  34804. }
  34805. /*
  34806. * string decoct(int $number)
  34807. * Decimal to Octal.
  34808. * Parameters
  34809. * $number
  34810. * Decimal value to convert
  34811. * Return
  34812. * Octal string representation of number
  34813. */
  34814. static int jx9Builtin_decoct(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34815. {
  34816. int iVal;
  34817. if( nArg < 1 ){
  34818. /* Missing arguments, return null */
  34819. jx9_result_null(pCtx);
  34820. return JX9_OK;
  34821. }
  34822. /* Extract the given number */
  34823. iVal = jx9_value_to_int(apArg[0]);
  34824. /* Format */
  34825. jx9_result_string_format(pCtx, "%o", iVal);
  34826. return JX9_OK;
  34827. }
  34828. /*
  34829. * string decbin(int $number)
  34830. * Decimal to binary.
  34831. * Parameters
  34832. * $number
  34833. * Decimal value to convert
  34834. * Return
  34835. * Binary string representation of number
  34836. */
  34837. static int jx9Builtin_decbin(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34838. {
  34839. int iVal;
  34840. if( nArg < 1 ){
  34841. /* Missing arguments, return null */
  34842. jx9_result_null(pCtx);
  34843. return JX9_OK;
  34844. }
  34845. /* Extract the given number */
  34846. iVal = jx9_value_to_int(apArg[0]);
  34847. /* Format */
  34848. jx9_result_string_format(pCtx, "%B", iVal);
  34849. return JX9_OK;
  34850. }
  34851. /*
  34852. * int64 hexdec(string $hex_string)
  34853. * Hexadecimal to decimal.
  34854. * Parameters
  34855. * $hex_string
  34856. * The hexadecimal string to convert
  34857. * Return
  34858. * The decimal representation of hex_string
  34859. */
  34860. static int jx9Builtin_hexdec(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34861. {
  34862. const char *zString, *zEnd;
  34863. jx9_int64 iVal;
  34864. int nLen;
  34865. if( nArg < 1 ){
  34866. /* Missing arguments, return -1 */
  34867. jx9_result_int(pCtx, -1);
  34868. return JX9_OK;
  34869. }
  34870. iVal = 0;
  34871. if( jx9_value_is_string(apArg[0]) ){
  34872. /* Extract the given string */
  34873. zString = jx9_value_to_string(apArg[0], &nLen);
  34874. /* Delimit the string */
  34875. zEnd = &zString[nLen];
  34876. /* Ignore non hex-stream */
  34877. while( zString < zEnd ){
  34878. if( (unsigned char)zString[0] >= 0xc0 ){
  34879. /* UTF-8 stream */
  34880. zString++;
  34881. while( zString < zEnd && (((unsigned char)zString[0] & 0xc0) == 0x80) ){
  34882. zString++;
  34883. }
  34884. }else{
  34885. if( SyisHex(zString[0]) ){
  34886. break;
  34887. }
  34888. /* Ignore */
  34889. zString++;
  34890. }
  34891. }
  34892. if( zString < zEnd ){
  34893. /* Cast */
  34894. SyHexStrToInt64(zString, (sxu32)(zEnd-zString), (void *)&iVal, 0);
  34895. }
  34896. }else{
  34897. /* Extract as a 64-bit integer */
  34898. iVal = jx9_value_to_int64(apArg[0]);
  34899. }
  34900. /* Return the number */
  34901. jx9_result_int64(pCtx, iVal);
  34902. return JX9_OK;
  34903. }
  34904. /*
  34905. * int64 bindec(string $bin_string)
  34906. * Binary to decimal.
  34907. * Parameters
  34908. * $bin_string
  34909. * The binary string to convert
  34910. * Return
  34911. * Returns the decimal equivalent of the binary number represented by the binary_string argument.
  34912. */
  34913. static int jx9Builtin_bindec(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34914. {
  34915. const char *zString;
  34916. jx9_int64 iVal;
  34917. int nLen;
  34918. if( nArg < 1 ){
  34919. /* Missing arguments, return -1 */
  34920. jx9_result_int(pCtx, -1);
  34921. return JX9_OK;
  34922. }
  34923. iVal = 0;
  34924. if( jx9_value_is_string(apArg[0]) ){
  34925. /* Extract the given string */
  34926. zString = jx9_value_to_string(apArg[0], &nLen);
  34927. if( nLen > 0 ){
  34928. /* Perform a binary cast */
  34929. SyBinaryStrToInt64(zString, (sxu32)nLen, (void *)&iVal, 0);
  34930. }
  34931. }else{
  34932. /* Extract as a 64-bit integer */
  34933. iVal = jx9_value_to_int64(apArg[0]);
  34934. }
  34935. /* Return the number */
  34936. jx9_result_int64(pCtx, iVal);
  34937. return JX9_OK;
  34938. }
  34939. /*
  34940. * int64 octdec(string $oct_string)
  34941. * Octal to decimal.
  34942. * Parameters
  34943. * $oct_string
  34944. * The octal string to convert
  34945. * Return
  34946. * Returns the decimal equivalent of the octal number represented by the octal_string argument.
  34947. */
  34948. static int jx9Builtin_octdec(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34949. {
  34950. const char *zString;
  34951. jx9_int64 iVal;
  34952. int nLen;
  34953. if( nArg < 1 ){
  34954. /* Missing arguments, return -1 */
  34955. jx9_result_int(pCtx, -1);
  34956. return JX9_OK;
  34957. }
  34958. iVal = 0;
  34959. if( jx9_value_is_string(apArg[0]) ){
  34960. /* Extract the given string */
  34961. zString = jx9_value_to_string(apArg[0], &nLen);
  34962. if( nLen > 0 ){
  34963. /* Perform the cast */
  34964. SyOctalStrToInt64(zString, (sxu32)nLen, (void *)&iVal, 0);
  34965. }
  34966. }else{
  34967. /* Extract as a 64-bit integer */
  34968. iVal = jx9_value_to_int64(apArg[0]);
  34969. }
  34970. /* Return the number */
  34971. jx9_result_int64(pCtx, iVal);
  34972. return JX9_OK;
  34973. }
  34974. /*
  34975. * string base_convert(string $number, int $frombase, int $tobase)
  34976. * Convert a number between arbitrary bases.
  34977. * Parameters
  34978. * $number
  34979. * The number to convert
  34980. * $frombase
  34981. * The base number is in
  34982. * $tobase
  34983. * The base to convert number to
  34984. * Return
  34985. * Number converted to base tobase
  34986. */
  34987. static int jx9Builtin_base_convert(jx9_context *pCtx, int nArg, jx9_value **apArg)
  34988. {
  34989. int nLen, iFbase, iTobase;
  34990. const char *zNum;
  34991. jx9_int64 iNum;
  34992. if( nArg < 3 ){
  34993. /* Return the empty string*/
  34994. jx9_result_string(pCtx, "", 0);
  34995. return JX9_OK;
  34996. }
  34997. /* Base numbers */
  34998. iFbase = jx9_value_to_int(apArg[1]);
  34999. iTobase = jx9_value_to_int(apArg[2]);
  35000. if( jx9_value_is_string(apArg[0]) ){
  35001. /* Extract the target number */
  35002. zNum = jx9_value_to_string(apArg[0], &nLen);
  35003. if( nLen < 1 ){
  35004. /* Return the empty string*/
  35005. jx9_result_string(pCtx, "", 0);
  35006. return JX9_OK;
  35007. }
  35008. /* Base conversion */
  35009. switch(iFbase){
  35010. case 16:
  35011. /* Hex */
  35012. SyHexStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
  35013. break;
  35014. case 8:
  35015. /* Octal */
  35016. SyOctalStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
  35017. break;
  35018. case 2:
  35019. /* Binary */
  35020. SyBinaryStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
  35021. break;
  35022. default:
  35023. /* Decimal */
  35024. SyStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0);
  35025. break;
  35026. }
  35027. }else{
  35028. iNum = jx9_value_to_int64(apArg[0]);
  35029. }
  35030. switch(iTobase){
  35031. case 16:
  35032. /* Hex */
  35033. jx9_result_string_format(pCtx, "%qx", iNum); /* Quad hex */
  35034. break;
  35035. case 8:
  35036. /* Octal */
  35037. jx9_result_string_format(pCtx, "%qo", iNum); /* Quad octal */
  35038. break;
  35039. case 2:
  35040. /* Binary */
  35041. jx9_result_string_format(pCtx, "%qB", iNum); /* Quad binary */
  35042. break;
  35043. default:
  35044. /* Decimal */
  35045. jx9_result_string_format(pCtx, "%qd", iNum); /* Quad decimal */
  35046. break;
  35047. }
  35048. return JX9_OK;
  35049. }
  35050. /*
  35051. * Section:
  35052. * String handling Functions.
  35053. * Authors:
  35054. * Symisc Systems, devel@symisc.net.
  35055. * Copyright (C) Symisc Systems, http://jx9.symisc.net
  35056. * Status:
  35057. * Stable.
  35058. */
  35059. /*
  35060. * string substr(string $string, int $start[, int $length ])
  35061. * Return part of a string.
  35062. * Parameters
  35063. * $string
  35064. * The input string. Must be one character or longer.
  35065. * $start
  35066. * If start is non-negative, the returned string will start at the start'th position
  35067. * in string, counting from zero. For instance, in the string 'abcdef', the character
  35068. * at position 0 is 'a', the character at position 2 is 'c', and so forth.
  35069. * If start is negative, the returned string will start at the start'th character
  35070. * from the end of string.
  35071. * If string is less than or equal to start characters long, FALSE will be returned.
  35072. * $length
  35073. * If length is given and is positive, the string returned will contain at most length
  35074. * characters beginning from start (depending on the length of string).
  35075. * If length is given and is negative, then that many characters will be omitted from
  35076. * the end of string (after the start position has been calculated when a start is negative).
  35077. * If start denotes the position of this truncation or beyond, false will be returned.
  35078. * If length is given and is 0, FALSE or NULL an empty string will be returned.
  35079. * If length is omitted, the substring starting from start until the end of the string
  35080. * will be returned.
  35081. * Return
  35082. * Returns the extracted part of string, or FALSE on failure or an empty string.
  35083. */
  35084. static int jx9Builtin_substr(jx9_context *pCtx, int nArg, jx9_value **apArg)
  35085. {
  35086. const char *zSource, *zOfft;
  35087. int nOfft, nLen, nSrcLen;
  35088. if( nArg < 2 ){
  35089. /* return FALSE */
  35090. jx9_result_bool(pCtx, 0);
  35091. return JX9_OK;
  35092. }
  35093. /* Extract the target string */
  35094. zSource = jx9_value_to_string(apArg[0], &nSrcLen);
  35095. if( nSrcLen < 1 ){
  35096. /* Empty string, return FALSE */
  35097. jx9_result_bool(pCtx, 0);
  35098. return JX9_OK;
  35099. }
  35100. nLen = nSrcLen; /* cc warning */
  35101. /* Extract the offset */
  35102. nOfft = jx9_value_to_int(apArg[1]);
  35103. if( nOfft < 0 ){
  35104. zOfft = &zSource[nSrcLen+nOfft];
  35105. if( zOfft < zSource ){
  35106. /* Invalid offset */
  35107. jx9_result_bool(pCtx, 0);
  35108. return JX9_OK;
  35109. }
  35110. nLen = (int)(&zSource[nSrcLen]-zOfft);
  35111. nOfft = (int)(zOfft-zSource);
  35112. }else if( nOfft >= nSrcLen ){
  35113. /* Invalid offset */
  35114. jx9_result_bool(pCtx, 0);
  35115. return JX9_OK;
  35116. }else{
  35117. zOfft = &zSource[nOfft];
  35118. nLen = nSrcLen - nOfft;
  35119. }
  35120. if( nArg > 2 ){
  35121. /* Extract the length */
  35122. nLen = jx9_value_to_int(apArg[2]);
  35123. if( nLen == 0 ){
  35124. /* Invalid length, return an empty string */
  35125. jx9_result_string(pCtx, "", 0);
  35126. return JX9_OK;
  35127. }else if( nLen < 0 ){
  35128. nLen = nSrcLen + nLen - nOfft;
  35129. if( nLen < 1 ){
  35130. /* Invalid length */
  35131. nLen = nSrcLen - nOfft;
  35132. }
  35133. }
  35134. if( nLen + nOfft > nSrcLen ){
  35135. /* Invalid length */
  35136. nLen = nSrcLen - nOfft;
  35137. }
  35138. }
  35139. /* Return the substring */
  35140. jx9_result_string(pCtx, zOfft, nLen);
  35141. return JX9_OK;
  35142. }
  35143. /*
  35144. * int substr_compare(string $main_str, string $str , int $offset[, int $length[, bool $case_insensitivity = false ]])
  35145. * Binary safe comparison of two strings from an offset, up to length characters.
  35146. * Parameters
  35147. * $main_str
  35148. * The main string being compared.
  35149. * $str
  35150. * The secondary string being compared.
  35151. * $offset
  35152. * The start position for the comparison. If negative, it starts counting from
  35153. * the end of the string.
  35154. * $length
  35155. * The length of the comparison. The default value is the largest of the length
  35156. * of the str compared to the length of main_str less the offset.
  35157. * $case_insensitivity
  35158. * If case_insensitivity is TRUE, comparison is case insensitive.
  35159. * Return
  35160. * Returns < 0 if main_str from position offset is less than str, > 0 if it is greater than
  35161. * str, and 0 if they are equal. If offset is equal to or greater than the length of main_str
  35162. * or length is set and is less than 1, substr_compare() prints a warning and returns FALSE.
  35163. */
  35164. static int jx9Builtin_substr_compare(jx9_context *pCtx, int nArg, jx9_value **apArg)
  35165. {
  35166. const char *zSource, *zOfft, *zSub;
  35167. int nOfft, nLen, nSrcLen, nSublen;
  35168. int iCase = 0;
  35169. int rc;
  35170. if( nArg < 3 ){
  35171. /* Missing arguments, return FALSE */
  35172. jx9_result_bool(pCtx, 0);
  35173. return JX9_OK;
  35174. }
  35175. /* Extract the target string */
  35176. zSource = jx9_value_to_string(apArg[0], &nSrcLen);
  35177. if( nSrcLen < 1 ){
  35178. /* Empty string, return FALSE */
  35179. jx9_result_bool(pCtx, 0);
  35180. return JX9_OK;
  35181. }
  35182. nLen = nSrcLen; /* cc warning */
  35183. /* Extract the substring */
  35184. zSub = jx9_value_to_string(apArg[1], &nSublen);
  35185. if( nSublen < 1 || nSublen > nSrcLen){
  35186. /* Empty string, return FALSE */
  35187. jx9_result_bool(pCtx, 0);
  35188. return JX9_OK;
  35189. }
  35190. /* Extract the offset */
  35191. nOfft = jx9_value_to_int(apArg[2]);
  35192. if( nOfft < 0 ){
  35193. zOfft = &zSource[nSrcLen+nOfft];
  35194. if( zOfft < zSource ){
  35195. /* Invalid offset */
  35196. jx9_result_bool(pCtx, 0);
  35197. return JX9_OK;
  35198. }
  35199. nLen = (int)(&zSource[nSrcLen]-zOfft);
  35200. nOfft = (int)(zOfft-zSource);
  35201. }else if( nOfft >= nSrcLen ){
  35202. /* Invalid offset */
  35203. jx9_result_bool(pCtx, 0);
  35204. return JX9_OK;
  35205. }else{
  35206. zOfft = &zSource[nOfft];
  35207. nLen = nSrcLen - nOfft;
  35208. }
  35209. if( nArg > 3 ){
  35210. /* Extract the length */
  35211. nLen = jx9_value_to_int(apArg[3]);
  35212. if( nLen < 1 ){
  35213. /* Invalid length */
  35214. jx9_result_int(pCtx, 1);
  35215. return JX9_OK;
  35216. }else if( nLen + nOfft > nSrcLen ){
  35217. /* Invalid length */
  35218. nLen = nSrcLen - nOfft;
  35219. }
  35220. if( nArg > 4 ){
  35221. /* Case-sensitive or not */
  35222. iCase = jx9_value_to_bool(apArg[4]);
  35223. }
  35224. }
  35225. /* Perform the comparison */
  35226. if( iCase ){
  35227. rc = SyStrnicmp(zOfft, zSub, (sxu32)nLen);
  35228. }else{
  35229. rc = SyStrncmp(zOfft, zSub, (sxu32)nLen);
  35230. }
  35231. /* Comparison result */
  35232. jx9_result_int(pCtx, rc);
  35233. return JX9_OK;
  35234. }
  35235. /*
  35236. * int substr_count(string $haystack, string $needle[, int $offset = 0 [, int $length ]])
  35237. * Count the number of substring occurrences.
  35238. * Parameters
  35239. * $haystack
  35240. * The string to search in
  35241. * $needle
  35242. * The substring to search for
  35243. * $offset
  35244. * The offset where to start counting
  35245. * $length (NOT USED)
  35246. * The maximum length after the specified offset to search for the substring.
  35247. * It outputs a warning if the offset plus the length is greater than the haystack length.
  35248. * Return
  35249. * Toral number of substring occurrences.
  35250. */
  35251. static int jx9Builtin_substr_count(jx9_context *pCtx, int nArg, jx9_value **apArg)
  35252. {
  35253. const char *zText, *zPattern, *zEnd;
  35254. int nTextlen, nPatlen;
  35255. int iCount = 0;
  35256. sxu32 nOfft;
  35257. sxi32 rc;
  35258. if( nArg < 2 ){
  35259. /* Missing arguments */
  35260. jx9_result_int(pCtx, 0);
  35261. return JX9_OK;
  35262. }
  35263. /* Point to the haystack */
  35264. zText = jx9_value_to_string(apArg[0], &nTextlen);
  35265. /* Point to the neddle */
  35266. zPattern = jx9_value_to_string(apArg[1], &nPatlen);
  35267. if( nTextlen < 1 || nPatlen < 1 || nPatlen > nTextlen ){
  35268. /* NOOP, return zero */
  35269. jx9_result_int(pCtx, 0);
  35270. return JX9_OK;
  35271. }
  35272. if( nArg > 2 ){
  35273. int nOfft;
  35274. /* Extract the offset */
  35275. nOfft = jx9_value_to_int(apArg[2]);
  35276. if( nOfft < 0 || nOfft > nTextlen ){
  35277. /* Invalid offset, return zero */
  35278. jx9_result_int(pCtx, 0);
  35279. return JX9_OK;
  35280. }
  35281. /* Point to the desired offset */
  35282. zText = &zText[nOfft];
  35283. /* Adjust length */
  35284. nTextlen -= nOfft;
  35285. }
  35286. /* Point to the end of the string */
  35287. zEnd = &zText[nTextlen];
  35288. if( nArg > 3 ){
  35289. int nLen;
  35290. /* Extract the length */
  35291. nLen = jx9_value_to_int(apArg[3]);
  35292. if( nLen < 0 || nLen > nTextlen ){
  35293. /* Invalid length, return 0 */
  35294. jx9_result_int(pCtx, 0);
  35295. return JX9_OK;
  35296. }
  35297. /* Adjust pointer */
  35298. nTextlen = nLen;
  35299. zEnd = &zText[nTextlen];
  35300. }
  35301. /* Perform the search */
  35302. for(;;){
  35303. rc = SyBlobSearch((const void *)zText, (sxu32)(zEnd-zText), (const void *)zPattern, nPatlen, &nOfft);
  35304. if( rc != SXRET_OK ){
  35305. /* Pattern not found, break immediately */
  35306. break;
  35307. }
  35308. /* Increment counter and update the offset */
  35309. iCount++;
  35310. zText += nOfft + nPatlen;
  35311. if( zText >= zEnd ){
  35312. break;
  35313. }
  35314. }
  35315. /* Pattern count */
  35316. jx9_result_int(pCtx, iCount);
  35317. return JX9_OK;
  35318. }
  35319. /*
  35320. * string chunk_split(string $body[, int $chunklen = 76 [, string $end = "\r\n" ]])
  35321. * Split a string into smaller chunks.
  35322. * Parameters
  35323. * $body
  35324. * The string to be chunked.
  35325. * $chunklen
  35326. * The chunk length.
  35327. * $end
  35328. * The line ending sequence.
  35329. * Return
  35330. * The chunked string or NULL on failure.
  35331. */
  35332. static int jx9Builtin_chunk_split(jx9_context *pCtx, int nArg, jx9_value **apArg)
  35333. {
  35334. const char *zIn, *zEnd, *zSep = "\r\n";
  35335. int nSepLen, nChunkLen, nLen;
  35336. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  35337. /* Nothing to split, return null */
  35338. jx9_result_null(pCtx);
  35339. return JX9_OK;
  35340. }
  35341. /* initialize/Extract arguments */
  35342. nSepLen = (int)sizeof("\r\n") - 1;
  35343. nChunkLen = 76;
  35344. zIn = jx9_value_to_string(apArg[0], &nLen);
  35345. zEnd = &zIn[nLen];
  35346. if( nArg > 1 ){
  35347. /* Chunk length */
  35348. nChunkLen = jx9_value_to_int(apArg[1]);
  35349. if( nChunkLen < 1 ){
  35350. /* Switch back to the default length */
  35351. nChunkLen = 76;
  35352. }
  35353. if( nArg > 2 ){
  35354. /* Separator */
  35355. zSep = jx9_value_to_string(apArg[2], &nSepLen);
  35356. if( nSepLen < 1 ){
  35357. /* Switch back to the default separator */
  35358. zSep = "\r\n";
  35359. nSepLen = (int)sizeof("\r\n") - 1;
  35360. }
  35361. }
  35362. }
  35363. /* Perform the requested operation */
  35364. if( nChunkLen > nLen ){
  35365. /* Nothing to split, return the string and the separator */
  35366. jx9_result_string_format(pCtx, "%.*s%.*s", nLen, zIn, nSepLen, zSep);
  35367. return JX9_OK;
  35368. }
  35369. while( zIn < zEnd ){
  35370. if( nChunkLen > (int)(zEnd-zIn) ){
  35371. nChunkLen = (int)(zEnd - zIn);
  35372. }
  35373. /* Append the chunk and the separator */
  35374. jx9_result_string_format(pCtx, "%.*s%.*s", nChunkLen, zIn, nSepLen, zSep);
  35375. /* Point beyond the chunk */
  35376. zIn += nChunkLen;
  35377. }
  35378. return JX9_OK;
  35379. }
  35380. /*
  35381. * string htmlspecialchars(string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string $charset]])
  35382. * HTML escaping of special characters.
  35383. * The translations performed are:
  35384. * '&' (ampersand) ==> '&amp;'
  35385. * '"' (double quote) ==> '&quot;' when ENT_NOQUOTES is not set.
  35386. * "'" (single quote) ==> '&#039;' only when ENT_QUOTES is set.
  35387. * '<' (less than) ==> '&lt;'
  35388. * '>' (greater than) ==> '&gt;'
  35389. * Parameters
  35390. * $string
  35391. * The string being converted.
  35392. * $flags
  35393. * A bitmask of one or more of the following flags, which specify how to handle quotes.
  35394. * The default is ENT_COMPAT | ENT_HTML401.
  35395. * ENT_COMPAT Will convert double-quotes and leave single-quotes alone.
  35396. * ENT_QUOTES Will convert both double and single quotes.
  35397. * ENT_NOQUOTES Will leave both double and single quotes unconverted.
  35398. * ENT_IGNORE Silently discard invalid code unit sequences instead of returning an empty string.
  35399. * $charset
  35400. * Defines character set used in conversion. The default character set is ISO-8859-1. (Not used)
  35401. * Return
  35402. * The escaped string or NULL on failure.
  35403. */
  35404. static int jx9Builtin_htmlspecialchars(jx9_context *pCtx, int nArg, jx9_value **apArg)
  35405. {
  35406. const char *zCur, *zIn, *zEnd;
  35407. int iFlags = 0x01|0x40; /* ENT_COMPAT | ENT_HTML401 */
  35408. int nLen, c;
  35409. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  35410. /* Missing/Invalid arguments, return NULL */
  35411. jx9_result_null(pCtx);
  35412. return JX9_OK;
  35413. }
  35414. /* Extract the target string */
  35415. zIn = jx9_value_to_string(apArg[0], &nLen);
  35416. zEnd = &zIn[nLen];
  35417. /* Extract the flags if available */
  35418. if( nArg > 1 ){
  35419. iFlags = jx9_value_to_int(apArg[1]);
  35420. if( iFlags < 0 ){
  35421. iFlags = 0x01|0x40;
  35422. }
  35423. }
  35424. /* Perform the requested operation */
  35425. for(;;){
  35426. if( zIn >= zEnd ){
  35427. break;
  35428. }
  35429. zCur = zIn;
  35430. while( zIn < zEnd && zIn[0] != '&' && zIn[0] != '\'' && zIn[0] != '"' && zIn[0] != '<' && zIn[0] != '>' ){
  35431. zIn++;
  35432. }
  35433. if( zCur < zIn ){
  35434. /* Append the raw string verbatim */
  35435. jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
  35436. }
  35437. if( zIn >= zEnd ){
  35438. break;
  35439. }
  35440. c = zIn[0];
  35441. if( c == '&' ){
  35442. /* Expand '&amp;' */
  35443. jx9_result_string(pCtx, "&amp;", (int)sizeof("&amp;")-1);
  35444. }else if( c == '<' ){
  35445. /* Expand '&lt;' */
  35446. jx9_result_string(pCtx, "&lt;", (int)sizeof("&lt;")-1);
  35447. }else if( c == '>' ){
  35448. /* Expand '&gt;' */
  35449. jx9_result_string(pCtx, "&gt;", (int)sizeof("&gt;")-1);
  35450. }else if( c == '\'' ){
  35451. if( iFlags & 0x02 /*ENT_QUOTES*/ ){
  35452. /* Expand '&#039;' */
  35453. jx9_result_string(pCtx, "&#039;", (int)sizeof("&#039;")-1);
  35454. }else{
  35455. /* Leave the single quote untouched */
  35456. jx9_result_string(pCtx, "'", (int)sizeof(char));
  35457. }
  35458. }else if( c == '"' ){
  35459. if( (iFlags & 0x04) == 0 /*ENT_NOQUOTES*/ ){
  35460. /* Expand '&quot;' */
  35461. jx9_result_string(pCtx, "&quot;", (int)sizeof("&quot;")-1);
  35462. }else{
  35463. /* Leave the double quote untouched */
  35464. jx9_result_string(pCtx, "\"", (int)sizeof(char));
  35465. }
  35466. }
  35467. /* Ignore the unsafe HTML character */
  35468. zIn++;
  35469. }
  35470. return JX9_OK;
  35471. }
  35472. /*
  35473. * string htmlspecialchars_decode(string $string[, int $quote_style = ENT_COMPAT ])
  35474. * Unescape HTML entities.
  35475. * Parameters
  35476. * $string
  35477. * The string to decode
  35478. * $quote_style
  35479. * The quote style. One of the following constants:
  35480. * ENT_COMPAT Will convert double-quotes and leave single-quotes alone (default)
  35481. * ENT_QUOTES Will convert both double and single quotes
  35482. * ENT_NOQUOTES Will leave both double and single quotes unconverted
  35483. * Return
  35484. * The unescaped string or NULL on failure.
  35485. */
  35486. static int jx9Builtin_htmlspecialchars_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
  35487. {
  35488. const char *zCur, *zIn, *zEnd;
  35489. int iFlags = 0x01; /* ENT_COMPAT */
  35490. int nLen, nJump;
  35491. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  35492. /* Missing/Invalid arguments, return NULL */
  35493. jx9_result_null(pCtx);
  35494. return JX9_OK;
  35495. }
  35496. /* Extract the target string */
  35497. zIn = jx9_value_to_string(apArg[0], &nLen);
  35498. zEnd = &zIn[nLen];
  35499. /* Extract the flags if available */
  35500. if( nArg > 1 ){
  35501. iFlags = jx9_value_to_int(apArg[1]);
  35502. if( iFlags < 0 ){
  35503. iFlags = 0x01;
  35504. }
  35505. }
  35506. /* Perform the requested operation */
  35507. for(;;){
  35508. if( zIn >= zEnd ){
  35509. break;
  35510. }
  35511. zCur = zIn;
  35512. while( zIn < zEnd && zIn[0] != '&' ){
  35513. zIn++;
  35514. }
  35515. if( zCur < zIn ){
  35516. /* Append the raw string verbatim */
  35517. jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
  35518. }
  35519. nLen = (int)(zEnd-zIn);
  35520. nJump = (int)sizeof(char);
  35521. if( nLen >= (int)sizeof("&amp;")-1 && SyStrnicmp(zIn, "&amp;", sizeof("&amp;")-1) == 0 ){
  35522. /* &amp; ==> '&' */
  35523. jx9_result_string(pCtx, "&", (int)sizeof(char));
  35524. nJump = (int)sizeof("&amp;")-1;
  35525. }else if( nLen >= (int)sizeof("&lt;")-1 && SyStrnicmp(zIn, "&lt;", sizeof("&lt;")-1) == 0 ){
  35526. /* &lt; ==> < */
  35527. jx9_result_string(pCtx, "<", (int)sizeof(char));
  35528. nJump = (int)sizeof("&lt;")-1;
  35529. }else if( nLen >= (int)sizeof("&gt;")-1 && SyStrnicmp(zIn, "&gt;", sizeof("&gt;")-1) == 0 ){
  35530. /* &gt; ==> '>' */
  35531. jx9_result_string(pCtx, ">", (int)sizeof(char));
  35532. nJump = (int)sizeof("&gt;")-1;
  35533. }else if( nLen >= (int)sizeof("&quot;")-1 && SyStrnicmp(zIn, "&quot;", sizeof("&quot;")-1) == 0 ){
  35534. /* &quot; ==> '"' */
  35535. if( (iFlags & 0x04) == 0 /*ENT_NOQUOTES*/ ){
  35536. jx9_result_string(pCtx, "\"", (int)sizeof(char));
  35537. }else{
  35538. /* Leave untouched */
  35539. jx9_result_string(pCtx, "&quot;", (int)sizeof("&quot;")-1);
  35540. }
  35541. nJump = (int)sizeof("&quot;")-1;
  35542. }else if( nLen >= (int)sizeof("&#039;")-1 && SyStrnicmp(zIn, "&#039;", sizeof("&#039;")-1) == 0 ){
  35543. /* &#039; ==> ''' */
  35544. if( iFlags & 0x02 /*ENT_QUOTES*/ ){
  35545. /* Expand ''' */
  35546. jx9_result_string(pCtx, "'", (int)sizeof(char));
  35547. }else{
  35548. /* Leave untouched */
  35549. jx9_result_string(pCtx, "&#039;", (int)sizeof("&#039;")-1);
  35550. }
  35551. nJump = (int)sizeof("&#039;")-1;
  35552. }else if( nLen >= (int)sizeof(char) ){
  35553. /* expand '&' */
  35554. jx9_result_string(pCtx, "&", (int)sizeof(char));
  35555. }else{
  35556. /* No more input to process */
  35557. break;
  35558. }
  35559. zIn += nJump;
  35560. }
  35561. return JX9_OK;
  35562. }
  35563. /* HTML encoding/Decoding table
  35564. * Source: Symisc RunTime API.[chm@symisc.net]
  35565. */
  35566. static const char *azHtmlEscape[] = {
  35567. "&lt;", "<", "&gt;", ">", "&amp;", "&", "&quot;", "\"", "&#39;", "'",
  35568. "&#33;", "!", "&#36;", "$", "&#35;", "#", "&#37;", "%", "&#40;", "(",
  35569. "&#41;", ")", "&#123;", "{", "&#125;", "}", "&#61;", "=", "&#43;", "+",
  35570. "&#63;", "?", "&#91;", "[", "&#93;", "]", "&#64;", "@", "&#44;", ","
  35571. };
  35572. /*
  35573. * array get_html_translation_table(void)
  35574. * Returns the translation table used by htmlspecialchars() and htmlentities().
  35575. * Parameters
  35576. * None
  35577. * Return
  35578. * The translation table as an array or NULL on failure.
  35579. */
  35580. static int jx9Builtin_get_html_translation_table(jx9_context *pCtx, int nArg, jx9_value **apArg)
  35581. {
  35582. jx9_value *pArray, *pValue;
  35583. sxu32 n;
  35584. /* Element value */
  35585. pValue = jx9_context_new_scalar(pCtx);
  35586. if( pValue == 0 ){
  35587. SXUNUSED(nArg); /* cc warning */
  35588. SXUNUSED(apArg);
  35589. /* Return NULL */
  35590. jx9_result_null(pCtx);
  35591. return JX9_OK;
  35592. }
  35593. /* Create a new array */
  35594. pArray = jx9_context_new_array(pCtx);
  35595. if( pArray == 0 ){
  35596. /* Return NULL */
  35597. jx9_result_null(pCtx);
  35598. return JX9_OK;
  35599. }
  35600. /* Make the table */
  35601. for( n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){
  35602. /* Prepare the value */
  35603. jx9_value_string(pValue, azHtmlEscape[n], -1 /* Compute length automatically */);
  35604. /* Insert the value */
  35605. jx9_array_add_strkey_elem(pArray, azHtmlEscape[n+1], pValue);
  35606. /* Reset the string cursor */
  35607. jx9_value_reset_string_cursor(pValue);
  35608. }
  35609. /*
  35610. * Return the array.
  35611. * Don't worry about freeing memory, everything will be automatically
  35612. * released upon we return from this function.
  35613. */
  35614. jx9_result_value(pCtx, pArray);
  35615. return JX9_OK;
  35616. }
  35617. /*
  35618. * string htmlentities( string $string [, int $flags = ENT_COMPAT | ENT_HTML401]);
  35619. * Convert all applicable characters to HTML entities
  35620. * Parameters
  35621. * $string
  35622. * The input string.
  35623. * $flags
  35624. * A bitmask of one or more of the flags (see block-comment on jx9Builtin_htmlspecialchars())
  35625. * Return
  35626. * The encoded string.
  35627. */
  35628. static int jx9Builtin_htmlentities(jx9_context *pCtx, int nArg, jx9_value **apArg)
  35629. {
  35630. int iFlags = 0x01; /* ENT_COMPAT */
  35631. const char *zIn, *zEnd;
  35632. int nLen, c;
  35633. sxu32 n;
  35634. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  35635. /* Missing/Invalid arguments, return NULL */
  35636. jx9_result_null(pCtx);
  35637. return JX9_OK;
  35638. }
  35639. /* Extract the target string */
  35640. zIn = jx9_value_to_string(apArg[0], &nLen);
  35641. zEnd = &zIn[nLen];
  35642. /* Extract the flags if available */
  35643. if( nArg > 1 ){
  35644. iFlags = jx9_value_to_int(apArg[1]);
  35645. if( iFlags < 0 ){
  35646. iFlags = 0x01;
  35647. }
  35648. }
  35649. /* Perform the requested operation */
  35650. for(;;){
  35651. if( zIn >= zEnd ){
  35652. /* No more input to process */
  35653. break;
  35654. }
  35655. c = zIn[0];
  35656. /* Perform a linear lookup on the decoding table */
  35657. for( n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){
  35658. if( azHtmlEscape[n+1][0] == c ){
  35659. /* Got one */
  35660. break;
  35661. }
  35662. }
  35663. if( n < SX_ARRAYSIZE(azHtmlEscape) ){
  35664. /* Output the safe sequence [i.e: '<' ==> '&lt;"] */
  35665. if( c == '"' && (iFlags & 0x04) /*ENT_NOQUOTES*/ ){
  35666. /* Expand the double quote verbatim */
  35667. jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
  35668. }else if(c == '\'' && ((iFlags & 0x02 /*ENT_QUOTES*/) == 0 || (iFlags & 0x04) /*ENT_NOQUOTES*/) ){
  35669. /* expand single quote verbatim */
  35670. jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
  35671. }else{
  35672. jx9_result_string(pCtx, azHtmlEscape[n], -1/*Compute length automatically */);
  35673. }
  35674. }else{
  35675. /* Output character verbatim */
  35676. jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
  35677. }
  35678. zIn++;
  35679. }
  35680. return JX9_OK;
  35681. }
  35682. /*
  35683. * string html_entity_decode(string $string [, int $quote_style = ENT_COMPAT [, string $charset = 'UTF-8' ]])
  35684. * Perform the reverse operation of html_entity_decode().
  35685. * Parameters
  35686. * $string
  35687. * The input string.
  35688. * $flags
  35689. * A bitmask of one or more of the flags (see comment on jx9Builtin_htmlspecialchars())
  35690. * Return
  35691. * The decoded string.
  35692. */
  35693. static int jx9Builtin_html_entity_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
  35694. {
  35695. const char *zCur, *zIn, *zEnd;
  35696. int iFlags = 0x01; /* ENT_COMPAT */
  35697. int nLen;
  35698. sxu32 n;
  35699. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  35700. /* Missing/Invalid arguments, return NULL */
  35701. jx9_result_null(pCtx);
  35702. return JX9_OK;
  35703. }
  35704. /* Extract the target string */
  35705. zIn = jx9_value_to_string(apArg[0], &nLen);
  35706. zEnd = &zIn[nLen];
  35707. /* Extract the flags if available */
  35708. if( nArg > 1 ){
  35709. iFlags = jx9_value_to_int(apArg[1]);
  35710. if( iFlags < 0 ){
  35711. iFlags = 0x01;
  35712. }
  35713. }
  35714. /* Perform the requested operation */
  35715. for(;;){
  35716. if( zIn >= zEnd ){
  35717. /* No more input to process */
  35718. break;
  35719. }
  35720. zCur = zIn;
  35721. while( zIn < zEnd && zIn[0] != '&' ){
  35722. zIn++;
  35723. }
  35724. if( zCur < zIn ){
  35725. /* Append raw string verbatim */
  35726. jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
  35727. }
  35728. if( zIn >= zEnd ){
  35729. break;
  35730. }
  35731. nLen = (int)(zEnd-zIn);
  35732. /* Find an encoded sequence */
  35733. for(n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){
  35734. int iLen = (int)SyStrlen(azHtmlEscape[n]);
  35735. if( nLen >= iLen && SyStrnicmp(zIn, azHtmlEscape[n], (sxu32)iLen) == 0 ){
  35736. /* Got one */
  35737. zIn += iLen;
  35738. break;
  35739. }
  35740. }
  35741. if( n < SX_ARRAYSIZE(azHtmlEscape) ){
  35742. int c = azHtmlEscape[n+1][0];
  35743. /* Output the decoded character */
  35744. if( c == '\'' && ((iFlags & 0x02) == 0 /*ENT_QUOTES*/|| (iFlags & 0x04) /*ENT_NOQUOTES*/) ){
  35745. /* Do not process single quotes */
  35746. jx9_result_string(pCtx, azHtmlEscape[n], -1);
  35747. }else if( c == '"' && (iFlags & 0x04) /*ENT_NOQUOTES*/ ){
  35748. /* Do not process double quotes */
  35749. jx9_result_string(pCtx, azHtmlEscape[n], -1);
  35750. }else{
  35751. jx9_result_string(pCtx, azHtmlEscape[n+1], -1); /* Compute length automatically */
  35752. }
  35753. }else{
  35754. /* Append '&' */
  35755. jx9_result_string(pCtx, "&", (int)sizeof(char));
  35756. zIn++;
  35757. }
  35758. }
  35759. return JX9_OK;
  35760. }
  35761. /*
  35762. * int strlen($string)
  35763. * return the length of the given string.
  35764. * Parameter
  35765. * string: The string being measured for length.
  35766. * Return
  35767. * length of the given string.
  35768. */
  35769. static int jx9Builtin_strlen(jx9_context *pCtx, int nArg, jx9_value **apArg)
  35770. {
  35771. int iLen = 0;
  35772. if( nArg > 0 ){
  35773. jx9_value_to_string(apArg[0], &iLen);
  35774. }
  35775. /* String length */
  35776. jx9_result_int(pCtx, iLen);
  35777. return JX9_OK;
  35778. }
  35779. /*
  35780. * int strcmp(string $str1, string $str2)
  35781. * Perform a binary safe string comparison.
  35782. * Parameter
  35783. * str1: The first string
  35784. * str2: The second string
  35785. * Return
  35786. * Returns < 0 if str1 is less than str2; > 0 if str1 is greater
  35787. * than str2, and 0 if they are equal.
  35788. */
  35789. static int jx9Builtin_strcmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
  35790. {
  35791. const char *z1, *z2;
  35792. int n1, n2;
  35793. int res;
  35794. if( nArg < 2 ){
  35795. res = nArg == 0 ? 0 : 1;
  35796. jx9_result_int(pCtx, res);
  35797. return JX9_OK;
  35798. }
  35799. /* Perform the comparison */
  35800. z1 = jx9_value_to_string(apArg[0], &n1);
  35801. z2 = jx9_value_to_string(apArg[1], &n2);
  35802. res = SyStrncmp(z1, z2, (sxu32)(SXMAX(n1, n2)));
  35803. /* Comparison result */
  35804. jx9_result_int(pCtx, res);
  35805. return JX9_OK;
  35806. }
  35807. /*
  35808. * int strncmp(string $str1, string $str2, int n)
  35809. * Perform a binary safe string comparison of the first n characters.
  35810. * Parameter
  35811. * str1: The first string
  35812. * str2: The second string
  35813. * Return
  35814. * Returns < 0 if str1 is less than str2; > 0 if str1 is greater
  35815. * than str2, and 0 if they are equal.
  35816. */
  35817. static int jx9Builtin_strncmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
  35818. {
  35819. const char *z1, *z2;
  35820. int res;
  35821. int n;
  35822. if( nArg < 3 ){
  35823. /* Perform a standard comparison */
  35824. return jx9Builtin_strcmp(pCtx, nArg, apArg);
  35825. }
  35826. /* Desired comparison length */
  35827. n = jx9_value_to_int(apArg[2]);
  35828. if( n < 0 ){
  35829. /* Invalid length */
  35830. jx9_result_int(pCtx, -1);
  35831. return JX9_OK;
  35832. }
  35833. /* Perform the comparison */
  35834. z1 = jx9_value_to_string(apArg[0], 0);
  35835. z2 = jx9_value_to_string(apArg[1], 0);
  35836. res = SyStrncmp(z1, z2, (sxu32)n);
  35837. /* Comparison result */
  35838. jx9_result_int(pCtx, res);
  35839. return JX9_OK;
  35840. }
  35841. /*
  35842. * int strcasecmp(string $str1, string $str2, int n)
  35843. * Perform a binary safe case-insensitive string comparison.
  35844. * Parameter
  35845. * str1: The first string
  35846. * str2: The second string
  35847. * Return
  35848. * Returns < 0 if str1 is less than str2; > 0 if str1 is greater
  35849. * than str2, and 0 if they are equal.
  35850. */
  35851. static int jx9Builtin_strcasecmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
  35852. {
  35853. const char *z1, *z2;
  35854. int n1, n2;
  35855. int res;
  35856. if( nArg < 2 ){
  35857. res = nArg == 0 ? 0 : 1;
  35858. jx9_result_int(pCtx, res);
  35859. return JX9_OK;
  35860. }
  35861. /* Perform the comparison */
  35862. z1 = jx9_value_to_string(apArg[0], &n1);
  35863. z2 = jx9_value_to_string(apArg[1], &n2);
  35864. res = SyStrnicmp(z1, z2, (sxu32)(SXMAX(n1, n2)));
  35865. /* Comparison result */
  35866. jx9_result_int(pCtx, res);
  35867. return JX9_OK;
  35868. }
  35869. /*
  35870. * int strncasecmp(string $str1, string $str2, int n)
  35871. * Perform a binary safe case-insensitive string comparison of the first n characters.
  35872. * Parameter
  35873. * $str1: The first string
  35874. * $str2: The second string
  35875. * $len: The length of strings to be used in the comparison.
  35876. * Return
  35877. * Returns < 0 if str1 is less than str2; > 0 if str1 is greater
  35878. * than str2, and 0 if they are equal.
  35879. */
  35880. static int jx9Builtin_strncasecmp(jx9_context *pCtx, int nArg, jx9_value **apArg)
  35881. {
  35882. const char *z1, *z2;
  35883. int res;
  35884. int n;
  35885. if( nArg < 3 ){
  35886. /* Perform a standard comparison */
  35887. return jx9Builtin_strcasecmp(pCtx, nArg, apArg);
  35888. }
  35889. /* Desired comparison length */
  35890. n = jx9_value_to_int(apArg[2]);
  35891. if( n < 0 ){
  35892. /* Invalid length */
  35893. jx9_result_int(pCtx, -1);
  35894. return JX9_OK;
  35895. }
  35896. /* Perform the comparison */
  35897. z1 = jx9_value_to_string(apArg[0], 0);
  35898. z2 = jx9_value_to_string(apArg[1], 0);
  35899. res = SyStrnicmp(z1, z2, (sxu32)n);
  35900. /* Comparison result */
  35901. jx9_result_int(pCtx, res);
  35902. return JX9_OK;
  35903. }
  35904. /*
  35905. * Implode context [i.e: it's private data].
  35906. * A pointer to the following structure is forwarded
  35907. * verbatim to the array walker callback defined below.
  35908. */
  35909. struct implode_data {
  35910. jx9_context *pCtx; /* Call context */
  35911. int bRecursive; /* TRUE if recursive implode [this is a symisc eXtension] */
  35912. const char *zSep; /* Arguments separator if any */
  35913. int nSeplen; /* Separator length */
  35914. int bFirst; /* TRUE if first call */
  35915. int nRecCount; /* Recursion count to avoid infinite loop */
  35916. };
  35917. /*
  35918. * Implode walker callback for the [jx9_array_walk()] interface.
  35919. * The following routine is invoked for each array entry passed
  35920. * to the implode() function.
  35921. */
  35922. static int implode_callback(jx9_value *pKey, jx9_value *pValue, void *pUserData)
  35923. {
  35924. struct implode_data *pData = (struct implode_data *)pUserData;
  35925. const char *zData;
  35926. int nLen;
  35927. if( pData->bRecursive && jx9_value_is_json_array(pValue) && pData->nRecCount < 32 ){
  35928. if( pData->nSeplen > 0 ){
  35929. if( !pData->bFirst ){
  35930. /* append the separator first */
  35931. jx9_result_string(pData->pCtx, pData->zSep, pData->nSeplen);
  35932. }else{
  35933. pData->bFirst = 0;
  35934. }
  35935. }
  35936. /* Recurse */
  35937. pData->bFirst = 1;
  35938. pData->nRecCount++;
  35939. jx9HashmapWalk((jx9_hashmap *)pValue->x.pOther, implode_callback, pData);
  35940. pData->nRecCount--;
  35941. return JX9_OK;
  35942. }
  35943. /* Extract the string representation of the entry value */
  35944. zData = jx9_value_to_string(pValue, &nLen);
  35945. if( nLen > 0 ){
  35946. if( pData->nSeplen > 0 ){
  35947. if( !pData->bFirst ){
  35948. /* append the separator first */
  35949. jx9_result_string(pData->pCtx, pData->zSep, pData->nSeplen);
  35950. }else{
  35951. pData->bFirst = 0;
  35952. }
  35953. }
  35954. jx9_result_string(pData->pCtx, zData, nLen);
  35955. }else{
  35956. SXUNUSED(pKey); /* cc warning */
  35957. }
  35958. return JX9_OK;
  35959. }
  35960. /*
  35961. * string implode(string $glue, array $pieces, ...)
  35962. * string implode(array $pieces, ...)
  35963. * Join array elements with a string.
  35964. * $glue
  35965. * Defaults to an empty string. This is not the preferred usage of implode() as glue
  35966. * would be the second parameter and thus, the bad prototype would be used.
  35967. * $pieces
  35968. * The array of strings to implode.
  35969. * Return
  35970. * Returns a string containing a string representation of all the array elements in the same
  35971. * order, with the glue string between each element.
  35972. */
  35973. static int jx9Builtin_implode(jx9_context *pCtx, int nArg, jx9_value **apArg)
  35974. {
  35975. struct implode_data imp_data;
  35976. int i = 1;
  35977. if( nArg < 1 ){
  35978. /* Missing argument, return NULL */
  35979. jx9_result_null(pCtx);
  35980. return JX9_OK;
  35981. }
  35982. /* Prepare the implode context */
  35983. imp_data.pCtx = pCtx;
  35984. imp_data.bRecursive = 0;
  35985. imp_data.bFirst = 1;
  35986. imp_data.nRecCount = 0;
  35987. if( !jx9_value_is_json_array(apArg[0]) ){
  35988. imp_data.zSep = jx9_value_to_string(apArg[0], &imp_data.nSeplen);
  35989. }else{
  35990. imp_data.zSep = 0;
  35991. imp_data.nSeplen = 0;
  35992. i = 0;
  35993. }
  35994. jx9_result_string(pCtx, "", 0); /* Set an empty stirng */
  35995. /* Start the 'join' process */
  35996. while( i < nArg ){
  35997. if( jx9_value_is_json_array(apArg[i]) ){
  35998. /* Iterate throw array entries */
  35999. jx9_array_walk(apArg[i], implode_callback, &imp_data);
  36000. }else{
  36001. const char *zData;
  36002. int nLen;
  36003. /* Extract the string representation of the jx9 value */
  36004. zData = jx9_value_to_string(apArg[i], &nLen);
  36005. if( nLen > 0 ){
  36006. if( imp_data.nSeplen > 0 ){
  36007. if( !imp_data.bFirst ){
  36008. /* append the separator first */
  36009. jx9_result_string(pCtx, imp_data.zSep, imp_data.nSeplen);
  36010. }else{
  36011. imp_data.bFirst = 0;
  36012. }
  36013. }
  36014. jx9_result_string(pCtx, zData, nLen);
  36015. }
  36016. }
  36017. i++;
  36018. }
  36019. return JX9_OK;
  36020. }
  36021. /*
  36022. * string implode_recursive(string $glue, array $pieces, ...)
  36023. * Purpose
  36024. * Same as implode() but recurse on arrays.
  36025. * Example:
  36026. * $a = array('usr', array('home', 'dean'));
  36027. * print implode_recursive("/", $a);
  36028. * Will output
  36029. * usr/home/dean.
  36030. * While the standard implode would produce.
  36031. * usr/Array.
  36032. * Parameter
  36033. * Refer to implode().
  36034. * Return
  36035. * Refer to implode().
  36036. */
  36037. static int jx9Builtin_implode_recursive(jx9_context *pCtx, int nArg, jx9_value **apArg)
  36038. {
  36039. struct implode_data imp_data;
  36040. int i = 1;
  36041. if( nArg < 1 ){
  36042. /* Missing argument, return NULL */
  36043. jx9_result_null(pCtx);
  36044. return JX9_OK;
  36045. }
  36046. /* Prepare the implode context */
  36047. imp_data.pCtx = pCtx;
  36048. imp_data.bRecursive = 1;
  36049. imp_data.bFirst = 1;
  36050. imp_data.nRecCount = 0;
  36051. if( !jx9_value_is_json_array(apArg[0]) ){
  36052. imp_data.zSep = jx9_value_to_string(apArg[0], &imp_data.nSeplen);
  36053. }else{
  36054. imp_data.zSep = 0;
  36055. imp_data.nSeplen = 0;
  36056. i = 0;
  36057. }
  36058. jx9_result_string(pCtx, "", 0); /* Set an empty stirng */
  36059. /* Start the 'join' process */
  36060. while( i < nArg ){
  36061. if( jx9_value_is_json_array(apArg[i]) ){
  36062. /* Iterate throw array entries */
  36063. jx9_array_walk(apArg[i], implode_callback, &imp_data);
  36064. }else{
  36065. const char *zData;
  36066. int nLen;
  36067. /* Extract the string representation of the jx9 value */
  36068. zData = jx9_value_to_string(apArg[i], &nLen);
  36069. if( nLen > 0 ){
  36070. if( imp_data.nSeplen > 0 ){
  36071. if( !imp_data.bFirst ){
  36072. /* append the separator first */
  36073. jx9_result_string(pCtx, imp_data.zSep, imp_data.nSeplen);
  36074. }else{
  36075. imp_data.bFirst = 0;
  36076. }
  36077. }
  36078. jx9_result_string(pCtx, zData, nLen);
  36079. }
  36080. }
  36081. i++;
  36082. }
  36083. return JX9_OK;
  36084. }
  36085. /*
  36086. * array explode(string $delimiter, string $string[, int $limit ])
  36087. * Returns an array of strings, each of which is a substring of string
  36088. * formed by splitting it on boundaries formed by the string delimiter.
  36089. * Parameters
  36090. * $delimiter
  36091. * The boundary string.
  36092. * $string
  36093. * The input string.
  36094. * $limit
  36095. * If limit is set and positive, the returned array will contain a maximum
  36096. * of limit elements with the last element containing the rest of string.
  36097. * If the limit parameter is negative, all fields except the last -limit are returned.
  36098. * If the limit parameter is zero, then this is treated as 1.
  36099. * Returns
  36100. * Returns an array of strings created by splitting the string parameter
  36101. * on boundaries formed by the delimiter.
  36102. * If delimiter is an empty string (""), explode() will return FALSE.
  36103. * If delimiter contains a value that is not contained in string and a negative
  36104. * limit is used, then an empty array will be returned, otherwise an array containing string
  36105. * will be returned.
  36106. * NOTE:
  36107. * Negative limit is not supported.
  36108. */
  36109. static int jx9Builtin_explode(jx9_context *pCtx, int nArg, jx9_value **apArg)
  36110. {
  36111. const char *zDelim, *zString, *zCur, *zEnd;
  36112. int nDelim, nStrlen, iLimit;
  36113. jx9_value *pArray;
  36114. jx9_value *pValue;
  36115. sxu32 nOfft;
  36116. sxi32 rc;
  36117. if( nArg < 2 ){
  36118. /* Missing arguments, return FALSE */
  36119. jx9_result_bool(pCtx, 0);
  36120. return JX9_OK;
  36121. }
  36122. /* Extract the delimiter */
  36123. zDelim = jx9_value_to_string(apArg[0], &nDelim);
  36124. if( nDelim < 1 ){
  36125. /* Empty delimiter, return FALSE */
  36126. jx9_result_bool(pCtx, 0);
  36127. return JX9_OK;
  36128. }
  36129. /* Extract the string */
  36130. zString = jx9_value_to_string(apArg[1], &nStrlen);
  36131. if( nStrlen < 1 ){
  36132. /* Empty delimiter, return FALSE */
  36133. jx9_result_bool(pCtx, 0);
  36134. return JX9_OK;
  36135. }
  36136. /* Point to the end of the string */
  36137. zEnd = &zString[nStrlen];
  36138. /* Create the array */
  36139. pArray = jx9_context_new_array(pCtx);
  36140. pValue = jx9_context_new_scalar(pCtx);
  36141. if( pArray == 0 || pValue == 0 ){
  36142. /* Out of memory, return FALSE */
  36143. jx9_result_bool(pCtx, 0);
  36144. return JX9_OK;
  36145. }
  36146. /* Set a defualt limit */
  36147. iLimit = SXI32_HIGH;
  36148. if( nArg > 2 ){
  36149. iLimit = jx9_value_to_int(apArg[2]);
  36150. if( iLimit < 0 ){
  36151. iLimit = -iLimit;
  36152. }
  36153. if( iLimit == 0 ){
  36154. iLimit = 1;
  36155. }
  36156. iLimit--;
  36157. }
  36158. /* Start exploding */
  36159. for(;;){
  36160. if( zString >= zEnd ){
  36161. /* No more entry to process */
  36162. break;
  36163. }
  36164. rc = SyBlobSearch(zString, (sxu32)(zEnd-zString), zDelim, nDelim, &nOfft);
  36165. if( rc != SXRET_OK || iLimit <= (int)jx9_array_count(pArray) ){
  36166. /* Limit reached, insert the rest of the string and break */
  36167. if( zEnd > zString ){
  36168. jx9_value_string(pValue, zString, (int)(zEnd-zString));
  36169. jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pValue);
  36170. }
  36171. break;
  36172. }
  36173. /* Point to the desired offset */
  36174. zCur = &zString[nOfft];
  36175. if( zCur > zString ){
  36176. /* Perform the store operation */
  36177. jx9_value_string(pValue, zString, (int)(zCur-zString));
  36178. jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pValue);
  36179. }
  36180. /* Point beyond the delimiter */
  36181. zString = &zCur[nDelim];
  36182. /* Reset the cursor */
  36183. jx9_value_reset_string_cursor(pValue);
  36184. }
  36185. /* Return the freshly created array */
  36186. jx9_result_value(pCtx, pArray);
  36187. /* NOTE that every allocated jx9_value will be automatically
  36188. * released as soon we return from this foregin function.
  36189. */
  36190. return JX9_OK;
  36191. }
  36192. /*
  36193. * string trim(string $str[, string $charlist ])
  36194. * Strip whitespace (or other characters) from the beginning and end of a string.
  36195. * Parameters
  36196. * $str
  36197. * The string that will be trimmed.
  36198. * $charlist
  36199. * Optionally, the stripped characters can also be specified using the charlist parameter.
  36200. * Simply list all characters that you want to be stripped.
  36201. * With .. you can specify a range of characters.
  36202. * Returns.
  36203. * Thr processed string.
  36204. */
  36205. static int jx9Builtin_trim(jx9_context *pCtx, int nArg, jx9_value **apArg)
  36206. {
  36207. const char *zString;
  36208. int nLen;
  36209. if( nArg < 1 ){
  36210. /* Missing arguments, return null */
  36211. jx9_result_null(pCtx);
  36212. return JX9_OK;
  36213. }
  36214. /* Extract the target string */
  36215. zString = jx9_value_to_string(apArg[0], &nLen);
  36216. if( nLen < 1 ){
  36217. /* Empty string, return */
  36218. jx9_result_string(pCtx, "", 0);
  36219. return JX9_OK;
  36220. }
  36221. /* Start the trim process */
  36222. if( nArg < 2 ){
  36223. SyString sStr;
  36224. /* Remove white spaces and NUL bytes */
  36225. SyStringInitFromBuf(&sStr, zString, nLen);
  36226. SyStringFullTrimSafe(&sStr);
  36227. jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte);
  36228. }else{
  36229. /* Char list */
  36230. const char *zList;
  36231. int nListlen;
  36232. zList = jx9_value_to_string(apArg[1], &nListlen);
  36233. if( nListlen < 1 ){
  36234. /* Return the string unchanged */
  36235. jx9_result_string(pCtx, zString, nLen);
  36236. }else{
  36237. const char *zEnd = &zString[nLen];
  36238. const char *zCur = zString;
  36239. const char *zPtr;
  36240. int i;
  36241. /* Left trim */
  36242. for(;;){
  36243. if( zCur >= zEnd ){
  36244. break;
  36245. }
  36246. zPtr = zCur;
  36247. for( i = 0 ; i < nListlen ; i++ ){
  36248. if( zCur < zEnd && zCur[0] == zList[i] ){
  36249. zCur++;
  36250. }
  36251. }
  36252. if( zCur == zPtr ){
  36253. /* No match, break immediately */
  36254. break;
  36255. }
  36256. }
  36257. /* Right trim */
  36258. zEnd--;
  36259. for(;;){
  36260. if( zEnd <= zCur ){
  36261. break;
  36262. }
  36263. zPtr = zEnd;
  36264. for( i = 0 ; i < nListlen ; i++ ){
  36265. if( zEnd > zCur && zEnd[0] == zList[i] ){
  36266. zEnd--;
  36267. }
  36268. }
  36269. if( zEnd == zPtr ){
  36270. break;
  36271. }
  36272. }
  36273. if( zCur >= zEnd ){
  36274. /* Return the empty string */
  36275. jx9_result_string(pCtx, "", 0);
  36276. }else{
  36277. zEnd++;
  36278. jx9_result_string(pCtx, zCur, (int)(zEnd-zCur));
  36279. }
  36280. }
  36281. }
  36282. return JX9_OK;
  36283. }
  36284. /*
  36285. * string rtrim(string $str[, string $charlist ])
  36286. * Strip whitespace (or other characters) from the end of a string.
  36287. * Parameters
  36288. * $str
  36289. * The string that will be trimmed.
  36290. * $charlist
  36291. * Optionally, the stripped characters can also be specified using the charlist parameter.
  36292. * Simply list all characters that you want to be stripped.
  36293. * With .. you can specify a range of characters.
  36294. * Returns.
  36295. * Thr processed string.
  36296. */
  36297. static int jx9Builtin_rtrim(jx9_context *pCtx, int nArg, jx9_value **apArg)
  36298. {
  36299. const char *zString;
  36300. int nLen;
  36301. if( nArg < 1 ){
  36302. /* Missing arguments, return null */
  36303. jx9_result_null(pCtx);
  36304. return JX9_OK;
  36305. }
  36306. /* Extract the target string */
  36307. zString = jx9_value_to_string(apArg[0], &nLen);
  36308. if( nLen < 1 ){
  36309. /* Empty string, return */
  36310. jx9_result_string(pCtx, "", 0);
  36311. return JX9_OK;
  36312. }
  36313. /* Start the trim process */
  36314. if( nArg < 2 ){
  36315. SyString sStr;
  36316. /* Remove white spaces and NUL bytes*/
  36317. SyStringInitFromBuf(&sStr, zString, nLen);
  36318. SyStringRightTrimSafe(&sStr);
  36319. jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte);
  36320. }else{
  36321. /* Char list */
  36322. const char *zList;
  36323. int nListlen;
  36324. zList = jx9_value_to_string(apArg[1], &nListlen);
  36325. if( nListlen < 1 ){
  36326. /* Return the string unchanged */
  36327. jx9_result_string(pCtx, zString, nLen);
  36328. }else{
  36329. const char *zEnd = &zString[nLen - 1];
  36330. const char *zCur = zString;
  36331. const char *zPtr;
  36332. int i;
  36333. /* Right trim */
  36334. for(;;){
  36335. if( zEnd <= zCur ){
  36336. break;
  36337. }
  36338. zPtr = zEnd;
  36339. for( i = 0 ; i < nListlen ; i++ ){
  36340. if( zEnd > zCur && zEnd[0] == zList[i] ){
  36341. zEnd--;
  36342. }
  36343. }
  36344. if( zEnd == zPtr ){
  36345. break;
  36346. }
  36347. }
  36348. if( zEnd <= zCur ){
  36349. /* Return the empty string */
  36350. jx9_result_string(pCtx, "", 0);
  36351. }else{
  36352. zEnd++;
  36353. jx9_result_string(pCtx, zCur, (int)(zEnd-zCur));
  36354. }
  36355. }
  36356. }
  36357. return JX9_OK;
  36358. }
  36359. /*
  36360. * string ltrim(string $str[, string $charlist ])
  36361. * Strip whitespace (or other characters) from the beginning and end of a string.
  36362. * Parameters
  36363. * $str
  36364. * The string that will be trimmed.
  36365. * $charlist
  36366. * Optionally, the stripped characters can also be specified using the charlist parameter.
  36367. * Simply list all characters that you want to be stripped.
  36368. * With .. you can specify a range of characters.
  36369. * Returns.
  36370. * The processed string.
  36371. */
  36372. static int jx9Builtin_ltrim(jx9_context *pCtx, int nArg, jx9_value **apArg)
  36373. {
  36374. const char *zString;
  36375. int nLen;
  36376. if( nArg < 1 ){
  36377. /* Missing arguments, return null */
  36378. jx9_result_null(pCtx);
  36379. return JX9_OK;
  36380. }
  36381. /* Extract the target string */
  36382. zString = jx9_value_to_string(apArg[0], &nLen);
  36383. if( nLen < 1 ){
  36384. /* Empty string, return */
  36385. jx9_result_string(pCtx, "", 0);
  36386. return JX9_OK;
  36387. }
  36388. /* Start the trim process */
  36389. if( nArg < 2 ){
  36390. SyString sStr;
  36391. /* Remove white spaces and NUL byte */
  36392. SyStringInitFromBuf(&sStr, zString, nLen);
  36393. SyStringLeftTrimSafe(&sStr);
  36394. jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte);
  36395. }else{
  36396. /* Char list */
  36397. const char *zList;
  36398. int nListlen;
  36399. zList = jx9_value_to_string(apArg[1], &nListlen);
  36400. if( nListlen < 1 ){
  36401. /* Return the string unchanged */
  36402. jx9_result_string(pCtx, zString, nLen);
  36403. }else{
  36404. const char *zEnd = &zString[nLen];
  36405. const char *zCur = zString;
  36406. const char *zPtr;
  36407. int i;
  36408. /* Left trim */
  36409. for(;;){
  36410. if( zCur >= zEnd ){
  36411. break;
  36412. }
  36413. zPtr = zCur;
  36414. for( i = 0 ; i < nListlen ; i++ ){
  36415. if( zCur < zEnd && zCur[0] == zList[i] ){
  36416. zCur++;
  36417. }
  36418. }
  36419. if( zCur == zPtr ){
  36420. /* No match, break immediately */
  36421. break;
  36422. }
  36423. }
  36424. if( zCur >= zEnd ){
  36425. /* Return the empty string */
  36426. jx9_result_string(pCtx, "", 0);
  36427. }else{
  36428. jx9_result_string(pCtx, zCur, (int)(zEnd-zCur));
  36429. }
  36430. }
  36431. }
  36432. return JX9_OK;
  36433. }
  36434. /*
  36435. * string strtolower(string $str)
  36436. * Make a string lowercase.
  36437. * Parameters
  36438. * $str
  36439. * The input string.
  36440. * Returns.
  36441. * The lowercased string.
  36442. */
  36443. static int jx9Builtin_strtolower(jx9_context *pCtx, int nArg, jx9_value **apArg)
  36444. {
  36445. const char *zString, *zCur, *zEnd;
  36446. int nLen;
  36447. if( nArg < 1 ){
  36448. /* Missing arguments, return null */
  36449. jx9_result_null(pCtx);
  36450. return JX9_OK;
  36451. }
  36452. /* Extract the target string */
  36453. zString = jx9_value_to_string(apArg[0], &nLen);
  36454. if( nLen < 1 ){
  36455. /* Empty string, return */
  36456. jx9_result_string(pCtx, "", 0);
  36457. return JX9_OK;
  36458. }
  36459. /* Perform the requested operation */
  36460. zEnd = &zString[nLen];
  36461. for(;;){
  36462. if( zString >= zEnd ){
  36463. /* No more input, break immediately */
  36464. break;
  36465. }
  36466. if( (unsigned char)zString[0] >= 0xc0 ){
  36467. /* UTF-8 stream, output verbatim */
  36468. zCur = zString;
  36469. zString++;
  36470. while( zString < zEnd && ((unsigned char)zString[0] & 0xc0) == 0x80){
  36471. zString++;
  36472. }
  36473. /* Append UTF-8 stream */
  36474. jx9_result_string(pCtx, zCur, (int)(zString-zCur));
  36475. }else{
  36476. int c = zString[0];
  36477. if( SyisUpper(c) ){
  36478. c = SyToLower(zString[0]);
  36479. }
  36480. /* Append character */
  36481. jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
  36482. /* Advance the cursor */
  36483. zString++;
  36484. }
  36485. }
  36486. return JX9_OK;
  36487. }
  36488. /*
  36489. * string strtolower(string $str)
  36490. * Make a string uppercase.
  36491. * Parameters
  36492. * $str
  36493. * The input string.
  36494. * Returns.
  36495. * The uppercased string.
  36496. */
  36497. static int jx9Builtin_strtoupper(jx9_context *pCtx, int nArg, jx9_value **apArg)
  36498. {
  36499. const char *zString, *zCur, *zEnd;
  36500. int nLen;
  36501. if( nArg < 1 ){
  36502. /* Missing arguments, return null */
  36503. jx9_result_null(pCtx);
  36504. return JX9_OK;
  36505. }
  36506. /* Extract the target string */
  36507. zString = jx9_value_to_string(apArg[0], &nLen);
  36508. if( nLen < 1 ){
  36509. /* Empty string, return */
  36510. jx9_result_string(pCtx, "", 0);
  36511. return JX9_OK;
  36512. }
  36513. /* Perform the requested operation */
  36514. zEnd = &zString[nLen];
  36515. for(;;){
  36516. if( zString >= zEnd ){
  36517. /* No more input, break immediately */
  36518. break;
  36519. }
  36520. if( (unsigned char)zString[0] >= 0xc0 ){
  36521. /* UTF-8 stream, output verbatim */
  36522. zCur = zString;
  36523. zString++;
  36524. while( zString < zEnd && ((unsigned char)zString[0] & 0xc0) == 0x80){
  36525. zString++;
  36526. }
  36527. /* Append UTF-8 stream */
  36528. jx9_result_string(pCtx, zCur, (int)(zString-zCur));
  36529. }else{
  36530. int c = zString[0];
  36531. if( SyisLower(c) ){
  36532. c = SyToUpper(zString[0]);
  36533. }
  36534. /* Append character */
  36535. jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
  36536. /* Advance the cursor */
  36537. zString++;
  36538. }
  36539. }
  36540. return JX9_OK;
  36541. }
  36542. /*
  36543. * int ord(string $string)
  36544. * Returns the ASCII value of the first character of string.
  36545. * Parameters
  36546. * $str
  36547. * The input string.
  36548. * Returns.
  36549. * The ASCII value as an integer.
  36550. */
  36551. static int jx9Builtin_ord(jx9_context *pCtx, int nArg, jx9_value **apArg)
  36552. {
  36553. const char *zString;
  36554. int nLen, c;
  36555. if( nArg < 1 ){
  36556. /* Missing arguments, return -1 */
  36557. jx9_result_int(pCtx, -1);
  36558. return JX9_OK;
  36559. }
  36560. /* Extract the target string */
  36561. zString = jx9_value_to_string(apArg[0], &nLen);
  36562. if( nLen < 1 ){
  36563. /* Empty string, return -1 */
  36564. jx9_result_int(pCtx, -1);
  36565. return JX9_OK;
  36566. }
  36567. /* Extract the ASCII value of the first character */
  36568. c = zString[0];
  36569. /* Return that value */
  36570. jx9_result_int(pCtx, c);
  36571. return JX9_OK;
  36572. }
  36573. /*
  36574. * string chr(int $ascii)
  36575. * Returns a one-character string containing the character specified by ascii.
  36576. * Parameters
  36577. * $ascii
  36578. * The ascii code.
  36579. * Returns.
  36580. * The specified character.
  36581. */
  36582. static int jx9Builtin_chr(jx9_context *pCtx, int nArg, jx9_value **apArg)
  36583. {
  36584. int c;
  36585. if( nArg < 1 ){
  36586. /* Missing arguments, return null */
  36587. jx9_result_null(pCtx);
  36588. return JX9_OK;
  36589. }
  36590. /* Extract the ASCII value */
  36591. c = jx9_value_to_int(apArg[0]);
  36592. /* Return the specified character */
  36593. jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
  36594. return JX9_OK;
  36595. }
  36596. /*
  36597. * Binary to hex consumer callback.
  36598. * This callback is the default consumer used by the hash functions
  36599. * [i.e: bin2hex(), md5(), sha1(), md5_file() ... ] defined below.
  36600. */
  36601. static int HashConsumer(const void *pData, unsigned int nLen, void *pUserData)
  36602. {
  36603. /* Append hex chunk verbatim */
  36604. jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen);
  36605. return SXRET_OK;
  36606. }
  36607. /*
  36608. * string bin2hex(string $str)
  36609. * Convert binary data into hexadecimal representation.
  36610. * Parameters
  36611. * $str
  36612. * The input string.
  36613. * Returns.
  36614. * Returns the hexadecimal representation of the given string.
  36615. */
  36616. static int jx9Builtin_bin2hex(jx9_context *pCtx, int nArg, jx9_value **apArg)
  36617. {
  36618. const char *zString;
  36619. int nLen;
  36620. if( nArg < 1 ){
  36621. /* Missing arguments, return null */
  36622. jx9_result_null(pCtx);
  36623. return JX9_OK;
  36624. }
  36625. /* Extract the target string */
  36626. zString = jx9_value_to_string(apArg[0], &nLen);
  36627. if( nLen < 1 ){
  36628. /* Empty string, return */
  36629. jx9_result_string(pCtx, "", 0);
  36630. return JX9_OK;
  36631. }
  36632. /* Perform the requested operation */
  36633. SyBinToHexConsumer((const void *)zString, (sxu32)nLen, HashConsumer, pCtx);
  36634. return JX9_OK;
  36635. }
  36636. /* Search callback signature */
  36637. typedef sxi32 (*ProcStringMatch)(const void *, sxu32, const void *, sxu32, sxu32 *);
  36638. /*
  36639. * Case-insensitive pattern match.
  36640. * Brute force is the default search method used here.
  36641. * This is due to the fact that brute-forcing works quite
  36642. * well for short/medium texts on modern hardware.
  36643. */
  36644. static sxi32 iPatternMatch(const void *pText, sxu32 nLen, const void *pPattern, sxu32 iPatLen, sxu32 *pOfft)
  36645. {
  36646. const char *zpIn = (const char *)pPattern;
  36647. const char *zIn = (const char *)pText;
  36648. const char *zpEnd = &zpIn[iPatLen];
  36649. const char *zEnd = &zIn[nLen];
  36650. const char *zPtr, *zPtr2;
  36651. int c, d;
  36652. if( iPatLen > nLen ){
  36653. /* Don't bother processing */
  36654. return SXERR_NOTFOUND;
  36655. }
  36656. for(;;){
  36657. if( zIn >= zEnd ){
  36658. break;
  36659. }
  36660. c = SyToLower(zIn[0]);
  36661. d = SyToLower(zpIn[0]);
  36662. if( c == d ){
  36663. zPtr = &zIn[1];
  36664. zPtr2 = &zpIn[1];
  36665. for(;;){
  36666. if( zPtr2 >= zpEnd ){
  36667. /* Pattern found */
  36668. if( pOfft ){ *pOfft = (sxu32)(zIn-(const char *)pText); }
  36669. return SXRET_OK;
  36670. }
  36671. if( zPtr >= zEnd ){
  36672. break;
  36673. }
  36674. c = SyToLower(zPtr[0]);
  36675. d = SyToLower(zPtr2[0]);
  36676. if( c != d ){
  36677. break;
  36678. }
  36679. zPtr++; zPtr2++;
  36680. }
  36681. }
  36682. zIn++;
  36683. }
  36684. /* Pattern not found */
  36685. return SXERR_NOTFOUND;
  36686. }
  36687. /*
  36688. * string strstr(string $haystack, string $needle[, bool $before_needle = false ])
  36689. * Find the first occurrence of a string.
  36690. * Parameters
  36691. * $haystack
  36692. * The input string.
  36693. * $needle
  36694. * Search pattern (must be a string).
  36695. * $before_needle
  36696. * If TRUE, strstr() returns the part of the haystack before the first occurrence
  36697. * of the needle (excluding the needle).
  36698. * Return
  36699. * Returns the portion of string, or FALSE if needle is not found.
  36700. */
  36701. static int jx9Builtin_strstr(jx9_context *pCtx, int nArg, jx9_value **apArg)
  36702. {
  36703. ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */
  36704. const char *zBlob, *zPattern;
  36705. int nLen, nPatLen;
  36706. sxu32 nOfft;
  36707. sxi32 rc;
  36708. if( nArg < 2 ){
  36709. /* Missing arguments, return FALSE */
  36710. jx9_result_bool(pCtx, 0);
  36711. return JX9_OK;
  36712. }
  36713. /* Extract the needle and the haystack */
  36714. zBlob = jx9_value_to_string(apArg[0], &nLen);
  36715. zPattern = jx9_value_to_string(apArg[1], &nPatLen);
  36716. nOfft = 0; /* cc warning */
  36717. if( nLen > 0 && nPatLen > 0 ){
  36718. int before = 0;
  36719. /* Perform the lookup */
  36720. rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
  36721. if( rc != SXRET_OK ){
  36722. /* Pattern not found, return FALSE */
  36723. jx9_result_bool(pCtx, 0);
  36724. return JX9_OK;
  36725. }
  36726. /* Return the portion of the string */
  36727. if( nArg > 2 ){
  36728. before = jx9_value_to_int(apArg[2]);
  36729. }
  36730. if( before ){
  36731. jx9_result_string(pCtx, zBlob, (int)(&zBlob[nOfft]-zBlob));
  36732. }else{
  36733. jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft]));
  36734. }
  36735. }else{
  36736. jx9_result_bool(pCtx, 0);
  36737. }
  36738. return JX9_OK;
  36739. }
  36740. /*
  36741. * string stristr(string $haystack, string $needle[, bool $before_needle = false ])
  36742. * Case-insensitive strstr().
  36743. * Parameters
  36744. * $haystack
  36745. * The input string.
  36746. * $needle
  36747. * Search pattern (must be a string).
  36748. * $before_needle
  36749. * If TRUE, strstr() returns the part of the haystack before the first occurrence
  36750. * of the needle (excluding the needle).
  36751. * Return
  36752. * Returns the portion of string, or FALSE if needle is not found.
  36753. */
  36754. static int jx9Builtin_stristr(jx9_context *pCtx, int nArg, jx9_value **apArg)
  36755. {
  36756. ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */
  36757. const char *zBlob, *zPattern;
  36758. int nLen, nPatLen;
  36759. sxu32 nOfft;
  36760. sxi32 rc;
  36761. if( nArg < 2 ){
  36762. /* Missing arguments, return FALSE */
  36763. jx9_result_bool(pCtx, 0);
  36764. return JX9_OK;
  36765. }
  36766. /* Extract the needle and the haystack */
  36767. zBlob = jx9_value_to_string(apArg[0], &nLen);
  36768. zPattern = jx9_value_to_string(apArg[1], &nPatLen);
  36769. nOfft = 0; /* cc warning */
  36770. if( nLen > 0 && nPatLen > 0 ){
  36771. int before = 0;
  36772. /* Perform the lookup */
  36773. rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
  36774. if( rc != SXRET_OK ){
  36775. /* Pattern not found, return FALSE */
  36776. jx9_result_bool(pCtx, 0);
  36777. return JX9_OK;
  36778. }
  36779. /* Return the portion of the string */
  36780. if( nArg > 2 ){
  36781. before = jx9_value_to_int(apArg[2]);
  36782. }
  36783. if( before ){
  36784. jx9_result_string(pCtx, zBlob, (int)(&zBlob[nOfft]-zBlob));
  36785. }else{
  36786. jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft]));
  36787. }
  36788. }else{
  36789. jx9_result_bool(pCtx, 0);
  36790. }
  36791. return JX9_OK;
  36792. }
  36793. /*
  36794. * int strpos(string $haystack, string $needle [, int $offset = 0 ] )
  36795. * Returns the numeric position of the first occurrence of needle in the haystack string.
  36796. * Parameters
  36797. * $haystack
  36798. * The input string.
  36799. * $needle
  36800. * Search pattern (must be a string).
  36801. * $offset
  36802. * This optional offset parameter allows you to specify which character in haystack
  36803. * to start searching. The position returned is still relative to the beginning
  36804. * of haystack.
  36805. * Return
  36806. * Returns the position as an integer.If needle is not found, strpos() will return FALSE.
  36807. */
  36808. static int jx9Builtin_strpos(jx9_context *pCtx, int nArg, jx9_value **apArg)
  36809. {
  36810. ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */
  36811. const char *zBlob, *zPattern;
  36812. int nLen, nPatLen, nStart;
  36813. sxu32 nOfft;
  36814. sxi32 rc;
  36815. if( nArg < 2 ){
  36816. /* Missing arguments, return FALSE */
  36817. jx9_result_bool(pCtx, 0);
  36818. return JX9_OK;
  36819. }
  36820. /* Extract the needle and the haystack */
  36821. zBlob = jx9_value_to_string(apArg[0], &nLen);
  36822. zPattern = jx9_value_to_string(apArg[1], &nPatLen);
  36823. nOfft = 0; /* cc warning */
  36824. nStart = 0;
  36825. /* Peek the starting offset if available */
  36826. if( nArg > 2 ){
  36827. nStart = jx9_value_to_int(apArg[2]);
  36828. if( nStart < 0 ){
  36829. nStart = -nStart;
  36830. }
  36831. if( nStart >= nLen ){
  36832. /* Invalid offset */
  36833. nStart = 0;
  36834. }else{
  36835. zBlob += nStart;
  36836. nLen -= nStart;
  36837. }
  36838. }
  36839. if( nLen > 0 && nPatLen > 0 ){
  36840. /* Perform the lookup */
  36841. rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
  36842. if( rc != SXRET_OK ){
  36843. /* Pattern not found, return FALSE */
  36844. jx9_result_bool(pCtx, 0);
  36845. return JX9_OK;
  36846. }
  36847. /* Return the pattern position */
  36848. jx9_result_int64(pCtx, (jx9_int64)(nOfft+nStart));
  36849. }else{
  36850. jx9_result_bool(pCtx, 0);
  36851. }
  36852. return JX9_OK;
  36853. }
  36854. /*
  36855. * int stripos(string $haystack, string $needle [, int $offset = 0 ] )
  36856. * Case-insensitive strpos.
  36857. * Parameters
  36858. * $haystack
  36859. * The input string.
  36860. * $needle
  36861. * Search pattern (must be a string).
  36862. * $offset
  36863. * This optional offset parameter allows you to specify which character in haystack
  36864. * to start searching. The position returned is still relative to the beginning
  36865. * of haystack.
  36866. * Return
  36867. * Returns the position as an integer.If needle is not found, strpos() will return FALSE.
  36868. */
  36869. static int jx9Builtin_stripos(jx9_context *pCtx, int nArg, jx9_value **apArg)
  36870. {
  36871. ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */
  36872. const char *zBlob, *zPattern;
  36873. int nLen, nPatLen, nStart;
  36874. sxu32 nOfft;
  36875. sxi32 rc;
  36876. if( nArg < 2 ){
  36877. /* Missing arguments, return FALSE */
  36878. jx9_result_bool(pCtx, 0);
  36879. return JX9_OK;
  36880. }
  36881. /* Extract the needle and the haystack */
  36882. zBlob = jx9_value_to_string(apArg[0], &nLen);
  36883. zPattern = jx9_value_to_string(apArg[1], &nPatLen);
  36884. nOfft = 0; /* cc warning */
  36885. nStart = 0;
  36886. /* Peek the starting offset if available */
  36887. if( nArg > 2 ){
  36888. nStart = jx9_value_to_int(apArg[2]);
  36889. if( nStart < 0 ){
  36890. nStart = -nStart;
  36891. }
  36892. if( nStart >= nLen ){
  36893. /* Invalid offset */
  36894. nStart = 0;
  36895. }else{
  36896. zBlob += nStart;
  36897. nLen -= nStart;
  36898. }
  36899. }
  36900. if( nLen > 0 && nPatLen > 0 ){
  36901. /* Perform the lookup */
  36902. rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft);
  36903. if( rc != SXRET_OK ){
  36904. /* Pattern not found, return FALSE */
  36905. jx9_result_bool(pCtx, 0);
  36906. return JX9_OK;
  36907. }
  36908. /* Return the pattern position */
  36909. jx9_result_int64(pCtx, (jx9_int64)(nOfft+nStart));
  36910. }else{
  36911. jx9_result_bool(pCtx, 0);
  36912. }
  36913. return JX9_OK;
  36914. }
  36915. /*
  36916. * int strrpos(string $haystack, string $needle [, int $offset = 0 ] )
  36917. * Find the numeric position of the last occurrence of needle in the haystack string.
  36918. * Parameters
  36919. * $haystack
  36920. * The input string.
  36921. * $needle
  36922. * Search pattern (must be a string).
  36923. * $offset
  36924. * If specified, search will start this number of characters counted from the beginning
  36925. * of the string. If the value is negative, search will instead start from that many
  36926. * characters from the end of the string, searching backwards.
  36927. * Return
  36928. * Returns the position as an integer.If needle is not found, strrpos() will return FALSE.
  36929. */
  36930. static int jx9Builtin_strrpos(jx9_context *pCtx, int nArg, jx9_value **apArg)
  36931. {
  36932. const char *zStart, *zBlob, *zPattern, *zPtr, *zEnd;
  36933. ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */
  36934. int nLen, nPatLen;
  36935. sxu32 nOfft;
  36936. sxi32 rc;
  36937. if( nArg < 2 ){
  36938. /* Missing arguments, return FALSE */
  36939. jx9_result_bool(pCtx, 0);
  36940. return JX9_OK;
  36941. }
  36942. /* Extract the needle and the haystack */
  36943. zBlob = jx9_value_to_string(apArg[0], &nLen);
  36944. zPattern = jx9_value_to_string(apArg[1], &nPatLen);
  36945. /* Point to the end of the pattern */
  36946. zPtr = &zBlob[nLen - 1];
  36947. zEnd = &zBlob[nLen];
  36948. /* Save the starting posistion */
  36949. zStart = zBlob;
  36950. nOfft = 0; /* cc warning */
  36951. /* Peek the starting offset if available */
  36952. if( nArg > 2 ){
  36953. int nStart;
  36954. nStart = jx9_value_to_int(apArg[2]);
  36955. if( nStart < 0 ){
  36956. nStart = -nStart;
  36957. if( nStart >= nLen ){
  36958. /* Invalid offset */
  36959. jx9_result_bool(pCtx, 0);
  36960. return JX9_OK;
  36961. }else{
  36962. nLen -= nStart;
  36963. zPtr = &zBlob[nLen - 1];
  36964. zEnd = &zBlob[nLen];
  36965. }
  36966. }else{
  36967. if( nStart >= nLen ){
  36968. /* Invalid offset */
  36969. jx9_result_bool(pCtx, 0);
  36970. return JX9_OK;
  36971. }else{
  36972. zBlob += nStart;
  36973. nLen -= nStart;
  36974. }
  36975. }
  36976. }
  36977. if( nLen > 0 && nPatLen > 0 ){
  36978. /* Perform the lookup */
  36979. for(;;){
  36980. if( zBlob >= zPtr ){
  36981. break;
  36982. }
  36983. rc = xPatternMatch((const void *)zPtr, (sxu32)(zEnd-zPtr), (const void *)zPattern, (sxu32)nPatLen, &nOfft);
  36984. if( rc == SXRET_OK ){
  36985. /* Pattern found, return it's position */
  36986. jx9_result_int64(pCtx, (jx9_int64)(&zPtr[nOfft] - zStart));
  36987. return JX9_OK;
  36988. }
  36989. zPtr--;
  36990. }
  36991. /* Pattern not found, return FALSE */
  36992. jx9_result_bool(pCtx, 0);
  36993. }else{
  36994. jx9_result_bool(pCtx, 0);
  36995. }
  36996. return JX9_OK;
  36997. }
  36998. /*
  36999. * int strripos(string $haystack, string $needle [, int $offset = 0 ] )
  37000. * Case-insensitive strrpos.
  37001. * Parameters
  37002. * $haystack
  37003. * The input string.
  37004. * $needle
  37005. * Search pattern (must be a string).
  37006. * $offset
  37007. * If specified, search will start this number of characters counted from the beginning
  37008. * of the string. If the value is negative, search will instead start from that many
  37009. * characters from the end of the string, searching backwards.
  37010. * Return
  37011. * Returns the position as an integer.If needle is not found, strrpos() will return FALSE.
  37012. */
  37013. static int jx9Builtin_strripos(jx9_context *pCtx, int nArg, jx9_value **apArg)
  37014. {
  37015. const char *zStart, *zBlob, *zPattern, *zPtr, *zEnd;
  37016. ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */
  37017. int nLen, nPatLen;
  37018. sxu32 nOfft;
  37019. sxi32 rc;
  37020. if( nArg < 2 ){
  37021. /* Missing arguments, return FALSE */
  37022. jx9_result_bool(pCtx, 0);
  37023. return JX9_OK;
  37024. }
  37025. /* Extract the needle and the haystack */
  37026. zBlob = jx9_value_to_string(apArg[0], &nLen);
  37027. zPattern = jx9_value_to_string(apArg[1], &nPatLen);
  37028. /* Point to the end of the pattern */
  37029. zPtr = &zBlob[nLen - 1];
  37030. zEnd = &zBlob[nLen];
  37031. /* Save the starting posistion */
  37032. zStart = zBlob;
  37033. nOfft = 0; /* cc warning */
  37034. /* Peek the starting offset if available */
  37035. if( nArg > 2 ){
  37036. int nStart;
  37037. nStart = jx9_value_to_int(apArg[2]);
  37038. if( nStart < 0 ){
  37039. nStart = -nStart;
  37040. if( nStart >= nLen ){
  37041. /* Invalid offset */
  37042. jx9_result_bool(pCtx, 0);
  37043. return JX9_OK;
  37044. }else{
  37045. nLen -= nStart;
  37046. zPtr = &zBlob[nLen - 1];
  37047. zEnd = &zBlob[nLen];
  37048. }
  37049. }else{
  37050. if( nStart >= nLen ){
  37051. /* Invalid offset */
  37052. jx9_result_bool(pCtx, 0);
  37053. return JX9_OK;
  37054. }else{
  37055. zBlob += nStart;
  37056. nLen -= nStart;
  37057. }
  37058. }
  37059. }
  37060. if( nLen > 0 && nPatLen > 0 ){
  37061. /* Perform the lookup */
  37062. for(;;){
  37063. if( zBlob >= zPtr ){
  37064. break;
  37065. }
  37066. rc = xPatternMatch((const void *)zPtr, (sxu32)(zEnd-zPtr), (const void *)zPattern, (sxu32)nPatLen, &nOfft);
  37067. if( rc == SXRET_OK ){
  37068. /* Pattern found, return it's position */
  37069. jx9_result_int64(pCtx, (jx9_int64)(&zPtr[nOfft] - zStart));
  37070. return JX9_OK;
  37071. }
  37072. zPtr--;
  37073. }
  37074. /* Pattern not found, return FALSE */
  37075. jx9_result_bool(pCtx, 0);
  37076. }else{
  37077. jx9_result_bool(pCtx, 0);
  37078. }
  37079. return JX9_OK;
  37080. }
  37081. /*
  37082. * int strrchr(string $haystack, mixed $needle)
  37083. * Find the last occurrence of a character in a string.
  37084. * Parameters
  37085. * $haystack
  37086. * The input string.
  37087. * $needle
  37088. * If needle contains more than one character, only the first is used.
  37089. * This behavior is different from that of strstr().
  37090. * If needle is not a string, it is converted to an integer and applied
  37091. * as the ordinal value of a character.
  37092. * Return
  37093. * This function returns the portion of string, or FALSE if needle is not found.
  37094. */
  37095. static int jx9Builtin_strrchr(jx9_context *pCtx, int nArg, jx9_value **apArg)
  37096. {
  37097. const char *zBlob;
  37098. int nLen, c;
  37099. if( nArg < 2 ){
  37100. /* Missing arguments, return FALSE */
  37101. jx9_result_bool(pCtx, 0);
  37102. return JX9_OK;
  37103. }
  37104. /* Extract the haystack */
  37105. zBlob = jx9_value_to_string(apArg[0], &nLen);
  37106. c = 0; /* cc warning */
  37107. if( nLen > 0 ){
  37108. sxu32 nOfft;
  37109. sxi32 rc;
  37110. if( jx9_value_is_string(apArg[1]) ){
  37111. const char *zPattern;
  37112. zPattern = jx9_value_to_string(apArg[1], 0); /* Never fail, so there is no need to check
  37113. * for NULL pointer.
  37114. */
  37115. c = zPattern[0];
  37116. }else{
  37117. /* Int cast */
  37118. c = jx9_value_to_int(apArg[1]);
  37119. }
  37120. /* Perform the lookup */
  37121. rc = SyByteFind2(zBlob, (sxu32)nLen, c, &nOfft);
  37122. if( rc != SXRET_OK ){
  37123. /* No such entry, return FALSE */
  37124. jx9_result_bool(pCtx, 0);
  37125. return JX9_OK;
  37126. }
  37127. /* Return the string portion */
  37128. jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft]));
  37129. }else{
  37130. jx9_result_bool(pCtx, 0);
  37131. }
  37132. return JX9_OK;
  37133. }
  37134. /*
  37135. * string strrev(string $string)
  37136. * Reverse a string.
  37137. * Parameters
  37138. * $string
  37139. * String to be reversed.
  37140. * Return
  37141. * The reversed string.
  37142. */
  37143. static int jx9Builtin_strrev(jx9_context *pCtx, int nArg, jx9_value **apArg)
  37144. {
  37145. const char *zIn, *zEnd;
  37146. int nLen, c;
  37147. if( nArg < 1 ){
  37148. /* Missing arguments, return NULL */
  37149. jx9_result_null(pCtx);
  37150. return JX9_OK;
  37151. }
  37152. /* Extract the target string */
  37153. zIn = jx9_value_to_string(apArg[0], &nLen);
  37154. if( nLen < 1 ){
  37155. /* Empty string Return null */
  37156. jx9_result_null(pCtx);
  37157. return JX9_OK;
  37158. }
  37159. /* Perform the requested operation */
  37160. zEnd = &zIn[nLen - 1];
  37161. for(;;){
  37162. if( zEnd < zIn ){
  37163. /* No more input to process */
  37164. break;
  37165. }
  37166. /* Append current character */
  37167. c = zEnd[0];
  37168. jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
  37169. zEnd--;
  37170. }
  37171. return JX9_OK;
  37172. }
  37173. /*
  37174. * string str_repeat(string $input, int $multiplier)
  37175. * Returns input repeated multiplier times.
  37176. * Parameters
  37177. * $string
  37178. * String to be repeated.
  37179. * $multiplier
  37180. * Number of time the input string should be repeated.
  37181. * multiplier has to be greater than or equal to 0. If the multiplier is set
  37182. * to 0, the function will return an empty string.
  37183. * Return
  37184. * The repeated string.
  37185. */
  37186. static int jx9Builtin_str_repeat(jx9_context *pCtx, int nArg, jx9_value **apArg)
  37187. {
  37188. const char *zIn;
  37189. int nLen, nMul;
  37190. int rc;
  37191. if( nArg < 2 ){
  37192. /* Missing arguments, return NULL */
  37193. jx9_result_null(pCtx);
  37194. return JX9_OK;
  37195. }
  37196. /* Extract the target string */
  37197. zIn = jx9_value_to_string(apArg[0], &nLen);
  37198. if( nLen < 1 ){
  37199. /* Empty string.Return null */
  37200. jx9_result_null(pCtx);
  37201. return JX9_OK;
  37202. }
  37203. /* Extract the multiplier */
  37204. nMul = jx9_value_to_int(apArg[1]);
  37205. if( nMul < 1 ){
  37206. /* Return the empty string */
  37207. jx9_result_string(pCtx, "", 0);
  37208. return JX9_OK;
  37209. }
  37210. /* Perform the requested operation */
  37211. for(;;){
  37212. if( nMul < 1 ){
  37213. break;
  37214. }
  37215. /* Append the copy */
  37216. rc = jx9_result_string(pCtx, zIn, nLen);
  37217. if( rc != JX9_OK ){
  37218. /* Out of memory, break immediately */
  37219. break;
  37220. }
  37221. nMul--;
  37222. }
  37223. return JX9_OK;
  37224. }
  37225. /*
  37226. * string nl2br(string $string[, bool $is_xhtml = true ])
  37227. * Inserts HTML line breaks before all newlines in a string.
  37228. * Parameters
  37229. * $string
  37230. * The input string.
  37231. * $is_xhtml
  37232. * Whenever to use XHTML compatible line breaks or not.
  37233. * Return
  37234. * The processed string.
  37235. */
  37236. static int jx9Builtin_nl2br(jx9_context *pCtx, int nArg, jx9_value **apArg)
  37237. {
  37238. const char *zIn, *zCur, *zEnd;
  37239. int is_xhtml = 0;
  37240. int nLen;
  37241. if( nArg < 1 ){
  37242. /* Missing arguments, return the empty string */
  37243. jx9_result_string(pCtx, "", 0);
  37244. return JX9_OK;
  37245. }
  37246. /* Extract the target string */
  37247. zIn = jx9_value_to_string(apArg[0], &nLen);
  37248. if( nLen < 1 ){
  37249. /* Empty string, return null */
  37250. jx9_result_null(pCtx);
  37251. return JX9_OK;
  37252. }
  37253. if( nArg > 1 ){
  37254. is_xhtml = jx9_value_to_bool(apArg[1]);
  37255. }
  37256. zEnd = &zIn[nLen];
  37257. /* Perform the requested operation */
  37258. for(;;){
  37259. zCur = zIn;
  37260. /* Delimit the string */
  37261. while( zIn < zEnd && (zIn[0] != '\n'&& zIn[0] != '\r') ){
  37262. zIn++;
  37263. }
  37264. if( zCur < zIn ){
  37265. /* Output chunk verbatim */
  37266. jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
  37267. }
  37268. if( zIn >= zEnd ){
  37269. /* No more input to process */
  37270. break;
  37271. }
  37272. /* Output the HTML line break */
  37273. if( is_xhtml ){
  37274. jx9_result_string(pCtx, "<br>", (int)sizeof("<br>")-1);
  37275. }else{
  37276. jx9_result_string(pCtx, "<br/>", (int)sizeof("<br/>")-1);
  37277. }
  37278. zCur = zIn;
  37279. /* Append trailing line */
  37280. while( zIn < zEnd && (zIn[0] == '\n' || zIn[0] == '\r') ){
  37281. zIn++;
  37282. }
  37283. if( zCur < zIn ){
  37284. /* Output chunk verbatim */
  37285. jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
  37286. }
  37287. }
  37288. return JX9_OK;
  37289. }
  37290. /*
  37291. * Format a given string and invoke the given callback on each processed chunk.
  37292. * According to the JX9 reference manual.
  37293. * The format string is composed of zero or more directives: ordinary characters
  37294. * (excluding %) that are copied directly to the result, and conversion
  37295. * specifications, each of which results in fetching its own parameter.
  37296. * This applies to both sprintf() and printf().
  37297. * Each conversion specification consists of a percent sign (%), followed by one
  37298. * or more of these elements, in order:
  37299. * An optional sign specifier that forces a sign (- or +) to be used on a number.
  37300. * By default, only the - sign is used on a number if it's negative. This specifier forces
  37301. * positive numbers to have the + sign attached as well.
  37302. * An optional padding specifier that says what character will be used for padding
  37303. * the results to the right string size. This may be a space character or a 0 (zero character).
  37304. * The default is to pad with spaces. An alternate padding character can be specified by prefixing
  37305. * it with a single quote ('). See the examples below.
  37306. * An optional alignment specifier that says if the result should be left-justified or right-justified.
  37307. * The default is right-justified; a - character here will make it left-justified.
  37308. * An optional number, a width specifier that says how many characters (minimum) this conversion
  37309. * should result in.
  37310. * An optional precision specifier in the form of a period (`.') followed by an optional decimal
  37311. * digit string that says how many decimal digits should be displayed for floating-point numbers.
  37312. * When using this specifier on a string, it acts as a cutoff point, setting a maximum character
  37313. * limit to the string.
  37314. * A type specifier that says what type the argument data should be treated as. Possible types:
  37315. * % - a literal percent character. No argument is required.
  37316. * b - the argument is treated as an integer, and presented as a binary number.
  37317. * c - the argument is treated as an integer, and presented as the character with that ASCII value.
  37318. * d - the argument is treated as an integer, and presented as a (signed) decimal number.
  37319. * e - the argument is treated as scientific notation (e.g. 1.2e+2). The precision specifier stands
  37320. * for the number of digits after the decimal point.
  37321. * E - like %e but uses uppercase letter (e.g. 1.2E+2).
  37322. * u - the argument is treated as an integer, and presented as an unsigned decimal number.
  37323. * f - the argument is treated as a float, and presented as a floating-point number (locale aware).
  37324. * F - the argument is treated as a float, and presented as a floating-point number (non-locale aware).
  37325. * g - shorter of %e and %f.
  37326. * G - shorter of %E and %f.
  37327. * o - the argument is treated as an integer, and presented as an octal number.
  37328. * s - the argument is treated as and presented as a string.
  37329. * x - the argument is treated as an integer and presented as a hexadecimal number (with lowercase letters).
  37330. * X - the argument is treated as an integer and presented as a hexadecimal number (with uppercase letters).
  37331. */
  37332. /*
  37333. * This implementation is based on the one found in the SQLite3 source tree.
  37334. */
  37335. #define JX9_FMT_BUFSIZ 1024 /* Conversion buffer size */
  37336. /*
  37337. ** Conversion types fall into various categories as defined by the
  37338. ** following enumeration.
  37339. */
  37340. #define JX9_FMT_RADIX 1 /* Integer types.%d, %x, %o, and so forth */
  37341. #define JX9_FMT_FLOAT 2 /* Floating point.%f */
  37342. #define JX9_FMT_EXP 3 /* Exponentional notation.%e and %E */
  37343. #define JX9_FMT_GENERIC 4 /* Floating or exponential, depending on exponent.%g */
  37344. #define JX9_FMT_SIZE 5 /* Total number of characters processed so far.%n */
  37345. #define JX9_FMT_STRING 6 /* Strings.%s */
  37346. #define JX9_FMT_PERCENT 7 /* Percent symbol.%% */
  37347. #define JX9_FMT_CHARX 8 /* Characters.%c */
  37348. #define JX9_FMT_ERROR 9 /* Used to indicate no such conversion type */
  37349. /*
  37350. ** Allowed values for jx9_fmt_info.flags
  37351. */
  37352. #define JX9_FMT_FLAG_SIGNED 0x01
  37353. #define JX9_FMT_FLAG_UNSIGNED 0x02
  37354. /*
  37355. ** Each builtin conversion character (ex: the 'd' in "%d") is described
  37356. ** by an instance of the following structure
  37357. */
  37358. typedef struct jx9_fmt_info jx9_fmt_info;
  37359. struct jx9_fmt_info
  37360. {
  37361. char fmttype; /* The format field code letter [i.e: 'd', 's', 'x'] */
  37362. sxu8 base; /* The base for radix conversion */
  37363. int flags; /* One or more of JX9_FMT_FLAG_ constants below */
  37364. sxu8 type; /* Conversion paradigm */
  37365. char *charset; /* The character set for conversion */
  37366. char *prefix; /* Prefix on non-zero values in alt format */
  37367. };
  37368. #ifndef JX9_OMIT_FLOATING_POINT
  37369. /*
  37370. ** "*val" is a double such that 0.1 <= *val < 10.0
  37371. ** Return the ascii code for the leading digit of *val, then
  37372. ** multiply "*val" by 10.0 to renormalize.
  37373. **
  37374. ** Example:
  37375. ** input: *val = 3.14159
  37376. ** output: *val = 1.4159 function return = '3'
  37377. **
  37378. ** The counter *cnt is incremented each time. After counter exceeds
  37379. ** 16 (the number of significant digits in a 64-bit float) '0' is
  37380. ** always returned.
  37381. */
  37382. static int vxGetdigit(sxlongreal *val, int *cnt)
  37383. {
  37384. sxlongreal d;
  37385. int digit;
  37386. if( (*cnt)++ >= 16 ){
  37387. return '0';
  37388. }
  37389. digit = (int)*val;
  37390. d = digit;
  37391. *val = (*val - d)*10.0;
  37392. return digit + '0' ;
  37393. }
  37394. #endif /* JX9_OMIT_FLOATING_POINT */
  37395. /*
  37396. * The following table is searched linearly, so it is good to put the most frequently
  37397. * used conversion types first.
  37398. */
  37399. static const jx9_fmt_info aFmt[] = {
  37400. { 'd', 10, JX9_FMT_FLAG_SIGNED, JX9_FMT_RADIX, "0123456789", 0 },
  37401. { 's', 0, 0, JX9_FMT_STRING, 0, 0 },
  37402. { 'c', 0, 0, JX9_FMT_CHARX, 0, 0 },
  37403. { 'x', 16, 0, JX9_FMT_RADIX, "0123456789abcdef", "x0" },
  37404. { 'X', 16, 0, JX9_FMT_RADIX, "0123456789ABCDEF", "X0" },
  37405. { 'b', 2, 0, JX9_FMT_RADIX, "01", "b0"},
  37406. { 'o', 8, 0, JX9_FMT_RADIX, "01234567", "0" },
  37407. { 'u', 10, 0, JX9_FMT_RADIX, "0123456789", 0 },
  37408. { 'f', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_FLOAT, 0, 0 },
  37409. { 'F', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_FLOAT, 0, 0 },
  37410. { 'e', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_EXP, "e", 0 },
  37411. { 'E', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_EXP, "E", 0 },
  37412. { 'g', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_GENERIC, "e", 0 },
  37413. { 'G', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_GENERIC, "E", 0 },
  37414. { '%', 0, 0, JX9_FMT_PERCENT, 0, 0 }
  37415. };
  37416. /*
  37417. * Format a given string.
  37418. * The root program. All variations call this core.
  37419. * INPUTS:
  37420. * xConsumer This is a pointer to a function taking four arguments
  37421. * 1. A pointer to the call context.
  37422. * 2. A pointer to the list of characters to be output
  37423. * (Note, this list is NOT null terminated.)
  37424. * 3. An integer number of characters to be output.
  37425. * (Note: This number might be zero.)
  37426. * 4. Upper layer private data.
  37427. * zIn This is the format string, as in the usual print.
  37428. * apArg This is a pointer to a list of arguments.
  37429. */
  37430. JX9_PRIVATE sxi32 jx9InputFormat(
  37431. int (*xConsumer)(jx9_context *, const char *, int, void *), /* Format consumer */
  37432. jx9_context *pCtx, /* call context */
  37433. const char *zIn, /* Format string */
  37434. int nByte, /* Format string length */
  37435. int nArg, /* Total argument of the given arguments */
  37436. jx9_value **apArg, /* User arguments */
  37437. void *pUserData, /* Last argument to xConsumer() */
  37438. int vf /* TRUE if called from vfprintf, vsprintf context */
  37439. )
  37440. {
  37441. char spaces[] = " ";
  37442. #define etSPACESIZE ((int)sizeof(spaces)-1)
  37443. const char *zCur, *zEnd = &zIn[nByte];
  37444. char *zBuf, zWorker[JX9_FMT_BUFSIZ]; /* Working buffer */
  37445. const jx9_fmt_info *pInfo; /* Pointer to the appropriate info structure */
  37446. int flag_alternateform; /* True if "#" flag is present */
  37447. int flag_leftjustify; /* True if "-" flag is present */
  37448. int flag_blanksign; /* True if " " flag is present */
  37449. int flag_plussign; /* True if "+" flag is present */
  37450. int flag_zeropad; /* True if field width constant starts with zero */
  37451. jx9_value *pArg; /* Current processed argument */
  37452. jx9_int64 iVal;
  37453. int precision; /* Precision of the current field */
  37454. char *zExtra;
  37455. int c, rc, n;
  37456. int length; /* Length of the field */
  37457. int prefix;
  37458. sxu8 xtype; /* Conversion paradigm */
  37459. int width; /* Width of the current field */
  37460. int idx;
  37461. n = (vf == TRUE) ? 0 : 1;
  37462. #define NEXT_ARG ( n < nArg ? apArg[n++] : 0 )
  37463. /* Start the format process */
  37464. for(;;){
  37465. zCur = zIn;
  37466. while( zIn < zEnd && zIn[0] != '%' ){
  37467. zIn++;
  37468. }
  37469. if( zCur < zIn ){
  37470. /* Consume chunk verbatim */
  37471. rc = xConsumer(pCtx, zCur, (int)(zIn-zCur), pUserData);
  37472. if( rc == SXERR_ABORT ){
  37473. /* Callback request an operation abort */
  37474. break;
  37475. }
  37476. }
  37477. if( zIn >= zEnd ){
  37478. /* No more input to process, break immediately */
  37479. break;
  37480. }
  37481. /* Find out what flags are present */
  37482. flag_leftjustify = flag_plussign = flag_blanksign =
  37483. flag_alternateform = flag_zeropad = 0;
  37484. zIn++; /* Jump the precent sign */
  37485. do{
  37486. c = zIn[0];
  37487. switch( c ){
  37488. case '-': flag_leftjustify = 1; c = 0; break;
  37489. case '+': flag_plussign = 1; c = 0; break;
  37490. case ' ': flag_blanksign = 1; c = 0; break;
  37491. case '#': flag_alternateform = 1; c = 0; break;
  37492. case '0': flag_zeropad = 1; c = 0; break;
  37493. case '\'':
  37494. zIn++;
  37495. if( zIn < zEnd ){
  37496. /* An alternate padding character can be specified by prefixing it with a single quote (') */
  37497. c = zIn[0];
  37498. for(idx = 0 ; idx < etSPACESIZE ; ++idx ){
  37499. spaces[idx] = (char)c;
  37500. }
  37501. c = 0;
  37502. }
  37503. break;
  37504. default: break;
  37505. }
  37506. }while( c==0 && (zIn++ < zEnd) );
  37507. /* Get the field width */
  37508. width = 0;
  37509. while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){
  37510. width = width*10 + (zIn[0] - '0');
  37511. zIn++;
  37512. }
  37513. if( zIn < zEnd && zIn[0] == '$' ){
  37514. /* Position specifer */
  37515. if( width > 0 ){
  37516. n = width;
  37517. if( vf && n > 0 ){
  37518. n--;
  37519. }
  37520. }
  37521. zIn++;
  37522. width = 0;
  37523. if( zIn < zEnd && zIn[0] == '0' ){
  37524. flag_zeropad = 1;
  37525. zIn++;
  37526. }
  37527. while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){
  37528. width = width*10 + (zIn[0] - '0');
  37529. zIn++;
  37530. }
  37531. }
  37532. if( width > JX9_FMT_BUFSIZ-10 ){
  37533. width = JX9_FMT_BUFSIZ-10;
  37534. }
  37535. /* Get the precision */
  37536. precision = -1;
  37537. if( zIn < zEnd && zIn[0] == '.' ){
  37538. precision = 0;
  37539. zIn++;
  37540. while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){
  37541. precision = precision*10 + (zIn[0] - '0');
  37542. zIn++;
  37543. }
  37544. }
  37545. if( zIn >= zEnd ){
  37546. /* No more input */
  37547. break;
  37548. }
  37549. /* Fetch the info entry for the field */
  37550. pInfo = 0;
  37551. xtype = JX9_FMT_ERROR;
  37552. c = zIn[0];
  37553. zIn++; /* Jump the format specifer */
  37554. for(idx=0; idx< (int)SX_ARRAYSIZE(aFmt); idx++){
  37555. if( c==aFmt[idx].fmttype ){
  37556. pInfo = &aFmt[idx];
  37557. xtype = pInfo->type;
  37558. break;
  37559. }
  37560. }
  37561. zBuf = zWorker; /* Point to the working buffer */
  37562. length = 0;
  37563. zExtra = 0;
  37564. /*
  37565. ** At this point, variables are initialized as follows:
  37566. **
  37567. ** flag_alternateform TRUE if a '#' is present.
  37568. ** flag_plussign TRUE if a '+' is present.
  37569. ** flag_leftjustify TRUE if a '-' is present or if the
  37570. ** field width was negative.
  37571. ** flag_zeropad TRUE if the width began with 0.
  37572. ** the conversion character.
  37573. ** flag_blanksign TRUE if a ' ' is present.
  37574. ** width The specified field width. This is
  37575. ** always non-negative. Zero is the default.
  37576. ** precision The specified precision. The default
  37577. ** is -1.
  37578. */
  37579. switch(xtype){
  37580. case JX9_FMT_PERCENT:
  37581. /* A literal percent character */
  37582. zWorker[0] = '%';
  37583. length = (int)sizeof(char);
  37584. break;
  37585. case JX9_FMT_CHARX:
  37586. /* The argument is treated as an integer, and presented as the character
  37587. * with that ASCII value
  37588. */
  37589. pArg = NEXT_ARG;
  37590. if( pArg == 0 ){
  37591. c = 0;
  37592. }else{
  37593. c = jx9_value_to_int(pArg);
  37594. }
  37595. /* NUL byte is an acceptable value */
  37596. zWorker[0] = (char)c;
  37597. length = (int)sizeof(char);
  37598. break;
  37599. case JX9_FMT_STRING:
  37600. /* the argument is treated as and presented as a string */
  37601. pArg = NEXT_ARG;
  37602. if( pArg == 0 ){
  37603. length = 0;
  37604. }else{
  37605. zBuf = (char *)jx9_value_to_string(pArg, &length);
  37606. }
  37607. if( length < 1 ){
  37608. zBuf = " ";
  37609. length = (int)sizeof(char);
  37610. }
  37611. if( precision>=0 && precision<length ){
  37612. length = precision;
  37613. }
  37614. if( flag_zeropad ){
  37615. /* zero-padding works on strings too */
  37616. for(idx = 0 ; idx < etSPACESIZE ; ++idx ){
  37617. spaces[idx] = '0';
  37618. }
  37619. }
  37620. break;
  37621. case JX9_FMT_RADIX:
  37622. pArg = NEXT_ARG;
  37623. if( pArg == 0 ){
  37624. iVal = 0;
  37625. }else{
  37626. iVal = jx9_value_to_int64(pArg);
  37627. }
  37628. /* Limit the precision to prevent overflowing buf[] during conversion */
  37629. if( precision>JX9_FMT_BUFSIZ-40 ){
  37630. precision = JX9_FMT_BUFSIZ-40;
  37631. }
  37632. #if 1
  37633. /* For the format %#x, the value zero is printed "0" not "0x0".
  37634. ** I think this is stupid.*/
  37635. if( iVal==0 ) flag_alternateform = 0;
  37636. #else
  37637. /* More sensible: turn off the prefix for octal (to prevent "00"),
  37638. ** but leave the prefix for hex.*/
  37639. if( iVal==0 && pInfo->base==8 ) flag_alternateform = 0;
  37640. #endif
  37641. if( pInfo->flags & JX9_FMT_FLAG_SIGNED ){
  37642. if( iVal<0 ){
  37643. iVal = -iVal;
  37644. /* Ticket 1433-003 */
  37645. if( iVal < 0 ){
  37646. /* Overflow */
  37647. iVal= 0x7FFFFFFFFFFFFFFF;
  37648. }
  37649. prefix = '-';
  37650. }else if( flag_plussign ) prefix = '+';
  37651. else if( flag_blanksign ) prefix = ' ';
  37652. else prefix = 0;
  37653. }else{
  37654. if( iVal<0 ){
  37655. iVal = -iVal;
  37656. /* Ticket 1433-003 */
  37657. if( iVal < 0 ){
  37658. /* Overflow */
  37659. iVal= 0x7FFFFFFFFFFFFFFF;
  37660. }
  37661. }
  37662. prefix = 0;
  37663. }
  37664. if( flag_zeropad && precision<width-(prefix!=0) ){
  37665. precision = width-(prefix!=0);
  37666. }
  37667. zBuf = &zWorker[JX9_FMT_BUFSIZ-1];
  37668. {
  37669. register char *cset; /* Use registers for speed */
  37670. register int base;
  37671. cset = pInfo->charset;
  37672. base = pInfo->base;
  37673. do{ /* Convert to ascii */
  37674. *(--zBuf) = cset[iVal%base];
  37675. iVal = iVal/base;
  37676. }while( iVal>0 );
  37677. }
  37678. length = &zWorker[JX9_FMT_BUFSIZ-1]-zBuf;
  37679. for(idx=precision-length; idx>0; idx--){
  37680. *(--zBuf) = '0'; /* Zero pad */
  37681. }
  37682. if( prefix ) *(--zBuf) = (char)prefix; /* Add sign */
  37683. if( flag_alternateform && pInfo->prefix ){ /* Add "0" or "0x" */
  37684. char *pre, x;
  37685. pre = pInfo->prefix;
  37686. if( *zBuf!=pre[0] ){
  37687. for(pre=pInfo->prefix; (x=(*pre))!=0; pre++) *(--zBuf) = x;
  37688. }
  37689. }
  37690. length = &zWorker[JX9_FMT_BUFSIZ-1]-zBuf;
  37691. break;
  37692. case JX9_FMT_FLOAT:
  37693. case JX9_FMT_EXP:
  37694. case JX9_FMT_GENERIC:{
  37695. #ifndef JX9_OMIT_FLOATING_POINT
  37696. long double realvalue;
  37697. int exp; /* exponent of real numbers */
  37698. double rounder; /* Used for rounding floating point values */
  37699. int flag_dp; /* True if decimal point should be shown */
  37700. int flag_rtz; /* True if trailing zeros should be removed */
  37701. int flag_exp; /* True to force display of the exponent */
  37702. int nsd; /* Number of significant digits returned */
  37703. pArg = NEXT_ARG;
  37704. if( pArg == 0 ){
  37705. realvalue = 0;
  37706. }else{
  37707. realvalue = jx9_value_to_double(pArg);
  37708. }
  37709. if( precision<0 ) precision = 6; /* Set default precision */
  37710. if( precision>JX9_FMT_BUFSIZ-40) precision = JX9_FMT_BUFSIZ-40;
  37711. if( realvalue<0.0 ){
  37712. realvalue = -realvalue;
  37713. prefix = '-';
  37714. }else{
  37715. if( flag_plussign ) prefix = '+';
  37716. else if( flag_blanksign ) prefix = ' ';
  37717. else prefix = 0;
  37718. }
  37719. if( pInfo->type==JX9_FMT_GENERIC && precision>0 ) precision--;
  37720. rounder = 0.0;
  37721. #if 0
  37722. /* Rounding works like BSD when the constant 0.4999 is used.Wierd! */
  37723. for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
  37724. #else
  37725. /* It makes more sense to use 0.5 */
  37726. for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1);
  37727. #endif
  37728. if( pInfo->type==JX9_FMT_FLOAT ) realvalue += rounder;
  37729. /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
  37730. exp = 0;
  37731. if( realvalue>0.0 ){
  37732. while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
  37733. while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
  37734. while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; }
  37735. while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; }
  37736. if( exp>350 || exp<-350 ){
  37737. zBuf = "NaN";
  37738. length = 3;
  37739. break;
  37740. }
  37741. }
  37742. zBuf = zWorker;
  37743. /*
  37744. ** If the field type is etGENERIC, then convert to either etEXP
  37745. ** or etFLOAT, as appropriate.
  37746. */
  37747. flag_exp = xtype==JX9_FMT_EXP;
  37748. if( xtype!=JX9_FMT_FLOAT ){
  37749. realvalue += rounder;
  37750. if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
  37751. }
  37752. if( xtype==JX9_FMT_GENERIC ){
  37753. flag_rtz = !flag_alternateform;
  37754. if( exp<-4 || exp>precision ){
  37755. xtype = JX9_FMT_EXP;
  37756. }else{
  37757. precision = precision - exp;
  37758. xtype = JX9_FMT_FLOAT;
  37759. }
  37760. }else{
  37761. flag_rtz = 0;
  37762. }
  37763. /*
  37764. ** The "exp+precision" test causes output to be of type etEXP if
  37765. ** the precision is too large to fit in buf[].
  37766. */
  37767. nsd = 0;
  37768. if( xtype==JX9_FMT_FLOAT && exp+precision<JX9_FMT_BUFSIZ-30 ){
  37769. flag_dp = (precision>0 || flag_alternateform);
  37770. if( prefix ) *(zBuf++) = (char)prefix; /* Sign */
  37771. if( exp<0 ) *(zBuf++) = '0'; /* Digits before "." */
  37772. else for(; exp>=0; exp--) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd);
  37773. if( flag_dp ) *(zBuf++) = '.'; /* The decimal point */
  37774. for(exp++; exp<0 && precision>0; precision--, exp++){
  37775. *(zBuf++) = '0';
  37776. }
  37777. while( (precision--)>0 ) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd);
  37778. *(zBuf--) = 0; /* Null terminate */
  37779. if( flag_rtz && flag_dp ){ /* Remove trailing zeros and "." */
  37780. while( zBuf>=zWorker && *zBuf=='0' ) *(zBuf--) = 0;
  37781. if( zBuf>=zWorker && *zBuf=='.' ) *(zBuf--) = 0;
  37782. }
  37783. zBuf++; /* point to next free slot */
  37784. }else{ /* etEXP or etGENERIC */
  37785. flag_dp = (precision>0 || flag_alternateform);
  37786. if( prefix ) *(zBuf++) = (char)prefix; /* Sign */
  37787. *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd); /* First digit */
  37788. if( flag_dp ) *(zBuf++) = '.'; /* Decimal point */
  37789. while( (precision--)>0 ) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd);
  37790. zBuf--; /* point to last digit */
  37791. if( flag_rtz && flag_dp ){ /* Remove tail zeros */
  37792. while( zBuf>=zWorker && *zBuf=='0' ) *(zBuf--) = 0;
  37793. if( zBuf>=zWorker && *zBuf=='.' ) *(zBuf--) = 0;
  37794. }
  37795. zBuf++; /* point to next free slot */
  37796. if( exp || flag_exp ){
  37797. *(zBuf++) = pInfo->charset[0];
  37798. if( exp<0 ){ *(zBuf++) = '-'; exp = -exp; } /* sign of exp */
  37799. else { *(zBuf++) = '+'; }
  37800. if( exp>=100 ){
  37801. *(zBuf++) = (char)((exp/100)+'0'); /* 100's digit */
  37802. exp %= 100;
  37803. }
  37804. *(zBuf++) = (char)(exp/10+'0'); /* 10's digit */
  37805. *(zBuf++) = (char)(exp%10+'0'); /* 1's digit */
  37806. }
  37807. }
  37808. /* The converted number is in buf[] and zero terminated.Output it.
  37809. ** Note that the number is in the usual order, not reversed as with
  37810. ** integer conversions.*/
  37811. length = (int)(zBuf-zWorker);
  37812. zBuf = zWorker;
  37813. /* Special case: Add leading zeros if the flag_zeropad flag is
  37814. ** set and we are not left justified */
  37815. if( flag_zeropad && !flag_leftjustify && length < width){
  37816. int i;
  37817. int nPad = width - length;
  37818. for(i=width; i>=nPad; i--){
  37819. zBuf[i] = zBuf[i-nPad];
  37820. }
  37821. i = prefix!=0;
  37822. while( nPad-- ) zBuf[i++] = '0';
  37823. length = width;
  37824. }
  37825. #else
  37826. zBuf = " ";
  37827. length = (int)sizeof(char);
  37828. #endif /* JX9_OMIT_FLOATING_POINT */
  37829. break;
  37830. }
  37831. default:
  37832. /* Invalid format specifer */
  37833. zWorker[0] = '?';
  37834. length = (int)sizeof(char);
  37835. break;
  37836. }
  37837. /*
  37838. ** The text of the conversion is pointed to by "zBuf" and is
  37839. ** "length" characters long.The field width is "width".Do
  37840. ** the output.
  37841. */
  37842. if( !flag_leftjustify ){
  37843. register int nspace;
  37844. nspace = width-length;
  37845. if( nspace>0 ){
  37846. while( nspace>=etSPACESIZE ){
  37847. rc = xConsumer(pCtx, spaces, etSPACESIZE, pUserData);
  37848. if( rc != SXRET_OK ){
  37849. return SXERR_ABORT; /* Consumer routine request an operation abort */
  37850. }
  37851. nspace -= etSPACESIZE;
  37852. }
  37853. if( nspace>0 ){
  37854. rc = xConsumer(pCtx, spaces, (unsigned int)nspace, pUserData);
  37855. if( rc != SXRET_OK ){
  37856. return SXERR_ABORT; /* Consumer routine request an operation abort */
  37857. }
  37858. }
  37859. }
  37860. }
  37861. if( length>0 ){
  37862. rc = xConsumer(pCtx, zBuf, (unsigned int)length, pUserData);
  37863. if( rc != SXRET_OK ){
  37864. return SXERR_ABORT; /* Consumer routine request an operation abort */
  37865. }
  37866. }
  37867. if( flag_leftjustify ){
  37868. register int nspace;
  37869. nspace = width-length;
  37870. if( nspace>0 ){
  37871. while( nspace>=etSPACESIZE ){
  37872. rc = xConsumer(pCtx, spaces, etSPACESIZE, pUserData);
  37873. if( rc != SXRET_OK ){
  37874. return SXERR_ABORT; /* Consumer routine request an operation abort */
  37875. }
  37876. nspace -= etSPACESIZE;
  37877. }
  37878. if( nspace>0 ){
  37879. rc = xConsumer(pCtx, spaces, (unsigned int)nspace, pUserData);
  37880. if( rc != SXRET_OK ){
  37881. return SXERR_ABORT; /* Consumer routine request an operation abort */
  37882. }
  37883. }
  37884. }
  37885. }
  37886. }/* for(;;) */
  37887. return SXRET_OK;
  37888. }
  37889. /*
  37890. * Callback [i.e: Formatted input consumer] of the sprintf function.
  37891. */
  37892. static int sprintfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData)
  37893. {
  37894. /* Consume directly */
  37895. jx9_result_string(pCtx, zInput, nLen);
  37896. SXUNUSED(pUserData); /* cc warning */
  37897. return JX9_OK;
  37898. }
  37899. /*
  37900. * string sprintf(string $format[, mixed $args [, mixed $... ]])
  37901. * Return a formatted string.
  37902. * Parameters
  37903. * $format
  37904. * The format string (see block comment above)
  37905. * Return
  37906. * A string produced according to the formatting string format.
  37907. */
  37908. static int jx9Builtin_sprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
  37909. {
  37910. const char *zFormat;
  37911. int nLen;
  37912. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  37913. /* Missing/Invalid arguments, return the empty string */
  37914. jx9_result_string(pCtx, "", 0);
  37915. return JX9_OK;
  37916. }
  37917. /* Extract the string format */
  37918. zFormat = jx9_value_to_string(apArg[0], &nLen);
  37919. if( nLen < 1 ){
  37920. /* Empty string */
  37921. jx9_result_string(pCtx, "", 0);
  37922. return JX9_OK;
  37923. }
  37924. /* Format the string */
  37925. jx9InputFormat(sprintfConsumer, pCtx, zFormat, nLen, nArg, apArg, 0, FALSE);
  37926. return JX9_OK;
  37927. }
  37928. /*
  37929. * Callback [i.e: Formatted input consumer] of the printf function.
  37930. */
  37931. static int printfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData)
  37932. {
  37933. jx9_int64 *pCounter = (jx9_int64 *)pUserData;
  37934. /* Call the VM output consumer directly */
  37935. jx9_context_output(pCtx, zInput, nLen);
  37936. /* Increment counter */
  37937. *pCounter += nLen;
  37938. return JX9_OK;
  37939. }
  37940. /*
  37941. * int64 printf(string $format[, mixed $args[, mixed $... ]])
  37942. * Output a formatted string.
  37943. * Parameters
  37944. * $format
  37945. * See sprintf() for a description of format.
  37946. * Return
  37947. * The length of the outputted string.
  37948. */
  37949. static int jx9Builtin_printf(jx9_context *pCtx, int nArg, jx9_value **apArg)
  37950. {
  37951. jx9_int64 nCounter = 0;
  37952. const char *zFormat;
  37953. int nLen;
  37954. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  37955. /* Missing/Invalid arguments, return 0 */
  37956. jx9_result_int(pCtx, 0);
  37957. return JX9_OK;
  37958. }
  37959. /* Extract the string format */
  37960. zFormat = jx9_value_to_string(apArg[0], &nLen);
  37961. if( nLen < 1 ){
  37962. /* Empty string */
  37963. jx9_result_int(pCtx, 0);
  37964. return JX9_OK;
  37965. }
  37966. /* Format the string */
  37967. jx9InputFormat(printfConsumer, pCtx, zFormat, nLen, nArg, apArg, (void *)&nCounter, FALSE);
  37968. /* Return the length of the outputted string */
  37969. jx9_result_int64(pCtx, nCounter);
  37970. return JX9_OK;
  37971. }
  37972. /*
  37973. * int vprintf(string $format, array $args)
  37974. * Output a formatted string.
  37975. * Parameters
  37976. * $format
  37977. * See sprintf() for a description of format.
  37978. * Return
  37979. * The length of the outputted string.
  37980. */
  37981. static int jx9Builtin_vprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
  37982. {
  37983. jx9_int64 nCounter = 0;
  37984. const char *zFormat;
  37985. jx9_hashmap *pMap;
  37986. SySet sArg;
  37987. int nLen, n;
  37988. if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
  37989. /* Missing/Invalid arguments, return 0 */
  37990. jx9_result_int(pCtx, 0);
  37991. return JX9_OK;
  37992. }
  37993. /* Extract the string format */
  37994. zFormat = jx9_value_to_string(apArg[0], &nLen);
  37995. if( nLen < 1 ){
  37996. /* Empty string */
  37997. jx9_result_int(pCtx, 0);
  37998. return JX9_OK;
  37999. }
  38000. /* Point to the hashmap */
  38001. pMap = (jx9_hashmap *)apArg[1]->x.pOther;
  38002. /* Extract arguments from the hashmap */
  38003. n = jx9HashmapValuesToSet(pMap, &sArg);
  38004. /* Format the string */
  38005. jx9InputFormat(printfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), (void *)&nCounter, TRUE);
  38006. /* Return the length of the outputted string */
  38007. jx9_result_int64(pCtx, nCounter);
  38008. /* Release the container */
  38009. SySetRelease(&sArg);
  38010. return JX9_OK;
  38011. }
  38012. /*
  38013. * int vsprintf(string $format, array $args)
  38014. * Output a formatted string.
  38015. * Parameters
  38016. * $format
  38017. * See sprintf() for a description of format.
  38018. * Return
  38019. * A string produced according to the formatting string format.
  38020. */
  38021. static int jx9Builtin_vsprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
  38022. {
  38023. const char *zFormat;
  38024. jx9_hashmap *pMap;
  38025. SySet sArg;
  38026. int nLen, n;
  38027. if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
  38028. /* Missing/Invalid arguments, return the empty string */
  38029. jx9_result_string(pCtx, "", 0);
  38030. return JX9_OK;
  38031. }
  38032. /* Extract the string format */
  38033. zFormat = jx9_value_to_string(apArg[0], &nLen);
  38034. if( nLen < 1 ){
  38035. /* Empty string */
  38036. jx9_result_string(pCtx, "", 0);
  38037. return JX9_OK;
  38038. }
  38039. /* Point to hashmap */
  38040. pMap = (jx9_hashmap *)apArg[1]->x.pOther;
  38041. /* Extract arguments from the hashmap */
  38042. n = jx9HashmapValuesToSet(pMap, &sArg);
  38043. /* Format the string */
  38044. jx9InputFormat(sprintfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), 0, TRUE);
  38045. /* Release the container */
  38046. SySetRelease(&sArg);
  38047. return JX9_OK;
  38048. }
  38049. /*
  38050. * string size_format(int64 $size)
  38051. * Return a smart string represenation of the given size [i.e: 64-bit integer]
  38052. * Example:
  38053. * print size_format(1*1024*1024*1024);// 1GB
  38054. * print size_format(512*1024*1024); // 512 MB
  38055. * print size_format(file_size(/path/to/my/file_8192)); //8KB
  38056. * Parameter
  38057. * $size
  38058. * Entity size in bytes.
  38059. * Return
  38060. * Formatted string representation of the given size.
  38061. */
  38062. static int jx9Builtin_size_format(jx9_context *pCtx, int nArg, jx9_value **apArg)
  38063. {
  38064. /*Kilo*/ /*Mega*/ /*Giga*/ /*Tera*/ /*Peta*/ /*Exa*/ /*Zeta*/
  38065. static const char zUnit[] = {"KMGTPEZ"};
  38066. sxi32 nRest, i_32;
  38067. jx9_int64 iSize;
  38068. int c = -1; /* index in zUnit[] */
  38069. if( nArg < 1 ){
  38070. /* Missing argument, return the empty string */
  38071. jx9_result_string(pCtx, "", 0);
  38072. return JX9_OK;
  38073. }
  38074. /* Extract the given size */
  38075. iSize = jx9_value_to_int64(apArg[0]);
  38076. if( iSize < 100 /* Bytes */ ){
  38077. /* Don't bother formatting, return immediately */
  38078. jx9_result_string(pCtx, "0.1 KB", (int)sizeof("0.1 KB")-1);
  38079. return JX9_OK;
  38080. }
  38081. for(;;){
  38082. nRest = (sxi32)(iSize & 0x3FF);
  38083. iSize >>= 10;
  38084. c++;
  38085. if( (iSize & (~0 ^ 1023)) == 0 ){
  38086. break;
  38087. }
  38088. }
  38089. nRest /= 100;
  38090. if( nRest > 9 ){
  38091. nRest = 9;
  38092. }
  38093. if( iSize > 999 ){
  38094. c++;
  38095. nRest = 9;
  38096. iSize = 0;
  38097. }
  38098. i_32 = (sxi32)iSize;
  38099. /* Format */
  38100. jx9_result_string_format(pCtx, "%d.%d %cB", i_32, nRest, zUnit[c]);
  38101. return JX9_OK;
  38102. }
  38103. #if !defined(JX9_DISABLE_HASH_FUNC)
  38104. /*
  38105. * string md5(string $str[, bool $raw_output = false])
  38106. * Calculate the md5 hash of a string.
  38107. * Parameter
  38108. * $str
  38109. * Input string
  38110. * $raw_output
  38111. * If the optional raw_output is set to TRUE, then the md5 digest
  38112. * is instead returned in raw binary format with a length of 16.
  38113. * Return
  38114. * MD5 Hash as a 32-character hexadecimal string.
  38115. */
  38116. static int jx9Builtin_md5(jx9_context *pCtx, int nArg, jx9_value **apArg)
  38117. {
  38118. unsigned char zDigest[16];
  38119. int raw_output = FALSE;
  38120. const void *pIn;
  38121. int nLen;
  38122. if( nArg < 1 ){
  38123. /* Missing arguments, return the empty string */
  38124. jx9_result_string(pCtx, "", 0);
  38125. return JX9_OK;
  38126. }
  38127. /* Extract the input string */
  38128. pIn = (const void *)jx9_value_to_string(apArg[0], &nLen);
  38129. if( nLen < 1 ){
  38130. /* Empty string */
  38131. jx9_result_string(pCtx, "", 0);
  38132. return JX9_OK;
  38133. }
  38134. if( nArg > 1 && jx9_value_is_bool(apArg[1])){
  38135. raw_output = jx9_value_to_bool(apArg[1]);
  38136. }
  38137. /* Compute the MD5 digest */
  38138. SyMD5Compute(pIn, (sxu32)nLen, zDigest);
  38139. if( raw_output ){
  38140. /* Output raw digest */
  38141. jx9_result_string(pCtx, (const char *)zDigest, (int)sizeof(zDigest));
  38142. }else{
  38143. /* Perform a binary to hex conversion */
  38144. SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), HashConsumer, pCtx);
  38145. }
  38146. return JX9_OK;
  38147. }
  38148. /*
  38149. * string sha1(string $str[, bool $raw_output = false])
  38150. * Calculate the sha1 hash of a string.
  38151. * Parameter
  38152. * $str
  38153. * Input string
  38154. * $raw_output
  38155. * If the optional raw_output is set to TRUE, then the md5 digest
  38156. * is instead returned in raw binary format with a length of 16.
  38157. * Return
  38158. * SHA1 Hash as a 40-character hexadecimal string.
  38159. */
  38160. static int jx9Builtin_sha1(jx9_context *pCtx, int nArg, jx9_value **apArg)
  38161. {
  38162. unsigned char zDigest[20];
  38163. int raw_output = FALSE;
  38164. const void *pIn;
  38165. int nLen;
  38166. if( nArg < 1 ){
  38167. /* Missing arguments, return the empty string */
  38168. jx9_result_string(pCtx, "", 0);
  38169. return JX9_OK;
  38170. }
  38171. /* Extract the input string */
  38172. pIn = (const void *)jx9_value_to_string(apArg[0], &nLen);
  38173. if( nLen < 1 ){
  38174. /* Empty string */
  38175. jx9_result_string(pCtx, "", 0);
  38176. return JX9_OK;
  38177. }
  38178. if( nArg > 1 && jx9_value_is_bool(apArg[1])){
  38179. raw_output = jx9_value_to_bool(apArg[1]);
  38180. }
  38181. /* Compute the SHA1 digest */
  38182. SySha1Compute(pIn, (sxu32)nLen, zDigest);
  38183. if( raw_output ){
  38184. /* Output raw digest */
  38185. jx9_result_string(pCtx, (const char *)zDigest, (int)sizeof(zDigest));
  38186. }else{
  38187. /* Perform a binary to hex conversion */
  38188. SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), HashConsumer, pCtx);
  38189. }
  38190. return JX9_OK;
  38191. }
  38192. /*
  38193. * int64 crc32(string $str)
  38194. * Calculates the crc32 polynomial of a strin.
  38195. * Parameter
  38196. * $str
  38197. * Input string
  38198. * Return
  38199. * CRC32 checksum of the given input (64-bit integer).
  38200. */
  38201. static int jx9Builtin_crc32(jx9_context *pCtx, int nArg, jx9_value **apArg)
  38202. {
  38203. const void *pIn;
  38204. sxu32 nCRC;
  38205. int nLen;
  38206. if( nArg < 1 ){
  38207. /* Missing arguments, return 0 */
  38208. jx9_result_int(pCtx, 0);
  38209. return JX9_OK;
  38210. }
  38211. /* Extract the input string */
  38212. pIn = (const void *)jx9_value_to_string(apArg[0], &nLen);
  38213. if( nLen < 1 ){
  38214. /* Empty string */
  38215. jx9_result_int(pCtx, 0);
  38216. return JX9_OK;
  38217. }
  38218. /* Calculate the sum */
  38219. nCRC = SyCrc32(pIn, (sxu32)nLen);
  38220. /* Return the CRC32 as 64-bit integer */
  38221. jx9_result_int64(pCtx, (jx9_int64)nCRC^ 0xFFFFFFFF);
  38222. return JX9_OK;
  38223. }
  38224. #endif /* JX9_DISABLE_HASH_FUNC */
  38225. /*
  38226. * Parse a CSV string and invoke the supplied callback for each processed xhunk.
  38227. */
  38228. JX9_PRIVATE sxi32 jx9ProcessCsv(
  38229. const char *zInput, /* Raw input */
  38230. int nByte, /* Input length */
  38231. int delim, /* Delimiter */
  38232. int encl, /* Enclosure */
  38233. int escape, /* Escape character */
  38234. sxi32 (*xConsumer)(const char *, int, void *), /* User callback */
  38235. void *pUserData /* Last argument to xConsumer() */
  38236. )
  38237. {
  38238. const char *zEnd = &zInput[nByte];
  38239. const char *zIn = zInput;
  38240. const char *zPtr;
  38241. int isEnc;
  38242. /* Start processing */
  38243. for(;;){
  38244. if( zIn >= zEnd ){
  38245. /* No more input to process */
  38246. break;
  38247. }
  38248. isEnc = 0;
  38249. zPtr = zIn;
  38250. /* Find the first delimiter */
  38251. while( zIn < zEnd ){
  38252. if( zIn[0] == delim && !isEnc){
  38253. /* Delimiter found, break imediately */
  38254. break;
  38255. }else if( zIn[0] == encl ){
  38256. /* Inside enclosure? */
  38257. isEnc = !isEnc;
  38258. }else if( zIn[0] == escape ){
  38259. /* Escape sequence */
  38260. zIn++;
  38261. }
  38262. /* Advance the cursor */
  38263. zIn++;
  38264. }
  38265. if( zIn > zPtr ){
  38266. int nByte = (int)(zIn-zPtr);
  38267. sxi32 rc;
  38268. /* Invoke the supllied callback */
  38269. if( zPtr[0] == encl ){
  38270. zPtr++;
  38271. nByte-=2;
  38272. }
  38273. if( nByte > 0 ){
  38274. rc = xConsumer(zPtr, nByte, pUserData);
  38275. if( rc == SXERR_ABORT ){
  38276. /* User callback request an operation abort */
  38277. break;
  38278. }
  38279. }
  38280. }
  38281. /* Ignore trailing delimiter */
  38282. while( zIn < zEnd && zIn[0] == delim ){
  38283. zIn++;
  38284. }
  38285. }
  38286. return SXRET_OK;
  38287. }
  38288. /*
  38289. * Default consumer callback for the CSV parsing routine defined above.
  38290. * All the processed input is insereted into an array passed as the last
  38291. * argument to this callback.
  38292. */
  38293. JX9_PRIVATE sxi32 jx9CsvConsumer(const char *zToken, int nTokenLen, void *pUserData)
  38294. {
  38295. jx9_value *pArray = (jx9_value *)pUserData;
  38296. jx9_value sEntry;
  38297. SyString sToken;
  38298. /* Insert the token in the given array */
  38299. SyStringInitFromBuf(&sToken, zToken, nTokenLen);
  38300. /* Remove trailing and leading white spcaces and null bytes */
  38301. SyStringFullTrimSafe(&sToken);
  38302. if( sToken.nByte < 1){
  38303. return SXRET_OK;
  38304. }
  38305. jx9MemObjInitFromString(pArray->pVm, &sEntry, &sToken);
  38306. jx9_array_add_elem(pArray, 0, &sEntry);
  38307. jx9MemObjRelease(&sEntry);
  38308. return SXRET_OK;
  38309. }
  38310. /*
  38311. * array str_getcsv(string $input[, string $delimiter = ', '[, string $enclosure = '"' [, string $escape='\\']]])
  38312. * Parse a CSV string into an array.
  38313. * Parameters
  38314. * $input
  38315. * The string to parse.
  38316. * $delimiter
  38317. * Set the field delimiter (one character only).
  38318. * $enclosure
  38319. * Set the field enclosure character (one character only).
  38320. * $escape
  38321. * Set the escape character (one character only). Defaults as a backslash (\)
  38322. * Return
  38323. * An indexed array containing the CSV fields or NULL on failure.
  38324. */
  38325. static int jx9Builtin_str_getcsv(jx9_context *pCtx, int nArg, jx9_value **apArg)
  38326. {
  38327. const char *zInput, *zPtr;
  38328. jx9_value *pArray;
  38329. int delim = ','; /* Delimiter */
  38330. int encl = '"' ; /* Enclosure */
  38331. int escape = '\\'; /* Escape character */
  38332. int nLen;
  38333. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  38334. /* Missing/Invalid arguments, return NULL */
  38335. jx9_result_null(pCtx);
  38336. return JX9_OK;
  38337. }
  38338. /* Extract the raw input */
  38339. zInput = jx9_value_to_string(apArg[0], &nLen);
  38340. if( nArg > 1 ){
  38341. int i;
  38342. if( jx9_value_is_string(apArg[1]) ){
  38343. /* Extract the delimiter */
  38344. zPtr = jx9_value_to_string(apArg[1], &i);
  38345. if( i > 0 ){
  38346. delim = zPtr[0];
  38347. }
  38348. }
  38349. if( nArg > 2 ){
  38350. if( jx9_value_is_string(apArg[2]) ){
  38351. /* Extract the enclosure */
  38352. zPtr = jx9_value_to_string(apArg[2], &i);
  38353. if( i > 0 ){
  38354. encl = zPtr[0];
  38355. }
  38356. }
  38357. if( nArg > 3 ){
  38358. if( jx9_value_is_string(apArg[3]) ){
  38359. /* Extract the escape character */
  38360. zPtr = jx9_value_to_string(apArg[3], &i);
  38361. if( i > 0 ){
  38362. escape = zPtr[0];
  38363. }
  38364. }
  38365. }
  38366. }
  38367. }
  38368. /* Create our array */
  38369. pArray = jx9_context_new_array(pCtx);
  38370. if( pArray == 0 ){
  38371. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
  38372. jx9_result_null(pCtx);
  38373. return JX9_OK;
  38374. }
  38375. /* Parse the raw input */
  38376. jx9ProcessCsv(zInput, nLen, delim, encl, escape, jx9CsvConsumer, pArray);
  38377. /* Return the freshly created array */
  38378. jx9_result_value(pCtx, pArray);
  38379. return JX9_OK;
  38380. }
  38381. /*
  38382. * Extract a tag name from a raw HTML input and insert it in the given
  38383. * container.
  38384. * Refer to [strip_tags()].
  38385. */
  38386. static sxi32 AddTag(SySet *pSet, const char *zTag, int nByte)
  38387. {
  38388. const char *zEnd = &zTag[nByte];
  38389. const char *zPtr;
  38390. SyString sEntry;
  38391. /* Strip tags */
  38392. for(;;){
  38393. while( zTag < zEnd && (zTag[0] == '<' || zTag[0] == '/' || zTag[0] == '?'
  38394. || zTag[0] == '!' || zTag[0] == '-' || ((unsigned char)zTag[0] < 0xc0 && SyisSpace(zTag[0]))) ){
  38395. zTag++;
  38396. }
  38397. if( zTag >= zEnd ){
  38398. break;
  38399. }
  38400. zPtr = zTag;
  38401. /* Delimit the tag */
  38402. while(zTag < zEnd ){
  38403. if( (unsigned char)zTag[0] >= 0xc0 ){
  38404. /* UTF-8 stream */
  38405. zTag++;
  38406. SX_JMP_UTF8(zTag, zEnd);
  38407. }else if( !SyisAlphaNum(zTag[0]) ){
  38408. break;
  38409. }else{
  38410. zTag++;
  38411. }
  38412. }
  38413. if( zTag > zPtr ){
  38414. /* Perform the insertion */
  38415. SyStringInitFromBuf(&sEntry, zPtr, (int)(zTag-zPtr));
  38416. SyStringFullTrim(&sEntry);
  38417. SySetPut(pSet, (const void *)&sEntry);
  38418. }
  38419. /* Jump the trailing '>' */
  38420. zTag++;
  38421. }
  38422. return SXRET_OK;
  38423. }
  38424. /*
  38425. * Check if the given HTML tag name is present in the given container.
  38426. * Return SXRET_OK if present.SXERR_NOTFOUND otherwise.
  38427. * Refer to [strip_tags()].
  38428. */
  38429. static sxi32 FindTag(SySet *pSet, const char *zTag, int nByte)
  38430. {
  38431. if( SySetUsed(pSet) > 0 ){
  38432. const char *zCur, *zEnd = &zTag[nByte];
  38433. SyString sTag;
  38434. while( zTag < zEnd && (zTag[0] == '<' || zTag[0] == '/' || zTag[0] == '?' ||
  38435. ((unsigned char)zTag[0] < 0xc0 && SyisSpace(zTag[0]))) ){
  38436. zTag++;
  38437. }
  38438. /* Delimit the tag */
  38439. zCur = zTag;
  38440. while(zTag < zEnd ){
  38441. if( (unsigned char)zTag[0] >= 0xc0 ){
  38442. /* UTF-8 stream */
  38443. zTag++;
  38444. SX_JMP_UTF8(zTag, zEnd);
  38445. }else if( !SyisAlphaNum(zTag[0]) ){
  38446. break;
  38447. }else{
  38448. zTag++;
  38449. }
  38450. }
  38451. SyStringInitFromBuf(&sTag, zCur, zTag-zCur);
  38452. /* Trim leading white spaces and null bytes */
  38453. SyStringLeftTrimSafe(&sTag);
  38454. if( sTag.nByte > 0 ){
  38455. SyString *aEntry, *pEntry;
  38456. sxi32 rc;
  38457. sxu32 n;
  38458. /* Perform the lookup */
  38459. aEntry = (SyString *)SySetBasePtr(pSet);
  38460. for( n = 0 ; n < SySetUsed(pSet) ; ++n ){
  38461. pEntry = &aEntry[n];
  38462. /* Do the comparison */
  38463. rc = SyStringCmp(pEntry, &sTag, SyStrnicmp);
  38464. if( !rc ){
  38465. return SXRET_OK;
  38466. }
  38467. }
  38468. }
  38469. }
  38470. /* No such tag */
  38471. return SXERR_NOTFOUND;
  38472. }
  38473. /*
  38474. * This function tries to return a string [i.e: in the call context result buffer]
  38475. * with all NUL bytes, HTML and JX9 tags stripped from a given string.
  38476. * Refer to [strip_tags()].
  38477. */
  38478. JX9_PRIVATE sxi32 jx9StripTagsFromString(jx9_context *pCtx, const char *zIn, int nByte, const char *zTaglist, int nTaglen)
  38479. {
  38480. const char *zEnd = &zIn[nByte];
  38481. const char *zPtr, *zTag;
  38482. SySet sSet;
  38483. /* initialize the set of allowed tags */
  38484. SySetInit(&sSet, &pCtx->pVm->sAllocator, sizeof(SyString));
  38485. if( nTaglen > 0 ){
  38486. /* Set of allowed tags */
  38487. AddTag(&sSet, zTaglist, nTaglen);
  38488. }
  38489. /* Set the empty string */
  38490. jx9_result_string(pCtx, "", 0);
  38491. /* Start processing */
  38492. for(;;){
  38493. if(zIn >= zEnd){
  38494. /* No more input to process */
  38495. break;
  38496. }
  38497. zPtr = zIn;
  38498. /* Find a tag */
  38499. while( zIn < zEnd && zIn[0] != '<' && zIn[0] != 0 /* NUL byte */ ){
  38500. zIn++;
  38501. }
  38502. if( zIn > zPtr ){
  38503. /* Consume raw input */
  38504. jx9_result_string(pCtx, zPtr, (int)(zIn-zPtr));
  38505. }
  38506. /* Ignore trailing null bytes */
  38507. while( zIn < zEnd && zIn[0] == 0 ){
  38508. zIn++;
  38509. }
  38510. if(zIn >= zEnd){
  38511. /* No more input to process */
  38512. break;
  38513. }
  38514. if( zIn[0] == '<' ){
  38515. sxi32 rc;
  38516. zTag = zIn++;
  38517. /* Delimit the tag */
  38518. while( zIn < zEnd && zIn[0] != '>' ){
  38519. zIn++;
  38520. }
  38521. if( zIn < zEnd ){
  38522. zIn++; /* Ignore the trailing closing tag */
  38523. }
  38524. /* Query the set */
  38525. rc = FindTag(&sSet, zTag, (int)(zIn-zTag));
  38526. if( rc == SXRET_OK ){
  38527. /* Keep the tag */
  38528. jx9_result_string(pCtx, zTag, (int)(zIn-zTag));
  38529. }
  38530. }
  38531. }
  38532. /* Cleanup */
  38533. SySetRelease(&sSet);
  38534. return SXRET_OK;
  38535. }
  38536. /*
  38537. * string strip_tags(string $str[, string $allowable_tags])
  38538. * Strip HTML and JX9 tags from a string.
  38539. * Parameters
  38540. * $str
  38541. * The input string.
  38542. * $allowable_tags
  38543. * You can use the optional second parameter to specify tags which should not be stripped.
  38544. * Return
  38545. * Returns the stripped string.
  38546. */
  38547. static int jx9Builtin_strip_tags(jx9_context *pCtx, int nArg, jx9_value **apArg)
  38548. {
  38549. const char *zTaglist = 0;
  38550. const char *zString;
  38551. int nTaglen = 0;
  38552. int nLen;
  38553. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  38554. /* Missing/Invalid arguments, return the empty string */
  38555. jx9_result_string(pCtx, "", 0);
  38556. return JX9_OK;
  38557. }
  38558. /* Point to the raw string */
  38559. zString = jx9_value_to_string(apArg[0], &nLen);
  38560. if( nArg > 1 && jx9_value_is_string(apArg[1]) ){
  38561. /* Allowed tag */
  38562. zTaglist = jx9_value_to_string(apArg[1], &nTaglen);
  38563. }
  38564. /* Process input */
  38565. jx9StripTagsFromString(pCtx, zString, nLen, zTaglist, nTaglen);
  38566. return JX9_OK;
  38567. }
  38568. /*
  38569. * array str_split(string $string[, int $split_length = 1 ])
  38570. * Convert a string to an array.
  38571. * Parameters
  38572. * $str
  38573. * The input string.
  38574. * $split_length
  38575. * Maximum length of the chunk.
  38576. * Return
  38577. * If the optional split_length parameter is specified, the returned array
  38578. * will be broken down into chunks with each being split_length in length, otherwise
  38579. * each chunk will be one character in length. FALSE is returned if split_length is less than 1.
  38580. * If the split_length length exceeds the length of string, the entire string is returned
  38581. * as the first (and only) array element.
  38582. */
  38583. static int jx9Builtin_str_split(jx9_context *pCtx, int nArg, jx9_value **apArg)
  38584. {
  38585. const char *zString, *zEnd;
  38586. jx9_value *pArray, *pValue;
  38587. int split_len;
  38588. int nLen;
  38589. if( nArg < 1 ){
  38590. /* Missing arguments, return FALSE */
  38591. jx9_result_bool(pCtx, 0);
  38592. return JX9_OK;
  38593. }
  38594. /* Point to the target string */
  38595. zString = jx9_value_to_string(apArg[0], &nLen);
  38596. if( nLen < 1 ){
  38597. /* Nothing to process, return FALSE */
  38598. jx9_result_bool(pCtx, 0);
  38599. return JX9_OK;
  38600. }
  38601. split_len = (int)sizeof(char);
  38602. if( nArg > 1 ){
  38603. /* Split length */
  38604. split_len = jx9_value_to_int(apArg[1]);
  38605. if( split_len < 1 ){
  38606. /* Invalid length, return FALSE */
  38607. jx9_result_bool(pCtx, 0);
  38608. return JX9_OK;
  38609. }
  38610. if( split_len > nLen ){
  38611. split_len = nLen;
  38612. }
  38613. }
  38614. /* Create the array and the scalar value */
  38615. pArray = jx9_context_new_array(pCtx);
  38616. /*Chunk value */
  38617. pValue = jx9_context_new_scalar(pCtx);
  38618. if( pValue == 0 || pArray == 0 ){
  38619. /* Return FALSE */
  38620. jx9_result_bool(pCtx, 0);
  38621. return JX9_OK;
  38622. }
  38623. /* Point to the end of the string */
  38624. zEnd = &zString[nLen];
  38625. /* Perform the requested operation */
  38626. for(;;){
  38627. int nMax;
  38628. if( zString >= zEnd ){
  38629. /* No more input to process */
  38630. break;
  38631. }
  38632. nMax = (int)(zEnd-zString);
  38633. if( nMax < split_len ){
  38634. split_len = nMax;
  38635. }
  38636. /* Copy the current chunk */
  38637. jx9_value_string(pValue, zString, split_len);
  38638. /* Insert it */
  38639. jx9_array_add_elem(pArray, 0, pValue); /* Will make it's own copy */
  38640. /* reset the string cursor */
  38641. jx9_value_reset_string_cursor(pValue);
  38642. /* Update position */
  38643. zString += split_len;
  38644. }
  38645. /*
  38646. * Return the array.
  38647. * Don't worry about freeing memory, everything will be automatically released
  38648. * upon we return from this function.
  38649. */
  38650. jx9_result_value(pCtx, pArray);
  38651. return JX9_OK;
  38652. }
  38653. /*
  38654. * Tokenize a raw string and extract the first non-space token.
  38655. * Refer to [strspn()].
  38656. */
  38657. static sxi32 ExtractNonSpaceToken(const char **pzIn, const char *zEnd, SyString *pOut)
  38658. {
  38659. const char *zIn = *pzIn;
  38660. const char *zPtr;
  38661. /* Ignore leading white spaces */
  38662. while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
  38663. zIn++;
  38664. }
  38665. if( zIn >= zEnd ){
  38666. /* End of input */
  38667. return SXERR_EOF;
  38668. }
  38669. zPtr = zIn;
  38670. /* Extract the token */
  38671. while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && !SyisSpace(zIn[0]) ){
  38672. zIn++;
  38673. }
  38674. SyStringInitFromBuf(pOut, zPtr, zIn-zPtr);
  38675. /* Synchronize pointers */
  38676. *pzIn = zIn;
  38677. /* Return to the caller */
  38678. return SXRET_OK;
  38679. }
  38680. /*
  38681. * Check if the given string contains only characters from the given mask.
  38682. * return the longest match.
  38683. * Refer to [strspn()].
  38684. */
  38685. static int LongestStringMask(const char *zString, int nLen, const char *zMask, int nMaskLen)
  38686. {
  38687. const char *zEnd = &zString[nLen];
  38688. const char *zIn = zString;
  38689. int i, c;
  38690. for(;;){
  38691. if( zString >= zEnd ){
  38692. break;
  38693. }
  38694. /* Extract current character */
  38695. c = zString[0];
  38696. /* Perform the lookup */
  38697. for( i = 0 ; i < nMaskLen ; i++ ){
  38698. if( c == zMask[i] ){
  38699. /* Character found */
  38700. break;
  38701. }
  38702. }
  38703. if( i >= nMaskLen ){
  38704. /* Character not in the current mask, break immediately */
  38705. break;
  38706. }
  38707. /* Advance cursor */
  38708. zString++;
  38709. }
  38710. /* Longest match */
  38711. return (int)(zString-zIn);
  38712. }
  38713. /*
  38714. * Do the reverse operation of the previous function [i.e: LongestStringMask()].
  38715. * Refer to [strcspn()].
  38716. */
  38717. static int LongestStringMask2(const char *zString, int nLen, const char *zMask, int nMaskLen)
  38718. {
  38719. const char *zEnd = &zString[nLen];
  38720. const char *zIn = zString;
  38721. int i, c;
  38722. for(;;){
  38723. if( zString >= zEnd ){
  38724. break;
  38725. }
  38726. /* Extract current character */
  38727. c = zString[0];
  38728. /* Perform the lookup */
  38729. for( i = 0 ; i < nMaskLen ; i++ ){
  38730. if( c == zMask[i] ){
  38731. break;
  38732. }
  38733. }
  38734. if( i < nMaskLen ){
  38735. /* Character in the current mask, break immediately */
  38736. break;
  38737. }
  38738. /* Advance cursor */
  38739. zString++;
  38740. }
  38741. /* Longest match */
  38742. return (int)(zString-zIn);
  38743. }
  38744. /*
  38745. * int strspn(string $str, string $mask[, int $start[, int $length]])
  38746. * Finds the length of the initial segment of a string consisting entirely
  38747. * of characters contained within a given mask.
  38748. * Parameters
  38749. * $str
  38750. * The input string.
  38751. * $mask
  38752. * The list of allowable characters.
  38753. * $start
  38754. * The position in subject to start searching.
  38755. * If start is given and is non-negative, then strspn() will begin examining
  38756. * subject at the start'th position. For instance, in the string 'abcdef', the character
  38757. * at position 0 is 'a', the character at position 2 is 'c', and so forth.
  38758. * If start is given and is negative, then strspn() will begin examining subject at the
  38759. * start'th position from the end of subject.
  38760. * $length
  38761. * The length of the segment from subject to examine.
  38762. * If length is given and is non-negative, then subject will be examined for length
  38763. * characters after the starting position.
  38764. * If lengthis given and is negative, then subject will be examined from the starting
  38765. * position up to length characters from the end of subject.
  38766. * Return
  38767. * Returns the length of the initial segment of subject which consists entirely of characters
  38768. * in mask.
  38769. */
  38770. static int jx9Builtin_strspn(jx9_context *pCtx, int nArg, jx9_value **apArg)
  38771. {
  38772. const char *zString, *zMask, *zEnd;
  38773. int iMasklen, iLen;
  38774. SyString sToken;
  38775. int iCount = 0;
  38776. int rc;
  38777. if( nArg < 2 ){
  38778. /* Missing agruments, return zero */
  38779. jx9_result_int(pCtx, 0);
  38780. return JX9_OK;
  38781. }
  38782. /* Extract the target string */
  38783. zString = jx9_value_to_string(apArg[0], &iLen);
  38784. /* Extract the mask */
  38785. zMask = jx9_value_to_string(apArg[1], &iMasklen);
  38786. if( iLen < 1 || iMasklen < 1 ){
  38787. /* Nothing to process, return zero */
  38788. jx9_result_int(pCtx, 0);
  38789. return JX9_OK;
  38790. }
  38791. if( nArg > 2 ){
  38792. int nOfft;
  38793. /* Extract the offset */
  38794. nOfft = jx9_value_to_int(apArg[2]);
  38795. if( nOfft < 0 ){
  38796. const char *zBase = &zString[iLen + nOfft];
  38797. if( zBase > zString ){
  38798. iLen = (int)(&zString[iLen]-zBase);
  38799. zString = zBase;
  38800. }else{
  38801. /* Invalid offset */
  38802. jx9_result_int(pCtx, 0);
  38803. return JX9_OK;
  38804. }
  38805. }else{
  38806. if( nOfft >= iLen ){
  38807. /* Invalid offset */
  38808. jx9_result_int(pCtx, 0);
  38809. return JX9_OK;
  38810. }else{
  38811. /* Update offset */
  38812. zString += nOfft;
  38813. iLen -= nOfft;
  38814. }
  38815. }
  38816. if( nArg > 3 ){
  38817. int iUserlen;
  38818. /* Extract the desired length */
  38819. iUserlen = jx9_value_to_int(apArg[3]);
  38820. if( iUserlen > 0 && iUserlen < iLen ){
  38821. iLen = iUserlen;
  38822. }
  38823. }
  38824. }
  38825. /* Point to the end of the string */
  38826. zEnd = &zString[iLen];
  38827. /* Extract the first non-space token */
  38828. rc = ExtractNonSpaceToken(&zString, zEnd, &sToken);
  38829. if( rc == SXRET_OK && sToken.nByte > 0 ){
  38830. /* Compare against the current mask */
  38831. iCount = LongestStringMask(sToken.zString, (int)sToken.nByte, zMask, iMasklen);
  38832. }
  38833. /* Longest match */
  38834. jx9_result_int(pCtx, iCount);
  38835. return JX9_OK;
  38836. }
  38837. /*
  38838. * int strcspn(string $str, string $mask[, int $start[, int $length]])
  38839. * Find length of initial segment not matching mask.
  38840. * Parameters
  38841. * $str
  38842. * The input string.
  38843. * $mask
  38844. * The list of not allowed characters.
  38845. * $start
  38846. * The position in subject to start searching.
  38847. * If start is given and is non-negative, then strspn() will begin examining
  38848. * subject at the start'th position. For instance, in the string 'abcdef', the character
  38849. * at position 0 is 'a', the character at position 2 is 'c', and so forth.
  38850. * If start is given and is negative, then strspn() will begin examining subject at the
  38851. * start'th position from the end of subject.
  38852. * $length
  38853. * The length of the segment from subject to examine.
  38854. * If length is given and is non-negative, then subject will be examined for length
  38855. * characters after the starting position.
  38856. * If lengthis given and is negative, then subject will be examined from the starting
  38857. * position up to length characters from the end of subject.
  38858. * Return
  38859. * Returns the length of the segment as an integer.
  38860. */
  38861. static int jx9Builtin_strcspn(jx9_context *pCtx, int nArg, jx9_value **apArg)
  38862. {
  38863. const char *zString, *zMask, *zEnd;
  38864. int iMasklen, iLen;
  38865. SyString sToken;
  38866. int iCount = 0;
  38867. int rc;
  38868. if( nArg < 2 ){
  38869. /* Missing agruments, return zero */
  38870. jx9_result_int(pCtx, 0);
  38871. return JX9_OK;
  38872. }
  38873. /* Extract the target string */
  38874. zString = jx9_value_to_string(apArg[0], &iLen);
  38875. /* Extract the mask */
  38876. zMask = jx9_value_to_string(apArg[1], &iMasklen);
  38877. if( iLen < 1 ){
  38878. /* Nothing to process, return zero */
  38879. jx9_result_int(pCtx, 0);
  38880. return JX9_OK;
  38881. }
  38882. if( iMasklen < 1 ){
  38883. /* No given mask, return the string length */
  38884. jx9_result_int(pCtx, iLen);
  38885. return JX9_OK;
  38886. }
  38887. if( nArg > 2 ){
  38888. int nOfft;
  38889. /* Extract the offset */
  38890. nOfft = jx9_value_to_int(apArg[2]);
  38891. if( nOfft < 0 ){
  38892. const char *zBase = &zString[iLen + nOfft];
  38893. if( zBase > zString ){
  38894. iLen = (int)(&zString[iLen]-zBase);
  38895. zString = zBase;
  38896. }else{
  38897. /* Invalid offset */
  38898. jx9_result_int(pCtx, 0);
  38899. return JX9_OK;
  38900. }
  38901. }else{
  38902. if( nOfft >= iLen ){
  38903. /* Invalid offset */
  38904. jx9_result_int(pCtx, 0);
  38905. return JX9_OK;
  38906. }else{
  38907. /* Update offset */
  38908. zString += nOfft;
  38909. iLen -= nOfft;
  38910. }
  38911. }
  38912. if( nArg > 3 ){
  38913. int iUserlen;
  38914. /* Extract the desired length */
  38915. iUserlen = jx9_value_to_int(apArg[3]);
  38916. if( iUserlen > 0 && iUserlen < iLen ){
  38917. iLen = iUserlen;
  38918. }
  38919. }
  38920. }
  38921. /* Point to the end of the string */
  38922. zEnd = &zString[iLen];
  38923. /* Extract the first non-space token */
  38924. rc = ExtractNonSpaceToken(&zString, zEnd, &sToken);
  38925. if( rc == SXRET_OK && sToken.nByte > 0 ){
  38926. /* Compare against the current mask */
  38927. iCount = LongestStringMask2(sToken.zString, (int)sToken.nByte, zMask, iMasklen);
  38928. }
  38929. /* Longest match */
  38930. jx9_result_int(pCtx, iCount);
  38931. return JX9_OK;
  38932. }
  38933. /*
  38934. * string strpbrk(string $haystack, string $char_list)
  38935. * Search a string for any of a set of characters.
  38936. * Parameters
  38937. * $haystack
  38938. * The string where char_list is looked for.
  38939. * $char_list
  38940. * This parameter is case sensitive.
  38941. * Return
  38942. * Returns a string starting from the character found, or FALSE if it is not found.
  38943. */
  38944. static int jx9Builtin_strpbrk(jx9_context *pCtx, int nArg, jx9_value **apArg)
  38945. {
  38946. const char *zString, *zList, *zEnd;
  38947. int iLen, iListLen, i, c;
  38948. sxu32 nOfft, nMax;
  38949. sxi32 rc;
  38950. if( nArg < 2 ){
  38951. /* Missing arguments, return FALSE */
  38952. jx9_result_bool(pCtx, 0);
  38953. return JX9_OK;
  38954. }
  38955. /* Extract the haystack and the char list */
  38956. zString = jx9_value_to_string(apArg[0], &iLen);
  38957. zList = jx9_value_to_string(apArg[1], &iListLen);
  38958. if( iLen < 1 ){
  38959. /* Nothing to process, return FALSE */
  38960. jx9_result_bool(pCtx, 0);
  38961. return JX9_OK;
  38962. }
  38963. /* Point to the end of the string */
  38964. zEnd = &zString[iLen];
  38965. nOfft = nMax = SXU32_HIGH;
  38966. /* perform the requested operation */
  38967. for( i = 0 ; i < iListLen ; i++ ){
  38968. c = zList[i];
  38969. rc = SyByteFind(zString, (sxu32)iLen, c, &nMax);
  38970. if( rc == SXRET_OK ){
  38971. if( nMax < nOfft ){
  38972. nOfft = nMax;
  38973. }
  38974. }
  38975. }
  38976. if( nOfft == SXU32_HIGH ){
  38977. /* No such substring, return FALSE */
  38978. jx9_result_bool(pCtx, 0);
  38979. }else{
  38980. /* Return the substring */
  38981. jx9_result_string(pCtx, &zString[nOfft], (int)(zEnd-&zString[nOfft]));
  38982. }
  38983. return JX9_OK;
  38984. }
  38985. /*
  38986. * string soundex(string $str)
  38987. * Calculate the soundex key of a string.
  38988. * Parameters
  38989. * $str
  38990. * The input string.
  38991. * Return
  38992. * Returns the soundex key as a string.
  38993. * Note:
  38994. * This implementation is based on the one found in the SQLite3
  38995. * source tree.
  38996. */
  38997. static int jx9Builtin_soundex(jx9_context *pCtx, int nArg, jx9_value **apArg)
  38998. {
  38999. const unsigned char *zIn;
  39000. char zResult[8];
  39001. int i, j;
  39002. static const unsigned char iCode[] = {
  39003. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  39004. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  39005. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  39006. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  39007. 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0,
  39008. 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0,
  39009. 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0,
  39010. 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0,
  39011. };
  39012. if( nArg < 1 ){
  39013. /* Missing arguments, return the empty string */
  39014. jx9_result_string(pCtx, "", 0);
  39015. return JX9_OK;
  39016. }
  39017. zIn = (unsigned char *)jx9_value_to_string(apArg[0], 0);
  39018. for(i=0; zIn[i] && zIn[i] < 0xc0 && !SyisAlpha(zIn[i]); i++){}
  39019. if( zIn[i] ){
  39020. unsigned char prevcode = iCode[zIn[i]&0x7f];
  39021. zResult[0] = (char)SyToUpper(zIn[i]);
  39022. for(j=1; j<4 && zIn[i]; i++){
  39023. int code = iCode[zIn[i]&0x7f];
  39024. if( code>0 ){
  39025. if( code!=prevcode ){
  39026. prevcode = (unsigned char)code;
  39027. zResult[j++] = (char)code + '0';
  39028. }
  39029. }else{
  39030. prevcode = 0;
  39031. }
  39032. }
  39033. while( j<4 ){
  39034. zResult[j++] = '0';
  39035. }
  39036. jx9_result_string(pCtx, zResult, 4);
  39037. }else{
  39038. jx9_result_string(pCtx, "?000", 4);
  39039. }
  39040. return JX9_OK;
  39041. }
  39042. /*
  39043. * string wordwrap(string $str[, int $width = 75[, string $break = "\n"]])
  39044. * Wraps a string to a given number of characters.
  39045. * Parameters
  39046. * $str
  39047. * The input string.
  39048. * $width
  39049. * The column width.
  39050. * $break
  39051. * The line is broken using the optional break parameter.
  39052. * Return
  39053. * Returns the given string wrapped at the specified column.
  39054. */
  39055. static int jx9Builtin_wordwrap(jx9_context *pCtx, int nArg, jx9_value **apArg)
  39056. {
  39057. const char *zIn, *zEnd, *zBreak;
  39058. int iLen, iBreaklen, iChunk;
  39059. if( nArg < 1 ){
  39060. /* Missing arguments, return the empty string */
  39061. jx9_result_string(pCtx, "", 0);
  39062. return JX9_OK;
  39063. }
  39064. /* Extract the input string */
  39065. zIn = jx9_value_to_string(apArg[0], &iLen);
  39066. if( iLen < 1 ){
  39067. /* Nothing to process, return the empty string */
  39068. jx9_result_string(pCtx, "", 0);
  39069. return JX9_OK;
  39070. }
  39071. /* Chunk length */
  39072. iChunk = 75;
  39073. iBreaklen = 0;
  39074. zBreak = ""; /* cc warning */
  39075. if( nArg > 1 ){
  39076. iChunk = jx9_value_to_int(apArg[1]);
  39077. if( iChunk < 1 ){
  39078. iChunk = 75;
  39079. }
  39080. if( nArg > 2 ){
  39081. zBreak = jx9_value_to_string(apArg[2], &iBreaklen);
  39082. }
  39083. }
  39084. if( iBreaklen < 1 ){
  39085. /* Set a default column break */
  39086. #ifdef __WINNT__
  39087. zBreak = "\r\n";
  39088. iBreaklen = (int)sizeof("\r\n")-1;
  39089. #else
  39090. zBreak = "\n";
  39091. iBreaklen = (int)sizeof(char);
  39092. #endif
  39093. }
  39094. /* Perform the requested operation */
  39095. zEnd = &zIn[iLen];
  39096. for(;;){
  39097. int nMax;
  39098. if( zIn >= zEnd ){
  39099. /* No more input to process */
  39100. break;
  39101. }
  39102. nMax = (int)(zEnd-zIn);
  39103. if( iChunk > nMax ){
  39104. iChunk = nMax;
  39105. }
  39106. /* Append the column first */
  39107. jx9_result_string(pCtx, zIn, iChunk); /* Will make it's own copy */
  39108. /* Advance the cursor */
  39109. zIn += iChunk;
  39110. if( zIn < zEnd ){
  39111. /* Append the line break */
  39112. jx9_result_string(pCtx, zBreak, iBreaklen);
  39113. }
  39114. }
  39115. return JX9_OK;
  39116. }
  39117. /*
  39118. * Check if the given character is a member of the given mask.
  39119. * Return TRUE on success. FALSE otherwise.
  39120. * Refer to [strtok()].
  39121. */
  39122. static int CheckMask(int c, const char *zMask, int nMasklen, int *pOfft)
  39123. {
  39124. int i;
  39125. for( i = 0 ; i < nMasklen ; ++i ){
  39126. if( c == zMask[i] ){
  39127. if( pOfft ){
  39128. *pOfft = i;
  39129. }
  39130. return TRUE;
  39131. }
  39132. }
  39133. return FALSE;
  39134. }
  39135. /*
  39136. * Extract a single token from the input stream.
  39137. * Refer to [strtok()].
  39138. */
  39139. static sxi32 ExtractToken(const char **pzIn, const char *zEnd, const char *zMask, int nMasklen, SyString *pOut)
  39140. {
  39141. const char *zIn = *pzIn;
  39142. const char *zPtr;
  39143. /* Ignore leading delimiter */
  39144. while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && CheckMask(zIn[0], zMask, nMasklen, 0) ){
  39145. zIn++;
  39146. }
  39147. if( zIn >= zEnd ){
  39148. /* End of input */
  39149. return SXERR_EOF;
  39150. }
  39151. zPtr = zIn;
  39152. /* Extract the token */
  39153. while( zIn < zEnd ){
  39154. if( (unsigned char)zIn[0] >= 0xc0 ){
  39155. /* UTF-8 stream */
  39156. zIn++;
  39157. SX_JMP_UTF8(zIn, zEnd);
  39158. }else{
  39159. if( CheckMask(zIn[0], zMask, nMasklen, 0) ){
  39160. break;
  39161. }
  39162. zIn++;
  39163. }
  39164. }
  39165. SyStringInitFromBuf(pOut, zPtr, zIn-zPtr);
  39166. /* Update the cursor */
  39167. *pzIn = zIn;
  39168. /* Return to the caller */
  39169. return SXRET_OK;
  39170. }
  39171. /* strtok auxiliary private data */
  39172. typedef struct strtok_aux_data strtok_aux_data;
  39173. struct strtok_aux_data
  39174. {
  39175. const char *zDup; /* Complete duplicate of the input */
  39176. const char *zIn; /* Current input stream */
  39177. const char *zEnd; /* End of input */
  39178. };
  39179. /*
  39180. * string strtok(string $str, string $token)
  39181. * string strtok(string $token)
  39182. * strtok() splits a string (str) into smaller strings (tokens), with each token
  39183. * being delimited by any character from token. That is, if you have a string like
  39184. * "This is an example string" you could tokenize this string into its individual
  39185. * words by using the space character as the token.
  39186. * Note that only the first call to strtok uses the string argument. Every subsequent
  39187. * call to strtok only needs the token to use, as it keeps track of where it is in
  39188. * the current string. To start over, or to tokenize a new string you simply call strtok
  39189. * with the string argument again to initialize it. Note that you may put multiple tokens
  39190. * in the token parameter. The string will be tokenized when any one of the characters in
  39191. * the argument are found.
  39192. * Parameters
  39193. * $str
  39194. * The string being split up into smaller strings (tokens).
  39195. * $token
  39196. * The delimiter used when splitting up str.
  39197. * Return
  39198. * Current token or FALSE on EOF.
  39199. */
  39200. static int jx9Builtin_strtok(jx9_context *pCtx, int nArg, jx9_value **apArg)
  39201. {
  39202. strtok_aux_data *pAux;
  39203. const char *zMask;
  39204. SyString sToken;
  39205. int nMasklen;
  39206. sxi32 rc;
  39207. if( nArg < 2 ){
  39208. /* Extract top aux data */
  39209. pAux = (strtok_aux_data *)jx9_context_peek_aux_data(pCtx);
  39210. if( pAux == 0 ){
  39211. /* No aux data, return FALSE */
  39212. jx9_result_bool(pCtx, 0);
  39213. return JX9_OK;
  39214. }
  39215. nMasklen = 0;
  39216. zMask = ""; /* cc warning */
  39217. if( nArg > 0 ){
  39218. /* Extract the mask */
  39219. zMask = jx9_value_to_string(apArg[0], &nMasklen);
  39220. }
  39221. if( nMasklen < 1 ){
  39222. /* Invalid mask, return FALSE */
  39223. jx9_context_free_chunk(pCtx, (void *)pAux->zDup);
  39224. jx9_context_free_chunk(pCtx, pAux);
  39225. (void)jx9_context_pop_aux_data(pCtx);
  39226. jx9_result_bool(pCtx, 0);
  39227. return JX9_OK;
  39228. }
  39229. /* Extract the token */
  39230. rc = ExtractToken(&pAux->zIn, pAux->zEnd, zMask, nMasklen, &sToken);
  39231. if( rc != SXRET_OK ){
  39232. /* EOF , discard the aux data */
  39233. jx9_context_free_chunk(pCtx, (void *)pAux->zDup);
  39234. jx9_context_free_chunk(pCtx, pAux);
  39235. (void)jx9_context_pop_aux_data(pCtx);
  39236. jx9_result_bool(pCtx, 0);
  39237. }else{
  39238. /* Return the extracted token */
  39239. jx9_result_string(pCtx, sToken.zString, (int)sToken.nByte);
  39240. }
  39241. }else{
  39242. const char *zInput, *zCur;
  39243. char *zDup;
  39244. int nLen;
  39245. /* Extract the raw input */
  39246. zCur = zInput = jx9_value_to_string(apArg[0], &nLen);
  39247. if( nLen < 1 ){
  39248. /* Empty input, return FALSE */
  39249. jx9_result_bool(pCtx, 0);
  39250. return JX9_OK;
  39251. }
  39252. /* Extract the mask */
  39253. zMask = jx9_value_to_string(apArg[1], &nMasklen);
  39254. if( nMasklen < 1 ){
  39255. /* Set a default mask */
  39256. #define TOK_MASK " \n\t\r\f"
  39257. zMask = TOK_MASK;
  39258. nMasklen = (int)sizeof(TOK_MASK) - 1;
  39259. #undef TOK_MASK
  39260. }
  39261. /* Extract a single token */
  39262. rc = ExtractToken(&zInput, &zInput[nLen], zMask, nMasklen, &sToken);
  39263. if( rc != SXRET_OK ){
  39264. /* Empty input */
  39265. jx9_result_bool(pCtx, 0);
  39266. return JX9_OK;
  39267. }else{
  39268. /* Return the extracted token */
  39269. jx9_result_string(pCtx, sToken.zString, (int)sToken.nByte);
  39270. }
  39271. /* Create our auxilliary data and copy the input */
  39272. pAux = (strtok_aux_data *)jx9_context_alloc_chunk(pCtx, sizeof(strtok_aux_data), TRUE, FALSE);
  39273. if( pAux ){
  39274. nLen -= (int)(zInput-zCur);
  39275. if( nLen < 1 ){
  39276. jx9_context_free_chunk(pCtx, pAux);
  39277. return JX9_OK;
  39278. }
  39279. /* Duplicate input */
  39280. zDup = (char *)jx9_context_alloc_chunk(pCtx, (unsigned int)(nLen+1), TRUE, FALSE);
  39281. if( zDup ){
  39282. SyMemcpy(zInput, zDup, (sxu32)nLen);
  39283. /* Register the aux data */
  39284. pAux->zDup = pAux->zIn = zDup;
  39285. pAux->zEnd = &zDup[nLen];
  39286. jx9_context_push_aux_data(pCtx, pAux);
  39287. }
  39288. }
  39289. }
  39290. return JX9_OK;
  39291. }
  39292. /*
  39293. * string str_pad(string $input, int $pad_length[, string $pad_string = " " [, int $pad_type = STR_PAD_RIGHT]])
  39294. * Pad a string to a certain length with another string
  39295. * Parameters
  39296. * $input
  39297. * The input string.
  39298. * $pad_length
  39299. * If the value of pad_length is negative, less than, or equal to the length of the input
  39300. * string, no padding takes place.
  39301. * $pad_string
  39302. * Note:
  39303. * The pad_string WIIL NOT BE truncated if the required number of padding characters can't be evenly
  39304. * divided by the pad_string's length.
  39305. * $pad_type
  39306. * Optional argument pad_type can be STR_PAD_RIGHT, STR_PAD_LEFT, or STR_PAD_BOTH. If pad_type
  39307. * is not specified it is assumed to be STR_PAD_RIGHT.
  39308. * Return
  39309. * The padded string.
  39310. */
  39311. static int jx9Builtin_str_pad(jx9_context *pCtx, int nArg, jx9_value **apArg)
  39312. {
  39313. int iLen, iPadlen, iType, i, iDiv, iStrpad, iRealPad, jPad;
  39314. const char *zIn, *zPad;
  39315. if( nArg < 2 ){
  39316. /* Missing arguments, return the empty string */
  39317. jx9_result_string(pCtx, "", 0);
  39318. return JX9_OK;
  39319. }
  39320. /* Extract the target string */
  39321. zIn = jx9_value_to_string(apArg[0], &iLen);
  39322. /* Padding length */
  39323. iRealPad = iPadlen = jx9_value_to_int(apArg[1]);
  39324. if( iPadlen > 0 ){
  39325. iPadlen -= iLen;
  39326. }
  39327. if( iPadlen < 1 ){
  39328. /* Return the string verbatim */
  39329. jx9_result_string(pCtx, zIn, iLen);
  39330. return JX9_OK;
  39331. }
  39332. zPad = " "; /* Whitespace padding */
  39333. iStrpad = (int)sizeof(char);
  39334. iType = 1 ; /* STR_PAD_RIGHT */
  39335. if( nArg > 2 ){
  39336. /* Padding string */
  39337. zPad = jx9_value_to_string(apArg[2], &iStrpad);
  39338. if( iStrpad < 1 ){
  39339. /* Empty string */
  39340. zPad = " "; /* Whitespace padding */
  39341. iStrpad = (int)sizeof(char);
  39342. }
  39343. if( nArg > 3 ){
  39344. /* Padd type */
  39345. iType = jx9_value_to_int(apArg[3]);
  39346. if( iType != 0 /* STR_PAD_LEFT */ && iType != 2 /* STR_PAD_BOTH */ ){
  39347. iType = 1 ; /* STR_PAD_RIGHT */
  39348. }
  39349. }
  39350. }
  39351. iDiv = 1;
  39352. if( iType == 2 ){
  39353. iDiv = 2; /* STR_PAD_BOTH */
  39354. }
  39355. /* Perform the requested operation */
  39356. if( iType == 0 /* STR_PAD_LEFT */ || iType == 2 /* STR_PAD_BOTH */ ){
  39357. jPad = iStrpad;
  39358. for( i = 0 ; i < iPadlen/iDiv ; i += jPad ){
  39359. /* Padding */
  39360. if( (int)jx9_context_result_buf_length(pCtx) + iLen + jPad >= iRealPad ){
  39361. break;
  39362. }
  39363. jx9_result_string(pCtx, zPad, jPad);
  39364. }
  39365. if( iType == 0 /* STR_PAD_LEFT */ ){
  39366. while( (int)jx9_context_result_buf_length(pCtx) + iLen < iRealPad ){
  39367. jPad = iRealPad - (iLen + (int)jx9_context_result_buf_length(pCtx) );
  39368. if( jPad > iStrpad ){
  39369. jPad = iStrpad;
  39370. }
  39371. if( jPad < 1){
  39372. break;
  39373. }
  39374. jx9_result_string(pCtx, zPad, jPad);
  39375. }
  39376. }
  39377. }
  39378. if( iLen > 0 ){
  39379. /* Append the input string */
  39380. jx9_result_string(pCtx, zIn, iLen);
  39381. }
  39382. if( iType == 1 /* STR_PAD_RIGHT */ || iType == 2 /* STR_PAD_BOTH */ ){
  39383. for( i = 0 ; i < iPadlen/iDiv ; i += iStrpad ){
  39384. /* Padding */
  39385. if( (int)jx9_context_result_buf_length(pCtx) + iStrpad >= iRealPad ){
  39386. break;
  39387. }
  39388. jx9_result_string(pCtx, zPad, iStrpad);
  39389. }
  39390. while( (int)jx9_context_result_buf_length(pCtx) < iRealPad ){
  39391. jPad = iRealPad - (int)jx9_context_result_buf_length(pCtx);
  39392. if( jPad > iStrpad ){
  39393. jPad = iStrpad;
  39394. }
  39395. if( jPad < 1){
  39396. break;
  39397. }
  39398. jx9_result_string(pCtx, zPad, jPad);
  39399. }
  39400. }
  39401. return JX9_OK;
  39402. }
  39403. /*
  39404. * String replacement private data.
  39405. */
  39406. typedef struct str_replace_data str_replace_data;
  39407. struct str_replace_data
  39408. {
  39409. /* The following two fields are only used by the strtr function */
  39410. SyBlob *pWorker; /* Working buffer */
  39411. ProcStringMatch xMatch; /* Pattern match routine */
  39412. /* The following two fields are only used by the str_replace function */
  39413. SySet *pCollector; /* Argument collector*/
  39414. jx9_context *pCtx; /* Call context */
  39415. };
  39416. /*
  39417. * Remove a substring.
  39418. */
  39419. #define STRDEL(SRC, SLEN, OFFT, ILEN){\
  39420. for(;;){\
  39421. if( OFFT + ILEN >= SLEN ) break; SRC[OFFT] = SRC[OFFT+ILEN]; ++OFFT;\
  39422. }\
  39423. }
  39424. /*
  39425. * Shift right and insert algorithm.
  39426. */
  39427. #define SHIFTRANDINSERT(SRC, LEN, OFFT, ENTRY, ELEN){\
  39428. sxu32 INLEN = LEN - OFFT;\
  39429. for(;;){\
  39430. if( LEN > 0 ){ LEN--; } if(INLEN < 1 ) break; SRC[LEN + ELEN] = SRC[LEN] ; --INLEN; \
  39431. }\
  39432. for(;;){\
  39433. if(ELEN < 1)break; SRC[OFFT] = ENTRY[0]; OFFT++; ENTRY++; --ELEN;\
  39434. }\
  39435. }
  39436. /*
  39437. * Replace all occurrences of the search string at offset (nOfft) with the given
  39438. * replacement string [i.e: zReplace].
  39439. */
  39440. static int StringReplace(SyBlob *pWorker, sxu32 nOfft, int nLen, const char *zReplace, int nReplen)
  39441. {
  39442. char *zInput = (char *)SyBlobData(pWorker);
  39443. sxu32 n, m;
  39444. n = SyBlobLength(pWorker);
  39445. m = nOfft;
  39446. /* Delete the old entry */
  39447. STRDEL(zInput, n, m, nLen);
  39448. SyBlobLength(pWorker) -= nLen;
  39449. if( nReplen > 0 ){
  39450. sxi32 iRep = nReplen;
  39451. sxi32 rc;
  39452. /*
  39453. * Make sure the working buffer is big enough to hold the replacement
  39454. * string.
  39455. */
  39456. rc = SyBlobAppend(pWorker, 0/* Grow without an append operation*/, (sxu32)nReplen);
  39457. if( rc != SXRET_OK ){
  39458. /* Simply ignore any memory failure problem */
  39459. return SXRET_OK;
  39460. }
  39461. /* Perform the insertion now */
  39462. zInput = (char *)SyBlobData(pWorker);
  39463. n = SyBlobLength(pWorker);
  39464. SHIFTRANDINSERT(zInput, n, nOfft, zReplace, iRep);
  39465. SyBlobLength(pWorker) += nReplen;
  39466. }
  39467. return SXRET_OK;
  39468. }
  39469. /*
  39470. * String replacement walker callback.
  39471. * The following callback is invoked for each array entry that hold
  39472. * the replace string.
  39473. * Refer to the strtr() implementation for more information.
  39474. */
  39475. static int StringReplaceWalker(jx9_value *pKey, jx9_value *pData, void *pUserData)
  39476. {
  39477. str_replace_data *pRepData = (str_replace_data *)pUserData;
  39478. const char *zTarget, *zReplace;
  39479. SyBlob *pWorker;
  39480. int tLen, nLen;
  39481. sxu32 nOfft;
  39482. sxi32 rc;
  39483. /* Point to the working buffer */
  39484. pWorker = pRepData->pWorker;
  39485. if( !jx9_value_is_string(pKey) ){
  39486. /* Target and replace must be a string */
  39487. return JX9_OK;
  39488. }
  39489. /* Extract the target and the replace */
  39490. zTarget = jx9_value_to_string(pKey, &tLen);
  39491. if( tLen < 1 ){
  39492. /* Empty target, return immediately */
  39493. return JX9_OK;
  39494. }
  39495. /* Perform a pattern search */
  39496. rc = pRepData->xMatch(SyBlobData(pWorker), SyBlobLength(pWorker), (const void *)zTarget, (sxu32)tLen, &nOfft);
  39497. if( rc != SXRET_OK ){
  39498. /* Pattern not found */
  39499. return JX9_OK;
  39500. }
  39501. /* Extract the replace string */
  39502. zReplace = jx9_value_to_string(pData, &nLen);
  39503. /* Perform the replace process */
  39504. StringReplace(pWorker, nOfft, tLen, zReplace, nLen);
  39505. /* All done */
  39506. return JX9_OK;
  39507. }
  39508. /*
  39509. * The following walker callback is invoked by the str_rplace() function inorder
  39510. * to collect search/replace string.
  39511. * This callback is invoked only if the given argument is of type array.
  39512. */
  39513. static int StrReplaceWalker(jx9_value *pKey, jx9_value *pData, void *pUserData)
  39514. {
  39515. str_replace_data *pRep = (str_replace_data *)pUserData;
  39516. SyString sWorker;
  39517. const char *zIn;
  39518. int nByte;
  39519. /* Extract a string representation of the given argument */
  39520. zIn = jx9_value_to_string(pData, &nByte);
  39521. SyStringInitFromBuf(&sWorker, 0, 0);
  39522. if( nByte > 0 ){
  39523. char *zDup;
  39524. /* Duplicate the chunk */
  39525. zDup = (char *)jx9_context_alloc_chunk(pRep->pCtx, (unsigned int)nByte, FALSE,
  39526. TRUE /* Release the chunk automatically, upon this context is destroyd */
  39527. );
  39528. if( zDup == 0 ){
  39529. /* Ignore any memory failure problem */
  39530. jx9_context_throw_error(pRep->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
  39531. return JX9_OK;
  39532. }
  39533. SyMemcpy(zIn, zDup, (sxu32)nByte);
  39534. /* Save the chunk */
  39535. SyStringInitFromBuf(&sWorker, zDup, nByte);
  39536. }
  39537. /* Save for later processing */
  39538. SySetPut(pRep->pCollector, (const void *)&sWorker);
  39539. /* All done */
  39540. SXUNUSED(pKey); /* cc warning */
  39541. return JX9_OK;
  39542. }
  39543. /*
  39544. * mixed str_replace(mixed $search, mixed $replace, mixed $subject[, int &$count ])
  39545. * mixed str_ireplace(mixed $search, mixed $replace, mixed $subject[, int &$count ])
  39546. * Replace all occurrences of the search string with the replacement string.
  39547. * Parameters
  39548. * If search and replace are arrays, then str_replace() takes a value from each
  39549. * array and uses them to search and replace on subject. If replace has fewer values
  39550. * than search, then an empty string is used for the rest of replacement values.
  39551. * If search is an array and replace is a string, then this replacement string is used
  39552. * for every value of search. The converse would not make sense, though.
  39553. * If search or replace are arrays, their elements are processed first to last.
  39554. * $search
  39555. * The value being searched for, otherwise known as the needle. An array may be used
  39556. * to designate multiple needles.
  39557. * $replace
  39558. * The replacement value that replaces found search values. An array may be used
  39559. * to designate multiple replacements.
  39560. * $subject
  39561. * The string or array being searched and replaced on, otherwise known as the haystack.
  39562. * If subject is an array, then the search and replace is performed with every entry
  39563. * of subject, and the return value is an array as well.
  39564. * $count (Not used)
  39565. * If passed, this will be set to the number of replacements performed.
  39566. * Return
  39567. * This function returns a string or an array with the replaced values.
  39568. */
  39569. static int jx9Builtin_str_replace(jx9_context *pCtx, int nArg, jx9_value **apArg)
  39570. {
  39571. SyString sTemp, *pSearch, *pReplace;
  39572. ProcStringMatch xMatch;
  39573. const char *zIn, *zFunc;
  39574. str_replace_data sRep;
  39575. SyBlob sWorker;
  39576. SySet sReplace;
  39577. SySet sSearch;
  39578. int rep_str;
  39579. int nByte;
  39580. sxi32 rc;
  39581. if( nArg < 3 ){
  39582. /* Missing/Invalid arguments, return null */
  39583. jx9_result_null(pCtx);
  39584. return JX9_OK;
  39585. }
  39586. /* Initialize fields */
  39587. SySetInit(&sSearch, &pCtx->pVm->sAllocator, sizeof(SyString));
  39588. SySetInit(&sReplace, &pCtx->pVm->sAllocator, sizeof(SyString));
  39589. SyBlobInit(&sWorker, &pCtx->pVm->sAllocator);
  39590. SyZero(&sRep, sizeof(str_replace_data));
  39591. sRep.pCtx = pCtx;
  39592. sRep.pCollector = &sSearch;
  39593. rep_str = 0;
  39594. /* Extract the subject */
  39595. zIn = jx9_value_to_string(apArg[2], &nByte);
  39596. if( nByte < 1 ){
  39597. /* Nothing to replace, return the empty string */
  39598. jx9_result_string(pCtx, "", 0);
  39599. return JX9_OK;
  39600. }
  39601. /* Copy the subject */
  39602. SyBlobAppend(&sWorker, (const void *)zIn, (sxu32)nByte);
  39603. /* Search string */
  39604. if( jx9_value_is_json_array(apArg[0]) ){
  39605. /* Collect search string */
  39606. jx9_array_walk(apArg[0], StrReplaceWalker, &sRep);
  39607. }else{
  39608. /* Single pattern */
  39609. zIn = jx9_value_to_string(apArg[0], &nByte);
  39610. if( nByte < 1 ){
  39611. /* Return the subject untouched since no search string is available */
  39612. jx9_result_value(pCtx, apArg[2]/* Subject as thrird argument*/);
  39613. return JX9_OK;
  39614. }
  39615. SyStringInitFromBuf(&sTemp, zIn, nByte);
  39616. /* Save for later processing */
  39617. SySetPut(&sSearch, (const void *)&sTemp);
  39618. }
  39619. /* Replace string */
  39620. if( jx9_value_is_json_array(apArg[1]) ){
  39621. /* Collect replace string */
  39622. sRep.pCollector = &sReplace;
  39623. jx9_array_walk(apArg[1], StrReplaceWalker, &sRep);
  39624. }else{
  39625. /* Single needle */
  39626. zIn = jx9_value_to_string(apArg[1], &nByte);
  39627. rep_str = 1;
  39628. SyStringInitFromBuf(&sTemp, zIn, nByte);
  39629. /* Save for later processing */
  39630. SySetPut(&sReplace, (const void *)&sTemp);
  39631. }
  39632. /* Reset loop cursors */
  39633. SySetResetCursor(&sSearch);
  39634. SySetResetCursor(&sReplace);
  39635. pReplace = pSearch = 0; /* cc warning */
  39636. SyStringInitFromBuf(&sTemp, "", 0);
  39637. /* Extract function name */
  39638. zFunc = jx9_function_name(pCtx);
  39639. /* Set the default pattern match routine */
  39640. xMatch = SyBlobSearch;
  39641. if( SyStrncmp(zFunc, "str_ireplace", sizeof("str_ireplace") - 1) == 0 ){
  39642. /* Case insensitive pattern match */
  39643. xMatch = iPatternMatch;
  39644. }
  39645. /* Start the replace process */
  39646. while( SXRET_OK == SySetGetNextEntry(&sSearch, (void **)&pSearch) ){
  39647. sxu32 nCount, nOfft;
  39648. if( pSearch->nByte < 1 ){
  39649. /* Empty string, ignore */
  39650. continue;
  39651. }
  39652. /* Extract the replace string */
  39653. if( rep_str ){
  39654. pReplace = (SyString *)SySetPeek(&sReplace);
  39655. }else{
  39656. if( SXRET_OK != SySetGetNextEntry(&sReplace, (void **)&pReplace) ){
  39657. /* Sepecial case when 'replace set' has fewer values than the search set.
  39658. * An empty string is used for the rest of replacement values
  39659. */
  39660. pReplace = 0;
  39661. }
  39662. }
  39663. if( pReplace == 0 ){
  39664. /* Use an empty string instead */
  39665. pReplace = &sTemp;
  39666. }
  39667. nOfft = nCount = 0;
  39668. for(;;){
  39669. if( nCount >= SyBlobLength(&sWorker) ){
  39670. break;
  39671. }
  39672. /* Perform a pattern lookup */
  39673. rc = xMatch(SyBlobDataAt(&sWorker, nCount), SyBlobLength(&sWorker) - nCount, (const void *)pSearch->zString,
  39674. pSearch->nByte, &nOfft);
  39675. if( rc != SXRET_OK ){
  39676. /* Pattern not found */
  39677. break;
  39678. }
  39679. /* Perform the replace operation */
  39680. StringReplace(&sWorker, nCount+nOfft, (int)pSearch->nByte, pReplace->zString, (int)pReplace->nByte);
  39681. /* Increment offset counter */
  39682. nCount += nOfft + pReplace->nByte;
  39683. }
  39684. }
  39685. /* All done, clean-up the mess left behind */
  39686. jx9_result_string(pCtx, (const char *)SyBlobData(&sWorker), (int)SyBlobLength(&sWorker));
  39687. SySetRelease(&sSearch);
  39688. SySetRelease(&sReplace);
  39689. SyBlobRelease(&sWorker);
  39690. return JX9_OK;
  39691. }
  39692. /*
  39693. * string strtr(string $str, string $from, string $to)
  39694. * string strtr(string $str, array $replace_pairs)
  39695. * Translate characters or replace substrings.
  39696. * Parameters
  39697. * $str
  39698. * The string being translated.
  39699. * $from
  39700. * The string being translated to to.
  39701. * $to
  39702. * The string replacing from.
  39703. * $replace_pairs
  39704. * The replace_pairs parameter may be used instead of to and
  39705. * from, in which case it's an array in the form array('from' => 'to', ...).
  39706. * Return
  39707. * The translated string.
  39708. * If replace_pairs contains a key which is an empty string (""), FALSE will be returned.
  39709. */
  39710. static int jx9Builtin_strtr(jx9_context *pCtx, int nArg, jx9_value **apArg)
  39711. {
  39712. const char *zIn;
  39713. int nLen;
  39714. if( nArg < 1 ){
  39715. /* Nothing to replace, return FALSE */
  39716. jx9_result_bool(pCtx, 0);
  39717. return JX9_OK;
  39718. }
  39719. zIn = jx9_value_to_string(apArg[0], &nLen);
  39720. if( nLen < 1 || nArg < 2 ){
  39721. /* Invalid arguments */
  39722. jx9_result_string(pCtx, zIn, nLen);
  39723. return JX9_OK;
  39724. }
  39725. if( nArg == 2 && jx9_value_is_json_array(apArg[1]) ){
  39726. str_replace_data sRepData;
  39727. SyBlob sWorker;
  39728. /* Initilaize the working buffer */
  39729. SyBlobInit(&sWorker, &pCtx->pVm->sAllocator);
  39730. /* Copy raw string */
  39731. SyBlobAppend(&sWorker, (const void *)zIn, (sxu32)nLen);
  39732. /* Init our replace data instance */
  39733. sRepData.pWorker = &sWorker;
  39734. sRepData.xMatch = SyBlobSearch;
  39735. /* Iterate throw array entries and perform the replace operation.*/
  39736. jx9_array_walk(apArg[1], StringReplaceWalker, &sRepData);
  39737. /* All done, return the result string */
  39738. jx9_result_string(pCtx, (const char *)SyBlobData(&sWorker),
  39739. (int)SyBlobLength(&sWorker)); /* Will make it's own copy */
  39740. /* Clean-up */
  39741. SyBlobRelease(&sWorker);
  39742. }else{
  39743. int i, flen, tlen, c, iOfft;
  39744. const char *zFrom, *zTo;
  39745. if( nArg < 3 ){
  39746. /* Nothing to replace */
  39747. jx9_result_string(pCtx, zIn, nLen);
  39748. return JX9_OK;
  39749. }
  39750. /* Extract given arguments */
  39751. zFrom = jx9_value_to_string(apArg[1], &flen);
  39752. zTo = jx9_value_to_string(apArg[2], &tlen);
  39753. if( flen < 1 || tlen < 1 ){
  39754. /* Nothing to replace */
  39755. jx9_result_string(pCtx, zIn, nLen);
  39756. return JX9_OK;
  39757. }
  39758. /* Start the replace process */
  39759. for( i = 0 ; i < nLen ; ++i ){
  39760. c = zIn[i];
  39761. if( CheckMask(c, zFrom, flen, &iOfft) ){
  39762. if ( iOfft < tlen ){
  39763. c = zTo[iOfft];
  39764. }
  39765. }
  39766. jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char));
  39767. }
  39768. }
  39769. return JX9_OK;
  39770. }
  39771. /*
  39772. * Parse an INI string.
  39773. * According to wikipedia
  39774. * The INI file format is an informal standard for configuration files for some platforms or software.
  39775. * INI files are simple text files with a basic structure composed of "sections" and "properties".
  39776. * Format
  39777. * Properties
  39778. * The basic element contained in an INI file is the property. Every property has a name and a value
  39779. * delimited by an equals sign (=). The name appears to the left of the equals sign.
  39780. * Example:
  39781. * name=value
  39782. * Sections
  39783. * Properties may be grouped into arbitrarily named sections. The section name appears on a line by itself
  39784. * in square brackets ([ and ]). All properties after the section declaration are associated with that section.
  39785. * There is no explicit "end of section" delimiter; sections end at the next section declaration
  39786. * or the end of the file. Sections may not be nested.
  39787. * Example:
  39788. * [section]
  39789. * Comments
  39790. * Semicolons (;) at the beginning of the line indicate a comment. Comment lines are ignored.
  39791. * This function return an array holding parsed values on success.FALSE otherwise.
  39792. */
  39793. JX9_PRIVATE sxi32 jx9ParseIniString(jx9_context *pCtx, const char *zIn, sxu32 nByte, int bProcessSection)
  39794. {
  39795. jx9_value *pCur, *pArray, *pSection, *pWorker, *pValue;
  39796. const char *zCur, *zEnd = &zIn[nByte];
  39797. SyHashEntry *pEntry;
  39798. SyString sEntry;
  39799. SyHash sHash;
  39800. int c;
  39801. /* Create an empty array and worker variables */
  39802. pArray = jx9_context_new_array(pCtx);
  39803. pWorker = jx9_context_new_scalar(pCtx);
  39804. pValue = jx9_context_new_scalar(pCtx);
  39805. if( pArray == 0 || pWorker == 0 || pValue == 0){
  39806. /* Out of memory */
  39807. jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
  39808. /* Return FALSE */
  39809. jx9_result_bool(pCtx, 0);
  39810. return JX9_OK;
  39811. }
  39812. SyHashInit(&sHash, &pCtx->pVm->sAllocator, 0, 0);
  39813. pCur = pArray;
  39814. /* Start the parse process */
  39815. for(;;){
  39816. /* Ignore leading white spaces */
  39817. while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0])){
  39818. zIn++;
  39819. }
  39820. if( zIn >= zEnd ){
  39821. /* No more input to process */
  39822. break;
  39823. }
  39824. if( zIn[0] == ';' || zIn[0] == '#' ){
  39825. /* Comment til the end of line */
  39826. zIn++;
  39827. while(zIn < zEnd && zIn[0] != '\n' ){
  39828. zIn++;
  39829. }
  39830. continue;
  39831. }
  39832. /* Reset the string cursor of the working variable */
  39833. jx9_value_reset_string_cursor(pWorker);
  39834. if( zIn[0] == '[' ){
  39835. /* Section: Extract the section name */
  39836. zIn++;
  39837. zCur = zIn;
  39838. while( zIn < zEnd && zIn[0] != ']' ){
  39839. zIn++;
  39840. }
  39841. if( zIn > zCur && bProcessSection ){
  39842. /* Save the section name */
  39843. SyStringInitFromBuf(&sEntry, zCur, (int)(zIn-zCur));
  39844. SyStringFullTrim(&sEntry);
  39845. jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte);
  39846. if( sEntry.nByte > 0 ){
  39847. /* Associate an array with the section */
  39848. pSection = jx9_context_new_array(pCtx);
  39849. if( pSection ){
  39850. jx9_array_add_elem(pArray, pWorker/*Section name*/, pSection);
  39851. pCur = pSection;
  39852. }
  39853. }
  39854. }
  39855. zIn++; /* Trailing square brackets ']' */
  39856. }else{
  39857. jx9_value *pOldCur;
  39858. int is_array;
  39859. int iLen;
  39860. /* Properties */
  39861. is_array = 0;
  39862. zCur = zIn;
  39863. iLen = 0; /* cc warning */
  39864. pOldCur = pCur;
  39865. while( zIn < zEnd && zIn[0] != '=' ){
  39866. if( zIn[0] == '[' && !is_array ){
  39867. /* Array */
  39868. iLen = (int)(zIn-zCur);
  39869. is_array = 1;
  39870. if( iLen > 0 ){
  39871. jx9_value *pvArr = 0; /* cc warning */
  39872. /* Query the hashtable */
  39873. SyStringInitFromBuf(&sEntry, zCur, iLen);
  39874. SyStringFullTrim(&sEntry);
  39875. pEntry = SyHashGet(&sHash, (const void *)sEntry.zString, sEntry.nByte);
  39876. if( pEntry ){
  39877. pvArr = (jx9_value *)SyHashEntryGetUserData(pEntry);
  39878. }else{
  39879. /* Create an empty array */
  39880. pvArr = jx9_context_new_array(pCtx);
  39881. if( pvArr ){
  39882. /* Save the entry */
  39883. SyHashInsert(&sHash, (const void *)sEntry.zString, sEntry.nByte, pvArr);
  39884. /* Insert the entry */
  39885. jx9_value_reset_string_cursor(pWorker);
  39886. jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte);
  39887. jx9_array_add_elem(pCur, pWorker, pvArr);
  39888. jx9_value_reset_string_cursor(pWorker);
  39889. }
  39890. }
  39891. if( pvArr ){
  39892. pCur = pvArr;
  39893. }
  39894. }
  39895. while ( zIn < zEnd && zIn[0] != ']' ){
  39896. zIn++;
  39897. }
  39898. }
  39899. zIn++;
  39900. }
  39901. if( !is_array ){
  39902. iLen = (int)(zIn-zCur);
  39903. }
  39904. /* Trim the key */
  39905. SyStringInitFromBuf(&sEntry, zCur, iLen);
  39906. SyStringFullTrim(&sEntry);
  39907. if( sEntry.nByte > 0 ){
  39908. if( !is_array ){
  39909. /* Save the key name */
  39910. jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte);
  39911. }
  39912. /* extract key value */
  39913. jx9_value_reset_string_cursor(pValue);
  39914. zIn++; /* '=' */
  39915. while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){
  39916. zIn++;
  39917. }
  39918. if( zIn < zEnd ){
  39919. zCur = zIn;
  39920. c = zIn[0];
  39921. if( c == '"' || c == '\'' ){
  39922. zIn++;
  39923. /* Delimit the value */
  39924. while( zIn < zEnd ){
  39925. if ( zIn[0] == c && zIn[-1] != '\\' ){
  39926. break;
  39927. }
  39928. zIn++;
  39929. }
  39930. if( zIn < zEnd ){
  39931. zIn++;
  39932. }
  39933. }else{
  39934. while( zIn < zEnd ){
  39935. if( zIn[0] == '\n' ){
  39936. if( zIn[-1] != '\\' ){
  39937. break;
  39938. }
  39939. }else if( zIn[0] == ';' || zIn[0] == '#' ){
  39940. /* Inline comments */
  39941. break;
  39942. }
  39943. zIn++;
  39944. }
  39945. }
  39946. /* Trim the value */
  39947. SyStringInitFromBuf(&sEntry, zCur, (int)(zIn-zCur));
  39948. SyStringFullTrim(&sEntry);
  39949. if( c == '"' || c == '\'' ){
  39950. SyStringTrimLeadingChar(&sEntry, c);
  39951. SyStringTrimTrailingChar(&sEntry, c);
  39952. }
  39953. if( sEntry.nByte > 0 ){
  39954. jx9_value_string(pValue, sEntry.zString, (int)sEntry.nByte);
  39955. }
  39956. /* Insert the key and it's value */
  39957. jx9_array_add_elem(pCur, is_array ? 0 /*Automatic index assign */: pWorker, pValue);
  39958. }
  39959. }else{
  39960. while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && ( SyisSpace(zIn[0]) || zIn[0] == '=' ) ){
  39961. zIn++;
  39962. }
  39963. }
  39964. pCur = pOldCur;
  39965. }
  39966. }
  39967. SyHashRelease(&sHash);
  39968. /* Return the parse of the INI string */
  39969. jx9_result_value(pCtx, pArray);
  39970. return SXRET_OK;
  39971. }
  39972. /*
  39973. * array parse_ini_string(string $ini[, bool $process_sections = false[, int $scanner_mode = INI_SCANNER_NORMAL ]])
  39974. * Parse a configuration string.
  39975. * Parameters
  39976. * $ini
  39977. * The contents of the ini file being parsed.
  39978. * $process_sections
  39979. * By setting the process_sections parameter to TRUE, you get a multidimensional array, with the section names
  39980. * and settings included. The default for process_sections is FALSE.
  39981. * $scanner_mode (Not used)
  39982. * Can either be INI_SCANNER_NORMAL (default) or INI_SCANNER_RAW. If INI_SCANNER_RAW is supplied
  39983. * then option values will not be parsed.
  39984. * Return
  39985. * The settings are returned as an associative array on success, and FALSE on failure.
  39986. */
  39987. static int jx9Builtin_parse_ini_string(jx9_context *pCtx, int nArg, jx9_value **apArg)
  39988. {
  39989. const char *zIni;
  39990. int nByte;
  39991. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  39992. /* Missing/Invalid arguments, return FALSE*/
  39993. jx9_result_bool(pCtx, 0);
  39994. return JX9_OK;
  39995. }
  39996. /* Extract the raw INI buffer */
  39997. zIni = jx9_value_to_string(apArg[0], &nByte);
  39998. /* Process the INI buffer*/
  39999. jx9ParseIniString(pCtx, zIni, (sxu32)nByte, (nArg > 1) ? jx9_value_to_bool(apArg[1]) : 0);
  40000. return JX9_OK;
  40001. }
  40002. /*
  40003. * Ctype Functions.
  40004. * Authors:
  40005. * Symisc Systems, devel@symisc.net.
  40006. * Copyright (C) Symisc Systems, http://jx9.symisc.net
  40007. * Status:
  40008. * Stable.
  40009. */
  40010. /*
  40011. * bool ctype_alnum(string $text)
  40012. * Checks if all of the characters in the provided string, text, are alphanumeric.
  40013. * Parameters
  40014. * $text
  40015. * The tested string.
  40016. * Return
  40017. * TRUE if every character in text is either a letter or a digit, FALSE otherwise.
  40018. */
  40019. static int jx9Builtin_ctype_alnum(jx9_context *pCtx, int nArg, jx9_value **apArg)
  40020. {
  40021. const unsigned char *zIn, *zEnd;
  40022. int nLen;
  40023. if( nArg < 1 ){
  40024. /* Missing arguments, return FALSE */
  40025. jx9_result_bool(pCtx, 0);
  40026. return JX9_OK;
  40027. }
  40028. /* Extract the target string */
  40029. zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
  40030. zEnd = &zIn[nLen];
  40031. if( nLen < 1 ){
  40032. /* Empty string, return FALSE */
  40033. jx9_result_bool(pCtx, 0);
  40034. return JX9_OK;
  40035. }
  40036. /* Perform the requested operation */
  40037. for(;;){
  40038. if( zIn >= zEnd ){
  40039. /* If we reach the end of the string, then the test succeeded. */
  40040. jx9_result_bool(pCtx, 1);
  40041. return JX9_OK;
  40042. }
  40043. if( !SyisAlphaNum(zIn[0]) ){
  40044. break;
  40045. }
  40046. /* Point to the next character */
  40047. zIn++;
  40048. }
  40049. /* The test failed, return FALSE */
  40050. jx9_result_bool(pCtx, 0);
  40051. return JX9_OK;
  40052. }
  40053. /*
  40054. * bool ctype_alpha(string $text)
  40055. * Checks if all of the characters in the provided string, text, are alphabetic.
  40056. * Parameters
  40057. * $text
  40058. * The tested string.
  40059. * Return
  40060. * TRUE if every character in text is a letter from the current locale, FALSE otherwise.
  40061. */
  40062. static int jx9Builtin_ctype_alpha(jx9_context *pCtx, int nArg, jx9_value **apArg)
  40063. {
  40064. const unsigned char *zIn, *zEnd;
  40065. int nLen;
  40066. if( nArg < 1 ){
  40067. /* Missing arguments, return FALSE */
  40068. jx9_result_bool(pCtx, 0);
  40069. return JX9_OK;
  40070. }
  40071. /* Extract the target string */
  40072. zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
  40073. zEnd = &zIn[nLen];
  40074. if( nLen < 1 ){
  40075. /* Empty string, return FALSE */
  40076. jx9_result_bool(pCtx, 0);
  40077. return JX9_OK;
  40078. }
  40079. /* Perform the requested operation */
  40080. for(;;){
  40081. if( zIn >= zEnd ){
  40082. /* If we reach the end of the string, then the test succeeded. */
  40083. jx9_result_bool(pCtx, 1);
  40084. return JX9_OK;
  40085. }
  40086. if( !SyisAlpha(zIn[0]) ){
  40087. break;
  40088. }
  40089. /* Point to the next character */
  40090. zIn++;
  40091. }
  40092. /* The test failed, return FALSE */
  40093. jx9_result_bool(pCtx, 0);
  40094. return JX9_OK;
  40095. }
  40096. /*
  40097. * bool ctype_cntrl(string $text)
  40098. * Checks if all of the characters in the provided string, text, are control characters.
  40099. * Parameters
  40100. * $text
  40101. * The tested string.
  40102. * Return
  40103. * TRUE if every character in text is a control characters, FALSE otherwise.
  40104. */
  40105. static int jx9Builtin_ctype_cntrl(jx9_context *pCtx, int nArg, jx9_value **apArg)
  40106. {
  40107. const unsigned char *zIn, *zEnd;
  40108. int nLen;
  40109. if( nArg < 1 ){
  40110. /* Missing arguments, return FALSE */
  40111. jx9_result_bool(pCtx, 0);
  40112. return JX9_OK;
  40113. }
  40114. /* Extract the target string */
  40115. zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
  40116. zEnd = &zIn[nLen];
  40117. if( nLen < 1 ){
  40118. /* Empty string, return FALSE */
  40119. jx9_result_bool(pCtx, 0);
  40120. return JX9_OK;
  40121. }
  40122. /* Perform the requested operation */
  40123. for(;;){
  40124. if( zIn >= zEnd ){
  40125. /* If we reach the end of the string, then the test succeeded. */
  40126. jx9_result_bool(pCtx, 1);
  40127. return JX9_OK;
  40128. }
  40129. if( zIn[0] >= 0xc0 ){
  40130. /* UTF-8 stream */
  40131. break;
  40132. }
  40133. if( !SyisCtrl(zIn[0]) ){
  40134. break;
  40135. }
  40136. /* Point to the next character */
  40137. zIn++;
  40138. }
  40139. /* The test failed, return FALSE */
  40140. jx9_result_bool(pCtx, 0);
  40141. return JX9_OK;
  40142. }
  40143. /*
  40144. * bool ctype_digit(string $text)
  40145. * Checks if all of the characters in the provided string, text, are numerical.
  40146. * Parameters
  40147. * $text
  40148. * The tested string.
  40149. * Return
  40150. * TRUE if every character in the string text is a decimal digit, FALSE otherwise.
  40151. */
  40152. static int jx9Builtin_ctype_digit(jx9_context *pCtx, int nArg, jx9_value **apArg)
  40153. {
  40154. const unsigned char *zIn, *zEnd;
  40155. int nLen;
  40156. if( nArg < 1 ){
  40157. /* Missing arguments, return FALSE */
  40158. jx9_result_bool(pCtx, 0);
  40159. return JX9_OK;
  40160. }
  40161. /* Extract the target string */
  40162. zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
  40163. zEnd = &zIn[nLen];
  40164. if( nLen < 1 ){
  40165. /* Empty string, return FALSE */
  40166. jx9_result_bool(pCtx, 0);
  40167. return JX9_OK;
  40168. }
  40169. /* Perform the requested operation */
  40170. for(;;){
  40171. if( zIn >= zEnd ){
  40172. /* If we reach the end of the string, then the test succeeded. */
  40173. jx9_result_bool(pCtx, 1);
  40174. return JX9_OK;
  40175. }
  40176. if( zIn[0] >= 0xc0 ){
  40177. /* UTF-8 stream */
  40178. break;
  40179. }
  40180. if( !SyisDigit(zIn[0]) ){
  40181. break;
  40182. }
  40183. /* Point to the next character */
  40184. zIn++;
  40185. }
  40186. /* The test failed, return FALSE */
  40187. jx9_result_bool(pCtx, 0);
  40188. return JX9_OK;
  40189. }
  40190. /*
  40191. * bool ctype_xdigit(string $text)
  40192. * Check for character(s) representing a hexadecimal digit.
  40193. * Parameters
  40194. * $text
  40195. * The tested string.
  40196. * Return
  40197. * Returns TRUE if every character in text is a hexadecimal 'digit', that is
  40198. * a decimal digit or a character from [A-Fa-f] , FALSE otherwise.
  40199. */
  40200. static int jx9Builtin_ctype_xdigit(jx9_context *pCtx, int nArg, jx9_value **apArg)
  40201. {
  40202. const unsigned char *zIn, *zEnd;
  40203. int nLen;
  40204. if( nArg < 1 ){
  40205. /* Missing arguments, return FALSE */
  40206. jx9_result_bool(pCtx, 0);
  40207. return JX9_OK;
  40208. }
  40209. /* Extract the target string */
  40210. zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
  40211. zEnd = &zIn[nLen];
  40212. if( nLen < 1 ){
  40213. /* Empty string, return FALSE */
  40214. jx9_result_bool(pCtx, 0);
  40215. return JX9_OK;
  40216. }
  40217. /* Perform the requested operation */
  40218. for(;;){
  40219. if( zIn >= zEnd ){
  40220. /* If we reach the end of the string, then the test succeeded. */
  40221. jx9_result_bool(pCtx, 1);
  40222. return JX9_OK;
  40223. }
  40224. if( zIn[0] >= 0xc0 ){
  40225. /* UTF-8 stream */
  40226. break;
  40227. }
  40228. if( !SyisHex(zIn[0]) ){
  40229. break;
  40230. }
  40231. /* Point to the next character */
  40232. zIn++;
  40233. }
  40234. /* The test failed, return FALSE */
  40235. jx9_result_bool(pCtx, 0);
  40236. return JX9_OK;
  40237. }
  40238. /*
  40239. * bool ctype_graph(string $text)
  40240. * Checks if all of the characters in the provided string, text, creates visible output.
  40241. * Parameters
  40242. * $text
  40243. * The tested string.
  40244. * Return
  40245. * Returns TRUE if every character in text is printable and actually creates visible output
  40246. * (no white space), FALSE otherwise.
  40247. */
  40248. static int jx9Builtin_ctype_graph(jx9_context *pCtx, int nArg, jx9_value **apArg)
  40249. {
  40250. const unsigned char *zIn, *zEnd;
  40251. int nLen;
  40252. if( nArg < 1 ){
  40253. /* Missing arguments, return FALSE */
  40254. jx9_result_bool(pCtx, 0);
  40255. return JX9_OK;
  40256. }
  40257. /* Extract the target string */
  40258. zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
  40259. zEnd = &zIn[nLen];
  40260. if( nLen < 1 ){
  40261. /* Empty string, return FALSE */
  40262. jx9_result_bool(pCtx, 0);
  40263. return JX9_OK;
  40264. }
  40265. /* Perform the requested operation */
  40266. for(;;){
  40267. if( zIn >= zEnd ){
  40268. /* If we reach the end of the string, then the test succeeded. */
  40269. jx9_result_bool(pCtx, 1);
  40270. return JX9_OK;
  40271. }
  40272. if( zIn[0] >= 0xc0 ){
  40273. /* UTF-8 stream */
  40274. break;
  40275. }
  40276. if( !SyisGraph(zIn[0]) ){
  40277. break;
  40278. }
  40279. /* Point to the next character */
  40280. zIn++;
  40281. }
  40282. /* The test failed, return FALSE */
  40283. jx9_result_bool(pCtx, 0);
  40284. return JX9_OK;
  40285. }
  40286. /*
  40287. * bool ctype_print(string $text)
  40288. * Checks if all of the characters in the provided string, text, are printable.
  40289. * Parameters
  40290. * $text
  40291. * The tested string.
  40292. * Return
  40293. * Returns TRUE if every character in text will actually create output (including blanks).
  40294. * Returns FALSE if text contains control characters or characters that do not have any output
  40295. * or control function at all.
  40296. */
  40297. static int jx9Builtin_ctype_print(jx9_context *pCtx, int nArg, jx9_value **apArg)
  40298. {
  40299. const unsigned char *zIn, *zEnd;
  40300. int nLen;
  40301. if( nArg < 1 ){
  40302. /* Missing arguments, return FALSE */
  40303. jx9_result_bool(pCtx, 0);
  40304. return JX9_OK;
  40305. }
  40306. /* Extract the target string */
  40307. zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
  40308. zEnd = &zIn[nLen];
  40309. if( nLen < 1 ){
  40310. /* Empty string, return FALSE */
  40311. jx9_result_bool(pCtx, 0);
  40312. return JX9_OK;
  40313. }
  40314. /* Perform the requested operation */
  40315. for(;;){
  40316. if( zIn >= zEnd ){
  40317. /* If we reach the end of the string, then the test succeeded. */
  40318. jx9_result_bool(pCtx, 1);
  40319. return JX9_OK;
  40320. }
  40321. if( zIn[0] >= 0xc0 ){
  40322. /* UTF-8 stream */
  40323. break;
  40324. }
  40325. if( !SyisPrint(zIn[0]) ){
  40326. break;
  40327. }
  40328. /* Point to the next character */
  40329. zIn++;
  40330. }
  40331. /* The test failed, return FALSE */
  40332. jx9_result_bool(pCtx, 0);
  40333. return JX9_OK;
  40334. }
  40335. /*
  40336. * bool ctype_punct(string $text)
  40337. * Checks if all of the characters in the provided string, text, are punctuation character.
  40338. * Parameters
  40339. * $text
  40340. * The tested string.
  40341. * Return
  40342. * Returns TRUE if every character in text is printable, but neither letter
  40343. * digit or blank, FALSE otherwise.
  40344. */
  40345. static int jx9Builtin_ctype_punct(jx9_context *pCtx, int nArg, jx9_value **apArg)
  40346. {
  40347. const unsigned char *zIn, *zEnd;
  40348. int nLen;
  40349. if( nArg < 1 ){
  40350. /* Missing arguments, return FALSE */
  40351. jx9_result_bool(pCtx, 0);
  40352. return JX9_OK;
  40353. }
  40354. /* Extract the target string */
  40355. zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
  40356. zEnd = &zIn[nLen];
  40357. if( nLen < 1 ){
  40358. /* Empty string, return FALSE */
  40359. jx9_result_bool(pCtx, 0);
  40360. return JX9_OK;
  40361. }
  40362. /* Perform the requested operation */
  40363. for(;;){
  40364. if( zIn >= zEnd ){
  40365. /* If we reach the end of the string, then the test succeeded. */
  40366. jx9_result_bool(pCtx, 1);
  40367. return JX9_OK;
  40368. }
  40369. if( zIn[0] >= 0xc0 ){
  40370. /* UTF-8 stream */
  40371. break;
  40372. }
  40373. if( !SyisPunct(zIn[0]) ){
  40374. break;
  40375. }
  40376. /* Point to the next character */
  40377. zIn++;
  40378. }
  40379. /* The test failed, return FALSE */
  40380. jx9_result_bool(pCtx, 0);
  40381. return JX9_OK;
  40382. }
  40383. /*
  40384. * bool ctype_space(string $text)
  40385. * Checks if all of the characters in the provided string, text, creates whitespace.
  40386. * Parameters
  40387. * $text
  40388. * The tested string.
  40389. * Return
  40390. * Returns TRUE if every character in text creates some sort of white space, FALSE otherwise.
  40391. * Besides the blank character this also includes tab, vertical tab, line feed, carriage return
  40392. * and form feed characters.
  40393. */
  40394. static int jx9Builtin_ctype_space(jx9_context *pCtx, int nArg, jx9_value **apArg)
  40395. {
  40396. const unsigned char *zIn, *zEnd;
  40397. int nLen;
  40398. if( nArg < 1 ){
  40399. /* Missing arguments, return FALSE */
  40400. jx9_result_bool(pCtx, 0);
  40401. return JX9_OK;
  40402. }
  40403. /* Extract the target string */
  40404. zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
  40405. zEnd = &zIn[nLen];
  40406. if( nLen < 1 ){
  40407. /* Empty string, return FALSE */
  40408. jx9_result_bool(pCtx, 0);
  40409. return JX9_OK;
  40410. }
  40411. /* Perform the requested operation */
  40412. for(;;){
  40413. if( zIn >= zEnd ){
  40414. /* If we reach the end of the string, then the test succeeded. */
  40415. jx9_result_bool(pCtx, 1);
  40416. return JX9_OK;
  40417. }
  40418. if( zIn[0] >= 0xc0 ){
  40419. /* UTF-8 stream */
  40420. break;
  40421. }
  40422. if( !SyisSpace(zIn[0]) ){
  40423. break;
  40424. }
  40425. /* Point to the next character */
  40426. zIn++;
  40427. }
  40428. /* The test failed, return FALSE */
  40429. jx9_result_bool(pCtx, 0);
  40430. return JX9_OK;
  40431. }
  40432. /*
  40433. * bool ctype_lower(string $text)
  40434. * Checks if all of the characters in the provided string, text, are lowercase letters.
  40435. * Parameters
  40436. * $text
  40437. * The tested string.
  40438. * Return
  40439. * Returns TRUE if every character in text is a lowercase letter in the current locale.
  40440. */
  40441. static int jx9Builtin_ctype_lower(jx9_context *pCtx, int nArg, jx9_value **apArg)
  40442. {
  40443. const unsigned char *zIn, *zEnd;
  40444. int nLen;
  40445. if( nArg < 1 ){
  40446. /* Missing arguments, return FALSE */
  40447. jx9_result_bool(pCtx, 0);
  40448. return JX9_OK;
  40449. }
  40450. /* Extract the target string */
  40451. zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
  40452. zEnd = &zIn[nLen];
  40453. if( nLen < 1 ){
  40454. /* Empty string, return FALSE */
  40455. jx9_result_bool(pCtx, 0);
  40456. return JX9_OK;
  40457. }
  40458. /* Perform the requested operation */
  40459. for(;;){
  40460. if( zIn >= zEnd ){
  40461. /* If we reach the end of the string, then the test succeeded. */
  40462. jx9_result_bool(pCtx, 1);
  40463. return JX9_OK;
  40464. }
  40465. if( !SyisLower(zIn[0]) ){
  40466. break;
  40467. }
  40468. /* Point to the next character */
  40469. zIn++;
  40470. }
  40471. /* The test failed, return FALSE */
  40472. jx9_result_bool(pCtx, 0);
  40473. return JX9_OK;
  40474. }
  40475. /*
  40476. * bool ctype_upper(string $text)
  40477. * Checks if all of the characters in the provided string, text, are uppercase letters.
  40478. * Parameters
  40479. * $text
  40480. * The tested string.
  40481. * Return
  40482. * Returns TRUE if every character in text is a uppercase letter in the current locale.
  40483. */
  40484. static int jx9Builtin_ctype_upper(jx9_context *pCtx, int nArg, jx9_value **apArg)
  40485. {
  40486. const unsigned char *zIn, *zEnd;
  40487. int nLen;
  40488. if( nArg < 1 ){
  40489. /* Missing arguments, return FALSE */
  40490. jx9_result_bool(pCtx, 0);
  40491. return JX9_OK;
  40492. }
  40493. /* Extract the target string */
  40494. zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen);
  40495. zEnd = &zIn[nLen];
  40496. if( nLen < 1 ){
  40497. /* Empty string, return FALSE */
  40498. jx9_result_bool(pCtx, 0);
  40499. return JX9_OK;
  40500. }
  40501. /* Perform the requested operation */
  40502. for(;;){
  40503. if( zIn >= zEnd ){
  40504. /* If we reach the end of the string, then the test succeeded. */
  40505. jx9_result_bool(pCtx, 1);
  40506. return JX9_OK;
  40507. }
  40508. if( !SyisUpper(zIn[0]) ){
  40509. break;
  40510. }
  40511. /* Point to the next character */
  40512. zIn++;
  40513. }
  40514. /* The test failed, return FALSE */
  40515. jx9_result_bool(pCtx, 0);
  40516. return JX9_OK;
  40517. }
  40518. /*
  40519. * Date/Time functions
  40520. * Authors:
  40521. * Symisc Systems, devel@symisc.net.
  40522. * Copyright (C) Symisc Systems, http://jx9.symisc.net
  40523. * Status:
  40524. * Devel.
  40525. */
  40526. #include <time.h>
  40527. #ifdef __WINNT__
  40528. /* GetSystemTime() */
  40529. #include <Windows.h>
  40530. #ifdef _WIN32_WCE
  40531. /*
  40532. ** WindowsCE does not have a localtime() function. So create a
  40533. ** substitute.
  40534. ** Taken from the SQLite3 source tree.
  40535. ** Status: Public domain
  40536. */
  40537. struct tm *__cdecl localtime(const time_t *t)
  40538. {
  40539. static struct tm y;
  40540. FILETIME uTm, lTm;
  40541. SYSTEMTIME pTm;
  40542. jx9_int64 t64;
  40543. t64 = *t;
  40544. t64 = (t64 + 11644473600)*10000000;
  40545. uTm.dwLowDateTime = (DWORD)(t64 & 0xFFFFFFFF);
  40546. uTm.dwHighDateTime= (DWORD)(t64 >> 32);
  40547. FileTimeToLocalFileTime(&uTm, &lTm);
  40548. FileTimeToSystemTime(&lTm, &pTm);
  40549. y.tm_year = pTm.wYear - 1900;
  40550. y.tm_mon = pTm.wMonth - 1;
  40551. y.tm_wday = pTm.wDayOfWeek;
  40552. y.tm_mday = pTm.wDay;
  40553. y.tm_hour = pTm.wHour;
  40554. y.tm_min = pTm.wMinute;
  40555. y.tm_sec = pTm.wSecond;
  40556. return &y;
  40557. }
  40558. #endif /*_WIN32_WCE */
  40559. #elif defined(__UNIXES__)
  40560. #include <sys/time.h>
  40561. #endif /* __WINNT__*/
  40562. /*
  40563. * int64 time(void)
  40564. * Current Unix timestamp
  40565. * Parameters
  40566. * None.
  40567. * Return
  40568. * Returns the current time measured in the number of seconds
  40569. * since the Unix Epoch (January 1 1970 00:00:00 GMT).
  40570. */
  40571. static int jx9Builtin_time(jx9_context *pCtx, int nArg, jx9_value **apArg)
  40572. {
  40573. time_t tt;
  40574. SXUNUSED(nArg); /* cc warning */
  40575. SXUNUSED(apArg);
  40576. /* Extract the current time */
  40577. time(&tt);
  40578. /* Return as 64-bit integer */
  40579. jx9_result_int64(pCtx, (jx9_int64)tt);
  40580. return JX9_OK;
  40581. }
  40582. /*
  40583. * string/float microtime([ bool $get_as_float = false ])
  40584. * microtime() returns the current Unix timestamp with microseconds.
  40585. * Parameters
  40586. * $get_as_float
  40587. * If used and set to TRUE, microtime() will return a float instead of a string
  40588. * as described in the return values section below.
  40589. * Return
  40590. * By default, microtime() returns a string in the form "msec sec", where sec
  40591. * is the current time measured in the number of seconds since the Unix
  40592. * epoch (0:00:00 January 1, 1970 GMT), and msec is the number of microseconds
  40593. * that have elapsed since sec expressed in seconds.
  40594. * If get_as_float is set to TRUE, then microtime() returns a float, which represents
  40595. * the current time in seconds since the Unix epoch accurate to the nearest microsecond.
  40596. */
  40597. static int jx9Builtin_microtime(jx9_context *pCtx, int nArg, jx9_value **apArg)
  40598. {
  40599. int bFloat = 0;
  40600. sytime sTime;
  40601. #if defined(__UNIXES__)
  40602. struct timeval tv;
  40603. gettimeofday(&tv, 0);
  40604. sTime.tm_sec = (long)tv.tv_sec;
  40605. sTime.tm_usec = (long)tv.tv_usec;
  40606. #else
  40607. time_t tt;
  40608. time(&tt);
  40609. sTime.tm_sec = (long)tt;
  40610. sTime.tm_usec = (long)(tt%SX_USEC_PER_SEC);
  40611. #endif /* __UNIXES__ */
  40612. if( nArg > 0 ){
  40613. bFloat = jx9_value_to_bool(apArg[0]);
  40614. }
  40615. if( bFloat ){
  40616. /* Return as float */
  40617. jx9_result_double(pCtx, (double)sTime.tm_sec);
  40618. }else{
  40619. /* Return as string */
  40620. jx9_result_string_format(pCtx, "%ld %ld", sTime.tm_usec, sTime.tm_sec);
  40621. }
  40622. return JX9_OK;
  40623. }
  40624. /*
  40625. * array getdate ([ int $timestamp = time() ])
  40626. * Get date/time information.
  40627. * Parameter
  40628. * $timestamp: The optional timestamp parameter is an integer Unix timestamp
  40629. * that defaults to the current local time if a timestamp is not given.
  40630. * In other words, it defaults to the value of time().
  40631. * Returns
  40632. * Returns an associative array of information related to the timestamp.
  40633. * Elements from the returned associative array are as follows:
  40634. * KEY VALUE
  40635. * --------- -------
  40636. * "seconds" Numeric representation of seconds 0 to 59
  40637. * "minutes" Numeric representation of minutes 0 to 59
  40638. * "hours" Numeric representation of hours 0 to 23
  40639. * "mday" Numeric representation of the day of the month 1 to 31
  40640. * "wday" Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday)
  40641. * "mon" Numeric representation of a month 1 through 12
  40642. * "year" A full numeric representation of a year, 4 digits Examples: 1999 or 2003
  40643. * "yday" Numeric representation of the day of the year 0 through 365
  40644. * "weekday" A full textual representation of the day of the week Sunday through Saturday
  40645. * "month" A full textual representation of a month, such as January or March January through December
  40646. * 0 Seconds since the Unix Epoch, similar to the values returned by time() and used by date().
  40647. * NOTE:
  40648. * NULL is returned on failure.
  40649. */
  40650. static int jx9Builtin_getdate(jx9_context *pCtx, int nArg, jx9_value **apArg)
  40651. {
  40652. jx9_value *pValue, *pArray;
  40653. Sytm sTm;
  40654. if( nArg < 1 ){
  40655. #ifdef __WINNT__
  40656. SYSTEMTIME sOS;
  40657. GetSystemTime(&sOS);
  40658. SYSTEMTIME_TO_SYTM(&sOS, &sTm);
  40659. #else
  40660. struct tm *pTm;
  40661. time_t t;
  40662. time(&t);
  40663. pTm = localtime(&t);
  40664. STRUCT_TM_TO_SYTM(pTm, &sTm);
  40665. #endif
  40666. }else{
  40667. /* Use the given timestamp */
  40668. time_t t;
  40669. struct tm *pTm;
  40670. #ifdef __WINNT__
  40671. #ifdef _MSC_VER
  40672. #if _MSC_VER >= 1400 /* Visual Studio 2005 and up */
  40673. #pragma warning(disable:4996) /* _CRT_SECURE...*/
  40674. #endif
  40675. #endif
  40676. #endif
  40677. if( jx9_value_is_int(apArg[0]) ){
  40678. t = (time_t)jx9_value_to_int64(apArg[0]);
  40679. pTm = localtime(&t);
  40680. if( pTm == 0 ){
  40681. time(&t);
  40682. }
  40683. }else{
  40684. time(&t);
  40685. }
  40686. pTm = localtime(&t);
  40687. STRUCT_TM_TO_SYTM(pTm, &sTm);
  40688. }
  40689. /* Element value */
  40690. pValue = jx9_context_new_scalar(pCtx);
  40691. if( pValue == 0 ){
  40692. /* Return NULL */
  40693. jx9_result_null(pCtx);
  40694. return JX9_OK;
  40695. }
  40696. /* Create a new array */
  40697. pArray = jx9_context_new_array(pCtx);
  40698. if( pArray == 0 ){
  40699. /* Return NULL */
  40700. jx9_result_null(pCtx);
  40701. return JX9_OK;
  40702. }
  40703. /* Fill the array */
  40704. /* Seconds */
  40705. jx9_value_int(pValue, sTm.tm_sec);
  40706. jx9_array_add_strkey_elem(pArray, "seconds", pValue);
  40707. /* Minutes */
  40708. jx9_value_int(pValue, sTm.tm_min);
  40709. jx9_array_add_strkey_elem(pArray, "minutes", pValue);
  40710. /* Hours */
  40711. jx9_value_int(pValue, sTm.tm_hour);
  40712. jx9_array_add_strkey_elem(pArray, "hours", pValue);
  40713. /* mday */
  40714. jx9_value_int(pValue, sTm.tm_mday);
  40715. jx9_array_add_strkey_elem(pArray, "mday", pValue);
  40716. /* wday */
  40717. jx9_value_int(pValue, sTm.tm_wday);
  40718. jx9_array_add_strkey_elem(pArray, "wday", pValue);
  40719. /* mon */
  40720. jx9_value_int(pValue, sTm.tm_mon+1);
  40721. jx9_array_add_strkey_elem(pArray, "mon", pValue);
  40722. /* year */
  40723. jx9_value_int(pValue, sTm.tm_year);
  40724. jx9_array_add_strkey_elem(pArray, "year", pValue);
  40725. /* yday */
  40726. jx9_value_int(pValue, sTm.tm_yday);
  40727. jx9_array_add_strkey_elem(pArray, "yday", pValue);
  40728. /* Weekday */
  40729. jx9_value_string(pValue, SyTimeGetDay(sTm.tm_wday), -1);
  40730. jx9_array_add_strkey_elem(pArray, "weekday", pValue);
  40731. /* Month */
  40732. jx9_value_reset_string_cursor(pValue);
  40733. jx9_value_string(pValue, SyTimeGetMonth(sTm.tm_mon), -1);
  40734. jx9_array_add_strkey_elem(pArray, "month", pValue);
  40735. /* Seconds since the epoch */
  40736. jx9_value_int64(pValue, (jx9_int64)time(0));
  40737. jx9_array_add_elem(pArray, 0 /* Index zero */, pValue);
  40738. /* Return the freshly created array */
  40739. jx9_result_value(pCtx, pArray);
  40740. return JX9_OK;
  40741. }
  40742. /*
  40743. * mixed gettimeofday([ bool $return_float = false ] )
  40744. * Returns an associative array containing the data returned from the system call.
  40745. * Parameters
  40746. * $return_float
  40747. * When set to TRUE, a float instead of an array is returned.
  40748. * Return
  40749. * By default an array is returned. If return_float is set, then
  40750. * a float is returned.
  40751. */
  40752. static int jx9Builtin_gettimeofday(jx9_context *pCtx, int nArg, jx9_value **apArg)
  40753. {
  40754. int bFloat = 0;
  40755. sytime sTime;
  40756. #if defined(__UNIXES__)
  40757. struct timeval tv;
  40758. gettimeofday(&tv, 0);
  40759. sTime.tm_sec = (long)tv.tv_sec;
  40760. sTime.tm_usec = (long)tv.tv_usec;
  40761. #else
  40762. time_t tt;
  40763. time(&tt);
  40764. sTime.tm_sec = (long)tt;
  40765. sTime.tm_usec = (long)(tt%SX_USEC_PER_SEC);
  40766. #endif /* __UNIXES__ */
  40767. if( nArg > 0 ){
  40768. bFloat = jx9_value_to_bool(apArg[0]);
  40769. }
  40770. if( bFloat ){
  40771. /* Return as float */
  40772. jx9_result_double(pCtx, (double)sTime.tm_sec);
  40773. }else{
  40774. /* Return an associative array */
  40775. jx9_value *pValue, *pArray;
  40776. /* Create a new array */
  40777. pArray = jx9_context_new_array(pCtx);
  40778. /* Element value */
  40779. pValue = jx9_context_new_scalar(pCtx);
  40780. if( pValue == 0 || pArray == 0 ){
  40781. /* Return NULL */
  40782. jx9_result_null(pCtx);
  40783. return JX9_OK;
  40784. }
  40785. /* Fill the array */
  40786. /* sec */
  40787. jx9_value_int64(pValue, sTime.tm_sec);
  40788. jx9_array_add_strkey_elem(pArray, "sec", pValue);
  40789. /* usec */
  40790. jx9_value_int64(pValue, sTime.tm_usec);
  40791. jx9_array_add_strkey_elem(pArray, "usec", pValue);
  40792. /* Return the array */
  40793. jx9_result_value(pCtx, pArray);
  40794. }
  40795. return JX9_OK;
  40796. }
  40797. /* Check if the given year is leap or not */
  40798. #define IS_LEAP_YEAR(YEAR) (YEAR % 400 ? ( YEAR % 100 ? ( YEAR % 4 ? 0 : 1 ) : 0 ) : 1)
  40799. /* ISO-8601 numeric representation of the day of the week */
  40800. static const int aISO8601[] = { 7 /* Sunday */, 1 /* Monday */, 2, 3, 4, 5, 6 };
  40801. /*
  40802. * Format a given date string.
  40803. * Supported format: (Taken from JX9 online docs)
  40804. * character Description
  40805. * d Day of the month
  40806. * D A textual representation of a days
  40807. * j Day of the month without leading zeros
  40808. * l A full textual representation of the day of the week
  40809. * N ISO-8601 numeric representation of the day of the week
  40810. * w Numeric representation of the day of the week
  40811. * z The day of the year (starting from 0)
  40812. * F A full textual representation of a month, such as January or March
  40813. * m Numeric representation of a month, with leading zeros 01 through 12
  40814. * M A short textual representation of a month, three letters Jan through Dec
  40815. * n Numeric representation of a month, without leading zeros 1 through 12
  40816. * t Number of days in the given month 28 through 31
  40817. * L Whether it's a leap year 1 if it is a leap year, 0 otherwise.
  40818. * o ISO-8601 year number. This has the same value as Y, except that if the ISO week number
  40819. * (W) belongs to the previous or next year, that year is used instead. (added in JX9 5.1.0) Examples: 1999 or 2003
  40820. * Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003
  40821. * y A two digit representation of a year Examples: 99 or 03
  40822. * a Lowercase Ante meridiem and Post meridiem am or pm
  40823. * A Uppercase Ante meridiem and Post meridiem AM or PM
  40824. * g 12-hour format of an hour without leading zeros 1 through 12
  40825. * G 24-hour format of an hour without leading zeros 0 through 23
  40826. * h 12-hour format of an hour with leading zeros 01 through 12
  40827. * H 24-hour format of an hour with leading zeros 00 through 23
  40828. * i Minutes with leading zeros 00 to 59
  40829. * s Seconds, with leading zeros 00 through 59
  40830. * u Microseconds Example: 654321
  40831. * e Timezone identifier Examples: UTC, GMT, Atlantic/Azores
  40832. * I (capital i) Whether or not the date is in daylight saving time 1 if Daylight Saving Time, 0 otherwise.
  40833. * r RFC 2822 formatted date Example: Thu, 21 Dec 2000 16:01:07 +0200
  40834. * U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
  40835. * S English ordinal suffix for the day of the month, 2 characters
  40836. * O Difference to Greenwich time (GMT) in hours
  40837. * Z Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those
  40838. * east of UTC is always positive.
  40839. * c ISO 8601 date
  40840. */
  40841. static sxi32 DateFormat(jx9_context *pCtx, const char *zIn, int nLen, Sytm *pTm)
  40842. {
  40843. const char *zEnd = &zIn[nLen];
  40844. const char *zCur;
  40845. /* Start the format process */
  40846. for(;;){
  40847. if( zIn >= zEnd ){
  40848. /* No more input to process */
  40849. break;
  40850. }
  40851. switch(zIn[0]){
  40852. case 'd':
  40853. /* Day of the month, 2 digits with leading zeros */
  40854. jx9_result_string_format(pCtx, "%02d", pTm->tm_mday);
  40855. break;
  40856. case 'D':
  40857. /*A textual representation of a day, three letters*/
  40858. zCur = SyTimeGetDay(pTm->tm_wday);
  40859. jx9_result_string(pCtx, zCur, 3);
  40860. break;
  40861. case 'j':
  40862. /* Day of the month without leading zeros */
  40863. jx9_result_string_format(pCtx, "%d", pTm->tm_mday);
  40864. break;
  40865. case 'l':
  40866. /* A full textual representation of the day of the week */
  40867. zCur = SyTimeGetDay(pTm->tm_wday);
  40868. jx9_result_string(pCtx, zCur, -1/*Compute length automatically*/);
  40869. break;
  40870. case 'N':{
  40871. /* ISO-8601 numeric representation of the day of the week */
  40872. jx9_result_string_format(pCtx, "%d", aISO8601[pTm->tm_wday % 7 ]);
  40873. break;
  40874. }
  40875. case 'w':
  40876. /*Numeric representation of the day of the week*/
  40877. jx9_result_string_format(pCtx, "%d", pTm->tm_wday);
  40878. break;
  40879. case 'z':
  40880. /*The day of the year*/
  40881. jx9_result_string_format(pCtx, "%d", pTm->tm_yday);
  40882. break;
  40883. case 'F':
  40884. /*A full textual representation of a month, such as January or March*/
  40885. zCur = SyTimeGetMonth(pTm->tm_mon);
  40886. jx9_result_string(pCtx, zCur, -1/*Compute length automatically*/);
  40887. break;
  40888. case 'm':
  40889. /*Numeric representation of a month, with leading zeros*/
  40890. jx9_result_string_format(pCtx, "%02d", pTm->tm_mon + 1);
  40891. break;
  40892. case 'M':
  40893. /*A short textual representation of a month, three letters*/
  40894. zCur = SyTimeGetMonth(pTm->tm_mon);
  40895. jx9_result_string(pCtx, zCur, 3);
  40896. break;
  40897. case 'n':
  40898. /*Numeric representation of a month, without leading zeros*/
  40899. jx9_result_string_format(pCtx, "%d", pTm->tm_mon + 1);
  40900. break;
  40901. case 't':{
  40902. static const int aMonDays[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  40903. int nDays = aMonDays[pTm->tm_mon % 12 ];
  40904. if( pTm->tm_mon == 1 /* 'February' */ && !IS_LEAP_YEAR(pTm->tm_year) ){
  40905. nDays = 28;
  40906. }
  40907. /*Number of days in the given month*/
  40908. jx9_result_string_format(pCtx, "%d", nDays);
  40909. break;
  40910. }
  40911. case 'L':{
  40912. int isLeap = IS_LEAP_YEAR(pTm->tm_year);
  40913. /* Whether it's a leap year */
  40914. jx9_result_string_format(pCtx, "%d", isLeap);
  40915. break;
  40916. }
  40917. case 'o':
  40918. /* ISO-8601 year number.*/
  40919. jx9_result_string_format(pCtx, "%4d", pTm->tm_year);
  40920. break;
  40921. case 'Y':
  40922. /* A full numeric representation of a year, 4 digits */
  40923. jx9_result_string_format(pCtx, "%4d", pTm->tm_year);
  40924. break;
  40925. case 'y':
  40926. /*A two digit representation of a year*/
  40927. jx9_result_string_format(pCtx, "%02d", pTm->tm_year%100);
  40928. break;
  40929. case 'a':
  40930. /* Lowercase Ante meridiem and Post meridiem */
  40931. jx9_result_string(pCtx, pTm->tm_hour > 12 ? "pm" : "am", 2);
  40932. break;
  40933. case 'A':
  40934. /* Uppercase Ante meridiem and Post meridiem */
  40935. jx9_result_string(pCtx, pTm->tm_hour > 12 ? "PM" : "AM", 2);
  40936. break;
  40937. case 'g':
  40938. /* 12-hour format of an hour without leading zeros*/
  40939. jx9_result_string_format(pCtx, "%d", 1+(pTm->tm_hour%12));
  40940. break;
  40941. case 'G':
  40942. /* 24-hour format of an hour without leading zeros */
  40943. jx9_result_string_format(pCtx, "%d", pTm->tm_hour);
  40944. break;
  40945. case 'h':
  40946. /* 12-hour format of an hour with leading zeros */
  40947. jx9_result_string_format(pCtx, "%02d", 1+(pTm->tm_hour%12));
  40948. break;
  40949. case 'H':
  40950. /* 24-hour format of an hour with leading zeros */
  40951. jx9_result_string_format(pCtx, "%02d", pTm->tm_hour);
  40952. break;
  40953. case 'i':
  40954. /* Minutes with leading zeros */
  40955. jx9_result_string_format(pCtx, "%02d", pTm->tm_min);
  40956. break;
  40957. case 's':
  40958. /* second with leading zeros */
  40959. jx9_result_string_format(pCtx, "%02d", pTm->tm_sec);
  40960. break;
  40961. case 'u':
  40962. /* Microseconds */
  40963. jx9_result_string_format(pCtx, "%u", pTm->tm_sec * SX_USEC_PER_SEC);
  40964. break;
  40965. case 'S':{
  40966. /* English ordinal suffix for the day of the month, 2 characters */
  40967. static const char zSuffix[] = "thstndrdthththththth";
  40968. int v = pTm->tm_mday;
  40969. jx9_result_string(pCtx, &zSuffix[2 * (int)(v / 10 % 10 != 1 ? v % 10 : 0)], (int)sizeof(char) * 2);
  40970. break;
  40971. }
  40972. case 'e':
  40973. /* Timezone identifier */
  40974. zCur = pTm->tm_zone;
  40975. if( zCur == 0 ){
  40976. /* Assume GMT */
  40977. zCur = "GMT";
  40978. }
  40979. jx9_result_string(pCtx, zCur, -1);
  40980. break;
  40981. case 'I':
  40982. /* Whether or not the date is in daylight saving time */
  40983. #ifdef __WINNT__
  40984. #ifdef _MSC_VER
  40985. #ifndef _WIN32_WCE
  40986. _get_daylight(&pTm->tm_isdst);
  40987. #endif
  40988. #endif
  40989. #endif
  40990. jx9_result_string_format(pCtx, "%d", pTm->tm_isdst == 1);
  40991. break;
  40992. case 'r':
  40993. /* RFC 2822 formatted date Example: Thu, 21 Dec 2000 16:01:07 */
  40994. jx9_result_string_format(pCtx, "%.3s, %02d %.3s %4d %02d:%02d:%02d",
  40995. SyTimeGetDay(pTm->tm_wday),
  40996. pTm->tm_mday,
  40997. SyTimeGetMonth(pTm->tm_mon),
  40998. pTm->tm_year,
  40999. pTm->tm_hour,
  41000. pTm->tm_min,
  41001. pTm->tm_sec
  41002. );
  41003. break;
  41004. case 'U':{
  41005. time_t tt;
  41006. /* Seconds since the Unix Epoch */
  41007. time(&tt);
  41008. jx9_result_string_format(pCtx, "%u", (unsigned int)tt);
  41009. break;
  41010. }
  41011. case 'O':
  41012. case 'P':
  41013. /* Difference to Greenwich time (GMT) in hours */
  41014. jx9_result_string_format(pCtx, "%+05d", pTm->tm_gmtoff);
  41015. break;
  41016. case 'Z':
  41017. /* Timezone offset in seconds. The offset for timezones west of UTC
  41018. * is always negative, and for those east of UTC is always positive.
  41019. */
  41020. jx9_result_string_format(pCtx, "%+05d", pTm->tm_gmtoff);
  41021. break;
  41022. case 'c':
  41023. /* ISO 8601 date */
  41024. jx9_result_string_format(pCtx, "%4d-%02d-%02dT%02d:%02d:%02d%+05d",
  41025. pTm->tm_year,
  41026. pTm->tm_mon+1,
  41027. pTm->tm_mday,
  41028. pTm->tm_hour,
  41029. pTm->tm_min,
  41030. pTm->tm_sec,
  41031. pTm->tm_gmtoff
  41032. );
  41033. break;
  41034. case '\\':
  41035. zIn++;
  41036. /* Expand verbatim */
  41037. if( zIn < zEnd ){
  41038. jx9_result_string(pCtx, zIn, (int)sizeof(char));
  41039. }
  41040. break;
  41041. default:
  41042. /* Unknown format specifer, expand verbatim */
  41043. jx9_result_string(pCtx, zIn, (int)sizeof(char));
  41044. break;
  41045. }
  41046. /* Point to the next character */
  41047. zIn++;
  41048. }
  41049. return SXRET_OK;
  41050. }
  41051. /*
  41052. * JX9 implementation of the strftime() function.
  41053. * The following formats are supported:
  41054. * %a An abbreviated textual representation of the day
  41055. * %A A full textual representation of the day
  41056. * %d Two-digit day of the month (with leading zeros)
  41057. * %e Day of the month, with a space preceding single digits.
  41058. * %j Day of the year, 3 digits with leading zeros
  41059. * %u ISO-8601 numeric representation of the day of the week 1 (for Monday) though 7 (for Sunday)
  41060. * %w Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday)
  41061. * %U Week number of the given year, starting with the first Sunday as the first week
  41062. * %V ISO-8601:1988 week number of the given year, starting with the first week of the year with at least
  41063. * 4 weekdays, with Monday being the start of the week.
  41064. * %W A numeric representation of the week of the year
  41065. * %b Abbreviated month name, based on the locale
  41066. * %B Full month name, based on the locale
  41067. * %h Abbreviated month name, based on the locale (an alias of %b)
  41068. * %m Two digit representation of the month
  41069. * %C Two digit representation of the century (year divided by 100, truncated to an integer)
  41070. * %g Two digit representation of the year going by ISO-8601:1988 standards (see %V)
  41071. * %G The full four-digit version of %g
  41072. * %y Two digit representation of the year
  41073. * %Y Four digit representation for the year
  41074. * %H Two digit representation of the hour in 24-hour format
  41075. * %I Two digit representation of the hour in 12-hour format
  41076. * %l (lower-case 'L') Hour in 12-hour format, with a space preceeding single digits
  41077. * %M Two digit representation of the minute
  41078. * %p UPPER-CASE 'AM' or 'PM' based on the given time
  41079. * %P lower-case 'am' or 'pm' based on the given time
  41080. * %r Same as "%I:%M:%S %p"
  41081. * %R Same as "%H:%M"
  41082. * %S Two digit representation of the second
  41083. * %T Same as "%H:%M:%S"
  41084. * %X Preferred time representation based on locale, without the date
  41085. * %z Either the time zone offset from UTC or the abbreviation
  41086. * %Z The time zone offset/abbreviation option NOT given by %z
  41087. * %c Preferred date and time stamp based on local
  41088. * %D Same as "%m/%d/%y"
  41089. * %F Same as "%Y-%m-%d"
  41090. * %s Unix Epoch Time timestamp (same as the time() function)
  41091. * %x Preferred date representation based on locale, without the time
  41092. * %n A newline character ("\n")
  41093. * %t A Tab character ("\t")
  41094. * %% A literal percentage character ("%")
  41095. */
  41096. static int jx9Strftime(
  41097. jx9_context *pCtx, /* Call context */
  41098. const char *zIn, /* Input string */
  41099. int nLen, /* Input length */
  41100. Sytm *pTm /* Parse of the given time */
  41101. )
  41102. {
  41103. const char *zCur, *zEnd = &zIn[nLen];
  41104. int c;
  41105. /* Start the format process */
  41106. for(;;){
  41107. zCur = zIn;
  41108. while(zIn < zEnd && zIn[0] != '%' ){
  41109. zIn++;
  41110. }
  41111. if( zIn > zCur ){
  41112. /* Consume input verbatim */
  41113. jx9_result_string(pCtx, zCur, (int)(zIn-zCur));
  41114. }
  41115. zIn++; /* Jump the percent sign */
  41116. if( zIn >= zEnd ){
  41117. /* No more input to process */
  41118. break;
  41119. }
  41120. c = zIn[0];
  41121. /* Act according to the current specifer */
  41122. switch(c){
  41123. case '%':
  41124. /* A literal percentage character ("%") */
  41125. jx9_result_string(pCtx, "%", (int)sizeof(char));
  41126. break;
  41127. case 't':
  41128. /* A Tab character */
  41129. jx9_result_string(pCtx, "\t", (int)sizeof(char));
  41130. break;
  41131. case 'n':
  41132. /* A newline character */
  41133. jx9_result_string(pCtx, "\n", (int)sizeof(char));
  41134. break;
  41135. case 'a':
  41136. /* An abbreviated textual representation of the day */
  41137. jx9_result_string(pCtx, SyTimeGetDay(pTm->tm_wday), (int)sizeof(char)*3);
  41138. break;
  41139. case 'A':
  41140. /* A full textual representation of the day */
  41141. jx9_result_string(pCtx, SyTimeGetDay(pTm->tm_wday), -1/*Compute length automatically*/);
  41142. break;
  41143. case 'e':
  41144. /* Day of the month, 2 digits with leading space for single digit*/
  41145. jx9_result_string_format(pCtx, "%2d", pTm->tm_mday);
  41146. break;
  41147. case 'd':
  41148. /* Two-digit day of the month (with leading zeros) */
  41149. jx9_result_string_format(pCtx, "%02d", pTm->tm_mon+1);
  41150. break;
  41151. case 'j':
  41152. /*The day of the year, 3 digits with leading zeros*/
  41153. jx9_result_string_format(pCtx, "%03d", pTm->tm_yday);
  41154. break;
  41155. case 'u':
  41156. /* ISO-8601 numeric representation of the day of the week */
  41157. jx9_result_string_format(pCtx, "%d", aISO8601[pTm->tm_wday % 7 ]);
  41158. break;
  41159. case 'w':
  41160. /* Numeric representation of the day of the week */
  41161. jx9_result_string_format(pCtx, "%d", pTm->tm_wday);
  41162. break;
  41163. case 'b':
  41164. case 'h':
  41165. /*A short textual representation of a month, three letters (Not based on locale)*/
  41166. jx9_result_string(pCtx, SyTimeGetMonth(pTm->tm_mon), (int)sizeof(char)*3);
  41167. break;
  41168. case 'B':
  41169. /* Full month name (Not based on locale) */
  41170. jx9_result_string(pCtx, SyTimeGetMonth(pTm->tm_mon), -1/*Compute length automatically*/);
  41171. break;
  41172. case 'm':
  41173. /*Numeric representation of a month, with leading zeros*/
  41174. jx9_result_string_format(pCtx, "%02d", pTm->tm_mon + 1);
  41175. break;
  41176. case 'C':
  41177. /* Two digit representation of the century */
  41178. jx9_result_string_format(pCtx, "%2d", pTm->tm_year/100);
  41179. break;
  41180. case 'y':
  41181. case 'g':
  41182. /* Two digit representation of the year */
  41183. jx9_result_string_format(pCtx, "%2d", pTm->tm_year%100);
  41184. break;
  41185. case 'Y':
  41186. case 'G':
  41187. /* Four digit representation of the year */
  41188. jx9_result_string_format(pCtx, "%4d", pTm->tm_year);
  41189. break;
  41190. case 'I':
  41191. /* 12-hour format of an hour with leading zeros */
  41192. jx9_result_string_format(pCtx, "%02d", 1+(pTm->tm_hour%12));
  41193. break;
  41194. case 'l':
  41195. /* 12-hour format of an hour with leading space */
  41196. jx9_result_string_format(pCtx, "%2d", 1+(pTm->tm_hour%12));
  41197. break;
  41198. case 'H':
  41199. /* 24-hour format of an hour with leading zeros */
  41200. jx9_result_string_format(pCtx, "%02d", pTm->tm_hour);
  41201. break;
  41202. case 'M':
  41203. /* Minutes with leading zeros */
  41204. jx9_result_string_format(pCtx, "%02d", pTm->tm_min);
  41205. break;
  41206. case 'S':
  41207. /* Seconds with leading zeros */
  41208. jx9_result_string_format(pCtx, "%02d", pTm->tm_sec);
  41209. break;
  41210. case 'z':
  41211. case 'Z':
  41212. /* Timezone identifier */
  41213. zCur = pTm->tm_zone;
  41214. if( zCur == 0 ){
  41215. /* Assume GMT */
  41216. zCur = "GMT";
  41217. }
  41218. jx9_result_string(pCtx, zCur, -1);
  41219. break;
  41220. case 'T':
  41221. case 'X':
  41222. /* Same as "%H:%M:%S" */
  41223. jx9_result_string_format(pCtx, "%02d:%02d:%02d", pTm->tm_hour, pTm->tm_min, pTm->tm_sec);
  41224. break;
  41225. case 'R':
  41226. /* Same as "%H:%M" */
  41227. jx9_result_string_format(pCtx, "%02d:%02d", pTm->tm_hour, pTm->tm_min);
  41228. break;
  41229. case 'P':
  41230. /* Lowercase Ante meridiem and Post meridiem */
  41231. jx9_result_string(pCtx, pTm->tm_hour > 12 ? "pm" : "am", (int)sizeof(char)*2);
  41232. break;
  41233. case 'p':
  41234. /* Uppercase Ante meridiem and Post meridiem */
  41235. jx9_result_string(pCtx, pTm->tm_hour > 12 ? "PM" : "AM", (int)sizeof(char)*2);
  41236. break;
  41237. case 'r':
  41238. /* Same as "%I:%M:%S %p" */
  41239. jx9_result_string_format(pCtx, "%02d:%02d:%02d %s",
  41240. 1+(pTm->tm_hour%12),
  41241. pTm->tm_min,
  41242. pTm->tm_sec,
  41243. pTm->tm_hour > 12 ? "PM" : "AM"
  41244. );
  41245. break;
  41246. case 'D':
  41247. case 'x':
  41248. /* Same as "%m/%d/%y" */
  41249. jx9_result_string_format(pCtx, "%02d/%02d/%02d",
  41250. pTm->tm_mon+1,
  41251. pTm->tm_mday,
  41252. pTm->tm_year%100
  41253. );
  41254. break;
  41255. case 'F':
  41256. /* Same as "%Y-%m-%d" */
  41257. jx9_result_string_format(pCtx, "%d-%02d-%02d",
  41258. pTm->tm_year,
  41259. pTm->tm_mon+1,
  41260. pTm->tm_mday
  41261. );
  41262. break;
  41263. case 'c':
  41264. jx9_result_string_format(pCtx, "%d-%02d-%02d %02d:%02d:%02d",
  41265. pTm->tm_year,
  41266. pTm->tm_mon+1,
  41267. pTm->tm_mday,
  41268. pTm->tm_hour,
  41269. pTm->tm_min,
  41270. pTm->tm_sec
  41271. );
  41272. break;
  41273. case 's':{
  41274. time_t tt;
  41275. /* Seconds since the Unix Epoch */
  41276. time(&tt);
  41277. jx9_result_string_format(pCtx, "%u", (unsigned int)tt);
  41278. break;
  41279. }
  41280. default:
  41281. /* unknown specifer, simply ignore*/
  41282. break;
  41283. }
  41284. /* Advance the cursor */
  41285. zIn++;
  41286. }
  41287. return SXRET_OK;
  41288. }
  41289. /*
  41290. * string date(string $format [, int $timestamp = time() ] )
  41291. * Returns a string formatted according to the given format string using
  41292. * the given integer timestamp or the current time if no timestamp is given.
  41293. * In other words, timestamp is optional and defaults to the value of time().
  41294. * Parameters
  41295. * $format
  41296. * The format of the outputted date string (See code above)
  41297. * $timestamp
  41298. * The optional timestamp parameter is an integer Unix timestamp
  41299. * that defaults to the current local time if a timestamp is not given.
  41300. * In other words, it defaults to the value of time().
  41301. * Return
  41302. * A formatted date string. If a non-numeric value is used for timestamp, FALSE is returned.
  41303. */
  41304. static int jx9Builtin_date(jx9_context *pCtx, int nArg, jx9_value **apArg)
  41305. {
  41306. const char *zFormat;
  41307. int nLen;
  41308. Sytm sTm;
  41309. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  41310. /* Missing/Invalid argument, return FALSE */
  41311. jx9_result_bool(pCtx, 0);
  41312. return JX9_OK;
  41313. }
  41314. zFormat = jx9_value_to_string(apArg[0], &nLen);
  41315. if( nLen < 1 ){
  41316. /* Don't bother processing return the empty string */
  41317. jx9_result_string(pCtx, "", 0);
  41318. }
  41319. if( nArg < 2 ){
  41320. #ifdef __WINNT__
  41321. SYSTEMTIME sOS;
  41322. GetSystemTime(&sOS);
  41323. SYSTEMTIME_TO_SYTM(&sOS, &sTm);
  41324. #else
  41325. struct tm *pTm;
  41326. time_t t;
  41327. time(&t);
  41328. pTm = localtime(&t);
  41329. STRUCT_TM_TO_SYTM(pTm, &sTm);
  41330. #endif
  41331. }else{
  41332. /* Use the given timestamp */
  41333. time_t t;
  41334. struct tm *pTm;
  41335. if( jx9_value_is_int(apArg[1]) ){
  41336. t = (time_t)jx9_value_to_int64(apArg[1]);
  41337. pTm = localtime(&t);
  41338. if( pTm == 0 ){
  41339. time(&t);
  41340. }
  41341. }else{
  41342. time(&t);
  41343. }
  41344. pTm = localtime(&t);
  41345. STRUCT_TM_TO_SYTM(pTm, &sTm);
  41346. }
  41347. /* Format the given string */
  41348. DateFormat(pCtx, zFormat, nLen, &sTm);
  41349. return JX9_OK;
  41350. }
  41351. /*
  41352. * string strftime(string $format [, int $timestamp = time() ] )
  41353. * Format a local time/date (PLATFORM INDEPENDANT IMPLEENTATION NOT BASED ON LOCALE)
  41354. * Parameters
  41355. * $format
  41356. * The format of the outputted date string (See code above)
  41357. * $timestamp
  41358. * The optional timestamp parameter is an integer Unix timestamp
  41359. * that defaults to the current local time if a timestamp is not given.
  41360. * In other words, it defaults to the value of time().
  41361. * Return
  41362. * Returns a string formatted according format using the given timestamp
  41363. * or the current local time if no timestamp is given.
  41364. */
  41365. static int jx9Builtin_strftime(jx9_context *pCtx, int nArg, jx9_value **apArg)
  41366. {
  41367. const char *zFormat;
  41368. int nLen;
  41369. Sytm sTm;
  41370. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  41371. /* Missing/Invalid argument, return FALSE */
  41372. jx9_result_bool(pCtx, 0);
  41373. return JX9_OK;
  41374. }
  41375. zFormat = jx9_value_to_string(apArg[0], &nLen);
  41376. if( nLen < 1 ){
  41377. /* Don't bother processing return FALSE */
  41378. jx9_result_bool(pCtx, 0);
  41379. }
  41380. if( nArg < 2 ){
  41381. #ifdef __WINNT__
  41382. SYSTEMTIME sOS;
  41383. GetSystemTime(&sOS);
  41384. SYSTEMTIME_TO_SYTM(&sOS, &sTm);
  41385. #else
  41386. struct tm *pTm;
  41387. time_t t;
  41388. time(&t);
  41389. pTm = localtime(&t);
  41390. STRUCT_TM_TO_SYTM(pTm, &sTm);
  41391. #endif
  41392. }else{
  41393. /* Use the given timestamp */
  41394. time_t t;
  41395. struct tm *pTm;
  41396. if( jx9_value_is_int(apArg[1]) ){
  41397. t = (time_t)jx9_value_to_int64(apArg[1]);
  41398. pTm = localtime(&t);
  41399. if( pTm == 0 ){
  41400. time(&t);
  41401. }
  41402. }else{
  41403. time(&t);
  41404. }
  41405. pTm = localtime(&t);
  41406. STRUCT_TM_TO_SYTM(pTm, &sTm);
  41407. }
  41408. /* Format the given string */
  41409. jx9Strftime(pCtx, zFormat, nLen, &sTm);
  41410. if( jx9_context_result_buf_length(pCtx) < 1 ){
  41411. /* Nothing was formatted, return FALSE */
  41412. jx9_result_bool(pCtx, 0);
  41413. }
  41414. return JX9_OK;
  41415. }
  41416. /*
  41417. * string gmdate(string $format [, int $timestamp = time() ] )
  41418. * Identical to the date() function except that the time returned
  41419. * is Greenwich Mean Time (GMT).
  41420. * Parameters
  41421. * $format
  41422. * The format of the outputted date string (See code above)
  41423. * $timestamp
  41424. * The optional timestamp parameter is an integer Unix timestamp
  41425. * that defaults to the current local time if a timestamp is not given.
  41426. * In other words, it defaults to the value of time().
  41427. * Return
  41428. * A formatted date string. If a non-numeric value is used for timestamp, FALSE is returned.
  41429. */
  41430. static int jx9Builtin_gmdate(jx9_context *pCtx, int nArg, jx9_value **apArg)
  41431. {
  41432. const char *zFormat;
  41433. int nLen;
  41434. Sytm sTm;
  41435. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  41436. /* Missing/Invalid argument, return FALSE */
  41437. jx9_result_bool(pCtx, 0);
  41438. return JX9_OK;
  41439. }
  41440. zFormat = jx9_value_to_string(apArg[0], &nLen);
  41441. if( nLen < 1 ){
  41442. /* Don't bother processing return the empty string */
  41443. jx9_result_string(pCtx, "", 0);
  41444. }
  41445. if( nArg < 2 ){
  41446. #ifdef __WINNT__
  41447. SYSTEMTIME sOS;
  41448. GetSystemTime(&sOS);
  41449. SYSTEMTIME_TO_SYTM(&sOS, &sTm);
  41450. #else
  41451. struct tm *pTm;
  41452. time_t t;
  41453. time(&t);
  41454. pTm = gmtime(&t);
  41455. STRUCT_TM_TO_SYTM(pTm, &sTm);
  41456. #endif
  41457. }else{
  41458. /* Use the given timestamp */
  41459. time_t t;
  41460. struct tm *pTm;
  41461. if( jx9_value_is_int(apArg[1]) ){
  41462. t = (time_t)jx9_value_to_int64(apArg[1]);
  41463. pTm = gmtime(&t);
  41464. if( pTm == 0 ){
  41465. time(&t);
  41466. }
  41467. }else{
  41468. time(&t);
  41469. }
  41470. pTm = gmtime(&t);
  41471. STRUCT_TM_TO_SYTM(pTm, &sTm);
  41472. }
  41473. /* Format the given string */
  41474. DateFormat(pCtx, zFormat, nLen, &sTm);
  41475. return JX9_OK;
  41476. }
  41477. /*
  41478. * array localtime([ int $timestamp = time() [, bool $is_associative = false ]])
  41479. * Return the local time.
  41480. * Parameter
  41481. * $timestamp: The optional timestamp parameter is an integer Unix timestamp
  41482. * that defaults to the current local time if a timestamp is not given.
  41483. * In other words, it defaults to the value of time().
  41484. * $is_associative
  41485. * If set to FALSE or not supplied then the array is returned as a regular, numerically
  41486. * indexed array. If the argument is set to TRUE then localtime() returns an associative
  41487. * array containing all the different elements of the structure returned by the C function
  41488. * call to localtime. The names of the different keys of the associative array are as follows:
  41489. * "tm_sec" - seconds, 0 to 59
  41490. * "tm_min" - minutes, 0 to 59
  41491. * "tm_hour" - hours, 0 to 23
  41492. * "tm_mday" - day of the month, 1 to 31
  41493. * "tm_mon" - month of the year, 0 (Jan) to 11 (Dec)
  41494. * "tm_year" - years since 1900
  41495. * "tm_wday" - day of the week, 0 (Sun) to 6 (Sat)
  41496. * "tm_yday" - day of the year, 0 to 365
  41497. * "tm_isdst" - is daylight savings time in effect? Positive if yes, 0 if not, negative if unknown.
  41498. * Returns
  41499. * An associative array of information related to the timestamp.
  41500. */
  41501. static int jx9Builtin_localtime(jx9_context *pCtx, int nArg, jx9_value **apArg)
  41502. {
  41503. jx9_value *pValue, *pArray;
  41504. int isAssoc = 0;
  41505. Sytm sTm;
  41506. if( nArg < 1 ){
  41507. #ifdef __WINNT__
  41508. SYSTEMTIME sOS;
  41509. GetSystemTime(&sOS); /* TODO(chems): GMT not local */
  41510. SYSTEMTIME_TO_SYTM(&sOS, &sTm);
  41511. #else
  41512. struct tm *pTm;
  41513. time_t t;
  41514. time(&t);
  41515. pTm = localtime(&t);
  41516. STRUCT_TM_TO_SYTM(pTm, &sTm);
  41517. #endif
  41518. }else{
  41519. /* Use the given timestamp */
  41520. time_t t;
  41521. struct tm *pTm;
  41522. if( jx9_value_is_int(apArg[0]) ){
  41523. t = (time_t)jx9_value_to_int64(apArg[0]);
  41524. pTm = localtime(&t);
  41525. if( pTm == 0 ){
  41526. time(&t);
  41527. }
  41528. }else{
  41529. time(&t);
  41530. }
  41531. pTm = localtime(&t);
  41532. STRUCT_TM_TO_SYTM(pTm, &sTm);
  41533. }
  41534. /* Element value */
  41535. pValue = jx9_context_new_scalar(pCtx);
  41536. if( pValue == 0 ){
  41537. /* Return NULL */
  41538. jx9_result_null(pCtx);
  41539. return JX9_OK;
  41540. }
  41541. /* Create a new array */
  41542. pArray = jx9_context_new_array(pCtx);
  41543. if( pArray == 0 ){
  41544. /* Return NULL */
  41545. jx9_result_null(pCtx);
  41546. return JX9_OK;
  41547. }
  41548. if( nArg > 1 ){
  41549. isAssoc = jx9_value_to_bool(apArg[1]);
  41550. }
  41551. /* Fill the array */
  41552. /* Seconds */
  41553. jx9_value_int(pValue, sTm.tm_sec);
  41554. if( isAssoc ){
  41555. jx9_array_add_strkey_elem(pArray, "tm_sec", pValue);
  41556. }else{
  41557. jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
  41558. }
  41559. /* Minutes */
  41560. jx9_value_int(pValue, sTm.tm_min);
  41561. if( isAssoc ){
  41562. jx9_array_add_strkey_elem(pArray, "tm_min", pValue);
  41563. }else{
  41564. jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
  41565. }
  41566. /* Hours */
  41567. jx9_value_int(pValue, sTm.tm_hour);
  41568. if( isAssoc ){
  41569. jx9_array_add_strkey_elem(pArray, "tm_hour", pValue);
  41570. }else{
  41571. jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
  41572. }
  41573. /* mday */
  41574. jx9_value_int(pValue, sTm.tm_mday);
  41575. if( isAssoc ){
  41576. jx9_array_add_strkey_elem(pArray, "tm_mday", pValue);
  41577. }else{
  41578. jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
  41579. }
  41580. /* mon */
  41581. jx9_value_int(pValue, sTm.tm_mon);
  41582. if( isAssoc ){
  41583. jx9_array_add_strkey_elem(pArray, "tm_mon", pValue);
  41584. }else{
  41585. jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
  41586. }
  41587. /* year since 1900 */
  41588. jx9_value_int(pValue, sTm.tm_year-1900);
  41589. if( isAssoc ){
  41590. jx9_array_add_strkey_elem(pArray, "tm_year", pValue);
  41591. }else{
  41592. jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
  41593. }
  41594. /* wday */
  41595. jx9_value_int(pValue, sTm.tm_wday);
  41596. if( isAssoc ){
  41597. jx9_array_add_strkey_elem(pArray, "tm_wday", pValue);
  41598. }else{
  41599. jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
  41600. }
  41601. /* yday */
  41602. jx9_value_int(pValue, sTm.tm_yday);
  41603. if( isAssoc ){
  41604. jx9_array_add_strkey_elem(pArray, "tm_yday", pValue);
  41605. }else{
  41606. jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
  41607. }
  41608. /* isdst */
  41609. #ifdef __WINNT__
  41610. #ifdef _MSC_VER
  41611. #ifndef _WIN32_WCE
  41612. _get_daylight(&sTm.tm_isdst);
  41613. #endif
  41614. #endif
  41615. #endif
  41616. jx9_value_int(pValue, sTm.tm_isdst);
  41617. if( isAssoc ){
  41618. jx9_array_add_strkey_elem(pArray, "tm_isdst", pValue);
  41619. }else{
  41620. jx9_array_add_elem(pArray, 0/* Automatic index */, pValue);
  41621. }
  41622. /* Return the array */
  41623. jx9_result_value(pCtx, pArray);
  41624. return JX9_OK;
  41625. }
  41626. /*
  41627. * int idate(string $format [, int $timestamp = time() ])
  41628. * Returns a number formatted according to the given format string
  41629. * using the given integer timestamp or the current local time if
  41630. * no timestamp is given. In other words, timestamp is optional and defaults
  41631. * to the value of time().
  41632. * Unlike the function date(), idate() accepts just one char in the format
  41633. * parameter.
  41634. * $Parameters
  41635. * Supported format
  41636. * d Day of the month
  41637. * h Hour (12 hour format)
  41638. * H Hour (24 hour format)
  41639. * i Minutes
  41640. * I (uppercase i)1 if DST is activated, 0 otherwise
  41641. * L (uppercase l) returns 1 for leap year, 0 otherwise
  41642. * m Month number
  41643. * s Seconds
  41644. * t Days in current month
  41645. * U Seconds since the Unix Epoch - January 1 1970 00:00:00 UTC - this is the same as time()
  41646. * w Day of the week (0 on Sunday)
  41647. * W ISO-8601 week number of year, weeks starting on Monday
  41648. * y Year (1 or 2 digits - check note below)
  41649. * Y Year (4 digits)
  41650. * z Day of the year
  41651. * Z Timezone offset in seconds
  41652. * $timestamp
  41653. * The optional timestamp parameter is an integer Unix timestamp that defaults
  41654. * to the current local time if a timestamp is not given. In other words, it defaults
  41655. * to the value of time().
  41656. * Return
  41657. * An integer.
  41658. */
  41659. static int jx9Builtin_idate(jx9_context *pCtx, int nArg, jx9_value **apArg)
  41660. {
  41661. const char *zFormat;
  41662. jx9_int64 iVal = 0;
  41663. int nLen;
  41664. Sytm sTm;
  41665. if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
  41666. /* Missing/Invalid argument, return -1 */
  41667. jx9_result_int(pCtx, -1);
  41668. return JX9_OK;
  41669. }
  41670. zFormat = jx9_value_to_string(apArg[0], &nLen);
  41671. if( nLen < 1 ){
  41672. /* Don't bother processing return -1*/
  41673. jx9_result_int(pCtx, -1);
  41674. }
  41675. if( nArg < 2 ){
  41676. #ifdef __WINNT__
  41677. SYSTEMTIME sOS;
  41678. GetSystemTime(&sOS);
  41679. SYSTEMTIME_TO_SYTM(&sOS, &sTm);
  41680. #else
  41681. struct tm *pTm;
  41682. time_t t;
  41683. time(&t);
  41684. pTm = localtime(&t);
  41685. STRUCT_TM_TO_SYTM(pTm, &sTm);
  41686. #endif
  41687. }else{
  41688. /* Use the given timestamp */
  41689. time_t t;
  41690. struct tm *pTm;
  41691. if( jx9_value_is_int(apArg[1]) ){
  41692. t = (time_t)jx9_value_to_int64(apArg[1]);
  41693. pTm = localtime(&t);
  41694. if( pTm == 0 ){
  41695. time(&t);
  41696. }
  41697. }else{
  41698. time(&t);
  41699. }
  41700. pTm = localtime(&t);
  41701. STRUCT_TM_TO_SYTM(pTm, &sTm);
  41702. }
  41703. /* Perform the requested operation */
  41704. switch(zFormat[0]){
  41705. case 'd':
  41706. /* Day of the month */
  41707. iVal = sTm.tm_mday;
  41708. break;
  41709. case 'h':
  41710. /* Hour (12 hour format)*/
  41711. iVal = 1 + (sTm.tm_hour % 12);
  41712. break;
  41713. case 'H':
  41714. /* Hour (24 hour format)*/
  41715. iVal = sTm.tm_hour;
  41716. break;
  41717. case 'i':
  41718. /*Minutes*/
  41719. iVal = sTm.tm_min;
  41720. break;
  41721. case 'I':
  41722. /* returns 1 if DST is activated, 0 otherwise */
  41723. #ifdef __WINNT__
  41724. #ifdef _MSC_VER
  41725. #ifndef _WIN32_WCE
  41726. _get_daylight(&sTm.tm_isdst);
  41727. #endif
  41728. #endif
  41729. #endif
  41730. iVal = sTm.tm_isdst;
  41731. break;
  41732. case 'L':
  41733. /* returns 1 for leap year, 0 otherwise */
  41734. iVal = IS_LEAP_YEAR(sTm.tm_year);
  41735. break;
  41736. case 'm':
  41737. /* Month number*/
  41738. iVal = sTm.tm_mon;
  41739. break;
  41740. case 's':
  41741. /*Seconds*/
  41742. iVal = sTm.tm_sec;
  41743. break;
  41744. case 't':{
  41745. /*Days in current month*/
  41746. static const int aMonDays[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  41747. int nDays = aMonDays[sTm.tm_mon % 12 ];
  41748. if( sTm.tm_mon == 1 /* 'February' */ && !IS_LEAP_YEAR(sTm.tm_year) ){
  41749. nDays = 28;
  41750. }
  41751. iVal = nDays;
  41752. break;
  41753. }
  41754. case 'U':
  41755. /*Seconds since the Unix Epoch*/
  41756. iVal = (jx9_int64)time(0);
  41757. break;
  41758. case 'w':
  41759. /* Day of the week (0 on Sunday) */
  41760. iVal = sTm.tm_wday;
  41761. break;
  41762. case 'W': {
  41763. /* ISO-8601 week number of year, weeks starting on Monday */
  41764. static const int aISO8601[] = { 7 /* Sunday */, 1 /* Monday */, 2, 3, 4, 5, 6 };
  41765. iVal = aISO8601[sTm.tm_wday % 7 ];
  41766. break;
  41767. }
  41768. case 'y':
  41769. /* Year (2 digits) */
  41770. iVal = sTm.tm_year % 100;
  41771. break;
  41772. case 'Y':
  41773. /* Year (4 digits) */
  41774. iVal = sTm.tm_year;
  41775. break;
  41776. case 'z':
  41777. /* Day of the year */
  41778. iVal = sTm.tm_yday;
  41779. break;
  41780. case 'Z':
  41781. /*Timezone offset in seconds*/
  41782. iVal = sTm.tm_gmtoff;
  41783. break;
  41784. default:
  41785. /* unknown format, throw a warning */
  41786. jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Unknown date format token");
  41787. break;
  41788. }
  41789. /* Return the time value */
  41790. jx9_result_int64(pCtx, iVal);
  41791. return JX9_OK;
  41792. }
  41793. /*
  41794. * int mktime/gmmktime([ int $hour = date("H") [, int $minute = date("i") [, int $second = date("s")
  41795. * [, int $month = date("n") [, int $day = date("j") [, int $year = date("Y") [, int $is_dst = -1 ]]]]]]] )
  41796. * Returns the Unix timestamp corresponding to the arguments given. This timestamp is a 64bit integer
  41797. * containing the number of seconds between the Unix Epoch (January 1 1970 00:00:00 GMT) and the time
  41798. * specified.
  41799. * Arguments may be left out in order from right to left; any arguments thus omitted will be set to
  41800. * the current value according to the local date and time.
  41801. * Parameters
  41802. * $hour
  41803. * The number of the hour relevant to the start of the day determined by month, day and year.
  41804. * Negative values reference the hour before midnight of the day in question. Values greater
  41805. * than 23 reference the appropriate hour in the following day(s).
  41806. * $minute
  41807. * The number of the minute relevant to the start of the hour. Negative values reference
  41808. * the minute in the previous hour. Values greater than 59 reference the appropriate minute
  41809. * in the following hour(s).
  41810. * $second
  41811. * The number of seconds relevant to the start of the minute. Negative values reference
  41812. * the second in the previous minute. Values greater than 59 reference the appropriate
  41813. * second in the following minute(s).
  41814. * $month
  41815. * The number of the month relevant to the end of the previous year. Values 1 to 12 reference
  41816. * the normal calendar months of the year in question. Values less than 1 (including negative values)
  41817. * reference the months in the previous year in reverse order, so 0 is December, -1 is November)...
  41818. * $day
  41819. * The number of the day relevant to the end of the previous month. Values 1 to 28, 29, 30 or 31
  41820. * (depending upon the month) reference the normal days in the relevant month. Values less than 1
  41821. * (including negative values) reference the days in the previous month, so 0 is the last day
  41822. * of the previous month, -1 is the day before that, etc. Values greater than the number of days
  41823. * in the relevant month reference the appropriate day in the following month(s).
  41824. * $year
  41825. * The number of the year, may be a two or four digit value, with values between 0-69 mapping
  41826. * to 2000-2069 and 70-100 to 1970-2000. On systems where time_t is a 32bit signed integer, as
  41827. * most common today, the valid range for year is somewhere between 1901 and 2038.
  41828. * $is_dst
  41829. * This parameter can be set to 1 if the time is during daylight savings time (DST), 0 if it is not,
  41830. * or -1 (the default) if it is unknown whether the time is within daylight savings time or not.
  41831. * Return
  41832. * mktime() returns the Unix timestamp of the arguments given.
  41833. * If the arguments are invalid, the function returns FALSE
  41834. */
  41835. static int jx9Builtin_mktime(jx9_context *pCtx, int nArg, jx9_value **apArg)
  41836. {
  41837. const char *zFunction;
  41838. jx9_int64 iVal = 0;
  41839. struct tm *pTm;
  41840. time_t t;
  41841. /* Extract function name */
  41842. zFunction = jx9_function_name(pCtx);
  41843. /* Get the current time */
  41844. time(&t);
  41845. if( zFunction[0] == 'g' /* gmmktime */ ){
  41846. pTm = gmtime(&t);
  41847. }else{
  41848. /* localtime */
  41849. pTm = localtime(&t);
  41850. }
  41851. if( nArg > 0 ){
  41852. int iVal;
  41853. /* Hour */
  41854. iVal = jx9_value_to_int(apArg[0]);
  41855. pTm->tm_hour = iVal;
  41856. if( nArg > 1 ){
  41857. /* Minutes */
  41858. iVal = jx9_value_to_int(apArg[1]);
  41859. pTm->tm_min = iVal;
  41860. if( nArg > 2 ){
  41861. /* Seconds */
  41862. iVal = jx9_value_to_int(apArg[2]);
  41863. pTm->tm_sec = iVal;
  41864. if( nArg > 3 ){
  41865. /* Month */
  41866. iVal = jx9_value_to_int(apArg[3]);
  41867. pTm->tm_mon = iVal - 1;
  41868. if( nArg > 4 ){
  41869. /* mday */
  41870. iVal = jx9_value_to_int(apArg[4]);
  41871. pTm->tm_mday = iVal;
  41872. if( nArg > 5 ){
  41873. /* Year */
  41874. iVal = jx9_value_to_int(apArg[5]);
  41875. if( iVal > 1900 ){
  41876. iVal -= 1900;
  41877. }
  41878. pTm->tm_year = iVal;
  41879. if( nArg > 6 ){
  41880. /* is_dst */
  41881. iVal = jx9_value_to_bool(apArg[6]);
  41882. pTm->tm_isdst = iVal;
  41883. }
  41884. }
  41885. }
  41886. }
  41887. }
  41888. }
  41889. }
  41890. /* Make the time */
  41891. iVal = (jx9_int64)mktime(pTm);
  41892. /* Return the timesatmp as a 64bit integer */
  41893. jx9_result_int64(pCtx, iVal);
  41894. return JX9_OK;
  41895. }
  41896. /*
  41897. * Section:
  41898. * URL handling Functions.
  41899. * Authors:
  41900. * Symisc Systems, devel@symisc.net.
  41901. * Copyright (C) Symisc Systems, http://jx9.symisc.net
  41902. * Status:
  41903. * Stable.
  41904. */
  41905. /*
  41906. * Output consumer callback for the standard Symisc routines.
  41907. * [i.e: SyBase64Encode(), SyBase64Decode(), SyUriEncode(), ...].
  41908. */
  41909. static int Consumer(const void *pData, unsigned int nLen, void *pUserData)
  41910. {
  41911. /* Store in the call context result buffer */
  41912. jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen);
  41913. return SXRET_OK;
  41914. }
  41915. /*
  41916. * string base64_encode(string $data)
  41917. * string convert_uuencode(string $data)
  41918. * Encodes data with MIME base64
  41919. * Parameter
  41920. * $data
  41921. * Data to encode
  41922. * Return
  41923. * Encoded data or FALSE on failure.
  41924. */
  41925. static int jx9Builtin_base64_encode(jx9_context *pCtx, int nArg, jx9_value **apArg)
  41926. {
  41927. const char *zIn;
  41928. int nLen;
  41929. if( nArg < 1 ){
  41930. /* Missing arguments, return FALSE */
  41931. jx9_result_bool(pCtx, 0);
  41932. return JX9_OK;
  41933. }
  41934. /* Extract the input string */
  41935. zIn = jx9_value_to_string(apArg[0], &nLen);
  41936. if( nLen < 1 ){
  41937. /* Nothing to process, return FALSE */
  41938. jx9_result_bool(pCtx, 0);
  41939. return JX9_OK;
  41940. }
  41941. /* Perform the BASE64 encoding */
  41942. SyBase64Encode(zIn, (sxu32)nLen, Consumer, pCtx);
  41943. return JX9_OK;
  41944. }
  41945. /*
  41946. * string base64_decode(string $data)
  41947. * string convert_uudecode(string $data)
  41948. * Decodes data encoded with MIME base64
  41949. * Parameter
  41950. * $data
  41951. * Encoded data.
  41952. * Return
  41953. * Returns the original data or FALSE on failure.
  41954. */
  41955. static int jx9Builtin_base64_decode(jx9_context *pCtx, int nArg, jx9_value **apArg)
  41956. {
  41957. const char *zIn;
  41958. int nLen;
  41959. if( nArg < 1 ){
  41960. /* Missing arguments, return FALSE */
  41961. jx9_result_bool(pCtx, 0);
  41962. return JX9_OK;
  41963. }
  41964. /* Extract the input string */
  41965. zIn = jx9_value_to_string(apArg[0], &nLen);
  41966. if( nLen < 1 ){
  41967. /* Nothing to process, return FALSE */
  41968. jx9_result_bool(pCtx, 0);
  41969. return JX9_OK;
  41970. }
  41971. /* Perform the BASE64 decoding */
  41972. SyBase64Decode(zIn, (sxu32)nLen, Consumer, pCtx);
  41973. return JX9_OK;
  41974. }
  41975. /*
  41976. * string urlencode(string $str)
  41977. * URL encoding
  41978. * Parameter
  41979. * $data
  41980. * Input string.
  41981. * Return
  41982. * Returns a string in which all non-alphanumeric characters except -_. have
  41983. * been replaced with a percent (%) sign followed by two hex digits and spaces
  41984. * encoded as plus (+) signs.
  41985. */
  41986. static int jx9Builtin_urlencode(jx9_context *pCtx, int nArg, jx9_value **apArg)
  41987. {
  41988. const char *zIn;
  41989. int nLen;
  41990. if( nArg < 1 ){
  41991. /* Missing arguments, return FALSE */
  41992. jx9_result_bool(pCtx, 0);
  41993. return JX9_OK;
  41994. }
  41995. /* Extract the input string */
  41996. zIn = jx9_value_to_string(apArg[0], &nLen);
  41997. if( nLen < 1 ){
  41998. /* Nothing to process, return FALSE */
  41999. jx9_result_bool(pCtx, 0);
  42000. return JX9_OK;
  42001. }
  42002. /* Perform the URL encoding */
  42003. SyUriEncode(zIn, (sxu32)nLen, Consumer, pCtx);
  42004. return JX9_OK;
  42005. }
  42006. /*
  42007. * string urldecode(string $str)
  42008. * Decodes any %## encoding in the given string.
  42009. * Plus symbols ('+') are decoded to a space character.
  42010. * Parameter
  42011. * $data
  42012. * Input string.
  42013. * Return
  42014. * Decoded URL or FALSE on failure.
  42015. */
  42016. static int jx9Builtin_urldecode(jx9_context *pCtx, int nArg, jx9_value **apArg)
  42017. {
  42018. const char *zIn;
  42019. int nLen;
  42020. if( nArg < 1 ){
  42021. /* Missing arguments, return FALSE */
  42022. jx9_result_bool(pCtx, 0);
  42023. return JX9_OK;
  42024. }
  42025. /* Extract the input string */
  42026. zIn = jx9_value_to_string(apArg[0], &nLen);
  42027. if( nLen < 1 ){
  42028. /* Nothing to process, return FALSE */
  42029. jx9_result_bool(pCtx, 0);
  42030. return JX9_OK;
  42031. }
  42032. /* Perform the URL decoding */
  42033. SyUriDecode(zIn, (sxu32)nLen, Consumer, pCtx, TRUE);
  42034. return JX9_OK;
  42035. }
  42036. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  42037. /* Table of the built-in functions */
  42038. static const jx9_builtin_func aBuiltInFunc[] = {
  42039. /* Variable handling functions */
  42040. { "is_bool" , jx9Builtin_is_bool },
  42041. { "is_float" , jx9Builtin_is_float },
  42042. { "is_real" , jx9Builtin_is_float },
  42043. { "is_double" , jx9Builtin_is_float },
  42044. { "is_int" , jx9Builtin_is_int },
  42045. { "is_integer" , jx9Builtin_is_int },
  42046. { "is_long" , jx9Builtin_is_int },
  42047. { "is_string" , jx9Builtin_is_string },
  42048. { "is_null" , jx9Builtin_is_null },
  42049. { "is_numeric" , jx9Builtin_is_numeric },
  42050. { "is_scalar" , jx9Builtin_is_scalar },
  42051. { "is_array" , jx9Builtin_is_array },
  42052. { "is_object" , jx9Builtin_is_object },
  42053. { "is_resource", jx9Builtin_is_resource },
  42054. { "douleval" , jx9Builtin_floatval },
  42055. { "floatval" , jx9Builtin_floatval },
  42056. { "intval" , jx9Builtin_intval },
  42057. { "strval" , jx9Builtin_strval },
  42058. { "empty" , jx9Builtin_empty },
  42059. #ifndef JX9_DISABLE_BUILTIN_FUNC
  42060. #ifdef JX9_ENABLE_MATH_FUNC
  42061. /* Math functions */
  42062. { "abs" , jx9Builtin_abs },
  42063. { "sqrt" , jx9Builtin_sqrt },
  42064. { "exp" , jx9Builtin_exp },
  42065. { "floor", jx9Builtin_floor },
  42066. { "cos" , jx9Builtin_cos },
  42067. { "sin" , jx9Builtin_sin },
  42068. { "acos" , jx9Builtin_acos },
  42069. { "asin" , jx9Builtin_asin },
  42070. { "cosh" , jx9Builtin_cosh },
  42071. { "sinh" , jx9Builtin_sinh },
  42072. { "ceil" , jx9Builtin_ceil },
  42073. { "tan" , jx9Builtin_tan },
  42074. { "tanh" , jx9Builtin_tanh },
  42075. { "atan" , jx9Builtin_atan },
  42076. { "atan2", jx9Builtin_atan2 },
  42077. { "log" , jx9Builtin_log },
  42078. { "log10" , jx9Builtin_log10 },
  42079. { "pow" , jx9Builtin_pow },
  42080. { "pi", jx9Builtin_pi },
  42081. { "fmod", jx9Builtin_fmod },
  42082. { "hypot", jx9Builtin_hypot },
  42083. #endif /* JX9_ENABLE_MATH_FUNC */
  42084. { "round", jx9Builtin_round },
  42085. { "dechex", jx9Builtin_dechex },
  42086. { "decoct", jx9Builtin_decoct },
  42087. { "decbin", jx9Builtin_decbin },
  42088. { "hexdec", jx9Builtin_hexdec },
  42089. { "bindec", jx9Builtin_bindec },
  42090. { "octdec", jx9Builtin_octdec },
  42091. { "base_convert", jx9Builtin_base_convert },
  42092. /* String handling functions */
  42093. { "substr", jx9Builtin_substr },
  42094. { "substr_compare", jx9Builtin_substr_compare },
  42095. { "substr_count", jx9Builtin_substr_count },
  42096. { "chunk_split", jx9Builtin_chunk_split},
  42097. { "htmlspecialchars", jx9Builtin_htmlspecialchars },
  42098. { "htmlspecialchars_decode", jx9Builtin_htmlspecialchars_decode },
  42099. { "get_html_translation_table", jx9Builtin_get_html_translation_table },
  42100. { "htmlentities", jx9Builtin_htmlentities},
  42101. { "html_entity_decode", jx9Builtin_html_entity_decode},
  42102. { "strlen" , jx9Builtin_strlen },
  42103. { "strcmp" , jx9Builtin_strcmp },
  42104. { "strcoll" , jx9Builtin_strcmp },
  42105. { "strncmp" , jx9Builtin_strncmp },
  42106. { "strcasecmp" , jx9Builtin_strcasecmp },
  42107. { "strncasecmp", jx9Builtin_strncasecmp},
  42108. { "implode" , jx9Builtin_implode },
  42109. { "join" , jx9Builtin_implode },
  42110. { "implode_recursive" , jx9Builtin_implode_recursive },
  42111. { "join_recursive" , jx9Builtin_implode_recursive },
  42112. { "explode" , jx9Builtin_explode },
  42113. { "trim" , jx9Builtin_trim },
  42114. { "rtrim" , jx9Builtin_rtrim },
  42115. { "chop" , jx9Builtin_rtrim },
  42116. { "ltrim" , jx9Builtin_ltrim },
  42117. { "strtolower", jx9Builtin_strtolower },
  42118. { "mb_strtolower", jx9Builtin_strtolower }, /* Only UTF-8 encoding is supported */
  42119. { "strtoupper", jx9Builtin_strtoupper },
  42120. { "mb_strtoupper", jx9Builtin_strtoupper }, /* Only UTF-8 encoding is supported */
  42121. { "ord", jx9Builtin_ord },
  42122. { "chr", jx9Builtin_chr },
  42123. { "bin2hex", jx9Builtin_bin2hex },
  42124. { "strstr", jx9Builtin_strstr },
  42125. { "stristr", jx9Builtin_stristr },
  42126. { "strchr", jx9Builtin_strstr },
  42127. { "strpos", jx9Builtin_strpos },
  42128. { "stripos", jx9Builtin_stripos },
  42129. { "strrpos", jx9Builtin_strrpos },
  42130. { "strripos", jx9Builtin_strripos },
  42131. { "strrchr", jx9Builtin_strrchr },
  42132. { "strrev", jx9Builtin_strrev },
  42133. { "str_repeat", jx9Builtin_str_repeat },
  42134. { "nl2br", jx9Builtin_nl2br },
  42135. { "sprintf", jx9Builtin_sprintf },
  42136. { "printf", jx9Builtin_printf },
  42137. { "vprintf", jx9Builtin_vprintf },
  42138. { "vsprintf", jx9Builtin_vsprintf },
  42139. { "size_format", jx9Builtin_size_format},
  42140. #if !defined(JX9_DISABLE_HASH_FUNC)
  42141. { "md5", jx9Builtin_md5 },
  42142. { "sha1", jx9Builtin_sha1 },
  42143. { "crc32", jx9Builtin_crc32 },
  42144. #endif /* JX9_DISABLE_HASH_FUNC */
  42145. { "str_getcsv", jx9Builtin_str_getcsv },
  42146. { "strip_tags", jx9Builtin_strip_tags },
  42147. { "str_split", jx9Builtin_str_split },
  42148. { "strspn", jx9Builtin_strspn },
  42149. { "strcspn", jx9Builtin_strcspn },
  42150. { "strpbrk", jx9Builtin_strpbrk },
  42151. { "soundex", jx9Builtin_soundex },
  42152. { "wordwrap", jx9Builtin_wordwrap },
  42153. { "strtok", jx9Builtin_strtok },
  42154. { "str_pad", jx9Builtin_str_pad },
  42155. { "str_replace", jx9Builtin_str_replace},
  42156. { "str_ireplace", jx9Builtin_str_replace},
  42157. { "strtr", jx9Builtin_strtr },
  42158. { "parse_ini_string", jx9Builtin_parse_ini_string},
  42159. /* Ctype functions */
  42160. { "ctype_alnum", jx9Builtin_ctype_alnum },
  42161. { "ctype_alpha", jx9Builtin_ctype_alpha },
  42162. { "ctype_cntrl", jx9Builtin_ctype_cntrl },
  42163. { "ctype_digit", jx9Builtin_ctype_digit },
  42164. { "ctype_xdigit", jx9Builtin_ctype_xdigit},
  42165. { "ctype_graph", jx9Builtin_ctype_graph },
  42166. { "ctype_print", jx9Builtin_ctype_print },
  42167. { "ctype_punct", jx9Builtin_ctype_punct },
  42168. { "ctype_space", jx9Builtin_ctype_space },
  42169. { "ctype_lower", jx9Builtin_ctype_lower },
  42170. { "ctype_upper", jx9Builtin_ctype_upper },
  42171. /* Time functions */
  42172. { "time" , jx9Builtin_time },
  42173. { "microtime", jx9Builtin_microtime },
  42174. { "getdate" , jx9Builtin_getdate },
  42175. { "gettimeofday", jx9Builtin_gettimeofday },
  42176. { "date", jx9Builtin_date },
  42177. { "strftime", jx9Builtin_strftime },
  42178. { "idate", jx9Builtin_idate },
  42179. { "gmdate", jx9Builtin_gmdate },
  42180. { "localtime", jx9Builtin_localtime },
  42181. { "mktime", jx9Builtin_mktime },
  42182. { "gmmktime", jx9Builtin_mktime },
  42183. /* URL functions */
  42184. { "base64_encode", jx9Builtin_base64_encode },
  42185. { "base64_decode", jx9Builtin_base64_decode },
  42186. { "convert_uuencode", jx9Builtin_base64_encode },
  42187. { "convert_uudecode", jx9Builtin_base64_decode },
  42188. { "urlencode", jx9Builtin_urlencode },
  42189. { "urldecode", jx9Builtin_urldecode },
  42190. { "rawurlencode", jx9Builtin_urlencode },
  42191. { "rawurldecode", jx9Builtin_urldecode },
  42192. #endif /* JX9_DISABLE_BUILTIN_FUNC */
  42193. };
  42194. /*
  42195. * Register the built-in functions defined above, the array functions
  42196. * defined in hashmap.c and the IO functions defined in vfs.c.
  42197. */
  42198. JX9_PRIVATE void jx9RegisterBuiltInFunction(jx9_vm *pVm)
  42199. {
  42200. sxu32 n;
  42201. for( n = 0 ; n < SX_ARRAYSIZE(aBuiltInFunc) ; ++n ){
  42202. jx9_create_function(&(*pVm), aBuiltInFunc[n].zName, aBuiltInFunc[n].xFunc, 0);
  42203. }
  42204. /* Register hashmap functions [i.e: sort(), count(), array_diff(), ...] */
  42205. jx9RegisterHashmapFunctions(&(*pVm));
  42206. /* Register IO functions [i.e: fread(), fwrite(), chdir(), mkdir(), file(), ...] */
  42207. jx9RegisterIORoutine(&(*pVm));
  42208. }
  42209. /*
  42210. * ----------------------------------------------------------
  42211. * File: api.c
  42212. * MD5: 7bd7266409f07b45b14bd6ce14e17eb4
  42213. * ----------------------------------------------------------
  42214. */
  42215. /*
  42216. * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
  42217. * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
  42218. * Version 1.7.2
  42219. * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  42220. * please contact Symisc Systems via:
  42221. * legal@symisc.net
  42222. * licensing@symisc.net
  42223. * contact@symisc.net
  42224. * or visit:
  42225. * http://jx9.symisc.net/
  42226. */
  42227. /* $SymiscID: api.c v1.7 FreeBSD 2012-12-18 06:54 stable <chm@symisc.net> $ */
  42228. #ifndef JX9_AMALGAMATION
  42229. #include "jx9Int.h"
  42230. #endif
  42231. /* This file implement the public interfaces presented to host-applications.
  42232. * Routines in other files are for internal use by JX9 and should not be
  42233. * accessed by users of the library.
  42234. */
  42235. #define JX9_ENGINE_MAGIC 0xF874BCD7
  42236. #define JX9_ENGINE_MISUSE(ENGINE) (ENGINE == 0 || ENGINE->nMagic != JX9_ENGINE_MAGIC)
  42237. #define JX9_VM_MISUSE(VM) (VM == 0 || VM->nMagic == JX9_VM_STALE)
  42238. /* If another thread have released a working instance, the following macros
  42239. * evaluates to true. These macros are only used when the library
  42240. * is built with threading support enabled which is not the case in
  42241. * the default built.
  42242. */
  42243. #define JX9_THRD_ENGINE_RELEASE(ENGINE) (ENGINE->nMagic != JX9_ENGINE_MAGIC)
  42244. #define JX9_THRD_VM_RELEASE(VM) (VM->nMagic == JX9_VM_STALE)
  42245. /* IMPLEMENTATION: jx9@embedded@symisc 311-12-32 */
  42246. /*
  42247. * All global variables are collected in the structure named "sMPGlobal".
  42248. * That way it is clear in the code when we are using static variable because
  42249. * its name start with sMPGlobal.
  42250. */
  42251. static struct Global_Data
  42252. {
  42253. SyMemBackend sAllocator; /* Global low level memory allocator */
  42254. #if defined(JX9_ENABLE_THREADS)
  42255. const SyMutexMethods *pMutexMethods; /* Mutex methods */
  42256. SyMutex *pMutex; /* Global mutex */
  42257. sxu32 nThreadingLevel; /* Threading level: 0 == Single threaded/1 == Multi-Threaded
  42258. * The threading level can be set using the [jx9_lib_config()]
  42259. * interface with a configuration verb set to
  42260. * JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE or
  42261. * JX9_LIB_CONFIG_THREAD_LEVEL_MULTI
  42262. */
  42263. #endif
  42264. const jx9_vfs *pVfs; /* Underlying virtual file system */
  42265. sxi32 nEngine; /* Total number of active engines */
  42266. jx9 *pEngines; /* List of active engine */
  42267. sxu32 nMagic; /* Sanity check against library misuse */
  42268. }sMPGlobal = {
  42269. {0, 0, 0, 0, 0, 0, 0, 0, {0}},
  42270. #if defined(JX9_ENABLE_THREADS)
  42271. 0,
  42272. 0,
  42273. 0,
  42274. #endif
  42275. 0,
  42276. 0,
  42277. 0,
  42278. 0
  42279. };
  42280. #define JX9_LIB_MAGIC 0xEA1495BA
  42281. #define JX9_LIB_MISUSE (sMPGlobal.nMagic != JX9_LIB_MAGIC)
  42282. /*
  42283. * Supported threading level.
  42284. * These options have meaning only when the library is compiled with multi-threading
  42285. * support.That is, the JX9_ENABLE_THREADS compile time directive must be defined
  42286. * when JX9 is built.
  42287. * JX9_THREAD_LEVEL_SINGLE:
  42288. * In this mode, mutexing is disabled and the library can only be used by a single thread.
  42289. * JX9_THREAD_LEVEL_MULTI
  42290. * In this mode, all mutexes including the recursive mutexes on [jx9] objects
  42291. * are enabled so that the application is free to share the same engine
  42292. * between different threads at the same time.
  42293. */
  42294. #define JX9_THREAD_LEVEL_SINGLE 1
  42295. #define JX9_THREAD_LEVEL_MULTI 2
  42296. /*
  42297. * Configure a running JX9 engine instance.
  42298. * return JX9_OK on success.Any other return
  42299. * value indicates failure.
  42300. * Refer to [jx9_config()].
  42301. */
  42302. static sxi32 EngineConfig(jx9 *pEngine, sxi32 nOp, va_list ap)
  42303. {
  42304. jx9_conf *pConf = &pEngine->xConf;
  42305. int rc = JX9_OK;
  42306. /* Perform the requested operation */
  42307. switch(nOp){
  42308. case JX9_CONFIG_ERR_LOG:{
  42309. /* Extract compile-time error log if any */
  42310. const char **pzPtr = va_arg(ap, const char **);
  42311. int *pLen = va_arg(ap, int *);
  42312. if( pzPtr == 0 ){
  42313. rc = JX9_CORRUPT;
  42314. break;
  42315. }
  42316. /* NULL terminate the error-log buffer */
  42317. SyBlobNullAppend(&pConf->sErrConsumer);
  42318. /* Point to the error-log buffer */
  42319. *pzPtr = (const char *)SyBlobData(&pConf->sErrConsumer);
  42320. if( pLen ){
  42321. if( SyBlobLength(&pConf->sErrConsumer) > 1 /* NULL '\0' terminator */ ){
  42322. *pLen = (int)SyBlobLength(&pConf->sErrConsumer);
  42323. }else{
  42324. *pLen = 0;
  42325. }
  42326. }
  42327. break;
  42328. }
  42329. case JX9_CONFIG_ERR_ABORT:
  42330. /* Reserved for future use */
  42331. break;
  42332. default:
  42333. /* Unknown configuration verb */
  42334. rc = JX9_CORRUPT;
  42335. break;
  42336. } /* Switch() */
  42337. return rc;
  42338. }
  42339. /*
  42340. * Configure the JX9 library.
  42341. * Return JX9_OK on success. Any other return value indicates failure.
  42342. * Refer to [jx9_lib_config()].
  42343. */
  42344. static sxi32 JX9CoreConfigure(sxi32 nOp, va_list ap)
  42345. {
  42346. int rc = JX9_OK;
  42347. switch(nOp){
  42348. case JX9_LIB_CONFIG_VFS:{
  42349. /* Install a virtual file system */
  42350. const jx9_vfs *pVfs = va_arg(ap, const jx9_vfs *);
  42351. sMPGlobal.pVfs = pVfs;
  42352. break;
  42353. }
  42354. case JX9_LIB_CONFIG_USER_MALLOC: {
  42355. /* Use an alternative low-level memory allocation routines */
  42356. const SyMemMethods *pMethods = va_arg(ap, const SyMemMethods *);
  42357. /* Save the memory failure callback (if available) */
  42358. ProcMemError xMemErr = sMPGlobal.sAllocator.xMemError;
  42359. void *pMemErr = sMPGlobal.sAllocator.pUserData;
  42360. if( pMethods == 0 ){
  42361. /* Use the built-in memory allocation subsystem */
  42362. rc = SyMemBackendInit(&sMPGlobal.sAllocator, xMemErr, pMemErr);
  42363. }else{
  42364. rc = SyMemBackendInitFromOthers(&sMPGlobal.sAllocator, pMethods, xMemErr, pMemErr);
  42365. }
  42366. break;
  42367. }
  42368. case JX9_LIB_CONFIG_MEM_ERR_CALLBACK: {
  42369. /* Memory failure callback */
  42370. ProcMemError xMemErr = va_arg(ap, ProcMemError);
  42371. void *pUserData = va_arg(ap, void *);
  42372. sMPGlobal.sAllocator.xMemError = xMemErr;
  42373. sMPGlobal.sAllocator.pUserData = pUserData;
  42374. break;
  42375. }
  42376. case JX9_LIB_CONFIG_USER_MUTEX: {
  42377. #if defined(JX9_ENABLE_THREADS)
  42378. /* Use an alternative low-level mutex subsystem */
  42379. const SyMutexMethods *pMethods = va_arg(ap, const SyMutexMethods *);
  42380. #if defined (UNTRUST)
  42381. if( pMethods == 0 ){
  42382. rc = JX9_CORRUPT;
  42383. }
  42384. #endif
  42385. /* Sanity check */
  42386. if( pMethods->xEnter == 0 || pMethods->xLeave == 0 || pMethods->xNew == 0){
  42387. /* At least three criticial callbacks xEnter(), xLeave() and xNew() must be supplied */
  42388. rc = JX9_CORRUPT;
  42389. break;
  42390. }
  42391. if( sMPGlobal.pMutexMethods ){
  42392. /* Overwrite the previous mutex subsystem */
  42393. SyMutexRelease(sMPGlobal.pMutexMethods, sMPGlobal.pMutex);
  42394. if( sMPGlobal.pMutexMethods->xGlobalRelease ){
  42395. sMPGlobal.pMutexMethods->xGlobalRelease();
  42396. }
  42397. sMPGlobal.pMutex = 0;
  42398. }
  42399. /* Initialize and install the new mutex subsystem */
  42400. if( pMethods->xGlobalInit ){
  42401. rc = pMethods->xGlobalInit();
  42402. if ( rc != JX9_OK ){
  42403. break;
  42404. }
  42405. }
  42406. /* Create the global mutex */
  42407. sMPGlobal.pMutex = pMethods->xNew(SXMUTEX_TYPE_FAST);
  42408. if( sMPGlobal.pMutex == 0 ){
  42409. /*
  42410. * If the supplied mutex subsystem is so sick that we are unable to
  42411. * create a single mutex, there is no much we can do here.
  42412. */
  42413. if( pMethods->xGlobalRelease ){
  42414. pMethods->xGlobalRelease();
  42415. }
  42416. rc = JX9_CORRUPT;
  42417. break;
  42418. }
  42419. sMPGlobal.pMutexMethods = pMethods;
  42420. if( sMPGlobal.nThreadingLevel == 0 ){
  42421. /* Set a default threading level */
  42422. sMPGlobal.nThreadingLevel = JX9_THREAD_LEVEL_MULTI;
  42423. }
  42424. #endif
  42425. break;
  42426. }
  42427. case JX9_LIB_CONFIG_THREAD_LEVEL_SINGLE:
  42428. #if defined(JX9_ENABLE_THREADS)
  42429. /* Single thread mode(Only one thread is allowed to play with the library) */
  42430. sMPGlobal.nThreadingLevel = JX9_THREAD_LEVEL_SINGLE;
  42431. #endif
  42432. break;
  42433. case JX9_LIB_CONFIG_THREAD_LEVEL_MULTI:
  42434. #if defined(JX9_ENABLE_THREADS)
  42435. /* Multi-threading mode (library is thread safe and JX9 engines and virtual machines
  42436. * may be shared between multiple threads).
  42437. */
  42438. sMPGlobal.nThreadingLevel = JX9_THREAD_LEVEL_MULTI;
  42439. #endif
  42440. break;
  42441. default:
  42442. /* Unknown configuration option */
  42443. rc = JX9_CORRUPT;
  42444. break;
  42445. }
  42446. return rc;
  42447. }
  42448. /*
  42449. * [CAPIREF: jx9_lib_config()]
  42450. * Please refer to the official documentation for function purpose and expected parameters.
  42451. */
  42452. int jx9_lib_config(int nConfigOp, ...)
  42453. {
  42454. va_list ap;
  42455. int rc;
  42456. if( sMPGlobal.nMagic == JX9_LIB_MAGIC ){
  42457. /* Library is already initialized, this operation is forbidden */
  42458. return JX9_LOOKED;
  42459. }
  42460. va_start(ap, nConfigOp);
  42461. rc = JX9CoreConfigure(nConfigOp, ap);
  42462. va_end(ap);
  42463. return rc;
  42464. }
  42465. /*
  42466. * Global library initialization
  42467. * Refer to [jx9_lib_init()]
  42468. * This routine must be called to initialize the memory allocation subsystem, the mutex
  42469. * subsystem prior to doing any serious work with the library.The first thread to call
  42470. * this routine does the initialization process and set the magic number so no body later
  42471. * can re-initialize the library.If subsequent threads call this routine before the first
  42472. * thread have finished the initialization process, then the subsequent threads must block
  42473. * until the initialization process is done.
  42474. */
  42475. static sxi32 JX9CoreInitialize(void)
  42476. {
  42477. const jx9_vfs *pVfs; /* Built-in vfs */
  42478. #if defined(JX9_ENABLE_THREADS)
  42479. const SyMutexMethods *pMutexMethods = 0;
  42480. SyMutex *pMaster = 0;
  42481. #endif
  42482. int rc;
  42483. /*
  42484. * If the library is already initialized, then a call to this routine
  42485. * is a no-op.
  42486. */
  42487. if( sMPGlobal.nMagic == JX9_LIB_MAGIC ){
  42488. return JX9_OK; /* Already initialized */
  42489. }
  42490. /* Point to the built-in vfs */
  42491. pVfs = jx9ExportBuiltinVfs();
  42492. /* Install it */
  42493. jx9_lib_config(JX9_LIB_CONFIG_VFS, pVfs);
  42494. #if defined(JX9_ENABLE_THREADS)
  42495. if( sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_SINGLE ){
  42496. pMutexMethods = sMPGlobal.pMutexMethods;
  42497. if( pMutexMethods == 0 ){
  42498. /* Use the built-in mutex subsystem */
  42499. pMutexMethods = SyMutexExportMethods();
  42500. if( pMutexMethods == 0 ){
  42501. return JX9_CORRUPT; /* Can't happen */
  42502. }
  42503. /* Install the mutex subsystem */
  42504. rc = jx9_lib_config(JX9_LIB_CONFIG_USER_MUTEX, pMutexMethods);
  42505. if( rc != JX9_OK ){
  42506. return rc;
  42507. }
  42508. }
  42509. /* Obtain a static mutex so we can initialize the library without calling malloc() */
  42510. pMaster = SyMutexNew(pMutexMethods, SXMUTEX_TYPE_STATIC_1);
  42511. if( pMaster == 0 ){
  42512. return JX9_CORRUPT; /* Can't happen */
  42513. }
  42514. }
  42515. /* Lock the master mutex */
  42516. rc = JX9_OK;
  42517. SyMutexEnter(pMutexMethods, pMaster); /* NO-OP if sMPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
  42518. if( sMPGlobal.nMagic != JX9_LIB_MAGIC ){
  42519. #endif
  42520. if( sMPGlobal.sAllocator.pMethods == 0 ){
  42521. /* Install a memory subsystem */
  42522. rc = jx9_lib_config(JX9_LIB_CONFIG_USER_MALLOC, 0); /* zero mean use the built-in memory backend */
  42523. if( rc != JX9_OK ){
  42524. /* If we are unable to initialize the memory backend, there is no much we can do here.*/
  42525. goto End;
  42526. }
  42527. }
  42528. #if defined(JX9_ENABLE_THREADS)
  42529. if( sMPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE ){
  42530. /* Protect the memory allocation subsystem */
  42531. rc = SyMemBackendMakeThreadSafe(&sMPGlobal.sAllocator, sMPGlobal.pMutexMethods);
  42532. if( rc != JX9_OK ){
  42533. goto End;
  42534. }
  42535. }
  42536. #endif
  42537. /* Our library is initialized, set the magic number */
  42538. sMPGlobal.nMagic = JX9_LIB_MAGIC;
  42539. rc = JX9_OK;
  42540. #if defined(JX9_ENABLE_THREADS)
  42541. } /* sMPGlobal.nMagic != JX9_LIB_MAGIC */
  42542. #endif
  42543. End:
  42544. #if defined(JX9_ENABLE_THREADS)
  42545. /* Unlock the master mutex */
  42546. SyMutexLeave(pMutexMethods, pMaster); /* NO-OP if sMPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
  42547. #endif
  42548. return rc;
  42549. }
  42550. /*
  42551. * [CAPIREF: jx9_lib_init()]
  42552. * Please refer to the official documentation for function purpose and expected parameters.
  42553. */
  42554. int jx9_lib_init(void)
  42555. {
  42556. int rc;
  42557. rc = JX9CoreInitialize();
  42558. return rc;
  42559. }
  42560. /*
  42561. * Release an active JX9 engine and it's associated active virtual machines.
  42562. */
  42563. static sxi32 EngineRelease(jx9 *pEngine)
  42564. {
  42565. jx9_vm *pVm, *pNext;
  42566. /* Release all active VM */
  42567. pVm = pEngine->pVms;
  42568. for(;;){
  42569. if( pEngine->iVm < 1 ){
  42570. break;
  42571. }
  42572. pNext = pVm->pNext;
  42573. jx9VmRelease(pVm);
  42574. pVm = pNext;
  42575. pEngine->iVm--;
  42576. }
  42577. /* Set a dummy magic number */
  42578. pEngine->nMagic = 0x7635;
  42579. /* Release the private memory subsystem */
  42580. SyMemBackendRelease(&pEngine->sAllocator);
  42581. return JX9_OK;
  42582. }
  42583. /*
  42584. * Release all resources consumed by the library.
  42585. * If JX9 is already shut when this routine is invoked then this
  42586. * routine is a harmless no-op.
  42587. * Note: This call is not thread safe. Refer to [jx9_lib_shutdown()].
  42588. */
  42589. static void JX9CoreShutdown(void)
  42590. {
  42591. jx9 *pEngine, *pNext;
  42592. /* Release all active engines first */
  42593. pEngine = sMPGlobal.pEngines;
  42594. for(;;){
  42595. if( sMPGlobal.nEngine < 1 ){
  42596. break;
  42597. }
  42598. pNext = pEngine->pNext;
  42599. EngineRelease(pEngine);
  42600. pEngine = pNext;
  42601. sMPGlobal.nEngine--;
  42602. }
  42603. #if defined(JX9_ENABLE_THREADS)
  42604. /* Release the mutex subsystem */
  42605. if( sMPGlobal.pMutexMethods ){
  42606. if( sMPGlobal.pMutex ){
  42607. SyMutexRelease(sMPGlobal.pMutexMethods, sMPGlobal.pMutex);
  42608. sMPGlobal.pMutex = 0;
  42609. }
  42610. if( sMPGlobal.pMutexMethods->xGlobalRelease ){
  42611. sMPGlobal.pMutexMethods->xGlobalRelease();
  42612. }
  42613. sMPGlobal.pMutexMethods = 0;
  42614. }
  42615. sMPGlobal.nThreadingLevel = 0;
  42616. #endif
  42617. if( sMPGlobal.sAllocator.pMethods ){
  42618. /* Release the memory backend */
  42619. SyMemBackendRelease(&sMPGlobal.sAllocator);
  42620. }
  42621. sMPGlobal.nMagic = 0x1928;
  42622. }
  42623. /*
  42624. * [CAPIREF: jx9_lib_shutdown()]
  42625. * Please refer to the official documentation for function purpose and expected parameters.
  42626. */
  42627. int jx9_lib_shutdown(void)
  42628. {
  42629. if( sMPGlobal.nMagic != JX9_LIB_MAGIC ){
  42630. /* Already shut */
  42631. return JX9_OK;
  42632. }
  42633. JX9CoreShutdown();
  42634. return JX9_OK;
  42635. }
  42636. /*
  42637. * [CAPIREF: jx9_lib_is_threadsafe()]
  42638. * Please refer to the official documentation for function purpose and expected parameters.
  42639. */
  42640. int jx9_lib_is_threadsafe(void)
  42641. {
  42642. if( sMPGlobal.nMagic != JX9_LIB_MAGIC ){
  42643. return 0;
  42644. }
  42645. #if defined(JX9_ENABLE_THREADS)
  42646. if( sMPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE ){
  42647. /* Muli-threading support is enabled */
  42648. return 1;
  42649. }else{
  42650. /* Single-threading */
  42651. return 0;
  42652. }
  42653. #else
  42654. return 0;
  42655. #endif
  42656. }
  42657. /*
  42658. * [CAPIREF: jx9_lib_version()]
  42659. * Please refer to the official documentation for function purpose and expected parameters.
  42660. */
  42661. const char * jx9_lib_version(void)
  42662. {
  42663. return JX9_VERSION;
  42664. }
  42665. /*
  42666. * [CAPIREF: jx9_lib_signature()]
  42667. * Please refer to the official documentation for function purpose and expected parameters.
  42668. */
  42669. const char * jx9_lib_signature(void)
  42670. {
  42671. return JX9_SIG;
  42672. }
  42673. /*
  42674. * [CAPIREF: jx9_lib_ident()]
  42675. * Please refer to the official documentation for function purpose and expected parameters.
  42676. */
  42677. const char * jx9_lib_ident(void)
  42678. {
  42679. return JX9_IDENT;
  42680. }
  42681. /*
  42682. * [CAPIREF: jx9_lib_copyright()]
  42683. * Please refer to the official documentation for function purpose and expected parameters.
  42684. */
  42685. const char * jx9_lib_copyright(void)
  42686. {
  42687. return JX9_COPYRIGHT;
  42688. }
  42689. /*
  42690. * [CAPIREF: jx9_config()]
  42691. * Please refer to the official documentation for function purpose and expected parameters.
  42692. */
  42693. int jx9_config(jx9 *pEngine, int nConfigOp, ...)
  42694. {
  42695. va_list ap;
  42696. int rc;
  42697. if( JX9_ENGINE_MISUSE(pEngine) ){
  42698. return JX9_CORRUPT;
  42699. }
  42700. #if defined(JX9_ENABLE_THREADS)
  42701. /* Acquire engine mutex */
  42702. SyMutexEnter(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  42703. if( sMPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
  42704. JX9_THRD_ENGINE_RELEASE(pEngine) ){
  42705. return JX9_ABORT; /* Another thread have released this instance */
  42706. }
  42707. #endif
  42708. va_start(ap, nConfigOp);
  42709. rc = EngineConfig(&(*pEngine), nConfigOp, ap);
  42710. va_end(ap);
  42711. #if defined(JX9_ENABLE_THREADS)
  42712. /* Leave engine mutex */
  42713. SyMutexLeave(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  42714. #endif
  42715. return rc;
  42716. }
  42717. /*
  42718. * [CAPIREF: jx9_init()]
  42719. * Please refer to the official documentation for function purpose and expected parameters.
  42720. */
  42721. int jx9_init(jx9 **ppEngine)
  42722. {
  42723. jx9 *pEngine;
  42724. int rc;
  42725. #if defined(UNTRUST)
  42726. if( ppEngine == 0 ){
  42727. return JX9_CORRUPT;
  42728. }
  42729. #endif
  42730. *ppEngine = 0;
  42731. /* One-time automatic library initialization */
  42732. rc = JX9CoreInitialize();
  42733. if( rc != JX9_OK ){
  42734. return rc;
  42735. }
  42736. /* Allocate a new engine */
  42737. pEngine = (jx9 *)SyMemBackendPoolAlloc(&sMPGlobal.sAllocator, sizeof(jx9));
  42738. if( pEngine == 0 ){
  42739. return JX9_NOMEM;
  42740. }
  42741. /* Zero the structure */
  42742. SyZero(pEngine, sizeof(jx9));
  42743. /* Initialize engine fields */
  42744. pEngine->nMagic = JX9_ENGINE_MAGIC;
  42745. rc = SyMemBackendInitFromParent(&pEngine->sAllocator, &sMPGlobal.sAllocator);
  42746. if( rc != JX9_OK ){
  42747. goto Release;
  42748. }
  42749. #if defined(JX9_ENABLE_THREADS)
  42750. SyMemBackendDisbaleMutexing(&pEngine->sAllocator);
  42751. #endif
  42752. /* Default configuration */
  42753. SyBlobInit(&pEngine->xConf.sErrConsumer, &pEngine->sAllocator);
  42754. /* Install a default compile-time error consumer routine */
  42755. pEngine->xConf.xErr = jx9VmBlobConsumer;
  42756. pEngine->xConf.pErrData = &pEngine->xConf.sErrConsumer;
  42757. /* Built-in vfs */
  42758. pEngine->pVfs = sMPGlobal.pVfs;
  42759. #if defined(JX9_ENABLE_THREADS)
  42760. if( sMPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE ){
  42761. /* Associate a recursive mutex with this instance */
  42762. pEngine->pMutex = SyMutexNew(sMPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE);
  42763. if( pEngine->pMutex == 0 ){
  42764. rc = JX9_NOMEM;
  42765. goto Release;
  42766. }
  42767. }
  42768. #endif
  42769. /* Link to the list of active engines */
  42770. #if defined(JX9_ENABLE_THREADS)
  42771. /* Enter the global mutex */
  42772. SyMutexEnter(sMPGlobal.pMutexMethods, sMPGlobal.pMutex); /* NO-OP if sMPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
  42773. #endif
  42774. MACRO_LD_PUSH(sMPGlobal.pEngines, pEngine);
  42775. sMPGlobal.nEngine++;
  42776. #if defined(JX9_ENABLE_THREADS)
  42777. /* Leave the global mutex */
  42778. SyMutexLeave(sMPGlobal.pMutexMethods, sMPGlobal.pMutex); /* NO-OP if sMPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
  42779. #endif
  42780. /* Write a pointer to the new instance */
  42781. *ppEngine = pEngine;
  42782. return JX9_OK;
  42783. Release:
  42784. SyMemBackendRelease(&pEngine->sAllocator);
  42785. SyMemBackendPoolFree(&sMPGlobal.sAllocator,pEngine);
  42786. return rc;
  42787. }
  42788. /*
  42789. * [CAPIREF: jx9_release()]
  42790. * Please refer to the official documentation for function purpose and expected parameters.
  42791. */
  42792. int jx9_release(jx9 *pEngine)
  42793. {
  42794. int rc;
  42795. if( JX9_ENGINE_MISUSE(pEngine) ){
  42796. return JX9_CORRUPT;
  42797. }
  42798. #if defined(JX9_ENABLE_THREADS)
  42799. /* Acquire engine mutex */
  42800. SyMutexEnter(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  42801. if( sMPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
  42802. JX9_THRD_ENGINE_RELEASE(pEngine) ){
  42803. return JX9_ABORT; /* Another thread have released this instance */
  42804. }
  42805. #endif
  42806. /* Release the engine */
  42807. rc = EngineRelease(&(*pEngine));
  42808. #if defined(JX9_ENABLE_THREADS)
  42809. /* Leave engine mutex */
  42810. SyMutexLeave(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  42811. /* Release engine mutex */
  42812. SyMutexRelease(sMPGlobal.pMutexMethods, pEngine->pMutex) /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  42813. #endif
  42814. #if defined(JX9_ENABLE_THREADS)
  42815. /* Enter the global mutex */
  42816. SyMutexEnter(sMPGlobal.pMutexMethods, sMPGlobal.pMutex); /* NO-OP if sMPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
  42817. #endif
  42818. /* Unlink from the list of active engines */
  42819. MACRO_LD_REMOVE(sMPGlobal.pEngines, pEngine);
  42820. sMPGlobal.nEngine--;
  42821. #if defined(JX9_ENABLE_THREADS)
  42822. /* Leave the global mutex */
  42823. SyMutexLeave(sMPGlobal.pMutexMethods, sMPGlobal.pMutex); /* NO-OP if sMPGlobal.nThreadingLevel == JX9_THREAD_LEVEL_SINGLE */
  42824. #endif
  42825. /* Release the memory chunk allocated to this engine */
  42826. SyMemBackendPoolFree(&sMPGlobal.sAllocator, pEngine);
  42827. return rc;
  42828. }
  42829. /*
  42830. * Compile a raw JX9 script.
  42831. * To execute a JX9 code, it must first be compiled into a bytecode program using this routine.
  42832. * If something goes wrong [i.e: compile-time error], your error log [i.e: error consumer callback]
  42833. * should display the appropriate error message and this function set ppVm to null and return
  42834. * an error code that is different from JX9_OK. Otherwise when the script is successfully compiled
  42835. * ppVm should hold the JX9 bytecode and it's safe to call [jx9_vm_exec(), jx9_vm_reset(), etc.].
  42836. * This API does not actually evaluate the JX9 code. It merely compile and prepares the JX9 script
  42837. * for evaluation.
  42838. */
  42839. static sxi32 ProcessScript(
  42840. jx9 *pEngine, /* Running JX9 engine */
  42841. jx9_vm **ppVm, /* OUT: A pointer to the virtual machine */
  42842. SyString *pScript, /* Raw JX9 script to compile */
  42843. sxi32 iFlags, /* Compile-time flags */
  42844. const char *zFilePath /* File path if script come from a file. NULL otherwise */
  42845. )
  42846. {
  42847. jx9_vm *pVm;
  42848. int rc;
  42849. /* Allocate a new virtual machine */
  42850. pVm = (jx9_vm *)SyMemBackendPoolAlloc(&pEngine->sAllocator, sizeof(jx9_vm));
  42851. if( pVm == 0 ){
  42852. /* If the supplied memory subsystem is so sick that we are unable to allocate
  42853. * a tiny chunk of memory, there is no much we can do here. */
  42854. if( ppVm ){
  42855. *ppVm = 0;
  42856. }
  42857. return JX9_NOMEM;
  42858. }
  42859. if( iFlags < 0 ){
  42860. /* Default compile-time flags */
  42861. iFlags = 0;
  42862. }
  42863. /* Initialize the Virtual Machine */
  42864. rc = jx9VmInit(pVm, &(*pEngine));
  42865. if( rc != JX9_OK ){
  42866. SyMemBackendPoolFree(&pEngine->sAllocator, pVm);
  42867. if( ppVm ){
  42868. *ppVm = 0;
  42869. }
  42870. return JX9_VM_ERR;
  42871. }
  42872. if( zFilePath ){
  42873. /* Push processed file path */
  42874. jx9VmPushFilePath(pVm, zFilePath, -1, TRUE, 0);
  42875. }
  42876. /* Reset the error message consumer */
  42877. SyBlobReset(&pEngine->xConf.sErrConsumer);
  42878. /* Compile the script */
  42879. jx9CompileScript(pVm, &(*pScript), iFlags);
  42880. if( pVm->sCodeGen.nErr > 0 || pVm == 0){
  42881. sxu32 nErr = pVm->sCodeGen.nErr;
  42882. /* Compilation error or null ppVm pointer, release this VM */
  42883. SyMemBackendRelease(&pVm->sAllocator);
  42884. SyMemBackendPoolFree(&pEngine->sAllocator, pVm);
  42885. if( ppVm ){
  42886. *ppVm = 0;
  42887. }
  42888. return nErr > 0 ? JX9_COMPILE_ERR : JX9_OK;
  42889. }
  42890. /* Prepare the virtual machine for bytecode execution */
  42891. rc = jx9VmMakeReady(pVm);
  42892. if( rc != JX9_OK ){
  42893. goto Release;
  42894. }
  42895. /* Install local import path which is the current directory */
  42896. jx9_vm_config(pVm, JX9_VM_CONFIG_IMPORT_PATH, "./");
  42897. #if defined(JX9_ENABLE_THREADS)
  42898. if( sMPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE ){
  42899. /* Associate a recursive mutex with this instance */
  42900. pVm->pMutex = SyMutexNew(sMPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE);
  42901. if( pVm->pMutex == 0 ){
  42902. goto Release;
  42903. }
  42904. }
  42905. #endif
  42906. /* Script successfully compiled, link to the list of active virtual machines */
  42907. MACRO_LD_PUSH(pEngine->pVms, pVm);
  42908. pEngine->iVm++;
  42909. /* Point to the freshly created VM */
  42910. *ppVm = pVm;
  42911. /* Ready to execute JX9 bytecode */
  42912. return JX9_OK;
  42913. Release:
  42914. SyMemBackendRelease(&pVm->sAllocator);
  42915. SyMemBackendPoolFree(&pEngine->sAllocator, pVm);
  42916. *ppVm = 0;
  42917. return JX9_VM_ERR;
  42918. }
  42919. /*
  42920. * [CAPIREF: jx9_compile()]
  42921. * Please refer to the official documentation for function purpose and expected parameters.
  42922. */
  42923. int jx9_compile(jx9 *pEngine, const char *zSource, int nLen, jx9_vm **ppOutVm)
  42924. {
  42925. SyString sScript;
  42926. int rc;
  42927. if( JX9_ENGINE_MISUSE(pEngine) || zSource == 0 ){
  42928. return JX9_CORRUPT;
  42929. }
  42930. if( nLen < 0 ){
  42931. /* Compute input length automatically */
  42932. nLen = (int)SyStrlen(zSource);
  42933. }
  42934. SyStringInitFromBuf(&sScript, zSource, nLen);
  42935. #if defined(JX9_ENABLE_THREADS)
  42936. /* Acquire engine mutex */
  42937. SyMutexEnter(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  42938. if( sMPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
  42939. JX9_THRD_ENGINE_RELEASE(pEngine) ){
  42940. return JX9_ABORT; /* Another thread have released this instance */
  42941. }
  42942. #endif
  42943. /* Compile the script */
  42944. rc = ProcessScript(&(*pEngine),ppOutVm,&sScript,0,0);
  42945. #if defined(JX9_ENABLE_THREADS)
  42946. /* Leave engine mutex */
  42947. SyMutexLeave(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  42948. #endif
  42949. /* Compilation result */
  42950. return rc;
  42951. }
  42952. /*
  42953. * [CAPIREF: jx9_compile_file()]
  42954. * Please refer to the official documentation for function purpose and expected parameters.
  42955. */
  42956. int jx9_compile_file(jx9 *pEngine, const char *zFilePath, jx9_vm **ppOutVm)
  42957. {
  42958. const jx9_vfs *pVfs;
  42959. int rc;
  42960. if( ppOutVm ){
  42961. *ppOutVm = 0;
  42962. }
  42963. rc = JX9_OK; /* cc warning */
  42964. if( JX9_ENGINE_MISUSE(pEngine) || SX_EMPTY_STR(zFilePath) ){
  42965. return JX9_CORRUPT;
  42966. }
  42967. #if defined(JX9_ENABLE_THREADS)
  42968. /* Acquire engine mutex */
  42969. SyMutexEnter(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  42970. if( sMPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
  42971. JX9_THRD_ENGINE_RELEASE(pEngine) ){
  42972. return JX9_ABORT; /* Another thread have released this instance */
  42973. }
  42974. #endif
  42975. /*
  42976. * Check if the underlying vfs implement the memory map
  42977. * [i.e: mmap() under UNIX/MapViewOfFile() under windows] function.
  42978. */
  42979. pVfs = pEngine->pVfs;
  42980. if( pVfs == 0 || pVfs->xMmap == 0 ){
  42981. /* Memory map routine not implemented */
  42982. rc = JX9_IO_ERR;
  42983. }else{
  42984. void *pMapView = 0; /* cc warning */
  42985. jx9_int64 nSize = 0; /* cc warning */
  42986. SyString sScript;
  42987. /* Try to get a memory view of the whole file */
  42988. rc = pVfs->xMmap(zFilePath, &pMapView, &nSize);
  42989. if( rc != JX9_OK ){
  42990. /* Assume an IO error */
  42991. rc = JX9_IO_ERR;
  42992. }else{
  42993. /* Compile the file */
  42994. SyStringInitFromBuf(&sScript, pMapView, nSize);
  42995. rc = ProcessScript(&(*pEngine), ppOutVm, &sScript,0,zFilePath);
  42996. /* Release the memory view of the whole file */
  42997. if( pVfs->xUnmap ){
  42998. pVfs->xUnmap(pMapView, nSize);
  42999. }
  43000. }
  43001. }
  43002. #if defined(JX9_ENABLE_THREADS)
  43003. /* Leave engine mutex */
  43004. SyMutexLeave(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  43005. #endif
  43006. /* Compilation result */
  43007. return rc;
  43008. }
  43009. /*
  43010. * [CAPIREF: jx9_vm_dump_v2()]
  43011. * Please refer to the official documentation for function purpose and expected parameters.
  43012. */
  43013. int jx9_vm_dump_v2(jx9_vm *pVm, int (*xConsumer)(const void *, unsigned int, void *), void *pUserData)
  43014. {
  43015. int rc;
  43016. /* Ticket 1433-002: NULL VM is harmless operation */
  43017. if ( JX9_VM_MISUSE(pVm) ){
  43018. return JX9_CORRUPT;
  43019. }
  43020. #ifdef UNTRUST
  43021. if( xConsumer == 0 ){
  43022. return JX9_CORRUPT;
  43023. }
  43024. #endif
  43025. /* Dump VM instructions */
  43026. rc = jx9VmDump(&(*pVm), xConsumer, pUserData);
  43027. return rc;
  43028. }
  43029. /*
  43030. * [CAPIREF: jx9_vm_config()]
  43031. * Please refer to the official documentation for function purpose and expected parameters.
  43032. */
  43033. int jx9_vm_config(jx9_vm *pVm, int iConfigOp, ...)
  43034. {
  43035. va_list ap;
  43036. int rc;
  43037. /* Ticket 1433-002: NULL VM is harmless operation */
  43038. if ( JX9_VM_MISUSE(pVm) ){
  43039. return JX9_CORRUPT;
  43040. }
  43041. #if defined(JX9_ENABLE_THREADS)
  43042. /* Acquire VM mutex */
  43043. SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  43044. if( sMPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
  43045. JX9_THRD_VM_RELEASE(pVm) ){
  43046. return JX9_ABORT; /* Another thread have released this instance */
  43047. }
  43048. #endif
  43049. /* Confiugure the virtual machine */
  43050. va_start(ap, iConfigOp);
  43051. rc = jx9VmConfigure(&(*pVm), iConfigOp, ap);
  43052. va_end(ap);
  43053. #if defined(JX9_ENABLE_THREADS)
  43054. /* Leave VM mutex */
  43055. SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  43056. #endif
  43057. return rc;
  43058. }
  43059. /*
  43060. * [CAPIREF: jx9_vm_exec()]
  43061. * Please refer to the official documentation for function purpose and expected parameters.
  43062. */
  43063. int jx9_vm_exec(jx9_vm *pVm, int *pExitStatus)
  43064. {
  43065. int rc;
  43066. /* Ticket 1433-002: NULL VM is harmless operation */
  43067. if ( JX9_VM_MISUSE(pVm) ){
  43068. return JX9_CORRUPT;
  43069. }
  43070. #if defined(JX9_ENABLE_THREADS)
  43071. /* Acquire VM mutex */
  43072. SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  43073. if( sMPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
  43074. JX9_THRD_VM_RELEASE(pVm) ){
  43075. return JX9_ABORT; /* Another thread have released this instance */
  43076. }
  43077. #endif
  43078. /* Execute JX9 bytecode */
  43079. rc = jx9VmByteCodeExec(&(*pVm));
  43080. if( pExitStatus ){
  43081. /* Exit status */
  43082. *pExitStatus = pVm->iExitStatus;
  43083. }
  43084. #if defined(JX9_ENABLE_THREADS)
  43085. /* Leave VM mutex */
  43086. SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  43087. #endif
  43088. /* Execution result */
  43089. return rc;
  43090. }
  43091. /*
  43092. * [CAPIREF: jx9_vm_extract_variable()]
  43093. * Please refer to the official documentation for function purpose and expected parameters.
  43094. */
  43095. jx9_value * jx9_vm_extract_variable(jx9_vm *pVm,const char *zVarname)
  43096. {
  43097. SyString sVariable;
  43098. jx9_value *pValue;
  43099. /* Ticket 1433-002: NULL VM is harmless operation */
  43100. if ( JX9_VM_MISUSE(pVm) || SX_EMPTY_STR(zVarname) ){
  43101. return 0;
  43102. }
  43103. #if defined(JX9_ENABLE_THREADS)
  43104. /* Acquire VM mutex */
  43105. SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  43106. if( sMPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
  43107. JX9_THRD_VM_RELEASE(pVm) ){
  43108. return 0; /* Another thread have released this instance */
  43109. }
  43110. #endif
  43111. /* Extract variable name */
  43112. SyStringInitFromBuf(&sVariable,zVarname,SyStrlen(zVarname));
  43113. pValue = jx9VmExtractVariable(&(*pVm),&sVariable);
  43114. #if defined(JX9_ENABLE_THREADS)
  43115. /* Leave VM mutex */
  43116. SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  43117. #endif
  43118. /* Lookup result */
  43119. return pValue;
  43120. }
  43121. /*
  43122. * [CAPIREF: jx9_vm_reset()]
  43123. * Please refer to the official documentation for function purpose and expected parameters.
  43124. */
  43125. int jx9_vm_reset(jx9_vm *pVm)
  43126. {
  43127. int rc;
  43128. /* Ticket 1433-002: NULL VM is harmless operation */
  43129. if ( JX9_VM_MISUSE(pVm) ){
  43130. return JX9_CORRUPT;
  43131. }
  43132. #if defined(JX9_ENABLE_THREADS)
  43133. /* Acquire VM mutex */
  43134. SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  43135. if( sMPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
  43136. JX9_THRD_VM_RELEASE(pVm) ){
  43137. return JX9_ABORT; /* Another thread have released this instance */
  43138. }
  43139. #endif
  43140. rc = jx9VmReset(&(*pVm));
  43141. #if defined(JX9_ENABLE_THREADS)
  43142. /* Leave VM mutex */
  43143. SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  43144. #endif
  43145. return rc;
  43146. }
  43147. /*
  43148. * [CAPIREF: jx9_vm_release()]
  43149. * Please refer to the official documentation for function purpose and expected parameters.
  43150. */
  43151. int jx9_vm_release(jx9_vm *pVm)
  43152. {
  43153. jx9 *pEngine;
  43154. int rc;
  43155. /* Ticket 1433-002: NULL VM is harmless operation */
  43156. if ( JX9_VM_MISUSE(pVm) ){
  43157. return JX9_CORRUPT;
  43158. }
  43159. #if defined(JX9_ENABLE_THREADS)
  43160. /* Acquire VM mutex */
  43161. SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  43162. if( sMPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
  43163. JX9_THRD_VM_RELEASE(pVm) ){
  43164. return JX9_ABORT; /* Another thread have released this instance */
  43165. }
  43166. #endif
  43167. pEngine = pVm->pEngine;
  43168. rc = jx9VmRelease(&(*pVm));
  43169. #if defined(JX9_ENABLE_THREADS)
  43170. /* Leave VM mutex */
  43171. SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  43172. #endif
  43173. if( rc == JX9_OK ){
  43174. /* Unlink from the list of active VM */
  43175. #if defined(JX9_ENABLE_THREADS)
  43176. /* Acquire engine mutex */
  43177. SyMutexEnter(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  43178. if( sMPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
  43179. JX9_THRD_ENGINE_RELEASE(pEngine) ){
  43180. return JX9_ABORT; /* Another thread have released this instance */
  43181. }
  43182. #endif
  43183. MACRO_LD_REMOVE(pEngine->pVms, pVm);
  43184. pEngine->iVm--;
  43185. /* Release the memory chunk allocated to this VM */
  43186. SyMemBackendPoolFree(&pEngine->sAllocator, pVm);
  43187. #if defined(JX9_ENABLE_THREADS)
  43188. /* Leave engine mutex */
  43189. SyMutexLeave(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  43190. #endif
  43191. }
  43192. return rc;
  43193. }
  43194. /*
  43195. * [CAPIREF: jx9_create_function()]
  43196. * Please refer to the official documentation for function purpose and expected parameters.
  43197. */
  43198. int jx9_create_function(jx9_vm *pVm, const char *zName, int (*xFunc)(jx9_context *, int, jx9_value **), void *pUserData)
  43199. {
  43200. SyString sName;
  43201. int rc;
  43202. /* Ticket 1433-002: NULL VM is harmless operation */
  43203. if ( JX9_VM_MISUSE(pVm) ){
  43204. return JX9_CORRUPT;
  43205. }
  43206. SyStringInitFromBuf(&sName, zName, SyStrlen(zName));
  43207. /* Remove leading and trailing white spaces */
  43208. SyStringFullTrim(&sName);
  43209. /* Ticket 1433-003: NULL values are not allowed */
  43210. if( sName.nByte < 1 || xFunc == 0 ){
  43211. return JX9_CORRUPT;
  43212. }
  43213. #if defined(JX9_ENABLE_THREADS)
  43214. /* Acquire VM mutex */
  43215. SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  43216. if( sMPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
  43217. JX9_THRD_VM_RELEASE(pVm) ){
  43218. return JX9_ABORT; /* Another thread have released this instance */
  43219. }
  43220. #endif
  43221. /* Install the foreign function */
  43222. rc = jx9VmInstallForeignFunction(&(*pVm), &sName, xFunc, pUserData);
  43223. #if defined(JX9_ENABLE_THREADS)
  43224. /* Leave VM mutex */
  43225. SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  43226. #endif
  43227. return rc;
  43228. }
  43229. /*
  43230. * [CAPIREF: jx9_delete_function()]
  43231. * Please refer to the official documentation for function purpose and expected parameters.
  43232. */
  43233. int jx9_delete_function(jx9_vm *pVm, const char *zName)
  43234. {
  43235. jx9_user_func *pFunc = 0;
  43236. int rc;
  43237. /* Ticket 1433-002: NULL VM is harmless operation */
  43238. if ( JX9_VM_MISUSE(pVm) ){
  43239. return JX9_CORRUPT;
  43240. }
  43241. #if defined(JX9_ENABLE_THREADS)
  43242. /* Acquire VM mutex */
  43243. SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  43244. if( sMPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
  43245. JX9_THRD_VM_RELEASE(pVm) ){
  43246. return JX9_ABORT; /* Another thread have released this instance */
  43247. }
  43248. #endif
  43249. /* Perform the deletion */
  43250. rc = SyHashDeleteEntry(&pVm->hHostFunction, (const void *)zName, SyStrlen(zName), (void **)&pFunc);
  43251. if( rc == JX9_OK ){
  43252. /* Release internal fields */
  43253. SySetRelease(&pFunc->aAux);
  43254. SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pFunc->sName));
  43255. SyMemBackendPoolFree(&pVm->sAllocator, pFunc);
  43256. }
  43257. #if defined(JX9_ENABLE_THREADS)
  43258. /* Leave VM mutex */
  43259. SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  43260. #endif
  43261. return rc;
  43262. }
  43263. /*
  43264. * [CAPIREF: jx9_create_constant()]
  43265. * Please refer to the official documentation for function purpose and expected parameters.
  43266. */
  43267. int jx9_create_constant(jx9_vm *pVm, const char *zName, void (*xExpand)(jx9_value *, void *), void *pUserData)
  43268. {
  43269. SyString sName;
  43270. int rc;
  43271. /* Ticket 1433-002: NULL VM is harmless operation */
  43272. if ( JX9_VM_MISUSE(pVm) ){
  43273. return JX9_CORRUPT;
  43274. }
  43275. SyStringInitFromBuf(&sName, zName, SyStrlen(zName));
  43276. /* Remove leading and trailing white spaces */
  43277. SyStringFullTrim(&sName);
  43278. if( sName.nByte < 1 ){
  43279. /* Empty constant name */
  43280. return JX9_CORRUPT;
  43281. }
  43282. /* TICKET 1433-003: NULL pointer harmless operation */
  43283. if( xExpand == 0 ){
  43284. return JX9_CORRUPT;
  43285. }
  43286. #if defined(JX9_ENABLE_THREADS)
  43287. /* Acquire VM mutex */
  43288. SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  43289. if( sMPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
  43290. JX9_THRD_VM_RELEASE(pVm) ){
  43291. return JX9_ABORT; /* Another thread have released this instance */
  43292. }
  43293. #endif
  43294. /* Perform the registration */
  43295. rc = jx9VmRegisterConstant(&(*pVm), &sName, xExpand, pUserData);
  43296. #if defined(JX9_ENABLE_THREADS)
  43297. /* Leave VM mutex */
  43298. SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  43299. #endif
  43300. return rc;
  43301. }
  43302. /*
  43303. * [CAPIREF: jx9_delete_constant()]
  43304. * Please refer to the official documentation for function purpose and expected parameters.
  43305. */
  43306. int jx9_delete_constant(jx9_vm *pVm, const char *zName)
  43307. {
  43308. jx9_constant *pCons;
  43309. int rc;
  43310. /* Ticket 1433-002: NULL VM is harmless operation */
  43311. if ( JX9_VM_MISUSE(pVm) ){
  43312. return JX9_CORRUPT;
  43313. }
  43314. #if defined(JX9_ENABLE_THREADS)
  43315. /* Acquire VM mutex */
  43316. SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  43317. if( sMPGlobal.nThreadingLevel > JX9_THREAD_LEVEL_SINGLE &&
  43318. JX9_THRD_VM_RELEASE(pVm) ){
  43319. return JX9_ABORT; /* Another thread have released this instance */
  43320. }
  43321. #endif
  43322. /* Query the constant hashtable */
  43323. rc = SyHashDeleteEntry(&pVm->hConstant, (const void *)zName, SyStrlen(zName), (void **)&pCons);
  43324. if( rc == JX9_OK ){
  43325. /* Perform the deletion */
  43326. SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pCons->sName));
  43327. SyMemBackendPoolFree(&pVm->sAllocator, pCons);
  43328. }
  43329. #if defined(JX9_ENABLE_THREADS)
  43330. /* Leave VM mutex */
  43331. SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != JX9_THREAD_LEVEL_MULTI */
  43332. #endif
  43333. return rc;
  43334. }
  43335. /*
  43336. * [CAPIREF: jx9_new_scalar()]
  43337. * Please refer to the official documentation for function purpose and expected parameters.
  43338. */
  43339. jx9_value * jx9_new_scalar(jx9_vm *pVm)
  43340. {
  43341. jx9_value *pObj;
  43342. /* Ticket 1433-002: NULL VM is harmless operation */
  43343. if ( JX9_VM_MISUSE(pVm) ){
  43344. return 0;
  43345. }
  43346. /* Allocate a new scalar variable */
  43347. pObj = (jx9_value *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_value));
  43348. if( pObj == 0 ){
  43349. return 0;
  43350. }
  43351. /* Nullify the new scalar */
  43352. jx9MemObjInit(pVm, pObj);
  43353. return pObj;
  43354. }
  43355. /*
  43356. * [CAPIREF: jx9_new_array()]
  43357. * Please refer to the official documentation for function purpose and expected parameters.
  43358. */
  43359. jx9_value * jx9_new_array(jx9_vm *pVm)
  43360. {
  43361. jx9_hashmap *pMap;
  43362. jx9_value *pObj;
  43363. /* Ticket 1433-002: NULL VM is harmless operation */
  43364. if ( JX9_VM_MISUSE(pVm) ){
  43365. return 0;
  43366. }
  43367. /* Create a new hashmap first */
  43368. pMap = jx9NewHashmap(&(*pVm), 0, 0);
  43369. if( pMap == 0 ){
  43370. return 0;
  43371. }
  43372. /* Associate a new jx9_value with this hashmap */
  43373. pObj = (jx9_value *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_value));
  43374. if( pObj == 0 ){
  43375. jx9HashmapRelease(pMap, TRUE);
  43376. return 0;
  43377. }
  43378. jx9MemObjInitFromArray(pVm, pObj, pMap);
  43379. return pObj;
  43380. }
  43381. /*
  43382. * [CAPIREF: jx9_release_value()]
  43383. * Please refer to the official documentation for function purpose and expected parameters.
  43384. */
  43385. int jx9_release_value(jx9_vm *pVm, jx9_value *pValue)
  43386. {
  43387. /* Ticket 1433-002: NULL VM is harmless operation */
  43388. if ( JX9_VM_MISUSE(pVm) ){
  43389. return JX9_CORRUPT;
  43390. }
  43391. if( pValue ){
  43392. /* Release the value */
  43393. jx9MemObjRelease(pValue);
  43394. SyMemBackendPoolFree(&pVm->sAllocator, pValue);
  43395. }
  43396. return JX9_OK;
  43397. }
  43398. /*
  43399. * [CAPIREF: jx9_value_to_int()]
  43400. * Please refer to the official documentation for function purpose and expected parameters.
  43401. */
  43402. int jx9_value_to_int(jx9_value *pValue)
  43403. {
  43404. int rc;
  43405. rc = jx9MemObjToInteger(pValue);
  43406. if( rc != JX9_OK ){
  43407. return 0;
  43408. }
  43409. return (int)pValue->x.iVal;
  43410. }
  43411. /*
  43412. * [CAPIREF: jx9_value_to_bool()]
  43413. * Please refer to the official documentation for function purpose and expected parameters.
  43414. */
  43415. int jx9_value_to_bool(jx9_value *pValue)
  43416. {
  43417. int rc;
  43418. rc = jx9MemObjToBool(pValue);
  43419. if( rc != JX9_OK ){
  43420. return 0;
  43421. }
  43422. return (int)pValue->x.iVal;
  43423. }
  43424. /*
  43425. * [CAPIREF: jx9_value_to_int64()]
  43426. * Please refer to the official documentation for function purpose and expected parameters.
  43427. */
  43428. jx9_int64 jx9_value_to_int64(jx9_value *pValue)
  43429. {
  43430. int rc;
  43431. rc = jx9MemObjToInteger(pValue);
  43432. if( rc != JX9_OK ){
  43433. return 0;
  43434. }
  43435. return pValue->x.iVal;
  43436. }
  43437. /*
  43438. * [CAPIREF: jx9_value_to_double()]
  43439. * Please refer to the official documentation for function purpose and expected parameters.
  43440. */
  43441. double jx9_value_to_double(jx9_value *pValue)
  43442. {
  43443. int rc;
  43444. rc = jx9MemObjToReal(pValue);
  43445. if( rc != JX9_OK ){
  43446. return (double)0;
  43447. }
  43448. return (double)pValue->x.rVal;
  43449. }
  43450. /*
  43451. * [CAPIREF: jx9_value_to_string()]
  43452. * Please refer to the official documentation for function purpose and expected parameters.
  43453. */
  43454. const char * jx9_value_to_string(jx9_value *pValue, int *pLen)
  43455. {
  43456. jx9MemObjToString(pValue);
  43457. if( SyBlobLength(&pValue->sBlob) > 0 ){
  43458. SyBlobNullAppend(&pValue->sBlob);
  43459. if( pLen ){
  43460. *pLen = (int)SyBlobLength(&pValue->sBlob);
  43461. }
  43462. return (const char *)SyBlobData(&pValue->sBlob);
  43463. }else{
  43464. /* Return the empty string */
  43465. if( pLen ){
  43466. *pLen = 0;
  43467. }
  43468. return "";
  43469. }
  43470. }
  43471. /*
  43472. * [CAPIREF: jx9_value_to_resource()]
  43473. * Please refer to the official documentation for function purpose and expected parameters.
  43474. */
  43475. void * jx9_value_to_resource(jx9_value *pValue)
  43476. {
  43477. if( (pValue->iFlags & MEMOBJ_RES) == 0 ){
  43478. /* Not a resource, return NULL */
  43479. return 0;
  43480. }
  43481. return pValue->x.pOther;
  43482. }
  43483. /*
  43484. * [CAPIREF: jx9_value_compare()]
  43485. * Please refer to the official documentation for function purpose and expected parameters.
  43486. */
  43487. int jx9_value_compare(jx9_value *pLeft, jx9_value *pRight, int bStrict)
  43488. {
  43489. int rc;
  43490. if( pLeft == 0 || pRight == 0 ){
  43491. /* TICKET 1433-24: NULL values is harmless operation */
  43492. return 1;
  43493. }
  43494. /* Perform the comparison */
  43495. rc = jx9MemObjCmp(&(*pLeft), &(*pRight), bStrict, 0);
  43496. /* Comparison result */
  43497. return rc;
  43498. }
  43499. /*
  43500. * [CAPIREF: jx9_result_int()]
  43501. * Please refer to the official documentation for function purpose and expected parameters.
  43502. */
  43503. int jx9_result_int(jx9_context *pCtx, int iValue)
  43504. {
  43505. return jx9_value_int(pCtx->pRet, iValue);
  43506. }
  43507. /*
  43508. * [CAPIREF: jx9_result_int64()]
  43509. * Please refer to the official documentation for function purpose and expected parameters.
  43510. */
  43511. int jx9_result_int64(jx9_context *pCtx, jx9_int64 iValue)
  43512. {
  43513. return jx9_value_int64(pCtx->pRet, iValue);
  43514. }
  43515. /*
  43516. * [CAPIREF: jx9_result_bool()]
  43517. * Please refer to the official documentation for function purpose and expected parameters.
  43518. */
  43519. int jx9_result_bool(jx9_context *pCtx, int iBool)
  43520. {
  43521. return jx9_value_bool(pCtx->pRet, iBool);
  43522. }
  43523. /*
  43524. * [CAPIREF: jx9_result_double()]
  43525. * Please refer to the official documentation for function purpose and expected parameters.
  43526. */
  43527. int jx9_result_double(jx9_context *pCtx, double Value)
  43528. {
  43529. return jx9_value_double(pCtx->pRet, Value);
  43530. }
  43531. /*
  43532. * [CAPIREF: jx9_result_null()]
  43533. * Please refer to the official documentation for function purpose and expected parameters.
  43534. */
  43535. int jx9_result_null(jx9_context *pCtx)
  43536. {
  43537. /* Invalidate any prior representation and set the NULL flag */
  43538. jx9MemObjRelease(pCtx->pRet);
  43539. return JX9_OK;
  43540. }
  43541. /*
  43542. * [CAPIREF: jx9_result_string()]
  43543. * Please refer to the official documentation for function purpose and expected parameters.
  43544. */
  43545. int jx9_result_string(jx9_context *pCtx, const char *zString, int nLen)
  43546. {
  43547. return jx9_value_string(pCtx->pRet, zString, nLen);
  43548. }
  43549. /*
  43550. * [CAPIREF: jx9_result_string_format()]
  43551. * Please refer to the official documentation for function purpose and expected parameters.
  43552. */
  43553. int jx9_result_string_format(jx9_context *pCtx, const char *zFormat, ...)
  43554. {
  43555. jx9_value *p;
  43556. va_list ap;
  43557. int rc;
  43558. p = pCtx->pRet;
  43559. if( (p->iFlags & MEMOBJ_STRING) == 0 ){
  43560. /* Invalidate any prior representation */
  43561. jx9MemObjRelease(p);
  43562. MemObjSetType(p, MEMOBJ_STRING);
  43563. }
  43564. /* Format the given string */
  43565. va_start(ap, zFormat);
  43566. rc = SyBlobFormatAp(&p->sBlob, zFormat, ap);
  43567. va_end(ap);
  43568. return rc;
  43569. }
  43570. /*
  43571. * [CAPIREF: jx9_result_value()]
  43572. * Please refer to the official documentation for function purpose and expected parameters.
  43573. */
  43574. int jx9_result_value(jx9_context *pCtx, jx9_value *pValue)
  43575. {
  43576. int rc = JX9_OK;
  43577. if( pValue == 0 ){
  43578. jx9MemObjRelease(pCtx->pRet);
  43579. }else{
  43580. rc = jx9MemObjStore(pValue, pCtx->pRet);
  43581. }
  43582. return rc;
  43583. }
  43584. /*
  43585. * [CAPIREF: jx9_result_resource()]
  43586. * Please refer to the official documentation for function purpose and expected parameters.
  43587. */
  43588. int jx9_result_resource(jx9_context *pCtx, void *pUserData)
  43589. {
  43590. return jx9_value_resource(pCtx->pRet, pUserData);
  43591. }
  43592. /*
  43593. * [CAPIREF: jx9_context_new_scalar()]
  43594. * Please refer to the official documentation for function purpose and expected parameters.
  43595. */
  43596. jx9_value * jx9_context_new_scalar(jx9_context *pCtx)
  43597. {
  43598. jx9_value *pVal;
  43599. pVal = jx9_new_scalar(pCtx->pVm);
  43600. if( pVal ){
  43601. /* Record value address so it can be freed automatically
  43602. * when the calling function returns.
  43603. */
  43604. SySetPut(&pCtx->sVar, (const void *)&pVal);
  43605. }
  43606. return pVal;
  43607. }
  43608. /*
  43609. * [CAPIREF: jx9_context_new_array()]
  43610. * Please refer to the official documentation for function purpose and expected parameters.
  43611. */
  43612. jx9_value * jx9_context_new_array(jx9_context *pCtx)
  43613. {
  43614. jx9_value *pVal;
  43615. pVal = jx9_new_array(pCtx->pVm);
  43616. if( pVal ){
  43617. /* Record value address so it can be freed automatically
  43618. * when the calling function returns.
  43619. */
  43620. SySetPut(&pCtx->sVar, (const void *)&pVal);
  43621. }
  43622. return pVal;
  43623. }
  43624. /*
  43625. * [CAPIREF: jx9_context_release_value()]
  43626. * Please refer to the official documentation for function purpose and expected parameters.
  43627. */
  43628. void jx9_context_release_value(jx9_context *pCtx, jx9_value *pValue)
  43629. {
  43630. jx9VmReleaseContextValue(&(*pCtx), pValue);
  43631. }
  43632. /*
  43633. * [CAPIREF: jx9_context_alloc_chunk()]
  43634. * Please refer to the official documentation for function purpose and expected parameters.
  43635. */
  43636. void * jx9_context_alloc_chunk(jx9_context *pCtx, unsigned int nByte, int ZeroChunk, int AutoRelease)
  43637. {
  43638. void *pChunk;
  43639. pChunk = SyMemBackendAlloc(&pCtx->pVm->sAllocator, nByte);
  43640. if( pChunk ){
  43641. if( ZeroChunk ){
  43642. /* Zero the memory chunk */
  43643. SyZero(pChunk, nByte);
  43644. }
  43645. if( AutoRelease ){
  43646. jx9_aux_data sAux;
  43647. /* Track the chunk so that it can be released automatically
  43648. * upon this context is destroyed.
  43649. */
  43650. sAux.pAuxData = pChunk;
  43651. SySetPut(&pCtx->sChunk, (const void *)&sAux);
  43652. }
  43653. }
  43654. return pChunk;
  43655. }
  43656. /*
  43657. * Check if the given chunk address is registered in the call context
  43658. * chunk container.
  43659. * Return TRUE if registered.FALSE otherwise.
  43660. * Refer to [jx9_context_realloc_chunk(), jx9_context_free_chunk()].
  43661. */
  43662. static jx9_aux_data * ContextFindChunk(jx9_context *pCtx, void *pChunk)
  43663. {
  43664. jx9_aux_data *aAux, *pAux;
  43665. sxu32 n;
  43666. if( SySetUsed(&pCtx->sChunk) < 1 ){
  43667. /* Don't bother processing, the container is empty */
  43668. return 0;
  43669. }
  43670. /* Perform the lookup */
  43671. aAux = (jx9_aux_data *)SySetBasePtr(&pCtx->sChunk);
  43672. for( n = 0; n < SySetUsed(&pCtx->sChunk) ; ++n ){
  43673. pAux = &aAux[n];
  43674. if( pAux->pAuxData == pChunk ){
  43675. /* Chunk found */
  43676. return pAux;
  43677. }
  43678. }
  43679. /* No such allocated chunk */
  43680. return 0;
  43681. }
  43682. /*
  43683. * [CAPIREF: jx9_context_realloc_chunk()]
  43684. * Please refer to the official documentation for function purpose and expected parameters.
  43685. */
  43686. void * jx9_context_realloc_chunk(jx9_context *pCtx, void *pChunk, unsigned int nByte)
  43687. {
  43688. jx9_aux_data *pAux;
  43689. void *pNew;
  43690. pNew = SyMemBackendRealloc(&pCtx->pVm->sAllocator, pChunk, nByte);
  43691. if( pNew ){
  43692. pAux = ContextFindChunk(pCtx, pChunk);
  43693. if( pAux ){
  43694. pAux->pAuxData = pNew;
  43695. }
  43696. }
  43697. return pNew;
  43698. }
  43699. /*
  43700. * [CAPIREF: jx9_context_free_chunk()]
  43701. * Please refer to the official documentation for function purpose and expected parameters.
  43702. */
  43703. void jx9_context_free_chunk(jx9_context *pCtx, void *pChunk)
  43704. {
  43705. jx9_aux_data *pAux;
  43706. if( pChunk == 0 ){
  43707. /* TICKET-1433-93: NULL chunk is a harmless operation */
  43708. return;
  43709. }
  43710. pAux = ContextFindChunk(pCtx, pChunk);
  43711. if( pAux ){
  43712. /* Mark as destroyed */
  43713. pAux->pAuxData = 0;
  43714. }
  43715. SyMemBackendFree(&pCtx->pVm->sAllocator, pChunk);
  43716. }
  43717. /*
  43718. * [CAPIREF: jx9_array_fetch()]
  43719. * Please refer to the official documentation for function purpose and expected parameters.
  43720. */
  43721. jx9_value * jx9_array_fetch(jx9_value *pArray, const char *zKey, int nByte)
  43722. {
  43723. jx9_hashmap_node *pNode;
  43724. jx9_value *pValue;
  43725. jx9_value skey;
  43726. int rc;
  43727. /* Make sure we are dealing with a valid hashmap */
  43728. if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
  43729. return 0;
  43730. }
  43731. if( nByte < 0 ){
  43732. nByte = (int)SyStrlen(zKey);
  43733. }
  43734. /* Convert the key to a jx9_value */
  43735. jx9MemObjInit(pArray->pVm, &skey);
  43736. jx9MemObjStringAppend(&skey, zKey, (sxu32)nByte);
  43737. /* Perform the lookup */
  43738. rc = jx9HashmapLookup((jx9_hashmap *)pArray->x.pOther, &skey, &pNode);
  43739. jx9MemObjRelease(&skey);
  43740. if( rc != JX9_OK ){
  43741. /* No such entry */
  43742. return 0;
  43743. }
  43744. /* Extract the target value */
  43745. pValue = (jx9_value *)SySetAt(&pArray->pVm->aMemObj, pNode->nValIdx);
  43746. return pValue;
  43747. }
  43748. /*
  43749. * [CAPIREF: jx9_array_walk()]
  43750. * Please refer to the official documentation for function purpose and expected parameters.
  43751. */
  43752. int jx9_array_walk(jx9_value *pArray, int (*xWalk)(jx9_value *pValue, jx9_value *, void *), void *pUserData)
  43753. {
  43754. int rc;
  43755. if( xWalk == 0 ){
  43756. return JX9_CORRUPT;
  43757. }
  43758. /* Make sure we are dealing with a valid hashmap */
  43759. if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
  43760. return JX9_CORRUPT;
  43761. }
  43762. /* Start the walk process */
  43763. rc = jx9HashmapWalk((jx9_hashmap *)pArray->x.pOther, xWalk, pUserData);
  43764. return rc != JX9_OK ? JX9_ABORT /* User callback request an operation abort*/ : JX9_OK;
  43765. }
  43766. /*
  43767. * [CAPIREF: jx9_array_add_elem()]
  43768. * Please refer to the official documentation for function purpose and expected parameters.
  43769. */
  43770. int jx9_array_add_elem(jx9_value *pArray, jx9_value *pKey, jx9_value *pValue)
  43771. {
  43772. int rc;
  43773. /* Make sure we are dealing with a valid hashmap */
  43774. if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
  43775. return JX9_CORRUPT;
  43776. }
  43777. /* Perform the insertion */
  43778. rc = jx9HashmapInsert((jx9_hashmap *)pArray->x.pOther, &(*pKey), &(*pValue));
  43779. return rc;
  43780. }
  43781. /*
  43782. * [CAPIREF: jx9_array_add_strkey_elem()]
  43783. * Please refer to the official documentation for function purpose and expected parameters.
  43784. */
  43785. int jx9_array_add_strkey_elem(jx9_value *pArray, const char *zKey, jx9_value *pValue)
  43786. {
  43787. int rc;
  43788. /* Make sure we are dealing with a valid hashmap */
  43789. if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
  43790. return JX9_CORRUPT;
  43791. }
  43792. /* Perform the insertion */
  43793. if( SX_EMPTY_STR(zKey) ){
  43794. /* Empty key, assign an automatic index */
  43795. rc = jx9HashmapInsert((jx9_hashmap *)pArray->x.pOther, 0, &(*pValue));
  43796. }else{
  43797. jx9_value sKey;
  43798. jx9MemObjInitFromString(pArray->pVm, &sKey, 0);
  43799. jx9MemObjStringAppend(&sKey, zKey, (sxu32)SyStrlen(zKey));
  43800. rc = jx9HashmapInsert((jx9_hashmap *)pArray->x.pOther, &sKey, &(*pValue));
  43801. jx9MemObjRelease(&sKey);
  43802. }
  43803. return rc;
  43804. }
  43805. /*
  43806. * [CAPIREF: jx9_array_count()]
  43807. * Please refer to the official documentation for function purpose and expected parameters.
  43808. */
  43809. unsigned int jx9_array_count(jx9_value *pArray)
  43810. {
  43811. jx9_hashmap *pMap;
  43812. /* Make sure we are dealing with a valid hashmap */
  43813. if( (pArray->iFlags & MEMOBJ_HASHMAP) == 0 ){
  43814. return 0;
  43815. }
  43816. /* Point to the internal representation of the hashmap */
  43817. pMap = (jx9_hashmap *)pArray->x.pOther;
  43818. return pMap->nEntry;
  43819. }
  43820. /*
  43821. * [CAPIREF: jx9_context_output()]
  43822. * Please refer to the official documentation for function purpose and expected parameters.
  43823. */
  43824. int jx9_context_output(jx9_context *pCtx, const char *zString, int nLen)
  43825. {
  43826. SyString sData;
  43827. int rc;
  43828. if( nLen < 0 ){
  43829. nLen = (int)SyStrlen(zString);
  43830. }
  43831. SyStringInitFromBuf(&sData, zString, nLen);
  43832. rc = jx9VmOutputConsume(pCtx->pVm, &sData);
  43833. return rc;
  43834. }
  43835. /*
  43836. * [CAPIREF: jx9_context_output_format()]
  43837. * Please refer to the official documentation for function purpose and expected parameters.
  43838. */
  43839. int jx9_context_output_format(jx9_context *pCtx, const char *zFormat, ...)
  43840. {
  43841. va_list ap;
  43842. int rc;
  43843. va_start(ap, zFormat);
  43844. rc = jx9VmOutputConsumeAp(pCtx->pVm, zFormat, ap);
  43845. va_end(ap);
  43846. return rc;
  43847. }
  43848. /*
  43849. * [CAPIREF: jx9_context_throw_error()]
  43850. * Please refer to the official documentation for function purpose and expected parameters.
  43851. */
  43852. int jx9_context_throw_error(jx9_context *pCtx, int iErr, const char *zErr)
  43853. {
  43854. int rc = JX9_OK;
  43855. if( zErr ){
  43856. rc = jx9VmThrowError(pCtx->pVm, &pCtx->pFunc->sName, iErr, zErr);
  43857. }
  43858. return rc;
  43859. }
  43860. /*
  43861. * [CAPIREF: jx9_context_throw_error_format()]
  43862. * Please refer to the official documentation for function purpose and expected parameters.
  43863. */
  43864. int jx9_context_throw_error_format(jx9_context *pCtx, int iErr, const char *zFormat, ...)
  43865. {
  43866. va_list ap;
  43867. int rc;
  43868. if( zFormat == 0){
  43869. return JX9_OK;
  43870. }
  43871. va_start(ap, zFormat);
  43872. rc = jx9VmThrowErrorAp(pCtx->pVm, &pCtx->pFunc->sName, iErr, zFormat, ap);
  43873. va_end(ap);
  43874. return rc;
  43875. }
  43876. /*
  43877. * [CAPIREF: jx9_context_random_num()]
  43878. * Please refer to the official documentation for function purpose and expected parameters.
  43879. */
  43880. unsigned int jx9_context_random_num(jx9_context *pCtx)
  43881. {
  43882. sxu32 n;
  43883. n = jx9VmRandomNum(pCtx->pVm);
  43884. return n;
  43885. }
  43886. /*
  43887. * [CAPIREF: jx9_context_random_string()]
  43888. * Please refer to the official documentation for function purpose and expected parameters.
  43889. */
  43890. int jx9_context_random_string(jx9_context *pCtx, char *zBuf, int nBuflen)
  43891. {
  43892. if( nBuflen < 3 ){
  43893. return JX9_CORRUPT;
  43894. }
  43895. jx9VmRandomString(pCtx->pVm, zBuf, nBuflen);
  43896. return JX9_OK;
  43897. }
  43898. /*
  43899. * [CAPIREF: jx9_context_user_data()]
  43900. * Please refer to the official documentation for function purpose and expected parameters.
  43901. */
  43902. void * jx9_context_user_data(jx9_context *pCtx)
  43903. {
  43904. return pCtx->pFunc->pUserData;
  43905. }
  43906. /*
  43907. * [CAPIREF: jx9_context_push_aux_data()]
  43908. * Please refer to the official documentation for function purpose and expected parameters.
  43909. */
  43910. int jx9_context_push_aux_data(jx9_context *pCtx, void *pUserData)
  43911. {
  43912. jx9_aux_data sAux;
  43913. int rc;
  43914. sAux.pAuxData = pUserData;
  43915. rc = SySetPut(&pCtx->pFunc->aAux, (const void *)&sAux);
  43916. return rc;
  43917. }
  43918. /*
  43919. * [CAPIREF: jx9_context_peek_aux_data()]
  43920. * Please refer to the official documentation for function purpose and expected parameters.
  43921. */
  43922. void * jx9_context_peek_aux_data(jx9_context *pCtx)
  43923. {
  43924. jx9_aux_data *pAux;
  43925. pAux = (jx9_aux_data *)SySetPeek(&pCtx->pFunc->aAux);
  43926. return pAux ? pAux->pAuxData : 0;
  43927. }
  43928. /*
  43929. * [CAPIREF: jx9_context_pop_aux_data()]
  43930. * Please refer to the official documentation for function purpose and expected parameters.
  43931. */
  43932. void * jx9_context_pop_aux_data(jx9_context *pCtx)
  43933. {
  43934. jx9_aux_data *pAux;
  43935. pAux = (jx9_aux_data *)SySetPop(&pCtx->pFunc->aAux);
  43936. return pAux ? pAux->pAuxData : 0;
  43937. }
  43938. /*
  43939. * [CAPIREF: jx9_context_result_buf_length()]
  43940. * Please refer to the official documentation for function purpose and expected parameters.
  43941. */
  43942. unsigned int jx9_context_result_buf_length(jx9_context *pCtx)
  43943. {
  43944. return SyBlobLength(&pCtx->pRet->sBlob);
  43945. }
  43946. /*
  43947. * [CAPIREF: jx9_function_name()]
  43948. * Please refer to the official documentation for function purpose and expected parameters.
  43949. */
  43950. const char * jx9_function_name(jx9_context *pCtx)
  43951. {
  43952. SyString *pName;
  43953. pName = &pCtx->pFunc->sName;
  43954. return pName->zString;
  43955. }
  43956. /*
  43957. * [CAPIREF: jx9_value_int()]
  43958. * Please refer to the official documentation for function purpose and expected parameters.
  43959. */
  43960. int jx9_value_int(jx9_value *pVal, int iValue)
  43961. {
  43962. /* Invalidate any prior representation */
  43963. jx9MemObjRelease(pVal);
  43964. pVal->x.iVal = (jx9_int64)iValue;
  43965. MemObjSetType(pVal, MEMOBJ_INT);
  43966. return JX9_OK;
  43967. }
  43968. /*
  43969. * [CAPIREF: jx9_value_int64()]
  43970. * Please refer to the official documentation for function purpose and expected parameters.
  43971. */
  43972. int jx9_value_int64(jx9_value *pVal, jx9_int64 iValue)
  43973. {
  43974. /* Invalidate any prior representation */
  43975. jx9MemObjRelease(pVal);
  43976. pVal->x.iVal = iValue;
  43977. MemObjSetType(pVal, MEMOBJ_INT);
  43978. return JX9_OK;
  43979. }
  43980. /*
  43981. * [CAPIREF: jx9_value_bool()]
  43982. * Please refer to the official documentation for function purpose and expected parameters.
  43983. */
  43984. int jx9_value_bool(jx9_value *pVal, int iBool)
  43985. {
  43986. /* Invalidate any prior representation */
  43987. jx9MemObjRelease(pVal);
  43988. pVal->x.iVal = iBool ? 1 : 0;
  43989. MemObjSetType(pVal, MEMOBJ_BOOL);
  43990. return JX9_OK;
  43991. }
  43992. /*
  43993. * [CAPIREF: jx9_value_null()]
  43994. * Please refer to the official documentation for function purpose and expected parameters.
  43995. */
  43996. int jx9_value_null(jx9_value *pVal)
  43997. {
  43998. /* Invalidate any prior representation and set the NULL flag */
  43999. jx9MemObjRelease(pVal);
  44000. return JX9_OK;
  44001. }
  44002. /*
  44003. * [CAPIREF: jx9_value_double()]
  44004. * Please refer to the official documentation for function purpose and expected parameters.
  44005. */
  44006. int jx9_value_double(jx9_value *pVal, double Value)
  44007. {
  44008. /* Invalidate any prior representation */
  44009. jx9MemObjRelease(pVal);
  44010. pVal->x.rVal = (jx9_real)Value;
  44011. MemObjSetType(pVal, MEMOBJ_REAL);
  44012. /* Try to get an integer representation also */
  44013. jx9MemObjTryInteger(pVal);
  44014. return JX9_OK;
  44015. }
  44016. /*
  44017. * [CAPIREF: jx9_value_string()]
  44018. * Please refer to the official documentation for function purpose and expected parameters.
  44019. */
  44020. int jx9_value_string(jx9_value *pVal, const char *zString, int nLen)
  44021. {
  44022. if((pVal->iFlags & MEMOBJ_STRING) == 0 ){
  44023. /* Invalidate any prior representation */
  44024. jx9MemObjRelease(pVal);
  44025. MemObjSetType(pVal, MEMOBJ_STRING);
  44026. }
  44027. if( zString ){
  44028. if( nLen < 0 ){
  44029. /* Compute length automatically */
  44030. nLen = (int)SyStrlen(zString);
  44031. }
  44032. SyBlobAppend(&pVal->sBlob, (const void *)zString, (sxu32)nLen);
  44033. }
  44034. return JX9_OK;
  44035. }
  44036. /*
  44037. * [CAPIREF: jx9_value_string_format()]
  44038. * Please refer to the official documentation for function purpose and expected parameters.
  44039. */
  44040. int jx9_value_string_format(jx9_value *pVal, const char *zFormat, ...)
  44041. {
  44042. va_list ap;
  44043. int rc;
  44044. if((pVal->iFlags & MEMOBJ_STRING) == 0 ){
  44045. /* Invalidate any prior representation */
  44046. jx9MemObjRelease(pVal);
  44047. MemObjSetType(pVal, MEMOBJ_STRING);
  44048. }
  44049. va_start(ap, zFormat);
  44050. rc = SyBlobFormatAp(&pVal->sBlob, zFormat, ap);
  44051. va_end(ap);
  44052. return JX9_OK;
  44053. }
  44054. /*
  44055. * [CAPIREF: jx9_value_reset_string_cursor()]
  44056. * Please refer to the official documentation for function purpose and expected parameters.
  44057. */
  44058. int jx9_value_reset_string_cursor(jx9_value *pVal)
  44059. {
  44060. /* Reset the string cursor */
  44061. SyBlobReset(&pVal->sBlob);
  44062. return JX9_OK;
  44063. }
  44064. /*
  44065. * [CAPIREF: jx9_value_resource()]
  44066. * Please refer to the official documentation for function purpose and expected parameters.
  44067. */
  44068. int jx9_value_resource(jx9_value *pVal, void *pUserData)
  44069. {
  44070. /* Invalidate any prior representation */
  44071. jx9MemObjRelease(pVal);
  44072. /* Reflect the new type */
  44073. pVal->x.pOther = pUserData;
  44074. MemObjSetType(pVal, MEMOBJ_RES);
  44075. return JX9_OK;
  44076. }
  44077. /*
  44078. * [CAPIREF: jx9_value_release()]
  44079. * Please refer to the official documentation for function purpose and expected parameters.
  44080. */
  44081. int jx9_value_release(jx9_value *pVal)
  44082. {
  44083. jx9MemObjRelease(pVal);
  44084. return JX9_OK;
  44085. }
  44086. /*
  44087. * [CAPIREF: jx9_value_is_int()]
  44088. * Please refer to the official documentation for function purpose and expected parameters.
  44089. */
  44090. int jx9_value_is_int(jx9_value *pVal)
  44091. {
  44092. return (pVal->iFlags & MEMOBJ_INT) ? TRUE : FALSE;
  44093. }
  44094. /*
  44095. * [CAPIREF: jx9_value_is_float()]
  44096. * Please refer to the official documentation for function purpose and expected parameters.
  44097. */
  44098. int jx9_value_is_float(jx9_value *pVal)
  44099. {
  44100. return (pVal->iFlags & MEMOBJ_REAL) ? TRUE : FALSE;
  44101. }
  44102. /*
  44103. * [CAPIREF: jx9_value_is_bool()]
  44104. * Please refer to the official documentation for function purpose and expected parameters.
  44105. */
  44106. int jx9_value_is_bool(jx9_value *pVal)
  44107. {
  44108. return (pVal->iFlags & MEMOBJ_BOOL) ? TRUE : FALSE;
  44109. }
  44110. /*
  44111. * [CAPIREF: jx9_value_is_string()]
  44112. * Please refer to the official documentation for function purpose and expected parameters.
  44113. */
  44114. int jx9_value_is_string(jx9_value *pVal)
  44115. {
  44116. return (pVal->iFlags & MEMOBJ_STRING) ? TRUE : FALSE;
  44117. }
  44118. /*
  44119. * [CAPIREF: jx9_value_is_null()]
  44120. * Please refer to the official documentation for function purpose and expected parameters.
  44121. */
  44122. int jx9_value_is_null(jx9_value *pVal)
  44123. {
  44124. return (pVal->iFlags & MEMOBJ_NULL) ? TRUE : FALSE;
  44125. }
  44126. /*
  44127. * [CAPIREF: jx9_value_is_numeric()]
  44128. * Please refer to the official documentation for function purpose and expected parameters.
  44129. */
  44130. int jx9_value_is_numeric(jx9_value *pVal)
  44131. {
  44132. int rc;
  44133. rc = jx9MemObjIsNumeric(pVal);
  44134. return rc;
  44135. }
  44136. /*
  44137. * [CAPIREF: jx9_value_is_callable()]
  44138. * Please refer to the official documentation for function purpose and expected parameters.
  44139. */
  44140. int jx9_value_is_callable(jx9_value *pVal)
  44141. {
  44142. int rc;
  44143. rc = jx9VmIsCallable(pVal->pVm, pVal);
  44144. return rc;
  44145. }
  44146. /*
  44147. * [CAPIREF: jx9_value_is_scalar()]
  44148. * Please refer to the official documentation for function purpose and expected parameters.
  44149. */
  44150. int jx9_value_is_scalar(jx9_value *pVal)
  44151. {
  44152. return (pVal->iFlags & MEMOBJ_SCALAR) ? TRUE : FALSE;
  44153. }
  44154. /*
  44155. * [CAPIREF: jx9_value_is_json_array()]
  44156. * Please refer to the official documentation for function purpose and expected parameters.
  44157. */
  44158. int jx9_value_is_json_array(jx9_value *pVal)
  44159. {
  44160. return (pVal->iFlags & MEMOBJ_HASHMAP) ? TRUE : FALSE;
  44161. }
  44162. /*
  44163. * [CAPIREF: jx9_value_is_json_object()]
  44164. * Please refer to the official documentation for function purpose and expected parameters.
  44165. */
  44166. int jx9_value_is_json_object(jx9_value *pVal)
  44167. {
  44168. jx9_hashmap *pMap;
  44169. if( (pVal->iFlags & MEMOBJ_HASHMAP) == 0 ){
  44170. return FALSE;
  44171. }
  44172. pMap = (jx9_hashmap *)pVal->x.pOther;
  44173. if( (pMap->iFlags & HASHMAP_JSON_OBJECT) == 0 ){
  44174. return FALSE;
  44175. }
  44176. return TRUE;
  44177. }
  44178. /*
  44179. * [CAPIREF: jx9_value_is_resource()]
  44180. * Please refer to the official documentation for function purpose and expected parameters.
  44181. */
  44182. int jx9_value_is_resource(jx9_value *pVal)
  44183. {
  44184. return (pVal->iFlags & MEMOBJ_RES) ? TRUE : FALSE;
  44185. }
  44186. /*
  44187. * [CAPIREF: jx9_value_is_empty()]
  44188. * Please refer to the official documentation for function purpose and expected parameters.
  44189. */
  44190. int jx9_value_is_empty(jx9_value *pVal)
  44191. {
  44192. int rc;
  44193. rc = jx9MemObjIsEmpty(pVal);
  44194. return rc;
  44195. }
  44196. /* END-OF-IMPLEMENTATION: jx9@embedded@symisc 34-09-46 */
  44197. /*
  44198. * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
  44199. * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
  44200. * Version 1.7.2
  44201. * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
  44202. * please contact Symisc Systems via:
  44203. * legal@symisc.net
  44204. * licensing@symisc.net
  44205. * contact@symisc.net
  44206. * or visit:
  44207. * http://jx9.symisc.net/
  44208. */
  44209. /*
  44210. * Copyright (C) 2012, 2013 Symisc Systems. All rights reserved.
  44211. *
  44212. * Redistribution and use in source and binary forms, with or without
  44213. * modification, are permitted provided that the following conditions
  44214. * are met:
  44215. * 1. Redistributions of source code must retain the above copyright
  44216. * notice, this list of conditions and the following disclaimer.
  44217. * 2. Redistributions in binary form must reproduce the above copyright
  44218. * notice, this list of conditions and the following disclaimer in the
  44219. * documentation and/or other materials provided with the distribution.
  44220. * 3. Redistributions in any form must be accompanied by information on
  44221. * how to obtain complete source code for the JX9 engine and any
  44222. * accompanying software that uses the JX9 engine software.
  44223. * The source code must either be included in the distribution
  44224. * or be available for no more than the cost of distribution plus
  44225. * a nominal fee, and must be freely redistributable under reasonable
  44226. * conditions. For an executable file, complete source code means
  44227. * the source code for all modules it contains.It does not include
  44228. * source code for modules or files that typically accompany the major
  44229. * components of the operating system on which the executable file runs.
  44230. *
  44231. * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS
  44232. * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  44233. * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
  44234. * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SYMISC SYSTEMS
  44235. * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  44236. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  44237. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
  44238. * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  44239. * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
  44240. * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
  44241. * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  44242. */