Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 "use strict";
6 module.metadata = {
7 "stability": "experimental"
8 };
10 const { Cc, Ci, CC } = require("chrome");
12 const { setTimeout } = require("../timers");
13 const { Stream, InputStream, OutputStream } = require("./stream");
14 const { emit, on } = require("../event/core");
15 const { Buffer } = require("./buffer");
16 const { ns } = require("../core/namespace");
17 const { Class } = require("../core/heritage");
20 const nsILocalFile = CC("@mozilla.org/file/local;1", "nsILocalFile",
21 "initWithPath");
22 const FileOutputStream = CC("@mozilla.org/network/file-output-stream;1",
23 "nsIFileOutputStream", "init");
24 const FileInputStream = CC("@mozilla.org/network/file-input-stream;1",
25 "nsIFileInputStream", "init");
26 const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
27 "nsIBinaryInputStream", "setInputStream");
28 const BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1",
29 "nsIBinaryOutputStream", "setOutputStream");
30 const StreamPump = CC("@mozilla.org/network/input-stream-pump;1",
31 "nsIInputStreamPump", "init");
33 const { createOutputTransport, createInputTransport } =
34 Cc["@mozilla.org/network/stream-transport-service;1"].
35 getService(Ci.nsIStreamTransportService);
37 const { OPEN_UNBUFFERED } = Ci.nsITransport;
40 const { REOPEN_ON_REWIND, DEFER_OPEN } = Ci.nsIFileInputStream;
41 const { DIRECTORY_TYPE, NORMAL_FILE_TYPE } = Ci.nsIFile;
42 const { NS_SEEK_SET, NS_SEEK_CUR, NS_SEEK_END } = Ci.nsISeekableStream;
44 const FILE_PERMISSION = parseInt("0666", 8);
45 const PR_UINT32_MAX = 0xfffffff;
46 // Values taken from:
47 // http://mxr.mozilla.org/mozilla-central/source/nsprpub/pr/include/prio.h#615
48 const PR_RDONLY = 0x01;
49 const PR_WRONLY = 0x02;
50 const PR_RDWR = 0x04;
51 const PR_CREATE_FILE = 0x08;
52 const PR_APPEND = 0x10;
53 const PR_TRUNCATE = 0x20;
54 const PR_SYNC = 0x40;
55 const PR_EXCL = 0x80;
57 const FLAGS = {
58 "r": PR_RDONLY,
59 "r+": PR_RDWR,
60 "w": PR_CREATE_FILE | PR_TRUNCATE | PR_WRONLY,
61 "w+": PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR,
62 "a": PR_APPEND | PR_CREATE_FILE | PR_WRONLY,
63 "a+": PR_APPEND | PR_CREATE_FILE | PR_RDWR
64 };
66 function accessor() {
67 let map = new WeakMap();
68 return function(fd, value) {
69 if (value === null) map.delete(fd);
70 if (value !== undefined) map.set(fd, value);
71 return map.get(fd);
72 }
73 }
75 let nsIFile = accessor();
76 let nsIFileInputStream = accessor();
77 let nsIFileOutputStream = accessor();
78 let nsIBinaryInputStream = accessor();
79 let nsIBinaryOutputStream = accessor();
81 // Just a contstant object used to signal that all of the file
82 // needs to be read.
83 const ALL = new String("Read all of the file");
85 function isWritable(mode) !!(mode & PR_WRONLY || mode & PR_RDWR)
86 function isReadable(mode) !!(mode & PR_RDONLY || mode & PR_RDWR)
88 function isString(value) typeof(value) === "string"
89 function isFunction(value) typeof(value) === "function"
91 function toArray(enumerator) {
92 let value = [];
93 while(enumerator.hasMoreElements())
94 value.push(enumerator.getNext())
95 return value
96 }
98 function getFileName(file) file.QueryInterface(Ci.nsIFile).leafName
101 function remove(path, recursive) {
102 let fd = new nsILocalFile(path)
103 if (fd.exists()) {
104 fd.remove(recursive || false);
105 }
106 else {
107 throw FSError("remove", "ENOENT", 34, path);
108 }
109 }
111 /**
112 * Utility function to convert either an octal number or string
113 * into an octal number
114 * 0777 => 0777
115 * "0644" => 0644
116 */
117 function Mode(mode, fallback) {
118 return isString(mode) ? parseInt(mode, 8) : mode || fallback;
119 }
120 function Flags(flag) {
121 return !isString(flag) ? flag :
122 FLAGS[flag] || Error("Unknown file open flag: " + flag);
123 }
126 function FSError(op, code, errno, path, file, line) {
127 let error = Error(code + ", " + op + " " + path, file, line);
128 error.code = code;
129 error.path = path;
130 error.errno = errno;
131 return error;
132 }
134 const ReadStream = Class({
135 extends: InputStream,
136 initialize: function initialize(path, options) {
137 this.position = -1;
138 this.length = -1;
139 this.flags = "r";
140 this.mode = FILE_PERMISSION;
141 this.bufferSize = 64 * 1024;
143 options = options || {};
145 if ("flags" in options && options.flags)
146 this.flags = options.flags;
147 if ("bufferSize" in options && options.bufferSize)
148 this.bufferSize = options.bufferSize;
149 if ("length" in options && options.length)
150 this.length = options.length;
151 if ("position" in options && options.position !== undefined)
152 this.position = options.position;
154 let { flags, mode, position, length } = this;
155 let fd = isString(path) ? openSync(path, flags, mode) : path;
156 this.fd = fd;
158 let input = nsIFileInputStream(fd);
159 // Setting a stream position, unless it"s `-1` which means current position.
160 if (position >= 0)
161 input.QueryInterface(Ci.nsISeekableStream).seek(NS_SEEK_SET, position);
162 // We use `nsIStreamTransportService` service to transform blocking
163 // file input stream into a fully asynchronous stream that can be written
164 // without blocking the main thread.
165 let transport = createInputTransport(input, position, length, false);
166 // Open an input stream on a transport. We don"t pass flags to guarantee
167 // non-blocking stream semantics. Also we use defaults for segment size &
168 // count.
169 InputStream.prototype.initialize.call(this, {
170 asyncInputStream: transport.openInputStream(null, 0, 0)
171 });
173 // Close file descriptor on end and destroy the stream.
174 on(this, "end", _ => {
175 this.destroy();
176 emit(this, "close");
177 });
179 this.read();
180 },
181 destroy: function() {
182 closeSync(this.fd);
183 InputStream.prototype.destroy.call(this);
184 }
185 });
186 exports.ReadStream = ReadStream;
187 exports.createReadStream = function createReadStream(path, options) {
188 return new ReadStream(path, options);
189 };
191 const WriteStream = Class({
192 extends: OutputStream,
193 initialize: function initialize(path, options) {
194 this.drainable = true;
195 this.flags = "w";
196 this.position = -1;
197 this.mode = FILE_PERMISSION;
199 options = options || {};
201 if ("flags" in options && options.flags)
202 this.flags = options.flags;
203 if ("mode" in options && options.mode)
204 this.mode = options.mode;
205 if ("position" in options && options.position !== undefined)
206 this.position = options.position;
208 let { position, flags, mode } = this;
209 // If pass was passed we create a file descriptor out of it. Otherwise
210 // we just use given file descriptor.
211 let fd = isString(path) ? openSync(path, flags, mode) : path;
212 this.fd = fd;
214 let output = nsIFileOutputStream(fd);
215 // Setting a stream position, unless it"s `-1` which means current position.
216 if (position >= 0)
217 output.QueryInterface(Ci.nsISeekableStream).seek(NS_SEEK_SET, position);
218 // We use `nsIStreamTransportService` service to transform blocking
219 // file output stream into a fully asynchronous stream that can be written
220 // without blocking the main thread.
221 let transport = createOutputTransport(output, position, -1, false);
222 // Open an output stream on a transport. We don"t pass flags to guarantee
223 // non-blocking stream semantics. Also we use defaults for segment size &
224 // count.
225 OutputStream.prototype.initialize.call(this, {
226 asyncOutputStream: transport.openOutputStream(OPEN_UNBUFFERED, 0, 0),
227 output: output
228 });
230 // For write streams "finish" basically means close.
231 on(this, "finish", _ => {
232 this.destroy();
233 emit(this, "close");
234 });
235 },
236 destroy: function() {
237 OutputStream.prototype.destroy.call(this);
238 closeSync(this.fd);
239 }
240 });
241 exports.WriteStream = WriteStream;
242 exports.createWriteStream = function createWriteStream(path, options) {
243 return new WriteStream(path, options);
244 };
246 const Stats = Class({
247 initialize: function initialize(path) {
248 let file = new nsILocalFile(path);
249 if (!file.exists()) throw FSError("stat", "ENOENT", 34, path);
250 nsIFile(this, file);
251 },
252 isDirectory: function() nsIFile(this).isDirectory(),
253 isFile: function() nsIFile(this).isFile(),
254 isSymbolicLink: function() nsIFile(this).isSymlink(),
255 get mode() nsIFile(this).permissions,
256 get size() nsIFile(this).fileSize,
257 get mtime() nsIFile(this).lastModifiedTime,
258 isBlockDevice: function() nsIFile(this).isSpecial(),
259 isCharacterDevice: function() nsIFile(this).isSpecial(),
260 isFIFO: function() nsIFile(this).isSpecial(),
261 isSocket: function() nsIFile(this).isSpecial(),
262 // non standard
263 get exists() nsIFile(this).exists(),
264 get hidden() nsIFile(this).isHidden(),
265 get writable() nsIFile(this).isWritable(),
266 get readable() nsIFile(this).isReadable()
267 });
268 exports.Stats = Stats;
270 const LStats = Class({
271 extends: Stats,
272 get size() this.isSymbolicLink() ? nsIFile(this).fileSizeOfLink :
273 nsIFile(this).fileSize,
274 get mtime() this.isSymbolicLink() ? nsIFile(this).lastModifiedTimeOfLink :
275 nsIFile(this).lastModifiedTime,
276 // non standard
277 get permissions() this.isSymbolicLink() ? nsIFile(this).permissionsOfLink :
278 nsIFile(this).permissions
279 });
281 const FStat = Class({
282 extends: Stats,
283 initialize: function initialize(fd) {
284 nsIFile(this, nsIFile(fd));
285 }
286 });
288 function noop() {}
289 function Async(wrapped) {
290 return function (path, callback) {
291 let args = Array.slice(arguments);
292 callback = args.pop();
293 // If node is not given a callback argument
294 // it just does not calls it.
295 if (typeof(callback) !== "function") {
296 args.push(callback);
297 callback = noop;
298 }
299 setTimeout(function() {
300 try {
301 var result = wrapped.apply(this, args);
302 if (result === undefined) callback(null);
303 else callback(null, result);
304 } catch (error) {
305 callback(error);
306 }
307 }, 0);
308 }
309 }
312 /**
313 * Synchronous rename(2)
314 */
315 function renameSync(oldPath, newPath) {
316 let source = new nsILocalFile(oldPath);
317 let target = new nsILocalFile(newPath);
318 if (!source.exists()) throw FSError("rename", "ENOENT", 34, oldPath);
319 return source.moveTo(target.parent, target.leafName);
320 };
321 exports.renameSync = renameSync;
323 /**
324 * Asynchronous rename(2). No arguments other than a possible exception are
325 * given to the completion callback.
326 */
327 let rename = Async(renameSync);
328 exports.rename = rename;
330 /**
331 * Test whether or not the given path exists by checking with the file system.
332 */
333 function existsSync(path) {
334 return new nsILocalFile(path).exists();
335 }
336 exports.existsSync = existsSync;
338 let exists = Async(existsSync);
339 exports.exists = exists;
341 /**
342 * Synchronous ftruncate(2).
343 */
344 function truncateSync(path, length) {
345 let fd = openSync(path, "w");
346 ftruncateSync(fd, length);
347 closeSync(fd);
348 }
349 exports.truncateSync = truncateSync;
351 /**
352 * Asynchronous ftruncate(2). No arguments other than a possible exception are
353 * given to the completion callback.
354 */
355 function truncate(path, length, callback) {
356 open(path, "w", function(error, fd) {
357 if (error) return callback(error);
358 ftruncate(fd, length, function(error) {
359 if (error) {
360 closeSync(fd);
361 callback(error);
362 }
363 else {
364 close(fd, callback);
365 }
366 });
367 });
368 }
369 exports.truncate = truncate;
371 function ftruncate(fd, length, callback) {
372 write(fd, new Buffer(length), 0, length, 0, function(error) {
373 callback(error);
374 });
375 }
376 exports.ftruncate = ftruncate;
378 function ftruncateSync(fd, length = 0) {
379 writeSync(fd, new Buffer(length), 0, length, 0);
380 }
381 exports.ftruncateSync = ftruncateSync;
383 function chownSync(path, uid, gid) {
384 throw Error("Not implemented yet!!");
385 }
386 exports.chownSync = chownSync;
388 let chown = Async(chownSync);
389 exports.chown = chown;
391 function lchownSync(path, uid, gid) {
392 throw Error("Not implemented yet!!");
393 }
394 exports.lchownSync = chownSync;
396 let lchown = Async(lchown);
397 exports.lchown = lchown;
399 /**
400 * Synchronous chmod(2).
401 */
402 function chmodSync (path, mode) {
403 let file;
404 try {
405 file = new nsILocalFile(path);
406 } catch(e) {
407 throw FSError("chmod", "ENOENT", 34, path);
408 }
410 file.permissions = Mode(mode);
411 }
412 exports.chmodSync = chmodSync;
413 /**
414 * Asynchronous chmod(2). No arguments other than a possible exception are
415 * given to the completion callback.
416 */
417 let chmod = Async(chmodSync);
418 exports.chmod = chmod;
420 /**
421 * Synchronous chmod(2).
422 */
423 function fchmodSync(fd, mode) {
424 throw Error("Not implemented yet!!");
425 };
426 exports.fchmodSync = fchmodSync;
427 /**
428 * Asynchronous chmod(2). No arguments other than a possible exception are
429 * given to the completion callback.
430 */
431 let fchmod = Async(fchmodSync);
432 exports.fchmod = fchmod;
435 /**
436 * Synchronous stat(2). Returns an instance of `fs.Stats`
437 */
438 function statSync(path) {
439 return new Stats(path);
440 };
441 exports.statSync = statSync;
443 /**
444 * Asynchronous stat(2). The callback gets two arguments (err, stats) where
445 * stats is a `fs.Stats` object. It looks like this:
446 */
447 let stat = Async(statSync);
448 exports.stat = stat;
450 /**
451 * Synchronous lstat(2). Returns an instance of `fs.Stats`.
452 */
453 function lstatSync(path) {
454 return new LStats(path);
455 };
456 exports.lstatSync = lstatSync;
458 /**
459 * Asynchronous lstat(2). The callback gets two arguments (err, stats) where
460 * stats is a fs.Stats object. lstat() is identical to stat(), except that if
461 * path is a symbolic link, then the link itself is stat-ed, not the file that
462 * it refers to.
463 */
464 let lstat = Async(lstatSync);
465 exports.lstat = lstat;
467 /**
468 * Synchronous fstat(2). Returns an instance of `fs.Stats`.
469 */
470 function fstatSync(fd) {
471 return new FStat(fd);
472 };
473 exports.fstatSync = fstatSync;
475 /**
476 * Asynchronous fstat(2). The callback gets two arguments (err, stats) where
477 * stats is a fs.Stats object.
478 */
479 let fstat = Async(fstatSync);
480 exports.fstat = fstat;
482 /**
483 * Synchronous link(2).
484 */
485 function linkSync(source, target) {
486 throw Error("Not implemented yet!!");
487 };
488 exports.linkSync = linkSync;
490 /**
491 * Asynchronous link(2). No arguments other than a possible exception are given
492 * to the completion callback.
493 */
494 let link = Async(linkSync);
495 exports.link = link;
497 /**
498 * Synchronous symlink(2).
499 */
500 function symlinkSync(source, target) {
501 throw Error("Not implemented yet!!");
502 };
503 exports.symlinkSync = symlinkSync;
505 /**
506 * Asynchronous symlink(2). No arguments other than a possible exception are
507 * given to the completion callback.
508 */
509 let symlink = Async(symlinkSync);
510 exports.symlink = symlink;
512 /**
513 * Synchronous readlink(2). Returns the resolved path.
514 */
515 function readlinkSync(path) {
516 return new nsILocalFile(path).target;
517 };
518 exports.readlinkSync = readlinkSync;
520 /**
521 * Asynchronous readlink(2). The callback gets two arguments
522 * `(error, resolvedPath)`.
523 */
524 let readlink = Async(readlinkSync);
525 exports.readlink = readlink;
527 /**
528 * Synchronous realpath(2). Returns the resolved path.
529 */
530 function realpathSync(path) {
531 return new nsILocalFile(path).path;
532 };
533 exports.realpathSync = realpathSync;
535 /**
536 * Asynchronous realpath(2). The callback gets two arguments
537 * `(err, resolvedPath)`.
538 */
539 let realpath = Async(realpathSync);
540 exports.realpath = realpath;
542 /**
543 * Synchronous unlink(2).
544 */
545 let unlinkSync = remove;
546 exports.unlinkSync = unlinkSync;
548 /**
549 * Asynchronous unlink(2). No arguments other than a possible exception are
550 * given to the completion callback.
551 */
552 let unlink = Async(remove);
553 exports.unlink = unlink;
555 /**
556 * Synchronous rmdir(2).
557 */
558 let rmdirSync = remove;
559 exports.rmdirSync = rmdirSync;
561 /**
562 * Asynchronous rmdir(2). No arguments other than a possible exception are
563 * given to the completion callback.
564 */
565 let rmdir = Async(rmdirSync);
566 exports.rmdir = rmdir;
568 /**
569 * Synchronous mkdir(2).
570 */
571 function mkdirSync(path, mode) {
572 try {
573 return nsILocalFile(path).create(DIRECTORY_TYPE, Mode(mode));
574 } catch (error) {
575 // Adjust exception thorw to match ones thrown by node.
576 if (error.name === "NS_ERROR_FILE_ALREADY_EXISTS") {
577 let { fileName, lineNumber } = error;
578 error = FSError("mkdir", "EEXIST", 47, path, fileName, lineNumber);
579 }
580 throw error;
581 }
582 };
583 exports.mkdirSync = mkdirSync;
585 /**
586 * Asynchronous mkdir(2). No arguments other than a possible exception are
587 * given to the completion callback.
588 */
589 let mkdir = Async(mkdirSync);
590 exports.mkdir = mkdir;
592 /**
593 * Synchronous readdir(3). Returns an array of filenames excluding `"."` and
594 * `".."`.
595 */
596 function readdirSync(path) {
597 try {
598 return toArray(new nsILocalFile(path).directoryEntries).map(getFileName);
599 }
600 catch (error) {
601 // Adjust exception thorw to match ones thrown by node.
602 if (error.name === "NS_ERROR_FILE_TARGET_DOES_NOT_EXIST" ||
603 error.name === "NS_ERROR_FILE_NOT_FOUND")
604 {
605 let { fileName, lineNumber } = error;
606 error = FSError("readdir", "ENOENT", 34, path, fileName, lineNumber);
607 }
608 throw error;
609 }
610 };
611 exports.readdirSync = readdirSync;
613 /**
614 * Asynchronous readdir(3). Reads the contents of a directory. The callback
615 * gets two arguments `(error, files)` where `files` is an array of the names
616 * of the files in the directory excluding `"."` and `".."`.
617 */
618 let readdir = Async(readdirSync);
619 exports.readdir = readdir;
621 /**
622 * Synchronous close(2).
623 */
624 function closeSync(fd) {
625 let input = nsIFileInputStream(fd);
626 let output = nsIFileOutputStream(fd);
628 // Closing input stream and removing reference.
629 if (input) input.close();
630 // Closing output stream and removing reference.
631 if (output) output.close();
633 nsIFile(fd, null);
634 nsIFileInputStream(fd, null);
635 nsIFileOutputStream(fd, null);
636 nsIBinaryInputStream(fd, null);
637 nsIBinaryOutputStream(fd, null);
638 };
639 exports.closeSync = closeSync;
640 /**
641 * Asynchronous close(2). No arguments other than a possible exception are
642 * given to the completion callback.
643 */
644 let close = Async(closeSync);
645 exports.close = close;
647 /**
648 * Synchronous open(2).
649 */
650 function openSync(path, flags, mode) {
651 let [ fd, flags, mode, file ] =
652 [ { path: path }, Flags(flags), Mode(mode), nsILocalFile(path) ];
654 nsIFile(fd, file);
656 // If trying to open file for just read that does not exists
657 // need to throw exception as node does.
658 if (!file.exists() && !isWritable(flags))
659 throw FSError("open", "ENOENT", 34, path);
661 // If we want to open file in read mode we initialize input stream.
662 if (isReadable(flags)) {
663 let input = FileInputStream(file, flags, mode, DEFER_OPEN);
664 nsIFileInputStream(fd, input);
665 }
667 // If we want to open file in write mode we initialize output stream for it.
668 if (isWritable(flags)) {
669 let output = FileOutputStream(file, flags, mode, DEFER_OPEN);
670 nsIFileOutputStream(fd, output);
671 }
673 return fd;
674 }
675 exports.openSync = openSync;
676 /**
677 * Asynchronous file open. See open(2). Flags can be
678 * `"r", "r+", "w", "w+", "a"`, or `"a+"`. mode defaults to `0666`.
679 * The callback gets two arguments `(error, fd).
680 */
681 let open = Async(openSync);
682 exports.open = open;
684 /**
685 * Synchronous version of buffer-based fs.write(). Returns the number of bytes
686 * written.
687 */
688 function writeSync(fd, buffer, offset, length, position) {
689 if (length + offset > buffer.length) {
690 throw Error("Length is extends beyond buffer");
691 }
692 else if (length + offset !== buffer.length) {
693 buffer = buffer.slice(offset, offset + length);
694 }
695 let writeStream = new WriteStream(fd, { position: position,
696 length: length });
698 let output = BinaryOutputStream(nsIFileOutputStream(fd));
699 nsIBinaryOutputStream(fd, output);
700 // We write content as a byte array as this will avoid any transcoding
701 // if content was a buffer.
702 output.writeByteArray(buffer.valueOf(), buffer.length);
703 output.flush();
704 };
705 exports.writeSync = writeSync;
707 /**
708 * Write buffer to the file specified by fd.
709 *
710 * `offset` and `length` determine the part of the buffer to be written.
711 *
712 * `position` refers to the offset from the beginning of the file where this
713 * data should be written. If `position` is `null`, the data will be written
714 * at the current position. See pwrite(2).
715 *
716 * The callback will be given three arguments `(error, written, buffer)` where
717 * written specifies how many bytes were written into buffer.
718 *
719 * Note that it is unsafe to use `fs.write` multiple times on the same file
720 * without waiting for the callback.
721 */
722 function write(fd, buffer, offset, length, position, callback) {
723 if (!Buffer.isBuffer(buffer)) {
724 // (fd, data, position, encoding, callback)
725 let encoding = null;
726 [ position, encoding, callback ] = Array.slice(arguments, 1);
727 buffer = new Buffer(String(buffer), encoding);
728 offset = 0;
729 } else if (length + offset > buffer.length) {
730 throw Error("Length is extends beyond buffer");
731 } else if (length + offset !== buffer.length) {
732 buffer = buffer.slice(offset, offset + length);
733 }
735 let writeStream = new WriteStream(fd, { position: position,
736 length: length });
737 writeStream.on("error", callback);
738 writeStream.write(buffer, function onEnd() {
739 writeStream.destroy();
740 if (callback)
741 callback(null, buffer.length, buffer);
742 });
743 };
744 exports.write = write;
746 /**
747 * Synchronous version of string-based fs.read. Returns the number of
748 * bytes read.
749 */
750 function readSync(fd, buffer, offset, length, position) {
751 let input = nsIFileInputStream(fd);
752 // Setting a stream position, unless it"s `-1` which means current position.
753 if (position >= 0)
754 input.QueryInterface(Ci.nsISeekableStream).seek(NS_SEEK_SET, position);
755 // We use `nsIStreamTransportService` service to transform blocking
756 // file input stream into a fully asynchronous stream that can be written
757 // without blocking the main thread.
758 let binaryInputStream = BinaryInputStream(input);
759 let count = length === ALL ? binaryInputStream.available() : length;
760 if (offset === 0) binaryInputStream.readArrayBuffer(count, buffer.buffer);
761 else {
762 let chunk = new Buffer(count);
763 binaryInputStream.readArrayBuffer(count, chunk.buffer);
764 chunk.copy(buffer, offset);
765 }
767 return buffer.slice(offset, offset + count);
768 };
769 exports.readSync = readSync;
771 /**
772 * Read data from the file specified by `fd`.
773 *
774 * `buffer` is the buffer that the data will be written to.
775 * `offset` is offset within the buffer where writing will start.
776 *
777 * `length` is an integer specifying the number of bytes to read.
778 *
779 * `position` is an integer specifying where to begin reading from in the file.
780 * If `position` is `null`, data will be read from the current file position.
781 *
782 * The callback is given the three arguments, `(error, bytesRead, buffer)`.
783 */
784 function read(fd, buffer, offset, length, position, callback) {
785 let bytesRead = 0;
786 let readStream = new ReadStream(fd, { position: position, length: length });
787 readStream.on("data", function onData(data) {
788 data.copy(buffer, offset + bytesRead);
789 bytesRead += data.length;
790 });
791 readStream.on("end", function onEnd() {
792 callback(null, bytesRead, buffer);
793 readStream.destroy();
794 });
795 };
796 exports.read = read;
798 /**
799 * Asynchronously reads the entire contents of a file.
800 * The callback is passed two arguments `(error, data)`, where data is the
801 * contents of the file.
802 */
803 function readFile(path, encoding, callback) {
804 if (isFunction(encoding)) {
805 callback = encoding
806 encoding = null
807 }
809 let buffer = null;
810 try {
811 let readStream = new ReadStream(path);
812 readStream.on("data", function(data) {
813 if (!buffer) buffer = data;
814 else buffer = Buffer.concat([buffer, data], 2);
815 });
816 readStream.on("error", function onError(error) {
817 callback(error);
818 });
819 readStream.on("end", function onEnd() {
820 // Note: Need to destroy before invoking a callback
821 // so that file descriptor is released.
822 readStream.destroy();
823 callback(null, buffer);
824 });
825 } catch (error) {
826 setTimeout(callback, 0, error);
827 }
828 };
829 exports.readFile = readFile;
831 /**
832 * Synchronous version of `fs.readFile`. Returns the contents of the path.
833 * If encoding is specified then this function returns a string.
834 * Otherwise it returns a buffer.
835 */
836 function readFileSync(path, encoding) {
837 let fd = openSync(path, "r");
838 let size = fstatSync(fd).size;
839 let buffer = new Buffer(size);
840 try {
841 readSync(fd, buffer, 0, ALL, 0);
842 }
843 finally {
844 closeSync(fd);
845 }
846 return buffer;
847 };
848 exports.readFileSync = readFileSync;
850 /**
851 * Asynchronously writes data to a file, replacing the file if it already
852 * exists. data can be a string or a buffer.
853 */
854 function writeFile(path, content, encoding, callback) {
855 if (!isString(path))
856 throw new TypeError('path must be a string');
858 try {
859 if (isFunction(encoding)) {
860 callback = encoding
861 encoding = null
862 }
863 if (isString(content))
864 content = new Buffer(content, encoding);
866 let writeStream = new WriteStream(path);
867 let error = null;
869 writeStream.end(content, function() {
870 writeStream.destroy();
871 callback(error);
872 });
874 writeStream.on("error", function onError(reason) {
875 error = reason;
876 writeStream.destroy();
877 });
878 } catch (error) {
879 callback(error);
880 }
881 };
882 exports.writeFile = writeFile;
884 /**
885 * The synchronous version of `fs.writeFile`.
886 */
887 function writeFileSync(filename, data, encoding) {
888 throw Error("Not implemented");
889 };
890 exports.writeFileSync = writeFileSync;
893 function utimesSync(path, atime, mtime) {
894 throw Error("Not implemented");
895 }
896 exports.utimesSync = utimesSync;
898 let utimes = Async(utimesSync);
899 exports.utimes = utimes;
901 function futimesSync(fd, atime, mtime, callback) {
902 throw Error("Not implemented");
903 }
904 exports.futimesSync = futimesSync;
906 let futimes = Async(futimesSync);
907 exports.futimes = futimes;
909 function fsyncSync(fd, atime, mtime, callback) {
910 throw Error("Not implemented");
911 }
912 exports.fsyncSync = fsyncSync;
914 let fsync = Async(fsyncSync);
915 exports.fsync = fsync;
918 /**
919 * Watch for changes on filename. The callback listener will be called each
920 * time the file is accessed.
921 *
922 * The second argument is optional. The options if provided should be an object
923 * containing two members a boolean, persistent, and interval, a polling value
924 * in milliseconds. The default is { persistent: true, interval: 0 }.
925 */
926 function watchFile(path, options, listener) {
927 throw Error("Not implemented");
928 };
929 exports.watchFile = watchFile;
932 function unwatchFile(path, listener) {
933 throw Error("Not implemented");
934 }
935 exports.unwatchFile = unwatchFile;
937 function watch(path, options, listener) {
938 throw Error("Not implemented");
939 }
940 exports.watch = watch;