From 9ee8378d393778ac67314be7ea8d5bcbaeee9ee0 Mon Sep 17 00:00:00 2001 From: Aaron Seigo Date: Sun, 7 Dec 2014 10:08:07 +0100 Subject: try out unqlite --- common/unqlite/jx9_vfs.c | 8222 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 8222 insertions(+) create mode 100644 common/unqlite/jx9_vfs.c (limited to 'common/unqlite/jx9_vfs.c') 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 @@ +/* + * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON. + * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/ + * Version 1.7.2 + * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES + * please contact Symisc Systems via: + * legal@symisc.net + * licensing@symisc.net + * contact@symisc.net + * or visit: + * http://jx9.symisc.net/ + */ + /* $SymiscID: vfs.c v2.1 Ubuntu 2012-12-13 00:013 stable $ */ +#ifndef JX9_AMALGAMATION +#include "jx9Int.h" +#endif +/* + * This file implement a virtual file systems (VFS) for the JX9 engine. + */ +/* + * Given a string containing the path of a file or directory, this function + * return the parent directory's path. + */ +JX9_PRIVATE const char * jx9ExtractDirName(const char *zPath, int nByte, int *pLen) +{ + const char *zEnd = &zPath[nByte - 1]; + int c, d; + c = d = '/'; +#ifdef __WINNT__ + d = '\\'; +#endif + while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){ + zEnd--; + } + *pLen = (int)(zEnd-zPath); +#ifdef __WINNT__ + if( (*pLen) == (int)sizeof(char) && zPath[0] == '/' ){ + /* Normalize path on windows */ + return "\\"; + } +#endif + if( zEnd == zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d) ){ + /* No separator, return "." as the current directory */ + *pLen = sizeof(char); + return "."; + } + if( (*pLen) == 0 ){ + *pLen = sizeof(char); +#ifdef __WINNT__ + return "\\"; +#else + return "/"; +#endif + } + return zPath; +} +/* + * Omit the vfs layer implementation from the built if the JX9_DISABLE_BUILTIN_FUNC directive is defined. + */ +#ifndef JX9_DISABLE_BUILTIN_FUNC +/* + * bool chdir(string $directory) + * Change the current directory. + * Parameters + * $directory + * The new current directory + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Vfs_chdir(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath; + jx9_vfs *pVfs; + int rc; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xChdir == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the desired directory */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Perform the requested operation */ + rc = pVfs->xChdir(zPath); + /* IO return value */ + jx9_result_bool(pCtx, rc == JX9_OK); + return JX9_OK; +} +/* + * bool chroot(string $directory) + * Change the root directory. + * Parameters + * $directory + * The path to change the root directory to + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Vfs_chroot(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath; + jx9_vfs *pVfs; + int rc; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xChroot == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the desired directory */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Perform the requested operation */ + rc = pVfs->xChroot(zPath); + /* IO return value */ + jx9_result_bool(pCtx, rc == JX9_OK); + return JX9_OK; +} +/* + * string getcwd(void) + * Gets the current working directory. + * Parameters + * None + * Return + * Returns the current working directory on success, or FALSE on failure. + */ +static int jx9Vfs_getcwd(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + jx9_vfs *pVfs; + int rc; + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xGetcwd == 0 ){ + SXUNUSED(nArg); /* cc warning */ + SXUNUSED(apArg); + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + jx9_result_string(pCtx, "", 0); + /* Perform the requested operation */ + rc = pVfs->xGetcwd(pCtx); + if( rc != JX9_OK ){ + /* Error, return FALSE */ + jx9_result_bool(pCtx, 0); + } + return JX9_OK; +} +/* + * bool rmdir(string $directory) + * Removes directory. + * Parameters + * $directory + * The path to the directory + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Vfs_rmdir(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath; + jx9_vfs *pVfs; + int rc; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xRmdir == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the desired directory */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Perform the requested operation */ + rc = pVfs->xRmdir(zPath); + /* IO return value */ + jx9_result_bool(pCtx, rc == JX9_OK); + return JX9_OK; +} +/* + * bool is_dir(string $filename) + * Tells whether the given filename is a directory. + * Parameters + * $filename + * Path to the file. + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Vfs_is_dir(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath; + jx9_vfs *pVfs; + int rc; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xIsdir == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the desired directory */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Perform the requested operation */ + rc = pVfs->xIsdir(zPath); + /* IO return value */ + jx9_result_bool(pCtx, rc == JX9_OK); + return JX9_OK; +} +/* + * bool mkdir(string $pathname[, int $mode = 0777]) + * Make a directory. + * Parameters + * $pathname + * The directory path. + * $mode + * The mode is 0777 by default, which means the widest possible access. + * Note: + * mode is ignored on Windows. + * Note that you probably want to specify the mode as an octal number, which means + * it should have a leading zero. The mode is also modified by the current umask + * which you can change using umask(). + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Vfs_mkdir(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int iRecursive = 0; + const char *zPath; + jx9_vfs *pVfs; + int iMode, rc; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xMkdir == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the desired directory */ + zPath = jx9_value_to_string(apArg[0], 0); +#ifdef __WINNT__ + iMode = 0; +#else + /* Assume UNIX */ + iMode = 0777; +#endif + if( nArg > 1 ){ + iMode = jx9_value_to_int(apArg[1]); + if( nArg > 2 ){ + iRecursive = jx9_value_to_bool(apArg[2]); + } + } + /* Perform the requested operation */ + rc = pVfs->xMkdir(zPath, iMode, iRecursive); + /* IO return value */ + jx9_result_bool(pCtx, rc == JX9_OK); + return JX9_OK; +} +/* + * bool rename(string $oldname, string $newname) + * Attempts to rename oldname to newname. + * Parameters + * $oldname + * Old name. + * $newname + * New name. + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Vfs_rename(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zOld, *zNew; + jx9_vfs *pVfs; + int rc; + if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xRename == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + zOld = jx9_value_to_string(apArg[0], 0); + zNew = jx9_value_to_string(apArg[1], 0); + rc = pVfs->xRename(zOld, zNew); + /* IO result */ + jx9_result_bool(pCtx, rc == JX9_OK ); + return JX9_OK; +} +/* + * string realpath(string $path) + * Returns canonicalized absolute pathname. + * Parameters + * $path + * Target path. + * Return + * Canonicalized absolute pathname on success. or FALSE on failure. + */ +static int jx9Vfs_realpath(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath; + jx9_vfs *pVfs; + int rc; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xRealpath == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Set an empty string untnil the underlying OS interface change that */ + jx9_result_string(pCtx, "", 0); + /* Perform the requested operation */ + zPath = jx9_value_to_string(apArg[0], 0); + rc = pVfs->xRealpath(zPath, pCtx); + if( rc != JX9_OK ){ + jx9_result_bool(pCtx, 0); + } + return JX9_OK; +} +/* + * int sleep(int $seconds) + * Delays the program execution for the given number of seconds. + * Parameters + * $seconds + * Halt time in seconds. + * Return + * Zero on success or FALSE on failure. + */ +static int jx9Vfs_sleep(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + jx9_vfs *pVfs; + int rc, nSleep; + if( nArg < 1 || !jx9_value_is_int(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xSleep == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Amount to sleep */ + nSleep = jx9_value_to_int(apArg[0]); + if( nSleep < 0 ){ + /* Invalid value, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation (Microseconds) */ + rc = pVfs->xSleep((unsigned int)(nSleep * SX_USEC_PER_SEC)); + if( rc != JX9_OK ){ + /* Return FALSE */ + jx9_result_bool(pCtx, 0); + }else{ + /* Return zero */ + jx9_result_int(pCtx, 0); + } + return JX9_OK; +} +/* + * void usleep(int $micro_seconds) + * Delays program execution for the given number of micro seconds. + * Parameters + * $micro_seconds + * Halt time in micro seconds. A micro second is one millionth of a second. + * Return + * None. + */ +static int jx9Vfs_usleep(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + jx9_vfs *pVfs; + int nSleep; + if( nArg < 1 || !jx9_value_is_int(apArg[0]) ){ + /* Missing/Invalid argument, return immediately */ + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xSleep == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS", + jx9_function_name(pCtx) + ); + return JX9_OK; + } + /* Amount to sleep */ + nSleep = jx9_value_to_int(apArg[0]); + if( nSleep < 0 ){ + /* Invalid value, return immediately */ + return JX9_OK; + } + /* Perform the requested operation (Microseconds) */ + pVfs->xSleep((unsigned int)nSleep); + return JX9_OK; +} +/* + * bool unlink (string $filename) + * Delete a file. + * Parameters + * $filename + * Path to the file. + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Vfs_unlink(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath; + jx9_vfs *pVfs; + int rc; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xUnlink == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the desired directory */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Perform the requested operation */ + rc = pVfs->xUnlink(zPath); + /* IO return value */ + jx9_result_bool(pCtx, rc == JX9_OK); + return JX9_OK; +} +/* + * bool chmod(string $filename, int $mode) + * Attempts to change the mode of the specified file to that given in mode. + * Parameters + * $filename + * Path to the file. + * $mode + * Mode (Must be an integer) + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Vfs_chmod(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath; + jx9_vfs *pVfs; + int iMode; + int rc; + if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xChmod == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the desired directory */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Extract the mode */ + iMode = jx9_value_to_int(apArg[1]); + /* Perform the requested operation */ + rc = pVfs->xChmod(zPath, iMode); + /* IO return value */ + jx9_result_bool(pCtx, rc == JX9_OK); + return JX9_OK; +} +/* + * bool chown(string $filename, string $user) + * Attempts to change the owner of the file filename to user user. + * Parameters + * $filename + * Path to the file. + * $user + * Username. + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Vfs_chown(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath, *zUser; + jx9_vfs *pVfs; + int rc; + if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xChown == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the desired directory */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Extract the user */ + zUser = jx9_value_to_string(apArg[1], 0); + /* Perform the requested operation */ + rc = pVfs->xChown(zPath, zUser); + /* IO return value */ + jx9_result_bool(pCtx, rc == JX9_OK); + return JX9_OK; +} +/* + * bool chgrp(string $filename, string $group) + * Attempts to change the group of the file filename to group. + * Parameters + * $filename + * Path to the file. + * $group + * groupname. + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Vfs_chgrp(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath, *zGroup; + jx9_vfs *pVfs; + int rc; + if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xChgrp == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the desired directory */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Extract the user */ + zGroup = jx9_value_to_string(apArg[1], 0); + /* Perform the requested operation */ + rc = pVfs->xChgrp(zPath, zGroup); + /* IO return value */ + jx9_result_bool(pCtx, rc == JX9_OK); + return JX9_OK; +} +/* + * int64 disk_free_space(string $directory) + * Returns available space on filesystem or disk partition. + * Parameters + * $directory + * A directory of the filesystem or disk partition. + * Return + * Returns the number of available bytes as a 64-bit integer or FALSE on failure. + */ +static int jx9Vfs_disk_free_space(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath; + jx9_int64 iSize; + jx9_vfs *pVfs; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xFreeSpace == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the desired directory */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Perform the requested operation */ + iSize = pVfs->xFreeSpace(zPath); + /* IO return value */ + jx9_result_int64(pCtx, iSize); + return JX9_OK; +} +/* + * int64 disk_total_space(string $directory) + * Returns the total size of a filesystem or disk partition. + * Parameters + * $directory + * A directory of the filesystem or disk partition. + * Return + * Returns the number of available bytes as a 64-bit integer or FALSE on failure. + */ +static int jx9Vfs_disk_total_space(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath; + jx9_int64 iSize; + jx9_vfs *pVfs; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xTotalSpace == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the desired directory */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Perform the requested operation */ + iSize = pVfs->xTotalSpace(zPath); + /* IO return value */ + jx9_result_int64(pCtx, iSize); + return JX9_OK; +} +/* + * bool file_exists(string $filename) + * Checks whether a file or directory exists. + * Parameters + * $filename + * Path to the file. + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Vfs_file_exists(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath; + jx9_vfs *pVfs; + int rc; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xFileExists == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the desired directory */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Perform the requested operation */ + rc = pVfs->xFileExists(zPath); + /* IO return value */ + jx9_result_bool(pCtx, rc == JX9_OK); + return JX9_OK; +} +/* + * int64 file_size(string $filename) + * Gets the size for the given file. + * Parameters + * $filename + * Path to the file. + * Return + * File size on success or FALSE on failure. + */ +static int jx9Vfs_file_size(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath; + jx9_int64 iSize; + jx9_vfs *pVfs; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xFileSize == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the desired directory */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Perform the requested operation */ + iSize = pVfs->xFileSize(zPath); + /* IO return value */ + jx9_result_int64(pCtx, iSize); + return JX9_OK; +} +/* + * int64 fileatime(string $filename) + * Gets the last access time of the given file. + * Parameters + * $filename + * Path to the file. + * Return + * File atime on success or FALSE on failure. + */ +static int jx9Vfs_file_atime(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath; + jx9_int64 iTime; + jx9_vfs *pVfs; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xFileAtime == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the desired directory */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Perform the requested operation */ + iTime = pVfs->xFileAtime(zPath); + /* IO return value */ + jx9_result_int64(pCtx, iTime); + return JX9_OK; +} +/* + * int64 filemtime(string $filename) + * Gets file modification time. + * Parameters + * $filename + * Path to the file. + * Return + * File mtime on success or FALSE on failure. + */ +static int jx9Vfs_file_mtime(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath; + jx9_int64 iTime; + jx9_vfs *pVfs; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xFileMtime == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the desired directory */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Perform the requested operation */ + iTime = pVfs->xFileMtime(zPath); + /* IO return value */ + jx9_result_int64(pCtx, iTime); + return JX9_OK; +} +/* + * int64 filectime(string $filename) + * Gets inode change time of file. + * Parameters + * $filename + * Path to the file. + * Return + * File ctime on success or FALSE on failure. + */ +static int jx9Vfs_file_ctime(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath; + jx9_int64 iTime; + jx9_vfs *pVfs; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xFileCtime == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the desired directory */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Perform the requested operation */ + iTime = pVfs->xFileCtime(zPath); + /* IO return value */ + jx9_result_int64(pCtx, iTime); + return JX9_OK; +} +/* + * bool is_file(string $filename) + * Tells whether the filename is a regular file. + * Parameters + * $filename + * Path to the file. + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Vfs_is_file(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath; + jx9_vfs *pVfs; + int rc; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xIsfile == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the desired directory */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Perform the requested operation */ + rc = pVfs->xIsfile(zPath); + /* IO return value */ + jx9_result_bool(pCtx, rc == JX9_OK); + return JX9_OK; +} +/* + * bool is_link(string $filename) + * Tells whether the filename is a symbolic link. + * Parameters + * $filename + * Path to the file. + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Vfs_is_link(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath; + jx9_vfs *pVfs; + int rc; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xIslink == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the desired directory */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Perform the requested operation */ + rc = pVfs->xIslink(zPath); + /* IO return value */ + jx9_result_bool(pCtx, rc == JX9_OK); + return JX9_OK; +} +/* + * bool is_readable(string $filename) + * Tells whether a file exists and is readable. + * Parameters + * $filename + * Path to the file. + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Vfs_is_readable(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath; + jx9_vfs *pVfs; + int rc; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xReadable == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the desired directory */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Perform the requested operation */ + rc = pVfs->xReadable(zPath); + /* IO return value */ + jx9_result_bool(pCtx, rc == JX9_OK); + return JX9_OK; +} +/* + * bool is_writable(string $filename) + * Tells whether the filename is writable. + * Parameters + * $filename + * Path to the file. + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Vfs_is_writable(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath; + jx9_vfs *pVfs; + int rc; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xWritable == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the desired directory */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Perform the requested operation */ + rc = pVfs->xWritable(zPath); + /* IO return value */ + jx9_result_bool(pCtx, rc == JX9_OK); + return JX9_OK; +} +/* + * bool is_executable(string $filename) + * Tells whether the filename is executable. + * Parameters + * $filename + * Path to the file. + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Vfs_is_executable(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath; + jx9_vfs *pVfs; + int rc; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xExecutable == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the desired directory */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Perform the requested operation */ + rc = pVfs->xExecutable(zPath); + /* IO return value */ + jx9_result_bool(pCtx, rc == JX9_OK); + return JX9_OK; +} +/* + * string filetype(string $filename) + * Gets file type. + * Parameters + * $filename + * Path to the file. + * Return + * The type of the file. Possible values are fifo, char, dir, block, link + * file, socket and unknown. + */ +static int jx9Vfs_filetype(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath; + jx9_vfs *pVfs; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return 'unknown' */ + jx9_result_string(pCtx, "unknown", sizeof("unknown")-1); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xFiletype == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the desired directory */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Set the empty string as the default return value */ + jx9_result_string(pCtx, "", 0); + /* Perform the requested operation */ + pVfs->xFiletype(zPath, pCtx); + return JX9_OK; +} +/* + * array stat(string $filename) + * Gives information about a file. + * Parameters + * $filename + * Path to the file. + * Return + * An associative array on success holding the following entries on success + * 0 dev device number + * 1 ino inode number (zero on windows) + * 2 mode inode protection mode + * 3 nlink number of links + * 4 uid userid of owner (zero on windows) + * 5 gid groupid of owner (zero on windows) + * 6 rdev device type, if inode device + * 7 size size in bytes + * 8 atime time of last access (Unix timestamp) + * 9 mtime time of last modification (Unix timestamp) + * 10 ctime time of last inode change (Unix timestamp) + * 11 blksize blocksize of filesystem IO (zero on windows) + * 12 blocks number of 512-byte blocks allocated. + * Note: + * FALSE is returned on failure. + */ +static int jx9Vfs_stat(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + jx9_value *pArray, *pValue; + const char *zPath; + jx9_vfs *pVfs; + int rc; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xStat == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Create the array and the working value */ + pArray = jx9_context_new_array(pCtx); + pValue = jx9_context_new_scalar(pCtx); + if( pArray == 0 || pValue == 0 ){ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the file path */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Perform the requested operation */ + rc = pVfs->xStat(zPath, pArray, pValue); + if( rc != JX9_OK ){ + /* IO error, return FALSE */ + jx9_result_bool(pCtx, 0); + }else{ + /* Return the associative array */ + jx9_result_value(pCtx, pArray); + } + /* Don't worry about freeing memory here, everything will be released + * automatically as soon we return from this function. */ + return JX9_OK; +} +/* + * array lstat(string $filename) + * Gives information about a file or symbolic link. + * Parameters + * $filename + * Path to the file. + * Return + * An associative array on success holding the following entries on success + * 0 dev device number + * 1 ino inode number (zero on windows) + * 2 mode inode protection mode + * 3 nlink number of links + * 4 uid userid of owner (zero on windows) + * 5 gid groupid of owner (zero on windows) + * 6 rdev device type, if inode device + * 7 size size in bytes + * 8 atime time of last access (Unix timestamp) + * 9 mtime time of last modification (Unix timestamp) + * 10 ctime time of last inode change (Unix timestamp) + * 11 blksize blocksize of filesystem IO (zero on windows) + * 12 blocks number of 512-byte blocks allocated. + * Note: + * FALSE is returned on failure. + */ +static int jx9Vfs_lstat(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + jx9_value *pArray, *pValue; + const char *zPath; + jx9_vfs *pVfs; + int rc; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xlStat == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Create the array and the working value */ + pArray = jx9_context_new_array(pCtx); + pValue = jx9_context_new_scalar(pCtx); + if( pArray == 0 || pValue == 0 ){ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the file path */ + zPath = jx9_value_to_string(apArg[0], 0); + /* Perform the requested operation */ + rc = pVfs->xlStat(zPath, pArray, pValue); + if( rc != JX9_OK ){ + /* IO error, return FALSE */ + jx9_result_bool(pCtx, 0); + }else{ + /* Return the associative array */ + jx9_result_value(pCtx, pArray); + } + /* Don't worry about freeing memory here, everything will be released + * automatically as soon we return from this function. */ + return JX9_OK; +} +/* + * string getenv(string $varname) + * Gets the value of an environment variable. + * Parameters + * $varname + * The variable name. + * Return + * Returns the value of the environment variable varname, or FALSE if the environment + * variable varname does not exist. + */ +static int jx9Vfs_getenv(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zEnv; + jx9_vfs *pVfs; + int iLen; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xGetenv == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the environment variable */ + zEnv = jx9_value_to_string(apArg[0], &iLen); + /* Set a boolean FALSE as the default return value */ + jx9_result_bool(pCtx, 0); + if( iLen < 1 ){ + /* Empty string */ + return JX9_OK; + } + /* Perform the requested operation */ + pVfs->xGetenv(zEnv, pCtx); + return JX9_OK; +} +/* + * bool putenv(string $settings) + * Set the value of an environment variable. + * Parameters + * $setting + * The setting, like "FOO=BAR" + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Vfs_putenv(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zName, *zValue; + char *zSettings, *zEnd; + jx9_vfs *pVfs; + int iLen, rc; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the setting variable */ + zSettings = (char *)jx9_value_to_string(apArg[0], &iLen); + if( iLen < 1 ){ + /* Empty string, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Parse the setting */ + zEnd = &zSettings[iLen]; + zValue = 0; + zName = zSettings; + while( zSettings < zEnd ){ + if( zSettings[0] == '=' ){ + /* Null terminate the name */ + zSettings[0] = 0; + zValue = &zSettings[1]; + break; + } + zSettings++; + } + /* Install the environment variable in the $_Env array */ + if( zValue == 0 || zName[0] == 0 || zValue >= zEnd || zName >= zValue ){ + /* Invalid settings, retun FALSE */ + jx9_result_bool(pCtx, 0); + if( zSettings < zEnd ){ + zSettings[0] = '='; + } + return JX9_OK; + } + jx9_vm_config(pCtx->pVm, JX9_VM_CONFIG_ENV_ATTR, zName, zValue, (int)(zEnd-zValue)); + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xSetenv == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + zSettings[0] = '='; + return JX9_OK; + } + /* Perform the requested operation */ + rc = pVfs->xSetenv(zName, zValue); + jx9_result_bool(pCtx, rc == JX9_OK ); + zSettings[0] = '='; + return JX9_OK; +} +/* + * bool touch(string $filename[, int64 $time = time()[, int64 $atime]]) + * Sets access and modification time of file. + * Note: On windows + * If the file does not exists, it will not be created. + * Parameters + * $filename + * The name of the file being touched. + * $time + * The touch time. If time is not supplied, the current system time is used. + * $atime + * If present, the access time of the given filename is set to the value of atime. + * Otherwise, it is set to the value passed to the time parameter. If neither are + * present, the current system time is used. + * Return + * TRUE on success or FALSE on failure. +*/ +static int jx9Vfs_touch(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + jx9_int64 nTime, nAccess; + const char *zFile; + jx9_vfs *pVfs; + int rc; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xTouch == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + nTime = nAccess = -1; + zFile = jx9_value_to_string(apArg[0], 0); + if( nArg > 1 ){ + nTime = jx9_value_to_int64(apArg[1]); + if( nArg > 2 ){ + nAccess = jx9_value_to_int64(apArg[1]); + }else{ + nAccess = nTime; + } + } + rc = pVfs->xTouch(zFile, nTime, nAccess); + /* IO result */ + jx9_result_bool(pCtx, rc == JX9_OK); + return JX9_OK; +} +/* + * Path processing functions that do not need access to the VFS layer + * Authors: + * Symisc Systems, devel@symisc.net. + * Copyright (C) Symisc Systems, http://jx9.symisc.net + * Status: + * Stable. + */ +/* + * string dirname(string $path) + * Returns parent directory's path. + * Parameters + * $path + * Target path. + * On Windows, both slash (/) and backslash (\) are used as directory separator character. + * In other environments, it is the forward slash (/). + * Return + * The path of the parent directory. If there are no slashes in path, a dot ('.') + * is returned, indicating the current directory. + */ +static int jx9Builtin_dirname(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath, *zDir; + int iLen, iDirlen; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid arguments, return the empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Point to the target path */ + zPath = jx9_value_to_string(apArg[0], &iLen); + if( iLen < 1 ){ + /* Reuturn "." */ + jx9_result_string(pCtx, ".", sizeof(char)); + return JX9_OK; + } + /* Perform the requested operation */ + zDir = jx9ExtractDirName(zPath, iLen, &iDirlen); + /* Return directory name */ + jx9_result_string(pCtx, zDir, iDirlen); + return JX9_OK; +} +/* + * string basename(string $path[, string $suffix ]) + * Returns trailing name component of path. + * Parameters + * $path + * Target path. + * On Windows, both slash (/) and backslash (\) are used as directory separator character. + * In other environments, it is the forward slash (/). + * $suffix + * If the name component ends in suffix this will also be cut off. + * Return + * The base name of the given path. + */ +static int jx9Builtin_basename(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath, *zBase, *zEnd; + int c, d, iLen; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return the empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + c = d = '/'; +#ifdef __WINNT__ + d = '\\'; +#endif + /* Point to the target path */ + zPath = jx9_value_to_string(apArg[0], &iLen); + if( iLen < 1 ){ + /* Empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Perform the requested operation */ + zEnd = &zPath[iLen - 1]; + /* Ignore trailing '/' */ + while( zEnd > zPath && ( (int)zEnd[0] == c || (int)zEnd[0] == d ) ){ + zEnd--; + } + iLen = (int)(&zEnd[1]-zPath); + while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){ + zEnd--; + } + zBase = (zEnd > zPath) ? &zEnd[1] : zPath; + zEnd = &zPath[iLen]; + if( nArg > 1 && jx9_value_is_string(apArg[1]) ){ + const char *zSuffix; + int nSuffix; + /* Strip suffix */ + zSuffix = jx9_value_to_string(apArg[1], &nSuffix); + if( nSuffix > 0 && nSuffix < iLen && SyMemcmp(&zEnd[-nSuffix], zSuffix, nSuffix) == 0 ){ + zEnd -= nSuffix; + } + } + /* Store the basename */ + jx9_result_string(pCtx, zBase, (int)(zEnd-zBase)); + return JX9_OK; +} +/* + * value pathinfo(string $path [, int $options = PATHINFO_DIRNAME | PATHINFO_BASENAME | PATHINFO_EXTENSION | PATHINFO_FILENAME ]) + * Returns information about a file path. + * Parameter + * $path + * The path to be parsed. + * $options + * If present, specifies a specific element to be returned; one of + * PATHINFO_DIRNAME, PATHINFO_BASENAME, PATHINFO_EXTENSION or PATHINFO_FILENAME. + * Return + * If the options parameter is not passed, an associative array containing the following + * elements is returned: dirname, basename, extension (if any), and filename. + * If options is present, returns a string containing the requested element. + */ +typedef struct path_info path_info; +struct path_info +{ + SyString sDir; /* Directory [i.e: /var/www] */ + SyString sBasename; /* Basename [i.e httpd.conf] */ + SyString sExtension; /* File extension [i.e xml, pdf..] */ + SyString sFilename; /* Filename */ +}; +/* + * Extract path fields. + */ +static sxi32 ExtractPathInfo(const char *zPath, int nByte, path_info *pOut) +{ + const char *zPtr, *zEnd = &zPath[nByte - 1]; + SyString *pCur; + int c, d; + c = d = '/'; +#ifdef __WINNT__ + d = '\\'; +#endif + /* Zero the structure */ + SyZero(pOut, sizeof(path_info)); + /* Handle special case */ + if( nByte == sizeof(char) && ( (int)zPath[0] == c || (int)zPath[0] == d ) ){ +#ifdef __WINNT__ + SyStringInitFromBuf(&pOut->sDir, "\\", sizeof(char)); +#else + SyStringInitFromBuf(&pOut->sDir, "/", sizeof(char)); +#endif + return SXRET_OK; + } + /* Extract the basename */ + while( zEnd > zPath && ( (int)zEnd[0] != c && (int)zEnd[0] != d ) ){ + zEnd--; + } + zPtr = (zEnd > zPath) ? &zEnd[1] : zPath; + zEnd = &zPath[nByte]; + /* dirname */ + pCur = &pOut->sDir; + SyStringInitFromBuf(pCur, zPath, zPtr-zPath); + if( pCur->nByte > 1 ){ + SyStringTrimTrailingChar(pCur, '/'); +#ifdef __WINNT__ + SyStringTrimTrailingChar(pCur, '\\'); +#endif + }else if( (int)zPath[0] == c || (int)zPath[0] == d ){ +#ifdef __WINNT__ + SyStringInitFromBuf(&pOut->sDir, "\\", sizeof(char)); +#else + SyStringInitFromBuf(&pOut->sDir, "/", sizeof(char)); +#endif + } + /* basename/filename */ + pCur = &pOut->sBasename; + SyStringInitFromBuf(pCur, zPtr, zEnd-zPtr); + SyStringTrimLeadingChar(pCur, '/'); +#ifdef __WINNT__ + SyStringTrimLeadingChar(pCur, '\\'); +#endif + SyStringDupPtr(&pOut->sFilename, pCur); + if( pCur->nByte > 0 ){ + /* extension */ + zEnd--; + while( zEnd > pCur->zString /*basename*/ && zEnd[0] != '.' ){ + zEnd--; + } + if( zEnd > pCur->zString ){ + zEnd++; /* Jump leading dot */ + SyStringInitFromBuf(&pOut->sExtension, zEnd, &zPath[nByte]-zEnd); + /* Fix filename */ + pCur = &pOut->sFilename; + if( pCur->nByte > SyStringLength(&pOut->sExtension) ){ + pCur->nByte -= 1 + SyStringLength(&pOut->sExtension); + } + } + } + return SXRET_OK; +} +/* + * value pathinfo(string $path [, int $options = PATHINFO_DIRNAME | PATHINFO_BASENAME | PATHINFO_EXTENSION | PATHINFO_FILENAME ]) + * See block comment above. + */ +static int jx9Builtin_pathinfo(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zPath; + path_info sInfo; + SyString *pComp; + int iLen; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return the empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Point to the target path */ + zPath = jx9_value_to_string(apArg[0], &iLen); + if( iLen < 1 ){ + /* Empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Extract path info */ + ExtractPathInfo(zPath, iLen, &sInfo); + if( nArg > 1 && jx9_value_is_int(apArg[1]) ){ + /* Return path component */ + int nComp = jx9_value_to_int(apArg[1]); + switch(nComp){ + case 1: /* PATHINFO_DIRNAME */ + pComp = &sInfo.sDir; + if( pComp->nByte > 0 ){ + jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte); + }else{ + /* Expand the empty string */ + jx9_result_string(pCtx, "", 0); + } + break; + case 2: /*PATHINFO_BASENAME*/ + pComp = &sInfo.sBasename; + if( pComp->nByte > 0 ){ + jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte); + }else{ + /* Expand the empty string */ + jx9_result_string(pCtx, "", 0); + } + break; + case 3: /*PATHINFO_EXTENSION*/ + pComp = &sInfo.sExtension; + if( pComp->nByte > 0 ){ + jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte); + }else{ + /* Expand the empty string */ + jx9_result_string(pCtx, "", 0); + } + break; + case 4: /*PATHINFO_FILENAME*/ + pComp = &sInfo.sFilename; + if( pComp->nByte > 0 ){ + jx9_result_string(pCtx, pComp->zString, (int)pComp->nByte); + }else{ + /* Expand the empty string */ + jx9_result_string(pCtx, "", 0); + } + break; + default: + /* Expand the empty string */ + jx9_result_string(pCtx, "", 0); + break; + } + }else{ + /* Return an associative array */ + jx9_value *pArray, *pValue; + pArray = jx9_context_new_array(pCtx); + pValue = jx9_context_new_scalar(pCtx); + if( pArray == 0 || pValue == 0 ){ + /* Out of mem, return NULL */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* dirname */ + pComp = &sInfo.sDir; + if( pComp->nByte > 0 ){ + jx9_value_string(pValue, pComp->zString, (int)pComp->nByte); + /* Perform the insertion */ + jx9_array_add_strkey_elem(pArray, "dirname", pValue); /* Will make it's own copy */ + } + /* Reset the string cursor */ + jx9_value_reset_string_cursor(pValue); + /* basername */ + pComp = &sInfo.sBasename; + if( pComp->nByte > 0 ){ + jx9_value_string(pValue, pComp->zString, (int)pComp->nByte); + /* Perform the insertion */ + jx9_array_add_strkey_elem(pArray, "basename", pValue); /* Will make it's own copy */ + } + /* Reset the string cursor */ + jx9_value_reset_string_cursor(pValue); + /* extension */ + pComp = &sInfo.sExtension; + if( pComp->nByte > 0 ){ + jx9_value_string(pValue, pComp->zString, (int)pComp->nByte); + /* Perform the insertion */ + jx9_array_add_strkey_elem(pArray, "extension", pValue); /* Will make it's own copy */ + } + /* Reset the string cursor */ + jx9_value_reset_string_cursor(pValue); + /* filename */ + pComp = &sInfo.sFilename; + if( pComp->nByte > 0 ){ + jx9_value_string(pValue, pComp->zString, (int)pComp->nByte); + /* Perform the insertion */ + jx9_array_add_strkey_elem(pArray, "filename", pValue); /* Will make it's own copy */ + } + /* Return the created array */ + jx9_result_value(pCtx, pArray); + /* Don't worry about freeing memory, everything will be released + * automatically as soon we return from this foreign function. + */ + } + return JX9_OK; +} +/* + * Globbing implementation extracted from the sqlite3 source tree. + * Original author: D. Richard Hipp (http://www.sqlite.org) + * Status: Public Domain + */ +typedef unsigned char u8; +/* An array to map all upper-case characters into their corresponding +** lower-case character. +** +** SQLite only considers US-ASCII (or EBCDIC) characters. We do not +** handle case conversions for the UTF character set since the tables +** involved are nearly as big or bigger than SQLite itself. +*/ +static const unsigned char sqlite3UpperToLower[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255 +}; +#define GlogUpperToLower(A) if( A<0x80 ){ A = sqlite3UpperToLower[A]; } +/* +** Assuming zIn points to the first byte of a UTF-8 character, +** advance zIn to point to the first byte of the next UTF-8 character. +*/ +#define SQLITE_SKIP_UTF8(zIn) { \ + if( (*(zIn++))>=0xc0 ){ \ + while( (*zIn & 0xc0)==0x80 ){ zIn++; } \ + } \ +} +/* +** Compare two UTF-8 strings for equality where the first string can +** potentially be a "glob" expression. Return true (1) if they +** are the same and false (0) if they are different. +** +** Globbing rules: +** +** '*' Matches any sequence of zero or more characters. +** +** '?' Matches exactly one character. +** +** [...] Matches one character from the enclosed list of +** characters. +** +** [^...] Matches one character not in the enclosed list. +** +** With the [...] and [^...] matching, a ']' character can be included +** in the list by making it the first character after '[' or '^'. A +** range of characters can be specified using '-'. Example: +** "[a-z]" matches any single lower-case letter. To match a '-', make +** it the last character in the list. +** +** This routine is usually quick, but can be N**2 in the worst case. +** +** Hints: to match '*' or '?', put them in "[]". Like this: +** +** abc[*]xyz Matches "abc*xyz" only +*/ +static int patternCompare( + const u8 *zPattern, /* The glob pattern */ + const u8 *zString, /* The string to compare against the glob */ + const int esc, /* The escape character */ + int noCase +){ + int c, c2; + int invert; + int seen; + u8 matchOne = '?'; + u8 matchAll = '*'; + u8 matchSet = '['; + int prevEscape = 0; /* True if the previous character was 'escape' */ + + if( !zPattern || !zString ) return 0; + while( (c = jx9Utf8Read(zPattern, 0, &zPattern))!=0 ){ + if( !prevEscape && c==matchAll ){ + while( (c= jx9Utf8Read(zPattern, 0, &zPattern)) == matchAll + || c == matchOne ){ + if( c==matchOne && jx9Utf8Read(zString, 0, &zString)==0 ){ + return 0; + } + } + if( c==0 ){ + return 1; + }else if( c==esc ){ + c = jx9Utf8Read(zPattern, 0, &zPattern); + if( c==0 ){ + return 0; + } + }else if( c==matchSet ){ + if( (esc==0) || (matchSet<0x80) ) return 0; + while( *zString && patternCompare(&zPattern[-1], zString, esc, noCase)==0 ){ + SQLITE_SKIP_UTF8(zString); + } + return *zString!=0; + } + while( (c2 = jx9Utf8Read(zString, 0, &zString))!=0 ){ + if( noCase ){ + GlogUpperToLower(c2); + GlogUpperToLower(c); + while( c2 != 0 && c2 != c ){ + c2 = jx9Utf8Read(zString, 0, &zString); + GlogUpperToLower(c2); + } + }else{ + while( c2 != 0 && c2 != c ){ + c2 = jx9Utf8Read(zString, 0, &zString); + } + } + if( c2==0 ) return 0; + if( patternCompare(zPattern, zString, esc, noCase) ) return 1; + } + return 0; + }else if( !prevEscape && c==matchOne ){ + if( jx9Utf8Read(zString, 0, &zString)==0 ){ + return 0; + } + }else if( c==matchSet ){ + int prior_c = 0; + if( esc == 0 ) return 0; + seen = 0; + invert = 0; + c = jx9Utf8Read(zString, 0, &zString); + if( c==0 ) return 0; + c2 = jx9Utf8Read(zPattern, 0, &zPattern); + if( c2=='^' ){ + invert = 1; + c2 = jx9Utf8Read(zPattern, 0, &zPattern); + } + if( c2==']' ){ + if( c==']' ) seen = 1; + c2 = jx9Utf8Read(zPattern, 0, &zPattern); + } + while( c2 && c2!=']' ){ + if( c2=='-' && zPattern[0]!=']' && zPattern[0]!=0 && prior_c>0 ){ + c2 = jx9Utf8Read(zPattern, 0, &zPattern); + if( c>=prior_c && c<=c2 ) seen = 1; + prior_c = 0; + }else{ + if( c==c2 ){ + seen = 1; + } + prior_c = c2; + } + c2 = jx9Utf8Read(zPattern, 0, &zPattern); + } + if( c2==0 || (seen ^ invert)==0 ){ + return 0; + } + }else if( esc==c && !prevEscape ){ + prevEscape = 1; + }else{ + c2 = jx9Utf8Read(zString, 0, &zString); + if( noCase ){ + GlogUpperToLower(c); + GlogUpperToLower(c2); + } + if( c!=c2 ){ + return 0; + } + prevEscape = 0; + } + } + return *zString==0; +} +/* + * Wrapper around patternCompare() defined above. + * See block comment above for more information. + */ +static int Glob(const unsigned char *zPattern, const unsigned char *zString, int iEsc, int CaseCompare) +{ + int rc; + if( iEsc < 0 ){ + iEsc = '\\'; + } + rc = patternCompare(zPattern, zString, iEsc, CaseCompare); + return rc; +} +/* + * bool fnmatch(string $pattern, string $string[, int $flags = 0 ]) + * Match filename against a pattern. + * Parameters + * $pattern + * The shell wildcard pattern. + * $string + * The tested string. + * $flags + * A list of possible flags: + * FNM_NOESCAPE Disable backslash escaping. + * FNM_PATHNAME Slash in string only matches slash in the given pattern. + * FNM_PERIOD Leading period in string must be exactly matched by period in the given pattern. + * FNM_CASEFOLD Caseless match. + * Return + * TRUE if there is a match, FALSE otherwise. + */ +static int jx9Builtin_fnmatch(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zString, *zPattern; + int iEsc = '\\'; + int noCase = 0; + int rc; + if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the pattern and the string */ + zPattern = jx9_value_to_string(apArg[0], 0); + zString = jx9_value_to_string(apArg[1], 0); + /* Extract the flags if avaialble */ + if( nArg > 2 && jx9_value_is_int(apArg[2]) ){ + rc = jx9_value_to_int(apArg[2]); + if( rc & 0x01 /*FNM_NOESCAPE*/){ + iEsc = 0; + } + if( rc & 0x08 /*FNM_CASEFOLD*/){ + noCase = 1; + } + } + /* Go globbing */ + rc = Glob((const unsigned char *)zPattern, (const unsigned char *)zString, iEsc, noCase); + /* Globbing result */ + jx9_result_bool(pCtx, rc); + return JX9_OK; +} +/* + * bool strglob(string $pattern, string $string) + * Match string against a pattern. + * Parameters + * $pattern + * The shell wildcard pattern. + * $string + * The tested string. + * Return + * TRUE if there is a match, FALSE otherwise. + */ +static int jx9Builtin_strglob(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zString, *zPattern; + int iEsc = '\\'; + int rc; + if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the pattern and the string */ + zPattern = jx9_value_to_string(apArg[0], 0); + zString = jx9_value_to_string(apArg[1], 0); + /* Go globbing */ + rc = Glob((const unsigned char *)zPattern, (const unsigned char *)zString, iEsc, 0); + /* Globbing result */ + jx9_result_bool(pCtx, rc); + return JX9_OK; +} +/* + * bool link(string $target, string $link) + * Create a hard link. + * Parameters + * $target + * Target of the link. + * $link + * The link name. + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Vfs_link(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zTarget, *zLink; + jx9_vfs *pVfs; + int rc; + if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xLink == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the given arguments */ + zTarget = jx9_value_to_string(apArg[0], 0); + zLink = jx9_value_to_string(apArg[1], 0); + /* Perform the requested operation */ + rc = pVfs->xLink(zTarget, zLink, 0/*Not a symbolic link */); + /* IO result */ + jx9_result_bool(pCtx, rc == JX9_OK ); + return JX9_OK; +} +/* + * bool symlink(string $target, string $link) + * Creates a symbolic link. + * Parameters + * $target + * Target of the link. + * $link + * The link name. + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Vfs_symlink(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zTarget, *zLink; + jx9_vfs *pVfs; + int rc; + if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xLink == 0 ){ + /* IO routine not implemented, return NULL */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS, JX9 is returning FALSE", + jx9_function_name(pCtx) + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the given arguments */ + zTarget = jx9_value_to_string(apArg[0], 0); + zLink = jx9_value_to_string(apArg[1], 0); + /* Perform the requested operation */ + rc = pVfs->xLink(zTarget, zLink, 1/*A symbolic link */); + /* IO result */ + jx9_result_bool(pCtx, rc == JX9_OK ); + return JX9_OK; +} +/* + * int umask([ int $mask ]) + * Changes the current umask. + * Parameters + * $mask + * The new umask. + * Return + * umask() without arguments simply returns the current umask. + * Otherwise the old umask is returned. + */ +static int jx9Vfs_umask(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int iOld, iNew; + jx9_vfs *pVfs; + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xUmask == 0 ){ + /* IO routine not implemented, return -1 */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS", + jx9_function_name(pCtx) + ); + jx9_result_int(pCtx, 0); + return JX9_OK; + } + iNew = 0; + if( nArg > 0 ){ + iNew = jx9_value_to_int(apArg[0]); + } + /* Perform the requested operation */ + iOld = pVfs->xUmask(iNew); + /* Old mask */ + jx9_result_int(pCtx, iOld); + return JX9_OK; +} +/* + * string sys_get_temp_dir() + * Returns directory path used for temporary files. + * Parameters + * None + * Return + * Returns the path of the temporary directory. + */ +static int jx9Vfs_sys_get_temp_dir(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + jx9_vfs *pVfs; + /* Set the empty string as the default return value */ + jx9_result_string(pCtx, "", 0); + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xTempDir == 0 ){ + SXUNUSED(nArg); /* cc warning */ + SXUNUSED(apArg); + /* IO routine not implemented, return "" */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS", + jx9_function_name(pCtx) + ); + return JX9_OK; + } + /* Perform the requested operation */ + pVfs->xTempDir(pCtx); + return JX9_OK; +} +/* + * string get_current_user() + * Returns the name of the current working user. + * Parameters + * None + * Return + * Returns the name of the current working user. + */ +static int jx9Vfs_get_current_user(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + jx9_vfs *pVfs; + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xUsername == 0 ){ + SXUNUSED(nArg); /* cc warning */ + SXUNUSED(apArg); + /* IO routine not implemented */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS", + jx9_function_name(pCtx) + ); + /* Set a dummy username */ + jx9_result_string(pCtx, "unknown", sizeof("unknown")-1); + return JX9_OK; + } + /* Perform the requested operation */ + pVfs->xUsername(pCtx); + return JX9_OK; +} +/* + * int64 getmypid() + * Gets process ID. + * Parameters + * None + * Return + * Returns the process ID. + */ +static int jx9Vfs_getmypid(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + jx9_int64 nProcessId; + jx9_vfs *pVfs; + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xProcessId == 0 ){ + SXUNUSED(nArg); /* cc warning */ + SXUNUSED(apArg); + /* IO routine not implemented, return -1 */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS", + jx9_function_name(pCtx) + ); + jx9_result_int(pCtx, -1); + return JX9_OK; + } + /* Perform the requested operation */ + nProcessId = (jx9_int64)pVfs->xProcessId(); + /* Set the result */ + jx9_result_int64(pCtx, nProcessId); + return JX9_OK; +} +/* + * int getmyuid() + * Get user ID. + * Parameters + * None + * Return + * Returns the user ID. + */ +static int jx9Vfs_getmyuid(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + jx9_vfs *pVfs; + int nUid; + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xUid == 0 ){ + SXUNUSED(nArg); /* cc warning */ + SXUNUSED(apArg); + /* IO routine not implemented, return -1 */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS", + jx9_function_name(pCtx) + ); + jx9_result_int(pCtx, -1); + return JX9_OK; + } + /* Perform the requested operation */ + nUid = pVfs->xUid(); + /* Set the result */ + jx9_result_int(pCtx, nUid); + return JX9_OK; +} +/* + * int getmygid() + * Get group ID. + * Parameters + * None + * Return + * Returns the group ID. + */ +static int jx9Vfs_getmygid(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + jx9_vfs *pVfs; + int nGid; + /* Point to the underlying vfs */ + pVfs = (jx9_vfs *)jx9_context_user_data(pCtx); + if( pVfs == 0 || pVfs->xGid == 0 ){ + SXUNUSED(nArg); /* cc warning */ + SXUNUSED(apArg); + /* IO routine not implemented, return -1 */ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying VFS", + jx9_function_name(pCtx) + ); + jx9_result_int(pCtx, -1); + return JX9_OK; + } + /* Perform the requested operation */ + nGid = pVfs->xGid(); + /* Set the result */ + jx9_result_int(pCtx, nGid); + return JX9_OK; +} +#ifdef __WINNT__ +#include +#elif defined(__UNIXES__) +#include +#endif +/* + * string uname([ string $mode = "a" ]) + * Returns information about the host operating system. + * Parameters + * $mode + * mode is a single character that defines what information is returned: + * 'a': This is the default. Contains all modes in the sequence "s n r v m". + * 's': Operating system name. eg. FreeBSD. + * 'n': Host name. eg. localhost.example.com. + * 'r': Release name. eg. 5.1.2-RELEASE. + * 'v': Version information. Varies a lot between operating systems. + * 'm': Machine type. eg. i386. + * Return + * OS description as a string. + */ +static int jx9Vfs_uname(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ +#if defined(__WINNT__) + const char *zName = "Microsoft Windows"; + OSVERSIONINFOW sVer; +#elif defined(__UNIXES__) + struct utsname sName; +#endif + const char *zMode = "a"; + if( nArg > 0 && jx9_value_is_string(apArg[0]) ){ + /* Extract the desired mode */ + zMode = jx9_value_to_string(apArg[0], 0); + } +#if defined(__WINNT__) + sVer.dwOSVersionInfoSize = sizeof(sVer); + if( TRUE != GetVersionExW(&sVer)){ + jx9_result_string(pCtx, zName, -1); + return JX9_OK; + } + if( sVer.dwPlatformId == VER_PLATFORM_WIN32_NT ){ + if( sVer.dwMajorVersion <= 4 ){ + zName = "Microsoft Windows NT"; + }else if( sVer.dwMajorVersion == 5 ){ + switch(sVer.dwMinorVersion){ + case 0: zName = "Microsoft Windows 2000"; break; + case 1: zName = "Microsoft Windows XP"; break; + case 2: zName = "Microsoft Windows Server 2003"; break; + } + }else if( sVer.dwMajorVersion == 6){ + switch(sVer.dwMinorVersion){ + case 0: zName = "Microsoft Windows Vista"; break; + case 1: zName = "Microsoft Windows 7"; break; + case 2: zName = "Microsoft Windows 8"; break; + default: break; + } + } + } + switch(zMode[0]){ + case 's': + /* Operating system name */ + jx9_result_string(pCtx, zName, -1/* Compute length automatically*/); + break; + case 'n': + /* Host name */ + jx9_result_string(pCtx, "localhost", (int)sizeof("localhost")-1); + break; + case 'r': + case 'v': + /* Version information. */ + jx9_result_string_format(pCtx, "%u.%u build %u", + sVer.dwMajorVersion, sVer.dwMinorVersion, sVer.dwBuildNumber + ); + break; + case 'm': + /* Machine name */ + jx9_result_string(pCtx, "x86", (int)sizeof("x86")-1); + break; + default: + jx9_result_string_format(pCtx, "%s localhost %u.%u build %u x86", + zName, + sVer.dwMajorVersion, sVer.dwMinorVersion, sVer.dwBuildNumber + ); + break; + } +#elif defined(__UNIXES__) + if( uname(&sName) != 0 ){ + jx9_result_string(pCtx, "Unix", (int)sizeof("Unix")-1); + return JX9_OK; + } + switch(zMode[0]){ + case 's': + /* Operating system name */ + jx9_result_string(pCtx, sName.sysname, -1/* Compute length automatically*/); + break; + case 'n': + /* Host name */ + jx9_result_string(pCtx, sName.nodename, -1/* Compute length automatically*/); + break; + case 'r': + /* Release information */ + jx9_result_string(pCtx, sName.release, -1/* Compute length automatically*/); + break; + case 'v': + /* Version information. */ + jx9_result_string(pCtx, sName.version, -1/* Compute length automatically*/); + break; + case 'm': + /* Machine name */ + jx9_result_string(pCtx, sName.machine, -1/* Compute length automatically*/); + break; + default: + jx9_result_string_format(pCtx, + "%s %s %s %s %s", + sName.sysname, + sName.release, + sName.version, + sName.nodename, + sName.machine + ); + break; + } +#else + jx9_result_string(pCtx, "Host Operating System/localhost", (int)sizeof("Host Operating System/localhost")-1); +#endif + return JX9_OK; +} +/* + * Section: + * IO stream implementation. + * Authors: + * Symisc Systems, devel@symisc.net. + * Copyright (C) Symisc Systems, http://jx9.symisc.net + * Status: + * Stable. + */ +typedef struct io_private io_private; +struct io_private +{ + const jx9_io_stream *pStream; /* Underlying IO device */ + void *pHandle; /* IO handle */ + /* Unbuffered IO */ + SyBlob sBuffer; /* Working buffer */ + sxu32 nOfft; /* Current read offset */ + sxu32 iMagic; /* Sanity check to avoid misuse */ +}; +#define IO_PRIVATE_MAGIC 0xFEAC14 +/* Make sure we are dealing with a valid io_private instance */ +#define IO_PRIVATE_INVALID(IO) ( IO == 0 || IO->iMagic != IO_PRIVATE_MAGIC ) +/* Forward declaration */ +static void ResetIOPrivate(io_private *pDev); +/* + * bool ftruncate(resource $handle, int64 $size) + * Truncates a file to a given length. + * Parameters + * $handle + * The file pointer. + * Note: + * The handle must be open for writing. + * $size + * The size to truncate to. + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Builtin_ftruncate(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + io_private *pDev; + int rc; + if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract our private data */ + pDev = (io_private *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid io_private instance */ + if( IO_PRIVATE_INVALID(pDev) ){ + /*Expecting an IO handle */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the target IO stream device */ + pStream = pDev->pStream; + if( pStream == 0 || pStream->xTrunc == 0){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", + jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + rc = pStream->xTrunc(pDev->pHandle, jx9_value_to_int64(apArg[1])); + if( rc == JX9_OK ){ + /* Discard buffered data */ + ResetIOPrivate(pDev); + } + /* IO result */ + jx9_result_bool(pCtx, rc == JX9_OK); + return JX9_OK; +} +/* + * int fseek(resource $handle, int $offset[, int $whence = SEEK_SET ]) + * Seeks on a file pointer. + * Parameters + * $handle + * A file system pointer resource that is typically created using fopen(). + * $offset + * The offset. + * To move to a position before the end-of-file, you need to pass a negative + * value in offset and set whence to SEEK_END. + * whence + * whence values are: + * SEEK_SET - Set position equal to offset bytes. + * SEEK_CUR - Set position to current location plus offset. + * SEEK_END - Set position to end-of-file plus offset. + * Return + * 0 on success, -1 on failure + */ +static int jx9Builtin_fseek(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + io_private *pDev; + jx9_int64 iOfft; + int whence; + int rc; + if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_int(pCtx, -1); + return JX9_OK; + } + /* Extract our private data */ + pDev = (io_private *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid io_private instance */ + if( IO_PRIVATE_INVALID(pDev) ){ + /*Expecting an IO handle */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_int(pCtx, -1); + return JX9_OK; + } + /* Point to the target IO stream device */ + pStream = pDev->pStream; + if( pStream == 0 || pStream->xSeek == 0){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device", + jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" + ); + jx9_result_int(pCtx, -1); + return JX9_OK; + } + /* Extract the offset */ + iOfft = jx9_value_to_int64(apArg[1]); + whence = 0;/* SEEK_SET */ + if( nArg > 2 && jx9_value_is_int(apArg[2]) ){ + whence = jx9_value_to_int(apArg[2]); + } + /* Perform the requested operation */ + rc = pStream->xSeek(pDev->pHandle, iOfft, whence); + if( rc == JX9_OK ){ + /* Ignore buffered data */ + ResetIOPrivate(pDev); + } + /* IO result */ + jx9_result_int(pCtx, rc == JX9_OK ? 0 : - 1); + return JX9_OK; +} +/* + * int64 ftell(resource $handle) + * Returns the current position of the file read/write pointer. + * Parameters + * $handle + * The file pointer. + * Return + * Returns the position of the file pointer referenced by handle + * as an integer; i.e., its offset into the file stream. + * FALSE is returned on failure. + */ +static int jx9Builtin_ftell(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + io_private *pDev; + jx9_int64 iOfft; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract our private data */ + pDev = (io_private *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid io_private instance */ + if( IO_PRIVATE_INVALID(pDev) ){ + /*Expecting an IO handle */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the target IO stream device */ + pStream = pDev->pStream; + if( pStream == 0 || pStream->xTell == 0){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", + jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + iOfft = pStream->xTell(pDev->pHandle); + /* IO result */ + jx9_result_int64(pCtx, iOfft); + return JX9_OK; +} +/* + * bool rewind(resource $handle) + * Rewind the position of a file pointer. + * Parameters + * $handle + * The file pointer. + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Builtin_rewind(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + io_private *pDev; + int rc; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract our private data */ + pDev = (io_private *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid io_private instance */ + if( IO_PRIVATE_INVALID(pDev) ){ + /*Expecting an IO handle */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the target IO stream device */ + pStream = pDev->pStream; + if( pStream == 0 || pStream->xSeek == 0){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", + jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + rc = pStream->xSeek(pDev->pHandle, 0, 0/*SEEK_SET*/); + if( rc == JX9_OK ){ + /* Ignore buffered data */ + ResetIOPrivate(pDev); + } + /* IO result */ + jx9_result_bool(pCtx, rc == JX9_OK); + return JX9_OK; +} +/* + * bool fflush(resource $handle) + * Flushes the output to a file. + * Parameters + * $handle + * The file pointer. + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Builtin_fflush(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + io_private *pDev; + int rc; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract our private data */ + pDev = (io_private *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid io_private instance */ + if( IO_PRIVATE_INVALID(pDev) ){ + /*Expecting an IO handle */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the target IO stream device */ + pStream = pDev->pStream; + if( pStream == 0 || pStream->xSync == 0){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", + jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + rc = pStream->xSync(pDev->pHandle); + /* IO result */ + jx9_result_bool(pCtx, rc == JX9_OK); + return JX9_OK; +} +/* + * bool feof(resource $handle) + * Tests for end-of-file on a file pointer. + * Parameters + * $handle + * The file pointer. + * Return + * Returns TRUE if the file pointer is at EOF.FALSE otherwise + */ +static int jx9Builtin_feof(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + io_private *pDev; + int rc; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 1); + return JX9_OK; + } + /* Extract our private data */ + pDev = (io_private *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid io_private instance */ + if( IO_PRIVATE_INVALID(pDev) ){ + /*Expecting an IO handle */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 1); + return JX9_OK; + } + /* Point to the target IO stream device */ + pStream = pDev->pStream; + if( pStream == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", + jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" + ); + jx9_result_bool(pCtx, 1); + return JX9_OK; + } + rc = SXERR_EOF; + /* Perform the requested operation */ + if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){ + /* Data is available */ + rc = JX9_OK; + }else{ + char zBuf[4096]; + jx9_int64 n; + /* Perform a buffered read */ + n = pStream->xRead(pDev->pHandle, zBuf, sizeof(zBuf)); + if( n > 0 ){ + /* Copy buffered data */ + SyBlobAppend(&pDev->sBuffer, zBuf, (sxu32)n); + rc = JX9_OK; + } + } + /* EOF or not */ + jx9_result_bool(pCtx, rc == SXERR_EOF); + return JX9_OK; +} +/* + * Read n bytes from the underlying IO stream device. + * Return total numbers of bytes readen on success. A number < 1 on failure + * [i.e: IO error ] or EOF. + */ +static jx9_int64 StreamRead(io_private *pDev, void *pBuf, jx9_int64 nLen) +{ + const jx9_io_stream *pStream = pDev->pStream; + char *zBuf = (char *)pBuf; + jx9_int64 n, nRead; + n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft; + if( n > 0 ){ + if( n > nLen ){ + n = nLen; + } + /* Copy the buffered data */ + SyMemcpy(SyBlobDataAt(&pDev->sBuffer, pDev->nOfft), pBuf, (sxu32)n); + /* Update the read offset */ + pDev->nOfft += (sxu32)n; + if( pDev->nOfft >= SyBlobLength(&pDev->sBuffer) ){ + /* Reset the working buffer so that we avoid excessive memory allocation */ + SyBlobReset(&pDev->sBuffer); + pDev->nOfft = 0; + } + nLen -= n; + if( nLen < 1 ){ + /* All done */ + return n; + } + /* Advance the cursor */ + zBuf += n; + } + /* Read without buffering */ + nRead = pStream->xRead(pDev->pHandle, zBuf, nLen); + if( nRead > 0 ){ + n += nRead; + }else if( n < 1 ){ + /* EOF or IO error */ + return nRead; + } + return n; +} +/* + * Extract a single line from the buffered input. + */ +static sxi32 GetLine(io_private *pDev, jx9_int64 *pLen, const char **pzLine) +{ + const char *zIn, *zEnd, *zPtr; + zIn = (const char *)SyBlobDataAt(&pDev->sBuffer, pDev->nOfft); + zEnd = &zIn[SyBlobLength(&pDev->sBuffer)-pDev->nOfft]; + zPtr = zIn; + while( zIn < zEnd ){ + if( zIn[0] == '\n' ){ + /* Line found */ + zIn++; /* Include the line ending as requested by the JX9 specification */ + *pLen = (jx9_int64)(zIn-zPtr); + *pzLine = zPtr; + return SXRET_OK; + } + zIn++; + } + /* No line were found */ + return SXERR_NOTFOUND; +} +/* + * Read a single line from the underlying IO stream device. + */ +static jx9_int64 StreamReadLine(io_private *pDev, const char **pzData, jx9_int64 nMaxLen) +{ + const jx9_io_stream *pStream = pDev->pStream; + char zBuf[8192]; + jx9_int64 n; + sxi32 rc; + n = 0; + if( pDev->nOfft >= SyBlobLength(&pDev->sBuffer) ){ + /* Reset the working buffer so that we avoid excessive memory allocation */ + SyBlobReset(&pDev->sBuffer); + pDev->nOfft = 0; + } + if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){ + /* Check if there is a line */ + rc = GetLine(pDev, &n, pzData); + if( rc == SXRET_OK ){ + /* Got line, update the cursor */ + pDev->nOfft += (sxu32)n; + return n; + } + } + /* Perform the read operation until a new line is extracted or length + * limit is reached. + */ + for(;;){ + n = pStream->xRead(pDev->pHandle, zBuf, (nMaxLen > 0 && nMaxLen < sizeof(zBuf)) ? nMaxLen : sizeof(zBuf)); + if( n < 1 ){ + /* EOF or IO error */ + break; + } + /* Append the data just read */ + SyBlobAppend(&pDev->sBuffer, zBuf, (sxu32)n); + /* Try to extract a line */ + rc = GetLine(pDev, &n, pzData); + if( rc == SXRET_OK ){ + /* Got one, return immediately */ + pDev->nOfft += (sxu32)n; + return n; + } + if( nMaxLen > 0 && (SyBlobLength(&pDev->sBuffer) - pDev->nOfft >= nMaxLen) ){ + /* Read limit reached, return the available data */ + *pzData = (const char *)SyBlobDataAt(&pDev->sBuffer, pDev->nOfft); + n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft; + /* Reset the working buffer */ + SyBlobReset(&pDev->sBuffer); + pDev->nOfft = 0; + return n; + } + } + if( SyBlobLength(&pDev->sBuffer) - pDev->nOfft > 0 ){ + /* Read limit reached, return the available data */ + *pzData = (const char *)SyBlobDataAt(&pDev->sBuffer, pDev->nOfft); + n = SyBlobLength(&pDev->sBuffer) - pDev->nOfft; + /* Reset the working buffer */ + SyBlobReset(&pDev->sBuffer); + pDev->nOfft = 0; + } + return n; +} +/* + * Open an IO stream handle. + * Notes on stream: + * According to the JX9 reference manual. + * In its simplest definition, a stream is a resource object which exhibits streamable behavior. + * That is, it can be read from or written to in a linear fashion, and may be able to fseek() + * to an arbitrary locations within the stream. + * A wrapper is additional code which tells the stream how to handle specific protocols/encodings. + * For example, the http wrapper knows how to translate a URL into an HTTP/1.0 request for a file + * on a remote server. + * A stream is referenced as: scheme://target + * scheme(string) - The name of the wrapper to be used. Examples include: file, http... + * If no wrapper is specified, the function default is used (typically file://). + * target - Depends on the wrapper used. For filesystem related streams this is typically a path + * and filename of the desired file. For network related streams this is typically a hostname, often + * with a path appended. + * + * Note that JX9 IO streams looks like JX9 streams but their implementation differ greately. + * Please refer to the official documentation for a full discussion. + * This function return a handle on success. Otherwise null. + */ +JX9_PRIVATE void * jx9StreamOpenHandle(jx9_vm *pVm, const jx9_io_stream *pStream, const char *zFile, + int iFlags, int use_include, jx9_value *pResource, int bPushInclude, int *pNew) +{ + void *pHandle = 0; /* cc warning */ + SyString sFile; + int rc; + if( pStream == 0 ){ + /* No such stream device */ + return 0; + } + SyStringInitFromBuf(&sFile, zFile, SyStrlen(zFile)); + if( use_include ){ + if( sFile.zString[0] == '/' || +#ifdef __WINNT__ + (sFile.nByte > 2 && sFile.zString[1] == ':' && (sFile.zString[2] == '\\' || sFile.zString[2] == '/') ) || +#endif + (sFile.nByte > 1 && sFile.zString[0] == '.' && sFile.zString[1] == '/') || + (sFile.nByte > 2 && sFile.zString[0] == '.' && sFile.zString[1] == '.' && sFile.zString[2] == '/') ){ + /* Open the file directly */ + rc = pStream->xOpen(zFile, iFlags, pResource, &pHandle); + }else{ + SyString *pPath; + SyBlob sWorker; +#ifdef __WINNT__ + static const int c = '\\'; +#else + static const int c = '/'; +#endif + /* Init the path builder working buffer */ + SyBlobInit(&sWorker, &pVm->sAllocator); + /* Build a path from the set of include path */ + SySetResetCursor(&pVm->aPaths); + rc = SXERR_IO; + while( SXRET_OK == SySetGetNextEntry(&pVm->aPaths, (void **)&pPath) ){ + /* Build full path */ + SyBlobFormat(&sWorker, "%z%c%z", pPath, c, &sFile); + /* Append null terminator */ + if( SXRET_OK != SyBlobNullAppend(&sWorker) ){ + continue; + } + /* Try to open the file */ + rc = pStream->xOpen((const char *)SyBlobData(&sWorker), iFlags, pResource, &pHandle); + if( rc == JX9_OK ){ + if( bPushInclude ){ + /* Mark as included */ + jx9VmPushFilePath(pVm, (const char *)SyBlobData(&sWorker), SyBlobLength(&sWorker), FALSE, pNew); + } + break; + } + /* Reset the working buffer */ + SyBlobReset(&sWorker); + /* Check the next path */ + } + SyBlobRelease(&sWorker); + } + if( rc == JX9_OK ){ + if( bPushInclude ){ + /* Mark as included */ + jx9VmPushFilePath(pVm, sFile.zString, sFile.nByte, FALSE, pNew); + } + } + }else{ + /* Open the URI direcly */ + rc = pStream->xOpen(zFile, iFlags, pResource, &pHandle); + } + if( rc != JX9_OK ){ + /* IO error */ + return 0; + } + /* Return the file handle */ + return pHandle; +} +/* + * Read the whole contents of an open IO stream handle [i.e local file/URL..] + * Store the read data in the given BLOB (last argument). + * The read operation is stopped when he hit the EOF or an IO error occurs. + */ +JX9_PRIVATE sxi32 jx9StreamReadWholeFile(void *pHandle, const jx9_io_stream *pStream, SyBlob *pOut) +{ + jx9_int64 nRead; + char zBuf[8192]; /* 8K */ + int rc; + /* Perform the requested operation */ + for(;;){ + nRead = pStream->xRead(pHandle, zBuf, sizeof(zBuf)); + if( nRead < 1 ){ + /* EOF or IO error */ + break; + } + /* Append contents */ + rc = SyBlobAppend(pOut, zBuf, (sxu32)nRead); + if( rc != SXRET_OK ){ + break; + } + } + return SyBlobLength(pOut) > 0 ? SXRET_OK : -1; +} +/* + * Close an open IO stream handle [i.e local file/URI..]. + */ +JX9_PRIVATE void jx9StreamCloseHandle(const jx9_io_stream *pStream, void *pHandle) +{ + if( pStream->xClose ){ + pStream->xClose(pHandle); + } +} +/* + * string fgetc(resource $handle) + * Gets a character from the given file pointer. + * Parameters + * $handle + * The file pointer. + * Return + * Returns a string containing a single character read from the file + * pointed to by handle. Returns FALSE on EOF. + * WARNING + * This operation is extremely slow.Avoid using it. + */ +static int jx9Builtin_fgetc(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + io_private *pDev; + int c, n; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract our private data */ + pDev = (io_private *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid io_private instance */ + if( IO_PRIVATE_INVALID(pDev) ){ + /*Expecting an IO handle */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the target IO stream device */ + pStream = pDev->pStream; + if( pStream == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", + jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + n = (int)StreamRead(pDev, (void *)&c, sizeof(char)); + /* IO result */ + if( n < 1 ){ + /* EOF or error, return FALSE */ + jx9_result_bool(pCtx, 0); + }else{ + /* Return the string holding the character */ + jx9_result_string(pCtx, (const char *)&c, sizeof(char)); + } + return JX9_OK; +} +/* + * string fgets(resource $handle[, int64 $length ]) + * Gets line from file pointer. + * Parameters + * $handle + * The file pointer. + * $length + * Reading ends when length - 1 bytes have been read, on a newline + * (which is included in the return value), or on EOF (whichever comes first). + * If no length is specified, it will keep reading from the stream until it reaches + * the end of the line. + * Return + * Returns a string of up to length - 1 bytes read from the file pointed to by handle. + * If there is no more data to read in the file pointer, then FALSE is returned. + * If an error occurs, FALSE is returned. + */ +static int jx9Builtin_fgets(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + const char *zLine; + io_private *pDev; + jx9_int64 n, nLen; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract our private data */ + pDev = (io_private *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid io_private instance */ + if( IO_PRIVATE_INVALID(pDev) ){ + /*Expecting an IO handle */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the target IO stream device */ + pStream = pDev->pStream; + if( pStream == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", + jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + nLen = -1; + if( nArg > 1 ){ + /* Maximum data to read */ + nLen = jx9_value_to_int64(apArg[1]); + } + /* Perform the requested operation */ + n = StreamReadLine(pDev, &zLine, nLen); + if( n < 1 ){ + /* EOF or IO error, return FALSE */ + jx9_result_bool(pCtx, 0); + }else{ + /* Return the freshly extracted line */ + jx9_result_string(pCtx, zLine, (int)n); + } + return JX9_OK; +} +/* + * string fread(resource $handle, int64 $length) + * Binary-safe file read. + * Parameters + * $handle + * The file pointer. + * $length + * Up to length number of bytes read. + * Return + * The data readen on success or FALSE on failure. + */ +static int jx9Builtin_fread(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + io_private *pDev; + jx9_int64 nRead; + void *pBuf; + int nLen; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract our private data */ + pDev = (io_private *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid io_private instance */ + if( IO_PRIVATE_INVALID(pDev) ){ + /*Expecting an IO handle */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the target IO stream device */ + pStream = pDev->pStream; + if( pStream == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", + jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + nLen = 4096; + if( nArg > 1 ){ + nLen = jx9_value_to_int(apArg[1]); + if( nLen < 1 ){ + /* Invalid length, set a default length */ + nLen = 4096; + } + } + /* Allocate enough buffer */ + pBuf = jx9_context_alloc_chunk(pCtx, (unsigned int)nLen, FALSE, FALSE); + if( pBuf == 0 ){ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + nRead = StreamRead(pDev, pBuf, (jx9_int64)nLen); + if( nRead < 1 ){ + /* Nothing read, return FALSE */ + jx9_result_bool(pCtx, 0); + }else{ + /* Make a copy of the data just read */ + jx9_result_string(pCtx, (const char *)pBuf, (int)nRead); + } + /* Release the buffer */ + jx9_context_free_chunk(pCtx, pBuf); + return JX9_OK; +} +/* + * array fgetcsv(resource $handle [, int $length = 0 + * [, string $delimiter = ', '[, string $enclosure = '"'[, string $escape='\\']]]]) + * Gets line from file pointer and parse for CSV fields. + * Parameters + * $handle + * The file pointer. + * $length + * Reading ends when length - 1 bytes have been read, on a newline + * (which is included in the return value), or on EOF (whichever comes first). + * If no length is specified, it will keep reading from the stream until it reaches + * the end of the line. + * $delimiter + * Set the field delimiter (one character only). + * $enclosure + * Set the field enclosure character (one character only). + * $escape + * Set the escape character (one character only). Defaults as a backslash (\) + * Return + * Returns a string of up to length - 1 bytes read from the file pointed to by handle. + * If there is no more data to read in the file pointer, then FALSE is returned. + * If an error occurs, FALSE is returned. + */ +static int jx9Builtin_fgetcsv(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + const char *zLine; + io_private *pDev; + jx9_int64 n, nLen; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract our private data */ + pDev = (io_private *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid io_private instance */ + if( IO_PRIVATE_INVALID(pDev) ){ + /*Expecting an IO handle */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the target IO stream device */ + pStream = pDev->pStream; + if( pStream == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", + jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + nLen = -1; + if( nArg > 1 ){ + /* Maximum data to read */ + nLen = jx9_value_to_int64(apArg[1]); + } + /* Perform the requested operation */ + n = StreamReadLine(pDev, &zLine, nLen); + if( n < 1 ){ + /* EOF or IO error, return FALSE */ + jx9_result_bool(pCtx, 0); + }else{ + jx9_value *pArray; + int delim = ','; /* Delimiter */ + int encl = '"' ; /* Enclosure */ + int escape = '\\'; /* Escape character */ + if( nArg > 2 ){ + const char *zPtr; + int i; + if( jx9_value_is_string(apArg[2]) ){ + /* Extract the delimiter */ + zPtr = jx9_value_to_string(apArg[2], &i); + if( i > 0 ){ + delim = zPtr[0]; + } + } + if( nArg > 3 ){ + if( jx9_value_is_string(apArg[3]) ){ + /* Extract the enclosure */ + zPtr = jx9_value_to_string(apArg[3], &i); + if( i > 0 ){ + encl = zPtr[0]; + } + } + if( nArg > 4 ){ + if( jx9_value_is_string(apArg[4]) ){ + /* Extract the escape character */ + zPtr = jx9_value_to_string(apArg[4], &i); + if( i > 0 ){ + escape = zPtr[0]; + } + } + } + } + } + /* Create our array */ + pArray = jx9_context_new_array(pCtx); + if( pArray == 0 ){ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); + jx9_result_null(pCtx); + return JX9_OK; + } + /* Parse the raw input */ + jx9ProcessCsv(zLine, (int)n, delim, encl, escape, jx9CsvConsumer, pArray); + /* Return the freshly created array */ + jx9_result_value(pCtx, pArray); + } + return JX9_OK; +} +/* + * string fgetss(resource $handle [, int $length [, string $allowable_tags ]]) + * Gets line from file pointer and strip HTML tags. + * Parameters + * $handle + * The file pointer. + * $length + * Reading ends when length - 1 bytes have been read, on a newline + * (which is included in the return value), or on EOF (whichever comes first). + * If no length is specified, it will keep reading from the stream until it reaches + * the end of the line. + * $allowable_tags + * You can use the optional second parameter to specify tags which should not be stripped. + * Return + * Returns a string of up to length - 1 bytes read from the file pointed to by + * handle, with all HTML and JX9 code stripped. If an error occurs, returns FALSE. + */ +static int jx9Builtin_fgetss(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + const char *zLine; + io_private *pDev; + jx9_int64 n, nLen; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract our private data */ + pDev = (io_private *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid io_private instance */ + if( IO_PRIVATE_INVALID(pDev) ){ + /*Expecting an IO handle */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the target IO stream device */ + pStream = pDev->pStream; + if( pStream == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", + jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + nLen = -1; + if( nArg > 1 ){ + /* Maximum data to read */ + nLen = jx9_value_to_int64(apArg[1]); + } + /* Perform the requested operation */ + n = StreamReadLine(pDev, &zLine, nLen); + if( n < 1 ){ + /* EOF or IO error, return FALSE */ + jx9_result_bool(pCtx, 0); + }else{ + const char *zTaglist = 0; + int nTaglen = 0; + if( nArg > 2 && jx9_value_is_string(apArg[2]) ){ + /* Allowed tag */ + zTaglist = jx9_value_to_string(apArg[2], &nTaglen); + } + /* Process data just read */ + jx9StripTagsFromString(pCtx, zLine, (int)n, zTaglist, nTaglen); + } + return JX9_OK; +} +/* + * string readdir(resource $dir_handle) + * Read entry from directory handle. + * Parameter + * $dir_handle + * The directory handle resource previously opened with opendir(). + * Return + * Returns the filename on success or FALSE on failure. + */ +static int jx9Builtin_readdir(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + io_private *pDev; + int rc; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract our private data */ + pDev = (io_private *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid io_private instance */ + if( IO_PRIVATE_INVALID(pDev) ){ + /*Expecting an IO handle */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the target IO stream device */ + pStream = pDev->pStream; + if( pStream == 0 || pStream->xReadDir == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", + jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + jx9_result_bool(pCtx, 0); + /* Perform the requested operation */ + rc = pStream->xReadDir(pDev->pHandle, pCtx); + if( rc != JX9_OK ){ + /* Return FALSE */ + jx9_result_bool(pCtx, 0); + } + return JX9_OK; +} +/* + * void rewinddir(resource $dir_handle) + * Rewind directory handle. + * Parameter + * $dir_handle + * The directory handle resource previously opened with opendir(). + * Return + * FALSE on failure. + */ +static int jx9Builtin_rewinddir(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + io_private *pDev; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract our private data */ + pDev = (io_private *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid io_private instance */ + if( IO_PRIVATE_INVALID(pDev) ){ + /*Expecting an IO handle */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the target IO stream device */ + pStream = pDev->pStream; + if( pStream == 0 || pStream->xRewindDir == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", + jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + pStream->xRewindDir(pDev->pHandle); + return JX9_OK; + } +/* Forward declaration */ +static void InitIOPrivate(jx9_vm *pVm, const jx9_io_stream *pStream, io_private *pOut); +static void ReleaseIOPrivate(jx9_context *pCtx, io_private *pDev); +/* + * void closedir(resource $dir_handle) + * Close directory handle. + * Parameter + * $dir_handle + * The directory handle resource previously opened with opendir(). + * Return + * FALSE on failure. + */ +static int jx9Builtin_closedir(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + io_private *pDev; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract our private data */ + pDev = (io_private *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid io_private instance */ + if( IO_PRIVATE_INVALID(pDev) ){ + /*Expecting an IO handle */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the target IO stream device */ + pStream = pDev->pStream; + if( pStream == 0 || pStream->xCloseDir == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", + jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + pStream->xCloseDir(pDev->pHandle); + /* Release the private stucture */ + ReleaseIOPrivate(pCtx, pDev); + jx9MemObjRelease(apArg[0]); + return JX9_OK; + } +/* + * resource opendir(string $path[, resource $context]) + * Open directory handle. + * Parameters + * $path + * The directory path that is to be opened. + * $context + * A context stream resource. + * Return + * A directory handle resource on success, or FALSE on failure. + */ +static int jx9Builtin_opendir(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + const char *zPath; + io_private *pDev; + int iLen, rc; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a directory path"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the target path */ + zPath = jx9_value_to_string(apArg[0], &iLen); + /* Try to extract a stream */ + pStream = jx9VmGetStreamDevice(pCtx->pVm, &zPath, iLen); + if( pStream == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "No stream device is associated with the given path(%s)", zPath); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + if( pStream->xOpenDir == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device", + jx9_function_name(pCtx), pStream->zName + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Allocate a new IO private instance */ + pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE); + if( pDev == 0 ){ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Initialize the structure */ + InitIOPrivate(pCtx->pVm, pStream, pDev); + /* Open the target directory */ + rc = pStream->xOpenDir(zPath, nArg > 1 ? apArg[1] : 0, &pDev->pHandle); + if( rc != JX9_OK ){ + /* IO error, return FALSE */ + ReleaseIOPrivate(pCtx, pDev); + jx9_result_bool(pCtx, 0); + }else{ + /* Return the handle as a resource */ + jx9_result_resource(pCtx, pDev); + } + return JX9_OK; +} +/* + * int readfile(string $filename[, bool $use_include_path = false [, resource $context ]]) + * Reads a file and writes it to the output buffer. + * Parameters + * $filename + * The filename being read. + * $use_include_path + * You can use the optional second parameter and set it to + * TRUE, if you want to search for the file in the include_path, too. + * $context + * A context stream resource. + * Return + * The number of bytes read from the file on success or FALSE on failure. + */ +static int jx9Builtin_readfile(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int use_include = FALSE; + const jx9_io_stream *pStream; + jx9_int64 n, nRead; + const char *zFile; + char zBuf[8192]; + void *pHandle; + int rc, nLen; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the file path */ + zFile = jx9_value_to_string(apArg[0], &nLen); + /* Point to the target IO stream device */ + pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen); + if( pStream == 0 ){ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + if( nArg > 1 ){ + use_include = jx9_value_to_bool(apArg[1]); + } + /* Try to open the file in read-only mode */ + pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, + use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0); + if( pHandle == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + nRead = 0; + for(;;){ + n = pStream->xRead(pHandle, zBuf, sizeof(zBuf)); + if( n < 1 ){ + /* EOF or IO error, break immediately */ + break; + } + /* Output data */ + rc = jx9_context_output(pCtx, zBuf, (int)n); + if( rc == JX9_ABORT ){ + break; + } + /* Increment counter */ + nRead += n; + } + /* Close the stream */ + jx9StreamCloseHandle(pStream, pHandle); + /* Total number of bytes readen */ + jx9_result_int64(pCtx, nRead); + return JX9_OK; +} +/* + * string file_get_contents(string $filename[, bool $use_include_path = false + * [, resource $context [, int $offset = -1 [, int $maxlen ]]]]) + * Reads entire file into a string. + * Parameters + * $filename + * The filename being read. + * $use_include_path + * You can use the optional second parameter and set it to + * TRUE, if you want to search for the file in the include_path, too. + * $context + * A context stream resource. + * $offset + * The offset where the reading starts on the original stream. + * $maxlen + * Maximum length of data read. The default is to read until end of file + * is reached. Note that this parameter is applied to the stream processed by the filters. + * Return + * The function returns the read data or FALSE on failure. + */ +static int jx9Builtin_file_get_contents(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + jx9_int64 n, nRead, nMaxlen; + int use_include = FALSE; + const char *zFile; + char zBuf[8192]; + void *pHandle; + int nLen; + + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the file path */ + zFile = jx9_value_to_string(apArg[0], &nLen); + /* Point to the target IO stream device */ + pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen); + if( pStream == 0 ){ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + nMaxlen = -1; + if( nArg > 1 ){ + use_include = jx9_value_to_bool(apArg[1]); + } + /* Try to open the file in read-only mode */ + pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0); + if( pHandle == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + if( nArg > 3 ){ + /* Extract the offset */ + n = jx9_value_to_int64(apArg[3]); + if( n > 0 ){ + if( pStream->xSeek ){ + /* Seek to the desired offset */ + pStream->xSeek(pHandle, n, 0/*SEEK_SET*/); + } + } + if( nArg > 4 ){ + /* Maximum data to read */ + nMaxlen = jx9_value_to_int64(apArg[4]); + } + } + /* Perform the requested operation */ + nRead = 0; + for(;;){ + n = pStream->xRead(pHandle, zBuf, + (nMaxlen > 0 && (nMaxlen < sizeof(zBuf))) ? nMaxlen : sizeof(zBuf)); + if( n < 1 ){ + /* EOF or IO error, break immediately */ + break; + } + /* Append data */ + jx9_result_string(pCtx, zBuf, (int)n); + /* Increment read counter */ + nRead += n; + if( nMaxlen > 0 && nRead >= nMaxlen ){ + /* Read limit reached */ + break; + } + } + /* Close the stream */ + jx9StreamCloseHandle(pStream, pHandle); + /* Check if we have read something */ + if( jx9_context_result_buf_length(pCtx) < 1 ){ + /* Nothing read, return FALSE */ + jx9_result_bool(pCtx, 0); + } + return JX9_OK; +} +/* + * int file_put_contents(string $filename, mixed $data[, int $flags = 0[, resource $context]]) + * Write a string to a file. + * Parameters + * $filename + * Path to the file where to write the data. + * $data + * The data to write(Must be a string). + * $flags + * The value of flags can be any combination of the following + * flags, joined with the binary OR (|) operator. + * FILE_USE_INCLUDE_PATH Search for filename in the include directory. See include_path for more information. + * FILE_APPEND If file filename already exists, append the data to the file instead of overwriting it. + * LOCK_EX Acquire an exclusive lock on the file while proceeding to the writing. + * context + * A context stream resource. + * Return + * The function returns the number of bytes that were written to the file, or FALSE on failure. + */ +static int jx9Builtin_file_put_contents(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int use_include = FALSE; + const jx9_io_stream *pStream; + const char *zFile; + const char *zData; + int iOpenFlags; + void *pHandle; + int iFlags; + int nLen; + + if( nArg < 2 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the file path */ + zFile = jx9_value_to_string(apArg[0], &nLen); + /* Point to the target IO stream device */ + pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen); + if( pStream == 0 ){ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Data to write */ + zData = jx9_value_to_string(apArg[1], &nLen); + if( nLen < 1 ){ + /* Nothing to write, return immediately */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Try to open the file in read-write mode */ + iOpenFlags = JX9_IO_OPEN_CREATE|JX9_IO_OPEN_RDWR|JX9_IO_OPEN_TRUNC; + /* Extract the flags */ + iFlags = 0; + if( nArg > 2 ){ + iFlags = jx9_value_to_int(apArg[2]); + if( iFlags & 0x01 /*FILE_USE_INCLUDE_PATH*/){ + use_include = TRUE; + } + if( iFlags & 0x08 /* FILE_APPEND */){ + /* If the file already exists, append the data to the file + * instead of overwriting it. + */ + iOpenFlags &= ~JX9_IO_OPEN_TRUNC; + /* Append mode */ + iOpenFlags |= JX9_IO_OPEN_APPEND; + } + } + pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, iOpenFlags, use_include, + nArg > 3 ? apArg[3] : 0, FALSE, FALSE); + if( pHandle == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + if( pStream->xWrite ){ + jx9_int64 n; + if( (iFlags & 0x01/* LOCK_EX */) && pStream->xLock ){ + /* Try to acquire an exclusive lock */ + pStream->xLock(pHandle, 1/* LOCK_EX */); + } + /* Perform the write operation */ + n = pStream->xWrite(pHandle, (const void *)zData, nLen); + if( n < 1 ){ + /* IO error, return FALSE */ + jx9_result_bool(pCtx, 0); + }else{ + /* Total number of bytes written */ + jx9_result_int64(pCtx, n); + } + }else{ + /* Read-only stream */ + jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, + "Read-only stream(%s): Cannot perform write operation", + pStream ? pStream->zName : "null_stream" + ); + jx9_result_bool(pCtx, 0); + } + /* Close the handle */ + jx9StreamCloseHandle(pStream, pHandle); + return JX9_OK; +} +/* + * array file(string $filename[, int $flags = 0[, resource $context]]) + * Reads entire file into an array. + * Parameters + * $filename + * The filename being read. + * $flags + * The optional parameter flags can be one, or more, of the following constants: + * FILE_USE_INCLUDE_PATH + * Search for the file in the include_path. + * FILE_IGNORE_NEW_LINES + * Do not add newline at the end of each array element + * FILE_SKIP_EMPTY_LINES + * Skip empty lines + * $context + * A context stream resource. + * Return + * The function returns the read data or FALSE on failure. + */ +static int jx9Builtin_file(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zFile, *zPtr, *zEnd, *zBuf; + jx9_value *pArray, *pLine; + const jx9_io_stream *pStream; + int use_include = 0; + io_private *pDev; + jx9_int64 n; + int iFlags; + int nLen; + + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the file path */ + zFile = jx9_value_to_string(apArg[0], &nLen); + /* Point to the target IO stream device */ + pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen); + if( pStream == 0 ){ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Allocate a new IO private instance */ + pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE); + if( pDev == 0 ){ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Initialize the structure */ + InitIOPrivate(pCtx->pVm, pStream, pDev); + iFlags = 0; + if( nArg > 1 ){ + iFlags = jx9_value_to_int(apArg[1]); + } + if( iFlags & 0x01 /*FILE_USE_INCLUDE_PATH*/ ){ + use_include = TRUE; + } + /* Create the array and the working value */ + pArray = jx9_context_new_array(pCtx); + pLine = jx9_context_new_scalar(pCtx); + if( pArray == 0 || pLine == 0 ){ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Try to open the file in read-only mode */ + pDev->pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0); + if( pDev->pHandle == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile); + jx9_result_bool(pCtx, 0); + /* Don't worry about freeing memory, everything will be released automatically + * as soon we return from this function. + */ + return JX9_OK; + } + /* Perform the requested operation */ + for(;;){ + /* Try to extract a line */ + n = StreamReadLine(pDev, &zBuf, -1); + if( n < 1 ){ + /* EOF or IO error */ + break; + } + /* Reset the cursor */ + jx9_value_reset_string_cursor(pLine); + /* Remove line ending if requested by the caller */ + zPtr = zBuf; + zEnd = &zBuf[n]; + if( iFlags & 0x02 /* FILE_IGNORE_NEW_LINES */ ){ + /* Ignore trailig lines */ + while( zPtr < zEnd && (zEnd[-1] == '\n' +#ifdef __WINNT__ + || zEnd[-1] == '\r' +#endif + )){ + n--; + zEnd--; + } + } + if( iFlags & 0x04 /* FILE_SKIP_EMPTY_LINES */ ){ + /* Ignore empty lines */ + while( zPtr < zEnd && (unsigned char)zPtr[0] < 0xc0 && SyisSpace(zPtr[0]) ){ + zPtr++; + } + if( zPtr >= zEnd ){ + /* Empty line */ + continue; + } + } + jx9_value_string(pLine, zBuf, (int)(zEnd-zBuf)); + /* Insert line */ + jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pLine); + } + /* Close the stream */ + jx9StreamCloseHandle(pStream, pDev->pHandle); + /* Release the io_private instance */ + ReleaseIOPrivate(pCtx, pDev); + /* Return the created array */ + jx9_result_value(pCtx, pArray); + return JX9_OK; +} +/* + * bool copy(string $source, string $dest[, resource $context ] ) + * Makes a copy of the file source to dest. + * Parameters + * $source + * Path to the source file. + * $dest + * The destination path. If dest is a URL, the copy operation + * may fail if the wrapper does not support overwriting of existing files. + * $context + * A context stream resource. + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Builtin_copy(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pSin, *pSout; + const char *zFile; + char zBuf[8192]; + void *pIn, *pOut; + jx9_int64 n; + int nLen; + if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_string(apArg[1])){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a source and a destination path"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the source name */ + zFile = jx9_value_to_string(apArg[0], &nLen); + /* Point to the target IO stream device */ + pSin = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen); + if( pSin == 0 ){ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Try to open the source file in a read-only mode */ + pIn = jx9StreamOpenHandle(pCtx->pVm, pSin, zFile, JX9_IO_OPEN_RDONLY, FALSE, nArg > 2 ? apArg[2] : 0, FALSE, 0); + if( pIn == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening source: '%s'", zFile); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the destination name */ + zFile = jx9_value_to_string(apArg[1], &nLen); + /* Point to the target IO stream device */ + pSout = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen); + if( pSout == 0 ){ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE"); + jx9_result_bool(pCtx, 0); + jx9StreamCloseHandle(pSin, pIn); + return JX9_OK; + } + if( pSout->xWrite == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", + jx9_function_name(pCtx), pSin->zName + ); + jx9_result_bool(pCtx, 0); + jx9StreamCloseHandle(pSin, pIn); + return JX9_OK; + } + /* Try to open the destination file in a read-write mode */ + pOut = jx9StreamOpenHandle(pCtx->pVm, pSout, zFile, + JX9_IO_OPEN_CREATE|JX9_IO_OPEN_TRUNC|JX9_IO_OPEN_RDWR, FALSE, nArg > 2 ? apArg[2] : 0, FALSE, 0); + if( pOut == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening destination: '%s'", zFile); + jx9_result_bool(pCtx, 0); + jx9StreamCloseHandle(pSin, pIn); + return JX9_OK; + } + /* Perform the requested operation */ + for(;;){ + /* Read from source */ + n = pSin->xRead(pIn, zBuf, sizeof(zBuf)); + if( n < 1 ){ + /* EOF or IO error, break immediately */ + break; + } + /* Write to dest */ + n = pSout->xWrite(pOut, zBuf, n); + if( n < 1 ){ + /* IO error, break immediately */ + break; + } + } + /* Close the streams */ + jx9StreamCloseHandle(pSin, pIn); + jx9StreamCloseHandle(pSout, pOut); + /* Return TRUE */ + jx9_result_bool(pCtx, 1); + return JX9_OK; +} +/* + * array fstat(resource $handle) + * Gets information about a file using an open file pointer. + * Parameters + * $handle + * The file pointer. + * Return + * Returns an array with the statistics of the file or FALSE on failure. + */ +static int jx9Builtin_fstat(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + jx9_value *pArray, *pValue; + const jx9_io_stream *pStream; + io_private *pDev; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract our private data */ + pDev = (io_private *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid io_private instance */ + if( IO_PRIVATE_INVALID(pDev) ){ + /* Expecting an IO handle */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the target IO stream device */ + pStream = pDev->pStream; + if( pStream == 0 || pStream->xStat == 0){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", + jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Create the array and the working value */ + pArray = jx9_context_new_array(pCtx); + pValue = jx9_context_new_scalar(pCtx); + if( pArray == 0 || pValue == 0 ){ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + pStream->xStat(pDev->pHandle, pArray, pValue); + /* Return the freshly created array */ + jx9_result_value(pCtx, pArray); + /* Don't worry about freeing memory here, everything will be + * released automatically as soon we return from this function. + */ + return JX9_OK; +} +/* + * int fwrite(resource $handle, string $string[, int $length]) + * Writes the contents of string to the file stream pointed to by handle. + * Parameters + * $handle + * The file pointer. + * $string + * The string that is to be written. + * $length + * If the length argument is given, writing will stop after length bytes have been written + * or the end of string is reached, whichever comes first. + * Return + * Returns the number of bytes written, or FALSE on error. + */ +static int jx9Builtin_fwrite(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + const char *zString; + io_private *pDev; + int nLen, n; + if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract our private data */ + pDev = (io_private *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid io_private instance */ + if( IO_PRIVATE_INVALID(pDev) ){ + /* Expecting an IO handle */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the target IO stream device */ + pStream = pDev->pStream; + if( pStream == 0 || pStream->xWrite == 0){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", + jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the data to write */ + zString = jx9_value_to_string(apArg[1], &nLen); + if( nArg > 2 ){ + /* Maximum data length to write */ + n = jx9_value_to_int(apArg[2]); + if( n >= 0 && n < nLen ){ + nLen = n; + } + } + if( nLen < 1 ){ + /* Nothing to write */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + n = (int)pStream->xWrite(pDev->pHandle, (const void *)zString, nLen); + if( n < 0 ){ + /* IO error, return FALSE */ + jx9_result_bool(pCtx, 0); + }else{ + /* #Bytes written */ + jx9_result_int(pCtx, n); + } + return JX9_OK; +} +/* + * bool flock(resource $handle, int $operation) + * Portable advisory file locking. + * Parameters + * $handle + * The file pointer. + * $operation + * operation is one of the following: + * LOCK_SH to acquire a shared lock (reader). + * LOCK_EX to acquire an exclusive lock (writer). + * LOCK_UN to release a lock (shared or exclusive). + * Return + * Returns TRUE on success or FALSE on failure. + */ +static int jx9Builtin_flock(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + io_private *pDev; + int nLock; + int rc; + if( nArg < 2 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract our private data */ + pDev = (io_private *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid io_private instance */ + if( IO_PRIVATE_INVALID(pDev) ){ + /*Expecting an IO handle */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the target IO stream device */ + pStream = pDev->pStream; + if( pStream == 0 || pStream->xLock == 0){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", + jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Requested lock operation */ + nLock = jx9_value_to_int(apArg[1]); + /* Lock operation */ + rc = pStream->xLock(pDev->pHandle, nLock); + /* IO result */ + jx9_result_bool(pCtx, rc == JX9_OK); + return JX9_OK; +} +/* + * int fpassthru(resource $handle) + * Output all remaining data on a file pointer. + * Parameters + * $handle + * The file pointer. + * Return + * Total number of characters read from handle and passed through + * to the output on success or FALSE on failure. + */ +static int jx9Builtin_fpassthru(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + io_private *pDev; + jx9_int64 n, nRead; + char zBuf[8192]; + int rc; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract our private data */ + pDev = (io_private *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid io_private instance */ + if( IO_PRIVATE_INVALID(pDev) ){ + /*Expecting an IO handle */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the target IO stream device */ + pStream = pDev->pStream; + if( pStream == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", + jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + nRead = 0; + for(;;){ + n = StreamRead(pDev, zBuf, sizeof(zBuf)); + if( n < 1 ){ + /* Error or EOF */ + break; + } + /* Increment the read counter */ + nRead += n; + /* Output data */ + rc = jx9_context_output(pCtx, zBuf, (int)nRead /* FIXME: 64-bit issues */); + if( rc == JX9_ABORT ){ + /* Consumer callback request an operation abort */ + break; + } + } + /* Total number of bytes readen */ + jx9_result_int64(pCtx, nRead); + return JX9_OK; +} +/* CSV reader/writer private data */ +struct csv_data +{ + int delimiter; /* Delimiter. Default ', ' */ + int enclosure; /* Enclosure. Default '"'*/ + io_private *pDev; /* Open stream handle */ + int iCount; /* Counter */ +}; +/* + * The following callback is used by the fputcsv() function inorder to iterate + * throw array entries and output CSV data based on the current key and it's + * associated data. + */ +static int csv_write_callback(jx9_value *pKey, jx9_value *pValue, void *pUserData) +{ + struct csv_data *pData = (struct csv_data *)pUserData; + const char *zData; + int nLen, c2; + sxu32 n; + /* Point to the raw data */ + zData = jx9_value_to_string(pValue, &nLen); + if( nLen < 1 ){ + /* Nothing to write */ + return JX9_OK; + } + if( pData->iCount > 0 ){ + /* Write the delimiter */ + pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->delimiter, sizeof(char)); + } + n = 1; + c2 = 0; + if( SyByteFind(zData, (sxu32)nLen, pData->delimiter, 0) == SXRET_OK || + SyByteFind(zData, (sxu32)nLen, pData->enclosure, &n) == SXRET_OK ){ + c2 = 1; + if( n == 0 ){ + c2 = 2; + } + /* Write the enclosure */ + pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char)); + if( c2 > 1 ){ + pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char)); + } + } + /* Write the data */ + if( pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)zData, (jx9_int64)nLen) < 1 ){ + SXUNUSED(pKey); /* cc warning */ + return JX9_ABORT; + } + if( c2 > 0 ){ + /* Write the enclosure */ + pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char)); + if( c2 > 1 ){ + pData->pDev->pStream->xWrite(pData->pDev->pHandle, (const void *)&pData->enclosure, sizeof(char)); + } + } + pData->iCount++; + return JX9_OK; +} +/* + * int fputcsv(resource $handle, array $fields[, string $delimiter = ', '[, string $enclosure = '"' ]]) + * Format line as CSV and write to file pointer. + * Parameters + * $handle + * Open file handle. + * $fields + * An array of values. + * $delimiter + * The optional delimiter parameter sets the field delimiter (one character only). + * $enclosure + * The optional enclosure parameter sets the field enclosure (one character only). + */ +static int jx9Builtin_fputcsv(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + struct csv_data sCsv; + io_private *pDev; + char *zEol; + int eolen; + if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Missing/Invalid arguments"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract our private data */ + pDev = (io_private *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid io_private instance */ + if( IO_PRIVATE_INVALID(pDev) ){ + /*Expecting an IO handle */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the target IO stream device */ + pStream = pDev->pStream; + if( pStream == 0 || pStream->xWrite == 0){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", + jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Set default csv separator */ + sCsv.delimiter = ','; + sCsv.enclosure = '"'; + sCsv.pDev = pDev; + sCsv.iCount = 0; + if( nArg > 2 ){ + /* User delimiter */ + const char *z; + int n; + z = jx9_value_to_string(apArg[2], &n); + if( n > 0 ){ + sCsv.delimiter = z[0]; + } + if( nArg > 3 ){ + z = jx9_value_to_string(apArg[3], &n); + if( n > 0 ){ + sCsv.enclosure = z[0]; + } + } + } + /* Iterate throw array entries and write csv data */ + jx9_array_walk(apArg[1], csv_write_callback, &sCsv); + /* Write a line ending */ +#ifdef __WINNT__ + zEol = "\r\n"; + eolen = (int)sizeof("\r\n")-1; +#else + /* Assume UNIX LF */ + zEol = "\n"; + eolen = (int)sizeof(char); +#endif + pDev->pStream->xWrite(pDev->pHandle, (const void *)zEol, eolen); + return JX9_OK; +} +/* + * fprintf, vfprintf private data. + * An instance of the following structure is passed to the formatted + * input consumer callback defined below. + */ +typedef struct fprintf_data fprintf_data; +struct fprintf_data +{ + io_private *pIO; /* IO stream */ + jx9_int64 nCount; /* Total number of bytes written */ +}; +/* + * Callback [i.e: Formatted input consumer] for the fprintf function. + */ +static int fprintfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData) +{ + fprintf_data *pFdata = (fprintf_data *)pUserData; + jx9_int64 n; + /* Write the formatted data */ + n = pFdata->pIO->pStream->xWrite(pFdata->pIO->pHandle, (const void *)zInput, nLen); + if( n < 1 ){ + SXUNUSED(pCtx); /* cc warning */ + /* IO error, abort immediately */ + return SXERR_ABORT; + } + /* Increment counter */ + pFdata->nCount += n; + return JX9_OK; +} +/* + * int fprintf(resource $handle, string $format[, mixed $args [, mixed $... ]]) + * Write a formatted string to a stream. + * Parameters + * $handle + * The file pointer. + * $format + * String format (see sprintf()). + * Return + * The length of the written string. + */ +static int jx9Builtin_fprintf(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + fprintf_data sFdata; + const char *zFormat; + io_private *pDev; + int nLen; + if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_string(apArg[1]) ){ + /* Missing/Invalid arguments, return zero */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Invalid arguments"); + jx9_result_int(pCtx, 0); + return JX9_OK; + } + /* Extract our private data */ + pDev = (io_private *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid io_private instance */ + if( IO_PRIVATE_INVALID(pDev) ){ + /*Expecting an IO handle */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_int(pCtx, 0); + return JX9_OK; + } + /* Point to the target IO stream device */ + if( pDev->pStream == 0 || pDev->pStream->xWrite == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device", + jx9_function_name(pCtx), pDev->pStream ? pDev->pStream->zName : "null_stream" + ); + jx9_result_int(pCtx, 0); + return JX9_OK; + } + /* Extract the string format */ + zFormat = jx9_value_to_string(apArg[1], &nLen); + if( nLen < 1 ){ + /* Empty string, return zero */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + /* Prepare our private data */ + sFdata.nCount = 0; + sFdata.pIO = pDev; + /* Format the string */ + jx9InputFormat(fprintfConsumer, pCtx, zFormat, nLen, nArg - 1, &apArg[1], (void *)&sFdata, FALSE); + /* Return total number of bytes written */ + jx9_result_int64(pCtx, sFdata.nCount); + return JX9_OK; +} +/* + * int vfprintf(resource $handle, string $format, array $args) + * Write a formatted string to a stream. + * Parameters + * $handle + * The file pointer. + * $format + * String format (see sprintf()). + * $args + * User arguments. + * Return + * The length of the written string. + */ +static int jx9Builtin_vfprintf(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + fprintf_data sFdata; + const char *zFormat; + jx9_hashmap *pMap; + io_private *pDev; + SySet sArg; + int n, nLen; + if( nArg < 3 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_string(apArg[1]) || !jx9_value_is_json_array(apArg[2]) ){ + /* Missing/Invalid arguments, return zero */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Invalid arguments"); + jx9_result_int(pCtx, 0); + return JX9_OK; + } + /* Extract our private data */ + pDev = (io_private *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid io_private instance */ + if( IO_PRIVATE_INVALID(pDev) ){ + /*Expecting an IO handle */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_int(pCtx, 0); + return JX9_OK; + } + /* Point to the target IO stream device */ + if( pDev->pStream == 0 || pDev->pStream->xWrite == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device", + jx9_function_name(pCtx), pDev->pStream ? pDev->pStream->zName : "null_stream" + ); + jx9_result_int(pCtx, 0); + return JX9_OK; + } + /* Extract the string format */ + zFormat = jx9_value_to_string(apArg[1], &nLen); + if( nLen < 1 ){ + /* Empty string, return zero */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + /* Point to hashmap */ + pMap = (jx9_hashmap *)apArg[2]->x.pOther; + /* Extract arguments from the hashmap */ + n = jx9HashmapValuesToSet(pMap, &sArg); + /* Prepare our private data */ + sFdata.nCount = 0; + sFdata.pIO = pDev; + /* Format the string */ + jx9InputFormat(fprintfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), (void *)&sFdata, TRUE); + /* Return total number of bytes written*/ + jx9_result_int64(pCtx, sFdata.nCount); + SySetRelease(&sArg); + return JX9_OK; +} +/* + * Convert open modes (string passed to the fopen() function) [i.e: 'r', 'w+', 'a', ...] into JX9 flags. + * According to the JX9 reference manual: + * The mode parameter specifies the type of access you require to the stream. It may be any of the following + * 'r' Open for reading only; place the file pointer at the beginning of the file. + * 'r+' Open for reading and writing; place the file pointer at the beginning of the file. + * 'w' Open for writing only; place the file pointer at the beginning of the file and truncate the file + * to zero length. If the file does not exist, attempt to create it. + * 'w+' Open for reading and writing; place the file pointer at the beginning of the file and truncate + * the file to zero length. If the file does not exist, attempt to create it. + * 'a' Open for writing only; place the file pointer at the end of the file. If the file does not + * exist, attempt to create it. + * 'a+' Open for reading and writing; place the file pointer at the end of the file. If the file does + * not exist, attempt to create it. + * 'x' Create and open for writing only; place the file pointer at the beginning of the file. If the file + * already exists, + * the fopen() call will fail by returning FALSE and generating an error of level E_WARNING. If the file + * does not exist attempt to create it. This is equivalent to specifying O_EXCL|O_CREAT flags for + * the underlying open(2) system call. + * 'x+' Create and open for reading and writing; otherwise it has the same behavior as 'x'. + * 'c' Open the file for writing only. If the file does not exist, it is created. If it exists, it is neither truncated + * (as opposed to 'w'), nor the call to this function fails (as is the case with 'x'). The file pointer + * is positioned on the beginning of the file. + * This may be useful if it's desired to get an advisory lock (see flock()) before attempting to modify the file + * as using 'w' could truncate the file before the lock was obtained (if truncation is desired, ftruncate() can + * be used after the lock is requested). + * 'c+' Open the file for reading and writing; otherwise it has the same behavior as 'c'. + */ +static int StrModeToFlags(jx9_context *pCtx, const char *zMode, int nLen) +{ + const char *zEnd = &zMode[nLen]; + int iFlag = 0; + int c; + if( nLen < 1 ){ + /* Open in a read-only mode */ + return JX9_IO_OPEN_RDONLY; + } + c = zMode[0]; + if( c == 'r' || c == 'R' ){ + /* Read-only access */ + iFlag = JX9_IO_OPEN_RDONLY; + zMode++; /* Advance */ + if( zMode < zEnd ){ + c = zMode[0]; + if( c == '+' || c == 'w' || c == 'W' ){ + /* Read+Write access */ + iFlag = JX9_IO_OPEN_RDWR; + } + } + }else if( c == 'w' || c == 'W' ){ + /* Overwrite mode. + * If the file does not exists, try to create it + */ + iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_TRUNC|JX9_IO_OPEN_CREATE; + zMode++; /* Advance */ + if( zMode < zEnd ){ + c = zMode[0]; + if( c == '+' || c == 'r' || c == 'R' ){ + /* Read+Write access */ + iFlag &= ~JX9_IO_OPEN_WRONLY; + iFlag |= JX9_IO_OPEN_RDWR; + } + } + }else if( c == 'a' || c == 'A' ){ + /* Append mode (place the file pointer at the end of the file). + * Create the file if it does not exists. + */ + iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_APPEND|JX9_IO_OPEN_CREATE; + zMode++; /* Advance */ + if( zMode < zEnd ){ + c = zMode[0]; + if( c == '+' ){ + /* Read-Write access */ + iFlag &= ~JX9_IO_OPEN_WRONLY; + iFlag |= JX9_IO_OPEN_RDWR; + } + } + }else if( c == 'x' || c == 'X' ){ + /* Exclusive access. + * If the file already exists, return immediately with a failure code. + * Otherwise create a new file. + */ + iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_EXCL; + zMode++; /* Advance */ + if( zMode < zEnd ){ + c = zMode[0]; + if( c == '+' || c == 'r' || c == 'R' ){ + /* Read-Write access */ + iFlag &= ~JX9_IO_OPEN_WRONLY; + iFlag |= JX9_IO_OPEN_RDWR; + } + } + }else if( c == 'c' || c == 'C' ){ + /* Overwrite mode.Create the file if it does not exists.*/ + iFlag = JX9_IO_OPEN_WRONLY|JX9_IO_OPEN_CREATE; + zMode++; /* Advance */ + if( zMode < zEnd ){ + c = zMode[0]; + if( c == '+' ){ + /* Read-Write access */ + iFlag &= ~JX9_IO_OPEN_WRONLY; + iFlag |= JX9_IO_OPEN_RDWR; + } + } + }else{ + /* Invalid mode. Assume a read only open */ + jx9_context_throw_error(pCtx, JX9_CTX_NOTICE, "Invalid open mode, JX9 is assuming a Read-Only open"); + iFlag = JX9_IO_OPEN_RDONLY; + } + while( zMode < zEnd ){ + c = zMode[0]; + if( c == 'b' || c == 'B' ){ + iFlag &= ~JX9_IO_OPEN_TEXT; + iFlag |= JX9_IO_OPEN_BINARY; + }else if( c == 't' || c == 'T' ){ + iFlag &= ~JX9_IO_OPEN_BINARY; + iFlag |= JX9_IO_OPEN_TEXT; + } + zMode++; + } + return iFlag; +} +/* + * Initialize the IO private structure. + */ +static void InitIOPrivate(jx9_vm *pVm, const jx9_io_stream *pStream, io_private *pOut) +{ + pOut->pStream = pStream; + SyBlobInit(&pOut->sBuffer, &pVm->sAllocator); + pOut->nOfft = 0; + /* Set the magic number */ + pOut->iMagic = IO_PRIVATE_MAGIC; +} +/* + * Release the IO private structure. + */ +static void ReleaseIOPrivate(jx9_context *pCtx, io_private *pDev) +{ + SyBlobRelease(&pDev->sBuffer); + pDev->iMagic = 0x2126; /* Invalid magic number so we can detetct misuse */ + /* Release the whole structure */ + jx9_context_free_chunk(pCtx, pDev); +} +/* + * Reset the IO private structure. + */ +static void ResetIOPrivate(io_private *pDev) +{ + SyBlobReset(&pDev->sBuffer); + pDev->nOfft = 0; +} +/* Forward declaration */ +static int is_jx9_stream(const jx9_io_stream *pStream); +/* + * resource fopen(string $filename, string $mode [, bool $use_include_path = false[, resource $context ]]) + * Open a file, a URL or any other IO stream. + * Parameters + * $filename + * If filename is of the form "scheme://...", it is assumed to be a URL and JX9 will search + * for a protocol handler (also known as a wrapper) for that scheme. If no scheme is given + * then a regular file is assumed. + * $mode + * The mode parameter specifies the type of access you require to the stream + * See the block comment associated with the StrModeToFlags() for the supported + * modes. + * $use_include_path + * You can use the optional second parameter and set it to + * TRUE, if you want to search for the file in the include_path, too. + * $context + * A context stream resource. + * Return + * File handle on success or FALSE on failure. + */ +static int jx9Builtin_fopen(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + const char *zUri, *zMode; + jx9_value *pResource; + io_private *pDev; + int iLen, imLen; + int iOpenFlags; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path or URL"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the URI and the desired access mode */ + zUri = jx9_value_to_string(apArg[0], &iLen); + if( nArg > 1 ){ + zMode = jx9_value_to_string(apArg[1], &imLen); + }else{ + /* Set a default read-only mode */ + zMode = "r"; + imLen = (int)sizeof(char); + } + /* Try to extract a stream */ + pStream = jx9VmGetStreamDevice(pCtx->pVm, &zUri, iLen); + if( pStream == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "No stream device is associated with the given URI(%s)", zUri); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Allocate a new IO private instance */ + pDev = (io_private *)jx9_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE); + if( pDev == 0 ){ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + pResource = 0; + if( nArg > 3 ){ + pResource = apArg[3]; + }else if( is_jx9_stream(pStream) ){ + /* TICKET 1433-80: The jx9:// stream need a jx9_value to access the underlying + * virtual machine. + */ + pResource = apArg[0]; + } + /* Initialize the structure */ + InitIOPrivate(pCtx->pVm, pStream, pDev); + /* Convert open mode to JX9 flags */ + iOpenFlags = StrModeToFlags(pCtx, zMode, imLen); + /* Try to get a handle */ + pDev->pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zUri, iOpenFlags, + nArg > 2 ? jx9_value_to_bool(apArg[2]) : FALSE, pResource, FALSE, 0); + if( pDev->pHandle == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zUri); + jx9_result_bool(pCtx, 0); + jx9_context_free_chunk(pCtx, pDev); + return JX9_OK; + } + /* All done, return the io_private instance as a resource */ + jx9_result_resource(pCtx, pDev); + return JX9_OK; +} +/* + * bool fclose(resource $handle) + * Closes an open file pointer + * Parameters + * $handle + * The file pointer. + * Return + * TRUE on success or FALSE on failure. + */ +static int jx9Builtin_fclose(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + io_private *pDev; + jx9_vm *pVm; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract our private data */ + pDev = (io_private *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid io_private instance */ + if( IO_PRIVATE_INVALID(pDev) ){ + /*Expecting an IO handle */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting an IO handle"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the target IO stream device */ + pStream = pDev->pStream; + if( pStream == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_WARNING, + "IO routine(%s) not implemented in the underlying stream(%s) device, JX9 is returning FALSE", + jx9_function_name(pCtx), pStream ? pStream->zName : "null_stream" + ); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the VM that own this context */ + pVm = pCtx->pVm; + /* TICKET 1433-62: Keep the STDIN/STDOUT/STDERR handles open */ + if( pDev != pVm->pStdin && pDev != pVm->pStdout && pDev != pVm->pStderr ){ + /* Perform the requested operation */ + jx9StreamCloseHandle(pStream, pDev->pHandle); + /* Release the IO private structure */ + ReleaseIOPrivate(pCtx, pDev); + /* Invalidate the resource handle */ + jx9_value_release(apArg[0]); + } + /* Return TRUE */ + jx9_result_bool(pCtx, 1); + return JX9_OK; +} +#if !defined(JX9_DISABLE_HASH_FUNC) +/* + * MD5/SHA1 digest consumer. + */ +static int vfsHashConsumer(const void *pData, unsigned int nLen, void *pUserData) +{ + /* Append hex chunk verbatim */ + jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen); + return SXRET_OK; +} +/* + * string md5_file(string $uri[, bool $raw_output = false ]) + * Calculates the md5 hash of a given file. + * Parameters + * $uri + * Target URI (file(/path/to/something) or URL(http://www.symisc.net/)) + * $raw_output + * When TRUE, returns the digest in raw binary format with a length of 16. + * Return + * Return the MD5 digest on success or FALSE on failure. + */ +static int jx9Builtin_md5_file(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + unsigned char zDigest[16]; + int raw_output = FALSE; + const char *zFile; + MD5Context sCtx; + char zBuf[8192]; + void *pHandle; + jx9_int64 n; + int nLen; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the file path */ + zFile = jx9_value_to_string(apArg[0], &nLen); + /* Point to the target IO stream device */ + pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen); + if( pStream == 0 ){ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + if( nArg > 1 ){ + raw_output = jx9_value_to_bool(apArg[1]); + } + /* Try to open the file in read-only mode */ + pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0); + if( pHandle == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Init the MD5 context */ + MD5Init(&sCtx); + /* Perform the requested operation */ + for(;;){ + n = pStream->xRead(pHandle, zBuf, sizeof(zBuf)); + if( n < 1 ){ + /* EOF or IO error, break immediately */ + break; + } + MD5Update(&sCtx, (const unsigned char *)zBuf, (unsigned int)n); + } + /* Close the stream */ + jx9StreamCloseHandle(pStream, pHandle); + /* Extract the digest */ + MD5Final(zDigest, &sCtx); + if( raw_output ){ + /* Output raw digest */ + jx9_result_string(pCtx, (const char *)zDigest, sizeof(zDigest)); + }else{ + /* Perform a binary to hex conversion */ + SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), vfsHashConsumer, pCtx); + } + return JX9_OK; +} +/* + * string sha1_file(string $uri[, bool $raw_output = false ]) + * Calculates the SHA1 hash of a given file. + * Parameters + * $uri + * Target URI (file(/path/to/something) or URL(http://www.symisc.net/)) + * $raw_output + * When TRUE, returns the digest in raw binary format with a length of 20. + * Return + * Return the SHA1 digest on success or FALSE on failure. + */ +static int jx9Builtin_sha1_file(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + unsigned char zDigest[20]; + int raw_output = FALSE; + const char *zFile; + SHA1Context sCtx; + char zBuf[8192]; + void *pHandle; + jx9_int64 n; + int nLen; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the file path */ + zFile = jx9_value_to_string(apArg[0], &nLen); + /* Point to the target IO stream device */ + pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen); + if( pStream == 0 ){ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + if( nArg > 1 ){ + raw_output = jx9_value_to_bool(apArg[1]); + } + /* Try to open the file in read-only mode */ + pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0); + if( pHandle == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Init the SHA1 context */ + SHA1Init(&sCtx); + /* Perform the requested operation */ + for(;;){ + n = pStream->xRead(pHandle, zBuf, sizeof(zBuf)); + if( n < 1 ){ + /* EOF or IO error, break immediately */ + break; + } + SHA1Update(&sCtx, (const unsigned char *)zBuf, (unsigned int)n); + } + /* Close the stream */ + jx9StreamCloseHandle(pStream, pHandle); + /* Extract the digest */ + SHA1Final(&sCtx, zDigest); + if( raw_output ){ + /* Output raw digest */ + jx9_result_string(pCtx, (const char *)zDigest, sizeof(zDigest)); + }else{ + /* Perform a binary to hex conversion */ + SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), vfsHashConsumer, pCtx); + } + return JX9_OK; +} +#endif /* JX9_DISABLE_HASH_FUNC */ +/* + * array parse_ini_file(string $filename[, bool $process_sections = false [, int $scanner_mode = INI_SCANNER_NORMAL ]] ) + * Parse a configuration file. + * Parameters + * $filename + * The filename of the ini file being parsed. + * $process_sections + * By setting the process_sections parameter to TRUE, you get a multidimensional array + * with the section names and settings included. + * The default for process_sections is FALSE. + * $scanner_mode + * Can either be INI_SCANNER_NORMAL (default) or INI_SCANNER_RAW. + * If INI_SCANNER_RAW is supplied, then option values will not be parsed. + * Return + * The settings are returned as an associative array on success. + * Otherwise is returned. + */ +static int jx9Builtin_parse_ini_file(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + const char *zFile; + SyBlob sContents; + void *pHandle; + int nLen; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the file path */ + zFile = jx9_value_to_string(apArg[0], &nLen); + /* Point to the target IO stream device */ + pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen); + if( pStream == 0 ){ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Try to open the file in read-only mode */ + pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0); + if( pHandle == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + SyBlobInit(&sContents, &pCtx->pVm->sAllocator); + /* Read the whole file */ + jx9StreamReadWholeFile(pHandle, pStream, &sContents); + if( SyBlobLength(&sContents) < 1 ){ + /* Empty buffer, return FALSE */ + jx9_result_bool(pCtx, 0); + }else{ + /* Process the raw INI buffer */ + jx9ParseIniString(pCtx, (const char *)SyBlobData(&sContents), SyBlobLength(&sContents), + nArg > 1 ? jx9_value_to_bool(apArg[1]) : 0); + } + /* Close the stream */ + jx9StreamCloseHandle(pStream, pHandle); + /* Release the working buffer */ + SyBlobRelease(&sContents); + return JX9_OK; +} +/* + * Section: + * ZIP archive processing. + * Authors: + * Symisc Systems, devel@symisc.net. + * Copyright (C) Symisc Systems, http://jx9.symisc.net + * Status: + * Stable. + */ +typedef struct zip_raw_data zip_raw_data; +struct zip_raw_data +{ + int iType; /* Where the raw data is stored */ + union raw_data{ + struct mmap_data{ + void *pMap; /* Memory mapped data */ + jx9_int64 nSize; /* Map size */ + const jx9_vfs *pVfs; /* Underlying vfs */ + }mmap; + SyBlob sBlob; /* Memory buffer */ + }raw; +}; +#define ZIP_RAW_DATA_MMAPED 1 /* Memory mapped ZIP raw data */ +#define ZIP_RAW_DATA_MEMBUF 2 /* ZIP raw data stored in a dynamically + * allocated memory chunk. + */ + /* + * mixed zip_open(string $filename) + * Opens a new zip archive for reading. + * Parameters + * $filename + * The file name of the ZIP archive to open. + * Return + * A resource handle for later use with zip_read() and zip_close() or FALSE on failure. + */ +static int jx9Builtin_zip_open(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const jx9_io_stream *pStream; + SyArchive *pArchive; + zip_raw_data *pRaw; + const char *zFile; + SyBlob *pContents; + void *pHandle; + int nLen; + sxi32 rc; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Expecting a file path"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the file path */ + zFile = jx9_value_to_string(apArg[0], &nLen); + /* Point to the target IO stream device */ + pStream = jx9VmGetStreamDevice(pCtx->pVm, &zFile, nLen); + if( pStream == 0 ){ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "No such stream device, JX9 is returning FALSE"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Create an in-memory archive */ + pArchive = (SyArchive *)jx9_context_alloc_chunk(pCtx, sizeof(SyArchive)+sizeof(zip_raw_data), TRUE, FALSE); + if( pArchive == 0 ){ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "JX9 is running out of memory"); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + pRaw = (zip_raw_data *)&pArchive[1]; + /* Initialize the archive */ + SyArchiveInit(pArchive, &pCtx->pVm->sAllocator, 0, 0); + /* Extract the default stream */ + if( pStream == pCtx->pVm->pDefStream /* file:// stream*/){ + const jx9_vfs *pVfs; + /* Try to get a memory view of the whole file since ZIP files + * tends to be very big this days, this is a huge performance win. + */ + pVfs = jx9ExportBuiltinVfs(); + if( pVfs && pVfs->xMmap ){ + rc = pVfs->xMmap(zFile, &pRaw->raw.mmap.pMap, &pRaw->raw.mmap.nSize); + if( rc == JX9_OK ){ + /* Nice, Extract the whole archive */ + rc = SyZipExtractFromBuf(pArchive, (const char *)pRaw->raw.mmap.pMap, (sxu32)pRaw->raw.mmap.nSize); + if( rc != SXRET_OK ){ + if( pVfs->xUnmap ){ + pVfs->xUnmap(pRaw->raw.mmap.pMap, pRaw->raw.mmap.nSize); + } + /* Release the allocated chunk */ + jx9_context_free_chunk(pCtx, pArchive); + /* Something goes wrong with this ZIP archive, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Archive successfully opened */ + pRaw->iType = ZIP_RAW_DATA_MMAPED; + pRaw->raw.mmap.pVfs = pVfs; + goto success; + } + } + /* FALL THROUGH */ + } + /* Try to open the file in read-only mode */ + pHandle = jx9StreamOpenHandle(pCtx->pVm, pStream, zFile, JX9_IO_OPEN_RDONLY, FALSE, 0, FALSE, 0); + if( pHandle == 0 ){ + jx9_context_throw_error_format(pCtx, JX9_CTX_ERR, "IO error while opening '%s'", zFile); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + pContents = &pRaw->raw.sBlob; + SyBlobInit(pContents, &pCtx->pVm->sAllocator); + /* Read the whole file */ + jx9StreamReadWholeFile(pHandle, pStream, pContents); + /* Assume an invalid ZIP file */ + rc = SXERR_INVALID; + if( SyBlobLength(pContents) > 0 ){ + /* Extract archive entries */ + rc = SyZipExtractFromBuf(pArchive, (const char *)SyBlobData(pContents), SyBlobLength(pContents)); + } + pRaw->iType = ZIP_RAW_DATA_MEMBUF; + /* Close the stream */ + jx9StreamCloseHandle(pStream, pHandle); + if( rc != SXRET_OK ){ + /* Release the working buffer */ + SyBlobRelease(pContents); + /* Release the allocated chunk */ + jx9_context_free_chunk(pCtx, pArchive); + /* Something goes wrong with this ZIP archive, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } +success: + /* Reset the loop cursor */ + SyArchiveResetLoopCursor(pArchive); + /* Return the in-memory archive as a resource handle */ + jx9_result_resource(pCtx, pArchive); + return JX9_OK; +} +/* + * void zip_close(resource $zip) + * Close an in-memory ZIP archive. + * Parameters + * $zip + * A ZIP file previously opened with zip_open(). + * Return + * null. + */ +static int jx9Builtin_zip_close(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + SyArchive *pArchive; + zip_raw_data *pRaw; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments */ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive"); + return JX9_OK; + } + /* Point to the in-memory archive */ + pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid ZIP archive */ + if( SXARCH_INVALID(pArchive) ){ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive"); + return JX9_OK; + } + /* Release the archive */ + SyArchiveRelease(pArchive); + pRaw = (zip_raw_data *)&pArchive[1]; + if( pRaw->iType == ZIP_RAW_DATA_MEMBUF ){ + SyBlobRelease(&pRaw->raw.sBlob); + }else{ + const jx9_vfs *pVfs = pRaw->raw.mmap.pVfs; + if( pVfs->xUnmap ){ + /* Unmap the memory view */ + pVfs->xUnmap(pRaw->raw.mmap.pMap, pRaw->raw.mmap.nSize); + } + } + /* Release the memory chunk */ + jx9_context_free_chunk(pCtx, pArchive); + return JX9_OK; +} +/* + * mixed zip_read(resource $zip) + * Reads the next entry from an in-memory ZIP archive. + * Parameters + * $zip + * A ZIP file previously opened with zip_open(). + * Return + * A directory entry resource for later use with the zip_entry_... functions + * or FALSE if there are no more entries to read, or an error code if an error occurred. + */ +static int jx9Builtin_zip_read(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + SyArchiveEntry *pNext = 0; /* cc warning */ + SyArchive *pArchive; + sxi32 rc; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments */ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive"); + /* return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the in-memory archive */ + pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid ZIP archive */ + if( SXARCH_INVALID(pArchive) ){ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive"); + /* return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the next entry */ + rc = SyArchiveGetNextEntry(pArchive, &pNext); + if( rc != SXRET_OK ){ + /* No more entries in the central directory, return FALSE */ + jx9_result_bool(pCtx, 0); + }else{ + /* Return as a resource handle */ + jx9_result_resource(pCtx, pNext); + /* Point to the ZIP raw data */ + pNext->pUserData = (void *)&pArchive[1]; + } + return JX9_OK; +} +/* + * bool zip_entry_open(resource $zip, resource $zip_entry[, string $mode ]) + * Open a directory entry for reading + * Parameters + * $zip + * A ZIP file previously opened with zip_open(). + * $zip_entry + * A directory entry returned by zip_read(). + * $mode + * Not used + * Return + * A directory entry resource for later use with the zip_entry_... functions + * or FALSE if there are no more entries to read, or an error code if an error occurred. + */ +static int jx9Builtin_zip_entry_open(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + SyArchiveEntry *pEntry; + SyArchive *pArchive; + if( nArg < 2 || !jx9_value_is_resource(apArg[0]) || !jx9_value_is_resource(apArg[1]) ){ + /* Missing/Invalid arguments */ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive"); + /* return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the in-memory archive */ + pArchive = (SyArchive *)jx9_value_to_resource(apArg[0]); + /* Make sure we are dealing with a valid ZIP archive */ + if( SXARCH_INVALID(pArchive) ){ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive"); + /* return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Make sure we are dealing with a valid ZIP archive entry */ + pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[1]); + if( SXARCH_ENTRY_INVALID(pEntry) ){ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); + /* return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* All done. Actually this function is a no-op, return TRUE */ + jx9_result_bool(pCtx, 1); + return JX9_OK; +} +/* + * bool zip_entry_close(resource $zip_entry) + * Close a directory entry. + * Parameters + * $zip_entry + * A directory entry returned by zip_read(). + * Return + * Returns TRUE on success or FALSE on failure. + */ +static int jx9Builtin_zip_entry_close(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + SyArchiveEntry *pEntry; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments */ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); + /* return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Make sure we are dealing with a valid ZIP archive entry */ + pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]); + if( SXARCH_ENTRY_INVALID(pEntry) ){ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); + /* return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Reset the read cursor */ + pEntry->nReadCount = 0; + /*All done. Actually this function is a no-op, return TRUE */ + jx9_result_bool(pCtx, 1); + return JX9_OK; +} +/* + * string zip_entry_name(resource $zip_entry) + * Retrieve the name of a directory entry. + * Parameters + * $zip_entry + * A directory entry returned by zip_read(). + * Return + * The name of the directory entry. + */ +static int jx9Builtin_zip_entry_name(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + SyArchiveEntry *pEntry; + SyString *pName; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments */ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); + /* return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Make sure we are dealing with a valid ZIP archive entry */ + pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]); + if( SXARCH_ENTRY_INVALID(pEntry) ){ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); + /* return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Return entry name */ + pName = &pEntry->sFileName; + jx9_result_string(pCtx, pName->zString, (int)pName->nByte); + return JX9_OK; +} +/* + * int64 zip_entry_filesize(resource $zip_entry) + * Retrieve the actual file size of a directory entry. + * Parameters + * $zip_entry + * A directory entry returned by zip_read(). + * Return + * The size of the directory entry. + */ +static int jx9Builtin_zip_entry_filesize(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + SyArchiveEntry *pEntry; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments */ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); + /* return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Make sure we are dealing with a valid ZIP archive entry */ + pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]); + if( SXARCH_ENTRY_INVALID(pEntry) ){ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); + /* return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Return entry size */ + jx9_result_int64(pCtx, (jx9_int64)pEntry->nByte); + return JX9_OK; +} +/* + * int64 zip_entry_compressedsize(resource $zip_entry) + * Retrieve the compressed size of a directory entry. + * Parameters + * $zip_entry + * A directory entry returned by zip_read(). + * Return + * The compressed size. + */ +static int jx9Builtin_zip_entry_compressedsize(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + SyArchiveEntry *pEntry; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments */ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); + /* return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Make sure we are dealing with a valid ZIP archive entry */ + pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]); + if( SXARCH_ENTRY_INVALID(pEntry) ){ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); + /* return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Return entry compressed size */ + jx9_result_int64(pCtx, (jx9_int64)pEntry->nByteCompr); + return JX9_OK; +} +/* + * string zip_entry_read(resource $zip_entry[, int $length]) + * Reads from an open directory entry. + * Parameters + * $zip_entry + * A directory entry returned by zip_read(). + * $length + * The number of bytes to return. If not specified, this function + * will attempt to read 1024 bytes. + * Return + * Returns the data read, or FALSE if the end of the file is reached. + */ +static int jx9Builtin_zip_entry_read(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + SyArchiveEntry *pEntry; + zip_raw_data *pRaw; + const char *zData; + int iLength; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments */ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); + /* return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Make sure we are dealing with a valid ZIP archive entry */ + pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]); + if( SXARCH_ENTRY_INVALID(pEntry) ){ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); + /* return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + zData = 0; + if( pEntry->nReadCount >= pEntry->nByteCompr ){ + /* No more data to read, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Set a default read length */ + iLength = 1024; + if( nArg > 1 ){ + iLength = jx9_value_to_int(apArg[1]); + if( iLength < 1 ){ + iLength = 1024; + } + } + if( (sxu32)iLength > pEntry->nByteCompr - pEntry->nReadCount ){ + iLength = (int)(pEntry->nByteCompr - pEntry->nReadCount); + } + /* Return the entry contents */ + pRaw = (zip_raw_data *)pEntry->pUserData; + if( pRaw->iType == ZIP_RAW_DATA_MEMBUF ){ + zData = (const char *)SyBlobDataAt(&pRaw->raw.sBlob, (pEntry->nOfft+pEntry->nReadCount)); + }else{ + const char *zMap = (const char *)pRaw->raw.mmap.pMap; + /* Memory mmaped chunk */ + zData = &zMap[pEntry->nOfft+pEntry->nReadCount]; + } + /* Increment the read counter */ + pEntry->nReadCount += iLength; + /* Return the raw data */ + jx9_result_string(pCtx, zData, iLength); + return JX9_OK; +} +/* + * bool zip_entry_reset_cursor(resource $zip_entry) + * Reset the read cursor of an open directory entry. + * Parameters + * $zip_entry + * A directory entry returned by zip_read(). + * Return + * TRUE on success, FALSE on failure. + */ +static int jx9Builtin_zip_entry_reset_cursor(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + SyArchiveEntry *pEntry; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments */ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); + /* return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Make sure we are dealing with a valid ZIP archive entry */ + pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]); + if( SXARCH_ENTRY_INVALID(pEntry) ){ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); + /* return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Reset the cursor */ + pEntry->nReadCount = 0; + /* Return TRUE */ + jx9_result_bool(pCtx, 1); + return JX9_OK; +} +/* + * string zip_entry_compressionmethod(resource $zip_entry) + * Retrieve the compression method of a directory entry. + * Parameters + * $zip_entry + * A directory entry returned by zip_read(). + * Return + * The compression method on success or FALSE on failure. + */ +static int jx9Builtin_zip_entry_compressionmethod(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + SyArchiveEntry *pEntry; + if( nArg < 1 || !jx9_value_is_resource(apArg[0]) ){ + /* Missing/Invalid arguments */ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); + /* return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Make sure we are dealing with a valid ZIP archive entry */ + pEntry = (SyArchiveEntry *)jx9_value_to_resource(apArg[0]); + if( SXARCH_ENTRY_INVALID(pEntry) ){ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "Expecting a ZIP archive entry"); + /* return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + switch(pEntry->nComprMeth){ + case 0: + /* No compression;entry is stored */ + jx9_result_string(pCtx, "stored", (int)sizeof("stored")-1); + break; + case 8: + /* Entry is deflated (Default compression algorithm) */ + jx9_result_string(pCtx, "deflate", (int)sizeof("deflate")-1); + break; + /* Exotic compression algorithms */ + case 1: + jx9_result_string(pCtx, "shrunk", (int)sizeof("shrunk")-1); + break; + case 2: + case 3: + case 4: + case 5: + /* Entry is reduced */ + jx9_result_string(pCtx, "reduced", (int)sizeof("reduced")-1); + break; + case 6: + /* Entry is imploded */ + jx9_result_string(pCtx, "implode", (int)sizeof("implode")-1); + break; + default: + jx9_result_string(pCtx, "unknown", (int)sizeof("unknown")-1); + break; + } + return JX9_OK; +} +#endif /* #ifndef JX9_DISABLE_BUILTIN_FUNC*/ +/* NULL VFS [i.e: a no-op VFS]*/ +static const jx9_vfs null_vfs = { + "null_vfs", + JX9_VFS_VERSION, + 0, /* int (*xChdir)(const char *) */ + 0, /* int (*xChroot)(const char *); */ + 0, /* int (*xGetcwd)(jx9_context *) */ + 0, /* int (*xMkdir)(const char *, int, int) */ + 0, /* int (*xRmdir)(const char *) */ + 0, /* int (*xIsdir)(const char *) */ + 0, /* int (*xRename)(const char *, const char *) */ + 0, /*int (*xRealpath)(const char *, jx9_context *)*/ + 0, /* int (*xSleep)(unsigned int) */ + 0, /* int (*xUnlink)(const char *) */ + 0, /* int (*xFileExists)(const char *) */ + 0, /*int (*xChmod)(const char *, int)*/ + 0, /*int (*xChown)(const char *, const char *)*/ + 0, /*int (*xChgrp)(const char *, const char *)*/ + 0, /* jx9_int64 (*xFreeSpace)(const char *) */ + 0, /* jx9_int64 (*xTotalSpace)(const char *) */ + 0, /* jx9_int64 (*xFileSize)(const char *) */ + 0, /* jx9_int64 (*xFileAtime)(const char *) */ + 0, /* jx9_int64 (*xFileMtime)(const char *) */ + 0, /* jx9_int64 (*xFileCtime)(const char *) */ + 0, /* int (*xStat)(const char *, jx9_value *, jx9_value *) */ + 0, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */ + 0, /* int (*xIsfile)(const char *) */ + 0, /* int (*xIslink)(const char *) */ + 0, /* int (*xReadable)(const char *) */ + 0, /* int (*xWritable)(const char *) */ + 0, /* int (*xExecutable)(const char *) */ + 0, /* int (*xFiletype)(const char *, jx9_context *) */ + 0, /* int (*xGetenv)(const char *, jx9_context *) */ + 0, /* int (*xSetenv)(const char *, const char *) */ + 0, /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */ + 0, /* int (*xMmap)(const char *, void **, jx9_int64 *) */ + 0, /* void (*xUnmap)(void *, jx9_int64); */ + 0, /* int (*xLink)(const char *, const char *, int) */ + 0, /* int (*xUmask)(int) */ + 0, /* void (*xTempDir)(jx9_context *) */ + 0, /* unsigned int (*xProcessId)(void) */ + 0, /* int (*xUid)(void) */ + 0, /* int (*xGid)(void) */ + 0, /* void (*xUsername)(jx9_context *) */ + 0 /* int (*xExec)(const char *, jx9_context *) */ +}; +#ifndef JX9_DISABLE_BUILTIN_FUNC +#ifndef JX9_DISABLE_DISK_IO +#ifdef __WINNT__ +/* + * Windows VFS implementation for the JX9 engine. + * Authors: + * Symisc Systems, devel@symisc.net. + * Copyright (C) Symisc Systems, http://jx9.symisc.net + * Status: + * Stable. + */ +/* What follows here is code that is specific to windows systems. */ +#include +/* +** Convert a UTF-8 string to microsoft unicode (UTF-16?). +** +** Space to hold the returned string is obtained from HeapAlloc(). +** Taken from the sqlite3 source tree +** status: Public Domain +*/ +static WCHAR *jx9utf8ToUnicode(const char *zFilename){ + int nChar; + WCHAR *zWideFilename; + + nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, 0, 0); + zWideFilename = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, nChar*sizeof(zWideFilename[0])); + if( zWideFilename == 0 ){ + return 0; + } + nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar); + if( nChar==0 ){ + HeapFree(GetProcessHeap(), 0, zWideFilename); + return 0; + } + return zWideFilename; +} +/* +** Convert a UTF-8 filename into whatever form the underlying +** operating system wants filenames in.Space to hold the result +** is obtained from HeapAlloc() and must be freed by the calling +** function. +** Taken from the sqlite3 source tree +** status: Public Domain +*/ +static void *jx9convertUtf8Filename(const char *zFilename){ + void *zConverted; + zConverted = jx9utf8ToUnicode(zFilename); + return zConverted; +} +/* +** Convert microsoft unicode to UTF-8. Space to hold the returned string is +** obtained from HeapAlloc(). +** Taken from the sqlite3 source tree +** status: Public Domain +*/ +static char *jx9unicodeToUtf8(const WCHAR *zWideFilename){ + char *zFilename; + int nByte; + + nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0); + zFilename = (char *)HeapAlloc(GetProcessHeap(), 0, nByte); + if( zFilename == 0 ){ + return 0; + } + nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, 0, 0); + if( nByte == 0 ){ + HeapFree(GetProcessHeap(), 0, zFilename); + return 0; + } + return zFilename; +} +/* int (*xchdir)(const char *) */ +static int WinVfs_chdir(const char *zPath) +{ + void * pConverted; + BOOL rc; + pConverted = jx9convertUtf8Filename(zPath); + if( pConverted == 0 ){ + return -1; + } + rc = SetCurrentDirectoryW((LPCWSTR)pConverted); + HeapFree(GetProcessHeap(), 0, pConverted); + return rc ? JX9_OK : -1; +} +/* int (*xGetcwd)(jx9_context *) */ +static int WinVfs_getcwd(jx9_context *pCtx) +{ + WCHAR zDir[2048]; + char *zConverted; + DWORD rc; + /* Get the current directory */ + rc = GetCurrentDirectoryW(sizeof(zDir), zDir); + if( rc < 1 ){ + return -1; + } + zConverted = jx9unicodeToUtf8(zDir); + if( zConverted == 0 ){ + return -1; + } + jx9_result_string(pCtx, zConverted, -1/*Compute length automatically*/); /* Will make it's own copy */ + HeapFree(GetProcessHeap(), 0, zConverted); + return JX9_OK; +} +/* int (*xMkdir)(const char *, int, int) */ +static int WinVfs_mkdir(const char *zPath, int mode, int recursive) +{ + void * pConverted; + BOOL rc; + pConverted = jx9convertUtf8Filename(zPath); + if( pConverted == 0 ){ + return -1; + } + mode= 0; /* MSVC warning */ + recursive = 0; + rc = CreateDirectoryW((LPCWSTR)pConverted, 0); + HeapFree(GetProcessHeap(), 0, pConverted); + return rc ? JX9_OK : -1; +} +/* int (*xRmdir)(const char *) */ +static int WinVfs_rmdir(const char *zPath) +{ + void * pConverted; + BOOL rc; + pConverted = jx9convertUtf8Filename(zPath); + if( pConverted == 0 ){ + return -1; + } + rc = RemoveDirectoryW((LPCWSTR)pConverted); + HeapFree(GetProcessHeap(), 0, pConverted); + return rc ? JX9_OK : -1; +} +/* int (*xIsdir)(const char *) */ +static int WinVfs_isdir(const char *zPath) +{ + void * pConverted; + DWORD dwAttr; + pConverted = jx9convertUtf8Filename(zPath); + if( pConverted == 0 ){ + return -1; + } + dwAttr = GetFileAttributesW((LPCWSTR)pConverted); + HeapFree(GetProcessHeap(), 0, pConverted); + if( dwAttr == INVALID_FILE_ATTRIBUTES ){ + return -1; + } + return (dwAttr & FILE_ATTRIBUTE_DIRECTORY) ? JX9_OK : -1; +} +/* int (*xRename)(const char *, const char *) */ +static int WinVfs_Rename(const char *zOld, const char *zNew) +{ + void *pOld, *pNew; + BOOL rc = 0; + pOld = jx9convertUtf8Filename(zOld); + if( pOld == 0 ){ + return -1; + } + pNew = jx9convertUtf8Filename(zNew); + if( pNew ){ + rc = MoveFileW((LPCWSTR)pOld, (LPCWSTR)pNew); + } + HeapFree(GetProcessHeap(), 0, pOld); + if( pNew ){ + HeapFree(GetProcessHeap(), 0, pNew); + } + return rc ? JX9_OK : - 1; +} +/* int (*xRealpath)(const char *, jx9_context *) */ +static int WinVfs_Realpath(const char *zPath, jx9_context *pCtx) +{ + WCHAR zTemp[2048]; + void *pPath; + char *zReal; + DWORD n; + pPath = jx9convertUtf8Filename(zPath); + if( pPath == 0 ){ + return -1; + } + n = GetFullPathNameW((LPCWSTR)pPath, 0, 0, 0); + if( n > 0 ){ + if( n >= sizeof(zTemp) ){ + n = sizeof(zTemp) - 1; + } + GetFullPathNameW((LPCWSTR)pPath, n, zTemp, 0); + } + HeapFree(GetProcessHeap(), 0, pPath); + if( !n ){ + return -1; + } + zReal = jx9unicodeToUtf8(zTemp); + if( zReal == 0 ){ + return -1; + } + jx9_result_string(pCtx, zReal, -1); /* Will make it's own copy */ + HeapFree(GetProcessHeap(), 0, zReal); + return JX9_OK; +} +/* int (*xSleep)(unsigned int) */ +static int WinVfs_Sleep(unsigned int uSec) +{ + Sleep(uSec/1000/*uSec per Millisec */); + return JX9_OK; +} +/* int (*xUnlink)(const char *) */ +static int WinVfs_unlink(const char *zPath) +{ + void * pConverted; + BOOL rc; + pConverted = jx9convertUtf8Filename(zPath); + if( pConverted == 0 ){ + return -1; + } + rc = DeleteFileW((LPCWSTR)pConverted); + HeapFree(GetProcessHeap(), 0, pConverted); + return rc ? JX9_OK : - 1; +} +/* jx9_int64 (*xFreeSpace)(const char *) */ +static jx9_int64 WinVfs_DiskFreeSpace(const char *zPath) +{ +#ifdef _WIN32_WCE + /* GetDiskFreeSpace is not supported under WINCE */ + SXUNUSED(zPath); + return 0; +#else + DWORD dwSectPerClust, dwBytesPerSect, dwFreeClusters, dwTotalClusters; + void * pConverted; + WCHAR *p; + BOOL rc; + pConverted = jx9convertUtf8Filename(zPath); + if( pConverted == 0 ){ + return 0; + } + p = (WCHAR *)pConverted; + for(;*p;p++){ + if( *p == '\\' || *p == '/'){ + *p = '\0'; + break; + } + } + rc = GetDiskFreeSpaceW((LPCWSTR)pConverted, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters); + if( !rc ){ + return 0; + } + return (jx9_int64)dwFreeClusters * dwSectPerClust * dwBytesPerSect; +#endif +} +/* jx9_int64 (*xTotalSpace)(const char *) */ +static jx9_int64 WinVfs_DiskTotalSpace(const char *zPath) +{ +#ifdef _WIN32_WCE + /* GetDiskFreeSpace is not supported under WINCE */ + SXUNUSED(zPath); + return 0; +#else + DWORD dwSectPerClust, dwBytesPerSect, dwFreeClusters, dwTotalClusters; + void * pConverted; + WCHAR *p; + BOOL rc; + pConverted = jx9convertUtf8Filename(zPath); + if( pConverted == 0 ){ + return 0; + } + p = (WCHAR *)pConverted; + for(;*p;p++){ + if( *p == '\\' || *p == '/'){ + *p = '\0'; + break; + } + } + rc = GetDiskFreeSpaceW((LPCWSTR)pConverted, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters); + if( !rc ){ + return 0; + } + return (jx9_int64)dwTotalClusters * dwSectPerClust * dwBytesPerSect; +#endif +} +/* int (*xFileExists)(const char *) */ +static int WinVfs_FileExists(const char *zPath) +{ + void * pConverted; + DWORD dwAttr; + pConverted = jx9convertUtf8Filename(zPath); + if( pConverted == 0 ){ + return -1; + } + dwAttr = GetFileAttributesW((LPCWSTR)pConverted); + HeapFree(GetProcessHeap(), 0, pConverted); + if( dwAttr == INVALID_FILE_ATTRIBUTES ){ + return -1; + } + return JX9_OK; +} +/* Open a file in a read-only mode */ +static HANDLE OpenReadOnly(LPCWSTR pPath) +{ + DWORD dwType = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS; + DWORD dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE; + DWORD dwAccess = GENERIC_READ; + DWORD dwCreate = OPEN_EXISTING; + HANDLE pHandle; + pHandle = CreateFileW(pPath, dwAccess, dwShare, 0, dwCreate, dwType, 0); + if( pHandle == INVALID_HANDLE_VALUE){ + return 0; + } + return pHandle; +} +/* jx9_int64 (*xFileSize)(const char *) */ +static jx9_int64 WinVfs_FileSize(const char *zPath) +{ + DWORD dwLow, dwHigh; + void * pConverted; + jx9_int64 nSize; + HANDLE pHandle; + + pConverted = jx9convertUtf8Filename(zPath); + if( pConverted == 0 ){ + return -1; + } + /* Open the file in read-only mode */ + pHandle = OpenReadOnly((LPCWSTR)pConverted); + HeapFree(GetProcessHeap(), 0, pConverted); + if( pHandle ){ + dwLow = GetFileSize(pHandle, &dwHigh); + nSize = dwHigh; + nSize <<= 32; + nSize += dwLow; + CloseHandle(pHandle); + }else{ + nSize = -1; + } + return nSize; +} +#define TICKS_PER_SECOND 10000000 +#define EPOCH_DIFFERENCE 11644473600LL +/* Convert Windows timestamp to UNIX timestamp */ +static jx9_int64 convertWindowsTimeToUnixTime(LPFILETIME pTime) +{ + jx9_int64 input, temp; + input = pTime->dwHighDateTime; + input <<= 32; + input += pTime->dwLowDateTime; + temp = input / TICKS_PER_SECOND; /*convert from 100ns intervals to seconds*/ + temp = temp - EPOCH_DIFFERENCE; /*subtract number of seconds between epochs*/ + return temp; +} +/* Convert UNIX timestamp to Windows timestamp */ +static void convertUnixTimeToWindowsTime(jx9_int64 nUnixtime, LPFILETIME pOut) +{ + jx9_int64 result = EPOCH_DIFFERENCE; + result += nUnixtime; + result *= 10000000LL; + pOut->dwHighDateTime = (DWORD)(nUnixtime>>32); + pOut->dwLowDateTime = (DWORD)nUnixtime; +} +/* int (*xTouch)(const char *, jx9_int64, jx9_int64) */ +static int WinVfs_Touch(const char *zPath, jx9_int64 touch_time, jx9_int64 access_time) +{ + FILETIME sTouch, sAccess; + void *pConverted; + void *pHandle; + BOOL rc = 0; + pConverted = jx9convertUtf8Filename(zPath); + if( pConverted == 0 ){ + return -1; + } + pHandle = OpenReadOnly((LPCWSTR)pConverted); + if( pHandle ){ + if( touch_time < 0 ){ + GetSystemTimeAsFileTime(&sTouch); + }else{ + convertUnixTimeToWindowsTime(touch_time, &sTouch); + } + if( access_time < 0 ){ + /* Use the touch time */ + sAccess = sTouch; /* Structure assignment */ + }else{ + convertUnixTimeToWindowsTime(access_time, &sAccess); + } + rc = SetFileTime(pHandle, &sTouch, &sAccess, 0); + /* Close the handle */ + CloseHandle(pHandle); + } + HeapFree(GetProcessHeap(), 0, pConverted); + return rc ? JX9_OK : -1; +} +/* jx9_int64 (*xFileAtime)(const char *) */ +static jx9_int64 WinVfs_FileAtime(const char *zPath) +{ + BY_HANDLE_FILE_INFORMATION sInfo; + void * pConverted; + jx9_int64 atime; + HANDLE pHandle; + pConverted = jx9convertUtf8Filename(zPath); + if( pConverted == 0 ){ + return -1; + } + /* Open the file in read-only mode */ + pHandle = OpenReadOnly((LPCWSTR)pConverted); + if( pHandle ){ + BOOL rc; + rc = GetFileInformationByHandle(pHandle, &sInfo); + if( rc ){ + atime = convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime); + }else{ + atime = -1; + } + CloseHandle(pHandle); + }else{ + atime = -1; + } + HeapFree(GetProcessHeap(), 0, pConverted); + return atime; +} +/* jx9_int64 (*xFileMtime)(const char *) */ +static jx9_int64 WinVfs_FileMtime(const char *zPath) +{ + BY_HANDLE_FILE_INFORMATION sInfo; + void * pConverted; + jx9_int64 mtime; + HANDLE pHandle; + pConverted = jx9convertUtf8Filename(zPath); + if( pConverted == 0 ){ + return -1; + } + /* Open the file in read-only mode */ + pHandle = OpenReadOnly((LPCWSTR)pConverted); + if( pHandle ){ + BOOL rc; + rc = GetFileInformationByHandle(pHandle, &sInfo); + if( rc ){ + mtime = convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime); + }else{ + mtime = -1; + } + CloseHandle(pHandle); + }else{ + mtime = -1; + } + HeapFree(GetProcessHeap(), 0, pConverted); + return mtime; +} +/* jx9_int64 (*xFileCtime)(const char *) */ +static jx9_int64 WinVfs_FileCtime(const char *zPath) +{ + BY_HANDLE_FILE_INFORMATION sInfo; + void * pConverted; + jx9_int64 ctime; + HANDLE pHandle; + pConverted = jx9convertUtf8Filename(zPath); + if( pConverted == 0 ){ + return -1; + } + /* Open the file in read-only mode */ + pHandle = OpenReadOnly((LPCWSTR)pConverted); + if( pHandle ){ + BOOL rc; + rc = GetFileInformationByHandle(pHandle, &sInfo); + if( rc ){ + ctime = convertWindowsTimeToUnixTime(&sInfo.ftCreationTime); + }else{ + ctime = -1; + } + CloseHandle(pHandle); + }else{ + ctime = -1; + } + HeapFree(GetProcessHeap(), 0, pConverted); + return ctime; +} +/* int (*xStat)(const char *, jx9_value *, jx9_value *) */ +/* int (*xlStat)(const char *, jx9_value *, jx9_value *) */ +static int WinVfs_Stat(const char *zPath, jx9_value *pArray, jx9_value *pWorker) +{ + BY_HANDLE_FILE_INFORMATION sInfo; + void *pConverted; + HANDLE pHandle; + BOOL rc; + pConverted = jx9convertUtf8Filename(zPath); + if( pConverted == 0 ){ + return -1; + } + /* Open the file in read-only mode */ + pHandle = OpenReadOnly((LPCWSTR)pConverted); + HeapFree(GetProcessHeap(), 0, pConverted); + if( pHandle == 0 ){ + return -1; + } + rc = GetFileInformationByHandle(pHandle, &sInfo); + CloseHandle(pHandle); + if( !rc ){ + return -1; + } + /* dev */ + jx9_value_int64(pWorker, (jx9_int64)sInfo.dwVolumeSerialNumber); + jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */ + /* ino */ + jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileIndexHigh << 32) | sInfo.nFileIndexLow)); + jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */ + /* mode */ + jx9_value_int(pWorker, 0); + jx9_array_add_strkey_elem(pArray, "mode", pWorker); + /* nlink */ + jx9_value_int(pWorker, (int)sInfo.nNumberOfLinks); + jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */ + /* uid, gid, rdev */ + jx9_value_int(pWorker, 0); + jx9_array_add_strkey_elem(pArray, "uid", pWorker); + jx9_array_add_strkey_elem(pArray, "gid", pWorker); + jx9_array_add_strkey_elem(pArray, "rdev", pWorker); + /* size */ + jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileSizeHigh << 32) | sInfo.nFileSizeLow)); + jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */ + /* atime */ + jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime)); + jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */ + /* mtime */ + jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime)); + jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */ + /* ctime */ + jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftCreationTime)); + jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */ + /* blksize, blocks */ + jx9_value_int(pWorker, 0); + jx9_array_add_strkey_elem(pArray, "blksize", pWorker); + jx9_array_add_strkey_elem(pArray, "blocks", pWorker); + return JX9_OK; +} +/* int (*xIsfile)(const char *) */ +static int WinVfs_isfile(const char *zPath) +{ + void * pConverted; + DWORD dwAttr; + pConverted = jx9convertUtf8Filename(zPath); + if( pConverted == 0 ){ + return -1; + } + dwAttr = GetFileAttributesW((LPCWSTR)pConverted); + HeapFree(GetProcessHeap(), 0, pConverted); + if( dwAttr == INVALID_FILE_ATTRIBUTES ){ + return -1; + } + return (dwAttr & (FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE)) ? JX9_OK : -1; +} +/* int (*xIslink)(const char *) */ +static int WinVfs_islink(const char *zPath) +{ + void * pConverted; + DWORD dwAttr; + pConverted = jx9convertUtf8Filename(zPath); + if( pConverted == 0 ){ + return -1; + } + dwAttr = GetFileAttributesW((LPCWSTR)pConverted); + HeapFree(GetProcessHeap(), 0, pConverted); + if( dwAttr == INVALID_FILE_ATTRIBUTES ){ + return -1; + } + return (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT) ? JX9_OK : -1; +} +/* int (*xWritable)(const char *) */ +static int WinVfs_iswritable(const char *zPath) +{ + void * pConverted; + DWORD dwAttr; + pConverted = jx9convertUtf8Filename(zPath); + if( pConverted == 0 ){ + return -1; + } + dwAttr = GetFileAttributesW((LPCWSTR)pConverted); + HeapFree(GetProcessHeap(), 0, pConverted); + if( dwAttr == INVALID_FILE_ATTRIBUTES ){ + return -1; + } + if( (dwAttr & (FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_NORMAL)) == 0 ){ + /* Not a regular file */ + return -1; + } + if( dwAttr & FILE_ATTRIBUTE_READONLY ){ + /* Read-only file */ + return -1; + } + /* File is writable */ + return JX9_OK; +} +/* int (*xExecutable)(const char *) */ +static int WinVfs_isexecutable(const char *zPath) +{ + void * pConverted; + DWORD dwAttr; + pConverted = jx9convertUtf8Filename(zPath); + if( pConverted == 0 ){ + return -1; + } + dwAttr = GetFileAttributesW((LPCWSTR)pConverted); + HeapFree(GetProcessHeap(), 0, pConverted); + if( dwAttr == INVALID_FILE_ATTRIBUTES ){ + return -1; + } + if( (dwAttr & FILE_ATTRIBUTE_NORMAL) == 0 ){ + /* Not a regular file */ + return -1; + } + /* File is executable */ + return JX9_OK; +} +/* int (*xFiletype)(const char *, jx9_context *) */ +static int WinVfs_Filetype(const char *zPath, jx9_context *pCtx) +{ + void * pConverted; + DWORD dwAttr; + pConverted = jx9convertUtf8Filename(zPath); + if( pConverted == 0 ){ + /* Expand 'unknown' */ + jx9_result_string(pCtx, "unknown", sizeof("unknown")-1); + return -1; + } + dwAttr = GetFileAttributesW((LPCWSTR)pConverted); + HeapFree(GetProcessHeap(), 0, pConverted); + if( dwAttr == INVALID_FILE_ATTRIBUTES ){ + /* Expand 'unknown' */ + jx9_result_string(pCtx, "unknown", sizeof("unknown")-1); + return -1; + } + if(dwAttr & (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE) ){ + jx9_result_string(pCtx, "file", sizeof("file")-1); + }else if(dwAttr & FILE_ATTRIBUTE_DIRECTORY){ + jx9_result_string(pCtx, "dir", sizeof("dir")-1); + }else if(dwAttr & FILE_ATTRIBUTE_REPARSE_POINT){ + jx9_result_string(pCtx, "link", sizeof("link")-1); + }else if(dwAttr & (FILE_ATTRIBUTE_DEVICE)){ + jx9_result_string(pCtx, "block", sizeof("block")-1); + }else{ + jx9_result_string(pCtx, "unknown", sizeof("unknown")-1); + } + return JX9_OK; +} +/* int (*xGetenv)(const char *, jx9_context *) */ +static int WinVfs_Getenv(const char *zVar, jx9_context *pCtx) +{ + char zValue[1024]; + DWORD n; + /* + * According to MSDN + * If lpBuffer is not large enough to hold the data, the return + * value is the buffer size, in characters, required to hold the + * string and its terminating null character and the contents + * of lpBuffer are undefined. + */ + n = sizeof(zValue); + SyMemcpy("Undefined", zValue, sizeof("Undefined")-1); + /* Extract the environment value */ + n = GetEnvironmentVariableA(zVar, zValue, sizeof(zValue)); + if( !n ){ + /* No such variable*/ + return -1; + } + jx9_result_string(pCtx, zValue, (int)n); + return JX9_OK; +} +/* int (*xSetenv)(const char *, const char *) */ +static int WinVfs_Setenv(const char *zName, const char *zValue) +{ + BOOL rc; + rc = SetEnvironmentVariableA(zName, zValue); + return rc ? JX9_OK : -1; +} +/* int (*xMmap)(const char *, void **, jx9_int64 *) */ +static int WinVfs_Mmap(const char *zPath, void **ppMap, jx9_int64 *pSize) +{ + DWORD dwSizeLow, dwSizeHigh; + HANDLE pHandle, pMapHandle; + void *pConverted, *pView; + + pConverted = jx9convertUtf8Filename(zPath); + if( pConverted == 0 ){ + return -1; + } + pHandle = OpenReadOnly((LPCWSTR)pConverted); + HeapFree(GetProcessHeap(), 0, pConverted); + if( pHandle == 0 ){ + return -1; + } + /* Get the file size */ + dwSizeLow = GetFileSize(pHandle, &dwSizeHigh); + /* Create the mapping */ + pMapHandle = CreateFileMappingW(pHandle, 0, PAGE_READONLY, dwSizeHigh, dwSizeLow, 0); + if( pMapHandle == 0 ){ + CloseHandle(pHandle); + return -1; + } + *pSize = ((jx9_int64)dwSizeHigh << 32) | dwSizeLow; + /* Obtain the view */ + pView = MapViewOfFile(pMapHandle, FILE_MAP_READ, 0, 0, (SIZE_T)(*pSize)); + if( pView ){ + /* Let the upper layer point to the view */ + *ppMap = pView; + } + /* Close the handle + * According to MSDN it's OK the close the HANDLES. + */ + CloseHandle(pMapHandle); + CloseHandle(pHandle); + return pView ? JX9_OK : -1; +} +/* void (*xUnmap)(void *, jx9_int64) */ +static void WinVfs_Unmap(void *pView, jx9_int64 nSize) +{ + nSize = 0; /* Compiler warning */ + UnmapViewOfFile(pView); +} +/* void (*xTempDir)(jx9_context *) */ +static void WinVfs_TempDir(jx9_context *pCtx) +{ + CHAR zTemp[1024]; + DWORD n; + n = GetTempPathA(sizeof(zTemp), zTemp); + if( n < 1 ){ + /* Assume the default windows temp directory */ + jx9_result_string(pCtx, "C:\\Windows\\Temp", -1/*Compute length automatically*/); + }else{ + jx9_result_string(pCtx, zTemp, (int)n); + } +} +/* unsigned int (*xProcessId)(void) */ +static unsigned int WinVfs_ProcessId(void) +{ + DWORD nID = 0; +#ifndef __MINGW32__ + nID = GetProcessId(GetCurrentProcess()); +#endif /* __MINGW32__ */ + return (unsigned int)nID; +} + +/* Export the windows vfs */ +static const jx9_vfs sWinVfs = { + "Windows_vfs", + JX9_VFS_VERSION, + WinVfs_chdir, /* int (*xChdir)(const char *) */ + 0, /* int (*xChroot)(const char *); */ + WinVfs_getcwd, /* int (*xGetcwd)(jx9_context *) */ + WinVfs_mkdir, /* int (*xMkdir)(const char *, int, int) */ + WinVfs_rmdir, /* int (*xRmdir)(const char *) */ + WinVfs_isdir, /* int (*xIsdir)(const char *) */ + WinVfs_Rename, /* int (*xRename)(const char *, const char *) */ + WinVfs_Realpath, /*int (*xRealpath)(const char *, jx9_context *)*/ + WinVfs_Sleep, /* int (*xSleep)(unsigned int) */ + WinVfs_unlink, /* int (*xUnlink)(const char *) */ + WinVfs_FileExists, /* int (*xFileExists)(const char *) */ + 0, /*int (*xChmod)(const char *, int)*/ + 0, /*int (*xChown)(const char *, const char *)*/ + 0, /*int (*xChgrp)(const char *, const char *)*/ + WinVfs_DiskFreeSpace, /* jx9_int64 (*xFreeSpace)(const char *) */ + WinVfs_DiskTotalSpace, /* jx9_int64 (*xTotalSpace)(const char *) */ + WinVfs_FileSize, /* jx9_int64 (*xFileSize)(const char *) */ + WinVfs_FileAtime, /* jx9_int64 (*xFileAtime)(const char *) */ + WinVfs_FileMtime, /* jx9_int64 (*xFileMtime)(const char *) */ + WinVfs_FileCtime, /* jx9_int64 (*xFileCtime)(const char *) */ + WinVfs_Stat, /* int (*xStat)(const char *, jx9_value *, jx9_value *) */ + WinVfs_Stat, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */ + WinVfs_isfile, /* int (*xIsfile)(const char *) */ + WinVfs_islink, /* int (*xIslink)(const char *) */ + WinVfs_isfile, /* int (*xReadable)(const char *) */ + WinVfs_iswritable, /* int (*xWritable)(const char *) */ + WinVfs_isexecutable, /* int (*xExecutable)(const char *) */ + WinVfs_Filetype, /* int (*xFiletype)(const char *, jx9_context *) */ + WinVfs_Getenv, /* int (*xGetenv)(const char *, jx9_context *) */ + WinVfs_Setenv, /* int (*xSetenv)(const char *, const char *) */ + WinVfs_Touch, /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */ + WinVfs_Mmap, /* int (*xMmap)(const char *, void **, jx9_int64 *) */ + WinVfs_Unmap, /* void (*xUnmap)(void *, jx9_int64); */ + 0, /* int (*xLink)(const char *, const char *, int) */ + 0, /* int (*xUmask)(int) */ + WinVfs_TempDir, /* void (*xTempDir)(jx9_context *) */ + WinVfs_ProcessId, /* unsigned int (*xProcessId)(void) */ + 0, /* int (*xUid)(void) */ + 0, /* int (*xGid)(void) */ + 0, /* void (*xUsername)(jx9_context *) */ + 0 /* int (*xExec)(const char *, jx9_context *) */ +}; +/* Windows file IO */ +#ifndef INVALID_SET_FILE_POINTER +# define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif +/* int (*xOpen)(const char *, int, jx9_value *, void **) */ +static int WinFile_Open(const char *zPath, int iOpenMode, jx9_value *pResource, void **ppHandle) +{ + DWORD dwType = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS; + DWORD dwAccess = GENERIC_READ; + DWORD dwShare, dwCreate; + void *pConverted; + HANDLE pHandle; + + pConverted = jx9convertUtf8Filename(zPath); + if( pConverted == 0 ){ + return -1; + } + /* Set the desired flags according to the open mode */ + if( iOpenMode & JX9_IO_OPEN_CREATE ){ + /* Open existing file, or create if it doesn't exist */ + dwCreate = OPEN_ALWAYS; + if( iOpenMode & JX9_IO_OPEN_TRUNC ){ + /* If the specified file exists and is writable, the function overwrites the file */ + dwCreate = CREATE_ALWAYS; + } + }else if( iOpenMode & JX9_IO_OPEN_EXCL ){ + /* Creates a new file, only if it does not already exist. + * If the file exists, it fails. + */ + dwCreate = CREATE_NEW; + }else if( iOpenMode & JX9_IO_OPEN_TRUNC ){ + /* Opens a file and truncates it so that its size is zero bytes + * The file must exist. + */ + dwCreate = TRUNCATE_EXISTING; + }else{ + /* Opens a file, only if it exists. */ + dwCreate = OPEN_EXISTING; + } + if( iOpenMode & JX9_IO_OPEN_RDWR ){ + /* Read+Write access */ + dwAccess |= GENERIC_WRITE; + }else if( iOpenMode & JX9_IO_OPEN_WRONLY ){ + /* Write only access */ + dwAccess = GENERIC_WRITE; + } + if( iOpenMode & JX9_IO_OPEN_APPEND ){ + /* Append mode */ + dwAccess = FILE_APPEND_DATA; + } + if( iOpenMode & JX9_IO_OPEN_TEMP ){ + /* File is temporary */ + dwType = FILE_ATTRIBUTE_TEMPORARY; + } + dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE; + pHandle = CreateFileW((LPCWSTR)pConverted, dwAccess, dwShare, 0, dwCreate, dwType, 0); + HeapFree(GetProcessHeap(), 0, pConverted); + if( pHandle == INVALID_HANDLE_VALUE){ + SXUNUSED(pResource); /* MSVC warning */ + return -1; + } + /* Make the handle accessible to the upper layer */ + *ppHandle = (void *)pHandle; + return JX9_OK; +} +/* An instance of the following structure is used to record state information + * while iterating throw directory entries. + */ +typedef struct WinDir_Info WinDir_Info; +struct WinDir_Info +{ + HANDLE pDirHandle; + void *pPath; + WIN32_FIND_DATAW sInfo; + int rc; +}; +/* int (*xOpenDir)(const char *, jx9_value *, void **) */ +static int WinDir_Open(const char *zPath, jx9_value *pResource, void **ppHandle) +{ + WinDir_Info *pDirInfo; + void *pConverted; + char *zPrep; + sxu32 n; + /* Prepare the path */ + n = SyStrlen(zPath); + zPrep = (char *)HeapAlloc(GetProcessHeap(), 0, n+sizeof("\\*")+4); + if( zPrep == 0 ){ + return -1; + } + SyMemcpy((const void *)zPath, zPrep, n); + zPrep[n] = '\\'; + zPrep[n+1] = '*'; + zPrep[n+2] = 0; + pConverted = jx9convertUtf8Filename(zPrep); + HeapFree(GetProcessHeap(), 0, zPrep); + if( pConverted == 0 ){ + return -1; + } + /* Allocate a new instance */ + pDirInfo = (WinDir_Info *)HeapAlloc(GetProcessHeap(), 0, sizeof(WinDir_Info)); + if( pDirInfo == 0 ){ + pResource = 0; /* Compiler warning */ + return -1; + } + pDirInfo->rc = SXRET_OK; + pDirInfo->pDirHandle = FindFirstFileW((LPCWSTR)pConverted, &pDirInfo->sInfo); + if( pDirInfo->pDirHandle == INVALID_HANDLE_VALUE ){ + /* Cannot open directory */ + HeapFree(GetProcessHeap(), 0, pConverted); + HeapFree(GetProcessHeap(), 0, pDirInfo); + return -1; + } + /* Save the path */ + pDirInfo->pPath = pConverted; + /* Save our structure */ + *ppHandle = pDirInfo; + return JX9_OK; +} +/* void (*xCloseDir)(void *) */ +static void WinDir_Close(void *pUserData) +{ + WinDir_Info *pDirInfo = (WinDir_Info *)pUserData; + if( pDirInfo->pDirHandle != INVALID_HANDLE_VALUE ){ + FindClose(pDirInfo->pDirHandle); + } + HeapFree(GetProcessHeap(), 0, pDirInfo->pPath); + HeapFree(GetProcessHeap(), 0, pDirInfo); +} +/* void (*xClose)(void *); */ +static void WinFile_Close(void *pUserData) +{ + HANDLE pHandle = (HANDLE)pUserData; + CloseHandle(pHandle); +} +/* int (*xReadDir)(void *, jx9_context *) */ +static int WinDir_Read(void *pUserData, jx9_context *pCtx) +{ + WinDir_Info *pDirInfo = (WinDir_Info *)pUserData; + LPWIN32_FIND_DATAW pData; + char *zName; + BOOL rc; + sxu32 n; + if( pDirInfo->rc != SXRET_OK ){ + /* No more entry to process */ + return -1; + } + pData = &pDirInfo->sInfo; + for(;;){ + zName = jx9unicodeToUtf8(pData->cFileName); + if( zName == 0 ){ + /* Out of memory */ + return -1; + } + n = SyStrlen(zName); + /* Ignore '.' && '..' */ + if( n > sizeof("..")-1 || zName[0] != '.' || ( n == sizeof("..")-1 && zName[1] != '.') ){ + break; + } + HeapFree(GetProcessHeap(), 0, zName); + rc = FindNextFileW(pDirInfo->pDirHandle, &pDirInfo->sInfo); + if( !rc ){ + return -1; + } + } + /* Return the current file name */ + jx9_result_string(pCtx, zName, -1); + HeapFree(GetProcessHeap(), 0, zName); + /* Point to the next entry */ + rc = FindNextFileW(pDirInfo->pDirHandle, &pDirInfo->sInfo); + if( !rc ){ + pDirInfo->rc = SXERR_EOF; + } + return JX9_OK; +} +/* void (*xRewindDir)(void *) */ +static void WinDir_RewindDir(void *pUserData) +{ + WinDir_Info *pDirInfo = (WinDir_Info *)pUserData; + FindClose(pDirInfo->pDirHandle); + pDirInfo->pDirHandle = FindFirstFileW((LPCWSTR)pDirInfo->pPath, &pDirInfo->sInfo); + if( pDirInfo->pDirHandle == INVALID_HANDLE_VALUE ){ + pDirInfo->rc = SXERR_EOF; + }else{ + pDirInfo->rc = SXRET_OK; + } +} +/* jx9_int64 (*xRead)(void *, void *, jx9_int64); */ +static jx9_int64 WinFile_Read(void *pOS, void *pBuffer, jx9_int64 nDatatoRead) +{ + HANDLE pHandle = (HANDLE)pOS; + DWORD nRd; + BOOL rc; + rc = ReadFile(pHandle, pBuffer, (DWORD)nDatatoRead, &nRd, 0); + if( !rc ){ + /* EOF or IO error */ + return -1; + } + return (jx9_int64)nRd; +} +/* jx9_int64 (*xWrite)(void *, const void *, jx9_int64); */ +static jx9_int64 WinFile_Write(void *pOS, const void *pBuffer, jx9_int64 nWrite) +{ + const char *zData = (const char *)pBuffer; + HANDLE pHandle = (HANDLE)pOS; + jx9_int64 nCount; + DWORD nWr; + BOOL rc; + nWr = 0; + nCount = 0; + for(;;){ + if( nWrite < 1 ){ + break; + } + rc = WriteFile(pHandle, zData, (DWORD)nWrite, &nWr, 0); + if( !rc ){ + /* IO error */ + break; + } + nWrite -= nWr; + nCount += nWr; + zData += nWr; + } + if( nWrite > 0 ){ + return -1; + } + return nCount; +} +/* int (*xSeek)(void *, jx9_int64, int) */ +static int WinFile_Seek(void *pUserData, jx9_int64 iOfft, int whence) +{ + HANDLE pHandle = (HANDLE)pUserData; + DWORD dwMove, dwNew; + LONG nHighOfft; + switch(whence){ + case 1:/*SEEK_CUR*/ + dwMove = FILE_CURRENT; + break; + case 2: /* SEEK_END */ + dwMove = FILE_END; + break; + case 0: /* SEEK_SET */ + default: + dwMove = FILE_BEGIN; + break; + } + nHighOfft = (LONG)(iOfft >> 32); + dwNew = SetFilePointer(pHandle, (LONG)iOfft, &nHighOfft, dwMove); + if( dwNew == INVALID_SET_FILE_POINTER ){ + return -1; + } + return JX9_OK; +} +/* int (*xLock)(void *, int) */ +static int WinFile_Lock(void *pUserData, int lock_type) +{ + HANDLE pHandle = (HANDLE)pUserData; + static DWORD dwLo = 0, dwHi = 0; /* xx: MT-SAFE */ + OVERLAPPED sDummy; + BOOL rc; + SyZero(&sDummy, sizeof(sDummy)); + /* Get the file size */ + if( lock_type < 1 ){ + /* Unlock the file */ + rc = UnlockFileEx(pHandle, 0, dwLo, dwHi, &sDummy); + }else{ + DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY; /* Shared non-blocking lock by default*/ + /* Lock the file */ + if( lock_type == 1 /* LOCK_EXCL */ ){ + dwFlags |= LOCKFILE_EXCLUSIVE_LOCK; + } + dwLo = GetFileSize(pHandle, &dwHi); + rc = LockFileEx(pHandle, dwFlags, 0, dwLo, dwHi, &sDummy); + } + return rc ? JX9_OK : -1 /* Lock error */; +} +/* jx9_int64 (*xTell)(void *) */ +static jx9_int64 WinFile_Tell(void *pUserData) +{ + HANDLE pHandle = (HANDLE)pUserData; + DWORD dwNew; + dwNew = SetFilePointer(pHandle, 0, 0, FILE_CURRENT/* SEEK_CUR */); + if( dwNew == INVALID_SET_FILE_POINTER ){ + return -1; + } + return (jx9_int64)dwNew; +} +/* int (*xTrunc)(void *, jx9_int64) */ +static int WinFile_Trunc(void *pUserData, jx9_int64 nOfft) +{ + HANDLE pHandle = (HANDLE)pUserData; + LONG HighOfft; + DWORD dwNew; + BOOL rc; + HighOfft = (LONG)(nOfft >> 32); + dwNew = SetFilePointer(pHandle, (LONG)nOfft, &HighOfft, FILE_BEGIN); + if( dwNew == INVALID_SET_FILE_POINTER ){ + return -1; + } + rc = SetEndOfFile(pHandle); + return rc ? JX9_OK : -1; +} +/* int (*xSync)(void *); */ +static int WinFile_Sync(void *pUserData) +{ + HANDLE pHandle = (HANDLE)pUserData; + BOOL rc; + rc = FlushFileBuffers(pHandle); + return rc ? JX9_OK : - 1; +} +/* int (*xStat)(void *, jx9_value *, jx9_value *) */ +static int WinFile_Stat(void *pUserData, jx9_value *pArray, jx9_value *pWorker) +{ + BY_HANDLE_FILE_INFORMATION sInfo; + HANDLE pHandle = (HANDLE)pUserData; + BOOL rc; + rc = GetFileInformationByHandle(pHandle, &sInfo); + if( !rc ){ + return -1; + } + /* dev */ + jx9_value_int64(pWorker, (jx9_int64)sInfo.dwVolumeSerialNumber); + jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */ + /* ino */ + jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileIndexHigh << 32) | sInfo.nFileIndexLow)); + jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */ + /* mode */ + jx9_value_int(pWorker, 0); + jx9_array_add_strkey_elem(pArray, "mode", pWorker); + /* nlink */ + jx9_value_int(pWorker, (int)sInfo.nNumberOfLinks); + jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */ + /* uid, gid, rdev */ + jx9_value_int(pWorker, 0); + jx9_array_add_strkey_elem(pArray, "uid", pWorker); + jx9_array_add_strkey_elem(pArray, "gid", pWorker); + jx9_array_add_strkey_elem(pArray, "rdev", pWorker); + /* size */ + jx9_value_int64(pWorker, (jx9_int64)(((jx9_int64)sInfo.nFileSizeHigh << 32) | sInfo.nFileSizeLow)); + jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */ + /* atime */ + jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastAccessTime)); + jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */ + /* mtime */ + jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftLastWriteTime)); + jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */ + /* ctime */ + jx9_value_int64(pWorker, convertWindowsTimeToUnixTime(&sInfo.ftCreationTime)); + jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */ + /* blksize, blocks */ + jx9_value_int(pWorker, 0); + jx9_array_add_strkey_elem(pArray, "blksize", pWorker); + jx9_array_add_strkey_elem(pArray, "blocks", pWorker); + return JX9_OK; +} +/* Export the file:// stream */ +static const jx9_io_stream sWinFileStream = { + "file", /* Stream name */ + JX9_IO_STREAM_VERSION, + WinFile_Open, /* xOpen */ + WinDir_Open, /* xOpenDir */ + WinFile_Close, /* xClose */ + WinDir_Close, /* xCloseDir */ + WinFile_Read, /* xRead */ + WinDir_Read, /* xReadDir */ + WinFile_Write, /* xWrite */ + WinFile_Seek, /* xSeek */ + WinFile_Lock, /* xLock */ + WinDir_RewindDir, /* xRewindDir */ + WinFile_Tell, /* xTell */ + WinFile_Trunc, /* xTrunc */ + WinFile_Sync, /* xSeek */ + WinFile_Stat /* xStat */ +}; +#elif defined(__UNIXES__) +/* + * UNIX VFS implementation for the JX9 engine. + * Authors: + * Symisc Systems, devel@symisc.net. + * Copyright (C) Symisc Systems, http://jx9.symisc.net + * Status: + * Stable. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* int (*xchdir)(const char *) */ +static int UnixVfs_chdir(const char *zPath) +{ + int rc; + rc = chdir(zPath); + return rc == 0 ? JX9_OK : -1; +} +/* int (*xGetcwd)(jx9_context *) */ +static int UnixVfs_getcwd(jx9_context *pCtx) +{ + char zBuf[4096]; + char *zDir; + /* Get the current directory */ + zDir = getcwd(zBuf, sizeof(zBuf)); + if( zDir == 0 ){ + return -1; + } + jx9_result_string(pCtx, zDir, -1/*Compute length automatically*/); + return JX9_OK; +} +/* int (*xMkdir)(const char *, int, int) */ +static int UnixVfs_mkdir(const char *zPath, int mode, int recursive) +{ + int rc; + rc = mkdir(zPath, mode); + recursive = 0; /* cc warning */ + return rc == 0 ? JX9_OK : -1; +} +/* int (*xRmdir)(const char *) */ +static int UnixVfs_rmdir(const char *zPath) +{ + int rc; + rc = rmdir(zPath); + return rc == 0 ? JX9_OK : -1; +} +/* int (*xIsdir)(const char *) */ +static int UnixVfs_isdir(const char *zPath) +{ + struct stat st; + int rc; + rc = stat(zPath, &st); + if( rc != 0 ){ + return -1; + } + rc = S_ISDIR(st.st_mode); + return rc ? JX9_OK : -1 ; +} +/* int (*xRename)(const char *, const char *) */ +static int UnixVfs_Rename(const char *zOld, const char *zNew) +{ + int rc; + rc = rename(zOld, zNew); + return rc == 0 ? JX9_OK : -1; +} +/* int (*xRealpath)(const char *, jx9_context *) */ +static int UnixVfs_Realpath(const char *zPath, jx9_context *pCtx) +{ +#ifndef JX9_UNIX_OLD_LIBC + char *zReal; + zReal = realpath(zPath, 0); + if( zReal == 0 ){ + return -1; + } + jx9_result_string(pCtx, zReal, -1/*Compute length automatically*/); + /* Release the allocated buffer */ + free(zReal); + return JX9_OK; +#else + zPath = 0; /* cc warning */ + pCtx = 0; + return -1; +#endif +} +/* int (*xSleep)(unsigned int) */ +static int UnixVfs_Sleep(unsigned int uSec) +{ + usleep(uSec); + return JX9_OK; +} +/* int (*xUnlink)(const char *) */ +static int UnixVfs_unlink(const char *zPath) +{ + int rc; + rc = unlink(zPath); + return rc == 0 ? JX9_OK : -1 ; +} +/* int (*xFileExists)(const char *) */ +static int UnixVfs_FileExists(const char *zPath) +{ + int rc; + rc = access(zPath, F_OK); + return rc == 0 ? JX9_OK : -1; +} +/* jx9_int64 (*xFileSize)(const char *) */ +static jx9_int64 UnixVfs_FileSize(const char *zPath) +{ + struct stat st; + int rc; + rc = stat(zPath, &st); + if( rc != 0 ){ + return -1; + } + return (jx9_int64)st.st_size; +} +/* int (*xTouch)(const char *, jx9_int64, jx9_int64) */ +static int UnixVfs_Touch(const char *zPath, jx9_int64 touch_time, jx9_int64 access_time) +{ + struct utimbuf ut; + int rc; + ut.actime = (time_t)access_time; + ut.modtime = (time_t)touch_time; + rc = utime(zPath, &ut); + if( rc != 0 ){ + return -1; + } + return JX9_OK; +} +/* jx9_int64 (*xFileAtime)(const char *) */ +static jx9_int64 UnixVfs_FileAtime(const char *zPath) +{ + struct stat st; + int rc; + rc = stat(zPath, &st); + if( rc != 0 ){ + return -1; + } + return (jx9_int64)st.st_atime; +} +/* jx9_int64 (*xFileMtime)(const char *) */ +static jx9_int64 UnixVfs_FileMtime(const char *zPath) +{ + struct stat st; + int rc; + rc = stat(zPath, &st); + if( rc != 0 ){ + return -1; + } + return (jx9_int64)st.st_mtime; +} +/* jx9_int64 (*xFileCtime)(const char *) */ +static jx9_int64 UnixVfs_FileCtime(const char *zPath) +{ + struct stat st; + int rc; + rc = stat(zPath, &st); + if( rc != 0 ){ + return -1; + } + return (jx9_int64)st.st_ctime; +} +/* int (*xStat)(const char *, jx9_value *, jx9_value *) */ +static int UnixVfs_Stat(const char *zPath, jx9_value *pArray, jx9_value *pWorker) +{ + struct stat st; + int rc; + rc = stat(zPath, &st); + if( rc != 0 ){ + return -1; + } + /* dev */ + jx9_value_int64(pWorker, (jx9_int64)st.st_dev); + jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */ + /* ino */ + jx9_value_int64(pWorker, (jx9_int64)st.st_ino); + jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */ + /* mode */ + jx9_value_int(pWorker, (int)st.st_mode); + jx9_array_add_strkey_elem(pArray, "mode", pWorker); + /* nlink */ + jx9_value_int(pWorker, (int)st.st_nlink); + jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */ + /* uid, gid, rdev */ + jx9_value_int(pWorker, (int)st.st_uid); + jx9_array_add_strkey_elem(pArray, "uid", pWorker); + jx9_value_int(pWorker, (int)st.st_gid); + jx9_array_add_strkey_elem(pArray, "gid", pWorker); + jx9_value_int(pWorker, (int)st.st_rdev); + jx9_array_add_strkey_elem(pArray, "rdev", pWorker); + /* size */ + jx9_value_int64(pWorker, (jx9_int64)st.st_size); + jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */ + /* atime */ + jx9_value_int64(pWorker, (jx9_int64)st.st_atime); + jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */ + /* mtime */ + jx9_value_int64(pWorker, (jx9_int64)st.st_mtime); + jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */ + /* ctime */ + jx9_value_int64(pWorker, (jx9_int64)st.st_ctime); + jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */ + /* blksize, blocks */ + jx9_value_int(pWorker, (int)st.st_blksize); + jx9_array_add_strkey_elem(pArray, "blksize", pWorker); + jx9_value_int(pWorker, (int)st.st_blocks); + jx9_array_add_strkey_elem(pArray, "blocks", pWorker); + return JX9_OK; +} +/* int (*xlStat)(const char *, jx9_value *, jx9_value *) */ +static int UnixVfs_lStat(const char *zPath, jx9_value *pArray, jx9_value *pWorker) +{ + struct stat st; + int rc; + rc = lstat(zPath, &st); + if( rc != 0 ){ + return -1; + } + /* dev */ + jx9_value_int64(pWorker, (jx9_int64)st.st_dev); + jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */ + /* ino */ + jx9_value_int64(pWorker, (jx9_int64)st.st_ino); + jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */ + /* mode */ + jx9_value_int(pWorker, (int)st.st_mode); + jx9_array_add_strkey_elem(pArray, "mode", pWorker); + /* nlink */ + jx9_value_int(pWorker, (int)st.st_nlink); + jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */ + /* uid, gid, rdev */ + jx9_value_int(pWorker, (int)st.st_uid); + jx9_array_add_strkey_elem(pArray, "uid", pWorker); + jx9_value_int(pWorker, (int)st.st_gid); + jx9_array_add_strkey_elem(pArray, "gid", pWorker); + jx9_value_int(pWorker, (int)st.st_rdev); + jx9_array_add_strkey_elem(pArray, "rdev", pWorker); + /* size */ + jx9_value_int64(pWorker, (jx9_int64)st.st_size); + jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */ + /* atime */ + jx9_value_int64(pWorker, (jx9_int64)st.st_atime); + jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */ + /* mtime */ + jx9_value_int64(pWorker, (jx9_int64)st.st_mtime); + jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */ + /* ctime */ + jx9_value_int64(pWorker, (jx9_int64)st.st_ctime); + jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */ + /* blksize, blocks */ + jx9_value_int(pWorker, (int)st.st_blksize); + jx9_array_add_strkey_elem(pArray, "blksize", pWorker); + jx9_value_int(pWorker, (int)st.st_blocks); + jx9_array_add_strkey_elem(pArray, "blocks", pWorker); + return JX9_OK; +} +/* int (*xChmod)(const char *, int) */ +static int UnixVfs_Chmod(const char *zPath, int mode) +{ + int rc; + rc = chmod(zPath, (mode_t)mode); + return rc == 0 ? JX9_OK : - 1; +} +/* int (*xChown)(const char *, const char *) */ +static int UnixVfs_Chown(const char *zPath, const char *zUser) +{ +#ifndef JX9_UNIX_STATIC_BUILD + struct passwd *pwd; + uid_t uid; + int rc; + pwd = getpwnam(zUser); /* Try getting UID for username */ + if (pwd == 0) { + return -1; + } + uid = pwd->pw_uid; + rc = chown(zPath, uid, -1); + return rc == 0 ? JX9_OK : -1; +#else + SXUNUSED(zPath); + SXUNUSED(zUser); + return -1; +#endif /* JX9_UNIX_STATIC_BUILD */ +} +/* int (*xChgrp)(const char *, const char *) */ +static int UnixVfs_Chgrp(const char *zPath, const char *zGroup) +{ +#ifndef JX9_UNIX_STATIC_BUILD + struct group *group; + gid_t gid; + int rc; + group = getgrnam(zGroup); + if (group == 0) { + return -1; + } + gid = group->gr_gid; + rc = chown(zPath, -1, gid); + return rc == 0 ? JX9_OK : -1; +#else + SXUNUSED(zPath); + SXUNUSED(zGroup); + return -1; +#endif /* JX9_UNIX_STATIC_BUILD */ +} +/* int (*xIsfile)(const char *) */ +static int UnixVfs_isfile(const char *zPath) +{ + struct stat st; + int rc; + rc = stat(zPath, &st); + if( rc != 0 ){ + return -1; + } + rc = S_ISREG(st.st_mode); + return rc ? JX9_OK : -1 ; +} +/* int (*xIslink)(const char *) */ +static int UnixVfs_islink(const char *zPath) +{ + struct stat st; + int rc; + rc = stat(zPath, &st); + if( rc != 0 ){ + return -1; + } + rc = S_ISLNK(st.st_mode); + return rc ? JX9_OK : -1 ; +} +/* int (*xReadable)(const char *) */ +static int UnixVfs_isreadable(const char *zPath) +{ + int rc; + rc = access(zPath, R_OK); + return rc == 0 ? JX9_OK : -1; +} +/* int (*xWritable)(const char *) */ +static int UnixVfs_iswritable(const char *zPath) +{ + int rc; + rc = access(zPath, W_OK); + return rc == 0 ? JX9_OK : -1; +} +/* int (*xExecutable)(const char *) */ +static int UnixVfs_isexecutable(const char *zPath) +{ + int rc; + rc = access(zPath, X_OK); + return rc == 0 ? JX9_OK : -1; +} +/* int (*xFiletype)(const char *, jx9_context *) */ +static int UnixVfs_Filetype(const char *zPath, jx9_context *pCtx) +{ + struct stat st; + int rc; + rc = stat(zPath, &st); + if( rc != 0 ){ + /* Expand 'unknown' */ + jx9_result_string(pCtx, "unknown", sizeof("unknown")-1); + return -1; + } + if(S_ISREG(st.st_mode) ){ + jx9_result_string(pCtx, "file", sizeof("file")-1); + }else if(S_ISDIR(st.st_mode)){ + jx9_result_string(pCtx, "dir", sizeof("dir")-1); + }else if(S_ISLNK(st.st_mode)){ + jx9_result_string(pCtx, "link", sizeof("link")-1); + }else if(S_ISBLK(st.st_mode)){ + jx9_result_string(pCtx, "block", sizeof("block")-1); + }else if(S_ISSOCK(st.st_mode)){ + jx9_result_string(pCtx, "socket", sizeof("socket")-1); + }else if(S_ISFIFO(st.st_mode)){ + jx9_result_string(pCtx, "fifo", sizeof("fifo")-1); + }else{ + jx9_result_string(pCtx, "unknown", sizeof("unknown")-1); + } + return JX9_OK; +} +/* int (*xGetenv)(const char *, jx9_context *) */ +static int UnixVfs_Getenv(const char *zVar, jx9_context *pCtx) +{ + char *zEnv; + zEnv = getenv(zVar); + if( zEnv == 0 ){ + return -1; + } + jx9_result_string(pCtx, zEnv, -1/*Compute length automatically*/); + return JX9_OK; +} +/* int (*xSetenv)(const char *, const char *) */ +static int UnixVfs_Setenv(const char *zName, const char *zValue) +{ + int rc; + rc = setenv(zName, zValue, 1); + return rc == 0 ? JX9_OK : -1; +} +/* int (*xMmap)(const char *, void **, jx9_int64 *) */ +static int UnixVfs_Mmap(const char *zPath, void **ppMap, jx9_int64 *pSize) +{ + struct stat st; + void *pMap; + int fd; + int rc; + /* Open the file in a read-only mode */ + fd = open(zPath, O_RDONLY); + if( fd < 0 ){ + return -1; + } + /* stat the handle */ + fstat(fd, &st); + /* Obtain a memory view of the whole file */ + pMap = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE|MAP_FILE, fd, 0); + rc = JX9_OK; + if( pMap == MAP_FAILED ){ + rc = -1; + }else{ + /* Point to the memory view */ + *ppMap = pMap; + *pSize = (jx9_int64)st.st_size; + } + close(fd); + return rc; +} +/* void (*xUnmap)(void *, jx9_int64) */ +static void UnixVfs_Unmap(void *pView, jx9_int64 nSize) +{ + munmap(pView, (size_t)nSize); +} +/* void (*xTempDir)(jx9_context *) */ +static void UnixVfs_TempDir(jx9_context *pCtx) +{ + static const char *azDirs[] = { + "/var/tmp", + "/usr/tmp", + "/usr/local/tmp" + }; + unsigned int i; + struct stat buf; + const char *zDir; + zDir = getenv("TMPDIR"); + if( zDir && zDir[0] != 0 && !access(zDir, 07) ){ + jx9_result_string(pCtx, zDir, -1); + return; + } + for(i=0; ipw_name, -1); +#else + jx9_result_string(pCtx, "Unknown", -1); +#endif /* JX9_UNIX_STATIC_BUILD */ + return; +} +/* int (*xLink)(const char *, const char *, int) */ +static int UnixVfs_link(const char *zSrc, const char *zTarget, int is_sym) +{ + int rc; + if( is_sym ){ + /* Symbolic link */ + rc = symlink(zSrc, zTarget); + }else{ + /* Hard link */ + rc = link(zSrc, zTarget); + } + return rc == 0 ? JX9_OK : -1; +} +/* int (*xChroot)(const char *) */ +static int UnixVfs_chroot(const char *zRootDir) +{ + int rc; + rc = chroot(zRootDir); + return rc == 0 ? JX9_OK : -1; +} +/* Export the UNIX vfs */ +static const jx9_vfs sUnixVfs = { + "Unix_vfs", + JX9_VFS_VERSION, + UnixVfs_chdir, /* int (*xChdir)(const char *) */ + UnixVfs_chroot, /* int (*xChroot)(const char *); */ + UnixVfs_getcwd, /* int (*xGetcwd)(jx9_context *) */ + UnixVfs_mkdir, /* int (*xMkdir)(const char *, int, int) */ + UnixVfs_rmdir, /* int (*xRmdir)(const char *) */ + UnixVfs_isdir, /* int (*xIsdir)(const char *) */ + UnixVfs_Rename, /* int (*xRename)(const char *, const char *) */ + UnixVfs_Realpath, /*int (*xRealpath)(const char *, jx9_context *)*/ + UnixVfs_Sleep, /* int (*xSleep)(unsigned int) */ + UnixVfs_unlink, /* int (*xUnlink)(const char *) */ + UnixVfs_FileExists, /* int (*xFileExists)(const char *) */ + UnixVfs_Chmod, /*int (*xChmod)(const char *, int)*/ + UnixVfs_Chown, /*int (*xChown)(const char *, const char *)*/ + UnixVfs_Chgrp, /*int (*xChgrp)(const char *, const char *)*/ + 0, /* jx9_int64 (*xFreeSpace)(const char *) */ + 0, /* jx9_int64 (*xTotalSpace)(const char *) */ + UnixVfs_FileSize, /* jx9_int64 (*xFileSize)(const char *) */ + UnixVfs_FileAtime, /* jx9_int64 (*xFileAtime)(const char *) */ + UnixVfs_FileMtime, /* jx9_int64 (*xFileMtime)(const char *) */ + UnixVfs_FileCtime, /* jx9_int64 (*xFileCtime)(const char *) */ + UnixVfs_Stat, /* int (*xStat)(const char *, jx9_value *, jx9_value *) */ + UnixVfs_lStat, /* int (*xlStat)(const char *, jx9_value *, jx9_value *) */ + UnixVfs_isfile, /* int (*xIsfile)(const char *) */ + UnixVfs_islink, /* int (*xIslink)(const char *) */ + UnixVfs_isreadable, /* int (*xReadable)(const char *) */ + UnixVfs_iswritable, /* int (*xWritable)(const char *) */ + UnixVfs_isexecutable, /* int (*xExecutable)(const char *) */ + UnixVfs_Filetype, /* int (*xFiletype)(const char *, jx9_context *) */ + UnixVfs_Getenv, /* int (*xGetenv)(const char *, jx9_context *) */ + UnixVfs_Setenv, /* int (*xSetenv)(const char *, const char *) */ + UnixVfs_Touch, /* int (*xTouch)(const char *, jx9_int64, jx9_int64) */ + UnixVfs_Mmap, /* int (*xMmap)(const char *, void **, jx9_int64 *) */ + UnixVfs_Unmap, /* void (*xUnmap)(void *, jx9_int64); */ + UnixVfs_link, /* int (*xLink)(const char *, const char *, int) */ + UnixVfs_Umask, /* int (*xUmask)(int) */ + UnixVfs_TempDir, /* void (*xTempDir)(jx9_context *) */ + UnixVfs_ProcessId, /* unsigned int (*xProcessId)(void) */ + UnixVfs_uid, /* int (*xUid)(void) */ + UnixVfs_gid, /* int (*xGid)(void) */ + UnixVfs_Username, /* void (*xUsername)(jx9_context *) */ + 0 /* int (*xExec)(const char *, jx9_context *) */ +}; +/* UNIX File IO */ +#define JX9_UNIX_OPEN_MODE 0640 /* Default open mode */ +/* int (*xOpen)(const char *, int, jx9_value *, void **) */ +static int UnixFile_Open(const char *zPath, int iOpenMode, jx9_value *pResource, void **ppHandle) +{ + int iOpen = O_RDONLY; + int fd; + /* Set the desired flags according to the open mode */ + if( iOpenMode & JX9_IO_OPEN_CREATE ){ + /* Open existing file, or create if it doesn't exist */ + iOpen = O_CREAT; + if( iOpenMode & JX9_IO_OPEN_TRUNC ){ + /* If the specified file exists and is writable, the function overwrites the file */ + iOpen |= O_TRUNC; + SXUNUSED(pResource); /* cc warning */ + } + }else if( iOpenMode & JX9_IO_OPEN_EXCL ){ + /* Creates a new file, only if it does not already exist. + * If the file exists, it fails. + */ + iOpen = O_CREAT|O_EXCL; + }else if( iOpenMode & JX9_IO_OPEN_TRUNC ){ + /* Opens a file and truncates it so that its size is zero bytes + * The file must exist. + */ + iOpen = O_RDWR|O_TRUNC; + } + if( iOpenMode & JX9_IO_OPEN_RDWR ){ + /* Read+Write access */ + iOpen &= ~O_RDONLY; + iOpen |= O_RDWR; + }else if( iOpenMode & JX9_IO_OPEN_WRONLY ){ + /* Write only access */ + iOpen &= ~O_RDONLY; + iOpen |= O_WRONLY; + } + if( iOpenMode & JX9_IO_OPEN_APPEND ){ + /* Append mode */ + iOpen |= O_APPEND; + } +#ifdef O_TEMP + if( iOpenMode & JX9_IO_OPEN_TEMP ){ + /* File is temporary */ + iOpen |= O_TEMP; + } +#endif + /* Open the file now */ + fd = open(zPath, iOpen, JX9_UNIX_OPEN_MODE); + if( fd < 0 ){ + /* IO error */ + return -1; + } + /* Save the handle */ + *ppHandle = SX_INT_TO_PTR(fd); + return JX9_OK; +} +/* int (*xOpenDir)(const char *, jx9_value *, void **) */ +static int UnixDir_Open(const char *zPath, jx9_value *pResource, void **ppHandle) +{ + DIR *pDir; + /* Open the target directory */ + pDir = opendir(zPath); + if( pDir == 0 ){ + pResource = 0; /* Compiler warning */ + return -1; + } + /* Save our structure */ + *ppHandle = pDir; + return JX9_OK; +} +/* void (*xCloseDir)(void *) */ +static void UnixDir_Close(void *pUserData) +{ + closedir((DIR *)pUserData); +} +/* void (*xClose)(void *); */ +static void UnixFile_Close(void *pUserData) +{ + close(SX_PTR_TO_INT(pUserData)); +} +/* int (*xReadDir)(void *, jx9_context *) */ +static int UnixDir_Read(void *pUserData, jx9_context *pCtx) +{ + DIR *pDir = (DIR *)pUserData; + struct dirent *pEntry; + char *zName = 0; /* cc warning */ + sxu32 n = 0; + for(;;){ + pEntry = readdir(pDir); + if( pEntry == 0 ){ + /* No more entries to process */ + return -1; + } + zName = pEntry->d_name; + n = SyStrlen(zName); + /* Ignore '.' && '..' */ + if( n > sizeof("..")-1 || zName[0] != '.' || ( n == sizeof("..")-1 && zName[1] != '.') ){ + break; + } + /* Next entry */ + } + /* Return the current file name */ + jx9_result_string(pCtx, zName, (int)n); + return JX9_OK; +} +/* void (*xRewindDir)(void *) */ +static void UnixDir_Rewind(void *pUserData) +{ + rewinddir((DIR *)pUserData); +} +/* jx9_int64 (*xRead)(void *, void *, jx9_int64); */ +static jx9_int64 UnixFile_Read(void *pUserData, void *pBuffer, jx9_int64 nDatatoRead) +{ + ssize_t nRd; + nRd = read(SX_PTR_TO_INT(pUserData), pBuffer, (size_t)nDatatoRead); + if( nRd < 1 ){ + /* EOF or IO error */ + return -1; + } + return (jx9_int64)nRd; +} +/* jx9_int64 (*xWrite)(void *, const void *, jx9_int64); */ +static jx9_int64 UnixFile_Write(void *pUserData, const void *pBuffer, jx9_int64 nWrite) +{ + const char *zData = (const char *)pBuffer; + int fd = SX_PTR_TO_INT(pUserData); + jx9_int64 nCount; + ssize_t nWr; + nCount = 0; + for(;;){ + if( nWrite < 1 ){ + break; + } + nWr = write(fd, zData, (size_t)nWrite); + if( nWr < 1 ){ + /* IO error */ + break; + } + nWrite -= nWr; + nCount += nWr; + zData += nWr; + } + if( nWrite > 0 ){ + return -1; + } + return nCount; +} +/* int (*xSeek)(void *, jx9_int64, int) */ +static int UnixFile_Seek(void *pUserData, jx9_int64 iOfft, int whence) +{ + off_t iNew; + switch(whence){ + case 1:/*SEEK_CUR*/ + whence = SEEK_CUR; + break; + case 2: /* SEEK_END */ + whence = SEEK_END; + break; + case 0: /* SEEK_SET */ + default: + whence = SEEK_SET; + break; + } + iNew = lseek(SX_PTR_TO_INT(pUserData), (off_t)iOfft, whence); + if( iNew < 0 ){ + return -1; + } + return JX9_OK; +} +/* int (*xLock)(void *, int) */ +static int UnixFile_Lock(void *pUserData, int lock_type) +{ + int fd = SX_PTR_TO_INT(pUserData); + int rc = JX9_OK; /* cc warning */ + if( lock_type < 0 ){ + /* Unlock the file */ + rc = flock(fd, LOCK_UN); + }else{ + if( lock_type == 1 ){ + /* Exculsive lock */ + rc = flock(fd, LOCK_EX); + }else{ + /* Shared lock */ + rc = flock(fd, LOCK_SH); + } + } + return !rc ? JX9_OK : -1; +} +/* jx9_int64 (*xTell)(void *) */ +static jx9_int64 UnixFile_Tell(void *pUserData) +{ + off_t iNew; + iNew = lseek(SX_PTR_TO_INT(pUserData), 0, SEEK_CUR); + return (jx9_int64)iNew; +} +/* int (*xTrunc)(void *, jx9_int64) */ +static int UnixFile_Trunc(void *pUserData, jx9_int64 nOfft) +{ + int rc; + rc = ftruncate(SX_PTR_TO_INT(pUserData), (off_t)nOfft); + if( rc != 0 ){ + return -1; + } + return JX9_OK; +} +/* int (*xSync)(void *); */ +static int UnixFile_Sync(void *pUserData) +{ + int rc; + rc = fsync(SX_PTR_TO_INT(pUserData)); + return rc == 0 ? JX9_OK : - 1; +} +/* int (*xStat)(void *, jx9_value *, jx9_value *) */ +static int UnixFile_Stat(void *pUserData, jx9_value *pArray, jx9_value *pWorker) +{ + struct stat st; + int rc; + rc = fstat(SX_PTR_TO_INT(pUserData), &st); + if( rc != 0 ){ + return -1; + } + /* dev */ + jx9_value_int64(pWorker, (jx9_int64)st.st_dev); + jx9_array_add_strkey_elem(pArray, "dev", pWorker); /* Will make it's own copy */ + /* ino */ + jx9_value_int64(pWorker, (jx9_int64)st.st_ino); + jx9_array_add_strkey_elem(pArray, "ino", pWorker); /* Will make it's own copy */ + /* mode */ + jx9_value_int(pWorker, (int)st.st_mode); + jx9_array_add_strkey_elem(pArray, "mode", pWorker); + /* nlink */ + jx9_value_int(pWorker, (int)st.st_nlink); + jx9_array_add_strkey_elem(pArray, "nlink", pWorker); /* Will make it's own copy */ + /* uid, gid, rdev */ + jx9_value_int(pWorker, (int)st.st_uid); + jx9_array_add_strkey_elem(pArray, "uid", pWorker); + jx9_value_int(pWorker, (int)st.st_gid); + jx9_array_add_strkey_elem(pArray, "gid", pWorker); + jx9_value_int(pWorker, (int)st.st_rdev); + jx9_array_add_strkey_elem(pArray, "rdev", pWorker); + /* size */ + jx9_value_int64(pWorker, (jx9_int64)st.st_size); + jx9_array_add_strkey_elem(pArray, "size", pWorker); /* Will make it's own copy */ + /* atime */ + jx9_value_int64(pWorker, (jx9_int64)st.st_atime); + jx9_array_add_strkey_elem(pArray, "atime", pWorker); /* Will make it's own copy */ + /* mtime */ + jx9_value_int64(pWorker, (jx9_int64)st.st_mtime); + jx9_array_add_strkey_elem(pArray, "mtime", pWorker); /* Will make it's own copy */ + /* ctime */ + jx9_value_int64(pWorker, (jx9_int64)st.st_ctime); + jx9_array_add_strkey_elem(pArray, "ctime", pWorker); /* Will make it's own copy */ + /* blksize, blocks */ + jx9_value_int(pWorker, (int)st.st_blksize); + jx9_array_add_strkey_elem(pArray, "blksize", pWorker); + jx9_value_int(pWorker, (int)st.st_blocks); + jx9_array_add_strkey_elem(pArray, "blocks", pWorker); + return JX9_OK; +} +/* Export the file:// stream */ +static const jx9_io_stream sUnixFileStream = { + "file", /* Stream name */ + JX9_IO_STREAM_VERSION, + UnixFile_Open, /* xOpen */ + UnixDir_Open, /* xOpenDir */ + UnixFile_Close, /* xClose */ + UnixDir_Close, /* xCloseDir */ + UnixFile_Read, /* xRead */ + UnixDir_Read, /* xReadDir */ + UnixFile_Write, /* xWrite */ + UnixFile_Seek, /* xSeek */ + UnixFile_Lock, /* xLock */ + UnixDir_Rewind, /* xRewindDir */ + UnixFile_Tell, /* xTell */ + UnixFile_Trunc, /* xTrunc */ + UnixFile_Sync, /* xSeek */ + UnixFile_Stat /* xStat */ +}; +#endif /* __WINNT__/__UNIXES__ */ +#endif /* JX9_DISABLE_DISK_IO */ +#endif /* JX9_DISABLE_BUILTIN_FUNC */ +/* + * Export the builtin vfs. + * Return a pointer to the builtin vfs if available. + * Otherwise return the null_vfs [i.e: a no-op vfs] instead. + * Note: + * The built-in vfs is always available for Windows/UNIX systems. + * Note: + * If the engine is compiled with the JX9_DISABLE_DISK_IO/JX9_DISABLE_BUILTIN_FUNC + * directives defined then this function return the null_vfs instead. + */ +JX9_PRIVATE const jx9_vfs * jx9ExportBuiltinVfs(void) +{ +#ifndef JX9_DISABLE_BUILTIN_FUNC +#ifdef JX9_DISABLE_DISK_IO + return &null_vfs; +#else +#ifdef __WINNT__ + return &sWinVfs; +#elif defined(__UNIXES__) + return &sUnixVfs; +#else + return &null_vfs; +#endif /* __WINNT__/__UNIXES__ */ +#endif /*JX9_DISABLE_DISK_IO*/ +#else + return &null_vfs; +#endif /* JX9_DISABLE_BUILTIN_FUNC */ +} +#ifndef JX9_DISABLE_BUILTIN_FUNC +#ifndef JX9_DISABLE_DISK_IO +/* + * The following defines are mostly used by the UNIX built and have + * no particular meaning on windows. + */ +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif +/* + * jx9:// Accessing various I/O streams + * According to the JX9 langage reference manual + * JX9 provides a number of miscellaneous I/O streams that allow access to JX9's own input + * and output streams, the standard input, output and error file descriptors. + * jx9://stdin, jx9://stdout and jx9://stderr: + * Allow direct access to the corresponding input or output stream of the JX9 process. + * The stream references a duplicate file descriptor, so if you open jx9://stdin and later + * close it, you close only your copy of the descriptor-the actual stream referenced by STDIN is unaffected. + * jx9://stdin is read-only, whereas jx9://stdout and jx9://stderr are write-only. + * jx9://output + * jx9://output is a write-only stream that allows you to write to the output buffer + * mechanism in the same way as print and print. + */ +typedef struct jx9_stream_data jx9_stream_data; +/* Supported IO streams */ +#define JX9_IO_STREAM_STDIN 1 /* jx9://stdin */ +#define JX9_IO_STREAM_STDOUT 2 /* jx9://stdout */ +#define JX9_IO_STREAM_STDERR 3 /* jx9://stderr */ +#define JX9_IO_STREAM_OUTPUT 4 /* jx9://output */ + /* The following structure is the private data associated with the jx9:// stream */ +struct jx9_stream_data +{ + jx9_vm *pVm; /* VM that own this instance */ + int iType; /* Stream type */ + union{ + void *pHandle; /* Stream handle */ + jx9_output_consumer sConsumer; /* VM output consumer */ + }x; +}; +/* + * Allocate a new instance of the jx9_stream_data structure. + */ +static jx9_stream_data * JX9StreamDataInit(jx9_vm *pVm, int iType) +{ + jx9_stream_data *pData; + if( pVm == 0 ){ + return 0; + } + /* Allocate a new instance */ + pData = (jx9_stream_data *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(jx9_stream_data)); + if( pData == 0 ){ + return 0; + } + /* Zero the structure */ + SyZero(pData, sizeof(jx9_stream_data)); + /* Initialize fields */ + pData->iType = iType; + if( iType == JX9_IO_STREAM_OUTPUT ){ + /* Point to the default VM consumer routine. */ + pData->x.sConsumer = pVm->sVmConsumer; + }else{ +#ifdef __WINNT__ + DWORD nChannel; + switch(iType){ + case JX9_IO_STREAM_STDOUT: nChannel = STD_OUTPUT_HANDLE; break; + case JX9_IO_STREAM_STDERR: nChannel = STD_ERROR_HANDLE; break; + default: + nChannel = STD_INPUT_HANDLE; + break; + } + pData->x.pHandle = GetStdHandle(nChannel); +#else + /* Assume an UNIX system */ + int ifd = STDIN_FILENO; + switch(iType){ + case JX9_IO_STREAM_STDOUT: ifd = STDOUT_FILENO; break; + case JX9_IO_STREAM_STDERR: ifd = STDERR_FILENO; break; + default: + break; + } + pData->x.pHandle = SX_INT_TO_PTR(ifd); +#endif + } + pData->pVm = pVm; + return pData; +} +/* + * Implementation of the jx9:// IO streams routines + * Authors: + * Symisc Systems, devel@symisc.net. + * Copyright (C) Symisc Systems, http://jx9.symisc.net + * Status: + * Stable. + */ +/* int (*xOpen)(const char *, int, jx9_value *, void **) */ +static int JX9StreamData_Open(const char *zName, int iMode, jx9_value *pResource, void ** ppHandle) +{ + jx9_stream_data *pData; + SyString sStream; + SyStringInitFromBuf(&sStream, zName, SyStrlen(zName)); + /* Trim leading and trailing white spaces */ + SyStringFullTrim(&sStream); + /* Stream to open */ + if( SyStrnicmp(sStream.zString, "stdin", sizeof("stdin")-1) == 0 ){ + iMode = JX9_IO_STREAM_STDIN; + }else if( SyStrnicmp(sStream.zString, "output", sizeof("output")-1) == 0 ){ + iMode = JX9_IO_STREAM_OUTPUT; + }else if( SyStrnicmp(sStream.zString, "stdout", sizeof("stdout")-1) == 0 ){ + iMode = JX9_IO_STREAM_STDOUT; + }else if( SyStrnicmp(sStream.zString, "stderr", sizeof("stderr")-1) == 0 ){ + iMode = JX9_IO_STREAM_STDERR; + }else{ + /* unknown stream name */ + return -1; + } + /* Create our handle */ + pData = JX9StreamDataInit(pResource?pResource->pVm:0, iMode); + if( pData == 0 ){ + return -1; + } + /* Make the handle public */ + *ppHandle = (void *)pData; + return JX9_OK; +} +/* jx9_int64 (*xRead)(void *, void *, jx9_int64) */ +static jx9_int64 JX9StreamData_Read(void *pHandle, void *pBuffer, jx9_int64 nDatatoRead) +{ + jx9_stream_data *pData = (jx9_stream_data *)pHandle; + if( pData == 0 ){ + return -1; + } + if( pData->iType != JX9_IO_STREAM_STDIN ){ + /* Forbidden */ + return -1; + } +#ifdef __WINNT__ + { + DWORD nRd; + BOOL rc; + rc = ReadFile(pData->x.pHandle, pBuffer, (DWORD)nDatatoRead, &nRd, 0); + if( !rc ){ + /* IO error */ + return -1; + } + return (jx9_int64)nRd; + } +#elif defined(__UNIXES__) + { + ssize_t nRd; + int fd; + fd = SX_PTR_TO_INT(pData->x.pHandle); + nRd = read(fd, pBuffer, (size_t)nDatatoRead); + if( nRd < 1 ){ + return -1; + } + return (jx9_int64)nRd; + } +#else + return -1; +#endif +} +/* jx9_int64 (*xWrite)(void *, const void *, jx9_int64) */ +static jx9_int64 JX9StreamData_Write(void *pHandle, const void *pBuf, jx9_int64 nWrite) +{ + jx9_stream_data *pData = (jx9_stream_data *)pHandle; + if( pData == 0 ){ + return -1; + } + if( pData->iType == JX9_IO_STREAM_STDIN ){ + /* Forbidden */ + return -1; + }else if( pData->iType == JX9_IO_STREAM_OUTPUT ){ + jx9_output_consumer *pCons = &pData->x.sConsumer; + int rc; + /* Call the vm output consumer */ + rc = pCons->xConsumer(pBuf, (unsigned int)nWrite, pCons->pUserData); + if( rc == JX9_ABORT ){ + return -1; + } + return nWrite; + } +#ifdef __WINNT__ + { + DWORD nWr; + BOOL rc; + rc = WriteFile(pData->x.pHandle, pBuf, (DWORD)nWrite, &nWr, 0); + if( !rc ){ + /* IO error */ + return -1; + } + return (jx9_int64)nWr; + } +#elif defined(__UNIXES__) + { + ssize_t nWr; + int fd; + fd = SX_PTR_TO_INT(pData->x.pHandle); + nWr = write(fd, pBuf, (size_t)nWrite); + if( nWr < 1 ){ + return -1; + } + return (jx9_int64)nWr; + } +#else + return -1; +#endif +} +/* void (*xClose)(void *) */ +static void JX9StreamData_Close(void *pHandle) +{ + jx9_stream_data *pData = (jx9_stream_data *)pHandle; + jx9_vm *pVm; + if( pData == 0 ){ + return; + } + pVm = pData->pVm; + /* Free the instance */ + SyMemBackendFree(&pVm->sAllocator, pData); +} +/* Export the jx9:// stream */ +static const jx9_io_stream sjx9Stream = { + "jx9", + JX9_IO_STREAM_VERSION, + JX9StreamData_Open, /* xOpen */ + 0, /* xOpenDir */ + JX9StreamData_Close, /* xClose */ + 0, /* xCloseDir */ + JX9StreamData_Read, /* xRead */ + 0, /* xReadDir */ + JX9StreamData_Write, /* xWrite */ + 0, /* xSeek */ + 0, /* xLock */ + 0, /* xRewindDir */ + 0, /* xTell */ + 0, /* xTrunc */ + 0, /* xSeek */ + 0 /* xStat */ +}; +#endif /* JX9_DISABLE_DISK_IO */ +/* + * Return TRUE if we are dealing with the jx9:// stream. + * FALSE otherwise. + */ +static int is_jx9_stream(const jx9_io_stream *pStream) +{ +#ifndef JX9_DISABLE_DISK_IO + return pStream == &sjx9Stream; +#else + SXUNUSED(pStream); /* cc warning */ + return 0; +#endif /* JX9_DISABLE_DISK_IO */ +} + +#endif /* JX9_DISABLE_BUILTIN_FUNC */ +/* + * Export the IO routines defined above and the built-in IO streams + * [i.e: file://, jx9://]. + * Note: + * If the engine is compiled with the JX9_DISABLE_BUILTIN_FUNC directive + * defined then this function is a no-op. + */ +JX9_PRIVATE sxi32 jx9RegisterIORoutine(jx9_vm *pVm) +{ +#ifndef JX9_DISABLE_BUILTIN_FUNC + /* VFS functions */ + static const jx9_builtin_func aVfsFunc[] = { + {"chdir", jx9Vfs_chdir }, + {"chroot", jx9Vfs_chroot }, + {"getcwd", jx9Vfs_getcwd }, + {"rmdir", jx9Vfs_rmdir }, + {"is_dir", jx9Vfs_is_dir }, + {"mkdir", jx9Vfs_mkdir }, + {"rename", jx9Vfs_rename }, + {"realpath", jx9Vfs_realpath}, + {"sleep", jx9Vfs_sleep }, + {"usleep", jx9Vfs_usleep }, + {"unlink", jx9Vfs_unlink }, + {"delete", jx9Vfs_unlink }, + {"chmod", jx9Vfs_chmod }, + {"chown", jx9Vfs_chown }, + {"chgrp", jx9Vfs_chgrp }, + {"disk_free_space", jx9Vfs_disk_free_space }, + {"disk_total_space", jx9Vfs_disk_total_space}, + {"file_exists", jx9Vfs_file_exists }, + {"filesize", jx9Vfs_file_size }, + {"fileatime", jx9Vfs_file_atime }, + {"filemtime", jx9Vfs_file_mtime }, + {"filectime", jx9Vfs_file_ctime }, + {"is_file", jx9Vfs_is_file }, + {"is_link", jx9Vfs_is_link }, + {"is_readable", jx9Vfs_is_readable }, + {"is_writable", jx9Vfs_is_writable }, + {"is_executable", jx9Vfs_is_executable}, + {"filetype", jx9Vfs_filetype }, + {"stat", jx9Vfs_stat }, + {"lstat", jx9Vfs_lstat }, + {"getenv", jx9Vfs_getenv }, + {"setenv", jx9Vfs_putenv }, + {"putenv", jx9Vfs_putenv }, + {"touch", jx9Vfs_touch }, + {"link", jx9Vfs_link }, + {"symlink", jx9Vfs_symlink }, + {"umask", jx9Vfs_umask }, + {"sys_get_temp_dir", jx9Vfs_sys_get_temp_dir }, + {"get_current_user", jx9Vfs_get_current_user }, + {"getpid", jx9Vfs_getmypid }, + {"getuid", jx9Vfs_getmyuid }, + {"getgid", jx9Vfs_getmygid }, + {"uname", jx9Vfs_uname}, + /* Path processing */ + {"dirname", jx9Builtin_dirname }, + {"basename", jx9Builtin_basename }, + {"pathinfo", jx9Builtin_pathinfo }, + {"strglob", jx9Builtin_strglob }, + {"fnmatch", jx9Builtin_fnmatch }, + /* ZIP processing */ + {"zip_open", jx9Builtin_zip_open }, + {"zip_close", jx9Builtin_zip_close}, + {"zip_read", jx9Builtin_zip_read }, + {"zip_entry_open", jx9Builtin_zip_entry_open }, + {"zip_entry_close", jx9Builtin_zip_entry_close}, + {"zip_entry_name", jx9Builtin_zip_entry_name }, + {"zip_entry_filesize", jx9Builtin_zip_entry_filesize }, + {"zip_entry_compressedsize", jx9Builtin_zip_entry_compressedsize }, + {"zip_entry_read", jx9Builtin_zip_entry_read }, + {"zip_entry_reset_cursor", jx9Builtin_zip_entry_reset_cursor}, + {"zip_entry_compressionmethod", jx9Builtin_zip_entry_compressionmethod} + }; + /* IO stream functions */ + static const jx9_builtin_func aIOFunc[] = { + {"ftruncate", jx9Builtin_ftruncate }, + {"fseek", jx9Builtin_fseek }, + {"ftell", jx9Builtin_ftell }, + {"rewind", jx9Builtin_rewind }, + {"fflush", jx9Builtin_fflush }, + {"feof", jx9Builtin_feof }, + {"fgetc", jx9Builtin_fgetc }, + {"fgets", jx9Builtin_fgets }, + {"fread", jx9Builtin_fread }, + {"fgetcsv", jx9Builtin_fgetcsv}, + {"fgetss", jx9Builtin_fgetss }, + {"readdir", jx9Builtin_readdir}, + {"rewinddir", jx9Builtin_rewinddir }, + {"closedir", jx9Builtin_closedir}, + {"opendir", jx9Builtin_opendir }, + {"readfile", jx9Builtin_readfile}, + {"file_get_contents", jx9Builtin_file_get_contents}, + {"file_put_contents", jx9Builtin_file_put_contents}, + {"file", jx9Builtin_file }, + {"copy", jx9Builtin_copy }, + {"fstat", jx9Builtin_fstat }, + {"fwrite", jx9Builtin_fwrite }, + {"fputs", jx9Builtin_fwrite }, + {"flock", jx9Builtin_flock }, + {"fclose", jx9Builtin_fclose }, + {"fopen", jx9Builtin_fopen }, + {"fpassthru", jx9Builtin_fpassthru }, + {"fputcsv", jx9Builtin_fputcsv }, + {"fprintf", jx9Builtin_fprintf }, +#if !defined(JX9_DISABLE_HASH_FUNC) + {"md5_file", jx9Builtin_md5_file}, + {"sha1_file", jx9Builtin_sha1_file}, +#endif /* JX9_DISABLE_HASH_FUNC */ + {"parse_ini_file", jx9Builtin_parse_ini_file}, + {"vfprintf", jx9Builtin_vfprintf} + }; + const jx9_io_stream *pFileStream = 0; + sxu32 n = 0; + /* Register the functions defined above */ + for( n = 0 ; n < SX_ARRAYSIZE(aVfsFunc) ; ++n ){ + jx9_create_function(&(*pVm), aVfsFunc[n].zName, aVfsFunc[n].xFunc, (void *)pVm->pEngine->pVfs); + } + for( n = 0 ; n < SX_ARRAYSIZE(aIOFunc) ; ++n ){ + jx9_create_function(&(*pVm), aIOFunc[n].zName, aIOFunc[n].xFunc, pVm); + } +#ifndef JX9_DISABLE_DISK_IO + /* Register the file stream if available */ +#ifdef __WINNT__ + pFileStream = &sWinFileStream; +#elif defined(__UNIXES__) + pFileStream = &sUnixFileStream; +#endif + /* Install the jx9:// stream */ + jx9_vm_config(pVm, JX9_VM_CONFIG_IO_STREAM, &sjx9Stream); +#endif /* JX9_DISABLE_DISK_IO */ + if( pFileStream ){ + /* Install the file:// stream */ + jx9_vm_config(pVm, JX9_VM_CONFIG_IO_STREAM, pFileStream); + } +#else + SXUNUSED(pVm); /* cc warning */ +#endif /* JX9_DISABLE_BUILTIN_FUNC */ + return SXRET_OK; +} +/* + * Export the STDIN handle. + */ +JX9_PRIVATE void * jx9ExportStdin(jx9_vm *pVm) +{ +#ifndef JX9_DISABLE_BUILTIN_FUNC +#ifndef JX9_DISABLE_DISK_IO + if( pVm->pStdin == 0 ){ + io_private *pIn; + /* Allocate an IO private instance */ + pIn = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private)); + if( pIn == 0 ){ + return 0; + } + InitIOPrivate(pVm, &sjx9Stream, pIn); + /* Initialize the handle */ + pIn->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDIN); + /* Install the STDIN stream */ + pVm->pStdin = pIn; + return pIn; + }else{ + /* NULL or STDIN */ + return pVm->pStdin; + } +#else + return 0; +#endif +#else + SXUNUSED(pVm); /* cc warning */ + return 0; +#endif +} +/* + * Export the STDOUT handle. + */ +JX9_PRIVATE void * jx9ExportStdout(jx9_vm *pVm) +{ +#ifndef JX9_DISABLE_BUILTIN_FUNC +#ifndef JX9_DISABLE_DISK_IO + if( pVm->pStdout == 0 ){ + io_private *pOut; + /* Allocate an IO private instance */ + pOut = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private)); + if( pOut == 0 ){ + return 0; + } + InitIOPrivate(pVm, &sjx9Stream, pOut); + /* Initialize the handle */ + pOut->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDOUT); + /* Install the STDOUT stream */ + pVm->pStdout = pOut; + return pOut; + }else{ + /* NULL or STDOUT */ + return pVm->pStdout; + } +#else + return 0; +#endif +#else + SXUNUSED(pVm); /* cc warning */ + return 0; +#endif +} +/* + * Export the STDERR handle. + */ +JX9_PRIVATE void * jx9ExportStderr(jx9_vm *pVm) +{ +#ifndef JX9_DISABLE_BUILTIN_FUNC +#ifndef JX9_DISABLE_DISK_IO + if( pVm->pStderr == 0 ){ + io_private *pErr; + /* Allocate an IO private instance */ + pErr = (io_private *)SyMemBackendAlloc(&pVm->sAllocator, sizeof(io_private)); + if( pErr == 0 ){ + return 0; + } + InitIOPrivate(pVm, &sjx9Stream, pErr); + /* Initialize the handle */ + pErr->pHandle = JX9StreamDataInit(pVm, JX9_IO_STREAM_STDERR); + /* Install the STDERR stream */ + pVm->pStderr = pErr; + return pErr; + }else{ + /* NULL or STDERR */ + return pVm->pStderr; + } +#else + return 0; +#endif +#else + SXUNUSED(pVm); /* cc warning */ + return 0; +#endif +} -- cgit v1.2.3