/[jscoverage]/trunk/js/jsfile.cpp
ViewVC logotype

Contents of /trunk/js/jsfile.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 460 - (show annotations)
Sat Sep 26 23:15:22 2009 UTC (10 years, 10 months ago) by siliconforks
File size: 82781 byte(s)
Upgrade to SpiderMonkey from Firefox 3.5.3.

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

  ViewVC Help
Powered by ViewVC 1.1.24