Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / js / src / jsfile.cpp
blob15d323d9e33c07c97b4de51b56a35ea691b659d0
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=78:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
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.
25 * Contributor(s):
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.
39 * ***** END LICENSE BLOCK ***** */
42 * JS File object
44 #if JS_HAS_FILE_OBJECT
46 #include "jsstddef.h"
47 #include "jsfile.h"
49 /* ----------------- Platform-specific includes and defines ----------------- */
50 #if defined(XP_WIN) || defined(XP_OS2)
51 # include <direct.h>
52 # include <io.h>
53 # include <sys/types.h>
54 # include <sys/stat.h>
55 # define FILESEPARATOR '\\'
56 # define FILESEPARATOR2 '/'
57 # define CURRENT_DIR "c:\\"
58 # define POPEN _popen
59 # define PCLOSE _pclose
60 #elif defined(XP_UNIX) || defined(XP_BEOS)
61 # include <strings.h>
62 # include <stdio.h>
63 # include <stdlib.h>
64 # include <unistd.h>
65 # define FILESEPARATOR '/'
66 # define FILESEPARATOR2 '\0'
67 # define CURRENT_DIR "/"
68 # define POPEN popen
69 # define PCLOSE pclose
70 #endif
72 /* --------------- Platform-independent includes and defines ---------------- */
73 #include "jsapi.h"
74 #include "jsatom.h"
75 #include "jscntxt.h"
76 #include "jsdate.h"
77 #include "jsdbgapi.h"
78 #include "jsemit.h"
79 #include "jsfun.h"
80 #include "jslock.h"
81 #include "jsobj.h"
82 #include "jsparse.h"
83 #include "jsscan.h"
84 #include "jsscope.h"
85 #include "jsscript.h"
86 #include "jsstr.h"
87 #include "jsutil.h" /* Added by JSIFY */
88 #include <string.h>
90 /* NSPR dependencies */
91 #include "prio.h"
92 #include "prerror.h"
94 #define SPECIAL_FILE_STRING "Special File"
95 #define CURRENTDIR_PROPERTY "currentDir"
96 #define SEPARATOR_PROPERTY "separator"
97 #define FILE_CONSTRUCTOR "File"
98 #define PIPE_SYMBOL '|'
100 #define ASCII 0
101 #define UTF8 1
102 #define UCS2 2
104 #define asciistring "text"
105 #define utfstring "binary"
106 #define unicodestring "unicode"
108 #define MAX_PATH_LENGTH 1024
109 #define MODE_SIZE 256
110 #define NUMBER_SIZE 32
111 #define MAX_LINE_LENGTH 256
112 #define URL_PREFIX "file://"
114 #define STDINPUT_NAME "Standard input stream"
115 #define STDOUTPUT_NAME "Standard output stream"
116 #define STDERROR_NAME "Standard error stream"
118 #define RESOLVE_PATH js_canonicalPath /* js_absolutePath */
120 /* Error handling */
121 typedef enum JSFileErrNum {
122 #define MSG_DEF(name, number, count, exception, format) \
123 name = number,
124 #include "jsfile.msg"
125 #undef MSG_DEF
126 JSFileErr_Limit
127 #undef MSGDEF
128 } JSFileErrNum;
130 #define JSFILE_HAS_DFLT_MSG_STRINGS 1
132 JSErrorFormatString JSFile_ErrorFormatString[JSFileErr_Limit] = {
133 #if JSFILE_HAS_DFLT_MSG_STRINGS
134 #define MSG_DEF(name, number, count, exception, format) \
135 { format, count },
136 #else
137 #define MSG_DEF(name, number, count, exception, format) \
138 { NULL, count },
139 #endif
140 #include "jsfile.msg"
141 #undef MSG_DEF
144 const JSErrorFormatString *
145 JSFile_GetErrorMessage(void *userRef, const char *locale,
146 const uintN errorNumber)
148 if ((errorNumber > 0) && (errorNumber < JSFileErr_Limit))
149 return &JSFile_ErrorFormatString[errorNumber];
150 else
151 return NULL;
154 #define JSFILE_CHECK_NATIVE(op) \
155 if (file->isNative) { \
156 JS_ReportWarning(cx, "Cannot call or access \"%s\" on native file %s",\
157 op, file->path); \
158 goto out; \
161 #define JSFILE_CHECK_WRITE \
162 if (!file->isOpen) { \
163 JS_ReportWarning(cx, \
164 "File %s is closed, will open it for writing, proceeding", \
165 file->path); \
166 js_FileOpen(cx, obj, file, "write,append,create"); \
168 if (!js_canWrite(cx, file)) { \
169 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \
170 JSFILEMSG_CANNOT_WRITE, file->path); \
171 goto out; \
174 #define JSFILE_CHECK_READ \
175 if (!file->isOpen) { \
176 JS_ReportWarning(cx, \
177 "File %s is closed, will open it for reading, proceeding", \
178 file->path); \
179 js_FileOpen(cx, obj, file, "read"); \
181 if (!js_canRead(cx, file)) { \
182 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \
183 JSFILEMSG_CANNOT_READ, file->path); \
184 goto out; \
187 #define JSFILE_CHECK_OPEN(op) \
188 if (!file->isOpen) { \
189 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \
190 JSFILEMSG_FILE_MUST_BE_OPEN, op); \
191 goto out; \
194 #define JSFILE_CHECK_CLOSED(op) \
195 if (file->isOpen) { \
196 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \
197 JSFILEMSG_FILE_MUST_BE_CLOSED, op); \
198 goto out; \
201 #define JSFILE_CHECK_ONE_ARG(op) \
202 if (argc != 1) { \
203 char str[NUMBER_SIZE]; \
204 sprintf(str, "%d", argc); \
205 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \
206 JSFILEMSG_EXPECTS_ONE_ARG_ERROR, op, str); \
207 goto out; \
212 Security mechanism, should define a callback for this.
213 The parameters are as follows:
214 SECURITY_CHECK(JSContext *cx, JSPrincipals *ps, char *op_name, JSFile *file)
215 XXX Should this be a real function returning a JSBool result (and getting
216 some typesafety help from the compiler?).
218 #define SECURITY_CHECK(cx, ps, op, file) \
219 /* Define a callback here... */
222 /* Structure representing the file internally */
223 typedef struct JSFile {
224 char *path; /* the path to the file. */
225 JSBool isOpen;
226 int32 mode; /* mode used to open the file: read, write, append, create, etc.. */
227 int32 type; /* Asciiz, utf, unicode */
228 char byteBuffer[3]; /* bytes read in advance by js_FileRead ( UTF8 encoding ) */
229 jsint nbBytesInBuf; /* number of bytes stored in the buffer above */
230 jschar charBuffer; /* character read in advance by readln ( mac files only ) */
231 JSBool charBufferUsed; /* flag indicating if the buffer above is being used */
232 JSBool hasRandomAccess;/* can the file be randomly accessed? false for stdin, and
233 UTF-encoded files. */
234 JSBool hasAutoflush; /* should we force a flush for each line break? */
235 JSBool isNative; /* if the file is using OS-specific file FILE type */
236 /* We can actually put the following two in a union since they should never be used at the same time */
237 PRFileDesc *handle; /* the handle for the file, if open. */
238 FILE *nativehandle; /* native handle, for stuff NSPR doesn't do. */
239 JSBool isPipe; /* if the file is really an OS pipe */
240 } JSFile;
242 /* a few forward declarations... */
243 JS_PUBLIC_API(JSObject*) js_NewFileObject(JSContext *cx, char *filename);
244 static JSBool file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
245 static JSBool file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
247 /* New filename manipulation procesures */
248 /* assumes we don't have leading/trailing spaces */
249 static JSBool
250 js_filenameHasAPipe(const char *filename)
252 if (!filename)
253 return JS_FALSE;
255 return filename[0] == PIPE_SYMBOL ||
256 filename[strlen(filename) - 1] == PIPE_SYMBOL;
259 static JSBool
260 js_isAbsolute(const char *name)
262 #if defined(XP_WIN) || defined(XP_OS2)
263 return *name && name[1] == ':';
264 #else
265 return (name[0]
266 # if defined(XP_UNIX) || defined(XP_BEOS)
268 # else
270 # endif
271 FILESEPARATOR);
272 #endif
276 * Concatenates base and name to produce a valid filename.
277 * Returned string must be freed.
279 static char*
280 js_combinePath(JSContext *cx, const char *base, const char *name)
282 int len = strlen(base);
283 char* result = JS_malloc(cx, len + strlen(name) + 2);
285 if (!result)
286 return NULL;
288 strcpy(result, base);
290 if (base[len - 1] != FILESEPARATOR && base[len - 1] != FILESEPARATOR2) {
291 result[len] = FILESEPARATOR;
292 result[len + 1] = '\0';
294 strcat(result, name);
295 return result;
298 /* Extract the last component from a path name. Returned string must be freed */
299 static char *
300 js_fileBaseName(JSContext *cx, const char *pathname)
302 jsint index, aux;
303 char *result;
305 index = strlen(pathname)-1;
307 /* Chop off trailing seperators. */
308 while (index > 0 && (pathname[index]==FILESEPARATOR ||
309 pathname[index]==FILESEPARATOR2)) {
310 --index;
313 aux = index;
315 /* Now find the next separator. */
316 while (index >= 0 && pathname[index] != FILESEPARATOR &&
317 pathname[index] != FILESEPARATOR2) {
318 --index;
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;
331 * Returns everything but the last component from a path name.
332 * Returned string must be freed.
334 static char *
335 js_fileDirectoryName(JSContext *cx, const char *pathname)
337 char *result;
338 const char *cp, *end;
339 size_t pathsize;
341 end = pathname + strlen(pathname);
342 cp = end - 1;
344 /* If this is already a directory, chop off the trailing /s. */
345 while (cp >= pathname) {
346 if (*cp != FILESEPARATOR && *cp != FILESEPARATOR2)
347 break;
348 --cp;
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;
359 /* Now chop off the last portion. */
360 while (cp >= pathname) {
361 if (*cp == FILESEPARATOR || *cp == FILESEPARATOR2)
362 break;
363 --cp;
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);
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;
386 /* Return everything up to and including the seperator. */
387 pathsize = cp - pathname + 1;
388 result = JS_malloc(cx, pathsize + 1);
389 if (!result)
390 return NULL;
392 strncpy(result, pathname, pathsize);
393 result[pathsize] = '\0';
395 return result;
398 static char *
399 js_absolutePath(JSContext *cx, const char * path)
401 JSObject *obj;
402 JSString *str;
403 jsval prop;
405 if (js_isAbsolute(path)) {
406 return JS_strdup(cx, path);
407 } else {
408 obj = JS_GetGlobalObject(cx);
409 if (!JS_GetProperty(cx, obj, FILE_CONSTRUCTOR, &prop)) {
410 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
411 JSFILEMSG_FILE_CONSTRUCTOR_UNDEFINED_ERROR);
412 return JS_strdup(cx, path);
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);
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);
431 /* Side effect: will remove spaces in the beginning/end of the filename */
432 static char *
433 js_canonicalPath(JSContext *cx, char *oldpath)
435 char *tmp;
436 char *path = oldpath;
437 char *base, *dir, *current, *result;
438 jsint c;
439 jsint back = 0;
440 unsigned int i = 0, j = strlen(path)-1;
442 /* This is probably optional */
443 /* Remove possible spaces in the beginning and end */
444 while (i < j && path[i] == ' ')
445 i++;
446 while (j >= 0 && path[j] == ' ')
447 j--;
449 tmp = JS_malloc(cx, j-i+2);
450 if (!tmp)
451 return NULL;
453 strncpy(tmp, path + i, j - i + 1);
454 tmp[j - i + 1] = '\0';
456 path = tmp;
458 /* Pipe support. */
459 if (js_filenameHasAPipe(path))
460 return path;
462 /* file:// support. */
463 if (!strncmp(path, URL_PREFIX, strlen(URL_PREFIX))) {
464 tmp = js_canonicalPath(cx, path + strlen(URL_PREFIX));
465 JS_free(cx, path);
466 return tmp;
469 if (!js_isAbsolute(path)) {
470 tmp = js_absolutePath(cx, path);
471 if (!tmp)
472 return NULL;
473 JS_free(cx, path);
474 path = tmp;
477 result = JS_strdup(cx, "");
479 current = path;
481 base = js_fileBaseName(cx, current);
482 dir = js_fileDirectoryName(cx, current);
484 while (strcmp(dir, current)) {
485 if (!strcmp(base, "..")) {
486 back++;
487 } else {
488 if (back > 0) {
489 back--;
490 } else {
491 tmp = result;
492 result = JS_malloc(cx, strlen(base) + 1 + strlen(tmp) + 1);
493 if (!result)
494 goto out;
496 strcpy(result, base);
497 c = strlen(result);
498 if (*tmp) {
499 result[c] = FILESEPARATOR;
500 result[c + 1] = '\0';
501 strcat(result, tmp);
503 JS_free(cx, tmp);
506 JS_free(cx, current);
507 JS_free(cx, base);
508 current = dir;
509 base = js_fileBaseName(cx, current);
510 dir = js_fileDirectoryName(cx, current);
513 tmp = result;
514 result = JS_malloc(cx, strlen(dir)+1+strlen(tmp)+1);
515 if (!result)
516 goto out;
518 strcpy(result, dir);
519 c = strlen(result);
520 if (tmp[0]!='\0') {
521 if ((result[c-1]!=FILESEPARATOR)&&(result[c-1]!=FILESEPARATOR2)) {
522 result[c] = FILESEPARATOR;
523 result[c+1] = '\0';
525 strcat(result, tmp);
528 out:
529 if (tmp)
530 JS_free(cx, tmp);
531 if (dir)
532 JS_free(cx, dir);
533 if (base)
534 JS_free(cx, base);
535 if (current)
536 JS_free(cx, current);
538 return result;
541 /* -------------------------- Text conversion ------------------------------- */
542 /* The following is ripped from libi18n/unicvt.c and include files.. */
545 * UTF8 defines and macros
547 #define ONE_OCTET_BASE 0x00 /* 0xxxxxxx */
548 #define ONE_OCTET_MASK 0x7F /* x1111111 */
549 #define CONTINUING_OCTET_BASE 0x80 /* 10xxxxxx */
550 #define CONTINUING_OCTET_MASK 0x3F /* 00111111 */
551 #define TWO_OCTET_BASE 0xC0 /* 110xxxxx */
552 #define TWO_OCTET_MASK 0x1F /* 00011111 */
553 #define THREE_OCTET_BASE 0xE0 /* 1110xxxx */
554 #define THREE_OCTET_MASK 0x0F /* 00001111 */
555 #define FOUR_OCTET_BASE 0xF0 /* 11110xxx */
556 #define FOUR_OCTET_MASK 0x07 /* 00000111 */
557 #define FIVE_OCTET_BASE 0xF8 /* 111110xx */
558 #define FIVE_OCTET_MASK 0x03 /* 00000011 */
559 #define SIX_OCTET_BASE 0xFC /* 1111110x */
560 #define SIX_OCTET_MASK 0x01 /* 00000001 */
562 #define IS_UTF8_1ST_OF_1(x) (( (x)&~ONE_OCTET_MASK ) == ONE_OCTET_BASE)
563 #define IS_UTF8_1ST_OF_2(x) (( (x)&~TWO_OCTET_MASK ) == TWO_OCTET_BASE)
564 #define IS_UTF8_1ST_OF_3(x) (( (x)&~THREE_OCTET_MASK) == THREE_OCTET_BASE)
565 #define IS_UTF8_1ST_OF_4(x) (( (x)&~FOUR_OCTET_MASK ) == FOUR_OCTET_BASE)
566 #define IS_UTF8_1ST_OF_5(x) (( (x)&~FIVE_OCTET_MASK ) == FIVE_OCTET_BASE)
567 #define IS_UTF8_1ST_OF_6(x) (( (x)&~SIX_OCTET_MASK ) == SIX_OCTET_BASE)
568 #define IS_UTF8_2ND_THRU_6TH(x) \
569 (( (x)&~CONTINUING_OCTET_MASK ) == CONTINUING_OCTET_BASE)
570 #define IS_UTF8_1ST_OF_UCS2(x) \
571 IS_UTF8_1ST_OF_1(x) \
572 || IS_UTF8_1ST_OF_2(x) \
573 || IS_UTF8_1ST_OF_3(x)
576 #define MAX_UCS2 0xFFFF
577 #define DEFAULT_CHAR 0x003F /* Default char is "?" */
578 #define BYTE_MASK 0xBF
579 #define BYTE_MARK 0x80
582 /* Function: one_ucs2_to_utf8_char
584 * Function takes one UCS-2 char and writes it to a UTF-8 buffer.
585 * We need a UTF-8 buffer because we don't know before this
586 * function how many bytes of utf-8 data will be written. It also
587 * takes a pointer to the end of the UTF-8 buffer so that we don't
588 * overwrite data. This function returns the number of UTF-8 bytes
589 * of data written, or -1 if the buffer would have been overrun.
592 #define LINE_SEPARATOR 0x2028
593 #define PARAGRAPH_SEPARATOR 0x2029
594 static int16 one_ucs2_to_utf8_char(unsigned char *tobufp,
595 unsigned char *tobufendp,
596 uint16 onechar)
598 int16 numUTF8bytes = 0;
600 if (onechar == LINE_SEPARATOR || onechar == PARAGRAPH_SEPARATOR) {
601 strcpy((char*)tobufp, "\n");
602 return strlen((char*)tobufp);
605 if (onechar < 0x80) {
606 numUTF8bytes = 1;
607 } else if (onechar < 0x800) {
608 numUTF8bytes = 2;
609 } else {
610 /* 0x800 >= onechar <= MAX_UCS2 */
611 numUTF8bytes = 3;
614 tobufp += numUTF8bytes;
616 /* return error if we don't have space for the whole character */
617 if (tobufp > tobufendp) {
618 return(-1);
621 switch(numUTF8bytes) {
622 case 3: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
623 *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
624 *--tobufp = onechar | THREE_OCTET_BASE;
625 break;
627 case 2: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
628 *--tobufp = onechar | TWO_OCTET_BASE;
629 break;
631 case 1: *--tobufp = (unsigned char)onechar;
632 break;
635 return numUTF8bytes;
639 * utf8_to_ucs2_char
641 * Convert a utf8 multibyte character to ucs2
643 * inputs: pointer to utf8 character(s)
644 * length of utf8 buffer ("read" length limit)
645 * pointer to return ucs2 character
647 * outputs: number of bytes in the utf8 character
648 * -1 if not a valid utf8 character sequence
649 * -2 if the buffer is too short
651 static int16
652 utf8_to_ucs2_char(const unsigned char *utf8p, int16 buflen, uint16 *ucs2p)
654 uint16 lead, cont1, cont2;
657 * Check for minimum buffer length
659 if ((buflen < 1) || (utf8p == NULL)) {
660 return -2;
662 lead = (uint16) (*utf8p);
665 * Check for a one octet sequence
667 if (IS_UTF8_1ST_OF_1(lead)) {
668 *ucs2p = lead & ONE_OCTET_MASK;
669 return 1;
673 * Check for a two octet sequence
675 if (IS_UTF8_1ST_OF_2(*utf8p)) {
676 if (buflen < 2)
677 return -2;
678 cont1 = (uint16) *(utf8p+1);
679 if (!IS_UTF8_2ND_THRU_6TH(cont1))
680 return -1;
681 *ucs2p = (lead & TWO_OCTET_MASK) << 6;
682 *ucs2p |= cont1 & CONTINUING_OCTET_MASK;
683 return 2;
687 * Check for a three octet sequence
689 else if (IS_UTF8_1ST_OF_3(lead)) {
690 if (buflen < 3)
691 return -2;
692 cont1 = (uint16) *(utf8p+1);
693 cont2 = (uint16) *(utf8p+2);
694 if ( (!IS_UTF8_2ND_THRU_6TH(cont1))
695 || (!IS_UTF8_2ND_THRU_6TH(cont2)))
696 return -1;
697 *ucs2p = (lead & THREE_OCTET_MASK) << 12;
698 *ucs2p |= (cont1 & CONTINUING_OCTET_MASK) << 6;
699 *ucs2p |= cont2 & CONTINUING_OCTET_MASK;
700 return 3;
702 else { /* not a valid utf8/ucs2 character */
703 return -1;
707 /* ----------------------------- Helper functions --------------------------- */
708 /* Ripped off from lm_win.c .. */
709 /* where is strcasecmp?.. for now, it's case sensitive..
711 * strcasecmp is in strings.h, but on windows it's called _stricmp...
712 * will need to #ifdef this
715 static int32
716 js_FileHasOption(JSContext *cx, const char *oldoptions, const char *name)
718 char *comma, *equal, *current;
719 char *options = JS_strdup(cx, oldoptions);
720 int32 found = 0;
722 current = options;
723 for (;;) {
724 comma = strchr(current, ',');
725 if (comma) *comma = '\0';
726 equal = strchr(current, '=');
727 if (equal) *equal = '\0';
728 if (strcmp(current, name) == 0) {
729 if (!equal || strcmp(equal + 1, "yes") == 0)
730 found = 1;
731 else
732 found = atoi(equal + 1);
734 if (equal) *equal = '=';
735 if (comma) *comma = ',';
736 if (found || !comma)
737 break;
738 current = comma + 1;
740 JS_free(cx, options);
741 return found;
744 /* empty the buffer */
745 static void
746 js_ResetBuffers(JSFile * file)
748 file->charBufferUsed = JS_FALSE;
749 file->nbBytesInBuf = 0;
752 /* Reset file attributes */
753 static void
754 js_ResetAttributes(JSFile * file)
756 file->mode = file->type = 0;
757 file->isOpen = JS_FALSE;
758 file->handle = NULL;
759 file->nativehandle = NULL;
760 file->hasRandomAccess = JS_TRUE; /* Innocent until proven guilty. */
761 file->hasAutoflush = JS_FALSE;
762 file->isNative = JS_FALSE;
763 file->isPipe = JS_FALSE;
765 js_ResetBuffers(file);
768 static JSBool
769 js_FileOpen(JSContext *cx, JSObject *obj, JSFile *file, char *mode){
770 JSString *type, *mask;
771 jsval v[2];
772 jsval rval;
774 type = JS_InternString(cx, asciistring);
775 mask = JS_NewStringCopyZ(cx, mode);
776 v[0] = STRING_TO_JSVAL(mask);
777 v[1] = STRING_TO_JSVAL(type);
779 if (!file_open(cx, obj, 2, v, &rval))
780 return JS_FALSE;
781 return JS_TRUE;
784 /* Buffered version of PR_Read. Used by js_FileRead */
785 static int32
786 js_BufferedRead(JSFile *f, unsigned char *buf, int32 len)
788 int32 count = 0;
790 while (f->nbBytesInBuf>0&&len>0) {
791 buf[0] = f->byteBuffer[0];
792 f->byteBuffer[0] = f->byteBuffer[1];
793 f->byteBuffer[1] = f->byteBuffer[2];
794 f->nbBytesInBuf--;
795 len--;
796 buf+=1;
797 count++;
800 if (len > 0) {
801 count += (!f->isNative)
802 ? PR_Read(f->handle, buf, len)
803 : fread(buf, 1, len, f->nativehandle);
805 return count;
808 static int32
809 js_FileRead(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode)
811 unsigned char *aux;
812 int32 count = 0, i;
813 jsint remainder;
814 unsigned char utfbuf[3];
816 if (file->charBufferUsed) {
817 buf[0] = file->charBuffer;
818 buf++;
819 len--;
820 file->charBufferUsed = JS_FALSE;
823 switch (mode) {
824 case ASCII:
825 aux = (unsigned char*)JS_malloc(cx, len);
826 if (!aux)
827 return 0;
829 count = js_BufferedRead(file, aux, len);
830 if (count == -1) {
831 JS_free(cx, aux);
832 return 0;
835 for (i = 0; i < len; i++)
836 buf[i] = (jschar)aux[i];
838 JS_free(cx, aux);
839 break;
841 case UTF8:
842 remainder = 0;
843 for (count = 0;count<len;count++) {
844 i = js_BufferedRead(file, utfbuf+remainder, 3-remainder);
845 if (i<=0) {
846 return count;
848 i = utf8_to_ucs2_char(utfbuf, (int16)i, &buf[count] );
849 if (i<0) {
850 return count;
851 } else {
852 if (i==1) {
853 utfbuf[0] = utfbuf[1];
854 utfbuf[1] = utfbuf[2];
855 remainder = 2;
856 } else if (i==2) {
857 utfbuf[0] = utfbuf[2];
858 remainder = 1;
859 } else if (i==3) {
860 remainder = 0;
864 while (remainder>0) {
865 file->byteBuffer[file->nbBytesInBuf] = utfbuf[0];
866 file->nbBytesInBuf++;
867 utfbuf[0] = utfbuf[1];
868 utfbuf[1] = utfbuf[2];
869 remainder--;
871 break;
873 case UCS2:
874 count = js_BufferedRead(file, (unsigned char *)buf, len * 2) >> 1;
875 if (count == -1)
876 return 0;
878 break;
880 default:
881 /* Not reached. */
882 JS_ASSERT(0);
885 if(count == -1) {
886 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
887 JSFILEMSG_OP_FAILED, "read", file->path);
890 return count;
893 static int32
894 js_FileSeek(JSContext *cx, JSFile *file, int32 len, int32 mode)
896 int32 count = 0, i;
897 jsint remainder;
898 unsigned char utfbuf[3];
899 jschar tmp;
901 switch (mode) {
902 case ASCII:
903 count = PR_Seek(file->handle, len, PR_SEEK_CUR);
904 break;
906 case UTF8:
907 remainder = 0;
908 for (count = 0;count<len;count++) {
909 i = js_BufferedRead(file, utfbuf+remainder, 3-remainder);
910 if (i<=0) {
911 return 0;
913 i = utf8_to_ucs2_char(utfbuf, (int16)i, &tmp );
914 if (i<0) {
915 return 0;
916 } else {
917 if (i==1) {
918 utfbuf[0] = utfbuf[1];
919 utfbuf[1] = utfbuf[2];
920 remainder = 2;
921 } else if (i==2) {
922 utfbuf[0] = utfbuf[2];
923 remainder = 1;
924 } else if (i==3) {
925 remainder = 0;
929 while (remainder>0) {
930 file->byteBuffer[file->nbBytesInBuf] = utfbuf[0];
931 file->nbBytesInBuf++;
932 utfbuf[0] = utfbuf[1];
933 utfbuf[1] = utfbuf[2];
934 remainder--;
936 break;
938 case UCS2:
939 count = PR_Seek(file->handle, len*2, PR_SEEK_CUR)/2;
940 break;
942 default:
943 /* Not reached. */
944 JS_ASSERT(0);
947 if(count == -1) {
948 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
949 JSFILEMSG_OP_FAILED, "seek", file->path);
952 return count;
955 static int32
956 js_FileWrite(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode)
958 unsigned char *aux;
959 int32 count = 0, i, j;
960 unsigned char *utfbuf;
962 switch (mode) {
963 case ASCII:
964 aux = (unsigned char*)JS_malloc(cx, len);
965 if (!aux)
966 return 0;
968 for (i = 0; i<len; i++)
969 aux[i] = buf[i] % 256;
971 count = (!file->isNative)
972 ? PR_Write(file->handle, aux, len)
973 : fwrite(aux, 1, len, file->nativehandle);
975 if (count==-1) {
976 JS_free(cx, aux);
977 return 0;
980 JS_free(cx, aux);
981 break;
983 case UTF8:
984 utfbuf = (unsigned char*)JS_malloc(cx, len*3);
985 if (!utfbuf) return 0;
986 i = 0;
987 for (count = 0;count<len;count++) {
988 j = one_ucs2_to_utf8_char(utfbuf+i, utfbuf+len*3, buf[count]);
989 if (j==-1) {
990 JS_free(cx, utfbuf);
991 return 0;
993 i+=j;
995 j = (!file->isNative)
996 ? PR_Write(file->handle, utfbuf, i)
997 : fwrite(utfbuf, 1, i, file->nativehandle);
999 if (j<i) {
1000 JS_free(cx, utfbuf);
1001 return 0;
1003 JS_free(cx, utfbuf);
1004 break;
1006 case UCS2:
1007 count = (!file->isNative)
1008 ? PR_Write(file->handle, buf, len*2) >> 1
1009 : fwrite(buf, 1, len*2, file->nativehandle) >> 1;
1011 if (count == -1)
1012 return 0;
1013 break;
1015 default:
1016 /* Not reached. */
1017 JS_ASSERT(0);
1020 if(count == -1) {
1021 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1022 JSFILEMSG_OP_FAILED, "write", file->path);
1025 return count;
1028 /* ----------------------------- Property checkers -------------------------- */
1029 static JSBool
1030 js_exists(JSContext *cx, JSFile *file)
1032 if (file->isNative) {
1033 /* It doesn't make sense for a pipe of stdstream. */
1034 return JS_FALSE;
1037 return PR_Access(file->path, PR_ACCESS_EXISTS) == PR_SUCCESS;
1040 static JSBool
1041 js_canRead(JSContext *cx, JSFile *file)
1043 if (!file->isNative) {
1044 if (file->isOpen && !(file->mode & PR_RDONLY))
1045 return JS_FALSE;
1046 return PR_Access(file->path, PR_ACCESS_READ_OK) == PR_SUCCESS;
1049 if (file->isPipe) {
1050 /* Is this pipe open for reading? */
1051 return file->path[0] == PIPE_SYMBOL;
1054 return !strcmp(file->path, STDINPUT_NAME);
1057 static JSBool
1058 js_canWrite(JSContext *cx, JSFile *file)
1060 if (!file->isNative) {
1061 if (file->isOpen && !(file->mode & PR_WRONLY))
1062 return JS_FALSE;
1063 return PR_Access(file->path, PR_ACCESS_WRITE_OK) == PR_SUCCESS;
1066 if(file->isPipe) {
1067 /* Is this pipe open for writing? */
1068 return file->path[strlen(file->path)-1] == PIPE_SYMBOL;
1071 return !strcmp(file->path, STDOUTPUT_NAME) ||
1072 !strcmp(file->path, STDERROR_NAME);
1075 static JSBool
1076 js_isFile(JSContext *cx, JSFile *file)
1078 if (!file->isNative) {
1079 PRFileInfo info;
1081 if (file->isOpen
1082 ? PR_GetOpenFileInfo(file->handle, &info)
1083 : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) {
1084 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1085 JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
1086 return JS_FALSE;
1089 return info.type == PR_FILE_FILE;
1092 /* This doesn't make sense for a pipe of stdstream. */
1093 return JS_FALSE;
1096 static JSBool
1097 js_isDirectory(JSContext *cx, JSFile *file)
1099 if(!file->isNative){
1100 PRFileInfo info;
1102 /* Hack needed to get get_property to work. */
1103 if (!js_exists(cx, file))
1104 return JS_FALSE;
1106 if (file->isOpen
1107 ? PR_GetOpenFileInfo(file->handle, &info)
1108 : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) {
1109 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1110 JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
1111 return JS_FALSE;
1114 return info.type == PR_FILE_DIRECTORY;
1117 /* This doesn't make sense for a pipe of stdstream. */
1118 return JS_FALSE;
1121 static jsval
1122 js_size(JSContext *cx, JSFile *file)
1124 PRFileInfo info;
1126 JSFILE_CHECK_NATIVE("size");
1128 if (file->isOpen
1129 ? PR_GetOpenFileInfo(file->handle, &info)
1130 : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) {
1131 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1132 JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
1133 return JSVAL_VOID;
1136 return INT_TO_JSVAL(info.size);
1138 out:
1139 return JSVAL_VOID;
1143 * Return the parent object
1145 static JSBool
1146 js_parent(JSContext *cx, JSFile *file, jsval *resultp)
1148 char *str;
1150 /* Since we only care about pipes and native files, return NULL. */
1151 if (file->isNative) {
1152 *resultp = JSVAL_VOID;
1153 return JS_TRUE;
1156 str = js_fileDirectoryName(cx, file->path);
1157 if (!str)
1158 return JS_FALSE;
1160 /* If the directory is equal to the original path, we're at the root. */
1161 if (!strcmp(file->path, str)) {
1162 *resultp = JSVAL_NULL;
1163 } else {
1164 JSObject *obj = js_NewFileObject(cx, str);
1165 if (!obj) {
1166 JS_free(cx, str);
1167 return JS_FALSE;
1169 *resultp = OBJECT_TO_JSVAL(obj);
1172 JS_free(cx, str);
1173 return JS_TRUE;
1176 static JSBool
1177 js_name(JSContext *cx, JSFile *file, jsval *vp)
1179 char *name;
1180 JSString *str;
1182 if (file->isPipe) {
1183 *vp = JSVAL_VOID;
1184 return JS_TRUE;
1187 name = js_fileBaseName(cx, file->path);
1188 if (!name)
1189 return JS_FALSE;
1191 str = JS_NewString(cx, name, strlen(name));
1192 if (!str) {
1193 JS_free(cx, name);
1194 return JS_FALSE;
1197 *vp = STRING_TO_JSVAL(str);
1198 return JS_TRUE;
1201 /* ------------------------------ File object methods ---------------------------- */
1202 static JSBool
1203 file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1205 JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1206 JSString *strmode, *strtype;
1207 char *ctype, *mode;
1208 int32 mask, type;
1209 int len;
1211 mode = NULL;
1213 SECURITY_CHECK(cx, NULL, "open", file);
1215 /* A native file that is already open */
1216 if(file->isOpen && file->isNative) {
1217 JS_ReportWarning(cx, "Native file %s is already open, proceeding",
1218 file->path);
1219 goto good;
1222 /* Close before proceeding */
1223 if (file->isOpen) {
1224 JS_ReportWarning(cx, "File %s is already open, we will close it and "
1225 "reopen, proceeding", file->path);
1226 if(!file_close(cx, obj, 0, NULL, rval))
1227 goto out;
1230 if (js_isDirectory(cx, file)) {
1231 JS_ReportWarning(cx, "%s seems to be a directory, there is no point in "
1232 "trying to open it, proceeding", file->path);
1233 goto good;
1236 /* Path must be defined at this point */
1237 len = strlen(file->path);
1239 /* Mode */
1240 if (argc >= 1) {
1241 strmode = JS_ValueToString(cx, argv[0]);
1242 if (!strmode) {
1243 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1244 JSFILEMSG_FIRST_ARGUMENT_OPEN_NOT_STRING_ERROR,
1245 argv[0]);
1246 goto out;
1248 mode = JS_strdup(cx, JS_GetStringBytes(strmode));
1249 } else {
1250 if(file->path[0]==PIPE_SYMBOL) {
1251 /* pipe default mode */
1252 mode = JS_strdup(cx, "read");
1253 } else if(file->path[len-1]==PIPE_SYMBOL) {
1254 /* pipe default mode */
1255 mode = JS_strdup(cx, "write");
1256 } else {
1257 /* non-destructive, permissive defaults. */
1258 mode = JS_strdup(cx, "readWrite,append,create");
1262 /* Process the mode */
1263 mask = 0;
1264 /* TODO: this is pretty ugly, we walk thru the string too many times */
1265 mask |= js_FileHasOption(cx, mode, "read") ? PR_RDONLY : 0;
1266 mask |= js_FileHasOption(cx, mode, "write") ? PR_WRONLY : 0;
1267 mask |= js_FileHasOption(cx, mode, "readWrite")? PR_RDWR : 0;
1268 mask |= js_FileHasOption(cx, mode, "append") ? PR_APPEND : 0;
1269 mask |= js_FileHasOption(cx, mode, "create") ? PR_CREATE_FILE : 0;
1270 mask |= js_FileHasOption(cx, mode, "replace") ? PR_TRUNCATE : 0;
1272 if (mask & PR_RDWR)
1273 mask |= (PR_RDONLY | PR_WRONLY);
1274 if ((mask & PR_RDONLY) && (mask & PR_WRONLY))
1275 mask |= PR_RDWR;
1277 file->hasAutoflush |= js_FileHasOption(cx, mode, "autoflush");
1279 /* Type */
1280 if (argc > 1) {
1281 strtype = JS_ValueToString(cx, argv[1]);
1282 if (!strtype) {
1283 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1284 JSFILEMSG_SECOND_ARGUMENT_OPEN_NOT_STRING_ERROR,
1285 argv[1]);
1286 goto out;
1288 ctype = JS_GetStringBytes(strtype);
1290 if(!strcmp(ctype, utfstring)) {
1291 type = UTF8;
1292 } else if (!strcmp(ctype, unicodestring)) {
1293 type = UCS2;
1294 } else {
1295 if (strcmp(ctype, asciistring)) {
1296 JS_ReportWarning(cx, "File type %s is not supported, using "
1297 "'text' instead, proceeding", ctype);
1299 type = ASCII;
1301 } else {
1302 type = ASCII;
1305 /* Save the relevant fields */
1306 file->type = type;
1307 file->mode = mask;
1308 file->nativehandle = NULL;
1309 file->hasRandomAccess = (type != UTF8);
1312 * Deal with pipes here. We can't use NSPR for pipes, so we have to use
1313 * POPEN.
1315 if (file->path[0]==PIPE_SYMBOL || file->path[len-1]==PIPE_SYMBOL) {
1316 if (file->path[0] == PIPE_SYMBOL && file->path[len-1] == PIPE_SYMBOL) {
1317 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1318 JSFILEMSG_BIDIRECTIONAL_PIPE_NOT_SUPPORTED);
1319 goto out;
1320 } else {
1321 int i = 0;
1322 char pipemode[3];
1323 SECURITY_CHECK(cx, NULL, "pipe_open", file);
1325 if(file->path[0] == PIPE_SYMBOL){
1326 if(mask & (PR_WRONLY | PR_APPEND | PR_CREATE_FILE | PR_TRUNCATE)){
1327 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1328 JSFILEMSG_OPEN_MODE_NOT_SUPPORTED_WITH_PIPES,
1329 mode, file->path);
1330 goto out;
1332 /* open(SPOOLER, "| cat -v | lpr -h 2>/dev/null") -- pipe for writing */
1333 pipemode[i++] = 'r';
1334 #ifndef XP_UNIX
1335 pipemode[i++] = file->type==UTF8 ? 'b' : 't';
1336 #endif
1337 pipemode[i++] = '\0';
1338 file->nativehandle = POPEN(&file->path[1], pipemode);
1339 } else if(file->path[len-1] == PIPE_SYMBOL) {
1340 char *command = JS_malloc(cx, len);
1342 strncpy(command, file->path, len-1);
1343 command[len-1] = '\0';
1344 /* open(STATUS, "netstat -an 2>&1 |") */
1345 pipemode[i++] = 'w';
1346 #ifndef XP_UNIX
1347 pipemode[i++] = file->type==UTF8 ? 'b' : 't';
1348 #endif
1349 pipemode[i++] = '\0';
1350 file->nativehandle = POPEN(command, pipemode);
1351 JS_free(cx, command);
1353 /* set the flags */
1354 file->isNative = JS_TRUE;
1355 file->isPipe = JS_TRUE;
1356 file->hasRandomAccess = JS_FALSE;
1358 } else {
1359 /* TODO: what about the permissions?? Java ignores the problem... */
1360 file->handle = PR_Open(file->path, mask, 0644);
1363 js_ResetBuffers(file);
1364 JS_free(cx, mode);
1365 mode = NULL;
1367 /* Set the open flag and return result */
1368 if (file->handle == NULL && file->nativehandle == NULL) {
1369 file->isOpen = JS_FALSE;
1371 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1372 JSFILEMSG_OP_FAILED, "open", file->path);
1373 goto out;
1376 good:
1377 file->isOpen = JS_TRUE;
1378 *rval = JSVAL_TRUE;
1379 return JS_TRUE;
1381 out:
1382 if(mode)
1383 JS_free(cx, mode);
1384 return JS_FALSE;
1387 static JSBool
1388 file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1390 JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1392 SECURITY_CHECK(cx, NULL, "close", file);
1394 if(!file->isOpen){
1395 JS_ReportWarning(cx, "File %s is not open, can't close it, proceeding",
1396 file->path);
1397 goto out;
1400 if(!file->isPipe){
1401 if(file->isNative){
1402 JS_ReportWarning(cx, "Unable to close a native file, proceeding", file->path);
1403 goto out;
1404 }else{
1405 if(file->handle && PR_Close(file->handle)){
1406 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1407 JSFILEMSG_OP_FAILED, "close", file->path);
1409 goto out;
1412 }else{
1413 if(PCLOSE(file->nativehandle)==-1){
1414 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1415 JSFILEMSG_OP_FAILED, "pclose", file->path);
1416 goto out;
1420 js_ResetAttributes(file);
1421 *rval = JSVAL_TRUE;
1422 return JS_TRUE;
1424 out:
1425 return JS_FALSE;
1429 static JSBool
1430 file_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1432 JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1434 SECURITY_CHECK(cx, NULL, "remove", file);
1435 JSFILE_CHECK_NATIVE("remove");
1436 JSFILE_CHECK_CLOSED("remove");
1438 if ((js_isDirectory(cx, file) ?
1439 PR_RmDir(file->path) : PR_Delete(file->path))==PR_SUCCESS) {
1440 js_ResetAttributes(file);
1441 *rval = JSVAL_TRUE;
1442 return JS_TRUE;
1443 } else {
1444 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1445 JSFILEMSG_OP_FAILED, "remove", file->path);
1446 goto out;
1448 out:
1449 *rval = JSVAL_FALSE;
1450 return JS_FALSE;
1453 /* Raw PR-based function. No text processing. Just raw data copying. */
1454 static JSBool
1455 file_copyTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1457 JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1458 char *dest = NULL;
1459 PRFileDesc *handle = NULL;
1460 char *buffer;
1461 jsval count, size;
1462 JSBool fileInitiallyOpen=JS_FALSE;
1464 SECURITY_CHECK(cx, NULL, "copyTo", file); /* may need a second argument!*/
1465 JSFILE_CHECK_ONE_ARG("copyTo");
1466 JSFILE_CHECK_NATIVE("copyTo");
1467 /* remeber the state */
1468 fileInitiallyOpen = file->isOpen;
1469 JSFILE_CHECK_READ;
1471 dest = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
1473 /* make sure we are not reading a file open for writing */
1474 if (file->isOpen && !js_canRead(cx, file)) {
1475 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1476 JSFILEMSG_CANNOT_COPY_FILE_OPEN_FOR_WRITING_ERROR, file->path);
1477 goto out;
1480 if (file->handle==NULL){
1481 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1482 JSFILEMSG_OP_FAILED, "open", file->path);
1483 goto out;
1486 handle = PR_Open(dest, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, 0644);
1488 if(!handle){
1489 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1490 JSFILEMSG_OP_FAILED, "open", dest);
1491 goto out;
1494 if ((size=js_size(cx, file))==JSVAL_VOID) {
1495 goto out;
1498 buffer = JS_malloc(cx, size);
1500 count = INT_TO_JSVAL(PR_Read(file->handle, buffer, size));
1502 /* reading panic */
1503 if (count!=size) {
1504 JS_free(cx, buffer);
1505 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1506 JSFILEMSG_COPY_READ_ERROR, file->path);
1507 goto out;
1510 count = INT_TO_JSVAL(PR_Write(handle, buffer, JSVAL_TO_INT(size)));
1512 /* writing panic */
1513 if (count!=size) {
1514 JS_free(cx, buffer);
1515 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1516 JSFILEMSG_COPY_WRITE_ERROR, file->path);
1517 goto out;
1520 JS_free(cx, buffer);
1522 if(!fileInitiallyOpen){
1523 if(!file_close(cx, obj, 0, NULL, rval)) goto out;
1526 if(PR_Close(handle)!=PR_SUCCESS){
1527 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1528 JSFILEMSG_OP_FAILED, "close", dest);
1529 goto out;
1532 *rval = JSVAL_TRUE;
1533 return JS_TRUE;
1534 out:
1535 if(file->isOpen && !fileInitiallyOpen){
1536 if(PR_Close(file->handle)!=PR_SUCCESS){
1537 JS_ReportWarning(cx, "Can't close %s, proceeding", file->path);
1541 if(handle && PR_Close(handle)!=PR_SUCCESS){
1542 JS_ReportWarning(cx, "Can't close %s, proceeding", dest);
1545 *rval = JSVAL_FALSE;
1546 return JS_FALSE;
1549 static JSBool
1550 file_renameTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1552 JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1553 char *dest;
1555 SECURITY_CHECK(cx, NULL, "renameTo", file); /* may need a second argument!*/
1556 JSFILE_CHECK_ONE_ARG("renameTo");
1557 JSFILE_CHECK_NATIVE("renameTo");
1558 JSFILE_CHECK_CLOSED("renameTo");
1560 dest = RESOLVE_PATH(cx, JS_GetStringBytes(JS_ValueToString(cx, argv[0])));
1562 if (PR_Rename(file->path, dest)==PR_SUCCESS){
1563 /* copy the new filename */
1564 JS_free(cx, file->path);
1565 file->path = dest;
1566 *rval = JSVAL_TRUE;
1567 return JS_TRUE;
1568 }else{
1569 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1570 JSFILEMSG_RENAME_FAILED, file->path, dest);
1571 goto out;
1573 out:
1574 *rval = JSVAL_FALSE;
1575 return JS_FALSE;
1578 static JSBool
1579 file_flush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1581 JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1583 SECURITY_CHECK(cx, NULL, "flush", file);
1584 JSFILE_CHECK_NATIVE("flush");
1585 JSFILE_CHECK_OPEN("flush");
1587 if (PR_Sync(file->handle)==PR_SUCCESS){
1588 *rval = JSVAL_TRUE;
1589 return JS_TRUE;
1590 }else{
1591 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1592 JSFILEMSG_OP_FAILED, "flush", file->path);
1593 goto out;
1595 out:
1596 *rval = JSVAL_FALSE;
1597 return JS_FALSE;
1600 static JSBool
1601 file_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1603 JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1604 JSString *str;
1605 int32 count;
1606 uintN i;
1608 SECURITY_CHECK(cx, NULL, "write", file);
1609 JSFILE_CHECK_WRITE;
1611 for (i = 0; i<argc; i++) {
1612 str = JS_ValueToString(cx, argv[i]);
1613 count = js_FileWrite(cx, file, JS_GetStringChars(str),
1614 JS_GetStringLength(str), file->type);
1615 if (count==-1){
1616 *rval = JSVAL_FALSE;
1617 return JS_FALSE;
1621 *rval = JSVAL_TRUE;
1622 return JS_TRUE;
1623 out:
1624 *rval = JSVAL_FALSE;
1625 return JS_FALSE;
1628 static JSBool
1629 file_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1631 JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1632 JSString *str;
1634 SECURITY_CHECK(cx, NULL, "writeln", file);
1635 JSFILE_CHECK_WRITE;
1637 /* don't report an error here */
1638 if(!file_write(cx, obj, argc, argv, rval)) return JS_FALSE;
1639 /* don't do security here -- we passed the check in file_write */
1640 str = JS_NewStringCopyZ(cx, "\n");
1642 if (js_FileWrite(cx, file, JS_GetStringChars(str), JS_GetStringLength(str),
1643 file->type)==-1){
1644 *rval = JSVAL_FALSE;
1645 return JS_FALSE;
1648 /* eol causes flush if hasAutoflush is turned on */
1649 if (file->hasAutoflush)
1650 file_flush(cx, obj, 0, NULL, rval);
1652 *rval = JSVAL_TRUE;
1653 return JS_TRUE;
1654 out:
1655 *rval = JSVAL_FALSE;
1656 return JS_FALSE;
1659 static JSBool
1660 file_writeAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1662 JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1663 jsuint i;
1664 jsuint limit;
1665 JSObject *array;
1666 JSObject *elem;
1667 jsval elemval;
1669 SECURITY_CHECK(cx, NULL, "writeAll", file);
1670 JSFILE_CHECK_ONE_ARG("writeAll");
1671 JSFILE_CHECK_WRITE;
1673 if (!JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[0]))) {
1674 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1675 JSFILEMSG_FIRST_ARGUMENT_WRITEALL_NOT_ARRAY_ERROR);
1676 goto out;
1679 array = JSVAL_TO_OBJECT(argv[0]);
1681 JS_GetArrayLength(cx, array, &limit);
1683 for (i = 0; i<limit; i++) {
1684 if (!JS_GetElement(cx, array, i, &elemval)) return JS_FALSE;
1685 elem = JSVAL_TO_OBJECT(elemval);
1686 file_writeln(cx, obj, 1, &elemval, rval);
1689 *rval = JSVAL_TRUE;
1690 return JS_TRUE;
1691 out:
1692 *rval = JSVAL_FALSE;
1693 return JS_FALSE;
1696 static JSBool
1697 file_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1699 JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1700 JSString *str;
1701 int32 want, count;
1702 jschar *buf;
1704 SECURITY_CHECK(cx, NULL, "read", file);
1705 JSFILE_CHECK_ONE_ARG("read");
1706 JSFILE_CHECK_READ;
1708 if (!JS_ValueToInt32(cx, argv[0], &want)){
1709 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1710 JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "read", argv[0]);
1711 goto out;
1714 /* want = (want>262144)?262144:want; * arbitrary size limitation */
1716 buf = JS_malloc(cx, want*sizeof buf[0]);
1717 if (!buf) goto out;
1719 count = js_FileRead(cx, file, buf, want, file->type);
1720 if (count>0) {
1721 str = JS_NewUCStringCopyN(cx, buf, count);
1722 *rval = STRING_TO_JSVAL(str);
1723 JS_free(cx, buf);
1724 return JS_TRUE;
1725 } else {
1726 JS_free(cx, buf);
1727 goto out;
1729 out:
1730 *rval = JSVAL_FALSE;
1731 return JS_FALSE;
1734 static JSBool
1735 file_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1737 JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1738 JSString *str;
1739 jschar *buf = NULL, *tmp;
1740 int32 offset, read;
1741 intN room;
1742 jschar data, data2;
1744 SECURITY_CHECK(cx, NULL, "readln", file);
1745 JSFILE_CHECK_READ;
1747 buf = JS_malloc(cx, MAX_LINE_LENGTH * sizeof data);
1748 if (!buf)
1749 return JS_FALSE;
1751 room = MAX_LINE_LENGTH - 1;
1752 offset = 0;
1754 for (;;) {
1755 read = js_FileRead(cx, file, &data, 1, file->type);
1756 if (read < 0)
1757 goto out;
1758 if (read == 0)
1759 goto eof;
1761 switch (data) {
1762 case '\r':
1763 read = js_FileRead(cx, file, &data2, 1, file->type);
1764 if (read < 0)
1765 goto out;
1767 if (read == 1 && data2 != '\n') {
1768 /* We read one char too far. Buffer it. */
1769 file->charBuffer = data2;
1770 file->charBufferUsed = JS_TRUE;
1773 /* Fall through. */
1774 case '\n':
1775 goto done;
1777 default:
1778 if (--room < 0) {
1779 tmp = JS_realloc(cx, buf,
1780 (offset + MAX_LINE_LENGTH) * sizeof data);
1781 if (!tmp)
1782 goto out;
1784 room = MAX_LINE_LENGTH - 1;
1785 buf = tmp;
1788 buf[offset++] = data;
1789 break;
1793 eof:
1794 if (offset == 0) {
1795 *rval = JSVAL_NULL;
1796 return JS_TRUE;
1799 done:
1800 buf[offset] = 0;
1801 tmp = JS_realloc(cx, buf, (offset + 1) * sizeof data);
1802 if (!tmp)
1803 goto out;
1805 str = JS_NewUCString(cx, tmp, offset);
1806 if (!str)
1807 goto out;
1809 *rval = STRING_TO_JSVAL(str);
1810 return JS_TRUE;
1812 out:
1813 if (buf)
1814 JS_free(cx, buf);
1816 return JS_FALSE;
1819 static JSBool
1820 file_readAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1822 JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1823 JSObject *array;
1824 jsint len;
1825 jsval line;
1826 JSBool lineok = JS_FALSE;
1828 SECURITY_CHECK(cx, NULL, "readAll", file);
1829 JSFILE_CHECK_READ;
1831 array = JS_NewArrayObject(cx, 0, NULL);
1832 if (!array)
1833 return JS_FALSE;
1834 *rval = OBJECT_TO_JSVAL(array);
1836 len = 0;
1838 lineok = file_readln(cx, obj, 0, NULL, &line);
1839 while (lineok && !JSVAL_IS_NULL(line)) {
1840 JS_SetElement(cx, array, len++, &line);
1841 lineok = file_readln(cx, obj, 0, NULL, &line);
1844 out:
1845 return lineok;
1848 static JSBool
1849 file_seek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1851 JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1852 int32 toskip;
1853 int32 pos;
1855 SECURITY_CHECK(cx, NULL, "seek", file);
1856 JSFILE_CHECK_ONE_ARG("seek");
1857 JSFILE_CHECK_NATIVE("seek");
1858 JSFILE_CHECK_READ;
1860 if (!JS_ValueToInt32(cx, argv[0], &toskip)){
1861 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1862 JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "seek", argv[0]);
1863 goto out;
1866 if(!file->hasRandomAccess){
1867 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1868 JSFILEMSG_NO_RANDOM_ACCESS, file->path);
1869 goto out;
1872 if(js_isDirectory(cx, file)){
1873 JS_ReportWarning(cx,"Seek on directories is not supported, proceeding");
1874 goto out;
1877 pos = js_FileSeek(cx, file, toskip, file->type);
1879 if (pos!=-1) {
1880 *rval = INT_TO_JSVAL(pos);
1881 return JS_TRUE;
1883 out:
1884 *rval = JSVAL_VOID;
1885 return JS_FALSE;
1888 static JSBool
1889 file_list(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1891 PRDir *dir;
1892 PRDirEntry *entry;
1893 JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1894 JSObject *array;
1895 JSObject *eachFile;
1896 jsint len;
1897 jsval v;
1898 JSRegExp *re = NULL;
1899 JSFunction *func = NULL;
1900 JSString *str;
1901 jsval args[1];
1902 char *filePath;
1904 SECURITY_CHECK(cx, NULL, "list", file);
1905 JSFILE_CHECK_NATIVE("list");
1907 if (argc==1) {
1908 if (VALUE_IS_REGEXP(cx, argv[0])) {
1909 re = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0]));
1910 }else
1911 if (VALUE_IS_FUNCTION(cx, argv[0])) {
1912 func = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0]));
1913 }else{
1914 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1915 JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_FUNCTION_OR_REGEX, argv[0]);
1916 goto out;
1920 if (!js_isDirectory(cx, file)) {
1921 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1922 JSFILEMSG_CANNOT_DO_LIST_ON_A_FILE, file->path);
1923 goto out;
1926 dir = PR_OpenDir(file->path);
1927 if(!dir){
1928 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1929 JSFILEMSG_OP_FAILED, "open", file->path);
1930 goto out;
1933 /* create JSArray here... */
1934 array = JS_NewArrayObject(cx, 0, NULL);
1935 len = 0;
1937 while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))!=NULL) {
1938 /* first, check if we have a regexp */
1939 if (re!=NULL) {
1940 size_t index = 0;
1942 str = JS_NewStringCopyZ(cx, entry->name);
1943 if(!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &v)){
1944 /* don't report anything here */
1945 goto out;
1947 /* not matched! */
1948 if (JSVAL_IS_NULL(v)) {
1949 continue;
1951 }else
1952 if (func!=NULL) {
1953 str = JS_NewStringCopyZ(cx, entry->name);
1954 args[0] = STRING_TO_JSVAL(str);
1955 if(!JS_CallFunction(cx, obj, func, 1, args, &v)){
1956 goto out;
1959 if (v==JSVAL_FALSE) {
1960 continue;
1964 filePath = js_combinePath(cx, file->path, (char*)entry->name);
1966 eachFile = js_NewFileObject(cx, filePath);
1967 JS_free(cx, filePath);
1968 if (!eachFile){
1969 JS_ReportWarning(cx, "File %s cannot be retrieved", filePath);
1970 continue;
1972 v = OBJECT_TO_JSVAL(eachFile);
1973 JS_SetElement(cx, array, len, &v);
1974 JS_SetProperty(cx, array, entry->name, &v);
1975 len++;
1978 if(PR_CloseDir(dir)!=PR_SUCCESS){
1979 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1980 JSFILEMSG_OP_FAILED, "close", file->path);
1981 goto out;
1983 *rval = OBJECT_TO_JSVAL(array);
1984 return JS_TRUE;
1985 out:
1986 *rval = JSVAL_NULL;
1987 return JS_FALSE;
1990 static JSBool
1991 file_mkdir(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1993 JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1995 SECURITY_CHECK(cx, NULL, "mkdir", file);
1996 JSFILE_CHECK_ONE_ARG("mkdir");
1997 JSFILE_CHECK_NATIVE("mkdir");
1999 /* if the current file is not a directory, find out the directory name */
2000 if (!js_isDirectory(cx, file)) {
2001 char *dir = js_fileDirectoryName(cx, file->path);
2002 JSObject *dirObj = js_NewFileObject(cx, dir);
2004 JS_free(cx, dir);
2006 /* call file_mkdir with the right set of parameters if needed */
2007 if (file_mkdir(cx, dirObj, argc, argv, rval))
2008 return JS_TRUE;
2009 else
2010 goto out;
2011 }else{
2012 char *dirName = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
2013 char *fullName;
2015 fullName = js_combinePath(cx, file->path, dirName);
2016 if (PR_MkDir(fullName, 0755)==PR_SUCCESS){
2017 *rval = JSVAL_TRUE;
2018 JS_free(cx, fullName);
2019 return JS_TRUE;
2020 }else{
2021 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2022 JSFILEMSG_OP_FAILED, "mkdir", fullName);
2023 JS_free(cx, fullName);
2024 goto out;
2027 out:
2028 *rval = JSVAL_FALSE;
2029 return JS_FALSE;
2032 static JSBool
2033 file_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval*rval)
2035 JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
2036 JSString *str;
2038 str = JS_NewStringCopyZ(cx, file->path);
2039 if (!str)
2040 return JS_FALSE;
2041 *rval = STRING_TO_JSVAL(str);
2042 return JS_TRUE;
2045 static JSBool
2046 file_toURL(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2048 JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
2049 char url[MAX_PATH_LENGTH];
2050 jschar *urlChars;
2051 size_t len;
2052 JSString *str;
2054 JSFILE_CHECK_NATIVE("toURL");
2056 sprintf(url, "file://%s", file->path);
2058 len = strlen(url);
2059 urlChars = js_InflateString(cx, url, &len);
2060 if (!urlChars)
2061 return JS_FALSE;
2062 str = js_NewString(cx, urlChars, len);
2063 if (!str) {
2064 JS_free(cx, urlChars);
2065 return JS_FALSE;
2067 *rval = STRING_TO_JSVAL(str);
2069 /* TODO: js_escape in jsstr.h may go away at some point */
2070 return js_str_escape(cx, obj, 0, rval, rval);
2072 out:
2073 *rval = JSVAL_VOID;
2074 return JS_FALSE;
2078 static void
2079 file_finalize(JSContext *cx, JSObject *obj)
2081 JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
2083 if(file) {
2084 /* Close the file before exiting. */
2085 if(file->isOpen && !file->isNative) {
2086 jsval vp;
2087 file_close(cx, obj, 0, NULL, &vp);
2090 if (file->path)
2091 JS_free(cx, file->path);
2093 JS_free(cx, file);
2098 Allocates memory for the file object, sets fields to defaults.
2100 static JSFile*
2101 file_init(JSContext *cx, JSObject *obj, char *bytes)
2103 JSFile *file;
2105 file = JS_malloc(cx, sizeof *file);
2106 if (!file)
2107 return NULL;
2108 memset(file, 0 , sizeof *file);
2110 js_ResetAttributes(file);
2112 file->path = RESOLVE_PATH(cx, bytes);
2114 if (!JS_SetPrivate(cx, obj, file)) {
2115 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2116 JSFILEMSG_CANNOT_SET_PRIVATE_FILE, file->path);
2117 JS_free(cx, file);
2118 return NULL;
2121 return file;
2124 /* Returns a JSObject. This function is globally visible */
2125 JS_PUBLIC_API(JSObject*)
2126 js_NewFileObject(JSContext *cx, char *filename)
2128 JSObject *obj;
2129 JSFile *file;
2131 obj = JS_NewObject(cx, &js_FileClass, NULL, NULL);
2132 if (!obj){
2133 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2134 JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObject");
2135 return NULL;
2137 file = file_init(cx, obj, filename);
2138 if(!file) return NULL;
2139 return obj;
2142 /* Internal function, used for cases which NSPR file support doesn't cover */
2143 JSObject*
2144 js_NewFileObjectFromFILE(JSContext *cx, FILE *nativehandle, char *filename,
2145 int32 mode, JSBool open, JSBool randomAccess)
2147 JSObject *obj;
2148 JSFile *file;
2150 obj = JS_NewObject(cx, &js_FileClass, NULL, NULL);
2151 if (!obj){
2152 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2153 JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObjectFromFILE");
2154 return NULL;
2156 file = file_init(cx, obj, filename);
2157 if(!file) return NULL;
2159 file->nativehandle = nativehandle;
2161 /* free result of RESOLVE_PATH from file_init. */
2162 JS_ASSERT(file->path != NULL);
2163 JS_free(cx, file->path);
2165 file->path = strdup(filename);
2166 file->isOpen = open;
2167 file->mode = mode;
2168 file->hasRandomAccess = randomAccess;
2169 file->isNative = JS_TRUE;
2170 return obj;
2174 Real file constructor that is called from JavaScript.
2175 Basically, does error processing and calls file_init.
2177 static JSBool
2178 file_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
2179 jsval *rval)
2181 JSString *str;
2182 JSFile *file;
2184 if (!JS_IsConstructing(cx)) {
2185 /* Replace obj with a new File object. */
2186 obj = JS_NewObject(cx, &js_FileClass, NULL, NULL);
2187 if (!obj)
2188 return JS_FALSE;
2189 *rval = OBJECT_TO_JSVAL(obj);
2192 str = (argc == 0)
2193 ? JS_InternString(cx, "")
2194 : JS_ValueToString(cx, argv[0]);
2196 if (!str) {
2197 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2198 JSFILEMSG_FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR,
2199 argv[0]);
2200 return JS_FALSE;
2203 file = file_init(cx, obj, JS_GetStringBytes(str));
2204 if (!file)
2205 return JS_FALSE;
2207 SECURITY_CHECK(cx, NULL, "constructor", file);
2209 return JS_TRUE;
2212 /* -------------------- File methods and properties ------------------------- */
2213 static JSFunctionSpec file_functions[] = {
2214 { "open", file_open, 0},
2215 { "close", file_close, 0},
2216 { "remove", file_remove, 0},
2217 { "copyTo", file_copyTo, 0},
2218 { "renameTo", file_renameTo, 0},
2219 { "flush", file_flush, 0},
2220 { "seek", file_seek, 0},
2221 { "read", file_read, 0},
2222 { "readln", file_readln, 0},
2223 { "readAll", file_readAll, 0},
2224 { "write", file_write, 0},
2225 { "writeln", file_writeln, 0},
2226 { "writeAll", file_writeAll, 0},
2227 { "list", file_list, 0},
2228 { "mkdir", file_mkdir, 0},
2229 { "toString", file_toString, 0},
2230 { "toURL", file_toURL, 0},
2234 enum file_tinyid {
2235 FILE_LENGTH = -2,
2236 FILE_PARENT = -3,
2237 FILE_PATH = -4,
2238 FILE_NAME = -5,
2239 FILE_ISDIR = -6,
2240 FILE_ISFILE = -7,
2241 FILE_EXISTS = -8,
2242 FILE_CANREAD = -9,
2243 FILE_CANWRITE = -10,
2244 FILE_OPEN = -11,
2245 FILE_TYPE = -12,
2246 FILE_MODE = -13,
2247 FILE_CREATED = -14,
2248 FILE_MODIFIED = -15,
2249 FILE_SIZE = -16,
2250 FILE_RANDOMACCESS = -17,
2251 FILE_POSITION = -18,
2252 FILE_APPEND = -19,
2253 FILE_REPLACE = -20,
2254 FILE_AUTOFLUSH = -21,
2255 FILE_ISNATIVE = -22,
2258 static JSPropertySpec file_props[] = {
2259 {"length", FILE_LENGTH, JSPROP_ENUMERATE | JSPROP_READONLY },
2260 {"parent", FILE_PARENT, JSPROP_ENUMERATE | JSPROP_READONLY },
2261 {"path", FILE_PATH, JSPROP_ENUMERATE | JSPROP_READONLY },
2262 {"name", FILE_NAME, JSPROP_ENUMERATE | JSPROP_READONLY },
2263 {"isDirectory", FILE_ISDIR, JSPROP_ENUMERATE | JSPROP_READONLY },
2264 {"isFile", FILE_ISFILE, JSPROP_ENUMERATE | JSPROP_READONLY },
2265 {"exists", FILE_EXISTS, JSPROP_ENUMERATE | JSPROP_READONLY },
2266 {"canRead", FILE_CANREAD, JSPROP_ENUMERATE | JSPROP_READONLY },
2267 {"canWrite", FILE_CANWRITE, JSPROP_ENUMERATE | JSPROP_READONLY },
2268 {"canAppend", FILE_APPEND, JSPROP_ENUMERATE | JSPROP_READONLY },
2269 {"canReplace", FILE_REPLACE, JSPROP_ENUMERATE | JSPROP_READONLY },
2270 {"isOpen", FILE_OPEN, JSPROP_ENUMERATE | JSPROP_READONLY },
2271 {"type", FILE_TYPE, JSPROP_ENUMERATE | JSPROP_READONLY },
2272 {"mode", FILE_MODE, JSPROP_ENUMERATE | JSPROP_READONLY },
2273 {"creationTime", FILE_CREATED, JSPROP_ENUMERATE | JSPROP_READONLY },
2274 {"lastModified", FILE_MODIFIED, JSPROP_ENUMERATE | JSPROP_READONLY },
2275 {"size", FILE_SIZE, JSPROP_ENUMERATE | JSPROP_READONLY },
2276 {"hasRandomAccess", FILE_RANDOMACCESS, JSPROP_ENUMERATE | JSPROP_READONLY },
2277 {"hasAutoFlush", FILE_AUTOFLUSH, JSPROP_ENUMERATE | JSPROP_READONLY },
2278 {"position", FILE_POSITION, JSPROP_ENUMERATE },
2279 {"isNative", FILE_ISNATIVE, JSPROP_ENUMERATE | JSPROP_READONLY },
2283 /* ------------------------- Property getter/setter ------------------------- */
2284 static JSBool
2285 file_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2287 JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
2288 char *bytes;
2289 JSString *str;
2290 jsint tiny;
2291 PRFileInfo info;
2292 JSBool flag;
2293 PRExplodedTime expandedTime;
2295 tiny = JSVAL_TO_INT(id);
2296 if (!file)
2297 return JS_TRUE;
2299 switch (tiny) {
2300 case FILE_PARENT:
2301 SECURITY_CHECK(cx, NULL, "parent", file);
2302 if (!js_parent(cx, file, vp))
2303 return JS_FALSE;
2304 break;
2305 case FILE_PATH:
2306 str = JS_NewStringCopyZ(cx, file->path);
2307 if (!str)
2308 return JS_FALSE;
2309 *vp = STRING_TO_JSVAL(str);
2310 break;
2311 case FILE_NAME:
2312 if (!js_name(cx, file, vp))
2313 return JS_FALSE;
2314 break;
2315 case FILE_ISDIR:
2316 SECURITY_CHECK(cx, NULL, "isDirectory", file);
2317 *vp = BOOLEAN_TO_JSVAL(js_isDirectory(cx, file));
2318 break;
2319 case FILE_ISFILE:
2320 SECURITY_CHECK(cx, NULL, "isFile", file);
2321 *vp = BOOLEAN_TO_JSVAL(js_isFile(cx, file));
2322 break;
2323 case FILE_EXISTS:
2324 SECURITY_CHECK(cx, NULL, "exists", file);
2325 *vp = BOOLEAN_TO_JSVAL(js_exists(cx, file));
2326 break;
2327 case FILE_ISNATIVE:
2328 SECURITY_CHECK(cx, NULL, "isNative", file);
2329 *vp = BOOLEAN_TO_JSVAL(file->isNative);
2330 break;
2331 case FILE_CANREAD:
2332 SECURITY_CHECK(cx, NULL, "canRead", file);
2333 *vp = BOOLEAN_TO_JSVAL(js_canRead(cx, file));
2334 break;
2335 case FILE_CANWRITE:
2336 SECURITY_CHECK(cx, NULL, "canWrite", file);
2337 *vp = BOOLEAN_TO_JSVAL(js_canWrite(cx, file));
2338 break;
2339 case FILE_OPEN:
2340 SECURITY_CHECK(cx, NULL, "isOpen", file);
2341 *vp = BOOLEAN_TO_JSVAL(file->isOpen);
2342 break;
2343 case FILE_APPEND :
2344 SECURITY_CHECK(cx, NULL, "canAppend", file);
2345 JSFILE_CHECK_OPEN("canAppend");
2346 *vp = BOOLEAN_TO_JSVAL(!file->isNative &&
2347 (file->mode&PR_APPEND)==PR_APPEND);
2348 break;
2349 case FILE_REPLACE :
2350 SECURITY_CHECK(cx, NULL, "canReplace", file);
2351 JSFILE_CHECK_OPEN("canReplace");
2352 *vp = BOOLEAN_TO_JSVAL(!file->isNative &&
2353 (file->mode&PR_TRUNCATE)==PR_TRUNCATE);
2354 break;
2355 case FILE_AUTOFLUSH :
2356 SECURITY_CHECK(cx, NULL, "hasAutoFlush", file);
2357 JSFILE_CHECK_OPEN("hasAutoFlush");
2358 *vp = BOOLEAN_TO_JSVAL(!file->isNative && file->hasAutoflush);
2359 break;
2360 case FILE_TYPE:
2361 SECURITY_CHECK(cx, NULL, "type", file);
2362 JSFILE_CHECK_OPEN("type");
2363 if(js_isDirectory(cx, file)){
2364 *vp = JSVAL_VOID;
2365 break;
2368 switch (file->type) {
2369 case ASCII:
2370 *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, asciistring));
2371 break;
2372 case UTF8:
2373 *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, utfstring));
2374 break;
2375 case UCS2:
2376 *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, unicodestring));
2377 break;
2378 default:
2379 JS_ReportWarning(cx, "Unsupported file type %d, proceeding",
2380 file->type);
2382 break;
2383 case FILE_MODE:
2384 SECURITY_CHECK(cx, NULL, "mode", file);
2385 JSFILE_CHECK_OPEN("mode");
2386 bytes = JS_malloc(cx, MODE_SIZE);
2387 bytes[0] = '\0';
2388 flag = JS_FALSE;
2390 if ((file->mode&PR_RDONLY)==PR_RDONLY) {
2391 if (flag) strcat(bytes, ",");
2392 strcat(bytes, "read");
2393 flag = JS_TRUE;
2395 if ((file->mode&PR_WRONLY)==PR_WRONLY) {
2396 if (flag) strcat(bytes, ",");
2397 strcat(bytes, "write");
2398 flag = JS_TRUE;
2400 if ((file->mode&PR_RDWR)==PR_RDWR) {
2401 if (flag) strcat(bytes, ",");
2402 strcat(bytes, "readWrite");
2403 flag = JS_TRUE;
2405 if ((file->mode&PR_APPEND)==PR_APPEND) {
2406 if (flag) strcat(bytes, ",");
2407 strcat(bytes, "append");
2408 flag = JS_TRUE;
2410 if ((file->mode&PR_CREATE_FILE)==PR_CREATE_FILE) {
2411 if (flag) strcat(bytes, ",");
2412 strcat(bytes, "create");
2413 flag = JS_TRUE;
2415 if ((file->mode&PR_TRUNCATE)==PR_TRUNCATE) {
2416 if (flag) strcat(bytes, ",");
2417 strcat(bytes, "replace");
2418 flag = JS_TRUE;
2420 if (file->hasAutoflush) {
2421 if (flag) strcat(bytes, ",");
2422 strcat(bytes, "hasAutoFlush");
2423 flag = JS_TRUE;
2425 *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, bytes));
2426 JS_free(cx, bytes);
2427 break;
2428 case FILE_CREATED:
2429 SECURITY_CHECK(cx, NULL, "creationTime", file);
2430 JSFILE_CHECK_NATIVE("creationTime");
2431 if(((file->isOpen)?
2432 PR_GetOpenFileInfo(file->handle, &info):
2433 PR_GetFileInfo(file->path, &info))!=PR_SUCCESS){
2434 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2435 JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
2436 goto out;
2439 PR_ExplodeTime(info.creationTime, PR_LocalTimeParameters,&expandedTime);
2440 *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx, expandedTime.tm_year,
2441 expandedTime.tm_month,
2442 expandedTime.tm_mday,
2443 expandedTime.tm_hour,
2444 expandedTime.tm_min,
2445 expandedTime.tm_sec));
2446 break;
2447 case FILE_MODIFIED:
2448 SECURITY_CHECK(cx, NULL, "lastModified", file);
2449 JSFILE_CHECK_NATIVE("lastModified");
2450 if(((file->isOpen)?
2451 PR_GetOpenFileInfo(file->handle, &info):
2452 PR_GetFileInfo(file->path, &info))!=PR_SUCCESS){
2453 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2454 JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
2455 goto out;
2458 PR_ExplodeTime(info.modifyTime, PR_LocalTimeParameters, &expandedTime);
2459 *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx, expandedTime.tm_year,
2460 expandedTime.tm_month,
2461 expandedTime.tm_mday,
2462 expandedTime.tm_hour,
2463 expandedTime.tm_min,
2464 expandedTime.tm_sec));
2465 break;
2466 case FILE_SIZE:
2467 SECURITY_CHECK(cx, NULL, "size", file);
2468 *vp = js_size(cx, file);
2469 break;
2470 case FILE_LENGTH:
2471 SECURITY_CHECK(cx, NULL, "length", file);
2472 JSFILE_CHECK_NATIVE("length");
2474 if (js_isDirectory(cx, file)) { /* XXX debug me */
2475 PRDir *dir;
2476 PRDirEntry *entry;
2477 jsint count = 0;
2479 if(!(dir = PR_OpenDir(file->path))){
2480 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2481 JSFILEMSG_CANNOT_OPEN_DIR, file->path);
2482 goto out;
2485 while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))) {
2486 count++;
2489 if(!PR_CloseDir(dir)){
2490 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2491 JSFILEMSG_OP_FAILED, "close", file->path);
2493 goto out;
2496 *vp = INT_TO_JSVAL(count);
2497 break;
2498 }else{
2499 /* return file size */
2500 *vp = js_size(cx, file);
2502 break;
2503 case FILE_RANDOMACCESS:
2504 SECURITY_CHECK(cx, NULL, "hasRandomAccess", file);
2505 JSFILE_CHECK_OPEN("hasRandomAccess");
2506 *vp = BOOLEAN_TO_JSVAL(file->hasRandomAccess);
2507 break;
2508 case FILE_POSITION:
2509 SECURITY_CHECK(cx, NULL, "position", file);
2510 JSFILE_CHECK_NATIVE("position");
2511 JSFILE_CHECK_OPEN("position");
2513 if(!file->hasRandomAccess){
2514 JS_ReportWarning(cx, "File %s doesn't support random access, can't report the position, proceeding");
2515 *vp = JSVAL_VOID;
2516 break;
2519 if (file->isOpen && js_isFile(cx, file)) {
2520 int pos = PR_Seek(file->handle, 0, PR_SEEK_CUR);
2521 if(pos!=-1){
2522 *vp = INT_TO_JSVAL(pos);
2523 }else{
2524 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2525 JSFILEMSG_CANNOT_REPORT_POSITION, file->path);
2526 goto out;
2528 }else {
2529 JS_ReportWarning(cx, "File %s is closed or not a plain file,"
2530 " can't report position, proceeding");
2531 goto out;
2533 break;
2534 default:
2535 SECURITY_CHECK(cx, NULL, "file_access", file);
2537 /* this is some other property -- try to use the dir["file"] syntax */
2538 if (js_isDirectory(cx, file)) {
2539 PRDir *dir = NULL;
2540 PRDirEntry *entry = NULL;
2541 char *prop_name;
2543 str = JS_ValueToString(cx, id);
2544 if (!str)
2545 return JS_FALSE;
2547 prop_name = JS_GetStringBytes(str);
2549 /* no native files past this point */
2550 dir = PR_OpenDir(file->path);
2551 if(!dir) {
2552 /* This is probably not a directory */
2553 JS_ReportWarning(cx, "Can't open directory %s", file->path);
2554 return JS_FALSE;
2557 while ((entry = PR_ReadDir(dir, PR_SKIP_NONE)) != NULL) {
2558 if (!strcmp(entry->name, prop_name)){
2559 bytes = js_combinePath(cx, file->path, prop_name);
2560 *vp = OBJECT_TO_JSVAL(js_NewFileObject(cx, bytes));
2561 PR_CloseDir(dir);
2562 JS_free(cx, bytes);
2563 return !JSVAL_IS_NULL(*vp);
2566 PR_CloseDir(dir);
2569 return JS_TRUE;
2571 out:
2572 return JS_FALSE;
2575 static JSBool
2576 file_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2578 JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
2579 jsint slot;
2581 if (JSVAL_IS_STRING(id)){
2582 return JS_TRUE;
2585 slot = JSVAL_TO_INT(id);
2587 switch (slot) {
2588 /* File.position = 10 */
2589 case FILE_POSITION:
2590 SECURITY_CHECK(cx, NULL, "set_position", file);
2591 JSFILE_CHECK_NATIVE("set_position");
2593 if(!file->hasRandomAccess){
2594 JS_ReportWarning(cx, "File %s doesn't support random access, can't "
2595 "report the position, proceeding");
2596 goto out;
2599 if (file->isOpen && js_isFile(cx, file)) {
2600 int32 pos;
2601 int32 offset;
2603 if (!JS_ValueToInt32(cx, *vp, &offset)){
2604 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2605 JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "position", *vp);
2606 goto out;
2609 pos = PR_Seek(file->handle, offset, PR_SEEK_SET);
2611 if(pos!=-1){
2612 *vp = INT_TO_JSVAL(pos);
2613 }else{
2614 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2615 JSFILEMSG_CANNOT_SET_POSITION, file->path);
2616 goto out;
2618 } else {
2619 JS_ReportWarning(cx, "File %s is closed or not a file, can't set "
2620 "position, proceeding", file->path);
2621 goto out;
2625 return JS_TRUE;
2626 out:
2627 return JS_FALSE;
2631 File.currentDir = new File("D:\") or File.currentDir = "D:\"
2633 static JSBool
2634 file_currentDirSetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2636 JSFile *file;
2638 file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
2640 /* Look at the rhs and extract a file object from it */
2641 if (JSVAL_IS_OBJECT(*vp)) {
2642 if (JS_InstanceOf(cx, obj, &js_FileClass, NULL)) {
2643 /* Braindamaged rhs -- just return the old value */
2644 if (file && (!js_exists(cx, file) || !js_isDirectory(cx, file))) {
2645 JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp);
2646 return JS_FALSE;
2647 } else {
2648 chdir(file->path);
2649 return JS_TRUE;
2651 } else {
2652 return JS_FALSE;
2654 } else {
2655 JSObject *rhsObject;
2656 char *path;
2658 path = JS_GetStringBytes(JS_ValueToString(cx, *vp));
2659 rhsObject = js_NewFileObject(cx, path);
2660 if (!rhsObject)
2661 return JS_FALSE;
2663 if (!file || !js_exists(cx, file) || !js_isDirectory(cx, file)){
2664 JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp);
2665 } else {
2666 *vp = OBJECT_TO_JSVAL(rhsObject);
2667 chdir(path);
2671 return JS_TRUE;
2674 /* Declare class */
2675 JSClass js_FileClass = {
2676 "File", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_File),
2677 JS_PropertyStub, JS_PropertyStub, file_getProperty, file_setProperty,
2678 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, file_finalize
2681 /* -------------------- Functions exposed to the outside -------------------- */
2682 JS_PUBLIC_API(JSObject*)
2683 js_InitFileClass(JSContext *cx, JSObject* obj)
2685 JSObject *file, *ctor, *afile;
2686 jsval vp;
2687 char *currentdir;
2688 char separator[2];
2690 file = JS_InitClass(cx, obj, NULL, &js_FileClass, file_constructor, 1,
2691 file_props, file_functions, NULL, NULL);
2692 if (!file) {
2693 JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
2694 JSFILEMSG_INIT_FAILED);
2695 return NULL;
2698 ctor = JS_GetConstructor(cx, file);
2699 if (!ctor) return NULL;
2701 /* Define CURRENTDIR property. We are doing this to get a
2702 slash at the end of the current dir */
2703 afile = js_NewFileObject(cx, CURRENT_DIR);
2704 currentdir = JS_malloc(cx, MAX_PATH_LENGTH);
2705 currentdir = getcwd(currentdir, MAX_PATH_LENGTH);
2706 afile = js_NewFileObject(cx, currentdir);
2707 JS_free(cx, currentdir);
2708 vp = OBJECT_TO_JSVAL(afile);
2709 JS_DefinePropertyWithTinyId(cx, ctor, CURRENTDIR_PROPERTY, 0, vp,
2710 JS_PropertyStub, file_currentDirSetter,
2711 JSPROP_ENUMERATE | JSPROP_READONLY );
2713 /* Define input */
2714 vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stdin,
2715 STDINPUT_NAME, PR_RDONLY, JS_TRUE, JS_FALSE));
2716 JS_SetProperty(cx, ctor, "input", &vp);
2718 /* Define output */
2719 vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stdout,
2720 STDOUTPUT_NAME, PR_WRONLY, JS_TRUE, JS_FALSE));
2721 JS_SetProperty(cx, ctor, "output", &vp);
2723 /* Define error */
2724 vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stderr,
2725 STDERROR_NAME, PR_WRONLY, JS_TRUE, JS_FALSE));
2726 JS_SetProperty(cx, ctor, "error", &vp);
2728 separator[0] = FILESEPARATOR;
2729 separator[1] = '\0';
2730 vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, separator));
2731 JS_DefinePropertyWithTinyId(cx, ctor, SEPARATOR_PROPERTY, 0, vp,
2732 JS_PropertyStub, JS_PropertyStub,
2733 JSPROP_ENUMERATE | JSPROP_READONLY );
2734 return file;
2736 #endif /* JS_HAS_FILE_OBJECT */