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

Annotation of /trunk/js/jsfile.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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

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