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

Annotation of /trunk/js/jsfile.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 332 - (hide annotations)
Thu Oct 23 19:03:33 2008 UTC (11 years, 9 months ago) by siliconforks
File size: 82451 byte(s)
Add SpiderMonkey from Firefox 3.1b1.

The following directories and files were removed:
correct/, correct.js
liveconnect/
nanojit/
t/
v8/
vprof/
xpconnect/
all JavaScript files (Y.js, call.js, if.js, math-partial-sums.js, md5.js, perfect.js, trace-test.js, trace.js)


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