*** /dev/null Sat Nov 23 05:33:00 2024
--- - Sat Nov 23 05:33:05 2024
***************
*** 0 ****
--- 1,2720 ----
+ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=80:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+ /*
+ * JS File object
+ */
+ #if JS_HAS_FILE_OBJECT
+
+ #include "jsstddef.h"
+
+ /* ----------------- Platform-specific includes and defines ----------------- */
+ #if defined(XP_WIN) || defined(XP_OS2)
+ # include <direct.h>
+ # include <io.h>
+ # include <sys/types.h>
+ # include <sys/stat.h>
+ # define FILESEPARATOR '\\'
+ # define FILESEPARATOR2 '/'
+ # define CURRENT_DIR "c:\\"
+ # define POPEN _popen
+ # define PCLOSE _pclose
+ #elif defined(XP_UNIX) || defined(XP_BEOS)
+ # include <strings.h>
+ # include <stdio.h>
+ # include <stdlib.h>
+ # include <unistd.h>
+ # define FILESEPARATOR '/'
+ # define FILESEPARATOR2 '\0'
+ # define CURRENT_DIR "/"
+ # define POPEN popen
+ # define PCLOSE pclose
+ #endif
+
+ /* --------------- Platform-independent includes and defines ---------------- */
+ #include "jsapi.h"
+ #include "jsatom.h"
+ #include "jscntxt.h"
+ #include "jsdate.h"
+ #include "jsdbgapi.h"
+ #include "jsemit.h"
+ #include "jsfun.h"
+ #include "jslock.h"
+ #include "jsobj.h"
+ #include "jsparse.h"
+ #include "jsscan.h"
+ #include "jsscope.h"
+ #include "jsscript.h"
+ #include "jsstr.h"
+ #include "jsutil.h" /* Added by JSIFY */
+ #include <string.h>
+
+ /* NSPR dependencies */
+ #include "prio.h"
+ #include "prerror.h"
+
+ #define SPECIAL_FILE_STRING "Special File"
+ #define CURRENTDIR_PROPERTY "currentDir"
+ #define SEPARATOR_PROPERTY "separator"
+ #define FILE_CONSTRUCTOR "File"
+ #define PIPE_SYMBOL '|'
+
+ #define ASCII 0
+ #define UTF8 1
+ #define UCS2 2
+
+ #define asciistring "text"
+ #define utfstring "binary"
+ #define unicodestring "unicode"
+
+ #define MAX_PATH_LENGTH 1024
+ #define MODE_SIZE 256
+ #define NUMBER_SIZE 32
+ #define MAX_LINE_LENGTH 256
+ #define URL_PREFIX "file://"
+
+ #define STDINPUT_NAME "Standard input stream"
+ #define STDOUTPUT_NAME "Standard output stream"
+ #define STDERROR_NAME "Standard error stream"
+
+ #define RESOLVE_PATH js_canonicalPath /* js_absolutePath */
+
+ /* Error handling */
+ typedef enum JSFileErrNum {
+ #define MSG_DEF(name, number, count, exception, format) \
+ name = number,
+ #include "jsfile.msg"
+ #undef MSG_DEF
+ JSFileErr_Limit
+ #undef MSGDEF
+ } JSFileErrNum;
+
+ #define JSFILE_HAS_DFLT_MSG_STRINGS 1
+
+ JSErrorFormatString JSFile_ErrorFormatString[JSFileErr_Limit] = {
+ #if JSFILE_HAS_DFLT_MSG_STRINGS
+ #define MSG_DEF(name, number, count, exception, format) \
+ { format, count },
+ #else
+ #define MSG_DEF(name, number, count, exception, format) \
+ { NULL, count },
+ #endif
+ #include "jsfile.msg"
+ #undef MSG_DEF
+ };
+
+ const JSErrorFormatString *
+ JSFile_GetErrorMessage(void *userRef, const char *locale,
+ const uintN errorNumber)
+ {
+ if ((errorNumber > 0) && (errorNumber < JSFileErr_Limit))
+ return &JSFile_ErrorFormatString[errorNumber];
+ else
+ return NULL;
+ }
+
+ #define JSFILE_CHECK_NATIVE(op) \
+ if (file->isNative) { \
+ JS_ReportWarning(cx, "Cannot call or access \"%s\" on native file %s",\
+ op, file->path); \
+ goto out; \
+ }
+
+ #define JSFILE_CHECK_WRITE \
+ if (!file->isOpen) { \
+ JS_ReportWarning(cx, \
+ "File %s is closed, will open it for writing, proceeding", \
+ file->path); \
+ js_FileOpen(cx, obj, file, "write,append,create"); \
+ } \
+ if (!js_canWrite(cx, file)) { \
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \
+ JSFILEMSG_CANNOT_WRITE, file->path); \
+ goto out; \
+ }
+
+ #define JSFILE_CHECK_READ \
+ if (!file->isOpen) { \
+ JS_ReportWarning(cx, \
+ "File %s is closed, will open it for reading, proceeding", \
+ file->path); \
+ js_FileOpen(cx, obj, file, "read"); \
+ } \
+ if (!js_canRead(cx, file)) { \
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \
+ JSFILEMSG_CANNOT_READ, file->path); \
+ goto out; \
+ }
+
+ #define JSFILE_CHECK_OPEN(op) \
+ if (!file->isOpen) { \
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \
+ JSFILEMSG_FILE_MUST_BE_CLOSED, op); \
+ goto out; \
+ }
+
+ #define JSFILE_CHECK_CLOSED(op) \
+ if (file->isOpen) { \
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \
+ JSFILEMSG_FILE_MUST_BE_OPEN, op); \
+ goto out; \
+ }
+
+ #define JSFILE_CHECK_ONE_ARG(op) \
+ if (argc != 1) { \
+ char str[NUMBER_SIZE]; \
+ sprintf(str, "%d", argc); \
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \
+ JSFILEMSG_EXPECTS_ONE_ARG_ERROR, op, str); \
+ goto out; \
+ }
+
+
+ /*
+ Security mechanism, should define a callback for this.
+ The parameters are as follows:
+ SECURITY_CHECK(JSContext *cx, JSPrincipals *ps, char *op_name, JSFile *file)
+ XXX Should this be a real function returning a JSBool result (and getting
+ some typesafety help from the compiler?).
+ */
+ #define SECURITY_CHECK(cx, ps, op, file) \
+ /* Define a callback here... */
+
+
+ /* Structure representing the file internally */
+ typedef struct JSFile {
+ char *path; /* the path to the file. */
+ JSBool isOpen;
+ int32 mode; /* mode used to open the file: read, write, append, create, etc.. */
+ int32 type; /* Asciiz, utf, unicode */
+ char byteBuffer[3]; /* bytes read in advance by js_FileRead ( UTF8 encoding ) */
+ jsint nbBytesInBuf; /* number of bytes stored in the buffer above */
+ jschar charBuffer; /* character read in advance by readln ( mac files only ) */
+ JSBool charBufferUsed; /* flag indicating if the buffer above is being used */
+ JSBool hasRandomAccess;/* can the file be randomly accessed? false for stdin, and
+ UTF-encoded files. */
+ JSBool hasAutoflush; /* should we force a flush for each line break? */
+ JSBool isNative; /* if the file is using OS-specific file FILE type */
+ /* We can actually put the following two in a union since they should never be used at the same time */
+ PRFileDesc *handle; /* the handle for the file, if open. */
+ FILE *nativehandle; /* native handle, for stuff NSPR doesn't do. */
+ JSBool isPipe; /* if the file is really an OS pipe */
+ } JSFile;
+
+ /* a few forward declarations... */
+ static JSClass file_class;
+ JS_PUBLIC_API(JSObject*) js_NewFileObject(JSContext *cx, char *filename);
+ static JSBool file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
+ static JSBool file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
+
+ /* New filename manipulation procesures */
+ /* assumes we don't have leading/trailing spaces */
+ static JSBool
+ js_filenameHasAPipe(const char *filename)
+ {
+ if (!filename)
+ return JS_FALSE;
+
+ return filename[0] == PIPE_SYMBOL ||
+ filename[strlen(filename) - 1] == PIPE_SYMBOL;
+ }
+
+ static JSBool
+ js_isAbsolute(const char *name)
+ {
+ #if defined(XP_WIN) || defined(XP_OS2)
+ return *name && name[1] == ':';
+ #else
+ return (name[0]
+ # if defined(XP_UNIX) || defined(XP_BEOS)
+ ==
+ # else
+ !=
+ # endif
+ FILESEPARATOR);
+ #endif
+ }
+
+ /*
+ * Concatinates base and name to produce a valid filename.
+ * Returned string must be freed.
+ */
+ static char*
+ js_combinePath(JSContext *cx, const char *base, const char *name)
+ {
+ int len = strlen(base);
+ char* result = JS_malloc(cx, len + strlen(name) + 2);
+
+ if (!result)
+ return NULL;
+
+ strcpy(result, base);
+
+ if (base[len - 1] != FILESEPARATOR && base[len - 1] != FILESEPARATOR2) {
+ result[len] = FILESEPARATOR;
+ result[len + 1] = '\0';
+ }
+ strcat(result, name);
+ return result;
+ }
+
+ /* Extract the last component from a path name. Returned string must be freed */
+ static char *
+ js_fileBaseName(JSContext *cx, const char *pathname)
+ {
+ jsint index, aux;
+ char *result;
+
+ index = strlen(pathname)-1;
+
+ /* Chop off trailing seperators. */
+ while (index > 0 && (pathname[index]==FILESEPARATOR ||
+ pathname[index]==FILESEPARATOR2)) {
+ --index;
+ }
+
+ aux = index;
+
+ /* Now find the next separator. */
+ while (index >= 0 && pathname[index] != FILESEPARATOR &&
+ pathname[index] != FILESEPARATOR2) {
+ --index;
+ }
+
+ /* Allocate and copy. */
+ result = JS_malloc(cx, aux - index + 1);
+ if (!result)
+ return NULL;
+ strncpy(result, pathname + index + 1, aux - index);
+ result[aux - index] = '\0';
+ return result;
+ }
+
+ /*
+ * Returns everything but the last component from a path name.
+ * Returned string must be freed.
+ */
+ static char *
+ js_fileDirectoryName(JSContext *cx, const char *pathname)
+ {
+ char *result;
+ const char *cp, *end;
+ size_t pathsize;
+
+ end = pathname + strlen(pathname);
+ cp = end - 1;
+
+ /* If this is already a directory, chop off the trailing /s. */
+ while (cp >= pathname) {
+ if (*cp != FILESEPARATOR && *cp != FILESEPARATOR2)
+ break;
+ --cp;
+ }
+
+ if (cp < pathname && end != pathname) {
+ /* There were just /s, return the root. */
+ result = JS_malloc(cx, 1 + 1); /* The separator + trailing NUL. */
+ result[0] = FILESEPARATOR;
+ result[1] = '\0';
+ return result;
+ }
+
+ /* Now chop off the last portion. */
+ while (cp >= pathname) {
+ if (*cp == FILESEPARATOR || *cp == FILESEPARATOR2)
+ break;
+ --cp;
+ }
+
+ /* Check if this is a leaf. */
+ if (cp < pathname) {
+ /* It is, return "pathname/". */
+ if (end[-1] == FILESEPARATOR || end[-1] == FILESEPARATOR2) {
+ /* Already has its terminating /. */
+ return JS_strdup(cx, pathname);
+ }
+
+ pathsize = end - pathname + 1;
+ result = JS_malloc(cx, pathsize + 1);
+ if (!result)
+ return NULL;
+
+ strcpy(result, pathname);
+ result[pathsize - 1] = FILESEPARATOR;
+ result[pathsize] = '\0';
+
+ return result;
+ }
+
+ /* Return everything up to and including the seperator. */
+ pathsize = cp - pathname + 1;
+ result = JS_malloc(cx, pathsize + 1);
+ if (!result)
+ return NULL;
+
+ strncpy(result, pathname, pathsize);
+ result[pathsize] = '\0';
+
+ return result;
+ }
+
+ static char *
+ js_absolutePath(JSContext *cx, const char * path)
+ {
+ JSObject *obj;
+ JSString *str;
+ jsval prop;
+
+ if (js_isAbsolute(path)) {
+ return JS_strdup(cx, path);
+ } else {
+ obj = JS_GetGlobalObject(cx);
+ if (!JS_GetProperty(cx, obj, FILE_CONSTRUCTOR, &prop)) {
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_FILE_CONSTRUCTOR_UNDEFINED_ERROR);
+ return JS_strdup(cx, path);
+ }
+
+ obj = JSVAL_TO_OBJECT(prop);
+ if (!JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, &prop)) {
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_FILE_CURRENTDIR_UNDEFINED_ERROR);
+ return JS_strdup(cx, path);
+ }
+
+ str = JS_ValueToString(cx, prop);
+ if (!str)
+ return JS_strdup(cx, path);
+
+ /* should we have an array of curr dirs indexed by drive for windows? */
+ return js_combinePath(cx, JS_GetStringBytes(str), path);
+ }
+ }
+
+ /* Side effect: will remove spaces in the beginning/end of the filename */
+ static char *
+ js_canonicalPath(JSContext *cx, char *oldpath)
+ {
+ char *tmp;
+ char *path = oldpath;
+ char *base, *dir, *current, *result;
+ jsint c;
+ jsint back = 0;
+ unsigned int i = 0, j = strlen(path)-1;
+
+ /* This is probably optional */
+ /* Remove possible spaces in the beginning and end */
+ while (i < j && path[i] == ' ')
+ i++;
+ while (j >= 0 && path[j] == ' ')
+ j--;
+
+ tmp = JS_malloc(cx, j-i+2);
+ if (!tmp)
+ return NULL;
+
+ strncpy(tmp, path + i, j - i + 1);
+ tmp[j - i + 1] = '\0';
+
+ path = tmp;
+
+ /* Pipe support. */
+ if (js_filenameHasAPipe(path))
+ return path;
+
+ /* file:// support. */
+ if (!strncmp(path, URL_PREFIX, strlen(URL_PREFIX))) {
+ tmp = js_canonicalPath(cx, path + strlen(URL_PREFIX));
+ JS_free(cx, path);
+ return tmp;
+ }
+
+ if (!js_isAbsolute(path)) {
+ tmp = js_absolutePath(cx, path);
+ if (!tmp)
+ return NULL;
+ path = tmp;
+ }
+
+ result = JS_strdup(cx, "");
+
+ current = path;
+
+ base = js_fileBaseName(cx, current);
+ dir = js_fileDirectoryName(cx, current);
+
+ while (strcmp(dir, current)) {
+ if (!strcmp(base, "..")) {
+ back++;
+ } else {
+ if (back > 0) {
+ back--;
+ } else {
+ tmp = result;
+ result = JS_malloc(cx, strlen(base) + 1 + strlen(tmp) + 1);
+ if (!result)
+ goto out;
+
+ strcpy(result, base);
+ c = strlen(result);
+ if (*tmp) {
+ result[c] = FILESEPARATOR;
+ result[c + 1] = '\0';
+ strcat(result, tmp);
+ }
+ JS_free(cx, tmp);
+ }
+ }
+ JS_free(cx, current);
+ JS_free(cx, base);
+ current = dir;
+ base = js_fileBaseName(cx, current);
+ dir = js_fileDirectoryName(cx, current);
+ }
+
+ tmp = result;
+ result = JS_malloc(cx, strlen(dir)+1+strlen(tmp)+1);
+ if (!result)
+ goto out;
+
+ strcpy(result, dir);
+ c = strlen(result);
+ if (tmp[0]!='\0') {
+ if ((result[c-1]!=FILESEPARATOR)&&(result[c-1]!=FILESEPARATOR2)) {
+ result[c] = FILESEPARATOR;
+ result[c+1] = '\0';
+ }
+ strcat(result, tmp);
+ }
+
+ out:
+ if (tmp)
+ JS_free(cx, tmp);
+ if (dir)
+ JS_free(cx, dir);
+ if (base)
+ JS_free(cx, base);
+ if (current)
+ JS_free(cx, current);
+
+ return result;
+ }
+
+ /* -------------------------- Text conversion ------------------------------- */
+ /* The following is ripped from libi18n/unicvt.c and include files.. */
+
+ /*
+ * UTF8 defines and macros
+ */
+ #define ONE_OCTET_BASE 0x00 /* 0xxxxxxx */
+ #define ONE_OCTET_MASK 0x7F /* x1111111 */
+ #define CONTINUING_OCTET_BASE 0x80 /* 10xxxxxx */
+ #define CONTINUING_OCTET_MASK 0x3F /* 00111111 */
+ #define TWO_OCTET_BASE 0xC0 /* 110xxxxx */
+ #define TWO_OCTET_MASK 0x1F /* 00011111 */
+ #define THREE_OCTET_BASE 0xE0 /* 1110xxxx */
+ #define THREE_OCTET_MASK 0x0F /* 00001111 */
+ #define FOUR_OCTET_BASE 0xF0 /* 11110xxx */
+ #define FOUR_OCTET_MASK 0x07 /* 00000111 */
+ #define FIVE_OCTET_BASE 0xF8 /* 111110xx */
+ #define FIVE_OCTET_MASK 0x03 /* 00000011 */
+ #define SIX_OCTET_BASE 0xFC /* 1111110x */
+ #define SIX_OCTET_MASK 0x01 /* 00000001 */
+
+ #define IS_UTF8_1ST_OF_1(x) (( (x)&~ONE_OCTET_MASK ) == ONE_OCTET_BASE)
+ #define IS_UTF8_1ST_OF_2(x) (( (x)&~TWO_OCTET_MASK ) == TWO_OCTET_BASE)
+ #define IS_UTF8_1ST_OF_3(x) (( (x)&~THREE_OCTET_MASK) == THREE_OCTET_BASE)
+ #define IS_UTF8_1ST_OF_4(x) (( (x)&~FOUR_OCTET_MASK ) == FOUR_OCTET_BASE)
+ #define IS_UTF8_1ST_OF_5(x) (( (x)&~FIVE_OCTET_MASK ) == FIVE_OCTET_BASE)
+ #define IS_UTF8_1ST_OF_6(x) (( (x)&~SIX_OCTET_MASK ) == SIX_OCTET_BASE)
+ #define IS_UTF8_2ND_THRU_6TH(x) \
+ (( (x)&~CONTINUING_OCTET_MASK ) == CONTINUING_OCTET_BASE)
+ #define IS_UTF8_1ST_OF_UCS2(x) \
+ IS_UTF8_1ST_OF_1(x) \
+ || IS_UTF8_1ST_OF_2(x) \
+ || IS_UTF8_1ST_OF_3(x)
+
+
+ #define MAX_UCS2 0xFFFF
+ #define DEFAULT_CHAR 0x003F /* Default char is "?" */
+ #define BYTE_MASK 0xBF
+ #define BYTE_MARK 0x80
+
+
+ /* Function: one_ucs2_to_utf8_char
+ *
+ * Function takes one UCS-2 char and writes it to a UTF-8 buffer.
+ * We need a UTF-8 buffer because we don't know before this
+ * function how many bytes of utf-8 data will be written. It also
+ * takes a pointer to the end of the UTF-8 buffer so that we don't
+ * overwrite data. This function returns the number of UTF-8 bytes
+ * of data written, or -1 if the buffer would have been overrun.
+ */
+
+ #define LINE_SEPARATOR 0x2028
+ #define PARAGRAPH_SEPARATOR 0x2029
+ static int16 one_ucs2_to_utf8_char(unsigned char *tobufp,
+ unsigned char *tobufendp,
+ uint16 onechar)
+ {
+ int16 numUTF8bytes = 0;
+
+ if (onechar == LINE_SEPARATOR || onechar == PARAGRAPH_SEPARATOR) {
+ strcpy((char*)tobufp, "\n");
+ return strlen((char*)tobufp);
+ }
+
+ if (onechar < 0x80) {
+ numUTF8bytes = 1;
+ } else if (onechar < 0x800) {
+ numUTF8bytes = 2;
+ } else {
+ /* 0x800 >= onechar <= MAX_UCS2 */
+ numUTF8bytes = 3;
+ }
+
+ tobufp += numUTF8bytes;
+
+ /* return error if we don't have space for the whole character */
+ if (tobufp > tobufendp) {
+ return(-1);
+ }
+
+ switch(numUTF8bytes) {
+ case 3: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
+ *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
+ *--tobufp = onechar | THREE_OCTET_BASE;
+ break;
+
+ case 2: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
+ *--tobufp = onechar | TWO_OCTET_BASE;
+ break;
+
+ case 1: *--tobufp = (unsigned char)onechar;
+ break;
+ }
+
+ return numUTF8bytes;
+ }
+
+ /*
+ * utf8_to_ucs2_char
+ *
+ * Convert a utf8 multibyte character to ucs2
+ *
+ * inputs: pointer to utf8 character(s)
+ * length of utf8 buffer ("read" length limit)
+ * pointer to return ucs2 character
+ *
+ * outputs: number of bytes in the utf8 character
+ * -1 if not a valid utf8 character sequence
+ * -2 if the buffer is too short
+ */
+ static int16
+ utf8_to_ucs2_char(const unsigned char *utf8p, int16 buflen, uint16 *ucs2p)
+ {
+ uint16 lead, cont1, cont2;
+
+ /*
+ * Check for minimum buffer length
+ */
+ if ((buflen < 1) || (utf8p == NULL)) {
+ return -2;
+ }
+ lead = (uint16) (*utf8p);
+
+ /*
+ * Check for a one octet sequence
+ */
+ if (IS_UTF8_1ST_OF_1(lead)) {
+ *ucs2p = lead & ONE_OCTET_MASK;
+ return 1;
+ }
+
+ /*
+ * Check for a two octet sequence
+ */
+ if (IS_UTF8_1ST_OF_2(*utf8p)) {
+ if (buflen < 2)
+ return -2;
+ cont1 = (uint16) *(utf8p+1);
+ if (!IS_UTF8_2ND_THRU_6TH(cont1))
+ return -1;
+ *ucs2p = (lead & TWO_OCTET_MASK) << 6;
+ *ucs2p |= cont1 & CONTINUING_OCTET_MASK;
+ return 2;
+ }
+
+ /*
+ * Check for a three octet sequence
+ */
+ else if (IS_UTF8_1ST_OF_3(lead)) {
+ if (buflen < 3)
+ return -2;
+ cont1 = (uint16) *(utf8p+1);
+ cont2 = (uint16) *(utf8p+2);
+ if ( (!IS_UTF8_2ND_THRU_6TH(cont1))
+ || (!IS_UTF8_2ND_THRU_6TH(cont2)))
+ return -1;
+ *ucs2p = (lead & THREE_OCTET_MASK) << 12;
+ *ucs2p |= (cont1 & CONTINUING_OCTET_MASK) << 6;
+ *ucs2p |= cont2 & CONTINUING_OCTET_MASK;
+ return 3;
+ }
+ else { /* not a valid utf8/ucs2 character */
+ return -1;
+ }
+ }
+
+ /* ----------------------------- Helper functions --------------------------- */
+ /* Ripped off from lm_win.c .. */
+ /* where is strcasecmp?.. for now, it's case sensitive..
+ *
+ * strcasecmp is in strings.h, but on windows it's called _stricmp...
+ * will need to #ifdef this
+ */
+
+ static int32
+ js_FileHasOption(JSContext *cx, const char *oldoptions, const char *name)
+ {
+ char *comma, *equal, *current;
+ char *options = JS_strdup(cx, oldoptions);
+ int32 found = 0;
+
+ current = options;
+ for (;;) {
+ comma = strchr(current, ',');
+ if (comma) *comma = '\0';
+ equal = strchr(current, '=');
+ if (equal) *equal = '\0';
+ if (strcmp(current, name) == 0) {
+ if (!equal || strcmp(equal + 1, "yes") == 0)
+ found = 1;
+ else
+ found = atoi(equal + 1);
+ }
+ if (equal) *equal = '=';
+ if (comma) *comma = ',';
+ if (found || !comma)
+ break;
+ current = comma + 1;
+ }
+ JS_free(cx, options);
+ return found;
+ }
+
+ /* empty the buffer */
+ static void
+ js_ResetBuffers(JSFile * file)
+ {
+ file->charBufferUsed = JS_FALSE;
+ file->nbBytesInBuf = 0;
+ }
+
+ /* Reset file attributes */
+ static void
+ js_ResetAttributes(JSFile * file)
+ {
+ file->mode = file->type = 0;
+ file->isOpen = JS_FALSE;
+ file->handle = NULL;
+ file->nativehandle = NULL;
+ file->hasRandomAccess = JS_TRUE; /* Innocent until proven guilty. */
+ file->hasAutoflush = JS_FALSE;
+ file->isNative = JS_FALSE;
+ file->isPipe = JS_FALSE;
+
+ js_ResetBuffers(file);
+ }
+
+ static JSBool
+ js_FileOpen(JSContext *cx, JSObject *obj, JSFile *file, char *mode){
+ JSString *type, *mask;
+ jsval v[2];
+ jsval rval;
+
+ type = JS_InternString(cx, asciistring);
+ mask = JS_NewStringCopyZ(cx, mode);
+ v[0] = STRING_TO_JSVAL(mask);
+ v[1] = STRING_TO_JSVAL(type);
+
+ if (!file_open(cx, obj, 2, v, &rval))
+ return JS_FALSE;
+ return JS_TRUE;
+ }
+
+ /* Buffered version of PR_Read. Used by js_FileRead */
+ static int32
+ js_BufferedRead(JSFile * f, char *buf, int32 len)
+ {
+ int32 count = 0;
+
+ while (f->nbBytesInBuf>0&&len>0) {
+ buf[0] = f->byteBuffer[0];
+ f->byteBuffer[0] = f->byteBuffer[1];
+ f->byteBuffer[1] = f->byteBuffer[2];
+ f->nbBytesInBuf--;
+ len--;
+ buf+=1;
+ count++;
+ }
+
+ if (len>0) {
+ count += (!f->isNative)
+ ? PR_Read(f->handle, buf, len)
+ : fread(buf, 1, len, f->nativehandle);
+ }
+ return count;
+ }
+
+ static int32
+ js_FileRead(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode)
+ {
+ unsigned char *aux;
+ int32 count = 0, i;
+ jsint remainder;
+ unsigned char utfbuf[3];
+
+ if (file->charBufferUsed) {
+ buf[0] = file->charBuffer;
+ buf++;
+ len--;
+ file->charBufferUsed = JS_FALSE;
+ }
+
+ switch (mode) {
+ case ASCII:
+ aux = (unsigned char*)JS_malloc(cx, len);
+ if (!aux)
+ return 0;
+
+ count = js_BufferedRead(file, aux, len);
+ if (count == -1) {
+ JS_free(cx, aux);
+ return 0;
+ }
+
+ for (i = 0; i < len; i++)
+ buf[i] = (jschar)aux[i];
+
+ JS_free(cx, aux);
+ break;
+
+ case UTF8:
+ remainder = 0;
+ for (count = 0;count<len;count++) {
+ i = js_BufferedRead(file, utfbuf+remainder, 3-remainder);
+ if (i<=0) {
+ return count;
+ }
+ i = utf8_to_ucs2_char(utfbuf, (int16)i, &buf[count] );
+ if (i<0) {
+ return count;
+ } else {
+ if (i==1) {
+ utfbuf[0] = utfbuf[1];
+ utfbuf[1] = utfbuf[2];
+ remainder = 2;
+ } else if (i==2) {
+ utfbuf[0] = utfbuf[2];
+ remainder = 1;
+ } else if (i==3) {
+ remainder = 0;
+ }
+ }
+ }
+ while (remainder>0) {
+ file->byteBuffer[file->nbBytesInBuf] = utfbuf[0];
+ file->nbBytesInBuf++;
+ utfbuf[0] = utfbuf[1];
+ utfbuf[1] = utfbuf[2];
+ remainder--;
+ }
+ break;
+
+ case UCS2:
+ count = js_BufferedRead(file, (char*)buf, len*2) >> 1;
+ if (count == -1)
+ return 0;
+
+ break;
+
+ default:
+ /* Not reached. */
+ JS_ASSERT(0);
+ }
+
+ if(count == -1) {
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_OP_FAILED, "read", file->path);
+ }
+
+ return count;
+ }
+
+ static int32
+ js_FileSeek(JSContext *cx, JSFile *file, int32 len, int32 mode)
+ {
+ int32 count = 0, i;
+ jsint remainder;
+ unsigned char utfbuf[3];
+ jschar tmp;
+
+ switch (mode) {
+ case ASCII:
+ count = PR_Seek(file->handle, len, PR_SEEK_CUR);
+ break;
+
+ case UTF8:
+ remainder = 0;
+ for (count = 0;count<len;count++) {
+ i = js_BufferedRead(file, utfbuf+remainder, 3-remainder);
+ if (i<=0) {
+ return 0;
+ }
+ i = utf8_to_ucs2_char(utfbuf, (int16)i, &tmp );
+ if (i<0) {
+ return 0;
+ } else {
+ if (i==1) {
+ utfbuf[0] = utfbuf[1];
+ utfbuf[1] = utfbuf[2];
+ remainder = 2;
+ } else if (i==2) {
+ utfbuf[0] = utfbuf[2];
+ remainder = 1;
+ } else if (i==3) {
+ remainder = 0;
+ }
+ }
+ }
+ while (remainder>0) {
+ file->byteBuffer[file->nbBytesInBuf] = utfbuf[0];
+ file->nbBytesInBuf++;
+ utfbuf[0] = utfbuf[1];
+ utfbuf[1] = utfbuf[2];
+ remainder--;
+ }
+ break;
+
+ case UCS2:
+ count = PR_Seek(file->handle, len*2, PR_SEEK_CUR)/2;
+ break;
+
+ default:
+ /* Not reached. */
+ JS_ASSERT(0);
+ }
+
+ if(count == -1) {
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_OP_FAILED, "seek", file->path);
+ }
+
+ return count;
+ }
+
+ static int32
+ js_FileWrite(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode)
+ {
+ unsigned char *aux;
+ int32 count = 0, i, j;
+ unsigned char *utfbuf;
+
+ switch (mode) {
+ case ASCII:
+ aux = (unsigned char*)JS_malloc(cx, len);
+ if (!aux)
+ return 0;
+
+ for (i = 0; i<len; i++)
+ aux[i] = buf[i] % 256;
+
+ count = (!file->isNative)
+ ? PR_Write(file->handle, aux, len)
+ : fwrite(aux, 1, len, file->nativehandle);
+
+ if (count==-1) {
+ JS_free(cx, aux);
+ return 0;
+ }
+
+ JS_free(cx, aux);
+ break;
+
+ case UTF8:
+ utfbuf = (unsigned char*)JS_malloc(cx, len*3);
+ if (!utfbuf) return 0;
+ i = 0;
+ for (count = 0;count<len;count++) {
+ j = one_ucs2_to_utf8_char(utfbuf+i, utfbuf+len*3, buf[count]);
+ if (j==-1) {
+ JS_free(cx, utfbuf);
+ return 0;
+ }
+ i+=j;
+ }
+ j = (!file->isNative)
+ ? PR_Write(file->handle, utfbuf, i)
+ : fwrite(utfbuf, 1, i, file->nativehandle);
+
+ if (j<i) {
+ JS_free(cx, utfbuf);
+ return 0;
+ }
+ JS_free(cx, utfbuf);
+ break;
+
+ case UCS2:
+ count = (!file->isNative)
+ ? PR_Write(file->handle, buf, len*2) >> 1
+ : fwrite(buf, 1, len*2, file->nativehandle) >> 1;
+
+ if (count == -1)
+ return 0;
+ break;
+
+ default:
+ /* Not reached. */
+ JS_ASSERT(0);
+ }
+
+ if(count == -1) {
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_OP_FAILED, "write", file->path);
+ }
+
+ return count;
+ }
+
+ /* ----------------------------- Property checkers -------------------------- */
+ static JSBool
+ js_exists(JSContext *cx, JSFile *file)
+ {
+ if (file->isNative) {
+ /* It doesn't make sense for a pipe of stdstream. */
+ return JS_FALSE;
+ }
+
+ return PR_Access(file->path, PR_ACCESS_EXISTS) == PR_SUCCESS;
+ }
+
+ static JSBool
+ js_canRead(JSContext *cx, JSFile *file)
+ {
+ if (!file->isNative) {
+ if (file->isOpen && !(file->mode & PR_RDONLY))
+ return JS_FALSE;
+ return PR_Access(file->path, PR_ACCESS_READ_OK) == PR_SUCCESS;
+ }
+
+ if (file->isPipe) {
+ /* Is this pipe open for reading? */
+ return file->path[0] == PIPE_SYMBOL;
+ }
+
+ return !strcmp(file->path, STDINPUT_NAME);
+ }
+
+ static JSBool
+ js_canWrite(JSContext *cx, JSFile *file)
+ {
+ if (!file->isNative) {
+ if (file->isOpen && !(file->mode & PR_WRONLY))
+ return JS_FALSE;
+ return PR_Access(file->path, PR_ACCESS_WRITE_OK) == PR_SUCCESS;
+ }
+
+ if(file->isPipe) {
+ /* Is this pipe open for writing? */
+ return file->path[strlen(file->path)-1] == PIPE_SYMBOL;
+ }
+
+ return !strcmp(file->path, STDOUTPUT_NAME) ||
+ !strcmp(file->path, STDERROR_NAME);
+ }
+
+ static JSBool
+ js_isFile(JSContext *cx, JSFile *file)
+ {
+ if (!file->isNative) {
+ PRFileInfo info;
+
+ if (file->isOpen
+ ? PR_GetOpenFileInfo(file->handle, &info)
+ : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) {
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
+ return JS_FALSE;
+ }
+
+ return info.type == PR_FILE_FILE;
+ }
+
+ /* This doesn't make sense for a pipe of stdstream. */
+ return JS_FALSE;
+ }
+
+ static JSBool
+ js_isDirectory(JSContext *cx, JSFile *file)
+ {
+ if(!file->isNative){
+ PRFileInfo info;
+
+ /* Hack needed to get get_property to work. */
+ if (!js_exists(cx, file))
+ return JS_FALSE;
+
+ if (file->isOpen
+ ? PR_GetOpenFileInfo(file->handle, &info)
+ : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) {
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
+ return JS_FALSE;
+ }
+
+ return info.type == PR_FILE_DIRECTORY;
+ }
+
+ /* This doesn't make sense for a pipe of stdstream. */
+ return JS_FALSE;
+ }
+
+ static jsval
+ js_size(JSContext *cx, JSFile *file)
+ {
+ PRFileInfo info;
+
+ JSFILE_CHECK_NATIVE("size");
+
+ if (file->isOpen
+ ? PR_GetOpenFileInfo(file->handle, &info)
+ : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) {
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
+ return JS_FALSE;
+ }
+
+ return INT_TO_JSVAL(info.size);
+
+ out:
+ return JSVAL_VOID;
+ }
+
+ /*
+ * Return the parent object
+ */
+ static JSBool
+ js_parent(JSContext *cx, JSFile *file, jsval *resultp)
+ {
+ char *str;
+
+ /* Since we only care about pipes and native files, return NULL. */
+ if (file->isNative) {
+ *resultp = JSVAL_VOID;
+ return JS_TRUE;
+ }
+
+ str = js_fileDirectoryName(cx, file->path);
+ if (!str)
+ return JS_FALSE;
+
+ /* If the directory is equal to the original path, we're at the root. */
+ if (!strcmp(file->path, str)) {
+ *resultp = JSVAL_NULL;
+ } else {
+ JSObject *obj = js_NewFileObject(cx, str);
+ if (!obj) {
+ JS_free(cx, str);
+ return JS_FALSE;
+ }
+ *resultp = OBJECT_TO_JSVAL(obj);
+ }
+
+ JS_free(cx, str);
+ return JS_TRUE;
+ }
+
+ static JSBool
+ js_name(JSContext *cx, JSFile *file, jsval *vp)
+ {
+ char *name;
+ JSString *str;
+
+ if (file->isPipe) {
+ *vp = JSVAL_VOID;
+ return JS_TRUE;
+ }
+
+ name = js_fileBaseName(cx, file->path);
+ if (!name)
+ return JS_FALSE;
+
+ str = JS_NewString(cx, name, strlen(name));
+ if (!str) {
+ JS_free(cx, name);
+ return JS_FALSE;
+ }
+
+ *vp = STRING_TO_JSVAL(str);
+ return JS_TRUE;
+ }
+
+ /* ------------------------------ File object methods ---------------------------- */
+ static JSBool
+ file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+ {
+ JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+ JSString *strmode, *strtype;
+ char *ctype, *mode;
+ int32 mask, type;
+ int len;
+
+ mode = NULL;
+
+ SECURITY_CHECK(cx, NULL, "open", file);
+
+ /* A native file that is already open */
+ if(file->isOpen && file->isNative) {
+ JS_ReportWarning(cx, "Native file %s is already open, proceeding",
+ file->path);
+ goto good;
+ }
+
+ /* Close before proceeding */
+ if (file->isOpen) {
+ JS_ReportWarning(cx, "File %s is already open, we will close it and "
+ "reopen, proceeding", file->path);
+ if(!file_close(cx, obj, 0, NULL, rval))
+ goto out;
+ }
+
+ if (js_isDirectory(cx, file)) {
+ JS_ReportWarning(cx, "%s seems to be a directory, there is no point in "
+ "trying to open it, proceeding", file->path);
+ goto good;
+ }
+
+ /* Path must be defined at this point */
+ len = strlen(file->path);
+
+ /* Mode */
+ if (argc >= 1) {
+ strmode = JS_ValueToString(cx, argv[0]);
+ if (!strmode) {
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_FIRST_ARGUMENT_OPEN_NOT_STRING_ERROR,
+ argv[0]);
+ goto out;
+ }
+ mode = JS_strdup(cx, JS_GetStringBytes(strmode));
+ } else {
+ if(file->path[0]==PIPE_SYMBOL) {
+ /* pipe default mode */
+ mode = JS_strdup(cx, "read");
+ } else if(file->path[len-1]==PIPE_SYMBOL) {
+ /* pipe default mode */
+ mode = JS_strdup(cx, "write");
+ } else {
+ /* non-destructive, permissive defaults. */
+ mode = JS_strdup(cx, "readWrite,append,create");
+ }
+ }
+
+ /* Process the mode */
+ mask = 0;
+ /* TODO: this is pretty ugly, we walk thru the string too many times */
+ mask |= js_FileHasOption(cx, mode, "read") ? PR_RDONLY : 0;
+ mask |= js_FileHasOption(cx, mode, "write") ? PR_WRONLY : 0;
+ mask |= js_FileHasOption(cx, mode, "readWrite")? PR_RDWR : 0;
+ mask |= js_FileHasOption(cx, mode, "append") ? PR_APPEND : 0;
+ mask |= js_FileHasOption(cx, mode, "create") ? PR_CREATE_FILE : 0;
+ mask |= js_FileHasOption(cx, mode, "replace") ? PR_TRUNCATE : 0;
+
+ if (mask & PR_RDWR)
+ mask |= (PR_RDONLY | PR_WRONLY);
+ if ((mask & PR_RDONLY) && (mask & PR_WRONLY))
+ mask |= PR_RDWR;
+
+ file->hasAutoflush |= js_FileHasOption(cx, mode, "autoflush");
+
+ /* Type */
+ if (argc > 1) {
+ strtype = JS_ValueToString(cx, argv[1]);
+ if (!strtype) {
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_SECOND_ARGUMENT_OPEN_NOT_STRING_ERROR,
+ argv[1]);
+ goto out;
+ }
+ ctype = JS_GetStringBytes(strtype);
+
+ if(!strcmp(ctype, utfstring)) {
+ type = UTF8;
+ } else if (!strcmp(ctype, unicodestring)) {
+ type = UCS2;
+ } else {
+ if (strcmp(ctype, asciistring)) {
+ JS_ReportWarning(cx, "File type %s is not supported, using "
+ "'text' instead, proceeding", ctype);
+ }
+ type = ASCII;
+ }
+ } else {
+ type = ASCII;
+ }
+
+ /* Save the relevant fields */
+ file->type = type;
+ file->mode = mask;
+ file->nativehandle = NULL;
+ file->hasRandomAccess = (type != UTF8);
+
+ /*
+ * Deal with pipes here. We can't use NSPR for pipes, so we have to use
+ * POPEN.
+ */
+ if (file->path[0]==PIPE_SYMBOL || file->path[len-1]==PIPE_SYMBOL) {
+ if (file->path[0] == PIPE_SYMBOL && file->path[len-1] == PIPE_SYMBOL) {
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_BIDIRECTIONAL_PIPE_NOT_SUPPORTED);
+ goto out;
+ } else {
+ int i = 0;
+ char pipemode[3];
+ SECURITY_CHECK(cx, NULL, "pipe_open", file);
+
+ if(file->path[0] == PIPE_SYMBOL){
+ if(mask & (PR_WRONLY | PR_APPEND | PR_CREATE_FILE | PR_TRUNCATE)){
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_OPEN_MODE_NOT_SUPPORTED_WITH_PIPES,
+ mode, file->path);
+ goto out;
+ }
+ /* open(SPOOLER, "| cat -v | lpr -h 2>/dev/null") -- pipe for writing */
+ pipemode[i++] = 'r';
+ #ifndef XP_UNIX
+ pipemode[i++] = file->type==UTF8 ? 'b' : 't';
+ #endif
+ pipemode[i++] = '\0';
+ file->nativehandle = POPEN(&file->path[1], pipemode);
+ } else if(file->path[len-1] == PIPE_SYMBOL) {
+ char *command = JS_malloc(cx, len);
+
+ strncpy(command, file->path, len-1);
+ command[len-1] = '\0';
+ /* open(STATUS, "netstat -an 2>&1 |") */
+ pipemode[i++] = 'w';
+ #ifndef XP_UNIX
+ pipemode[i++] = file->type==UTF8 ? 'b' : 't';
+ #endif
+ pipemode[i++] = '\0';
+ file->nativehandle = POPEN(command, pipemode);
+ JS_free(cx, command);
+ }
+ /* set the flags */
+ file->isNative = JS_TRUE;
+ file->isPipe = JS_TRUE;
+ file->hasRandomAccess = JS_FALSE;
+ }
+ } else {
+ /* TODO: what about the permissions?? Java ignores the problem... */
+ file->handle = PR_Open(file->path, mask, 0644);
+ }
+
+ js_ResetBuffers(file);
+ JS_free(cx, mode);
+ mode = NULL;
+
+ /* Set the open flag and return result */
+ if (file->handle == NULL && file->nativehandle == NULL) {
+ file->isOpen = JS_FALSE;
+
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_OP_FAILED, "open", file->path);
+ goto out;
+ }
+
+ good:
+ file->isOpen = JS_TRUE;
+ *rval = JSVAL_TRUE;
+ return JS_TRUE;
+
+ out:
+ if(mode)
+ JS_free(cx, mode);
+ return JS_FALSE;
+ }
+
+ static JSBool
+ file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+ {
+ JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+
+ SECURITY_CHECK(cx, NULL, "close", file);
+
+ if(!file->isOpen){
+ JS_ReportWarning(cx, "File %s is not open, can't close it, proceeding",
+ file->path);
+ goto out;
+ }
+
+ if(!file->isPipe){
+ if(file->isNative){
+ JS_ReportWarning(cx, "Unable to close a native file, proceeding", file->path);
+ goto out;
+ }else{
+ if(file->handle && PR_Close(file->handle)){
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_OP_FAILED, "close", file->path);
+
+ goto out;
+ }
+ }
+ }else{
+ if(PCLOSE(file->nativehandle)==-1){
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_OP_FAILED, "pclose", file->path);
+ goto out;
+ }
+ }
+
+ js_ResetAttributes(file);
+ *rval = JSVAL_TRUE;
+ return JS_TRUE;
+
+ out:
+ return JS_FALSE;
+ }
+
+
+ static JSBool
+ file_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+ {
+ JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+
+ SECURITY_CHECK(cx, NULL, "remove", file);
+ JSFILE_CHECK_NATIVE("remove");
+ JSFILE_CHECK_CLOSED("remove");
+
+ if ((js_isDirectory(cx, file) ?
+ PR_RmDir(file->path) : PR_Delete(file->path))==PR_SUCCESS) {
+ js_ResetAttributes(file);
+ *rval = JSVAL_TRUE;
+ return JS_TRUE;
+ } else {
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_OP_FAILED, "remove", file->path);
+ goto out;
+ }
+ out:
+ *rval = JSVAL_FALSE;
+ return JS_FALSE;
+ }
+
+ /* Raw PR-based function. No text processing. Just raw data copying. */
+ static JSBool
+ file_copyTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+ {
+ JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+ char *dest = NULL;
+ PRFileDesc *handle = NULL;
+ char *buffer;
+ jsval count, size;
+ JSBool fileInitiallyOpen=JS_FALSE;
+
+ SECURITY_CHECK(cx, NULL, "copyTo", file); /* may need a second argument!*/
+ JSFILE_CHECK_ONE_ARG("copyTo");
+ JSFILE_CHECK_NATIVE("copyTo");
+ /* remeber the state */
+ fileInitiallyOpen = file->isOpen;
+ JSFILE_CHECK_READ;
+
+ dest = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
+
+ /* make sure we are not reading a file open for writing */
+ if (file->isOpen && !js_canRead(cx, file)) {
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_CANNOT_COPY_FILE_OPEN_FOR_WRITING_ERROR, file->path);
+ goto out;
+ }
+
+ if (file->handle==NULL){
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_OP_FAILED, "open", file->path);
+ goto out;
+ }
+
+ handle = PR_Open(dest, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, 0644);
+
+ if(!handle){
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_OP_FAILED, "open", dest);
+ goto out;
+ }
+
+ if ((size=js_size(cx, file))==JSVAL_VOID) {
+ goto out;
+ }
+
+ buffer = JS_malloc(cx, size);
+
+ count = INT_TO_JSVAL(PR_Read(file->handle, buffer, size));
+
+ /* reading panic */
+ if (count!=size) {
+ JS_free(cx, buffer);
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_COPY_READ_ERROR, file->path);
+ goto out;
+ }
+
+ count = INT_TO_JSVAL(PR_Write(handle, buffer, JSVAL_TO_INT(size)));
+
+ /* writing panic */
+ if (count!=size) {
+ JS_free(cx, buffer);
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_COPY_WRITE_ERROR, file->path);
+ goto out;
+ }
+
+ JS_free(cx, buffer);
+
+ if(!fileInitiallyOpen){
+ if(!file_close(cx, obj, 0, NULL, rval)) goto out;
+ }
+
+ if(PR_Close(handle)!=PR_SUCCESS){
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_OP_FAILED, "close", dest);
+ goto out;
+ }
+
+ *rval = JSVAL_TRUE;
+ return JS_TRUE;
+ out:
+ if(file->isOpen && !fileInitiallyOpen){
+ if(PR_Close(file->handle)!=PR_SUCCESS){
+ JS_ReportWarning(cx, "Can't close %s, proceeding", file->path);
+ }
+ }
+
+ if(handle && PR_Close(handle)!=PR_SUCCESS){
+ JS_ReportWarning(cx, "Can't close %s, proceeding", dest);
+ }
+
+ *rval = JSVAL_FALSE;
+ return JS_FALSE;
+ }
+
+ static JSBool
+ file_renameTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+ {
+ JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+ char *dest;
+
+ SECURITY_CHECK(cx, NULL, "renameTo", file); /* may need a second argument!*/
+ JSFILE_CHECK_ONE_ARG("renameTo");
+ JSFILE_CHECK_NATIVE("renameTo");
+ JSFILE_CHECK_CLOSED("renameTo");
+
+ dest = RESOLVE_PATH(cx, JS_GetStringBytes(JS_ValueToString(cx, argv[0])));
+
+ if (PR_Rename(file->path, dest)==PR_SUCCESS){
+ /* copy the new filename */
+ JS_free(cx, file->path);
+ file->path = dest;
+ *rval = JSVAL_TRUE;
+ return JS_TRUE;
+ }else{
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_RENAME_FAILED, file->path, dest);
+ goto out;
+ }
+ out:
+ *rval = JSVAL_FALSE;
+ return JS_FALSE;
+ }
+
+ static JSBool
+ file_flush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+ {
+ JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+
+ SECURITY_CHECK(cx, NULL, "flush", file);
+ JSFILE_CHECK_NATIVE("flush");
+ JSFILE_CHECK_OPEN("flush");
+
+ if (PR_Sync(file->handle)==PR_SUCCESS){
+ *rval = JSVAL_TRUE;
+ return JS_TRUE;
+ }else{
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_OP_FAILED, "flush", file->path);
+ goto out;
+ }
+ out:
+ *rval = JSVAL_FALSE;
+ return JS_FALSE;
+ }
+
+ static JSBool
+ file_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+ {
+ JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+ JSString *str;
+ int32 count;
+ uintN i;
+
+ SECURITY_CHECK(cx, NULL, "write", file);
+ JSFILE_CHECK_WRITE;
+
+ for (i = 0; i<argc; i++) {
+ str = JS_ValueToString(cx, argv[i]);
+ count = js_FileWrite(cx, file, JS_GetStringChars(str),
+ JS_GetStringLength(str), file->type);
+ if (count==-1){
+ *rval = JSVAL_FALSE;
+ return JS_FALSE;
+ }
+ }
+
+ *rval = JSVAL_TRUE;
+ return JS_TRUE;
+ out:
+ *rval = JSVAL_FALSE;
+ return JS_FALSE;
+ }
+
+ static JSBool
+ file_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+ {
+ JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+ JSString *str;
+
+ SECURITY_CHECK(cx, NULL, "writeln", file);
+ JSFILE_CHECK_WRITE;
+
+ /* don't report an error here */
+ if(!file_write(cx, obj, argc, argv, rval)) return JS_FALSE;
+ /* don't do security here -- we passed the check in file_write */
+ str = JS_NewStringCopyZ(cx, "\n");
+
+ if (js_FileWrite(cx, file, JS_GetStringChars(str), JS_GetStringLength(str),
+ file->type)==-1){
+ *rval = JSVAL_FALSE;
+ return JS_FALSE;
+ }
+
+ /* eol causes flush if hasAutoflush is turned on */
+ if (file->hasAutoflush)
+ file_flush(cx, obj, 0, NULL, rval);
+
+ *rval = JSVAL_TRUE;
+ return JS_TRUE;
+ out:
+ *rval = JSVAL_FALSE;
+ return JS_FALSE;
+ }
+
+ static JSBool
+ file_writeAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+ {
+ JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+ jsuint i;
+ jsuint limit;
+ JSObject *array;
+ JSObject *elem;
+ jsval elemval;
+
+ SECURITY_CHECK(cx, NULL, "writeAll", file);
+ JSFILE_CHECK_ONE_ARG("writeAll");
+ JSFILE_CHECK_WRITE;
+
+ if (!JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[0]))) {
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_FIRST_ARGUMENT_WRITEALL_NOT_ARRAY_ERROR);
+ goto out;
+ }
+
+ array = JSVAL_TO_OBJECT(argv[0]);
+
+ JS_GetArrayLength(cx, array, &limit);
+
+ for (i = 0; i<limit; i++) {
+ if (!JS_GetElement(cx, array, i, &elemval)) return JS_FALSE;
+ elem = JSVAL_TO_OBJECT(elemval);
+ file_writeln(cx, obj, 1, &elemval, rval);
+ }
+
+ *rval = JSVAL_TRUE;
+ return JS_TRUE;
+ out:
+ *rval = JSVAL_FALSE;
+ return JS_FALSE;
+ }
+
+ static JSBool
+ file_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+ {
+ JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+ JSString *str;
+ int32 want, count;
+ jschar *buf;
+
+ SECURITY_CHECK(cx, NULL, "read", file);
+ JSFILE_CHECK_ONE_ARG("read");
+ JSFILE_CHECK_READ;
+
+ if (!JS_ValueToInt32(cx, argv[0], &want)){
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "read", argv[0]);
+ goto out;
+ }
+
+ /* want = (want>262144)?262144:want; * arbitrary size limitation */
+
+ buf = JS_malloc(cx, want*sizeof buf[0]);
+ if (!buf) goto out;
+
+ count = js_FileRead(cx, file, buf, want, file->type);
+ if (count>0) {
+ str = JS_NewUCStringCopyN(cx, buf, count);
+ *rval = STRING_TO_JSVAL(str);
+ JS_free(cx, buf);
+ return JS_TRUE;
+ } else {
+ JS_free(cx, buf);
+ goto out;
+ }
+ out:
+ *rval = JSVAL_FALSE;
+ return JS_FALSE;
+ }
+
+ static JSBool
+ file_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+ {
+ JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+ JSString *str;
+ jschar *buf = NULL, *tmp;
+ int32 offset, read;
+ intN room;
+ jschar data, data2;
+
+ SECURITY_CHECK(cx, NULL, "readln", file);
+ JSFILE_CHECK_READ;
+
+ buf = JS_malloc(cx, MAX_LINE_LENGTH * sizeof data);
+ if (!buf)
+ return JS_FALSE;
+
+ room = MAX_LINE_LENGTH - 1;
+ offset = 0;
+
+ for (;;) {
+ read = js_FileRead(cx, file, &data, 1, file->type);
+ if (read < 0)
+ goto out;
+ if (read == 0)
+ goto eof;
+
+ switch (data) {
+ case '\r':
+ read = js_FileRead(cx, file, &data2, 1, file->type);
+ if (read < 0)
+ goto out;
+
+ if (read == 1 && data2 != '\n') {
+ /* We read one char too far. Buffer it. */
+ file->charBuffer = data2;
+ file->charBufferUsed = JS_TRUE;
+ }
+
+ /* Fall through. */
+ case '\n':
+ goto done;
+
+ default:
+ if (--room < 0) {
+ tmp = JS_realloc(cx, buf,
+ (offset + MAX_LINE_LENGTH) * sizeof data);
+ if (!tmp)
+ goto out;
+
+ room = MAX_LINE_LENGTH - 1;
+ buf = tmp;
+ }
+
+ buf[offset++] = data;
+ break;
+ }
+ }
+
+ eof:
+ if (offset == 0) {
+ *rval = JSVAL_NULL;
+ return JS_TRUE;
+ }
+
+ done:
+ buf[offset] = 0;
+ tmp = JS_realloc(cx, buf, (offset + 1) * sizeof data);
+ if (!tmp)
+ goto out;
+
+ str = JS_NewUCString(cx, tmp, offset);
+ if (!str)
+ goto out;
+
+ *rval = STRING_TO_JSVAL(str);
+ return JS_TRUE;
+
+ out:
+ if (buf)
+ JS_free(cx, buf);
+
+ return JS_FALSE;
+ }
+
+ static JSBool
+ file_readAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+ {
+ JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+ JSObject *array;
+ jsint len;
+ jsval line;
+ JSBool lineok = JS_FALSE;
+
+ SECURITY_CHECK(cx, NULL, "readAll", file);
+ JSFILE_CHECK_READ;
+
+ array = JS_NewArrayObject(cx, 0, NULL);
+ if (!array)
+ return JS_FALSE;
+ *rval = OBJECT_TO_JSVAL(array);
+
+ len = 0;
+
+ lineok = file_readln(cx, obj, 0, NULL, &line);
+ while (lineok && !JSVAL_IS_NULL(line)) {
+ JS_SetElement(cx, array, len++, &line);
+ lineok = file_readln(cx, obj, 0, NULL, &line);
+ }
+
+ out:
+ return lineok;
+ }
+
+ static JSBool
+ file_seek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+ {
+ JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+ int32 toskip;
+ int32 pos;
+
+ SECURITY_CHECK(cx, NULL, "seek", file);
+ JSFILE_CHECK_ONE_ARG("seek");
+ JSFILE_CHECK_NATIVE("seek");
+ JSFILE_CHECK_READ;
+
+ if (!JS_ValueToInt32(cx, argv[0], &toskip)){
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "seek", argv[0]);
+ goto out;
+ }
+
+ if(!file->hasRandomAccess){
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_NO_RANDOM_ACCESS, file->path);
+ goto out;
+ }
+
+ if(js_isDirectory(cx, file)){
+ JS_ReportWarning(cx,"Seek on directories is not supported, proceeding");
+ goto out;
+ }
+
+ pos = js_FileSeek(cx, file, toskip, file->type);
+
+ if (pos!=-1) {
+ *rval = INT_TO_JSVAL(pos);
+ return JS_TRUE;
+ }
+ out:
+ *rval = JSVAL_VOID;
+ return JS_FALSE;
+ }
+
+ static JSBool
+ file_list(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+ {
+ PRDir *dir;
+ PRDirEntry *entry;
+ JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+ JSObject *array;
+ JSObject *eachFile;
+ jsint len;
+ jsval v;
+ JSRegExp *re = NULL;
+ JSFunction *func = NULL;
+ JSString *str;
+ jsval args[1];
+ char *filePath;
+
+ SECURITY_CHECK(cx, NULL, "list", file);
+ JSFILE_CHECK_NATIVE("list");
+
+ if (argc==1) {
+ if (JSVAL_IS_REGEXP(cx, argv[0])) {
+ re = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0]));
+ }else
+ if (JSVAL_IS_FUNCTION(cx, argv[0])) {
+ func = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0]));
+ }else{
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_FUNCTION_OR_REGEX, argv[0]);
+ goto out;
+ }
+ }
+
+ if (!js_isDirectory(cx, file)) {
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_CANNOT_DO_LIST_ON_A_FILE, file->path);
+ goto out;
+ }
+
+ dir = PR_OpenDir(file->path);
+ if(!dir){
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_OP_FAILED, "open", file->path);
+ goto out;
+ }
+
+ /* create JSArray here... */
+ array = JS_NewArrayObject(cx, 0, NULL);
+ len = 0;
+
+ while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))!=NULL) {
+ /* first, check if we have a regexp */
+ if (re!=NULL) {
+ size_t index = 0;
+
+ str = JS_NewStringCopyZ(cx, entry->name);
+ if(!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &v)){
+ /* don't report anything here */
+ goto out;
+ }
+ /* not matched! */
+ if (JSVAL_IS_NULL(v)) {
+ continue;
+ }
+ }else
+ if (func!=NULL) {
+ str = JS_NewStringCopyZ(cx, entry->name);
+ args[0] = STRING_TO_JSVAL(str);
+ if(!JS_CallFunction(cx, obj, func, 1, args, &v)){
+ goto out;
+ }
+
+ if (v==JSVAL_FALSE) {
+ continue;
+ }
+ }
+
+ filePath = js_combinePath(cx, file->path, (char*)entry->name);
+
+ eachFile = js_NewFileObject(cx, filePath);
+ JS_free(cx, filePath);
+ if (!eachFile){
+ JS_ReportWarning(cx, "File %s cannot be retrieved", filePath);
+ continue;
+ }
+ v = OBJECT_TO_JSVAL(eachFile);
+ JS_SetElement(cx, array, len, &v);
+ JS_SetProperty(cx, array, entry->name, &v);
+ len++;
+ }
+
+ if(PR_CloseDir(dir)!=PR_SUCCESS){
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_OP_FAILED, "close", file->path);
+ goto out;
+ }
+ *rval = OBJECT_TO_JSVAL(array);
+ return JS_TRUE;
+ out:
+ *rval = JSVAL_NULL;
+ return JS_FALSE;
+ }
+
+ static JSBool
+ file_mkdir(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+ {
+ JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+
+ SECURITY_CHECK(cx, NULL, "mkdir", file);
+ JSFILE_CHECK_ONE_ARG("mkdir");
+ JSFILE_CHECK_NATIVE("mkdir");
+
+ /* if the current file is not a directory, find out the directory name */
+ if (!js_isDirectory(cx, file)) {
+ char *dir = js_fileDirectoryName(cx, file->path);
+ JSObject *dirObj = js_NewFileObject(cx, dir);
+
+ JS_free(cx, dir);
+
+ /* call file_mkdir with the right set of parameters if needed */
+ if (file_mkdir(cx, dirObj, argc, argv, rval))
+ return JS_TRUE;
+ else
+ goto out;
+ }else{
+ char *dirName = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
+ char *fullName;
+
+ fullName = js_combinePath(cx, file->path, dirName);
+ if (PR_MkDir(fullName, 0755)==PR_SUCCESS){
+ *rval = JSVAL_TRUE;
+ JS_free(cx, fullName);
+ return JS_TRUE;
+ }else{
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_OP_FAILED, "mkdir", fullName);
+ JS_free(cx, fullName);
+ goto out;
+ }
+ }
+ out:
+ *rval = JSVAL_FALSE;
+ return JS_FALSE;
+ }
+
+ static JSBool
+ file_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval*rval)
+ {
+ JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+
+ *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, file->path));
+ return JS_TRUE;
+ }
+
+ static JSBool
+ file_toURL(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+ {
+ JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+ char url[MAX_PATH_LENGTH];
+ jschar *urlChars;
+
+ JSFILE_CHECK_NATIVE("toURL");
+
+ sprintf(url, "file://%s", file->path);
+ /* TODO: js_escape in jsstr.h may go away at some point */
+
+ urlChars = js_InflateString(cx, url, strlen(url));
+ if (urlChars == NULL) return JS_FALSE;
+ *rval = STRING_TO_JSVAL(js_NewString(cx, urlChars, strlen(url), 0));
+ if (!js_str_escape(cx, obj, 0, rval, rval)) return JS_FALSE;
+
+ return JS_TRUE;
+ out:
+ *rval = JSVAL_VOID;
+ return JS_FALSE;
+ }
+
+
+ static void
+ file_finalize(JSContext *cx, JSObject *obj)
+ {
+ JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+
+ if(file) {
+ /* Close the file before exiting. */
+ if(file->isOpen && !file->isNative) {
+ jsval vp;
+ file_close(cx, obj, 0, NULL, &vp);
+ }
+
+ if (file->path)
+ JS_free(cx, file->path);
+
+ JS_free(cx, file);
+ }
+ }
+
+ /*
+ Allocates memory for the file object, sets fields to defaults.
+ */
+ static JSFile*
+ file_init(JSContext *cx, JSObject *obj, char *bytes)
+ {
+ JSFile *file;
+
+ file = JS_malloc(cx, sizeof *file);
+ if (!file)
+ return NULL;
+ memset(file, 0 , sizeof *file);
+
+ js_ResetAttributes(file);
+
+ file->path = RESOLVE_PATH(cx, bytes);
+
+ if (!JS_SetPrivate(cx, obj, file)) {
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_CANNOT_SET_PRIVATE_FILE, file->path);
+ JS_free(cx, file);
+ return NULL;
+ }
+
+ return file;
+ }
+
+ /* Returns a JSObject. This function is globally visible */
+ JS_PUBLIC_API(JSObject*)
+ js_NewFileObject(JSContext *cx, char *filename)
+ {
+ JSObject *obj;
+ JSFile *file;
+
+ obj = JS_NewObject(cx, &file_class, NULL, NULL);
+ if (!obj){
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObject");
+ return NULL;
+ }
+ file = file_init(cx, obj, filename);
+ if(!file) return NULL;
+ return obj;
+ }
+
+ /* Internal function, used for cases which NSPR file support doesn't cover */
+ JSObject*
+ js_NewFileObjectFromFILE(JSContext *cx, FILE *nativehandle, char *filename,
+ int32 mode, JSBool open, JSBool randomAccess)
+ {
+ JSObject *obj;
+ JSFile *file;
+
+ obj = JS_NewObject(cx, &file_class, NULL, NULL);
+ if (!obj){
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObjectFromFILE");
+ return NULL;
+ }
+ file = file_init(cx, obj, filename);
+ if(!file) return NULL;
+
+ file->nativehandle = nativehandle;
+
+ /* free result of RESOLVE_PATH from file_init. */
+ JS_ASSERT(file->path != NULL);
+ JS_free(cx, file->path);
+
+ file->path = strdup(filename);
+ file->isOpen = open;
+ file->mode = mode;
+ file->hasRandomAccess = randomAccess;
+ file->isNative = JS_TRUE;
+ return obj;
+ }
+
+ /*
+ Real file constructor that is called from JavaScript.
+ Basically, does error processing and calls file_init.
+ */
+ static JSBool
+ file_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+ jsval *rval)
+ {
+ JSString *str;
+ JSFile *file;
+
+ if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
+ /* Replace obj with a new File object. */
+ obj = JS_NewObject(cx, &file_class, NULL, NULL);
+ if (!obj)
+ return JS_FALSE;
+ *rval = OBJECT_TO_JSVAL(obj);
+ }
+
+ str = (argc == 0)
+ ? JS_InternString(cx, "")
+ : JS_ValueToString(cx, argv[0]);
+
+ if (!str) {
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR,
+ argv[0]);
+ return JS_FALSE;
+ }
+
+ file = file_init(cx, obj, JS_GetStringBytes(str));
+ if (!file)
+ return JS_FALSE;
+
+ SECURITY_CHECK(cx, NULL, "constructor", file);
+
+ return JS_TRUE;
+ }
+
+ /* -------------------- File methods and properties ------------------------- */
+ static JSFunctionSpec file_functions[] = {
+ { "open", file_open, 0},
+ { "close", file_close, 0},
+ { "remove", file_remove, 0},
+ { "copyTo", file_copyTo, 0},
+ { "renameTo", file_renameTo, 0},
+ { "flush", file_flush, 0},
+ { "seek", file_seek, 0},
+ { "read", file_read, 0},
+ { "readln", file_readln, 0},
+ { "readAll", file_readAll, 0},
+ { "write", file_write, 0},
+ { "writeln", file_writeln, 0},
+ { "writeAll", file_writeAll, 0},
+ { "list", file_list, 0},
+ { "mkdir", file_mkdir, 0},
+ { "toString", file_toString, 0},
+ { "toURL", file_toURL, 0},
+ {0}
+ };
+
+ enum file_tinyid {
+ FILE_LENGTH = -2,
+ FILE_PARENT = -3,
+ FILE_PATH = -4,
+ FILE_NAME = -5,
+ FILE_ISDIR = -6,
+ FILE_ISFILE = -7,
+ FILE_EXISTS = -8,
+ FILE_CANREAD = -9,
+ FILE_CANWRITE = -10,
+ FILE_OPEN = -11,
+ FILE_TYPE = -12,
+ FILE_MODE = -13,
+ FILE_CREATED = -14,
+ FILE_MODIFIED = -15,
+ FILE_SIZE = -16,
+ FILE_RANDOMACCESS = -17,
+ FILE_POSITION = -18,
+ FILE_APPEND = -19,
+ FILE_REPLACE = -20,
+ FILE_AUTOFLUSH = -21,
+ FILE_ISNATIVE = -22,
+ };
+
+ static JSPropertySpec file_props[] = {
+ {"length", FILE_LENGTH, JSPROP_ENUMERATE | JSPROP_READONLY },
+ {"parent", FILE_PARENT, JSPROP_ENUMERATE | JSPROP_READONLY },
+ {"path", FILE_PATH, JSPROP_ENUMERATE | JSPROP_READONLY },
+ {"name", FILE_NAME, JSPROP_ENUMERATE | JSPROP_READONLY },
+ {"isDirectory", FILE_ISDIR, JSPROP_ENUMERATE | JSPROP_READONLY },
+ {"isFile", FILE_ISFILE, JSPROP_ENUMERATE | JSPROP_READONLY },
+ {"exists", FILE_EXISTS, JSPROP_ENUMERATE | JSPROP_READONLY },
+ {"canRead", FILE_CANREAD, JSPROP_ENUMERATE | JSPROP_READONLY },
+ {"canWrite", FILE_CANWRITE, JSPROP_ENUMERATE | JSPROP_READONLY },
+ {"canAppend", FILE_APPEND, JSPROP_ENUMERATE | JSPROP_READONLY },
+ {"canReplace", FILE_REPLACE, JSPROP_ENUMERATE | JSPROP_READONLY },
+ {"isOpen", FILE_OPEN, JSPROP_ENUMERATE | JSPROP_READONLY },
+ {"type", FILE_TYPE, JSPROP_ENUMERATE | JSPROP_READONLY },
+ {"mode", FILE_MODE, JSPROP_ENUMERATE | JSPROP_READONLY },
+ {"creationTime", FILE_CREATED, JSPROP_ENUMERATE | JSPROP_READONLY },
+ {"lastModified", FILE_MODIFIED, JSPROP_ENUMERATE | JSPROP_READONLY },
+ {"size", FILE_SIZE, JSPROP_ENUMERATE | JSPROP_READONLY },
+ {"hasRandomAccess", FILE_RANDOMACCESS, JSPROP_ENUMERATE | JSPROP_READONLY },
+ {"hasAutoFlush", FILE_AUTOFLUSH, JSPROP_ENUMERATE | JSPROP_READONLY },
+ {"position", FILE_POSITION, JSPROP_ENUMERATE },
+ {"isNative", FILE_ISNATIVE, JSPROP_ENUMERATE | JSPROP_READONLY },
+ {0}
+ };
+
+ /* ------------------------- Property getter/setter ------------------------- */
+ static JSBool
+ file_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
+ {
+ JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+ char *bytes;
+ JSString *str;
+ jsint tiny;
+ PRFileInfo info;
+ JSBool flag;
+ PRExplodedTime expandedTime;
+
+ tiny = JSVAL_TO_INT(id);
+ if (!file)
+ return JS_TRUE;
+
+ switch (tiny) {
+ case FILE_PARENT:
+ SECURITY_CHECK(cx, NULL, "parent", file);
+ if (!js_parent(cx, file, vp))
+ return JS_FALSE;
+ break;
+ case FILE_PATH:
+ str = JS_NewStringCopyZ(cx, file->path);
+ if (!str)
+ return JS_FALSE;
+ *vp = STRING_TO_JSVAL(str);
+ break;
+ case FILE_NAME:
+ if (!js_name(cx, file, vp))
+ return JS_FALSE;
+ break;
+ case FILE_ISDIR:
+ SECURITY_CHECK(cx, NULL, "isDirectory", file);
+ *vp = BOOLEAN_TO_JSVAL(js_isDirectory(cx, file));
+ break;
+ case FILE_ISFILE:
+ SECURITY_CHECK(cx, NULL, "isFile", file);
+ *vp = BOOLEAN_TO_JSVAL(js_isFile(cx, file));
+ break;
+ case FILE_EXISTS:
+ SECURITY_CHECK(cx, NULL, "exists", file);
+ *vp = BOOLEAN_TO_JSVAL(js_exists(cx, file));
+ break;
+ case FILE_ISNATIVE:
+ SECURITY_CHECK(cx, NULL, "isNative", file);
+ *vp = BOOLEAN_TO_JSVAL(file->isNative);
+ break;
+ case FILE_CANREAD:
+ SECURITY_CHECK(cx, NULL, "canRead", file);
+ *vp = BOOLEAN_TO_JSVAL(js_canRead(cx, file));
+ break;
+ case FILE_CANWRITE:
+ SECURITY_CHECK(cx, NULL, "canWrite", file);
+ *vp = BOOLEAN_TO_JSVAL(js_canWrite(cx, file));
+ break;
+ case FILE_OPEN:
+ SECURITY_CHECK(cx, NULL, "isOpen", file);
+ *vp = BOOLEAN_TO_JSVAL(file->isOpen);
+ break;
+ case FILE_APPEND :
+ SECURITY_CHECK(cx, NULL, "canAppend", file);
+ JSFILE_CHECK_OPEN("canAppend");
+ *vp = BOOLEAN_TO_JSVAL(!file->isNative &&
+ (file->mode&PR_APPEND)==PR_APPEND);
+ break;
+ case FILE_REPLACE :
+ SECURITY_CHECK(cx, NULL, "canReplace", file);
+ JSFILE_CHECK_OPEN("canReplace");
+ *vp = BOOLEAN_TO_JSVAL(!file->isNative &&
+ (file->mode&PR_TRUNCATE)==PR_TRUNCATE);
+ break;
+ case FILE_AUTOFLUSH :
+ SECURITY_CHECK(cx, NULL, "hasAutoFlush", file);
+ JSFILE_CHECK_OPEN("hasAutoFlush");
+ *vp = BOOLEAN_TO_JSVAL(!file->isNative && file->hasAutoflush);
+ break;
+ case FILE_TYPE:
+ SECURITY_CHECK(cx, NULL, "type", file);
+ JSFILE_CHECK_OPEN("type");
+ if(js_isDirectory(cx, file)){
+ *vp = JSVAL_VOID;
+ break;
+ }
+
+ switch (file->type) {
+ case ASCII:
+ *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, asciistring));
+ break;
+ case UTF8:
+ *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, utfstring));
+ break;
+ case UCS2:
+ *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, unicodestring));
+ break;
+ default:
+ JS_ReportWarning(cx, "Unsupported file type %d, proceeding",
+ file->type);
+ }
+ break;
+ case FILE_MODE:
+ SECURITY_CHECK(cx, NULL, "mode", file);
+ JSFILE_CHECK_OPEN("mode");
+ bytes = JS_malloc(cx, MODE_SIZE);
+ bytes[0] = '\0';
+ flag = JS_FALSE;
+
+ if ((file->mode&PR_RDONLY)==PR_RDONLY) {
+ if (flag) strcat(bytes, ",");
+ strcat(bytes, "read");
+ flag = JS_TRUE;
+ }
+ if ((file->mode&PR_WRONLY)==PR_WRONLY) {
+ if (flag) strcat(bytes, ",");
+ strcat(bytes, "write");
+ flag = JS_TRUE;
+ }
+ if ((file->mode&PR_RDWR)==PR_RDWR) {
+ if (flag) strcat(bytes, ",");
+ strcat(bytes, "readWrite");
+ flag = JS_TRUE;
+ }
+ if ((file->mode&PR_APPEND)==PR_APPEND) {
+ if (flag) strcat(bytes, ",");
+ strcat(bytes, "append");
+ flag = JS_TRUE;
+ }
+ if ((file->mode&PR_CREATE_FILE)==PR_CREATE_FILE) {
+ if (flag) strcat(bytes, ",");
+ strcat(bytes, "create");
+ flag = JS_TRUE;
+ }
+ if ((file->mode&PR_TRUNCATE)==PR_TRUNCATE) {
+ if (flag) strcat(bytes, ",");
+ strcat(bytes, "replace");
+ flag = JS_TRUE;
+ }
+ if (file->hasAutoflush) {
+ if (flag) strcat(bytes, ",");
+ strcat(bytes, "hasAutoFlush");
+ flag = JS_TRUE;
+ }
+ *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, bytes));
+ JS_free(cx, bytes);
+ break;
+ case FILE_CREATED:
+ SECURITY_CHECK(cx, NULL, "creationTime", file);
+ JSFILE_CHECK_NATIVE("creationTime");
+ if(((file->isOpen)?
+ PR_GetOpenFileInfo(file->handle, &info):
+ PR_GetFileInfo(file->path, &info))!=PR_SUCCESS){
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
+ goto out;
+ }
+
+ PR_ExplodeTime(info.creationTime, PR_LocalTimeParameters,&expandedTime);
+ *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx, expandedTime.tm_year,
+ expandedTime.tm_month,
+ expandedTime.tm_mday,
+ expandedTime.tm_hour,
+ expandedTime.tm_min,
+ expandedTime.tm_sec));
+ break;
+ case FILE_MODIFIED:
+ SECURITY_CHECK(cx, NULL, "lastModified", file);
+ JSFILE_CHECK_NATIVE("lastModified");
+ if(((file->isOpen)?
+ PR_GetOpenFileInfo(file->handle, &info):
+ PR_GetFileInfo(file->path, &info))!=PR_SUCCESS){
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
+ goto out;
+ }
+
+ PR_ExplodeTime(info.modifyTime, PR_LocalTimeParameters, &expandedTime);
+ *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx, expandedTime.tm_year,
+ expandedTime.tm_month,
+ expandedTime.tm_mday,
+ expandedTime.tm_hour,
+ expandedTime.tm_min,
+ expandedTime.tm_sec));
+ break;
+ case FILE_SIZE:
+ SECURITY_CHECK(cx, NULL, "size", file);
+ *vp = js_size(cx, file);
+ break;
+ case FILE_LENGTH:
+ SECURITY_CHECK(cx, NULL, "length", file);
+ JSFILE_CHECK_NATIVE("length");
+
+ if (js_isDirectory(cx, file)) { /* XXX debug me */
+ PRDir *dir;
+ PRDirEntry *entry;
+ jsint count = 0;
+
+ if(!(dir = PR_OpenDir(file->path))){
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_CANNOT_OPEN_DIR, file->path);
+ goto out;
+ }
+
+ while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))) {
+ count++;
+ }
+
+ if(!PR_CloseDir(dir)){
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_OP_FAILED, "close", file->path);
+
+ goto out;
+ }
+
+ *vp = INT_TO_JSVAL(count);
+ break;
+ }else{
+ /* return file size */
+ *vp = js_size(cx, file);
+ }
+ break;
+ case FILE_RANDOMACCESS:
+ SECURITY_CHECK(cx, NULL, "hasRandomAccess", file);
+ JSFILE_CHECK_OPEN("hasRandomAccess");
+ *vp = BOOLEAN_TO_JSVAL(file->hasRandomAccess);
+ break;
+ case FILE_POSITION:
+ SECURITY_CHECK(cx, NULL, "position", file);
+ JSFILE_CHECK_NATIVE("position");
+ JSFILE_CHECK_OPEN("position");
+
+ if(!file->hasRandomAccess){
+ JS_ReportWarning(cx, "File %s doesn't support random access, can't report the position, proceeding");
+ *vp = JSVAL_VOID;
+ break;
+ }
+
+ if (file->isOpen && js_isFile(cx, file)) {
+ int pos = PR_Seek(file->handle, 0, PR_SEEK_CUR);
+ if(pos!=-1){
+ *vp = INT_TO_JSVAL(pos);
+ }else{
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_CANNOT_REPORT_POSITION, file->path);
+ goto out;
+ }
+ }else {
+ JS_ReportWarning(cx, "File %s is closed or not a plain file,"
+ " can't report position, proceeding");
+ goto out;
+ }
+ break;
+ default:
+ SECURITY_CHECK(cx, NULL, "file_access", file);
+
+ /* this is some other property -- try to use the dir["file"] syntax */
+ if (js_isDirectory(cx, file)) {
+ PRDir *dir = NULL;
+ PRDirEntry *entry = NULL;
+ char *prop_name;
+
+ str = JS_ValueToString(cx, id);
+ if (!str)
+ return JS_FALSE;
+
+ prop_name = JS_GetStringBytes(str);
+
+ /* no native files past this point */
+ dir = PR_OpenDir(file->path);
+ if(!dir) {
+ /* This is probably not a directory */
+ JS_ReportWarning(cx, "Can't open directory %s", file->path);
+ return JS_FALSE;
+ }
+
+ while ((entry = PR_ReadDir(dir, PR_SKIP_NONE)) != NULL) {
+ if (!strcmp(entry->name, prop_name)){
+ bytes = js_combinePath(cx, file->path, prop_name);
+ *vp = OBJECT_TO_JSVAL(js_NewFileObject(cx, bytes));
+ JS_free(cx, bytes);
+ return JS_TRUE;
+ }
+ }
+ }
+ }
+ return JS_TRUE;
+
+ out:
+ return JS_FALSE;
+ }
+
+ static JSBool
+ file_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
+ {
+ JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+ jsint slot;
+
+ if (JSVAL_IS_STRING(id)){
+ return JS_TRUE;
+ }
+
+ slot = JSVAL_TO_INT(id);
+
+ switch (slot) {
+ /* File.position = 10 */
+ case FILE_POSITION:
+ SECURITY_CHECK(cx, NULL, "set_position", file);
+ JSFILE_CHECK_NATIVE("set_position");
+
+ if(!file->hasRandomAccess){
+ JS_ReportWarning(cx, "File %s doesn't support random access, can't "
+ "report the position, proceeding");
+ goto out;
+ }
+
+ if (file->isOpen && js_isFile(cx, file)) {
+ int32 pos;
+ int32 offset;
+
+ if (!JS_ValueToInt32(cx, *vp, &offset)){
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "position", *vp);
+ goto out;
+ }
+
+ pos = PR_Seek(file->handle, offset, PR_SEEK_SET);
+
+ if(pos!=-1){
+ *vp = INT_TO_JSVAL(pos);
+ }else{
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_CANNOT_SET_POSITION, file->path);
+ goto out;
+ }
+ } else {
+ JS_ReportWarning(cx, "File %s is closed or not a file, can't set "
+ "position, proceeding", file->path);
+ goto out;
+ }
+ }
+
+ return JS_TRUE;
+ out:
+ return JS_FALSE;
+ }
+
+ /*
+ File.currentDir = new File("D:\") or File.currentDir = "D:\"
+ */
+ static JSBool
+ file_currentDirSetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
+ {
+ JSFile *file;
+
+ file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+
+ /* Look at the rhs and extract a file object from it */
+ if (JSVAL_IS_OBJECT(*vp)) {
+ if (JS_InstanceOf(cx, obj, &file_class, NULL)) {
+ /* Braindamaged rhs -- just return the old value */
+ if (file && (!js_exists(cx, file) || !js_isDirectory(cx, file))) {
+ JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp);
+ return JS_FALSE;
+ } else {
+ chdir(file->path);
+ return JS_TRUE;
+ }
+ } else {
+ return JS_FALSE;
+ }
+ } else {
+ JSObject *rhsObject;
+ char *path;
+
+ path = JS_GetStringBytes(JS_ValueToString(cx, *vp));
+ rhsObject = js_NewFileObject(cx, path);
+ if (!rhsObject)
+ return JS_FALSE;
+
+ if (!file || !js_exists(cx, file) || !js_isDirectory(cx, file)){
+ JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp);
+ } else {
+ *vp = OBJECT_TO_JSVAL(rhsObject);
+ chdir(path);
+ }
+ }
+
+ return JS_TRUE;
+ }
+
+ /* Declare class */
+ static JSClass file_class = {
+ FILE_CONSTRUCTOR, JSCLASS_HAS_PRIVATE,
+ JS_PropertyStub, JS_PropertyStub, file_getProperty, file_setProperty,
+ JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, file_finalize
+ };
+
+ /* -------------------- Functions exposed to the outside -------------------- */
+ JS_PUBLIC_API(JSObject*)
+ js_InitFileClass(JSContext *cx, JSObject* obj)
+ {
+ JSObject *file, *ctor, *afile;
+ jsval vp;
+ char *currentdir;
+ char separator[2];
+
+ file = JS_InitClass(cx, obj, NULL, &file_class, file_constructor, 1,
+ file_props, file_functions, NULL, NULL);
+ if (!file) {
+ JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
+ JSFILEMSG_INIT_FAILED);
+ return NULL;
+ }
+
+ ctor = JS_GetConstructor(cx, file);
+ if (!ctor) return NULL;
+
+ /* Define CURRENTDIR property. We are doing this to get a
+ slash at the end of the current dir */
+ afile = js_NewFileObject(cx, CURRENT_DIR);
+ currentdir = JS_malloc(cx, MAX_PATH_LENGTH);
+ currentdir = getcwd(currentdir, MAX_PATH_LENGTH);
+ afile = js_NewFileObject(cx, currentdir);
+ JS_free(cx, currentdir);
+ vp = OBJECT_TO_JSVAL(afile);
+ JS_DefinePropertyWithTinyId(cx, ctor, CURRENTDIR_PROPERTY, 0, vp,
+ JS_PropertyStub, file_currentDirSetter,
+ JSPROP_ENUMERATE | JSPROP_READONLY );
+
+ /* Define input */
+ vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stdin,
+ STDINPUT_NAME, PR_RDONLY, JS_TRUE, JS_FALSE));
+ JS_SetProperty(cx, ctor, "input", &vp);
+
+ /* Define output */
+ vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stdout,
+ STDOUTPUT_NAME, PR_WRONLY, JS_TRUE, JS_FALSE));
+ JS_SetProperty(cx, ctor, "output", &vp);
+
+ /* Define error */
+ vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stderr,
+ STDERROR_NAME, PR_WRONLY, JS_TRUE, JS_FALSE));
+ JS_SetProperty(cx, ctor, "error", &vp);
+
+ separator[0] = FILESEPARATOR;
+ separator[1] = '\0';
+ vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, separator));
+ JS_DefinePropertyWithTinyId(cx, ctor, SEPARATOR_PROPERTY, 0, vp,
+ JS_PropertyStub, JS_PropertyStub,
+ JSPROP_ENUMERATE | JSPROP_READONLY );
+ return file;
+ }
+ #endif /* JS_HAS_FILE_OBJECT */
|