summaryrefslogtreecommitdiffstats
path: root/common/unqlite/jx9_vfs.c
diff options
context:
space:
mode:
authorAaron Seigo <aseigo@kde.org>2014-12-14 12:00:05 +0100
committerAaron Seigo <aseigo@kde.org>2014-12-14 12:00:05 +0100
commit7cc25005b8c46d1fa783d33def2c6923e8ef8469 (patch)
tree64fa59d17af29838396cf37b912b3babd885e5dd /common/unqlite/jx9_vfs.c
parentbfc32f265e8ad72823db960fed371d72596003b7 (diff)
parenta6ed70495f9f3ecb21c26860dda16aadcdc91c3a (diff)
downloadsink-7cc25005b8c46d1fa783d33def2c6923e8ef8469.tar.gz
sink-7cc25005b8c46d1fa783d33def2c6923e8ef8469.zip
Merge branch 'unqlite'
Diffstat (limited to 'common/unqlite/jx9_vfs.c')
-rw-r--r--common/unqlite/jx9_vfs.c8222
1 files changed, 8222 insertions, 0 deletions
diff --git a/common/unqlite/jx9_vfs.c b/common/unqlite/jx9_vfs.c
new file mode 100644
index 0000000..d6aee2e
--- /dev/null
+++ b/common/unqlite/jx9_vfs.c
@@ -0,0 +1,8222 @@
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 /* $SymiscID: vfs.c v2.1 Ubuntu 2012-12-13 00:013 stable <chm@symisc.net> $ */
14#ifndef JX9_AMALGAMATION
15#include "jx9Int.h"
16#endif
17/*
18 * This file implement a virtual file systems (VFS) for the JX9 engine.
19 */
20/*
21 * Given a string containing the path of a file or directory, this function
22 * return the parent directory's path.
23 */
24JX9_PRIVATE const char * jx9ExtractDirName(const char *zPath, int nByte, int *pLen)
25{
26 const char *zEnd = &zPath[nByte - 1];
27 int c, d;
28 c = d = '/';
29#ifdef __WINNT__
30 d = '\\';
31#endif
32 while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){
33 zEnd--;
34 }
35 *pLen = (int)(zEnd-zPath);
36#ifdef __WINNT__
37 if( (*pLen) == (int)sizeof(char) && zPath[0] == '/' ){
38 /* Normalize path on windows */
39 return "\\";
40 }
41#endif
42 if( zEnd == zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d) ){
43 /* No separator, return "." as the current directory */
44 *pLen = sizeof(char);
45 return ".";
46 }
47 if( (*pLen) == 0 ){
48 *pLen = sizeof(char);
49#ifdef __WINNT__
50 return "\\";
51#else
52 return "/";
53#endif
54 }
55 return zPath;
56}
57/*
58 * Omit the vfs layer implementation from the built if the JX9_DISABLE_BUILTIN_FUNC directive is defined.
59 */
60#ifndef JX9_DISABLE_BUILTIN_FUNC
61/*
62 * bool chdir(string $directory)
63 * Change the current directory.
64 * Parameters
65 * $directory
66 * The new current directory
67 * Return
68 * TRUE on success or FALSE on failure.
69 */
70static int jx9Vfs_chdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
71{
72 const char *zPath;
73 jx9_vfs *pVfs;
74 int rc;
75 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
76 /* Missing/Invalid argument, return FALSE */
77 jx9_result_bool(pCtx, 0);
78 return JX9_OK;
79 }
80 /* Point to the underlying vfs */
81 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
82 if( pVfs == 0 || pVfs->xChdir == 0 ){
83 /* IO routine not implemented, return NULL */
84 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
85 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
86 jx9_function_name(pCtx)
87 );
88 jx9_result_bool(pCtx, 0);
89 return JX9_OK;
90 }
91 /* Point to the desired directory */
92 zPath = jx9_value_to_string(apArg[0], 0);
93 /* Perform the requested operation */
94 rc = pVfs->xChdir(zPath);
95 /* IO return value */
96 jx9_result_bool(pCtx, rc == JX9_OK);
97 return JX9_OK;
98}
99/*
100 * bool chroot(string $directory)
101 * Change the root directory.
102 * Parameters
103 * $directory
104 * The path to change the root directory to
105 * Return
106 * TRUE on success or FALSE on failure.
107 */
108static int jx9Vfs_chroot(jx9_context *pCtx, int nArg, jx9_value **apArg)
109{
110 const char *zPath;
111 jx9_vfs *pVfs;
112 int rc;
113 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
114 /* Missing/Invalid argument, return FALSE */
115 jx9_result_bool(pCtx, 0);
116 return JX9_OK;
117 }
118 /* Point to the underlying vfs */
119 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
120 if( pVfs == 0 || pVfs->xChroot == 0 ){
121 /* IO routine not implemented, return NULL */
122 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
123 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
124 jx9_function_name(pCtx)
125 );
126 jx9_result_bool(pCtx, 0);
127 return JX9_OK;
128 }
129 /* Point to the desired directory */
130 zPath = jx9_value_to_string(apArg[0], 0);
131 /* Perform the requested operation */
132 rc = pVfs->xChroot(zPath);
133 /* IO return value */
134 jx9_result_bool(pCtx, rc == JX9_OK);
135 return JX9_OK;
136}
137/*
138 * string getcwd(void)
139 * Gets the current working directory.
140 * Parameters
141 * None
142 * Return
143 * Returns the current working directory on success, or FALSE on failure.
144 */
145static int jx9Vfs_getcwd(jx9_context *pCtx, int nArg, jx9_value **apArg)
146{
147 jx9_vfs *pVfs;
148 int rc;
149 /* Point to the underlying vfs */
150 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
151 if( pVfs == 0 || pVfs->xGetcwd == 0 ){
152 SXUNUSED(nArg); /* cc warning */
153 SXUNUSED(apArg);
154 /* IO routine not implemented, return NULL */
155 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
156 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
157 jx9_function_name(pCtx)
158 );
159 jx9_result_bool(pCtx, 0);
160 return JX9_OK;
161 }
162 jx9_result_string(pCtx, "", 0);
163 /* Perform the requested operation */
164 rc = pVfs->xGetcwd(pCtx);
165 if( rc != JX9_OK ){
166 /* Error, return FALSE */
167 jx9_result_bool(pCtx, 0);
168 }
169 return JX9_OK;
170}
171/*
172 * bool rmdir(string $directory)
173 * Removes directory.
174 * Parameters
175 * $directory
176 * The path to the directory
177 * Return
178 * TRUE on success or FALSE on failure.
179 */
180static int jx9Vfs_rmdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
181{
182 const char *zPath;
183 jx9_vfs *pVfs;
184 int rc;
185 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
186 /* Missing/Invalid argument, return FALSE */
187 jx9_result_bool(pCtx, 0);
188 return JX9_OK;
189 }
190 /* Point to the underlying vfs */
191 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
192 if( pVfs == 0 || pVfs->xRmdir == 0 ){
193 /* IO routine not implemented, return NULL */
194 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
195 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
196 jx9_function_name(pCtx)
197 );
198 jx9_result_bool(pCtx, 0);
199 return JX9_OK;
200 }
201 /* Point to the desired directory */
202 zPath = jx9_value_to_string(apArg[0], 0);
203 /* Perform the requested operation */
204 rc = pVfs->xRmdir(zPath);
205 /* IO return value */
206 jx9_result_bool(pCtx, rc == JX9_OK);
207 return JX9_OK;
208}
209/*
210 * bool is_dir(string $filename)
211 * Tells whether the given filename is a directory.
212 * Parameters
213 * $filename
214 * Path to the file.
215 * Return
216 * TRUE on success or FALSE on failure.
217 */
218static int jx9Vfs_is_dir(jx9_context *pCtx, int nArg, jx9_value **apArg)
219{
220 const char *zPath;
221 jx9_vfs *pVfs;
222 int rc;
223 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
224 /* Missing/Invalid argument, return FALSE */
225 jx9_result_bool(pCtx, 0);
226 return JX9_OK;
227 }
228 /* Point to the underlying vfs */
229 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
230 if( pVfs == 0 || pVfs->xIsdir == 0 ){
231 /* IO routine not implemented, return NULL */
232 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
233 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
234 jx9_function_name(pCtx)
235 );
236 jx9_result_bool(pCtx, 0);
237 return JX9_OK;
238 }
239 /* Point to the desired directory */
240 zPath = jx9_value_to_string(apArg[0], 0);
241 /* Perform the requested operation */
242 rc = pVfs->xIsdir(zPath);
243 /* IO return value */
244 jx9_result_bool(pCtx, rc == JX9_OK);
245 return JX9_OK;
246}
247/*
248 * bool mkdir(string $pathname[, int $mode = 0777])
249 * Make a directory.
250 * Parameters
251 * $pathname
252 * The directory path.
253 * $mode
254 * The mode is 0777 by default, which means the widest possible access.
255 * Note:
256 * mode is ignored on Windows.
257 * Note that you probably want to specify the mode as an octal number, which means
258 * it should have a leading zero. The mode is also modified by the current umask
259 * which you can change using umask().
260 * Return
261 * TRUE on success or FALSE on failure.
262 */
263static int jx9Vfs_mkdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
264{
265 int iRecursive = 0;
266 const char *zPath;
267 jx9_vfs *pVfs;
268 int iMode, rc;
269 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
270 /* Missing/Invalid argument, return FALSE */
271 jx9_result_bool(pCtx, 0);
272 return JX9_OK;
273 }
274 /* Point to the underlying vfs */
275 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
276 if( pVfs == 0 || pVfs->xMkdir == 0 ){
277 /* IO routine not implemented, return NULL */
278 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
279 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
280 jx9_function_name(pCtx)
281 );
282 jx9_result_bool(pCtx, 0);
283 return JX9_OK;
284 }
285 /* Point to the desired directory */
286 zPath = jx9_value_to_string(apArg[0], 0);
287#ifdef __WINNT__
288 iMode = 0;
289#else
290 /* Assume UNIX */
291 iMode = 0777;
292#endif
293 if( nArg > 1 ){
294 iMode = jx9_value_to_int(apArg[1]);
295 if( nArg > 2 ){
296 iRecursive = jx9_value_to_bool(apArg[2]);
297 }
298 }
299 /* Perform the requested operation */
300 rc = pVfs->xMkdir(zPath, iMode, iRecursive);
301 /* IO return value */
302 jx9_result_bool(pCtx, rc == JX9_OK);
303 return JX9_OK;
304}
305/*
306 * bool rename(string $oldname, string $newname)
307 * Attempts to rename oldname to newname.
308 * Parameters
309 * $oldname
310 * Old name.
311 * $newname
312 * New name.
313 * Return
314 * TRUE on success or FALSE on failure.
315 */
316static int jx9Vfs_rename(jx9_context *pCtx, int nArg, jx9_value **apArg)
317{
318 const char *zOld, *zNew;
319 jx9_vfs *pVfs;
320 int rc;
321 if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
322 /* Missing/Invalid arguments, return FALSE */
323 jx9_result_bool(pCtx, 0);
324 return JX9_OK;
325 }
326 /* Point to the underlying vfs */
327 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
328 if( pVfs == 0 || pVfs->xRename == 0 ){
329 /* IO routine not implemented, return NULL */
330 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
331 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
332 jx9_function_name(pCtx)
333 );
334 jx9_result_bool(pCtx, 0);
335 return JX9_OK;
336 }
337 /* Perform the requested operation */
338 zOld = jx9_value_to_string(apArg[0], 0);
339 zNew = jx9_value_to_string(apArg[1], 0);
340 rc = pVfs->xRename(zOld, zNew);
341 /* IO result */
342 jx9_result_bool(pCtx, rc == JX9_OK );
343 return JX9_OK;
344}
345/*
346 * string realpath(string $path)
347 * Returns canonicalized absolute pathname.
348 * Parameters
349 * $path
350 * Target path.
351 * Return
352 * Canonicalized absolute pathname on success. or FALSE on failure.
353 */
354static int jx9Vfs_realpath(jx9_context *pCtx, int nArg, jx9_value **apArg)
355{
356 const char *zPath;
357 jx9_vfs *pVfs;
358 int rc;
359 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
360 /* Missing/Invalid argument, return FALSE */
361 jx9_result_bool(pCtx, 0);
362 return JX9_OK;
363 }
364 /* Point to the underlying vfs */
365 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
366 if( pVfs == 0 || pVfs->xRealpath == 0 ){
367 /* IO routine not implemented, return NULL */
368 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
369 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
370 jx9_function_name(pCtx)
371 );
372 jx9_result_bool(pCtx, 0);
373 return JX9_OK;
374 }
375 /* Set an empty string untnil the underlying OS interface change that */
376 jx9_result_string(pCtx, "", 0);
377 /* Perform the requested operation */
378 zPath = jx9_value_to_string(apArg[0], 0);
379 rc = pVfs->xRealpath(zPath, pCtx);
380 if( rc != JX9_OK ){
381 jx9_result_bool(pCtx, 0);
382 }
383 return JX9_OK;
384}
385/*
386 * int sleep(int $seconds)
387 * Delays the program execution for the given number of seconds.
388 * Parameters
389 * $seconds
390 * Halt time in seconds.
391 * Return
392 * Zero on success or FALSE on failure.
393 */
394static int jx9Vfs_sleep(jx9_context *pCtx, int nArg, jx9_value **apArg)
395{
396 jx9_vfs *pVfs;
397 int rc, nSleep;
398 if( nArg < 1 || !jx9_value_is_int(apArg[0]) ){
399 /* Missing/Invalid argument, return FALSE */
400 jx9_result_bool(pCtx, 0);
401 return JX9_OK;
402 }
403 /* Point to the underlying vfs */
404 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
405 if( pVfs == 0 || pVfs->xSleep == 0 ){
406 /* IO routine not implemented, return NULL */
407 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
408 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
409 jx9_function_name(pCtx)
410 );
411 jx9_result_bool(pCtx, 0);
412 return JX9_OK;
413 }
414 /* Amount to sleep */
415 nSleep = jx9_value_to_int(apArg[0]);
416 if( nSleep < 0 ){
417 /* Invalid value, return FALSE */
418 jx9_result_bool(pCtx, 0);
419 return JX9_OK;
420 }
421 /* Perform the requested operation (Microseconds) */
422 rc = pVfs->xSleep((unsigned int)(nSleep * SX_USEC_PER_SEC));
423 if( rc != JX9_OK ){
424 /* Return FALSE */
425 jx9_result_bool(pCtx, 0);
426 }else{
427 /* Return zero */
428 jx9_result_int(pCtx, 0);
429 }
430 return JX9_OK;
431}
432/*
433 * void usleep(int $micro_seconds)
434 * Delays program execution for the given number of micro seconds.
435 * Parameters
436 * $micro_seconds
437 * Halt time in micro seconds. A micro second is one millionth of a second.
438 * Return
439 * None.
440 */
441static int jx9Vfs_usleep(jx9_context *pCtx, int nArg, jx9_value **apArg)
442{
443 jx9_vfs *pVfs;
444 int nSleep;
445 if( nArg < 1 || !jx9_value_is_int(apArg[0]) ){
446 /* Missing/Invalid argument, return immediately */
447 return JX9_OK;
448 }
449 /* Point to the underlying vfs */
450 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
451 if( pVfs == 0 || pVfs->xSleep == 0 ){
452 /* IO routine not implemented, return NULL */
453 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
454 "IO routine(%s) not implemented in the underlying VFS",
455 jx9_function_name(pCtx)
456 );
457 return JX9_OK;
458 }
459 /* Amount to sleep */
460 nSleep = jx9_value_to_int(apArg[0]);
461 if( nSleep < 0 ){
462 /* Invalid value, return immediately */
463 return JX9_OK;
464 }
465 /* Perform the requested operation (Microseconds) */
466 pVfs->xSleep((unsigned int)nSleep);
467 return JX9_OK;
468}
469/*
470 * bool unlink (string $filename)
471 * Delete a file.
472 * Parameters
473 * $filename
474 * Path to the file.
475 * Return
476 * TRUE on success or FALSE on failure.
477 */
478static int jx9Vfs_unlink(jx9_context *pCtx, int nArg, jx9_value **apArg)
479{
480 const char *zPath;
481 jx9_vfs *pVfs;
482 int rc;
483 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
484 /* Missing/Invalid argument, return FALSE */
485 jx9_result_bool(pCtx, 0);
486 return JX9_OK;
487 }
488 /* Point to the underlying vfs */
489 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
490 if( pVfs == 0 || pVfs->xUnlink == 0 ){
491 /* IO routine not implemented, return NULL */
492 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
493 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
494 jx9_function_name(pCtx)
495 );
496 jx9_result_bool(pCtx, 0);
497 return JX9_OK;
498 }
499 /* Point to the desired directory */
500 zPath = jx9_value_to_string(apArg[0], 0);
501 /* Perform the requested operation */
502 rc = pVfs->xUnlink(zPath);
503 /* IO return value */
504 jx9_result_bool(pCtx, rc == JX9_OK);
505 return JX9_OK;
506}
507/*
508 * bool chmod(string $filename, int $mode)
509 * Attempts to change the mode of the specified file to that given in mode.
510 * Parameters
511 * $filename
512 * Path to the file.
513 * $mode
514 * Mode (Must be an integer)
515 * Return
516 * TRUE on success or FALSE on failure.
517 */
518static int jx9Vfs_chmod(jx9_context *pCtx, int nArg, jx9_value **apArg)
519{
520 const char *zPath;
521 jx9_vfs *pVfs;
522 int iMode;
523 int rc;
524 if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
525 /* Missing/Invalid argument, return FALSE */
526 jx9_result_bool(pCtx, 0);
527 return JX9_OK;
528 }
529 /* Point to the underlying vfs */
530 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
531 if( pVfs == 0 || pVfs->xChmod == 0 ){
532 /* IO routine not implemented, return NULL */
533 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
534 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
535 jx9_function_name(pCtx)
536 );
537 jx9_result_bool(pCtx, 0);
538 return JX9_OK;
539 }
540 /* Point to the desired directory */
541 zPath = jx9_value_to_string(apArg[0], 0);
542 /* Extract the mode */
543 iMode = jx9_value_to_int(apArg[1]);
544 /* Perform the requested operation */
545 rc = pVfs->xChmod(zPath, iMode);
546 /* IO return value */
547 jx9_result_bool(pCtx, rc == JX9_OK);
548 return JX9_OK;
549}
550/*
551 * bool chown(string $filename, string $user)
552 * Attempts to change the owner of the file filename to user user.
553 * Parameters
554 * $filename
555 * Path to the file.
556 * $user
557 * Username.
558 * Return
559 * TRUE on success or FALSE on failure.
560 */
561static int jx9Vfs_chown(jx9_context *pCtx, int nArg, jx9_value **apArg)
562{
563 const char *zPath, *zUser;
564 jx9_vfs *pVfs;
565 int rc;
566 if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
567 /* Missing/Invalid arguments, return FALSE */
568 jx9_result_bool(pCtx, 0);
569 return JX9_OK;
570 }
571 /* Point to the underlying vfs */
572 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
573 if( pVfs == 0 || pVfs->xChown == 0 ){
574 /* IO routine not implemented, return NULL */
575 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
576 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
577 jx9_function_name(pCtx)
578 );
579 jx9_result_bool(pCtx, 0);
580 return JX9_OK;
581 }
582 /* Point to the desired directory */
583 zPath = jx9_value_to_string(apArg[0], 0);
584 /* Extract the user */
585 zUser = jx9_value_to_string(apArg[1], 0);
586 /* Perform the requested operation */
587 rc = pVfs->xChown(zPath, zUser);
588 /* IO return value */
589 jx9_result_bool(pCtx, rc == JX9_OK);
590 return JX9_OK;
591}
592/*
593 * bool chgrp(string $filename, string $group)
594 * Attempts to change the group of the file filename to group.
595 * Parameters
596 * $filename
597 * Path to the file.
598 * $group
599 * groupname.
600 * Return
601 * TRUE on success or FALSE on failure.
602 */
603static int jx9Vfs_chgrp(jx9_context *pCtx, int nArg, jx9_value **apArg)
604{
605 const char *zPath, *zGroup;
606 jx9_vfs *pVfs;
607 int rc;
608 if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
609 /* Missing/Invalid arguments, return FALSE */
610 jx9_result_bool(pCtx, 0);
611 return JX9_OK;
612 }
613 /* Point to the underlying vfs */
614 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
615 if( pVfs == 0 || pVfs->xChgrp == 0 ){
616 /* IO routine not implemented, return NULL */
617 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
618 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
619 jx9_function_name(pCtx)
620 );
621 jx9_result_bool(pCtx, 0);
622 return JX9_OK;
623 }
624 /* Point to the desired directory */
625 zPath = jx9_value_to_string(apArg[0], 0);
626 /* Extract the user */
627 zGroup = jx9_value_to_string(apArg[1], 0);
628 /* Perform the requested operation */
629 rc = pVfs->xChgrp(zPath, zGroup);
630 /* IO return value */
631 jx9_result_bool(pCtx, rc == JX9_OK);
632 return JX9_OK;
633}
634/*
635 * int64 disk_free_space(string $directory)
636 * Returns available space on filesystem or disk partition.
637 * Parameters
638 * $directory
639 * A directory of the filesystem or disk partition.
640 * Return
641 * Returns the number of available bytes as a 64-bit integer or FALSE on failure.
642 */
643static int jx9Vfs_disk_free_space(jx9_context *pCtx, int nArg, jx9_value **apArg)
644{
645 const char *zPath;
646 jx9_int64 iSize;
647 jx9_vfs *pVfs;
648 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
649 /* Missing/Invalid argument, return FALSE */
650 jx9_result_bool(pCtx, 0);
651 return JX9_OK;
652 }
653 /* Point to the underlying vfs */
654 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
655 if( pVfs == 0 || pVfs->xFreeSpace == 0 ){
656 /* IO routine not implemented, return NULL */
657 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
658 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
659 jx9_function_name(pCtx)
660 );
661 jx9_result_bool(pCtx, 0);
662 return JX9_OK;
663 }
664 /* Point to the desired directory */
665 zPath = jx9_value_to_string(apArg[0], 0);
666 /* Perform the requested operation */
667 iSize = pVfs->xFreeSpace(zPath);
668 /* IO return value */
669 jx9_result_int64(pCtx, iSize);
670 return JX9_OK;
671}
672/*
673 * int64 disk_total_space(string $directory)
674 * Returns the total size of a filesystem or disk partition.
675 * Parameters
676 * $directory
677 * A directory of the filesystem or disk partition.
678 * Return
679 * Returns the number of available bytes as a 64-bit integer or FALSE on failure.
680 */
681static int jx9Vfs_disk_total_space(jx9_context *pCtx, int nArg, jx9_value **apArg)
682{
683 const char *zPath;
684 jx9_int64 iSize;
685 jx9_vfs *pVfs;
686 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
687 /* Missing/Invalid argument, return FALSE */
688 jx9_result_bool(pCtx, 0);
689 return JX9_OK;
690 }
691 /* Point to the underlying vfs */
692 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
693 if( pVfs == 0 || pVfs->xTotalSpace == 0 ){
694 /* IO routine not implemented, return NULL */
695 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
696 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
697 jx9_function_name(pCtx)
698 );
699 jx9_result_bool(pCtx, 0);
700 return JX9_OK;
701 }
702 /* Point to the desired directory */
703 zPath = jx9_value_to_string(apArg[0], 0);
704 /* Perform the requested operation */
705 iSize = pVfs->xTotalSpace(zPath);
706 /* IO return value */
707 jx9_result_int64(pCtx, iSize);
708 return JX9_OK;
709}
710/*
711 * bool file_exists(string $filename)
712 * Checks whether a file or directory exists.
713 * Parameters
714 * $filename
715 * Path to the file.
716 * Return
717 * TRUE on success or FALSE on failure.
718 */
719static int jx9Vfs_file_exists(jx9_context *pCtx, int nArg, jx9_value **apArg)
720{
721 const char *zPath;
722 jx9_vfs *pVfs;
723 int rc;
724 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
725 /* Missing/Invalid argument, return FALSE */
726 jx9_result_bool(pCtx, 0);
727 return JX9_OK;
728 }
729 /* Point to the underlying vfs */
730 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
731 if( pVfs == 0 || pVfs->xFileExists == 0 ){
732 /* IO routine not implemented, return NULL */
733 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
734 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
735 jx9_function_name(pCtx)
736 );
737 jx9_result_bool(pCtx, 0);
738 return JX9_OK;
739 }
740 /* Point to the desired directory */
741 zPath = jx9_value_to_string(apArg[0], 0);
742 /* Perform the requested operation */
743 rc = pVfs->xFileExists(zPath);
744 /* IO return value */
745 jx9_result_bool(pCtx, rc == JX9_OK);
746 return JX9_OK;
747}
748/*
749 * int64 file_size(string $filename)
750 * Gets the size for the given file.
751 * Parameters
752 * $filename
753 * Path to the file.
754 * Return
755 * File size on success or FALSE on failure.
756 */
757static int jx9Vfs_file_size(jx9_context *pCtx, int nArg, jx9_value **apArg)
758{
759 const char *zPath;
760 jx9_int64 iSize;
761 jx9_vfs *pVfs;
762 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
763 /* Missing/Invalid argument, return FALSE */
764 jx9_result_bool(pCtx, 0);
765 return JX9_OK;
766 }
767 /* Point to the underlying vfs */
768 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
769 if( pVfs == 0 || pVfs->xFileSize == 0 ){
770 /* IO routine not implemented, return NULL */
771 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
772 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
773 jx9_function_name(pCtx)
774 );
775 jx9_result_bool(pCtx, 0);
776 return JX9_OK;
777 }
778 /* Point to the desired directory */
779 zPath = jx9_value_to_string(apArg[0], 0);
780 /* Perform the requested operation */
781 iSize = pVfs->xFileSize(zPath);
782 /* IO return value */
783 jx9_result_int64(pCtx, iSize);
784 return JX9_OK;
785}
786/*
787 * int64 fileatime(string $filename)
788 * Gets the last access time of the given file.
789 * Parameters
790 * $filename
791 * Path to the file.
792 * Return
793 * File atime on success or FALSE on failure.
794 */
795static int jx9Vfs_file_atime(jx9_context *pCtx, int nArg, jx9_value **apArg)
796{
797 const char *zPath;
798 jx9_int64 iTime;
799 jx9_vfs *pVfs;
800 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
801 /* Missing/Invalid argument, return FALSE */
802 jx9_result_bool(pCtx, 0);
803 return JX9_OK;
804 }
805 /* Point to the underlying vfs */
806 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
807 if( pVfs == 0 || pVfs->xFileAtime == 0 ){
808 /* IO routine not implemented, return NULL */
809 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
810 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
811 jx9_function_name(pCtx)
812 );
813 jx9_result_bool(pCtx, 0);
814 return JX9_OK;
815 }
816 /* Point to the desired directory */
817 zPath = jx9_value_to_string(apArg[0], 0);
818 /* Perform the requested operation */
819 iTime = pVfs->xFileAtime(zPath);
820 /* IO return value */
821 jx9_result_int64(pCtx, iTime);
822 return JX9_OK;
823}
824/*
825 * int64 filemtime(string $filename)
826 * Gets file modification time.
827 * Parameters
828 * $filename
829 * Path to the file.
830 * Return
831 * File mtime on success or FALSE on failure.
832 */
833static int jx9Vfs_file_mtime(jx9_context *pCtx, int nArg, jx9_value **apArg)
834{
835 const char *zPath;
836 jx9_int64 iTime;
837 jx9_vfs *pVfs;
838 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
839 /* Missing/Invalid argument, return FALSE */
840 jx9_result_bool(pCtx, 0);
841 return JX9_OK;
842 }
843 /* Point to the underlying vfs */
844 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
845 if( pVfs == 0 || pVfs->xFileMtime == 0 ){
846 /* IO routine not implemented, return NULL */
847 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
848 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
849 jx9_function_name(pCtx)
850 );
851 jx9_result_bool(pCtx, 0);
852 return JX9_OK;
853 }
854 /* Point to the desired directory */
855 zPath = jx9_value_to_string(apArg[0], 0);
856 /* Perform the requested operation */
857 iTime = pVfs->xFileMtime(zPath);
858 /* IO return value */
859 jx9_result_int64(pCtx, iTime);
860 return JX9_OK;
861}
862/*
863 * int64 filectime(string $filename)
864 * Gets inode change time of file.
865 * Parameters
866 * $filename
867 * Path to the file.
868 * Return
869 * File ctime on success or FALSE on failure.
870 */
871static int jx9Vfs_file_ctime(jx9_context *pCtx, int nArg, jx9_value **apArg)
872{
873 const char *zPath;
874 jx9_int64 iTime;
875 jx9_vfs *pVfs;
876 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
877 /* Missing/Invalid argument, return FALSE */
878 jx9_result_bool(pCtx, 0);
879 return JX9_OK;
880 }
881 /* Point to the underlying vfs */
882 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
883 if( pVfs == 0 || pVfs->xFileCtime == 0 ){
884 /* IO routine not implemented, return NULL */
885 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
886 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
887 jx9_function_name(pCtx)
888 );
889 jx9_result_bool(pCtx, 0);
890 return JX9_OK;
891 }
892 /* Point to the desired directory */
893 zPath = jx9_value_to_string(apArg[0], 0);
894 /* Perform the requested operation */
895 iTime = pVfs->xFileCtime(zPath);
896 /* IO return value */
897 jx9_result_int64(pCtx, iTime);
898 return JX9_OK;
899}
900/*
901 * bool is_file(string $filename)
902 * Tells whether the filename is a regular file.
903 * Parameters
904 * $filename
905 * Path to the file.
906 * Return
907 * TRUE on success or FALSE on failure.
908 */
909static int jx9Vfs_is_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
910{
911 const char *zPath;
912 jx9_vfs *pVfs;
913 int rc;
914 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
915 /* Missing/Invalid argument, return FALSE */
916 jx9_result_bool(pCtx, 0);
917 return JX9_OK;
918 }
919 /* Point to the underlying vfs */
920 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
921 if( pVfs == 0 || pVfs->xIsfile == 0 ){
922 /* IO routine not implemented, return NULL */
923 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
924 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
925 jx9_function_name(pCtx)
926 );
927 jx9_result_bool(pCtx, 0);
928 return JX9_OK;
929 }
930 /* Point to the desired directory */
931 zPath = jx9_value_to_string(apArg[0], 0);
932 /* Perform the requested operation */
933 rc = pVfs->xIsfile(zPath);
934 /* IO return value */
935 jx9_result_bool(pCtx, rc == JX9_OK);
936 return JX9_OK;
937}
938/*
939 * bool is_link(string $filename)
940 * Tells whether the filename is a symbolic link.
941 * Parameters
942 * $filename
943 * Path to the file.
944 * Return
945 * TRUE on success or FALSE on failure.
946 */
947static int jx9Vfs_is_link(jx9_context *pCtx, int nArg, jx9_value **apArg)
948{
949 const char *zPath;
950 jx9_vfs *pVfs;
951 int rc;
952 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
953 /* Missing/Invalid argument, return FALSE */
954 jx9_result_bool(pCtx, 0);
955 return JX9_OK;
956 }
957 /* Point to the underlying vfs */
958 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
959 if( pVfs == 0 || pVfs->xIslink == 0 ){
960 /* IO routine not implemented, return NULL */
961 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
962 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
963 jx9_function_name(pCtx)
964 );
965 jx9_result_bool(pCtx, 0);
966 return JX9_OK;
967 }
968 /* Point to the desired directory */
969 zPath = jx9_value_to_string(apArg[0], 0);
970 /* Perform the requested operation */
971 rc = pVfs->xIslink(zPath);
972 /* IO return value */
973 jx9_result_bool(pCtx, rc == JX9_OK);
974 return JX9_OK;
975}
976/*
977 * bool is_readable(string $filename)
978 * Tells whether a file exists and is readable.
979 * Parameters
980 * $filename
981 * Path to the file.
982 * Return
983 * TRUE on success or FALSE on failure.
984 */
985static int jx9Vfs_is_readable(jx9_context *pCtx, int nArg, jx9_value **apArg)
986{
987 const char *zPath;
988 jx9_vfs *pVfs;
989 int rc;
990 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
991 /* Missing/Invalid argument, return FALSE */
992 jx9_result_bool(pCtx, 0);
993 return JX9_OK;
994 }
995 /* Point to the underlying vfs */
996 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
997 if( pVfs == 0 || pVfs->xReadable == 0 ){
998 /* IO routine not implemented, return NULL */
999 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
1000 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
1001 jx9_function_name(pCtx)
1002 );
1003 jx9_result_bool(pCtx, 0);
1004 return JX9_OK;
1005 }
1006 /* Point to the desired directory */
1007 zPath = jx9_value_to_string(apArg[0], 0);
1008 /* Perform the requested operation */
1009 rc = pVfs->xReadable(zPath);
1010 /* IO return value */
1011 jx9_result_bool(pCtx, rc == JX9_OK);
1012 return JX9_OK;
1013}
1014/*
1015 * bool is_writable(string $filename)
1016 * Tells whether the filename is writable.
1017 * Parameters
1018 * $filename
1019 * Path to the file.
1020 * Return
1021 * TRUE on success or FALSE on failure.
1022 */
1023static int jx9Vfs_is_writable(jx9_context *pCtx, int nArg, jx9_value **apArg)
1024{
1025 const char *zPath;
1026 jx9_vfs *pVfs;
1027 int rc;
1028 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
1029 /* Missing/Invalid argument, return FALSE */
1030 jx9_result_bool(pCtx, 0);
1031 return JX9_OK;
1032 }
1033 /* Point to the underlying vfs */
1034 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
1035 if( pVfs == 0 || pVfs->xWritable == 0 ){
1036 /* IO routine not implemented, return NULL */
1037 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
1038 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
1039 jx9_function_name(pCtx)
1040 );
1041 jx9_result_bool(pCtx, 0);
1042 return JX9_OK;
1043 }
1044 /* Point to the desired directory */
1045 zPath = jx9_value_to_string(apArg[0], 0);
1046 /* Perform the requested operation */
1047 rc = pVfs->xWritable(zPath);
1048 /* IO return value */
1049 jx9_result_bool(pCtx, rc == JX9_OK);
1050 return JX9_OK;
1051}
1052/*
1053 * bool is_executable(string $filename)
1054 * Tells whether the filename is executable.
1055 * Parameters
1056 * $filename
1057 * Path to the file.
1058 * Return
1059 * TRUE on success or FALSE on failure.
1060 */
1061static int jx9Vfs_is_executable(jx9_context *pCtx, int nArg, jx9_value **apArg)
1062{
1063 const char *zPath;
1064 jx9_vfs *pVfs;
1065 int rc;
1066 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
1067 /* Missing/Invalid argument, return FALSE */
1068 jx9_result_bool(pCtx, 0);
1069 return JX9_OK;
1070 }
1071 /* Point to the underlying vfs */
1072 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
1073 if( pVfs == 0 || pVfs->xExecutable == 0 ){
1074 /* IO routine not implemented, return NULL */
1075 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
1076 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
1077 jx9_function_name(pCtx)
1078 );
1079 jx9_result_bool(pCtx, 0);
1080 return JX9_OK;
1081 }
1082 /* Point to the desired directory */
1083 zPath = jx9_value_to_string(apArg[0], 0);
1084 /* Perform the requested operation */
1085 rc = pVfs->xExecutable(zPath);
1086 /* IO return value */
1087 jx9_result_bool(pCtx, rc == JX9_OK);
1088 return JX9_OK;
1089}
1090/*
1091 * string filetype(string $filename)
1092 * Gets file type.
1093 * Parameters
1094 * $filename
1095 * Path to the file.
1096 * Return
1097 * The type of the file. Possible values are fifo, char, dir, block, link
1098 * file, socket and unknown.
1099 */
1100static int jx9Vfs_filetype(jx9_context *pCtx, int nArg, jx9_value **apArg)
1101{
1102 const char *zPath;
1103 jx9_vfs *pVfs;
1104 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
1105 /* Missing/Invalid argument, return 'unknown' */
1106 jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
1107 return JX9_OK;
1108 }
1109 /* Point to the underlying vfs */
1110 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
1111 if( pVfs == 0 || pVfs->xFiletype == 0 ){
1112 /* IO routine not implemented, return NULL */
1113 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
1114 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
1115 jx9_function_name(pCtx)
1116 );
1117 jx9_result_bool(pCtx, 0);
1118 return JX9_OK;
1119 }
1120 /* Point to the desired directory */
1121 zPath = jx9_value_to_string(apArg[0], 0);
1122 /* Set the empty string as the default return value */
1123 jx9_result_string(pCtx, "", 0);
1124 /* Perform the requested operation */
1125 pVfs->xFiletype(zPath, pCtx);
1126 return JX9_OK;
1127}
1128/*
1129 * array stat(string $filename)
1130 * Gives information about a file.
1131 * Parameters
1132 * $filename
1133 * Path to the file.
1134 * Return
1135 * An associative array on success holding the following entries on success
1136 * 0 dev device number
1137 * 1 ino inode number (zero on windows)
1138 * 2 mode inode protection mode
1139 * 3 nlink number of links
1140 * 4 uid userid of owner (zero on windows)
1141 * 5 gid groupid of owner (zero on windows)
1142 * 6 rdev device type, if inode device
1143 * 7 size size in bytes
1144 * 8 atime time of last access (Unix timestamp)
1145 * 9 mtime time of last modification (Unix timestamp)
1146 * 10 ctime time of last inode change (Unix timestamp)
1147 * 11 blksize blocksize of filesystem IO (zero on windows)
1148 * 12 blocks number of 512-byte blocks allocated.
1149 * Note:
1150 * FALSE is returned on failure.
1151 */
1152static int jx9Vfs_stat(jx9_context *pCtx, int nArg, jx9_value **apArg)
1153{
1154 jx9_value *pArray, *pValue;
1155 const char *zPath;
1156 jx9_vfs *pVfs;
1157 int rc;
1158 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
1159 /* Missing/Invalid argument, return FALSE */
1160 jx9_result_bool(pCtx, 0);
1161 return JX9_OK;
1162 }
1163 /* Point to the underlying vfs */
1164 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
1165 if( pVfs == 0 || pVfs->xStat == 0 ){
1166 /* IO routine not implemented, return NULL */
1167 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
1168 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
1169 jx9_function_name(pCtx)
1170 );
1171 jx9_result_bool(pCtx, 0);
1172 return JX9_OK;
1173 }
1174 /* Create the array and the working value */
1175 pArray = jx9_context_new_array(pCtx);
1176 pValue = jx9_context_new_scalar(pCtx);
1177 if( pArray == 0 || pValue == 0 ){
1178 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
1179 jx9_result_bool(pCtx, 0);
1180 return JX9_OK;
1181 }
1182 /* Extract the file path */
1183 zPath = jx9_value_to_string(apArg[0], 0);
1184 /* Perform the requested operation */
1185 rc = pVfs->xStat(zPath, pArray, pValue);
1186 if( rc != JX9_OK ){
1187 /* IO error, return FALSE */
1188 jx9_result_bool(pCtx, 0);
1189 }else{
1190 /* Return the associative array */
1191 jx9_result_value(pCtx, pArray);
1192 }
1193 /* Don't worry about freeing memory here, everything will be released
1194 * automatically as soon we return from this function. */
1195 return JX9_OK;
1196}
1197/*
1198 * array lstat(string $filename)
1199 * Gives information about a file or symbolic link.
1200 * Parameters
1201 * $filename
1202 * Path to the file.
1203 * Return
1204 * An associative array on success holding the following entries on success
1205 * 0 dev device number
1206 * 1 ino inode number (zero on windows)
1207 * 2 mode inode protection mode
1208 * 3 nlink number of links
1209 * 4 uid userid of owner (zero on windows)
1210 * 5 gid groupid of owner (zero on windows)
1211 * 6 rdev device type, if inode device
1212 * 7 size size in bytes
1213 * 8 atime time of last access (Unix timestamp)
1214 * 9 mtime time of last modification (Unix timestamp)
1215 * 10 ctime time of last inode change (Unix timestamp)
1216 * 11 blksize blocksize of filesystem IO (zero on windows)
1217 * 12 blocks number of 512-byte blocks allocated.
1218 * Note:
1219 * FALSE is returned on failure.
1220 */
1221static int jx9Vfs_lstat(jx9_context *pCtx, int nArg, jx9_value **apArg)
1222{
1223 jx9_value *pArray, *pValue;
1224 const char *zPath;
1225 jx9_vfs *pVfs;
1226 int rc;
1227 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
1228 /* Missing/Invalid argument, return FALSE */
1229 jx9_result_bool(pCtx, 0);
1230 return JX9_OK;
1231 }
1232 /* Point to the underlying vfs */
1233 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
1234 if( pVfs == 0 || pVfs->xlStat == 0 ){
1235 /* IO routine not implemented, return NULL */
1236 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
1237 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
1238 jx9_function_name(pCtx)
1239 );
1240 jx9_result_bool(pCtx, 0);
1241 return JX9_OK;
1242 }
1243 /* Create the array and the working value */
1244 pArray = jx9_context_new_array(pCtx);
1245 pValue = jx9_context_new_scalar(pCtx);
1246 if( pArray == 0 || pValue == 0 ){
1247 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
1248 jx9_result_bool(pCtx, 0);
1249 return JX9_OK;
1250 }
1251 /* Extract the file path */
1252 zPath = jx9_value_to_string(apArg[0], 0);
1253 /* Perform the requested operation */
1254 rc = pVfs->xlStat(zPath, pArray, pValue);
1255 if( rc != JX9_OK ){
1256 /* IO error, return FALSE */
1257 jx9_result_bool(pCtx, 0);
1258 }else{
1259 /* Return the associative array */
1260 jx9_result_value(pCtx, pArray);
1261 }
1262 /* Don't worry about freeing memory here, everything will be released
1263 * automatically as soon we return from this function. */
1264 return JX9_OK;
1265}
1266/*
1267 * string getenv(string $varname)
1268 * Gets the value of an environment variable.
1269 * Parameters
1270 * $varname
1271 * The variable name.
1272 * Return
1273 * Returns the value of the environment variable varname, or FALSE if the environment
1274 * variable varname does not exist.
1275 */
1276static int jx9Vfs_getenv(jx9_context *pCtx, int nArg, jx9_value **apArg)
1277{
1278 const char *zEnv;
1279 jx9_vfs *pVfs;
1280 int iLen;
1281 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
1282 /* Missing/Invalid argument, return FALSE */
1283 jx9_result_bool(pCtx, 0);
1284 return JX9_OK;
1285 }
1286 /* Point to the underlying vfs */
1287 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
1288 if( pVfs == 0 || pVfs->xGetenv == 0 ){
1289 /* IO routine not implemented, return NULL */
1290 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
1291 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
1292 jx9_function_name(pCtx)
1293 );
1294 jx9_result_bool(pCtx, 0);
1295 return JX9_OK;
1296 }
1297 /* Extract the environment variable */
1298 zEnv = jx9_value_to_string(apArg[0], &iLen);
1299 /* Set a boolean FALSE as the default return value */
1300 jx9_result_bool(pCtx, 0);
1301 if( iLen < 1 ){
1302 /* Empty string */
1303 return JX9_OK;
1304 }
1305 /* Perform the requested operation */
1306 pVfs->xGetenv(zEnv, pCtx);
1307 return JX9_OK;
1308}
1309/*
1310 * bool putenv(string $settings)
1311 * Set the value of an environment variable.
1312 * Parameters
1313 * $setting
1314 * The setting, like "FOO=BAR"
1315 * Return
1316 * TRUE on success or FALSE on failure.
1317 */
1318static int jx9Vfs_putenv(jx9_context *pCtx, int nArg, jx9_value **apArg)
1319{
1320 const char *zName, *zValue;
1321 char *zSettings, *zEnd;
1322 jx9_vfs *pVfs;
1323 int iLen, rc;
1324 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
1325 /* Missing/Invalid argument, return FALSE */
1326 jx9_result_bool(pCtx, 0);
1327 return JX9_OK;
1328 }
1329 /* Extract the setting variable */
1330 zSettings = (char *)jx9_value_to_string(apArg[0], &iLen);
1331 if( iLen < 1 ){
1332 /* Empty string, return FALSE */
1333 jx9_result_bool(pCtx, 0);
1334 return JX9_OK;
1335 }
1336 /* Parse the setting */
1337 zEnd = &zSettings[iLen];
1338 zValue = 0;
1339 zName = zSettings;
1340 while( zSettings < zEnd ){
1341 if( zSettings[0] == '=' ){
1342 /* Null terminate the name */
1343 zSettings[0] = 0;
1344 zValue = &zSettings[1];
1345 break;
1346 }
1347 zSettings++;
1348 }
1349 /* Install the environment variable in the $_Env array */
1350 if( zValue == 0 || zName[0] == 0 || zValue >= zEnd || zName >= zValue ){
1351 /* Invalid settings, retun FALSE */
1352 jx9_result_bool(pCtx, 0);
1353 if( zSettings < zEnd ){
1354 zSettings[0] = '=';
1355 }
1356 return JX9_OK;
1357 }
1358 jx9_vm_config(pCtx->pVm, JX9_VM_CONFIG_ENV_ATTR, zName, zValue, (int)(zEnd-zValue));
1359 /* Point to the underlying vfs */
1360 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
1361 if( pVfs == 0 || pVfs->xSetenv == 0 ){
1362 /* IO routine not implemented, return NULL */
1363 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
1364 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
1365 jx9_function_name(pCtx)
1366 );
1367 jx9_result_bool(pCtx, 0);
1368 zSettings[0] = '=';
1369 return JX9_OK;
1370 }
1371 /* Perform the requested operation */
1372 rc = pVfs->xSetenv(zName, zValue);
1373 jx9_result_bool(pCtx, rc == JX9_OK );
1374 zSettings[0] = '=';
1375 return JX9_OK;
1376}
1377/*
1378 * bool touch(string $filename[, int64 $time = time()[, int64 $atime]])
1379 * Sets access and modification time of file.
1380 * Note: On windows
1381 * If the file does not exists, it will not be created.
1382 * Parameters
1383 * $filename
1384 * The name of the file being touched.
1385 * $time
1386 * The touch time. If time is not supplied, the current system time is used.
1387 * $atime
1388 * If present, the access time of the given filename is set to the value of atime.
1389 * Otherwise, it is set to the value passed to the time parameter. If neither are
1390 * present, the current system time is used.
1391 * Return
1392 * TRUE on success or FALSE on failure.
1393*/
1394static int jx9Vfs_touch(jx9_context *pCtx, int nArg, jx9_value **apArg)
1395{
1396 jx9_int64 nTime, nAccess;
1397 const char *zFile;
1398 jx9_vfs *pVfs;
1399 int rc;
1400 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
1401 /* Missing/Invalid argument, return FALSE */
1402 jx9_result_bool(pCtx, 0);
1403 return JX9_OK;
1404 }
1405 /* Point to the underlying vfs */
1406 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
1407 if( pVfs == 0 || pVfs->xTouch == 0 ){
1408 /* IO routine not implemented, return NULL */
1409 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
1410 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
1411 jx9_function_name(pCtx)
1412 );
1413 jx9_result_bool(pCtx, 0);
1414 return JX9_OK;
1415 }
1416 /* Perform the requested operation */
1417 nTime = nAccess = -1;
1418 zFile = jx9_value_to_string(apArg[0], 0);
1419 if( nArg > 1 ){
1420 nTime = jx9_value_to_int64(apArg[1]);
1421 if( nArg > 2 ){
1422 nAccess = jx9_value_to_int64(apArg[1]);
1423 }else{
1424 nAccess = nTime;
1425 }
1426 }
1427 rc = pVfs->xTouch(zFile, nTime, nAccess);
1428 /* IO result */
1429 jx9_result_bool(pCtx, rc == JX9_OK);
1430 return JX9_OK;
1431}
1432/*
1433 * Path processing functions that do not need access to the VFS layer
1434 * Authors:
1435 * Symisc Systems, devel@symisc.net.
1436 * Copyright (C) Symisc Systems, http://jx9.symisc.net
1437 * Status:
1438 * Stable.
1439 */
1440/*
1441 * string dirname(string $path)
1442 * Returns parent directory's path.
1443 * Parameters
1444 * $path
1445 * Target path.
1446 * On Windows, both slash (/) and backslash (\) are used as directory separator character.
1447 * In other environments, it is the forward slash (/).
1448 * Return
1449 * The path of the parent directory. If there are no slashes in path, a dot ('.')
1450 * is returned, indicating the current directory.
1451 */
1452static int jx9Builtin_dirname(jx9_context *pCtx, int nArg, jx9_value **apArg)
1453{
1454 const char *zPath, *zDir;
1455 int iLen, iDirlen;
1456 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
1457 /* Missing/Invalid arguments, return the empty string */
1458 jx9_result_string(pCtx, "", 0);
1459 return JX9_OK;
1460 }
1461 /* Point to the target path */
1462 zPath = jx9_value_to_string(apArg[0], &iLen);
1463 if( iLen < 1 ){
1464 /* Reuturn "." */
1465 jx9_result_string(pCtx, ".", sizeof(char));
1466 return JX9_OK;
1467 }
1468 /* Perform the requested operation */
1469 zDir = jx9ExtractDirName(zPath, iLen, &iDirlen);
1470 /* Return directory name */
1471 jx9_result_string(pCtx, zDir, iDirlen);
1472 return JX9_OK;
1473}
1474/*
1475 * string basename(string $path[, string $suffix ])
1476 * Returns trailing name component of path.
1477 * Parameters
1478 * $path
1479 * Target path.
1480 * On Windows, both slash (/) and backslash (\) are used as directory separator character.
1481 * In other environments, it is the forward slash (/).
1482 * $suffix
1483 * If the name component ends in suffix this will also be cut off.
1484 * Return
1485 * The base name of the given path.
1486 */
1487static int jx9Builtin_basename(jx9_context *pCtx, int nArg, jx9_value **apArg)
1488{
1489 const char *zPath, *zBase, *zEnd;
1490 int c, d, iLen;
1491 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
1492 /* Missing/Invalid argument, return the empty string */
1493 jx9_result_string(pCtx, "", 0);
1494 return JX9_OK;
1495 }
1496 c = d = '/';
1497#ifdef __WINNT__
1498 d = '\\';
1499#endif
1500 /* Point to the target path */
1501 zPath = jx9_value_to_string(apArg[0], &iLen);
1502 if( iLen < 1 ){
1503 /* Empty string */
1504 jx9_result_string(pCtx, "", 0);
1505 return JX9_OK;
1506 }
1507 /* Perform the requested operation */
1508 zEnd = &zPath[iLen - 1];
1509 /* Ignore trailing '/' */
1510 while( zEnd > zPath && ( (int)zEnd[0] == c || (int)zEnd[0] == d ) ){
1511 zEnd--;
1512 }
1513 iLen = (int)(&zEnd[1]-zPath);
1514 while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){
1515 zEnd--;
1516 }
1517 zBase = (zEnd > zPath) ? &zEnd[1] : zPath;
1518 zEnd = &zPath[iLen];
1519 if( nArg > 1 && jx9_value_is_string(apArg[1]) ){
1520 const char *zSuffix;
1521 int nSuffix;
1522 /* Strip suffix */
1523 zSuffix = jx9_value_to_string(apArg[1], &nSuffix);
1524 if( nSuffix > 0 && nSuffix < iLen && SyMemcmp(&zEnd[-nSuffix], zSuffix, nSuffix) == 0 ){
1525 zEnd -= nSuffix;
1526 }
1527 }
1528 /* Store the basename */
1529 jx9_result_string(pCtx, zBase, (int)(zEnd-zBase));
1530 return JX9_OK;
1531}
1532/*
1533 * value pathinfo(string $path [, int $options = PATHINFO_DIRNAME | PATHINFO_BASENAME | PATHINFO_EXTENSION | PATHINFO_FILENAME ])
1534 * Returns information about a file path.
1535 * Parameter
1536 * $path
1537 * The path to be parsed.
1538 * $options
1539 * If present, specifies a specific element to be returned; one of
1540 * PATHINFO_DIRNAME, PATHINFO_BASENAME, PATHINFO_EXTENSION or PATHINFO_FILENAME.
1541 * Return
1542 * If the options parameter is not passed, an associative array containing the following
1543 * elements is returned: dirname, basename, extension (if any), and filename.
1544 * If options is present, returns a string containing the requested element.
1545 */
1546typedef struct path_info path_info;
1547struct path_info
1548{
1549 SyString sDir; /* Directory [i.e: /var/www] */
1550 SyString sBasename; /* Basename [i.e httpd.conf] */
1551 SyString sExtension; /* File extension [i.e xml, pdf..] */
1552 SyString sFilename; /* Filename */
1553};
1554/*
1555 * Extract path fields.
1556 */
1557static sxi32 ExtractPathInfo(const char *zPath, int nByte, path_info *pOut)
1558{
1559 const char *zPtr, *zEnd = &zPath[nByte - 1];
1560 SyString *pCur;
1561 int c, d;
1562 c = d = '/';
1563#ifdef __WINNT__
1564 d = '\\';
1565#endif
1566 /* Zero the structure */
1567 SyZero(pOut, sizeof(path_info));
1568 /* Handle special case */
1569 if( nByte == sizeof(char) && ( (int)zPath[0] == c || (int)zPath[0] == d ) ){
1570#ifdef __WINNT__
1571 SyStringInitFromBuf(&pOut->sDir, "\\", sizeof(char));
1572#else
1573 SyStringInitFromBuf(&pOut->sDir, "/", sizeof(char));
1574#endif
1575 return SXRET_OK;
1576 }
1577 /* Extract the basename */
1578 while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){
1579 zEnd--;
1580 }
1581 zPtr = (zEnd > zPath) ? &zEnd[1] : zPath;
1582 zEnd = &zPath[nByte];
1583 /* dirname */
1584 pCur = &pOut->sDir;
1585 SyStringInitFromBuf(pCur, zPath, zPtr-zPath);
1586 if( pCur->nByte > 1 ){
1587 SyStringTrimTrailingChar(pCur, '/');
1588#ifdef __WINNT__
1589 SyStringTrimTrailingChar(pCur, '\\');
1590#endif
1591 }else if( (int)zPath[0] == c || (int)zPath[0] == d ){
1592#ifdef __WINNT__
1593 SyStringInitFromBuf(&pOut->sDir, "\\", sizeof(char));
1594#else
1595 SyStringInitFromBuf(&pOut->sDir, "/", sizeof(char));
1596#endif
1597 }
1598 /* basename/filename */
1599 pCur = &pOut->sBasename;
1600 SyStringInitFromBuf(pCur, zPtr, zEnd-zPtr);
1601 SyStringTrimLeadingChar(pCur, '/');
1602#ifdef __WINNT__
1603 SyStringTrimLeadingChar(pCur, '\\');
1604#endif
1605 SyStringDupPtr(&pOut->sFilename, pCur);
1606 if( pCur->nByte > 0 ){
1607 /* extension */
1608 zEnd--;
1609 while( zEnd > pCur->zString /*basename*/ && zEnd[0] != '.' ){
1610 zEnd--;
1611 }
1612 if( zEnd > pCur->zString ){
1613 zEnd++; /* Jump leading dot */
1614 SyStringInitFromBuf(&pOut->sExtension, zEnd, &zPath[nByte]-zEnd);
1615 /* Fix filename */
1616 pCur = &pOut->sFilename;
1617 if( pCur->nByte > SyStringLength(&pOut->sExtension) ){
1618 pCur->nByte -= 1 + SyStringLength(&pOut->sExtension);
1619 }
1620 }
1621 }
1622 return SXRET_OK;
1623}
1624/*
1625 * value pathinfo(string $path [, int $options = PATHINFO_DIRNAME | PATHINFO_BASENAME | PATHINFO_EXTENSION | PATHINFO_FILENAME ])
1626 * See block comment above.
1627 */
1628static int jx9Builtin_pathinfo(jx9_context *pCtx, int nArg, jx9_value **apArg)
1629{
1630 const char *zPath;
1631 path_info sInfo;
1632 SyString *pComp;
1633 int iLen;
1634 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
1635 /* Missing/Invalid argument, return the empty string */
1636 jx9_result_string(pCtx, "", 0);
1637 return JX9_OK;
1638 }
1639 /* Point to the target path */
1640 zPath = jx9_value_to_string(apArg[0], &iLen);
1641 if( iLen < 1 ){
1642 /* Empty string */
1643 jx9_result_string(pCtx, "", 0);
1644 return JX9_OK;
1645 }
1646 /* Extract path info */
1647 ExtractPathInfo(zPath, iLen, &sInfo);
1648 if( nArg > 1 && jx9_value_is_int(apArg[1]) ){
1649 /* Return path component */
1650 int nComp = jx9_value_to_int(apArg[1]);
1651 switch(nComp){
1652 case 1: /* PATHINFO_DIRNAME */
1653 pComp = &sInfo.sDir;
1654 if( pComp->nByte > 0 ){
1655 jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
1656 }else{
1657 /* Expand the empty string */
1658 jx9_result_string(pCtx, "", 0);
1659 }
1660 break;
1661 case 2: /*PATHINFO_BASENAME*/
1662 pComp = &sInfo.sBasename;
1663 if( pComp->nByte > 0 ){
1664 jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
1665 }else{
1666 /* Expand the empty string */
1667 jx9_result_string(pCtx, "", 0);
1668 }
1669 break;
1670 case 3: /*PATHINFO_EXTENSION*/
1671 pComp = &sInfo.sExtension;
1672 if( pComp->nByte > 0 ){
1673 jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
1674 }else{
1675 /* Expand the empty string */
1676 jx9_result_string(pCtx, "", 0);
1677 }
1678 break;
1679 case 4: /*PATHINFO_FILENAME*/
1680 pComp = &sInfo.sFilename;
1681 if( pComp->nByte > 0 ){
1682 jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte);
1683 }else{
1684 /* Expand the empty string */
1685 jx9_result_string(pCtx, "", 0);
1686 }
1687 break;
1688 default:
1689 /* Expand the empty string */
1690 jx9_result_string(pCtx, "", 0);
1691 break;
1692 }
1693 }else{
1694 /* Return an associative array */
1695 jx9_value *pArray, *pValue;
1696 pArray = jx9_context_new_array(pCtx);
1697 pValue = jx9_context_new_scalar(pCtx);
1698 if( pArray == 0 || pValue == 0 ){
1699 /* Out of mem, return NULL */
1700 jx9_result_bool(pCtx, 0);
1701 return JX9_OK;
1702 }
1703 /* dirname */
1704 pComp = &sInfo.sDir;
1705 if( pComp->nByte > 0 ){
1706 jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
1707 /* Perform the insertion */
1708 jx9_array_add_strkey_elem(pArray, "dirname", pValue); /* Will make it's own copy */
1709 }
1710 /* Reset the string cursor */
1711 jx9_value_reset_string_cursor(pValue);
1712 /* basername */
1713 pComp = &sInfo.sBasename;
1714 if( pComp->nByte > 0 ){
1715 jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
1716 /* Perform the insertion */
1717 jx9_array_add_strkey_elem(pArray, "basename", pValue); /* Will make it's own copy */
1718 }
1719 /* Reset the string cursor */
1720 jx9_value_reset_string_cursor(pValue);
1721 /* extension */
1722 pComp = &sInfo.sExtension;
1723 if( pComp->nByte > 0 ){
1724 jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
1725 /* Perform the insertion */
1726 jx9_array_add_strkey_elem(pArray, "extension", pValue); /* Will make it's own copy */
1727 }
1728 /* Reset the string cursor */
1729 jx9_value_reset_string_cursor(pValue);
1730 /* filename */
1731 pComp = &sInfo.sFilename;
1732 if( pComp->nByte > 0 ){
1733 jx9_value_string(pValue, pComp->zString, (int)pComp->nByte);
1734 /* Perform the insertion */
1735 jx9_array_add_strkey_elem(pArray, "filename", pValue); /* Will make it's own copy */
1736 }
1737 /* Return the created array */
1738 jx9_result_value(pCtx, pArray);
1739 /* Don't worry about freeing memory, everything will be released
1740 * automatically as soon we return from this foreign function.
1741 */
1742 }
1743 return JX9_OK;
1744}
1745/*
1746 * Globbing implementation extracted from the sqlite3 source tree.
1747 * Original author: D. Richard Hipp (http://www.sqlite.org)
1748 * Status: Public Domain
1749 */
1750typedef unsigned char u8;
1751/* An array to map all upper-case characters into their corresponding
1752** lower-case character.
1753**
1754** SQLite only considers US-ASCII (or EBCDIC) characters. We do not
1755** handle case conversions for the UTF character set since the tables
1756** involved are nearly as big or bigger than SQLite itself.
1757*/
1758static const unsigned char sqlite3UpperToLower[] = {
1759 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
1760 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
1761 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
1762 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103,
1763 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
1764 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
1765 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
1766 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
1767 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161,
1768 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
1769 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197,
1770 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215,
1771 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233,
1772 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251,
1773 252, 253, 254, 255
1774};
1775#define GlogUpperToLower(A) if( A<0x80 ){ A = sqlite3UpperToLower[A]; }
1776/*
1777** Assuming zIn points to the first byte of a UTF-8 character,
1778** advance zIn to point to the first byte of the next UTF-8 character.
1779*/
1780#define SQLITE_SKIP_UTF8(zIn) { \
1781 if( (*(zIn++))>=0xc0 ){ \
1782 while( (*zIn & 0xc0)==0x80 ){ zIn++; } \
1783 } \
1784}
1785/*
1786** Compare two UTF-8 strings for equality where the first string can
1787** potentially be a "glob" expression. Return true (1) if they
1788** are the same and false (0) if they are different.
1789**
1790** Globbing rules:
1791**
1792** '*' Matches any sequence of zero or more characters.
1793**
1794** '?' Matches exactly one character.
1795**
1796** [...] Matches one character from the enclosed list of
1797** characters.
1798**
1799** [^...] Matches one character not in the enclosed list.
1800**
1801** With the [...] and [^...] matching, a ']' character can be included
1802** in the list by making it the first character after '[' or '^'. A
1803** range of characters can be specified using '-'. Example:
1804** "[a-z]" matches any single lower-case letter. To match a '-', make
1805** it the last character in the list.
1806**
1807** This routine is usually quick, but can be N**2 in the worst case.
1808**
1809** Hints: to match '*' or '?', put them in "[]". Like this:
1810**
1811** abc[*]xyz Matches "abc*xyz" only
1812*/
1813static int patternCompare(
1814 const u8 *zPattern, /* The glob pattern */
1815 const u8 *zString, /* The string to compare against the glob */
1816 const int esc, /* The escape character */
1817 int noCase
1818){
1819 int c, c2;
1820 int invert;
1821 int seen;
1822 u8 matchOne = '?';
1823 u8 matchAll = '*';
1824 u8 matchSet = '[';
1825 int prevEscape = 0; /* True if the previous character was 'escape' */
1826
1827 if( !zPattern || !zString ) return 0;
1828 while( (c = jx9Utf8Read(zPattern, 0, &zPattern))!=0 ){
1829 if( !prevEscape && c==matchAll ){
1830 while( (c= jx9Utf8Read(zPattern, 0, &zPattern)) == matchAll
1831 || c == matchOne ){
1832 if( c==matchOne && jx9Utf8Read(zString, 0, &zString)==0 ){
1833 return 0;
1834 }
1835 }
1836 if( c==0 ){
1837 return 1;
1838 }else if( c==esc ){
1839 c = jx9Utf8Read(zPattern, 0, &zPattern);
1840 if( c==0 ){
1841 return 0;
1842 }
1843 }else if( c==matchSet ){
1844 if( (esc==0) || (matchSet<0x80) ) return 0;
1845 while( *zString && patternCompare(&zPattern[-1], zString, esc, noCase)==0 ){
1846 SQLITE_SKIP_UTF8(zString);
1847 }
1848 return *zString!=0;
1849 }
1850 while( (c2 = jx9Utf8Read(zString, 0, &zString))!=0 ){
1851 if( noCase ){
1852 GlogUpperToLower(c2);
1853 GlogUpperToLower(c);
1854 while( c2 != 0 && c2 != c ){
1855 c2 = jx9Utf8Read(zString, 0, &zString);
1856 GlogUpperToLower(c2);
1857 }
1858 }else{
1859 while( c2 != 0 && c2 != c ){
1860 c2 = jx9Utf8Read(zString, 0, &zString);
1861 }
1862 }
1863 if( c2==0 ) return 0;
1864 if( patternCompare(zPattern, zString, esc, noCase) ) return 1;
1865 }
1866 return 0;
1867 }else if( !prevEscape && c==matchOne ){
1868 if( jx9Utf8Read(zString, 0, &zString)==0 ){
1869 return 0;
1870 }
1871 }else if( c==matchSet ){
1872 int prior_c = 0;
1873 if( esc == 0 ) return 0;
1874 seen = 0;
1875 invert = 0;
1876 c = jx9Utf8Read(zString, 0, &zString);
1877 if( c==0 ) return 0;
1878 c2 = jx9Utf8Read(zPattern, 0, &zPattern);
1879 if( c2=='^' ){
1880 invert = 1;
1881 c2 = jx9Utf8Read(zPattern, 0, &zPattern);
1882 }
1883 if( c2==']' ){
1884 if( c==']' ) seen = 1;
1885 c2 = jx9Utf8Read(zPattern, 0, &zPattern);
1886 }
1887 while( c2 && c2!=']' ){
1888 if( c2=='-' && zPattern[0]!=']' && zPattern[0]!=0 && prior_c>0 ){
1889 c2 = jx9Utf8Read(zPattern, 0, &zPattern);
1890 if( c>=prior_c && c<=c2 ) seen = 1;
1891 prior_c = 0;
1892 }else{
1893 if( c==c2 ){
1894 seen = 1;
1895 }
1896 prior_c = c2;
1897 }
1898 c2 = jx9Utf8Read(zPattern, 0, &zPattern);
1899 }
1900 if( c2==0 || (seen ^ invert)==0 ){
1901 return 0;
1902 }
1903 }else if( esc==c && !prevEscape ){
1904 prevEscape = 1;
1905 }else{
1906 c2 = jx9Utf8Read(zString, 0, &zString);
1907 if( noCase ){
1908 GlogUpperToLower(c);
1909 GlogUpperToLower(c2);
1910 }
1911 if( c!=c2 ){
1912 return 0;
1913 }
1914 prevEscape = 0;
1915 }
1916 }
1917 return *zString==0;
1918}
1919/*
1920 * Wrapper around patternCompare() defined above.
1921 * See block comment above for more information.
1922 */
1923static int Glob(const unsigned char *zPattern, const unsigned char *zString, int iEsc, int CaseCompare)
1924{
1925 int rc;
1926 if( iEsc < 0 ){
1927 iEsc = '\\';
1928 }
1929 rc = patternCompare(zPattern, zString, iEsc, CaseCompare);
1930 return rc;
1931}
1932/*
1933 * bool fnmatch(string $pattern, string $string[, int $flags = 0 ])
1934 * Match filename against a pattern.
1935 * Parameters
1936 * $pattern
1937 * The shell wildcard pattern.
1938 * $string
1939 * The tested string.
1940 * $flags
1941 * A list of possible flags:
1942 * FNM_NOESCAPE Disable backslash escaping.
1943 * FNM_PATHNAME Slash in string only matches slash in the given pattern.
1944 * FNM_PERIOD Leading period in string must be exactly matched by period in the given pattern.
1945 * FNM_CASEFOLD Caseless match.
1946 * Return
1947 * TRUE if there is a match, FALSE otherwise.
1948 */
1949static int jx9Builtin_fnmatch(jx9_context *pCtx, int nArg, jx9_value **apArg)
1950{
1951 const char *zString, *zPattern;
1952 int iEsc = '\\';
1953 int noCase = 0;
1954 int rc;
1955 if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
1956 /* Missing/Invalid arguments, return FALSE */
1957 jx9_result_bool(pCtx, 0);
1958 return JX9_OK;
1959 }
1960 /* Extract the pattern and the string */
1961 zPattern = jx9_value_to_string(apArg[0], 0);
1962 zString = jx9_value_to_string(apArg[1], 0);
1963 /* Extract the flags if avaialble */
1964 if( nArg > 2 && jx9_value_is_int(apArg[2]) ){
1965 rc = jx9_value_to_int(apArg[2]);
1966 if( rc & 0x01 /*FNM_NOESCAPE*/){
1967 iEsc = 0;
1968 }
1969 if( rc & 0x08 /*FNM_CASEFOLD*/){
1970 noCase = 1;
1971 }
1972 }
1973 /* Go globbing */
1974 rc = Glob((const unsigned char *)zPattern, (const unsigned char *)zString, iEsc, noCase);
1975 /* Globbing result */
1976 jx9_result_bool(pCtx, rc);
1977 return JX9_OK;
1978}
1979/*
1980 * bool strglob(string $pattern, string $string)
1981 * Match string against a pattern.
1982 * Parameters
1983 * $pattern
1984 * The shell wildcard pattern.
1985 * $string
1986 * The tested string.
1987 * Return
1988 * TRUE if there is a match, FALSE otherwise.
1989 */
1990static int jx9Builtin_strglob(jx9_context *pCtx, int nArg, jx9_value **apArg)
1991{
1992 const char *zString, *zPattern;
1993 int iEsc = '\\';
1994 int rc;
1995 if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
1996 /* Missing/Invalid arguments, return FALSE */
1997 jx9_result_bool(pCtx, 0);
1998 return JX9_OK;
1999 }
2000 /* Extract the pattern and the string */
2001 zPattern = jx9_value_to_string(apArg[0], 0);
2002 zString = jx9_value_to_string(apArg[1], 0);
2003 /* Go globbing */
2004 rc = Glob((const unsigned char *)zPattern, (const unsigned char *)zString, iEsc, 0);
2005 /* Globbing result */
2006 jx9_result_bool(pCtx, rc);
2007 return JX9_OK;
2008}
2009/*
2010 * bool link(string $target, string $link)
2011 * Create a hard link.
2012 * Parameters
2013 * $target
2014 * Target of the link.
2015 * $link
2016 * The link name.
2017 * Return
2018 * TRUE on success or FALSE on failure.
2019 */
2020static int jx9Vfs_link(jx9_context *pCtx, int nArg, jx9_value **apArg)
2021{
2022 const char *zTarget, *zLink;
2023 jx9_vfs *pVfs;
2024 int rc;
2025 if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
2026 /* Missing/Invalid arguments, return FALSE */
2027 jx9_result_bool(pCtx, 0);
2028 return JX9_OK;
2029 }
2030 /* Point to the underlying vfs */
2031 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
2032 if( pVfs == 0 || pVfs->xLink == 0 ){
2033 /* IO routine not implemented, return NULL */
2034 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
2035 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
2036 jx9_function_name(pCtx)
2037 );
2038 jx9_result_bool(pCtx, 0);
2039 return JX9_OK;
2040 }
2041 /* Extract the given arguments */
2042 zTarget = jx9_value_to_string(apArg[0], 0);
2043 zLink = jx9_value_to_string(apArg[1], 0);
2044 /* Perform the requested operation */
2045 rc = pVfs->xLink(zTarget, zLink, 0/*Not a symbolic link */);
2046 /* IO result */
2047 jx9_result_bool(pCtx, rc == JX9_OK );
2048 return JX9_OK;
2049}
2050/*
2051 * bool symlink(string $target, string $link)
2052 * Creates a symbolic link.
2053 * Parameters
2054 * $target
2055 * Target of the link.
2056 * $link
2057 * The link name.
2058 * Return
2059 * TRUE on success or FALSE on failure.
2060 */
2061static int jx9Vfs_symlink(jx9_context *pCtx, int nArg, jx9_value **apArg)
2062{
2063 const char *zTarget, *zLink;
2064 jx9_vfs *pVfs;
2065 int rc;
2066 if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
2067 /* Missing/Invalid arguments, return FALSE */
2068 jx9_result_bool(pCtx, 0);
2069 return JX9_OK;
2070 }
2071 /* Point to the underlying vfs */
2072 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
2073 if( pVfs == 0 || pVfs->xLink == 0 ){
2074 /* IO routine not implemented, return NULL */
2075 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
2076 "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE",
2077 jx9_function_name(pCtx)
2078 );
2079 jx9_result_bool(pCtx, 0);
2080 return JX9_OK;
2081 }
2082 /* Extract the given arguments */
2083 zTarget = jx9_value_to_string(apArg[0], 0);
2084 zLink = jx9_value_to_string(apArg[1], 0);
2085 /* Perform the requested operation */
2086 rc = pVfs->xLink(zTarget, zLink, 1/*A symbolic link */);
2087 /* IO result */
2088 jx9_result_bool(pCtx, rc == JX9_OK );
2089 return JX9_OK;
2090}
2091/*
2092 * int umask([ int $mask ])
2093 * Changes the current umask.
2094 * Parameters
2095 * $mask
2096 * The new umask.
2097 * Return
2098 * umask() without arguments simply returns the current umask.
2099 * Otherwise the old umask is returned.
2100 */
2101static int jx9Vfs_umask(jx9_context *pCtx, int nArg, jx9_value **apArg)
2102{
2103 int iOld, iNew;
2104 jx9_vfs *pVfs;
2105 /* Point to the underlying vfs */
2106 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
2107 if( pVfs == 0 || pVfs->xUmask == 0 ){
2108 /* IO routine not implemented, return -1 */
2109 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
2110 "IO routine(%s) not implemented in the underlying VFS",
2111 jx9_function_name(pCtx)
2112 );
2113 jx9_result_int(pCtx, 0);
2114 return JX9_OK;
2115 }
2116 iNew = 0;
2117 if( nArg > 0 ){
2118 iNew = jx9_value_to_int(apArg[0]);
2119 }
2120 /* Perform the requested operation */
2121 iOld = pVfs->xUmask(iNew);
2122 /* Old mask */
2123 jx9_result_int(pCtx, iOld);
2124 return JX9_OK;
2125}
2126/*
2127 * string sys_get_temp_dir()
2128 * Returns directory path used for temporary files.
2129 * Parameters
2130 * None
2131 * Return
2132 * Returns the path of the temporary directory.
2133 */
2134static int jx9Vfs_sys_get_temp_dir(jx9_context *pCtx, int nArg, jx9_value **apArg)
2135{
2136 jx9_vfs *pVfs;
2137 /* Set the empty string as the default return value */
2138 jx9_result_string(pCtx, "", 0);
2139 /* Point to the underlying vfs */
2140 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
2141 if( pVfs == 0 || pVfs->xTempDir == 0 ){
2142 SXUNUSED(nArg); /* cc warning */
2143 SXUNUSED(apArg);
2144 /* IO routine not implemented, return "" */
2145 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
2146 "IO routine(%s) not implemented in the underlying VFS",
2147 jx9_function_name(pCtx)
2148 );
2149 return JX9_OK;
2150 }
2151 /* Perform the requested operation */
2152 pVfs->xTempDir(pCtx);
2153 return JX9_OK;
2154}
2155/*
2156 * string get_current_user()
2157 * Returns the name of the current working user.
2158 * Parameters
2159 * None
2160 * Return
2161 * Returns the name of the current working user.
2162 */
2163static int jx9Vfs_get_current_user(jx9_context *pCtx, int nArg, jx9_value **apArg)
2164{
2165 jx9_vfs *pVfs;
2166 /* Point to the underlying vfs */
2167 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
2168 if( pVfs == 0 || pVfs->xUsername == 0 ){
2169 SXUNUSED(nArg); /* cc warning */
2170 SXUNUSED(apArg);
2171 /* IO routine not implemented */
2172 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
2173 "IO routine(%s) not implemented in the underlying VFS",
2174 jx9_function_name(pCtx)
2175 );
2176 /* Set a dummy username */
2177 jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
2178 return JX9_OK;
2179 }
2180 /* Perform the requested operation */
2181 pVfs->xUsername(pCtx);
2182 return JX9_OK;
2183}
2184/*
2185 * int64 getmypid()
2186 * Gets process ID.
2187 * Parameters
2188 * None
2189 * Return
2190 * Returns the process ID.
2191 */
2192static int jx9Vfs_getmypid(jx9_context *pCtx, int nArg, jx9_value **apArg)
2193{
2194 jx9_int64 nProcessId;
2195 jx9_vfs *pVfs;
2196 /* Point to the underlying vfs */
2197 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
2198 if( pVfs == 0 || pVfs->xProcessId == 0 ){
2199 SXUNUSED(nArg); /* cc warning */
2200 SXUNUSED(apArg);
2201 /* IO routine not implemented, return -1 */
2202 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
2203 "IO routine(%s) not implemented in the underlying VFS",
2204 jx9_function_name(pCtx)
2205 );
2206 jx9_result_int(pCtx, -1);
2207 return JX9_OK;
2208 }
2209 /* Perform the requested operation */
2210 nProcessId = (jx9_int64)pVfs->xProcessId();
2211 /* Set the result */
2212 jx9_result_int64(pCtx, nProcessId);
2213 return JX9_OK;
2214}
2215/*
2216 * int getmyuid()
2217 * Get user ID.
2218 * Parameters
2219 * None
2220 * Return
2221 * Returns the user ID.
2222 */
2223static int jx9Vfs_getmyuid(jx9_context *pCtx, int nArg, jx9_value **apArg)
2224{
2225 jx9_vfs *pVfs;
2226 int nUid;
2227 /* Point to the underlying vfs */
2228 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
2229 if( pVfs == 0 || pVfs->xUid == 0 ){
2230 SXUNUSED(nArg); /* cc warning */
2231 SXUNUSED(apArg);
2232 /* IO routine not implemented, return -1 */
2233 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
2234 "IO routine(%s) not implemented in the underlying VFS",
2235 jx9_function_name(pCtx)
2236 );
2237 jx9_result_int(pCtx, -1);
2238 return JX9_OK;
2239 }
2240 /* Perform the requested operation */
2241 nUid = pVfs->xUid();
2242 /* Set the result */
2243 jx9_result_int(pCtx, nUid);
2244 return JX9_OK;
2245}
2246/*
2247 * int getmygid()
2248 * Get group ID.
2249 * Parameters
2250 * None
2251 * Return
2252 * Returns the group ID.
2253 */
2254static int jx9Vfs_getmygid(jx9_context *pCtx, int nArg, jx9_value **apArg)
2255{
2256 jx9_vfs *pVfs;
2257 int nGid;
2258 /* Point to the underlying vfs */
2259 pVfs = (jx9_vfs *)jx9_context_user_data(pCtx);
2260 if( pVfs == 0 || pVfs->xGid == 0 ){
2261 SXUNUSED(nArg); /* cc warning */
2262 SXUNUSED(apArg);
2263 /* IO routine not implemented, return -1 */
2264 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
2265 "IO routine(%s) not implemented in the underlying VFS",
2266 jx9_function_name(pCtx)
2267 );
2268 jx9_result_int(pCtx, -1);
2269 return JX9_OK;
2270 }
2271 /* Perform the requested operation */
2272 nGid = pVfs->xGid();
2273 /* Set the result */
2274 jx9_result_int(pCtx, nGid);
2275 return JX9_OK;
2276}
2277#ifdef __WINNT__
2278#include <Windows.h>
2279#elif defined(__UNIXES__)
2280#include <sys/utsname.h>
2281#endif
2282/*
2283 * string uname([ string $mode = "a" ])
2284 * Returns information about the host operating system.
2285 * Parameters
2286 * $mode
2287 * mode is a single character that defines what information is returned:
2288 * 'a': This is the default. Contains all modes in the sequence "s n r v m".
2289 * 's': Operating system name. eg. FreeBSD.
2290 * 'n': Host name. eg. localhost.example.com.
2291 * 'r': Release name. eg. 5.1.2-RELEASE.
2292 * 'v': Version information. Varies a lot between operating systems.
2293 * 'm': Machine type. eg. i386.
2294 * Return
2295 * OS description as a string.
2296 */
2297static int jx9Vfs_uname(jx9_context *pCtx, int nArg, jx9_value **apArg)
2298{
2299#if defined(__WINNT__)
2300 const char *zName = "Microsoft Windows";
2301 OSVERSIONINFOW sVer;
2302#elif defined(__UNIXES__)
2303 struct utsname sName;
2304#endif
2305 const char *zMode = "a";
2306 if( nArg > 0 && jx9_value_is_string(apArg[0]) ){
2307 /* Extract the desired mode */
2308 zMode = jx9_value_to_string(apArg[0], 0);
2309 }
2310#if defined(__WINNT__)
2311 sVer.dwOSVersionInfoSize = sizeof(sVer);
2312 if( TRUE != GetVersionExW(&sVer)){
2313 jx9_result_string(pCtx, zName, -1);
2314 return JX9_OK;
2315 }
2316 if( sVer.dwPlatformId == VER_PLATFORM_WIN32_NT ){
2317 if( sVer.dwMajorVersion <= 4 ){
2318 zName = "Microsoft Windows NT";
2319 }else if( sVer.dwMajorVersion == 5 ){
2320 switch(sVer.dwMinorVersion){
2321 case 0: zName = "Microsoft Windows 2000"; break;
2322 case 1: zName = "Microsoft Windows XP"; break;
2323 case 2: zName = "Microsoft Windows Server 2003"; break;
2324 }
2325 }else if( sVer.dwMajorVersion == 6){
2326 switch(sVer.dwMinorVersion){
2327 case 0: zName = "Microsoft Windows Vista"; break;
2328 case 1: zName = "Microsoft Windows 7"; break;
2329 case 2: zName = "Microsoft Windows 8"; break;
2330 default: break;
2331 }
2332 }
2333 }
2334 switch(zMode[0]){
2335 case 's':
2336 /* Operating system name */
2337 jx9_result_string(pCtx, zName, -1/* Compute length automatically*/);
2338 break;
2339 case 'n':
2340 /* Host name */
2341 jx9_result_string(pCtx, "localhost", (int)sizeof("localhost")-1);
2342 break;
2343 case 'r':
2344 case 'v':
2345 /* Version information. */
2346 jx9_result_string_format(pCtx, "%u.%u build %u",
2347 sVer.dwMajorVersion, sVer.dwMinorVersion, sVer.dwBuildNumber
2348 );
2349 break;
2350 case 'm':
2351 /* Machine name */
2352 jx9_result_string(pCtx, "x86", (int)sizeof("x86")-1);
2353 break;
2354 default:
2355 jx9_result_string_format(pCtx, "%s localhost %u.%u build %u x86",
2356 zName,
2357 sVer.dwMajorVersion, sVer.dwMinorVersion, sVer.dwBuildNumber
2358 );
2359 break;
2360 }
2361#elif defined(__UNIXES__)
2362 if( uname(&sName) != 0 ){
2363 jx9_result_string(pCtx, "Unix", (int)sizeof("Unix")-1);
2364 return JX9_OK;
2365 }
2366 switch(zMode[0]){
2367 case 's':
2368 /* Operating system name */
2369 jx9_result_string(pCtx, sName.sysname, -1/* Compute length automatically*/);
2370 break;
2371 case 'n':
2372 /* Host name */
2373 jx9_result_string(pCtx, sName.nodename, -1/* Compute length automatically*/);
2374 break;
2375 case 'r':
2376 /* Release information */
2377 jx9_result_string(pCtx, sName.release, -1/* Compute length automatically*/);
2378 break;
2379 case 'v':
2380 /* Version information. */
2381 jx9_result_string(pCtx, sName.version, -1/* Compute length automatically*/);
2382 break;
2383 case 'm':
2384 /* Machine name */
2385 jx9_result_string(pCtx, sName.machine, -1/* Compute length automatically*/);
2386 break;
2387 default:
2388 jx9_result_string_format(pCtx,
2389 "%s %s %s %s %s",
2390 sName.sysname,
2391 sName.release,
2392 sName.version,
2393 sName.nodename,
2394 sName.machine
2395 );
2396 break;
2397 }
2398#else
2399 jx9_result_string(pCtx, "Host Operating System/localhost", (int)sizeof("Host Operating System/localhost")-1);
2400#endif
2401 return JX9_OK;
2402}
2403/*
2404 * Section:
2405 * IO stream implementation.
2406 * Authors:
2407 * Symisc Systems, devel@symisc.net.
2408 * Copyright (C) Symisc Systems, http://jx9.symisc.net
2409 * Status:
2410 * Stable.
2411 */
2412typedef struct io_private io_private;
2413struct io_private
2414{
2415 const jx9_io_stream *pStream; /* Underlying IO device */
2416 void *pHandle; /* IO handle */
2417 /* Unbuffered IO */
2418 SyBlob sBuffer; /* Working buffer */
2419 sxu32 nOfft; /* Current read offset */
2420 sxu32 iMagic; /* Sanity check to avoid misuse */
2421};
2422#define IO_PRIVATE_MAGIC 0xFEAC14
2423/* Make sure we are dealing with a valid io_private instance */
2424#define IO_PRIVATE_INVALID(IO) ( IO == 0 || IO->iMagic != IO_PRIVATE_MAGIC )
2425/* Forward declaration */
2426static void ResetIOPrivate(io_private *pDev);
2427/*
2428 * bool ftruncate(resource $handle, int64 $size)
2429 * Truncates a file to a given length.
2430 * Parameters
2431 * $handle
2432 * The file pointer.
2433 * Note:
2434 * The handle must be open for writing.
2435 * $size
2436 * The size to truncate to.
2437 * Return
2438 * TRUE on success or FALSE on failure.
2439 */
2440static int jx9Builtin_ftruncate(jx9_context *pCtx, int nArg, jx9_value **apArg)
2441{
2442 const jx9_io_stream *pStream;
2443 io_private *pDev;
2444 int rc;
2445 if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
2446 /* Missing/Invalid arguments, return FALSE */
2447 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
2448 jx9_result_bool(pCtx, 0);
2449 return JX9_OK;
2450 }
2451 /* Extract our private data */
2452 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
2453 /* Make sure we are dealing with a valid io_private instance */
2454 if( IO_PRIVATE_INVALID(pDev) ){
2455 /*Expecting an IO handle */
2456 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
2457 jx9_result_bool(pCtx, 0);
2458 return JX9_OK;
2459 }
2460 /* Point to the target IO stream device */
2461 pStream = pDev->pStream;
2462 if( pStream == 0 || pStream->xTrunc == 0){
2463 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
2464 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
2465 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
2466 );
2467 jx9_result_bool(pCtx, 0);
2468 return JX9_OK;
2469 }
2470 /* Perform the requested operation */
2471 rc = pStream->xTrunc(pDev->pHandle, jx9_value_to_int64(apArg[1]));
2472 if( rc == JX9_OK ){
2473 /* Discard buffered data */
2474 ResetIOPrivate(pDev);
2475 }
2476 /* IO result */
2477 jx9_result_bool(pCtx, rc == JX9_OK);
2478 return JX9_OK;
2479}
2480/*
2481 * int fseek(resource $handle, int $offset[, int $whence = SEEK_SET ])
2482 * Seeks on a file pointer.
2483 * Parameters
2484 * $handle
2485 * A file system pointer resource that is typically created using fopen().
2486 * $offset
2487 * The offset.
2488 * To move to a position before the end-of-file, you need to pass a negative
2489 * value in offset and set whence to SEEK_END.
2490 * whence
2491 * whence values are:
2492 * SEEK_SET - Set position equal to offset bytes.
2493 * SEEK_CUR - Set position to current location plus offset.
2494 * SEEK_END - Set position to end-of-file plus offset.
2495 * Return
2496 * 0 on success, -1 on failure
2497 */
2498static int jx9Builtin_fseek(jx9_context *pCtx, int nArg, jx9_value **apArg)
2499{
2500 const jx9_io_stream *pStream;
2501 io_private *pDev;
2502 jx9_int64 iOfft;
2503 int whence;
2504 int rc;
2505 if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
2506 /* Missing/Invalid arguments, return FALSE */
2507 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
2508 jx9_result_int(pCtx, -1);
2509 return JX9_OK;
2510 }
2511 /* Extract our private data */
2512 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
2513 /* Make sure we are dealing with a valid io_private instance */
2514 if( IO_PRIVATE_INVALID(pDev) ){
2515 /*Expecting an IO handle */
2516 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
2517 jx9_result_int(pCtx, -1);
2518 return JX9_OK;
2519 }
2520 /* Point to the target IO stream device */
2521 pStream = pDev->pStream;
2522 if( pStream == 0 || pStream->xSeek == 0){
2523 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
2524 "IO routine(%s) not implemented in the underlying stream(%s) device",
2525 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
2526 );
2527 jx9_result_int(pCtx, -1);
2528 return JX9_OK;
2529 }
2530 /* Extract the offset */
2531 iOfft = jx9_value_to_int64(apArg[1]);
2532 whence = 0;/* SEEK_SET */
2533 if( nArg > 2 && jx9_value_is_int(apArg[2]) ){
2534 whence = jx9_value_to_int(apArg[2]);
2535 }
2536 /* Perform the requested operation */
2537 rc = pStream->xSeek(pDev->pHandle, iOfft, whence);
2538 if( rc == JX9_OK ){
2539 /* Ignore buffered data */
2540 ResetIOPrivate(pDev);
2541 }
2542 /* IO result */
2543 jx9_result_int(pCtx, rc == JX9_OK ? 0 : - 1);
2544 return JX9_OK;
2545}
2546/*
2547 * int64 ftell(resource $handle)
2548 * Returns the current position of the file read/write pointer.
2549 * Parameters
2550 * $handle
2551 * The file pointer.
2552 * Return
2553 * Returns the position of the file pointer referenced by handle
2554 * as an integer; i.e., its offset into the file stream.
2555 * FALSE is returned on failure.
2556 */
2557static int jx9Builtin_ftell(jx9_context *pCtx, int nArg, jx9_value **apArg)
2558{
2559 const jx9_io_stream *pStream;
2560 io_private *pDev;
2561 jx9_int64 iOfft;
2562 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
2563 /* Missing/Invalid arguments, return FALSE */
2564 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
2565 jx9_result_bool(pCtx, 0);
2566 return JX9_OK;
2567 }
2568 /* Extract our private data */
2569 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
2570 /* Make sure we are dealing with a valid io_private instance */
2571 if( IO_PRIVATE_INVALID(pDev) ){
2572 /*Expecting an IO handle */
2573 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
2574 jx9_result_bool(pCtx, 0);
2575 return JX9_OK;
2576 }
2577 /* Point to the target IO stream device */
2578 pStream = pDev->pStream;
2579 if( pStream == 0 || pStream->xTell == 0){
2580 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
2581 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
2582 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
2583 );
2584 jx9_result_bool(pCtx, 0);
2585 return JX9_OK;
2586 }
2587 /* Perform the requested operation */
2588 iOfft = pStream->xTell(pDev->pHandle);
2589 /* IO result */
2590 jx9_result_int64(pCtx, iOfft);
2591 return JX9_OK;
2592}
2593/*
2594 * bool rewind(resource $handle)
2595 * Rewind the position of a file pointer.
2596 * Parameters
2597 * $handle
2598 * The file pointer.
2599 * Return
2600 * TRUE on success or FALSE on failure.
2601 */
2602static int jx9Builtin_rewind(jx9_context *pCtx, int nArg, jx9_value **apArg)
2603{
2604 const jx9_io_stream *pStream;
2605 io_private *pDev;
2606 int rc;
2607 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
2608 /* Missing/Invalid arguments, return FALSE */
2609 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
2610 jx9_result_bool(pCtx, 0);
2611 return JX9_OK;
2612 }
2613 /* Extract our private data */
2614 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
2615 /* Make sure we are dealing with a valid io_private instance */
2616 if( IO_PRIVATE_INVALID(pDev) ){
2617 /*Expecting an IO handle */
2618 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
2619 jx9_result_bool(pCtx, 0);
2620 return JX9_OK;
2621 }
2622 /* Point to the target IO stream device */
2623 pStream = pDev->pStream;
2624 if( pStream == 0 || pStream->xSeek == 0){
2625 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
2626 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
2627 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
2628 );
2629 jx9_result_bool(pCtx, 0);
2630 return JX9_OK;
2631 }
2632 /* Perform the requested operation */
2633 rc = pStream->xSeek(pDev->pHandle, 0, 0/*SEEK_SET*/);
2634 if( rc == JX9_OK ){
2635 /* Ignore buffered data */
2636 ResetIOPrivate(pDev);
2637 }
2638 /* IO result */
2639 jx9_result_bool(pCtx, rc == JX9_OK);
2640 return JX9_OK;
2641}
2642/*
2643 * bool fflush(resource $handle)
2644 * Flushes the output to a file.
2645 * Parameters
2646 * $handle
2647 * The file pointer.
2648 * Return
2649 * TRUE on success or FALSE on failure.
2650 */
2651static int jx9Builtin_fflush(jx9_context *pCtx, int nArg, jx9_value **apArg)
2652{
2653 const jx9_io_stream *pStream;
2654 io_private *pDev;
2655 int rc;
2656 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
2657 /* Missing/Invalid arguments, return FALSE */
2658 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
2659 jx9_result_bool(pCtx, 0);
2660 return JX9_OK;
2661 }
2662 /* Extract our private data */
2663 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
2664 /* Make sure we are dealing with a valid io_private instance */
2665 if( IO_PRIVATE_INVALID(pDev) ){
2666 /*Expecting an IO handle */
2667 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
2668 jx9_result_bool(pCtx, 0);
2669 return JX9_OK;
2670 }
2671 /* Point to the target IO stream device */
2672 pStream = pDev->pStream;
2673 if( pStream == 0 || pStream->xSync == 0){
2674 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
2675 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
2676 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
2677 );
2678 jx9_result_bool(pCtx, 0);
2679 return JX9_OK;
2680 }
2681 /* Perform the requested operation */
2682 rc = pStream->xSync(pDev->pHandle);
2683 /* IO result */
2684 jx9_result_bool(pCtx, rc == JX9_OK);
2685 return JX9_OK;
2686}
2687/*
2688 * bool feof(resource $handle)
2689 * Tests for end-of-file on a file pointer.
2690 * Parameters
2691 * $handle
2692 * The file pointer.
2693 * Return
2694 * Returns TRUE if the file pointer is at EOF.FALSE otherwise
2695 */
2696static int jx9Builtin_feof(jx9_context *pCtx, int nArg, jx9_value **apArg)
2697{
2698 const jx9_io_stream *pStream;
2699 io_private *pDev;
2700 int rc;
2701 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
2702 /* Missing/Invalid arguments */
2703 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
2704 jx9_result_bool(pCtx, 1);
2705 return JX9_OK;
2706 }
2707 /* Extract our private data */
2708 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
2709 /* Make sure we are dealing with a valid io_private instance */
2710 if( IO_PRIVATE_INVALID(pDev) ){
2711 /*Expecting an IO handle */
2712 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
2713 jx9_result_bool(pCtx, 1);
2714 return JX9_OK;
2715 }
2716 /* Point to the target IO stream device */
2717 pStream = pDev->pStream;
2718 if( pStream == 0 ){
2719 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
2720 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
2721 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
2722 );
2723 jx9_result_bool(pCtx, 1);
2724 return JX9_OK;
2725 }
2726 rc = SXERR_EOF;
2727 /* Perform the requested operation */
2728 if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){
2729 /* Data is available */
2730 rc = JX9_OK;
2731 }else{
2732 char zBuf[4096];
2733 jx9_int64 n;
2734 /* Perform a buffered read */
2735 n = pStream->xRead(pDev->pHandle, zBuf, sizeof(zBuf));
2736 if( n > 0 ){
2737 /* Copy buffered data */
2738 SyBlobAppend(&pDev->sBuffer, zBuf, (sxu32)n);
2739 rc = JX9_OK;
2740 }
2741 }
2742 /* EOF or not */
2743 jx9_result_bool(pCtx, rc == SXERR_EOF);
2744 return JX9_OK;
2745}
2746/*
2747 * Read n bytes from the underlying IO stream device.
2748 * Return total numbers of bytes readen on success. A number < 1 on failure
2749 * [i.e: IO error ] or EOF.
2750 */
2751static jx9_int64 StreamRead(io_private *pDev, void *pBuf, jx9_int64 nLen)
2752{
2753 const jx9_io_stream *pStream = pDev->pStream;
2754 char *zBuf = (char *)pBuf;
2755 jx9_int64 n, nRead;
2756 n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft;
2757 if( n > 0 ){
2758 if( n > nLen ){
2759 n = nLen;
2760 }
2761 /* Copy the buffered data */
2762 SyMemcpy(SyBlobDataAt(&pDev->sBuffer, pDev->nOfft), pBuf, (sxu32)n);
2763 /* Update the read offset */
2764 pDev->nOfft += (sxu32)n;
2765 if( pDev->nOfft >= SyBlobLength(&pDev->sBuffer) ){
2766 /* Reset the working buffer so that we avoid excessive memory allocation */
2767 SyBlobReset(&pDev->sBuffer);
2768 pDev->nOfft = 0;
2769 }
2770 nLen -= n;
2771 if( nLen < 1 ){
2772 /* All done */
2773 return n;
2774 }
2775 /* Advance the cursor */
2776 zBuf += n;
2777 }
2778 /* Read without buffering */
2779 nRead = pStream->xRead(pDev->pHandle, zBuf, nLen);
2780 if( nRead > 0 ){
2781 n += nRead;
2782 }else if( n < 1 ){
2783 /* EOF or IO error */
2784 return nRead;
2785 }
2786 return n;
2787}
2788/*
2789 * Extract a single line from the buffered input.
2790 */
2791static sxi32 GetLine(io_private *pDev, jx9_int64 *pLen, const char **pzLine)
2792{
2793 const char *zIn, *zEnd, *zPtr;
2794 zIn = (const char *)SyBlobDataAt(&pDev->sBuffer, pDev->nOfft);
2795 zEnd = &zIn[SyBlobLength(&pDev->sBuffer)-pDev->nOfft];
2796 zPtr = zIn;
2797 while( zIn < zEnd ){
2798 if( zIn[0] == '\n' ){
2799 /* Line found */
2800 zIn++; /* Include the line ending as requested by the JX9 specification */
2801 *pLen = (jx9_int64)(zIn-zPtr);
2802 *pzLine = zPtr;
2803 return SXRET_OK;
2804 }
2805 zIn++;
2806 }
2807 /* No line were found */
2808 return SXERR_NOTFOUND;
2809}
2810/*
2811 * Read a single line from the underlying IO stream device.
2812 */
2813static jx9_int64 StreamReadLine(io_private *pDev, const char **pzData, jx9_int64 nMaxLen)
2814{
2815 const jx9_io_stream *pStream = pDev->pStream;
2816 char zBuf[8192];
2817 jx9_int64 n;
2818 sxi32 rc;
2819 n = 0;
2820 if( pDev->nOfft >= SyBlobLength(&pDev->sBuffer) ){
2821 /* Reset the working buffer so that we avoid excessive memory allocation */
2822 SyBlobReset(&pDev->sBuffer);
2823 pDev->nOfft = 0;
2824 }
2825 if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){
2826 /* Check if there is a line */
2827 rc = GetLine(pDev, &n, pzData);
2828 if( rc == SXRET_OK ){
2829 /* Got line, update the cursor */
2830 pDev->nOfft += (sxu32)n;
2831 return n;
2832 }
2833 }
2834 /* Perform the read operation until a new line is extracted or length
2835 * limit is reached.
2836 */
2837 for(;;){
2838 n = pStream->xRead(pDev->pHandle, zBuf, (nMaxLen > 0 && nMaxLen < sizeof(zBuf)) ? nMaxLen : sizeof(zBuf));
2839 if( n < 1 ){
2840 /* EOF or IO error */
2841 break;
2842 }
2843 /* Append the data just read */
2844 SyBlobAppend(&pDev->sBuffer, zBuf, (sxu32)n);
2845 /* Try to extract a line */
2846 rc = GetLine(pDev, &n, pzData);
2847 if( rc == SXRET_OK ){
2848 /* Got one, return immediately */
2849 pDev->nOfft += (sxu32)n;
2850 return n;
2851 }
2852 if( nMaxLen > 0 && (SyBlobLength(&pDev->sBuffer) - pDev->nOfft >= nMaxLen) ){
2853 /* Read limit reached, return the available data */
2854 *pzData = (const char *)SyBlobDataAt(&pDev->sBuffer, pDev->nOfft);
2855 n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft;
2856 /* Reset the working buffer */
2857 SyBlobReset(&pDev->sBuffer);
2858 pDev->nOfft = 0;
2859 return n;
2860 }
2861 }
2862 if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){
2863 /* Read limit reached, return the available data */
2864 *pzData = (const char *)SyBlobDataAt(&pDev->sBuffer, pDev->nOfft);
2865 n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft;
2866 /* Reset the working buffer */
2867 SyBlobReset(&pDev->sBuffer);
2868 pDev->nOfft = 0;
2869 }
2870 return n;
2871}
2872/*
2873 * Open an IO stream handle.
2874 * Notes on stream:
2875 * According to the JX9 reference manual.
2876 * In its simplest definition, a stream is a resource object which exhibits streamable behavior.
2877 * That is, it can be read from or written to in a linear fashion, and may be able to fseek()
2878 * to an arbitrary locations within the stream.
2879 * A wrapper is additional code which tells the stream how to handle specific protocols/encodings.
2880 * For example, the http wrapper knows how to translate a URL into an HTTP/1.0 request for a file
2881 * on a remote server.
2882 * A stream is referenced as: scheme://target
2883 * scheme(string) - The name of the wrapper to be used. Examples include: file, http...
2884 * If no wrapper is specified, the function default is used (typically file://).
2885 * target - Depends on the wrapper used. For filesystem related streams this is typically a path
2886 * and filename of the desired file. For network related streams this is typically a hostname, often
2887 * with a path appended.
2888 *
2889 * Note that JX9 IO streams looks like JX9 streams but their implementation differ greately.
2890 * Please refer to the official documentation for a full discussion.
2891 * This function return a handle on success. Otherwise null.
2892 */
2893JX9_PRIVATE void * jx9StreamOpenHandle(jx9_vm *pVm, const jx9_io_stream *pStream, const char *zFile,
2894 int iFlags, int use_include, jx9_value *pResource, int bPushInclude, int *pNew)
2895{
2896 void *pHandle = 0; /* cc warning */
2897 SyString sFile;
2898 int rc;
2899 if( pStream == 0 ){
2900 /* No such stream device */
2901 return 0;
2902 }
2903 SyStringInitFromBuf(&sFile, zFile, SyStrlen(zFile));
2904 if( use_include ){
2905 if( sFile.zString[0] == '/' ||
2906#ifdef __WINNT__
2907 (sFile.nByte > 2 && sFile.zString[1] == ':' && (sFile.zString[2] == '\\' || sFile.zString[2] == '/') ) ||
2908#endif
2909 (sFile.nByte > 1 && sFile.zString[0] == '.' && sFile.zString[1] == '/') ||
2910 (sFile.nByte > 2 && sFile.zString[0] == '.' && sFile.zString[1] == '.' && sFile.zString[2] == '/') ){
2911 /* Open the file directly */
2912 rc = pStream->xOpen(zFile, iFlags, pResource, &pHandle);
2913 }else{
2914 SyString *pPath;
2915 SyBlob sWorker;
2916#ifdef __WINNT__
2917 static const int c = '\\';
2918#else
2919 static const int c = '/';
2920#endif
2921 /* Init the path builder working buffer */
2922 SyBlobInit(&sWorker, &pVm->sAllocator);
2923 /* Build a path from the set of include path */
2924 SySetResetCursor(&pVm->aPaths);
2925 rc = SXERR_IO;
2926 while( SXRET_OK == SySetGetNextEntry(&pVm->aPaths, (void **)&pPath) ){
2927 /* Build full path */
2928 SyBlobFormat(&sWorker, "%z%c%z", pPath, c, &sFile);
2929 /* Append null terminator */
2930 if( SXRET_OK != SyBlobNullAppend(&sWorker) ){
2931 continue;
2932 }
2933 /* Try to open the file */
2934 rc = pStream->xOpen((const char *)SyBlobData(&sWorker), iFlags, pResource, &pHandle);
2935 if( rc == JX9_OK ){
2936 if( bPushInclude ){
2937 /* Mark as included */
2938 jx9VmPushFilePath(pVm, (const char *)SyBlobData(&sWorker), SyBlobLength(&sWorker), FALSE, pNew);
2939 }
2940 break;
2941 }
2942 /* Reset the working buffer */
2943 SyBlobReset(&sWorker);
2944 /* Check the next path */
2945 }
2946 SyBlobRelease(&sWorker);
2947 }
2948 if( rc == JX9_OK ){
2949 if( bPushInclude ){
2950 /* Mark as included */
2951 jx9VmPushFilePath(pVm, sFile.zString, sFile.nByte, FALSE, pNew);
2952 }
2953 }
2954 }else{
2955 /* Open the URI direcly */
2956 rc = pStream->xOpen(zFile, iFlags, pResource, &pHandle);
2957 }
2958 if( rc != JX9_OK ){
2959 /* IO error */
2960 return 0;
2961 }
2962 /* Return the file handle */
2963 return pHandle;
2964}
2965/*
2966 * Read the whole contents of an open IO stream handle [i.e local file/URL..]
2967 * Store the read data in the given BLOB (last argument).
2968 * The read operation is stopped when he hit the EOF or an IO error occurs.
2969 */
2970JX9_PRIVATE sxi32 jx9StreamReadWholeFile(void *pHandle, const jx9_io_stream *pStream, SyBlob *pOut)
2971{
2972 jx9_int64 nRead;
2973 char zBuf[8192]; /* 8K */
2974 int rc;
2975 /* Perform the requested operation */
2976 for(;;){
2977 nRead = pStream->xRead(pHandle, zBuf, sizeof(zBuf));
2978 if( nRead < 1 ){
2979 /* EOF or IO error */
2980 break;
2981 }
2982 /* Append contents */
2983 rc = SyBlobAppend(pOut, zBuf, (sxu32)nRead);
2984 if( rc != SXRET_OK ){
2985 break;
2986 }
2987 }
2988 return SyBlobLength(pOut) > 0 ? SXRET_OK : -1;
2989}
2990/*
2991 * Close an open IO stream handle [i.e local file/URI..].
2992 */
2993JX9_PRIVATE void jx9StreamCloseHandle(const jx9_io_stream *pStream, void *pHandle)
2994{
2995 if( pStream->xClose ){
2996 pStream->xClose(pHandle);
2997 }
2998}
2999/*
3000 * string fgetc(resource $handle)
3001 * Gets a character from the given file pointer.
3002 * Parameters
3003 * $handle
3004 * The file pointer.
3005 * Return
3006 * Returns a string containing a single character read from the file
3007 * pointed to by handle. Returns FALSE on EOF.
3008 * WARNING
3009 * This operation is extremely slow.Avoid using it.
3010 */
3011static int jx9Builtin_fgetc(jx9_context *pCtx, int nArg, jx9_value **apArg)
3012{
3013 const jx9_io_stream *pStream;
3014 io_private *pDev;
3015 int c, n;
3016 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
3017 /* Missing/Invalid arguments, return FALSE */
3018 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
3019 jx9_result_bool(pCtx, 0);
3020 return JX9_OK;
3021 }
3022 /* Extract our private data */
3023 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
3024 /* Make sure we are dealing with a valid io_private instance */
3025 if( IO_PRIVATE_INVALID(pDev) ){
3026 /*Expecting an IO handle */
3027 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
3028 jx9_result_bool(pCtx, 0);
3029 return JX9_OK;
3030 }
3031 /* Point to the target IO stream device */
3032 pStream = pDev->pStream;
3033 if( pStream == 0 ){
3034 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
3035 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
3036 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
3037 );
3038 jx9_result_bool(pCtx, 0);
3039 return JX9_OK;
3040 }
3041 /* Perform the requested operation */
3042 n = (int)StreamRead(pDev, (void *)&c, sizeof(char));
3043 /* IO result */
3044 if( n < 1 ){
3045 /* EOF or error, return FALSE */
3046 jx9_result_bool(pCtx, 0);
3047 }else{
3048 /* Return the string holding the character */
3049 jx9_result_string(pCtx, (const char *)&c, sizeof(char));
3050 }
3051 return JX9_OK;
3052}
3053/*
3054 * string fgets(resource $handle[, int64 $length ])
3055 * Gets line from file pointer.
3056 * Parameters
3057 * $handle
3058 * The file pointer.
3059 * $length
3060 * Reading ends when length - 1 bytes have been read, on a newline
3061 * (which is included in the return value), or on EOF (whichever comes first).
3062 * If no length is specified, it will keep reading from the stream until it reaches
3063 * the end of the line.
3064 * Return
3065 * Returns a string of up to length - 1 bytes read from the file pointed to by handle.
3066 * If there is no more data to read in the file pointer, then FALSE is returned.
3067 * If an error occurs, FALSE is returned.
3068 */
3069static int jx9Builtin_fgets(jx9_context *pCtx, int nArg, jx9_value **apArg)
3070{
3071 const jx9_io_stream *pStream;
3072 const char *zLine;
3073 io_private *pDev;
3074 jx9_int64 n, nLen;
3075 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
3076 /* Missing/Invalid arguments, return FALSE */
3077 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
3078 jx9_result_bool(pCtx, 0);
3079 return JX9_OK;
3080 }
3081 /* Extract our private data */
3082 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
3083 /* Make sure we are dealing with a valid io_private instance */
3084 if( IO_PRIVATE_INVALID(pDev) ){
3085 /*Expecting an IO handle */
3086 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
3087 jx9_result_bool(pCtx, 0);
3088 return JX9_OK;
3089 }
3090 /* Point to the target IO stream device */
3091 pStream = pDev->pStream;
3092 if( pStream == 0 ){
3093 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
3094 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
3095 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
3096 );
3097 jx9_result_bool(pCtx, 0);
3098 return JX9_OK;
3099 }
3100 nLen = -1;
3101 if( nArg > 1 ){
3102 /* Maximum data to read */
3103 nLen = jx9_value_to_int64(apArg[1]);
3104 }
3105 /* Perform the requested operation */
3106 n = StreamReadLine(pDev, &zLine, nLen);
3107 if( n < 1 ){
3108 /* EOF or IO error, return FALSE */
3109 jx9_result_bool(pCtx, 0);
3110 }else{
3111 /* Return the freshly extracted line */
3112 jx9_result_string(pCtx, zLine, (int)n);
3113 }
3114 return JX9_OK;
3115}
3116/*
3117 * string fread(resource $handle, int64 $length)
3118 * Binary-safe file read.
3119 * Parameters
3120 * $handle
3121 * The file pointer.
3122 * $length
3123 * Up to length number of bytes read.
3124 * Return
3125 * The data readen on success or FALSE on failure.
3126 */
3127static int jx9Builtin_fread(jx9_context *pCtx, int nArg, jx9_value **apArg)
3128{
3129 const jx9_io_stream *pStream;
3130 io_private *pDev;
3131 jx9_int64 nRead;
3132 void *pBuf;
3133 int nLen;
3134 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
3135 /* Missing/Invalid arguments, return FALSE */
3136 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
3137 jx9_result_bool(pCtx, 0);
3138 return JX9_OK;
3139 }
3140 /* Extract our private data */
3141 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
3142 /* Make sure we are dealing with a valid io_private instance */
3143 if( IO_PRIVATE_INVALID(pDev) ){
3144 /*Expecting an IO handle */
3145 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
3146 jx9_result_bool(pCtx, 0);
3147 return JX9_OK;
3148 }
3149 /* Point to the target IO stream device */
3150 pStream = pDev->pStream;
3151 if( pStream == 0 ){
3152 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
3153 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
3154 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
3155 );
3156 jx9_result_bool(pCtx, 0);
3157 return JX9_OK;
3158 }
3159 nLen = 4096;
3160 if( nArg > 1 ){
3161 nLen = jx9_value_to_int(apArg[1]);
3162 if( nLen < 1 ){
3163 /* Invalid length, set a default length */
3164 nLen = 4096;
3165 }
3166 }
3167 /* Allocate enough buffer */
3168 pBuf = jx9_context_alloc_chunk(pCtx, (unsigned int)nLen, FALSE, FALSE);
3169 if( pBuf == 0 ){
3170 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
3171 jx9_result_bool(pCtx, 0);
3172 return JX9_OK;
3173 }
3174 /* Perform the requested operation */
3175 nRead = StreamRead(pDev, pBuf, (jx9_int64)nLen);
3176 if( nRead < 1 ){
3177 /* Nothing read, return FALSE */
3178 jx9_result_bool(pCtx, 0);
3179 }else{
3180 /* Make a copy of the data just read */
3181 jx9_result_string(pCtx, (const char *)pBuf, (int)nRead);
3182 }
3183 /* Release the buffer */
3184 jx9_context_free_chunk(pCtx, pBuf);
3185 return JX9_OK;
3186}
3187/*
3188 * array fgetcsv(resource $handle [, int $length = 0
3189 * [, string $delimiter = ', '[, string $enclosure = '"'[, string $escape='\\']]]])
3190 * Gets line from file pointer and parse for CSV fields.
3191 * Parameters
3192 * $handle
3193 * The file pointer.
3194 * $length
3195 * Reading ends when length - 1 bytes have been read, on a newline
3196 * (which is included in the return value), or on EOF (whichever comes first).
3197 * If no length is specified, it will keep reading from the stream until it reaches
3198 * the end of the line.
3199 * $delimiter
3200 * Set the field delimiter (one character only).
3201 * $enclosure
3202 * Set the field enclosure character (one character only).
3203 * $escape
3204 * Set the escape character (one character only). Defaults as a backslash (\)
3205 * Return
3206 * Returns a string of up to length - 1 bytes read from the file pointed to by handle.
3207 * If there is no more data to read in the file pointer, then FALSE is returned.
3208 * If an error occurs, FALSE is returned.
3209 */
3210static int jx9Builtin_fgetcsv(jx9_context *pCtx, int nArg, jx9_value **apArg)
3211{
3212 const jx9_io_stream *pStream;
3213 const char *zLine;
3214 io_private *pDev;
3215 jx9_int64 n, nLen;
3216 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
3217 /* Missing/Invalid arguments, return FALSE */
3218 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
3219 jx9_result_bool(pCtx, 0);
3220 return JX9_OK;
3221 }
3222 /* Extract our private data */
3223 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
3224 /* Make sure we are dealing with a valid io_private instance */
3225 if( IO_PRIVATE_INVALID(pDev) ){
3226 /*Expecting an IO handle */
3227 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
3228 jx9_result_bool(pCtx, 0);
3229 return JX9_OK;
3230 }
3231 /* Point to the target IO stream device */
3232 pStream = pDev->pStream;
3233 if( pStream == 0 ){
3234 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
3235 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
3236 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
3237 );
3238 jx9_result_bool(pCtx, 0);
3239 return JX9_OK;
3240 }
3241 nLen = -1;
3242 if( nArg > 1 ){
3243 /* Maximum data to read */
3244 nLen = jx9_value_to_int64(apArg[1]);
3245 }
3246 /* Perform the requested operation */
3247 n = StreamReadLine(pDev, &zLine, nLen);
3248 if( n < 1 ){
3249 /* EOF or IO error, return FALSE */
3250 jx9_result_bool(pCtx, 0);
3251 }else{
3252 jx9_value *pArray;
3253 int delim = ','; /* Delimiter */
3254 int encl = '"' ; /* Enclosure */
3255 int escape = '\\'; /* Escape character */
3256 if( nArg > 2 ){
3257 const char *zPtr;
3258 int i;
3259 if( jx9_value_is_string(apArg[2]) ){
3260 /* Extract the delimiter */
3261 zPtr = jx9_value_to_string(apArg[2], &i);
3262 if( i > 0 ){
3263 delim = zPtr[0];
3264 }
3265 }
3266 if( nArg > 3 ){
3267 if( jx9_value_is_string(apArg[3]) ){
3268 /* Extract the enclosure */
3269 zPtr = jx9_value_to_string(apArg[3], &i);
3270 if( i > 0 ){
3271 encl = zPtr[0];
3272 }
3273 }
3274 if( nArg > 4 ){
3275 if( jx9_value_is_string(apArg[4]) ){
3276 /* Extract the escape character */
3277 zPtr = jx9_value_to_string(apArg[4], &i);
3278 if( i > 0 ){
3279 escape = zPtr[0];
3280 }
3281 }
3282 }
3283 }
3284 }
3285 /* Create our array */
3286 pArray = jx9_context_new_array(pCtx);
3287 if( pArray == 0 ){
3288 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
3289 jx9_result_null(pCtx);
3290 return JX9_OK;
3291 }
3292 /* Parse the raw input */
3293 jx9ProcessCsv(zLine, (int)n, delim, encl, escape, jx9CsvConsumer, pArray);
3294 /* Return the freshly created array */
3295 jx9_result_value(pCtx, pArray);
3296 }
3297 return JX9_OK;
3298}
3299/*
3300 * string fgetss(resource $handle [, int $length [, string $allowable_tags ]])
3301 * Gets line from file pointer and strip HTML tags.
3302 * Parameters
3303 * $handle
3304 * The file pointer.
3305 * $length
3306 * Reading ends when length - 1 bytes have been read, on a newline
3307 * (which is included in the return value), or on EOF (whichever comes first).
3308 * If no length is specified, it will keep reading from the stream until it reaches
3309 * the end of the line.
3310 * $allowable_tags
3311 * You can use the optional second parameter to specify tags which should not be stripped.
3312 * Return
3313 * Returns a string of up to length - 1 bytes read from the file pointed to by
3314 * handle, with all HTML and JX9 code stripped. If an error occurs, returns FALSE.
3315 */
3316static int jx9Builtin_fgetss(jx9_context *pCtx, int nArg, jx9_value **apArg)
3317{
3318 const jx9_io_stream *pStream;
3319 const char *zLine;
3320 io_private *pDev;
3321 jx9_int64 n, nLen;
3322 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
3323 /* Missing/Invalid arguments, return FALSE */
3324 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
3325 jx9_result_bool(pCtx, 0);
3326 return JX9_OK;
3327 }
3328 /* Extract our private data */
3329 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
3330 /* Make sure we are dealing with a valid io_private instance */
3331 if( IO_PRIVATE_INVALID(pDev) ){
3332 /*Expecting an IO handle */
3333 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
3334 jx9_result_bool(pCtx, 0);
3335 return JX9_OK;
3336 }
3337 /* Point to the target IO stream device */
3338 pStream = pDev->pStream;
3339 if( pStream == 0 ){
3340 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
3341 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
3342 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
3343 );
3344 jx9_result_bool(pCtx, 0);
3345 return JX9_OK;
3346 }
3347 nLen = -1;
3348 if( nArg > 1 ){
3349 /* Maximum data to read */
3350 nLen = jx9_value_to_int64(apArg[1]);
3351 }
3352 /* Perform the requested operation */
3353 n = StreamReadLine(pDev, &zLine, nLen);
3354 if( n < 1 ){
3355 /* EOF or IO error, return FALSE */
3356 jx9_result_bool(pCtx, 0);
3357 }else{
3358 const char *zTaglist = 0;
3359 int nTaglen = 0;
3360 if( nArg > 2 && jx9_value_is_string(apArg[2]) ){
3361 /* Allowed tag */
3362 zTaglist = jx9_value_to_string(apArg[2], &nTaglen);
3363 }
3364 /* Process data just read */
3365 jx9StripTagsFromString(pCtx, zLine, (int)n, zTaglist, nTaglen);
3366 }
3367 return JX9_OK;
3368}
3369/*
3370 * string readdir(resource $dir_handle)
3371 * Read entry from directory handle.
3372 * Parameter
3373 * $dir_handle
3374 * The directory handle resource previously opened with opendir().
3375 * Return
3376 * Returns the filename on success or FALSE on failure.
3377 */
3378static int jx9Builtin_readdir(jx9_context *pCtx, int nArg, jx9_value **apArg)
3379{
3380 const jx9_io_stream *pStream;
3381 io_private *pDev;
3382 int rc;
3383 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
3384 /* Missing/Invalid arguments, return FALSE */
3385 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
3386 jx9_result_bool(pCtx, 0);
3387 return JX9_OK;
3388 }
3389 /* Extract our private data */
3390 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
3391 /* Make sure we are dealing with a valid io_private instance */
3392 if( IO_PRIVATE_INVALID(pDev) ){
3393 /*Expecting an IO handle */
3394 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
3395 jx9_result_bool(pCtx, 0);
3396 return JX9_OK;
3397 }
3398 /* Point to the target IO stream device */
3399 pStream = pDev->pStream;
3400 if( pStream == 0 || pStream->xReadDir == 0 ){
3401 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
3402 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
3403 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
3404 );
3405 jx9_result_bool(pCtx, 0);
3406 return JX9_OK;
3407 }
3408 jx9_result_bool(pCtx, 0);
3409 /* Perform the requested operation */
3410 rc = pStream->xReadDir(pDev->pHandle, pCtx);
3411 if( rc != JX9_OK ){
3412 /* Return FALSE */
3413 jx9_result_bool(pCtx, 0);
3414 }
3415 return JX9_OK;
3416}
3417/*
3418 * void rewinddir(resource $dir_handle)
3419 * Rewind directory handle.
3420 * Parameter
3421 * $dir_handle
3422 * The directory handle resource previously opened with opendir().
3423 * Return
3424 * FALSE on failure.
3425 */
3426static int jx9Builtin_rewinddir(jx9_context *pCtx, int nArg, jx9_value **apArg)
3427{
3428 const jx9_io_stream *pStream;
3429 io_private *pDev;
3430 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
3431 /* Missing/Invalid arguments, return FALSE */
3432 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
3433 jx9_result_bool(pCtx, 0);
3434 return JX9_OK;
3435 }
3436 /* Extract our private data */
3437 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
3438 /* Make sure we are dealing with a valid io_private instance */
3439 if( IO_PRIVATE_INVALID(pDev) ){
3440 /*Expecting an IO handle */
3441 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
3442 jx9_result_bool(pCtx, 0);
3443 return JX9_OK;
3444 }
3445 /* Point to the target IO stream device */
3446 pStream = pDev->pStream;
3447 if( pStream == 0 || pStream->xRewindDir == 0 ){
3448 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
3449 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
3450 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
3451 );
3452 jx9_result_bool(pCtx, 0);
3453 return JX9_OK;
3454 }
3455 /* Perform the requested operation */
3456 pStream->xRewindDir(pDev->pHandle);
3457 return JX9_OK;
3458 }
3459/* Forward declaration */
3460static void InitIOPrivate(jx9_vm *pVm, const jx9_io_stream *pStream, io_private *pOut);
3461static void ReleaseIOPrivate(jx9_context *pCtx, io_private *pDev);
3462/*
3463 * void closedir(resource $dir_handle)
3464 * Close directory handle.
3465 * Parameter
3466 * $dir_handle
3467 * The directory handle resource previously opened with opendir().
3468 * Return
3469 * FALSE on failure.
3470 */
3471static int jx9Builtin_closedir(jx9_context *pCtx, int nArg, jx9_value **apArg)
3472{
3473 const jx9_io_stream *pStream;
3474 io_private *pDev;
3475 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
3476 /* Missing/Invalid arguments, return FALSE */
3477 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
3478 jx9_result_bool(pCtx, 0);
3479 return JX9_OK;
3480 }
3481 /* Extract our private data */
3482 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
3483 /* Make sure we are dealing with a valid io_private instance */
3484 if( IO_PRIVATE_INVALID(pDev) ){
3485 /*Expecting an IO handle */
3486 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
3487 jx9_result_bool(pCtx, 0);
3488 return JX9_OK;
3489 }
3490 /* Point to the target IO stream device */
3491 pStream = pDev->pStream;
3492 if( pStream == 0 || pStream->xCloseDir == 0 ){
3493 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
3494 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
3495 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
3496 );
3497 jx9_result_bool(pCtx, 0);
3498 return JX9_OK;
3499 }
3500 /* Perform the requested operation */
3501 pStream->xCloseDir(pDev->pHandle);
3502 /* Release the private stucture */
3503 ReleaseIOPrivate(pCtx, pDev);
3504 jx9MemObjRelease(apArg[0]);
3505 return JX9_OK;
3506 }
3507/*
3508 * resource opendir(string $path[, resource $context])
3509 * Open directory handle.
3510 * Parameters
3511 * $path
3512 * The directory path that is to be opened.
3513 * $context
3514 * A context stream resource.
3515 * Return
3516 * A directory handle resource on success, or FALSE on failure.
3517 */
3518static int jx9Builtin_opendir(jx9_context *pCtx, int nArg, jx9_value **apArg)
3519{
3520 const jx9_io_stream *pStream;
3521 const char *zPath;
3522 io_private *pDev;
3523 int iLen, rc;
3524 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
3525 /* Missing/Invalid arguments, return FALSE */
3526 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a directory path");
3527 jx9_result_bool(pCtx, 0);
3528 return JX9_OK;
3529 }
3530 /* Extract the target path */
3531 zPath = jx9_value_to_string(apArg[0], &iLen);
3532 /* Try to extract a stream */
3533 pStream = jx9VmGetStreamDevice(pCtx->pVm, &zPath, iLen);
3534 if( pStream == 0 ){
3535 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
3536 "No stream device is associated with the given path(%s)", zPath);
3537 jx9_result_bool(pCtx, 0);
3538 return JX9_OK;
3539 }
3540 if( pStream->xOpenDir == 0 ){
3541 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
3542 "IO routine(%s) not implemented in the underlying stream(%s) device",
3543 jx9_function_name(pCtx), pStream->zName
3544 );
3545 jx9_result_bool(pCtx, 0);
3546 return JX9_OK;
3547 }
3548 /* Allocate a new IO private instance */
3549 pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE);
3550 if( pDev == 0 ){
3551 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
3552 jx9_result_bool(pCtx, 0);
3553 return JX9_OK;
3554 }
3555 /* Initialize the structure */
3556 InitIOPrivate(pCtx->pVm, pStream, pDev);
3557 /* Open the target directory */
3558 rc = pStream->xOpenDir(zPath, nArg > 1 ? apArg[1] : 0, &pDev->pHandle);
3559 if( rc != JX9_OK ){
3560 /* IO error, return FALSE */
3561 ReleaseIOPrivate(pCtx, pDev);
3562 jx9_result_bool(pCtx, 0);
3563 }else{
3564 /* Return the handle as a resource */
3565 jx9_result_resource(pCtx, pDev);
3566 }
3567 return JX9_OK;
3568}
3569/*
3570 * int readfile(string $filename[, bool $use_include_path = false [, resource $context ]])
3571 * Reads a file and writes it to the output buffer.
3572 * Parameters
3573 * $filename
3574 * The filename being read.
3575 * $use_include_path
3576 * You can use the optional second parameter and set it to
3577 * TRUE, if you want to search for the file in the include_path, too.
3578 * $context
3579 * A context stream resource.
3580 * Return
3581 * The number of bytes read from the file on success or FALSE on failure.
3582 */
3583static int jx9Builtin_readfile(jx9_context *pCtx, int nArg, jx9_value **apArg)
3584{
3585 int use_include = FALSE;
3586 const jx9_io_stream *pStream;
3587 jx9_int64 n, nRead;
3588 const char *zFile;
3589 char zBuf[8192];
3590 void *pHandle;
3591 int rc, nLen;
3592 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
3593 /* Missing/Invalid arguments, return FALSE */
3594 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
3595 jx9_result_bool(pCtx, 0);
3596 return JX9_OK;
3597 }
3598 /* Extract the file path */
3599 zFile = jx9_value_to_string(apArg[0], &nLen);
3600 /* Point to the target IO stream device */
3601 pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
3602 if( pStream == 0 ){
3603 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
3604 jx9_result_bool(pCtx, 0);
3605 return JX9_OK;
3606 }
3607 if( nArg > 1 ){
3608 use_include = jx9_value_to_bool(apArg[1]);
3609 }
3610 /* Try to open the file in read-only mode */
3611 pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY,
3612 use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0);
3613 if( pHandle == 0 ){
3614 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
3615 jx9_result_bool(pCtx, 0);
3616 return JX9_OK;
3617 }
3618 /* Perform the requested operation */
3619 nRead = 0;
3620 for(;;){
3621 n = pStream->xRead(pHandle, zBuf, sizeof(zBuf));
3622 if( n < 1 ){
3623 /* EOF or IO error, break immediately */
3624 break;
3625 }
3626 /* Output data */
3627 rc = jx9_context_output(pCtx, zBuf, (int)n);
3628 if( rc == JX9_ABORT ){
3629 break;
3630 }
3631 /* Increment counter */
3632 nRead += n;
3633 }
3634 /* Close the stream */
3635 jx9StreamCloseHandle(pStream, pHandle);
3636 /* Total number of bytes readen */
3637 jx9_result_int64(pCtx, nRead);
3638 return JX9_OK;
3639}
3640/*
3641 * string file_get_contents(string $filename[, bool $use_include_path = false
3642 * [, resource $context [, int $offset = -1 [, int $maxlen ]]]])
3643 * Reads entire file into a string.
3644 * Parameters
3645 * $filename
3646 * The filename being read.
3647 * $use_include_path
3648 * You can use the optional second parameter and set it to
3649 * TRUE, if you want to search for the file in the include_path, too.
3650 * $context
3651 * A context stream resource.
3652 * $offset
3653 * The offset where the reading starts on the original stream.
3654 * $maxlen
3655 * Maximum length of data read. The default is to read until end of file
3656 * is reached. Note that this parameter is applied to the stream processed by the filters.
3657 * Return
3658 * The function returns the read data or FALSE on failure.
3659 */
3660static int jx9Builtin_file_get_contents(jx9_context *pCtx, int nArg, jx9_value **apArg)
3661{
3662 const jx9_io_stream *pStream;
3663 jx9_int64 n, nRead, nMaxlen;
3664 int use_include = FALSE;
3665 const char *zFile;
3666 char zBuf[8192];
3667 void *pHandle;
3668 int nLen;
3669
3670 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
3671 /* Missing/Invalid arguments, return FALSE */
3672 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
3673 jx9_result_bool(pCtx, 0);
3674 return JX9_OK;
3675 }
3676 /* Extract the file path */
3677 zFile = jx9_value_to_string(apArg[0], &nLen);
3678 /* Point to the target IO stream device */
3679 pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
3680 if( pStream == 0 ){
3681 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
3682 jx9_result_bool(pCtx, 0);
3683 return JX9_OK;
3684 }
3685 nMaxlen = -1;
3686 if( nArg > 1 ){
3687 use_include = jx9_value_to_bool(apArg[1]);
3688 }
3689 /* Try to open the file in read-only mode */
3690 pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0);
3691 if( pHandle == 0 ){
3692 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
3693 jx9_result_bool(pCtx, 0);
3694 return JX9_OK;
3695 }
3696 if( nArg > 3 ){
3697 /* Extract the offset */
3698 n = jx9_value_to_int64(apArg[3]);
3699 if( n > 0 ){
3700 if( pStream->xSeek ){
3701 /* Seek to the desired offset */
3702 pStream->xSeek(pHandle, n, 0/*SEEK_SET*/);
3703 }
3704 }
3705 if( nArg > 4 ){
3706 /* Maximum data to read */
3707 nMaxlen = jx9_value_to_int64(apArg[4]);
3708 }
3709 }
3710 /* Perform the requested operation */
3711 nRead = 0;
3712 for(;;){
3713 n = pStream->xRead(pHandle, zBuf,
3714 (nMaxlen > 0 && (nMaxlen < sizeof(zBuf))) ? nMaxlen : sizeof(zBuf));
3715 if( n < 1 ){
3716 /* EOF or IO error, break immediately */
3717 break;
3718 }
3719 /* Append data */
3720 jx9_result_string(pCtx, zBuf, (int)n);
3721 /* Increment read counter */
3722 nRead += n;
3723 if( nMaxlen > 0 && nRead >= nMaxlen ){
3724 /* Read limit reached */
3725 break;
3726 }
3727 }
3728 /* Close the stream */
3729 jx9StreamCloseHandle(pStream, pHandle);
3730 /* Check if we have read something */
3731 if( jx9_context_result_buf_length(pCtx) < 1 ){
3732 /* Nothing read, return FALSE */
3733 jx9_result_bool(pCtx, 0);
3734 }
3735 return JX9_OK;
3736}
3737/*
3738 * int file_put_contents(string $filename, mixed $data[, int $flags = 0[, resource $context]])
3739 * Write a string to a file.
3740 * Parameters
3741 * $filename
3742 * Path to the file where to write the data.
3743 * $data
3744 * The data to write(Must be a string).
3745 * $flags
3746 * The value of flags can be any combination of the following
3747 * flags, joined with the binary OR (|) operator.
3748 * FILE_USE_INCLUDE_PATH Search for filename in the include directory. See include_path for more information.
3749 * FILE_APPEND If file filename already exists, append the data to the file instead of overwriting it.
3750 * LOCK_EX Acquire an exclusive lock on the file while proceeding to the writing.
3751 * context
3752 * A context stream resource.
3753 * Return
3754 * The function returns the number of bytes that were written to the file, or FALSE on failure.
3755 */
3756static int jx9Builtin_file_put_contents(jx9_context *pCtx, int nArg, jx9_value **apArg)
3757{
3758 int use_include = FALSE;
3759 const jx9_io_stream *pStream;
3760 const char *zFile;
3761 const char *zData;
3762 int iOpenFlags;
3763 void *pHandle;
3764 int iFlags;
3765 int nLen;
3766
3767 if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){
3768 /* Missing/Invalid arguments, return FALSE */
3769 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
3770 jx9_result_bool(pCtx, 0);
3771 return JX9_OK;
3772 }
3773 /* Extract the file path */
3774 zFile = jx9_value_to_string(apArg[0], &nLen);
3775 /* Point to the target IO stream device */
3776 pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
3777 if( pStream == 0 ){
3778 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
3779 jx9_result_bool(pCtx, 0);
3780 return JX9_OK;
3781 }
3782 /* Data to write */
3783 zData = jx9_value_to_string(apArg[1], &nLen);
3784 if( nLen < 1 ){
3785 /* Nothing to write, return immediately */
3786 jx9_result_bool(pCtx, 0);
3787 return JX9_OK;
3788 }
3789 /* Try to open the file in read-write mode */
3790 iOpenFlags = JX9_IO_OPEN_CREATE|JX9_IO_OPEN_RDWR|JX9_IO_OPEN_TRUNC;
3791 /* Extract the flags */
3792 iFlags = 0;
3793 if( nArg > 2 ){
3794 iFlags = jx9_value_to_int(apArg[2]);
3795 if( iFlags & 0x01 /*FILE_USE_INCLUDE_PATH*/){
3796 use_include = TRUE;
3797 }
3798 if( iFlags & 0x08 /* FILE_APPEND */){
3799 /* If the file already exists, append the data to the file
3800 * instead of overwriting it.
3801 */
3802 iOpenFlags &= ~JX9_IO_OPEN_TRUNC;
3803 /* Append mode */
3804 iOpenFlags |= JX9_IO_OPEN_APPEND;
3805 }
3806 }
3807 pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, iOpenFlags, use_include,
3808 nArg > 3 ? apArg[3] : 0, FALSE, FALSE);
3809 if( pHandle == 0 ){
3810 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
3811 jx9_result_bool(pCtx, 0);
3812 return JX9_OK;
3813 }
3814 if( pStream->xWrite ){
3815 jx9_int64 n;
3816 if( (iFlags & 0x01/* LOCK_EX */) && pStream->xLock ){
3817 /* Try to acquire an exclusive lock */
3818 pStream->xLock(pHandle, 1/* LOCK_EX */);
3819 }
3820 /* Perform the write operation */
3821 n = pStream->xWrite(pHandle, (const void *)zData, nLen);
3822 if( n < 1 ){
3823 /* IO error, return FALSE */
3824 jx9_result_bool(pCtx, 0);
3825 }else{
3826 /* Total number of bytes written */
3827 jx9_result_int64(pCtx, n);
3828 }
3829 }else{
3830 /* Read-only stream */
3831 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR,
3832 "Read-only stream(%s): Cannot perform write operation",
3833 pStream ? pStream->zName : "null_stream"
3834 );
3835 jx9_result_bool(pCtx, 0);
3836 }
3837 /* Close the handle */
3838 jx9StreamCloseHandle(pStream, pHandle);
3839 return JX9_OK;
3840}
3841/*
3842 * array file(string $filename[, int $flags = 0[, resource $context]])
3843 * Reads entire file into an array.
3844 * Parameters
3845 * $filename
3846 * The filename being read.
3847 * $flags
3848 * The optional parameter flags can be one, or more, of the following constants:
3849 * FILE_USE_INCLUDE_PATH
3850 * Search for the file in the include_path.
3851 * FILE_IGNORE_NEW_LINES
3852 * Do not add newline at the end of each array element
3853 * FILE_SKIP_EMPTY_LINES
3854 * Skip empty lines
3855 * $context
3856 * A context stream resource.
3857 * Return
3858 * The function returns the read data or FALSE on failure.
3859 */
3860static int jx9Builtin_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
3861{
3862 const char *zFile, *zPtr, *zEnd, *zBuf;
3863 jx9_value *pArray, *pLine;
3864 const jx9_io_stream *pStream;
3865 int use_include = 0;
3866 io_private *pDev;
3867 jx9_int64 n;
3868 int iFlags;
3869 int nLen;
3870
3871 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
3872 /* Missing/Invalid arguments, return FALSE */
3873 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
3874 jx9_result_bool(pCtx, 0);
3875 return JX9_OK;
3876 }
3877 /* Extract the file path */
3878 zFile = jx9_value_to_string(apArg[0], &nLen);
3879 /* Point to the target IO stream device */
3880 pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
3881 if( pStream == 0 ){
3882 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
3883 jx9_result_bool(pCtx, 0);
3884 return JX9_OK;
3885 }
3886 /* Allocate a new IO private instance */
3887 pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE);
3888 if( pDev == 0 ){
3889 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
3890 jx9_result_bool(pCtx, 0);
3891 return JX9_OK;
3892 }
3893 /* Initialize the structure */
3894 InitIOPrivate(pCtx->pVm, pStream, pDev);
3895 iFlags = 0;
3896 if( nArg > 1 ){
3897 iFlags = jx9_value_to_int(apArg[1]);
3898 }
3899 if( iFlags & 0x01 /*FILE_USE_INCLUDE_PATH*/ ){
3900 use_include = TRUE;
3901 }
3902 /* Create the array and the working value */
3903 pArray = jx9_context_new_array(pCtx);
3904 pLine = jx9_context_new_scalar(pCtx);
3905 if( pArray == 0 || pLine == 0 ){
3906 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
3907 jx9_result_bool(pCtx, 0);
3908 return JX9_OK;
3909 }
3910 /* Try to open the file in read-only mode */
3911 pDev->pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0);
3912 if( pDev->pHandle == 0 ){
3913 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
3914 jx9_result_bool(pCtx, 0);
3915 /* Don't worry about freeing memory, everything will be released automatically
3916 * as soon we return from this function.
3917 */
3918 return JX9_OK;
3919 }
3920 /* Perform the requested operation */
3921 for(;;){
3922 /* Try to extract a line */
3923 n = StreamReadLine(pDev, &zBuf, -1);
3924 if( n < 1 ){
3925 /* EOF or IO error */
3926 break;
3927 }
3928 /* Reset the cursor */
3929 jx9_value_reset_string_cursor(pLine);
3930 /* Remove line ending if requested by the caller */
3931 zPtr = zBuf;
3932 zEnd = &zBuf[n];
3933 if( iFlags & 0x02 /* FILE_IGNORE_NEW_LINES */ ){
3934 /* Ignore trailig lines */
3935 while( zPtr < zEnd && (zEnd[-1] == '\n'
3936#ifdef __WINNT__
3937 || zEnd[-1] == '\r'
3938#endif
3939 )){
3940 n--;
3941 zEnd--;
3942 }
3943 }
3944 if( iFlags & 0x04 /* FILE_SKIP_EMPTY_LINES */ ){
3945 /* Ignore empty lines */
3946 while( zPtr < zEnd && (unsigned char)zPtr[0] < 0xc0 && SyisSpace(zPtr[0]) ){
3947 zPtr++;
3948 }
3949 if( zPtr >= zEnd ){
3950 /* Empty line */
3951 continue;
3952 }
3953 }
3954 jx9_value_string(pLine, zBuf, (int)(zEnd-zBuf));
3955 /* Insert line */
3956 jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pLine);
3957 }
3958 /* Close the stream */
3959 jx9StreamCloseHandle(pStream, pDev->pHandle);
3960 /* Release the io_private instance */
3961 ReleaseIOPrivate(pCtx, pDev);
3962 /* Return the created array */
3963 jx9_result_value(pCtx, pArray);
3964 return JX9_OK;
3965}
3966/*
3967 * bool copy(string $source, string $dest[, resource $context ] )
3968 * Makes a copy of the file source to dest.
3969 * Parameters
3970 * $source
3971 * Path to the source file.
3972 * $dest
3973 * The destination path. If dest is a URL, the copy operation
3974 * may fail if the wrapper does not support overwriting of existing files.
3975 * $context
3976 * A context stream resource.
3977 * Return
3978 * TRUE on success or FALSE on failure.
3979 */
3980static int jx9Builtin_copy(jx9_context *pCtx, int nArg, jx9_value **apArg)
3981{
3982 const jx9_io_stream *pSin, *pSout;
3983 const char *zFile;
3984 char zBuf[8192];
3985 void *pIn, *pOut;
3986 jx9_int64 n;
3987 int nLen;
3988 if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1])){
3989 /* Missing/Invalid arguments, return FALSE */
3990 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a source and a destination path");
3991 jx9_result_bool(pCtx, 0);
3992 return JX9_OK;
3993 }
3994 /* Extract the source name */
3995 zFile = jx9_value_to_string(apArg[0], &nLen);
3996 /* Point to the target IO stream device */
3997 pSin = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
3998 if( pSin == 0 ){
3999 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
4000 jx9_result_bool(pCtx, 0);
4001 return JX9_OK;
4002 }
4003 /* Try to open the source file in a read-only mode */
4004 pIn = jx9StreamOpenHandle(pCtx->pVm, pSin, zFile, JX9_IO_OPEN_RDONLY, FALSE, nArg > 2 ? apArg[2] : 0, FALSE, 0);
4005 if( pIn == 0 ){
4006 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening source: '%s'", zFile);
4007 jx9_result_bool(pCtx, 0);
4008 return JX9_OK;
4009 }
4010 /* Extract the destination name */
4011 zFile = jx9_value_to_string(apArg[1], &nLen);
4012 /* Point to the target IO stream device */
4013 pSout = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
4014 if( pSout == 0 ){
4015 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
4016 jx9_result_bool(pCtx, 0);
4017 jx9StreamCloseHandle(pSin, pIn);
4018 return JX9_OK;
4019 }
4020 if( pSout->xWrite == 0 ){
4021 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
4022 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
4023 jx9_function_name(pCtx), pSin->zName
4024 );
4025 jx9_result_bool(pCtx, 0);
4026 jx9StreamCloseHandle(pSin, pIn);
4027 return JX9_OK;
4028 }
4029 /* Try to open the destination file in a read-write mode */
4030 pOut = jx9StreamOpenHandle(pCtx->pVm, pSout, zFile,
4031 JX9_IO_OPEN_CREATE|JX9_IO_OPEN_TRUNC|JX9_IO_OPEN_RDWR, FALSE, nArg > 2 ? apArg[2] : 0, FALSE, 0);
4032 if( pOut == 0 ){
4033 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening destination: '%s'", zFile);
4034 jx9_result_bool(pCtx, 0);
4035 jx9StreamCloseHandle(pSin, pIn);
4036 return JX9_OK;
4037 }
4038 /* Perform the requested operation */
4039 for(;;){
4040 /* Read from source */
4041 n = pSin->xRead(pIn, zBuf, sizeof(zBuf));
4042 if( n < 1 ){
4043 /* EOF or IO error, break immediately */
4044 break;
4045 }
4046 /* Write to dest */
4047 n = pSout->xWrite(pOut, zBuf, n);
4048 if( n < 1 ){
4049 /* IO error, break immediately */
4050 break;
4051 }
4052 }
4053 /* Close the streams */
4054 jx9StreamCloseHandle(pSin, pIn);
4055 jx9StreamCloseHandle(pSout, pOut);
4056 /* Return TRUE */
4057 jx9_result_bool(pCtx, 1);
4058 return JX9_OK;
4059}
4060/*
4061 * array fstat(resource $handle)
4062 * Gets information about a file using an open file pointer.
4063 * Parameters
4064 * $handle
4065 * The file pointer.
4066 * Return
4067 * Returns an array with the statistics of the file or FALSE on failure.
4068 */
4069static int jx9Builtin_fstat(jx9_context *pCtx, int nArg, jx9_value **apArg)
4070{
4071 jx9_value *pArray, *pValue;
4072 const jx9_io_stream *pStream;
4073 io_private *pDev;
4074 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
4075 /* Missing/Invalid arguments, return FALSE */
4076 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
4077 jx9_result_bool(pCtx, 0);
4078 return JX9_OK;
4079 }
4080 /* Extract our private data */
4081 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
4082 /* Make sure we are dealing with a valid io_private instance */
4083 if( IO_PRIVATE_INVALID(pDev) ){
4084 /* Expecting an IO handle */
4085 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
4086 jx9_result_bool(pCtx, 0);
4087 return JX9_OK;
4088 }
4089 /* Point to the target IO stream device */
4090 pStream = pDev->pStream;
4091 if( pStream == 0 || pStream->xStat == 0){
4092 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
4093 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
4094 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
4095 );
4096 jx9_result_bool(pCtx, 0);
4097 return JX9_OK;
4098 }
4099 /* Create the array and the working value */
4100 pArray = jx9_context_new_array(pCtx);
4101 pValue = jx9_context_new_scalar(pCtx);
4102 if( pArray == 0 || pValue == 0 ){
4103 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
4104 jx9_result_bool(pCtx, 0);
4105 return JX9_OK;
4106 }
4107 /* Perform the requested operation */
4108 pStream->xStat(pDev->pHandle, pArray, pValue);
4109 /* Return the freshly created array */
4110 jx9_result_value(pCtx, pArray);
4111 /* Don't worry about freeing memory here, everything will be
4112 * released automatically as soon we return from this function.
4113 */
4114 return JX9_OK;
4115}
4116/*
4117 * int fwrite(resource $handle, string $string[, int $length])
4118 * Writes the contents of string to the file stream pointed to by handle.
4119 * Parameters
4120 * $handle
4121 * The file pointer.
4122 * $string
4123 * The string that is to be written.
4124 * $length
4125 * If the length argument is given, writing will stop after length bytes have been written
4126 * or the end of string is reached, whichever comes first.
4127 * Return
4128 * Returns the number of bytes written, or FALSE on error.
4129 */
4130static int jx9Builtin_fwrite(jx9_context *pCtx, int nArg, jx9_value **apArg)
4131{
4132 const jx9_io_stream *pStream;
4133 const char *zString;
4134 io_private *pDev;
4135 int nLen, n;
4136 if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
4137 /* Missing/Invalid arguments, return FALSE */
4138 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
4139 jx9_result_bool(pCtx, 0);
4140 return JX9_OK;
4141 }
4142 /* Extract our private data */
4143 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
4144 /* Make sure we are dealing with a valid io_private instance */
4145 if( IO_PRIVATE_INVALID(pDev) ){
4146 /* Expecting an IO handle */
4147 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
4148 jx9_result_bool(pCtx, 0);
4149 return JX9_OK;
4150 }
4151 /* Point to the target IO stream device */
4152 pStream = pDev->pStream;
4153 if( pStream == 0 || pStream->xWrite == 0){
4154 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
4155 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
4156 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
4157 );
4158 jx9_result_bool(pCtx, 0);
4159 return JX9_OK;
4160 }
4161 /* Extract the data to write */
4162 zString = jx9_value_to_string(apArg[1], &nLen);
4163 if( nArg > 2 ){
4164 /* Maximum data length to write */
4165 n = jx9_value_to_int(apArg[2]);
4166 if( n >= 0 && n < nLen ){
4167 nLen = n;
4168 }
4169 }
4170 if( nLen < 1 ){
4171 /* Nothing to write */
4172 jx9_result_int(pCtx, 0);
4173 return JX9_OK;
4174 }
4175 /* Perform the requested operation */
4176 n = (int)pStream->xWrite(pDev->pHandle, (const void *)zString, nLen);
4177 if( n < 0 ){
4178 /* IO error, return FALSE */
4179 jx9_result_bool(pCtx, 0);
4180 }else{
4181 /* #Bytes written */
4182 jx9_result_int(pCtx, n);
4183 }
4184 return JX9_OK;
4185}
4186/*
4187 * bool flock(resource $handle, int $operation)
4188 * Portable advisory file locking.
4189 * Parameters
4190 * $handle
4191 * The file pointer.
4192 * $operation
4193 * operation is one of the following:
4194 * LOCK_SH to acquire a shared lock (reader).
4195 * LOCK_EX to acquire an exclusive lock (writer).
4196 * LOCK_UN to release a lock (shared or exclusive).
4197 * Return
4198 * Returns TRUE on success or FALSE on failure.
4199 */
4200static int jx9Builtin_flock(jx9_context *pCtx, int nArg, jx9_value **apArg)
4201{
4202 const jx9_io_stream *pStream;
4203 io_private *pDev;
4204 int nLock;
4205 int rc;
4206 if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){
4207 /* Missing/Invalid arguments, return FALSE */
4208 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
4209 jx9_result_bool(pCtx, 0);
4210 return JX9_OK;
4211 }
4212 /* Extract our private data */
4213 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
4214 /* Make sure we are dealing with a valid io_private instance */
4215 if( IO_PRIVATE_INVALID(pDev) ){
4216 /*Expecting an IO handle */
4217 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
4218 jx9_result_bool(pCtx, 0);
4219 return JX9_OK;
4220 }
4221 /* Point to the target IO stream device */
4222 pStream = pDev->pStream;
4223 if( pStream == 0 || pStream->xLock == 0){
4224 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
4225 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
4226 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
4227 );
4228 jx9_result_bool(pCtx, 0);
4229 return JX9_OK;
4230 }
4231 /* Requested lock operation */
4232 nLock = jx9_value_to_int(apArg[1]);
4233 /* Lock operation */
4234 rc = pStream->xLock(pDev->pHandle, nLock);
4235 /* IO result */
4236 jx9_result_bool(pCtx, rc == JX9_OK);
4237 return JX9_OK;
4238}
4239/*
4240 * int fpassthru(resource $handle)
4241 * Output all remaining data on a file pointer.
4242 * Parameters
4243 * $handle
4244 * The file pointer.
4245 * Return
4246 * Total number of characters read from handle and passed through
4247 * to the output on success or FALSE on failure.
4248 */
4249static int jx9Builtin_fpassthru(jx9_context *pCtx, int nArg, jx9_value **apArg)
4250{
4251 const jx9_io_stream *pStream;
4252 io_private *pDev;
4253 jx9_int64 n, nRead;
4254 char zBuf[8192];
4255 int rc;
4256 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
4257 /* Missing/Invalid arguments, return FALSE */
4258 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
4259 jx9_result_bool(pCtx, 0);
4260 return JX9_OK;
4261 }
4262 /* Extract our private data */
4263 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
4264 /* Make sure we are dealing with a valid io_private instance */
4265 if( IO_PRIVATE_INVALID(pDev) ){
4266 /*Expecting an IO handle */
4267 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
4268 jx9_result_bool(pCtx, 0);
4269 return JX9_OK;
4270 }
4271 /* Point to the target IO stream device */
4272 pStream = pDev->pStream;
4273 if( pStream == 0 ){
4274 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
4275 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
4276 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
4277 );
4278 jx9_result_bool(pCtx, 0);
4279 return JX9_OK;
4280 }
4281 /* Perform the requested operation */
4282 nRead = 0;
4283 for(;;){
4284 n = StreamRead(pDev, zBuf, sizeof(zBuf));
4285 if( n < 1 ){
4286 /* Error or EOF */
4287 break;
4288 }
4289 /* Increment the read counter */
4290 nRead += n;
4291 /* Output data */
4292 rc = jx9_context_output(pCtx, zBuf, (int)nRead /* FIXME: 64-bit issues */);
4293 if( rc == JX9_ABORT ){
4294 /* Consumer callback request an operation abort */
4295 break;
4296 }
4297 }
4298 /* Total number of bytes readen */
4299 jx9_result_int64(pCtx, nRead);
4300 return JX9_OK;
4301}
4302/* CSV reader/writer private data */
4303struct csv_data
4304{
4305 int delimiter; /* Delimiter. Default ', ' */
4306 int enclosure; /* Enclosure. Default '"'*/
4307 io_private *pDev; /* Open stream handle */
4308 int iCount; /* Counter */
4309};
4310/*
4311 * The following callback is used by the fputcsv() function inorder to iterate
4312 * throw array entries and output CSV data based on the current key and it's
4313 * associated data.
4314 */
4315static int csv_write_callback(jx9_value *pKey, jx9_value *pValue, void *pUserData)
4316{
4317 struct csv_data *pData = (struct csv_data *)pUserData;
4318 const char *zData;
4319 int nLen, c2;
4320 sxu32 n;
4321 /* Point to the raw data */
4322 zData = jx9_value_to_string(pValue, &nLen);
4323 if( nLen < 1 ){
4324 /* Nothing to write */
4325 return JX9_OK;
4326 }
4327 if( pData->iCount > 0 ){
4328 /* Write the delimiter */
4329 pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->delimiter, sizeof(char));
4330 }
4331 n = 1;
4332 c2 = 0;
4333 if( SyByteFind(zData, (sxu32)nLen, pData->delimiter, 0) == SXRET_OK ||
4334 SyByteFind(zData, (sxu32)nLen, pData->enclosure, &n) == SXRET_OK ){
4335 c2 = 1;
4336 if( n == 0 ){
4337 c2 = 2;
4338 }
4339 /* Write the enclosure */
4340 pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char));
4341 if( c2 > 1 ){
4342 pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char));
4343 }
4344 }
4345 /* Write the data */
4346 if( pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)zData, (jx9_int64)nLen) < 1 ){
4347 SXUNUSED(pKey); /* cc warning */
4348 return JX9_ABORT;
4349 }
4350 if( c2 > 0 ){
4351 /* Write the enclosure */
4352 pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char));
4353 if( c2 > 1 ){
4354 pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char));
4355 }
4356 }
4357 pData->iCount++;
4358 return JX9_OK;
4359}
4360/*
4361 * int fputcsv(resource $handle, array $fields[, string $delimiter = ', '[, string $enclosure = '"' ]])
4362 * Format line as CSV and write to file pointer.
4363 * Parameters
4364 * $handle
4365 * Open file handle.
4366 * $fields
4367 * An array of values.
4368 * $delimiter
4369 * The optional delimiter parameter sets the field delimiter (one character only).
4370 * $enclosure
4371 * The optional enclosure parameter sets the field enclosure (one character only).
4372 */
4373static int jx9Builtin_fputcsv(jx9_context *pCtx, int nArg, jx9_value **apArg)
4374{
4375 const jx9_io_stream *pStream;
4376 struct csv_data sCsv;
4377 io_private *pDev;
4378 char *zEol;
4379 int eolen;
4380 if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){
4381 /* Missing/Invalid arguments, return FALSE */
4382 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Missing/Invalid arguments");
4383 jx9_result_bool(pCtx, 0);
4384 return JX9_OK;
4385 }
4386 /* Extract our private data */
4387 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
4388 /* Make sure we are dealing with a valid io_private instance */
4389 if( IO_PRIVATE_INVALID(pDev) ){
4390 /*Expecting an IO handle */
4391 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
4392 jx9_result_bool(pCtx, 0);
4393 return JX9_OK;
4394 }
4395 /* Point to the target IO stream device */
4396 pStream = pDev->pStream;
4397 if( pStream == 0 || pStream->xWrite == 0){
4398 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
4399 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
4400 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
4401 );
4402 jx9_result_bool(pCtx, 0);
4403 return JX9_OK;
4404 }
4405 /* Set default csv separator */
4406 sCsv.delimiter = ',';
4407 sCsv.enclosure = '"';
4408 sCsv.pDev = pDev;
4409 sCsv.iCount = 0;
4410 if( nArg > 2 ){
4411 /* User delimiter */
4412 const char *z;
4413 int n;
4414 z = jx9_value_to_string(apArg[2], &n);
4415 if( n > 0 ){
4416 sCsv.delimiter = z[0];
4417 }
4418 if( nArg > 3 ){
4419 z = jx9_value_to_string(apArg[3], &n);
4420 if( n > 0 ){
4421 sCsv.enclosure = z[0];
4422 }
4423 }
4424 }
4425 /* Iterate throw array entries and write csv data */
4426 jx9_array_walk(apArg[1], csv_write_callback, &sCsv);
4427 /* Write a line ending */
4428#ifdef __WINNT__
4429 zEol = "\r\n";
4430 eolen = (int)sizeof("\r\n")-1;
4431#else
4432 /* Assume UNIX LF */
4433 zEol = "\n";
4434 eolen = (int)sizeof(char);
4435#endif
4436 pDev->pStream->xWrite(pDev->pHandle, (const void *)zEol, eolen);
4437 return JX9_OK;
4438}
4439/*
4440 * fprintf, vfprintf private data.
4441 * An instance of the following structure is passed to the formatted
4442 * input consumer callback defined below.
4443 */
4444typedef struct fprintf_data fprintf_data;
4445struct fprintf_data
4446{
4447 io_private *pIO; /* IO stream */
4448 jx9_int64 nCount; /* Total number of bytes written */
4449};
4450/*
4451 * Callback [i.e: Formatted input consumer] for the fprintf function.
4452 */
4453static int fprintfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData)
4454{
4455 fprintf_data *pFdata = (fprintf_data *)pUserData;
4456 jx9_int64 n;
4457 /* Write the formatted data */
4458 n = pFdata->pIO->pStream->xWrite(pFdata->pIO->pHandle, (const void *)zInput, nLen);
4459 if( n < 1 ){
4460 SXUNUSED(pCtx); /* cc warning */
4461 /* IO error, abort immediately */
4462 return SXERR_ABORT;
4463 }
4464 /* Increment counter */
4465 pFdata->nCount += n;
4466 return JX9_OK;
4467}
4468/*
4469 * int fprintf(resource $handle, string $format[, mixed $args [, mixed $... ]])
4470 * Write a formatted string to a stream.
4471 * Parameters
4472 * $handle
4473 * The file pointer.
4474 * $format
4475 * String format (see sprintf()).
4476 * Return
4477 * The length of the written string.
4478 */
4479static int jx9Builtin_fprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
4480{
4481 fprintf_data sFdata;
4482 const char *zFormat;
4483 io_private *pDev;
4484 int nLen;
4485 if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_string(apArg[1]) ){
4486 /* Missing/Invalid arguments, return zero */
4487 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Invalid arguments");
4488 jx9_result_int(pCtx, 0);
4489 return JX9_OK;
4490 }
4491 /* Extract our private data */
4492 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
4493 /* Make sure we are dealing with a valid io_private instance */
4494 if( IO_PRIVATE_INVALID(pDev) ){
4495 /*Expecting an IO handle */
4496 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
4497 jx9_result_int(pCtx, 0);
4498 return JX9_OK;
4499 }
4500 /* Point to the target IO stream device */
4501 if( pDev->pStream == 0 || pDev->pStream->xWrite == 0 ){
4502 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
4503 "IO routine(%s) not implemented in the underlying stream(%s) device",
4504 jx9_function_name(pCtx), pDev->pStream ? pDev->pStream->zName : "null_stream"
4505 );
4506 jx9_result_int(pCtx, 0);
4507 return JX9_OK;
4508 }
4509 /* Extract the string format */
4510 zFormat = jx9_value_to_string(apArg[1], &nLen);
4511 if( nLen < 1 ){
4512 /* Empty string, return zero */
4513 jx9_result_int(pCtx, 0);
4514 return JX9_OK;
4515 }
4516 /* Prepare our private data */
4517 sFdata.nCount = 0;
4518 sFdata.pIO = pDev;
4519 /* Format the string */
4520 jx9InputFormat(fprintfConsumer, pCtx, zFormat, nLen, nArg - 1, &apArg[1], (void *)&sFdata, FALSE);
4521 /* Return total number of bytes written */
4522 jx9_result_int64(pCtx, sFdata.nCount);
4523 return JX9_OK;
4524}
4525/*
4526 * int vfprintf(resource $handle, string $format, array $args)
4527 * Write a formatted string to a stream.
4528 * Parameters
4529 * $handle
4530 * The file pointer.
4531 * $format
4532 * String format (see sprintf()).
4533 * $args
4534 * User arguments.
4535 * Return
4536 * The length of the written string.
4537 */
4538static int jx9Builtin_vfprintf(jx9_context *pCtx, int nArg, jx9_value **apArg)
4539{
4540 fprintf_data sFdata;
4541 const char *zFormat;
4542 jx9_hashmap *pMap;
4543 io_private *pDev;
4544 SySet sArg;
4545 int n, nLen;
4546 if( nArg < 3 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_string(apArg[1]) || !jx9_value_is_json_array(apArg[2]) ){
4547 /* Missing/Invalid arguments, return zero */
4548 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Invalid arguments");
4549 jx9_result_int(pCtx, 0);
4550 return JX9_OK;
4551 }
4552 /* Extract our private data */
4553 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
4554 /* Make sure we are dealing with a valid io_private instance */
4555 if( IO_PRIVATE_INVALID(pDev) ){
4556 /*Expecting an IO handle */
4557 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
4558 jx9_result_int(pCtx, 0);
4559 return JX9_OK;
4560 }
4561 /* Point to the target IO stream device */
4562 if( pDev->pStream == 0 || pDev->pStream->xWrite == 0 ){
4563 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
4564 "IO routine(%s) not implemented in the underlying stream(%s) device",
4565 jx9_function_name(pCtx), pDev->pStream ? pDev->pStream->zName : "null_stream"
4566 );
4567 jx9_result_int(pCtx, 0);
4568 return JX9_OK;
4569 }
4570 /* Extract the string format */
4571 zFormat = jx9_value_to_string(apArg[1], &nLen);
4572 if( nLen < 1 ){
4573 /* Empty string, return zero */
4574 jx9_result_int(pCtx, 0);
4575 return JX9_OK;
4576 }
4577 /* Point to hashmap */
4578 pMap = (jx9_hashmap *)apArg[2]->x.pOther;
4579 /* Extract arguments from the hashmap */
4580 n = jx9HashmapValuesToSet(pMap, &sArg);
4581 /* Prepare our private data */
4582 sFdata.nCount = 0;
4583 sFdata.pIO = pDev;
4584 /* Format the string */
4585 jx9InputFormat(fprintfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), (void *)&sFdata, TRUE);
4586 /* Return total number of bytes written*/
4587 jx9_result_int64(pCtx, sFdata.nCount);
4588 SySetRelease(&sArg);
4589 return JX9_OK;
4590}
4591/*
4592 * Convert open modes (string passed to the fopen() function) [i.e: 'r', 'w+', 'a', ...] into JX9 flags.
4593 * According to the JX9 reference manual:
4594 * The mode parameter specifies the type of access you require to the stream. It may be any of the following
4595 * 'r' Open for reading only; place the file pointer at the beginning of the file.
4596 * 'r+' Open for reading and writing; place the file pointer at the beginning of the file.
4597 * 'w' Open for writing only; place the file pointer at the beginning of the file and truncate the file
4598 * to zero length. If the file does not exist, attempt to create it.
4599 * 'w+' Open for reading and writing; place the file pointer at the beginning of the file and truncate
4600 * the file to zero length. If the file does not exist, attempt to create it.
4601 * 'a' Open for writing only; place the file pointer at the end of the file. If the file does not
4602 * exist, attempt to create it.
4603 * 'a+' Open for reading and writing; place the file pointer at the end of the file. If the file does
4604 * not exist, attempt to create it.
4605 * 'x' Create and open for writing only; place the file pointer at the beginning of the file. If the file
4606 * already exists,
4607 * the fopen() call will fail by returning FALSE and generating an error of level E_WARNING. If the file
4608 * does not exist attempt to create it. This is equivalent to specifying O_EXCL|O_CREAT flags for
4609 * the underlying open(2) system call.
4610 * 'x+' Create and open for reading and writing; otherwise it has the same behavior as 'x'.
4611 * 'c' Open the file for writing only. If the file does not exist, it is created. If it exists, it is neither truncated
4612 * (as opposed to 'w'), nor the call to this function fails (as is the case with 'x'). The file pointer
4613 * is positioned on the beginning of the file.
4614 * This may be useful if it's desired to get an advisory lock (see flock()) before attempting to modify the file
4615 * as using 'w' could truncate the file before the lock was obtained (if truncation is desired, ftruncate() can
4616 * be used after the lock is requested).
4617 * 'c+' Open the file for reading and writing; otherwise it has the same behavior as 'c'.
4618 */
4619static int StrModeToFlags(jx9_context *pCtx, const char *zMode, int nLen)
4620{
4621 const char *zEnd = &zMode[nLen];
4622 int iFlag = 0;
4623 int c;
4624 if( nLen < 1 ){
4625 /* Open in a read-only mode */
4626 return JX9_IO_OPEN_RDONLY;
4627 }
4628 c = zMode[0];
4629 if( c == 'r' || c == 'R' ){
4630 /* Read-only access */
4631 iFlag = JX9_IO_OPEN_RDONLY;
4632 zMode++; /* Advance */
4633 if( zMode < zEnd ){
4634 c = zMode[0];
4635 if( c == '+' || c == 'w' || c == 'W' ){
4636 /* Read+Write access */
4637 iFlag = JX9_IO_OPEN_RDWR;
4638 }
4639 }
4640 }else if( c == 'w' || c == 'W' ){
4641 /* Overwrite mode.
4642 * If the file does not exists, try to create it
4643 */
4644 iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_TRUNC|JX9_IO_OPEN_CREATE;
4645 zMode++; /* Advance */
4646 if( zMode < zEnd ){
4647 c = zMode[0];
4648 if( c == '+' || c == 'r' || c == 'R' ){
4649 /* Read+Write access */
4650 iFlag &= ~JX9_IO_OPEN_WRONLY;
4651 iFlag |= JX9_IO_OPEN_RDWR;
4652 }
4653 }
4654 }else if( c == 'a' || c == 'A' ){
4655 /* Append mode (place the file pointer at the end of the file).
4656 * Create the file if it does not exists.
4657 */
4658 iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_APPEND|JX9_IO_OPEN_CREATE;
4659 zMode++; /* Advance */
4660 if( zMode < zEnd ){
4661 c = zMode[0];
4662 if( c == '+' ){
4663 /* Read-Write access */
4664 iFlag &= ~JX9_IO_OPEN_WRONLY;
4665 iFlag |= JX9_IO_OPEN_RDWR;
4666 }
4667 }
4668 }else if( c == 'x' || c == 'X' ){
4669 /* Exclusive access.
4670 * If the file already exists, return immediately with a failure code.
4671 * Otherwise create a new file.
4672 */
4673 iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_EXCL;
4674 zMode++; /* Advance */
4675 if( zMode < zEnd ){
4676 c = zMode[0];
4677 if( c == '+' || c == 'r' || c == 'R' ){
4678 /* Read-Write access */
4679 iFlag &= ~JX9_IO_OPEN_WRONLY;
4680 iFlag |= JX9_IO_OPEN_RDWR;
4681 }
4682 }
4683 }else if( c == 'c' || c == 'C' ){
4684 /* Overwrite mode.Create the file if it does not exists.*/
4685 iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_CREATE;
4686 zMode++; /* Advance */
4687 if( zMode < zEnd ){
4688 c = zMode[0];
4689 if( c == '+' ){
4690 /* Read-Write access */
4691 iFlag &= ~JX9_IO_OPEN_WRONLY;
4692 iFlag |= JX9_IO_OPEN_RDWR;
4693 }
4694 }
4695 }else{
4696 /* Invalid mode. Assume a read only open */
4697 jx9_context_throw_error(pCtx, JX9_CTX_NOTICE, "Invalid open mode, JX9 is assuming a Read-Only open");
4698 iFlag = JX9_IO_OPEN_RDONLY;
4699 }
4700 while( zMode < zEnd ){
4701 c = zMode[0];
4702 if( c == 'b' || c == 'B' ){
4703 iFlag &= ~JX9_IO_OPEN_TEXT;
4704 iFlag |= JX9_IO_OPEN_BINARY;
4705 }else if( c == 't' || c == 'T' ){
4706 iFlag &= ~JX9_IO_OPEN_BINARY;
4707 iFlag |= JX9_IO_OPEN_TEXT;
4708 }
4709 zMode++;
4710 }
4711 return iFlag;
4712}
4713/*
4714 * Initialize the IO private structure.
4715 */
4716static void InitIOPrivate(jx9_vm *pVm, const jx9_io_stream *pStream, io_private *pOut)
4717{
4718 pOut->pStream = pStream;
4719 SyBlobInit(&pOut->sBuffer, &pVm->sAllocator);
4720 pOut->nOfft = 0;
4721 /* Set the magic number */
4722 pOut->iMagic = IO_PRIVATE_MAGIC;
4723}
4724/*
4725 * Release the IO private structure.
4726 */
4727static void ReleaseIOPrivate(jx9_context *pCtx, io_private *pDev)
4728{
4729 SyBlobRelease(&pDev->sBuffer);
4730 pDev->iMagic = 0x2126; /* Invalid magic number so we can detetct misuse */
4731 /* Release the whole structure */
4732 jx9_context_free_chunk(pCtx, pDev);
4733}
4734/*
4735 * Reset the IO private structure.
4736 */
4737static void ResetIOPrivate(io_private *pDev)
4738{
4739 SyBlobReset(&pDev->sBuffer);
4740 pDev->nOfft = 0;
4741}
4742/* Forward declaration */
4743static int is_jx9_stream(const jx9_io_stream *pStream);
4744/*
4745 * resource fopen(string $filename, string $mode [, bool $use_include_path = false[, resource $context ]])
4746 * Open a file, a URL or any other IO stream.
4747 * Parameters
4748 * $filename
4749 * If filename is of the form "scheme://...", it is assumed to be a URL and JX9 will search
4750 * for a protocol handler (also known as a wrapper) for that scheme. If no scheme is given
4751 * then a regular file is assumed.
4752 * $mode
4753 * The mode parameter specifies the type of access you require to the stream
4754 * See the block comment associated with the StrModeToFlags() for the supported
4755 * modes.
4756 * $use_include_path
4757 * You can use the optional second parameter and set it to
4758 * TRUE, if you want to search for the file in the include_path, too.
4759 * $context
4760 * A context stream resource.
4761 * Return
4762 * File handle on success or FALSE on failure.
4763 */
4764static int jx9Builtin_fopen(jx9_context *pCtx, int nArg, jx9_value **apArg)
4765{
4766 const jx9_io_stream *pStream;
4767 const char *zUri, *zMode;
4768 jx9_value *pResource;
4769 io_private *pDev;
4770 int iLen, imLen;
4771 int iOpenFlags;
4772 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
4773 /* Missing/Invalid arguments, return FALSE */
4774 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path or URL");
4775 jx9_result_bool(pCtx, 0);
4776 return JX9_OK;
4777 }
4778 /* Extract the URI and the desired access mode */
4779 zUri = jx9_value_to_string(apArg[0], &iLen);
4780 if( nArg > 1 ){
4781 zMode = jx9_value_to_string(apArg[1], &imLen);
4782 }else{
4783 /* Set a default read-only mode */
4784 zMode = "r";
4785 imLen = (int)sizeof(char);
4786 }
4787 /* Try to extract a stream */
4788 pStream = jx9VmGetStreamDevice(pCtx->pVm, &zUri, iLen);
4789 if( pStream == 0 ){
4790 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
4791 "No stream device is associated with the given URI(%s)", zUri);
4792 jx9_result_bool(pCtx, 0);
4793 return JX9_OK;
4794 }
4795 /* Allocate a new IO private instance */
4796 pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE);
4797 if( pDev == 0 ){
4798 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
4799 jx9_result_bool(pCtx, 0);
4800 return JX9_OK;
4801 }
4802 pResource = 0;
4803 if( nArg > 3 ){
4804 pResource = apArg[3];
4805 }else if( is_jx9_stream(pStream) ){
4806 /* TICKET 1433-80: The jx9:// stream need a jx9_value to access the underlying
4807 * virtual machine.
4808 */
4809 pResource = apArg[0];
4810 }
4811 /* Initialize the structure */
4812 InitIOPrivate(pCtx->pVm, pStream, pDev);
4813 /* Convert open mode to JX9 flags */
4814 iOpenFlags = StrModeToFlags(pCtx, zMode, imLen);
4815 /* Try to get a handle */
4816 pDev->pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zUri, iOpenFlags,
4817 nArg > 2 ? jx9_value_to_bool(apArg[2]) : FALSE, pResource, FALSE, 0);
4818 if( pDev->pHandle == 0 ){
4819 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zUri);
4820 jx9_result_bool(pCtx, 0);
4821 jx9_context_free_chunk(pCtx, pDev);
4822 return JX9_OK;
4823 }
4824 /* All done, return the io_private instance as a resource */
4825 jx9_result_resource(pCtx, pDev);
4826 return JX9_OK;
4827}
4828/*
4829 * bool fclose(resource $handle)
4830 * Closes an open file pointer
4831 * Parameters
4832 * $handle
4833 * The file pointer.
4834 * Return
4835 * TRUE on success or FALSE on failure.
4836 */
4837static int jx9Builtin_fclose(jx9_context *pCtx, int nArg, jx9_value **apArg)
4838{
4839 const jx9_io_stream *pStream;
4840 io_private *pDev;
4841 jx9_vm *pVm;
4842 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
4843 /* Missing/Invalid arguments, return FALSE */
4844 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
4845 jx9_result_bool(pCtx, 0);
4846 return JX9_OK;
4847 }
4848 /* Extract our private data */
4849 pDev = (io_private *)jx9_value_to_resource(apArg[0]);
4850 /* Make sure we are dealing with a valid io_private instance */
4851 if( IO_PRIVATE_INVALID(pDev) ){
4852 /*Expecting an IO handle */
4853 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle");
4854 jx9_result_bool(pCtx, 0);
4855 return JX9_OK;
4856 }
4857 /* Point to the target IO stream device */
4858 pStream = pDev->pStream;
4859 if( pStream == 0 ){
4860 jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING,
4861 "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE",
4862 jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream"
4863 );
4864 jx9_result_bool(pCtx, 0);
4865 return JX9_OK;
4866 }
4867 /* Point to the VM that own this context */
4868 pVm = pCtx->pVm;
4869 /* TICKET 1433-62: Keep the STDIN/STDOUT/STDERR handles open */
4870 if( pDev != pVm->pStdin && pDev != pVm->pStdout && pDev != pVm->pStderr ){
4871 /* Perform the requested operation */
4872 jx9StreamCloseHandle(pStream, pDev->pHandle);
4873 /* Release the IO private structure */
4874 ReleaseIOPrivate(pCtx, pDev);
4875 /* Invalidate the resource handle */
4876 jx9_value_release(apArg[0]);
4877 }
4878 /* Return TRUE */
4879 jx9_result_bool(pCtx, 1);
4880 return JX9_OK;
4881}
4882#if !defined(JX9_DISABLE_HASH_FUNC)
4883/*
4884 * MD5/SHA1 digest consumer.
4885 */
4886static int vfsHashConsumer(const void *pData, unsigned int nLen, void *pUserData)
4887{
4888 /* Append hex chunk verbatim */
4889 jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen);
4890 return SXRET_OK;
4891}
4892/*
4893 * string md5_file(string $uri[, bool $raw_output = false ])
4894 * Calculates the md5 hash of a given file.
4895 * Parameters
4896 * $uri
4897 * Target URI (file(/path/to/something) or URL(http://www.symisc.net/))
4898 * $raw_output
4899 * When TRUE, returns the digest in raw binary format with a length of 16.
4900 * Return
4901 * Return the MD5 digest on success or FALSE on failure.
4902 */
4903static int jx9Builtin_md5_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
4904{
4905 const jx9_io_stream *pStream;
4906 unsigned char zDigest[16];
4907 int raw_output = FALSE;
4908 const char *zFile;
4909 MD5Context sCtx;
4910 char zBuf[8192];
4911 void *pHandle;
4912 jx9_int64 n;
4913 int nLen;
4914 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
4915 /* Missing/Invalid arguments, return FALSE */
4916 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
4917 jx9_result_bool(pCtx, 0);
4918 return JX9_OK;
4919 }
4920 /* Extract the file path */
4921 zFile = jx9_value_to_string(apArg[0], &nLen);
4922 /* Point to the target IO stream device */
4923 pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
4924 if( pStream == 0 ){
4925 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
4926 jx9_result_bool(pCtx, 0);
4927 return JX9_OK;
4928 }
4929 if( nArg > 1 ){
4930 raw_output = jx9_value_to_bool(apArg[1]);
4931 }
4932 /* Try to open the file in read-only mode */
4933 pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
4934 if( pHandle == 0 ){
4935 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
4936 jx9_result_bool(pCtx, 0);
4937 return JX9_OK;
4938 }
4939 /* Init the MD5 context */
4940 MD5Init(&sCtx);
4941 /* Perform the requested operation */
4942 for(;;){
4943 n = pStream->xRead(pHandle, zBuf, sizeof(zBuf));
4944 if( n < 1 ){
4945 /* EOF or IO error, break immediately */
4946 break;
4947 }
4948 MD5Update(&sCtx, (const unsigned char *)zBuf, (unsigned int)n);
4949 }
4950 /* Close the stream */
4951 jx9StreamCloseHandle(pStream, pHandle);
4952 /* Extract the digest */
4953 MD5Final(zDigest, &sCtx);
4954 if( raw_output ){
4955 /* Output raw digest */
4956 jx9_result_string(pCtx, (const char *)zDigest, sizeof(zDigest));
4957 }else{
4958 /* Perform a binary to hex conversion */
4959 SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), vfsHashConsumer, pCtx);
4960 }
4961 return JX9_OK;
4962}
4963/*
4964 * string sha1_file(string $uri[, bool $raw_output = false ])
4965 * Calculates the SHA1 hash of a given file.
4966 * Parameters
4967 * $uri
4968 * Target URI (file(/path/to/something) or URL(http://www.symisc.net/))
4969 * $raw_output
4970 * When TRUE, returns the digest in raw binary format with a length of 20.
4971 * Return
4972 * Return the SHA1 digest on success or FALSE on failure.
4973 */
4974static int jx9Builtin_sha1_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
4975{
4976 const jx9_io_stream *pStream;
4977 unsigned char zDigest[20];
4978 int raw_output = FALSE;
4979 const char *zFile;
4980 SHA1Context sCtx;
4981 char zBuf[8192];
4982 void *pHandle;
4983 jx9_int64 n;
4984 int nLen;
4985 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
4986 /* Missing/Invalid arguments, return FALSE */
4987 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
4988 jx9_result_bool(pCtx, 0);
4989 return JX9_OK;
4990 }
4991 /* Extract the file path */
4992 zFile = jx9_value_to_string(apArg[0], &nLen);
4993 /* Point to the target IO stream device */
4994 pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
4995 if( pStream == 0 ){
4996 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
4997 jx9_result_bool(pCtx, 0);
4998 return JX9_OK;
4999 }
5000 if( nArg > 1 ){
5001 raw_output = jx9_value_to_bool(apArg[1]);
5002 }
5003 /* Try to open the file in read-only mode */
5004 pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
5005 if( pHandle == 0 ){
5006 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
5007 jx9_result_bool(pCtx, 0);
5008 return JX9_OK;
5009 }
5010 /* Init the SHA1 context */
5011 SHA1Init(&sCtx);
5012 /* Perform the requested operation */
5013 for(;;){
5014 n = pStream->xRead(pHandle, zBuf, sizeof(zBuf));
5015 if( n < 1 ){
5016 /* EOF or IO error, break immediately */
5017 break;
5018 }
5019 SHA1Update(&sCtx, (const unsigned char *)zBuf, (unsigned int)n);
5020 }
5021 /* Close the stream */
5022 jx9StreamCloseHandle(pStream, pHandle);
5023 /* Extract the digest */
5024 SHA1Final(&sCtx, zDigest);
5025 if( raw_output ){
5026 /* Output raw digest */
5027 jx9_result_string(pCtx, (const char *)zDigest, sizeof(zDigest));
5028 }else{
5029 /* Perform a binary to hex conversion */
5030 SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), vfsHashConsumer, pCtx);
5031 }
5032 return JX9_OK;
5033}
5034#endif /* JX9_DISABLE_HASH_FUNC */
5035/*
5036 * array parse_ini_file(string $filename[, bool $process_sections = false [, int $scanner_mode = INI_SCANNER_NORMAL ]] )
5037 * Parse a configuration file.
5038 * Parameters
5039 * $filename
5040 * The filename of the ini file being parsed.
5041 * $process_sections
5042 * By setting the process_sections parameter to TRUE, you get a multidimensional array
5043 * with the section names and settings included.
5044 * The default for process_sections is FALSE.
5045 * $scanner_mode
5046 * Can either be INI_SCANNER_NORMAL (default) or INI_SCANNER_RAW.
5047 * If INI_SCANNER_RAW is supplied, then option values will not be parsed.
5048 * Return
5049 * The settings are returned as an associative array on success.
5050 * Otherwise is returned.
5051 */
5052static int jx9Builtin_parse_ini_file(jx9_context *pCtx, int nArg, jx9_value **apArg)
5053{
5054 const jx9_io_stream *pStream;
5055 const char *zFile;
5056 SyBlob sContents;
5057 void *pHandle;
5058 int nLen;
5059 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
5060 /* Missing/Invalid arguments, return FALSE */
5061 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
5062 jx9_result_bool(pCtx, 0);
5063 return JX9_OK;
5064 }
5065 /* Extract the file path */
5066 zFile = jx9_value_to_string(apArg[0], &nLen);
5067 /* Point to the target IO stream device */
5068 pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
5069 if( pStream == 0 ){
5070 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
5071 jx9_result_bool(pCtx, 0);
5072 return JX9_OK;
5073 }
5074 /* Try to open the file in read-only mode */
5075 pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
5076 if( pHandle == 0 ){
5077 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
5078 jx9_result_bool(pCtx, 0);
5079 return JX9_OK;
5080 }
5081 SyBlobInit(&sContents, &pCtx->pVm->sAllocator);
5082 /* Read the whole file */
5083 jx9StreamReadWholeFile(pHandle, pStream, &sContents);
5084 if( SyBlobLength(&sContents) < 1 ){
5085 /* Empty buffer, return FALSE */
5086 jx9_result_bool(pCtx, 0);
5087 }else{
5088 /* Process the raw INI buffer */
5089 jx9ParseIniString(pCtx, (const char *)SyBlobData(&sContents), SyBlobLength(&sContents),
5090 nArg > 1 ? jx9_value_to_bool(apArg[1]) : 0);
5091 }
5092 /* Close the stream */
5093 jx9StreamCloseHandle(pStream, pHandle);
5094 /* Release the working buffer */
5095 SyBlobRelease(&sContents);
5096 return JX9_OK;
5097}
5098/*
5099 * Section:
5100 * ZIP archive processing.
5101 * Authors:
5102 * Symisc Systems, devel@symisc.net.
5103 * Copyright (C) Symisc Systems, http://jx9.symisc.net
5104 * Status:
5105 * Stable.
5106 */
5107typedef struct zip_raw_data zip_raw_data;
5108struct zip_raw_data
5109{
5110 int iType; /* Where the raw data is stored */
5111 union raw_data{
5112 struct mmap_data{
5113 void *pMap; /* Memory mapped data */
5114 jx9_int64 nSize; /* Map size */
5115 const jx9_vfs *pVfs; /* Underlying vfs */
5116 }mmap;
5117 SyBlob sBlob; /* Memory buffer */
5118 }raw;
5119};
5120#define ZIP_RAW_DATA_MMAPED 1 /* Memory mapped ZIP raw data */
5121#define ZIP_RAW_DATA_MEMBUF 2 /* ZIP raw data stored in a dynamically
5122 * allocated memory chunk.
5123 */
5124 /*
5125 * mixed zip_open(string $filename)
5126 * Opens a new zip archive for reading.
5127 * Parameters
5128 * $filename
5129 * The file name of the ZIP archive to open.
5130 * Return
5131 * A resource handle for later use with zip_read() and zip_close() or FALSE on failure.
5132 */
5133static int jx9Builtin_zip_open(jx9_context *pCtx, int nArg, jx9_value **apArg)
5134{
5135 const jx9_io_stream *pStream;
5136 SyArchive *pArchive;
5137 zip_raw_data *pRaw;
5138 const char *zFile;
5139 SyBlob *pContents;
5140 void *pHandle;
5141 int nLen;
5142 sxi32 rc;
5143 if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){
5144 /* Missing/Invalid arguments, return FALSE */
5145 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path");
5146 jx9_result_bool(pCtx, 0);
5147 return JX9_OK;
5148 }
5149 /* Extract the file path */
5150 zFile = jx9_value_to_string(apArg[0], &nLen);
5151 /* Point to the target IO stream device */
5152 pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen);
5153 if( pStream == 0 ){
5154 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE");
5155 jx9_result_bool(pCtx, 0);
5156 return JX9_OK;
5157 }
5158 /* Create an in-memory archive */
5159 pArchive = (SyArchive *)jx9_context_alloc_chunk(pCtx, sizeof(SyArchive)+sizeof(zip_raw_data), TRUE, FALSE);
5160 if( pArchive == 0 ){
5161 jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "JX9 is running out of memory");
5162 jx9_result_bool(pCtx, 0);
5163 return JX9_OK;
5164 }
5165 pRaw = (zip_raw_data *)&pArchive[1];
5166 /* Initialize the archive */
5167 SyArchiveInit(pArchive, &pCtx->pVm->sAllocator, 0, 0);
5168 /* Extract the default stream */
5169 if( pStream == pCtx->pVm->pDefStream /* file:// stream*/){
5170 const jx9_vfs *pVfs;
5171 /* Try to get a memory view of the whole file since ZIP files
5172 * tends to be very big this days, this is a huge performance win.
5173 */
5174 pVfs = jx9ExportBuiltinVfs();
5175 if( pVfs && pVfs->xMmap ){
5176 rc = pVfs->xMmap(zFile, &pRaw->raw.mmap.pMap, &pRaw->raw.mmap.nSize);
5177 if( rc == JX9_OK ){
5178 /* Nice, Extract the whole archive */
5179 rc = SyZipExtractFromBuf(pArchive, (const char *)pRaw->raw.mmap.pMap, (sxu32)pRaw->raw.mmap.nSize);
5180 if( rc != SXRET_OK ){
5181 if( pVfs->xUnmap ){
5182 pVfs->xUnmap(pRaw->raw.mmap.pMap, pRaw->raw.mmap.nSize);
5183 }
5184 /* Release the allocated chunk */
5185 jx9_context_free_chunk(pCtx, pArchive);
5186 /* Something goes wrong with this ZIP archive, return FALSE */
5187 jx9_result_bool(pCtx, 0);
5188 return JX9_OK;
5189 }
5190 /* Archive successfully opened */
5191 pRaw->iType = ZIP_RAW_DATA_MMAPED;
5192 pRaw->raw.mmap.pVfs = pVfs;
5193 goto success;
5194 }
5195 }
5196 /* FALL THROUGH */
5197 }
5198 /* Try to open the file in read-only mode */
5199 pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0);
5200 if( pHandle == 0 ){
5201 jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile);
5202 jx9_result_bool(pCtx, 0);
5203 return JX9_OK;
5204 }
5205 pContents = &pRaw->raw.sBlob;
5206 SyBlobInit(pContents, &pCtx->pVm->sAllocator);
5207 /* Read the whole file */
5208 jx9StreamReadWholeFile(pHandle, pStream, pContents);
5209 /* Assume an invalid ZIP file */
5210 rc = SXERR_INVALID;
5211 if( SyBlobLength(pContents) > 0 ){
5212 /* Extract archive entries */
5213 rc = SyZipExtractFromBuf(pArchive, (const char *)SyBlobData(pContents), SyBlobLength(pContents));
5214 }
5215 pRaw->iType = ZIP_RAW_DATA_MEMBUF;
5216 /* Close the stream */
5217 jx9StreamCloseHandle(pStream, pHandle);
5218 if( rc != SXRET_OK ){
5219 /* Release the working buffer */
5220 SyBlobRelease(pContents);
5221 /* Release the allocated chunk */
5222 jx9_context_free_chunk(pCtx, pArchive);
5223 /* Something goes wrong with this ZIP archive, return FALSE */
5224 jx9_result_bool(pCtx, 0);
5225 return JX9_OK;
5226 }
5227success:
5228 /* Reset the loop cursor */
5229 SyArchiveResetLoopCursor(pArchive);
5230 /* Return the in-memory archive as a resource handle */
5231 jx9_result_resource(pCtx, pArchive);
5232 return JX9_OK;
5233}
5234/*
5235 * void zip_close(resource $zip)
5236 * Close an in-memory ZIP archive.
5237 * Parameters
5238 * $zip
5239 * A ZIP file previously opened with zip_open().
5240 * Return
5241 * null.
5242 */
5243static int jx9Builtin_zip_close(jx9_context *pCtx, int nArg, jx9_value **apArg)
5244{
5245 SyArchive *pArchive;
5246 zip_raw_data *pRaw;
5247 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
5248 /* Missing/Invalid arguments */
5249 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
5250 return JX9_OK;
5251 }
5252 /* Point to the in-memory archive */
5253 pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]);
5254 /* Make sure we are dealing with a valid ZIP archive */
5255 if( SXARCH_INVALID(pArchive) ){
5256 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
5257 return JX9_OK;
5258 }
5259 /* Release the archive */
5260 SyArchiveRelease(pArchive);
5261 pRaw = (zip_raw_data *)&pArchive[1];
5262 if( pRaw->iType == ZIP_RAW_DATA_MEMBUF ){
5263 SyBlobRelease(&pRaw->raw.sBlob);
5264 }else{
5265 const jx9_vfs *pVfs = pRaw->raw.mmap.pVfs;
5266 if( pVfs->xUnmap ){
5267 /* Unmap the memory view */
5268 pVfs->xUnmap(pRaw->raw.mmap.pMap, pRaw->raw.mmap.nSize);
5269 }
5270 }
5271 /* Release the memory chunk */
5272 jx9_context_free_chunk(pCtx, pArchive);
5273 return JX9_OK;
5274}
5275/*
5276 * mixed zip_read(resource $zip)
5277 * Reads the next entry from an in-memory ZIP archive.
5278 * Parameters
5279 * $zip
5280 * A ZIP file previously opened with zip_open().
5281 * Return
5282 * A directory entry resource for later use with the zip_entry_... functions
5283 * or FALSE if there are no more entries to read, or an error code if an error occurred.
5284 */
5285static int jx9Builtin_zip_read(jx9_context *pCtx, int nArg, jx9_value **apArg)
5286{
5287 SyArchiveEntry *pNext = 0; /* cc warning */
5288 SyArchive *pArchive;
5289 sxi32 rc;
5290 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
5291 /* Missing/Invalid arguments */
5292 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
5293 /* return FALSE */
5294 jx9_result_bool(pCtx, 0);
5295 return JX9_OK;
5296 }
5297 /* Point to the in-memory archive */
5298 pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]);
5299 /* Make sure we are dealing with a valid ZIP archive */
5300 if( SXARCH_INVALID(pArchive) ){
5301 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
5302 /* return FALSE */
5303 jx9_result_bool(pCtx, 0);
5304 return JX9_OK;
5305 }
5306 /* Extract the next entry */
5307 rc = SyArchiveGetNextEntry(pArchive, &pNext);
5308 if( rc != SXRET_OK ){
5309 /* No more entries in the central directory, return FALSE */
5310 jx9_result_bool(pCtx, 0);
5311 }else{
5312 /* Return as a resource handle */
5313 jx9_result_resource(pCtx, pNext);
5314 /* Point to the ZIP raw data */
5315 pNext->pUserData = (void *)&pArchive[1];
5316 }
5317 return JX9_OK;
5318}
5319/*
5320 * bool zip_entry_open(resource $zip, resource $zip_entry[, string $mode ])
5321 * Open a directory entry for reading
5322 * Parameters
5323 * $zip
5324 * A ZIP file previously opened with zip_open().
5325 * $zip_entry
5326 * A directory entry returned by zip_read().
5327 * $mode
5328 * Not used
5329 * Return
5330 * A directory entry resource for later use with the zip_entry_... functions
5331 * or FALSE if there are no more entries to read, or an error code if an error occurred.
5332 */
5333static int jx9Builtin_zip_entry_open(jx9_context *pCtx, int nArg, jx9_value **apArg)
5334{
5335 SyArchiveEntry *pEntry;
5336 SyArchive *pArchive;
5337 if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_resource(apArg[1]) ){
5338 /* Missing/Invalid arguments */
5339 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
5340 /* return FALSE */
5341 jx9_result_bool(pCtx, 0);
5342 return JX9_OK;
5343 }
5344 /* Point to the in-memory archive */
5345 pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]);
5346 /* Make sure we are dealing with a valid ZIP archive */
5347 if( SXARCH_INVALID(pArchive) ){
5348 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive");
5349 /* return FALSE */
5350 jx9_result_bool(pCtx, 0);
5351 return JX9_OK;
5352 }
5353 /* Make sure we are dealing with a valid ZIP archive entry */
5354 pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[1]);
5355 if( SXARCH_ENTRY_INVALID(pEntry) ){
5356 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
5357 /* return FALSE */
5358 jx9_result_bool(pCtx, 0);
5359 return JX9_OK;
5360 }
5361 /* All done. Actually this function is a no-op, return TRUE */
5362 jx9_result_bool(pCtx, 1);
5363 return JX9_OK;
5364}
5365/*
5366 * bool zip_entry_close(resource $zip_entry)
5367 * Close a directory entry.
5368 * Parameters
5369 * $zip_entry
5370 * A directory entry returned by zip_read().
5371 * Return
5372 * Returns TRUE on success or FALSE on failure.
5373 */
5374static int jx9Builtin_zip_entry_close(jx9_context *pCtx, int nArg, jx9_value **apArg)
5375{
5376 SyArchiveEntry *pEntry;
5377 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
5378 /* Missing/Invalid arguments */
5379 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
5380 /* return FALSE */
5381 jx9_result_bool(pCtx, 0);
5382 return JX9_OK;
5383 }
5384 /* Make sure we are dealing with a valid ZIP archive entry */
5385 pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
5386 if( SXARCH_ENTRY_INVALID(pEntry) ){
5387 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
5388 /* return FALSE */
5389 jx9_result_bool(pCtx, 0);
5390 return JX9_OK;
5391 }
5392 /* Reset the read cursor */
5393 pEntry->nReadCount = 0;
5394 /*All done. Actually this function is a no-op, return TRUE */
5395 jx9_result_bool(pCtx, 1);
5396 return JX9_OK;
5397}
5398/*
5399 * string zip_entry_name(resource $zip_entry)
5400 * Retrieve the name of a directory entry.
5401 * Parameters
5402 * $zip_entry
5403 * A directory entry returned by zip_read().
5404 * Return
5405 * The name of the directory entry.
5406 */
5407static int jx9Builtin_zip_entry_name(jx9_context *pCtx, int nArg, jx9_value **apArg)
5408{
5409 SyArchiveEntry *pEntry;
5410 SyString *pName;
5411 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
5412 /* Missing/Invalid arguments */
5413 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
5414 /* return FALSE */
5415 jx9_result_bool(pCtx, 0);
5416 return JX9_OK;
5417 }
5418 /* Make sure we are dealing with a valid ZIP archive entry */
5419 pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
5420 if( SXARCH_ENTRY_INVALID(pEntry) ){
5421 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
5422 /* return FALSE */
5423 jx9_result_bool(pCtx, 0);
5424 return JX9_OK;
5425 }
5426 /* Return entry name */
5427 pName = &pEntry->sFileName;
5428 jx9_result_string(pCtx, pName->zString, (int)pName->nByte);
5429 return JX9_OK;
5430}
5431/*
5432 * int64 zip_entry_filesize(resource $zip_entry)
5433 * Retrieve the actual file size of a directory entry.
5434 * Parameters
5435 * $zip_entry
5436 * A directory entry returned by zip_read().
5437 * Return
5438 * The size of the directory entry.
5439 */
5440static int jx9Builtin_zip_entry_filesize(jx9_context *pCtx, int nArg, jx9_value **apArg)
5441{
5442 SyArchiveEntry *pEntry;
5443 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
5444 /* Missing/Invalid arguments */
5445 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
5446 /* return FALSE */
5447 jx9_result_bool(pCtx, 0);
5448 return JX9_OK;
5449 }
5450 /* Make sure we are dealing with a valid ZIP archive entry */
5451 pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
5452 if( SXARCH_ENTRY_INVALID(pEntry) ){
5453 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
5454 /* return FALSE */
5455 jx9_result_bool(pCtx, 0);
5456 return JX9_OK;
5457 }
5458 /* Return entry size */
5459 jx9_result_int64(pCtx, (jx9_int64)pEntry->nByte);
5460 return JX9_OK;
5461}
5462/*
5463 * int64 zip_entry_compressedsize(resource $zip_entry)
5464 * Retrieve the compressed size of a directory entry.
5465 * Parameters
5466 * $zip_entry
5467 * A directory entry returned by zip_read().
5468 * Return
5469 * The compressed size.
5470 */
5471static int jx9Builtin_zip_entry_compressedsize(jx9_context *pCtx, int nArg, jx9_value **apArg)
5472{
5473 SyArchiveEntry *pEntry;
5474 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
5475 /* Missing/Invalid arguments */
5476 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
5477 /* return FALSE */
5478 jx9_result_bool(pCtx, 0);
5479 return JX9_OK;
5480 }
5481 /* Make sure we are dealing with a valid ZIP archive entry */
5482 pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
5483 if( SXARCH_ENTRY_INVALID(pEntry) ){
5484 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
5485 /* return FALSE */
5486 jx9_result_bool(pCtx, 0);
5487 return JX9_OK;
5488 }
5489 /* Return entry compressed size */
5490 jx9_result_int64(pCtx, (jx9_int64)pEntry->nByteCompr);
5491 return JX9_OK;
5492}
5493/*
5494 * string zip_entry_read(resource $zip_entry[, int $length])
5495 * Reads from an open directory entry.
5496 * Parameters
5497 * $zip_entry
5498 * A directory entry returned by zip_read().
5499 * $length
5500 * The number of bytes to return. If not specified, this function
5501 * will attempt to read 1024 bytes.
5502 * Return
5503 * Returns the data read, or FALSE if the end of the file is reached.
5504 */
5505static int jx9Builtin_zip_entry_read(jx9_context *pCtx, int nArg, jx9_value **apArg)
5506{
5507 SyArchiveEntry *pEntry;
5508 zip_raw_data *pRaw;
5509 const char *zData;
5510 int iLength;
5511 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
5512 /* Missing/Invalid arguments */
5513 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
5514 /* return FALSE */
5515 jx9_result_bool(pCtx, 0);
5516 return JX9_OK;
5517 }
5518 /* Make sure we are dealing with a valid ZIP archive entry */
5519 pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
5520 if( SXARCH_ENTRY_INVALID(pEntry) ){
5521 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
5522 /* return FALSE */
5523 jx9_result_bool(pCtx, 0);
5524 return JX9_OK;
5525 }
5526 zData = 0;
5527 if( pEntry->nReadCount >= pEntry->nByteCompr ){
5528 /* No more data to read, return FALSE */
5529 jx9_result_bool(pCtx, 0);
5530 return JX9_OK;
5531 }
5532 /* Set a default read length */
5533 iLength = 1024;
5534 if( nArg > 1 ){
5535 iLength = jx9_value_to_int(apArg[1]);
5536 if( iLength < 1 ){
5537 iLength = 1024;
5538 }
5539 }
5540 if( (sxu32)iLength > pEntry->nByteCompr - pEntry->nReadCount ){
5541 iLength = (int)(pEntry->nByteCompr - pEntry->nReadCount);
5542 }
5543 /* Return the entry contents */
5544 pRaw = (zip_raw_data *)pEntry->pUserData;
5545 if( pRaw->iType == ZIP_RAW_DATA_MEMBUF ){
5546 zData = (const char *)SyBlobDataAt(&pRaw->raw.sBlob, (pEntry->nOfft+pEntry->nReadCount));
5547 }else{
5548 const char *zMap = (const char *)pRaw->raw.mmap.pMap;
5549 /* Memory mmaped chunk */
5550 zData = &zMap[pEntry->nOfft+pEntry->nReadCount];
5551 }
5552 /* Increment the read counter */
5553 pEntry->nReadCount += iLength;
5554 /* Return the raw data */
5555 jx9_result_string(pCtx, zData, iLength);
5556 return JX9_OK;
5557}
5558/*
5559 * bool zip_entry_reset_cursor(resource $zip_entry)
5560 * Reset the read cursor of an open directory entry.
5561 * Parameters
5562 * $zip_entry
5563 * A directory entry returned by zip_read().
5564 * Return
5565 * TRUE on success, FALSE on failure.
5566 */
5567static int jx9Builtin_zip_entry_reset_cursor(jx9_context *pCtx, int nArg, jx9_value **apArg)
5568{
5569 SyArchiveEntry *pEntry;
5570 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
5571 /* Missing/Invalid arguments */
5572 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
5573 /* return FALSE */
5574 jx9_result_bool(pCtx, 0);
5575 return JX9_OK;
5576 }
5577 /* Make sure we are dealing with a valid ZIP archive entry */
5578 pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
5579 if( SXARCH_ENTRY_INVALID(pEntry) ){
5580 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
5581 /* return FALSE */
5582 jx9_result_bool(pCtx, 0);
5583 return JX9_OK;
5584 }
5585 /* Reset the cursor */
5586 pEntry->nReadCount = 0;
5587 /* Return TRUE */
5588 jx9_result_bool(pCtx, 1);
5589 return JX9_OK;
5590}
5591/*
5592 * string zip_entry_compressionmethod(resource $zip_entry)
5593 * Retrieve the compression method of a directory entry.
5594 * Parameters
5595 * $zip_entry
5596 * A directory entry returned by zip_read().
5597 * Return
5598 * The compression method on success or FALSE on failure.
5599 */
5600static int jx9Builtin_zip_entry_compressionmethod(jx9_context *pCtx, int nArg, jx9_value **apArg)
5601{
5602 SyArchiveEntry *pEntry;
5603 if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){
5604 /* Missing/Invalid arguments */
5605 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
5606 /* return FALSE */
5607 jx9_result_bool(pCtx, 0);
5608 return JX9_OK;
5609 }
5610 /* Make sure we are dealing with a valid ZIP archive entry */
5611 pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]);
5612 if( SXARCH_ENTRY_INVALID(pEntry) ){
5613 jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry");
5614 /* return FALSE */
5615 jx9_result_bool(pCtx, 0);
5616 return JX9_OK;
5617 }
5618 switch(pEntry->nComprMeth){
5619 case 0:
5620 /* No compression;entry is stored */
5621 jx9_result_string(pCtx, "stored", (int)sizeof("stored")-1);
5622 break;
5623 case 8:
5624 /* Entry is deflated (Default compression algorithm) */
5625 jx9_result_string(pCtx, "deflate", (int)sizeof("deflate")-1);
5626 break;
5627 /* Exotic compression algorithms */
5628 case 1:
5629 jx9_result_string(pCtx, "shrunk", (int)sizeof("shrunk")-1);
5630 break;
5631 case 2:
5632 case 3:
5633 case 4:
5634 case 5:
5635 /* Entry is reduced */
5636 jx9_result_string(pCtx, "reduced", (int)sizeof("reduced")-1);
5637 break;
5638 case 6:
5639 /* Entry is imploded */
5640 jx9_result_string(pCtx, "implode", (int)sizeof("implode")-1);
5641 break;
5642 default:
5643 jx9_result_string(pCtx, "unknown", (int)sizeof("unknown")-1);
5644 break;
5645 }
5646 return JX9_OK;
5647}
5648#endif /* #ifndef JX9_DISABLE_BUILTIN_FUNC*/
5649/* NULL VFS [i.e: a no-op VFS]*/
5650static const jx9_vfs null_vfs = {
5651 "null_vfs",
5652 JX9_VFS_VERSION,
5653 0, /* int (*xChdir)(const char *) */
5654 0, /* int (*xChroot)(const char *); */
5655 0, /* int (*xGetcwd)(jx9_context *) */
5656 0, /* int (*xMkdir)(const char *, int, int) */
5657 0, /* int (*xRmdir)(const char *) */
5658 0, /* int (*xIsdir)(const char *) */
5659 0, /* int (*xRename)(const char *, const char *) */
5660 0, /*int (*xRealpath)(const char *, jx9_context *)*/
5661 0, /* int (*xSleep)(unsigned int) */
5662 0, /* int (*xUnlink)(const char *) */
5663 0, /* int (*xFileExists)(const char *) */
5664 0, /*int (*xChmod)(const char *, int)*/
5665 0, /*int (*xChown)(const char *, const char *)*/
5666 0, /*int (*xChgrp)(const char *, const char *)*/
5667 0, /* jx9_int64 (*xFreeSpace)(const char *) */
5668 0, /* jx9_int64 (*xTotalSpace)(const char *) */
5669 0, /* jx9_int64 (*xFileSize)(const char *) */
5670 0, /* jx9_int64 (*xFileAtime)(const char *) */
5671 0, /* jx9_int64 (*xFileMtime)(const char *) */
5672 0, /* jx9_int64 (*xFileCtime)(const char *) */
5673 0, /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
5674 0, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
5675 0, /* int (*xIsfile)(const char *) */
5676 0, /* int (*xIslink)(const char *) */
5677 0, /* int (*xReadable)(const char *) */
5678 0, /* int (*xWritable)(const char *) */
5679 0, /* int (*xExecutable)(const char *) */
5680 0, /* int (*xFiletype)(const char *, jx9_context *) */
5681 0, /* int (*xGetenv)(const char *, jx9_context *) */
5682 0, /* int (*xSetenv)(const char *, const char *) */
5683 0, /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
5684 0, /* int (*xMmap)(const char *, void **, jx9_int64 *) */
5685 0, /* void (*xUnmap)(void *, jx9_int64); */
5686 0, /* int (*xLink)(const char *, const char *, int) */
5687 0, /* int (*xUmask)(int) */
5688 0, /* void (*xTempDir)(jx9_context *) */
5689 0, /* unsigned int (*xProcessId)(void) */
5690 0, /* int (*xUid)(void) */
5691 0, /* int (*xGid)(void) */
5692 0, /* void (*xUsername)(jx9_context *) */
5693 0 /* int (*xExec)(const char *, jx9_context *) */
5694};
5695#ifndef JX9_DISABLE_BUILTIN_FUNC
5696#ifndef JX9_DISABLE_DISK_IO
5697#ifdef __WINNT__
5698/*
5699 * Windows VFS implementation for the JX9 engine.
5700 * Authors:
5701 * Symisc Systems, devel@symisc.net.
5702 * Copyright (C) Symisc Systems, http://jx9.symisc.net
5703 * Status:
5704 * Stable.
5705 */
5706/* What follows here is code that is specific to windows systems. */
5707#include <Windows.h>
5708/*
5709** Convert a UTF-8 string to microsoft unicode (UTF-16?).
5710**
5711** Space to hold the returned string is obtained from HeapAlloc().
5712** Taken from the sqlite3 source tree
5713** status: Public Domain
5714*/
5715static WCHAR *jx9utf8ToUnicode(const char *zFilename){
5716 int nChar;
5717 WCHAR *zWideFilename;
5718
5719 nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, 0, 0);
5720 zWideFilename = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, nChar*sizeof(zWideFilename[0]));
5721 if( zWideFilename == 0 ){
5722 return 0;
5723 }
5724 nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar);
5725 if( nChar==0 ){
5726 HeapFree(GetProcessHeap(), 0, zWideFilename);
5727 return 0;
5728 }
5729 return zWideFilename;
5730}
5731/*
5732** Convert a UTF-8 filename into whatever form the underlying
5733** operating system wants filenames in.Space to hold the result
5734** is obtained from HeapAlloc() and must be freed by the calling
5735** function.
5736** Taken from the sqlite3 source tree
5737** status: Public Domain
5738*/
5739static void *jx9convertUtf8Filename(const char *zFilename){
5740 void *zConverted;
5741 zConverted = jx9utf8ToUnicode(zFilename);
5742 return zConverted;
5743}
5744/*
5745** Convert microsoft unicode to UTF-8. Space to hold the returned string is
5746** obtained from HeapAlloc().
5747** Taken from the sqlite3 source tree
5748** status: Public Domain
5749*/
5750static char *jx9unicodeToUtf8(const WCHAR *zWideFilename){
5751 char *zFilename;
5752 int nByte;
5753
5754 nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0);
5755 zFilename = (char *)HeapAlloc(GetProcessHeap(), 0, nByte);
5756 if( zFilename == 0 ){
5757 return 0;
5758 }
5759 nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, 0, 0);
5760 if( nByte == 0 ){
5761 HeapFree(GetProcessHeap(), 0, zFilename);
5762 return 0;
5763 }
5764 return zFilename;
5765}
5766/* int (*xchdir)(const char *) */
5767static int WinVfs_chdir(const char *zPath)
5768{
5769 void * pConverted;
5770 BOOL rc;
5771 pConverted = jx9convertUtf8Filename(zPath);
5772 if( pConverted == 0 ){
5773 return -1;
5774 }
5775 rc = SetCurrentDirectoryW((LPCWSTR)pConverted);
5776 HeapFree(GetProcessHeap(), 0, pConverted);
5777 return rc ? JX9_OK : -1;
5778}
5779/* int (*xGetcwd)(jx9_context *) */
5780static int WinVfs_getcwd(jx9_context *pCtx)
5781{
5782 WCHAR zDir[2048];
5783 char *zConverted;
5784 DWORD rc;
5785 /* Get the current directory */
5786 rc = GetCurrentDirectoryW(sizeof(zDir), zDir);
5787 if( rc < 1 ){
5788 return -1;
5789 }
5790 zConverted = jx9unicodeToUtf8(zDir);
5791 if( zConverted == 0 ){
5792 return -1;
5793 }
5794 jx9_result_string(pCtx, zConverted, -1/*Compute length automatically*/); /* Will make it's own copy */
5795 HeapFree(GetProcessHeap(), 0, zConverted);
5796 return JX9_OK;
5797}
5798/* int (*xMkdir)(const char *, int, int) */
5799static int WinVfs_mkdir(const char *zPath, int mode, int recursive)
5800{
5801 void * pConverted;
5802 BOOL rc;
5803 pConverted = jx9convertUtf8Filename(zPath);
5804 if( pConverted == 0 ){
5805 return -1;
5806 }
5807 mode= 0; /* MSVC warning */
5808 recursive = 0;
5809 rc = CreateDirectoryW((LPCWSTR)pConverted, 0);
5810 HeapFree(GetProcessHeap(), 0, pConverted);
5811 return rc ? JX9_OK : -1;
5812}
5813/* int (*xRmdir)(const char *) */
5814static int WinVfs_rmdir(const char *zPath)
5815{
5816 void * pConverted;
5817 BOOL rc;
5818 pConverted = jx9convertUtf8Filename(zPath);
5819 if( pConverted == 0 ){
5820 return -1;
5821 }
5822 rc = RemoveDirectoryW((LPCWSTR)pConverted);
5823 HeapFree(GetProcessHeap(), 0, pConverted);
5824 return rc ? JX9_OK : -1;
5825}
5826/* int (*xIsdir)(const char *) */
5827static int WinVfs_isdir(const char *zPath)
5828{
5829 void * pConverted;
5830 DWORD dwAttr;
5831 pConverted = jx9convertUtf8Filename(zPath);
5832 if( pConverted == 0 ){
5833 return -1;
5834 }
5835 dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
5836 HeapFree(GetProcessHeap(), 0, pConverted);
5837 if( dwAttr == INVALID_FILE_ATTRIBUTES ){
5838 return -1;
5839 }
5840 return (dwAttr & FILE_ATTRIBUTE_DIRECTORY) ? JX9_OK : -1;
5841}
5842/* int (*xRename)(const char *, const char *) */
5843static int WinVfs_Rename(const char *zOld, const char *zNew)
5844{
5845 void *pOld, *pNew;
5846 BOOL rc = 0;
5847 pOld = jx9convertUtf8Filename(zOld);
5848 if( pOld == 0 ){
5849 return -1;
5850 }
5851 pNew = jx9convertUtf8Filename(zNew);
5852 if( pNew ){
5853 rc = MoveFileW((LPCWSTR)pOld, (LPCWSTR)pNew);
5854 }
5855 HeapFree(GetProcessHeap(), 0, pOld);
5856 if( pNew ){
5857 HeapFree(GetProcessHeap(), 0, pNew);
5858 }
5859 return rc ? JX9_OK : - 1;
5860}
5861/* int (*xRealpath)(const char *, jx9_context *) */
5862static int WinVfs_Realpath(const char *zPath, jx9_context *pCtx)
5863{
5864 WCHAR zTemp[2048];
5865 void *pPath;
5866 char *zReal;
5867 DWORD n;
5868 pPath = jx9convertUtf8Filename(zPath);
5869 if( pPath == 0 ){
5870 return -1;
5871 }
5872 n = GetFullPathNameW((LPCWSTR)pPath, 0, 0, 0);
5873 if( n > 0 ){
5874 if( n >= sizeof(zTemp) ){
5875 n = sizeof(zTemp) - 1;
5876 }
5877 GetFullPathNameW((LPCWSTR)pPath, n, zTemp, 0);
5878 }
5879 HeapFree(GetProcessHeap(), 0, pPath);
5880 if( !n ){
5881 return -1;
5882 }
5883 zReal = jx9unicodeToUtf8(zTemp);
5884 if( zReal == 0 ){
5885 return -1;
5886 }
5887 jx9_result_string(pCtx, zReal, -1); /* Will make it's own copy */
5888 HeapFree(GetProcessHeap(), 0, zReal);
5889 return JX9_OK;
5890}
5891/* int (*xSleep)(unsigned int) */
5892static int WinVfs_Sleep(unsigned int uSec)
5893{
5894 Sleep(uSec/1000/*uSec per Millisec */);
5895 return JX9_OK;
5896}
5897/* int (*xUnlink)(const char *) */
5898static int WinVfs_unlink(const char *zPath)
5899{
5900 void * pConverted;
5901 BOOL rc;
5902 pConverted = jx9convertUtf8Filename(zPath);
5903 if( pConverted == 0 ){
5904 return -1;
5905 }
5906 rc = DeleteFileW((LPCWSTR)pConverted);
5907 HeapFree(GetProcessHeap(), 0, pConverted);
5908 return rc ? JX9_OK : - 1;
5909}
5910/* jx9_int64 (*xFreeSpace)(const char *) */
5911static jx9_int64 WinVfs_DiskFreeSpace(const char *zPath)
5912{
5913#ifdef _WIN32_WCE
5914 /* GetDiskFreeSpace is not supported under WINCE */
5915 SXUNUSED(zPath);
5916 return 0;
5917#else
5918 DWORD dwSectPerClust, dwBytesPerSect, dwFreeClusters, dwTotalClusters;
5919 void * pConverted;
5920 WCHAR *p;
5921 BOOL rc;
5922 pConverted = jx9convertUtf8Filename(zPath);
5923 if( pConverted == 0 ){
5924 return 0;
5925 }
5926 p = (WCHAR *)pConverted;
5927 for(;*p;p++){
5928 if( *p == '\\' || *p == '/'){
5929 *p = '\0';
5930 break;
5931 }
5932 }
5933 rc = GetDiskFreeSpaceW((LPCWSTR)pConverted, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters);
5934 if( !rc ){
5935 return 0;
5936 }
5937 return (jx9_int64)dwFreeClusters * dwSectPerClust * dwBytesPerSect;
5938#endif
5939}
5940/* jx9_int64 (*xTotalSpace)(const char *) */
5941static jx9_int64 WinVfs_DiskTotalSpace(const char *zPath)
5942{
5943#ifdef _WIN32_WCE
5944 /* GetDiskFreeSpace is not supported under WINCE */
5945 SXUNUSED(zPath);
5946 return 0;
5947#else
5948 DWORD dwSectPerClust, dwBytesPerSect, dwFreeClusters, dwTotalClusters;
5949 void * pConverted;
5950 WCHAR *p;
5951 BOOL rc;
5952 pConverted = jx9convertUtf8Filename(zPath);
5953 if( pConverted == 0 ){
5954 return 0;
5955 }
5956 p = (WCHAR *)pConverted;
5957 for(;*p;p++){
5958 if( *p == '\\' || *p == '/'){
5959 *p = '\0';
5960 break;
5961 }
5962 }
5963 rc = GetDiskFreeSpaceW((LPCWSTR)pConverted, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters);
5964 if( !rc ){
5965 return 0;
5966 }
5967 return (jx9_int64)dwTotalClusters * dwSectPerClust * dwBytesPerSect;
5968#endif
5969}
5970/* int (*xFileExists)(const char *) */
5971static int WinVfs_FileExists(const char *zPath)
5972{
5973 void * pConverted;
5974 DWORD dwAttr;
5975 pConverted = jx9convertUtf8Filename(zPath);
5976 if( pConverted == 0 ){
5977 return -1;
5978 }
5979 dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
5980 HeapFree(GetProcessHeap(), 0, pConverted);
5981 if( dwAttr == INVALID_FILE_ATTRIBUTES ){
5982 return -1;
5983 }
5984 return JX9_OK;
5985}
5986/* Open a file in a read-only mode */
5987static HANDLE OpenReadOnly(LPCWSTR pPath)
5988{
5989 DWORD dwType = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5990 DWORD dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE;
5991 DWORD dwAccess = GENERIC_READ;
5992 DWORD dwCreate = OPEN_EXISTING;
5993 HANDLE pHandle;
5994 pHandle = CreateFileW(pPath, dwAccess, dwShare, 0, dwCreate, dwType, 0);
5995 if( pHandle == INVALID_HANDLE_VALUE){
5996 return 0;
5997 }
5998 return pHandle;
5999}
6000/* jx9_int64 (*xFileSize)(const char *) */
6001static jx9_int64 WinVfs_FileSize(const char *zPath)
6002{
6003 DWORD dwLow, dwHigh;
6004 void * pConverted;
6005 jx9_int64 nSize;
6006 HANDLE pHandle;
6007
6008 pConverted = jx9convertUtf8Filename(zPath);
6009 if( pConverted == 0 ){
6010 return -1;
6011 }
6012 /* Open the file in read-only mode */
6013 pHandle = OpenReadOnly((LPCWSTR)pConverted);
6014 HeapFree(GetProcessHeap(), 0, pConverted);
6015 if( pHandle ){
6016 dwLow = GetFileSize(pHandle, &dwHigh);
6017 nSize = dwHigh;
6018 nSize <<= 32;
6019 nSize += dwLow;
6020 CloseHandle(pHandle);
6021 }else{
6022 nSize = -1;
6023 }
6024 return nSize;
6025}
6026#define TICKS_PER_SECOND 10000000
6027#define EPOCH_DIFFERENCE 11644473600LL
6028/* Convert Windows timestamp to UNIX timestamp */
6029static jx9_int64 convertWindowsTimeToUnixTime(LPFILETIME pTime)
6030{
6031 jx9_int64 input, temp;
6032 input = pTime->dwHighDateTime;
6033 input <<= 32;
6034 input += pTime->dwLowDateTime;
6035 temp = input / TICKS_PER_SECOND; /*convert from 100ns intervals to seconds*/
6036 temp = temp - EPOCH_DIFFERENCE; /*subtract number of seconds between epochs*/
6037 return temp;
6038}
6039/* Convert UNIX timestamp to Windows timestamp */
6040static void convertUnixTimeToWindowsTime(jx9_int64 nUnixtime, LPFILETIME pOut)
6041{
6042 jx9_int64 result = EPOCH_DIFFERENCE;
6043 result += nUnixtime;
6044 result *= 10000000LL;
6045 pOut->dwHighDateTime = (DWORD)(nUnixtime>>32);
6046 pOut->dwLowDateTime = (DWORD)nUnixtime;
6047}
6048/* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
6049static int WinVfs_Touch(const char *zPath, jx9_int64 touch_time, jx9_int64 access_time)
6050{
6051 FILETIME sTouch, sAccess;
6052 void *pConverted;
6053 void *pHandle;
6054 BOOL rc = 0;
6055 pConverted = jx9convertUtf8Filename(zPath);
6056 if( pConverted == 0 ){
6057 return -1;
6058 }
6059 pHandle = OpenReadOnly((LPCWSTR)pConverted);
6060 if( pHandle ){
6061 if( touch_time < 0 ){
6062 GetSystemTimeAsFileTime(&sTouch);
6063 }else{
6064 convertUnixTimeToWindowsTime(touch_time, &sTouch);
6065 }
6066 if( access_time < 0 ){
6067 /* Use the touch time */
6068 sAccess = sTouch; /* Structure assignment */
6069 }else{
6070 convertUnixTimeToWindowsTime(access_time, &sAccess);
6071 }
6072 rc = SetFileTime(pHandle, &sTouch, &sAccess, 0);
6073 /* Close the handle */
6074 CloseHandle(pHandle);
6075 }
6076 HeapFree(GetProcessHeap(), 0, pConverted);
6077 return rc ? JX9_OK : -1;
6078}
6079/* jx9_int64 (*xFileAtime)(const char *) */
6080static jx9_int64 WinVfs_FileAtime(const char *zPath)
6081{
6082 BY_HANDLE_FILE_INFORMATION sInfo;
6083 void * pConverted;
6084 jx9_int64 atime;
6085 HANDLE pHandle;
6086 pConverted = jx9convertUtf8Filename(zPath);
6087 if( pConverted == 0 ){
6088 return -1;
6089 }
6090 /* Open the file in read-only mode */
6091 pHandle = OpenReadOnly((LPCWSTR)pConverted);
6092 if( pHandle ){
6093 BOOL rc;
6094 rc = GetFileInformationByHandle(pHandle, &sInfo);
6095 if( rc ){
6096 atime = convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime);
6097 }else{
6098 atime = -1;
6099 }
6100 CloseHandle(pHandle);
6101 }else{
6102 atime = -1;
6103 }
6104 HeapFree(GetProcessHeap(), 0, pConverted);
6105 return atime;
6106}
6107/* jx9_int64 (*xFileMtime)(const char *) */
6108static jx9_int64 WinVfs_FileMtime(const char *zPath)
6109{
6110 BY_HANDLE_FILE_INFORMATION sInfo;
6111 void * pConverted;
6112 jx9_int64 mtime;
6113 HANDLE pHandle;
6114 pConverted = jx9convertUtf8Filename(zPath);
6115 if( pConverted == 0 ){
6116 return -1;
6117 }
6118 /* Open the file in read-only mode */
6119 pHandle = OpenReadOnly((LPCWSTR)pConverted);
6120 if( pHandle ){
6121 BOOL rc;
6122 rc = GetFileInformationByHandle(pHandle, &sInfo);
6123 if( rc ){
6124 mtime = convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime);
6125 }else{
6126 mtime = -1;
6127 }
6128 CloseHandle(pHandle);
6129 }else{
6130 mtime = -1;
6131 }
6132 HeapFree(GetProcessHeap(), 0, pConverted);
6133 return mtime;
6134}
6135/* jx9_int64 (*xFileCtime)(const char *) */
6136static jx9_int64 WinVfs_FileCtime(const char *zPath)
6137{
6138 BY_HANDLE_FILE_INFORMATION sInfo;
6139 void * pConverted;
6140 jx9_int64 ctime;
6141 HANDLE pHandle;
6142 pConverted = jx9convertUtf8Filename(zPath);
6143 if( pConverted == 0 ){
6144 return -1;
6145 }
6146 /* Open the file in read-only mode */
6147 pHandle = OpenReadOnly((LPCWSTR)pConverted);
6148 if( pHandle ){
6149 BOOL rc;
6150 rc = GetFileInformationByHandle(pHandle, &sInfo);
6151 if( rc ){
6152 ctime = convertWindowsTimeToUnixTime(&sInfo.ftCreationTime);
6153 }else{
6154 ctime = -1;
6155 }
6156 CloseHandle(pHandle);
6157 }else{
6158 ctime = -1;
6159 }
6160 HeapFree(GetProcessHeap(), 0, pConverted);
6161 return ctime;
6162}
6163/* int (*xStat)(const char *, jx9_value *, jx9_value *) */
6164/* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
6165static int WinVfs_Stat(const char *zPath, jx9_value *pArray, jx9_value *pWorker)
6166{
6167 BY_HANDLE_FILE_INFORMATION sInfo;
6168 void *pConverted;
6169 HANDLE pHandle;
6170 BOOL rc;
6171 pConverted = jx9convertUtf8Filename(zPath);
6172 if( pConverted == 0 ){
6173 return -1;
6174 }
6175 /* Open the file in read-only mode */
6176 pHandle = OpenReadOnly((LPCWSTR)pConverted);
6177 HeapFree(GetProcessHeap(), 0, pConverted);
6178 if( pHandle == 0 ){
6179 return -1;
6180 }
6181 rc = GetFileInformationByHandle(pHandle, &sInfo);
6182 CloseHandle(pHandle);
6183 if( !rc ){
6184 return -1;
6185 }
6186 /* dev */
6187 jx9_value_int64(pWorker, (jx9_int64)sInfo.dwVolumeSerialNumber);
6188 jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
6189 /* ino */
6190 jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileIndexHigh << 32) | sInfo.nFileIndexLow));
6191 jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
6192 /* mode */
6193 jx9_value_int(pWorker, 0);
6194 jx9_array_add_strkey_elem(pArray, "mode", pWorker);
6195 /* nlink */
6196 jx9_value_int(pWorker, (int)sInfo.nNumberOfLinks);
6197 jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
6198 /* uid, gid, rdev */
6199 jx9_value_int(pWorker, 0);
6200 jx9_array_add_strkey_elem(pArray, "uid", pWorker);
6201 jx9_array_add_strkey_elem(pArray, "gid", pWorker);
6202 jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
6203 /* size */
6204 jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileSizeHigh << 32) | sInfo.nFileSizeLow));
6205 jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
6206 /* atime */
6207 jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime));
6208 jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
6209 /* mtime */
6210 jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime));
6211 jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
6212 /* ctime */
6213 jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftCreationTime));
6214 jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
6215 /* blksize, blocks */
6216 jx9_value_int(pWorker, 0);
6217 jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
6218 jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
6219 return JX9_OK;
6220}
6221/* int (*xIsfile)(const char *) */
6222static int WinVfs_isfile(const char *zPath)
6223{
6224 void * pConverted;
6225 DWORD dwAttr;
6226 pConverted = jx9convertUtf8Filename(zPath);
6227 if( pConverted == 0 ){
6228 return -1;
6229 }
6230 dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
6231 HeapFree(GetProcessHeap(), 0, pConverted);
6232 if( dwAttr == INVALID_FILE_ATTRIBUTES ){
6233 return -1;
6234 }
6235 return (dwAttr & (FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE)) ? JX9_OK : -1;
6236}
6237/* int (*xIslink)(const char *) */
6238static int WinVfs_islink(const char *zPath)
6239{
6240 void * pConverted;
6241 DWORD dwAttr;
6242 pConverted = jx9convertUtf8Filename(zPath);
6243 if( pConverted == 0 ){
6244 return -1;
6245 }
6246 dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
6247 HeapFree(GetProcessHeap(), 0, pConverted);
6248 if( dwAttr == INVALID_FILE_ATTRIBUTES ){
6249 return -1;
6250 }
6251 return (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT) ? JX9_OK : -1;
6252}
6253/* int (*xWritable)(const char *) */
6254static int WinVfs_iswritable(const char *zPath)
6255{
6256 void * pConverted;
6257 DWORD dwAttr;
6258 pConverted = jx9convertUtf8Filename(zPath);
6259 if( pConverted == 0 ){
6260 return -1;
6261 }
6262 dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
6263 HeapFree(GetProcessHeap(), 0, pConverted);
6264 if( dwAttr == INVALID_FILE_ATTRIBUTES ){
6265 return -1;
6266 }
6267 if( (dwAttr & (FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_NORMAL)) == 0 ){
6268 /* Not a regular file */
6269 return -1;
6270 }
6271 if( dwAttr & FILE_ATTRIBUTE_READONLY ){
6272 /* Read-only file */
6273 return -1;
6274 }
6275 /* File is writable */
6276 return JX9_OK;
6277}
6278/* int (*xExecutable)(const char *) */
6279static int WinVfs_isexecutable(const char *zPath)
6280{
6281 void * pConverted;
6282 DWORD dwAttr;
6283 pConverted = jx9convertUtf8Filename(zPath);
6284 if( pConverted == 0 ){
6285 return -1;
6286 }
6287 dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
6288 HeapFree(GetProcessHeap(), 0, pConverted);
6289 if( dwAttr == INVALID_FILE_ATTRIBUTES ){
6290 return -1;
6291 }
6292 if( (dwAttr & FILE_ATTRIBUTE_NORMAL) == 0 ){
6293 /* Not a regular file */
6294 return -1;
6295 }
6296 /* File is executable */
6297 return JX9_OK;
6298}
6299/* int (*xFiletype)(const char *, jx9_context *) */
6300static int WinVfs_Filetype(const char *zPath, jx9_context *pCtx)
6301{
6302 void * pConverted;
6303 DWORD dwAttr;
6304 pConverted = jx9convertUtf8Filename(zPath);
6305 if( pConverted == 0 ){
6306 /* Expand 'unknown' */
6307 jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
6308 return -1;
6309 }
6310 dwAttr = GetFileAttributesW((LPCWSTR)pConverted);
6311 HeapFree(GetProcessHeap(), 0, pConverted);
6312 if( dwAttr == INVALID_FILE_ATTRIBUTES ){
6313 /* Expand 'unknown' */
6314 jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
6315 return -1;
6316 }
6317 if(dwAttr & (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE) ){
6318 jx9_result_string(pCtx, "file", sizeof("file")-1);
6319 }else if(dwAttr & FILE_ATTRIBUTE_DIRECTORY){
6320 jx9_result_string(pCtx, "dir", sizeof("dir")-1);
6321 }else if(dwAttr & FILE_ATTRIBUTE_REPARSE_POINT){
6322 jx9_result_string(pCtx, "link", sizeof("link")-1);
6323 }else if(dwAttr & (FILE_ATTRIBUTE_DEVICE)){
6324 jx9_result_string(pCtx, "block", sizeof("block")-1);
6325 }else{
6326 jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
6327 }
6328 return JX9_OK;
6329}
6330/* int (*xGetenv)(const char *, jx9_context *) */
6331static int WinVfs_Getenv(const char *zVar, jx9_context *pCtx)
6332{
6333 char zValue[1024];
6334 DWORD n;
6335 /*
6336 * According to MSDN
6337 * If lpBuffer is not large enough to hold the data, the return
6338 * value is the buffer size, in characters, required to hold the
6339 * string and its terminating null character and the contents
6340 * of lpBuffer are undefined.
6341 */
6342 n = sizeof(zValue);
6343 SyMemcpy("Undefined", zValue, sizeof("Undefined")-1);
6344 /* Extract the environment value */
6345 n = GetEnvironmentVariableA(zVar, zValue, sizeof(zValue));
6346 if( !n ){
6347 /* No such variable*/
6348 return -1;
6349 }
6350 jx9_result_string(pCtx, zValue, (int)n);
6351 return JX9_OK;
6352}
6353/* int (*xSetenv)(const char *, const char *) */
6354static int WinVfs_Setenv(const char *zName, const char *zValue)
6355{
6356 BOOL rc;
6357 rc = SetEnvironmentVariableA(zName, zValue);
6358 return rc ? JX9_OK : -1;
6359}
6360/* int (*xMmap)(const char *, void **, jx9_int64 *) */
6361static int WinVfs_Mmap(const char *zPath, void **ppMap, jx9_int64 *pSize)
6362{
6363 DWORD dwSizeLow, dwSizeHigh;
6364 HANDLE pHandle, pMapHandle;
6365 void *pConverted, *pView;
6366
6367 pConverted = jx9convertUtf8Filename(zPath);
6368 if( pConverted == 0 ){
6369 return -1;
6370 }
6371 pHandle = OpenReadOnly((LPCWSTR)pConverted);
6372 HeapFree(GetProcessHeap(), 0, pConverted);
6373 if( pHandle == 0 ){
6374 return -1;
6375 }
6376 /* Get the file size */
6377 dwSizeLow = GetFileSize(pHandle, &dwSizeHigh);
6378 /* Create the mapping */
6379 pMapHandle = CreateFileMappingW(pHandle, 0, PAGE_READONLY, dwSizeHigh, dwSizeLow, 0);
6380 if( pMapHandle == 0 ){
6381 CloseHandle(pHandle);
6382 return -1;
6383 }
6384 *pSize = ((jx9_int64)dwSizeHigh << 32) | dwSizeLow;
6385 /* Obtain the view */
6386 pView = MapViewOfFile(pMapHandle, FILE_MAP_READ, 0, 0, (SIZE_T)(*pSize));
6387 if( pView ){
6388 /* Let the upper layer point to the view */
6389 *ppMap = pView;
6390 }
6391 /* Close the handle
6392 * According to MSDN it's OK the close the HANDLES.
6393 */
6394 CloseHandle(pMapHandle);
6395 CloseHandle(pHandle);
6396 return pView ? JX9_OK : -1;
6397}
6398/* void (*xUnmap)(void *, jx9_int64) */
6399static void WinVfs_Unmap(void *pView, jx9_int64 nSize)
6400{
6401 nSize = 0; /* Compiler warning */
6402 UnmapViewOfFile(pView);
6403}
6404/* void (*xTempDir)(jx9_context *) */
6405static void WinVfs_TempDir(jx9_context *pCtx)
6406{
6407 CHAR zTemp[1024];
6408 DWORD n;
6409 n = GetTempPathA(sizeof(zTemp), zTemp);
6410 if( n < 1 ){
6411 /* Assume the default windows temp directory */
6412 jx9_result_string(pCtx, "C:\\Windows\\Temp", -1/*Compute length automatically*/);
6413 }else{
6414 jx9_result_string(pCtx, zTemp, (int)n);
6415 }
6416}
6417/* unsigned int (*xProcessId)(void) */
6418static unsigned int WinVfs_ProcessId(void)
6419{
6420 DWORD nID = 0;
6421#ifndef __MINGW32__
6422 nID = GetProcessId(GetCurrentProcess());
6423#endif /* __MINGW32__ */
6424 return (unsigned int)nID;
6425}
6426
6427/* Export the windows vfs */
6428static const jx9_vfs sWinVfs = {
6429 "Windows_vfs",
6430 JX9_VFS_VERSION,
6431 WinVfs_chdir, /* int (*xChdir)(const char *) */
6432 0, /* int (*xChroot)(const char *); */
6433 WinVfs_getcwd, /* int (*xGetcwd)(jx9_context *) */
6434 WinVfs_mkdir, /* int (*xMkdir)(const char *, int, int) */
6435 WinVfs_rmdir, /* int (*xRmdir)(const char *) */
6436 WinVfs_isdir, /* int (*xIsdir)(const char *) */
6437 WinVfs_Rename, /* int (*xRename)(const char *, const char *) */
6438 WinVfs_Realpath, /*int (*xRealpath)(const char *, jx9_context *)*/
6439 WinVfs_Sleep, /* int (*xSleep)(unsigned int) */
6440 WinVfs_unlink, /* int (*xUnlink)(const char *) */
6441 WinVfs_FileExists, /* int (*xFileExists)(const char *) */
6442 0, /*int (*xChmod)(const char *, int)*/
6443 0, /*int (*xChown)(const char *, const char *)*/
6444 0, /*int (*xChgrp)(const char *, const char *)*/
6445 WinVfs_DiskFreeSpace, /* jx9_int64 (*xFreeSpace)(const char *) */
6446 WinVfs_DiskTotalSpace, /* jx9_int64 (*xTotalSpace)(const char *) */
6447 WinVfs_FileSize, /* jx9_int64 (*xFileSize)(const char *) */
6448 WinVfs_FileAtime, /* jx9_int64 (*xFileAtime)(const char *) */
6449 WinVfs_FileMtime, /* jx9_int64 (*xFileMtime)(const char *) */
6450 WinVfs_FileCtime, /* jx9_int64 (*xFileCtime)(const char *) */
6451 WinVfs_Stat, /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
6452 WinVfs_Stat, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
6453 WinVfs_isfile, /* int (*xIsfile)(const char *) */
6454 WinVfs_islink, /* int (*xIslink)(const char *) */
6455 WinVfs_isfile, /* int (*xReadable)(const char *) */
6456 WinVfs_iswritable, /* int (*xWritable)(const char *) */
6457 WinVfs_isexecutable, /* int (*xExecutable)(const char *) */
6458 WinVfs_Filetype, /* int (*xFiletype)(const char *, jx9_context *) */
6459 WinVfs_Getenv, /* int (*xGetenv)(const char *, jx9_context *) */
6460 WinVfs_Setenv, /* int (*xSetenv)(const char *, const char *) */
6461 WinVfs_Touch, /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
6462 WinVfs_Mmap, /* int (*xMmap)(const char *, void **, jx9_int64 *) */
6463 WinVfs_Unmap, /* void (*xUnmap)(void *, jx9_int64); */
6464 0, /* int (*xLink)(const char *, const char *, int) */
6465 0, /* int (*xUmask)(int) */
6466 WinVfs_TempDir, /* void (*xTempDir)(jx9_context *) */
6467 WinVfs_ProcessId, /* unsigned int (*xProcessId)(void) */
6468 0, /* int (*xUid)(void) */
6469 0, /* int (*xGid)(void) */
6470 0, /* void (*xUsername)(jx9_context *) */
6471 0 /* int (*xExec)(const char *, jx9_context *) */
6472};
6473/* Windows file IO */
6474#ifndef INVALID_SET_FILE_POINTER
6475# define INVALID_SET_FILE_POINTER ((DWORD)-1)
6476#endif
6477/* int (*xOpen)(const char *, int, jx9_value *, void **) */
6478static int WinFile_Open(const char *zPath, int iOpenMode, jx9_value *pResource, void **ppHandle)
6479{
6480 DWORD dwType = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
6481 DWORD dwAccess = GENERIC_READ;
6482 DWORD dwShare, dwCreate;
6483 void *pConverted;
6484 HANDLE pHandle;
6485
6486 pConverted = jx9convertUtf8Filename(zPath);
6487 if( pConverted == 0 ){
6488 return -1;
6489 }
6490 /* Set the desired flags according to the open mode */
6491 if( iOpenMode & JX9_IO_OPEN_CREATE ){
6492 /* Open existing file, or create if it doesn't exist */
6493 dwCreate = OPEN_ALWAYS;
6494 if( iOpenMode & JX9_IO_OPEN_TRUNC ){
6495 /* If the specified file exists and is writable, the function overwrites the file */
6496 dwCreate = CREATE_ALWAYS;
6497 }
6498 }else if( iOpenMode & JX9_IO_OPEN_EXCL ){
6499 /* Creates a new file, only if it does not already exist.
6500 * If the file exists, it fails.
6501 */
6502 dwCreate = CREATE_NEW;
6503 }else if( iOpenMode & JX9_IO_OPEN_TRUNC ){
6504 /* Opens a file and truncates it so that its size is zero bytes
6505 * The file must exist.
6506 */
6507 dwCreate = TRUNCATE_EXISTING;
6508 }else{
6509 /* Opens a file, only if it exists. */
6510 dwCreate = OPEN_EXISTING;
6511 }
6512 if( iOpenMode & JX9_IO_OPEN_RDWR ){
6513 /* Read+Write access */
6514 dwAccess |= GENERIC_WRITE;
6515 }else if( iOpenMode & JX9_IO_OPEN_WRONLY ){
6516 /* Write only access */
6517 dwAccess = GENERIC_WRITE;
6518 }
6519 if( iOpenMode & JX9_IO_OPEN_APPEND ){
6520 /* Append mode */
6521 dwAccess = FILE_APPEND_DATA;
6522 }
6523 if( iOpenMode & JX9_IO_OPEN_TEMP ){
6524 /* File is temporary */
6525 dwType = FILE_ATTRIBUTE_TEMPORARY;
6526 }
6527 dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE;
6528 pHandle = CreateFileW((LPCWSTR)pConverted, dwAccess, dwShare, 0, dwCreate, dwType, 0);
6529 HeapFree(GetProcessHeap(), 0, pConverted);
6530 if( pHandle == INVALID_HANDLE_VALUE){
6531 SXUNUSED(pResource); /* MSVC warning */
6532 return -1;
6533 }
6534 /* Make the handle accessible to the upper layer */
6535 *ppHandle = (void *)pHandle;
6536 return JX9_OK;
6537}
6538/* An instance of the following structure is used to record state information
6539 * while iterating throw directory entries.
6540 */
6541typedef struct WinDir_Info WinDir_Info;
6542struct WinDir_Info
6543{
6544 HANDLE pDirHandle;
6545 void *pPath;
6546 WIN32_FIND_DATAW sInfo;
6547 int rc;
6548};
6549/* int (*xOpenDir)(const char *, jx9_value *, void **) */
6550static int WinDir_Open(const char *zPath, jx9_value *pResource, void **ppHandle)
6551{
6552 WinDir_Info *pDirInfo;
6553 void *pConverted;
6554 char *zPrep;
6555 sxu32 n;
6556 /* Prepare the path */
6557 n = SyStrlen(zPath);
6558 zPrep = (char *)HeapAlloc(GetProcessHeap(), 0, n+sizeof("\\*")+4);
6559 if( zPrep == 0 ){
6560 return -1;
6561 }
6562 SyMemcpy((const void *)zPath, zPrep, n);
6563 zPrep[n] = '\\';
6564 zPrep[n+1] = '*';
6565 zPrep[n+2] = 0;
6566 pConverted = jx9convertUtf8Filename(zPrep);
6567 HeapFree(GetProcessHeap(), 0, zPrep);
6568 if( pConverted == 0 ){
6569 return -1;
6570 }
6571 /* Allocate a new instance */
6572 pDirInfo = (WinDir_Info *)HeapAlloc(GetProcessHeap(), 0, sizeof(WinDir_Info));
6573 if( pDirInfo == 0 ){
6574 pResource = 0; /* Compiler warning */
6575 return -1;
6576 }
6577 pDirInfo->rc = SXRET_OK;
6578 pDirInfo->pDirHandle = FindFirstFileW((LPCWSTR)pConverted, &pDirInfo->sInfo);
6579 if( pDirInfo->pDirHandle == INVALID_HANDLE_VALUE ){
6580 /* Cannot open directory */
6581 HeapFree(GetProcessHeap(), 0, pConverted);
6582 HeapFree(GetProcessHeap(), 0, pDirInfo);
6583 return -1;
6584 }
6585 /* Save the path */
6586 pDirInfo->pPath = pConverted;
6587 /* Save our structure */
6588 *ppHandle = pDirInfo;
6589 return JX9_OK;
6590}
6591/* void (*xCloseDir)(void *) */
6592static void WinDir_Close(void *pUserData)
6593{
6594 WinDir_Info *pDirInfo = (WinDir_Info *)pUserData;
6595 if( pDirInfo->pDirHandle != INVALID_HANDLE_VALUE ){
6596 FindClose(pDirInfo->pDirHandle);
6597 }
6598 HeapFree(GetProcessHeap(), 0, pDirInfo->pPath);
6599 HeapFree(GetProcessHeap(), 0, pDirInfo);
6600}
6601/* void (*xClose)(void *); */
6602static void WinFile_Close(void *pUserData)
6603{
6604 HANDLE pHandle = (HANDLE)pUserData;
6605 CloseHandle(pHandle);
6606}
6607/* int (*xReadDir)(void *, jx9_context *) */
6608static int WinDir_Read(void *pUserData, jx9_context *pCtx)
6609{
6610 WinDir_Info *pDirInfo = (WinDir_Info *)pUserData;
6611 LPWIN32_FIND_DATAW pData;
6612 char *zName;
6613 BOOL rc;
6614 sxu32 n;
6615 if( pDirInfo->rc != SXRET_OK ){
6616 /* No more entry to process */
6617 return -1;
6618 }
6619 pData = &pDirInfo->sInfo;
6620 for(;;){
6621 zName = jx9unicodeToUtf8(pData->cFileName);
6622 if( zName == 0 ){
6623 /* Out of memory */
6624 return -1;
6625 }
6626 n = SyStrlen(zName);
6627 /* Ignore '.' && '..' */
6628 if( n > sizeof("..")-1 || zName[0] != '.' || ( n == sizeof("..")-1 && zName[1] != '.') ){
6629 break;
6630 }
6631 HeapFree(GetProcessHeap(), 0, zName);
6632 rc = FindNextFileW(pDirInfo->pDirHandle, &pDirInfo->sInfo);
6633 if( !rc ){
6634 return -1;
6635 }
6636 }
6637 /* Return the current file name */
6638 jx9_result_string(pCtx, zName, -1);
6639 HeapFree(GetProcessHeap(), 0, zName);
6640 /* Point to the next entry */
6641 rc = FindNextFileW(pDirInfo->pDirHandle, &pDirInfo->sInfo);
6642 if( !rc ){
6643 pDirInfo->rc = SXERR_EOF;
6644 }
6645 return JX9_OK;
6646}
6647/* void (*xRewindDir)(void *) */
6648static void WinDir_RewindDir(void *pUserData)
6649{
6650 WinDir_Info *pDirInfo = (WinDir_Info *)pUserData;
6651 FindClose(pDirInfo->pDirHandle);
6652 pDirInfo->pDirHandle = FindFirstFileW((LPCWSTR)pDirInfo->pPath, &pDirInfo->sInfo);
6653 if( pDirInfo->pDirHandle == INVALID_HANDLE_VALUE ){
6654 pDirInfo->rc = SXERR_EOF;
6655 }else{
6656 pDirInfo->rc = SXRET_OK;
6657 }
6658}
6659/* jx9_int64 (*xRead)(void *, void *, jx9_int64); */
6660static jx9_int64 WinFile_Read(void *pOS, void *pBuffer, jx9_int64 nDatatoRead)
6661{
6662 HANDLE pHandle = (HANDLE)pOS;
6663 DWORD nRd;
6664 BOOL rc;
6665 rc = ReadFile(pHandle, pBuffer, (DWORD)nDatatoRead, &nRd, 0);
6666 if( !rc ){
6667 /* EOF or IO error */
6668 return -1;
6669 }
6670 return (jx9_int64)nRd;
6671}
6672/* jx9_int64 (*xWrite)(void *, const void *, jx9_int64); */
6673static jx9_int64 WinFile_Write(void *pOS, const void *pBuffer, jx9_int64 nWrite)
6674{
6675 const char *zData = (const char *)pBuffer;
6676 HANDLE pHandle = (HANDLE)pOS;
6677 jx9_int64 nCount;
6678 DWORD nWr;
6679 BOOL rc;
6680 nWr = 0;
6681 nCount = 0;
6682 for(;;){
6683 if( nWrite < 1 ){
6684 break;
6685 }
6686 rc = WriteFile(pHandle, zData, (DWORD)nWrite, &nWr, 0);
6687 if( !rc ){
6688 /* IO error */
6689 break;
6690 }
6691 nWrite -= nWr;
6692 nCount += nWr;
6693 zData += nWr;
6694 }
6695 if( nWrite > 0 ){
6696 return -1;
6697 }
6698 return nCount;
6699}
6700/* int (*xSeek)(void *, jx9_int64, int) */
6701static int WinFile_Seek(void *pUserData, jx9_int64 iOfft, int whence)
6702{
6703 HANDLE pHandle = (HANDLE)pUserData;
6704 DWORD dwMove, dwNew;
6705 LONG nHighOfft;
6706 switch(whence){
6707 case 1:/*SEEK_CUR*/
6708 dwMove = FILE_CURRENT;
6709 break;
6710 case 2: /* SEEK_END */
6711 dwMove = FILE_END;
6712 break;
6713 case 0: /* SEEK_SET */
6714 default:
6715 dwMove = FILE_BEGIN;
6716 break;
6717 }
6718 nHighOfft = (LONG)(iOfft >> 32);
6719 dwNew = SetFilePointer(pHandle, (LONG)iOfft, &nHighOfft, dwMove);
6720 if( dwNew == INVALID_SET_FILE_POINTER ){
6721 return -1;
6722 }
6723 return JX9_OK;
6724}
6725/* int (*xLock)(void *, int) */
6726static int WinFile_Lock(void *pUserData, int lock_type)
6727{
6728 HANDLE pHandle = (HANDLE)pUserData;
6729 static DWORD dwLo = 0, dwHi = 0; /* xx: MT-SAFE */
6730 OVERLAPPED sDummy;
6731 BOOL rc;
6732 SyZero(&sDummy, sizeof(sDummy));
6733 /* Get the file size */
6734 if( lock_type < 1 ){
6735 /* Unlock the file */
6736 rc = UnlockFileEx(pHandle, 0, dwLo, dwHi, &sDummy);
6737 }else{
6738 DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY; /* Shared non-blocking lock by default*/
6739 /* Lock the file */
6740 if( lock_type == 1 /* LOCK_EXCL */ ){
6741 dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
6742 }
6743 dwLo = GetFileSize(pHandle, &dwHi);
6744 rc = LockFileEx(pHandle, dwFlags, 0, dwLo, dwHi, &sDummy);
6745 }
6746 return rc ? JX9_OK : -1 /* Lock error */;
6747}
6748/* jx9_int64 (*xTell)(void *) */
6749static jx9_int64 WinFile_Tell(void *pUserData)
6750{
6751 HANDLE pHandle = (HANDLE)pUserData;
6752 DWORD dwNew;
6753 dwNew = SetFilePointer(pHandle, 0, 0, FILE_CURRENT/* SEEK_CUR */);
6754 if( dwNew == INVALID_SET_FILE_POINTER ){
6755 return -1;
6756 }
6757 return (jx9_int64)dwNew;
6758}
6759/* int (*xTrunc)(void *, jx9_int64) */
6760static int WinFile_Trunc(void *pUserData, jx9_int64 nOfft)
6761{
6762 HANDLE pHandle = (HANDLE)pUserData;
6763 LONG HighOfft;
6764 DWORD dwNew;
6765 BOOL rc;
6766 HighOfft = (LONG)(nOfft >> 32);
6767 dwNew = SetFilePointer(pHandle, (LONG)nOfft, &HighOfft, FILE_BEGIN);
6768 if( dwNew == INVALID_SET_FILE_POINTER ){
6769 return -1;
6770 }
6771 rc = SetEndOfFile(pHandle);
6772 return rc ? JX9_OK : -1;
6773}
6774/* int (*xSync)(void *); */
6775static int WinFile_Sync(void *pUserData)
6776{
6777 HANDLE pHandle = (HANDLE)pUserData;
6778 BOOL rc;
6779 rc = FlushFileBuffers(pHandle);
6780 return rc ? JX9_OK : - 1;
6781}
6782/* int (*xStat)(void *, jx9_value *, jx9_value *) */
6783static int WinFile_Stat(void *pUserData, jx9_value *pArray, jx9_value *pWorker)
6784{
6785 BY_HANDLE_FILE_INFORMATION sInfo;
6786 HANDLE pHandle = (HANDLE)pUserData;
6787 BOOL rc;
6788 rc = GetFileInformationByHandle(pHandle, &sInfo);
6789 if( !rc ){
6790 return -1;
6791 }
6792 /* dev */
6793 jx9_value_int64(pWorker, (jx9_int64)sInfo.dwVolumeSerialNumber);
6794 jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
6795 /* ino */
6796 jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileIndexHigh << 32) | sInfo.nFileIndexLow));
6797 jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
6798 /* mode */
6799 jx9_value_int(pWorker, 0);
6800 jx9_array_add_strkey_elem(pArray, "mode", pWorker);
6801 /* nlink */
6802 jx9_value_int(pWorker, (int)sInfo.nNumberOfLinks);
6803 jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
6804 /* uid, gid, rdev */
6805 jx9_value_int(pWorker, 0);
6806 jx9_array_add_strkey_elem(pArray, "uid", pWorker);
6807 jx9_array_add_strkey_elem(pArray, "gid", pWorker);
6808 jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
6809 /* size */
6810 jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileSizeHigh << 32) | sInfo.nFileSizeLow));
6811 jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
6812 /* atime */
6813 jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime));
6814 jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
6815 /* mtime */
6816 jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime));
6817 jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
6818 /* ctime */
6819 jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftCreationTime));
6820 jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
6821 /* blksize, blocks */
6822 jx9_value_int(pWorker, 0);
6823 jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
6824 jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
6825 return JX9_OK;
6826}
6827/* Export the file:// stream */
6828static const jx9_io_stream sWinFileStream = {
6829 "file", /* Stream name */
6830 JX9_IO_STREAM_VERSION,
6831 WinFile_Open, /* xOpen */
6832 WinDir_Open, /* xOpenDir */
6833 WinFile_Close, /* xClose */
6834 WinDir_Close, /* xCloseDir */
6835 WinFile_Read, /* xRead */
6836 WinDir_Read, /* xReadDir */
6837 WinFile_Write, /* xWrite */
6838 WinFile_Seek, /* xSeek */
6839 WinFile_Lock, /* xLock */
6840 WinDir_RewindDir, /* xRewindDir */
6841 WinFile_Tell, /* xTell */
6842 WinFile_Trunc, /* xTrunc */
6843 WinFile_Sync, /* xSeek */
6844 WinFile_Stat /* xStat */
6845};
6846#elif defined(__UNIXES__)
6847/*
6848 * UNIX VFS implementation for the JX9 engine.
6849 * Authors:
6850 * Symisc Systems, devel@symisc.net.
6851 * Copyright (C) Symisc Systems, http://jx9.symisc.net
6852 * Status:
6853 * Stable.
6854 */
6855#include <sys/types.h>
6856#include <limits.h>
6857#include <fcntl.h>
6858#include <unistd.h>
6859#include <sys/uio.h>
6860#include <sys/stat.h>
6861#include <sys/mman.h>
6862#include <sys/file.h>
6863#include <pwd.h>
6864#include <grp.h>
6865#include <dirent.h>
6866#include <utime.h>
6867#include <stdio.h>
6868#include <stdlib.h>
6869/* int (*xchdir)(const char *) */
6870static int UnixVfs_chdir(const char *zPath)
6871{
6872 int rc;
6873 rc = chdir(zPath);
6874 return rc == 0 ? JX9_OK : -1;
6875}
6876/* int (*xGetcwd)(jx9_context *) */
6877static int UnixVfs_getcwd(jx9_context *pCtx)
6878{
6879 char zBuf[4096];
6880 char *zDir;
6881 /* Get the current directory */
6882 zDir = getcwd(zBuf, sizeof(zBuf));
6883 if( zDir == 0 ){
6884 return -1;
6885 }
6886 jx9_result_string(pCtx, zDir, -1/*Compute length automatically*/);
6887 return JX9_OK;
6888}
6889/* int (*xMkdir)(const char *, int, int) */
6890static int UnixVfs_mkdir(const char *zPath, int mode, int recursive)
6891{
6892 int rc;
6893 rc = mkdir(zPath, mode);
6894 recursive = 0; /* cc warning */
6895 return rc == 0 ? JX9_OK : -1;
6896}
6897/* int (*xRmdir)(const char *) */
6898static int UnixVfs_rmdir(const char *zPath)
6899{
6900 int rc;
6901 rc = rmdir(zPath);
6902 return rc == 0 ? JX9_OK : -1;
6903}
6904/* int (*xIsdir)(const char *) */
6905static int UnixVfs_isdir(const char *zPath)
6906{
6907 struct stat st;
6908 int rc;
6909 rc = stat(zPath, &st);
6910 if( rc != 0 ){
6911 return -1;
6912 }
6913 rc = S_ISDIR(st.st_mode);
6914 return rc ? JX9_OK : -1 ;
6915}
6916/* int (*xRename)(const char *, const char *) */
6917static int UnixVfs_Rename(const char *zOld, const char *zNew)
6918{
6919 int rc;
6920 rc = rename(zOld, zNew);
6921 return rc == 0 ? JX9_OK : -1;
6922}
6923/* int (*xRealpath)(const char *, jx9_context *) */
6924static int UnixVfs_Realpath(const char *zPath, jx9_context *pCtx)
6925{
6926#ifndef JX9_UNIX_OLD_LIBC
6927 char *zReal;
6928 zReal = realpath(zPath, 0);
6929 if( zReal == 0 ){
6930 return -1;
6931 }
6932 jx9_result_string(pCtx, zReal, -1/*Compute length automatically*/);
6933 /* Release the allocated buffer */
6934 free(zReal);
6935 return JX9_OK;
6936#else
6937 zPath = 0; /* cc warning */
6938 pCtx = 0;
6939 return -1;
6940#endif
6941}
6942/* int (*xSleep)(unsigned int) */
6943static int UnixVfs_Sleep(unsigned int uSec)
6944{
6945 usleep(uSec);
6946 return JX9_OK;
6947}
6948/* int (*xUnlink)(const char *) */
6949static int UnixVfs_unlink(const char *zPath)
6950{
6951 int rc;
6952 rc = unlink(zPath);
6953 return rc == 0 ? JX9_OK : -1 ;
6954}
6955/* int (*xFileExists)(const char *) */
6956static int UnixVfs_FileExists(const char *zPath)
6957{
6958 int rc;
6959 rc = access(zPath, F_OK);
6960 return rc == 0 ? JX9_OK : -1;
6961}
6962/* jx9_int64 (*xFileSize)(const char *) */
6963static jx9_int64 UnixVfs_FileSize(const char *zPath)
6964{
6965 struct stat st;
6966 int rc;
6967 rc = stat(zPath, &st);
6968 if( rc != 0 ){
6969 return -1;
6970 }
6971 return (jx9_int64)st.st_size;
6972}
6973/* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
6974static int UnixVfs_Touch(const char *zPath, jx9_int64 touch_time, jx9_int64 access_time)
6975{
6976 struct utimbuf ut;
6977 int rc;
6978 ut.actime = (time_t)access_time;
6979 ut.modtime = (time_t)touch_time;
6980 rc = utime(zPath, &ut);
6981 if( rc != 0 ){
6982 return -1;
6983 }
6984 return JX9_OK;
6985}
6986/* jx9_int64 (*xFileAtime)(const char *) */
6987static jx9_int64 UnixVfs_FileAtime(const char *zPath)
6988{
6989 struct stat st;
6990 int rc;
6991 rc = stat(zPath, &st);
6992 if( rc != 0 ){
6993 return -1;
6994 }
6995 return (jx9_int64)st.st_atime;
6996}
6997/* jx9_int64 (*xFileMtime)(const char *) */
6998static jx9_int64 UnixVfs_FileMtime(const char *zPath)
6999{
7000 struct stat st;
7001 int rc;
7002 rc = stat(zPath, &st);
7003 if( rc != 0 ){
7004 return -1;
7005 }
7006 return (jx9_int64)st.st_mtime;
7007}
7008/* jx9_int64 (*xFileCtime)(const char *) */
7009static jx9_int64 UnixVfs_FileCtime(const char *zPath)
7010{
7011 struct stat st;
7012 int rc;
7013 rc = stat(zPath, &st);
7014 if( rc != 0 ){
7015 return -1;
7016 }
7017 return (jx9_int64)st.st_ctime;
7018}
7019/* int (*xStat)(const char *, jx9_value *, jx9_value *) */
7020static int UnixVfs_Stat(const char *zPath, jx9_value *pArray, jx9_value *pWorker)
7021{
7022 struct stat st;
7023 int rc;
7024 rc = stat(zPath, &st);
7025 if( rc != 0 ){
7026 return -1;
7027 }
7028 /* dev */
7029 jx9_value_int64(pWorker, (jx9_int64)st.st_dev);
7030 jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
7031 /* ino */
7032 jx9_value_int64(pWorker, (jx9_int64)st.st_ino);
7033 jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
7034 /* mode */
7035 jx9_value_int(pWorker, (int)st.st_mode);
7036 jx9_array_add_strkey_elem(pArray, "mode", pWorker);
7037 /* nlink */
7038 jx9_value_int(pWorker, (int)st.st_nlink);
7039 jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
7040 /* uid, gid, rdev */
7041 jx9_value_int(pWorker, (int)st.st_uid);
7042 jx9_array_add_strkey_elem(pArray, "uid", pWorker);
7043 jx9_value_int(pWorker, (int)st.st_gid);
7044 jx9_array_add_strkey_elem(pArray, "gid", pWorker);
7045 jx9_value_int(pWorker, (int)st.st_rdev);
7046 jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
7047 /* size */
7048 jx9_value_int64(pWorker, (jx9_int64)st.st_size);
7049 jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
7050 /* atime */
7051 jx9_value_int64(pWorker, (jx9_int64)st.st_atime);
7052 jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
7053 /* mtime */
7054 jx9_value_int64(pWorker, (jx9_int64)st.st_mtime);
7055 jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
7056 /* ctime */
7057 jx9_value_int64(pWorker, (jx9_int64)st.st_ctime);
7058 jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
7059 /* blksize, blocks */
7060 jx9_value_int(pWorker, (int)st.st_blksize);
7061 jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
7062 jx9_value_int(pWorker, (int)st.st_blocks);
7063 jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
7064 return JX9_OK;
7065}
7066/* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
7067static int UnixVfs_lStat(const char *zPath, jx9_value *pArray, jx9_value *pWorker)
7068{
7069 struct stat st;
7070 int rc;
7071 rc = lstat(zPath, &st);
7072 if( rc != 0 ){
7073 return -1;
7074 }
7075 /* dev */
7076 jx9_value_int64(pWorker, (jx9_int64)st.st_dev);
7077 jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
7078 /* ino */
7079 jx9_value_int64(pWorker, (jx9_int64)st.st_ino);
7080 jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
7081 /* mode */
7082 jx9_value_int(pWorker, (int)st.st_mode);
7083 jx9_array_add_strkey_elem(pArray, "mode", pWorker);
7084 /* nlink */
7085 jx9_value_int(pWorker, (int)st.st_nlink);
7086 jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
7087 /* uid, gid, rdev */
7088 jx9_value_int(pWorker, (int)st.st_uid);
7089 jx9_array_add_strkey_elem(pArray, "uid", pWorker);
7090 jx9_value_int(pWorker, (int)st.st_gid);
7091 jx9_array_add_strkey_elem(pArray, "gid", pWorker);
7092 jx9_value_int(pWorker, (int)st.st_rdev);
7093 jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
7094 /* size */
7095 jx9_value_int64(pWorker, (jx9_int64)st.st_size);
7096 jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
7097 /* atime */
7098 jx9_value_int64(pWorker, (jx9_int64)st.st_atime);
7099 jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
7100 /* mtime */
7101 jx9_value_int64(pWorker, (jx9_int64)st.st_mtime);
7102 jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
7103 /* ctime */
7104 jx9_value_int64(pWorker, (jx9_int64)st.st_ctime);
7105 jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
7106 /* blksize, blocks */
7107 jx9_value_int(pWorker, (int)st.st_blksize);
7108 jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
7109 jx9_value_int(pWorker, (int)st.st_blocks);
7110 jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
7111 return JX9_OK;
7112}
7113/* int (*xChmod)(const char *, int) */
7114static int UnixVfs_Chmod(const char *zPath, int mode)
7115{
7116 int rc;
7117 rc = chmod(zPath, (mode_t)mode);
7118 return rc == 0 ? JX9_OK : - 1;
7119}
7120/* int (*xChown)(const char *, const char *) */
7121static int UnixVfs_Chown(const char *zPath, const char *zUser)
7122{
7123#ifndef JX9_UNIX_STATIC_BUILD
7124 struct passwd *pwd;
7125 uid_t uid;
7126 int rc;
7127 pwd = getpwnam(zUser); /* Try getting UID for username */
7128 if (pwd == 0) {
7129 return -1;
7130 }
7131 uid = pwd->pw_uid;
7132 rc = chown(zPath, uid, -1);
7133 return rc == 0 ? JX9_OK : -1;
7134#else
7135 SXUNUSED(zPath);
7136 SXUNUSED(zUser);
7137 return -1;
7138#endif /* JX9_UNIX_STATIC_BUILD */
7139}
7140/* int (*xChgrp)(const char *, const char *) */
7141static int UnixVfs_Chgrp(const char *zPath, const char *zGroup)
7142{
7143#ifndef JX9_UNIX_STATIC_BUILD
7144 struct group *group;
7145 gid_t gid;
7146 int rc;
7147 group = getgrnam(zGroup);
7148 if (group == 0) {
7149 return -1;
7150 }
7151 gid = group->gr_gid;
7152 rc = chown(zPath, -1, gid);
7153 return rc == 0 ? JX9_OK : -1;
7154#else
7155 SXUNUSED(zPath);
7156 SXUNUSED(zGroup);
7157 return -1;
7158#endif /* JX9_UNIX_STATIC_BUILD */
7159}
7160/* int (*xIsfile)(const char *) */
7161static int UnixVfs_isfile(const char *zPath)
7162{
7163 struct stat st;
7164 int rc;
7165 rc = stat(zPath, &st);
7166 if( rc != 0 ){
7167 return -1;
7168 }
7169 rc = S_ISREG(st.st_mode);
7170 return rc ? JX9_OK : -1 ;
7171}
7172/* int (*xIslink)(const char *) */
7173static int UnixVfs_islink(const char *zPath)
7174{
7175 struct stat st;
7176 int rc;
7177 rc = stat(zPath, &st);
7178 if( rc != 0 ){
7179 return -1;
7180 }
7181 rc = S_ISLNK(st.st_mode);
7182 return rc ? JX9_OK : -1 ;
7183}
7184/* int (*xReadable)(const char *) */
7185static int UnixVfs_isreadable(const char *zPath)
7186{
7187 int rc;
7188 rc = access(zPath, R_OK);
7189 return rc == 0 ? JX9_OK : -1;
7190}
7191/* int (*xWritable)(const char *) */
7192static int UnixVfs_iswritable(const char *zPath)
7193{
7194 int rc;
7195 rc = access(zPath, W_OK);
7196 return rc == 0 ? JX9_OK : -1;
7197}
7198/* int (*xExecutable)(const char *) */
7199static int UnixVfs_isexecutable(const char *zPath)
7200{
7201 int rc;
7202 rc = access(zPath, X_OK);
7203 return rc == 0 ? JX9_OK : -1;
7204}
7205/* int (*xFiletype)(const char *, jx9_context *) */
7206static int UnixVfs_Filetype(const char *zPath, jx9_context *pCtx)
7207{
7208 struct stat st;
7209 int rc;
7210 rc = stat(zPath, &st);
7211 if( rc != 0 ){
7212 /* Expand 'unknown' */
7213 jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
7214 return -1;
7215 }
7216 if(S_ISREG(st.st_mode) ){
7217 jx9_result_string(pCtx, "file", sizeof("file")-1);
7218 }else if(S_ISDIR(st.st_mode)){
7219 jx9_result_string(pCtx, "dir", sizeof("dir")-1);
7220 }else if(S_ISLNK(st.st_mode)){
7221 jx9_result_string(pCtx, "link", sizeof("link")-1);
7222 }else if(S_ISBLK(st.st_mode)){
7223 jx9_result_string(pCtx, "block", sizeof("block")-1);
7224 }else if(S_ISSOCK(st.st_mode)){
7225 jx9_result_string(pCtx, "socket", sizeof("socket")-1);
7226 }else if(S_ISFIFO(st.st_mode)){
7227 jx9_result_string(pCtx, "fifo", sizeof("fifo")-1);
7228 }else{
7229 jx9_result_string(pCtx, "unknown", sizeof("unknown")-1);
7230 }
7231 return JX9_OK;
7232}
7233/* int (*xGetenv)(const char *, jx9_context *) */
7234static int UnixVfs_Getenv(const char *zVar, jx9_context *pCtx)
7235{
7236 char *zEnv;
7237 zEnv = getenv(zVar);
7238 if( zEnv == 0 ){
7239 return -1;
7240 }
7241 jx9_result_string(pCtx, zEnv, -1/*Compute length automatically*/);
7242 return JX9_OK;
7243}
7244/* int (*xSetenv)(const char *, const char *) */
7245static int UnixVfs_Setenv(const char *zName, const char *zValue)
7246{
7247 int rc;
7248 rc = setenv(zName, zValue, 1);
7249 return rc == 0 ? JX9_OK : -1;
7250}
7251/* int (*xMmap)(const char *, void **, jx9_int64 *) */
7252static int UnixVfs_Mmap(const char *zPath, void **ppMap, jx9_int64 *pSize)
7253{
7254 struct stat st;
7255 void *pMap;
7256 int fd;
7257 int rc;
7258 /* Open the file in a read-only mode */
7259 fd = open(zPath, O_RDONLY);
7260 if( fd < 0 ){
7261 return -1;
7262 }
7263 /* stat the handle */
7264 fstat(fd, &st);
7265 /* Obtain a memory view of the whole file */
7266 pMap = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE|MAP_FILE, fd, 0);
7267 rc = JX9_OK;
7268 if( pMap == MAP_FAILED ){
7269 rc = -1;
7270 }else{
7271 /* Point to the memory view */
7272 *ppMap = pMap;
7273 *pSize = (jx9_int64)st.st_size;
7274 }
7275 close(fd);
7276 return rc;
7277}
7278/* void (*xUnmap)(void *, jx9_int64) */
7279static void UnixVfs_Unmap(void *pView, jx9_int64 nSize)
7280{
7281 munmap(pView, (size_t)nSize);
7282}
7283/* void (*xTempDir)(jx9_context *) */
7284static void UnixVfs_TempDir(jx9_context *pCtx)
7285{
7286 static const char *azDirs[] = {
7287 "/var/tmp",
7288 "/usr/tmp",
7289 "/usr/local/tmp"
7290 };
7291 unsigned int i;
7292 struct stat buf;
7293 const char *zDir;
7294 zDir = getenv("TMPDIR");
7295 if( zDir && zDir[0] != 0 && !access(zDir, 07) ){
7296 jx9_result_string(pCtx, zDir, -1);
7297 return;
7298 }
7299 for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){
7300 zDir=azDirs[i];
7301 if( zDir==0 ) continue;
7302 if( stat(zDir, &buf) ) continue;
7303 if( !S_ISDIR(buf.st_mode) ) continue;
7304 if( access(zDir, 07) ) continue;
7305 /* Got one */
7306 jx9_result_string(pCtx, zDir, -1);
7307 return;
7308 }
7309 /* Default temp dir */
7310 jx9_result_string(pCtx, "/tmp", (int)sizeof("/tmp")-1);
7311}
7312/* unsigned int (*xProcessId)(void) */
7313static unsigned int UnixVfs_ProcessId(void)
7314{
7315 return (unsigned int)getpid();
7316}
7317/* int (*xUid)(void) */
7318static int UnixVfs_uid(void)
7319{
7320 return (int)getuid();
7321}
7322/* int (*xGid)(void) */
7323static int UnixVfs_gid(void)
7324{
7325 return (int)getgid();
7326}
7327/* int (*xUmask)(int) */
7328static int UnixVfs_Umask(int new_mask)
7329{
7330 int old_mask;
7331 old_mask = umask(new_mask);
7332 return old_mask;
7333}
7334/* void (*xUsername)(jx9_context *) */
7335static void UnixVfs_Username(jx9_context *pCtx)
7336{
7337#ifndef JX9_UNIX_STATIC_BUILD
7338 struct passwd *pwd;
7339 uid_t uid;
7340 uid = getuid();
7341 pwd = getpwuid(uid); /* Try getting UID for username */
7342 if (pwd == 0) {
7343 return;
7344 }
7345 /* Return the username */
7346 jx9_result_string(pCtx, pwd->pw_name, -1);
7347#else
7348 jx9_result_string(pCtx, "Unknown", -1);
7349#endif /* JX9_UNIX_STATIC_BUILD */
7350 return;
7351}
7352/* int (*xLink)(const char *, const char *, int) */
7353static int UnixVfs_link(const char *zSrc, const char *zTarget, int is_sym)
7354{
7355 int rc;
7356 if( is_sym ){
7357 /* Symbolic link */
7358 rc = symlink(zSrc, zTarget);
7359 }else{
7360 /* Hard link */
7361 rc = link(zSrc, zTarget);
7362 }
7363 return rc == 0 ? JX9_OK : -1;
7364}
7365/* int (*xChroot)(const char *) */
7366static int UnixVfs_chroot(const char *zRootDir)
7367{
7368 int rc;
7369 rc = chroot(zRootDir);
7370 return rc == 0 ? JX9_OK : -1;
7371}
7372/* Export the UNIX vfs */
7373static const jx9_vfs sUnixVfs = {
7374 "Unix_vfs",
7375 JX9_VFS_VERSION,
7376 UnixVfs_chdir, /* int (*xChdir)(const char *) */
7377 UnixVfs_chroot, /* int (*xChroot)(const char *); */
7378 UnixVfs_getcwd, /* int (*xGetcwd)(jx9_context *) */
7379 UnixVfs_mkdir, /* int (*xMkdir)(const char *, int, int) */
7380 UnixVfs_rmdir, /* int (*xRmdir)(const char *) */
7381 UnixVfs_isdir, /* int (*xIsdir)(const char *) */
7382 UnixVfs_Rename, /* int (*xRename)(const char *, const char *) */
7383 UnixVfs_Realpath, /*int (*xRealpath)(const char *, jx9_context *)*/
7384 UnixVfs_Sleep, /* int (*xSleep)(unsigned int) */
7385 UnixVfs_unlink, /* int (*xUnlink)(const char *) */
7386 UnixVfs_FileExists, /* int (*xFileExists)(const char *) */
7387 UnixVfs_Chmod, /*int (*xChmod)(const char *, int)*/
7388 UnixVfs_Chown, /*int (*xChown)(const char *, const char *)*/
7389 UnixVfs_Chgrp, /*int (*xChgrp)(const char *, const char *)*/
7390 0, /* jx9_int64 (*xFreeSpace)(const char *) */
7391 0, /* jx9_int64 (*xTotalSpace)(const char *) */
7392 UnixVfs_FileSize, /* jx9_int64 (*xFileSize)(const char *) */
7393 UnixVfs_FileAtime, /* jx9_int64 (*xFileAtime)(const char *) */
7394 UnixVfs_FileMtime, /* jx9_int64 (*xFileMtime)(const char *) */
7395 UnixVfs_FileCtime, /* jx9_int64 (*xFileCtime)(const char *) */
7396 UnixVfs_Stat, /* int (*xStat)(const char *, jx9_value *, jx9_value *) */
7397 UnixVfs_lStat, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */
7398 UnixVfs_isfile, /* int (*xIsfile)(const char *) */
7399 UnixVfs_islink, /* int (*xIslink)(const char *) */
7400 UnixVfs_isreadable, /* int (*xReadable)(const char *) */
7401 UnixVfs_iswritable, /* int (*xWritable)(const char *) */
7402 UnixVfs_isexecutable, /* int (*xExecutable)(const char *) */
7403 UnixVfs_Filetype, /* int (*xFiletype)(const char *, jx9_context *) */
7404 UnixVfs_Getenv, /* int (*xGetenv)(const char *, jx9_context *) */
7405 UnixVfs_Setenv, /* int (*xSetenv)(const char *, const char *) */
7406 UnixVfs_Touch, /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */
7407 UnixVfs_Mmap, /* int (*xMmap)(const char *, void **, jx9_int64 *) */
7408 UnixVfs_Unmap, /* void (*xUnmap)(void *, jx9_int64); */
7409 UnixVfs_link, /* int (*xLink)(const char *, const char *, int) */
7410 UnixVfs_Umask, /* int (*xUmask)(int) */
7411 UnixVfs_TempDir, /* void (*xTempDir)(jx9_context *) */
7412 UnixVfs_ProcessId, /* unsigned int (*xProcessId)(void) */
7413 UnixVfs_uid, /* int (*xUid)(void) */
7414 UnixVfs_gid, /* int (*xGid)(void) */
7415 UnixVfs_Username, /* void (*xUsername)(jx9_context *) */
7416 0 /* int (*xExec)(const char *, jx9_context *) */
7417};
7418/* UNIX File IO */
7419#define JX9_UNIX_OPEN_MODE 0640 /* Default open mode */
7420/* int (*xOpen)(const char *, int, jx9_value *, void **) */
7421static int UnixFile_Open(const char *zPath, int iOpenMode, jx9_value *pResource, void **ppHandle)
7422{
7423 int iOpen = O_RDONLY;
7424 int fd;
7425 /* Set the desired flags according to the open mode */
7426 if( iOpenMode & JX9_IO_OPEN_CREATE ){
7427 /* Open existing file, or create if it doesn't exist */
7428 iOpen = O_CREAT;
7429 if( iOpenMode & JX9_IO_OPEN_TRUNC ){
7430 /* If the specified file exists and is writable, the function overwrites the file */
7431 iOpen |= O_TRUNC;
7432 SXUNUSED(pResource); /* cc warning */
7433 }
7434 }else if( iOpenMode & JX9_IO_OPEN_EXCL ){
7435 /* Creates a new file, only if it does not already exist.
7436 * If the file exists, it fails.
7437 */
7438 iOpen = O_CREAT|O_EXCL;
7439 }else if( iOpenMode & JX9_IO_OPEN_TRUNC ){
7440 /* Opens a file and truncates it so that its size is zero bytes
7441 * The file must exist.
7442 */
7443 iOpen = O_RDWR|O_TRUNC;
7444 }
7445 if( iOpenMode & JX9_IO_OPEN_RDWR ){
7446 /* Read+Write access */
7447 iOpen &= ~O_RDONLY;
7448 iOpen |= O_RDWR;
7449 }else if( iOpenMode & JX9_IO_OPEN_WRONLY ){
7450 /* Write only access */
7451 iOpen &= ~O_RDONLY;
7452 iOpen |= O_WRONLY;
7453 }
7454 if( iOpenMode & JX9_IO_OPEN_APPEND ){
7455 /* Append mode */
7456 iOpen |= O_APPEND;
7457 }
7458#ifdef O_TEMP
7459 if( iOpenMode & JX9_IO_OPEN_TEMP ){
7460 /* File is temporary */
7461 iOpen |= O_TEMP;
7462 }
7463#endif
7464 /* Open the file now */
7465 fd = open(zPath, iOpen, JX9_UNIX_OPEN_MODE);
7466 if( fd < 0 ){
7467 /* IO error */
7468 return -1;
7469 }
7470 /* Save the handle */
7471 *ppHandle = SX_INT_TO_PTR(fd);
7472 return JX9_OK;
7473}
7474/* int (*xOpenDir)(const char *, jx9_value *, void **) */
7475static int UnixDir_Open(const char *zPath, jx9_value *pResource, void **ppHandle)
7476{
7477 DIR *pDir;
7478 /* Open the target directory */
7479 pDir = opendir(zPath);
7480 if( pDir == 0 ){
7481 pResource = 0; /* Compiler warning */
7482 return -1;
7483 }
7484 /* Save our structure */
7485 *ppHandle = pDir;
7486 return JX9_OK;
7487}
7488/* void (*xCloseDir)(void *) */
7489static void UnixDir_Close(void *pUserData)
7490{
7491 closedir((DIR *)pUserData);
7492}
7493/* void (*xClose)(void *); */
7494static void UnixFile_Close(void *pUserData)
7495{
7496 close(SX_PTR_TO_INT(pUserData));
7497}
7498/* int (*xReadDir)(void *, jx9_context *) */
7499static int UnixDir_Read(void *pUserData, jx9_context *pCtx)
7500{
7501 DIR *pDir = (DIR *)pUserData;
7502 struct dirent *pEntry;
7503 char *zName = 0; /* cc warning */
7504 sxu32 n = 0;
7505 for(;;){
7506 pEntry = readdir(pDir);
7507 if( pEntry == 0 ){
7508 /* No more entries to process */
7509 return -1;
7510 }
7511 zName = pEntry->d_name;
7512 n = SyStrlen(zName);
7513 /* Ignore '.' && '..' */
7514 if( n > sizeof("..")-1 || zName[0] != '.' || ( n == sizeof("..")-1 && zName[1] != '.') ){
7515 break;
7516 }
7517 /* Next entry */
7518 }
7519 /* Return the current file name */
7520 jx9_result_string(pCtx, zName, (int)n);
7521 return JX9_OK;
7522}
7523/* void (*xRewindDir)(void *) */
7524static void UnixDir_Rewind(void *pUserData)
7525{
7526 rewinddir((DIR *)pUserData);
7527}
7528/* jx9_int64 (*xRead)(void *, void *, jx9_int64); */
7529static jx9_int64 UnixFile_Read(void *pUserData, void *pBuffer, jx9_int64 nDatatoRead)
7530{
7531 ssize_t nRd;
7532 nRd = read(SX_PTR_TO_INT(pUserData), pBuffer, (size_t)nDatatoRead);
7533 if( nRd < 1 ){
7534 /* EOF or IO error */
7535 return -1;
7536 }
7537 return (jx9_int64)nRd;
7538}
7539/* jx9_int64 (*xWrite)(void *, const void *, jx9_int64); */
7540static jx9_int64 UnixFile_Write(void *pUserData, const void *pBuffer, jx9_int64 nWrite)
7541{
7542 const char *zData = (const char *)pBuffer;
7543 int fd = SX_PTR_TO_INT(pUserData);
7544 jx9_int64 nCount;
7545 ssize_t nWr;
7546 nCount = 0;
7547 for(;;){
7548 if( nWrite < 1 ){
7549 break;
7550 }
7551 nWr = write(fd, zData, (size_t)nWrite);
7552 if( nWr < 1 ){
7553 /* IO error */
7554 break;
7555 }
7556 nWrite -= nWr;
7557 nCount += nWr;
7558 zData += nWr;
7559 }
7560 if( nWrite > 0 ){
7561 return -1;
7562 }
7563 return nCount;
7564}
7565/* int (*xSeek)(void *, jx9_int64, int) */
7566static int UnixFile_Seek(void *pUserData, jx9_int64 iOfft, int whence)
7567{
7568 off_t iNew;
7569 switch(whence){
7570 case 1:/*SEEK_CUR*/
7571 whence = SEEK_CUR;
7572 break;
7573 case 2: /* SEEK_END */
7574 whence = SEEK_END;
7575 break;
7576 case 0: /* SEEK_SET */
7577 default:
7578 whence = SEEK_SET;
7579 break;
7580 }
7581 iNew = lseek(SX_PTR_TO_INT(pUserData), (off_t)iOfft, whence);
7582 if( iNew < 0 ){
7583 return -1;
7584 }
7585 return JX9_OK;
7586}
7587/* int (*xLock)(void *, int) */
7588static int UnixFile_Lock(void *pUserData, int lock_type)
7589{
7590 int fd = SX_PTR_TO_INT(pUserData);
7591 int rc = JX9_OK; /* cc warning */
7592 if( lock_type < 0 ){
7593 /* Unlock the file */
7594 rc = flock(fd, LOCK_UN);
7595 }else{
7596 if( lock_type == 1 ){
7597 /* Exculsive lock */
7598 rc = flock(fd, LOCK_EX);
7599 }else{
7600 /* Shared lock */
7601 rc = flock(fd, LOCK_SH);
7602 }
7603 }
7604 return !rc ? JX9_OK : -1;
7605}
7606/* jx9_int64 (*xTell)(void *) */
7607static jx9_int64 UnixFile_Tell(void *pUserData)
7608{
7609 off_t iNew;
7610 iNew = lseek(SX_PTR_TO_INT(pUserData), 0, SEEK_CUR);
7611 return (jx9_int64)iNew;
7612}
7613/* int (*xTrunc)(void *, jx9_int64) */
7614static int UnixFile_Trunc(void *pUserData, jx9_int64 nOfft)
7615{
7616 int rc;
7617 rc = ftruncate(SX_PTR_TO_INT(pUserData), (off_t)nOfft);
7618 if( rc != 0 ){
7619 return -1;
7620 }
7621 return JX9_OK;
7622}
7623/* int (*xSync)(void *); */
7624static int UnixFile_Sync(void *pUserData)
7625{
7626 int rc;
7627 rc = fsync(SX_PTR_TO_INT(pUserData));
7628 return rc == 0 ? JX9_OK : - 1;
7629}
7630/* int (*xStat)(void *, jx9_value *, jx9_value *) */
7631static int UnixFile_Stat(void *pUserData, jx9_value *pArray, jx9_value *pWorker)
7632{
7633 struct stat st;
7634 int rc;
7635 rc = fstat(SX_PTR_TO_INT(pUserData), &st);
7636 if( rc != 0 ){
7637 return -1;
7638 }
7639 /* dev */
7640 jx9_value_int64(pWorker, (jx9_int64)st.st_dev);
7641 jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */
7642 /* ino */
7643 jx9_value_int64(pWorker, (jx9_int64)st.st_ino);
7644 jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */
7645 /* mode */
7646 jx9_value_int(pWorker, (int)st.st_mode);
7647 jx9_array_add_strkey_elem(pArray, "mode", pWorker);
7648 /* nlink */
7649 jx9_value_int(pWorker, (int)st.st_nlink);
7650 jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */
7651 /* uid, gid, rdev */
7652 jx9_value_int(pWorker, (int)st.st_uid);
7653 jx9_array_add_strkey_elem(pArray, "uid", pWorker);
7654 jx9_value_int(pWorker, (int)st.st_gid);
7655 jx9_array_add_strkey_elem(pArray, "gid", pWorker);
7656 jx9_value_int(pWorker, (int)st.st_rdev);
7657 jx9_array_add_strkey_elem(pArray, "rdev", pWorker);
7658 /* size */
7659 jx9_value_int64(pWorker, (jx9_int64)st.st_size);
7660 jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */
7661 /* atime */
7662 jx9_value_int64(pWorker, (jx9_int64)st.st_atime);
7663 jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */
7664 /* mtime */
7665 jx9_value_int64(pWorker, (jx9_int64)st.st_mtime);
7666 jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */
7667 /* ctime */
7668 jx9_value_int64(pWorker, (jx9_int64)st.st_ctime);
7669 jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */
7670 /* blksize, blocks */
7671 jx9_value_int(pWorker, (int)st.st_blksize);
7672 jx9_array_add_strkey_elem(pArray, "blksize", pWorker);
7673 jx9_value_int(pWorker, (int)st.st_blocks);
7674 jx9_array_add_strkey_elem(pArray, "blocks", pWorker);
7675 return JX9_OK;
7676}
7677/* Export the file:// stream */
7678static const jx9_io_stream sUnixFileStream = {
7679 "file", /* Stream name */
7680 JX9_IO_STREAM_VERSION,
7681 UnixFile_Open, /* xOpen */
7682 UnixDir_Open, /* xOpenDir */
7683 UnixFile_Close, /* xClose */
7684 UnixDir_Close, /* xCloseDir */
7685 UnixFile_Read, /* xRead */
7686 UnixDir_Read, /* xReadDir */
7687 UnixFile_Write, /* xWrite */
7688 UnixFile_Seek, /* xSeek */
7689 UnixFile_Lock, /* xLock */
7690 UnixDir_Rewind, /* xRewindDir */
7691 UnixFile_Tell, /* xTell */
7692 UnixFile_Trunc, /* xTrunc */
7693 UnixFile_Sync, /* xSeek */
7694 UnixFile_Stat /* xStat */
7695};
7696#endif /* __WINNT__/__UNIXES__ */
7697#endif /* JX9_DISABLE_DISK_IO */
7698#endif /* JX9_DISABLE_BUILTIN_FUNC */
7699/*
7700 * Export the builtin vfs.
7701 * Return a pointer to the builtin vfs if available.
7702 * Otherwise return the null_vfs [i.e: a no-op vfs] instead.
7703 * Note:
7704 * The built-in vfs is always available for Windows/UNIX systems.
7705 * Note:
7706 * If the engine is compiled with the JX9_DISABLE_DISK_IO/JX9_DISABLE_BUILTIN_FUNC
7707 * directives defined then this function return the null_vfs instead.
7708 */
7709JX9_PRIVATE const jx9_vfs * jx9ExportBuiltinVfs(void)
7710{
7711#ifndef JX9_DISABLE_BUILTIN_FUNC
7712#ifdef JX9_DISABLE_DISK_IO
7713 return &null_vfs;
7714#else
7715#ifdef __WINNT__
7716 return &sWinVfs;
7717#elif defined(__UNIXES__)
7718 return &sUnixVfs;
7719#else
7720 return &null_vfs;
7721#endif /* __WINNT__/__UNIXES__ */
7722#endif /*JX9_DISABLE_DISK_IO*/
7723#else
7724 return &null_vfs;
7725#endif /* JX9_DISABLE_BUILTIN_FUNC */
7726}
7727#ifndef JX9_DISABLE_BUILTIN_FUNC
7728#ifndef JX9_DISABLE_DISK_IO
7729/*
7730 * The following defines are mostly used by the UNIX built and have
7731 * no particular meaning on windows.
7732 */
7733#ifndef STDIN_FILENO
7734#define STDIN_FILENO 0
7735#endif
7736#ifndef STDOUT_FILENO
7737#define STDOUT_FILENO 1
7738#endif
7739#ifndef STDERR_FILENO
7740#define STDERR_FILENO 2
7741#endif
7742/*
7743 * jx9:// Accessing various I/O streams
7744 * According to the JX9 langage reference manual
7745 * JX9 provides a number of miscellaneous I/O streams that allow access to JX9's own input
7746 * and output streams, the standard input, output and error file descriptors.
7747 * jx9://stdin, jx9://stdout and jx9://stderr:
7748 * Allow direct access to the corresponding input or output stream of the JX9 process.
7749 * The stream references a duplicate file descriptor, so if you open jx9://stdin and later
7750 * close it, you close only your copy of the descriptor-the actual stream referenced by STDIN is unaffected.
7751 * jx9://stdin is read-only, whereas jx9://stdout and jx9://stderr are write-only.
7752 * jx9://output
7753 * jx9://output is a write-only stream that allows you to write to the output buffer
7754 * mechanism in the same way as print and print.
7755 */
7756typedef struct jx9_stream_data jx9_stream_data;
7757/* Supported IO streams */
7758#define JX9_IO_STREAM_STDIN 1 /* jx9://stdin */
7759#define JX9_IO_STREAM_STDOUT 2 /* jx9://stdout */
7760#define JX9_IO_STREAM_STDERR 3 /* jx9://stderr */
7761#define JX9_IO_STREAM_OUTPUT 4 /* jx9://output */
7762 /* The following structure is the private data associated with the jx9:// stream */
7763struct jx9_stream_data
7764{
7765 jx9_vm *pVm; /* VM that own this instance */
7766 int iType; /* Stream type */
7767 union{
7768 void *pHandle; /* Stream handle */
7769 jx9_output_consumer sConsumer; /* VM output consumer */
7770 }x;
7771};
7772/*
7773 * Allocate a new instance of the jx9_stream_data structure.
7774 */
7775static jx9_stream_data * JX9StreamDataInit(jx9_vm *pVm, int iType)
7776{
7777 jx9_stream_data *pData;
7778 if( pVm == 0 ){
7779 return 0;
7780 }
7781 /* Allocate a new instance */
7782 pData = (jx9_stream_data *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(jx9_stream_data));
7783 if( pData == 0 ){
7784 return 0;
7785 }
7786 /* Zero the structure */
7787 SyZero(pData, sizeof(jx9_stream_data));
7788 /* Initialize fields */
7789 pData->iType = iType;
7790 if( iType == JX9_IO_STREAM_OUTPUT ){
7791 /* Point to the default VM consumer routine. */
7792 pData->x.sConsumer = pVm->sVmConsumer;
7793 }else{
7794#ifdef __WINNT__
7795 DWORD nChannel;
7796 switch(iType){
7797 case JX9_IO_STREAM_STDOUT: nChannel = STD_OUTPUT_HANDLE; break;
7798 case JX9_IO_STREAM_STDERR: nChannel = STD_ERROR_HANDLE; break;
7799 default:
7800 nChannel = STD_INPUT_HANDLE;
7801 break;
7802 }
7803 pData->x.pHandle = GetStdHandle(nChannel);
7804#else
7805 /* Assume an UNIX system */
7806 int ifd = STDIN_FILENO;
7807 switch(iType){
7808 case JX9_IO_STREAM_STDOUT: ifd = STDOUT_FILENO; break;
7809 case JX9_IO_STREAM_STDERR: ifd = STDERR_FILENO; break;
7810 default:
7811 break;
7812 }
7813 pData->x.pHandle = SX_INT_TO_PTR(ifd);
7814#endif
7815 }
7816 pData->pVm = pVm;
7817 return pData;
7818}
7819/*
7820 * Implementation of the jx9:// IO streams routines
7821 * Authors:
7822 * Symisc Systems, devel@symisc.net.
7823 * Copyright (C) Symisc Systems, http://jx9.symisc.net
7824 * Status:
7825 * Stable.
7826 */
7827/* int (*xOpen)(const char *, int, jx9_value *, void **) */
7828static int JX9StreamData_Open(const char *zName, int iMode, jx9_value *pResource, void ** ppHandle)
7829{
7830 jx9_stream_data *pData;
7831 SyString sStream;
7832 SyStringInitFromBuf(&sStream, zName, SyStrlen(zName));
7833 /* Trim leading and trailing white spaces */
7834 SyStringFullTrim(&sStream);
7835 /* Stream to open */
7836 if( SyStrnicmp(sStream.zString, "stdin", sizeof("stdin")-1) == 0 ){
7837 iMode = JX9_IO_STREAM_STDIN;
7838 }else if( SyStrnicmp(sStream.zString, "output", sizeof("output")-1) == 0 ){
7839 iMode = JX9_IO_STREAM_OUTPUT;
7840 }else if( SyStrnicmp(sStream.zString, "stdout", sizeof("stdout")-1) == 0 ){
7841 iMode = JX9_IO_STREAM_STDOUT;
7842 }else if( SyStrnicmp(sStream.zString, "stderr", sizeof("stderr")-1) == 0 ){
7843 iMode = JX9_IO_STREAM_STDERR;
7844 }else{
7845 /* unknown stream name */
7846 return -1;
7847 }
7848 /* Create our handle */
7849 pData = JX9StreamDataInit(pResource?pResource->pVm:0, iMode);
7850 if( pData == 0 ){
7851 return -1;
7852 }
7853 /* Make the handle public */
7854 *ppHandle = (void *)pData;
7855 return JX9_OK;
7856}
7857/* jx9_int64 (*xRead)(void *, void *, jx9_int64) */
7858static jx9_int64 JX9StreamData_Read(void *pHandle, void *pBuffer, jx9_int64 nDatatoRead)
7859{
7860 jx9_stream_data *pData = (jx9_stream_data *)pHandle;
7861 if( pData == 0 ){
7862 return -1;
7863 }
7864 if( pData->iType != JX9_IO_STREAM_STDIN ){
7865 /* Forbidden */
7866 return -1;
7867 }
7868#ifdef __WINNT__
7869 {
7870 DWORD nRd;
7871 BOOL rc;
7872 rc = ReadFile(pData->x.pHandle, pBuffer, (DWORD)nDatatoRead, &nRd, 0);
7873 if( !rc ){
7874 /* IO error */
7875 return -1;
7876 }
7877 return (jx9_int64)nRd;
7878 }
7879#elif defined(__UNIXES__)
7880 {
7881 ssize_t nRd;
7882 int fd;
7883 fd = SX_PTR_TO_INT(pData->x.pHandle);
7884 nRd = read(fd, pBuffer, (size_t)nDatatoRead);
7885 if( nRd < 1 ){
7886 return -1;
7887 }
7888 return (jx9_int64)nRd;
7889 }
7890#else
7891 return -1;
7892#endif
7893}
7894/* jx9_int64 (*xWrite)(void *, const void *, jx9_int64) */
7895static jx9_int64 JX9StreamData_Write(void *pHandle, const void *pBuf, jx9_int64 nWrite)
7896{
7897 jx9_stream_data *pData = (jx9_stream_data *)pHandle;
7898 if( pData == 0 ){
7899 return -1;
7900 }
7901 if( pData->iType == JX9_IO_STREAM_STDIN ){
7902 /* Forbidden */
7903 return -1;
7904 }else if( pData->iType == JX9_IO_STREAM_OUTPUT ){
7905 jx9_output_consumer *pCons = &pData->x.sConsumer;
7906 int rc;
7907 /* Call the vm output consumer */
7908 rc = pCons->xConsumer(pBuf, (unsigned int)nWrite, pCons->pUserData);
7909 if( rc == JX9_ABORT ){
7910 return -1;
7911 }
7912 return nWrite;
7913 }
7914#ifdef __WINNT__
7915 {
7916 DWORD nWr;
7917 BOOL rc;
7918 rc = WriteFile(pData->x.pHandle, pBuf, (DWORD)nWrite, &nWr, 0);
7919 if( !rc ){
7920 /* IO error */
7921 return -1;
7922 }
7923 return (jx9_int64)nWr;
7924 }
7925#elif defined(__UNIXES__)
7926 {
7927 ssize_t nWr;
7928 int fd;
7929 fd = SX_PTR_TO_INT(pData->x.pHandle);
7930 nWr = write(fd, pBuf, (size_t)nWrite);
7931 if( nWr < 1 ){
7932 return -1;
7933 }
7934 return (jx9_int64)nWr;
7935 }
7936#else
7937 return -1;
7938#endif
7939}
7940/* void (*xClose)(void *) */
7941static void JX9StreamData_Close(void *pHandle)
7942{
7943 jx9_stream_data *pData = (jx9_stream_data *)pHandle;
7944 jx9_vm *pVm;
7945 if( pData == 0 ){
7946 return;
7947 }
7948 pVm = pData->pVm;
7949 /* Free the instance */
7950 SyMemBackendFree(&pVm->sAllocator, pData);
7951}
7952/* Export the jx9:// stream */
7953static const jx9_io_stream sjx9Stream = {
7954 "jx9",
7955 JX9_IO_STREAM_VERSION,
7956 JX9StreamData_Open, /* xOpen */
7957 0, /* xOpenDir */
7958 JX9StreamData_Close, /* xClose */
7959 0, /* xCloseDir */
7960 JX9StreamData_Read, /* xRead */
7961 0, /* xReadDir */
7962 JX9StreamData_Write, /* xWrite */
7963 0, /* xSeek */
7964 0, /* xLock */
7965 0, /* xRewindDir */
7966 0, /* xTell */
7967 0, /* xTrunc */
7968 0, /* xSeek */
7969 0 /* xStat */
7970};
7971#endif /* JX9_DISABLE_DISK_IO */
7972/*
7973 * Return TRUE if we are dealing with the jx9:// stream.
7974 * FALSE otherwise.
7975 */
7976static int is_jx9_stream(const jx9_io_stream *pStream)
7977{
7978#ifndef JX9_DISABLE_DISK_IO
7979 return pStream == &sjx9Stream;
7980#else
7981 SXUNUSED(pStream); /* cc warning */
7982 return 0;
7983#endif /* JX9_DISABLE_DISK_IO */
7984}
7985
7986#endif /* JX9_DISABLE_BUILTIN_FUNC */
7987/*
7988 * Export the IO routines defined above and the built-in IO streams
7989 * [i.e: file://, jx9://].
7990 * Note:
7991 * If the engine is compiled with the JX9_DISABLE_BUILTIN_FUNC directive
7992 * defined then this function is a no-op.
7993 */
7994JX9_PRIVATE sxi32 jx9RegisterIORoutine(jx9_vm *pVm)
7995{
7996#ifndef JX9_DISABLE_BUILTIN_FUNC
7997 /* VFS functions */
7998 static const jx9_builtin_func aVfsFunc[] = {
7999 {"chdir", jx9Vfs_chdir },
8000 {"chroot", jx9Vfs_chroot },
8001 {"getcwd", jx9Vfs_getcwd },
8002 {"rmdir", jx9Vfs_rmdir },
8003 {"is_dir", jx9Vfs_is_dir },
8004 {"mkdir", jx9Vfs_mkdir },
8005 {"rename", jx9Vfs_rename },
8006 {"realpath", jx9Vfs_realpath},
8007 {"sleep", jx9Vfs_sleep },
8008 {"usleep", jx9Vfs_usleep },
8009 {"unlink", jx9Vfs_unlink },
8010 {"delete", jx9Vfs_unlink },
8011 {"chmod", jx9Vfs_chmod },
8012 {"chown", jx9Vfs_chown },
8013 {"chgrp", jx9Vfs_chgrp },
8014 {"disk_free_space", jx9Vfs_disk_free_space },
8015 {"disk_total_space", jx9Vfs_disk_total_space},
8016 {"file_exists", jx9Vfs_file_exists },
8017 {"filesize", jx9Vfs_file_size },
8018 {"fileatime", jx9Vfs_file_atime },
8019 {"filemtime", jx9Vfs_file_mtime },
8020 {"filectime", jx9Vfs_file_ctime },
8021 {"is_file", jx9Vfs_is_file },
8022 {"is_link", jx9Vfs_is_link },
8023 {"is_readable", jx9Vfs_is_readable },
8024 {"is_writable", jx9Vfs_is_writable },
8025 {"is_executable", jx9Vfs_is_executable},
8026 {"filetype", jx9Vfs_filetype },
8027 {"stat", jx9Vfs_stat },
8028 {"lstat", jx9Vfs_lstat },
8029 {"getenv", jx9Vfs_getenv },
8030 {"setenv", jx9Vfs_putenv },
8031 {"putenv", jx9Vfs_putenv },
8032 {"touch", jx9Vfs_touch },
8033 {"link", jx9Vfs_link },
8034 {"symlink", jx9Vfs_symlink },
8035 {"umask", jx9Vfs_umask },
8036 {"sys_get_temp_dir", jx9Vfs_sys_get_temp_dir },
8037 {"get_current_user", jx9Vfs_get_current_user },
8038 {"getpid", jx9Vfs_getmypid },
8039 {"getuid", jx9Vfs_getmyuid },
8040 {"getgid", jx9Vfs_getmygid },
8041 {"uname", jx9Vfs_uname},
8042 /* Path processing */
8043 {"dirname", jx9Builtin_dirname },
8044 {"basename", jx9Builtin_basename },
8045 {"pathinfo", jx9Builtin_pathinfo },
8046 {"strglob", jx9Builtin_strglob },
8047 {"fnmatch", jx9Builtin_fnmatch },
8048 /* ZIP processing */
8049 {"zip_open", jx9Builtin_zip_open },
8050 {"zip_close", jx9Builtin_zip_close},
8051 {"zip_read", jx9Builtin_zip_read },
8052 {"zip_entry_open", jx9Builtin_zip_entry_open },
8053 {"zip_entry_close", jx9Builtin_zip_entry_close},
8054 {"zip_entry_name", jx9Builtin_zip_entry_name },
8055 {"zip_entry_filesize", jx9Builtin_zip_entry_filesize },
8056 {"zip_entry_compressedsize", jx9Builtin_zip_entry_compressedsize },
8057 {"zip_entry_read", jx9Builtin_zip_entry_read },
8058 {"zip_entry_reset_cursor", jx9Builtin_zip_entry_reset_cursor},
8059 {"zip_entry_compressionmethod", jx9Builtin_zip_entry_compressionmethod}
8060 };
8061 /* IO stream functions */
8062 static const jx9_builtin_func aIOFunc[] = {
8063 {"ftruncate", jx9Builtin_ftruncate },
8064 {"fseek", jx9Builtin_fseek },
8065 {"ftell", jx9Builtin_ftell },
8066 {"rewind", jx9Builtin_rewind },
8067 {"fflush", jx9Builtin_fflush },
8068 {"feof", jx9Builtin_feof },
8069 {"fgetc", jx9Builtin_fgetc },
8070 {"fgets", jx9Builtin_fgets },
8071 {"fread", jx9Builtin_fread },
8072 {"fgetcsv", jx9Builtin_fgetcsv},
8073 {"fgetss", jx9Builtin_fgetss },
8074 {"readdir", jx9Builtin_readdir},
8075 {"rewinddir", jx9Builtin_rewinddir },
8076 {"closedir", jx9Builtin_closedir},
8077 {"opendir", jx9Builtin_opendir },
8078 {"readfile", jx9Builtin_readfile},
8079 {"file_get_contents", jx9Builtin_file_get_contents},
8080 {"file_put_contents", jx9Builtin_file_put_contents},
8081 {"file", jx9Builtin_file },
8082 {"copy", jx9Builtin_copy },
8083 {"fstat", jx9Builtin_fstat },
8084 {"fwrite", jx9Builtin_fwrite },
8085 {"fputs", jx9Builtin_fwrite },
8086 {"flock", jx9Builtin_flock },
8087 {"fclose", jx9Builtin_fclose },
8088 {"fopen", jx9Builtin_fopen },
8089 {"fpassthru", jx9Builtin_fpassthru },
8090 {"fputcsv", jx9Builtin_fputcsv },
8091 {"fprintf", jx9Builtin_fprintf },
8092#if !defined(JX9_DISABLE_HASH_FUNC)
8093 {"md5_file", jx9Builtin_md5_file},
8094 {"sha1_file", jx9Builtin_sha1_file},
8095#endif /* JX9_DISABLE_HASH_FUNC */
8096 {"parse_ini_file", jx9Builtin_parse_ini_file},
8097 {"vfprintf", jx9Builtin_vfprintf}
8098 };
8099 const jx9_io_stream *pFileStream = 0;
8100 sxu32 n = 0;
8101 /* Register the functions defined above */
8102 for( n = 0 ; n < SX_ARRAYSIZE(aVfsFunc) ; ++n ){
8103 jx9_create_function(&(*pVm), aVfsFunc[n].zName, aVfsFunc[n].xFunc, (void *)pVm->pEngine->pVfs);
8104 }
8105 for( n = 0 ; n < SX_ARRAYSIZE(aIOFunc) ; ++n ){
8106 jx9_create_function(&(*pVm), aIOFunc[n].zName, aIOFunc[n].xFunc, pVm);
8107 }
8108#ifndef JX9_DISABLE_DISK_IO
8109 /* Register the file stream if available */
8110#ifdef __WINNT__
8111 pFileStream = &sWinFileStream;
8112#elif defined(__UNIXES__)
8113 pFileStream = &sUnixFileStream;
8114#endif
8115 /* Install the jx9:// stream */
8116 jx9_vm_config(pVm, JX9_VM_CONFIG_IO_STREAM, &sjx9Stream);
8117#endif /* JX9_DISABLE_DISK_IO */
8118 if( pFileStream ){
8119 /* Install the file:// stream */
8120 jx9_vm_config(pVm, JX9_VM_CONFIG_IO_STREAM, pFileStream);
8121 }
8122#else
8123 SXUNUSED(pVm); /* cc warning */
8124#endif /* JX9_DISABLE_BUILTIN_FUNC */
8125 return SXRET_OK;
8126}
8127/*
8128 * Export the STDIN handle.
8129 */
8130JX9_PRIVATE void * jx9ExportStdin(jx9_vm *pVm)
8131{
8132#ifndef JX9_DISABLE_BUILTIN_FUNC
8133#ifndef JX9_DISABLE_DISK_IO
8134 if( pVm->pStdin == 0 ){
8135 io_private *pIn;
8136 /* Allocate an IO private instance */
8137 pIn = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private));
8138 if( pIn == 0 ){
8139 return 0;
8140 }
8141 InitIOPrivate(pVm, &sjx9Stream, pIn);
8142 /* Initialize the handle */
8143 pIn->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDIN);
8144 /* Install the STDIN stream */
8145 pVm->pStdin = pIn;
8146 return pIn;
8147 }else{
8148 /* NULL or STDIN */
8149 return pVm->pStdin;
8150 }
8151#else
8152 return 0;
8153#endif
8154#else
8155 SXUNUSED(pVm); /* cc warning */
8156 return 0;
8157#endif
8158}
8159/*
8160 * Export the STDOUT handle.
8161 */
8162JX9_PRIVATE void * jx9ExportStdout(jx9_vm *pVm)
8163{
8164#ifndef JX9_DISABLE_BUILTIN_FUNC
8165#ifndef JX9_DISABLE_DISK_IO
8166 if( pVm->pStdout == 0 ){
8167 io_private *pOut;
8168 /* Allocate an IO private instance */
8169 pOut = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private));
8170 if( pOut == 0 ){
8171 return 0;
8172 }
8173 InitIOPrivate(pVm, &sjx9Stream, pOut);
8174 /* Initialize the handle */
8175 pOut->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDOUT);
8176 /* Install the STDOUT stream */
8177 pVm->pStdout = pOut;
8178 return pOut;
8179 }else{
8180 /* NULL or STDOUT */
8181 return pVm->pStdout;
8182 }
8183#else
8184 return 0;
8185#endif
8186#else
8187 SXUNUSED(pVm); /* cc warning */
8188 return 0;
8189#endif
8190}
8191/*
8192 * Export the STDERR handle.
8193 */
8194JX9_PRIVATE void * jx9ExportStderr(jx9_vm *pVm)
8195{
8196#ifndef JX9_DISABLE_BUILTIN_FUNC
8197#ifndef JX9_DISABLE_DISK_IO
8198 if( pVm->pStderr == 0 ){
8199 io_private *pErr;
8200 /* Allocate an IO private instance */
8201 pErr = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private));
8202 if( pErr == 0 ){
8203 return 0;
8204 }
8205 InitIOPrivate(pVm, &sjx9Stream, pErr);
8206 /* Initialize the handle */
8207 pErr->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDERR);
8208 /* Install the STDERR stream */
8209 pVm->pStderr = pErr;
8210 return pErr;
8211 }else{
8212 /* NULL or STDERR */
8213 return pVm->pStderr;
8214 }
8215#else
8216 return 0;
8217#endif
8218#else
8219 SXUNUSED(pVm); /* cc warning */
8220 return 0;
8221#endif
8222}