Parent Directory
|
Revision Log
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 |