Code

r11451@tres: ted | 2006-04-17 22:21:33 -0700
[inkscape.git] / src / dom / js / jsfile.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is Mozilla Communicator client code, released
17  * March 31, 1998.
18  *
19  * The Initial Developer of the Original Code is
20  * Netscape Communications Corporation.
21  * Portions created by the Initial Developer are Copyright (C) 1998
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either of the GNU General Public License Version 2 or later (the "GPL"),
28  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29  * in which case the provisions of the GPL or the LGPL are applicable instead
30  * of those above. If you wish to allow use of your version of this file only
31  * under the terms of either the GPL or the LGPL, and not to allow others to
32  * use your version of this file under the terms of the MPL, indicate your
33  * decision by deleting the provisions above and replace them with the notice
34  * and other provisions required by the GPL or the LGPL. If you do not delete
35  * the provisions above, a recipient may use your version of this file under
36  * the terms of any one of the MPL, the GPL or the LGPL.
37  *
38  * ***** END LICENSE BLOCK ***** */
40 /*
41  * JS File object
42  */
43 #if JS_HAS_FILE_OBJECT
45 #include "jsstddef.h"
47 /* ----------------- Platform-specific includes and defines ----------------- */
48 #ifdef XP_MAC
49 #   define FILESEPARATOR         ':'
50 #   define FILESEPARATOR2        '\0'
51 #   define CURRENT_DIR          "HARD DISK:Desktop Folder"
52 /*  TODO: #include <???> */
53 #elif defined(XP_WIN) || defined(XP_OS2)
54 #   include <direct.h>
55 #   include <io.h>
56 #   include <sys/types.h>
57 #   include <sys/stat.h>
58 #   define FILESEPARATOR        '\\'
59 #   define FILESEPARATOR2       '/'
60 #   define CURRENT_DIR          "c:\\"
61 #   define POPEN                _popen
62 #   define PCLOSE               _pclose
63 #elif defined(XP_UNIX) || defined(XP_BEOS)
64 #   include <strings.h>
65 #   include <stdio.h>
66 #   include <stdlib.h>
67 #   include <unistd.h>
68 #   define FILESEPARATOR        '/'
69 #   define FILESEPARATOR2       '\0'
70 #   define CURRENT_DIR          "/"
71 #   define POPEN                popen
72 #   define PCLOSE               pclose
73 #endif
75 /* --------------- Platform-independent includes and defines ---------------- */
76 #include "jsapi.h"
77 #include "jsatom.h"
78 #include "jscntxt.h"
79 #include "jsdate.h"
80 #include "jsdbgapi.h"
81 #include "jsemit.h"
82 #include "jsfun.h"
83 #include "jslock.h"
84 #include "jsobj.h"
85 #include "jsparse.h"
86 #include "jsscan.h"
87 #include "jsscope.h"
88 #include "jsscript.h"
89 #include "jsstr.h"
90 #include "jsutil.h" /* Added by JSIFY */
91 #include <string.h>
93 /* NSPR dependencies */
94 #include "prio.h"
95 #include "prerror.h"
97 #define SPECIAL_FILE_STRING     "Special File"
98 #define CURRENTDIR_PROPERTY     "currentDir"
99 #define SEPARATOR_PROPERTY      "separator"
100 #define FILE_CONSTRUCTOR        "File"
101 #define PIPE_SYMBOL             '|'
103 #define ASCII                   0
104 #define UTF8                    1
105 #define UCS2                    2
107 #define asciistring             "text"
108 #define utfstring               "binary"
109 #define unicodestring           "unicode"
111 #define MAX_PATH_LENGTH         1024
112 #define MODE_SIZE               256
113 #define NUMBER_SIZE             32
114 #define MAX_LINE_LENGTH         256
115 #define URL_PREFIX              "file://"
117 #define STDINPUT_NAME           "Standard input stream"
118 #define STDOUTPUT_NAME          "Standard output stream"
119 #define STDERROR_NAME           "Standard error stream"
121 #define RESOLVE_PATH            js_canonicalPath        /* js_absolutePath */
123 /* Error handling */
124 typedef enum JSFileErrNum {
125 #define MSG_DEF(name, number, count, exception, format) \
126     name = number,
127 #include "jsfile.msg"
128 #undef MSG_DEF
129     JSFileErr_Limit
130 #undef MSGDEF
131 } JSFileErrNum;
133 #define JSFILE_HAS_DFLT_MSG_STRINGS 1
135 JSErrorFormatString JSFile_ErrorFormatString[JSFileErr_Limit] = {
136 #if JSFILE_HAS_DFLT_MSG_STRINGS
137 #define MSG_DEF(name, number, count, exception, format) \
138     { format, count } ,
139 #else
140 #define MSG_DEF(name, number, count, exception, format) \
141     { NULL, count } ,
142 #endif
143 #include "jsfile.msg"
144 #undef MSG_DEF
145 };
147 const JSErrorFormatString *
148 JSFile_GetErrorMessage(void *userRef, const char *locale,
149                                                         const uintN errorNumber)
151     if ((errorNumber > 0) && (errorNumber < JSFileErr_Limit))
152         return &JSFile_ErrorFormatString[errorNumber];
153         else
154             return NULL;
157 #define JSFILE_CHECK_NATIVE(op)     \
158     if(file->isNative){         \
159         JS_ReportWarning(cx, "Cannot call or access \"%s\" on native file %s", \
160                                                 op, file->path);   \
161         goto out;   \
162     }
164 #define JSFILE_CHECK_WRITE      \
165     if (!file->isOpen){     \
166         JS_ReportWarning(cx,    \
167                 "File %s is closed, will open it for writing, proceeding",  \
168                 file->path);    \
169         js_FileOpen(cx, obj, file, "write,append,create");     \
170     }else   \
171     if(!js_canWrite(cx, file)){     \
172         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,  \
173             JSFILEMSG_CANNOT_WRITE, file->path);    \
174         goto out;   \
175     }
177 #define JSFILE_CHECK_READ      \
178     if (!file->isOpen){     \
179         JS_ReportWarning(cx,    \
180                 "File %s is closed, will open it for reading, proceeding",  \
181                 file->path); \
182         js_FileOpen(cx, obj, file, "read");     \
183     }else   \
184     if(!js_canRead(cx, file)){     \
185         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,  \
186             JSFILEMSG_CANNOT_READ, file->path);    \
187         goto out;   \
188     }
190 #define JSFILE_CHECK_OPEN(op)      \
191     if(!file->isOpen){     \
192         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,  \
193             JSFILEMSG_FILE_MUST_BE_CLOSED, op);    \
194         goto out;   \
195     }
197 #define JSFILE_CHECK_CLOSED(op)      \
198     if(file->isOpen){     \
199         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,  \
200             JSFILEMSG_FILE_MUST_BE_OPEN, op);    \
201         goto out;   \
202     }
204 #define JSFILE_CHECK_ONE_ARG(op)      \
205     if (argc!=1){   \
206         char str[NUMBER_SIZE];  \
207                                 \
208         sprintf(str, "%d", argc);   \
209         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,  \
210             JSFILEMSG_EXPECTS_ONE_ARG_ERROR, op, str);  \
211         goto out;   \
212     }
215 /*
216     Security mechanism, should define a callback for this.
217     The parameters are as follows:
218     SECURITY_CHECK(JSContext *cx, JSPrincipals *ps, char *op_name, JSFile *file)
219 */
220 #define SECURITY_CHECK(cx, ps, op, file)    \
221         /* Define a callback here... */
224 /* Structure representing the file internally */
225 typedef struct JSFile {
226     char        *path;          /* the path to the file. */
227     JSBool      isOpen;
228     JSString    *linebuffer;    /* temp buffer used by readln. */
229     int32       mode;           /* mode used to open the file: read, write, append, create, etc.. */
230     int32       type;           /* Asciiz, utf, unicode */
231     char        byteBuffer[3];  /* bytes read in advance by js_FileRead ( UTF8 encoding ) */
232     jsint       nbBytesInBuf;   /* number of bytes stored in the buffer above */
233     jschar      charBuffer;     /* character read in advance by readln ( mac files only ) */
234     JSBool      charBufferUsed; /* flag indicating if the buffer above is being used */
235     JSBool      hasRandomAccess;   /* can the file be randomly accessed? false for stdin, and
236                                  UTF-encoded files. */
237     JSBool      hasAutoflush;   /* should we force a flush for each line break? */
238     JSBool      isNative;       /* if the file is using OS-specific file FILE type */
239     /* We can actually put the following two in a union since they should never be used at the same time */
240     PRFileDesc  *handle;        /* the handle for the file, if open.  */
241     FILE        *nativehandle;  /* native handle, for stuff NSPR doesn't do. */
242     JSBool      isPipe;         /* if the file is really an OS pipe */
243 } JSFile;
245 /* a few forward declarations... */
246 static JSClass file_class;
247 JS_PUBLIC_API(JSObject*) js_NewFileObject(JSContext *cx, char *filename);
248 static JSBool file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
249 static JSBool file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
251 /* --------------------------- New filename manipulation procesures -------------------------- */
252 /* assumes we don't have leading/trailing spaces */
253 static JSBool
254 js_filenameHasAPipe(const char *filename)
256 #ifdef XP_MAC
257     /* pipes are not supported on the MAC */
258     return JS_FALSE;
259 #else
260     if(!filename) return JS_FALSE;
261     return  filename[0]==PIPE_SYMBOL ||
262             filename[strlen(filename)-1]==PIPE_SYMBOL;
263 #endif
266 static JSBool
267 js_isAbsolute(const char *name)
269 #if defined(XP_WIN) || defined(XP_OS2)
270     return (strlen(name)>1)?((name[1]==':')?JS_TRUE:JS_FALSE):JS_FALSE;
271 #else
272     return (name[0]
273 #   if defined(XP_UNIX) || defined(XP_BEOS)
274             ==
275 #   else
276             !=
277 #   endif
278             FILESEPARATOR)?JS_TRUE:JS_FALSE;
279 #endif
282 /*
283     Concatinates base and name to produce a valid filename.
284     Returned string must be freed.
285 */
286 static char*
287 js_combinePath(JSContext *cx, const char *base, const char *name)
289     int len = strlen(base);
290     char* result = (char*)JS_malloc(cx, len+strlen(name)+2);
292     if (!result)  return NULL;
294     strcpy(result, base);
296     if (base[len-1]!=FILESEPARATOR
297 #if defined(XP_WIN) || defined(XP_OS2)
298             && base[len-1]!=FILESEPARATOR2
299 #endif
300             ) {
301       result[len] = FILESEPARATOR;
302       result[len+1] = '\0';
303     }
304     strcat(result, name);
305     return result;
308 /* Extract the last component from a path name. Returned string must be freed */
309 static char *
310 js_fileBaseName(JSContext *cx, const char *pathname)
312     jsint index, aux;
313     char *result;
315 #if defined(XP_WIN) || defined(XP_OS2)
316     /* First, get rid of the drive selector */
317     if ((strlen(pathname)>=2)&&(pathname[1]==':')) {
318         pathname = &pathname[2];
319     }
320 #endif
321     index = strlen(pathname)-1;
322     /*
323         remove trailing separators -- don't necessarily need to check for
324         FILESEPARATOR2, but that's fine
325     */
326     while ((index>0)&&((pathname[index]==FILESEPARATOR)||
327                        (pathname[index]==FILESEPARATOR2))) index--;
328     aux = index;
329     /* now find the next separator */
330     while ((index>=0)&&(pathname[index]!=FILESEPARATOR)&&
331                       (pathname[index]!=FILESEPARATOR2)) index--;
332     /* allocate and copy */
333     result = (char*)JS_malloc(cx, aux-index+1);
334     if (!result)  return NULL;
335     strncpy(result, &pathname[index+1], aux-index);
336     result[aux-index] = '\0';
337     return result;
340 /*
341     Returns everytynig but the last component from a path name.
342     Returned string must be freed. Returned string must be freed.
343 */
344 static char *
345 js_fileDirectoryName(JSContext *cx, const char *pathname)
347     jsint index;
348     char  *result;
350 #if defined(XP_WIN) || defined(XP_OS2)
351     char  drive = '\0';
352     const char *oldpathname = pathname;
354     /* First, get rid of the drive selector */
355     if ((strlen(pathname)>=2)&&(pathname[1]==':')) {
356         drive = pathname[0];
357         pathname = &pathname[2];
358     }
359 #endif
360     index = strlen(pathname)-1;
361     while ((index>0)&&((pathname[index]==FILESEPARATOR)||
362                        (pathname[index]==FILESEPARATOR2))) index--;
363     while ((index>0)&&(pathname[index]!=FILESEPARATOR)&&
364                       (pathname[index]!=FILESEPARATOR2)) index--;
366     if (index>=0){
367         result = (char*)JS_malloc(cx, index+4);
368         if (!result)  return NULL;
369 #if defined(XP_WIN) || defined(XP_OS2)
370         if (drive!='\0') {
371             result[0] = toupper(drive);
372             result[1] = ':';
373             strncpy(&result[2], pathname, index);
374                         result[index+3] = '\0';
375         }else
376 #endif
377         {
378             strncpy(result, pathname, index);
379                         result[index] = '\0';
380         }
382         /* add terminating separator */
383         index = strlen(result)-1;
384         result[index] = FILESEPARATOR;
385         result[index+1] = '\0';
386     } else{
387 #if defined(XP_WIN) || defined(XP_OS2)
388         result = JS_strdup(cx, oldpathname); /* may include drive selector */
389 #else
390         result = JS_strdup(cx, pathname);
391 #endif
392     }
394     return result;
397 static char *
398 js_absolutePath(JSContext *cx, const char * path)
400     JSObject *obj;
401     JSString *str;
402     jsval prop;
404     if (js_isAbsolute(path)){
405         return JS_strdup(cx, path);
406     }else{
407         obj = JS_GetGlobalObject(cx);
408         if (!JS_GetProperty(cx, obj, FILE_CONSTRUCTOR, &prop)) {
409             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
410                  JSFILEMSG_FILE_CONSTRUCTOR_UNDEFINED_ERROR);
411             return JS_strdup(cx, path);
412         }
413         obj = JSVAL_TO_OBJECT(prop);
414         if (!JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, &prop)) {
415             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
416                  JSFILEMSG_FILE_CURRENTDIR_UNDEFINED_ERROR);
417             return JS_strdup(cx, path);
418         }
419         str = JS_ValueToString(cx, prop);
420         if (!str ) {
421             return JS_strdup(cx, path);
422         }
423         /* should we have an array of curr dirs indexed by drive for windows? */
424         return js_combinePath(cx, JS_GetStringBytes(str), path);
425     }
428 /* Side effect: will remove spaces in the beginning/end of the filename */
429 static char *
430 js_canonicalPath(JSContext *cx, char *oldpath)
432     char *tmp;
433     char *path = oldpath;
434     char *base, *dir, *current, *result;
435     jsint c;
436     jsint back = 0;
437     unsigned int i = 0, j = strlen(path)-1;
439     /* This is probably optional */
440         /* Remove possible spaces in the beginning and end */
441     while(i<strlen(path)-1 && path[i]==' ') i++;
442         while(j>=0 && path[j]==' ') j--;
444         tmp = JS_malloc(cx, j-i+2);
445         strncpy(tmp, &path[i], j-i+1);
446     tmp[j-i+1] = '\0';
448     path = tmp;
450     /* pipe support */
451     if(js_filenameHasAPipe(path))
452         return JS_strdup(cx, path);
453     /* file:// support */
454     if(!strncmp(path, URL_PREFIX, strlen(URL_PREFIX)))
455         return js_canonicalPath(cx, &path[strlen(URL_PREFIX)-1]);
457     if (!js_isAbsolute(path))
458         path = js_absolutePath(cx, path);
459     else
460         path = JS_strdup(cx, path);
462     result = JS_strdup(cx, "");
464     current = path;
466     base = js_fileBaseName(cx, current);
467     dir = js_fileDirectoryName(cx, current);
469     /* TODO: MAC -- not going to work??? */
470     while (strcmp(dir, current)) {
471         if (!strcmp(base, "..")) {
472             back++;
473         } else
474         if(!strcmp(base, ".")){
475             /* ??? */
476         } else {
477             if (back>0)
478                 back--;
479             else {
480                 tmp = result;
481                 result = JS_malloc(cx, strlen(base)+1+strlen(tmp)+1);
482                 if (!result) {
483                     JS_free(cx, dir);
484                     JS_free(cx, base);
485                     JS_free(cx, current);
486                     return NULL;
487                 }
488                 strcpy(result, base);
489                 c = strlen(result);
490                 if (*tmp) {
491                     result[c] = FILESEPARATOR;
492                     result[c+1] = '\0';
493                     strcat(result, tmp);
494                 }
495                 JS_free(cx, tmp);
496             }
497         }
498         JS_free(cx, current);
499         JS_free(cx, base);
500         current = dir;
501         base =  js_fileBaseName(cx, current);
502         dir = js_fileDirectoryName(cx, current);
503     }
505     tmp = result;
506     result = JS_malloc(cx, strlen(dir)+1+strlen(tmp)+1);
507     if (!result) {
508         JS_free(cx, dir);
509         JS_free(cx, base);
510         JS_free(cx, current);
511         return NULL;
512     }
513     strcpy(result, dir);
514     c = strlen(result);
515     if (tmp[0]!='\0') {
516         if ((result[c-1]!=FILESEPARATOR)&&(result[c-1]!=FILESEPARATOR2)) {
517             result[c] = FILESEPARATOR;
518             result[c+1] = '\0';
519         }
520         strcat(result, tmp);
521     }
522     JS_free(cx, tmp);
523     JS_free(cx, dir);
524     JS_free(cx, base);
525     JS_free(cx, current);
527     return result;
530 /* -------------------------- Text conversion ------------------------------- */
531 /* The following is ripped from libi18n/unicvt.c and include files.. */
533 /*
534  * UTF8 defines and macros
535  */
536 #define ONE_OCTET_BASE          0x00    /* 0xxxxxxx */
537 #define ONE_OCTET_MASK          0x7F    /* x1111111 */
538 #define CONTINUING_OCTET_BASE   0x80    /* 10xxxxxx */
539 #define CONTINUING_OCTET_MASK   0x3F    /* 00111111 */
540 #define TWO_OCTET_BASE          0xC0    /* 110xxxxx */
541 #define TWO_OCTET_MASK          0x1F    /* 00011111 */
542 #define THREE_OCTET_BASE        0xE0    /* 1110xxxx */
543 #define THREE_OCTET_MASK        0x0F    /* 00001111 */
544 #define FOUR_OCTET_BASE         0xF0    /* 11110xxx */
545 #define FOUR_OCTET_MASK         0x07    /* 00000111 */
546 #define FIVE_OCTET_BASE         0xF8    /* 111110xx */
547 #define FIVE_OCTET_MASK         0x03    /* 00000011 */
548 #define SIX_OCTET_BASE          0xFC    /* 1111110x */
549 #define SIX_OCTET_MASK          0x01    /* 00000001 */
551 #define IS_UTF8_1ST_OF_1(x) (( (x)&~ONE_OCTET_MASK  ) == ONE_OCTET_BASE)
552 #define IS_UTF8_1ST_OF_2(x) (( (x)&~TWO_OCTET_MASK  ) == TWO_OCTET_BASE)
553 #define IS_UTF8_1ST_OF_3(x) (( (x)&~THREE_OCTET_MASK) == THREE_OCTET_BASE)
554 #define IS_UTF8_1ST_OF_4(x) (( (x)&~FOUR_OCTET_MASK ) == FOUR_OCTET_BASE)
555 #define IS_UTF8_1ST_OF_5(x) (( (x)&~FIVE_OCTET_MASK ) == FIVE_OCTET_BASE)
556 #define IS_UTF8_1ST_OF_6(x) (( (x)&~SIX_OCTET_MASK  ) == SIX_OCTET_BASE)
557 #define IS_UTF8_2ND_THRU_6TH(x) \
558                     (( (x)&~CONTINUING_OCTET_MASK  ) == CONTINUING_OCTET_BASE)
559 #define IS_UTF8_1ST_OF_UCS2(x) \
560             IS_UTF8_1ST_OF_1(x) \
561             || IS_UTF8_1ST_OF_2(x) \
562             || IS_UTF8_1ST_OF_3(x)
565 #define MAX_UCS2            0xFFFF
566 #define DEFAULT_CHAR        0x003F  /* Default char is "?" */
567 #define BYTE_MASK           0xBF
568 #define BYTE_MARK           0x80
571 /* Function: one_ucs2_to_utf8_char
572  *
573  * Function takes one UCS-2 char and writes it to a UTF-8 buffer.
574  * We need a UTF-8 buffer because we don't know before this
575  * function how many bytes of utf-8 data will be written. It also
576  * takes a pointer to the end of the UTF-8 buffer so that we don't
577  * overwrite data. This function returns the number of UTF-8 bytes
578  * of data written, or -1 if the buffer would have been overrun.
579  */
581 #define LINE_SEPARATOR      0x2028
582 #define PARAGRAPH_SEPARATOR 0x2029
583 static int16 one_ucs2_to_utf8_char(unsigned char *tobufp,
584         unsigned char *tobufendp, uint16 onechar)
587      int16 numUTF8bytes = 0;
589     if((onechar == LINE_SEPARATOR)||(onechar == PARAGRAPH_SEPARATOR))
590     {
591         strcpy((char*)tobufp, "\n");
592         return strlen((char*)tobufp);;
593     }
595         if (onechar < 0x80) {               numUTF8bytes = 1;
596         } else if (onechar < 0x800) {       numUTF8bytes = 2;
597         } else if (onechar <= MAX_UCS2) {   numUTF8bytes = 3;
598         } else { numUTF8bytes = 2;
599                  onechar = DEFAULT_CHAR;
600         }
602         tobufp += numUTF8bytes;
604         /* return error if we don't have space for the whole character */
605         if (tobufp > tobufendp) {
606             return(-1);
607         }
610         switch(numUTF8bytes) {
612             case 3: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
613                     *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
614                     *--tobufp = onechar |  THREE_OCTET_BASE;
615                     break;
617             case 2: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
618                     *--tobufp = onechar | TWO_OCTET_BASE;
619                     break;
620             case 1: *--tobufp = (unsigned char)onechar;  break;
621         }
623         return(numUTF8bytes);
626 /*
627  * utf8_to_ucs2_char
628  *
629  * Convert a utf8 multibyte character to ucs2
630  *
631  * inputs: pointer to utf8 character(s)
632  *         length of utf8 buffer ("read" length limit)
633  *         pointer to return ucs2 character
634  *
635  * outputs: number of bytes in the utf8 character
636  *          -1 if not a valid utf8 character sequence
637  *          -2 if the buffer is too short
638  */
639 static int16
640 utf8_to_ucs2_char(const unsigned char *utf8p, int16 buflen, uint16 *ucs2p)
642     uint16 lead, cont1, cont2;
644     /*
645      * Check for minimum buffer length
646      */
647     if ((buflen < 1) || (utf8p == NULL)) {
648         return -2;
649     }
650     lead = (uint16) (*utf8p);
652     /*
653      * Check for a one octet sequence
654      */
655     if (IS_UTF8_1ST_OF_1(lead)) {
656         *ucs2p = lead & ONE_OCTET_MASK;
657         return 1;
658     }
660     /*
661      * Check for a two octet sequence
662      */
663     if (IS_UTF8_1ST_OF_2(*utf8p)) {
664         if (buflen < 2)
665             return -2;
666         cont1 = (uint16) *(utf8p+1);
667         if (!IS_UTF8_2ND_THRU_6TH(cont1))
668             return -1;
669         *ucs2p =  (lead & TWO_OCTET_MASK) << 6;
670         *ucs2p |= cont1 & CONTINUING_OCTET_MASK;
671         return 2;
672     }
674     /*
675      * Check for a three octet sequence
676      */
677     else if (IS_UTF8_1ST_OF_3(lead)) {
678         if (buflen < 3)
679             return -2;
680         cont1 = (uint16) *(utf8p+1);
681         cont2 = (uint16) *(utf8p+2);
682         if (   (!IS_UTF8_2ND_THRU_6TH(cont1))
683             || (!IS_UTF8_2ND_THRU_6TH(cont2)))
684             return -1;
685         *ucs2p =  (lead & THREE_OCTET_MASK) << 12;
686         *ucs2p |= (cont1 & CONTINUING_OCTET_MASK) << 6;
687         *ucs2p |= cont2 & CONTINUING_OCTET_MASK;
688         return 3;
689     }
690     else { /* not a valid utf8/ucs2 character */
691         return -1;
692     }
695 /* ----------------------------- Helper functions --------------------------- */
696 /* Ripped off from lm_win.c .. */
697 /* where is strcasecmp?.. for now, it's case sensitive..
698  *
699  * strcasecmp is in strings.h, but on windows it's called _stricmp...
700  * will need to #ifdef this
701 */
703 static int32
704 js_FileHasOption(JSContext *cx, const char *oldoptions, const char *name)
706     char *comma, *equal, *current;
707     char *options = JS_strdup(cx, oldoptions);
708     int32 found = 0;
710         current = options;
711     for (;;) {
712         comma = strchr(current, ',');
713         if (comma) *comma = '\0';
714         equal = strchr(current, '=');
715         if (equal) *equal = '\0';
716         if (strcmp(current, name) == 0) {
717             if (!equal || strcmp(equal + 1, "yes") == 0)
718                 found = 1;
719             else
720                 found = atoi(equal + 1);
721         }
722         if (equal) *equal = '=';
723         if (comma) *comma = ',';
724         if (found || !comma)
725             break;
726         current = comma + 1;
727     }
728     JS_free(cx, options);
729     return found;
732 /* empty the buffer */
733 static void
734 js_ResetBuffers(JSFile * file)
736     file->charBufferUsed = JS_FALSE;
737     file->nbBytesInBuf = 0;
738     file->linebuffer = NULL;    /* TODO: check for mem. leak? */
741 /* Reset file attributes */
742 static void
743 js_ResetAttributes(JSFile * file){
744         file->mode = file->type = 0;
745     file->isOpen = JS_FALSE;
746     file->handle = NULL;
747     file->nativehandle = NULL;
748     file->hasRandomAccess = JS_TRUE; /* innocent until proven guilty */
749         file->hasAutoflush = JS_FALSE;
750     file->isNative = JS_FALSE;
751     file->isPipe = JS_FALSE;
753         js_ResetBuffers(file);
756 static JSBool
757 js_FileOpen(JSContext *cx, JSObject *obj, JSFile *file, char *mode){
758     JSString *type, *mask;
759     jsval v[2];
760     jsval rval;
762     type =  JS_InternString(cx, asciistring);
763     mask =  JS_NewStringCopyZ(cx, mode);
764     v[0] = STRING_TO_JSVAL(mask);
765     v[1] = STRING_TO_JSVAL(type);
767     if (!file_open(cx, obj, 2, v, &rval)) {
768         return JS_FALSE;
769     }
770     return JS_TRUE;
773 /* Buffered version of PR_Read. Used by js_FileRead */
774 static int32
775 js_BufferedRead(JSFile * f, char *buf, int32 len)
777     int32 count = 0;
779     while (f->nbBytesInBuf>0&&len>0) {
780         buf[0] = f->byteBuffer[0];
781         f->byteBuffer[0] = f->byteBuffer[1];
782         f->byteBuffer[1] = f->byteBuffer[2];
783         f->nbBytesInBuf--;
784         len--;
785         buf+=1;
786         count++;
787     }
789     if (len>0) {
790         count+= (!f->isNative)?
791                     PR_Read(f->handle, buf, len):
792                     fread(buf, 1, len, f->nativehandle);
793     }
794     return count;
797 static int32
798 js_FileRead(JSContext *cx, JSFile * file, jschar*buf, int32 len, int32 mode)
800     unsigned char*aux;
801     int32 count, i;
802     jsint remainder;
803     unsigned char utfbuf[3];
805     if (file->charBufferUsed) {
806         buf[0] = file->charBuffer;
807         buf++;
808         len--;
809         file->charBufferUsed = JS_FALSE;
810     }
812     switch (mode) {
813     case ASCII:
814         aux = (unsigned char*)JS_malloc(cx, len);
815         if (!aux) {
816         return 0;
817         }
818         count = js_BufferedRead(file, aux, len);
819         if (count==-1) {
820         JS_free(cx, aux);
821         return 0;
822         }
823         for (i = 0;i<len;i++) {
824         buf[i] = (jschar)aux[i];
825         }
826         JS_free(cx, aux);
827         break;
828     case UTF8:
829         remainder = 0;
830         for (count = 0;count<len;count++) {
831             i = js_BufferedRead(file, utfbuf+remainder, 3-remainder);
832             if (i<=0) {
833                 return count;
834             }
835             i = utf8_to_ucs2_char(utfbuf, (int16)i, &buf[count] );
836             if (i<0) {
837                 return count;
838             } else {
839                 if (i==1) {
840                     utfbuf[0] = utfbuf[1];
841                     utfbuf[1] = utfbuf[2];
842                     remainder = 2;
843                 } else
844                 if (i==2) {
845                     utfbuf[0] = utfbuf[2];
846                     remainder = 1;
847                 } else
848                 if (i==3)
849                     remainder = 0;
850             }
851         }
852         while (remainder>0) {
853             file->byteBuffer[file->nbBytesInBuf] = utfbuf[0];
854             file->nbBytesInBuf++;
855             utfbuf[0] = utfbuf[1];
856             utfbuf[1] = utfbuf[2];
857             remainder--;
858         }
859         break;
860     case UCS2:
861         count = js_BufferedRead(file, (char*)buf, len*2)>>1;
862         if (count==-1) {
863             return 0;
864         }
865         break;
866     }
868     if(count==-1){
869         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
870             JSFILEMSG_OP_FAILED, "read", file->path);
871     }
873     return count;
876 static int32
877 js_FileSeek(JSContext *cx, JSFile *file, int32 len, int32 mode)
879     int32 count, i;
880     jsint remainder;
881     unsigned char utfbuf[3];
882     jschar tmp;
884     switch (mode) {
885     case ASCII:
886         count = PR_Seek(file->handle, len, PR_SEEK_CUR);
887         break;
888     case UTF8:
889         remainder = 0;
890         for (count = 0;count<len;count++) {
891             i = js_BufferedRead(file, utfbuf+remainder, 3-remainder);
892             if (i<=0) {
893                 return 0;
894             }
895             i = utf8_to_ucs2_char(utfbuf, (int16)i, &tmp );
896             if (i<0) {
897                 return 0;
898             } else {
900                 if (i==1) {
901                     utfbuf[0] = utfbuf[1];
902                     utfbuf[1] = utfbuf[2];
903                     remainder = 2;
904                 } else
905                 if (i==2) {
906                     utfbuf[0] = utfbuf[2];
907                     remainder = 1;
908                 } else
909                 if (i==3)
910                     remainder = 0;
911             }
912         }
913         while (remainder>0) {
914             file->byteBuffer[file->nbBytesInBuf] = utfbuf[0];
915             file->nbBytesInBuf++;
916             utfbuf[0] = utfbuf[1];
917             utfbuf[1] = utfbuf[2];
918             remainder--;
919         }
920       break;
921     case UCS2:
922         count = PR_Seek(file->handle, len*2, PR_SEEK_CUR)/2;
923         break;
924     }
926     if(count==-1){
927         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
928             JSFILEMSG_OP_FAILED, "seek", file->path);
929     }
931     return count;
934 static int32
935 js_FileWrite(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode)
937     unsigned char   *aux;
938     int32           count, i, j;
939     unsigned char   *utfbuf;
941     switch (mode) {
942     case ASCII:
943         aux = (unsigned char*)JS_malloc(cx, len);
944         if (!aux)  return 0;
946         for (i = 0; i<len; i++) {
947             aux[i]=buf[i]%256;
948         }
950         count = (!file->isNative)?
951                     PR_Write(file->handle, aux, len):
952                     fwrite(aux, 1, len, file->nativehandle);
954         if (count==-1) {
955             JS_free(cx, aux);
956             return 0;
957         }
958         JS_free(cx, aux);
959         break;
960     case UTF8:
961         utfbuf = (unsigned char*)JS_malloc(cx, len*3);
962         if (!utfbuf)  return 0;
963         i = 0;
964         for (count = 0;count<len;count++) {
965             j = one_ucs2_to_utf8_char(utfbuf+i, utfbuf+len*3, buf[count]);
966             if (j==-1) {
967                 JS_free(cx, utfbuf);
968                 return 0;
969             }
970             i+=j;
971         }
972         j = (!file->isNative)?
973                 PR_Write(file->handle, utfbuf, i):
974                 fwrite(utfbuf, 1, i, file->nativehandle);
976         if (j<i) {
977             JS_free(cx, utfbuf);
978             return 0;
979         }
980         JS_free(cx, utfbuf);
981       break;
982     case UCS2:
983         count = (!file->isNative)?
984                 PR_Write(file->handle, buf, len*2)>>1:
985                 fwrite(buf, 1, len*2, file->nativehandle)>>1;
987         if (count==-1) {
988             return 0;
989         }
990         break;
991     }
992     if(count==-1){
993         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
994             JSFILEMSG_OP_FAILED, "write", file->path);
995     }
996     return count;
999 /* ----------------------------- Property checkers -------------------------- */
1000 static JSBool
1001 js_exists(JSContext *cx, JSFile *file)
1003     if(!file->isNative){
1004         return (PR_Access(file->path, PR_ACCESS_EXISTS)==PR_SUCCESS);
1005     }else{
1006         /* doesn't make sense for a pipe of stdstream */
1007         return JS_FALSE;
1008     }
1011 static JSBool
1012 js_canRead(JSContext *cx, JSFile *file)
1014     if(!file->isNative){
1015         if(file->isOpen&&!(file->mode&PR_RDONLY)) return JS_FALSE;
1016         return (PR_Access(file->path, PR_ACCESS_READ_OK)==PR_SUCCESS);
1017     }else{
1018         if(file->isPipe){
1019             /* pipe open for reading */
1020             return file->path[0]==PIPE_SYMBOL;
1021         }else{
1022             return !strcmp(file->path, STDINPUT_NAME);
1023         }
1024     }
1027 static JSBool
1028 js_canWrite(JSContext *cx, JSFile *file)
1030     if(!file->isNative){
1031         if(file->isOpen&&!(file->mode&PR_WRONLY)) return JS_FALSE;
1032         return (PR_Access(file->path, PR_ACCESS_WRITE_OK)==PR_SUCCESS);
1033     }else{
1034         if(file->isPipe){
1035             /* pipe open for writing */
1036             return file->path[strlen(file->path)-1]==PIPE_SYMBOL;
1037         }else{
1038             return  !strcmp(file->path, STDOUTPUT_NAME) ||
1039                     !strcmp(file->path, STDERROR_NAME);
1040         }
1041     }
1044 static JSBool
1045 js_isFile(JSContext *cx, JSFile *file)
1047     if(!file->isNative){
1048         PRFileInfo info;
1050         if ((file->isOpen)?
1051                         PR_GetOpenFileInfo(file->handle, &info):
1052                         PR_GetFileInfo(file->path, &info)!=PR_SUCCESS){
1053             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1054                 JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
1055             return JS_FALSE;
1056         }else
1057             return (info.type==PR_FILE_FILE);
1058     }else{
1059         /* doesn't make sense for a pipe of stdstream */
1060         return JS_FALSE;
1061     }
1064 static JSBool
1065 js_isDirectory(JSContext *cx, JSFile *file)
1067     if(!file->isNative){
1068         PRFileInfo info;
1070                 /* hack needed to get get_property to work */
1071                 if(!js_exists(cx, file)) return JS_FALSE;
1073         if ((file->isOpen)?
1074                         PR_GetOpenFileInfo(file->handle, &info):
1075                         PR_GetFileInfo(file->path, &info)!=PR_SUCCESS){
1076             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1077                 JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
1078             return JS_FALSE;
1079         }else
1080             return (info.type==PR_FILE_DIRECTORY);
1081     }else{
1082         /* doesn't make sense for a pipe of stdstream */
1083         return JS_FALSE;
1084     }
1087 static jsval
1088 js_size(JSContext *cx, JSFile *file)
1090     PRFileInfo info;
1092     JSFILE_CHECK_NATIVE("size");
1094     if ((file->isOpen)?
1095                     PR_GetOpenFileInfo(file->handle, &info):
1096                     PR_GetFileInfo(file->path, &info)!=PR_SUCCESS){
1097         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1098             JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
1099         goto out;
1100     }else
1101         return INT_TO_JSVAL(info.size);
1102 out:
1103     return JSVAL_VOID;
1106 /* Return the parent object */
1107 static jsval
1108 js_parent(JSContext *cx, JSFile *file)
1110     char *str;
1112     /* since we only care about pipes and native files, return NULL */
1113     if(file->isNative)  return JSVAL_VOID;
1115     str = js_fileDirectoryName(cx, file->path);
1116     /* root.parent = null ??? */
1117     if(!strcmp(file->path, str) ||
1118             (!strncmp(str, file->path, strlen(str)-1)&&
1119             file->path[strlen(file->path)]-1)==FILESEPARATOR){
1120         return JSVAL_NULL;
1121     }else{
1122         return OBJECT_TO_JSVAL(js_NewFileObject(cx, str));
1123         JS_free(cx, str);
1124     }
1127 static jsval
1128 js_name(JSContext *cx, JSFile *file){
1129     return  file->isPipe?
1130                 JSVAL_VOID:
1131                 STRING_TO_JSVAL(JS_NewStringCopyZ(cx, js_fileBaseName(cx, file->path)));
1134 /* ------------------------------ File object methods ---------------------------- */
1135 static JSBool
1136 file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1138     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1139     JSString    *strmode, *strtype;
1140     char        *ctype, *mode;
1141     int32       mask, type;
1142     int         len;
1144     SECURITY_CHECK(cx, NULL, "open", file);
1146     /* A native file that is already open */
1147     if(file->isOpen && file->isNative){
1148         JS_ReportWarning(cx, "Native file %s is already open, proceeding",
1149             file->path);
1150         goto good;
1151     }
1153     /* Close before proceeding */
1154     if (file->isOpen) {
1155         JS_ReportWarning(cx,
1156             "File %s is already open, we will close it and reopen, proceeding",
1157             file->path);
1158         if(!file_close(cx, obj, 0, NULL, rval)) goto out;
1159     }
1161     if(js_isDirectory(cx, file)){
1162         JS_ReportWarning(cx, "%s seems to be a directory, there is no point in "
1163                 "trying to open it, proceeding", file->path);
1164         goto good;
1165     }
1167     /* Path must be defined at this point */
1168     len = strlen(file->path);
1170     /* Mode */
1171     if (argc>=1){
1172         strmode = JS_ValueToString(cx, argv[0]);
1173         if (!strmode){
1174             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1175                 JSFILEMSG_FIRST_ARGUMENT_OPEN_NOT_STRING_ERROR, argv[0]);
1176             goto out;
1177         }
1178         mode = JS_strdup(cx, JS_GetStringBytes(strmode));
1179     }else{
1180         if(file->path[0]==PIPE_SYMBOL){
1181             /* pipe default mode */
1182             mode = JS_strdup(cx, "read");
1183         }else
1184         if(file->path[len-1]==PIPE_SYMBOL){
1185             /* pipe default mode */
1186             mode = JS_strdup(cx, "write");
1187         }else{
1188             /* non-destructive, permissive defaults. */
1189             mode = JS_strdup(cx, "readWrite,append,create");
1190         }
1191     }
1193     /* Process the mode */
1194     mask = 0;
1195     /* TODO: this is pretty ugly, BTW, we walk thru the string too many times */
1196     mask|=(js_FileHasOption(cx, mode, "read"))?      PR_RDONLY       :   0;
1197     mask|=(js_FileHasOption(cx, mode, "write"))?     PR_WRONLY       :   0;
1198     mask|=(js_FileHasOption(cx, mode, "readWrite"))? PR_RDWR         :   0;
1199     mask|=(js_FileHasOption(cx, mode, "append"))?    PR_APPEND       :   0;
1200     mask|=(js_FileHasOption(cx, mode, "create"))?    PR_CREATE_FILE  :   0;
1201     mask|=(js_FileHasOption(cx, mode, "replace"))?   PR_TRUNCATE     :   0;
1203     if((mask&PR_RDWR)) mask|=(PR_RDONLY|PR_WRONLY);
1204     if((mask&PR_RDONLY)&&(mask&PR_WRONLY)) mask|=PR_RDWR;
1206     file->hasAutoflush|=(js_FileHasOption(cx, mode, "autoflush"));
1208     /* Type */
1209     if (argc>1) {
1210         strtype = JS_ValueToString(cx, argv[1]);
1211         if (!strtype) {
1212             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1213                 JSFILEMSG_SECOND_ARGUMENT_OPEN_NOT_STRING_ERROR, argv[1]);
1214             goto out;
1215         }
1216         ctype = JS_GetStringBytes(strtype);
1218         if(!strcmp(ctype, utfstring))
1219             type = UTF8;
1220         else
1221         if (!strcmp(ctype, unicodestring))
1222             type = UCS2;
1223         else{
1224             if(strcmp(ctype, asciistring)){
1225                 JS_ReportWarning(cx, "File type %s is not supported, using "
1226                         "'text' instead, proceeding", ctype);
1227             }
1228             type = ASCII;
1229         }
1230     }else{
1231         type = ASCII;
1232     }
1234     /* Save the relevant fields */
1235     file->type = type;
1236     file->mode = mask;
1237     file->nativehandle = NULL;
1238     file->hasRandomAccess = (type!=UTF8);
1240     /*
1241                 Deal with pipes here. We can't use NSPR for pipes,
1242         so we have to use POPEN.
1243         */
1244     if(file->path[0]==PIPE_SYMBOL || file->path[len-1]==PIPE_SYMBOL){
1245         if(file->path[0]==PIPE_SYMBOL && file->path[len-1]==PIPE_SYMBOL){
1246             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1247                                 JSFILEMSG_BIDIRECTIONAL_PIPE_NOT_SUPPORTED);
1248                 goto out;
1249         }else{
1250             char pipemode[3];
1251             SECURITY_CHECK(cx, NULL, "pipe_open", file);
1253             if(file->path[0] == PIPE_SYMBOL){
1254                 if(mask & (PR_WRONLY | PR_APPEND | PR_CREATE_FILE | PR_TRUNCATE)){
1255                     JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1256                                                 JSFILEMSG_OPEN_MODE_NOT_SUPPORTED_WITH_PIPES,
1257                                                 mode, file->path);
1258                     goto out;
1259                 }
1260                 /* open(SPOOLER, "| cat -v | lpr -h 2>/dev/null") -- pipe for writing */
1261                 pipemode[0] = 'r';
1262                 pipemode[1] = file->type==UTF8?'b':'t';
1263                 pipemode[2] = '\0';
1264                 file->nativehandle = POPEN(&file->path[1], pipemode);
1265             }else
1266             if(file->path[len-1] == PIPE_SYMBOL){
1267                 char *command = JS_malloc(cx, len);
1269                 strncpy(command, file->path, len-1);
1270                 command[len-1] = '\0';
1271                 /* open(STATUS, "netstat -an 2>&1 |") */
1272                 pipemode[0] = 'w';
1273                 pipemode[1] = file->type==UTF8?'b':'t';
1274                 pipemode[2] = '\0';
1275                 file->nativehandle = POPEN(command, pipemode);
1276                                 JS_free(cx, command);
1277             }
1278             /* set the flags */
1279             file->isNative = JS_TRUE;
1280             file->isPipe  = JS_TRUE;
1281             file->hasRandomAccess = JS_FALSE;
1282         }
1283     }else{
1284         /* TODO: what about the permissions?? Java ignores the problem... */
1285         file->handle = PR_Open(file->path, mask, 0644);
1286     }
1288     js_ResetBuffers(file);
1289     JS_free(cx, mode);
1290     mode = NULL;
1292     /* Set the open flag and return result */
1293     if (file->handle==NULL && file->nativehandle==NULL){
1294                 file->isOpen = JS_FALSE;
1296         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1297             JSFILEMSG_OP_FAILED, "open", file->path);
1298         goto out;
1299     }else
1300         goto good;
1301 good:
1302     file->isOpen = JS_TRUE;
1303     *rval = JSVAL_TRUE;
1304     return JS_TRUE;
1305 out:
1306     if(mode) JS_free(cx, mode);
1307     *rval = JSVAL_VOID;
1308     return JS_FALSE;
1311 static JSBool
1312 file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1314     JSFile  *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1316     SECURITY_CHECK(cx, NULL, "close", file);
1318     if(!file->isOpen){
1319         JS_ReportWarning(cx, "File %s is not open, can't close it, proceeding",
1320             file->path);
1321         goto out;
1322     }
1324     if(!file->isPipe){
1325         if(file->isNative){
1326             JS_ReportWarning(cx, "Unable to close a native file, proceeding", file->path);
1327             goto out;
1328         }else{
1329             if(file->handle && PR_Close(file->handle)){
1330                 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1331                     JSFILEMSG_OP_FAILED, "close", file->path);
1333                 goto out;
1334             }
1335         }
1336     }else{
1337         if(PCLOSE(file->nativehandle)==-1){
1338             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1339                 JSFILEMSG_OP_FAILED, "pclose", file->path);
1340             goto out;
1341         }
1342     }
1344     js_ResetAttributes(file);
1345     *rval = JSVAL_TRUE;
1346     return JS_TRUE;
1347 out:
1348     *rval = JSVAL_FALSE;
1349     return JS_FALSE;
1353 static JSBool
1354 file_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1356         JSFile  *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1358     SECURITY_CHECK(cx, NULL, "remove", file);
1359     JSFILE_CHECK_NATIVE("remove");
1360     JSFILE_CHECK_CLOSED("remove");
1362     if ((js_isDirectory(cx, file) ?
1363             PR_RmDir(file->path) : PR_Delete(file->path))==PR_SUCCESS) {
1364         js_ResetAttributes(file);
1365         *rval = JSVAL_TRUE;
1366         return JS_TRUE;
1367     } else {
1368         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1369             JSFILEMSG_OP_FAILED, "remove", file->path);
1370         goto out;
1371     }
1372 out:
1373     *rval = JSVAL_FALSE;
1374     return JS_FALSE;
1377 /* Raw PR-based function. No text processing. Just raw data copying. */
1378 static JSBool
1379 file_copyTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1381     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1382     char        *dest = NULL;
1383     PRFileDesc  *handle = NULL;
1384     char        *buffer;
1385     jsval               count, size;
1386     JSBool      fileInitiallyOpen=JS_FALSE;
1388     SECURITY_CHECK(cx, NULL, "copyTo", file);   /* may need a second argument!*/
1389     JSFILE_CHECK_ONE_ARG("copyTo");
1390     JSFILE_CHECK_NATIVE("copyTo");
1391     /* remeber the state */
1392     fileInitiallyOpen = file->isOpen;
1393     JSFILE_CHECK_READ;
1395     dest = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
1397     /* make sure we are not reading a file open for writing */
1398     if (file->isOpen && !js_canRead(cx, file)) {
1399         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1400                 JSFILEMSG_CANNOT_COPY_FILE_OPEN_FOR_WRITING_ERROR, file->path);
1401         goto out;
1402     }
1404     if (file->handle==NULL){
1405         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1406             JSFILEMSG_OP_FAILED, "open", file->path);
1407         goto out;
1408     }
1410     handle = PR_Open(dest, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, 0644);
1412     if(!handle){
1413         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1414             JSFILEMSG_OP_FAILED, "open", dest);
1415         goto out;
1416     }
1418     if ((size=js_size(cx, file))==JSVAL_VOID) {
1419         goto out;
1420     }
1422     buffer = JS_malloc(cx, size);
1424     count = INT_TO_JSVAL(PR_Read(file->handle, buffer, size));
1426     /* reading panic */
1427     if (count!=size) {
1428         JS_free(cx, buffer);
1429         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1430               JSFILEMSG_COPY_READ_ERROR, file->path);
1431         goto out;
1432     }
1434     count = INT_TO_JSVAL(PR_Write(handle, buffer, JSVAL_TO_INT(size)));
1436     /* writing panic */
1437     if (count!=size) {
1438         JS_free(cx, buffer);
1439         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1440               JSFILEMSG_COPY_WRITE_ERROR, file->path);
1441         goto out;
1442     }
1444     JS_free(cx, buffer);
1446         if(!fileInitiallyOpen){
1447                 if(!file_close(cx, obj, 0, NULL, rval)) goto out;
1448         }
1450     if(PR_Close(handle)!=PR_SUCCESS){
1451         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1452               JSFILEMSG_OP_FAILED, "close", dest);
1453         goto out;
1454     }
1456     *rval = JSVAL_TRUE;
1457     return JS_TRUE;
1458 out:
1459     if(file->isOpen && !fileInitiallyOpen){
1460         if(PR_Close(file->handle)!=PR_SUCCESS){
1461             JS_ReportWarning(cx, "Can't close %s, proceeding", file->path);
1462         }
1463     }
1465     if(handle && PR_Close(handle)!=PR_SUCCESS){
1466         JS_ReportWarning(cx, "Can't close %s, proceeding", dest);
1467     }
1469     *rval = JSVAL_FALSE;
1470     return JS_FALSE;
1473 static JSBool
1474 file_renameTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1476     JSFile  *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1477     char    *dest;
1479     SECURITY_CHECK(cx, NULL, "renameTo", file); /* may need a second argument!*/
1480     JSFILE_CHECK_ONE_ARG("renameTo");
1481     JSFILE_CHECK_NATIVE("renameTo");
1482     JSFILE_CHECK_CLOSED("renameTo");
1484     dest = RESOLVE_PATH(cx, JS_GetStringBytes(JS_ValueToString(cx, argv[0])));
1486     if (PR_Rename(file->path, dest)==PR_SUCCESS){
1487         /* copy the new filename */
1488         JS_free(cx, file->path);
1489         file->path = dest;
1490         *rval = JSVAL_TRUE;
1491         return JS_TRUE;
1492     }else{
1493         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1494             JSFILEMSG_RENAME_FAILED, file->path, dest);
1495         goto out;
1496     }
1497 out:
1498     *rval = JSVAL_FALSE;
1499     return JS_FALSE;
1502 static JSBool
1503 file_flush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1505     JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1507     SECURITY_CHECK(cx, NULL, "flush", file);
1508     JSFILE_CHECK_NATIVE("flush");
1509     JSFILE_CHECK_OPEN("flush");
1511     if (PR_Sync(file->handle)==PR_SUCCESS){
1512       *rval = JSVAL_TRUE;
1513       return JS_TRUE;
1514     }else{
1515         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1516            JSFILEMSG_OP_FAILED, "flush", file->path);
1517        goto out;
1518     }
1519 out:
1520     *rval = JSVAL_FALSE;
1521     return JS_FALSE;
1524 static JSBool
1525 file_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1527     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1528     JSString    *str;
1529     int32       count;
1530     uintN       i;
1532     SECURITY_CHECK(cx, NULL, "write", file);
1533     JSFILE_CHECK_WRITE;
1535     for (i = 0; i<argc; i++) {
1536         str = JS_ValueToString(cx, argv[i]);
1537         count = js_FileWrite(cx, file, JS_GetStringChars(str),
1538             JS_GetStringLength(str), file->type);
1539         if (count==-1){
1540           *rval = JSVAL_FALSE;
1541           return JS_FALSE;
1542         }
1543     }
1545     *rval = JSVAL_TRUE;
1546     return JS_TRUE;
1547 out:
1548     *rval = JSVAL_FALSE;
1549     return JS_FALSE;
1552 static JSBool
1553 file_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1555     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1556     JSString    *str;
1558     SECURITY_CHECK(cx, NULL, "writeln", file);
1559     JSFILE_CHECK_WRITE;
1561     /* don't report an error here */
1562     if(!file_write(cx, obj, argc, argv, rval))  return JS_FALSE;
1563     /* don't do security here -- we passed the check in file_write */
1564     str = JS_NewStringCopyZ(cx, "\n");
1566     if (js_FileWrite(cx, file, JS_GetStringChars(str), JS_GetStringLength(str),
1567             file->type)==-1){
1568         *rval = JSVAL_FALSE;
1569         return JS_FALSE;
1570     }
1572     /* eol causes flush if hasAutoflush is turned on */
1573     if (file->hasAutoflush)
1574         file_flush(cx, obj, 0, NULL, rval);
1576     *rval =  JSVAL_TRUE;
1577     return JS_TRUE;
1578 out:
1579     *rval = JSVAL_FALSE;
1580     return JS_FALSE;
1583 static JSBool
1584 file_writeAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1586     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1587     jsuint      i;
1588     jsuint      limit;
1589     JSObject    *array;
1590     JSObject    *elem;
1591     jsval       elemval;
1593     SECURITY_CHECK(cx, NULL, "writeAll", file);
1594     JSFILE_CHECK_ONE_ARG("writeAll");
1595     JSFILE_CHECK_WRITE;
1597     if (!JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[0]))) {
1598         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1599             JSFILEMSG_FIRST_ARGUMENT_WRITEALL_NOT_ARRAY_ERROR);
1600         goto out;
1601     }
1603     array = JSVAL_TO_OBJECT(argv[0]);
1605     JS_GetArrayLength(cx, array, &limit);
1607     for (i = 0; i<limit; i++) {
1608         if (!JS_GetElement(cx, array, i, &elemval))  return JS_FALSE;
1609         elem = JSVAL_TO_OBJECT(elemval);
1610         file_writeln(cx, obj, 1, &elemval, rval);
1611     }
1613     *rval = JSVAL_TRUE;
1614     return JS_TRUE;
1615 out:
1616     *rval = JSVAL_FALSE;
1617     return JS_FALSE;
1620 static JSBool
1621 file_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1623     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1624     JSString    *str;
1625     int32       want, count;
1626     jschar      *buf;
1628     SECURITY_CHECK(cx, NULL, "read", file);
1629     JSFILE_CHECK_ONE_ARG("read");
1630     JSFILE_CHECK_READ;
1632     if (!JS_ValueToInt32(cx, argv[0], &want)){
1633         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1634             JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "read", argv[0]);
1635         goto out;
1636     }
1638     /* want = (want>262144)?262144:want; * arbitrary size limitation */
1640     buf = JS_malloc(cx, want*sizeof buf[0]);
1641     if (!buf)  goto out;
1643     count =  js_FileRead(cx, file, buf, want, file->type);
1644     if (count>0) {
1645         str = JS_NewUCStringCopyN(cx, buf, count);
1646         *rval = STRING_TO_JSVAL(str);
1647         JS_free(cx, buf);
1648         return JS_TRUE;
1649     } else {
1650         JS_free(cx, buf);
1651         goto out;
1652     }
1653 out:
1654     *rval = JSVAL_FALSE;
1655     return JS_FALSE;
1658 static JSBool
1659 file_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1661     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1662     JSString    *str;
1663     jschar      *buf;
1664     int32       offset;
1665     intN        room;
1666     jschar      data, data2;
1667     JSBool      endofline;
1669     SECURITY_CHECK(cx, NULL, "readln", file);
1670     JSFILE_CHECK_READ;
1672     if (!file->linebuffer) {
1673         buf = JS_malloc(cx, MAX_LINE_LENGTH*(sizeof data));
1674         if (!buf) goto out;
1675         file->linebuffer = JS_NewUCString(cx, buf, MAX_LINE_LENGTH);
1676     }
1677     room = JS_GetStringLength(file->linebuffer);
1678     offset = 0;
1680     /* XXX TEST ME!! TODO: yes, please do */
1681     for(;;) {
1682         if (!js_FileRead(cx, file, &data, 1, file->type)) {
1683             endofline = JS_FALSE;
1684             goto loop;
1685         }
1686         switch (data) {
1687         case '\n' :
1688             endofline = JS_TRUE;
1689             goto loop;
1690         case '\r' :
1691             if (!js_FileRead(cx, file, &data2, 1, file->type)) {
1692                 endofline = JS_TRUE;
1693                 goto loop;
1694             }
1695             if (data2!='\n') { /* We read one char too far.  Buffer it. */
1696                 file->charBuffer = data2;
1697                 file->charBufferUsed = JS_TRUE;
1698             }
1699             endofline = JS_TRUE;
1700             goto loop;
1701         default:
1702             if (--room < 0) {
1703                 buf = JS_malloc(cx, (offset+MAX_LINE_LENGTH)*sizeof data);
1704                 if (!buf) return JS_FALSE;
1705                 room = MAX_LINE_LENGTH-1;
1706                 memcpy(buf, JS_GetStringChars(file->linebuffer),
1707                     JS_GetStringLength(file->linebuffer));
1708                 /* what follows may not be the cleanest way. */
1709                 file->linebuffer->chars = buf;
1710                 file->linebuffer->length =  offset+MAX_LINE_LENGTH;
1711             }
1712             file->linebuffer->chars[offset++] = data;
1713             break;
1714         }
1715     }
1716 loop:
1717     file->linebuffer->chars[offset] = 0;
1718     if ((endofline==JS_TRUE)) {
1719         str = JS_NewUCStringCopyN(cx, JS_GetStringChars(file->linebuffer),
1720                                     offset);
1721         *rval = STRING_TO_JSVAL(str);
1722         return JS_TRUE;
1723     }else{
1724         goto out;
1725     }
1726 out:
1727     *rval = JSVAL_NULL;
1728     return JS_FALSE;
1731 static JSBool
1732 file_readAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1734     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1735     JSObject    *array;
1736     jsint       len;
1737     jsval       line;
1739     SECURITY_CHECK(cx, NULL, "readAll", file);
1740     JSFILE_CHECK_READ;
1742     array = JS_NewArrayObject(cx, 0, NULL);
1743     len = 0;
1745     while(file_readln(cx, obj, 0, NULL, &line)){
1746         JS_SetElement(cx, array, len, &line);
1747         len++;
1748     }
1750     *rval = OBJECT_TO_JSVAL(array);
1751     return JS_TRUE;
1752 out:
1753     *rval = JSVAL_FALSE;
1754     return JS_FALSE;
1757 static JSBool
1758 file_seek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1760     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1761     int32       toskip;
1762     int32       pos;
1764     SECURITY_CHECK(cx, NULL, "seek", file);
1765     JSFILE_CHECK_ONE_ARG("seek");
1766     JSFILE_CHECK_NATIVE("seek");
1767     JSFILE_CHECK_READ;
1769     if (!JS_ValueToInt32(cx, argv[0], &toskip)){
1770         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1771             JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "seek", argv[0]);
1772         goto out;
1773     }
1775     if(!file->hasRandomAccess){
1776         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1777             JSFILEMSG_NO_RANDOM_ACCESS, file->path);
1778        goto out;
1779     }
1781     if(js_isDirectory(cx, file)){
1782         JS_ReportWarning(cx,"Seek on directories is not supported, proceeding");
1783         goto out;
1784     }
1786     pos = js_FileSeek(cx, file, toskip, file->type);
1788     if (pos!=-1) {
1789         *rval = INT_TO_JSVAL(pos);
1790         return JS_TRUE;
1791     }
1792 out:
1793     *rval = JSVAL_VOID;
1794     return JS_FALSE;
1797 static JSBool
1798 file_list(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1800     PRDir       *dir;
1801     PRDirEntry  *entry;
1802     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1803     JSObject    *array;
1804     JSObject    *eachFile;
1805     jsint       len;
1806     jsval       v;
1807     JSRegExp    *re = NULL;
1808     JSFunction  *func = NULL;
1809     JSString    *str;
1810     jsval       args[1];
1811     char        *filePath;
1813     SECURITY_CHECK(cx, NULL, "list", file);
1814     JSFILE_CHECK_NATIVE("list");
1816     if (argc==1) {
1817         if (JSVAL_IS_REGEXP(cx, argv[0])) {
1818             re = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0]));
1819         }else
1820         if (JSVAL_IS_FUNCTION(cx, argv[0])) {
1821             func = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0]));
1822         }else{
1823             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1824                 JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_FUNCTION_OR_REGEX, argv[0]);
1825             goto out;
1826         }
1827     }
1829     if (!js_isDirectory(cx, file)) {
1830         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1831             JSFILEMSG_CANNOT_DO_LIST_ON_A_FILE, file->path);
1832         goto out;
1833     }
1835     dir = PR_OpenDir(file->path);
1836     if(!dir){
1837         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1838             JSFILEMSG_OP_FAILED, "open", file->path);
1839         goto out;
1840     }
1842     /* create JSArray here... */
1843     array = JS_NewArrayObject(cx, 0, NULL);
1844     len = 0;
1846     while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))!=NULL) {
1847         /* first, check if we have a regexp */
1848         if (re!=NULL) {
1849             size_t index = 0;
1851             str = JS_NewStringCopyZ(cx, entry->name);
1852             if(!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &v)){
1853                 /* don't report anything here */
1854                 goto out;
1855             }
1856             /* not matched! */
1857             if (JSVAL_IS_NULL(v)) {
1858                 continue;
1859             }
1860         }else
1861         if (func!=NULL) {
1862             str = JS_NewStringCopyZ(cx, entry->name);
1863             args[0] = STRING_TO_JSVAL(str);
1864             if(!JS_CallFunction(cx, obj, func, 1, args, &v)){
1865                 goto out;
1866             }
1868             if (v==JSVAL_FALSE) {
1869                 continue;
1870             }
1871         }
1873         filePath = js_combinePath(cx, file->path, (char*)entry->name);
1875         eachFile = js_NewFileObject(cx, filePath);
1876         JS_free(cx, filePath);
1877         if (!eachFile){
1878             JS_ReportWarning(cx, "File %s cannot be retrieved", filePath);
1879             continue;
1880         }
1881         v = OBJECT_TO_JSVAL(eachFile);
1882         JS_SetElement(cx, array, len, &v);
1883         JS_SetProperty(cx, array, entry->name, &v);
1884         len++;
1885     }
1887     if(PR_CloseDir(dir)!=PR_SUCCESS){
1888         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1889             JSFILEMSG_OP_FAILED, "close", file->path);
1890         goto out;
1891     }
1892     *rval = OBJECT_TO_JSVAL(array);
1893     return JS_TRUE;
1894 out:
1895     *rval = JSVAL_NULL;
1896     return JS_FALSE;
1899 static JSBool
1900 file_mkdir(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1902     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1904     SECURITY_CHECK(cx, NULL, "mkdir", file);
1905     JSFILE_CHECK_ONE_ARG("mkdir");
1906     JSFILE_CHECK_NATIVE("mkdir");
1908     /* if the current file is not a directory, find out the directory name */
1909     if (!js_isDirectory(cx, file)) {
1910         char        *dir = js_fileDirectoryName(cx, file->path);
1911         JSObject    *dirObj = js_NewFileObject(cx, dir);
1913         JS_free(cx, dir);
1915         /* call file_mkdir with the right set of parameters if needed */
1916         if (file_mkdir(cx, dirObj, argc, argv, rval))
1917                         return JS_TRUE;
1918                 else
1919             goto out;
1920     }else{
1921         char *dirName = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
1922         char *fullName;
1924         fullName = js_combinePath(cx, file->path, dirName);
1925         if (PR_MkDir(fullName, 0755)==PR_SUCCESS){
1926             *rval = JSVAL_TRUE;
1927             JS_free(cx, fullName);
1928             return JS_TRUE;
1929         }else{
1930             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1931                 JSFILEMSG_OP_FAILED, "mkdir", fullName);
1932             JS_free(cx, fullName);
1933             goto out;
1934         }
1935     }
1936 out:
1937     *rval = JSVAL_FALSE;
1938     return JS_FALSE;
1941 static JSBool
1942 file_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval*rval)
1944     JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1946     *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, file->path));
1947     return JS_TRUE;
1950 static JSBool
1951 file_toURL(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1953     JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1954     char url[MAX_PATH_LENGTH];
1955     jschar *urlChars;
1957         JSFILE_CHECK_NATIVE("toURL");
1959     sprintf(url, "file://%s", file->path);
1960     /* TODO: js_escape in jsstr.h may go away at some point */
1962     urlChars = js_InflateString(cx, url, strlen(url));
1963     if (urlChars == NULL) return JS_FALSE;
1964     *rval = STRING_TO_JSVAL(js_NewString(cx, urlChars, strlen(url), 0));
1965     if (!js_str_escape(cx, obj, 0, rval, rval)) return JS_FALSE;
1967     return JS_TRUE;
1968 out:
1969     *rval = JSVAL_VOID;
1970     return JS_FALSE;
1974 static void
1975 file_finalize(JSContext *cx, JSObject *obj)
1977     JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1979         if(file){
1980         /* close the file before exiting */
1981         if(file->isOpen && !file->isNative){
1982             jsval vp;
1983             file_close(cx, obj, 0, NULL, &vp);
1984         }
1986                 if (file->path)
1987                         JS_free(cx, file->path);
1989                 JS_free(cx, file);
1990         }
1993 /*
1994     Allocates memory for the file object, sets fields to defaults.
1995 */
1996 static JSFile*
1997 file_init(JSContext *cx, JSObject *obj, char *bytes)
1999     JSFile      *file;
2001     file = JS_malloc(cx, sizeof *file);
2002     if (!file) return NULL;
2003     memset(file, 0 , sizeof *file);
2005     js_ResetAttributes(file);
2007     file->path = RESOLVE_PATH(cx, bytes);
2009     if (!JS_SetPrivate(cx, obj, file)) {
2010         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2011             JSFILEMSG_CANNOT_SET_PRIVATE_FILE, file->path);
2012         JS_free(cx, file);
2013         return NULL;
2014     }else
2015         return file;
2018 /* Returns a JSObject. This function is globally visible */
2019 JS_PUBLIC_API(JSObject*)
2020 js_NewFileObject(JSContext *cx, char *filename)
2022     JSObject    *obj;
2023     JSFile      *file;
2025     obj = JS_NewObject(cx, &file_class, NULL, NULL);
2026     if (!obj){
2027         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2028             JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObject");
2029         return NULL;
2030     }
2031     file = file_init(cx, obj, filename);
2032     if(!file) return NULL;
2033     return obj;
2036 /* Internal function, used for cases which NSPR file support doesn't cover */
2037 JSObject*
2038 js_NewFileObjectFromFILE(JSContext *cx, FILE *nativehandle, char *filename,
2039     int32 mode, JSBool open, JSBool randomAccess)
2041     JSObject *obj;
2042     JSFile   *file;
2043 #ifdef XP_MAC
2044     JS_ReportWarning(cx, "Native files are not fully supported on the MAC");
2045 #endif
2047     obj = JS_NewObject(cx, &file_class, NULL, NULL);
2048     if (!obj){
2049         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2050             JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObjectFromFILE");
2051         return NULL;
2052     }
2053     file = file_init(cx, obj, filename);
2054     if(!file) return NULL;
2056     file->nativehandle = nativehandle;
2058     /* free result of RESOLVE_PATH from file_init. */
2059     JS_ASSERT(file->path != NULL);
2060     JS_free(cx, file->path);
2062     file->path = strdup(filename);
2063     file->isOpen = open;
2064     file->mode = mode;
2065     file->hasRandomAccess = randomAccess;
2066     file->isNative = JS_TRUE;
2067     return obj;
2070 /*
2071     Real file constructor that is called from JavaScript.
2072     Basically, does error processing and calls file_init.
2073 */
2074 static JSBool
2075 file_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
2076     jsval *rval)
2078     JSString *str;
2079     JSFile   *file;
2081     str = (argc==0)?JS_InternString(cx, ""):JS_ValueToString(cx, argv[0]);
2083     if (!str){
2084         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2085             JSFILEMSG_FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR, argv[0]);
2086         goto out;
2087     }
2089     file = file_init(cx, obj, JS_GetStringBytes(str));
2090     if (!file)  goto out;
2092     SECURITY_CHECK(cx, NULL, "constructor", file);
2094     return JS_TRUE;
2095 out:
2096     *rval = JSVAL_VOID;
2097     return JS_FALSE;
2100 /* -------------------- File methods and properties ------------------------- */
2101 static JSFunctionSpec file_functions[] = {
2102     { "open",           file_open, 0},
2103     { "close",          file_close, 0},
2104     { "remove",         file_remove, 0},
2105     { "copyTo",         file_copyTo, 0},
2106     { "renameTo",       file_renameTo, 0},
2107     { "flush",          file_flush, 0},
2108     { "seek",           file_seek, 0},
2109     { "read",           file_read, 0},
2110     { "readln",         file_readln, 0},
2111     { "readAll",        file_readAll, 0},
2112     { "write",          file_write, 0},
2113     { "writeln",        file_writeln, 0},
2114     { "writeAll",       file_writeAll, 0},
2115     { "list",           file_list, 0},
2116     { "mkdir",          file_mkdir, 0},
2117         { "toString",       file_toString, 0},
2118     { "toURL",                  file_toURL, 0},
2119     {0}
2120 };
2122 enum file_tinyid {
2123     FILE_LENGTH             = -2,
2124     FILE_PARENT             = -3,
2125     FILE_PATH               = -4,
2126     FILE_NAME               = -5,
2127     FILE_ISDIR              = -6,
2128     FILE_ISFILE             = -7,
2129     FILE_EXISTS             = -8,
2130     FILE_CANREAD            = -9,
2131     FILE_CANWRITE           = -10,
2132     FILE_OPEN               = -11,
2133     FILE_TYPE               = -12,
2134     FILE_MODE               = -13,
2135     FILE_CREATED            = -14,
2136     FILE_MODIFIED           = -15,
2137     FILE_SIZE               = -16,
2138     FILE_RANDOMACCESS       = -17,
2139     FILE_POSITION           = -18,
2140     FILE_APPEND             = -19,
2141     FILE_REPLACE            = -20,
2142     FILE_AUTOFLUSH          = -21,
2143     FILE_ISNATIVE           = -22,
2144 };
2146 static JSPropertySpec file_props[] = {
2147    {"length",          FILE_LENGTH,        JSPROP_ENUMERATE | JSPROP_READONLY },
2148    {"parent",          FILE_PARENT,        JSPROP_ENUMERATE | JSPROP_READONLY },
2149    {"path",            FILE_PATH,          JSPROP_ENUMERATE | JSPROP_READONLY },
2150    {"name",            FILE_NAME,          JSPROP_ENUMERATE | JSPROP_READONLY },
2151    {"isDirectory",     FILE_ISDIR,         JSPROP_ENUMERATE | JSPROP_READONLY },
2152    {"isFile",          FILE_ISFILE,        JSPROP_ENUMERATE | JSPROP_READONLY },
2153    {"exists",          FILE_EXISTS,        JSPROP_ENUMERATE | JSPROP_READONLY },
2154    {"canRead",         FILE_CANREAD,       JSPROP_ENUMERATE | JSPROP_READONLY },
2155    {"canWrite",        FILE_CANWRITE,      JSPROP_ENUMERATE | JSPROP_READONLY },
2156    {"canAppend",       FILE_APPEND,        JSPROP_ENUMERATE | JSPROP_READONLY },
2157    {"canReplace",      FILE_REPLACE,       JSPROP_ENUMERATE | JSPROP_READONLY },
2158    {"isOpen",          FILE_OPEN,          JSPROP_ENUMERATE | JSPROP_READONLY },
2159    {"type",            FILE_TYPE,          JSPROP_ENUMERATE | JSPROP_READONLY },
2160    {"mode",            FILE_MODE,          JSPROP_ENUMERATE | JSPROP_READONLY },
2161    {"creationTime",    FILE_CREATED,       JSPROP_ENUMERATE | JSPROP_READONLY },
2162    {"lastModified",    FILE_MODIFIED,      JSPROP_ENUMERATE | JSPROP_READONLY },
2163    {"size",            FILE_SIZE,          JSPROP_ENUMERATE | JSPROP_READONLY },
2164    {"hasRandomAccess", FILE_RANDOMACCESS,  JSPROP_ENUMERATE | JSPROP_READONLY },
2165    {"hasAutoFlush",    FILE_AUTOFLUSH,     JSPROP_ENUMERATE | JSPROP_READONLY },
2166    {"position",        FILE_POSITION,      JSPROP_ENUMERATE },
2167    {"isNative",        FILE_ISNATIVE,      JSPROP_ENUMERATE | JSPROP_READONLY },
2168    {0}
2169 };
2171 /* ------------------------- Property getter/setter ------------------------- */
2172 static JSBool
2173 file_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2175     JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
2176     char        *str;
2177     jsint       tiny;
2178     PRFileInfo  info;
2179         JSBool          flag;
2180     PRExplodedTime
2181                 expandedTime;
2183     tiny = JSVAL_TO_INT(id);
2184     if(!file) return JS_TRUE;
2186     switch (tiny) {
2187     case FILE_PARENT:
2188         SECURITY_CHECK(cx, NULL, "parent", file);
2189         *vp = js_parent(cx, file);
2190         break;
2191     case FILE_PATH:
2192         *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, file->path));
2193         break;
2194     case FILE_NAME:
2195         *vp = js_name(cx, file);
2196         break;
2197     case FILE_ISDIR:
2198         SECURITY_CHECK(cx, NULL, "isDirectory", file);
2199         *vp = BOOLEAN_TO_JSVAL(js_isDirectory(cx, file));
2200         break;
2201     case FILE_ISFILE:
2202         SECURITY_CHECK(cx, NULL, "isFile", file);
2203         *vp = BOOLEAN_TO_JSVAL(js_isFile(cx, file));
2204         break;
2205     case FILE_EXISTS:
2206         SECURITY_CHECK(cx, NULL, "exists", file);
2207         *vp = BOOLEAN_TO_JSVAL(js_exists(cx, file));
2208         break;
2209     case FILE_ISNATIVE:
2210         SECURITY_CHECK(cx, NULL, "isNative", file);
2211         *vp = BOOLEAN_TO_JSVAL(file->isNative);
2212         break;
2213     case FILE_CANREAD:
2214         SECURITY_CHECK(cx, NULL, "canRead", file);
2215         *vp = BOOLEAN_TO_JSVAL(js_canRead(cx, file));
2216         break;
2217     case FILE_CANWRITE:
2218         SECURITY_CHECK(cx, NULL, "canWrite", file);
2219         *vp = BOOLEAN_TO_JSVAL(js_canWrite(cx, file));
2220         break;
2221     case FILE_OPEN:
2222         SECURITY_CHECK(cx, NULL, "isOpen", file);
2223         *vp = BOOLEAN_TO_JSVAL(file->isOpen);
2224         break;
2225     case FILE_APPEND :
2226         SECURITY_CHECK(cx, NULL, "canAppend", file);
2227         JSFILE_CHECK_OPEN("canAppend");
2228         *vp = BOOLEAN_TO_JSVAL(!file->isNative &&
2229                 (file->mode&PR_APPEND)==PR_APPEND);
2230         break;
2231     case FILE_REPLACE :
2232         SECURITY_CHECK(cx, NULL, "canReplace", file);
2233         JSFILE_CHECK_OPEN("canReplace");
2234         *vp = BOOLEAN_TO_JSVAL(!file->isNative &&
2235                 (file->mode&PR_TRUNCATE)==PR_TRUNCATE);
2236         break;
2237     case FILE_AUTOFLUSH :
2238         SECURITY_CHECK(cx, NULL, "hasAutoFlush", file);
2239         JSFILE_CHECK_OPEN("hasAutoFlush");
2240         *vp = BOOLEAN_TO_JSVAL(!file->isNative && file->hasAutoflush);
2241         break;
2242     case FILE_TYPE:
2243         SECURITY_CHECK(cx, NULL, "type", file);
2244         JSFILE_CHECK_OPEN("type");
2245         if(js_isDirectory(cx, file)){
2246             *vp = JSVAL_VOID;
2247             break;
2248         }
2250         switch (file->type) {
2251         case ASCII:
2252             *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, asciistring));
2253             break;
2254         case UTF8:
2255             *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, utfstring));
2256             break;
2257         case UCS2:
2258             *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, unicodestring));
2259             break;
2260         default:
2261             JS_ReportWarning(cx, "Unsupported file type %d, proceeding",
2262                 file->type);
2263         }
2264         break;
2265     case FILE_MODE:
2266         SECURITY_CHECK(cx, NULL, "mode", file);
2267         JSFILE_CHECK_OPEN("mode");
2268         str = (char*)JS_malloc(cx, MODE_SIZE);
2269         str[0] = '\0';
2270         flag = JS_FALSE;
2272         if ((file->mode&PR_RDONLY)==PR_RDONLY) {
2273             if (flag) strcat(str, ",");
2274             strcat(str, "read");
2275             flag = JS_TRUE;
2276         }
2277         if ((file->mode&PR_WRONLY)==PR_WRONLY) {
2278             if (flag) strcat(str, ",");
2279             strcat(str, "write");
2280             flag = JS_TRUE;
2281         }
2282         if ((file->mode&PR_RDWR)==PR_RDWR) {
2283             if (flag) strcat(str, ",");
2284             strcat(str, "readWrite");
2285             flag = JS_TRUE;
2286         }
2287         if ((file->mode&PR_APPEND)==PR_APPEND) {
2288             if (flag) strcat(str, ",");
2289             strcat(str, "append");
2290             flag = JS_TRUE;
2291         }
2292         if ((file->mode&PR_CREATE_FILE)==PR_CREATE_FILE) {
2293             if (flag) strcat(str, ",");
2294             strcat(str, "create");
2295             flag = JS_TRUE;
2296         }
2297         if ((file->mode&PR_TRUNCATE)==PR_TRUNCATE) {
2298             if (flag) strcat(str, ",");
2299             strcat(str, "replace");
2300             flag = JS_TRUE;
2301         }
2302         if (file->hasAutoflush) {
2303             if (flag) strcat(str, ",");
2304             strcat(str, "hasAutoFlush");
2305             flag = JS_TRUE;
2306         }
2307         *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str));
2308         JS_free(cx, str);
2309         break;
2310     case FILE_CREATED:
2311         SECURITY_CHECK(cx, NULL, "creationTime", file);
2312         JSFILE_CHECK_NATIVE("creationTime");
2313         if(((file->isOpen)?
2314                         PR_GetOpenFileInfo(file->handle, &info):
2315                         PR_GetFileInfo(file->path, &info))!=PR_SUCCESS){
2316             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2317                 JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
2318             goto out;
2319         }
2321         PR_ExplodeTime(info.creationTime, PR_LocalTimeParameters,&expandedTime);
2322         *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx,  expandedTime.tm_year,
2323                                     expandedTime.tm_month,
2324                                     expandedTime.tm_mday,
2325                                     expandedTime.tm_hour,
2326                                     expandedTime.tm_min,
2327                                     expandedTime.tm_sec));
2328         break;
2329     case FILE_MODIFIED:
2330         SECURITY_CHECK(cx, NULL, "lastModified", file);
2331         JSFILE_CHECK_NATIVE("lastModified");
2332         if(((file->isOpen)?
2333                         PR_GetOpenFileInfo(file->handle, &info):
2334                         PR_GetFileInfo(file->path, &info))!=PR_SUCCESS){
2335             JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2336                 JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
2337             goto out;
2338         }
2340         PR_ExplodeTime(info.modifyTime, PR_LocalTimeParameters, &expandedTime);
2341         *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx, expandedTime.tm_year,
2342                                     expandedTime.tm_month,
2343                                     expandedTime.tm_mday,
2344                                     expandedTime.tm_hour,
2345                                     expandedTime.tm_min,
2346                                     expandedTime.tm_sec));
2347         break;
2348     case FILE_SIZE:
2349         SECURITY_CHECK(cx, NULL, "size", file);
2350         *vp = js_size(cx, file);
2351         break;
2352     case FILE_LENGTH:
2353         SECURITY_CHECK(cx, NULL, "length", file);
2354         JSFILE_CHECK_NATIVE("length");
2356         if (js_isDirectory(cx, file)) { /* XXX debug me */
2357             PRDir       *dir;
2358             PRDirEntry  *entry;
2359             jsint       count = 0;
2361             if(!(dir = PR_OpenDir(file->path))){
2362                 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2363                     JSFILEMSG_CANNOT_OPEN_DIR, file->path);
2364                 goto out;
2365             }
2367             while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))) {
2368                 count++;
2369             }
2371             if(!PR_CloseDir(dir)){
2372                 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2373                     JSFILEMSG_OP_FAILED, "close", file->path);
2375                 goto out;
2376             }
2378             *vp = INT_TO_JSVAL(count);
2379             break;
2380         }else{
2381             /* return file size */
2382             *vp = js_size(cx, file);
2383         }
2384         break;
2385     case FILE_RANDOMACCESS:
2386             SECURITY_CHECK(cx, NULL, "hasRandomAccess", file);
2387             JSFILE_CHECK_OPEN("hasRandomAccess");
2388             *vp = BOOLEAN_TO_JSVAL(file->hasRandomAccess);
2389         break;
2390     case FILE_POSITION:
2391         SECURITY_CHECK(cx, NULL, "position", file);
2392         JSFILE_CHECK_NATIVE("position");
2393         JSFILE_CHECK_OPEN("position");
2395         if(!file->hasRandomAccess){
2396             JS_ReportWarning(cx, "File %s doesn't support random access, can't report the position, proceeding");
2397             *vp = JSVAL_VOID;
2398             break;
2399         }
2401         if (file->isOpen && js_isFile(cx, file)) {
2402             int pos = PR_Seek(file->handle, 0, PR_SEEK_CUR);
2403             if(pos!=-1){
2404                 *vp = INT_TO_JSVAL(pos);
2405             }else{
2406                 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2407                     JSFILEMSG_CANNOT_REPORT_POSITION, file->path);
2408                 goto out;
2409             }
2410         }else {
2411             JS_ReportWarning(cx, "File %s is closed or not a plain file,"
2412                 " can't report position, proceeding");
2413             goto out;
2414         }
2415         break;
2416     default:
2417         SECURITY_CHECK(cx, NULL, "file_access", file);
2418                 /* this is some other property -- try to use the dir["file"] syntax */
2419         if(js_isDirectory(cx, file)){
2420                         PRDir *dir = NULL;
2421                         PRDirEntry *entry = NULL;
2422             char *prop_name = JS_GetStringBytes(JS_ValueToString(cx, id));
2424             /* no native files past this point */
2425             dir = PR_OpenDir(file->path);
2426             if(!dir) {
2427                 /* This is probably not a directory */
2428                                 JS_ReportWarning(cx, "Can't open directory %s", file->path);
2429                 return JS_FALSE;
2430             }
2432             while((entry = PR_ReadDir(dir, PR_SKIP_NONE))!=NULL){
2433                                 if(!strcmp(entry->name, prop_name)){
2434                     str = js_combinePath(cx, file->path, prop_name);
2435                     *vp = OBJECT_TO_JSVAL(js_NewFileObject(cx, str));
2436                                         JS_free(cx, str);
2437                     return JS_TRUE;
2438                                 }
2439                         }
2440                 }
2441     }
2442     return JS_TRUE;
2443 out:
2444         *vp = JSVAL_VOID;
2445     return JS_FALSE;
2448 static JSBool
2449 file_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2451     JSFile  *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
2452     jsint   slot;
2454     if (JSVAL_IS_STRING(id)){
2455         return JS_TRUE;
2456     }
2458     slot = JSVAL_TO_INT(id);
2460     switch (slot) {
2461     /* File.position  = 10 */
2462     case FILE_POSITION:
2463         SECURITY_CHECK(cx, NULL, "set_position", file);
2464         JSFILE_CHECK_NATIVE("set_position");
2466         if(!file->hasRandomAccess){
2467             JS_ReportWarning(cx, "File %s doesn't support random access, can't "
2468                 "report the position, proceeding");
2469             goto out;
2470         }
2472         if (file->isOpen && js_isFile(cx, file)) {
2473             int32 pos;
2474                     int32 offset;
2476                         if (!JS_ValueToInt32(cx, *vp, &offset)){
2477                                 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2478                                         JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "position", *vp);
2479                                 goto out;
2480                         }
2482                         pos = PR_Seek(file->handle, offset, PR_SEEK_SET);
2484             if(pos!=-1){
2485                 *vp = INT_TO_JSVAL(pos);
2486             }else{
2487                 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2488                     JSFILEMSG_CANNOT_SET_POSITION, file->path);
2489                 goto out;
2490             }
2491         } else {
2492             JS_ReportWarning(cx, "File %s is closed or not a file, can't set "
2493                 "position, proceeding", file->path);
2494             goto out;
2495         }
2496     }
2498     return JS_TRUE;
2499 out:
2500         *vp = JSVAL_VOID;
2501         return JS_FALSE;
2504 /*
2505     File.currentDir = new File("D:\") or File.currentDir = "D:\"
2506 */
2507 static JSBool
2508 file_currentDirSetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2510     JSObject *rhsObject;
2511     char     *path;
2512     JSFile   *file = JS_GetInstancePrivate(cx, rhsObject, &file_class, NULL);
2514     /* Look at the rhs and extract a file object from it */
2515     if (JSVAL_IS_OBJECT(*vp)){
2516         if (JS_InstanceOf(cx, rhsObject, &file_class, NULL)){
2517             /* Braindamaged rhs -- just return the old value */
2518             if (file && (!js_exists(cx, file) || !js_isDirectory(cx, file))){
2519                 JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp);
2520                 goto out;
2521             }else{
2522                 rhsObject = JSVAL_TO_OBJECT(*vp);
2523                 chdir(file->path);
2524                 return JS_TRUE;
2525             }
2526         }else
2527             goto out;
2528     }else{
2529         path      = JS_GetStringBytes(JS_ValueToString(cx, *vp));
2530         rhsObject = js_NewFileObject(cx, path);
2531         if (!rhsObject)  goto out;
2533         if (!file || !js_exists(cx, file) || !js_isDirectory(cx, file)){
2534             JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp);
2535         }else{
2536             *vp = OBJECT_TO_JSVAL(rhsObject);
2537             chdir(path);
2538         }
2539     }
2540     return JS_TRUE;
2541 out:
2542         *vp = JSVAL_VOID;
2543         return JS_FALSE;
2546 /* Declare class */
2547 static JSClass file_class = {
2548     FILE_CONSTRUCTOR, JSCLASS_HAS_PRIVATE,
2549     JS_PropertyStub,  JS_PropertyStub,  file_getProperty,  file_setProperty,
2550     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   file_finalize
2551 };
2553 /* -------------------- Functions exposed to the outside -------------------- */
2554 JS_PUBLIC_API(JSObject*)
2555 js_InitFileClass(JSContext *cx, JSObject* obj, JSBool initStandardStreams)
2557     JSObject *file, *ctor, *afile;
2558     jsval    vp;
2559     char     *currentdir;
2560     char     separator[2];
2562     file = JS_InitClass(cx, obj, NULL, &file_class, file_constructor, 1,
2563         file_props, file_functions, NULL, NULL);
2564     if (!file) {
2565         JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2566             JSFILEMSG_INIT_FAILED);
2567         return NULL;
2568     }
2570     ctor = JS_GetConstructor(cx, file);
2571     if (!ctor)  return NULL;
2573         /* Define CURRENTDIR property. We are doing this to get a
2574         slash at the end of the current dir */
2575     afile = js_NewFileObject(cx, CURRENT_DIR);
2576     currentdir =  JS_malloc(cx, MAX_PATH_LENGTH);
2577     currentdir =  getcwd(currentdir, MAX_PATH_LENGTH);
2578     afile = js_NewFileObject(cx, currentdir);
2579     JS_free(cx, currentdir);
2580     vp = OBJECT_TO_JSVAL(afile);
2581     JS_DefinePropertyWithTinyId(cx, ctor, CURRENTDIR_PROPERTY, 0, vp,
2582                 JS_PropertyStub, file_currentDirSetter,
2583                 JSPROP_ENUMERATE | JSPROP_READONLY );
2585     if(initStandardStreams){
2586         /* Code to create stdin, stdout, and stderr. Insert in the appropriate place. */
2587         /* Define input */
2588         vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stdin,
2589                 STDINPUT_NAME, PR_RDONLY, JS_TRUE, JS_FALSE));
2590         JS_SetProperty(cx, ctor, "input", &vp);
2592         /* Define output */
2593         vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stdout,
2594                 STDOUTPUT_NAME, PR_WRONLY, JS_TRUE, JS_FALSE));
2595         JS_SetProperty(cx, ctor, "output", &vp);
2597         /* Define error */
2598         vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stderr,
2599                 STDERROR_NAME, PR_WRONLY, JS_TRUE, JS_FALSE));
2600         JS_SetProperty(cx, ctor, "error", &vp);
2601     }
2602     separator[0] = FILESEPARATOR;
2603     separator[1] = '\0';
2604     vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, separator));
2605     JS_DefinePropertyWithTinyId(cx, ctor, SEPARATOR_PROPERTY, 0, vp,
2606                 JS_PropertyStub, JS_PropertyStub,
2607                 JSPROP_ENUMERATE | JSPROP_READONLY );
2608     return file;
2610 #endif /* JS_HAS_FILE_OBJECT */