/* -*- 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 defined(JS_HAS_FILE_OBJECT) && (JS_HAS_FILE_OBJECT - 0) /* OSSP BUGFIX */ #ifdef OSSP #include "../config.h" #endif #include "jsstddef.h" /* ----------------- Platform-specific includes and defines ----------------- */ #if defined(XP_WIN) || defined(XP_OS2) # include #ifdef OSSP # include # include # include #endif # include # include # include # define FILESEPARATOR '\\' # define FILESEPARATOR2 '/' # define CURRENT_DIR "c:\\" # define POPEN _popen # define PCLOSE _pclose #ifdef OSSP # undef mkdir # define mkdir(file, mode) _mkdir(file) #endif #elif defined(XP_UNIX) || defined(XP_BEOS) # include # include # include # include #ifdef OSSP # include # include # include # include # include #endif # 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 */ #ifdef OSSP /* CLEANUP */ #include "jsfile.h" #endif #include /* NSPR dependencies */ #ifdef OSSP #define PR_RDONLY 0x01 #define PR_WRONLY 0x02 #define PR_RDWR 0x04 #define PR_CREATE_FILE 0x08 #define PR_APPEND 0x10 #define PR_TRUNCATE 0x20 #define PR_SYNC 0x40 #define PR_EXCL 0x80 #else #include "prio.h" #include "prerror.h" #endif #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 }; #ifdef OSSP static #endif 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 */ #ifdef OSSP FILE *handle; /* the handle for the file, if open. */ #else PRFileDesc *handle; /* the handle for the file, if open. */ #endif 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 #ifdef OSSP js_BufferedRead(JSFile * f, unsigned char *buf, int32 len) #else js_BufferedRead(JSFile * f, char *buf, int32 len) #endif { 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) { #ifdef OSSP count += (!f->isNative) ? fread(buf, 1, len, f->handle) : fread(buf, 1, len, f->nativehandle); #else count += (!f->isNative) ? PR_Read(f->handle, buf, len) : fread(buf, 1, len, f->nativehandle); #endif } 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;count0) { file->byteBuffer[file->nbBytesInBuf] = utfbuf[0]; file->nbBytesInBuf++; utfbuf[0] = utfbuf[1]; utfbuf[1] = utfbuf[2]; remainder--; } break; case UCS2: #ifdef OSSP count = js_BufferedRead(file, (unsigned char*)buf, len*2) >> 1; #else count = js_BufferedRead(file, (char*)buf, len*2) >> 1; #endif 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: #ifdef OSSP count = fseek(file->handle, len, SEEK_CUR); #else count = PR_Seek(file->handle, len, PR_SEEK_CUR); #endif break; case UTF8: remainder = 0; for (count = 0;count0) { file->byteBuffer[file->nbBytesInBuf] = utfbuf[0]; file->nbBytesInBuf++; utfbuf[0] = utfbuf[1]; utfbuf[1] = utfbuf[2]; remainder--; } break; case UCS2: #ifdef OSSP count = fseek(file->handle, len*2, SEEK_CUR)/2; #else count = PR_Seek(file->handle, len*2, PR_SEEK_CUR)/2; #endif 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; iisNative) ? fwrite(aux, 1, len, file->handle) : fwrite(aux, 1, len, file->nativehandle); #else count = (!file->isNative) ? PR_Write(file->handle, aux, len) : fwrite(aux, 1, len, file->nativehandle); #endif 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;countisNative) ? fwrite(utfbuf, 1, i, file->handle) : fwrite(utfbuf, 1, i, file->nativehandle); #else j = (!file->isNative) ? PR_Write(file->handle, utfbuf, i) : fwrite(utfbuf, 1, i, file->nativehandle); #endif if (jisNative) ? fwrite(buf, 1, len*2, file->handle) >> 1 : fwrite(buf, 1, len*2, file->nativehandle) >> 1; #else count = (!file->isNative) ? PR_Write(file->handle, buf, len*2) >> 1 : fwrite(buf, 1, len*2, file->nativehandle) >> 1; #endif 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; } #ifdef OSSP return access(file->path, F_OK) == 0; #else return PR_Access(file->path, PR_ACCESS_EXISTS) == PR_SUCCESS; #endif } static JSBool js_canRead(JSContext *cx, JSFile *file) { if (!file->isNative) { if (file->isOpen && !(file->mode & PR_RDONLY)) return JS_FALSE; #ifdef OSSP return access(file->path, R_OK) == 0; #else return PR_Access(file->path, PR_ACCESS_READ_OK) == PR_SUCCESS; #endif } 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; #ifdef OSSP return access(file->path, W_OK) == 0; #else return PR_Access(file->path, PR_ACCESS_WRITE_OK) == PR_SUCCESS; #endif } 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) { #ifdef OSSP struct stat info; if (file->isOpen ? fstat(fileno(file->handle), &info) != 0 : stat(file->path, &info) != 0) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); return JS_FALSE; } return S_ISREG(info.st_mode) != 0; #else 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; #endif } /* This doesn't make sense for a pipe of stdstream. */ return JS_FALSE; } static JSBool js_isDirectory(JSContext *cx, JSFile *file) { if(!file->isNative){ #ifdef OSSP struct stat info; #else PRFileInfo info; #endif /* Hack needed to get get_property to work. */ if (!js_exists(cx, file)) return JS_FALSE; #ifdef OSSP if (file->isOpen ? fstat(fileno(file->handle), &info) != 0 : stat(file->path, &info) != 0) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); return JS_FALSE; } return S_ISDIR(info.st_mode) != 0; #else 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; #endif } /* This doesn't make sense for a pipe of stdstream. */ return JS_FALSE; } static jsval js_size(JSContext *cx, JSFile *file) { #ifdef OSSP struct stat info; #else PRFileInfo info; #endif JSFILE_CHECK_NATIVE("size"); #ifdef OSSP if (file->isOpen ? fstat(fileno(file->handle), &info) != 0 : stat(file->path, &info) != 0) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); return JS_FALSE; } return INT_TO_JSVAL(info.st_size); #else 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); #endif 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... */ #ifdef OSSP { int my_fd; int my_fd_mode = 0; char *my_fp_mode = ""; my_fd_mode |= ((mask & PR_RDONLY) ? O_RDONLY : 0); my_fd_mode |= ((mask & PR_WRONLY) ? O_WRONLY : 0); my_fd_mode |= ((mask & PR_RDWR) ? O_RDWR : 0); my_fd_mode |= ((mask & PR_APPEND) ? O_APPEND : 0); my_fd_mode |= ((mask & PR_CREATE_FILE) ? O_CREAT : 0); my_fd_mode |= ((mask & PR_TRUNCATE) ? O_TRUNC : 0); if ((mask & PR_RDWR)) my_fp_mode = "r+"; else if ((mask & PR_RDONLY)) my_fp_mode = "r"; else if ((mask & PR_WRONLY)) my_fp_mode = "w"; else if ((mask & PR_APPEND) && (mask & PR_RDWR)) my_fp_mode = "a+"; else if ((mask & PR_APPEND)) my_fp_mode = "a"; else my_fp_mode = "rw"; if ((my_fd = open(file->path, my_fd_mode, 0644)) != -1) file->handle = fdopen(my_fd, my_fp_mode); } #else file->handle = PR_Open(file->path, mask, 0644); #endif } 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{ #ifdef OSSP if (file->handle && fclose(file->handle) != 0) { #else if(file->handle && PR_Close(file->handle)){ #endif 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"); #ifdef OSSP if (remove(file->path) == 0) { #else if ((js_isDirectory(cx, file) ? PR_RmDir(file->path) : PR_Delete(file->path))==PR_SUCCESS) { #endif 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; #ifdef OSSP FILE *handle = NULL; #else PRFileDesc *handle = NULL; #endif 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; } #ifdef OSSP { int my_fd; if ((my_fd = open(file->path, O_WRONLY|O_CREAT|O_TRUNC, 0644)) != -1) handle = fdopen(my_fd, "w"); } #else handle = PR_Open(dest, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, 0644); #endif 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); #ifdef OSSP count = INT_TO_JSVAL((int)fread(buffer, 1, (size_t)size, file->handle)); #else count = INT_TO_JSVAL(PR_Read(file->handle, buffer, size)); #endif /* reading panic */ if (count!=size) { JS_free(cx, buffer); JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_COPY_READ_ERROR, file->path); goto out; } #ifdef OSSP count = INT_TO_JSVAL((int)fwrite(buffer, 1, (size_t)JSVAL_TO_INT(size), handle)); #else count = INT_TO_JSVAL(PR_Write(handle, buffer, JSVAL_TO_INT(size))); #endif /* 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; } #ifdef OSSP if (fclose(handle) != 0) { #else if(PR_Close(handle)!=PR_SUCCESS){ #endif JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_OP_FAILED, "close", dest); goto out; } *rval = JSVAL_TRUE; return JS_TRUE; out: if(file->isOpen && !fileInitiallyOpen){ #ifdef OSSP if (fclose(file->handle) != 0) { #else if(PR_Close(file->handle)!=PR_SUCCESS){ #endif JS_ReportWarning(cx, "Can't close %s, proceeding", file->path); } } #ifdef OSSP if (handle && fclose(handle) != 0) { #else if(handle && PR_Close(handle)!=PR_SUCCESS){ #endif 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]))); #ifdef OSSP if (rename(file->path, dest) == 0){ #else if (PR_Rename(file->path, dest)==PR_SUCCESS){ #endif /* 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"); #ifdef OSSP if (fflush(file->handle) == 0){ #else if (PR_Sync(file->handle)==PR_SUCCESS){ #endif *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; itype); 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; i262144)?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) { #ifdef OSSP DIR *dir; struct dirent *entry; #else PRDir *dir; PRDirEntry *entry; #endif 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; } #ifdef OSSP dir = opendir(file->path); #else dir = PR_OpenDir(file->path); #endif 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; #ifdef OSSP while ((entry = readdir(dir))!=NULL) { #else while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))!=NULL) { #endif #ifdef OSSP if ( strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; #endif /* first, check if we have a regexp */ if (re!=NULL) { size_t index = 0; #ifdef OSSP str = JS_NewStringCopyZ(cx, entry->d_name); #else str = JS_NewStringCopyZ(cx, entry->name); #endif 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) { #ifdef OSSP str = JS_NewStringCopyZ(cx, entry->d_name); #else str = JS_NewStringCopyZ(cx, entry->name); #endif args[0] = STRING_TO_JSVAL(str); if(!JS_CallFunction(cx, obj, func, 1, args, &v)){ goto out; } if (v==JSVAL_FALSE) { continue; } } #ifdef OSSP filePath = js_combinePath(cx, file->path, (char*)entry->d_name); #else filePath = js_combinePath(cx, file->path, (char*)entry->name); #endif 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); #ifdef OSSP JS_SetProperty(cx, array, entry->d_name, &v); #else JS_SetProperty(cx, array, entry->name, &v); #endif len++; } #ifdef OSSP if (closedir(dir) != 0) { #else if(PR_CloseDir(dir)!=PR_SUCCESS){ #endif 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); #ifdef OSSP if (mkdir(fullName, 0755) == 0) { #else if (PR_MkDir(fullName, 0755)==PR_SUCCESS){ #endif *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); #ifdef OSSP JSString *str; if ((str = JS_NewStringCopyZ(cx, file->path)) == NULL) return JS_FALSE; *rval = STRING_TO_JSVAL(str); #else *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, file->path)); #endif 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; #ifdef OSSP size_t len; JSString *str; #endif JSFILE_CHECK_NATIVE("toURL"); sprintf(url, "file://%s", file->path); /* TODO: js_escape in jsstr.h may go away at some point */ #ifdef OSSP /* BUGFIX */ len = strlen(url); if ((urlChars = js_InflateString(cx, url, &len)) == NULL) return JS_FALSE; if ((str = js_NewString(cx, urlChars, len, 0)) == NULL) { JS_free(cx, urlChars); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); #else urlChars = js_InflateString(cx, url, strlen(url)); if (urlChars == NULL) return JS_FALSE; *rval = STRING_TO_JSVAL(js_NewString(cx, urlChars, strlen(url), 0)); #endif 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 */ #ifdef OSSP /* CLEANUP */ static #endif 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; } 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); *rval = OBJECT_TO_JSVAL(obj); 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, #ifdef OSSP /* BUGFIX */ FILE_ISNATIVE = -22 #else FILE_ISNATIVE = -22, #endif }; 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; #ifdef OSSP struct stat info; #else PRFileInfo info; #endif JSBool flag; #ifdef OSSP struct tm *tm; time_t t; #else PRExplodedTime expandedTime; #endif 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"); #ifdef OSSP if (file->isOpen ? fstat(fileno(file->handle), &info) != 0 : stat(file->path, &info) != 0) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); goto out; } #if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) t = (time_t)(info.st_birthtime); #elif defined(HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC) t = (time_t)(info.st_birthtimensec / (1000*1000*1000)); #else /* not really correct, but the only possible fallback on not fully POSIX compliant platforms. */ t = (time_t)(info.st_ctime); #endif tm = localtime(&t); *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec)); #else 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)); #endif break; case FILE_MODIFIED: SECURITY_CHECK(cx, NULL, "lastModified", file); JSFILE_CHECK_NATIVE("lastModified"); #ifdef OSSP if (file->isOpen ? fstat(fileno(file->handle), &info) != 0 : stat(file->path, &info) != 0) { JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); goto out; } t = (time_t)(info.st_mtime); tm = localtime(&t); *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec)); #else 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)); #endif 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 */ #ifdef OSSP DIR *dir; struct dirent *entry; #else PRDir *dir; PRDirEntry *entry; #endif jsint count = 0; #ifdef OSSP if(!(dir = opendir(file->path))){ #else if(!(dir = PR_OpenDir(file->path))){ #endif JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, JSFILEMSG_CANNOT_OPEN_DIR, file->path); goto out; } #ifdef OSSP while ((entry = readdir(dir))) { if ( strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; #else while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))) { #endif count++; } #ifdef OSSP if(closedir(dir) != 0){ #else if(!PR_CloseDir(dir)){ #endif 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)) { #ifdef OSSP int pos = fseek(file->handle, 0, SEEK_CUR); #else int pos = PR_Seek(file->handle, 0, PR_SEEK_CUR); #endif 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)) { #ifdef OSSP DIR *dir = NULL; struct dirent *entry = NULL; #else PRDir *dir = NULL; PRDirEntry *entry = NULL; #endif char *prop_name; str = JS_ValueToString(cx, id); if (!str) return JS_FALSE; prop_name = JS_GetStringBytes(str); /* no native files past this point */ #ifdef OSSP dir = opendir(file->path); #else dir = PR_OpenDir(file->path); #endif if(!dir) { /* This is probably not a directory */ JS_ReportWarning(cx, "Can't open directory %s", file->path); return JS_FALSE; } #ifdef OSSP while ((entry = readdir(dir)) != NULL) { if ( strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; if (!strcmp(entry->d_name, prop_name)){ #else while ((entry = PR_ReadDir(dir, PR_SKIP_NONE)) != NULL) { if (!strcmp(entry->name, prop_name)){ #endif bytes = js_combinePath(cx, file->path, prop_name); *vp = OBJECT_TO_JSVAL(js_NewFileObject(cx, bytes)); JS_free(cx, bytes); #ifdef OSSP /* BUGFIX */ closedir(dir); #else PR_CloseDir(dir); #endif return JS_TRUE; } } #ifdef OSSP /* BUGFIX */ closedir(dir); #else PR_CloseDir(dir); #endif } } 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; } #ifdef OSSP pos = fseek(file->handle, offset, SEEK_SET); #else pos = PR_Seek(file->handle, offset, PR_SEEK_SET); #endif 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 */