/* * 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 }