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