PageRenderTime 135ms CodeModel.GetById 36ms RepoModel.GetById 0ms app.codeStats 1ms

/ethica130820_maquette/src/jx9.c

https://github.com/vrx/ethica
C | 14971 lines | 10653 code | 27 blank | 4291 comment | 1726 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(