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)
150 {
151 if ((errorNumber > 0) && (errorNumber < JSFileErr_Limit))
152 return &JSFile_ErrorFormatString[errorNumber];
153 else
154 return NULL;
155 }
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)
255 {
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
264 }
266 static JSBool
267 js_isAbsolute(const char *name)
268 {
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
280 }
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)
288 {
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;
306 }
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)
311 {
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;
338 }
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)
346 {
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;
395 }
397 static char *
398 js_absolutePath(JSContext *cx, const char * path)
399 {
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 }
426 }
428 /* Side effect: will remove spaces in the beginning/end of the filename */
429 static char *
430 js_canonicalPath(JSContext *cx, char *oldpath)
431 {
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;
528 }
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)
585 {
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);
624 }
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)
641 {
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 }
693 }
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)
705 {
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;
730 }
732 /* empty the buffer */
733 static void
734 js_ResetBuffers(JSFile * file)
735 {
736 file->charBufferUsed = JS_FALSE;
737 file->nbBytesInBuf = 0;
738 file->linebuffer = NULL; /* TODO: check for mem. leak? */
739 }
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);
754 }
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;
771 }
773 /* Buffered version of PR_Read. Used by js_FileRead */
774 static int32
775 js_BufferedRead(JSFile * f, char *buf, int32 len)
776 {
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;
795 }
797 static int32
798 js_FileRead(JSContext *cx, JSFile * file, jschar*buf, int32 len, int32 mode)
799 {
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;
874 }
876 static int32
877 js_FileSeek(JSContext *cx, JSFile *file, int32 len, int32 mode)
878 {
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;
932 }
934 static int32
935 js_FileWrite(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode)
936 {
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;
997 }
999 /* ----------------------------- Property checkers -------------------------- */
1000 static JSBool
1001 js_exists(JSContext *cx, JSFile *file)
1002 {
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 }
1009 }
1011 static JSBool
1012 js_canRead(JSContext *cx, JSFile *file)
1013 {
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 }
1025 }
1027 static JSBool
1028 js_canWrite(JSContext *cx, JSFile *file)
1029 {
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 }
1042 }
1044 static JSBool
1045 js_isFile(JSContext *cx, JSFile *file)
1046 {
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 }
1062 }
1064 static JSBool
1065 js_isDirectory(JSContext *cx, JSFile *file)
1066 {
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 }
1085 }
1087 static jsval
1088 js_size(JSContext *cx, JSFile *file)
1089 {
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;
1104 }
1106 /* Return the parent object */
1107 static jsval
1108 js_parent(JSContext *cx, JSFile *file)
1109 {
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 }
1125 }
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)));
1132 }
1134 /* ------------------------------ File object methods ---------------------------- */
1135 static JSBool
1136 file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1137 {
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;
1309 }
1311 static JSBool
1312 file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1313 {
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;
1350 }
1353 static JSBool
1354 file_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1355 {
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;
1375 }
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)
1380 {
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;
1471 }
1473 static JSBool
1474 file_renameTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1475 {
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;
1500 }
1502 static JSBool
1503 file_flush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1504 {
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;
1522 }
1524 static JSBool
1525 file_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1526 {
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;
1550 }
1552 static JSBool
1553 file_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1554 {
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;
1581 }
1583 static JSBool
1584 file_writeAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1585 {
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;
1618 }
1620 static JSBool
1621 file_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1622 {
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;
1656 }
1658 static JSBool
1659 file_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1660 {
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;
1729 }
1731 static JSBool
1732 file_readAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1733 {
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;
1755 }
1757 static JSBool
1758 file_seek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1759 {
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;
1795 }
1797 static JSBool
1798 file_list(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1799 {
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;
1897 }
1899 static JSBool
1900 file_mkdir(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1901 {
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;
1939 }
1941 static JSBool
1942 file_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval*rval)
1943 {
1944 JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
1946 *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, file->path));
1947 return JS_TRUE;
1948 }
1950 static JSBool
1951 file_toURL(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1952 {
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;
1971 }
1974 static void
1975 file_finalize(JSContext *cx, JSObject *obj)
1976 {
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 }
1991 }
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)
1998 {
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;
2016 }
2018 /* Returns a JSObject. This function is globally visible */
2019 JS_PUBLIC_API(JSObject*)
2020 js_NewFileObject(JSContext *cx, char *filename)
2021 {
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;
2034 }
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)
2040 {
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;
2068 }
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)
2077 {
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;
2098 }
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)
2174 {
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;
2446 }
2448 static JSBool
2449 file_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2450 {
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;
2502 }
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)
2509 {
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;
2544 }
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)
2556 {
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;
2609 }
2610 #endif /* JS_HAS_FILE_OBJECT */