PageRenderTime 60ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsfile.cpp

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C++ | 2255 lines | 1724 code | 329 blank | 202 comment | 347 complexity | 3bf1c2ece828bedc0e49f097d9a62449 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
  1. /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. * vim: set ts=8 sw=4 et tw=78:
  3. *
  4. * ***** BEGIN LICENSE BLOCK *****
  5. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  6. *
  7. * The contents of this file are subject to the Mozilla Public License Version
  8. * 1.1 (the "License"); you may not use this file except in compliance with
  9. * the License. You may obtain a copy of the License at
  10. * http://www.mozilla.org/MPL/
  11. *
  12. * Software distributed under the License is distributed on an "AS IS" basis,
  13. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  14. * for the specific language governing rights and limitations under the
  15. * License.
  16. *
  17. * The Original Code is Mozilla Communicator client code, released
  18. * March 31, 1998.
  19. *
  20. * The Initial Developer of the Original Code is
  21. * Netscape Communications Corporation.
  22. * Portions created by the Initial Developer are Copyright (C) 1998
  23. * the Initial Developer. All Rights Reserved.
  24. *
  25. * Contributor(s):
  26. *
  27. * Alternatively, the contents of this file may be used under the terms of
  28. * either of the GNU General Public License Version 2 or later (the "GPL"),
  29. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30. * in which case the provisions of the GPL or the LGPL are applicable instead
  31. * of those above. If you wish to allow use of your version of this file only
  32. * under the terms of either the GPL or the LGPL, and not to allow others to
  33. * use your version of this file under the terms of the MPL, indicate your
  34. * decision by deleting the provisions above and replace them with the notice
  35. * and other provisions required by the GPL or the LGPL. If you do not delete
  36. * the provisions above, a recipient may use your version of this file under
  37. * the terms of any one of the MPL, the GPL or the LGPL.
  38. *
  39. * ***** END LICENSE BLOCK ***** */
  40. /*
  41. * JS File object
  42. */
  43. #if JS_HAS_FILE_OBJECT
  44. #include "jsstddef.h"
  45. #include "jsfile.h"
  46. /* ----------------- Platform-specific includes and defines ----------------- */
  47. #if defined(XP_WIN) || defined(XP_OS2)
  48. # include <direct.h>
  49. # include <io.h>
  50. # include <sys/types.h>
  51. # include <sys/stat.h>
  52. # define FILESEPARATOR '\\'
  53. # define FILESEPARATOR2 '/'
  54. # define CURRENT_DIR "c:\\"
  55. # define POPEN _popen
  56. # define PCLOSE _pclose
  57. #elif defined(XP_UNIX) || defined(XP_BEOS)
  58. # include <strings.h>
  59. # include <stdio.h>
  60. # include <stdlib.h>
  61. # include <unistd.h>
  62. # define FILESEPARATOR '/'
  63. # define FILESEPARATOR2 '\0'
  64. # define CURRENT_DIR "/"
  65. # define POPEN popen
  66. # define PCLOSE pclose
  67. #endif
  68. /* --------------- Platform-independent includes and defines ---------------- */
  69. #include "jsapi.h"
  70. #include "jsatom.h"
  71. #include "jscntxt.h"
  72. #include "jsdate.h"
  73. #include "jsdbgapi.h"
  74. #include "jsemit.h"
  75. #include "jsfun.h"
  76. #include "jslock.h"
  77. #include "jsobj.h"
  78. #include "jsparse.h"
  79. #include "jsscan.h"
  80. #include "jsscope.h"
  81. #include "jsscript.h"
  82. #include "jsstr.h"
  83. #include "jsutil.h" /* Added by JSIFY */
  84. #include <string.h>
  85. /* NSPR dependencies */
  86. #include "prio.h"
  87. #include "prerror.h"
  88. #define SPECIAL_FILE_STRING "Special File"
  89. #define CURRENTDIR_PROPERTY "currentDir"
  90. #define SEPARATOR_PROPERTY "separator"
  91. #define FILE_CONSTRUCTOR "File"
  92. #define PIPE_SYMBOL '|'
  93. #define ASCII 0
  94. #define UTF8 1
  95. #define UCS2 2
  96. #define asciistring "text"
  97. #define utfstring "binary"
  98. #define unicodestring "unicode"
  99. #define MAX_PATH_LENGTH 1024
  100. #define MODE_SIZE 256
  101. #define NUMBER_SIZE 32
  102. #define MAX_LINE_LENGTH 256
  103. #define URL_PREFIX "file://"
  104. #define STDINPUT_NAME "Standard input stream"
  105. #define STDOUTPUT_NAME "Standard output stream"
  106. #define STDERROR_NAME "Standard error stream"
  107. #define RESOLVE_PATH js_canonicalPath /* js_absolutePath */
  108. /* Error handling */
  109. typedef enum JSFileErrNum {
  110. #define MSG_DEF(name, number, count, exception, format) \
  111. name = number,
  112. #include "jsfile.msg"
  113. #undef MSG_DEF
  114. JSFileErr_Limit
  115. #undef MSGDEF
  116. } JSFileErrNum;
  117. #define JSFILE_HAS_DFLT_MSG_STRINGS 1
  118. JSErrorFormatString JSFile_ErrorFormatString[JSFileErr_Limit] = {
  119. #if JSFILE_HAS_DFLT_MSG_STRINGS
  120. #define MSG_DEF(name, number, count, exception, format) \
  121. { format, count },
  122. #else
  123. #define MSG_DEF(name, number, count, exception, format) \
  124. { NULL, count },
  125. #endif
  126. #include "jsfile.msg"
  127. #undef MSG_DEF
  128. };
  129. const JSErrorFormatString *
  130. JSFile_GetErrorMessage(void *userRef, const char *locale,
  131. const uintN errorNumber)
  132. {
  133. if ((errorNumber > 0) && (errorNumber < JSFileErr_Limit))
  134. return &JSFile_ErrorFormatString[errorNumber];
  135. else
  136. return NULL;
  137. }
  138. #define JSFILE_CHECK_NATIVE(op) \
  139. if (file->isNative) { \
  140. JS_ReportWarning(cx, "Cannot call or access \"%s\" on native file %s",\
  141. op, file->path); \
  142. goto out; \
  143. }
  144. #define JSFILE_CHECK_WRITE \
  145. if (!file->isOpen) { \
  146. JS_ReportWarning(cx, \
  147. "File %s is closed, will open it for writing, proceeding", \
  148. file->path); \
  149. js_FileOpen(cx, obj, file, "write,append,create"); \
  150. } \
  151. if (!js_canWrite(cx, file)) { \
  152. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \
  153. JSFILEMSG_CANNOT_WRITE, file->path); \
  154. goto out; \
  155. }
  156. #define JSFILE_CHECK_READ \
  157. if (!file->isOpen) { \
  158. JS_ReportWarning(cx, \
  159. "File %s is closed, will open it for reading, proceeding", \
  160. file->path); \
  161. js_FileOpen(cx, obj, file, "read"); \
  162. } \
  163. if (!js_canRead(cx, file)) { \
  164. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \
  165. JSFILEMSG_CANNOT_READ, file->path); \
  166. goto out; \
  167. }
  168. #define JSFILE_CHECK_OPEN(op) \
  169. if (!file->isOpen) { \
  170. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \
  171. JSFILEMSG_FILE_MUST_BE_OPEN, op); \
  172. goto out; \
  173. }
  174. #define JSFILE_CHECK_CLOSED(op) \
  175. if (file->isOpen) { \
  176. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \
  177. JSFILEMSG_FILE_MUST_BE_CLOSED, op); \
  178. goto out; \
  179. }
  180. #define JSFILE_CHECK_ONE_ARG(op) \
  181. if (argc != 1) { \
  182. char str[NUMBER_SIZE]; \
  183. sprintf(str, "%d", argc); \
  184. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \
  185. JSFILEMSG_EXPECTS_ONE_ARG_ERROR, op, str); \
  186. goto out; \
  187. }
  188. /*
  189. Security mechanism, should define a callback for this.
  190. The parameters are as follows:
  191. SECURITY_CHECK(JSContext *cx, JSPrincipals *ps, char *op_name, JSFile *file)
  192. XXX Should this be a real function returning a JSBool result (and getting
  193. some typesafety help from the compiler?).
  194. */
  195. #define SECURITY_CHECK(cx, ps, op, file) \
  196. /* Define a callback here... */
  197. /* Structure representing the file internally */
  198. typedef struct JSFile {
  199. char *path; /* the path to the file. */
  200. JSBool isOpen;
  201. int32 mode; /* mode used to open the file: read, write, append, create, etc.. */
  202. int32 type; /* Asciiz, utf, unicode */
  203. char byteBuffer[3]; /* bytes read in advance by js_FileRead ( UTF8 encoding ) */
  204. jsint nbBytesInBuf; /* number of bytes stored in the buffer above */
  205. jschar charBuffer; /* character read in advance by readln ( mac files only ) */
  206. JSBool charBufferUsed; /* flag indicating if the buffer above is being used */
  207. JSBool hasRandomAccess;/* can the file be randomly accessed? false for stdin, and
  208. UTF-encoded files. */
  209. JSBool hasAutoflush; /* should we force a flush for each line break? */
  210. JSBool isNative; /* if the file is using OS-specific file FILE type */
  211. /* We can actually put the following two in a union since they should never be used at the same time */
  212. PRFileDesc *handle; /* the handle for the file, if open. */
  213. FILE *nativehandle; /* native handle, for stuff NSPR doesn't do. */
  214. JSBool isPipe; /* if the file is really an OS pipe */
  215. } JSFile;
  216. /* a few forward declarations... */
  217. JS_PUBLIC_API(JSObject*) js_NewFileObject(JSContext *cx, char *filename);
  218. static JSBool file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
  219. static JSBool file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
  220. /* New filename manipulation procesures */
  221. /* assumes we don't have leading/trailing spaces */
  222. static JSBool
  223. js_filenameHasAPipe(const char *filename)
  224. {
  225. if (!filename)
  226. return JS_FALSE;
  227. return filename[0] == PIPE_SYMBOL ||
  228. filename[strlen(filename) - 1] == PIPE_SYMBOL;
  229. }
  230. static JSBool
  231. js_isAbsolute(const char *name)
  232. {
  233. #if defined(XP_WIN) || defined(XP_OS2)
  234. return *name && name[1] == ':';
  235. #else
  236. return (name[0]
  237. # if defined(XP_UNIX) || defined(XP_BEOS)
  238. ==
  239. # else
  240. !=
  241. # endif
  242. FILESEPARATOR);
  243. #endif
  244. }
  245. /*
  246. * Concatenates base and name to produce a valid filename.
  247. * Returned string must be freed.
  248. */
  249. static char*
  250. js_combinePath(JSContext *cx, const char *base, const char *name)
  251. {
  252. int len = strlen(base);
  253. char* result = JS_malloc(cx, len + strlen(name) + 2);
  254. if (!result)
  255. return NULL;
  256. strcpy(result, base);
  257. if (base[len - 1] != FILESEPARATOR && base[len - 1] != FILESEPARATOR2) {
  258. result[len] = FILESEPARATOR;
  259. result[len + 1] = '\0';
  260. }
  261. strcat(result, name);
  262. return result;
  263. }
  264. /* Extract the last component from a path name. Returned string must be freed */
  265. static char *
  266. js_fileBaseName(JSContext *cx, const char *pathname)
  267. {
  268. jsint index, aux;
  269. char *result;
  270. index = strlen(pathname)-1;
  271. /* Chop off trailing seperators. */
  272. while (index > 0 && (pathname[index]==FILESEPARATOR ||
  273. pathname[index]==FILESEPARATOR2)) {
  274. --index;
  275. }
  276. aux = index;
  277. /* Now find the next separator. */
  278. while (index >= 0 && pathname[index] != FILESEPARATOR &&
  279. pathname[index] != FILESEPARATOR2) {
  280. --index;
  281. }
  282. /* Allocate and copy. */
  283. result = JS_malloc(cx, aux - index + 1);
  284. if (!result)
  285. return NULL;
  286. strncpy(result, pathname + index + 1, aux - index);
  287. result[aux - index] = '\0';
  288. return result;
  289. }
  290. /*
  291. * Returns everything but the last component from a path name.
  292. * Returned string must be freed.
  293. */
  294. static char *
  295. js_fileDirectoryName(JSContext *cx, const char *pathname)
  296. {
  297. char *result;
  298. const char *cp, *end;
  299. size_t pathsize;
  300. end = pathname + strlen(pathname);
  301. cp = end - 1;
  302. /* If this is already a directory, chop off the trailing /s. */
  303. while (cp >= pathname) {
  304. if (*cp != FILESEPARATOR && *cp != FILESEPARATOR2)
  305. break;
  306. --cp;
  307. }
  308. if (cp < pathname && end != pathname) {
  309. /* There were just /s, return the root. */
  310. result = JS_malloc(cx, 1 + 1); /* The separator + trailing NUL. */
  311. result[0] = FILESEPARATOR;
  312. result[1] = '\0';
  313. return result;
  314. }
  315. /* Now chop off the last portion. */
  316. while (cp >= pathname) {
  317. if (*cp == FILESEPARATOR || *cp == FILESEPARATOR2)
  318. break;
  319. --cp;
  320. }
  321. /* Check if this is a leaf. */
  322. if (cp < pathname) {
  323. /* It is, return "pathname/". */
  324. if (end[-1] == FILESEPARATOR || end[-1] == FILESEPARATOR2) {
  325. /* Already has its terminating /. */
  326. return JS_strdup(cx, pathname);
  327. }
  328. pathsize = end - pathname + 1;
  329. result = JS_malloc(cx, pathsize + 1);
  330. if (!result)
  331. return NULL;
  332. strcpy(result, pathname);
  333. result[pathsize - 1] = FILESEPARATOR;
  334. result[pathsize] = '\0';
  335. return result;
  336. }
  337. /* Return everything up to and including the seperator. */
  338. pathsize = cp - pathname + 1;
  339. result = JS_malloc(cx, pathsize + 1);
  340. if (!result)
  341. return NULL;
  342. strncpy(result, pathname, pathsize);
  343. result[pathsize] = '\0';
  344. return result;
  345. }
  346. static char *
  347. js_absolutePath(JSContext *cx, const char * path)
  348. {
  349. JSObject *obj;
  350. JSString *str;
  351. jsval prop;
  352. if (js_isAbsolute(path)) {
  353. return JS_strdup(cx, path);
  354. } else {
  355. obj = JS_GetGlobalObject(cx);
  356. if (!JS_GetProperty(cx, obj, FILE_CONSTRUCTOR, &prop)) {
  357. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  358. JSFILEMSG_FILE_CONSTRUCTOR_UNDEFINED_ERROR);
  359. return JS_strdup(cx, path);
  360. }
  361. obj = JSVAL_TO_OBJECT(prop);
  362. if (!JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, &prop)) {
  363. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  364. JSFILEMSG_FILE_CURRENTDIR_UNDEFINED_ERROR);
  365. return JS_strdup(cx, path);
  366. }
  367. str = JS_ValueToString(cx, prop);
  368. if (!str)
  369. return JS_strdup(cx, path);
  370. /* should we have an array of curr dirs indexed by drive for windows? */
  371. return js_combinePath(cx, JS_GetStringBytes(str), path);
  372. }
  373. }
  374. /* Side effect: will remove spaces in the beginning/end of the filename */
  375. static char *
  376. js_canonicalPath(JSContext *cx, char *oldpath)
  377. {
  378. char *tmp;
  379. char *path = oldpath;
  380. char *base, *dir, *current, *result;
  381. jsint c;
  382. jsint back = 0;
  383. unsigned int i = 0, j = strlen(path)-1;
  384. /* This is probably optional */
  385. /* Remove possible spaces in the beginning and end */
  386. while (i < j && path[i] == ' ')
  387. i++;
  388. while (j >= 0 && path[j] == ' ')
  389. j--;
  390. tmp = JS_malloc(cx, j-i+2);
  391. if (!tmp)
  392. return NULL;
  393. strncpy(tmp, path + i, j - i + 1);
  394. tmp[j - i + 1] = '\0';
  395. path = tmp;
  396. /* Pipe support. */
  397. if (js_filenameHasAPipe(path))
  398. return path;
  399. /* file:// support. */
  400. if (!strncmp(path, URL_PREFIX, strlen(URL_PREFIX))) {
  401. tmp = js_canonicalPath(cx, path + strlen(URL_PREFIX));
  402. JS_free(cx, path);
  403. return tmp;
  404. }
  405. if (!js_isAbsolute(path)) {
  406. tmp = js_absolutePath(cx, path);
  407. if (!tmp)
  408. return NULL;
  409. JS_free(cx, path);
  410. path = tmp;
  411. }
  412. result = JS_strdup(cx, "");
  413. current = path;
  414. base = js_fileBaseName(cx, current);
  415. dir = js_fileDirectoryName(cx, current);
  416. while (strcmp(dir, current)) {
  417. if (!strcmp(base, "..")) {
  418. back++;
  419. } else {
  420. if (back > 0) {
  421. back--;
  422. } else {
  423. tmp = result;
  424. result = JS_malloc(cx, strlen(base) + 1 + strlen(tmp) + 1);
  425. if (!result)
  426. goto out;
  427. strcpy(result, base);
  428. c = strlen(result);
  429. if (*tmp) {
  430. result[c] = FILESEPARATOR;
  431. result[c + 1] = '\0';
  432. strcat(result, tmp);
  433. }
  434. JS_free(cx, tmp);
  435. }
  436. }
  437. JS_free(cx, current);
  438. JS_free(cx, base);
  439. current = dir;
  440. base = js_fileBaseName(cx, current);
  441. dir = js_fileDirectoryName(cx, current);
  442. }
  443. tmp = result;
  444. result = JS_malloc(cx, strlen(dir)+1+strlen(tmp)+1);
  445. if (!result)
  446. goto out;
  447. strcpy(result, dir);
  448. c = strlen(result);
  449. if (tmp[0]!='\0') {
  450. if ((result[c-1]!=FILESEPARATOR)&&(result[c-1]!=FILESEPARATOR2)) {
  451. result[c] = FILESEPARATOR;
  452. result[c+1] = '\0';
  453. }
  454. strcat(result, tmp);
  455. }
  456. out:
  457. if (tmp)
  458. JS_free(cx, tmp);
  459. if (dir)
  460. JS_free(cx, dir);
  461. if (base)
  462. JS_free(cx, base);
  463. if (current)
  464. JS_free(cx, current);
  465. return result;
  466. }
  467. /* -------------------------- Text conversion ------------------------------- */
  468. /* The following is ripped from libi18n/unicvt.c and include files.. */
  469. /*
  470. * UTF8 defines and macros
  471. */
  472. #define ONE_OCTET_BASE 0x00 /* 0xxxxxxx */
  473. #define ONE_OCTET_MASK 0x7F /* x1111111 */
  474. #define CONTINUING_OCTET_BASE 0x80 /* 10xxxxxx */
  475. #define CONTINUING_OCTET_MASK 0x3F /* 00111111 */
  476. #define TWO_OCTET_BASE 0xC0 /* 110xxxxx */
  477. #define TWO_OCTET_MASK 0x1F /* 00011111 */
  478. #define THREE_OCTET_BASE 0xE0 /* 1110xxxx */
  479. #define THREE_OCTET_MASK 0x0F /* 00001111 */
  480. #define FOUR_OCTET_BASE 0xF0 /* 11110xxx */
  481. #define FOUR_OCTET_MASK 0x07 /* 00000111 */
  482. #define FIVE_OCTET_BASE 0xF8 /* 111110xx */
  483. #define FIVE_OCTET_MASK 0x03 /* 00000011 */
  484. #define SIX_OCTET_BASE 0xFC /* 1111110x */
  485. #define SIX_OCTET_MASK 0x01 /* 00000001 */
  486. #define IS_UTF8_1ST_OF_1(x) (( (x)&~ONE_OCTET_MASK ) == ONE_OCTET_BASE)
  487. #define IS_UTF8_1ST_OF_2(x) (( (x)&~TWO_OCTET_MASK ) == TWO_OCTET_BASE)
  488. #define IS_UTF8_1ST_OF_3(x) (( (x)&~THREE_OCTET_MASK) == THREE_OCTET_BASE)
  489. #define IS_UTF8_1ST_OF_4(x) (( (x)&~FOUR_OCTET_MASK ) == FOUR_OCTET_BASE)
  490. #define IS_UTF8_1ST_OF_5(x) (( (x)&~FIVE_OCTET_MASK ) == FIVE_OCTET_BASE)
  491. #define IS_UTF8_1ST_OF_6(x) (( (x)&~SIX_OCTET_MASK ) == SIX_OCTET_BASE)
  492. #define IS_UTF8_2ND_THRU_6TH(x) \
  493. (( (x)&~CONTINUING_OCTET_MASK ) == CONTINUING_OCTET_BASE)
  494. #define IS_UTF8_1ST_OF_UCS2(x) \
  495. IS_UTF8_1ST_OF_1(x) \
  496. || IS_UTF8_1ST_OF_2(x) \
  497. || IS_UTF8_1ST_OF_3(x)
  498. #define MAX_UCS2 0xFFFF
  499. #define DEFAULT_CHAR 0x003F /* Default char is "?" */
  500. #define BYTE_MASK 0xBF
  501. #define BYTE_MARK 0x80
  502. /* Function: one_ucs2_to_utf8_char
  503. *
  504. * Function takes one UCS-2 char and writes it to a UTF-8 buffer.
  505. * We need a UTF-8 buffer because we don't know before this
  506. * function how many bytes of utf-8 data will be written. It also
  507. * takes a pointer to the end of the UTF-8 buffer so that we don't
  508. * overwrite data. This function returns the number of UTF-8 bytes
  509. * of data written, or -1 if the buffer would have been overrun.
  510. */
  511. #define LINE_SEPARATOR 0x2028
  512. #define PARAGRAPH_SEPARATOR 0x2029
  513. static int16 one_ucs2_to_utf8_char(unsigned char *tobufp,
  514. unsigned char *tobufendp,
  515. uint16 onechar)
  516. {
  517. int16 numUTF8bytes = 0;
  518. if (onechar == LINE_SEPARATOR || onechar == PARAGRAPH_SEPARATOR) {
  519. strcpy((char*)tobufp, "\n");
  520. return strlen((char*)tobufp);
  521. }
  522. if (onechar < 0x80) {
  523. numUTF8bytes = 1;
  524. } else if (onechar < 0x800) {
  525. numUTF8bytes = 2;
  526. } else {
  527. /* 0x800 >= onechar <= MAX_UCS2 */
  528. numUTF8bytes = 3;
  529. }
  530. tobufp += numUTF8bytes;
  531. /* return error if we don't have space for the whole character */
  532. if (tobufp > tobufendp) {
  533. return(-1);
  534. }
  535. switch(numUTF8bytes) {
  536. case 3: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
  537. *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
  538. *--tobufp = onechar | THREE_OCTET_BASE;
  539. break;
  540. case 2: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
  541. *--tobufp = onechar | TWO_OCTET_BASE;
  542. break;
  543. case 1: *--tobufp = (unsigned char)onechar;
  544. break;
  545. }
  546. return numUTF8bytes;
  547. }
  548. /*
  549. * utf8_to_ucs2_char
  550. *
  551. * Convert a utf8 multibyte character to ucs2
  552. *
  553. * inputs: pointer to utf8 character(s)
  554. * length of utf8 buffer ("read" length limit)
  555. * pointer to return ucs2 character
  556. *
  557. * outputs: number of bytes in the utf8 character
  558. * -1 if not a valid utf8 character sequence
  559. * -2 if the buffer is too short
  560. */
  561. static int16
  562. utf8_to_ucs2_char(const unsigned char *utf8p, int16 buflen, uint16 *ucs2p)
  563. {
  564. uint16 lead, cont1, cont2;
  565. /*
  566. * Check for minimum buffer length
  567. */
  568. if ((buflen < 1) || (utf8p == NULL)) {
  569. return -2;
  570. }
  571. lead = (uint16) (*utf8p);
  572. /*
  573. * Check for a one octet sequence
  574. */
  575. if (IS_UTF8_1ST_OF_1(lead)) {
  576. *ucs2p = lead & ONE_OCTET_MASK;
  577. return 1;
  578. }
  579. /*
  580. * Check for a two octet sequence
  581. */
  582. if (IS_UTF8_1ST_OF_2(*utf8p)) {
  583. if (buflen < 2)
  584. return -2;
  585. cont1 = (uint16) *(utf8p+1);
  586. if (!IS_UTF8_2ND_THRU_6TH(cont1))
  587. return -1;
  588. *ucs2p = (lead & TWO_OCTET_MASK) << 6;
  589. *ucs2p |= cont1 & CONTINUING_OCTET_MASK;
  590. return 2;
  591. }
  592. /*
  593. * Check for a three octet sequence
  594. */
  595. else if (IS_UTF8_1ST_OF_3(lead)) {
  596. if (buflen < 3)
  597. return -2;
  598. cont1 = (uint16) *(utf8p+1);
  599. cont2 = (uint16) *(utf8p+2);
  600. if ( (!IS_UTF8_2ND_THRU_6TH(cont1))
  601. || (!IS_UTF8_2ND_THRU_6TH(cont2)))
  602. return -1;
  603. *ucs2p = (lead & THREE_OCTET_MASK) << 12;
  604. *ucs2p |= (cont1 & CONTINUING_OCTET_MASK) << 6;
  605. *ucs2p |= cont2 & CONTINUING_OCTET_MASK;
  606. return 3;
  607. }
  608. else { /* not a valid utf8/ucs2 character */
  609. return -1;
  610. }
  611. }
  612. /* ----------------------------- Helper functions --------------------------- */
  613. /* Ripped off from lm_win.c .. */
  614. /* where is strcasecmp?.. for now, it's case sensitive..
  615. *
  616. * strcasecmp is in strings.h, but on windows it's called _stricmp...
  617. * will need to #ifdef this
  618. */
  619. static int32
  620. js_FileHasOption(JSContext *cx, const char *oldoptions, const char *name)
  621. {
  622. char *comma, *equal, *current;
  623. char *options = JS_strdup(cx, oldoptions);
  624. int32 found = 0;
  625. current = options;
  626. for (;;) {
  627. comma = strchr(current, ',');
  628. if (comma) *comma = '\0';
  629. equal = strchr(current, '=');
  630. if (equal) *equal = '\0';
  631. if (strcmp(current, name) == 0) {
  632. if (!equal || strcmp(equal + 1, "yes") == 0)
  633. found = 1;
  634. else
  635. found = atoi(equal + 1);
  636. }
  637. if (equal) *equal = '=';
  638. if (comma) *comma = ',';
  639. if (found || !comma)
  640. break;
  641. current = comma + 1;
  642. }
  643. JS_free(cx, options);
  644. return found;
  645. }
  646. /* empty the buffer */
  647. static void
  648. js_ResetBuffers(JSFile * file)
  649. {
  650. file->charBufferUsed = JS_FALSE;
  651. file->nbBytesInBuf = 0;
  652. }
  653. /* Reset file attributes */
  654. static void
  655. js_ResetAttributes(JSFile * file)
  656. {
  657. file->mode = file->type = 0;
  658. file->isOpen = JS_FALSE;
  659. file->handle = NULL;
  660. file->nativehandle = NULL;
  661. file->hasRandomAccess = JS_TRUE; /* Innocent until proven guilty. */
  662. file->hasAutoflush = JS_FALSE;
  663. file->isNative = JS_FALSE;
  664. file->isPipe = JS_FALSE;
  665. js_ResetBuffers(file);
  666. }
  667. static JSBool
  668. js_FileOpen(JSContext *cx, JSObject *obj, JSFile *file, char *mode){
  669. JSString *type, *mask;
  670. jsval v[2];
  671. jsval rval;
  672. type = JS_InternString(cx, asciistring);
  673. mask = JS_NewStringCopyZ(cx, mode);
  674. v[0] = STRING_TO_JSVAL(mask);
  675. v[1] = STRING_TO_JSVAL(type);
  676. if (!file_open(cx, obj, 2, v, &rval))
  677. return JS_FALSE;
  678. return JS_TRUE;
  679. }
  680. /* Buffered version of PR_Read. Used by js_FileRead */
  681. static int32
  682. js_BufferedRead(JSFile *f, unsigned char *buf, int32 len)
  683. {
  684. int32 count = 0;
  685. while (f->nbBytesInBuf>0&&len>0) {
  686. buf[0] = f->byteBuffer[0];
  687. f->byteBuffer[0] = f->byteBuffer[1];
  688. f->byteBuffer[1] = f->byteBuffer[2];
  689. f->nbBytesInBuf--;
  690. len--;
  691. buf+=1;
  692. count++;
  693. }
  694. if (len > 0) {
  695. count += (!f->isNative)
  696. ? PR_Read(f->handle, buf, len)
  697. : fread(buf, 1, len, f->nativehandle);
  698. }
  699. return count;
  700. }
  701. static int32
  702. js_FileRead(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode)
  703. {
  704. unsigned char *aux;
  705. int32 count = 0, i;
  706. jsint remainder;
  707. unsigned char utfbuf[3];
  708. if (file->charBufferUsed) {
  709. buf[0] = file->charBuffer;
  710. buf++;
  711. len--;
  712. file->charBufferUsed = JS_FALSE;
  713. }
  714. switch (mode) {
  715. case ASCII:
  716. aux = (unsigned char*)JS_malloc(cx, len);
  717. if (!aux)
  718. return 0;
  719. count = js_BufferedRead(file, aux, len);
  720. if (count == -1) {
  721. JS_free(cx, aux);
  722. return 0;
  723. }
  724. for (i = 0; i < len; i++)
  725. buf[i] = (jschar)aux[i];
  726. JS_free(cx, aux);
  727. break;
  728. case UTF8:
  729. remainder = 0;
  730. for (count = 0;count<len;count++) {
  731. i = js_BufferedRead(file, utfbuf+remainder, 3-remainder);
  732. if (i<=0) {
  733. return count;
  734. }
  735. i = utf8_to_ucs2_char(utfbuf, (int16)i, &buf[count] );
  736. if (i<0) {
  737. return count;
  738. } else {
  739. if (i==1) {
  740. utfbuf[0] = utfbuf[1];
  741. utfbuf[1] = utfbuf[2];
  742. remainder = 2;
  743. } else if (i==2) {
  744. utfbuf[0] = utfbuf[2];
  745. remainder = 1;
  746. } else if (i==3) {
  747. remainder = 0;
  748. }
  749. }
  750. }
  751. while (remainder>0) {
  752. file->byteBuffer[file->nbBytesInBuf] = utfbuf[0];
  753. file->nbBytesInBuf++;
  754. utfbuf[0] = utfbuf[1];
  755. utfbuf[1] = utfbuf[2];
  756. remainder--;
  757. }
  758. break;
  759. case UCS2:
  760. count = js_BufferedRead(file, (unsigned char *)buf, len * 2) >> 1;
  761. if (count == -1)
  762. return 0;
  763. break;
  764. default:
  765. /* Not reached. */
  766. JS_ASSERT(0);
  767. }
  768. if(count == -1) {
  769. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  770. JSFILEMSG_OP_FAILED, "read", file->path);
  771. }
  772. return count;
  773. }
  774. static int32
  775. js_FileSeek(JSContext *cx, JSFile *file, int32 len, int32 mode)
  776. {
  777. int32 count = 0, i;
  778. jsint remainder;
  779. unsigned char utfbuf[3];
  780. jschar tmp;
  781. switch (mode) {
  782. case ASCII:
  783. count = PR_Seek(file->handle, len, PR_SEEK_CUR);
  784. break;
  785. case UTF8:
  786. remainder = 0;
  787. for (count = 0;count<len;count++) {
  788. i = js_BufferedRead(file, utfbuf+remainder, 3-remainder);
  789. if (i<=0) {
  790. return 0;
  791. }
  792. i = utf8_to_ucs2_char(utfbuf, (int16)i, &tmp );
  793. if (i<0) {
  794. return 0;
  795. } else {
  796. if (i==1) {
  797. utfbuf[0] = utfbuf[1];
  798. utfbuf[1] = utfbuf[2];
  799. remainder = 2;
  800. } else if (i==2) {
  801. utfbuf[0] = utfbuf[2];
  802. remainder = 1;
  803. } else if (i==3) {
  804. remainder = 0;
  805. }
  806. }
  807. }
  808. while (remainder>0) {
  809. file->byteBuffer[file->nbBytesInBuf] = utfbuf[0];
  810. file->nbBytesInBuf++;
  811. utfbuf[0] = utfbuf[1];
  812. utfbuf[1] = utfbuf[2];
  813. remainder--;
  814. }
  815. break;
  816. case UCS2:
  817. count = PR_Seek(file->handle, len*2, PR_SEEK_CUR)/2;
  818. break;
  819. default:
  820. /* Not reached. */
  821. JS_ASSERT(0);
  822. }
  823. if(count == -1) {
  824. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  825. JSFILEMSG_OP_FAILED, "seek", file->path);
  826. }
  827. return count;
  828. }
  829. static int32
  830. js_FileWrite(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode)
  831. {
  832. unsigned char *aux;
  833. int32 count = 0, i, j;
  834. unsigned char *utfbuf;
  835. switch (mode) {
  836. case ASCII:
  837. aux = (unsigned char*)JS_malloc(cx, len);
  838. if (!aux)
  839. return 0;
  840. for (i = 0; i<len; i++)
  841. aux[i] = buf[i] % 256;
  842. count = (!file->isNative)
  843. ? PR_Write(file->handle, aux, len)
  844. : fwrite(aux, 1, len, file->nativehandle);
  845. if (count==-1) {
  846. JS_free(cx, aux);
  847. return 0;
  848. }
  849. JS_free(cx, aux);
  850. break;
  851. case UTF8:
  852. utfbuf = (unsigned char*)JS_malloc(cx, len*3);
  853. if (!utfbuf) return 0;
  854. i = 0;
  855. for (count = 0;count<len;count++) {
  856. j = one_ucs2_to_utf8_char(utfbuf+i, utfbuf+len*3, buf[count]);
  857. if (j==-1) {
  858. JS_free(cx, utfbuf);
  859. return 0;
  860. }
  861. i+=j;
  862. }
  863. j = (!file->isNative)
  864. ? PR_Write(file->handle, utfbuf, i)
  865. : fwrite(utfbuf, 1, i, file->nativehandle);
  866. if (j<i) {
  867. JS_free(cx, utfbuf);
  868. return 0;
  869. }
  870. JS_free(cx, utfbuf);
  871. break;
  872. case UCS2:
  873. count = (!file->isNative)
  874. ? PR_Write(file->handle, buf, len*2) >> 1
  875. : fwrite(buf, 1, len*2, file->nativehandle) >> 1;
  876. if (count == -1)
  877. return 0;
  878. break;
  879. default:
  880. /* Not reached. */
  881. JS_ASSERT(0);
  882. }
  883. if(count == -1) {
  884. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  885. JSFILEMSG_OP_FAILED, "write", file->path);
  886. }
  887. return count;
  888. }
  889. /* ----------------------------- Property checkers -------------------------- */
  890. static JSBool
  891. js_exists(JSContext *cx, JSFile *file)
  892. {
  893. if (file->isNative) {
  894. /* It doesn't make sense for a pipe of stdstream. */
  895. return JS_FALSE;
  896. }
  897. return PR_Access(file->path, PR_ACCESS_EXISTS) == PR_SUCCESS;
  898. }
  899. static JSBool
  900. js_canRead(JSContext *cx, JSFile *file)
  901. {
  902. if (!file->isNative) {
  903. if (file->isOpen && !(file->mode & PR_RDONLY))
  904. return JS_FALSE;
  905. return PR_Access(file->path, PR_ACCESS_READ_OK) == PR_SUCCESS;
  906. }
  907. if (file->isPipe) {
  908. /* Is this pipe open for reading? */
  909. return file->path[0] == PIPE_SYMBOL;
  910. }
  911. return !strcmp(file->path, STDINPUT_NAME);
  912. }
  913. static JSBool
  914. js_canWrite(JSContext *cx, JSFile *file)
  915. {
  916. if (!file->isNative) {
  917. if (file->isOpen && !(file->mode & PR_WRONLY))
  918. return JS_FALSE;
  919. return PR_Access(file->path, PR_ACCESS_WRITE_OK) == PR_SUCCESS;
  920. }
  921. if(file->isPipe) {
  922. /* Is this pipe open for writing? */
  923. return file->path[strlen(file->path)-1] == PIPE_SYMBOL;
  924. }
  925. return !strcmp(file->path, STDOUTPUT_NAME) ||
  926. !strcmp(file->path, STDERROR_NAME);
  927. }
  928. static JSBool
  929. js_isFile(JSContext *cx, JSFile *file)
  930. {
  931. if (!file->isNative) {
  932. PRFileInfo info;
  933. if (file->isOpen
  934. ? PR_GetOpenFileInfo(file->handle, &info)
  935. : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) {
  936. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  937. JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
  938. return JS_FALSE;
  939. }
  940. return info.type == PR_FILE_FILE;
  941. }
  942. /* This doesn't make sense for a pipe of stdstream. */
  943. return JS_FALSE;
  944. }
  945. static JSBool
  946. js_isDirectory(JSContext *cx, JSFile *file)
  947. {
  948. if(!file->isNative){
  949. PRFileInfo info;
  950. /* Hack needed to get get_property to work. */
  951. if (!js_exists(cx, file))
  952. return JS_FALSE;
  953. if (file->isOpen
  954. ? PR_GetOpenFileInfo(file->handle, &info)
  955. : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) {
  956. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  957. JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
  958. return JS_FALSE;
  959. }
  960. return info.type == PR_FILE_DIRECTORY;
  961. }
  962. /* This doesn't make sense for a pipe of stdstream. */
  963. return JS_FALSE;
  964. }
  965. static jsval
  966. js_size(JSContext *cx, JSFile *file)
  967. {
  968. PRFileInfo info;
  969. JSFILE_CHECK_NATIVE("size");
  970. if (file->isOpen
  971. ? PR_GetOpenFileInfo(file->handle, &info)
  972. : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) {
  973. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  974. JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
  975. return JSVAL_VOID;
  976. }
  977. return INT_TO_JSVAL(info.size);
  978. out:
  979. return JSVAL_VOID;
  980. }
  981. /*
  982. * Return the parent object
  983. */
  984. static JSBool
  985. js_parent(JSContext *cx, JSFile *file, jsval *resultp)
  986. {
  987. char *str;
  988. /* Since we only care about pipes and native files, return NULL. */
  989. if (file->isNative) {
  990. *resultp = JSVAL_VOID;
  991. return JS_TRUE;
  992. }
  993. str = js_fileDirectoryName(cx, file->path);
  994. if (!str)
  995. return JS_FALSE;
  996. /* If the directory is equal to the original path, we're at the root. */
  997. if (!strcmp(file->path, str)) {
  998. *resultp = JSVAL_NULL;
  999. } else {
  1000. JSObject *obj = js_NewFileObject(cx, str);
  1001. if (!obj) {
  1002. JS_free(cx, str);
  1003. return JS_FALSE;
  1004. }
  1005. *resultp = OBJECT_TO_JSVAL(obj);
  1006. }
  1007. JS_free(cx, str);
  1008. return JS_TRUE;
  1009. }
  1010. static JSBool
  1011. js_name(JSContext *cx, JSFile *file, jsval *vp)
  1012. {
  1013. char *name;
  1014. JSString *str;
  1015. if (file->isPipe) {
  1016. *vp = JSVAL_VOID;
  1017. return JS_TRUE;
  1018. }
  1019. name = js_fileBaseName(cx, file->path);
  1020. if (!name)
  1021. return JS_FALSE;
  1022. str = JS_NewString(cx, name, strlen(name));
  1023. if (!str) {
  1024. JS_free(cx, name);
  1025. return JS_FALSE;
  1026. }
  1027. *vp = STRING_TO_JSVAL(str);
  1028. return JS_TRUE;
  1029. }
  1030. /* ------------------------------ File object methods ---------------------------- */
  1031. static JSBool
  1032. file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1033. {
  1034. JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
  1035. JSString *strmode, *strtype;
  1036. char *ctype, *mode;
  1037. int32 mask, type;
  1038. int len;
  1039. mode = NULL;
  1040. SECURITY_CHECK(cx, NULL, "open", file);
  1041. /* A native file that is already open */
  1042. if(file->isOpen && file->isNative) {
  1043. JS_ReportWarning(cx, "Native file %s is already open, proceeding",
  1044. file->path);
  1045. goto good;
  1046. }
  1047. /* Close before proceeding */
  1048. if (file->isOpen) {
  1049. JS_ReportWarning(cx, "File %s is already open, we will close it and "
  1050. "reopen, proceeding", file->path);
  1051. if(!file_close(cx, obj, 0, NULL, rval))
  1052. goto out;
  1053. }
  1054. if (js_isDirectory(cx, file)) {
  1055. JS_ReportWarning(cx, "%s seems to be a directory, there is no point in "
  1056. "trying to open it, proceeding", file->path);
  1057. goto good;
  1058. }
  1059. /* Path must be defined at this point */
  1060. len = strlen(file->path);
  1061. /* Mode */
  1062. if (argc >= 1) {
  1063. strmode = JS_ValueToString(cx, argv[0]);
  1064. if (!strmode) {
  1065. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1066. JSFILEMSG_FIRST_ARGUMENT_OPEN_NOT_STRING_ERROR,
  1067. argv[0]);
  1068. goto out;
  1069. }
  1070. mode = JS_strdup(cx, JS_GetStringBytes(strmode));
  1071. } else {
  1072. if(file->path[0]==PIPE_SYMBOL) {
  1073. /* pipe default mode */
  1074. mode = JS_strdup(cx, "read");
  1075. } else if(file->path[len-1]==PIPE_SYMBOL) {
  1076. /* pipe default mode */
  1077. mode = JS_strdup(cx, "write");
  1078. } else {
  1079. /* non-destructive, permissive defaults. */
  1080. mode = JS_strdup(cx, "readWrite,append,create");
  1081. }
  1082. }
  1083. /* Process the mode */
  1084. mask = 0;
  1085. /* TODO: this is pretty ugly, we walk thru the string too many times */
  1086. mask |= js_FileHasOption(cx, mode, "read") ? PR_RDONLY : 0;
  1087. mask |= js_FileHasOption(cx, mode, "write") ? PR_WRONLY : 0;
  1088. mask |= js_FileHasOption(cx, mode, "readWrite")? PR_RDWR : 0;
  1089. mask |= js_FileHasOption(cx, mode, "append") ? PR_APPEND : 0;
  1090. mask |= js_FileHasOption(cx, mode, "create") ? PR_CREATE_FILE : 0;
  1091. mask |= js_FileHasOption(cx, mode, "replace") ? PR_TRUNCATE : 0;
  1092. if (mask & PR_RDWR)
  1093. mask |= (PR_RDONLY | PR_WRONLY);
  1094. if ((mask & PR_RDONLY) && (mask & PR_WRONLY))
  1095. mask |= PR_RDWR;
  1096. file->hasAutoflush |= js_FileHasOption(cx, mode, "autoflush");
  1097. /* Type */
  1098. if (argc > 1) {
  1099. strtype = JS_ValueToString(cx, argv[1]);
  1100. if (!strtype) {
  1101. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1102. JSFILEMSG_SECOND_ARGUMENT_OPEN_NOT_STRING_ERROR,
  1103. argv[1]);
  1104. goto out;
  1105. }
  1106. ctype = JS_GetStringBytes(strtype);
  1107. if(!strcmp(ctype, utfstring)) {
  1108. type = UTF8;
  1109. } else if (!strcmp(ctype, unicodestring)) {
  1110. type = UCS2;
  1111. } else {
  1112. if (strcmp(ctype, asciistring)) {
  1113. JS_ReportWarning(cx, "File type %s is not supported, using "
  1114. "'text' instead, proceeding", ctype);
  1115. }
  1116. type = ASCII;
  1117. }
  1118. } else {
  1119. type = ASCII;
  1120. }
  1121. /* Save the relevant fields */
  1122. file->type = type;
  1123. file->mode = mask;
  1124. file->nativehandle = NULL;
  1125. file->hasRandomAccess = (type != UTF8);
  1126. /*
  1127. * Deal with pipes here. We can't use NSPR for pipes, so we have to use
  1128. * POPEN.
  1129. */
  1130. if (file->path[0]==PIPE_SYMBOL || file->path[len-1]==PIPE_SYMBOL) {
  1131. if (file->path[0] == PIPE_SYMBOL && file->path[len-1] == PIPE_SYMBOL) {
  1132. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1133. JSFILEMSG_BIDIRECTIONAL_PIPE_NOT_SUPPORTED);
  1134. goto out;
  1135. } else {
  1136. int i = 0;
  1137. char pipemode[3];
  1138. SECURITY_CHECK(cx, NULL, "pipe_open", file);
  1139. if(file->path[0] == PIPE_SYMBOL){
  1140. if(mask & (PR_WRONLY | PR_APPEND | PR_CREATE_FILE | PR_TRUNCATE)){
  1141. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1142. JSFILEMSG_OPEN_MODE_NOT_SUPPORTED_WITH_PIPES,
  1143. mode, file->path);
  1144. goto out;
  1145. }
  1146. /* open(SPOOLER, "| cat -v | lpr -h 2>/dev/null") -- pipe for writing */
  1147. pipemode[i++] = 'r';
  1148. #ifndef XP_UNIX
  1149. pipemode[i++] = file->type==UTF8 ? 'b' : 't';
  1150. #endif
  1151. pipemode[i++] = '\0';
  1152. file->nativehandle = POPEN(&file->path[1], pipemode);
  1153. } else if(file->path[len-1] == PIPE_SYMBOL) {
  1154. char *command = JS_malloc(cx, len);
  1155. strncpy(command, file->path, len-1);
  1156. command[len-1] = '\0';
  1157. /* open(STATUS, "netstat -an 2>&1 |") */
  1158. pipemode[i++] = 'w';
  1159. #ifndef XP_UNIX
  1160. pipemode[i++] = file->type==UTF8 ? 'b' : 't';
  1161. #endif
  1162. pipemode[i++] = '\0';
  1163. file->nativehandle = POPEN(command, pipemode);
  1164. JS_free(cx, command);
  1165. }
  1166. /* set the flags */
  1167. file->isNative = JS_TRUE;
  1168. file->isPipe = JS_TRUE;
  1169. file->hasRandomAccess = JS_FALSE;
  1170. }
  1171. } else {
  1172. /* TODO: what about the permissions?? Java ignores the problem... */
  1173. file->handle = PR_Open(file->path, mask, 0644);
  1174. }
  1175. js_ResetBuffers(file);
  1176. JS_free(cx, mode);
  1177. mode = NULL;
  1178. /* Set the open flag and return result */
  1179. if (file->handle == NULL && file->nativehandle == NULL) {
  1180. file->isOpen = JS_FALSE;
  1181. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1182. JSFILEMSG_OP_FAILED, "open", file->path);
  1183. goto out;
  1184. }
  1185. good:
  1186. file->isOpen = JS_TRUE;
  1187. *rval = JSVAL_TRUE;
  1188. return JS_TRUE;
  1189. out:
  1190. if(mode)
  1191. JS_free(cx, mode);
  1192. return JS_FALSE;
  1193. }
  1194. static JSBool
  1195. file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1196. {
  1197. JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
  1198. SECURITY_CHECK(cx, NULL, "close", file);
  1199. if(!file->isOpen){
  1200. JS_ReportWarning(cx, "File %s is not open, can't close it, proceeding",
  1201. file->path);
  1202. goto out;
  1203. }
  1204. if(!file->isPipe){
  1205. if(file->isNative){
  1206. JS_ReportWarning(cx, "Unable to close a native file, proceeding", file->path);
  1207. goto out;
  1208. }else{
  1209. if(file->handle && PR_Close(file->handle)){
  1210. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1211. JSFILEMSG_OP_FAILED, "close", file->path);
  1212. goto out;
  1213. }
  1214. }
  1215. }else{
  1216. if(PCLOSE(file->nativehandle)==-1){
  1217. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1218. JSFILEMSG_OP_FAILED, "pclose", file->path);
  1219. goto out;
  1220. }
  1221. }
  1222. js_ResetAttributes(file);
  1223. *rval = JSVAL_TRUE;
  1224. return JS_TRUE;
  1225. out:
  1226. return JS_FALSE;
  1227. }
  1228. static JSBool
  1229. file_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1230. {
  1231. JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
  1232. SECURITY_CHECK(cx, NULL, "remove", file);
  1233. JSFILE_CHECK_NATIVE("remove");
  1234. JSFILE_CHECK_CLOSED("remove");
  1235. if ((js_isDirectory(cx, file) ?
  1236. PR_RmDir(file->path) : PR_Delete(file->path))==PR_SUCCESS) {
  1237. js_ResetAttributes(file);
  1238. *rval = JSVAL_TRUE;
  1239. return JS_TRUE;
  1240. } else {
  1241. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1242. JSFILEMSG_OP_FAILED, "remove", file->path);
  1243. goto out;
  1244. }
  1245. out:
  1246. *rval = JSVAL_FALSE;
  1247. return JS_FALSE;
  1248. }
  1249. /* Raw PR-based function. No text processing. Just raw data copying. */
  1250. static JSBool
  1251. file_copyTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1252. {
  1253. JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
  1254. char *dest = NULL;
  1255. PRFileDesc *handle = NULL;
  1256. char *buffer;
  1257. jsval count, size;
  1258. JSBool fileInitiallyOpen=JS_FALSE;
  1259. SECURITY_CHECK(cx, NULL, "copyTo", file); /* may need a second argument!*/
  1260. JSFILE_CHECK_ONE_ARG("copyTo");
  1261. JSFILE_CHECK_NATIVE("copyTo");
  1262. /* remeber the state */
  1263. fileInitiallyOpen = file->isOpen;
  1264. JSFILE_CHECK_READ;
  1265. dest = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
  1266. /* make sure we are not reading a file open for writing */
  1267. if (file->isOpen && !js_canRead(cx, file)) {
  1268. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1269. JSFILEMSG_CANNOT_COPY_FILE_OPEN_FOR_WRITING_ERROR, file->path);
  1270. goto out;
  1271. }
  1272. if (file->handle==NULL){
  1273. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1274. JSFILEMSG_OP_FAILED, "open", file->path);
  1275. goto out;
  1276. }
  1277. handle = PR_Open(dest, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, 0644);
  1278. if(!handle){
  1279. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1280. JSFILEMSG_OP_FAILED, "open", dest);
  1281. goto out;
  1282. }
  1283. if ((size=js_size(cx, file))==JSVAL_VOID) {
  1284. goto out;
  1285. }
  1286. buffer = JS_malloc(cx, size);
  1287. count = INT_TO_JSVAL(PR_Read(file->handle, buffer, size));
  1288. /* reading panic */
  1289. if (count!=size) {
  1290. JS_free(cx, buffer);
  1291. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1292. JSFILEMSG_COPY_READ_ERROR, file->path);
  1293. goto out;
  1294. }
  1295. count = INT_TO_JSVAL(PR_Write(handle, buffer, JSVAL_TO_INT(size)));
  1296. /* writing panic */
  1297. if (count!=size) {
  1298. JS_free(cx, buffer);
  1299. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1300. JSFILEMSG_COPY_WRITE_ERROR, file->path);
  1301. goto out;
  1302. }
  1303. JS_free(cx, buffer);
  1304. if(!fileInitiallyOpen){
  1305. if(!file_close(cx, obj, 0, NULL, rval)) goto out;
  1306. }
  1307. if(PR_Close(handle)!=PR_SUCCESS){
  1308. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1309. JSFILEMSG_OP_FAILED, "close", dest);
  1310. goto out;
  1311. }
  1312. *rval = JSVAL_TRUE;
  1313. return JS_TRUE;
  1314. out:
  1315. if(file->isOpen && !fileInitiallyOpen){
  1316. if(PR_Close(file->handle)!=PR_SUCCESS){
  1317. JS_ReportWarning(cx, "Can't close %s, proceeding", file->path);
  1318. }
  1319. }
  1320. if(handle && PR_Close(handle)!=PR_SUCCESS){
  1321. JS_ReportWarning(cx, "Can't close %s, proceeding", dest);
  1322. }
  1323. *rval = JSVAL_FALSE;
  1324. return JS_FALSE;
  1325. }
  1326. static JSBool
  1327. file_renameTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1328. {
  1329. JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
  1330. char *dest;
  1331. SECURITY_CHECK(cx, NULL, "renameTo", file); /* may need a second argument!*/
  1332. JSFILE_CHECK_ONE_ARG("renameTo");
  1333. JSFILE_CHECK_NATIVE("renameTo");
  1334. JSFILE_CHECK_CLOSED("renameTo");
  1335. dest = RESOLVE_PATH(cx, JS_GetStringBytes(JS_ValueToString(cx, argv[0])));
  1336. if (PR_Rename(file->path, dest)==PR_SUCCESS){
  1337. /* copy the new filename */
  1338. JS_free(cx, file->path);
  1339. file->path = dest;
  1340. *rval = JSVAL_TRUE;
  1341. return JS_TRUE;
  1342. }else{
  1343. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1344. JSFILEMSG_RENAME_FAILED, file->path, dest);
  1345. goto out;
  1346. }
  1347. out:
  1348. *rval = JSVAL_FALSE;
  1349. return JS_FALSE;
  1350. }
  1351. static JSBool
  1352. file_flush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1353. {
  1354. JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
  1355. SECURITY_CHECK(cx, NULL, "flush", file);
  1356. JSFILE_CHECK_NATIVE("flush");
  1357. JSFILE_CHECK_OPEN("flush");
  1358. if (PR_Sync(file->handle)==PR_SUCCESS){
  1359. *rval = JSVAL_TRUE;
  1360. return JS_TRUE;
  1361. }else{
  1362. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1363. JSFILEMSG_OP_FAILED, "flush", file->path);
  1364. goto out;
  1365. }
  1366. out:
  1367. *rval = JSVAL_FALSE;
  1368. return JS_FALSE;
  1369. }
  1370. static JSBool
  1371. file_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1372. {
  1373. JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
  1374. JSString *str;
  1375. int32 count;
  1376. uintN i;
  1377. SECURITY_CHECK(cx, NULL, "write", file);
  1378. JSFILE_CHECK_WRITE;
  1379. for (i = 0; i<argc; i++) {
  1380. str = JS_ValueToString(cx, argv[i]);
  1381. count = js_FileWrite(cx, file, JS_GetStringChars(str),
  1382. JS_GetStringLength(str), file->type);
  1383. if (count==-1){
  1384. *rval = JSVAL_FALSE;
  1385. return JS_FALSE;
  1386. }
  1387. }
  1388. *rval = JSVAL_TRUE;
  1389. return JS_TRUE;
  1390. out:
  1391. *rval = JSVAL_FALSE;
  1392. return JS_FALSE;
  1393. }
  1394. static JSBool
  1395. file_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1396. {
  1397. JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
  1398. JSString *str;
  1399. SECURITY_CHECK(cx, NULL, "writeln", file);
  1400. JSFILE_CHECK_WRITE;
  1401. /* don't report an error here */
  1402. if(!file_write(cx, obj, argc, argv, rval)) return JS_FALSE;
  1403. /* don't do security here -- we passed the check in file_write */
  1404. str = JS_NewStringCopyZ(cx, "\n");
  1405. if (js_FileWrite(cx, file, JS_GetStringChars(str), JS_GetStringLength(str),
  1406. file->type)==-1){
  1407. *rval = JSVAL_FALSE;
  1408. return JS_FALSE;
  1409. }
  1410. /* eol causes flush if hasAutoflush is turned on */
  1411. if (file->hasAutoflush)
  1412. file_flush(cx, obj, 0, NULL, rval);
  1413. *rval = JSVAL_TRUE;
  1414. return JS_TRUE;
  1415. out:
  1416. *rval = JSVAL_FALSE;
  1417. return JS_FALSE;
  1418. }
  1419. static JSBool
  1420. file_writeAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1421. {
  1422. JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
  1423. jsuint i;
  1424. jsuint limit;
  1425. JSObject *array;
  1426. JSObject *elem;
  1427. jsval elemval;
  1428. SECURITY_CHECK(cx, NULL, "writeAll", file);
  1429. JSFILE_CHECK_ONE_ARG("writeAll");
  1430. JSFILE_CHECK_WRITE;
  1431. if (!JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[0]))) {
  1432. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1433. JSFILEMSG_FIRST_ARGUMENT_WRITEALL_NOT_ARRAY_ERROR);
  1434. goto out;
  1435. }
  1436. array = JSVAL_TO_OBJECT(argv[0]);
  1437. JS_GetArrayLength(cx, array, &limit);
  1438. for (i = 0; i<limit; i++) {
  1439. if (!JS_GetElement(cx, array, i, &elemval)) return JS_FALSE;
  1440. elem = JSVAL_TO_OBJECT(elemval);
  1441. file_writeln(cx, obj, 1, &elemval, rval);
  1442. }
  1443. *rval = JSVAL_TRUE;
  1444. return JS_TRUE;
  1445. out:
  1446. *rval = JSVAL_FALSE;
  1447. return JS_FALSE;
  1448. }
  1449. static JSBool
  1450. file_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1451. {
  1452. JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
  1453. JSString *str;
  1454. int32 want, count;
  1455. jschar *buf;
  1456. SECURITY_CHECK(cx, NULL, "read", file);
  1457. JSFILE_CHECK_ONE_ARG("read");
  1458. JSFILE_CHECK_READ;
  1459. if (!JS_ValueToInt32(cx, argv[0], &want)){
  1460. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1461. JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "read", argv[0]);
  1462. goto out;
  1463. }
  1464. /* want = (want>262144)?262144:want; * arbitrary size limitation */
  1465. buf = JS_malloc(cx, want*sizeof buf[0]);
  1466. if (!buf) goto out;
  1467. count = js_FileRead(cx, file, buf, want, file->type);
  1468. if (count>0) {
  1469. str = JS_NewUCStringCopyN(cx, buf, count);
  1470. *rval = STRING_TO_JSVAL(str);
  1471. JS_free(cx, buf);
  1472. return JS_TRUE;
  1473. } else {
  1474. JS_free(cx, buf);
  1475. goto out;
  1476. }
  1477. out:
  1478. *rval = JSVAL_FALSE;
  1479. return JS_FALSE;
  1480. }
  1481. static JSBool
  1482. file_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1483. {
  1484. JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
  1485. JSString *str;
  1486. jschar *buf = NULL, *tmp;
  1487. int32 offset, read;
  1488. intN room;
  1489. jschar data, data2;
  1490. SECURITY_CHECK(cx, NULL, "readln", file);
  1491. JSFILE_CHECK_READ;
  1492. buf = JS_malloc(cx, MAX_LINE_LENGTH * sizeof data);
  1493. if (!buf)
  1494. return JS_FALSE;
  1495. room = MAX_LINE_LENGTH - 1;
  1496. offset = 0;
  1497. for (;;) {
  1498. read = js_FileRead(cx, file, &data, 1, file->type);
  1499. if (read < 0)
  1500. goto out;
  1501. if (read == 0)
  1502. goto eof;
  1503. switch (data) {
  1504. case '\r':
  1505. read = js_FileRead(cx, file, &data2, 1, file->type);
  1506. if (read < 0)
  1507. goto out;
  1508. if (read == 1 && data2 != '\n') {
  1509. /* We read one char too far. Buffer it. */
  1510. file->charBuffer = data2;
  1511. file->charBufferUsed = JS_TRUE;
  1512. }
  1513. /* Fall through. */
  1514. case '\n':
  1515. goto done;
  1516. default:
  1517. if (--room < 0) {
  1518. tmp = JS_realloc(cx, buf,
  1519. (offset + MAX_LINE_LENGTH) * sizeof data);
  1520. if (!tmp)
  1521. goto out;
  1522. room = MAX_LINE_LENGTH - 1;
  1523. buf = tmp;
  1524. }
  1525. buf[offset++] = data;
  1526. break;
  1527. }
  1528. }
  1529. eof:
  1530. if (offset == 0) {
  1531. *rval = JSVAL_NULL;
  1532. return JS_TRUE;
  1533. }
  1534. done:
  1535. buf[offset] = 0;
  1536. tmp = JS_realloc(cx, buf, (offset + 1) * sizeof data);
  1537. if (!tmp)
  1538. goto out;
  1539. str = JS_NewUCString(cx, tmp, offset);
  1540. if (!str)
  1541. goto out;
  1542. *rval = STRING_TO_JSVAL(str);
  1543. return JS_TRUE;
  1544. out:
  1545. if (buf)
  1546. JS_free(cx, buf);
  1547. return JS_FALSE;
  1548. }
  1549. static JSBool
  1550. file_readAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1551. {
  1552. JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
  1553. JSObject *array;
  1554. jsint len;
  1555. jsval line;
  1556. JSBool lineok = JS_FALSE;
  1557. SECURITY_CHECK(cx, NULL, "readAll", file);
  1558. JSFILE_CHECK_READ;
  1559. array = JS_NewArrayObject(cx, 0, NULL);
  1560. if (!array)
  1561. return JS_FALSE;
  1562. *rval = OBJECT_TO_JSVAL(array);
  1563. len = 0;
  1564. lineok = file_readln(cx, obj, 0, NULL, &line);
  1565. while (lineok && !JSVAL_IS_NULL(line)) {
  1566. JS_SetElement(cx, array, len++, &line);
  1567. lineok = file_readln(cx, obj, 0, NULL, &line);
  1568. }
  1569. out:
  1570. return lineok;
  1571. }
  1572. static JSBool
  1573. file_seek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1574. {
  1575. JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
  1576. int32 toskip;
  1577. int32 pos;
  1578. SECURITY_CHECK(cx, NULL, "seek", file);
  1579. JSFILE_CHECK_ONE_ARG("seek");
  1580. JSFILE_CHECK_NATIVE("seek");
  1581. JSFILE_CHECK_READ;
  1582. if (!JS_ValueToInt32(cx, argv[0], &toskip)){
  1583. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1584. JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "seek", argv[0]);
  1585. goto out;
  1586. }
  1587. if(!file->hasRandomAccess){
  1588. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1589. JSFILEMSG_NO_RANDOM_ACCESS, file->path);
  1590. goto out;
  1591. }
  1592. if(js_isDirectory(cx, file)){
  1593. JS_ReportWarning(cx,"Seek on directories is not supported, proceeding");
  1594. goto out;
  1595. }
  1596. pos = js_FileSeek(cx, file, toskip, file->type);
  1597. if (pos!=-1) {
  1598. *rval = INT_TO_JSVAL(pos);
  1599. return JS_TRUE;
  1600. }
  1601. out:
  1602. *rval = JSVAL_VOID;
  1603. return JS_FALSE;
  1604. }
  1605. static JSBool
  1606. file_list(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1607. {
  1608. PRDir *dir;
  1609. PRDirEntry *entry;
  1610. JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
  1611. JSObject *array;
  1612. JSObject *eachFile;
  1613. jsint len;
  1614. jsval v;
  1615. JSRegExp *re = NULL;
  1616. JSFunction *func = NULL;
  1617. JSString *str;
  1618. jsval args[1];
  1619. char *filePath;
  1620. SECURITY_CHECK(cx, NULL, "list", file);
  1621. JSFILE_CHECK_NATIVE("list");
  1622. if (argc==1) {
  1623. if (VALUE_IS_REGEXP(cx, argv[0])) {
  1624. re = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0]));
  1625. }else
  1626. if (VALUE_IS_FUNCTION(cx, argv[0])) {
  1627. func = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0]));
  1628. }else{
  1629. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1630. JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_FUNCTION_OR_REGEX, argv[0]);
  1631. goto out;
  1632. }
  1633. }
  1634. if (!js_isDirectory(cx, file)) {
  1635. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1636. JSFILEMSG_CANNOT_DO_LIST_ON_A_FILE, file->path);
  1637. goto out;
  1638. }
  1639. dir = PR_OpenDir(file->path);
  1640. if(!dir){
  1641. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1642. JSFILEMSG_OP_FAILED, "open", file->path);
  1643. goto out;
  1644. }
  1645. /* create JSArray here... */
  1646. array = JS_NewArrayObject(cx, 0, NULL);
  1647. len = 0;
  1648. while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))!=NULL) {
  1649. /* first, check if we have a regexp */
  1650. if (re!=NULL) {
  1651. size_t index = 0;
  1652. str = JS_NewStringCopyZ(cx, entry->name);
  1653. if(!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &v)){
  1654. /* don't report anything here */
  1655. goto out;
  1656. }
  1657. /* not matched! */
  1658. if (JSVAL_IS_NULL(v)) {
  1659. continue;
  1660. }
  1661. }else
  1662. if (func!=NULL) {
  1663. str = JS_NewStringCopyZ(cx, entry->name);
  1664. args[0] = STRING_TO_JSVAL(str);
  1665. if(!JS_CallFunction(cx, obj, func, 1, args, &v)){
  1666. goto out;
  1667. }
  1668. if (v==JSVAL_FALSE) {
  1669. continue;
  1670. }
  1671. }
  1672. filePath = js_combinePath(cx, file->path, (char*)entry->name);
  1673. eachFile = js_NewFileObject(cx, filePath);
  1674. JS_free(cx, filePath);
  1675. if (!eachFile){
  1676. JS_ReportWarning(cx, "File %s cannot be retrieved", filePath);
  1677. continue;
  1678. }
  1679. v = OBJECT_TO_JSVAL(eachFile);
  1680. JS_SetElement(cx, array, len, &v);
  1681. JS_SetProperty(cx, array, entry->name, &v);
  1682. len++;
  1683. }
  1684. if(PR_CloseDir(dir)!=PR_SUCCESS){
  1685. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1686. JSFILEMSG_OP_FAILED, "close", file->path);
  1687. goto out;
  1688. }
  1689. *rval = OBJECT_TO_JSVAL(array);
  1690. return JS_TRUE;
  1691. out:
  1692. *rval = JSVAL_NULL;
  1693. return JS_FALSE;
  1694. }
  1695. static JSBool
  1696. file_mkdir(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1697. {
  1698. JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
  1699. SECURITY_CHECK(cx, NULL, "mkdir", file);
  1700. JSFILE_CHECK_ONE_ARG("mkdir");
  1701. JSFILE_CHECK_NATIVE("mkdir");
  1702. /* if the current file is not a directory, find out the directory name */
  1703. if (!js_isDirectory(cx, file)) {
  1704. char *dir = js_fileDirectoryName(cx, file->path);
  1705. JSObject *dirObj = js_NewFileObject(cx, dir);
  1706. JS_free(cx, dir);
  1707. /* call file_mkdir with the right set of parameters if needed */
  1708. if (file_mkdir(cx, dirObj, argc, argv, rval))
  1709. return JS_TRUE;
  1710. else
  1711. goto out;
  1712. }else{
  1713. char *dirName = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
  1714. char *fullName;
  1715. fullName = js_combinePath(cx, file->path, dirName);
  1716. if (PR_MkDir(fullName, 0755)==PR_SUCCESS){
  1717. *rval = JSVAL_TRUE;
  1718. JS_free(cx, fullName);
  1719. return JS_TRUE;
  1720. }else{
  1721. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1722. JSFILEMSG_OP_FAILED, "mkdir", fullName);
  1723. JS_free(cx, fullName);
  1724. goto out;
  1725. }
  1726. }
  1727. out:
  1728. *rval = JSVAL_FALSE;
  1729. return JS_FALSE;
  1730. }
  1731. static JSBool
  1732. file_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval*rval)
  1733. {
  1734. JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
  1735. JSString *str;
  1736. str = JS_NewStringCopyZ(cx, file->path);
  1737. if (!str)
  1738. return JS_FALSE;
  1739. *rval = STRING_TO_JSVAL(str);
  1740. return JS_TRUE;
  1741. }
  1742. static JSBool
  1743. file_toURL(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  1744. {
  1745. JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
  1746. char url[MAX_PATH_LENGTH];
  1747. jschar *urlChars;
  1748. size_t len;
  1749. JSString *str;
  1750. JSFILE_CHECK_NATIVE("toURL");
  1751. sprintf(url, "file://%s", file->path);
  1752. len = strlen(url);
  1753. urlChars = js_InflateString(cx, url, &len);
  1754. if (!urlChars)
  1755. return JS_FALSE;
  1756. str = js_NewString(cx, urlChars, len);
  1757. if (!str) {
  1758. JS_free(cx, urlChars);
  1759. return JS_FALSE;
  1760. }
  1761. *rval = STRING_TO_JSVAL(str);
  1762. /* TODO: js_escape in jsstr.h may go away at some point */
  1763. return js_str_escape(cx, obj, 0, rval, rval);
  1764. out:
  1765. *rval = JSVAL_VOID;
  1766. return JS_FALSE;
  1767. }
  1768. static void
  1769. file_finalize(JSContext *cx, JSObject *obj)
  1770. {
  1771. JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
  1772. if(file) {
  1773. /* Close the file before exiting. */
  1774. if(file->isOpen && !file->isNative) {
  1775. jsval vp;
  1776. file_close(cx, obj, 0, NULL, &vp);
  1777. }
  1778. if (file->path)
  1779. JS_free(cx, file->path);
  1780. JS_free(cx, file);
  1781. }
  1782. }
  1783. /*
  1784. Allocates memory for the file object, sets fields to defaults.
  1785. */
  1786. static JSFile*
  1787. file_init(JSContext *cx, JSObject *obj, char *bytes)
  1788. {
  1789. JSFile *file;
  1790. file = JS_malloc(cx, sizeof *file);
  1791. if (!file)
  1792. return NULL;
  1793. memset(file, 0 , sizeof *file);
  1794. js_ResetAttributes(file);
  1795. file->path = RESOLVE_PATH(cx, bytes);
  1796. if (!JS_SetPrivate(cx, obj, file)) {
  1797. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1798. JSFILEMSG_CANNOT_SET_PRIVATE_FILE, file->path);
  1799. JS_free(cx, file);
  1800. return NULL;
  1801. }
  1802. return file;
  1803. }
  1804. /* Returns a JSObject. This function is globally visible */
  1805. JS_PUBLIC_API(JSObject*)
  1806. js_NewFileObject(JSContext *cx, char *filename)
  1807. {
  1808. JSObject *obj;
  1809. JSFile *file;
  1810. obj = JS_NewObject(cx, &js_FileClass, NULL, NULL);
  1811. if (!obj){
  1812. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1813. JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObject");
  1814. return NULL;
  1815. }
  1816. file = file_init(cx, obj, filename);
  1817. if(!file) return NULL;
  1818. return obj;
  1819. }
  1820. /* Internal function, used for cases which NSPR file support doesn't cover */
  1821. JSObject*
  1822. js_NewFileObjectFromFILE(JSContext *cx, FILE *nativehandle, char *filename,
  1823. int32 mode, JSBool open, JSBool randomAccess)
  1824. {
  1825. JSObject *obj;
  1826. JSFile *file;
  1827. obj = JS_NewObject(cx, &js_FileClass, NULL, NULL);
  1828. if (!obj){
  1829. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1830. JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObjectFromFILE");
  1831. return NULL;
  1832. }
  1833. file = file_init(cx, obj, filename);
  1834. if(!file) return NULL;
  1835. file->nativehandle = nativehandle;
  1836. /* free result of RESOLVE_PATH from file_init. */
  1837. JS_ASSERT(file->path != NULL);
  1838. JS_free(cx, file->path);
  1839. file->path = strdup(filename);
  1840. file->isOpen = open;
  1841. file->mode = mode;
  1842. file->hasRandomAccess = randomAccess;
  1843. file->isNative = JS_TRUE;
  1844. return obj;
  1845. }
  1846. /*
  1847. Real file constructor that is called from JavaScript.
  1848. Basically, does error processing and calls file_init.
  1849. */
  1850. static JSBool
  1851. file_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  1852. jsval *rval)
  1853. {
  1854. JSString *str;
  1855. JSFile *file;
  1856. if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
  1857. /* Replace obj with a new File object. */
  1858. obj = JS_NewObject(cx, &js_FileClass, NULL, NULL);
  1859. if (!obj)
  1860. return JS_FALSE;
  1861. *rval = OBJECT_TO_JSVAL(obj);
  1862. }
  1863. str = (argc == 0)
  1864. ? JS_InternString(cx, "")
  1865. : JS_ValueToString(cx, argv[0]);
  1866. if (!str) {
  1867. JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
  1868. JSFILEMSG_FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR,
  1869. argv[0]);
  1870. return JS_FALSE;
  1871. }
  1872. file = file_init(cx, obj, JS_GetStringBytes(str));
  1873. if (!file)
  1874. return JS_FALSE;
  1875. SECURITY_CHECK(cx, NULL, "constructor", file);
  1876. return JS_TRUE;
  1877. }
  1878. /* -------------------- File methods and properties ------------------------- */
  1879. static JSFunctionSpec file_functions[] = {
  1880. { "open", file_open, 0},
  1881. { "close", file_close, 0},
  1882. { "remove", file_remove, 0},
  1883. { "copyTo", file_copyTo, 0},
  1884. { "renameTo", file_renameTo, 0},
  1885. { "flush", file_flush, 0},
  1886. { "seek", file_seek, 0},
  1887. { "read", file_read, 0},
  1888. { "readln", file_readln, 0},
  1889. { "readAll", file_readAll, 0},
  1890. { "write", file_write, 0},
  1891. { "writeln", file_writeln, 0},
  1892. { "writeAll", file_writeAll, 0},
  1893. { "list", file_list, 0},
  1894. { "mkdir", file_mkdir, 0},
  1895. { "toString", file_toString, 0},
  1896. { "toURL", file_toURL, 0},
  1897. {0}
  1898. };
  1899. enum file_tinyid {
  1900. FILE_LENGTH = -2,
  1901. FILE_PARENT = -3,
  1902. FILE_PATH = -4,
  1903. FILE_NAME = -5,
  1904. FILE_ISDIR = -6,
  1905. FILE_ISFILE = -7,
  1906. FILE_EXISTS = -8,
  1907. FILE_CANREAD = -9,
  1908. FILE_CANWRITE = -10,
  1909. FILE_OPEN = -11,
  1910. FILE_TYPE = -12,
  1911. FILE_MODE = -13,
  1912. FILE_CREATED = -14,
  1913. FILE_MODIFIED = -15,
  1914. FILE_SIZE = -16,
  1915. FILE_RANDOMACCESS = -17,
  1916. FILE_POSITION = -18,
  1917. FILE_APPEND = -19,
  1918. FILE_REPLACE = -20,
  1919. FILE_AUTOFLUSH = -21,
  1920. FILE_ISNATI