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

Contents of /trunk/js/jsfile.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 507 - (show annotations)
Sun Jan 10 07:23:34 2010 UTC (9 years, 10 months ago) by siliconforks
File size: 82924 byte(s)
Update SpiderMonkey from Firefox 3.6rc1.

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

  ViewVC Help
Powered by ViewVC 1.1.24