1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/lib/sdk/io/fs.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,940 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 +"use strict"; 1.8 + 1.9 +module.metadata = { 1.10 + "stability": "experimental" 1.11 +}; 1.12 + 1.13 +const { Cc, Ci, CC } = require("chrome"); 1.14 + 1.15 +const { setTimeout } = require("../timers"); 1.16 +const { Stream, InputStream, OutputStream } = require("./stream"); 1.17 +const { emit, on } = require("../event/core"); 1.18 +const { Buffer } = require("./buffer"); 1.19 +const { ns } = require("../core/namespace"); 1.20 +const { Class } = require("../core/heritage"); 1.21 + 1.22 + 1.23 +const nsILocalFile = CC("@mozilla.org/file/local;1", "nsILocalFile", 1.24 + "initWithPath"); 1.25 +const FileOutputStream = CC("@mozilla.org/network/file-output-stream;1", 1.26 + "nsIFileOutputStream", "init"); 1.27 +const FileInputStream = CC("@mozilla.org/network/file-input-stream;1", 1.28 + "nsIFileInputStream", "init"); 1.29 +const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", 1.30 + "nsIBinaryInputStream", "setInputStream"); 1.31 +const BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1", 1.32 + "nsIBinaryOutputStream", "setOutputStream"); 1.33 +const StreamPump = CC("@mozilla.org/network/input-stream-pump;1", 1.34 + "nsIInputStreamPump", "init"); 1.35 + 1.36 +const { createOutputTransport, createInputTransport } = 1.37 + Cc["@mozilla.org/network/stream-transport-service;1"]. 1.38 + getService(Ci.nsIStreamTransportService); 1.39 + 1.40 +const { OPEN_UNBUFFERED } = Ci.nsITransport; 1.41 + 1.42 + 1.43 +const { REOPEN_ON_REWIND, DEFER_OPEN } = Ci.nsIFileInputStream; 1.44 +const { DIRECTORY_TYPE, NORMAL_FILE_TYPE } = Ci.nsIFile; 1.45 +const { NS_SEEK_SET, NS_SEEK_CUR, NS_SEEK_END } = Ci.nsISeekableStream; 1.46 + 1.47 +const FILE_PERMISSION = parseInt("0666", 8); 1.48 +const PR_UINT32_MAX = 0xfffffff; 1.49 +// Values taken from: 1.50 +// http://mxr.mozilla.org/mozilla-central/source/nsprpub/pr/include/prio.h#615 1.51 +const PR_RDONLY = 0x01; 1.52 +const PR_WRONLY = 0x02; 1.53 +const PR_RDWR = 0x04; 1.54 +const PR_CREATE_FILE = 0x08; 1.55 +const PR_APPEND = 0x10; 1.56 +const PR_TRUNCATE = 0x20; 1.57 +const PR_SYNC = 0x40; 1.58 +const PR_EXCL = 0x80; 1.59 + 1.60 +const FLAGS = { 1.61 + "r": PR_RDONLY, 1.62 + "r+": PR_RDWR, 1.63 + "w": PR_CREATE_FILE | PR_TRUNCATE | PR_WRONLY, 1.64 + "w+": PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR, 1.65 + "a": PR_APPEND | PR_CREATE_FILE | PR_WRONLY, 1.66 + "a+": PR_APPEND | PR_CREATE_FILE | PR_RDWR 1.67 +}; 1.68 + 1.69 +function accessor() { 1.70 + let map = new WeakMap(); 1.71 + return function(fd, value) { 1.72 + if (value === null) map.delete(fd); 1.73 + if (value !== undefined) map.set(fd, value); 1.74 + return map.get(fd); 1.75 + } 1.76 +} 1.77 + 1.78 +let nsIFile = accessor(); 1.79 +let nsIFileInputStream = accessor(); 1.80 +let nsIFileOutputStream = accessor(); 1.81 +let nsIBinaryInputStream = accessor(); 1.82 +let nsIBinaryOutputStream = accessor(); 1.83 + 1.84 +// Just a contstant object used to signal that all of the file 1.85 +// needs to be read. 1.86 +const ALL = new String("Read all of the file"); 1.87 + 1.88 +function isWritable(mode) !!(mode & PR_WRONLY || mode & PR_RDWR) 1.89 +function isReadable(mode) !!(mode & PR_RDONLY || mode & PR_RDWR) 1.90 + 1.91 +function isString(value) typeof(value) === "string" 1.92 +function isFunction(value) typeof(value) === "function" 1.93 + 1.94 +function toArray(enumerator) { 1.95 + let value = []; 1.96 + while(enumerator.hasMoreElements()) 1.97 + value.push(enumerator.getNext()) 1.98 + return value 1.99 +} 1.100 + 1.101 +function getFileName(file) file.QueryInterface(Ci.nsIFile).leafName 1.102 + 1.103 + 1.104 +function remove(path, recursive) { 1.105 + let fd = new nsILocalFile(path) 1.106 + if (fd.exists()) { 1.107 + fd.remove(recursive || false); 1.108 + } 1.109 + else { 1.110 + throw FSError("remove", "ENOENT", 34, path); 1.111 + } 1.112 +} 1.113 + 1.114 +/** 1.115 + * Utility function to convert either an octal number or string 1.116 + * into an octal number 1.117 + * 0777 => 0777 1.118 + * "0644" => 0644 1.119 + */ 1.120 +function Mode(mode, fallback) { 1.121 + return isString(mode) ? parseInt(mode, 8) : mode || fallback; 1.122 +} 1.123 +function Flags(flag) { 1.124 + return !isString(flag) ? flag : 1.125 + FLAGS[flag] || Error("Unknown file open flag: " + flag); 1.126 +} 1.127 + 1.128 + 1.129 +function FSError(op, code, errno, path, file, line) { 1.130 + let error = Error(code + ", " + op + " " + path, file, line); 1.131 + error.code = code; 1.132 + error.path = path; 1.133 + error.errno = errno; 1.134 + return error; 1.135 +} 1.136 + 1.137 +const ReadStream = Class({ 1.138 + extends: InputStream, 1.139 + initialize: function initialize(path, options) { 1.140 + this.position = -1; 1.141 + this.length = -1; 1.142 + this.flags = "r"; 1.143 + this.mode = FILE_PERMISSION; 1.144 + this.bufferSize = 64 * 1024; 1.145 + 1.146 + options = options || {}; 1.147 + 1.148 + if ("flags" in options && options.flags) 1.149 + this.flags = options.flags; 1.150 + if ("bufferSize" in options && options.bufferSize) 1.151 + this.bufferSize = options.bufferSize; 1.152 + if ("length" in options && options.length) 1.153 + this.length = options.length; 1.154 + if ("position" in options && options.position !== undefined) 1.155 + this.position = options.position; 1.156 + 1.157 + let { flags, mode, position, length } = this; 1.158 + let fd = isString(path) ? openSync(path, flags, mode) : path; 1.159 + this.fd = fd; 1.160 + 1.161 + let input = nsIFileInputStream(fd); 1.162 + // Setting a stream position, unless it"s `-1` which means current position. 1.163 + if (position >= 0) 1.164 + input.QueryInterface(Ci.nsISeekableStream).seek(NS_SEEK_SET, position); 1.165 + // We use `nsIStreamTransportService` service to transform blocking 1.166 + // file input stream into a fully asynchronous stream that can be written 1.167 + // without blocking the main thread. 1.168 + let transport = createInputTransport(input, position, length, false); 1.169 + // Open an input stream on a transport. We don"t pass flags to guarantee 1.170 + // non-blocking stream semantics. Also we use defaults for segment size & 1.171 + // count. 1.172 + InputStream.prototype.initialize.call(this, { 1.173 + asyncInputStream: transport.openInputStream(null, 0, 0) 1.174 + }); 1.175 + 1.176 + // Close file descriptor on end and destroy the stream. 1.177 + on(this, "end", _ => { 1.178 + this.destroy(); 1.179 + emit(this, "close"); 1.180 + }); 1.181 + 1.182 + this.read(); 1.183 + }, 1.184 + destroy: function() { 1.185 + closeSync(this.fd); 1.186 + InputStream.prototype.destroy.call(this); 1.187 + } 1.188 +}); 1.189 +exports.ReadStream = ReadStream; 1.190 +exports.createReadStream = function createReadStream(path, options) { 1.191 + return new ReadStream(path, options); 1.192 +}; 1.193 + 1.194 +const WriteStream = Class({ 1.195 + extends: OutputStream, 1.196 + initialize: function initialize(path, options) { 1.197 + this.drainable = true; 1.198 + this.flags = "w"; 1.199 + this.position = -1; 1.200 + this.mode = FILE_PERMISSION; 1.201 + 1.202 + options = options || {}; 1.203 + 1.204 + if ("flags" in options && options.flags) 1.205 + this.flags = options.flags; 1.206 + if ("mode" in options && options.mode) 1.207 + this.mode = options.mode; 1.208 + if ("position" in options && options.position !== undefined) 1.209 + this.position = options.position; 1.210 + 1.211 + let { position, flags, mode } = this; 1.212 + // If pass was passed we create a file descriptor out of it. Otherwise 1.213 + // we just use given file descriptor. 1.214 + let fd = isString(path) ? openSync(path, flags, mode) : path; 1.215 + this.fd = fd; 1.216 + 1.217 + let output = nsIFileOutputStream(fd); 1.218 + // Setting a stream position, unless it"s `-1` which means current position. 1.219 + if (position >= 0) 1.220 + output.QueryInterface(Ci.nsISeekableStream).seek(NS_SEEK_SET, position); 1.221 + // We use `nsIStreamTransportService` service to transform blocking 1.222 + // file output stream into a fully asynchronous stream that can be written 1.223 + // without blocking the main thread. 1.224 + let transport = createOutputTransport(output, position, -1, false); 1.225 + // Open an output stream on a transport. We don"t pass flags to guarantee 1.226 + // non-blocking stream semantics. Also we use defaults for segment size & 1.227 + // count. 1.228 + OutputStream.prototype.initialize.call(this, { 1.229 + asyncOutputStream: transport.openOutputStream(OPEN_UNBUFFERED, 0, 0), 1.230 + output: output 1.231 + }); 1.232 + 1.233 + // For write streams "finish" basically means close. 1.234 + on(this, "finish", _ => { 1.235 + this.destroy(); 1.236 + emit(this, "close"); 1.237 + }); 1.238 + }, 1.239 + destroy: function() { 1.240 + OutputStream.prototype.destroy.call(this); 1.241 + closeSync(this.fd); 1.242 + } 1.243 +}); 1.244 +exports.WriteStream = WriteStream; 1.245 +exports.createWriteStream = function createWriteStream(path, options) { 1.246 + return new WriteStream(path, options); 1.247 +}; 1.248 + 1.249 +const Stats = Class({ 1.250 + initialize: function initialize(path) { 1.251 + let file = new nsILocalFile(path); 1.252 + if (!file.exists()) throw FSError("stat", "ENOENT", 34, path); 1.253 + nsIFile(this, file); 1.254 + }, 1.255 + isDirectory: function() nsIFile(this).isDirectory(), 1.256 + isFile: function() nsIFile(this).isFile(), 1.257 + isSymbolicLink: function() nsIFile(this).isSymlink(), 1.258 + get mode() nsIFile(this).permissions, 1.259 + get size() nsIFile(this).fileSize, 1.260 + get mtime() nsIFile(this).lastModifiedTime, 1.261 + isBlockDevice: function() nsIFile(this).isSpecial(), 1.262 + isCharacterDevice: function() nsIFile(this).isSpecial(), 1.263 + isFIFO: function() nsIFile(this).isSpecial(), 1.264 + isSocket: function() nsIFile(this).isSpecial(), 1.265 + // non standard 1.266 + get exists() nsIFile(this).exists(), 1.267 + get hidden() nsIFile(this).isHidden(), 1.268 + get writable() nsIFile(this).isWritable(), 1.269 + get readable() nsIFile(this).isReadable() 1.270 +}); 1.271 +exports.Stats = Stats; 1.272 + 1.273 +const LStats = Class({ 1.274 + extends: Stats, 1.275 + get size() this.isSymbolicLink() ? nsIFile(this).fileSizeOfLink : 1.276 + nsIFile(this).fileSize, 1.277 + get mtime() this.isSymbolicLink() ? nsIFile(this).lastModifiedTimeOfLink : 1.278 + nsIFile(this).lastModifiedTime, 1.279 + // non standard 1.280 + get permissions() this.isSymbolicLink() ? nsIFile(this).permissionsOfLink : 1.281 + nsIFile(this).permissions 1.282 +}); 1.283 + 1.284 +const FStat = Class({ 1.285 + extends: Stats, 1.286 + initialize: function initialize(fd) { 1.287 + nsIFile(this, nsIFile(fd)); 1.288 + } 1.289 +}); 1.290 + 1.291 +function noop() {} 1.292 +function Async(wrapped) { 1.293 + return function (path, callback) { 1.294 + let args = Array.slice(arguments); 1.295 + callback = args.pop(); 1.296 + // If node is not given a callback argument 1.297 + // it just does not calls it. 1.298 + if (typeof(callback) !== "function") { 1.299 + args.push(callback); 1.300 + callback = noop; 1.301 + } 1.302 + setTimeout(function() { 1.303 + try { 1.304 + var result = wrapped.apply(this, args); 1.305 + if (result === undefined) callback(null); 1.306 + else callback(null, result); 1.307 + } catch (error) { 1.308 + callback(error); 1.309 + } 1.310 + }, 0); 1.311 + } 1.312 +} 1.313 + 1.314 + 1.315 +/** 1.316 + * Synchronous rename(2) 1.317 + */ 1.318 +function renameSync(oldPath, newPath) { 1.319 + let source = new nsILocalFile(oldPath); 1.320 + let target = new nsILocalFile(newPath); 1.321 + if (!source.exists()) throw FSError("rename", "ENOENT", 34, oldPath); 1.322 + return source.moveTo(target.parent, target.leafName); 1.323 +}; 1.324 +exports.renameSync = renameSync; 1.325 + 1.326 +/** 1.327 + * Asynchronous rename(2). No arguments other than a possible exception are 1.328 + * given to the completion callback. 1.329 + */ 1.330 +let rename = Async(renameSync); 1.331 +exports.rename = rename; 1.332 + 1.333 +/** 1.334 + * Test whether or not the given path exists by checking with the file system. 1.335 + */ 1.336 +function existsSync(path) { 1.337 + return new nsILocalFile(path).exists(); 1.338 +} 1.339 +exports.existsSync = existsSync; 1.340 + 1.341 +let exists = Async(existsSync); 1.342 +exports.exists = exists; 1.343 + 1.344 +/** 1.345 + * Synchronous ftruncate(2). 1.346 + */ 1.347 +function truncateSync(path, length) { 1.348 + let fd = openSync(path, "w"); 1.349 + ftruncateSync(fd, length); 1.350 + closeSync(fd); 1.351 +} 1.352 +exports.truncateSync = truncateSync; 1.353 + 1.354 +/** 1.355 + * Asynchronous ftruncate(2). No arguments other than a possible exception are 1.356 + * given to the completion callback. 1.357 + */ 1.358 +function truncate(path, length, callback) { 1.359 + open(path, "w", function(error, fd) { 1.360 + if (error) return callback(error); 1.361 + ftruncate(fd, length, function(error) { 1.362 + if (error) { 1.363 + closeSync(fd); 1.364 + callback(error); 1.365 + } 1.366 + else { 1.367 + close(fd, callback); 1.368 + } 1.369 + }); 1.370 + }); 1.371 +} 1.372 +exports.truncate = truncate; 1.373 + 1.374 +function ftruncate(fd, length, callback) { 1.375 + write(fd, new Buffer(length), 0, length, 0, function(error) { 1.376 + callback(error); 1.377 + }); 1.378 +} 1.379 +exports.ftruncate = ftruncate; 1.380 + 1.381 +function ftruncateSync(fd, length = 0) { 1.382 + writeSync(fd, new Buffer(length), 0, length, 0); 1.383 +} 1.384 +exports.ftruncateSync = ftruncateSync; 1.385 + 1.386 +function chownSync(path, uid, gid) { 1.387 + throw Error("Not implemented yet!!"); 1.388 +} 1.389 +exports.chownSync = chownSync; 1.390 + 1.391 +let chown = Async(chownSync); 1.392 +exports.chown = chown; 1.393 + 1.394 +function lchownSync(path, uid, gid) { 1.395 + throw Error("Not implemented yet!!"); 1.396 +} 1.397 +exports.lchownSync = chownSync; 1.398 + 1.399 +let lchown = Async(lchown); 1.400 +exports.lchown = lchown; 1.401 + 1.402 +/** 1.403 + * Synchronous chmod(2). 1.404 + */ 1.405 +function chmodSync (path, mode) { 1.406 + let file; 1.407 + try { 1.408 + file = new nsILocalFile(path); 1.409 + } catch(e) { 1.410 + throw FSError("chmod", "ENOENT", 34, path); 1.411 + } 1.412 + 1.413 + file.permissions = Mode(mode); 1.414 +} 1.415 +exports.chmodSync = chmodSync; 1.416 +/** 1.417 + * Asynchronous chmod(2). No arguments other than a possible exception are 1.418 + * given to the completion callback. 1.419 + */ 1.420 +let chmod = Async(chmodSync); 1.421 +exports.chmod = chmod; 1.422 + 1.423 +/** 1.424 + * Synchronous chmod(2). 1.425 + */ 1.426 +function fchmodSync(fd, mode) { 1.427 + throw Error("Not implemented yet!!"); 1.428 +}; 1.429 +exports.fchmodSync = fchmodSync; 1.430 +/** 1.431 + * Asynchronous chmod(2). No arguments other than a possible exception are 1.432 + * given to the completion callback. 1.433 + */ 1.434 +let fchmod = Async(fchmodSync); 1.435 +exports.fchmod = fchmod; 1.436 + 1.437 + 1.438 +/** 1.439 + * Synchronous stat(2). Returns an instance of `fs.Stats` 1.440 + */ 1.441 +function statSync(path) { 1.442 + return new Stats(path); 1.443 +}; 1.444 +exports.statSync = statSync; 1.445 + 1.446 +/** 1.447 + * Asynchronous stat(2). The callback gets two arguments (err, stats) where 1.448 + * stats is a `fs.Stats` object. It looks like this: 1.449 + */ 1.450 +let stat = Async(statSync); 1.451 +exports.stat = stat; 1.452 + 1.453 +/** 1.454 + * Synchronous lstat(2). Returns an instance of `fs.Stats`. 1.455 + */ 1.456 +function lstatSync(path) { 1.457 + return new LStats(path); 1.458 +}; 1.459 +exports.lstatSync = lstatSync; 1.460 + 1.461 +/** 1.462 + * Asynchronous lstat(2). The callback gets two arguments (err, stats) where 1.463 + * stats is a fs.Stats object. lstat() is identical to stat(), except that if 1.464 + * path is a symbolic link, then the link itself is stat-ed, not the file that 1.465 + * it refers to. 1.466 + */ 1.467 +let lstat = Async(lstatSync); 1.468 +exports.lstat = lstat; 1.469 + 1.470 +/** 1.471 + * Synchronous fstat(2). Returns an instance of `fs.Stats`. 1.472 + */ 1.473 +function fstatSync(fd) { 1.474 + return new FStat(fd); 1.475 +}; 1.476 +exports.fstatSync = fstatSync; 1.477 + 1.478 +/** 1.479 + * Asynchronous fstat(2). The callback gets two arguments (err, stats) where 1.480 + * stats is a fs.Stats object. 1.481 + */ 1.482 +let fstat = Async(fstatSync); 1.483 +exports.fstat = fstat; 1.484 + 1.485 +/** 1.486 + * Synchronous link(2). 1.487 + */ 1.488 +function linkSync(source, target) { 1.489 + throw Error("Not implemented yet!!"); 1.490 +}; 1.491 +exports.linkSync = linkSync; 1.492 + 1.493 +/** 1.494 + * Asynchronous link(2). No arguments other than a possible exception are given 1.495 + * to the completion callback. 1.496 + */ 1.497 +let link = Async(linkSync); 1.498 +exports.link = link; 1.499 + 1.500 +/** 1.501 + * Synchronous symlink(2). 1.502 + */ 1.503 +function symlinkSync(source, target) { 1.504 + throw Error("Not implemented yet!!"); 1.505 +}; 1.506 +exports.symlinkSync = symlinkSync; 1.507 + 1.508 +/** 1.509 + * Asynchronous symlink(2). No arguments other than a possible exception are 1.510 + * given to the completion callback. 1.511 + */ 1.512 +let symlink = Async(symlinkSync); 1.513 +exports.symlink = symlink; 1.514 + 1.515 +/** 1.516 + * Synchronous readlink(2). Returns the resolved path. 1.517 + */ 1.518 +function readlinkSync(path) { 1.519 + return new nsILocalFile(path).target; 1.520 +}; 1.521 +exports.readlinkSync = readlinkSync; 1.522 + 1.523 +/** 1.524 + * Asynchronous readlink(2). The callback gets two arguments 1.525 + * `(error, resolvedPath)`. 1.526 + */ 1.527 +let readlink = Async(readlinkSync); 1.528 +exports.readlink = readlink; 1.529 + 1.530 +/** 1.531 + * Synchronous realpath(2). Returns the resolved path. 1.532 + */ 1.533 +function realpathSync(path) { 1.534 + return new nsILocalFile(path).path; 1.535 +}; 1.536 +exports.realpathSync = realpathSync; 1.537 + 1.538 +/** 1.539 + * Asynchronous realpath(2). The callback gets two arguments 1.540 + * `(err, resolvedPath)`. 1.541 + */ 1.542 +let realpath = Async(realpathSync); 1.543 +exports.realpath = realpath; 1.544 + 1.545 +/** 1.546 + * Synchronous unlink(2). 1.547 + */ 1.548 +let unlinkSync = remove; 1.549 +exports.unlinkSync = unlinkSync; 1.550 + 1.551 +/** 1.552 + * Asynchronous unlink(2). No arguments other than a possible exception are 1.553 + * given to the completion callback. 1.554 + */ 1.555 +let unlink = Async(remove); 1.556 +exports.unlink = unlink; 1.557 + 1.558 +/** 1.559 + * Synchronous rmdir(2). 1.560 + */ 1.561 +let rmdirSync = remove; 1.562 +exports.rmdirSync = rmdirSync; 1.563 + 1.564 +/** 1.565 + * Asynchronous rmdir(2). No arguments other than a possible exception are 1.566 + * given to the completion callback. 1.567 + */ 1.568 +let rmdir = Async(rmdirSync); 1.569 +exports.rmdir = rmdir; 1.570 + 1.571 +/** 1.572 + * Synchronous mkdir(2). 1.573 + */ 1.574 +function mkdirSync(path, mode) { 1.575 + try { 1.576 + return nsILocalFile(path).create(DIRECTORY_TYPE, Mode(mode)); 1.577 + } catch (error) { 1.578 + // Adjust exception thorw to match ones thrown by node. 1.579 + if (error.name === "NS_ERROR_FILE_ALREADY_EXISTS") { 1.580 + let { fileName, lineNumber } = error; 1.581 + error = FSError("mkdir", "EEXIST", 47, path, fileName, lineNumber); 1.582 + } 1.583 + throw error; 1.584 + } 1.585 +}; 1.586 +exports.mkdirSync = mkdirSync; 1.587 + 1.588 +/** 1.589 + * Asynchronous mkdir(2). No arguments other than a possible exception are 1.590 + * given to the completion callback. 1.591 + */ 1.592 +let mkdir = Async(mkdirSync); 1.593 +exports.mkdir = mkdir; 1.594 + 1.595 +/** 1.596 + * Synchronous readdir(3). Returns an array of filenames excluding `"."` and 1.597 + * `".."`. 1.598 + */ 1.599 +function readdirSync(path) { 1.600 + try { 1.601 + return toArray(new nsILocalFile(path).directoryEntries).map(getFileName); 1.602 + } 1.603 + catch (error) { 1.604 + // Adjust exception thorw to match ones thrown by node. 1.605 + if (error.name === "NS_ERROR_FILE_TARGET_DOES_NOT_EXIST" || 1.606 + error.name === "NS_ERROR_FILE_NOT_FOUND") 1.607 + { 1.608 + let { fileName, lineNumber } = error; 1.609 + error = FSError("readdir", "ENOENT", 34, path, fileName, lineNumber); 1.610 + } 1.611 + throw error; 1.612 + } 1.613 +}; 1.614 +exports.readdirSync = readdirSync; 1.615 + 1.616 +/** 1.617 + * Asynchronous readdir(3). Reads the contents of a directory. The callback 1.618 + * gets two arguments `(error, files)` where `files` is an array of the names 1.619 + * of the files in the directory excluding `"."` and `".."`. 1.620 + */ 1.621 +let readdir = Async(readdirSync); 1.622 +exports.readdir = readdir; 1.623 + 1.624 +/** 1.625 + * Synchronous close(2). 1.626 + */ 1.627 + function closeSync(fd) { 1.628 + let input = nsIFileInputStream(fd); 1.629 + let output = nsIFileOutputStream(fd); 1.630 + 1.631 + // Closing input stream and removing reference. 1.632 + if (input) input.close(); 1.633 + // Closing output stream and removing reference. 1.634 + if (output) output.close(); 1.635 + 1.636 + nsIFile(fd, null); 1.637 + nsIFileInputStream(fd, null); 1.638 + nsIFileOutputStream(fd, null); 1.639 + nsIBinaryInputStream(fd, null); 1.640 + nsIBinaryOutputStream(fd, null); 1.641 +}; 1.642 +exports.closeSync = closeSync; 1.643 +/** 1.644 + * Asynchronous close(2). No arguments other than a possible exception are 1.645 + * given to the completion callback. 1.646 + */ 1.647 +let close = Async(closeSync); 1.648 +exports.close = close; 1.649 + 1.650 +/** 1.651 + * Synchronous open(2). 1.652 + */ 1.653 +function openSync(path, flags, mode) { 1.654 + let [ fd, flags, mode, file ] = 1.655 + [ { path: path }, Flags(flags), Mode(mode), nsILocalFile(path) ]; 1.656 + 1.657 + nsIFile(fd, file); 1.658 + 1.659 + // If trying to open file for just read that does not exists 1.660 + // need to throw exception as node does. 1.661 + if (!file.exists() && !isWritable(flags)) 1.662 + throw FSError("open", "ENOENT", 34, path); 1.663 + 1.664 + // If we want to open file in read mode we initialize input stream. 1.665 + if (isReadable(flags)) { 1.666 + let input = FileInputStream(file, flags, mode, DEFER_OPEN); 1.667 + nsIFileInputStream(fd, input); 1.668 + } 1.669 + 1.670 + // If we want to open file in write mode we initialize output stream for it. 1.671 + if (isWritable(flags)) { 1.672 + let output = FileOutputStream(file, flags, mode, DEFER_OPEN); 1.673 + nsIFileOutputStream(fd, output); 1.674 + } 1.675 + 1.676 + return fd; 1.677 +} 1.678 +exports.openSync = openSync; 1.679 +/** 1.680 + * Asynchronous file open. See open(2). Flags can be 1.681 + * `"r", "r+", "w", "w+", "a"`, or `"a+"`. mode defaults to `0666`. 1.682 + * The callback gets two arguments `(error, fd). 1.683 + */ 1.684 +let open = Async(openSync); 1.685 +exports.open = open; 1.686 + 1.687 +/** 1.688 + * Synchronous version of buffer-based fs.write(). Returns the number of bytes 1.689 + * written. 1.690 + */ 1.691 +function writeSync(fd, buffer, offset, length, position) { 1.692 + if (length + offset > buffer.length) { 1.693 + throw Error("Length is extends beyond buffer"); 1.694 + } 1.695 + else if (length + offset !== buffer.length) { 1.696 + buffer = buffer.slice(offset, offset + length); 1.697 + } 1.698 + let writeStream = new WriteStream(fd, { position: position, 1.699 + length: length }); 1.700 + 1.701 + let output = BinaryOutputStream(nsIFileOutputStream(fd)); 1.702 + nsIBinaryOutputStream(fd, output); 1.703 + // We write content as a byte array as this will avoid any transcoding 1.704 + // if content was a buffer. 1.705 + output.writeByteArray(buffer.valueOf(), buffer.length); 1.706 + output.flush(); 1.707 +}; 1.708 +exports.writeSync = writeSync; 1.709 + 1.710 +/** 1.711 + * Write buffer to the file specified by fd. 1.712 + * 1.713 + * `offset` and `length` determine the part of the buffer to be written. 1.714 + * 1.715 + * `position` refers to the offset from the beginning of the file where this 1.716 + * data should be written. If `position` is `null`, the data will be written 1.717 + * at the current position. See pwrite(2). 1.718 + * 1.719 + * The callback will be given three arguments `(error, written, buffer)` where 1.720 + * written specifies how many bytes were written into buffer. 1.721 + * 1.722 + * Note that it is unsafe to use `fs.write` multiple times on the same file 1.723 + * without waiting for the callback. 1.724 + */ 1.725 +function write(fd, buffer, offset, length, position, callback) { 1.726 + if (!Buffer.isBuffer(buffer)) { 1.727 + // (fd, data, position, encoding, callback) 1.728 + let encoding = null; 1.729 + [ position, encoding, callback ] = Array.slice(arguments, 1); 1.730 + buffer = new Buffer(String(buffer), encoding); 1.731 + offset = 0; 1.732 + } else if (length + offset > buffer.length) { 1.733 + throw Error("Length is extends beyond buffer"); 1.734 + } else if (length + offset !== buffer.length) { 1.735 + buffer = buffer.slice(offset, offset + length); 1.736 + } 1.737 + 1.738 + let writeStream = new WriteStream(fd, { position: position, 1.739 + length: length }); 1.740 + writeStream.on("error", callback); 1.741 + writeStream.write(buffer, function onEnd() { 1.742 + writeStream.destroy(); 1.743 + if (callback) 1.744 + callback(null, buffer.length, buffer); 1.745 + }); 1.746 +}; 1.747 +exports.write = write; 1.748 + 1.749 +/** 1.750 + * Synchronous version of string-based fs.read. Returns the number of 1.751 + * bytes read. 1.752 + */ 1.753 +function readSync(fd, buffer, offset, length, position) { 1.754 + let input = nsIFileInputStream(fd); 1.755 + // Setting a stream position, unless it"s `-1` which means current position. 1.756 + if (position >= 0) 1.757 + input.QueryInterface(Ci.nsISeekableStream).seek(NS_SEEK_SET, position); 1.758 + // We use `nsIStreamTransportService` service to transform blocking 1.759 + // file input stream into a fully asynchronous stream that can be written 1.760 + // without blocking the main thread. 1.761 + let binaryInputStream = BinaryInputStream(input); 1.762 + let count = length === ALL ? binaryInputStream.available() : length; 1.763 + if (offset === 0) binaryInputStream.readArrayBuffer(count, buffer.buffer); 1.764 + else { 1.765 + let chunk = new Buffer(count); 1.766 + binaryInputStream.readArrayBuffer(count, chunk.buffer); 1.767 + chunk.copy(buffer, offset); 1.768 + } 1.769 + 1.770 + return buffer.slice(offset, offset + count); 1.771 +}; 1.772 +exports.readSync = readSync; 1.773 + 1.774 +/** 1.775 + * Read data from the file specified by `fd`. 1.776 + * 1.777 + * `buffer` is the buffer that the data will be written to. 1.778 + * `offset` is offset within the buffer where writing will start. 1.779 + * 1.780 + * `length` is an integer specifying the number of bytes to read. 1.781 + * 1.782 + * `position` is an integer specifying where to begin reading from in the file. 1.783 + * If `position` is `null`, data will be read from the current file position. 1.784 + * 1.785 + * The callback is given the three arguments, `(error, bytesRead, buffer)`. 1.786 + */ 1.787 +function read(fd, buffer, offset, length, position, callback) { 1.788 + let bytesRead = 0; 1.789 + let readStream = new ReadStream(fd, { position: position, length: length }); 1.790 + readStream.on("data", function onData(data) { 1.791 + data.copy(buffer, offset + bytesRead); 1.792 + bytesRead += data.length; 1.793 + }); 1.794 + readStream.on("end", function onEnd() { 1.795 + callback(null, bytesRead, buffer); 1.796 + readStream.destroy(); 1.797 + }); 1.798 +}; 1.799 +exports.read = read; 1.800 + 1.801 +/** 1.802 + * Asynchronously reads the entire contents of a file. 1.803 + * The callback is passed two arguments `(error, data)`, where data is the 1.804 + * contents of the file. 1.805 + */ 1.806 +function readFile(path, encoding, callback) { 1.807 + if (isFunction(encoding)) { 1.808 + callback = encoding 1.809 + encoding = null 1.810 + } 1.811 + 1.812 + let buffer = null; 1.813 + try { 1.814 + let readStream = new ReadStream(path); 1.815 + readStream.on("data", function(data) { 1.816 + if (!buffer) buffer = data; 1.817 + else buffer = Buffer.concat([buffer, data], 2); 1.818 + }); 1.819 + readStream.on("error", function onError(error) { 1.820 + callback(error); 1.821 + }); 1.822 + readStream.on("end", function onEnd() { 1.823 + // Note: Need to destroy before invoking a callback 1.824 + // so that file descriptor is released. 1.825 + readStream.destroy(); 1.826 + callback(null, buffer); 1.827 + }); 1.828 + } catch (error) { 1.829 + setTimeout(callback, 0, error); 1.830 + } 1.831 +}; 1.832 +exports.readFile = readFile; 1.833 + 1.834 +/** 1.835 + * Synchronous version of `fs.readFile`. Returns the contents of the path. 1.836 + * If encoding is specified then this function returns a string. 1.837 + * Otherwise it returns a buffer. 1.838 + */ 1.839 +function readFileSync(path, encoding) { 1.840 + let fd = openSync(path, "r"); 1.841 + let size = fstatSync(fd).size; 1.842 + let buffer = new Buffer(size); 1.843 + try { 1.844 + readSync(fd, buffer, 0, ALL, 0); 1.845 + } 1.846 + finally { 1.847 + closeSync(fd); 1.848 + } 1.849 + return buffer; 1.850 +}; 1.851 +exports.readFileSync = readFileSync; 1.852 + 1.853 +/** 1.854 + * Asynchronously writes data to a file, replacing the file if it already 1.855 + * exists. data can be a string or a buffer. 1.856 + */ 1.857 +function writeFile(path, content, encoding, callback) { 1.858 + if (!isString(path)) 1.859 + throw new TypeError('path must be a string'); 1.860 + 1.861 + try { 1.862 + if (isFunction(encoding)) { 1.863 + callback = encoding 1.864 + encoding = null 1.865 + } 1.866 + if (isString(content)) 1.867 + content = new Buffer(content, encoding); 1.868 + 1.869 + let writeStream = new WriteStream(path); 1.870 + let error = null; 1.871 + 1.872 + writeStream.end(content, function() { 1.873 + writeStream.destroy(); 1.874 + callback(error); 1.875 + }); 1.876 + 1.877 + writeStream.on("error", function onError(reason) { 1.878 + error = reason; 1.879 + writeStream.destroy(); 1.880 + }); 1.881 + } catch (error) { 1.882 + callback(error); 1.883 + } 1.884 +}; 1.885 +exports.writeFile = writeFile; 1.886 + 1.887 +/** 1.888 + * The synchronous version of `fs.writeFile`. 1.889 + */ 1.890 +function writeFileSync(filename, data, encoding) { 1.891 + throw Error("Not implemented"); 1.892 +}; 1.893 +exports.writeFileSync = writeFileSync; 1.894 + 1.895 + 1.896 +function utimesSync(path, atime, mtime) { 1.897 + throw Error("Not implemented"); 1.898 +} 1.899 +exports.utimesSync = utimesSync; 1.900 + 1.901 +let utimes = Async(utimesSync); 1.902 +exports.utimes = utimes; 1.903 + 1.904 +function futimesSync(fd, atime, mtime, callback) { 1.905 + throw Error("Not implemented"); 1.906 +} 1.907 +exports.futimesSync = futimesSync; 1.908 + 1.909 +let futimes = Async(futimesSync); 1.910 +exports.futimes = futimes; 1.911 + 1.912 +function fsyncSync(fd, atime, mtime, callback) { 1.913 + throw Error("Not implemented"); 1.914 +} 1.915 +exports.fsyncSync = fsyncSync; 1.916 + 1.917 +let fsync = Async(fsyncSync); 1.918 +exports.fsync = fsync; 1.919 + 1.920 + 1.921 +/** 1.922 + * Watch for changes on filename. The callback listener will be called each 1.923 + * time the file is accessed. 1.924 + * 1.925 + * The second argument is optional. The options if provided should be an object 1.926 + * containing two members a boolean, persistent, and interval, a polling value 1.927 + * in milliseconds. The default is { persistent: true, interval: 0 }. 1.928 + */ 1.929 +function watchFile(path, options, listener) { 1.930 + throw Error("Not implemented"); 1.931 +}; 1.932 +exports.watchFile = watchFile; 1.933 + 1.934 + 1.935 +function unwatchFile(path, listener) { 1.936 + throw Error("Not implemented"); 1.937 +} 1.938 +exports.unwatchFile = unwatchFile; 1.939 + 1.940 +function watch(path, options, listener) { 1.941 + throw Error("Not implemented"); 1.942 +} 1.943 +exports.watch = watch;