Code

fix 1243587 and misc fixes
[inkscape.git] / src / dom / js / jsfile.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sw=4 et tw=80:
3  *
4  * ***** BEGIN LICENSE BLOCK *****
5  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is Mozilla Communicator client code, released
18  * March 31, 1998.
19  *
20  * The Initial Developer of the Original Code is
21  * Netscape Communications Corporation.
22  * Portions created by the Initial Developer are Copyright (C) 1998
23  * the Initial Developer. All Rights Reserved.
24  *
25  * Contributor(s):
26  *
27  * Alternatively, the contents of this file may be used under the terms of
28  * either of the GNU General Public License Version 2 or later (the "GPL"),
29  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30  * in which case the provisions of the GPL or the LGPL are applicable instead
31  * of those above. If you wish to allow use of your version of this file only
32  * under the terms of either the GPL or the LGPL, and not to allow others to
33  * use your version of this file under the terms of the MPL, indicate your
34  * decision by deleting the provisions above and replace them with the notice
35  * and other provisions required by the GPL or the LGPL. If you do not delete
36  * the provisions above, a recipient may use your version of this file under
37  * the terms of any one of the MPL, the GPL or the LGPL.
38  *
39  * ***** END LICENSE BLOCK ***** */
41 /*
42  * JS File object
43  */
44 #if JS_HAS_FILE_OBJECT
46 #include "jsstddef.h"
48 /* ----------------- Platform-specific includes and defines ----------------- */
49 #if defined(XP_WIN) || defined(XP_OS2)
50 #   include <direct.h>
51 #   include <io.h>
52 #   include <sys/types.h>
53 #   include <sys/stat.h>
54 #   define FILESEPARATOR        '\\'
55 #   define FILESEPARATOR2       '/'
56 #   define CURRENT_DIR          "c:\\"
57 #   define POPEN                _popen
58 #   define PCLOSE               _pclose
59 #elif defined(XP_UNIX) || defined(XP_BEOS)
60 #   include <strings.h>
61 #   include <stdio.h>
62 #   include <stdlib.h>
63 #   include <unistd.h>
64 #   define FILESEPARATOR        '/'
65 #   define FILESEPARATOR2       '\0'
66 #   define CURRENT_DIR          "/"
67 #   define POPEN                popen
68 #   define PCLOSE               pclose
69 #endif
71 /* --------------- Platform-independent includes and defines ---------------- */
72 #include "jsapi.h"
73 #include "jsatom.h"
74 #include "jscntxt.h"
75 #include "jsdate.h"
76 #include "jsdbgapi.h"
77 #include "jsemit.h"
78 #include "jsfun.h"
79 #include "jslock.h"
80 #include "jsobj.h"
81 #include "jsparse.h"
82 #include "jsscan.h"
83 #include "jsscope.h"
84 #include "jsscript.h"
85 #include "jsstr.h"
86 #include "jsutil.h" /* Added by JSIFY */
87 #include <string.h>
89 /* NSPR dependencies */
90 #include "prio.h"
91 #include "prerror.h"
93 #define SPECIAL_FILE_STRING     "Special File"
94 #define CURRENTDIR_PROPERTY     "currentDir"
95 #define SEPARATOR_PROPERTY      "separator"
96 #define FILE_CONSTRUCTOR        "File"
97 #define PIPE_SYMBOL             '|'
99 #define ASCII                   0
100 #define UTF8                    1
101 #define UCS2                    2
103 #define asciistring             "text"
104 #define utfstring               "binary"
105 #define unicodestring           "unicode"
107 #define MAX_PATH_LENGTH         1024
108 #define MODE_SIZE               256
109 #define NUMBER_SIZE             32
110 #define MAX_LINE_LENGTH         256
111 #define URL_PREFIX              "file://"
113 #define STDINPUT_NAME           "Standard input stream"
114 #define STDOUTPUT_NAME          "Standard output stream"
115 #define STDERROR_NAME           "Standard error stream"
117 #define RESOLVE_PATH            js_canonicalPath        /* js_absolutePath */
119 /* Error handling */
120 typedef enum JSFileErrNum {
121 #define MSG_DEF(name, number, count, exception, format) \
122     name = number,
123 #include "jsfile.msg"
124 #undef MSG_DEF
125     JSFileErr_Limit
126 #undef MSGDEF
127 } JSFileErrNum;
129 #define JSFILE_HAS_DFLT_MSG_STRINGS 1
131 JSErrorFormatString JSFile_ErrorFormatString[JSFileErr_Limit] = {
132 #if JSFILE_HAS_DFLT_MSG_STRINGS
133 #define MSG_DEF(name, number, count, exception, format) \
134     { format, count },
135 #else
136 #define MSG_DEF(name, number, count, exception, format) \
137     { NULL, count },
138 #endif
139 #include "jsfile.msg"
140 #undef MSG_DEF
141 };
143 const JSErrorFormatString *
144 JSFile_GetErrorMessage(void *userRef, const char *locale,
145                                                         const uintN errorNumber)
147     if ((errorNumber > 0) && (errorNumber < JSFileErr_Limit))
148         return &JSFile_ErrorFormatString[errorNumber];
149     else
150         return NULL;
153 #define JSFILE_CHECK_NATIVE(op)                                               \
154     if (file->isNative) {                                                     \
155         JS_ReportWarning(cx, "Cannot call or access \"%s\" on native file %s",\
156                          op, file->path);                                     \
157         goto out;                                                             \
158     }
160 #define JSFILE_CHECK_WRITE                                                    \
161     if (!file->isOpen) {                                                      \
162         JS_ReportWarning(cx,                                                  \
163                 "File %s is closed, will open it for writing, proceeding",    \
164                 file->path);                                                  \
165         js_FileOpen(cx, obj, file, "write,append,create");                    \
166     }                                                                         \
167     if (!js_canWrite(cx, file)) {                                             \
168         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,                \
169                              JSFILEMSG_CANNOT_WRITE, file->path);             \
170         goto out;                                                             \
171     }
173 #define JSFILE_CHECK_READ                                                     \
174     if (!file->isOpen) {                                                      \
175         JS_ReportWarning(cx,                                                  \
176                 "File %s is closed, will open it for reading, proceeding",    \
177                 file->path);                                                  \
178         js_FileOpen(cx, obj, file, "read");                                   \
179     }                                                                         \
180     if (!js_canRead(cx, file)) {                                              \
181         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,                \
182                              JSFILEMSG_CANNOT_READ, file->path);              \
183         goto out;                                                             \
184     }
186 #define JSFILE_CHECK_OPEN(op)                                                 \
187     if (!file->isOpen) {                                                      \
188         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,                \
189                              JSFILEMSG_FILE_MUST_BE_CLOSED, op);              \
190         goto out;                                                             \
191     }
193 #define JSFILE_CHECK_CLOSED(op)                                               \
194     if (file->isOpen) {                                                       \
195         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,                \
196             JSFILEMSG_FILE_MUST_BE_OPEN, op);                                 \
197         goto out;                                                             \
198     }
200 #define JSFILE_CHECK_ONE_ARG(op)                                              \
201     if (argc != 1) {                                                          \
202         char str[NUMBER_SIZE];                                                \
203         sprintf(str, "%d", argc);                                             \
204         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,                \
205                              JSFILEMSG_EXPECTS_ONE_ARG_ERROR, op, str);       \
206         goto out;                                                             \
207     }
210 /*
211     Security mechanism, should define a callback for this.
212     The parameters are as follows:
213     SECURITY_CHECK(JSContext *cx, JSPrincipals *ps, char *op_name, JSFile *file)
214     XXX Should this be a real function returning a JSBool result (and getting
215     some typesafety help from the compiler?).
216 */
217 #define SECURITY_CHECK(cx, ps, op, file)    \
218         /* Define a callback here... */
221 /* Structure representing the file internally */
222 typedef struct JSFile {
223     char        *path;          /* the path to the file. */
224     JSBool      isOpen;
225     int32       mode;           /* mode used to open the file: read, write, append, create, etc.. */
226     int32       type;           /* Asciiz, utf, unicode */
227     char        byteBuffer[3];  /* bytes read in advance by js_FileRead ( UTF8 encoding ) */
228     jsint       nbBytesInBuf;   /* number of bytes stored in the buffer above */
229     jschar      charBuffer;     /* character read in advance by readln ( mac files only ) */
230     JSBool      charBufferUsed; /* flag indicating if the buffer above is being used */
231     JSBool      hasRandomAccess;/* can the file be randomly accessed? false for stdin, and
232                                  UTF-encoded files. */
233     JSBool      hasAutoflush;   /* should we force a flush for each line break? */
234     JSBool      isNative;       /* if the file is using OS-specific file FILE type */
235     /* We can actually put the following two in a union since they should never be used at the same time */
236     PRFileDesc  *handle;        /* the handle for the file, if open.  */
237     FILE        *nativehandle;  /* native handle, for stuff NSPR doesn't do. */
238     JSBool      isPipe;         /* if the file is really an OS pipe */
239 } JSFile;
241 /* a few forward declarations... */
242 static JSClass file_class;
243 JS_PUBLIC_API(JSObject*) js_NewFileObject(JSContext *cx, char *filename);
244 static JSBool file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
245 static JSBool file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
247 /* New filename manipulation procesures */
248 /* assumes we don't have leading/trailing spaces */
249 static JSBool
250 js_filenameHasAPipe(const char *filename)
252     if (!filename)
253         return JS_FALSE;
255     return  filename[0] == PIPE_SYMBOL ||
256             filename[strlen(filename) - 1] == PIPE_SYMBOL;
259 static JSBool
260 js_isAbsolute(const char *name)
262 #if defined(XP_WIN) || defined(XP_OS2)
263     return *name && name[1] == ':';
264 #else
265     return (name[0]
266 #   if defined(XP_UNIX) || defined(XP_BEOS)
267             ==
268 #   else
269             !=
270 #   endif
271             FILESEPARATOR);
272 #endif
275 /*
276  * Concatinates base and name to produce a valid filename.
277  * Returned string must be freed.
278 */
279 static char*
280 js_combinePath(JSContext *cx, const char *base, const char *name)
282     int len = strlen(base);
283     char* result = JS_malloc(cx, len + strlen(name) + 2);
285     if (!result)
286         return NULL;
288     strcpy(result, base);
290     if (base[len - 1] != FILESEPARATOR && base[len - 1] != FILESEPARATOR2) {
291         result[len] = FILESEPARATOR;
292         result[len + 1] = '\0';
293     }
294     strcat(result, name);
295     return result;
298 /* Extract the last component from a path name. Returned string must be freed */
299 static char *
300 js_fileBaseName(JSContext *cx, const char *pathname)
302     jsint index, aux;
303     char *result;
305     index = strlen(pathname)-1;
307     /* Chop off trailing seperators. */
308     while (index > 0 && (pathname[index]==FILESEPARATOR ||
309                          pathname[index]==FILESEPARATOR2)) {
310         --index;
311     }
313     aux = index;
315     /* Now find the next separator. */
316     while (index >= 0 && pathname[index] != FILESEPARATOR &&
317                          pathname[index] != FILESEPARATOR2) {
318         --index;
319     }
321     /* Allocate and copy. */
322     result = JS_malloc(cx, aux - index + 1);
323     if (!result)
324         return NULL;
325     strncpy(result, pathname + index + 1, aux - index);
326     result[aux - index] = '\0';
327     return result;
330 /*
331  * Returns everything but the last component from a path name.
332  * Returned string must be freed.
333  */
334 static char *
335 js_fileDirectoryName(JSContext *cx, const char *pathname)
337     char *result;
338     const char *cp, *end;
339     size_t pathsize;
341     end = pathname + strlen(pathname);
342     cp = end - 1;
344     /* If this is already a directory, chop off the trailing /s. */
345     while (cp >= pathname) {
346         if (*cp != FILESEPARATOR && *cp != FILESEPARATOR2)
347             break;
348         --cp;
349     }
351     if (cp < pathname && end != pathname) {
352         /* There were just /s, return the root. */
353         result = JS_malloc(cx, 1 + 1); /* The separator + trailing NUL. */
354         result[0] = FILESEPARATOR;
355         result[1] = '\0';
356         return result;
357     }
359     /* Now chop off the last portion. */
360     while (cp >= pathname) {
361         if (*cp == FILESEPARATOR || *cp == FILESEPARATOR2)
362             break;
363         --cp;
364     }
366     /* Check if this is a leaf. */
367     if (cp < pathname) {
368         /* It is, return "pathname/". */
369         if (end[-1] == FILESEPARATOR || end[-1] == FILESEPARATOR2) {
370             /* Already has its terminating /. */
371             return JS_strdup(cx, pathname);
372         }
374         pathsize = end - pathname + 1;
375         result = JS_malloc(cx, pathsize + 1);
376         if (!result)
377             return NULL;
379         strcpy(result, pathname);
380         result[pathsize - 1] = FILESEPARATOR;
381         result[pathsize] = '\0';
383         return result;
384     }
386     /* Return everything up to and including the seperator. */
387     pathsize = cp - pathname + 1;
388     result = JS_malloc(cx, pathsize + 1);
389     if (!result)
390         return NULL;
392     strncpy(result, pathname, pathsize);
393     result[pathsize] = '\0';
395     return result;
398 static char *
399 js_absolutePath(JSContext *cx, const char * path)
401     JSObject *obj;
402     JSString *str;
403     jsval prop;
405     if (js_isAbsolute(path)) {
406         return JS_strdup(cx, path);
407     } else {
408         obj = JS_GetGlobalObject(cx);
409         if (!JS_GetProperty(cx, obj, FILE_CONSTRUCTOR, &prop)) {
410             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
411                                  JSFILEMSG_FILE_CONSTRUCTOR_UNDEFINED_ERROR);
412             return JS_strdup(cx, path);
413         }
415         obj = JSVAL_TO_OBJECT(prop);
416         if (!JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, &prop)) {
417             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
418                                  JSFILEMSG_FILE_CURRENTDIR_UNDEFINED_ERROR);
419             return JS_strdup(cx, path);
420         }
422         str = JS_ValueToString(cx, prop);
423         if (!str)
424             return JS_strdup(cx, path);
426         /* should we have an array of curr dirs indexed by drive for windows? */
427         return js_combinePath(cx, JS_GetStringBytes(str), path);
428     }
431 /* Side effect: will remove spaces in the beginning/end of the filename */
432 static char *
433 js_canonicalPath(JSContext *cx, char *oldpath)
435     char *tmp;
436     char *path = oldpath;
437     char *base, *dir, *current, *result;
438     jsint c;
439     jsint back = 0;
440     unsigned int i = 0, j = strlen(path)-1;
442     /* This is probably optional */
443         /* Remove possible spaces in the beginning and end */
444     while (i < j && path[i] == ' ')
445         i++;
446     while (j >= 0 && path[j] == ' ')
447         j--;
449     tmp = JS_malloc(cx, j-i+2);
450     if (!tmp)
451         return NULL;
453     strncpy(tmp, path + i, j - i + 1);
454     tmp[j - i + 1] = '\0';
456     path = tmp;
458     /* Pipe support. */
459     if (js_filenameHasAPipe(path))
460         return path;
462     /* file:// support. */
463     if (!strncmp(path, URL_PREFIX, strlen(URL_PREFIX))) {
464         tmp = js_canonicalPath(cx, path + strlen(URL_PREFIX));
465         JS_free(cx, path);
466         return tmp;
467     }
469     if (!js_isAbsolute(path)) {
470         tmp = js_absolutePath(cx, path);
471         if (!tmp)
472             return NULL;
473         path = tmp;
474     }
476     result = JS_strdup(cx, "");
478     current = path;
480     base = js_fileBaseName(cx, current);
481     dir = js_fileDirectoryName(cx, current);
483     while (strcmp(dir, current)) {
484         if (!strcmp(base, "..")) {
485             back++;
486         } else {
487             if (back > 0) {
488                 back--;
489             } else {
490                 tmp = result;
491                 result = JS_malloc(cx, strlen(base) + 1 + strlen(tmp) + 1);
492                 if (!result)
493                     goto out;
495                 strcpy(result, base);
496                 c = strlen(result);
497                 if (*tmp) {
498                     result[c] = FILESEPARATOR;
499                     result[c + 1] = '\0';
500                     strcat(result, tmp);
501                 }
502                 JS_free(cx, tmp);
503             }
504         }
505         JS_free(cx, current);
506         JS_free(cx, base);
507         current = dir;
508         base =  js_fileBaseName(cx, current);
509         dir = js_fileDirectoryName(cx, current);
510     }
512     tmp = result;
513     result = JS_malloc(cx, strlen(dir)+1+strlen(tmp)+1);
514     if (!result)
515         goto out;
517     strcpy(result, dir);
518     c = strlen(result);
519     if (tmp[0]!='\0') {
520         if ((result[c-1]!=FILESEPARATOR)&&(result[c-1]!=FILESEPARATOR2)) {
521             result[c] = FILESEPARATOR;
522             result[c+1] = '\0';
523         }
524         strcat(result, tmp);
525     }
527 out:
528     if (tmp)
529         JS_free(cx, tmp);
530     if (dir)
531         JS_free(cx, dir);
532     if (base)
533         JS_free(cx, base);
534     if (current)
535         JS_free(cx, current);
537     return result;
540 /* -------------------------- Text conversion ------------------------------- */
541 /* The following is ripped from libi18n/unicvt.c and include files.. */
543 /*
544  * UTF8 defines and macros
545  */
546 #define ONE_OCTET_BASE          0x00    /* 0xxxxxxx */
547 #define ONE_OCTET_MASK          0x7F    /* x1111111 */
548 #define CONTINUING_OCTET_BASE   0x80    /* 10xxxxxx */
549 #define CONTINUING_OCTET_MASK   0x3F    /* 00111111 */
550 #define TWO_OCTET_BASE          0xC0    /* 110xxxxx */
551 #define TWO_OCTET_MASK          0x1F    /* 00011111 */
552 #define THREE_OCTET_BASE        0xE0    /* 1110xxxx */
553 #define THREE_OCTET_MASK        0x0F    /* 00001111 */
554 #define FOUR_OCTET_BASE         0xF0    /* 11110xxx */
555 #define FOUR_OCTET_MASK         0x07    /* 00000111 */
556 #define FIVE_OCTET_BASE         0xF8    /* 111110xx */
557 #define FIVE_OCTET_MASK         0x03    /* 00000011 */
558 #define SIX_OCTET_BASE          0xFC    /* 1111110x */
559 #define SIX_OCTET_MASK          0x01    /* 00000001 */
561 #define IS_UTF8_1ST_OF_1(x) (( (x)&~ONE_OCTET_MASK  ) == ONE_OCTET_BASE)
562 #define IS_UTF8_1ST_OF_2(x) (( (x)&~TWO_OCTET_MASK  ) == TWO_OCTET_BASE)
563 #define IS_UTF8_1ST_OF_3(x) (( (x)&~THREE_OCTET_MASK) == THREE_OCTET_BASE)
564 #define IS_UTF8_1ST_OF_4(x) (( (x)&~FOUR_OCTET_MASK ) == FOUR_OCTET_BASE)
565 #define IS_UTF8_1ST_OF_5(x) (( (x)&~FIVE_OCTET_MASK ) == FIVE_OCTET_BASE)
566 #define IS_UTF8_1ST_OF_6(x) (( (x)&~SIX_OCTET_MASK  ) == SIX_OCTET_BASE)
567 #define IS_UTF8_2ND_THRU_6TH(x) \
568                     (( (x)&~CONTINUING_OCTET_MASK  ) == CONTINUING_OCTET_BASE)
569 #define IS_UTF8_1ST_OF_UCS2(x) \
570             IS_UTF8_1ST_OF_1(x) \
571             || IS_UTF8_1ST_OF_2(x) \
572             || IS_UTF8_1ST_OF_3(x)
575 #define MAX_UCS2            0xFFFF
576 #define DEFAULT_CHAR        0x003F  /* Default char is "?" */
577 #define BYTE_MASK           0xBF
578 #define BYTE_MARK           0x80
581 /* Function: one_ucs2_to_utf8_char
582  *
583  * Function takes one UCS-2 char and writes it to a UTF-8 buffer.
584  * We need a UTF-8 buffer because we don't know before this
585  * function how many bytes of utf-8 data will be written. It also
586  * takes a pointer to the end of the UTF-8 buffer so that we don't
587  * overwrite data. This function returns the number of UTF-8 bytes
588  * of data written, or -1 if the buffer would have been overrun.
589  */
591 #define LINE_SEPARATOR      0x2028
592 #define PARAGRAPH_SEPARATOR 0x2029
593 static int16 one_ucs2_to_utf8_char(unsigned char *tobufp,
594                                    unsigned char *tobufendp,
595                                    uint16 onechar)
597     int16 numUTF8bytes = 0;
599     if (onechar == LINE_SEPARATOR || onechar == PARAGRAPH_SEPARATOR) {
600         strcpy((char*)tobufp, "\n");
601         return strlen((char*)tobufp);
602     }
604     if (onechar < 0x80) {
605         numUTF8bytes = 1;
606     } else if (onechar < 0x800) {
607         numUTF8bytes = 2;
608     } else {
609         /* 0x800 >= onechar <= MAX_UCS2 */
610         numUTF8bytes = 3;
611     }
613     tobufp += numUTF8bytes;
615     /* return error if we don't have space for the whole character */
616     if (tobufp > tobufendp) {
617         return(-1);
618     }
620     switch(numUTF8bytes) {
621       case 3: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
622               *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
623               *--tobufp = onechar |  THREE_OCTET_BASE;
624               break;
626       case 2: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
627               *--tobufp = onechar | TWO_OCTET_BASE;
628               break;
630       case 1: *--tobufp = (unsigned char)onechar;
631               break;
632     }
634     return numUTF8bytes;
637 /*
638  * utf8_to_ucs2_char
639  *
640  * Convert a utf8 multibyte character to ucs2
641  *
642  * inputs: pointer to utf8 character(s)
643  *         length of utf8 buffer ("read" length limit)
644  *         pointer to return ucs2 character
645  *
646  * outputs: number of bytes in the utf8 character
647  *          -1 if not a valid utf8 character sequence
648  *          -2 if the buffer is too short
649  */
650 static int16
651 utf8_to_ucs2_char(const unsigned char *utf8p, int16 buflen, uint16 *ucs2p)
653     uint16 lead, cont1, cont2;
655     /*
656      * Check for minimum buffer length
657      */
658     if ((buflen < 1) || (utf8p == NULL)) {
659         return -2;
660     }
661     lead = (uint16) (*utf8p);
663     /*
664      * Check for a one octet sequence
665      */
666     if (IS_UTF8_1ST_OF_1(lead)) {
667         *ucs2p = lead & ONE_OCTET_MASK;
668         return 1;
669     }
671     /*
672      * Check for a two octet sequence
673      */
674     if (IS_UTF8_1ST_OF_2(*utf8p)) {
675         if (buflen < 2)
676             return -2;
677         cont1 = (uint16) *(utf8p+1);
678         if (!IS_UTF8_2ND_THRU_6TH(cont1))
679             return -1;
680         *ucs2p =  (lead & TWO_OCTET_MASK) << 6;
681         *ucs2p |= cont1 & CONTINUING_OCTET_MASK;
682         return 2;
683     }
685     /*
686      * Check for a three octet sequence
687      */
688     else if (IS_UTF8_1ST_OF_3(lead)) {
689         if (buflen < 3)
690             return -2;
691         cont1 = (uint16) *(utf8p+1);
692         cont2 = (uint16) *(utf8p+2);
693         if (   (!IS_UTF8_2ND_THRU_6TH(cont1))
694             || (!IS_UTF8_2ND_THRU_6TH(cont2)))
695             return -1;
696         *ucs2p =  (lead & THREE_OCTET_MASK) << 12;
697         *ucs2p |= (cont1 & CONTINUING_OCTET_MASK) << 6;
698         *ucs2p |= cont2 & CONTINUING_OCTET_MASK;
699         return 3;
700     }
701     else { /* not a valid utf8/ucs2 character */
702         return -1;
703     }
706 /* ----------------------------- Helper functions --------------------------- */
707 /* Ripped off from lm_win.c .. */
708 /* where is strcasecmp?.. for now, it's case sensitive..
709  *
710  * strcasecmp is in strings.h, but on windows it's called _stricmp...
711  * will need to #ifdef this
712 */
714 static int32
715 js_FileHasOption(JSContext *cx, const char *oldoptions, const char *name)
717     char *comma, *equal, *current;
718     char *options = JS_strdup(cx, oldoptions);
719     int32 found = 0;
721     current = options;
722     for (;;) {
723         comma = strchr(current, ',');
724         if (comma) *comma = '\0';
725         equal = strchr(current, '=');
726         if (equal) *equal = '\0';
727         if (strcmp(current, name) == 0) {
728             if (!equal || strcmp(equal + 1, "yes") == 0)
729                 found = 1;
730             else
731                 found = atoi(equal + 1);
732         }
733         if (equal) *equal = '=';
734         if (comma) *comma = ',';
735         if (found || !comma)
736             break;
737         current = comma + 1;
738     }
739     JS_free(cx, options);
740     return found;
743 /* empty the buffer */
744 static void
745 js_ResetBuffers(JSFile * file)
747     file->charBufferUsed = JS_FALSE;
748     file->nbBytesInBuf = 0;
751 /* Reset file attributes */
752 static void
753 js_ResetAttributes(JSFile * file)
755     file->mode = file->type = 0;
756     file->isOpen = JS_FALSE;
757     file->handle = NULL;
758     file->nativehandle = NULL;
759     file->hasRandomAccess = JS_TRUE; /* Innocent until proven guilty. */
760     file->hasAutoflush = JS_FALSE;
761     file->isNative = JS_FALSE;
762     file->isPipe = JS_FALSE;
764     js_ResetBuffers(file);
767 static JSBool
768 js_FileOpen(JSContext *cx, JSObject *obj, JSFile *file, char *mode){
769     JSString *type, *mask;
770     jsval v[2];
771     jsval rval;
773     type =  JS_InternString(cx, asciistring);
774     mask =  JS_NewStringCopyZ(cx, mode);
775     v[0] = STRING_TO_JSVAL(mask);
776     v[1] = STRING_TO_JSVAL(type);
778     if (!file_open(cx, obj, 2, v, &rval))
779         return JS_FALSE;
780     return JS_TRUE;
783 /* Buffered version of PR_Read. Used by js_FileRead */
784 static int32
785 js_BufferedRead(JSFile * f, char *buf, int32 len)
787     int32 count = 0;
789     while (f->nbBytesInBuf>0&&len>0) {
790         buf[0] = f->byteBuffer[0];
791         f->byteBuffer[0] = f->byteBuffer[1];
792         f->byteBuffer[1] = f->byteBuffer[2];
793         f->nbBytesInBuf--;
794         len--;
795         buf+=1;
796         count++;
797     }
799     if (len>0) {
800         count += (!f->isNative)
801                  ? PR_Read(f->handle, buf, len)
802                  : fread(buf, 1, len, f->nativehandle);
803     }
804     return count;
807 static int32
808 js_FileRead(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode)
810     unsigned char *aux;
811     int32 count = 0, i;
812     jsint remainder;
813     unsigned char utfbuf[3];
815     if (file->charBufferUsed) {
816         buf[0] = file->charBuffer;
817         buf++;
818         len--;
819         file->charBufferUsed = JS_FALSE;
820     }
822     switch (mode) {
823       case ASCII:
824         aux = (unsigned char*)JS_malloc(cx, len);
825         if (!aux)
826             return 0;
828         count = js_BufferedRead(file, aux, len);
829         if (count == -1) {
830             JS_free(cx, aux);
831             return 0;
832         }
834         for (i = 0; i < len; i++)
835             buf[i] = (jschar)aux[i];
837         JS_free(cx, aux);
838         break;
840       case UTF8:
841         remainder = 0;
842         for (count = 0;count<len;count++) {
843             i = js_BufferedRead(file, utfbuf+remainder, 3-remainder);
844             if (i<=0) {
845                 return count;
846             }
847             i = utf8_to_ucs2_char(utfbuf, (int16)i, &buf[count] );
848             if (i<0) {
849                 return count;
850             } else {
851                 if (i==1) {
852                     utfbuf[0] = utfbuf[1];
853                     utfbuf[1] = utfbuf[2];
854                     remainder = 2;
855                 } else if (i==2) {
856                     utfbuf[0] = utfbuf[2];
857                     remainder = 1;
858                 } else if (i==3) {
859                     remainder = 0;
860                 }
861             }
862         }
863         while (remainder>0) {
864             file->byteBuffer[file->nbBytesInBuf] = utfbuf[0];
865             file->nbBytesInBuf++;
866             utfbuf[0] = utfbuf[1];
867             utfbuf[1] = utfbuf[2];
868             remainder--;
869         }
870         break;
872       case UCS2:
873         count = js_BufferedRead(file, (char*)buf, len*2) >> 1;
874         if (count == -1)
875             return 0;
877         break;
879       default:
880         /* Not reached. */
881         JS_ASSERT(0);
882     }
884     if(count == -1) {
885         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
886                              JSFILEMSG_OP_FAILED, "read", file->path);
887     }
889     return count;
892 static int32
893 js_FileSeek(JSContext *cx, JSFile *file, int32 len, int32 mode)
895     int32 count = 0, i;
896     jsint remainder;
897     unsigned char utfbuf[3];
898     jschar tmp;
900     switch (mode) {
901       case ASCII:
902         count = PR_Seek(file->handle, len, PR_SEEK_CUR);
903         break;
905       case UTF8:
906         remainder = 0;
907         for (count = 0;count<len;count++) {
908             i = js_BufferedRead(file, utfbuf+remainder, 3-remainder);
909             if (i<=0) {
910                 return 0;
911             }
912             i = utf8_to_ucs2_char(utfbuf, (int16)i, &tmp );
913             if (i<0) {
914                 return 0;
915             } else {
916                 if (i==1) {
917                     utfbuf[0] = utfbuf[1];
918                     utfbuf[1] = utfbuf[2];
919                     remainder = 2;
920                 } else if (i==2) {
921                     utfbuf[0] = utfbuf[2];
922                     remainder = 1;
923                 } else if (i==3) {
924                     remainder = 0;
925                 }
926             }
927         }
928         while (remainder>0) {
929             file->byteBuffer[file->nbBytesInBuf] = utfbuf[0];
930             file->nbBytesInBuf++;
931             utfbuf[0] = utfbuf[1];
932             utfbuf[1] = utfbuf[2];
933             remainder--;
934         }
935         break;
937       case UCS2:
938         count = PR_Seek(file->handle, len*2, PR_SEEK_CUR)/2;
939         break;
941       default:
942         /* Not reached. */
943         JS_ASSERT(0);
944     }
946     if(count == -1) {
947         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
948                              JSFILEMSG_OP_FAILED, "seek", file->path);
949     }
951     return count;
954 static int32
955 js_FileWrite(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode)
957     unsigned char   *aux;
958     int32           count = 0, i, j;
959     unsigned char   *utfbuf;
961     switch (mode) {
962       case ASCII:
963         aux = (unsigned char*)JS_malloc(cx, len);
964         if (!aux)
965             return 0;
967         for (i = 0; i<len; i++)
968             aux[i] = buf[i] % 256;
970         count = (!file->isNative)
971                 ? PR_Write(file->handle, aux, len)
972                 : fwrite(aux, 1, len, file->nativehandle);
974         if (count==-1) {
975             JS_free(cx, aux);
976             return 0;
977         }
979         JS_free(cx, aux);
980         break;
982       case UTF8:
983         utfbuf = (unsigned char*)JS_malloc(cx, len*3);
984         if (!utfbuf)  return 0;
985         i = 0;
986         for (count = 0;count<len;count++) {
987             j = one_ucs2_to_utf8_char(utfbuf+i, utfbuf+len*3, buf[count]);
988             if (j==-1) {
989                 JS_free(cx, utfbuf);
990                 return 0;
991             }
992             i+=j;
993         }
994         j = (!file->isNative) 
995             ? PR_Write(file->handle, utfbuf, i)
996             : fwrite(utfbuf, 1, i, file->nativehandle);
998         if (j<i) {
999             JS_free(cx, utfbuf);
1000             return 0;
1001         }
1002         JS_free(cx, utfbuf);
1003         break;
1005       case UCS2:
1006         count = (!file->isNative) 
1007                 ? PR_Write(file->handle, buf, len*2) >> 1
1008                 : fwrite(buf, 1, len*2, file->nativehandle) >> 1;
1010         if (count == -1)
1011             return 0;
1012         break;
1014       default:
1015         /* Not reached. */
1016         JS_ASSERT(0);
1017     }
1019     if(count == -1) {
1020         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1021                              JSFILEMSG_OP_FAILED, "write", file->path);
1022     }
1024     return count;
1027 /* ----------------------------- Property checkers -------------------------- */
1028 static JSBool
1029 js_exists(JSContext *cx, JSFile *file)
1031     if (file->isNative) {
1032         /* It doesn't make sense for a pipe of stdstream. */
1033         return JS_FALSE;
1034     }
1036     return PR_Access(file->path, PR_ACCESS_EXISTS) == PR_SUCCESS;
1039 static JSBool
1040 js_canRead(JSContext *cx, JSFile *file)
1042     if (!file->isNative) {
1043         if (file->isOpen && !(file->mode & PR_RDONLY))
1044             return JS_FALSE;
1045         return PR_Access(file->path, PR_ACCESS_READ_OK) == PR_SUCCESS;
1046     }
1048     if (file->isPipe) {
1049         /* Is this pipe open for reading? */
1050         return file->path[0] == PIPE_SYMBOL;
1051     }
1053     return !strcmp(file->path, STDINPUT_NAME);
1056 static JSBool
1057 js_canWrite(JSContext *cx, JSFile *file)
1059     if (!file->isNative) {
1060         if (file->isOpen && !(file->mode & PR_WRONLY))
1061             return JS_FALSE;
1062         return PR_Access(file->path, PR_ACCESS_WRITE_OK) == PR_SUCCESS;
1063     }
1065     if(file->isPipe) {
1066         /* Is this pipe open for writing? */
1067         return file->path[strlen(file->path)-1] == PIPE_SYMBOL;
1068     }
1070     return !strcmp(file->path, STDOUTPUT_NAME) ||
1071            !strcmp(file->path, STDERROR_NAME);
1074 static JSBool
1075 js_isFile(JSContext *cx, JSFile *file)
1077     if (!file->isNative) {
1078         PRFileInfo info;
1080         if (file->isOpen
1081             ? PR_GetOpenFileInfo(file->handle, &info)
1082             : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) {
1083             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1084                                  JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
1085             return JS_FALSE;
1086         }
1088         return info.type == PR_FILE_FILE;
1089     }
1091     /* This doesn't make sense for a pipe of stdstream. */
1092     return JS_FALSE;
1095 static JSBool
1096 js_isDirectory(JSContext *cx, JSFile *file)
1098     if(!file->isNative){
1099         PRFileInfo info;
1101         /* Hack needed to get get_property to work. */
1102         if (!js_exists(cx, file))
1103             return JS_FALSE;
1105         if (file->isOpen
1106             ? PR_GetOpenFileInfo(file->handle, &info)
1107             : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) {
1108             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1109                                  JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
1110             return JS_FALSE;
1111         }
1113         return info.type == PR_FILE_DIRECTORY;
1114     }
1116     /* This doesn't make sense for a pipe of stdstream. */
1117     return JS_FALSE;
1120 static jsval
1121 js_size(JSContext *cx, JSFile *file)
1123     PRFileInfo info;
1125     JSFILE_CHECK_NATIVE("size");
1127     if (file->isOpen
1128         ? PR_GetOpenFileInfo(file->handle, &info)
1129         : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) {
1130         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1131                              JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
1132         return JS_FALSE;
1133     }
1135     return INT_TO_JSVAL(info.size);
1137 out:
1138     return JSVAL_VOID;
1141 /*
1142  * Return the parent object
1143  */
1144 static JSBool
1145 js_parent(JSContext *cx, JSFile *file, jsval *resultp)
1147     char *str;
1149     /* Since we only care about pipes and native files, return NULL. */
1150     if (file->isNative) {
1151         *resultp = JSVAL_VOID;
1152         return JS_TRUE;
1153     }
1155     str = js_fileDirectoryName(cx, file->path);
1156     if (!str)
1157         return JS_FALSE;
1159     /* If the directory is equal to the original path, we're at the root. */
1160     if (!strcmp(file->path, str)) {
1161         *resultp = JSVAL_NULL;
1162     } else {
1163         JSObject *obj = js_NewFileObject(cx, str);
1164         if (!obj) {
1165             JS_free(cx, str);
1166             return JS_FALSE;
1167         }
1168         *resultp = OBJECT_TO_JSVAL(obj);
1169     }
1171     JS_free(cx, str);
1172     return JS_TRUE;
1175 static JSBool
1176 js_name(JSContext *cx, JSFile *file, jsval *vp)
1178     char *name;
1179     JSString *str;
1181     if (file->isPipe) {
1182         *vp = JSVAL_VOID;
1183         return JS_TRUE;
1184     }
1186     name = js_fileBaseName(cx, file->path);
1187     if (!name)
1188         return JS_FALSE;
1190     str = JS_NewString(cx, name, strlen(name));
1191     if (!str) {
1192         JS_free(cx, name);
1193         return JS_FALSE;
1194     }
1196     *vp = STRING_TO_JSVAL(str);
1197     return JS_TRUE;
1200 /* ------------------------------ File object methods ---------------------------- */
1201 static JSBool
1202 file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1204     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1205     JSString    *strmode, *strtype;
1206     char        *ctype, *mode;
1207     int32       mask, type;
1208     int         len;
1210     mode = NULL;
1212     SECURITY_CHECK(cx, NULL, "open", file);
1214     /* A native file that is already open */
1215     if(file->isOpen && file->isNative) {
1216         JS_ReportWarning(cx, "Native file %s is already open, proceeding",
1217                          file->path);
1218         goto good;
1219     }
1221     /* Close before proceeding */
1222     if (file->isOpen) {
1223         JS_ReportWarning(cx, "File %s is already open, we will close it and "
1224                          "reopen, proceeding", file->path);
1225         if(!file_close(cx, obj, 0, NULL, rval))
1226             goto out;
1227     }
1229     if (js_isDirectory(cx, file)) {
1230         JS_ReportWarning(cx, "%s seems to be a directory, there is no point in "
1231                          "trying to open it, proceeding", file->path);
1232         goto good;
1233     }
1235     /* Path must be defined at this point */
1236     len = strlen(file->path);
1238     /* Mode */
1239     if (argc >= 1) {
1240         strmode = JS_ValueToString(cx, argv[0]);
1241         if (!strmode) {
1242             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1243                                  JSFILEMSG_FIRST_ARGUMENT_OPEN_NOT_STRING_ERROR,
1244                                  argv[0]);
1245             goto out;
1246         }
1247         mode = JS_strdup(cx, JS_GetStringBytes(strmode));
1248     } else {
1249         if(file->path[0]==PIPE_SYMBOL) {
1250             /* pipe default mode */
1251             mode = JS_strdup(cx, "read");
1252         } else if(file->path[len-1]==PIPE_SYMBOL) {
1253             /* pipe default mode */
1254             mode = JS_strdup(cx, "write");
1255         } else {
1256             /* non-destructive, permissive defaults. */
1257             mode = JS_strdup(cx, "readWrite,append,create");
1258         }
1259     }
1261     /* Process the mode */
1262     mask = 0;
1263     /* TODO: this is pretty ugly, we walk thru the string too many times */
1264     mask |= js_FileHasOption(cx, mode, "read")     ? PR_RDONLY       :   0;
1265     mask |= js_FileHasOption(cx, mode, "write")    ? PR_WRONLY       :   0;
1266     mask |= js_FileHasOption(cx, mode, "readWrite")? PR_RDWR         :   0;
1267     mask |= js_FileHasOption(cx, mode, "append")   ? PR_APPEND       :   0;
1268     mask |= js_FileHasOption(cx, mode, "create")   ? PR_CREATE_FILE  :   0;
1269     mask |= js_FileHasOption(cx, mode, "replace")  ? PR_TRUNCATE     :   0;
1271     if (mask & PR_RDWR)
1272         mask |= (PR_RDONLY | PR_WRONLY);
1273     if ((mask & PR_RDONLY) && (mask & PR_WRONLY))
1274         mask |= PR_RDWR;
1276     file->hasAutoflush |= js_FileHasOption(cx, mode, "autoflush");
1278     /* Type */
1279     if (argc > 1) {
1280         strtype = JS_ValueToString(cx, argv[1]);
1281         if (!strtype) {
1282             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1283                                 JSFILEMSG_SECOND_ARGUMENT_OPEN_NOT_STRING_ERROR,
1284                                  argv[1]);
1285             goto out;
1286         }
1287         ctype = JS_GetStringBytes(strtype);
1289         if(!strcmp(ctype, utfstring)) {
1290             type = UTF8;
1291         } else if (!strcmp(ctype, unicodestring)) {
1292             type = UCS2;
1293         } else {
1294             if (strcmp(ctype, asciistring)) {
1295                 JS_ReportWarning(cx, "File type %s is not supported, using "
1296                                  "'text' instead, proceeding", ctype);
1297             }
1298             type = ASCII;
1299         }
1300     } else {
1301         type = ASCII;
1302     }
1304     /* Save the relevant fields */
1305     file->type = type;
1306     file->mode = mask;
1307     file->nativehandle = NULL;
1308     file->hasRandomAccess = (type != UTF8);
1310     /*
1311      * Deal with pipes here. We can't use NSPR for pipes, so we have to use
1312      * POPEN.
1313      */
1314     if (file->path[0]==PIPE_SYMBOL || file->path[len-1]==PIPE_SYMBOL) {
1315         if (file->path[0] == PIPE_SYMBOL && file->path[len-1] == PIPE_SYMBOL) {
1316             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1317                                  JSFILEMSG_BIDIRECTIONAL_PIPE_NOT_SUPPORTED);
1318             goto out;
1319         } else {
1320             int i = 0;
1321             char pipemode[3];
1322             SECURITY_CHECK(cx, NULL, "pipe_open", file);
1324             if(file->path[0] == PIPE_SYMBOL){
1325                 if(mask & (PR_WRONLY | PR_APPEND | PR_CREATE_FILE | PR_TRUNCATE)){
1326                     JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1327                                    JSFILEMSG_OPEN_MODE_NOT_SUPPORTED_WITH_PIPES,
1328                                          mode, file->path);
1329                     goto out;
1330                 }
1331                 /* open(SPOOLER, "| cat -v | lpr -h 2>/dev/null") -- pipe for writing */
1332                 pipemode[i++] = 'r';
1333 #ifndef XP_UNIX
1334                 pipemode[i++] = file->type==UTF8 ? 'b' : 't';
1335 #endif
1336                 pipemode[i++] = '\0';
1337                 file->nativehandle = POPEN(&file->path[1], pipemode);
1338             } else if(file->path[len-1] == PIPE_SYMBOL) {
1339                 char *command = JS_malloc(cx, len);
1341                 strncpy(command, file->path, len-1);
1342                 command[len-1] = '\0';
1343                 /* open(STATUS, "netstat -an 2>&1 |") */
1344                 pipemode[i++] = 'w';
1345 #ifndef XP_UNIX
1346                 pipemode[i++] = file->type==UTF8 ? 'b' : 't';
1347 #endif
1348                 pipemode[i++] = '\0';
1349                 file->nativehandle = POPEN(command, pipemode);
1350                 JS_free(cx, command);
1351             }
1352             /* set the flags */
1353             file->isNative = JS_TRUE;
1354             file->isPipe  = JS_TRUE;
1355             file->hasRandomAccess = JS_FALSE;
1356         }
1357     } else {
1358         /* TODO: what about the permissions?? Java ignores the problem... */
1359         file->handle = PR_Open(file->path, mask, 0644);
1360     }
1362     js_ResetBuffers(file);
1363     JS_free(cx, mode);
1364     mode = NULL;
1366     /* Set the open flag and return result */
1367     if (file->handle == NULL && file->nativehandle == NULL) {
1368         file->isOpen = JS_FALSE;
1370         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1371                              JSFILEMSG_OP_FAILED, "open", file->path);
1372         goto out;
1373     }
1375 good:
1376     file->isOpen = JS_TRUE;
1377     *rval = JSVAL_TRUE;
1378     return JS_TRUE;
1380 out:
1381     if(mode)
1382         JS_free(cx, mode);
1383     return JS_FALSE;
1386 static JSBool
1387 file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1389     JSFile  *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1391     SECURITY_CHECK(cx, NULL, "close", file);
1393     if(!file->isOpen){
1394         JS_ReportWarning(cx, "File %s is not open, can't close it, proceeding",
1395             file->path);
1396         goto out;
1397     }
1399     if(!file->isPipe){
1400         if(file->isNative){
1401             JS_ReportWarning(cx, "Unable to close a native file, proceeding", file->path);
1402             goto out;
1403         }else{
1404             if(file->handle && PR_Close(file->handle)){
1405                 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1406                     JSFILEMSG_OP_FAILED, "close", file->path);
1408                 goto out;
1409             }
1410         }
1411     }else{
1412         if(PCLOSE(file->nativehandle)==-1){
1413             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1414                 JSFILEMSG_OP_FAILED, "pclose", file->path);
1415             goto out;
1416         }
1417     }
1419     js_ResetAttributes(file);
1420     *rval = JSVAL_TRUE;
1421     return JS_TRUE;
1423 out:
1424     return JS_FALSE;
1428 static JSBool
1429 file_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1431         JSFile  *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1433     SECURITY_CHECK(cx, NULL, "remove", file);
1434     JSFILE_CHECK_NATIVE("remove");
1435     JSFILE_CHECK_CLOSED("remove");
1437     if ((js_isDirectory(cx, file) ?
1438             PR_RmDir(file->path) : PR_Delete(file->path))==PR_SUCCESS) {
1439         js_ResetAttributes(file);
1440         *rval = JSVAL_TRUE;
1441         return JS_TRUE;
1442     } else {
1443         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1444             JSFILEMSG_OP_FAILED, "remove", file->path);
1445         goto out;
1446     }
1447 out:
1448     *rval = JSVAL_FALSE;
1449     return JS_FALSE;
1452 /* Raw PR-based function. No text processing. Just raw data copying. */
1453 static JSBool
1454 file_copyTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1456     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1457     char        *dest = NULL;
1458     PRFileDesc  *handle = NULL;
1459     char        *buffer;
1460     jsval               count, size;
1461     JSBool      fileInitiallyOpen=JS_FALSE;
1463     SECURITY_CHECK(cx, NULL, "copyTo", file);   /* may need a second argument!*/
1464     JSFILE_CHECK_ONE_ARG("copyTo");
1465     JSFILE_CHECK_NATIVE("copyTo");
1466     /* remeber the state */
1467     fileInitiallyOpen = file->isOpen;
1468     JSFILE_CHECK_READ;
1470     dest = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
1472     /* make sure we are not reading a file open for writing */
1473     if (file->isOpen && !js_canRead(cx, file)) {
1474         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1475                 JSFILEMSG_CANNOT_COPY_FILE_OPEN_FOR_WRITING_ERROR, file->path);
1476         goto out;
1477     }
1479     if (file->handle==NULL){
1480         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1481             JSFILEMSG_OP_FAILED, "open", file->path);
1482         goto out;
1483     }
1485     handle = PR_Open(dest, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, 0644);
1487     if(!handle){
1488         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1489             JSFILEMSG_OP_FAILED, "open", dest);
1490         goto out;
1491     }
1493     if ((size=js_size(cx, file))==JSVAL_VOID) {
1494         goto out;
1495     }
1497     buffer = JS_malloc(cx, size);
1499     count = INT_TO_JSVAL(PR_Read(file->handle, buffer, size));
1501     /* reading panic */
1502     if (count!=size) {
1503         JS_free(cx, buffer);
1504         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1505               JSFILEMSG_COPY_READ_ERROR, file->path);
1506         goto out;
1507     }
1509     count = INT_TO_JSVAL(PR_Write(handle, buffer, JSVAL_TO_INT(size)));
1511     /* writing panic */
1512     if (count!=size) {
1513         JS_free(cx, buffer);
1514         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1515               JSFILEMSG_COPY_WRITE_ERROR, file->path);
1516         goto out;
1517     }
1519     JS_free(cx, buffer);
1521         if(!fileInitiallyOpen){
1522                 if(!file_close(cx, obj, 0, NULL, rval)) goto out;
1523         }
1525     if(PR_Close(handle)!=PR_SUCCESS){
1526         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1527               JSFILEMSG_OP_FAILED, "close", dest);
1528         goto out;
1529     }
1531     *rval = JSVAL_TRUE;
1532     return JS_TRUE;
1533 out:
1534     if(file->isOpen && !fileInitiallyOpen){
1535         if(PR_Close(file->handle)!=PR_SUCCESS){
1536             JS_ReportWarning(cx, "Can't close %s, proceeding", file->path);
1537         }
1538     }
1540     if(handle && PR_Close(handle)!=PR_SUCCESS){
1541         JS_ReportWarning(cx, "Can't close %s, proceeding", dest);
1542     }
1544     *rval = JSVAL_FALSE;
1545     return JS_FALSE;
1548 static JSBool
1549 file_renameTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1551     JSFile  *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1552     char    *dest;
1554     SECURITY_CHECK(cx, NULL, "renameTo", file); /* may need a second argument!*/
1555     JSFILE_CHECK_ONE_ARG("renameTo");
1556     JSFILE_CHECK_NATIVE("renameTo");
1557     JSFILE_CHECK_CLOSED("renameTo");
1559     dest = RESOLVE_PATH(cx, JS_GetStringBytes(JS_ValueToString(cx, argv[0])));
1561     if (PR_Rename(file->path, dest)==PR_SUCCESS){
1562         /* copy the new filename */
1563         JS_free(cx, file->path);
1564         file->path = dest;
1565         *rval = JSVAL_TRUE;
1566         return JS_TRUE;
1567     }else{
1568         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1569             JSFILEMSG_RENAME_FAILED, file->path, dest);
1570         goto out;
1571     }
1572 out:
1573     *rval = JSVAL_FALSE;
1574     return JS_FALSE;
1577 static JSBool
1578 file_flush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1580     JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1582     SECURITY_CHECK(cx, NULL, "flush", file);
1583     JSFILE_CHECK_NATIVE("flush");
1584     JSFILE_CHECK_OPEN("flush");
1586     if (PR_Sync(file->handle)==PR_SUCCESS){
1587       *rval = JSVAL_TRUE;
1588       return JS_TRUE;
1589     }else{
1590         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1591            JSFILEMSG_OP_FAILED, "flush", file->path);
1592        goto out;
1593     }
1594 out:
1595     *rval = JSVAL_FALSE;
1596     return JS_FALSE;
1599 static JSBool
1600 file_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1602     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1603     JSString    *str;
1604     int32       count;
1605     uintN       i;
1607     SECURITY_CHECK(cx, NULL, "write", file);
1608     JSFILE_CHECK_WRITE;
1610     for (i = 0; i<argc; i++) {
1611         str = JS_ValueToString(cx, argv[i]);
1612         count = js_FileWrite(cx, file, JS_GetStringChars(str),
1613             JS_GetStringLength(str), file->type);
1614         if (count==-1){
1615           *rval = JSVAL_FALSE;
1616           return JS_FALSE;
1617         }
1618     }
1620     *rval = JSVAL_TRUE;
1621     return JS_TRUE;
1622 out:
1623     *rval = JSVAL_FALSE;
1624     return JS_FALSE;
1627 static JSBool
1628 file_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1630     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1631     JSString    *str;
1633     SECURITY_CHECK(cx, NULL, "writeln", file);
1634     JSFILE_CHECK_WRITE;
1636     /* don't report an error here */
1637     if(!file_write(cx, obj, argc, argv, rval))  return JS_FALSE;
1638     /* don't do security here -- we passed the check in file_write */
1639     str = JS_NewStringCopyZ(cx, "\n");
1641     if (js_FileWrite(cx, file, JS_GetStringChars(str), JS_GetStringLength(str),
1642             file->type)==-1){
1643         *rval = JSVAL_FALSE;
1644         return JS_FALSE;
1645     }
1647     /* eol causes flush if hasAutoflush is turned on */
1648     if (file->hasAutoflush)
1649         file_flush(cx, obj, 0, NULL, rval);
1651     *rval =  JSVAL_TRUE;
1652     return JS_TRUE;
1653 out:
1654     *rval = JSVAL_FALSE;
1655     return JS_FALSE;
1658 static JSBool
1659 file_writeAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1661     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1662     jsuint      i;
1663     jsuint      limit;
1664     JSObject    *array;
1665     JSObject    *elem;
1666     jsval       elemval;
1668     SECURITY_CHECK(cx, NULL, "writeAll", file);
1669     JSFILE_CHECK_ONE_ARG("writeAll");
1670     JSFILE_CHECK_WRITE;
1672     if (!JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[0]))) {
1673         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1674             JSFILEMSG_FIRST_ARGUMENT_WRITEALL_NOT_ARRAY_ERROR);
1675         goto out;
1676     }
1678     array = JSVAL_TO_OBJECT(argv[0]);
1680     JS_GetArrayLength(cx, array, &limit);
1682     for (i = 0; i<limit; i++) {
1683         if (!JS_GetElement(cx, array, i, &elemval))  return JS_FALSE;
1684         elem = JSVAL_TO_OBJECT(elemval);
1685         file_writeln(cx, obj, 1, &elemval, rval);
1686     }
1688     *rval = JSVAL_TRUE;
1689     return JS_TRUE;
1690 out:
1691     *rval = JSVAL_FALSE;
1692     return JS_FALSE;
1695 static JSBool
1696 file_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1698     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1699     JSString    *str;
1700     int32       want, count;
1701     jschar      *buf;
1703     SECURITY_CHECK(cx, NULL, "read", file);
1704     JSFILE_CHECK_ONE_ARG("read");
1705     JSFILE_CHECK_READ;
1707     if (!JS_ValueToInt32(cx, argv[0], &want)){
1708         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1709             JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "read", argv[0]);
1710         goto out;
1711     }
1713     /* want = (want>262144)?262144:want; * arbitrary size limitation */
1715     buf = JS_malloc(cx, want*sizeof buf[0]);
1716     if (!buf)  goto out;
1718     count =  js_FileRead(cx, file, buf, want, file->type);
1719     if (count>0) {
1720         str = JS_NewUCStringCopyN(cx, buf, count);
1721         *rval = STRING_TO_JSVAL(str);
1722         JS_free(cx, buf);
1723         return JS_TRUE;
1724     } else {
1725         JS_free(cx, buf);
1726         goto out;
1727     }
1728 out:
1729     *rval = JSVAL_FALSE;
1730     return JS_FALSE;
1733 static JSBool
1734 file_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1736     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1737     JSString    *str;
1738     jschar      *buf = NULL, *tmp;
1739     int32       offset, read;
1740     intN        room;
1741     jschar      data, data2;
1743     SECURITY_CHECK(cx, NULL, "readln", file);
1744     JSFILE_CHECK_READ;
1746     buf = JS_malloc(cx, MAX_LINE_LENGTH * sizeof data);
1747     if (!buf)
1748         return JS_FALSE;
1750     room = MAX_LINE_LENGTH - 1;
1751     offset = 0;
1753     for (;;) {
1754         read = js_FileRead(cx, file, &data, 1, file->type);
1755         if (read < 0)
1756             goto out;
1757         if (read == 0)
1758             goto eof;
1760         switch (data) {
1761           case '\r':
1762             read = js_FileRead(cx, file, &data2, 1, file->type);
1763             if (read < 0)
1764                 goto out;
1766             if (read == 1 && data2 != '\n') {
1767                 /* We read one char too far.  Buffer it. */
1768                 file->charBuffer = data2;
1769                 file->charBufferUsed = JS_TRUE;
1770             }
1772             /* Fall through. */
1773           case '\n':
1774             goto done;
1776           default:
1777             if (--room < 0) {
1778                 tmp = JS_realloc(cx, buf,
1779                                  (offset + MAX_LINE_LENGTH) * sizeof data);
1780                 if (!tmp)
1781                     goto out;
1783                 room = MAX_LINE_LENGTH - 1;
1784                 buf = tmp;
1785             }
1787             buf[offset++] = data;
1788             break;
1789         }
1790     }
1792 eof:
1793     if (offset == 0) {
1794         *rval = JSVAL_NULL;
1795         return JS_TRUE;
1796     }
1798 done:
1799     buf[offset] = 0;
1800     tmp = JS_realloc(cx, buf, (offset + 1) * sizeof data);
1801     if (!tmp)
1802         goto out;
1804     str = JS_NewUCString(cx, tmp, offset);
1805     if (!str)
1806         goto out;
1808     *rval = STRING_TO_JSVAL(str);
1809     return JS_TRUE;
1811 out:
1812     if (buf)
1813         JS_free(cx, buf);
1815     return JS_FALSE;
1818 static JSBool
1819 file_readAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1821     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1822     JSObject    *array;
1823     jsint       len;
1824     jsval       line;
1825     JSBool      lineok = JS_FALSE;
1827     SECURITY_CHECK(cx, NULL, "readAll", file);
1828     JSFILE_CHECK_READ;
1830     array = JS_NewArrayObject(cx, 0, NULL);
1831     if (!array)
1832         return JS_FALSE;
1833     *rval = OBJECT_TO_JSVAL(array);
1835     len = 0;
1837     lineok = file_readln(cx, obj, 0, NULL, &line);
1838     while (lineok && !JSVAL_IS_NULL(line)) {
1839         JS_SetElement(cx, array, len++, &line);
1840         lineok = file_readln(cx, obj, 0, NULL, &line);
1841     }
1843 out:
1844     return lineok;
1847 static JSBool
1848 file_seek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1850     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1851     int32       toskip;
1852     int32       pos;
1854     SECURITY_CHECK(cx, NULL, "seek", file);
1855     JSFILE_CHECK_ONE_ARG("seek");
1856     JSFILE_CHECK_NATIVE("seek");
1857     JSFILE_CHECK_READ;
1859     if (!JS_ValueToInt32(cx, argv[0], &toskip)){
1860         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1861             JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "seek", argv[0]);
1862         goto out;
1863     }
1865     if(!file->hasRandomAccess){
1866         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1867             JSFILEMSG_NO_RANDOM_ACCESS, file->path);
1868        goto out;
1869     }
1871     if(js_isDirectory(cx, file)){
1872         JS_ReportWarning(cx,"Seek on directories is not supported, proceeding");
1873         goto out;
1874     }
1876     pos = js_FileSeek(cx, file, toskip, file->type);
1878     if (pos!=-1) {
1879         *rval = INT_TO_JSVAL(pos);
1880         return JS_TRUE;
1881     }
1882 out:
1883     *rval = JSVAL_VOID;
1884     return JS_FALSE;
1887 static JSBool
1888 file_list(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1890     PRDir       *dir;
1891     PRDirEntry  *entry;
1892     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1893     JSObject    *array;
1894     JSObject    *eachFile;
1895     jsint       len;
1896     jsval       v;
1897     JSRegExp    *re = NULL;
1898     JSFunction  *func = NULL;
1899     JSString    *str;
1900     jsval       args[1];
1901     char        *filePath;
1903     SECURITY_CHECK(cx, NULL, "list", file);
1904     JSFILE_CHECK_NATIVE("list");
1906     if (argc==1) {
1907         if (JSVAL_IS_REGEXP(cx, argv[0])) {
1908             re = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0]));
1909         }else
1910         if (JSVAL_IS_FUNCTION(cx, argv[0])) {
1911             func = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0]));
1912         }else{
1913             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1914                 JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_FUNCTION_OR_REGEX, argv[0]);
1915             goto out;
1916         }
1917     }
1919     if (!js_isDirectory(cx, file)) {
1920         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1921             JSFILEMSG_CANNOT_DO_LIST_ON_A_FILE, file->path);
1922         goto out;
1923     }
1925     dir = PR_OpenDir(file->path);
1926     if(!dir){
1927         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1928             JSFILEMSG_OP_FAILED, "open", file->path);
1929         goto out;
1930     }
1932     /* create JSArray here... */
1933     array = JS_NewArrayObject(cx, 0, NULL);
1934     len = 0;
1936     while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))!=NULL) {
1937         /* first, check if we have a regexp */
1938         if (re!=NULL) {
1939             size_t index = 0;
1941             str = JS_NewStringCopyZ(cx, entry->name);
1942             if(!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &v)){
1943                 /* don't report anything here */
1944                 goto out;
1945             }
1946             /* not matched! */
1947             if (JSVAL_IS_NULL(v)) {
1948                 continue;
1949             }
1950         }else
1951         if (func!=NULL) {
1952             str = JS_NewStringCopyZ(cx, entry->name);
1953             args[0] = STRING_TO_JSVAL(str);
1954             if(!JS_CallFunction(cx, obj, func, 1, args, &v)){
1955                 goto out;
1956             }
1958             if (v==JSVAL_FALSE) {
1959                 continue;
1960             }
1961         }
1963         filePath = js_combinePath(cx, file->path, (char*)entry->name);
1965         eachFile = js_NewFileObject(cx, filePath);
1966         JS_free(cx, filePath);
1967         if (!eachFile){
1968             JS_ReportWarning(cx, "File %s cannot be retrieved", filePath);
1969             continue;
1970         }
1971         v = OBJECT_TO_JSVAL(eachFile);
1972         JS_SetElement(cx, array, len, &v);
1973         JS_SetProperty(cx, array, entry->name, &v);
1974         len++;
1975     }
1977     if(PR_CloseDir(dir)!=PR_SUCCESS){
1978         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1979             JSFILEMSG_OP_FAILED, "close", file->path);
1980         goto out;
1981     }
1982     *rval = OBJECT_TO_JSVAL(array);
1983     return JS_TRUE;
1984 out:
1985     *rval = JSVAL_NULL;
1986     return JS_FALSE;
1989 static JSBool
1990 file_mkdir(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1992     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1994     SECURITY_CHECK(cx, NULL, "mkdir", file);
1995     JSFILE_CHECK_ONE_ARG("mkdir");
1996     JSFILE_CHECK_NATIVE("mkdir");
1998     /* if the current file is not a directory, find out the directory name */
1999     if (!js_isDirectory(cx, file)) {
2000         char        *dir = js_fileDirectoryName(cx, file->path);
2001         JSObject    *dirObj = js_NewFileObject(cx, dir);
2003         JS_free(cx, dir);
2005         /* call file_mkdir with the right set of parameters if needed */
2006         if (file_mkdir(cx, dirObj, argc, argv, rval))
2007                         return JS_TRUE;
2008                 else
2009             goto out;
2010     }else{
2011         char *dirName = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
2012         char *fullName;
2014         fullName = js_combinePath(cx, file->path, dirName);
2015         if (PR_MkDir(fullName, 0755)==PR_SUCCESS){
2016             *rval = JSVAL_TRUE;
2017             JS_free(cx, fullName);
2018             return JS_TRUE;
2019         }else{
2020             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2021                 JSFILEMSG_OP_FAILED, "mkdir", fullName);
2022             JS_free(cx, fullName);
2023             goto out;
2024         }
2025     }
2026 out:
2027     *rval = JSVAL_FALSE;
2028     return JS_FALSE;
2031 static JSBool
2032 file_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval*rval)
2034     JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
2036     *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, file->path));
2037     return JS_TRUE;
2040 static JSBool
2041 file_toURL(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2043     JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
2044     char url[MAX_PATH_LENGTH];
2045     jschar *urlChars;
2047         JSFILE_CHECK_NATIVE("toURL");
2049     sprintf(url, "file://%s", file->path);
2050     /* TODO: js_escape in jsstr.h may go away at some point */
2052     urlChars = js_InflateString(cx, url, strlen(url));
2053     if (urlChars == NULL) return JS_FALSE;
2054     *rval = STRING_TO_JSVAL(js_NewString(cx, urlChars, strlen(url), 0));
2055     if (!js_str_escape(cx, obj, 0, rval, rval)) return JS_FALSE;
2057     return JS_TRUE;
2058 out:
2059     *rval = JSVAL_VOID;
2060     return JS_FALSE;
2064 static void
2065 file_finalize(JSContext *cx, JSObject *obj)
2067     JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
2069     if(file) {
2070         /* Close the file before exiting. */
2071         if(file->isOpen && !file->isNative) {
2072             jsval vp;
2073             file_close(cx, obj, 0, NULL, &vp);
2074         }
2076         if (file->path)
2077             JS_free(cx, file->path);
2079         JS_free(cx, file);
2080     }
2083 /*
2084     Allocates memory for the file object, sets fields to defaults.
2085 */
2086 static JSFile*
2087 file_init(JSContext *cx, JSObject *obj, char *bytes)
2089     JSFile *file;
2091     file = JS_malloc(cx, sizeof *file);
2092     if (!file)
2093         return NULL;
2094     memset(file, 0 , sizeof *file);
2096     js_ResetAttributes(file);
2098     file->path = RESOLVE_PATH(cx, bytes);
2100     if (!JS_SetPrivate(cx, obj, file)) {
2101         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2102                              JSFILEMSG_CANNOT_SET_PRIVATE_FILE, file->path);
2103         JS_free(cx, file);
2104         return NULL;
2105     }
2107     return file;
2110 /* Returns a JSObject. This function is globally visible */
2111 JS_PUBLIC_API(JSObject*)
2112 js_NewFileObject(JSContext *cx, char *filename)
2114     JSObject    *obj;
2115     JSFile      *file;
2117     obj = JS_NewObject(cx, &file_class, NULL, NULL);
2118     if (!obj){
2119         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2120             JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObject");
2121         return NULL;
2122     }
2123     file = file_init(cx, obj, filename);
2124     if(!file) return NULL;
2125     return obj;
2128 /* Internal function, used for cases which NSPR file support doesn't cover */
2129 JSObject*
2130 js_NewFileObjectFromFILE(JSContext *cx, FILE *nativehandle, char *filename,
2131     int32 mode, JSBool open, JSBool randomAccess)
2133     JSObject *obj;
2134     JSFile   *file;
2136     obj = JS_NewObject(cx, &file_class, NULL, NULL);
2137     if (!obj){
2138         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2139             JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObjectFromFILE");
2140         return NULL;
2141     }
2142     file = file_init(cx, obj, filename);
2143     if(!file) return NULL;
2145     file->nativehandle = nativehandle;
2147     /* free result of RESOLVE_PATH from file_init. */
2148     JS_ASSERT(file->path != NULL);
2149     JS_free(cx, file->path);
2151     file->path = strdup(filename);
2152     file->isOpen = open;
2153     file->mode = mode;
2154     file->hasRandomAccess = randomAccess;
2155     file->isNative = JS_TRUE;
2156     return obj;
2159 /*
2160     Real file constructor that is called from JavaScript.
2161     Basically, does error processing and calls file_init.
2162 */
2163 static JSBool
2164 file_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
2165                  jsval *rval)
2167     JSString *str;
2168     JSFile   *file;
2170     if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
2171         /* Replace obj with a new File object. */
2172         obj = JS_NewObject(cx, &file_class, NULL, NULL);
2173         if (!obj)
2174             return JS_FALSE;
2175         *rval = OBJECT_TO_JSVAL(obj);
2176     }
2178     str = (argc == 0) 
2179           ? JS_InternString(cx, "")
2180           : JS_ValueToString(cx, argv[0]);
2182     if (!str) {
2183         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2184                          JSFILEMSG_FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR,
2185                              argv[0]);
2186         return JS_FALSE;
2187     }
2189     file = file_init(cx, obj, JS_GetStringBytes(str));
2190     if (!file)
2191         return JS_FALSE;
2193     SECURITY_CHECK(cx, NULL, "constructor", file);
2195     return JS_TRUE;
2198 /* -------------------- File methods and properties ------------------------- */
2199 static JSFunctionSpec file_functions[] = {
2200     { "open",           file_open, 0},
2201     { "close",          file_close, 0},
2202     { "remove",         file_remove, 0},
2203     { "copyTo",         file_copyTo, 0},
2204     { "renameTo",       file_renameTo, 0},
2205     { "flush",          file_flush, 0},
2206     { "seek",           file_seek, 0},
2207     { "read",           file_read, 0},
2208     { "readln",         file_readln, 0},
2209     { "readAll",        file_readAll, 0},
2210     { "write",          file_write, 0},
2211     { "writeln",        file_writeln, 0},
2212     { "writeAll",       file_writeAll, 0},
2213     { "list",           file_list, 0},
2214     { "mkdir",          file_mkdir, 0},
2215     { "toString",       file_toString, 0},
2216     { "toURL",          file_toURL, 0},
2217     {0}
2218 };
2220 enum file_tinyid {
2221     FILE_LENGTH             = -2,
2222     FILE_PARENT             = -3,
2223     FILE_PATH               = -4,
2224     FILE_NAME               = -5,
2225     FILE_ISDIR              = -6,
2226     FILE_ISFILE             = -7,
2227     FILE_EXISTS             = -8,
2228     FILE_CANREAD            = -9,
2229     FILE_CANWRITE           = -10,
2230     FILE_OPEN               = -11,
2231     FILE_TYPE               = -12,
2232     FILE_MODE               = -13,
2233     FILE_CREATED            = -14,
2234     FILE_MODIFIED           = -15,
2235     FILE_SIZE               = -16,
2236     FILE_RANDOMACCESS       = -17,
2237     FILE_POSITION           = -18,
2238     FILE_APPEND             = -19,
2239     FILE_REPLACE            = -20,
2240     FILE_AUTOFLUSH          = -21,
2241     FILE_ISNATIVE           = -22,
2242 };
2244 static JSPropertySpec file_props[] = {
2245    {"length",          FILE_LENGTH,        JSPROP_ENUMERATE | JSPROP_READONLY },
2246    {"parent",          FILE_PARENT,        JSPROP_ENUMERATE | JSPROP_READONLY },
2247    {"path",            FILE_PATH,          JSPROP_ENUMERATE | JSPROP_READONLY },
2248    {"name",            FILE_NAME,          JSPROP_ENUMERATE | JSPROP_READONLY },
2249    {"isDirectory",     FILE_ISDIR,         JSPROP_ENUMERATE | JSPROP_READONLY },
2250    {"isFile",          FILE_ISFILE,        JSPROP_ENUMERATE | JSPROP_READONLY },
2251    {"exists",          FILE_EXISTS,        JSPROP_ENUMERATE | JSPROP_READONLY },
2252    {"canRead",         FILE_CANREAD,       JSPROP_ENUMERATE | JSPROP_READONLY },
2253    {"canWrite",        FILE_CANWRITE,      JSPROP_ENUMERATE | JSPROP_READONLY },
2254    {"canAppend",       FILE_APPEND,        JSPROP_ENUMERATE | JSPROP_READONLY },
2255    {"canReplace",      FILE_REPLACE,       JSPROP_ENUMERATE | JSPROP_READONLY },
2256    {"isOpen",          FILE_OPEN,          JSPROP_ENUMERATE | JSPROP_READONLY },
2257    {"type",            FILE_TYPE,          JSPROP_ENUMERATE | JSPROP_READONLY },
2258    {"mode",            FILE_MODE,          JSPROP_ENUMERATE | JSPROP_READONLY },
2259    {"creationTime",    FILE_CREATED,       JSPROP_ENUMERATE | JSPROP_READONLY },
2260    {"lastModified",    FILE_MODIFIED,      JSPROP_ENUMERATE | JSPROP_READONLY },
2261    {"size",            FILE_SIZE,          JSPROP_ENUMERATE | JSPROP_READONLY },
2262    {"hasRandomAccess", FILE_RANDOMACCESS,  JSPROP_ENUMERATE | JSPROP_READONLY },
2263    {"hasAutoFlush",    FILE_AUTOFLUSH,     JSPROP_ENUMERATE | JSPROP_READONLY },
2264    {"position",        FILE_POSITION,      JSPROP_ENUMERATE },
2265    {"isNative",        FILE_ISNATIVE,      JSPROP_ENUMERATE | JSPROP_READONLY },
2266    {0}
2267 };
2269 /* ------------------------- Property getter/setter ------------------------- */
2270 static JSBool
2271 file_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2273     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
2274     char        *bytes;
2275     JSString    *str;
2276     jsint       tiny;
2277     PRFileInfo  info;
2278     JSBool      flag;
2279     PRExplodedTime expandedTime;
2281     tiny = JSVAL_TO_INT(id);
2282     if (!file)
2283         return JS_TRUE;
2285     switch (tiny) {
2286     case FILE_PARENT:
2287         SECURITY_CHECK(cx, NULL, "parent", file);
2288         if (!js_parent(cx, file, vp))
2289             return JS_FALSE;
2290         break;
2291     case FILE_PATH:
2292         str = JS_NewStringCopyZ(cx, file->path);
2293         if (!str)
2294             return JS_FALSE;
2295         *vp = STRING_TO_JSVAL(str);
2296         break;
2297     case FILE_NAME:
2298         if (!js_name(cx, file, vp))
2299             return JS_FALSE;
2300         break;
2301     case FILE_ISDIR:
2302         SECURITY_CHECK(cx, NULL, "isDirectory", file);
2303         *vp = BOOLEAN_TO_JSVAL(js_isDirectory(cx, file));
2304         break;
2305     case FILE_ISFILE:
2306         SECURITY_CHECK(cx, NULL, "isFile", file);
2307         *vp = BOOLEAN_TO_JSVAL(js_isFile(cx, file));
2308         break;
2309     case FILE_EXISTS:
2310         SECURITY_CHECK(cx, NULL, "exists", file);
2311         *vp = BOOLEAN_TO_JSVAL(js_exists(cx, file));
2312         break;
2313     case FILE_ISNATIVE:
2314         SECURITY_CHECK(cx, NULL, "isNative", file);
2315         *vp = BOOLEAN_TO_JSVAL(file->isNative);
2316         break;
2317     case FILE_CANREAD:
2318         SECURITY_CHECK(cx, NULL, "canRead", file);
2319         *vp = BOOLEAN_TO_JSVAL(js_canRead(cx, file));
2320         break;
2321     case FILE_CANWRITE:
2322         SECURITY_CHECK(cx, NULL, "canWrite", file);
2323         *vp = BOOLEAN_TO_JSVAL(js_canWrite(cx, file));
2324         break;
2325     case FILE_OPEN:
2326         SECURITY_CHECK(cx, NULL, "isOpen", file);
2327         *vp = BOOLEAN_TO_JSVAL(file->isOpen);
2328         break;
2329     case FILE_APPEND :
2330         SECURITY_CHECK(cx, NULL, "canAppend", file);
2331         JSFILE_CHECK_OPEN("canAppend");
2332         *vp = BOOLEAN_TO_JSVAL(!file->isNative &&
2333                 (file->mode&PR_APPEND)==PR_APPEND);
2334         break;
2335     case FILE_REPLACE :
2336         SECURITY_CHECK(cx, NULL, "canReplace", file);
2337         JSFILE_CHECK_OPEN("canReplace");
2338         *vp = BOOLEAN_TO_JSVAL(!file->isNative &&
2339                 (file->mode&PR_TRUNCATE)==PR_TRUNCATE);
2340         break;
2341     case FILE_AUTOFLUSH :
2342         SECURITY_CHECK(cx, NULL, "hasAutoFlush", file);
2343         JSFILE_CHECK_OPEN("hasAutoFlush");
2344         *vp = BOOLEAN_TO_JSVAL(!file->isNative && file->hasAutoflush);
2345         break;
2346     case FILE_TYPE:
2347         SECURITY_CHECK(cx, NULL, "type", file);
2348         JSFILE_CHECK_OPEN("type");
2349         if(js_isDirectory(cx, file)){
2350             *vp = JSVAL_VOID;
2351             break;
2352         }
2354         switch (file->type) {
2355         case ASCII:
2356             *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, asciistring));
2357             break;
2358         case UTF8:
2359             *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, utfstring));
2360             break;
2361         case UCS2:
2362             *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, unicodestring));
2363             break;
2364         default:
2365             JS_ReportWarning(cx, "Unsupported file type %d, proceeding",
2366                 file->type);
2367         }
2368         break;
2369     case FILE_MODE:
2370         SECURITY_CHECK(cx, NULL, "mode", file);
2371         JSFILE_CHECK_OPEN("mode");
2372         bytes = JS_malloc(cx, MODE_SIZE);
2373         bytes[0] = '\0';
2374         flag = JS_FALSE;
2376         if ((file->mode&PR_RDONLY)==PR_RDONLY) {
2377             if (flag) strcat(bytes, ",");
2378             strcat(bytes, "read");
2379             flag = JS_TRUE;
2380         }
2381         if ((file->mode&PR_WRONLY)==PR_WRONLY) {
2382             if (flag) strcat(bytes, ",");
2383             strcat(bytes, "write");
2384             flag = JS_TRUE;
2385         }
2386         if ((file->mode&PR_RDWR)==PR_RDWR) {
2387             if (flag) strcat(bytes, ",");
2388             strcat(bytes, "readWrite");
2389             flag = JS_TRUE;
2390         }
2391         if ((file->mode&PR_APPEND)==PR_APPEND) {
2392             if (flag) strcat(bytes, ",");
2393             strcat(bytes, "append");
2394             flag = JS_TRUE;
2395         }
2396         if ((file->mode&PR_CREATE_FILE)==PR_CREATE_FILE) {
2397             if (flag) strcat(bytes, ",");
2398             strcat(bytes, "create");
2399             flag = JS_TRUE;
2400         }
2401         if ((file->mode&PR_TRUNCATE)==PR_TRUNCATE) {
2402             if (flag) strcat(bytes, ",");
2403             strcat(bytes, "replace");
2404             flag = JS_TRUE;
2405         }
2406         if (file->hasAutoflush) {
2407             if (flag) strcat(bytes, ",");
2408             strcat(bytes, "hasAutoFlush");
2409             flag = JS_TRUE;
2410         }
2411         *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, bytes));
2412         JS_free(cx, bytes);
2413         break;
2414     case FILE_CREATED:
2415         SECURITY_CHECK(cx, NULL, "creationTime", file);
2416         JSFILE_CHECK_NATIVE("creationTime");
2417         if(((file->isOpen)?
2418                         PR_GetOpenFileInfo(file->handle, &info):
2419                         PR_GetFileInfo(file->path, &info))!=PR_SUCCESS){
2420             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2421                 JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
2422             goto out;
2423         }
2425         PR_ExplodeTime(info.creationTime, PR_LocalTimeParameters,&expandedTime);
2426         *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx,  expandedTime.tm_year,
2427                                     expandedTime.tm_month,
2428                                     expandedTime.tm_mday,
2429                                     expandedTime.tm_hour,
2430                                     expandedTime.tm_min,
2431                                     expandedTime.tm_sec));
2432         break;
2433     case FILE_MODIFIED:
2434         SECURITY_CHECK(cx, NULL, "lastModified", file);
2435         JSFILE_CHECK_NATIVE("lastModified");
2436         if(((file->isOpen)?
2437                         PR_GetOpenFileInfo(file->handle, &info):
2438                         PR_GetFileInfo(file->path, &info))!=PR_SUCCESS){
2439             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2440                 JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
2441             goto out;
2442         }
2444         PR_ExplodeTime(info.modifyTime, PR_LocalTimeParameters, &expandedTime);
2445         *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx, expandedTime.tm_year,
2446                                     expandedTime.tm_month,
2447                                     expandedTime.tm_mday,
2448                                     expandedTime.tm_hour,
2449                                     expandedTime.tm_min,
2450                                     expandedTime.tm_sec));
2451         break;
2452     case FILE_SIZE:
2453         SECURITY_CHECK(cx, NULL, "size", file);
2454         *vp = js_size(cx, file);
2455         break;
2456     case FILE_LENGTH:
2457         SECURITY_CHECK(cx, NULL, "length", file);
2458         JSFILE_CHECK_NATIVE("length");
2460         if (js_isDirectory(cx, file)) { /* XXX debug me */
2461             PRDir       *dir;
2462             PRDirEntry  *entry;
2463             jsint       count = 0;
2465             if(!(dir = PR_OpenDir(file->path))){
2466                 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2467                     JSFILEMSG_CANNOT_OPEN_DIR, file->path);
2468                 goto out;
2469             }
2471             while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))) {
2472                 count++;
2473             }
2475             if(!PR_CloseDir(dir)){
2476                 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2477                     JSFILEMSG_OP_FAILED, "close", file->path);
2479                 goto out;
2480             }
2482             *vp = INT_TO_JSVAL(count);
2483             break;
2484         }else{
2485             /* return file size */
2486             *vp = js_size(cx, file);
2487         }
2488         break;
2489     case FILE_RANDOMACCESS:
2490             SECURITY_CHECK(cx, NULL, "hasRandomAccess", file);
2491             JSFILE_CHECK_OPEN("hasRandomAccess");
2492             *vp = BOOLEAN_TO_JSVAL(file->hasRandomAccess);
2493         break;
2494     case FILE_POSITION:
2495         SECURITY_CHECK(cx, NULL, "position", file);
2496         JSFILE_CHECK_NATIVE("position");
2497         JSFILE_CHECK_OPEN("position");
2499         if(!file->hasRandomAccess){
2500             JS_ReportWarning(cx, "File %s doesn't support random access, can't report the position, proceeding");
2501             *vp = JSVAL_VOID;
2502             break;
2503         }
2505         if (file->isOpen && js_isFile(cx, file)) {
2506             int pos = PR_Seek(file->handle, 0, PR_SEEK_CUR);
2507             if(pos!=-1){
2508                 *vp = INT_TO_JSVAL(pos);
2509             }else{
2510                 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2511                     JSFILEMSG_CANNOT_REPORT_POSITION, file->path);
2512                 goto out;
2513             }
2514         }else {
2515             JS_ReportWarning(cx, "File %s is closed or not a plain file,"
2516                 " can't report position, proceeding");
2517             goto out;
2518         }
2519         break;
2520     default:
2521         SECURITY_CHECK(cx, NULL, "file_access", file);
2523         /* this is some other property -- try to use the dir["file"] syntax */
2524         if (js_isDirectory(cx, file)) {
2525             PRDir *dir = NULL;
2526             PRDirEntry *entry = NULL;
2527             char *prop_name;
2529             str = JS_ValueToString(cx, id);
2530             if (!str)
2531                 return JS_FALSE;
2533             prop_name = JS_GetStringBytes(str);
2535             /* no native files past this point */
2536             dir = PR_OpenDir(file->path);
2537             if(!dir) {
2538                 /* This is probably not a directory */
2539                 JS_ReportWarning(cx, "Can't open directory %s", file->path);
2540                 return JS_FALSE;
2541             }
2543             while ((entry = PR_ReadDir(dir, PR_SKIP_NONE)) != NULL) {
2544                 if (!strcmp(entry->name, prop_name)){
2545                     bytes = js_combinePath(cx, file->path, prop_name);
2546                     *vp = OBJECT_TO_JSVAL(js_NewFileObject(cx, bytes));
2547                     JS_free(cx, bytes);
2548                     return JS_TRUE;
2549                 }
2550             }
2551         }
2552     }
2553     return JS_TRUE;
2555 out:
2556     return JS_FALSE;
2559 static JSBool
2560 file_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2562     JSFile  *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
2563     jsint   slot;
2565     if (JSVAL_IS_STRING(id)){
2566         return JS_TRUE;
2567     }
2569     slot = JSVAL_TO_INT(id);
2571     switch (slot) {
2572     /* File.position  = 10 */
2573     case FILE_POSITION:
2574         SECURITY_CHECK(cx, NULL, "set_position", file);
2575         JSFILE_CHECK_NATIVE("set_position");
2577         if(!file->hasRandomAccess){
2578             JS_ReportWarning(cx, "File %s doesn't support random access, can't "
2579                 "report the position, proceeding");
2580             goto out;
2581         }
2583         if (file->isOpen && js_isFile(cx, file)) {
2584             int32 pos;
2585                     int32 offset;
2587                         if (!JS_ValueToInt32(cx, *vp, &offset)){
2588                                 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2589                                         JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "position", *vp);
2590                                 goto out;
2591                         }
2593                         pos = PR_Seek(file->handle, offset, PR_SEEK_SET);
2595             if(pos!=-1){
2596                 *vp = INT_TO_JSVAL(pos);
2597             }else{
2598                 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2599                     JSFILEMSG_CANNOT_SET_POSITION, file->path);
2600                 goto out;
2601             }
2602         } else {
2603             JS_ReportWarning(cx, "File %s is closed or not a file, can't set "
2604                 "position, proceeding", file->path);
2605             goto out;
2606         }
2607     }
2609     return JS_TRUE;
2610 out:
2611     return JS_FALSE;
2614 /*
2615     File.currentDir = new File("D:\") or File.currentDir = "D:\"
2616 */
2617 static JSBool
2618 file_currentDirSetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2620     JSFile   *file;
2622     file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
2624     /* Look at the rhs and extract a file object from it */
2625     if (JSVAL_IS_OBJECT(*vp)) {
2626         if (JS_InstanceOf(cx, obj, &file_class, NULL)) {
2627             /* Braindamaged rhs -- just return the old value */
2628             if (file && (!js_exists(cx, file) || !js_isDirectory(cx, file))) {
2629                 JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp);
2630                 return JS_FALSE;
2631             } else {
2632                 chdir(file->path);
2633                 return JS_TRUE;
2634             }
2635         } else {
2636             return JS_FALSE;
2637         }
2638     } else {
2639         JSObject *rhsObject;
2640         char     *path;
2642         path      = JS_GetStringBytes(JS_ValueToString(cx, *vp));
2643         rhsObject = js_NewFileObject(cx, path);
2644         if (!rhsObject)
2645             return JS_FALSE;
2647         if (!file || !js_exists(cx, file) || !js_isDirectory(cx, file)){
2648             JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp);
2649         } else {
2650             *vp = OBJECT_TO_JSVAL(rhsObject);
2651             chdir(path);
2652         }
2653     }
2655     return JS_TRUE;
2658 /* Declare class */
2659 static JSClass file_class = {
2660     FILE_CONSTRUCTOR, JSCLASS_HAS_PRIVATE,
2661     JS_PropertyStub,  JS_PropertyStub,  file_getProperty,  file_setProperty,
2662     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,    file_finalize
2663 };
2665 /* -------------------- Functions exposed to the outside -------------------- */
2666 JS_PUBLIC_API(JSObject*)
2667 js_InitFileClass(JSContext *cx, JSObject* obj)
2669     JSObject *file, *ctor, *afile;
2670     jsval    vp;
2671     char     *currentdir;
2672     char     separator[2];
2674     file = JS_InitClass(cx, obj, NULL, &file_class, file_constructor, 1,
2675         file_props, file_functions, NULL, NULL);
2676     if (!file) {
2677         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2678             JSFILEMSG_INIT_FAILED);
2679         return NULL;
2680     }
2682     ctor = JS_GetConstructor(cx, file);
2683     if (!ctor)  return NULL;
2685         /* Define CURRENTDIR property. We are doing this to get a
2686         slash at the end of the current dir */
2687     afile = js_NewFileObject(cx, CURRENT_DIR);
2688     currentdir =  JS_malloc(cx, MAX_PATH_LENGTH);
2689     currentdir =  getcwd(currentdir, MAX_PATH_LENGTH);
2690     afile = js_NewFileObject(cx, currentdir);
2691     JS_free(cx, currentdir);
2692     vp = OBJECT_TO_JSVAL(afile);
2693     JS_DefinePropertyWithTinyId(cx, ctor, CURRENTDIR_PROPERTY, 0, vp,
2694                 JS_PropertyStub, file_currentDirSetter,
2695                 JSPROP_ENUMERATE | JSPROP_READONLY );
2697     /* Define input */
2698     vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stdin,
2699             STDINPUT_NAME, PR_RDONLY, JS_TRUE, JS_FALSE));
2700     JS_SetProperty(cx, ctor, "input", &vp);
2702     /* Define output */
2703     vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stdout,
2704             STDOUTPUT_NAME, PR_WRONLY, JS_TRUE, JS_FALSE));
2705     JS_SetProperty(cx, ctor, "output", &vp);
2707     /* Define error */
2708     vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stderr,
2709             STDERROR_NAME, PR_WRONLY, JS_TRUE, JS_FALSE));
2710     JS_SetProperty(cx, ctor, "error", &vp);
2712     separator[0] = FILESEPARATOR;
2713     separator[1] = '\0';
2714     vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, separator));
2715     JS_DefinePropertyWithTinyId(cx, ctor, SEPARATOR_PROPERTY, 0, vp,
2716                 JS_PropertyStub, JS_PropertyStub,
2717                 JSPROP_ENUMERATE | JSPROP_READONLY );
2718     return file;
2720 #endif /* JS_HAS_FILE_OBJECT */