Back to index

lightning-sunbird  0.9+nobinonly
jsfile.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  * vim: set ts=8 sw=4 et tw=80:
00003  *
00004  * ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is Mozilla Communicator client code, released
00018  * March 31, 1998.
00019  *
00020  * The Initial Developer of the Original Code is
00021  * Netscape Communications Corporation.
00022  * Portions created by the Initial Developer are Copyright (C) 1998
00023  * the Initial Developer. All Rights Reserved.
00024  *
00025  * Contributor(s):
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 /*
00042  * JS File object
00043  */
00044 #if JS_HAS_FILE_OBJECT
00045 
00046 #include "jsstddef.h"
00047 #include "jsfile.h"
00048 
00049 /* ----------------- Platform-specific includes and defines ----------------- */
00050 #if defined(XP_WIN) || defined(XP_OS2)
00051 #   include <direct.h>
00052 #   include <io.h>
00053 #   include <sys/types.h>
00054 #   include <sys/stat.h>
00055 #   define FILESEPARATOR        '\\'
00056 #   define FILESEPARATOR2       '/'
00057 #   define CURRENT_DIR          "c:\\"
00058 #   define POPEN                _popen
00059 #   define PCLOSE               _pclose
00060 #elif defined(XP_UNIX) || defined(XP_BEOS)
00061 #   include <strings.h>
00062 #   include <stdio.h>
00063 #   include <stdlib.h>
00064 #   include <unistd.h>
00065 #   define FILESEPARATOR        '/'
00066 #   define FILESEPARATOR2       '\0'
00067 #   define CURRENT_DIR          "/"
00068 #   define POPEN                popen
00069 #   define PCLOSE               pclose
00070 #endif
00071 
00072 /* --------------- Platform-independent includes and defines ---------------- */
00073 #include "jsapi.h"
00074 #include "jsatom.h"
00075 #include "jscntxt.h"
00076 #include "jsdate.h"
00077 #include "jsdbgapi.h"
00078 #include "jsemit.h"
00079 #include "jsfun.h"
00080 #include "jslock.h"
00081 #include "jsobj.h"
00082 #include "jsparse.h"
00083 #include "jsscan.h"
00084 #include "jsscope.h"
00085 #include "jsscript.h"
00086 #include "jsstr.h"
00087 #include "jsutil.h" /* Added by JSIFY */
00088 #include <string.h>
00089 
00090 /* NSPR dependencies */
00091 #include "prio.h"
00092 #include "prerror.h"
00093 
00094 #define SPECIAL_FILE_STRING     "Special File"
00095 #define CURRENTDIR_PROPERTY     "currentDir"
00096 #define SEPARATOR_PROPERTY      "separator"
00097 #define FILE_CONSTRUCTOR        "File"
00098 #define PIPE_SYMBOL             '|'
00099 
00100 #define ASCII                   0
00101 #define UTF8                    1
00102 #define UCS2                    2
00103 
00104 #define asciistring             "text"
00105 #define utfstring               "binary"
00106 #define unicodestring           "unicode"
00107 
00108 #define MAX_PATH_LENGTH         1024
00109 #define MODE_SIZE               256
00110 #define NUMBER_SIZE             32
00111 #define MAX_LINE_LENGTH         256
00112 #define URL_PREFIX              "file://"
00113 
00114 #define STDINPUT_NAME           "Standard input stream"
00115 #define STDOUTPUT_NAME          "Standard output stream"
00116 #define STDERROR_NAME           "Standard error stream"
00117 
00118 #define RESOLVE_PATH            js_canonicalPath        /* js_absolutePath */
00119 
00120 /* Error handling */
00121 typedef enum JSFileErrNum {
00122 #define MSG_DEF(name, number, count, exception, format) \
00123     name = number,
00124 #include "jsfile.msg"
00125 #undef MSG_DEF
00126     JSFileErr_Limit
00127 #undef MSGDEF
00128 } JSFileErrNum;
00129 
00130 #define JSFILE_HAS_DFLT_MSG_STRINGS 1
00131 
00132 JSErrorFormatString JSFile_ErrorFormatString[JSFileErr_Limit] = {
00133 #if JSFILE_HAS_DFLT_MSG_STRINGS
00134 #define MSG_DEF(name, number, count, exception, format) \
00135     { format, count },
00136 #else
00137 #define MSG_DEF(name, number, count, exception, format) \
00138     { NULL, count },
00139 #endif
00140 #include "jsfile.msg"
00141 #undef MSG_DEF
00142 };
00143 
00144 const JSErrorFormatString *
00145 JSFile_GetErrorMessage(void *userRef, const char *locale,
00146                                                         const uintN errorNumber)
00147 {
00148     if ((errorNumber > 0) && (errorNumber < JSFileErr_Limit))
00149         return &JSFile_ErrorFormatString[errorNumber];
00150     else
00151         return NULL;
00152 }
00153 
00154 #define JSFILE_CHECK_NATIVE(op)                                               \
00155     if (file->isNative) {                                                     \
00156         JS_ReportWarning(cx, "Cannot call or access \"%s\" on native file %s",\
00157                          op, file->path);                                     \
00158         goto out;                                                             \
00159     }
00160 
00161 #define JSFILE_CHECK_WRITE                                                    \
00162     if (!file->isOpen) {                                                      \
00163         JS_ReportWarning(cx,                                                  \
00164                 "File %s is closed, will open it for writing, proceeding",    \
00165                 file->path);                                                  \
00166         js_FileOpen(cx, obj, file, "write,append,create");                    \
00167     }                                                                         \
00168     if (!js_canWrite(cx, file)) {                                             \
00169         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,                \
00170                              JSFILEMSG_CANNOT_WRITE, file->path);             \
00171         goto out;                                                             \
00172     }
00173 
00174 #define JSFILE_CHECK_READ                                                     \
00175     if (!file->isOpen) {                                                      \
00176         JS_ReportWarning(cx,                                                  \
00177                 "File %s is closed, will open it for reading, proceeding",    \
00178                 file->path);                                                  \
00179         js_FileOpen(cx, obj, file, "read");                                   \
00180     }                                                                         \
00181     if (!js_canRead(cx, file)) {                                              \
00182         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,                \
00183                              JSFILEMSG_CANNOT_READ, file->path);              \
00184         goto out;                                                             \
00185     }
00186 
00187 #define JSFILE_CHECK_OPEN(op)                                                 \
00188     if (!file->isOpen) {                                                      \
00189         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,                \
00190                              JSFILEMSG_FILE_MUST_BE_CLOSED, op);              \
00191         goto out;                                                             \
00192     }
00193 
00194 #define JSFILE_CHECK_CLOSED(op)                                               \
00195     if (file->isOpen) {                                                       \
00196         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,                \
00197             JSFILEMSG_FILE_MUST_BE_OPEN, op);                                 \
00198         goto out;                                                             \
00199     }
00200 
00201 #define JSFILE_CHECK_ONE_ARG(op)                                              \
00202     if (argc != 1) {                                                          \
00203         char str[NUMBER_SIZE];                                                \
00204         sprintf(str, "%d", argc);                                             \
00205         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,                \
00206                              JSFILEMSG_EXPECTS_ONE_ARG_ERROR, op, str);       \
00207         goto out;                                                             \
00208     }
00209 
00210 
00211 /*
00212     Security mechanism, should define a callback for this.
00213     The parameters are as follows:
00214     SECURITY_CHECK(JSContext *cx, JSPrincipals *ps, char *op_name, JSFile *file)
00215     XXX Should this be a real function returning a JSBool result (and getting
00216     some typesafety help from the compiler?).
00217 */
00218 #define SECURITY_CHECK(cx, ps, op, file)    \
00219         /* Define a callback here... */
00220 
00221 
00222 /* Structure representing the file internally */
00223 typedef struct JSFile {
00224     char        *path;          /* the path to the file. */
00225     JSBool      isOpen;
00226     int32       mode;           /* mode used to open the file: read, write, append, create, etc.. */
00227     int32       type;           /* Asciiz, utf, unicode */
00228     char        byteBuffer[3];  /* bytes read in advance by js_FileRead ( UTF8 encoding ) */
00229     jsint       nbBytesInBuf;   /* number of bytes stored in the buffer above */
00230     jschar      charBuffer;     /* character read in advance by readln ( mac files only ) */
00231     JSBool      charBufferUsed; /* flag indicating if the buffer above is being used */
00232     JSBool      hasRandomAccess;/* can the file be randomly accessed? false for stdin, and
00233                                  UTF-encoded files. */
00234     JSBool      hasAutoflush;   /* should we force a flush for each line break? */
00235     JSBool      isNative;       /* if the file is using OS-specific file FILE type */
00236     /* We can actually put the following two in a union since they should never be used at the same time */
00237     PRFileDesc  *handle;        /* the handle for the file, if open.  */
00238     FILE        *nativehandle;  /* native handle, for stuff NSPR doesn't do. */
00239     JSBool      isPipe;         /* if the file is really an OS pipe */
00240 } JSFile;
00241 
00242 /* a few forward declarations... */
00243 JS_PUBLIC_API(JSObject*) js_NewFileObject(JSContext *cx, char *filename);
00244 static JSBool file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
00245 static JSBool file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
00246 
00247 /* New filename manipulation procesures */
00248 /* assumes we don't have leading/trailing spaces */
00249 static JSBool
00250 js_filenameHasAPipe(const char *filename)
00251 {
00252     if (!filename)
00253         return JS_FALSE;
00254 
00255     return  filename[0] == PIPE_SYMBOL ||
00256             filename[strlen(filename) - 1] == PIPE_SYMBOL;
00257 }
00258 
00259 static JSBool
00260 js_isAbsolute(const char *name)
00261 {
00262 #if defined(XP_WIN) || defined(XP_OS2)
00263     return *name && name[1] == ':';
00264 #else
00265     return (name[0]
00266 #   if defined(XP_UNIX) || defined(XP_BEOS)
00267             ==
00268 #   else
00269             !=
00270 #   endif
00271             FILESEPARATOR);
00272 #endif
00273 }
00274 
00275 /*
00276  * Concatinates base and name to produce a valid filename.
00277  * Returned string must be freed.
00278 */
00279 static char*
00280 js_combinePath(JSContext *cx, const char *base, const char *name)
00281 {
00282     int len = strlen(base);
00283     char* result = JS_malloc(cx, len + strlen(name) + 2);
00284 
00285     if (!result)
00286         return NULL;
00287 
00288     strcpy(result, base);
00289 
00290     if (base[len - 1] != FILESEPARATOR && base[len - 1] != FILESEPARATOR2) {
00291         result[len] = FILESEPARATOR;
00292         result[len + 1] = '\0';
00293     }
00294     strcat(result, name);
00295     return result;
00296 }
00297 
00298 /* Extract the last component from a path name. Returned string must be freed */
00299 static char *
00300 js_fileBaseName(JSContext *cx, const char *pathname)
00301 {
00302     jsint index, aux;
00303     char *result;
00304 
00305     index = strlen(pathname)-1;
00306 
00307     /* Chop off trailing seperators. */
00308     while (index > 0 && (pathname[index]==FILESEPARATOR ||
00309                          pathname[index]==FILESEPARATOR2)) {
00310         --index;
00311     }
00312 
00313     aux = index;
00314 
00315     /* Now find the next separator. */
00316     while (index >= 0 && pathname[index] != FILESEPARATOR &&
00317                          pathname[index] != FILESEPARATOR2) {
00318         --index;
00319     }
00320 
00321     /* Allocate and copy. */
00322     result = JS_malloc(cx, aux - index + 1);
00323     if (!result)
00324         return NULL;
00325     strncpy(result, pathname + index + 1, aux - index);
00326     result[aux - index] = '\0';
00327     return result;
00328 }
00329 
00330 /*
00331  * Returns everything but the last component from a path name.
00332  * Returned string must be freed.
00333  */
00334 static char *
00335 js_fileDirectoryName(JSContext *cx, const char *pathname)
00336 {
00337     char *result;
00338     const char *cp, *end;
00339     size_t pathsize;
00340 
00341     end = pathname + strlen(pathname);
00342     cp = end - 1;
00343 
00344     /* If this is already a directory, chop off the trailing /s. */
00345     while (cp >= pathname) {
00346         if (*cp != FILESEPARATOR && *cp != FILESEPARATOR2)
00347             break;
00348         --cp;
00349     }
00350 
00351     if (cp < pathname && end != pathname) {
00352         /* There were just /s, return the root. */
00353         result = JS_malloc(cx, 1 + 1); /* The separator + trailing NUL. */
00354         result[0] = FILESEPARATOR;
00355         result[1] = '\0';
00356         return result;
00357     }
00358 
00359     /* Now chop off the last portion. */
00360     while (cp >= pathname) {
00361         if (*cp == FILESEPARATOR || *cp == FILESEPARATOR2)
00362             break;
00363         --cp;
00364     }
00365 
00366     /* Check if this is a leaf. */
00367     if (cp < pathname) {
00368         /* It is, return "pathname/". */
00369         if (end[-1] == FILESEPARATOR || end[-1] == FILESEPARATOR2) {
00370             /* Already has its terminating /. */
00371             return JS_strdup(cx, pathname);
00372         }
00373 
00374         pathsize = end - pathname + 1;
00375         result = JS_malloc(cx, pathsize + 1);
00376         if (!result)
00377             return NULL;
00378 
00379         strcpy(result, pathname);
00380         result[pathsize - 1] = FILESEPARATOR;
00381         result[pathsize] = '\0';
00382 
00383         return result;
00384     }
00385 
00386     /* Return everything up to and including the seperator. */
00387     pathsize = cp - pathname + 1;
00388     result = JS_malloc(cx, pathsize + 1);
00389     if (!result)
00390         return NULL;
00391 
00392     strncpy(result, pathname, pathsize);
00393     result[pathsize] = '\0';
00394 
00395     return result;
00396 }
00397 
00398 static char *
00399 js_absolutePath(JSContext *cx, const char * path)
00400 {
00401     JSObject *obj;
00402     JSString *str;
00403     jsval prop;
00404 
00405     if (js_isAbsolute(path)) {
00406         return JS_strdup(cx, path);
00407     } else {
00408         obj = JS_GetGlobalObject(cx);
00409         if (!JS_GetProperty(cx, obj, FILE_CONSTRUCTOR, &prop)) {
00410             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
00411                                  JSFILEMSG_FILE_CONSTRUCTOR_UNDEFINED_ERROR);
00412             return JS_strdup(cx, path);
00413         }
00414 
00415         obj = JSVAL_TO_OBJECT(prop);
00416         if (!JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, &prop)) {
00417             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
00418                                  JSFILEMSG_FILE_CURRENTDIR_UNDEFINED_ERROR);
00419             return JS_strdup(cx, path);
00420         }
00421 
00422         str = JS_ValueToString(cx, prop);
00423         if (!str)
00424             return JS_strdup(cx, path);
00425 
00426         /* should we have an array of curr dirs indexed by drive for windows? */
00427         return js_combinePath(cx, JS_GetStringBytes(str), path);
00428     }
00429 }
00430 
00431 /* Side effect: will remove spaces in the beginning/end of the filename */
00432 static char *
00433 js_canonicalPath(JSContext *cx, char *oldpath)
00434 {
00435     char *tmp;
00436     char *path = oldpath;
00437     char *base, *dir, *current, *result;
00438     jsint c;
00439     jsint back = 0;
00440     unsigned int i = 0, j = strlen(path)-1;
00441 
00442     /* This is probably optional */
00443        /* Remove possible spaces in the beginning and end */
00444     while (i < j && path[i] == ' ')
00445         i++;
00446     while (j >= 0 && path[j] == ' ')
00447         j--;
00448 
00449     tmp = JS_malloc(cx, j-i+2);
00450     if (!tmp)
00451         return NULL;
00452 
00453     strncpy(tmp, path + i, j - i + 1);
00454     tmp[j - i + 1] = '\0';
00455 
00456     path = tmp;
00457 
00458     /* Pipe support. */
00459     if (js_filenameHasAPipe(path))
00460         return path;
00461 
00462     /* file:// support. */
00463     if (!strncmp(path, URL_PREFIX, strlen(URL_PREFIX))) {
00464         tmp = js_canonicalPath(cx, path + strlen(URL_PREFIX));
00465         JS_free(cx, path);
00466         return tmp;
00467     }
00468 
00469     if (!js_isAbsolute(path)) {
00470         tmp = js_absolutePath(cx, path);
00471         if (!tmp)
00472             return NULL;
00473         path = tmp;
00474     }
00475 
00476     result = JS_strdup(cx, "");
00477 
00478     current = path;
00479 
00480     base = js_fileBaseName(cx, current);
00481     dir = js_fileDirectoryName(cx, current);
00482 
00483     while (strcmp(dir, current)) {
00484         if (!strcmp(base, "..")) {
00485             back++;
00486         } else {
00487             if (back > 0) {
00488                 back--;
00489             } else {
00490                 tmp = result;
00491                 result = JS_malloc(cx, strlen(base) + 1 + strlen(tmp) + 1);
00492                 if (!result)
00493                     goto out;
00494 
00495                 strcpy(result, base);
00496                 c = strlen(result);
00497                 if (*tmp) {
00498                     result[c] = FILESEPARATOR;
00499                     result[c + 1] = '\0';
00500                     strcat(result, tmp);
00501                 }
00502                 JS_free(cx, tmp);
00503             }
00504         }
00505         JS_free(cx, current);
00506         JS_free(cx, base);
00507         current = dir;
00508         base =  js_fileBaseName(cx, current);
00509         dir = js_fileDirectoryName(cx, current);
00510     }
00511 
00512     tmp = result;
00513     result = JS_malloc(cx, strlen(dir)+1+strlen(tmp)+1);
00514     if (!result)
00515         goto out;
00516 
00517     strcpy(result, dir);
00518     c = strlen(result);
00519     if (tmp[0]!='\0') {
00520         if ((result[c-1]!=FILESEPARATOR)&&(result[c-1]!=FILESEPARATOR2)) {
00521             result[c] = FILESEPARATOR;
00522             result[c+1] = '\0';
00523         }
00524         strcat(result, tmp);
00525     }
00526 
00527 out:
00528     if (tmp)
00529         JS_free(cx, tmp);
00530     if (dir)
00531         JS_free(cx, dir);
00532     if (base)
00533         JS_free(cx, base);
00534     if (current)
00535         JS_free(cx, current);
00536 
00537     return result;
00538 }
00539 
00540 /* -------------------------- Text conversion ------------------------------- */
00541 /* The following is ripped from libi18n/unicvt.c and include files.. */
00542 
00543 /*
00544  * UTF8 defines and macros
00545  */
00546 #define ONE_OCTET_BASE          0x00    /* 0xxxxxxx */
00547 #define ONE_OCTET_MASK          0x7F    /* x1111111 */
00548 #define CONTINUING_OCTET_BASE   0x80    /* 10xxxxxx */
00549 #define CONTINUING_OCTET_MASK   0x3F    /* 00111111 */
00550 #define TWO_OCTET_BASE          0xC0    /* 110xxxxx */
00551 #define TWO_OCTET_MASK          0x1F    /* 00011111 */
00552 #define THREE_OCTET_BASE        0xE0    /* 1110xxxx */
00553 #define THREE_OCTET_MASK        0x0F    /* 00001111 */
00554 #define FOUR_OCTET_BASE         0xF0    /* 11110xxx */
00555 #define FOUR_OCTET_MASK         0x07    /* 00000111 */
00556 #define FIVE_OCTET_BASE         0xF8    /* 111110xx */
00557 #define FIVE_OCTET_MASK         0x03    /* 00000011 */
00558 #define SIX_OCTET_BASE          0xFC    /* 1111110x */
00559 #define SIX_OCTET_MASK          0x01    /* 00000001 */
00560 
00561 #define IS_UTF8_1ST_OF_1(x) (( (x)&~ONE_OCTET_MASK  ) == ONE_OCTET_BASE)
00562 #define IS_UTF8_1ST_OF_2(x) (( (x)&~TWO_OCTET_MASK  ) == TWO_OCTET_BASE)
00563 #define IS_UTF8_1ST_OF_3(x) (( (x)&~THREE_OCTET_MASK) == THREE_OCTET_BASE)
00564 #define IS_UTF8_1ST_OF_4(x) (( (x)&~FOUR_OCTET_MASK ) == FOUR_OCTET_BASE)
00565 #define IS_UTF8_1ST_OF_5(x) (( (x)&~FIVE_OCTET_MASK ) == FIVE_OCTET_BASE)
00566 #define IS_UTF8_1ST_OF_6(x) (( (x)&~SIX_OCTET_MASK  ) == SIX_OCTET_BASE)
00567 #define IS_UTF8_2ND_THRU_6TH(x) \
00568                     (( (x)&~CONTINUING_OCTET_MASK  ) == CONTINUING_OCTET_BASE)
00569 #define IS_UTF8_1ST_OF_UCS2(x) \
00570             IS_UTF8_1ST_OF_1(x) \
00571             || IS_UTF8_1ST_OF_2(x) \
00572             || IS_UTF8_1ST_OF_3(x)
00573 
00574 
00575 #define MAX_UCS2            0xFFFF
00576 #define DEFAULT_CHAR        0x003F  /* Default char is "?" */
00577 #define BYTE_MASK           0xBF
00578 #define BYTE_MARK           0x80
00579 
00580 
00581 /* Function: one_ucs2_to_utf8_char
00582  *
00583  * Function takes one UCS-2 char and writes it to a UTF-8 buffer.
00584  * We need a UTF-8 buffer because we don't know before this
00585  * function how many bytes of utf-8 data will be written. It also
00586  * takes a pointer to the end of the UTF-8 buffer so that we don't
00587  * overwrite data. This function returns the number of UTF-8 bytes
00588  * of data written, or -1 if the buffer would have been overrun.
00589  */
00590 
00591 #define LINE_SEPARATOR      0x2028
00592 #define PARAGRAPH_SEPARATOR 0x2029
00593 static int16 one_ucs2_to_utf8_char(unsigned char *tobufp,
00594                                    unsigned char *tobufendp,
00595                                    uint16 onechar)
00596 {
00597     int16 numUTF8bytes = 0;
00598 
00599     if (onechar == LINE_SEPARATOR || onechar == PARAGRAPH_SEPARATOR) {
00600         strcpy((char*)tobufp, "\n");
00601         return strlen((char*)tobufp);
00602     }
00603 
00604     if (onechar < 0x80) {
00605         numUTF8bytes = 1;
00606     } else if (onechar < 0x800) {
00607         numUTF8bytes = 2;
00608     } else {
00609         /* 0x800 >= onechar <= MAX_UCS2 */
00610         numUTF8bytes = 3;
00611     }
00612 
00613     tobufp += numUTF8bytes;
00614 
00615     /* return error if we don't have space for the whole character */
00616     if (tobufp > tobufendp) {
00617         return(-1);
00618     }
00619 
00620     switch(numUTF8bytes) {
00621       case 3: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
00622               *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
00623               *--tobufp = onechar |  THREE_OCTET_BASE;
00624               break;
00625 
00626       case 2: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
00627               *--tobufp = onechar | TWO_OCTET_BASE;
00628               break;
00629 
00630       case 1: *--tobufp = (unsigned char)onechar;
00631               break;
00632     }
00633 
00634     return numUTF8bytes;
00635 }
00636 
00637 /*
00638  * utf8_to_ucs2_char
00639  *
00640  * Convert a utf8 multibyte character to ucs2
00641  *
00642  * inputs: pointer to utf8 character(s)
00643  *         length of utf8 buffer ("read" length limit)
00644  *         pointer to return ucs2 character
00645  *
00646  * outputs: number of bytes in the utf8 character
00647  *          -1 if not a valid utf8 character sequence
00648  *          -2 if the buffer is too short
00649  */
00650 static int16
00651 utf8_to_ucs2_char(const unsigned char *utf8p, int16 buflen, uint16 *ucs2p)
00652 {
00653     uint16 lead, cont1, cont2;
00654 
00655     /*
00656      * Check for minimum buffer length
00657      */
00658     if ((buflen < 1) || (utf8p == NULL)) {
00659         return -2;
00660     }
00661     lead = (uint16) (*utf8p);
00662 
00663     /*
00664      * Check for a one octet sequence
00665      */
00666     if (IS_UTF8_1ST_OF_1(lead)) {
00667         *ucs2p = lead & ONE_OCTET_MASK;
00668         return 1;
00669     }
00670 
00671     /*
00672      * Check for a two octet sequence
00673      */
00674     if (IS_UTF8_1ST_OF_2(*utf8p)) {
00675         if (buflen < 2)
00676             return -2;
00677         cont1 = (uint16) *(utf8p+1);
00678         if (!IS_UTF8_2ND_THRU_6TH(cont1))
00679             return -1;
00680         *ucs2p =  (lead & TWO_OCTET_MASK) << 6;
00681         *ucs2p |= cont1 & CONTINUING_OCTET_MASK;
00682         return 2;
00683     }
00684 
00685     /*
00686      * Check for a three octet sequence
00687      */
00688     else if (IS_UTF8_1ST_OF_3(lead)) {
00689         if (buflen < 3)
00690             return -2;
00691         cont1 = (uint16) *(utf8p+1);
00692         cont2 = (uint16) *(utf8p+2);
00693         if (   (!IS_UTF8_2ND_THRU_6TH(cont1))
00694             || (!IS_UTF8_2ND_THRU_6TH(cont2)))
00695             return -1;
00696         *ucs2p =  (lead & THREE_OCTET_MASK) << 12;
00697         *ucs2p |= (cont1 & CONTINUING_OCTET_MASK) << 6;
00698         *ucs2p |= cont2 & CONTINUING_OCTET_MASK;
00699         return 3;
00700     }
00701     else { /* not a valid utf8/ucs2 character */
00702         return -1;
00703     }
00704 }
00705 
00706 /* ----------------------------- Helper functions --------------------------- */
00707 /* Ripped off from lm_win.c .. */
00708 /* where is strcasecmp?.. for now, it's case sensitive..
00709  *
00710  * strcasecmp is in strings.h, but on windows it's called _stricmp...
00711  * will need to #ifdef this
00712 */
00713 
00714 static int32
00715 js_FileHasOption(JSContext *cx, const char *oldoptions, const char *name)
00716 {
00717     char *comma, *equal, *current;
00718     char *options = JS_strdup(cx, oldoptions);
00719     int32 found = 0;
00720 
00721     current = options;
00722     for (;;) {
00723         comma = strchr(current, ',');
00724         if (comma) *comma = '\0';
00725         equal = strchr(current, '=');
00726         if (equal) *equal = '\0';
00727         if (strcmp(current, name) == 0) {
00728             if (!equal || strcmp(equal + 1, "yes") == 0)
00729                 found = 1;
00730             else
00731                 found = atoi(equal + 1);
00732         }
00733         if (equal) *equal = '=';
00734         if (comma) *comma = ',';
00735         if (found || !comma)
00736             break;
00737         current = comma + 1;
00738     }
00739     JS_free(cx, options);
00740     return found;
00741 }
00742 
00743 /* empty the buffer */
00744 static void
00745 js_ResetBuffers(JSFile * file)
00746 {
00747     file->charBufferUsed = JS_FALSE;
00748     file->nbBytesInBuf = 0;
00749 }
00750 
00751 /* Reset file attributes */
00752 static void
00753 js_ResetAttributes(JSFile * file)
00754 {
00755     file->mode = file->type = 0;
00756     file->isOpen = JS_FALSE;
00757     file->handle = NULL;
00758     file->nativehandle = NULL;
00759     file->hasRandomAccess = JS_TRUE; /* Innocent until proven guilty. */
00760     file->hasAutoflush = JS_FALSE;
00761     file->isNative = JS_FALSE;
00762     file->isPipe = JS_FALSE;
00763 
00764     js_ResetBuffers(file);
00765 }
00766 
00767 static JSBool
00768 js_FileOpen(JSContext *cx, JSObject *obj, JSFile *file, char *mode){
00769     JSString *type, *mask;
00770     jsval v[2];
00771     jsval rval;
00772 
00773     type =  JS_InternString(cx, asciistring);
00774     mask =  JS_NewStringCopyZ(cx, mode);
00775     v[0] = STRING_TO_JSVAL(mask);
00776     v[1] = STRING_TO_JSVAL(type);
00777 
00778     if (!file_open(cx, obj, 2, v, &rval))
00779         return JS_FALSE;
00780     return JS_TRUE;
00781 }
00782 
00783 /* Buffered version of PR_Read. Used by js_FileRead */
00784 static int32
00785 js_BufferedRead(JSFile *f, unsigned char *buf, int32 len)
00786 {
00787     int32 count = 0;
00788 
00789     while (f->nbBytesInBuf>0&&len>0) {
00790         buf[0] = f->byteBuffer[0];
00791         f->byteBuffer[0] = f->byteBuffer[1];
00792         f->byteBuffer[1] = f->byteBuffer[2];
00793         f->nbBytesInBuf--;
00794         len--;
00795         buf+=1;
00796         count++;
00797     }
00798 
00799     if (len > 0) {
00800         count += (!f->isNative)
00801                  ? PR_Read(f->handle, buf, len)
00802                  : fread(buf, 1, len, f->nativehandle);
00803     }
00804     return count;
00805 }
00806 
00807 static int32
00808 js_FileRead(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode)
00809 {
00810     unsigned char *aux;
00811     int32 count = 0, i;
00812     jsint remainder;
00813     unsigned char utfbuf[3];
00814 
00815     if (file->charBufferUsed) {
00816         buf[0] = file->charBuffer;
00817         buf++;
00818         len--;
00819         file->charBufferUsed = JS_FALSE;
00820     }
00821 
00822     switch (mode) {
00823       case ASCII:
00824         aux = (unsigned char*)JS_malloc(cx, len);
00825         if (!aux)
00826             return 0;
00827 
00828         count = js_BufferedRead(file, aux, len);
00829         if (count == -1) {
00830             JS_free(cx, aux);
00831             return 0;
00832         }
00833 
00834         for (i = 0; i < len; i++)
00835             buf[i] = (jschar)aux[i];
00836 
00837         JS_free(cx, aux);
00838         break;
00839 
00840       case UTF8:
00841         remainder = 0;
00842         for (count = 0;count<len;count++) {
00843             i = js_BufferedRead(file, utfbuf+remainder, 3-remainder);
00844             if (i<=0) {
00845                 return count;
00846             }
00847             i = utf8_to_ucs2_char(utfbuf, (int16)i, &buf[count] );
00848             if (i<0) {
00849                 return count;
00850             } else {
00851                 if (i==1) {
00852                     utfbuf[0] = utfbuf[1];
00853                     utfbuf[1] = utfbuf[2];
00854                     remainder = 2;
00855                 } else if (i==2) {
00856                     utfbuf[0] = utfbuf[2];
00857                     remainder = 1;
00858                 } else if (i==3) {
00859                     remainder = 0;
00860                 }
00861             }
00862         }
00863         while (remainder>0) {
00864             file->byteBuffer[file->nbBytesInBuf] = utfbuf[0];
00865             file->nbBytesInBuf++;
00866             utfbuf[0] = utfbuf[1];
00867             utfbuf[1] = utfbuf[2];
00868             remainder--;
00869         }
00870         break;
00871 
00872       case UCS2:
00873         count = js_BufferedRead(file, (unsigned char *)buf, len * 2) >> 1;
00874         if (count == -1)
00875             return 0;
00876 
00877         break;
00878 
00879       default:
00880         /* Not reached. */
00881         JS_ASSERT(0);
00882     }
00883 
00884     if(count == -1) {
00885         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
00886                              JSFILEMSG_OP_FAILED, "read", file->path);
00887     }
00888 
00889     return count;
00890 }
00891 
00892 static int32
00893 js_FileSeek(JSContext *cx, JSFile *file, int32 len, int32 mode)
00894 {
00895     int32 count = 0, i;
00896     jsint remainder;
00897     unsigned char utfbuf[3];
00898     jschar tmp;
00899 
00900     switch (mode) {
00901       case ASCII:
00902         count = PR_Seek(file->handle, len, PR_SEEK_CUR);
00903         break;
00904 
00905       case UTF8:
00906         remainder = 0;
00907         for (count = 0;count<len;count++) {
00908             i = js_BufferedRead(file, utfbuf+remainder, 3-remainder);
00909             if (i<=0) {
00910                 return 0;
00911             }
00912             i = utf8_to_ucs2_char(utfbuf, (int16)i, &tmp );
00913             if (i<0) {
00914                 return 0;
00915             } else {
00916                 if (i==1) {
00917                     utfbuf[0] = utfbuf[1];
00918                     utfbuf[1] = utfbuf[2];
00919                     remainder = 2;
00920                 } else if (i==2) {
00921                     utfbuf[0] = utfbuf[2];
00922                     remainder = 1;
00923                 } else if (i==3) {
00924                     remainder = 0;
00925                 }
00926             }
00927         }
00928         while (remainder>0) {
00929             file->byteBuffer[file->nbBytesInBuf] = utfbuf[0];
00930             file->nbBytesInBuf++;
00931             utfbuf[0] = utfbuf[1];
00932             utfbuf[1] = utfbuf[2];
00933             remainder--;
00934         }
00935         break;
00936 
00937       case UCS2:
00938         count = PR_Seek(file->handle, len*2, PR_SEEK_CUR)/2;
00939         break;
00940 
00941       default:
00942         /* Not reached. */
00943         JS_ASSERT(0);
00944     }
00945 
00946     if(count == -1) {
00947         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
00948                              JSFILEMSG_OP_FAILED, "seek", file->path);
00949     }
00950 
00951     return count;
00952 }
00953 
00954 static int32
00955 js_FileWrite(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode)
00956 {
00957     unsigned char   *aux;
00958     int32           count = 0, i, j;
00959     unsigned char   *utfbuf;
00960 
00961     switch (mode) {
00962       case ASCII:
00963         aux = (unsigned char*)JS_malloc(cx, len);
00964         if (!aux)
00965             return 0;
00966 
00967         for (i = 0; i<len; i++)
00968             aux[i] = buf[i] % 256;
00969 
00970         count = (!file->isNative)
00971                 ? PR_Write(file->handle, aux, len)
00972                 : fwrite(aux, 1, len, file->nativehandle);
00973 
00974         if (count==-1) {
00975             JS_free(cx, aux);
00976             return 0;
00977         }
00978 
00979         JS_free(cx, aux);
00980         break;
00981 
00982       case UTF8:
00983         utfbuf = (unsigned char*)JS_malloc(cx, len*3);
00984         if (!utfbuf)  return 0;
00985         i = 0;
00986         for (count = 0;count<len;count++) {
00987             j = one_ucs2_to_utf8_char(utfbuf+i, utfbuf+len*3, buf[count]);
00988             if (j==-1) {
00989                 JS_free(cx, utfbuf);
00990                 return 0;
00991             }
00992             i+=j;
00993         }
00994         j = (!file->isNative) 
00995             ? PR_Write(file->handle, utfbuf, i)
00996             : fwrite(utfbuf, 1, i, file->nativehandle);
00997 
00998         if (j<i) {
00999             JS_free(cx, utfbuf);
01000             return 0;
01001         }
01002         JS_free(cx, utfbuf);
01003         break;
01004 
01005       case UCS2:
01006         count = (!file->isNative) 
01007                 ? PR_Write(file->handle, buf, len*2) >> 1
01008                 : fwrite(buf, 1, len*2, file->nativehandle) >> 1;
01009 
01010         if (count == -1)
01011             return 0;
01012         break;
01013 
01014       default:
01015         /* Not reached. */
01016         JS_ASSERT(0);
01017     }
01018 
01019     if(count == -1) {
01020         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01021                              JSFILEMSG_OP_FAILED, "write", file->path);
01022     }
01023 
01024     return count;
01025 }
01026 
01027 /* ----------------------------- Property checkers -------------------------- */
01028 static JSBool
01029 js_exists(JSContext *cx, JSFile *file)
01030 {
01031     if (file->isNative) {
01032         /* It doesn't make sense for a pipe of stdstream. */
01033         return JS_FALSE;
01034     }
01035 
01036     return PR_Access(file->path, PR_ACCESS_EXISTS) == PR_SUCCESS;
01037 }
01038 
01039 static JSBool
01040 js_canRead(JSContext *cx, JSFile *file)
01041 {
01042     if (!file->isNative) {
01043         if (file->isOpen && !(file->mode & PR_RDONLY))
01044             return JS_FALSE;
01045         return PR_Access(file->path, PR_ACCESS_READ_OK) == PR_SUCCESS;
01046     }
01047 
01048     if (file->isPipe) {
01049         /* Is this pipe open for reading? */
01050         return file->path[0] == PIPE_SYMBOL;
01051     }
01052 
01053     return !strcmp(file->path, STDINPUT_NAME);
01054 }
01055 
01056 static JSBool
01057 js_canWrite(JSContext *cx, JSFile *file)
01058 {
01059     if (!file->isNative) {
01060         if (file->isOpen && !(file->mode & PR_WRONLY))
01061             return JS_FALSE;
01062         return PR_Access(file->path, PR_ACCESS_WRITE_OK) == PR_SUCCESS;
01063     }
01064 
01065     if(file->isPipe) {
01066         /* Is this pipe open for writing? */
01067         return file->path[strlen(file->path)-1] == PIPE_SYMBOL;
01068     }
01069 
01070     return !strcmp(file->path, STDOUTPUT_NAME) ||
01071            !strcmp(file->path, STDERROR_NAME);
01072 }
01073 
01074 static JSBool
01075 js_isFile(JSContext *cx, JSFile *file)
01076 {
01077     if (!file->isNative) {
01078         PRFileInfo info;
01079 
01080         if (file->isOpen
01081             ? PR_GetOpenFileInfo(file->handle, &info)
01082             : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) {
01083             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01084                                  JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
01085             return JS_FALSE;
01086         }
01087 
01088         return info.type == PR_FILE_FILE;
01089     }
01090 
01091     /* This doesn't make sense for a pipe of stdstream. */
01092     return JS_FALSE;
01093 }
01094 
01095 static JSBool
01096 js_isDirectory(JSContext *cx, JSFile *file)
01097 {
01098     if(!file->isNative){
01099         PRFileInfo info;
01100 
01101         /* Hack needed to get get_property to work. */
01102         if (!js_exists(cx, file))
01103             return JS_FALSE;
01104 
01105         if (file->isOpen
01106             ? PR_GetOpenFileInfo(file->handle, &info)
01107             : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) {
01108             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01109                                  JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
01110             return JS_FALSE;
01111         }
01112 
01113         return info.type == PR_FILE_DIRECTORY;
01114     }
01115 
01116     /* This doesn't make sense for a pipe of stdstream. */
01117     return JS_FALSE;
01118 }
01119 
01120 static jsval
01121 js_size(JSContext *cx, JSFile *file)
01122 {
01123     PRFileInfo info;
01124 
01125     JSFILE_CHECK_NATIVE("size");
01126 
01127     if (file->isOpen
01128         ? PR_GetOpenFileInfo(file->handle, &info)
01129         : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) {
01130         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01131                              JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
01132         return JSVAL_VOID;
01133     }
01134 
01135     return INT_TO_JSVAL(info.size);
01136 
01137 out:
01138     return JSVAL_VOID;
01139 }
01140 
01141 /*
01142  * Return the parent object
01143  */
01144 static JSBool
01145 js_parent(JSContext *cx, JSFile *file, jsval *resultp)
01146 {
01147     char *str;
01148 
01149     /* Since we only care about pipes and native files, return NULL. */
01150     if (file->isNative) {
01151         *resultp = JSVAL_VOID;
01152         return JS_TRUE;
01153     }
01154 
01155     str = js_fileDirectoryName(cx, file->path);
01156     if (!str)
01157         return JS_FALSE;
01158 
01159     /* If the directory is equal to the original path, we're at the root. */
01160     if (!strcmp(file->path, str)) {
01161         *resultp = JSVAL_NULL;
01162     } else {
01163         JSObject *obj = js_NewFileObject(cx, str);
01164         if (!obj) {
01165             JS_free(cx, str);
01166             return JS_FALSE;
01167         }
01168         *resultp = OBJECT_TO_JSVAL(obj);
01169     }
01170 
01171     JS_free(cx, str);
01172     return JS_TRUE;
01173 }
01174 
01175 static JSBool
01176 js_name(JSContext *cx, JSFile *file, jsval *vp)
01177 {
01178     char *name;
01179     JSString *str;
01180 
01181     if (file->isPipe) {
01182         *vp = JSVAL_VOID;
01183         return JS_TRUE;
01184     }
01185 
01186     name = js_fileBaseName(cx, file->path);
01187     if (!name)
01188         return JS_FALSE;
01189 
01190     str = JS_NewString(cx, name, strlen(name));
01191     if (!str) {
01192         JS_free(cx, name);
01193         return JS_FALSE;
01194     }
01195 
01196     *vp = STRING_TO_JSVAL(str);
01197     return JS_TRUE;
01198 }
01199 
01200 /* ------------------------------ File object methods ---------------------------- */
01201 static JSBool
01202 file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01203 {
01204     JSFile      *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
01205     JSString  *strmode, *strtype;
01206     char        *ctype, *mode;
01207     int32       mask, type;
01208     int         len;
01209 
01210     mode = NULL;
01211 
01212     SECURITY_CHECK(cx, NULL, "open", file);
01213 
01214     /* A native file that is already open */
01215     if(file->isOpen && file->isNative) {
01216         JS_ReportWarning(cx, "Native file %s is already open, proceeding",
01217                          file->path);
01218         goto good;
01219     }
01220 
01221     /* Close before proceeding */
01222     if (file->isOpen) {
01223         JS_ReportWarning(cx, "File %s is already open, we will close it and "
01224                          "reopen, proceeding", file->path);
01225         if(!file_close(cx, obj, 0, NULL, rval))
01226             goto out;
01227     }
01228 
01229     if (js_isDirectory(cx, file)) {
01230         JS_ReportWarning(cx, "%s seems to be a directory, there is no point in "
01231                          "trying to open it, proceeding", file->path);
01232         goto good;
01233     }
01234 
01235     /* Path must be defined at this point */
01236     len = strlen(file->path);
01237 
01238     /* Mode */
01239     if (argc >= 1) {
01240         strmode = JS_ValueToString(cx, argv[0]);
01241         if (!strmode) {
01242             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01243                                  JSFILEMSG_FIRST_ARGUMENT_OPEN_NOT_STRING_ERROR,
01244                                  argv[0]);
01245             goto out;
01246         }
01247         mode = JS_strdup(cx, JS_GetStringBytes(strmode));
01248     } else {
01249         if(file->path[0]==PIPE_SYMBOL) {
01250             /* pipe default mode */
01251             mode = JS_strdup(cx, "read");
01252         } else if(file->path[len-1]==PIPE_SYMBOL) {
01253             /* pipe default mode */
01254             mode = JS_strdup(cx, "write");
01255         } else {
01256             /* non-destructive, permissive defaults. */
01257             mode = JS_strdup(cx, "readWrite,append,create");
01258         }
01259     }
01260 
01261     /* Process the mode */
01262     mask = 0;
01263     /* TODO: this is pretty ugly, we walk thru the string too many times */
01264     mask |= js_FileHasOption(cx, mode, "read")     ? PR_RDONLY       :   0;
01265     mask |= js_FileHasOption(cx, mode, "write")    ? PR_WRONLY       :   0;
01266     mask |= js_FileHasOption(cx, mode, "readWrite")? PR_RDWR         :   0;
01267     mask |= js_FileHasOption(cx, mode, "append")   ? PR_APPEND       :   0;
01268     mask |= js_FileHasOption(cx, mode, "create")   ? PR_CREATE_FILE  :   0;
01269     mask |= js_FileHasOption(cx, mode, "replace")  ? PR_TRUNCATE     :   0;
01270 
01271     if (mask & PR_RDWR)
01272         mask |= (PR_RDONLY | PR_WRONLY);
01273     if ((mask & PR_RDONLY) && (mask & PR_WRONLY))
01274         mask |= PR_RDWR;
01275 
01276     file->hasAutoflush |= js_FileHasOption(cx, mode, "autoflush");
01277 
01278     /* Type */
01279     if (argc > 1) {
01280         strtype = JS_ValueToString(cx, argv[1]);
01281         if (!strtype) {
01282             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01283                                 JSFILEMSG_SECOND_ARGUMENT_OPEN_NOT_STRING_ERROR,
01284                                  argv[1]);
01285             goto out;
01286         }
01287         ctype = JS_GetStringBytes(strtype);
01288 
01289         if(!strcmp(ctype, utfstring)) {
01290             type = UTF8;
01291         } else if (!strcmp(ctype, unicodestring)) {
01292             type = UCS2;
01293         } else {
01294             if (strcmp(ctype, asciistring)) {
01295                 JS_ReportWarning(cx, "File type %s is not supported, using "
01296                                  "'text' instead, proceeding", ctype);
01297             }
01298             type = ASCII;
01299         }
01300     } else {
01301         type = ASCII;
01302     }
01303 
01304     /* Save the relevant fields */
01305     file->type = type;
01306     file->mode = mask;
01307     file->nativehandle = NULL;
01308     file->hasRandomAccess = (type != UTF8);
01309 
01310     /*
01311      * Deal with pipes here. We can't use NSPR for pipes, so we have to use
01312      * POPEN.
01313      */
01314     if (file->path[0]==PIPE_SYMBOL || file->path[len-1]==PIPE_SYMBOL) {
01315         if (file->path[0] == PIPE_SYMBOL && file->path[len-1] == PIPE_SYMBOL) {
01316             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01317                                  JSFILEMSG_BIDIRECTIONAL_PIPE_NOT_SUPPORTED);
01318             goto out;
01319         } else {
01320             int i = 0;
01321             char pipemode[3];
01322             SECURITY_CHECK(cx, NULL, "pipe_open", file);
01323 
01324             if(file->path[0] == PIPE_SYMBOL){
01325                 if(mask & (PR_WRONLY | PR_APPEND | PR_CREATE_FILE | PR_TRUNCATE)){
01326                     JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01327                                    JSFILEMSG_OPEN_MODE_NOT_SUPPORTED_WITH_PIPES,
01328                                          mode, file->path);
01329                     goto out;
01330                 }
01331                 /* open(SPOOLER, "| cat -v | lpr -h 2>/dev/null") -- pipe for writing */
01332                 pipemode[i++] = 'r';
01333 #ifndef XP_UNIX
01334                 pipemode[i++] = file->type==UTF8 ? 'b' : 't';
01335 #endif
01336                 pipemode[i++] = '\0';
01337                 file->nativehandle = POPEN(&file->path[1], pipemode);
01338             } else if(file->path[len-1] == PIPE_SYMBOL) {
01339                 char *command = JS_malloc(cx, len);
01340 
01341                 strncpy(command, file->path, len-1);
01342                 command[len-1] = '\0';
01343                 /* open(STATUS, "netstat -an 2>&1 |") */
01344                 pipemode[i++] = 'w';
01345 #ifndef XP_UNIX
01346                 pipemode[i++] = file->type==UTF8 ? 'b' : 't';
01347 #endif
01348                 pipemode[i++] = '\0';
01349                 file->nativehandle = POPEN(command, pipemode);
01350                 JS_free(cx, command);
01351             }
01352             /* set the flags */
01353             file->isNative = JS_TRUE;
01354             file->isPipe  = JS_TRUE;
01355             file->hasRandomAccess = JS_FALSE;
01356         }
01357     } else {
01358         /* TODO: what about the permissions?? Java ignores the problem... */
01359         file->handle = PR_Open(file->path, mask, 0644);
01360     }
01361 
01362     js_ResetBuffers(file);
01363     JS_free(cx, mode);
01364     mode = NULL;
01365 
01366     /* Set the open flag and return result */
01367     if (file->handle == NULL && file->nativehandle == NULL) {
01368         file->isOpen = JS_FALSE;
01369 
01370         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01371                              JSFILEMSG_OP_FAILED, "open", file->path);
01372         goto out;
01373     }
01374 
01375 good:
01376     file->isOpen = JS_TRUE;
01377     *rval = JSVAL_TRUE;
01378     return JS_TRUE;
01379 
01380 out:
01381     if(mode)
01382         JS_free(cx, mode);
01383     return JS_FALSE;
01384 }
01385 
01386 static JSBool
01387 file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01388 {
01389     JSFile  *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
01390 
01391     SECURITY_CHECK(cx, NULL, "close", file);
01392 
01393     if(!file->isOpen){
01394         JS_ReportWarning(cx, "File %s is not open, can't close it, proceeding",
01395             file->path);
01396         goto out;
01397     }
01398 
01399     if(!file->isPipe){
01400         if(file->isNative){
01401             JS_ReportWarning(cx, "Unable to close a native file, proceeding", file->path);
01402             goto out;
01403         }else{
01404             if(file->handle && PR_Close(file->handle)){
01405                 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01406                     JSFILEMSG_OP_FAILED, "close", file->path);
01407 
01408                 goto out;
01409             }
01410         }
01411     }else{
01412         if(PCLOSE(file->nativehandle)==-1){
01413             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01414                 JSFILEMSG_OP_FAILED, "pclose", file->path);
01415             goto out;
01416         }
01417     }
01418 
01419     js_ResetAttributes(file);
01420     *rval = JSVAL_TRUE;
01421     return JS_TRUE;
01422 
01423 out:
01424     return JS_FALSE;
01425 }
01426 
01427 
01428 static JSBool
01429 file_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01430 {
01431        JSFile  *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
01432 
01433     SECURITY_CHECK(cx, NULL, "remove", file);
01434     JSFILE_CHECK_NATIVE("remove");
01435     JSFILE_CHECK_CLOSED("remove");
01436 
01437     if ((js_isDirectory(cx, file) ?
01438             PR_RmDir(file->path) : PR_Delete(file->path))==PR_SUCCESS) {
01439         js_ResetAttributes(file);
01440         *rval = JSVAL_TRUE;
01441         return JS_TRUE;
01442     } else {
01443         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01444             JSFILEMSG_OP_FAILED, "remove", file->path);
01445         goto out;
01446     }
01447 out:
01448     *rval = JSVAL_FALSE;
01449     return JS_FALSE;
01450 }
01451 
01452 /* Raw PR-based function. No text processing. Just raw data copying. */
01453 static JSBool
01454 file_copyTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01455 {
01456     JSFile      *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
01457     char        *dest = NULL;
01458     PRFileDesc  *handle = NULL;
01459     char        *buffer;
01460     jsval            count, size;
01461     JSBool      fileInitiallyOpen=JS_FALSE;
01462 
01463     SECURITY_CHECK(cx, NULL, "copyTo", file);   /* may need a second argument!*/
01464     JSFILE_CHECK_ONE_ARG("copyTo");
01465     JSFILE_CHECK_NATIVE("copyTo");
01466     /* remeber the state */
01467     fileInitiallyOpen = file->isOpen;
01468     JSFILE_CHECK_READ;
01469 
01470     dest = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
01471 
01472     /* make sure we are not reading a file open for writing */
01473     if (file->isOpen && !js_canRead(cx, file)) {
01474         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01475                 JSFILEMSG_CANNOT_COPY_FILE_OPEN_FOR_WRITING_ERROR, file->path);
01476         goto out;
01477     }
01478 
01479     if (file->handle==NULL){
01480         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01481             JSFILEMSG_OP_FAILED, "open", file->path);
01482         goto out;
01483     }
01484 
01485     handle = PR_Open(dest, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, 0644);
01486 
01487     if(!handle){
01488         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01489             JSFILEMSG_OP_FAILED, "open", dest);
01490         goto out;
01491     }
01492 
01493     if ((size=js_size(cx, file))==JSVAL_VOID) {
01494         goto out;
01495     }
01496 
01497     buffer = JS_malloc(cx, size);
01498 
01499     count = INT_TO_JSVAL(PR_Read(file->handle, buffer, size));
01500 
01501     /* reading panic */
01502     if (count!=size) {
01503         JS_free(cx, buffer);
01504         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01505               JSFILEMSG_COPY_READ_ERROR, file->path);
01506         goto out;
01507     }
01508 
01509     count = INT_TO_JSVAL(PR_Write(handle, buffer, JSVAL_TO_INT(size)));
01510 
01511     /* writing panic */
01512     if (count!=size) {
01513         JS_free(cx, buffer);
01514         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01515               JSFILEMSG_COPY_WRITE_ERROR, file->path);
01516         goto out;
01517     }
01518 
01519     JS_free(cx, buffer);
01520 
01521        if(!fileInitiallyOpen){
01522               if(!file_close(cx, obj, 0, NULL, rval)) goto out;
01523        }
01524 
01525     if(PR_Close(handle)!=PR_SUCCESS){
01526         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01527               JSFILEMSG_OP_FAILED, "close", dest);
01528         goto out;
01529     }
01530 
01531     *rval = JSVAL_TRUE;
01532     return JS_TRUE;
01533 out:
01534     if(file->isOpen && !fileInitiallyOpen){
01535         if(PR_Close(file->handle)!=PR_SUCCESS){
01536             JS_ReportWarning(cx, "Can't close %s, proceeding", file->path);
01537         }
01538     }
01539 
01540     if(handle && PR_Close(handle)!=PR_SUCCESS){
01541         JS_ReportWarning(cx, "Can't close %s, proceeding", dest);
01542     }
01543 
01544     *rval = JSVAL_FALSE;
01545     return JS_FALSE;
01546 }
01547 
01548 static JSBool
01549 file_renameTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01550 {
01551     JSFile  *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
01552     char    *dest;
01553 
01554     SECURITY_CHECK(cx, NULL, "renameTo", file); /* may need a second argument!*/
01555     JSFILE_CHECK_ONE_ARG("renameTo");
01556     JSFILE_CHECK_NATIVE("renameTo");
01557     JSFILE_CHECK_CLOSED("renameTo");
01558 
01559     dest = RESOLVE_PATH(cx, JS_GetStringBytes(JS_ValueToString(cx, argv[0])));
01560 
01561     if (PR_Rename(file->path, dest)==PR_SUCCESS){
01562         /* copy the new filename */
01563         JS_free(cx, file->path);
01564         file->path = dest;
01565         *rval = JSVAL_TRUE;
01566         return JS_TRUE;
01567     }else{
01568         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01569             JSFILEMSG_RENAME_FAILED, file->path, dest);
01570         goto out;
01571     }
01572 out:
01573     *rval = JSVAL_FALSE;
01574     return JS_FALSE;
01575 }
01576 
01577 static JSBool
01578 file_flush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01579 {
01580     JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
01581 
01582     SECURITY_CHECK(cx, NULL, "flush", file);
01583     JSFILE_CHECK_NATIVE("flush");
01584     JSFILE_CHECK_OPEN("flush");
01585 
01586     if (PR_Sync(file->handle)==PR_SUCCESS){
01587       *rval = JSVAL_TRUE;
01588       return JS_TRUE;
01589     }else{
01590         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01591            JSFILEMSG_OP_FAILED, "flush", file->path);
01592        goto out;
01593     }
01594 out:
01595     *rval = JSVAL_FALSE;
01596     return JS_FALSE;
01597 }
01598 
01599 static JSBool
01600 file_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01601 {
01602     JSFile      *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
01603     JSString    *str;
01604     int32       count;
01605     uintN       i;
01606 
01607     SECURITY_CHECK(cx, NULL, "write", file);
01608     JSFILE_CHECK_WRITE;
01609 
01610     for (i = 0; i<argc; i++) {
01611         str = JS_ValueToString(cx, argv[i]);
01612         count = js_FileWrite(cx, file, JS_GetStringChars(str),
01613             JS_GetStringLength(str), file->type);
01614         if (count==-1){
01615           *rval = JSVAL_FALSE;
01616           return JS_FALSE;
01617         }
01618     }
01619 
01620     *rval = JSVAL_TRUE;
01621     return JS_TRUE;
01622 out:
01623     *rval = JSVAL_FALSE;
01624     return JS_FALSE;
01625 }
01626 
01627 static JSBool
01628 file_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01629 {
01630     JSFile      *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
01631     JSString    *str;
01632 
01633     SECURITY_CHECK(cx, NULL, "writeln", file);
01634     JSFILE_CHECK_WRITE;
01635 
01636     /* don't report an error here */
01637     if(!file_write(cx, obj, argc, argv, rval))  return JS_FALSE;
01638     /* don't do security here -- we passed the check in file_write */
01639     str = JS_NewStringCopyZ(cx, "\n");
01640 
01641     if (js_FileWrite(cx, file, JS_GetStringChars(str), JS_GetStringLength(str),
01642             file->type)==-1){
01643         *rval = JSVAL_FALSE;
01644         return JS_FALSE;
01645     }
01646 
01647     /* eol causes flush if hasAutoflush is turned on */
01648     if (file->hasAutoflush)
01649         file_flush(cx, obj, 0, NULL, rval);
01650 
01651     *rval =  JSVAL_TRUE;
01652     return JS_TRUE;
01653 out:
01654     *rval = JSVAL_FALSE;
01655     return JS_FALSE;
01656 }
01657 
01658 static JSBool
01659 file_writeAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01660 {
01661     JSFile      *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
01662     jsuint      i;
01663     jsuint      limit;
01664     JSObject    *array;
01665     JSObject    *elem;
01666     jsval       elemval;
01667 
01668     SECURITY_CHECK(cx, NULL, "writeAll", file);
01669     JSFILE_CHECK_ONE_ARG("writeAll");
01670     JSFILE_CHECK_WRITE;
01671 
01672     if (!JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[0]))) {
01673         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01674             JSFILEMSG_FIRST_ARGUMENT_WRITEALL_NOT_ARRAY_ERROR);
01675         goto out;
01676     }
01677 
01678     array = JSVAL_TO_OBJECT(argv[0]);
01679 
01680     JS_GetArrayLength(cx, array, &limit);
01681 
01682     for (i = 0; i<limit; i++) {
01683         if (!JS_GetElement(cx, array, i, &elemval))  return JS_FALSE;
01684         elem = JSVAL_TO_OBJECT(elemval);
01685         file_writeln(cx, obj, 1, &elemval, rval);
01686     }
01687 
01688     *rval = JSVAL_TRUE;
01689     return JS_TRUE;
01690 out:
01691     *rval = JSVAL_FALSE;
01692     return JS_FALSE;
01693 }
01694 
01695 static JSBool
01696 file_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01697 {
01698     JSFile      *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
01699     JSString    *str;
01700     int32       want, count;
01701     jschar      *buf;
01702 
01703     SECURITY_CHECK(cx, NULL, "read", file);
01704     JSFILE_CHECK_ONE_ARG("read");
01705     JSFILE_CHECK_READ;
01706 
01707     if (!JS_ValueToInt32(cx, argv[0], &want)){
01708         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01709             JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "read", argv[0]);
01710         goto out;
01711     }
01712 
01713     /* want = (want>262144)?262144:want; * arbitrary size limitation */
01714 
01715     buf = JS_malloc(cx, want*sizeof buf[0]);
01716     if (!buf)  goto out;
01717 
01718     count =  js_FileRead(cx, file, buf, want, file->type);
01719     if (count>0) {
01720         str = JS_NewUCStringCopyN(cx, buf, count);
01721         *rval = STRING_TO_JSVAL(str);
01722         JS_free(cx, buf);
01723         return JS_TRUE;
01724     } else {
01725         JS_free(cx, buf);
01726         goto out;
01727     }
01728 out:
01729     *rval = JSVAL_FALSE;
01730     return JS_FALSE;
01731 }
01732 
01733 static JSBool
01734 file_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01735 {
01736     JSFile      *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
01737     JSString    *str;
01738     jschar      *buf = NULL, *tmp;
01739     int32       offset, read;
01740     intN        room;
01741     jschar      data, data2;
01742 
01743     SECURITY_CHECK(cx, NULL, "readln", file);
01744     JSFILE_CHECK_READ;
01745 
01746     buf = JS_malloc(cx, MAX_LINE_LENGTH * sizeof data);
01747     if (!buf)
01748         return JS_FALSE;
01749 
01750     room = MAX_LINE_LENGTH - 1;
01751     offset = 0;
01752 
01753     for (;;) {
01754         read = js_FileRead(cx, file, &data, 1, file->type);
01755         if (read < 0)
01756             goto out;
01757         if (read == 0)
01758             goto eof;
01759 
01760         switch (data) {
01761           case '\r':
01762             read = js_FileRead(cx, file, &data2, 1, file->type);
01763             if (read < 0)
01764                 goto out;
01765 
01766             if (read == 1 && data2 != '\n') {
01767                 /* We read one char too far.  Buffer it. */
01768                 file->charBuffer = data2;
01769                 file->charBufferUsed = JS_TRUE;
01770             }
01771 
01772             /* Fall through. */
01773           case '\n':
01774             goto done;
01775 
01776           default:
01777             if (--room < 0) {
01778                 tmp = JS_realloc(cx, buf,
01779                                  (offset + MAX_LINE_LENGTH) * sizeof data);
01780                 if (!tmp)
01781                     goto out;
01782 
01783                 room = MAX_LINE_LENGTH - 1;
01784                 buf = tmp;
01785             }
01786 
01787             buf[offset++] = data;
01788             break;
01789         }
01790     }
01791 
01792 eof:
01793     if (offset == 0) {
01794         *rval = JSVAL_NULL;
01795         return JS_TRUE;
01796     }
01797 
01798 done:
01799     buf[offset] = 0;
01800     tmp = JS_realloc(cx, buf, (offset + 1) * sizeof data);
01801     if (!tmp)
01802         goto out;
01803 
01804     str = JS_NewUCString(cx, tmp, offset);
01805     if (!str)
01806         goto out;
01807 
01808     *rval = STRING_TO_JSVAL(str);
01809     return JS_TRUE;
01810 
01811 out:
01812     if (buf)
01813         JS_free(cx, buf);
01814 
01815     return JS_FALSE;
01816 }
01817 
01818 static JSBool
01819 file_readAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01820 {
01821     JSFile      *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
01822     JSObject    *array;
01823     jsint       len;
01824     jsval       line;
01825     JSBool      lineok = JS_FALSE;
01826 
01827     SECURITY_CHECK(cx, NULL, "readAll", file);
01828     JSFILE_CHECK_READ;
01829 
01830     array = JS_NewArrayObject(cx, 0, NULL);
01831     if (!array)
01832         return JS_FALSE;
01833     *rval = OBJECT_TO_JSVAL(array);
01834 
01835     len = 0;
01836 
01837     lineok = file_readln(cx, obj, 0, NULL, &line);
01838     while (lineok && !JSVAL_IS_NULL(line)) {
01839         JS_SetElement(cx, array, len++, &line);
01840         lineok = file_readln(cx, obj, 0, NULL, &line);
01841     }
01842 
01843 out:
01844     return lineok;
01845 }
01846 
01847 static JSBool
01848 file_seek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01849 {
01850     JSFile      *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
01851     int32       toskip;
01852     int32       pos;
01853 
01854     SECURITY_CHECK(cx, NULL, "seek", file);
01855     JSFILE_CHECK_ONE_ARG("seek");
01856     JSFILE_CHECK_NATIVE("seek");
01857     JSFILE_CHECK_READ;
01858 
01859     if (!JS_ValueToInt32(cx, argv[0], &toskip)){
01860         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01861             JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "seek", argv[0]);
01862         goto out;
01863     }
01864 
01865     if(!file->hasRandomAccess){
01866         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01867             JSFILEMSG_NO_RANDOM_ACCESS, file->path);
01868        goto out;
01869     }
01870 
01871     if(js_isDirectory(cx, file)){
01872         JS_ReportWarning(cx,"Seek on directories is not supported, proceeding");
01873         goto out;
01874     }
01875 
01876     pos = js_FileSeek(cx, file, toskip, file->type);
01877 
01878     if (pos!=-1) {
01879         *rval = INT_TO_JSVAL(pos);
01880         return JS_TRUE;
01881     }
01882 out:
01883     *rval = JSVAL_VOID;
01884     return JS_FALSE;
01885 }
01886 
01887 static JSBool
01888 file_list(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01889 {
01890     PRDir       *dir;
01891     PRDirEntry  *entry;
01892     JSFile      *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
01893     JSObject    *array;
01894     JSObject    *eachFile;
01895     jsint       len;
01896     jsval       v;
01897     JSRegExp    *re = NULL;
01898     JSFunction  *func = NULL;
01899     JSString    *str;
01900     jsval       args[1];
01901     char        *filePath;
01902 
01903     SECURITY_CHECK(cx, NULL, "list", file);
01904     JSFILE_CHECK_NATIVE("list");
01905 
01906     if (argc==1) {
01907         if (JSVAL_IS_REGEXP(cx, argv[0])) {
01908             re = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0]));
01909         }else
01910         if (VALUE_IS_FUNCTION(cx, argv[0])) {
01911             func = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0]));
01912         }else{
01913             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01914                 JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_FUNCTION_OR_REGEX, argv[0]);
01915             goto out;
01916         }
01917     }
01918 
01919     if (!js_isDirectory(cx, file)) {
01920         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01921             JSFILEMSG_CANNOT_DO_LIST_ON_A_FILE, file->path);
01922         goto out;
01923     }
01924 
01925     dir = PR_OpenDir(file->path);
01926     if(!dir){
01927         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01928             JSFILEMSG_OP_FAILED, "open", file->path);
01929         goto out;
01930     }
01931 
01932     /* create JSArray here... */
01933     array = JS_NewArrayObject(cx, 0, NULL);
01934     len = 0;
01935 
01936     while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))!=NULL) {
01937         /* first, check if we have a regexp */
01938         if (re!=NULL) {
01939             size_t index = 0;
01940 
01941             str = JS_NewStringCopyZ(cx, entry->name);
01942             if(!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &v)){
01943                 /* don't report anything here */
01944                 goto out;
01945             }
01946             /* not matched! */
01947             if (JSVAL_IS_NULL(v)) {
01948                 continue;
01949             }
01950         }else
01951         if (func!=NULL) {
01952             str = JS_NewStringCopyZ(cx, entry->name);
01953             args[0] = STRING_TO_JSVAL(str);
01954             if(!JS_CallFunction(cx, obj, func, 1, args, &v)){
01955                 goto out;
01956             }
01957 
01958             if (v==JSVAL_FALSE) {
01959                 continue;
01960             }
01961         }
01962 
01963         filePath = js_combinePath(cx, file->path, (char*)entry->name);
01964 
01965         eachFile = js_NewFileObject(cx, filePath);
01966         JS_free(cx, filePath);
01967         if (!eachFile){
01968             JS_ReportWarning(cx, "File %s cannot be retrieved", filePath);
01969             continue;
01970         }
01971         v = OBJECT_TO_JSVAL(eachFile);
01972         JS_SetElement(cx, array, len, &v);
01973         JS_SetProperty(cx, array, entry->name, &v);
01974         len++;
01975     }
01976 
01977     if(PR_CloseDir(dir)!=PR_SUCCESS){
01978         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
01979             JSFILEMSG_OP_FAILED, "close", file->path);
01980         goto out;
01981     }
01982     *rval = OBJECT_TO_JSVAL(array);
01983     return JS_TRUE;
01984 out:
01985     *rval = JSVAL_NULL;
01986     return JS_FALSE;
01987 }
01988 
01989 static JSBool
01990 file_mkdir(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01991 {
01992     JSFile      *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
01993 
01994     SECURITY_CHECK(cx, NULL, "mkdir", file);
01995     JSFILE_CHECK_ONE_ARG("mkdir");
01996     JSFILE_CHECK_NATIVE("mkdir");
01997 
01998     /* if the current file is not a directory, find out the directory name */
01999     if (!js_isDirectory(cx, file)) {
02000         char        *dir = js_fileDirectoryName(cx, file->path);
02001         JSObject    *dirObj = js_NewFileObject(cx, dir);
02002 
02003         JS_free(cx, dir);
02004 
02005         /* call file_mkdir with the right set of parameters if needed */
02006         if (file_mkdir(cx, dirObj, argc, argv, rval))
02007                      return JS_TRUE;
02008               else
02009             goto out;
02010     }else{
02011         char *dirName = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
02012         char *fullName;
02013 
02014         fullName = js_combinePath(cx, file->path, dirName);
02015         if (PR_MkDir(fullName, 0755)==PR_SUCCESS){
02016             *rval = JSVAL_TRUE;
02017             JS_free(cx, fullName);
02018             return JS_TRUE;
02019         }else{
02020             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
02021                 JSFILEMSG_OP_FAILED, "mkdir", fullName);
02022             JS_free(cx, fullName);
02023             goto out;
02024         }
02025     }
02026 out:
02027     *rval = JSVAL_FALSE;
02028     return JS_FALSE;
02029 }
02030 
02031 static JSBool
02032 file_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval*rval)
02033 {
02034     JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
02035     JSString *str;
02036 
02037     str = JS_NewStringCopyZ(cx, file->path);
02038     if (!str)
02039         return JS_FALSE;
02040     *rval = STRING_TO_JSVAL(str);
02041     return JS_TRUE;
02042 }
02043 
02044 static JSBool
02045 file_toURL(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
02046 {
02047     JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
02048     char url[MAX_PATH_LENGTH];
02049     jschar *urlChars;
02050     size_t len;
02051     JSString *str;
02052 
02053     JSFILE_CHECK_NATIVE("toURL");
02054 
02055     sprintf(url, "file://%s", file->path);
02056 
02057     len = strlen(url);
02058     urlChars = js_InflateString(cx, url, &len);
02059     if (!urlChars)
02060         return JS_FALSE;
02061     str = js_NewString(cx, urlChars, len, 0);
02062     if (!str) {
02063         JS_free(cx, urlChars);
02064         return JS_FALSE;
02065     }
02066     *rval = STRING_TO_JSVAL(str);
02067 
02068     /* TODO: js_escape in jsstr.h may go away at some point */
02069     return js_str_escape(cx, obj, 0, rval, rval);
02070 
02071 out:
02072     *rval = JSVAL_VOID;
02073     return JS_FALSE;
02074 }
02075 
02076 
02077 static void
02078 file_finalize(JSContext *cx, JSObject *obj)
02079 {
02080     JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
02081 
02082     if(file) {
02083         /* Close the file before exiting. */
02084         if(file->isOpen && !file->isNative) {
02085             jsval vp;
02086             file_close(cx, obj, 0, NULL, &vp);
02087         }
02088 
02089         if (file->path)
02090             JS_free(cx, file->path);
02091 
02092         JS_free(cx, file);
02093     }
02094 }
02095 
02096 /*
02097     Allocates memory for the file object, sets fields to defaults.
02098 */
02099 static JSFile*
02100 file_init(JSContext *cx, JSObject *obj, char *bytes)
02101 {
02102     JSFile *file;
02103 
02104     file = JS_malloc(cx, sizeof *file);
02105     if (!file)
02106         return NULL;
02107     memset(file, 0 , sizeof *file);
02108 
02109     js_ResetAttributes(file);
02110 
02111     file->path = RESOLVE_PATH(cx, bytes);
02112 
02113     if (!JS_SetPrivate(cx, obj, file)) {
02114         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
02115                              JSFILEMSG_CANNOT_SET_PRIVATE_FILE, file->path);
02116         JS_free(cx, file);
02117         return NULL;
02118     }
02119 
02120     return file;
02121 }
02122 
02123 /* Returns a JSObject. This function is globally visible */
02124 JS_PUBLIC_API(JSObject*)
02125 js_NewFileObject(JSContext *cx, char *filename)
02126 {
02127     JSObject    *obj;
02128     JSFile      *file;
02129 
02130     obj = JS_NewObject(cx, &js_FileClass, NULL, NULL);
02131     if (!obj){
02132         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
02133             JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObject");
02134         return NULL;
02135     }
02136     file = file_init(cx, obj, filename);
02137     if(!file) return NULL;
02138     return obj;
02139 }
02140 
02141 /* Internal function, used for cases which NSPR file support doesn't cover */
02142 JSObject*
02143 js_NewFileObjectFromFILE(JSContext *cx, FILE *nativehandle, char *filename,
02144     int32 mode, JSBool open, JSBool randomAccess)
02145 {
02146     JSObject *obj;
02147     JSFile   *file;
02148 
02149     obj = JS_NewObject(cx, &js_FileClass, NULL, NULL);
02150     if (!obj){
02151         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
02152             JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObjectFromFILE");
02153         return NULL;
02154     }
02155     file = file_init(cx, obj, filename);
02156     if(!file) return NULL;
02157 
02158     file->nativehandle = nativehandle;
02159 
02160     /* free result of RESOLVE_PATH from file_init. */
02161     JS_ASSERT(file->path != NULL);
02162     JS_free(cx, file->path);
02163 
02164     file->path = strdup(filename);
02165     file->isOpen = open;
02166     file->mode = mode;
02167     file->hasRandomAccess = randomAccess;
02168     file->isNative = JS_TRUE;
02169     return obj;
02170 }
02171 
02172 /*
02173     Real file constructor that is called from JavaScript.
02174     Basically, does error processing and calls file_init.
02175 */
02176 static JSBool
02177 file_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
02178                  jsval *rval)
02179 {
02180     JSString *str;
02181     JSFile   *file;
02182 
02183     if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
02184         /* Replace obj with a new File object. */
02185         obj = JS_NewObject(cx, &js_FileClass, NULL, NULL);
02186         if (!obj)
02187             return JS_FALSE;
02188         *rval = OBJECT_TO_JSVAL(obj);
02189     }
02190 
02191     str = (argc == 0) 
02192           ? JS_InternString(cx, "")
02193           : JS_ValueToString(cx, argv[0]);
02194 
02195     if (!str) {
02196         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
02197                          JSFILEMSG_FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR,
02198                              argv[0]);
02199         return JS_FALSE;
02200     }
02201 
02202     file = file_init(cx, obj, JS_GetStringBytes(str));
02203     if (!file)
02204         return JS_FALSE;
02205 
02206     SECURITY_CHECK(cx, NULL, "constructor", file);
02207 
02208     return JS_TRUE;
02209 }
02210 
02211 /* -------------------- File methods and properties ------------------------- */
02212 static JSFunctionSpec file_functions[] = {
02213     { "open",           file_open, 0},
02214     { "close",          file_close, 0},
02215     { "remove",         file_remove, 0},
02216     { "copyTo",         file_copyTo, 0},
02217     { "renameTo",       file_renameTo, 0},
02218     { "flush",          file_flush, 0},
02219     { "seek",           file_seek, 0},
02220     { "read",           file_read, 0},
02221     { "readln",         file_readln, 0},
02222     { "readAll",        file_readAll, 0},
02223     { "write",          file_write, 0},
02224     { "writeln",        file_writeln, 0},
02225     { "writeAll",       file_writeAll, 0},
02226     { "list",           file_list, 0},
02227     { "mkdir",          file_mkdir, 0},
02228     { "toString",       file_toString, 0},
02229     { "toURL",          file_toURL, 0},
02230     {0}
02231 };
02232 
02233 enum file_tinyid {
02234     FILE_LENGTH             = -2,
02235     FILE_PARENT             = -3,
02236     FILE_PATH               = -4,
02237     FILE_NAME               = -5,
02238     FILE_ISDIR              = -6,
02239     FILE_ISFILE             = -7,
02240     FILE_EXISTS             = -8,
02241     FILE_CANREAD            = -9,
02242     FILE_CANWRITE           = -10,
02243     FILE_OPEN               = -11,
02244     FILE_TYPE               = -12,
02245     FILE_MODE               = -13,
02246     FILE_CREATED            = -14,
02247     FILE_MODIFIED           = -15,
02248     FILE_SIZE               = -16,
02249     FILE_RANDOMACCESS       = -17,
02250     FILE_POSITION           = -18,
02251     FILE_APPEND             = -19,
02252     FILE_REPLACE            = -20,
02253     FILE_AUTOFLUSH          = -21,
02254     FILE_ISNATIVE           = -22,
02255 };
02256 
02257 static JSPropertySpec file_props[] = {
02258    {"length",          FILE_LENGTH,        JSPROP_ENUMERATE | JSPROP_READONLY },
02259    {"parent",          FILE_PARENT,        JSPROP_ENUMERATE | JSPROP_READONLY },
02260    {"path",            FILE_PATH,          JSPROP_ENUMERATE | JSPROP_READONLY },
02261    {"name",            FILE_NAME,          JSPROP_ENUMERATE | JSPROP_READONLY },
02262    {"isDirectory",     FILE_ISDIR,         JSPROP_ENUMERATE | JSPROP_READONLY },
02263    {"isFile",          FILE_ISFILE,        JSPROP_ENUMERATE | JSPROP_READONLY },
02264    {"exists",          FILE_EXISTS,        JSPROP_ENUMERATE | JSPROP_READONLY },
02265    {"canRead",         FILE_CANREAD,       JSPROP_ENUMERATE | JSPROP_READONLY },
02266    {"canWrite",        FILE_CANWRITE,      JSPROP_ENUMERATE | JSPROP_READONLY },
02267    {"canAppend",       FILE_APPEND,        JSPROP_ENUMERATE | JSPROP_READONLY },
02268    {"canReplace",      FILE_REPLACE,       JSPROP_ENUMERATE | JSPROP_READONLY },
02269    {"isOpen",          FILE_OPEN,          JSPROP_ENUMERATE | JSPROP_READONLY },
02270    {"type",            FILE_TYPE,          JSPROP_ENUMERATE | JSPROP_READONLY },
02271    {"mode",            FILE_MODE,          JSPROP_ENUMERATE | JSPROP_READONLY },
02272    {"creationTime",    FILE_CREATED,       JSPROP_ENUMERATE | JSPROP_READONLY },
02273    {"lastModified",    FILE_MODIFIED,      JSPROP_ENUMERATE | JSPROP_READONLY },
02274    {"size",            FILE_SIZE,          JSPROP_ENUMERATE | JSPROP_READONLY },
02275    {"hasRandomAccess", FILE_RANDOMACCESS,  JSPROP_ENUMERATE | JSPROP_READONLY },
02276    {"hasAutoFlush",    FILE_AUTOFLUSH,     JSPROP_ENUMERATE | JSPROP_READONLY },
02277    {"position",        FILE_POSITION,      JSPROP_ENUMERATE },
02278    {"isNative",        FILE_ISNATIVE,      JSPROP_ENUMERATE | JSPROP_READONLY },
02279    {0}
02280 };
02281 
02282 /* ------------------------- Property getter/setter ------------------------- */
02283 static JSBool
02284 file_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
02285 {
02286     JSFile      *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
02287     char        *bytes;
02288     JSString    *str;
02289     jsint       tiny;
02290     PRFileInfo  info;
02291     JSBool      flag;
02292     PRExplodedTime expandedTime;
02293 
02294     tiny = JSVAL_TO_INT(id);
02295     if (!file)
02296         return JS_TRUE;
02297 
02298     switch (tiny) {
02299     case FILE_PARENT:
02300         SECURITY_CHECK(cx, NULL, "parent", file);
02301         if (!js_parent(cx, file, vp))
02302             return JS_FALSE;
02303         break;
02304     case FILE_PATH:
02305         str = JS_NewStringCopyZ(cx, file->path);
02306         if (!str)
02307             return JS_FALSE;
02308         *vp = STRING_TO_JSVAL(str);
02309         break;
02310     case FILE_NAME:
02311         if (!js_name(cx, file, vp))
02312             return JS_FALSE;
02313         break;
02314     case FILE_ISDIR:
02315         SECURITY_CHECK(cx, NULL, "isDirectory", file);
02316         *vp = BOOLEAN_TO_JSVAL(js_isDirectory(cx, file));
02317         break;
02318     case FILE_ISFILE:
02319         SECURITY_CHECK(cx, NULL, "isFile", file);
02320         *vp = BOOLEAN_TO_JSVAL(js_isFile(cx, file));
02321         break;
02322     case FILE_EXISTS:
02323         SECURITY_CHECK(cx, NULL, "exists", file);
02324         *vp = BOOLEAN_TO_JSVAL(js_exists(cx, file));
02325         break;
02326     case FILE_ISNATIVE:
02327         SECURITY_CHECK(cx, NULL, "isNative", file);
02328         *vp = BOOLEAN_TO_JSVAL(file->isNative);
02329         break;
02330     case FILE_CANREAD:
02331         SECURITY_CHECK(cx, NULL, "canRead", file);
02332         *vp = BOOLEAN_TO_JSVAL(js_canRead(cx, file));
02333         break;
02334     case FILE_CANWRITE:
02335         SECURITY_CHECK(cx, NULL, "canWrite", file);
02336         *vp = BOOLEAN_TO_JSVAL(js_canWrite(cx, file));
02337         break;
02338     case FILE_OPEN:
02339         SECURITY_CHECK(cx, NULL, "isOpen", file);
02340         *vp = BOOLEAN_TO_JSVAL(file->isOpen);
02341         break;
02342     case FILE_APPEND :
02343         SECURITY_CHECK(cx, NULL, "canAppend", file);
02344         JSFILE_CHECK_OPEN("canAppend");
02345         *vp = BOOLEAN_TO_JSVAL(!file->isNative &&
02346                 (file->mode&PR_APPEND)==PR_APPEND);
02347         break;
02348     case FILE_REPLACE :
02349         SECURITY_CHECK(cx, NULL, "canReplace", file);
02350         JSFILE_CHECK_OPEN("canReplace");
02351         *vp = BOOLEAN_TO_JSVAL(!file->isNative &&
02352                 (file->mode&PR_TRUNCATE)==PR_TRUNCATE);
02353         break;
02354     case FILE_AUTOFLUSH :
02355         SECURITY_CHECK(cx, NULL, "hasAutoFlush", file);
02356         JSFILE_CHECK_OPEN("hasAutoFlush");
02357         *vp = BOOLEAN_TO_JSVAL(!file->isNative && file->hasAutoflush);
02358         break;
02359     case FILE_TYPE:
02360         SECURITY_CHECK(cx, NULL, "type", file);
02361         JSFILE_CHECK_OPEN("type");
02362         if(js_isDirectory(cx, file)){
02363             *vp = JSVAL_VOID;
02364             break;
02365         }
02366 
02367         switch (file->type) {
02368         case ASCII:
02369             *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, asciistring));
02370             break;
02371         case UTF8:
02372             *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, utfstring));
02373             break;
02374         case UCS2:
02375             *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, unicodestring));
02376             break;
02377         default:
02378             JS_ReportWarning(cx, "Unsupported file type %d, proceeding",
02379                 file->type);
02380         }
02381         break;
02382     case FILE_MODE:
02383         SECURITY_CHECK(cx, NULL, "mode", file);
02384         JSFILE_CHECK_OPEN("mode");
02385         bytes = JS_malloc(cx, MODE_SIZE);
02386         bytes[0] = '\0';
02387         flag = JS_FALSE;
02388 
02389         if ((file->mode&PR_RDONLY)==PR_RDONLY) {
02390             if (flag) strcat(bytes, ",");
02391             strcat(bytes, "read");
02392             flag = JS_TRUE;
02393         }
02394         if ((file->mode&PR_WRONLY)==PR_WRONLY) {
02395             if (flag) strcat(bytes, ",");
02396             strcat(bytes, "write");
02397             flag = JS_TRUE;
02398         }
02399         if ((file->mode&PR_RDWR)==PR_RDWR) {
02400             if (flag) strcat(bytes, ",");
02401             strcat(bytes, "readWrite");
02402             flag = JS_TRUE;
02403         }
02404         if ((file->mode&PR_APPEND)==PR_APPEND) {
02405             if (flag) strcat(bytes, ",");
02406             strcat(bytes, "append");
02407             flag = JS_TRUE;
02408         }
02409         if ((file->mode&PR_CREATE_FILE)==PR_CREATE_FILE) {
02410             if (flag) strcat(bytes, ",");
02411             strcat(bytes, "create");
02412             flag = JS_TRUE;
02413         }
02414         if ((file->mode&PR_TRUNCATE)==PR_TRUNCATE) {
02415             if (flag) strcat(bytes, ",");
02416             strcat(bytes, "replace");
02417             flag = JS_TRUE;
02418         }
02419         if (file->hasAutoflush) {
02420             if (flag) strcat(bytes, ",");
02421             strcat(bytes, "hasAutoFlush");
02422             flag = JS_TRUE;
02423         }
02424         *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, bytes));
02425         JS_free(cx, bytes);
02426         break;
02427     case FILE_CREATED:
02428         SECURITY_CHECK(cx, NULL, "creationTime", file);
02429         JSFILE_CHECK_NATIVE("creationTime");
02430         if(((file->isOpen)?
02431                         PR_GetOpenFileInfo(file->handle, &info):
02432                         PR_GetFileInfo(file->path, &info))!=PR_SUCCESS){
02433             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
02434                 JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
02435             goto out;
02436         }
02437 
02438         PR_ExplodeTime(info.creationTime, PR_LocalTimeParameters,&expandedTime);
02439         *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx,  expandedTime.tm_year,
02440                                     expandedTime.tm_month,
02441                                     expandedTime.tm_mday,
02442                                     expandedTime.tm_hour,
02443                                     expandedTime.tm_min,
02444                                     expandedTime.tm_sec));
02445         break;
02446     case FILE_MODIFIED:
02447         SECURITY_CHECK(cx, NULL, "lastModified", file);
02448         JSFILE_CHECK_NATIVE("lastModified");
02449         if(((file->isOpen)?
02450                         PR_GetOpenFileInfo(file->handle, &info):
02451                         PR_GetFileInfo(file->path, &info))!=PR_SUCCESS){
02452             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
02453                 JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
02454             goto out;
02455         }
02456 
02457         PR_ExplodeTime(info.modifyTime, PR_LocalTimeParameters, &expandedTime);
02458         *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx, expandedTime.tm_year,
02459                                     expandedTime.tm_month,
02460                                     expandedTime.tm_mday,
02461                                     expandedTime.tm_hour,
02462                                     expandedTime.tm_min,
02463                                     expandedTime.tm_sec));
02464         break;
02465     case FILE_SIZE:
02466         SECURITY_CHECK(cx, NULL, "size", file);
02467         *vp = js_size(cx, file);
02468         break;
02469     case FILE_LENGTH:
02470         SECURITY_CHECK(cx, NULL, "length", file);
02471         JSFILE_CHECK_NATIVE("length");
02472 
02473         if (js_isDirectory(cx, file)) { /* XXX debug me */
02474             PRDir       *dir;
02475             PRDirEntry  *entry;
02476             jsint       count = 0;
02477 
02478             if(!(dir = PR_OpenDir(file->path))){
02479                 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
02480                     JSFILEMSG_CANNOT_OPEN_DIR, file->path);
02481                 goto out;
02482             }
02483 
02484             while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))) {
02485                 count++;
02486             }
02487 
02488             if(!PR_CloseDir(dir)){
02489                 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
02490                     JSFILEMSG_OP_FAILED, "close", file->path);
02491 
02492                 goto out;
02493             }
02494 
02495             *vp = INT_TO_JSVAL(count);
02496             break;
02497         }else{
02498             /* return file size */
02499             *vp = js_size(cx, file);
02500         }
02501         break;
02502     case FILE_RANDOMACCESS:
02503             SECURITY_CHECK(cx, NULL, "hasRandomAccess", file);
02504             JSFILE_CHECK_OPEN("hasRandomAccess");
02505             *vp = BOOLEAN_TO_JSVAL(file->hasRandomAccess);
02506         break;
02507     case FILE_POSITION:
02508         SECURITY_CHECK(cx, NULL, "position", file);
02509         JSFILE_CHECK_NATIVE("position");
02510         JSFILE_CHECK_OPEN("position");
02511 
02512         if(!file->hasRandomAccess){
02513             JS_ReportWarning(cx, "File %s doesn't support random access, can't report the position, proceeding");
02514             *vp = JSVAL_VOID;
02515             break;
02516         }
02517 
02518         if (file->isOpen && js_isFile(cx, file)) {
02519             int pos = PR_Seek(file->handle, 0, PR_SEEK_CUR);
02520             if(pos!=-1){
02521                 *vp = INT_TO_JSVAL(pos);
02522             }else{
02523                 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
02524                     JSFILEMSG_CANNOT_REPORT_POSITION, file->path);
02525                 goto out;
02526             }
02527         }else {
02528             JS_ReportWarning(cx, "File %s is closed or not a plain file,"
02529                 " can't report position, proceeding");
02530             goto out;
02531         }
02532         break;
02533     default:
02534         SECURITY_CHECK(cx, NULL, "file_access", file);
02535 
02536         /* this is some other property -- try to use the dir["file"] syntax */
02537         if (js_isDirectory(cx, file)) {
02538             PRDir *dir = NULL;
02539             PRDirEntry *entry = NULL;
02540             char *prop_name;
02541 
02542             str = JS_ValueToString(cx, id);
02543             if (!str)
02544                 return JS_FALSE;
02545 
02546             prop_name = JS_GetStringBytes(str);
02547 
02548             /* no native files past this point */
02549             dir = PR_OpenDir(file->path);
02550             if(!dir) {
02551                 /* This is probably not a directory */
02552                 JS_ReportWarning(cx, "Can't open directory %s", file->path);
02553                 return JS_FALSE;
02554             }
02555 
02556             while ((entry = PR_ReadDir(dir, PR_SKIP_NONE)) != NULL) {
02557                 if (!strcmp(entry->name, prop_name)){
02558                     bytes = js_combinePath(cx, file->path, prop_name);
02559                     *vp = OBJECT_TO_JSVAL(js_NewFileObject(cx, bytes));
02560                     PR_CloseDir(dir);
02561                     JS_free(cx, bytes);
02562                     return !JSVAL_IS_NULL(*vp);
02563                 }
02564             }
02565             PR_CloseDir(dir);
02566         }
02567     }
02568     return JS_TRUE;
02569 
02570 out:
02571     return JS_FALSE;
02572 }
02573 
02574 static JSBool
02575 file_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
02576 {
02577     JSFile  *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
02578     jsint   slot;
02579 
02580     if (JSVAL_IS_STRING(id)){
02581         return JS_TRUE;
02582     }
02583 
02584     slot = JSVAL_TO_INT(id);
02585 
02586     switch (slot) {
02587     /* File.position  = 10 */
02588     case FILE_POSITION:
02589         SECURITY_CHECK(cx, NULL, "set_position", file);
02590         JSFILE_CHECK_NATIVE("set_position");
02591 
02592         if(!file->hasRandomAccess){
02593             JS_ReportWarning(cx, "File %s doesn't support random access, can't "
02594                 "report the position, proceeding");
02595             goto out;
02596         }
02597 
02598         if (file->isOpen && js_isFile(cx, file)) {
02599             int32 pos;
02600                   int32 offset;
02601 
02602                      if (!JS_ValueToInt32(cx, *vp, &offset)){
02603                             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
02604                                    JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "position", *vp);
02605                             goto out;
02606                      }
02607 
02608                      pos = PR_Seek(file->handle, offset, PR_SEEK_SET);
02609 
02610             if(pos!=-1){
02611                 *vp = INT_TO_JSVAL(pos);
02612             }else{
02613                 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
02614                     JSFILEMSG_CANNOT_SET_POSITION, file->path);
02615                 goto out;
02616             }
02617         } else {
02618             JS_ReportWarning(cx, "File %s is closed or not a file, can't set "
02619                 "position, proceeding", file->path);
02620             goto out;
02621         }
02622     }
02623 
02624     return JS_TRUE;
02625 out:
02626     return JS_FALSE;
02627 }
02628 
02629 /*
02630     File.currentDir = new File("D:\") or File.currentDir = "D:\"
02631 */
02632 static JSBool
02633 file_currentDirSetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
02634 {
02635     JSFile   *file;
02636 
02637     file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
02638 
02639     /* Look at the rhs and extract a file object from it */
02640     if (JSVAL_IS_OBJECT(*vp)) {
02641         if (JS_InstanceOf(cx, obj, &js_FileClass, NULL)) {
02642             /* Braindamaged rhs -- just return the old value */
02643             if (file && (!js_exists(cx, file) || !js_isDirectory(cx, file))) {
02644                 JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp);
02645                 return JS_FALSE;
02646             } else {
02647                 chdir(file->path);
02648                 return JS_TRUE;
02649             }
02650         } else {
02651             return JS_FALSE;
02652         }
02653     } else {
02654         JSObject *rhsObject;
02655         char     *path;
02656 
02657         path      = JS_GetStringBytes(JS_ValueToString(cx, *vp));
02658         rhsObject = js_NewFileObject(cx, path);
02659         if (!rhsObject)
02660             return JS_FALSE;
02661 
02662         if (!file || !js_exists(cx, file) || !js_isDirectory(cx, file)){
02663             JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp);
02664         } else {
02665             *vp = OBJECT_TO_JSVAL(rhsObject);
02666             chdir(path);
02667         }
02668     }
02669 
02670     return JS_TRUE;
02671 }
02672 
02673 /* Declare class */
02674 JSClass js_FileClass = {
02675     "File", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_File),
02676     JS_PropertyStub,  JS_PropertyStub,  file_getProperty,  file_setProperty,
02677     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,    file_finalize
02678 };
02679 
02680 /* -------------------- Functions exposed to the outside -------------------- */
02681 JS_PUBLIC_API(JSObject*)
02682 js_InitFileClass(JSContext *cx, JSObject* obj)
02683 {
02684     JSObject *file, *ctor, *afile;
02685     jsval    vp;
02686     char     *currentdir;
02687     char     separator[2];
02688 
02689     file = JS_InitClass(cx, obj, NULL, &js_FileClass, file_constructor, 1,
02690         file_props, file_functions, NULL, NULL);
02691     if (!file) {
02692         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
02693             JSFILEMSG_INIT_FAILED);
02694         return NULL;
02695     }
02696 
02697     ctor = JS_GetConstructor(cx, file);
02698     if (!ctor)  return NULL;
02699 
02700        /* Define CURRENTDIR property. We are doing this to get a
02701        slash at the end of the current dir */
02702     afile = js_NewFileObject(cx, CURRENT_DIR);
02703     currentdir =  JS_malloc(cx, MAX_PATH_LENGTH);
02704     currentdir =  getcwd(currentdir, MAX_PATH_LENGTH);
02705     afile = js_NewFileObject(cx, currentdir);
02706     JS_free(cx, currentdir);
02707     vp = OBJECT_TO_JSVAL(afile);
02708     JS_DefinePropertyWithTinyId(cx, ctor, CURRENTDIR_PROPERTY, 0, vp,
02709                 JS_PropertyStub, file_currentDirSetter,
02710                 JSPROP_ENUMERATE | JSPROP_READONLY );
02711 
02712     /* Define input */
02713     vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stdin,
02714             STDINPUT_NAME, PR_RDONLY, JS_TRUE, JS_FALSE));
02715     JS_SetProperty(cx, ctor, "input", &vp);
02716 
02717     /* Define output */
02718     vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stdout,
02719             STDOUTPUT_NAME, PR_WRONLY, JS_TRUE, JS_FALSE));
02720     JS_SetProperty(cx, ctor, "output", &vp);
02721 
02722     /* Define error */
02723     vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stderr,
02724             STDERROR_NAME, PR_WRONLY, JS_TRUE, JS_FALSE));
02725     JS_SetProperty(cx, ctor, "error", &vp);
02726 
02727     separator[0] = FILESEPARATOR;
02728     separator[1] = '\0';
02729     vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, separator));
02730     JS_DefinePropertyWithTinyId(cx, ctor, SEPARATOR_PROPERTY, 0, vp,
02731                 JS_PropertyStub, JS_PropertyStub,
02732                 JSPROP_ENUMERATE | JSPROP_READONLY );
02733     return file;
02734 }
02735 #endif /* JS_HAS_FILE_OBJECT */