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

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     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;

mercurial