addon-sdk/source/lib/sdk/io/fs.js

changeset 0
6474c204b198
     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;

mercurial