mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-17 04:37:46 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
22
toolkit/components/osfile/modules/moz.build
Normal file
22
toolkit/components/osfile/modules/moz.build
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXTRA_JS_MODULES.osfile += [
|
||||
'osfile_async_front.jsm',
|
||||
'osfile_async_worker.js',
|
||||
'osfile_native.jsm',
|
||||
'osfile_shared_allthreads.jsm',
|
||||
'osfile_shared_front.jsm',
|
||||
'osfile_unix_allthreads.jsm',
|
||||
'osfile_unix_back.jsm',
|
||||
'osfile_unix_front.jsm',
|
||||
'osfile_win_allthreads.jsm',
|
||||
'osfile_win_back.jsm',
|
||||
'osfile_win_front.jsm',
|
||||
'ospath.jsm',
|
||||
'ospath_unix.jsm',
|
||||
'ospath_win.jsm',
|
||||
]
|
||||
1533
toolkit/components/osfile/modules/osfile_async_front.jsm
Normal file
1533
toolkit/components/osfile/modules/osfile_async_front.jsm
Normal file
File diff suppressed because it is too large
Load diff
407
toolkit/components/osfile/modules/osfile_async_worker.js
Normal file
407
toolkit/components/osfile/modules/osfile_async_worker.js
Normal file
|
|
@ -0,0 +1,407 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
if (this.Components) {
|
||||
throw new Error("This worker can only be loaded from a worker thread");
|
||||
}
|
||||
|
||||
// Worker thread for osfile asynchronous front-end
|
||||
|
||||
(function(exports) {
|
||||
"use strict";
|
||||
|
||||
// Timestamps, for use in Telemetry.
|
||||
// The object is set to |null| once it has been sent
|
||||
// to the main thread.
|
||||
let timeStamps = {
|
||||
entered: Date.now(),
|
||||
loaded: null
|
||||
};
|
||||
|
||||
importScripts("resource://gre/modules/osfile.jsm");
|
||||
|
||||
let PromiseWorker = require("resource://gre/modules/workers/PromiseWorker.js");
|
||||
let SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
|
||||
let LOG = SharedAll.LOG.bind(SharedAll, "Agent");
|
||||
|
||||
let worker = new PromiseWorker.AbstractWorker();
|
||||
worker.dispatch = function(method, args = []) {
|
||||
return Agent[method](...args);
|
||||
},
|
||||
worker.log = LOG;
|
||||
worker.postMessage = function(message, ...transfers) {
|
||||
if (timeStamps) {
|
||||
message.timeStamps = timeStamps;
|
||||
timeStamps = null;
|
||||
}
|
||||
self.postMessage(message, ...transfers);
|
||||
};
|
||||
worker.close = function() {
|
||||
self.close();
|
||||
};
|
||||
let Meta = PromiseWorker.Meta;
|
||||
|
||||
self.addEventListener("message", msg => worker.handleMessage(msg));
|
||||
|
||||
/**
|
||||
* A data structure used to track opened resources
|
||||
*/
|
||||
let ResourceTracker = function ResourceTracker() {
|
||||
// A number used to generate ids
|
||||
this._idgen = 0;
|
||||
// A map from id to resource
|
||||
this._map = new Map();
|
||||
};
|
||||
ResourceTracker.prototype = {
|
||||
/**
|
||||
* Get a resource from its unique identifier.
|
||||
*/
|
||||
get: function(id) {
|
||||
let result = this._map.get(id);
|
||||
if (result == null) {
|
||||
return result;
|
||||
}
|
||||
return result.resource;
|
||||
},
|
||||
/**
|
||||
* Remove a resource from its unique identifier.
|
||||
*/
|
||||
remove: function(id) {
|
||||
if (!this._map.has(id)) {
|
||||
throw new Error("Cannot find resource id " + id);
|
||||
}
|
||||
this._map.delete(id);
|
||||
},
|
||||
/**
|
||||
* Add a resource, return a new unique identifier
|
||||
*
|
||||
* @param {*} resource A resource.
|
||||
* @param {*=} info Optional information. For debugging purposes.
|
||||
*
|
||||
* @return {*} A unique identifier. For the moment, this is a number,
|
||||
* but this might not remain the case forever.
|
||||
*/
|
||||
add: function(resource, info) {
|
||||
let id = this._idgen++;
|
||||
this._map.set(id, {resource:resource, info:info});
|
||||
return id;
|
||||
},
|
||||
/**
|
||||
* Return a list of all open resources i.e. the ones still present in
|
||||
* ResourceTracker's _map.
|
||||
*/
|
||||
listOpenedResources: function listOpenedResources() {
|
||||
return Array.from(this._map, ([id, resource]) => resource.info.path);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A map of unique identifiers to opened files.
|
||||
*/
|
||||
let OpenedFiles = new ResourceTracker();
|
||||
|
||||
/**
|
||||
* Execute a function in the context of a given file.
|
||||
*
|
||||
* @param {*} id A unique identifier, as used by |OpenFiles|.
|
||||
* @param {Function} f A function to call.
|
||||
* @param {boolean} ignoreAbsent If |true|, the error is ignored. Otherwise, the error causes an exception.
|
||||
* @return The return value of |f()|
|
||||
*
|
||||
* This function attempts to get the file matching |id|. If
|
||||
* the file exists, it executes |f| within the |this| set
|
||||
* to the corresponding file. Otherwise, it throws an error.
|
||||
*/
|
||||
let withFile = function withFile(id, f, ignoreAbsent) {
|
||||
let file = OpenedFiles.get(id);
|
||||
if (file == null) {
|
||||
if (!ignoreAbsent) {
|
||||
throw OS.File.Error.closed("accessing file");
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
return f.call(file);
|
||||
};
|
||||
|
||||
let OpenedDirectoryIterators = new ResourceTracker();
|
||||
let withDir = function withDir(fd, f, ignoreAbsent) {
|
||||
let file = OpenedDirectoryIterators.get(fd);
|
||||
if (file == null) {
|
||||
if (!ignoreAbsent) {
|
||||
throw OS.File.Error.closed("accessing directory");
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
if (!(file instanceof File.DirectoryIterator)) {
|
||||
throw new Error("file is not a directory iterator " + file.__proto__.toSource());
|
||||
}
|
||||
return f.call(file);
|
||||
};
|
||||
|
||||
let Type = exports.OS.Shared.Type;
|
||||
|
||||
let File = exports.OS.File;
|
||||
|
||||
/**
|
||||
* The agent.
|
||||
*
|
||||
* It is in charge of performing method-specific deserialization
|
||||
* of messages, calling the function/method of OS.File and serializing
|
||||
* back the results.
|
||||
*/
|
||||
let Agent = {
|
||||
// Update worker's OS.Shared.DEBUG flag message from controller.
|
||||
SET_DEBUG: function(aDEBUG) {
|
||||
SharedAll.Config.DEBUG = aDEBUG;
|
||||
},
|
||||
// Return worker's current OS.Shared.DEBUG value to controller.
|
||||
// Note: This is used for testing purposes.
|
||||
GET_DEBUG: function() {
|
||||
return SharedAll.Config.DEBUG;
|
||||
},
|
||||
/**
|
||||
* Execute shutdown sequence, returning data on leaked file descriptors.
|
||||
*
|
||||
* @param {bool} If |true|, kill the worker if this would not cause
|
||||
* leaks.
|
||||
*/
|
||||
Meta_shutdown: function(kill) {
|
||||
let result = {
|
||||
openedFiles: OpenedFiles.listOpenedResources(),
|
||||
openedDirectoryIterators: OpenedDirectoryIterators.listOpenedResources(),
|
||||
killed: false // Placeholder
|
||||
};
|
||||
|
||||
// Is it safe to kill the worker?
|
||||
let safe = result.openedFiles.length == 0
|
||||
&& result.openedDirectoryIterators.length == 0;
|
||||
result.killed = safe && kill;
|
||||
|
||||
return new Meta(result, {shutdown: result.killed});
|
||||
},
|
||||
// Functions of OS.File
|
||||
stat: function stat(path, options) {
|
||||
return exports.OS.File.Info.toMsg(
|
||||
exports.OS.File.stat(Type.path.fromMsg(path), options));
|
||||
},
|
||||
setPermissions: function setPermissions(path, options = {}) {
|
||||
return exports.OS.File.setPermissions(Type.path.fromMsg(path), options);
|
||||
},
|
||||
setDates: function setDates(path, accessDate, modificationDate) {
|
||||
return exports.OS.File.setDates(Type.path.fromMsg(path), accessDate,
|
||||
modificationDate);
|
||||
},
|
||||
getCurrentDirectory: function getCurrentDirectory() {
|
||||
return exports.OS.Shared.Type.path.toMsg(File.getCurrentDirectory());
|
||||
},
|
||||
setCurrentDirectory: function setCurrentDirectory(path) {
|
||||
File.setCurrentDirectory(exports.OS.Shared.Type.path.fromMsg(path));
|
||||
},
|
||||
copy: function copy(sourcePath, destPath, options) {
|
||||
return File.copy(Type.path.fromMsg(sourcePath),
|
||||
Type.path.fromMsg(destPath), options);
|
||||
},
|
||||
move: function move(sourcePath, destPath, options) {
|
||||
return File.move(Type.path.fromMsg(sourcePath),
|
||||
Type.path.fromMsg(destPath), options);
|
||||
},
|
||||
getAvailableFreeSpace: function getAvailableFreeSpace(sourcePath) {
|
||||
return Type.uint64_t.toMsg(
|
||||
File.getAvailableFreeSpace(Type.path.fromMsg(sourcePath)));
|
||||
},
|
||||
makeDir: function makeDir(path, options) {
|
||||
return File.makeDir(Type.path.fromMsg(path), options);
|
||||
},
|
||||
removeEmptyDir: function removeEmptyDir(path, options) {
|
||||
return File.removeEmptyDir(Type.path.fromMsg(path), options);
|
||||
},
|
||||
remove: function remove(path, options) {
|
||||
return File.remove(Type.path.fromMsg(path), options);
|
||||
},
|
||||
open: function open(path, mode, options) {
|
||||
let filePath = Type.path.fromMsg(path);
|
||||
let file = File.open(filePath, mode, options);
|
||||
return OpenedFiles.add(file, {
|
||||
// Adding path information to keep track of opened files
|
||||
// to report leaks when debugging.
|
||||
path: filePath
|
||||
});
|
||||
},
|
||||
openUnique: function openUnique(path, options) {
|
||||
let filePath = Type.path.fromMsg(path);
|
||||
let openedFile = OS.Shared.AbstractFile.openUnique(filePath, options);
|
||||
let resourceId = OpenedFiles.add(openedFile.file, {
|
||||
// Adding path information to keep track of opened files
|
||||
// to report leaks when debugging.
|
||||
path: openedFile.path
|
||||
});
|
||||
|
||||
return {
|
||||
path: openedFile.path,
|
||||
file: resourceId
|
||||
};
|
||||
},
|
||||
read: function read(path, bytes, options) {
|
||||
let data = File.read(Type.path.fromMsg(path), bytes, options);
|
||||
if (typeof data == "string") {
|
||||
return data;
|
||||
}
|
||||
return new Meta({
|
||||
buffer: data.buffer,
|
||||
byteOffset: data.byteOffset,
|
||||
byteLength: data.byteLength
|
||||
}, {
|
||||
transfers: [data.buffer]
|
||||
});
|
||||
},
|
||||
exists: function exists(path) {
|
||||
return File.exists(Type.path.fromMsg(path));
|
||||
},
|
||||
writeAtomic: function writeAtomic(path, buffer, options) {
|
||||
if (options.tmpPath) {
|
||||
options.tmpPath = Type.path.fromMsg(options.tmpPath);
|
||||
}
|
||||
return File.writeAtomic(Type.path.fromMsg(path),
|
||||
Type.voidptr_t.fromMsg(buffer),
|
||||
options
|
||||
);
|
||||
},
|
||||
removeDir: function(path, options) {
|
||||
return File.removeDir(Type.path.fromMsg(path), options);
|
||||
},
|
||||
new_DirectoryIterator: function new_DirectoryIterator(path, options) {
|
||||
let directoryPath = Type.path.fromMsg(path);
|
||||
let iterator = new File.DirectoryIterator(directoryPath, options);
|
||||
return OpenedDirectoryIterators.add(iterator, {
|
||||
// Adding path information to keep track of opened directory
|
||||
// iterators to report leaks when debugging.
|
||||
path: directoryPath
|
||||
});
|
||||
},
|
||||
// Methods of OS.File
|
||||
File_prototype_close: function close(fd) {
|
||||
return withFile(fd,
|
||||
function do_close() {
|
||||
try {
|
||||
return this.close();
|
||||
} finally {
|
||||
OpenedFiles.remove(fd);
|
||||
}
|
||||
});
|
||||
},
|
||||
File_prototype_stat: function stat(fd) {
|
||||
return withFile(fd,
|
||||
function do_stat() {
|
||||
return exports.OS.File.Info.toMsg(this.stat());
|
||||
});
|
||||
},
|
||||
File_prototype_setPermissions: function setPermissions(fd, options = {}) {
|
||||
return withFile(fd,
|
||||
function do_setPermissions() {
|
||||
return this.setPermissions(options);
|
||||
});
|
||||
},
|
||||
File_prototype_setDates: function setDates(fd, accessTime, modificationTime) {
|
||||
return withFile(fd,
|
||||
function do_setDates() {
|
||||
return this.setDates(accessTime, modificationTime);
|
||||
});
|
||||
},
|
||||
File_prototype_read: function read(fd, nbytes, options) {
|
||||
return withFile(fd,
|
||||
function do_read() {
|
||||
let data = this.read(nbytes, options);
|
||||
return new Meta({
|
||||
buffer: data.buffer,
|
||||
byteOffset: data.byteOffset,
|
||||
byteLength: data.byteLength
|
||||
}, {
|
||||
transfers: [data.buffer]
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
File_prototype_readTo: function readTo(fd, buffer, options) {
|
||||
return withFile(fd,
|
||||
function do_readTo() {
|
||||
return this.readTo(exports.OS.Shared.Type.voidptr_t.fromMsg(buffer),
|
||||
options);
|
||||
});
|
||||
},
|
||||
File_prototype_write: function write(fd, buffer, options) {
|
||||
return withFile(fd,
|
||||
function do_write() {
|
||||
return this.write(exports.OS.Shared.Type.voidptr_t.fromMsg(buffer),
|
||||
options);
|
||||
});
|
||||
},
|
||||
File_prototype_setPosition: function setPosition(fd, pos, whence) {
|
||||
return withFile(fd,
|
||||
function do_setPosition() {
|
||||
return this.setPosition(pos, whence);
|
||||
});
|
||||
},
|
||||
File_prototype_getPosition: function getPosition(fd) {
|
||||
return withFile(fd,
|
||||
function do_getPosition() {
|
||||
return this.getPosition();
|
||||
});
|
||||
},
|
||||
File_prototype_flush: function flush(fd) {
|
||||
return withFile(fd,
|
||||
function do_flush() {
|
||||
return this.flush();
|
||||
});
|
||||
},
|
||||
// Methods of OS.File.DirectoryIterator
|
||||
DirectoryIterator_prototype_next: function next(dir) {
|
||||
return withDir(dir,
|
||||
function do_next() {
|
||||
try {
|
||||
return File.DirectoryIterator.Entry.toMsg(this.next());
|
||||
} catch (x) {
|
||||
if (x == StopIteration) {
|
||||
OpenedDirectoryIterators.remove(dir);
|
||||
}
|
||||
throw x;
|
||||
}
|
||||
}, false);
|
||||
},
|
||||
DirectoryIterator_prototype_nextBatch: function nextBatch(dir, size) {
|
||||
return withDir(dir,
|
||||
function do_nextBatch() {
|
||||
let result;
|
||||
try {
|
||||
result = this.nextBatch(size);
|
||||
} catch (x) {
|
||||
OpenedDirectoryIterators.remove(dir);
|
||||
throw x;
|
||||
}
|
||||
return result.map(File.DirectoryIterator.Entry.toMsg);
|
||||
}, false);
|
||||
},
|
||||
DirectoryIterator_prototype_close: function close(dir) {
|
||||
return withDir(dir,
|
||||
function do_close() {
|
||||
this.close();
|
||||
OpenedDirectoryIterators.remove(dir);
|
||||
}, true);// ignore error to support double-closing |DirectoryIterator|
|
||||
},
|
||||
DirectoryIterator_prototype_exists: function exists(dir) {
|
||||
return withDir(dir,
|
||||
function do_exists() {
|
||||
return this.exists();
|
||||
});
|
||||
}
|
||||
};
|
||||
if (!SharedAll.Constants.Win) {
|
||||
Agent.unixSymLink = function unixSymLink(sourcePath, destPath) {
|
||||
return File.unixSymLink(Type.path.fromMsg(sourcePath),
|
||||
Type.path.fromMsg(destPath));
|
||||
};
|
||||
}
|
||||
|
||||
timeStamps.loaded = Date.now();
|
||||
})(this);
|
||||
70
toolkit/components/osfile/modules/osfile_native.jsm
Normal file
70
toolkit/components/osfile/modules/osfile_native.jsm
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Native (xpcom) implementation of key OS.File functions
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["read"];
|
||||
|
||||
var {results: Cr, utils: Cu, interfaces: Ci} = Components;
|
||||
|
||||
var SharedAll = Cu.import("resource://gre/modules/osfile/osfile_shared_allthreads.jsm", {});
|
||||
|
||||
var SysAll = {};
|
||||
if (SharedAll.Constants.Win) {
|
||||
Cu.import("resource://gre/modules/osfile/osfile_win_allthreads.jsm", SysAll);
|
||||
} else if (SharedAll.Constants.libc) {
|
||||
Cu.import("resource://gre/modules/osfile/osfile_unix_allthreads.jsm", SysAll);
|
||||
} else {
|
||||
throw new Error("I am neither under Windows nor under a Posix system");
|
||||
}
|
||||
var {Promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
var {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
|
||||
|
||||
/**
|
||||
* The native service holding the implementation of the functions.
|
||||
*/
|
||||
XPCOMUtils.defineLazyServiceGetter(this,
|
||||
"Internals",
|
||||
"@mozilla.org/toolkit/osfile/native-internals;1",
|
||||
"nsINativeOSFileInternalsService");
|
||||
|
||||
/**
|
||||
* Native implementation of OS.File.read
|
||||
*
|
||||
* This implementation does not handle option |compression|.
|
||||
*/
|
||||
this.read = function(path, options = {}) {
|
||||
// Sanity check on types of options
|
||||
if ("encoding" in options && typeof options.encoding != "string") {
|
||||
return Promise.reject(new TypeError("Invalid type for option encoding"));
|
||||
}
|
||||
if ("compression" in options && typeof options.compression != "string") {
|
||||
return Promise.reject(new TypeError("Invalid type for option compression"));
|
||||
}
|
||||
if ("bytes" in options && typeof options.bytes != "number") {
|
||||
return Promise.reject(new TypeError("Invalid type for option bytes"));
|
||||
}
|
||||
|
||||
let deferred = Promise.defer();
|
||||
Internals.read(path,
|
||||
options,
|
||||
function onSuccess(success) {
|
||||
success.QueryInterface(Ci.nsINativeOSFileResult);
|
||||
if ("outExecutionDuration" in options) {
|
||||
options.outExecutionDuration =
|
||||
success.executionDurationMS +
|
||||
(options.outExecutionDuration || 0);
|
||||
}
|
||||
deferred.resolve(success.result);
|
||||
},
|
||||
function onError(operation, oserror) {
|
||||
deferred.reject(new SysAll.Error(operation, oserror, path));
|
||||
}
|
||||
);
|
||||
return deferred.promise;
|
||||
};
|
||||
1315
toolkit/components/osfile/modules/osfile_shared_allthreads.jsm
Normal file
1315
toolkit/components/osfile/modules/osfile_shared_allthreads.jsm
Normal file
File diff suppressed because it is too large
Load diff
567
toolkit/components/osfile/modules/osfile_shared_front.jsm
Normal file
567
toolkit/components/osfile/modules/osfile_shared_front.jsm
Normal file
|
|
@ -0,0 +1,567 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Code shared by OS.File front-ends.
|
||||
*
|
||||
* This code is meant to be included by another library. It is also meant to
|
||||
* be executed only on a worker thread.
|
||||
*/
|
||||
|
||||
if (typeof Components != "undefined") {
|
||||
throw new Error("osfile_shared_front.jsm cannot be used from the main thread");
|
||||
}
|
||||
(function(exports) {
|
||||
|
||||
var SharedAll =
|
||||
require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
|
||||
var Path = require("resource://gre/modules/osfile/ospath.jsm");
|
||||
var Lz4 =
|
||||
require("resource://gre/modules/lz4.js");
|
||||
var LOG = SharedAll.LOG.bind(SharedAll, "Shared front-end");
|
||||
var clone = SharedAll.clone;
|
||||
|
||||
/**
|
||||
* Code shared by implementations of File.
|
||||
*
|
||||
* @param {*} fd An OS-specific file handle.
|
||||
* @param {string} path File path of the file handle, used for error-reporting.
|
||||
* @constructor
|
||||
*/
|
||||
var AbstractFile = function AbstractFile(fd, path) {
|
||||
this._fd = fd;
|
||||
if (!path) {
|
||||
throw new TypeError("path is expected");
|
||||
}
|
||||
this._path = path;
|
||||
};
|
||||
|
||||
AbstractFile.prototype = {
|
||||
/**
|
||||
* Return the file handle.
|
||||
*
|
||||
* @throw OS.File.Error if the file has been closed.
|
||||
*/
|
||||
get fd() {
|
||||
if (this._fd) {
|
||||
return this._fd;
|
||||
}
|
||||
throw OS.File.Error.closed("accessing file", this._path);
|
||||
},
|
||||
/**
|
||||
* Read bytes from this file to a new buffer.
|
||||
*
|
||||
* @param {number=} maybeBytes (deprecated, please use options.bytes)
|
||||
* @param {JSON} options
|
||||
* @return {Uint8Array} An array containing the bytes read.
|
||||
*/
|
||||
read: function read(maybeBytes, options = {}) {
|
||||
if (typeof maybeBytes === "object") {
|
||||
// Caller has skipped `maybeBytes` and provided an options object.
|
||||
options = clone(maybeBytes);
|
||||
maybeBytes = null;
|
||||
} else {
|
||||
options = options || {};
|
||||
}
|
||||
let bytes = options.bytes || undefined;
|
||||
if (bytes === undefined) {
|
||||
bytes = maybeBytes == null ? this.stat().size : maybeBytes;
|
||||
}
|
||||
let buffer = new Uint8Array(bytes);
|
||||
let pos = 0;
|
||||
while (pos < bytes) {
|
||||
let length = bytes - pos;
|
||||
let view = new DataView(buffer.buffer, pos, length);
|
||||
let chunkSize = this._read(view, length, options);
|
||||
if (chunkSize == 0) {
|
||||
break;
|
||||
}
|
||||
pos += chunkSize;
|
||||
}
|
||||
if (pos == bytes) {
|
||||
return buffer;
|
||||
} else {
|
||||
return buffer.subarray(0, pos);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Write bytes from a buffer to this file.
|
||||
*
|
||||
* Note that, by default, this function may perform several I/O
|
||||
* operations to ensure that the buffer is fully written.
|
||||
*
|
||||
* @param {Typed array} buffer The buffer in which the the bytes are
|
||||
* stored. The buffer must be large enough to accomodate |bytes| bytes.
|
||||
* @param {*=} options Optionally, an object that may contain the
|
||||
* following fields:
|
||||
* - {number} bytes The number of |bytes| to write from the buffer. If
|
||||
* unspecified, this is |buffer.byteLength|.
|
||||
*
|
||||
* @return {number} The number of bytes actually written.
|
||||
*/
|
||||
write: function write(buffer, options = {}) {
|
||||
let bytes =
|
||||
SharedAll.normalizeBufferArgs(buffer, ("bytes" in options) ? options.bytes : undefined);
|
||||
let pos = 0;
|
||||
while (pos < bytes) {
|
||||
let length = bytes - pos;
|
||||
let view = new DataView(buffer.buffer, buffer.byteOffset + pos, length);
|
||||
let chunkSize = this._write(view, length, options);
|
||||
pos += chunkSize;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates and opens a file with a unique name. By default, generate a random HEX number and use it to create a unique new file name.
|
||||
*
|
||||
* @param {string} path The path to the file.
|
||||
* @param {*=} options Additional options for file opening. This
|
||||
* implementation interprets the following fields:
|
||||
*
|
||||
* - {number} humanReadable If |true|, create a new filename appending a decimal number. ie: filename-1.ext, filename-2.ext.
|
||||
* If |false| use HEX numbers ie: filename-A65BC0.ext
|
||||
* - {number} maxReadableNumber Used to limit the amount of tries after a failed
|
||||
* file creation. Default is 20.
|
||||
*
|
||||
* @return {Object} contains A file object{file} and the path{path}.
|
||||
* @throws {OS.File.Error} If the file could not be opened.
|
||||
*/
|
||||
AbstractFile.openUnique = function openUnique(path, options = {}) {
|
||||
let mode = {
|
||||
create : true
|
||||
};
|
||||
|
||||
let dirName = Path.dirname(path);
|
||||
let leafName = Path.basename(path);
|
||||
let lastDotCharacter = leafName.lastIndexOf('.');
|
||||
let fileName = leafName.substring(0, lastDotCharacter != -1 ? lastDotCharacter : leafName.length);
|
||||
let suffix = (lastDotCharacter != -1 ? leafName.substring(lastDotCharacter) : "");
|
||||
let uniquePath = "";
|
||||
let maxAttempts = options.maxAttempts || 99;
|
||||
let humanReadable = !!options.humanReadable;
|
||||
const HEX_RADIX = 16;
|
||||
// We produce HEX numbers between 0 and 2^24 - 1.
|
||||
const MAX_HEX_NUMBER = 16777215;
|
||||
|
||||
try {
|
||||
return {
|
||||
path: path,
|
||||
file: OS.File.open(path, mode)
|
||||
};
|
||||
} catch (ex if ex instanceof OS.File.Error && ex.becauseExists) {
|
||||
for (let i = 0; i < maxAttempts; ++i) {
|
||||
try {
|
||||
if (humanReadable) {
|
||||
uniquePath = Path.join(dirName, fileName + "-" + (i + 1) + suffix);
|
||||
} else {
|
||||
let hexNumber = Math.floor(Math.random() * MAX_HEX_NUMBER).toString(HEX_RADIX);
|
||||
uniquePath = Path.join(dirName, fileName + "-" + hexNumber + suffix);
|
||||
}
|
||||
return {
|
||||
path: uniquePath,
|
||||
file: OS.File.open(uniquePath, mode)
|
||||
};
|
||||
} catch (ex if ex instanceof OS.File.Error && ex.becauseExists) {
|
||||
// keep trying ...
|
||||
}
|
||||
}
|
||||
throw OS.File.Error.exists("could not find an unused file name.", path);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Code shared by iterators.
|
||||
*/
|
||||
AbstractFile.AbstractIterator = function AbstractIterator() {
|
||||
};
|
||||
AbstractFile.AbstractIterator.prototype = {
|
||||
/**
|
||||
* Allow iterating with |for|
|
||||
*/
|
||||
__iterator__: function __iterator__() {
|
||||
return this;
|
||||
},
|
||||
/**
|
||||
* Apply a function to all elements of the directory sequentially.
|
||||
*
|
||||
* @param {Function} cb This function will be applied to all entries
|
||||
* of the directory. It receives as arguments
|
||||
* - the OS.File.Entry corresponding to the entry;
|
||||
* - the index of the entry in the enumeration;
|
||||
* - the iterator itself - calling |close| on the iterator stops
|
||||
* the loop.
|
||||
*/
|
||||
forEach: function forEach(cb) {
|
||||
let index = 0;
|
||||
for (let entry in this) {
|
||||
cb(entry, index++, this);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Return several entries at once.
|
||||
*
|
||||
* Entries are returned in the same order as a walk with |forEach| or
|
||||
* |for(...)|.
|
||||
*
|
||||
* @param {number=} length If specified, the number of entries
|
||||
* to return. If unspecified, return all remaining entries.
|
||||
* @return {Array} An array containing the next |length| entries, or
|
||||
* less if the iteration contains less than |length| entries left.
|
||||
*/
|
||||
nextBatch: function nextBatch(length) {
|
||||
let array = [];
|
||||
let i = 0;
|
||||
for (let entry in this) {
|
||||
array.push(entry);
|
||||
if (++i >= length) {
|
||||
return array;
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility function shared by implementations of |OS.File.open|:
|
||||
* extract read/write/trunc/create/existing flags from a |mode|
|
||||
* object.
|
||||
*
|
||||
* @param {*=} mode An object that may contain fields |read|,
|
||||
* |write|, |truncate|, |create|, |existing|. These fields
|
||||
* are interpreted only if true-ish.
|
||||
* @return {{read:bool, write:bool, trunc:bool, create:bool,
|
||||
* existing:bool}} an object recapitulating the options set
|
||||
* by |mode|.
|
||||
* @throws {TypeError} If |mode| contains other fields, or
|
||||
* if it contains both |create| and |truncate|, or |create|
|
||||
* and |existing|.
|
||||
*/
|
||||
AbstractFile.normalizeOpenMode = function normalizeOpenMode(mode) {
|
||||
let result = {
|
||||
read: false,
|
||||
write: false,
|
||||
trunc: false,
|
||||
create: false,
|
||||
existing: false,
|
||||
append: true
|
||||
};
|
||||
for (let key in mode) {
|
||||
let val = !!mode[key]; // bool cast.
|
||||
switch (key) {
|
||||
case "read":
|
||||
result.read = val;
|
||||
break;
|
||||
case "write":
|
||||
result.write = val;
|
||||
break;
|
||||
case "truncate": // fallthrough
|
||||
case "trunc":
|
||||
result.trunc = val;
|
||||
result.write |= val;
|
||||
break;
|
||||
case "create":
|
||||
result.create = val;
|
||||
result.write |= val;
|
||||
break;
|
||||
case "existing": // fallthrough
|
||||
case "exist":
|
||||
result.existing = val;
|
||||
break;
|
||||
case "append":
|
||||
result.append = val;
|
||||
break;
|
||||
default:
|
||||
throw new TypeError("Mode " + key + " not understood");
|
||||
}
|
||||
}
|
||||
// Reject opposite modes
|
||||
if (result.existing && result.create) {
|
||||
throw new TypeError("Cannot specify both existing:true and create:true");
|
||||
}
|
||||
if (result.trunc && result.create) {
|
||||
throw new TypeError("Cannot specify both trunc:true and create:true");
|
||||
}
|
||||
// Handle read/write
|
||||
if (!result.write) {
|
||||
result.read = true;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the contents of a file.
|
||||
*
|
||||
* @param {string} path The path to the file.
|
||||
* @param {number=} bytes Optionally, an upper bound to the number of bytes
|
||||
* to read. DEPRECATED - please use options.bytes instead.
|
||||
* @param {object=} options Optionally, an object with some of the following
|
||||
* fields:
|
||||
* - {number} bytes An upper bound to the number of bytes to read.
|
||||
* - {string} compression If "lz4" and if the file is compressed using the lz4
|
||||
* compression algorithm, decompress the file contents on the fly.
|
||||
*
|
||||
* @return {Uint8Array} A buffer holding the bytes
|
||||
* and the number of bytes read from the file.
|
||||
*/
|
||||
AbstractFile.read = function read(path, bytes, options = {}) {
|
||||
if (bytes && typeof bytes == "object") {
|
||||
options = bytes;
|
||||
bytes = options.bytes || null;
|
||||
}
|
||||
if ("encoding" in options && typeof options.encoding != "string") {
|
||||
throw new TypeError("Invalid type for option encoding");
|
||||
}
|
||||
if ("compression" in options && typeof options.compression != "string") {
|
||||
throw new TypeError("Invalid type for option compression: " + options.compression);
|
||||
}
|
||||
if ("bytes" in options && typeof options.bytes != "number") {
|
||||
throw new TypeError("Invalid type for option bytes");
|
||||
}
|
||||
let file = exports.OS.File.open(path);
|
||||
try {
|
||||
let buffer = file.read(bytes, options);
|
||||
if ("compression" in options) {
|
||||
if (options.compression == "lz4") {
|
||||
options = Object.create(options);
|
||||
options.path = path;
|
||||
buffer = Lz4.decompressFileContent(buffer, options);
|
||||
} else {
|
||||
throw OS.File.Error.invalidArgument("Compression");
|
||||
}
|
||||
}
|
||||
if (!("encoding" in options)) {
|
||||
return buffer;
|
||||
}
|
||||
let decoder;
|
||||
try {
|
||||
decoder = new TextDecoder(options.encoding);
|
||||
} catch (ex if ex instanceof RangeError) {
|
||||
throw OS.File.Error.invalidArgument("Decode");
|
||||
}
|
||||
return decoder.decode(buffer);
|
||||
} finally {
|
||||
file.close();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Write a file, atomically.
|
||||
*
|
||||
* By opposition to a regular |write|, this operation ensures that,
|
||||
* until the contents are fully written, the destination file is
|
||||
* not modified.
|
||||
*
|
||||
* Limitation: In a few extreme cases (hardware failure during the
|
||||
* write, user unplugging disk during the write, etc.), data may be
|
||||
* corrupted. If your data is user-critical (e.g. preferences,
|
||||
* application data, etc.), you may wish to consider adding options
|
||||
* |tmpPath| and/or |flush| to reduce the likelihood of corruption, as
|
||||
* detailed below. Note that no combination of options can be
|
||||
* guaranteed to totally eliminate the risk of corruption.
|
||||
*
|
||||
* @param {string} path The path of the file to modify.
|
||||
* @param {Typed Array | C pointer} buffer A buffer containing the bytes to write.
|
||||
* @param {*=} options Optionally, an object determining the behavior
|
||||
* of this function. This object may contain the following fields:
|
||||
* - {number} bytes The number of bytes to write. If unspecified,
|
||||
* |buffer.byteLength|. Required if |buffer| is a C pointer.
|
||||
* - {string} tmpPath If |null| or unspecified, write all data directly
|
||||
* to |path|. If specified, write all data to a temporary file called
|
||||
* |tmpPath| and, once this write is complete, rename the file to
|
||||
* replace |path|. Performing this additional operation is a little
|
||||
* slower but also a little safer.
|
||||
* - {bool} noOverwrite - If set, this function will fail if a file already
|
||||
* exists at |path|.
|
||||
* - {bool} flush - If |false| or unspecified, return immediately once the
|
||||
* write is complete. If |true|, before writing, force the operating system
|
||||
* to write its internal disk buffers to the disk. This is considerably slower
|
||||
* (not just for the application but for the whole system) but also safer:
|
||||
* if the system shuts down improperly (typically due to a kernel freeze
|
||||
* or a power failure) or if the device is disconnected before the buffer
|
||||
* is flushed, the file has more chances of not being corrupted.
|
||||
* - {string} compression - If empty or unspecified, do not compress the file.
|
||||
* If "lz4", compress the contents of the file atomically using lz4. For the
|
||||
* time being, the container format is specific to Mozilla and cannot be read
|
||||
* by means other than OS.File.read(..., { compression: "lz4"})
|
||||
* - {string} backupTo - If specified, backup the destination file as |backupTo|.
|
||||
* Note that this function renames the destination file before overwriting it.
|
||||
* If the process or the operating system freezes or crashes
|
||||
* during the short window between these operations,
|
||||
* the destination file will have been moved to its backup.
|
||||
*
|
||||
* @return {number} The number of bytes actually written.
|
||||
*/
|
||||
AbstractFile.writeAtomic =
|
||||
function writeAtomic(path, buffer, options = {}) {
|
||||
|
||||
// Verify that path is defined and of the correct type
|
||||
if (typeof path != "string" || path == "") {
|
||||
throw new TypeError("File path should be a (non-empty) string");
|
||||
}
|
||||
let noOverwrite = options.noOverwrite;
|
||||
if (noOverwrite && OS.File.exists(path)) {
|
||||
throw OS.File.Error.exists("writeAtomic", path);
|
||||
}
|
||||
|
||||
if (typeof buffer == "string") {
|
||||
// Normalize buffer to a C buffer by encoding it
|
||||
let encoding = options.encoding || "utf-8";
|
||||
buffer = new TextEncoder(encoding).encode(buffer);
|
||||
}
|
||||
|
||||
if ("compression" in options && options.compression == "lz4") {
|
||||
buffer = Lz4.compressFileContent(buffer, options);
|
||||
options = Object.create(options);
|
||||
options.bytes = buffer.byteLength;
|
||||
}
|
||||
|
||||
let bytesWritten = 0;
|
||||
|
||||
if (!options.tmpPath) {
|
||||
if (options.backupTo) {
|
||||
try {
|
||||
OS.File.move(path, options.backupTo, {noCopy: true});
|
||||
} catch (ex if ex.becauseNoSuchFile) {
|
||||
// The file doesn't exist, nothing to backup.
|
||||
}
|
||||
}
|
||||
// Just write, without any renaming trick
|
||||
let dest = OS.File.open(path, {write: true, truncate: true});
|
||||
try {
|
||||
bytesWritten = dest.write(buffer, options);
|
||||
if (options.flush) {
|
||||
dest.flush();
|
||||
}
|
||||
} finally {
|
||||
dest.close();
|
||||
}
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
let tmpFile = OS.File.open(options.tmpPath, {write: true, truncate: true});
|
||||
try {
|
||||
bytesWritten = tmpFile.write(buffer, options);
|
||||
if (options.flush) {
|
||||
tmpFile.flush();
|
||||
}
|
||||
} catch (x) {
|
||||
OS.File.remove(options.tmpPath);
|
||||
throw x;
|
||||
} finally {
|
||||
tmpFile.close();
|
||||
}
|
||||
|
||||
if (options.backupTo) {
|
||||
try {
|
||||
OS.File.move(path, options.backupTo, {noCopy: true});
|
||||
} catch (ex if ex.becauseNoSuchFile) {
|
||||
// The file doesn't exist, nothing to backup.
|
||||
}
|
||||
}
|
||||
|
||||
OS.File.move(options.tmpPath, path, {noCopy: true});
|
||||
return bytesWritten;
|
||||
};
|
||||
|
||||
/**
|
||||
* This function is used by removeDir to avoid calling lstat for each
|
||||
* files under the specified directory. External callers should not call
|
||||
* this function directly.
|
||||
*/
|
||||
AbstractFile.removeRecursive = function(path, options = {}) {
|
||||
let iterator = new OS.File.DirectoryIterator(path);
|
||||
if (!iterator.exists()) {
|
||||
if (!("ignoreAbsent" in options) || options.ignoreAbsent) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
for (let entry in iterator) {
|
||||
if (entry.isDir) {
|
||||
if (entry.isLink) {
|
||||
// Unlike Unix symlinks, NTFS junctions or NTFS symlinks to
|
||||
// directories are directories themselves. OS.File.remove()
|
||||
// will not work for them.
|
||||
OS.File.removeEmptyDir(entry.path, options);
|
||||
} else {
|
||||
// Normal directories.
|
||||
AbstractFile.removeRecursive(entry.path, options);
|
||||
}
|
||||
} else {
|
||||
// NTFS symlinks to files, Unix symlinks, or regular files.
|
||||
OS.File.remove(entry.path, options);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
iterator.close();
|
||||
}
|
||||
|
||||
OS.File.removeEmptyDir(path);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a directory and, optionally, its parent directories.
|
||||
*
|
||||
* @param {string} path The name of the directory.
|
||||
* @param {*=} options Additional options.
|
||||
*
|
||||
* - {string} from If specified, the call to |makeDir| creates all the
|
||||
* ancestors of |path| that are descendants of |from|. Note that |path|
|
||||
* must be a descendant of |from|, and that |from| and its existing
|
||||
* subdirectories present in |path| must be user-writeable.
|
||||
* Example:
|
||||
* makeDir(Path.join(profileDir, "foo", "bar"), { from: profileDir });
|
||||
* creates directories profileDir/foo, profileDir/foo/bar
|
||||
* - {bool} ignoreExisting If |false|, throw an error if the directory
|
||||
* already exists. |true| by default. Ignored if |from| is specified.
|
||||
* - {number} unixMode Under Unix, if specified, a file creation mode,
|
||||
* as per libc function |mkdir|. If unspecified, dirs are
|
||||
* created with a default mode of 0700 (dir is private to
|
||||
* the user, the user can read, write and execute). Ignored under Windows
|
||||
* or if the file system does not support file creation modes.
|
||||
* - {C pointer} winSecurity Under Windows, if specified, security
|
||||
* attributes as per winapi function |CreateDirectory|. If
|
||||
* unspecified, use the default security descriptor, inherited from
|
||||
* the parent directory. Ignored under Unix or if the file system
|
||||
* does not support security descriptors.
|
||||
*/
|
||||
AbstractFile.makeDir = function(path, options = {}) {
|
||||
let from = options.from;
|
||||
if (!from) {
|
||||
OS.File._makeDir(path, options);
|
||||
return;
|
||||
}
|
||||
if (!path.startsWith(from)) {
|
||||
// Apparently, `from` is not a parent of `path`. However, we may
|
||||
// have false negatives due to non-normalized paths, e.g.
|
||||
// "foo//bar" is a parent of "foo/bar/sna".
|
||||
path = Path.normalize(path);
|
||||
from = Path.normalize(from);
|
||||
if (!path.startsWith(from)) {
|
||||
throw new Error("Incorrect use of option |from|: " + path + " is not a descendant of " + from);
|
||||
}
|
||||
}
|
||||
let innerOptions = Object.create(options, {
|
||||
ignoreExisting: {
|
||||
value: true
|
||||
}
|
||||
});
|
||||
// Compute the elements that appear in |path| but not in |from|.
|
||||
let items = Path.split(path).components.slice(Path.split(from).components.length);
|
||||
let current = from;
|
||||
for (let item of items) {
|
||||
current = Path.join(current, item);
|
||||
OS.File._makeDir(current, innerOptions);
|
||||
}
|
||||
};
|
||||
|
||||
if (!exports.OS.Shared) {
|
||||
exports.OS.Shared = {};
|
||||
}
|
||||
exports.OS.Shared.AbstractFile = AbstractFile;
|
||||
})(this);
|
||||
375
toolkit/components/osfile/modules/osfile_unix_allthreads.jsm
Normal file
375
toolkit/components/osfile/modules/osfile_unix_allthreads.jsm
Normal file
|
|
@ -0,0 +1,375 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* This module defines the thread-agnostic components of the Unix version
|
||||
* of OS.File. It depends on the thread-agnostic cross-platform components
|
||||
* of OS.File.
|
||||
*
|
||||
* It serves the following purposes:
|
||||
* - open libc;
|
||||
* - define OS.Unix.Error;
|
||||
* - define a few constants and types that need to be defined on all platforms.
|
||||
*
|
||||
* This module can be:
|
||||
* - opened from the main thread as a jsm module;
|
||||
* - opened from a chrome worker through require().
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var SharedAll;
|
||||
if (typeof Components != "undefined") {
|
||||
let Cu = Components.utils;
|
||||
// Module is opened as a jsm module
|
||||
Cu.import("resource://gre/modules/ctypes.jsm", this);
|
||||
|
||||
SharedAll = {};
|
||||
Cu.import("resource://gre/modules/osfile/osfile_shared_allthreads.jsm", SharedAll);
|
||||
this.exports = {};
|
||||
} else if (typeof module != "undefined" && typeof require != "undefined") {
|
||||
// Module is loaded with require()
|
||||
SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
|
||||
} else {
|
||||
throw new Error("Please open this module with Component.utils.import or with require()");
|
||||
}
|
||||
|
||||
var LOG = SharedAll.LOG.bind(SharedAll, "Unix", "allthreads");
|
||||
var Const = SharedAll.Constants.libc;
|
||||
|
||||
// Open libc
|
||||
var libc = new SharedAll.Library("libc",
|
||||
"libc.so", "libSystem.B.dylib", "a.out");
|
||||
exports.libc = libc;
|
||||
|
||||
// Define declareFFI
|
||||
var declareFFI = SharedAll.declareFFI.bind(null, libc);
|
||||
exports.declareFFI = declareFFI;
|
||||
|
||||
// Define lazy binding
|
||||
var LazyBindings = {};
|
||||
libc.declareLazy(LazyBindings, "strerror",
|
||||
"strerror", ctypes.default_abi,
|
||||
/*return*/ ctypes.char.ptr,
|
||||
/*errnum*/ ctypes.int);
|
||||
|
||||
/**
|
||||
* A File-related error.
|
||||
*
|
||||
* To obtain a human-readable error message, use method |toString|.
|
||||
* To determine the cause of the error, use the various |becauseX|
|
||||
* getters. To determine the operation that failed, use field
|
||||
* |operation|.
|
||||
*
|
||||
* Additionally, this implementation offers a field
|
||||
* |unixErrno|, which holds the OS-specific error
|
||||
* constant. If you need this level of detail, you may match the value
|
||||
* of this field against the error constants of |OS.Constants.libc|.
|
||||
*
|
||||
* @param {string=} operation The operation that failed. If unspecified,
|
||||
* the name of the calling function is taken to be the operation that
|
||||
* failed.
|
||||
* @param {number=} lastError The OS-specific constant detailing the
|
||||
* reason of the error. If unspecified, this is fetched from the system
|
||||
* status.
|
||||
* @param {string=} path The file path that manipulated. If unspecified,
|
||||
* assign the empty string.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {OS.Shared.Error}
|
||||
*/
|
||||
var OSError = function OSError(operation = "unknown operation",
|
||||
errno = ctypes.errno, path = "") {
|
||||
SharedAll.OSError.call(this, operation, path);
|
||||
this.unixErrno = errno;
|
||||
};
|
||||
OSError.prototype = Object.create(SharedAll.OSError.prototype);
|
||||
OSError.prototype.toString = function toString() {
|
||||
return "Unix error " + this.unixErrno +
|
||||
" during operation " + this.operation +
|
||||
(this.path? " on file " + this.path : "") +
|
||||
" (" + LazyBindings.strerror(this.unixErrno).readString() + ")";
|
||||
};
|
||||
OSError.prototype.toMsg = function toMsg() {
|
||||
return OSError.toMsg(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* |true| if the error was raised because a file or directory
|
||||
* already exists, |false| otherwise.
|
||||
*/
|
||||
Object.defineProperty(OSError.prototype, "becauseExists", {
|
||||
get: function becauseExists() {
|
||||
return this.unixErrno == Const.EEXIST;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* |true| if the error was raised because a file or directory
|
||||
* does not exist, |false| otherwise.
|
||||
*/
|
||||
Object.defineProperty(OSError.prototype, "becauseNoSuchFile", {
|
||||
get: function becauseNoSuchFile() {
|
||||
return this.unixErrno == Const.ENOENT;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* |true| if the error was raised because a directory is not empty
|
||||
* does not exist, |false| otherwise.
|
||||
*/
|
||||
Object.defineProperty(OSError.prototype, "becauseNotEmpty", {
|
||||
get: function becauseNotEmpty() {
|
||||
return this.unixErrno == Const.ENOTEMPTY;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* |true| if the error was raised because a file or directory
|
||||
* is closed, |false| otherwise.
|
||||
*/
|
||||
Object.defineProperty(OSError.prototype, "becauseClosed", {
|
||||
get: function becauseClosed() {
|
||||
return this.unixErrno == Const.EBADF;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* |true| if the error was raised because permission is denied to
|
||||
* access a file or directory, |false| otherwise.
|
||||
*/
|
||||
Object.defineProperty(OSError.prototype, "becauseAccessDenied", {
|
||||
get: function becauseAccessDenied() {
|
||||
return this.unixErrno == Const.EACCES;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* |true| if the error was raised because some invalid argument was passed,
|
||||
* |false| otherwise.
|
||||
*/
|
||||
Object.defineProperty(OSError.prototype, "becauseInvalidArgument", {
|
||||
get: function becauseInvalidArgument() {
|
||||
return this.unixErrno == Const.EINVAL;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Serialize an instance of OSError to something that can be
|
||||
* transmitted across threads (not necessarily a string).
|
||||
*/
|
||||
OSError.toMsg = function toMsg(error) {
|
||||
return {
|
||||
exn: "OS.File.Error",
|
||||
fileName: error.moduleName,
|
||||
lineNumber: error.lineNumber,
|
||||
stack: error.moduleStack,
|
||||
operation: error.operation,
|
||||
unixErrno: error.unixErrno,
|
||||
path: error.path
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Deserialize a message back to an instance of OSError
|
||||
*/
|
||||
OSError.fromMsg = function fromMsg(msg) {
|
||||
let error = new OSError(msg.operation, msg.unixErrno, msg.path);
|
||||
error.stack = msg.stack;
|
||||
error.fileName = msg.fileName;
|
||||
error.lineNumber = msg.lineNumber;
|
||||
return error;
|
||||
};
|
||||
exports.Error = OSError;
|
||||
|
||||
/**
|
||||
* Code shared by implementations of File.Info on Unix
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var AbstractInfo = function AbstractInfo(path, isDir, isSymLink, size, lastAccessDate,
|
||||
lastModificationDate, unixLastStatusChangeDate,
|
||||
unixOwner, unixGroup, unixMode) {
|
||||
this._path = path;
|
||||
this._isDir = isDir;
|
||||
this._isSymlLink = isSymLink;
|
||||
this._size = size;
|
||||
this._lastAccessDate = lastAccessDate;
|
||||
this._lastModificationDate = lastModificationDate;
|
||||
this._unixLastStatusChangeDate = unixLastStatusChangeDate;
|
||||
this._unixOwner = unixOwner;
|
||||
this._unixGroup = unixGroup;
|
||||
this._unixMode = unixMode;
|
||||
};
|
||||
|
||||
AbstractInfo.prototype = {
|
||||
/**
|
||||
* The path of the file, used for error-reporting.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get path() {
|
||||
return this._path;
|
||||
},
|
||||
/**
|
||||
* |true| if this file is a directory, |false| otherwise
|
||||
*/
|
||||
get isDir() {
|
||||
return this._isDir;
|
||||
},
|
||||
/**
|
||||
* |true| if this file is a symbolink link, |false| otherwise
|
||||
*/
|
||||
get isSymLink() {
|
||||
return this._isSymlLink;
|
||||
},
|
||||
/**
|
||||
* The size of the file, in bytes.
|
||||
*
|
||||
* Note that the result may be |NaN| if the size of the file cannot be
|
||||
* represented in JavaScript.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
get size() {
|
||||
return this._size;
|
||||
},
|
||||
/**
|
||||
* The date of last access to this file.
|
||||
*
|
||||
* Note that the definition of last access may depend on the
|
||||
* underlying operating system and file system.
|
||||
*
|
||||
* @type {Date}
|
||||
*/
|
||||
get lastAccessDate() {
|
||||
return this._lastAccessDate;
|
||||
},
|
||||
/**
|
||||
* Return the date of last modification of this file.
|
||||
*/
|
||||
get lastModificationDate() {
|
||||
return this._lastModificationDate;
|
||||
},
|
||||
/**
|
||||
* Return the date at which the status of this file was last modified
|
||||
* (this is the date of the latest write/renaming/mode change/...
|
||||
* of the file)
|
||||
*/
|
||||
get unixLastStatusChangeDate() {
|
||||
return this._unixLastStatusChangeDate;
|
||||
},
|
||||
/*
|
||||
* Return the Unix owner of this file
|
||||
*/
|
||||
get unixOwner() {
|
||||
return this._unixOwner;
|
||||
},
|
||||
/*
|
||||
* Return the Unix group of this file
|
||||
*/
|
||||
get unixGroup() {
|
||||
return this._unixGroup;
|
||||
},
|
||||
/*
|
||||
* Return the Unix group of this file
|
||||
*/
|
||||
get unixMode() {
|
||||
return this._unixMode;
|
||||
}
|
||||
};
|
||||
exports.AbstractInfo = AbstractInfo;
|
||||
|
||||
/**
|
||||
* Code shared by implementations of File.DirectoryIterator.Entry on Unix
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var AbstractEntry = function AbstractEntry(isDir, isSymLink, name, path) {
|
||||
this._isDir = isDir;
|
||||
this._isSymlLink = isSymLink;
|
||||
this._name = name;
|
||||
this._path = path;
|
||||
};
|
||||
|
||||
AbstractEntry.prototype = {
|
||||
/**
|
||||
* |true| if the entry is a directory, |false| otherwise
|
||||
*/
|
||||
get isDir() {
|
||||
return this._isDir;
|
||||
},
|
||||
/**
|
||||
* |true| if the entry is a directory, |false| otherwise
|
||||
*/
|
||||
get isSymLink() {
|
||||
return this._isSymlLink;
|
||||
},
|
||||
/**
|
||||
* The name of the entry
|
||||
* @type {string}
|
||||
*/
|
||||
get name() {
|
||||
return this._name;
|
||||
},
|
||||
/**
|
||||
* The full path to the entry
|
||||
*/
|
||||
get path() {
|
||||
return this._path;
|
||||
}
|
||||
};
|
||||
exports.AbstractEntry = AbstractEntry;
|
||||
|
||||
// Special constants that need to be defined on all platforms
|
||||
|
||||
exports.POS_START = Const.SEEK_SET;
|
||||
exports.POS_CURRENT = Const.SEEK_CUR;
|
||||
exports.POS_END = Const.SEEK_END;
|
||||
|
||||
// Special types that need to be defined for communication
|
||||
// between threads
|
||||
var Type = Object.create(SharedAll.Type);
|
||||
exports.Type = Type;
|
||||
|
||||
/**
|
||||
* Native paths
|
||||
*
|
||||
* Under Unix, expressed as C strings
|
||||
*/
|
||||
Type.path = Type.cstring.withName("[in] path");
|
||||
Type.out_path = Type.out_cstring.withName("[out] path");
|
||||
|
||||
// Special constructors that need to be defined on all threads
|
||||
OSError.closed = function closed(operation, path) {
|
||||
return new OSError(operation, Const.EBADF, path);
|
||||
};
|
||||
|
||||
OSError.exists = function exists(operation, path) {
|
||||
return new OSError(operation, Const.EEXIST, path);
|
||||
};
|
||||
|
||||
OSError.noSuchFile = function noSuchFile(operation, path) {
|
||||
return new OSError(operation, Const.ENOENT, path);
|
||||
};
|
||||
|
||||
OSError.invalidArgument = function invalidArgument(operation) {
|
||||
return new OSError(operation, Const.EINVAL);
|
||||
};
|
||||
|
||||
var EXPORTED_SYMBOLS = [
|
||||
"declareFFI",
|
||||
"libc",
|
||||
"Error",
|
||||
"AbstractInfo",
|
||||
"AbstractEntry",
|
||||
"Type",
|
||||
"POS_START",
|
||||
"POS_CURRENT",
|
||||
"POS_END"
|
||||
];
|
||||
|
||||
//////////// Boilerplate
|
||||
if (typeof Components != "undefined") {
|
||||
this.EXPORTED_SYMBOLS = EXPORTED_SYMBOLS;
|
||||
for (let symbol of EXPORTED_SYMBOLS) {
|
||||
this[symbol] = exports[symbol];
|
||||
}
|
||||
}
|
||||
735
toolkit/components/osfile/modules/osfile_unix_back.jsm
Normal file
735
toolkit/components/osfile/modules/osfile_unix_back.jsm
Normal file
|
|
@ -0,0 +1,735 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
{
|
||||
if (typeof Components != "undefined") {
|
||||
// We do not wish osfile_unix_back.jsm to be used directly as a main thread
|
||||
// module yet. When time comes, it will be loaded by a combination of
|
||||
// a main thread front-end/worker thread implementation that makes sure
|
||||
// that we are not executing synchronous IO code in the main thread.
|
||||
|
||||
throw new Error("osfile_unix_back.jsm cannot be used from the main thread yet");
|
||||
}
|
||||
(function(exports) {
|
||||
"use strict";
|
||||
if (exports.OS && exports.OS.Unix && exports.OS.Unix.File) {
|
||||
return; // Avoid double initialization
|
||||
}
|
||||
|
||||
let SharedAll =
|
||||
require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
|
||||
let SysAll =
|
||||
require("resource://gre/modules/osfile/osfile_unix_allthreads.jsm");
|
||||
let LOG = SharedAll.LOG.bind(SharedAll, "Unix", "back");
|
||||
let libc = SysAll.libc;
|
||||
let Const = SharedAll.Constants.libc;
|
||||
|
||||
/**
|
||||
* Initialize the Unix module.
|
||||
*
|
||||
* @param {function=} declareFFI
|
||||
*/
|
||||
// FIXME: Both |init| and |aDeclareFFI| are deprecated, we should remove them
|
||||
let init = function init(aDeclareFFI) {
|
||||
let declareFFI;
|
||||
if (aDeclareFFI) {
|
||||
declareFFI = aDeclareFFI.bind(null, libc);
|
||||
} else {
|
||||
declareFFI = SysAll.declareFFI;
|
||||
}
|
||||
let declareLazyFFI = SharedAll.declareLazyFFI;
|
||||
|
||||
// Initialize types that require additional OS-specific
|
||||
// support - either finalization or matching against
|
||||
// OS-specific constants.
|
||||
let Type = Object.create(SysAll.Type);
|
||||
let SysFile = exports.OS.Unix.File = { Type: Type };
|
||||
|
||||
/**
|
||||
* A file descriptor.
|
||||
*/
|
||||
Type.fd = Type.int.withName("fd");
|
||||
Type.fd.importFromC = function importFromC(fd_int) {
|
||||
return ctypes.CDataFinalizer(fd_int, SysFile._close);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A C integer holding -1 in case of error or a file descriptor
|
||||
* in case of success.
|
||||
*/
|
||||
Type.negativeone_or_fd = Type.fd.withName("negativeone_or_fd");
|
||||
Type.negativeone_or_fd.importFromC =
|
||||
function importFromC(fd_int) {
|
||||
if (fd_int == -1) {
|
||||
return -1;
|
||||
}
|
||||
return ctypes.CDataFinalizer(fd_int, SysFile._close);
|
||||
};
|
||||
|
||||
/**
|
||||
* A C integer holding -1 in case of error or a meaningless value
|
||||
* in case of success.
|
||||
*/
|
||||
Type.negativeone_or_nothing =
|
||||
Type.int.withName("negativeone_or_nothing");
|
||||
|
||||
/**
|
||||
* A C integer holding -1 in case of error or a positive integer
|
||||
* in case of success.
|
||||
*/
|
||||
Type.negativeone_or_ssize_t =
|
||||
Type.ssize_t.withName("negativeone_or_ssize_t");
|
||||
|
||||
/**
|
||||
* Various libc integer types
|
||||
*/
|
||||
Type.mode_t =
|
||||
Type.intn_t(Const.OSFILE_SIZEOF_MODE_T).withName("mode_t");
|
||||
Type.uid_t =
|
||||
Type.intn_t(Const.OSFILE_SIZEOF_UID_T).withName("uid_t");
|
||||
Type.gid_t =
|
||||
Type.intn_t(Const.OSFILE_SIZEOF_GID_T).withName("gid_t");
|
||||
|
||||
/**
|
||||
* Type |time_t|
|
||||
*/
|
||||
Type.time_t =
|
||||
Type.intn_t(Const.OSFILE_SIZEOF_TIME_T).withName("time_t");
|
||||
|
||||
// Structure |dirent|
|
||||
// Building this type is rather complicated, as its layout varies between
|
||||
// variants of Unix. For this reason, we rely on a number of constants
|
||||
// (computed in C from the C data structures) that give us the layout.
|
||||
// The structure we compute looks like
|
||||
// { int8_t[...] before_d_type; // ignored content
|
||||
// int8_t d_type ;
|
||||
// int8_t[...] before_d_name; // ignored content
|
||||
// char[...] d_name;
|
||||
// };
|
||||
{
|
||||
let d_name_extra_size = 0;
|
||||
if (Const.OSFILE_SIZEOF_DIRENT_D_NAME < 8) {
|
||||
// d_name is defined like "char d_name[1];" on some platforms
|
||||
// (e.g. Solaris), we need to give it more size for our structure.
|
||||
d_name_extra_size = 256;
|
||||
}
|
||||
|
||||
let dirent = new SharedAll.HollowStructure("dirent",
|
||||
Const.OSFILE_SIZEOF_DIRENT + d_name_extra_size);
|
||||
if (Const.OSFILE_OFFSETOF_DIRENT_D_TYPE != undefined) {
|
||||
// |dirent| doesn't have d_type on some platforms (e.g. Solaris).
|
||||
dirent.add_field_at(Const.OSFILE_OFFSETOF_DIRENT_D_TYPE,
|
||||
"d_type", ctypes.uint8_t);
|
||||
}
|
||||
dirent.add_field_at(Const.OSFILE_OFFSETOF_DIRENT_D_NAME,
|
||||
"d_name", ctypes.ArrayType(ctypes.char,
|
||||
Const.OSFILE_SIZEOF_DIRENT_D_NAME + d_name_extra_size));
|
||||
|
||||
// We now have built |dirent|.
|
||||
Type.dirent = dirent.getType();
|
||||
}
|
||||
Type.null_or_dirent_ptr =
|
||||
new SharedAll.Type("null_of_dirent",
|
||||
Type.dirent.out_ptr.implementation);
|
||||
|
||||
// Structure |stat|
|
||||
// Same technique
|
||||
{
|
||||
let stat = new SharedAll.HollowStructure("stat",
|
||||
Const.OSFILE_SIZEOF_STAT);
|
||||
stat.add_field_at(Const.OSFILE_OFFSETOF_STAT_ST_MODE,
|
||||
"st_mode", Type.mode_t.implementation);
|
||||
stat.add_field_at(Const.OSFILE_OFFSETOF_STAT_ST_UID,
|
||||
"st_uid", Type.uid_t.implementation);
|
||||
stat.add_field_at(Const.OSFILE_OFFSETOF_STAT_ST_GID,
|
||||
"st_gid", Type.gid_t.implementation);
|
||||
|
||||
// Here, things get complicated with different data structures.
|
||||
// Some platforms have |time_t st_atime| and some platforms have
|
||||
// |timespec st_atimespec|. However, since |timespec| starts with
|
||||
// a |time_t|, followed by nanoseconds, we just cheat and pretend
|
||||
// that everybody has |time_t st_atime|, possibly followed by padding
|
||||
stat.add_field_at(Const.OSFILE_OFFSETOF_STAT_ST_ATIME,
|
||||
"st_atime", Type.time_t.implementation);
|
||||
stat.add_field_at(Const.OSFILE_OFFSETOF_STAT_ST_MTIME,
|
||||
"st_mtime", Type.time_t.implementation);
|
||||
stat.add_field_at(Const.OSFILE_OFFSETOF_STAT_ST_CTIME,
|
||||
"st_ctime", Type.time_t.implementation);
|
||||
|
||||
// To complicate further, MacOS and some BSDs have a field |birthtime|
|
||||
if ("OSFILE_OFFSETOF_STAT_ST_BIRTHTIME" in Const) {
|
||||
stat.add_field_at(Const.OSFILE_OFFSETOF_STAT_ST_BIRTHTIME,
|
||||
"st_birthtime", Type.time_t.implementation);
|
||||
}
|
||||
|
||||
stat.add_field_at(Const.OSFILE_OFFSETOF_STAT_ST_SIZE,
|
||||
"st_size", Type.off_t.implementation);
|
||||
Type.stat = stat.getType();
|
||||
}
|
||||
|
||||
// Structure |DIR|
|
||||
if ("OSFILE_SIZEOF_DIR" in Const) {
|
||||
// On platforms for which we need to access the fields of DIR
|
||||
// directly (e.g. because certain functions are implemented
|
||||
// as macros), we need to define DIR as a hollow structure.
|
||||
let DIR = new SharedAll.HollowStructure(
|
||||
"DIR",
|
||||
Const.OSFILE_SIZEOF_DIR);
|
||||
|
||||
DIR.add_field_at(
|
||||
Const.OSFILE_OFFSETOF_DIR_DD_FD,
|
||||
"dd_fd",
|
||||
Type.fd.implementation);
|
||||
|
||||
Type.DIR = DIR.getType();
|
||||
} else {
|
||||
// On other platforms, we keep DIR as a blackbox
|
||||
Type.DIR =
|
||||
new SharedAll.Type("DIR",
|
||||
ctypes.StructType("DIR"));
|
||||
}
|
||||
|
||||
Type.null_or_DIR_ptr =
|
||||
Type.DIR.out_ptr.withName("null_or_DIR*");
|
||||
Type.null_or_DIR_ptr.importFromC = function importFromC(dir) {
|
||||
if (dir == null || dir.isNull()) {
|
||||
return null;
|
||||
}
|
||||
return ctypes.CDataFinalizer(dir, SysFile._close_dir);
|
||||
};
|
||||
|
||||
// Structure |timeval|
|
||||
{
|
||||
let timeval = new SharedAll.HollowStructure(
|
||||
"timeval",
|
||||
Const.OSFILE_SIZEOF_TIMEVAL);
|
||||
timeval.add_field_at(
|
||||
Const.OSFILE_OFFSETOF_TIMEVAL_TV_SEC,
|
||||
"tv_sec",
|
||||
Type.long.implementation);
|
||||
timeval.add_field_at(
|
||||
Const.OSFILE_OFFSETOF_TIMEVAL_TV_USEC,
|
||||
"tv_usec",
|
||||
Type.long.implementation);
|
||||
Type.timeval = timeval.getType();
|
||||
Type.timevals = new SharedAll.Type("two timevals",
|
||||
ctypes.ArrayType(Type.timeval.implementation, 2));
|
||||
}
|
||||
|
||||
// Types fsblkcnt_t and fsfilcnt_t, used by structure |statvfs|
|
||||
Type.fsblkcnt_t =
|
||||
Type.uintn_t(Const.OSFILE_SIZEOF_FSBLKCNT_T).withName("fsblkcnt_t");
|
||||
|
||||
// Structure |statvfs|
|
||||
// Use an hollow structure
|
||||
{
|
||||
let statvfs = new SharedAll.HollowStructure("statvfs",
|
||||
Const.OSFILE_SIZEOF_STATVFS);
|
||||
|
||||
statvfs.add_field_at(Const.OSFILE_OFFSETOF_STATVFS_F_BSIZE,
|
||||
"f_bsize", Type.unsigned_long.implementation);
|
||||
statvfs.add_field_at(Const.OSFILE_OFFSETOF_STATVFS_F_BAVAIL,
|
||||
"f_bavail", Type.fsblkcnt_t.implementation);
|
||||
|
||||
Type.statvfs = statvfs.getType();
|
||||
}
|
||||
|
||||
// Declare libc functions as functions of |OS.Unix.File|
|
||||
|
||||
// Finalizer-related functions
|
||||
libc.declareLazy(SysFile, "_close",
|
||||
"close", ctypes.default_abi,
|
||||
/*return */ctypes.int,
|
||||
/*fd*/ ctypes.int);
|
||||
|
||||
SysFile.close = function close(fd) {
|
||||
// Detach the finalizer and call |_close|.
|
||||
return fd.dispose();
|
||||
};
|
||||
|
||||
libc.declareLazy(SysFile, "_close_dir",
|
||||
"closedir", ctypes.default_abi,
|
||||
/*return */ctypes.int,
|
||||
/*dirp*/ Type.DIR.in_ptr.implementation);
|
||||
|
||||
SysFile.closedir = function closedir(fd) {
|
||||
// Detach the finalizer and call |_close_dir|.
|
||||
return fd.dispose();
|
||||
};
|
||||
|
||||
{
|
||||
// Symbol free() is special.
|
||||
// We override the definition of free() on several platforms.
|
||||
let default_lib = new SharedAll.Library("default_lib",
|
||||
"a.out");
|
||||
|
||||
// On platforms for which we override free(), nspr defines
|
||||
// a special library name "a.out" that will resolve to the
|
||||
// correct implementation free().
|
||||
// If it turns out we don't have an a.out library or a.out
|
||||
// doesn't contain free, use the ordinary libc free.
|
||||
|
||||
default_lib.declareLazyWithFallback(libc, SysFile, "free",
|
||||
"free", ctypes.default_abi,
|
||||
/*return*/ ctypes.void_t,
|
||||
/*ptr*/ ctypes.voidptr_t);
|
||||
}
|
||||
|
||||
|
||||
// Other functions
|
||||
libc.declareLazyFFI(SysFile, "access",
|
||||
"access", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*path*/ Type.path,
|
||||
/*mode*/ Type.int);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "chdir",
|
||||
"chdir", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*path*/ Type.path);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "chmod",
|
||||
"chmod", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*path*/ Type.path,
|
||||
/*mode*/ Type.mode_t);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "chown",
|
||||
"chown", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*path*/ Type.path,
|
||||
/*uid*/ Type.uid_t,
|
||||
/*gid*/ Type.gid_t);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "copyfile",
|
||||
"copyfile", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*source*/ Type.path,
|
||||
/*dest*/ Type.path,
|
||||
/*state*/ Type.void_t.in_ptr, // Ignored atm
|
||||
/*flags*/ Type.uint32_t);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "dup",
|
||||
"dup", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_fd,
|
||||
/*fd*/ Type.fd);
|
||||
|
||||
if ("OSFILE_SIZEOF_DIR" in Const) {
|
||||
// On platforms for which |dirfd| is a macro
|
||||
SysFile.dirfd =
|
||||
function dirfd(DIRp) {
|
||||
return Type.DIR.in_ptr.implementation(DIRp).contents.dd_fd;
|
||||
};
|
||||
} else {
|
||||
// On platforms for which |dirfd| is a function
|
||||
libc.declareLazyFFI(SysFile, "dirfd",
|
||||
"dirfd", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_fd,
|
||||
/*dir*/ Type.DIR.in_ptr);
|
||||
}
|
||||
|
||||
libc.declareLazyFFI(SysFile, "chdir",
|
||||
"chdir", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*path*/ Type.path);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "fchdir",
|
||||
"fchdir", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*fd*/ Type.fd);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "fchmod",
|
||||
"fchmod", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*fd*/ Type.fd,
|
||||
/*mode*/ Type.mode_t);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "fchown",
|
||||
"fchown", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*fd*/ Type.fd,
|
||||
/*uid_t*/ Type.uid_t,
|
||||
/*gid_t*/ Type.gid_t);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "fsync",
|
||||
"fsync", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*fd*/ Type.fd);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "getcwd",
|
||||
"getcwd", ctypes.default_abi,
|
||||
/*return*/ Type.out_path,
|
||||
/*buf*/ Type.out_path,
|
||||
/*size*/ Type.size_t);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "getwd",
|
||||
"getwd", ctypes.default_abi,
|
||||
/*return*/ Type.out_path,
|
||||
/*buf*/ Type.out_path);
|
||||
|
||||
// Two variants of |getwd| which allocate the memory
|
||||
// dynamically.
|
||||
|
||||
// Linux/Android version
|
||||
libc.declareLazyFFI(SysFile, "get_current_dir_name",
|
||||
"get_current_dir_name", ctypes.default_abi,
|
||||
/*return*/ Type.out_path.releaseWithLazy(() =>
|
||||
SysFile.free
|
||||
));
|
||||
|
||||
// MacOS/BSD version (will return NULL on Linux/Android)
|
||||
libc.declareLazyFFI(SysFile, "getwd_auto",
|
||||
"getwd", ctypes.default_abi,
|
||||
/*return*/ Type.out_path.releaseWithLazy(() =>
|
||||
SysFile.free
|
||||
),
|
||||
/*buf*/ Type.void_t.out_ptr);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "fdatasync",
|
||||
"fdatasync", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*fd*/ Type.fd); // Note: MacOS/BSD-specific
|
||||
|
||||
libc.declareLazyFFI(SysFile, "ftruncate",
|
||||
"ftruncate", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*fd*/ Type.fd,
|
||||
/*length*/ Type.off_t);
|
||||
|
||||
|
||||
libc.declareLazyFFI(SysFile, "lchown",
|
||||
"lchown", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*path*/ Type.path,
|
||||
/*uid_t*/ Type.uid_t,
|
||||
/*gid_t*/ Type.gid_t);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "link",
|
||||
"link", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*source*/ Type.path,
|
||||
/*dest*/ Type.path);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "lseek",
|
||||
"lseek", ctypes.default_abi,
|
||||
/*return*/ Type.off_t,
|
||||
/*fd*/ Type.fd,
|
||||
/*offset*/ Type.off_t,
|
||||
/*whence*/ Type.int);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "mkdir",
|
||||
"mkdir", ctypes.default_abi,
|
||||
/*return*/ Type.int,
|
||||
/*path*/ Type.path,
|
||||
/*mode*/ Type.int);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "mkstemp",
|
||||
"mkstemp", ctypes.default_abi,
|
||||
/*return*/ Type.fd,
|
||||
/*template*/ Type.out_path);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "open",
|
||||
"open", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_fd,
|
||||
/*path*/ Type.path,
|
||||
/*oflags*/ Type.int,
|
||||
/*mode*/ Type.int);
|
||||
|
||||
if (OS.Constants.Sys.Name == "NetBSD") {
|
||||
libc.declareLazyFFI(SysFile, "opendir",
|
||||
"__opendir30", ctypes.default_abi,
|
||||
/*return*/ Type.null_or_DIR_ptr,
|
||||
/*path*/ Type.path);
|
||||
} else {
|
||||
libc.declareLazyFFI(SysFile, "opendir",
|
||||
"opendir", ctypes.default_abi,
|
||||
/*return*/ Type.null_or_DIR_ptr,
|
||||
/*path*/ Type.path);
|
||||
}
|
||||
|
||||
libc.declareLazyFFI(SysFile, "pread",
|
||||
"pread", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_ssize_t,
|
||||
/*fd*/ Type.fd,
|
||||
/*buf*/ Type.void_t.out_ptr,
|
||||
/*nbytes*/ Type.size_t,
|
||||
/*offset*/ Type.off_t);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "pwrite",
|
||||
"pwrite", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_ssize_t,
|
||||
/*fd*/ Type.fd,
|
||||
/*buf*/ Type.void_t.in_ptr,
|
||||
/*nbytes*/ Type.size_t,
|
||||
/*offset*/ Type.off_t);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "read",
|
||||
"read", ctypes.default_abi,
|
||||
/*return*/Type.negativeone_or_ssize_t,
|
||||
/*fd*/ Type.fd,
|
||||
/*buf*/ Type.void_t.out_ptr,
|
||||
/*nbytes*/Type.size_t);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "posix_fadvise",
|
||||
"posix_fadvise", ctypes.default_abi,
|
||||
/*return*/ Type.int,
|
||||
/*fd*/ Type.fd,
|
||||
/*offset*/ Type.off_t,
|
||||
/*len*/ Type.off_t,
|
||||
/*advise*/ Type.int);
|
||||
|
||||
if (Const._DARWIN_FEATURE_64_BIT_INODE) {
|
||||
// Special case for MacOS X 10.5+
|
||||
// Symbol name "readdir" still exists but is used for a
|
||||
// deprecated function that does not match the
|
||||
// constants of |Const|.
|
||||
libc.declareLazyFFI(SysFile, "readdir",
|
||||
"readdir$INODE64", ctypes.default_abi,
|
||||
/*return*/ Type.null_or_dirent_ptr,
|
||||
/*dir*/ Type.DIR.in_ptr); // For MacOS X
|
||||
} else if (OS.Constants.Sys.Name == "NetBSD") {
|
||||
libc.declareLazyFFI(SysFile, "readdir",
|
||||
"__readdir30", ctypes.default_abi,
|
||||
/*return*/Type.null_or_dirent_ptr,
|
||||
/*dir*/ Type.DIR.in_ptr); // Other Unices
|
||||
} else {
|
||||
libc.declareLazyFFI(SysFile, "readdir",
|
||||
"readdir", ctypes.default_abi,
|
||||
/*return*/Type.null_or_dirent_ptr,
|
||||
/*dir*/ Type.DIR.in_ptr); // Other Unices
|
||||
}
|
||||
|
||||
libc.declareLazyFFI(SysFile, "rename",
|
||||
"rename", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*old*/ Type.path,
|
||||
/*new*/ Type.path);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "rmdir",
|
||||
"rmdir", ctypes.default_abi,
|
||||
/*return*/ Type.int,
|
||||
/*path*/ Type.path);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "splice",
|
||||
"splice", ctypes.default_abi,
|
||||
/*return*/ Type.long,
|
||||
/*fd_in*/ Type.fd,
|
||||
/*off_in*/ Type.off_t.in_ptr,
|
||||
/*fd_out*/ Type.fd,
|
||||
/*off_out*/Type.off_t.in_ptr,
|
||||
/*len*/ Type.size_t,
|
||||
/*flags*/ Type.unsigned_int); // Linux/Android-specific
|
||||
|
||||
libc.declareLazyFFI(SysFile, "statfs",
|
||||
"statfs", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*path*/ Type.path,
|
||||
/*buf*/ Type.statvfs.out_ptr); // Android,B2G
|
||||
|
||||
libc.declareLazyFFI(SysFile, "statvfs",
|
||||
"statvfs", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*path*/ Type.path,
|
||||
/*buf*/ Type.statvfs.out_ptr); // Other platforms
|
||||
|
||||
libc.declareLazyFFI(SysFile, "symlink",
|
||||
"symlink", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*source*/ Type.path,
|
||||
/*dest*/ Type.path);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "truncate",
|
||||
"truncate", ctypes.default_abi,
|
||||
/*return*/Type.negativeone_or_nothing,
|
||||
/*path*/ Type.path,
|
||||
/*length*/ Type.off_t);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "unlink",
|
||||
"unlink", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*path*/ Type.path);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "write",
|
||||
"write", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_ssize_t,
|
||||
/*fd*/ Type.fd,
|
||||
/*buf*/ Type.void_t.in_ptr,
|
||||
/*nbytes*/ Type.size_t);
|
||||
|
||||
// Weird cases that require special treatment
|
||||
|
||||
// OSes use a variety of hacks to differentiate between
|
||||
// 32-bits and 64-bits versions of |stat|, |lstat|, |fstat|.
|
||||
if (Const._DARWIN_FEATURE_64_BIT_INODE) {
|
||||
// MacOS X 64-bits
|
||||
libc.declareLazyFFI(SysFile, "stat",
|
||||
"stat$INODE64", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*path*/ Type.path,
|
||||
/*buf*/ Type.stat.out_ptr
|
||||
);
|
||||
libc.declareLazyFFI(SysFile, "lstat",
|
||||
"lstat$INODE64", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*path*/ Type.path,
|
||||
/*buf*/ Type.stat.out_ptr
|
||||
);
|
||||
libc.declareLazyFFI(SysFile, "fstat",
|
||||
"fstat$INODE64", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*path*/ Type.fd,
|
||||
/*buf*/ Type.stat.out_ptr
|
||||
);
|
||||
} else if (Const._STAT_VER != undefined) {
|
||||
const ver = Const._STAT_VER;
|
||||
let xstat_name, lxstat_name, fxstat_name;
|
||||
if (OS.Constants.Sys.Name == "SunOS") {
|
||||
// Solaris
|
||||
xstat_name = "_xstat";
|
||||
lxstat_name = "_lxstat";
|
||||
fxstat_name = "_fxstat";
|
||||
} else {
|
||||
// Linux, all widths
|
||||
xstat_name = "__xstat";
|
||||
lxstat_name = "__lxstat";
|
||||
fxstat_name = "__fxstat";
|
||||
}
|
||||
|
||||
let Stat = {};
|
||||
libc.declareLazyFFI(Stat, "xstat",
|
||||
xstat_name, ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*_stat_ver*/ Type.int,
|
||||
/*path*/ Type.path,
|
||||
/*buf*/ Type.stat.out_ptr);
|
||||
libc.declareLazyFFI(Stat, "lxstat",
|
||||
lxstat_name, ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*_stat_ver*/ Type.int,
|
||||
/*path*/ Type.path,
|
||||
/*buf*/ Type.stat.out_ptr);
|
||||
libc.declareLazyFFI(Stat, "fxstat",
|
||||
fxstat_name, ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*_stat_ver*/ Type.int,
|
||||
/*fd*/ Type.fd,
|
||||
/*buf*/ Type.stat.out_ptr);
|
||||
|
||||
|
||||
SysFile.stat = function stat(path, buf) {
|
||||
return Stat.xstat(ver, path, buf);
|
||||
};
|
||||
|
||||
SysFile.lstat = function lstat(path, buf) {
|
||||
return Stat.lxstat(ver, path, buf);
|
||||
};
|
||||
|
||||
SysFile.fstat = function fstat(fd, buf) {
|
||||
return Stat.fxstat(ver, fd, buf);
|
||||
};
|
||||
} else if (OS.Constants.Sys.Name == "NetBSD") {
|
||||
// NetBSD 5.0 and newer
|
||||
libc.declareLazyFFI(SysFile, "stat",
|
||||
"__stat50", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*path*/ Type.path,
|
||||
/*buf*/ Type.stat.out_ptr
|
||||
);
|
||||
libc.declareLazyFFI(SysFile, "lstat",
|
||||
"__lstat50", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*path*/ Type.path,
|
||||
/*buf*/ Type.stat.out_ptr
|
||||
);
|
||||
libc.declareLazyFFI(SysFile, "fstat",
|
||||
"__fstat50", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*fd*/ Type.fd,
|
||||
/*buf*/ Type.stat.out_ptr
|
||||
);
|
||||
} else {
|
||||
// Mac OS X 32-bits, other Unix
|
||||
libc.declareLazyFFI(SysFile, "stat",
|
||||
"stat", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*path*/ Type.path,
|
||||
/*buf*/ Type.stat.out_ptr
|
||||
);
|
||||
libc.declareLazyFFI(SysFile, "lstat",
|
||||
"lstat", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*path*/ Type.path,
|
||||
/*buf*/ Type.stat.out_ptr
|
||||
);
|
||||
libc.declareLazyFFI(SysFile, "fstat",
|
||||
"fstat", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*fd*/ Type.fd,
|
||||
/*buf*/ Type.stat.out_ptr
|
||||
);
|
||||
}
|
||||
|
||||
// We cannot make a C array of CDataFinalizer, so
|
||||
// pipe cannot be directly defined as a C function.
|
||||
|
||||
let Pipe = {};
|
||||
libc.declareLazyFFI(Pipe, "_pipe",
|
||||
"pipe", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*fds*/ new SharedAll.Type("two file descriptors",
|
||||
ctypes.ArrayType(ctypes.int, 2)));
|
||||
|
||||
// A shared per-thread buffer used to communicate with |pipe|
|
||||
let _pipebuf = new (ctypes.ArrayType(ctypes.int, 2))();
|
||||
|
||||
SysFile.pipe = function pipe(array) {
|
||||
let result = Pipe._pipe(_pipebuf);
|
||||
if (result == -1) {
|
||||
return result;
|
||||
}
|
||||
array[0] = ctypes.CDataFinalizer(_pipebuf[0], SysFile._close);
|
||||
array[1] = ctypes.CDataFinalizer(_pipebuf[1], SysFile._close);
|
||||
return result;
|
||||
};
|
||||
|
||||
if (OS.Constants.Sys.Name == "NetBSD") {
|
||||
libc.declareLazyFFI(SysFile, "utimes",
|
||||
"__utimes50", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*path*/ Type.path,
|
||||
/*timeval[2]*/ Type.timevals.out_ptr
|
||||
);
|
||||
} else {
|
||||
libc.declareLazyFFI(SysFile, "utimes",
|
||||
"utimes", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*path*/ Type.path,
|
||||
/*timeval[2]*/ Type.timevals.out_ptr
|
||||
);
|
||||
}
|
||||
if (OS.Constants.Sys.Name == "NetBSD") {
|
||||
libc.declareLazyFFI(SysFile, "futimes",
|
||||
"__futimes50", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*fd*/ Type.fd,
|
||||
/*timeval[2]*/ Type.timevals.out_ptr
|
||||
);
|
||||
} else {
|
||||
libc.declareLazyFFI(SysFile, "futimes",
|
||||
"futimes", ctypes.default_abi,
|
||||
/*return*/ Type.negativeone_or_nothing,
|
||||
/*fd*/ Type.fd,
|
||||
/*timeval[2]*/ Type.timevals.out_ptr
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
exports.OS.Unix = {
|
||||
File: {
|
||||
_init: init
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
}
|
||||
1193
toolkit/components/osfile/modules/osfile_unix_front.jsm
Normal file
1193
toolkit/components/osfile/modules/osfile_unix_front.jsm
Normal file
File diff suppressed because it is too large
Load diff
425
toolkit/components/osfile/modules/osfile_win_allthreads.jsm
Normal file
425
toolkit/components/osfile/modules/osfile_win_allthreads.jsm
Normal file
|
|
@ -0,0 +1,425 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* This module defines the thread-agnostic components of the Win version
|
||||
* of OS.File. It depends on the thread-agnostic cross-platform components
|
||||
* of OS.File.
|
||||
*
|
||||
* It serves the following purposes:
|
||||
* - open kernel32;
|
||||
* - define OS.Shared.Win.Error;
|
||||
* - define a few constants and types that need to be defined on all platforms.
|
||||
*
|
||||
* This module can be:
|
||||
* - opened from the main thread as a jsm module;
|
||||
* - opened from a chrome worker through require().
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var SharedAll;
|
||||
if (typeof Components != "undefined") {
|
||||
let Cu = Components.utils;
|
||||
// Module is opened as a jsm module
|
||||
Cu.import("resource://gre/modules/ctypes.jsm", this);
|
||||
|
||||
SharedAll = {};
|
||||
Cu.import("resource://gre/modules/osfile/osfile_shared_allthreads.jsm", SharedAll);
|
||||
this.exports = {};
|
||||
} else if (typeof module != "undefined" && typeof require != "undefined") {
|
||||
// Module is loaded with require()
|
||||
SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
|
||||
} else {
|
||||
throw new Error("Please open this module with Component.utils.import or with require()");
|
||||
}
|
||||
|
||||
var LOG = SharedAll.LOG.bind(SharedAll, "Win", "allthreads");
|
||||
var Const = SharedAll.Constants.Win;
|
||||
|
||||
// Open libc
|
||||
var libc = new SharedAll.Library("libc", "kernel32.dll");
|
||||
exports.libc = libc;
|
||||
|
||||
// Define declareFFI
|
||||
var declareFFI = SharedAll.declareFFI.bind(null, libc);
|
||||
exports.declareFFI = declareFFI;
|
||||
|
||||
var Scope = {};
|
||||
|
||||
// Define Error
|
||||
libc.declareLazy(Scope, "FormatMessage",
|
||||
"FormatMessageW", ctypes.winapi_abi,
|
||||
/*return*/ ctypes.uint32_t,
|
||||
/*flags*/ ctypes.uint32_t,
|
||||
/*source*/ ctypes.voidptr_t,
|
||||
/*msgid*/ ctypes.uint32_t,
|
||||
/*langid*/ ctypes.uint32_t,
|
||||
/*buf*/ ctypes.char16_t.ptr,
|
||||
/*size*/ ctypes.uint32_t,
|
||||
/*Arguments*/ctypes.voidptr_t);
|
||||
|
||||
/**
|
||||
* A File-related error.
|
||||
*
|
||||
* To obtain a human-readable error message, use method |toString|.
|
||||
* To determine the cause of the error, use the various |becauseX|
|
||||
* getters. To determine the operation that failed, use field
|
||||
* |operation|.
|
||||
*
|
||||
* Additionally, this implementation offers a field
|
||||
* |winLastError|, which holds the OS-specific error
|
||||
* constant. If you need this level of detail, you may match the value
|
||||
* of this field against the error constants of |OS.Constants.Win|.
|
||||
*
|
||||
* @param {string=} operation The operation that failed. If unspecified,
|
||||
* the name of the calling function is taken to be the operation that
|
||||
* failed.
|
||||
* @param {number=} lastError The OS-specific constant detailing the
|
||||
* reason of the error. If unspecified, this is fetched from the system
|
||||
* status.
|
||||
* @param {string=} path The file path that manipulated. If unspecified,
|
||||
* assign the empty string.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {OS.Shared.Error}
|
||||
*/
|
||||
var OSError = function OSError(operation = "unknown operation",
|
||||
lastError = ctypes.winLastError, path = "") {
|
||||
operation = operation;
|
||||
SharedAll.OSError.call(this, operation, path);
|
||||
this.winLastError = lastError;
|
||||
};
|
||||
OSError.prototype = Object.create(SharedAll.OSError.prototype);
|
||||
OSError.prototype.toString = function toString() {
|
||||
let buf = new (ctypes.ArrayType(ctypes.char16_t, 1024))();
|
||||
let result = Scope.FormatMessage(
|
||||
Const.FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
Const.FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
null,
|
||||
/* The error number */ this.winLastError,
|
||||
/* Default language */ 0,
|
||||
/* Output buffer*/ buf,
|
||||
/* Minimum size of buffer */ 1024,
|
||||
/* Format args*/ null
|
||||
);
|
||||
if (!result) {
|
||||
buf = "additional error " +
|
||||
ctypes.winLastError +
|
||||
" while fetching system error message";
|
||||
}
|
||||
return "Win error " + this.winLastError + " during operation "
|
||||
+ this.operation + (this.path? " on file " + this.path : "") +
|
||||
" (" + buf.readString() + ")";
|
||||
};
|
||||
OSError.prototype.toMsg = function toMsg() {
|
||||
return OSError.toMsg(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* |true| if the error was raised because a file or directory
|
||||
* already exists, |false| otherwise.
|
||||
*/
|
||||
Object.defineProperty(OSError.prototype, "becauseExists", {
|
||||
get: function becauseExists() {
|
||||
return this.winLastError == Const.ERROR_FILE_EXISTS ||
|
||||
this.winLastError == Const.ERROR_ALREADY_EXISTS;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* |true| if the error was raised because a file or directory
|
||||
* does not exist, |false| otherwise.
|
||||
*/
|
||||
Object.defineProperty(OSError.prototype, "becauseNoSuchFile", {
|
||||
get: function becauseNoSuchFile() {
|
||||
return this.winLastError == Const.ERROR_FILE_NOT_FOUND ||
|
||||
this.winLastError == Const.ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* |true| if the error was raised because a directory is not empty
|
||||
* does not exist, |false| otherwise.
|
||||
*/
|
||||
Object.defineProperty(OSError.prototype, "becauseNotEmpty", {
|
||||
get: function becauseNotEmpty() {
|
||||
return this.winLastError == Const.ERROR_DIR_NOT_EMPTY;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* |true| if the error was raised because a file or directory
|
||||
* is closed, |false| otherwise.
|
||||
*/
|
||||
Object.defineProperty(OSError.prototype, "becauseClosed", {
|
||||
get: function becauseClosed() {
|
||||
return this.winLastError == Const.ERROR_INVALID_HANDLE;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* |true| if the error was raised because permission is denied to
|
||||
* access a file or directory, |false| otherwise.
|
||||
*/
|
||||
Object.defineProperty(OSError.prototype, "becauseAccessDenied", {
|
||||
get: function becauseAccessDenied() {
|
||||
return this.winLastError == Const.ERROR_ACCESS_DENIED;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* |true| if the error was raised because some invalid argument was passed,
|
||||
* |false| otherwise.
|
||||
*/
|
||||
Object.defineProperty(OSError.prototype, "becauseInvalidArgument", {
|
||||
get: function becauseInvalidArgument() {
|
||||
return this.winLastError == Const.ERROR_NOT_SUPPORTED ||
|
||||
this.winLastError == Const.ERROR_BAD_ARGUMENTS;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Serialize an instance of OSError to something that can be
|
||||
* transmitted across threads (not necessarily a string).
|
||||
*/
|
||||
OSError.toMsg = function toMsg(error) {
|
||||
return {
|
||||
exn: "OS.File.Error",
|
||||
fileName: error.moduleName,
|
||||
lineNumber: error.lineNumber,
|
||||
stack: error.moduleStack,
|
||||
operation: error.operation,
|
||||
winLastError: error.winLastError,
|
||||
path: error.path
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Deserialize a message back to an instance of OSError
|
||||
*/
|
||||
OSError.fromMsg = function fromMsg(msg) {
|
||||
let error = new OSError(msg.operation, msg.winLastError, msg.path);
|
||||
error.stack = msg.stack;
|
||||
error.fileName = msg.fileName;
|
||||
error.lineNumber = msg.lineNumber;
|
||||
return error;
|
||||
};
|
||||
exports.Error = OSError;
|
||||
|
||||
/**
|
||||
* Code shared by implementation of File.Info on Windows
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var AbstractInfo = function AbstractInfo(path, isDir, isSymLink, size,
|
||||
winBirthDate,
|
||||
lastAccessDate, lastWriteDate,
|
||||
winAttributes) {
|
||||
this._path = path;
|
||||
this._isDir = isDir;
|
||||
this._isSymLink = isSymLink;
|
||||
this._size = size;
|
||||
this._winBirthDate = winBirthDate;
|
||||
this._lastAccessDate = lastAccessDate;
|
||||
this._lastModificationDate = lastWriteDate;
|
||||
this._winAttributes = winAttributes;
|
||||
};
|
||||
|
||||
AbstractInfo.prototype = {
|
||||
/**
|
||||
* The path of the file, used for error-reporting.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
get path() {
|
||||
return this._path;
|
||||
},
|
||||
/**
|
||||
* |true| if this file is a directory, |false| otherwise
|
||||
*/
|
||||
get isDir() {
|
||||
return this._isDir;
|
||||
},
|
||||
/**
|
||||
* |true| if this file is a symbolic link, |false| otherwise
|
||||
*/
|
||||
get isSymLink() {
|
||||
return this._isSymLink;
|
||||
},
|
||||
/**
|
||||
* The size of the file, in bytes.
|
||||
*
|
||||
* Note that the result may be |NaN| if the size of the file cannot be
|
||||
* represented in JavaScript.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
get size() {
|
||||
return this._size;
|
||||
},
|
||||
// Deprecated
|
||||
get creationDate() {
|
||||
return this._winBirthDate;
|
||||
},
|
||||
/**
|
||||
* The date of creation of this file.
|
||||
*
|
||||
* @type {Date}
|
||||
*/
|
||||
get winBirthDate() {
|
||||
return this._winBirthDate;
|
||||
},
|
||||
/**
|
||||
* The date of last access to this file.
|
||||
*
|
||||
* Note that the definition of last access may depend on the underlying
|
||||
* operating system and file system.
|
||||
*
|
||||
* @type {Date}
|
||||
*/
|
||||
get lastAccessDate() {
|
||||
return this._lastAccessDate;
|
||||
},
|
||||
/**
|
||||
* The date of last modification of this file.
|
||||
*
|
||||
* Note that the definition of last access may depend on the underlying
|
||||
* operating system and file system.
|
||||
*
|
||||
* @type {Date}
|
||||
*/
|
||||
get lastModificationDate() {
|
||||
return this._lastModificationDate;
|
||||
},
|
||||
/**
|
||||
* The Object with following boolean properties of this file.
|
||||
* {readOnly, system, hidden}
|
||||
*
|
||||
* @type {object}
|
||||
*/
|
||||
get winAttributes() {
|
||||
return this._winAttributes;
|
||||
}
|
||||
};
|
||||
exports.AbstractInfo = AbstractInfo;
|
||||
|
||||
/**
|
||||
* Code shared by implementation of File.DirectoryIterator.Entry on Windows
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var AbstractEntry = function AbstractEntry(isDir, isSymLink, name,
|
||||
winCreationDate, winLastWriteDate,
|
||||
winLastAccessDate, path) {
|
||||
this._isDir = isDir;
|
||||
this._isSymLink = isSymLink;
|
||||
this._name = name;
|
||||
this._winCreationDate = winCreationDate;
|
||||
this._winLastWriteDate = winLastWriteDate;
|
||||
this._winLastAccessDate = winLastAccessDate;
|
||||
this._path = path;
|
||||
};
|
||||
|
||||
AbstractEntry.prototype = {
|
||||
/**
|
||||
* |true| if the entry is a directory, |false| otherwise
|
||||
*/
|
||||
get isDir() {
|
||||
return this._isDir;
|
||||
},
|
||||
/**
|
||||
* |true| if the entry is a symbolic link, |false| otherwise
|
||||
*/
|
||||
get isSymLink() {
|
||||
return this._isSymLink;
|
||||
},
|
||||
/**
|
||||
* The name of the entry.
|
||||
* @type {string}
|
||||
*/
|
||||
get name() {
|
||||
return this._name;
|
||||
},
|
||||
/**
|
||||
* The creation time of this file.
|
||||
* @type {Date}
|
||||
*/
|
||||
get winCreationDate() {
|
||||
return this._winCreationDate;
|
||||
},
|
||||
/**
|
||||
* The last modification time of this file.
|
||||
* @type {Date}
|
||||
*/
|
||||
get winLastWriteDate() {
|
||||
return this._winLastWriteDate;
|
||||
},
|
||||
/**
|
||||
* The last access time of this file.
|
||||
* @type {Date}
|
||||
*/
|
||||
get winLastAccessDate() {
|
||||
return this._winLastAccessDate;
|
||||
},
|
||||
/**
|
||||
* The full path of the entry
|
||||
* @type {string}
|
||||
*/
|
||||
get path() {
|
||||
return this._path;
|
||||
}
|
||||
};
|
||||
exports.AbstractEntry = AbstractEntry;
|
||||
|
||||
// Special constants that need to be defined on all platforms
|
||||
|
||||
exports.POS_START = Const.FILE_BEGIN;
|
||||
exports.POS_CURRENT = Const.FILE_CURRENT;
|
||||
exports.POS_END = Const.FILE_END;
|
||||
|
||||
// Special types that need to be defined for communication
|
||||
// between threads
|
||||
var Type = Object.create(SharedAll.Type);
|
||||
exports.Type = Type;
|
||||
|
||||
/**
|
||||
* Native paths
|
||||
*
|
||||
* Under Windows, expressed as wide strings
|
||||
*/
|
||||
Type.path = Type.wstring.withName("[in] path");
|
||||
Type.out_path = Type.out_wstring.withName("[out] path");
|
||||
|
||||
// Special constructors that need to be defined on all threads
|
||||
OSError.closed = function closed(operation, path) {
|
||||
return new OSError(operation, Const.ERROR_INVALID_HANDLE, path);
|
||||
};
|
||||
|
||||
OSError.exists = function exists(operation, path) {
|
||||
return new OSError(operation, Const.ERROR_FILE_EXISTS, path);
|
||||
};
|
||||
|
||||
OSError.noSuchFile = function noSuchFile(operation, path) {
|
||||
return new OSError(operation, Const.ERROR_FILE_NOT_FOUND, path);
|
||||
};
|
||||
|
||||
OSError.invalidArgument = function invalidArgument(operation) {
|
||||
return new OSError(operation, Const.ERROR_NOT_SUPPORTED);
|
||||
};
|
||||
|
||||
var EXPORTED_SYMBOLS = [
|
||||
"declareFFI",
|
||||
"libc",
|
||||
"Error",
|
||||
"AbstractInfo",
|
||||
"AbstractEntry",
|
||||
"Type",
|
||||
"POS_START",
|
||||
"POS_CURRENT",
|
||||
"POS_END"
|
||||
];
|
||||
|
||||
//////////// Boilerplate
|
||||
if (typeof Components != "undefined") {
|
||||
this.EXPORTED_SYMBOLS = EXPORTED_SYMBOLS;
|
||||
for (let symbol of EXPORTED_SYMBOLS) {
|
||||
this[symbol] = exports[symbol];
|
||||
}
|
||||
}
|
||||
437
toolkit/components/osfile/modules/osfile_win_back.jsm
Normal file
437
toolkit/components/osfile/modules/osfile_win_back.jsm
Normal file
|
|
@ -0,0 +1,437 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* This file can be used in the following contexts:
|
||||
*
|
||||
* 1. included from a non-osfile worker thread using importScript
|
||||
* (it serves to define a synchronous API for that worker thread)
|
||||
* (bug 707681)
|
||||
*
|
||||
* 2. included from the main thread using Components.utils.import
|
||||
* (it serves to define the asynchronous API, whose implementation
|
||||
* resides in the worker thread)
|
||||
* (bug 729057)
|
||||
*
|
||||
* 3. included from the osfile worker thread using importScript
|
||||
* (it serves to define the implementation of the asynchronous API)
|
||||
* (bug 729057)
|
||||
*/
|
||||
|
||||
{
|
||||
if (typeof Components != "undefined") {
|
||||
// We do not wish osfile_win.jsm to be used directly as a main thread
|
||||
// module yet. When time comes, it will be loaded by a combination of
|
||||
// a main thread front-end/worker thread implementation that makes sure
|
||||
// that we are not executing synchronous IO code in the main thread.
|
||||
|
||||
throw new Error("osfile_win.jsm cannot be used from the main thread yet");
|
||||
}
|
||||
|
||||
(function(exports) {
|
||||
"use strict";
|
||||
if (exports.OS && exports.OS.Win && exports.OS.Win.File) {
|
||||
return; // Avoid double initialization
|
||||
}
|
||||
|
||||
let SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
|
||||
let SysAll = require("resource://gre/modules/osfile/osfile_win_allthreads.jsm");
|
||||
let LOG = SharedAll.LOG.bind(SharedAll, "Unix", "back");
|
||||
let libc = SysAll.libc;
|
||||
let advapi32 = new SharedAll.Library("advapi32", "advapi32.dll");
|
||||
let Const = SharedAll.Constants.Win;
|
||||
|
||||
/**
|
||||
* Initialize the Windows module.
|
||||
*
|
||||
* @param {function=} declareFFI
|
||||
*/
|
||||
// FIXME: Both |init| and |aDeclareFFI| are deprecated, we should remove them
|
||||
let init = function init(aDeclareFFI) {
|
||||
let declareFFI;
|
||||
if (aDeclareFFI) {
|
||||
declareFFI = aDeclareFFI.bind(null, libc);
|
||||
} else {
|
||||
declareFFI = SysAll.declareFFI;
|
||||
}
|
||||
let declareLazyFFI = SharedAll.declareLazyFFI;
|
||||
|
||||
// Initialize types that require additional OS-specific
|
||||
// support - either finalization or matching against
|
||||
// OS-specific constants.
|
||||
let Type = Object.create(SysAll.Type);
|
||||
let SysFile = exports.OS.Win.File = { Type: Type };
|
||||
|
||||
// Initialize types
|
||||
|
||||
/**
|
||||
* A C integer holding INVALID_HANDLE_VALUE in case of error or
|
||||
* a file descriptor in case of success.
|
||||
*/
|
||||
Type.HANDLE =
|
||||
Type.voidptr_t.withName("HANDLE");
|
||||
Type.HANDLE.importFromC = function importFromC(maybe) {
|
||||
if (Type.int.cast(maybe).value == INVALID_HANDLE) {
|
||||
// Ensure that API clients can effectively compare against
|
||||
// Const.INVALID_HANDLE_VALUE. Without this cast,
|
||||
// == would always return |false|.
|
||||
return INVALID_HANDLE;
|
||||
}
|
||||
return ctypes.CDataFinalizer(maybe, this.finalizeHANDLE);
|
||||
};
|
||||
Type.HANDLE.finalizeHANDLE = function placeholder() {
|
||||
throw new Error("finalizeHANDLE should be implemented");
|
||||
};
|
||||
let INVALID_HANDLE = Const.INVALID_HANDLE_VALUE;
|
||||
|
||||
Type.file_HANDLE = Type.HANDLE.withName("file HANDLE");
|
||||
SharedAll.defineLazyGetter(Type.file_HANDLE,
|
||||
"finalizeHANDLE",
|
||||
function() {
|
||||
return SysFile._CloseHandle;
|
||||
});
|
||||
|
||||
Type.find_HANDLE = Type.HANDLE.withName("find HANDLE");
|
||||
SharedAll.defineLazyGetter(Type.find_HANDLE,
|
||||
"finalizeHANDLE",
|
||||
function() {
|
||||
return SysFile._FindClose;
|
||||
});
|
||||
|
||||
Type.DWORD = Type.uint32_t.withName("DWORD");
|
||||
|
||||
/* A special type used to represent flags passed as DWORDs to a function.
|
||||
* In JavaScript, bitwise manipulation of numbers, such as or-ing flags,
|
||||
* can produce negative numbers. Since DWORD is unsigned, these negative
|
||||
* numbers simply cannot be converted to DWORD. For this reason, whenever
|
||||
* bit manipulation is called for, you should rather use DWORD_FLAGS,
|
||||
* which is represented as a signed integer, hence has the correct
|
||||
* semantics.
|
||||
*/
|
||||
Type.DWORD_FLAGS = Type.int32_t.withName("DWORD_FLAGS");
|
||||
|
||||
/**
|
||||
* A C integer holding 0 in case of error or a positive integer
|
||||
* in case of success.
|
||||
*/
|
||||
Type.zero_or_DWORD =
|
||||
Type.DWORD.withName("zero_or_DWORD");
|
||||
|
||||
/**
|
||||
* A C integer holding 0 in case of error, any other value in
|
||||
* case of success.
|
||||
*/
|
||||
Type.zero_or_nothing =
|
||||
Type.int.withName("zero_or_nothing");
|
||||
|
||||
/**
|
||||
* A C integer holding flags related to NTFS security.
|
||||
*/
|
||||
Type.SECURITY_ATTRIBUTES =
|
||||
Type.void_t.withName("SECURITY_ATTRIBUTES");
|
||||
|
||||
/**
|
||||
* A C integer holding pointers related to NTFS security.
|
||||
*/
|
||||
Type.PSID =
|
||||
Type.voidptr_t.withName("PSID");
|
||||
|
||||
Type.PACL =
|
||||
Type.voidptr_t.withName("PACL");
|
||||
|
||||
Type.PSECURITY_DESCRIPTOR =
|
||||
Type.voidptr_t.withName("PSECURITY_DESCRIPTOR");
|
||||
|
||||
/**
|
||||
* A C integer holding Win32 local memory handle.
|
||||
*/
|
||||
Type.HLOCAL =
|
||||
Type.voidptr_t.withName("HLOCAL");
|
||||
|
||||
Type.FILETIME =
|
||||
new SharedAll.Type("FILETIME",
|
||||
ctypes.StructType("FILETIME", [
|
||||
{ lo: Type.DWORD.implementation },
|
||||
{ hi: Type.DWORD.implementation }]));
|
||||
|
||||
Type.FindData =
|
||||
new SharedAll.Type("FIND_DATA",
|
||||
ctypes.StructType("FIND_DATA", [
|
||||
{ dwFileAttributes: ctypes.uint32_t },
|
||||
{ ftCreationTime: Type.FILETIME.implementation },
|
||||
{ ftLastAccessTime: Type.FILETIME.implementation },
|
||||
{ ftLastWriteTime: Type.FILETIME.implementation },
|
||||
{ nFileSizeHigh: Type.DWORD.implementation },
|
||||
{ nFileSizeLow: Type.DWORD.implementation },
|
||||
{ dwReserved0: Type.DWORD.implementation },
|
||||
{ dwReserved1: Type.DWORD.implementation },
|
||||
{ cFileName: ctypes.ArrayType(ctypes.char16_t, Const.MAX_PATH) },
|
||||
{ cAlternateFileName: ctypes.ArrayType(ctypes.char16_t, 14) }
|
||||
]));
|
||||
|
||||
Type.FILE_INFORMATION =
|
||||
new SharedAll.Type("FILE_INFORMATION",
|
||||
ctypes.StructType("FILE_INFORMATION", [
|
||||
{ dwFileAttributes: ctypes.uint32_t },
|
||||
{ ftCreationTime: Type.FILETIME.implementation },
|
||||
{ ftLastAccessTime: Type.FILETIME.implementation },
|
||||
{ ftLastWriteTime: Type.FILETIME.implementation },
|
||||
{ dwVolumeSerialNumber: ctypes.uint32_t },
|
||||
{ nFileSizeHigh: Type.DWORD.implementation },
|
||||
{ nFileSizeLow: Type.DWORD.implementation },
|
||||
{ nNumberOfLinks: ctypes.uint32_t },
|
||||
{ nFileIndex: ctypes.uint64_t }
|
||||
]));
|
||||
|
||||
Type.SystemTime =
|
||||
new SharedAll.Type("SystemTime",
|
||||
ctypes.StructType("SystemTime", [
|
||||
{ wYear: ctypes.int16_t },
|
||||
{ wMonth: ctypes.int16_t },
|
||||
{ wDayOfWeek: ctypes.int16_t },
|
||||
{ wDay: ctypes.int16_t },
|
||||
{ wHour: ctypes.int16_t },
|
||||
{ wMinute: ctypes.int16_t },
|
||||
{ wSecond: ctypes.int16_t },
|
||||
{ wMilliSeconds: ctypes.int16_t }
|
||||
]));
|
||||
|
||||
// Special case: these functions are used by the
|
||||
// finalizer
|
||||
libc.declareLazy(SysFile, "_CloseHandle",
|
||||
"CloseHandle", ctypes.winapi_abi,
|
||||
/*return */ctypes.bool,
|
||||
/*handle*/ ctypes.voidptr_t);
|
||||
|
||||
SysFile.CloseHandle = function(fd) {
|
||||
if (fd == INVALID_HANDLE) {
|
||||
return true;
|
||||
} else {
|
||||
return fd.dispose(); // Returns the value of |CloseHandle|.
|
||||
}
|
||||
};
|
||||
|
||||
libc.declareLazy(SysFile, "_FindClose",
|
||||
"FindClose", ctypes.winapi_abi,
|
||||
/*return */ctypes.bool,
|
||||
/*handle*/ ctypes.voidptr_t);
|
||||
|
||||
SysFile.FindClose = function(handle) {
|
||||
if (handle == INVALID_HANDLE) {
|
||||
return true;
|
||||
} else {
|
||||
return handle.dispose(); // Returns the value of |FindClose|.
|
||||
}
|
||||
};
|
||||
|
||||
// Declare libc functions as functions of |OS.Win.File|
|
||||
|
||||
libc.declareLazyFFI(SysFile, "CopyFile",
|
||||
"CopyFileW", ctypes.winapi_abi,
|
||||
/*return*/ Type.zero_or_nothing,
|
||||
/*sourcePath*/ Type.path,
|
||||
/*destPath*/ Type.path,
|
||||
/*bailIfExist*/Type.bool);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "CreateDirectory",
|
||||
"CreateDirectoryW", ctypes.winapi_abi,
|
||||
/*return*/ Type.zero_or_nothing,
|
||||
/*name*/ Type.char16_t.in_ptr,
|
||||
/*security*/Type.SECURITY_ATTRIBUTES.in_ptr);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "CreateFile",
|
||||
"CreateFileW", ctypes.winapi_abi,
|
||||
/*return*/ Type.file_HANDLE,
|
||||
/*name*/ Type.path,
|
||||
/*access*/ Type.DWORD_FLAGS,
|
||||
/*share*/ Type.DWORD_FLAGS,
|
||||
/*security*/Type.SECURITY_ATTRIBUTES.in_ptr,
|
||||
/*creation*/Type.DWORD_FLAGS,
|
||||
/*flags*/ Type.DWORD_FLAGS,
|
||||
/*template*/Type.HANDLE);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "DeleteFile",
|
||||
"DeleteFileW", ctypes.winapi_abi,
|
||||
/*return*/ Type.zero_or_nothing,
|
||||
/*path*/ Type.path);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "FileTimeToSystemTime",
|
||||
"FileTimeToSystemTime", ctypes.winapi_abi,
|
||||
/*return*/ Type.zero_or_nothing,
|
||||
/*filetime*/Type.FILETIME.in_ptr,
|
||||
/*systime*/ Type.SystemTime.out_ptr);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "SystemTimeToFileTime",
|
||||
"SystemTimeToFileTime", ctypes.winapi_abi,
|
||||
/*return*/ Type.zero_or_nothing,
|
||||
/*systime*/ Type.SystemTime.in_ptr,
|
||||
/*filetime*/ Type.FILETIME.out_ptr);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "FindFirstFile",
|
||||
"FindFirstFileW", ctypes.winapi_abi,
|
||||
/*return*/ Type.find_HANDLE,
|
||||
/*pattern*/Type.path,
|
||||
/*data*/ Type.FindData.out_ptr);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "FindNextFile",
|
||||
"FindNextFileW", ctypes.winapi_abi,
|
||||
/*return*/ Type.zero_or_nothing,
|
||||
/*prev*/ Type.find_HANDLE,
|
||||
/*data*/ Type.FindData.out_ptr);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "FormatMessage",
|
||||
"FormatMessageW", ctypes.winapi_abi,
|
||||
/*return*/ Type.DWORD,
|
||||
/*flags*/ Type.DWORD_FLAGS,
|
||||
/*source*/ Type.void_t.in_ptr,
|
||||
/*msgid*/ Type.DWORD_FLAGS,
|
||||
/*langid*/ Type.DWORD_FLAGS,
|
||||
/*buf*/ Type.out_wstring,
|
||||
/*size*/ Type.DWORD,
|
||||
/*Arguments*/Type.void_t.in_ptr
|
||||
);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "GetCurrentDirectory",
|
||||
"GetCurrentDirectoryW", ctypes.winapi_abi,
|
||||
/*return*/ Type.zero_or_DWORD,
|
||||
/*length*/ Type.DWORD,
|
||||
/*buf*/ Type.out_path
|
||||
);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "GetFullPathName",
|
||||
"GetFullPathNameW", ctypes.winapi_abi,
|
||||
/*return*/ Type.zero_or_DWORD,
|
||||
/*fileName*/ Type.path,
|
||||
/*length*/ Type.DWORD,
|
||||
/*buf*/ Type.out_path,
|
||||
/*filePart*/ Type.DWORD
|
||||
);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "GetDiskFreeSpaceEx",
|
||||
"GetDiskFreeSpaceExW", ctypes.winapi_abi,
|
||||
/*return*/ Type.zero_or_nothing,
|
||||
/*directoryName*/ Type.path,
|
||||
/*freeBytesForUser*/ Type.uint64_t.out_ptr,
|
||||
/*totalBytesForUser*/ Type.uint64_t.out_ptr,
|
||||
/*freeTotalBytesOnDrive*/ Type.uint64_t.out_ptr);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "GetFileInformationByHandle",
|
||||
"GetFileInformationByHandle", ctypes.winapi_abi,
|
||||
/*return*/ Type.zero_or_nothing,
|
||||
/*handle*/ Type.HANDLE,
|
||||
/*info*/ Type.FILE_INFORMATION.out_ptr);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "MoveFileEx",
|
||||
"MoveFileExW", ctypes.winapi_abi,
|
||||
/*return*/ Type.zero_or_nothing,
|
||||
/*sourcePath*/ Type.path,
|
||||
/*destPath*/ Type.path,
|
||||
/*flags*/ Type.DWORD
|
||||
);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "ReadFile",
|
||||
"ReadFile", ctypes.winapi_abi,
|
||||
/*return*/ Type.zero_or_nothing,
|
||||
/*file*/ Type.HANDLE,
|
||||
/*buffer*/ Type.voidptr_t,
|
||||
/*nbytes*/ Type.DWORD,
|
||||
/*nbytes_read*/Type.DWORD.out_ptr,
|
||||
/*overlapped*/Type.void_t.inout_ptr // FIXME: Implement?
|
||||
);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "RemoveDirectory",
|
||||
"RemoveDirectoryW", ctypes.winapi_abi,
|
||||
/*return*/ Type.zero_or_nothing,
|
||||
/*path*/ Type.path);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "SetCurrentDirectory",
|
||||
"SetCurrentDirectoryW", ctypes.winapi_abi,
|
||||
/*return*/ Type.zero_or_nothing,
|
||||
/*path*/ Type.path
|
||||
);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "SetEndOfFile",
|
||||
"SetEndOfFile", ctypes.winapi_abi,
|
||||
/*return*/ Type.zero_or_nothing,
|
||||
/*file*/ Type.HANDLE);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "SetFilePointer",
|
||||
"SetFilePointer", ctypes.winapi_abi,
|
||||
/*return*/ Type.DWORD,
|
||||
/*file*/ Type.HANDLE,
|
||||
/*distlow*/Type.long,
|
||||
/*disthi*/ Type.long.in_ptr,
|
||||
/*method*/ Type.DWORD);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "SetFileTime",
|
||||
"SetFileTime", ctypes.winapi_abi,
|
||||
/*return*/ Type.zero_or_nothing,
|
||||
/*file*/ Type.HANDLE,
|
||||
/*creation*/ Type.FILETIME.in_ptr,
|
||||
/*access*/ Type.FILETIME.in_ptr,
|
||||
/*write*/ Type.FILETIME.in_ptr);
|
||||
|
||||
|
||||
libc.declareLazyFFI(SysFile, "WriteFile",
|
||||
"WriteFile", ctypes.winapi_abi,
|
||||
/*return*/ Type.zero_or_nothing,
|
||||
/*file*/ Type.HANDLE,
|
||||
/*buffer*/ Type.voidptr_t,
|
||||
/*nbytes*/ Type.DWORD,
|
||||
/*nbytes_wr*/Type.DWORD.out_ptr,
|
||||
/*overlapped*/Type.void_t.inout_ptr // FIXME: Implement?
|
||||
);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "FlushFileBuffers",
|
||||
"FlushFileBuffers", ctypes.winapi_abi,
|
||||
/*return*/ Type.zero_or_nothing,
|
||||
/*file*/ Type.HANDLE);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "GetFileAttributes",
|
||||
"GetFileAttributesW", ctypes.winapi_abi,
|
||||
/*return*/ Type.DWORD_FLAGS,
|
||||
/*fileName*/ Type.path);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "SetFileAttributes",
|
||||
"SetFileAttributesW", ctypes.winapi_abi,
|
||||
/*return*/ Type.zero_or_nothing,
|
||||
/*fileName*/ Type.path,
|
||||
/*fileAttributes*/ Type.DWORD_FLAGS);
|
||||
|
||||
advapi32.declareLazyFFI(SysFile, "GetNamedSecurityInfo",
|
||||
"GetNamedSecurityInfoW", ctypes.winapi_abi,
|
||||
/*return*/ Type.DWORD,
|
||||
/*objectName*/ Type.path,
|
||||
/*objectType*/ Type.DWORD,
|
||||
/*securityInfo*/ Type.DWORD,
|
||||
/*sidOwner*/ Type.PSID.out_ptr,
|
||||
/*sidGroup*/ Type.PSID.out_ptr,
|
||||
/*dacl*/ Type.PACL.out_ptr,
|
||||
/*sacl*/ Type.PACL.out_ptr,
|
||||
/*securityDesc*/ Type.PSECURITY_DESCRIPTOR.out_ptr);
|
||||
|
||||
advapi32.declareLazyFFI(SysFile, "SetNamedSecurityInfo",
|
||||
"SetNamedSecurityInfoW", ctypes.winapi_abi,
|
||||
/*return*/ Type.DWORD,
|
||||
/*objectName*/ Type.path,
|
||||
/*objectType*/ Type.DWORD,
|
||||
/*securityInfo*/ Type.DWORD,
|
||||
/*sidOwner*/ Type.PSID,
|
||||
/*sidGroup*/ Type.PSID,
|
||||
/*dacl*/ Type.PACL,
|
||||
/*sacl*/ Type.PACL);
|
||||
|
||||
libc.declareLazyFFI(SysFile, "LocalFree",
|
||||
"LocalFree", ctypes.winapi_abi,
|
||||
/*return*/ Type.HLOCAL,
|
||||
/*mem*/ Type.HLOCAL);
|
||||
};
|
||||
|
||||
exports.OS.Win = {
|
||||
File: {
|
||||
_init: init
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
}
|
||||
1266
toolkit/components/osfile/modules/osfile_win_front.jsm
Normal file
1266
toolkit/components/osfile/modules/osfile_win_front.jsm
Normal file
File diff suppressed because it is too large
Load diff
45
toolkit/components/osfile/modules/ospath.jsm
Normal file
45
toolkit/components/osfile/modules/ospath.jsm
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Handling native paths.
|
||||
*
|
||||
* This module contains a number of functions destined to simplify
|
||||
* working with native paths through a cross-platform API. Functions
|
||||
* of this module will only work with the following assumptions:
|
||||
*
|
||||
* - paths are valid;
|
||||
* - paths are defined with one of the grammars that this module can
|
||||
* parse (see later);
|
||||
* - all path concatenations go through function |join|.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
if (typeof Components == "undefined") {
|
||||
let Path;
|
||||
if (OS.Constants.Win) {
|
||||
Path = require("resource://gre/modules/osfile/ospath_win.jsm");
|
||||
} else {
|
||||
Path = require("resource://gre/modules/osfile/ospath_unix.jsm");
|
||||
}
|
||||
module.exports = Path;
|
||||
} else {
|
||||
let Cu = Components.utils;
|
||||
let Scope = {};
|
||||
Cu.import("resource://gre/modules/osfile/osfile_shared_allthreads.jsm", Scope);
|
||||
|
||||
let Path = {};
|
||||
if (Scope.OS.Constants.Win) {
|
||||
Cu.import("resource://gre/modules/osfile/ospath_win.jsm", Path);
|
||||
} else {
|
||||
Cu.import("resource://gre/modules/osfile/ospath_unix.jsm", Path);
|
||||
}
|
||||
|
||||
this.EXPORTED_SYMBOLS = [];
|
||||
for (let k in Path) {
|
||||
this.EXPORTED_SYMBOLS.push(k);
|
||||
this[k] = Path[k];
|
||||
}
|
||||
}
|
||||
202
toolkit/components/osfile/modules/ospath_unix.jsm
Normal file
202
toolkit/components/osfile/modules/ospath_unix.jsm
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Handling native paths.
|
||||
*
|
||||
* This module contains a number of functions destined to simplify
|
||||
* working with native paths through a cross-platform API. Functions
|
||||
* of this module will only work with the following assumptions:
|
||||
*
|
||||
* - paths are valid;
|
||||
* - paths are defined with one of the grammars that this module can
|
||||
* parse (see later);
|
||||
* - all path concatenations go through function |join|.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
// Boilerplate used to be able to import this module both from the main
|
||||
// thread and from worker threads.
|
||||
if (typeof Components != "undefined") {
|
||||
Components.utils.importGlobalProperties(["URL"]);
|
||||
// Global definition of |exports|, to keep everybody happy.
|
||||
// In non-main thread, |exports| is provided by the module
|
||||
// loader.
|
||||
this.exports = {};
|
||||
} else if (typeof module == "undefined" || typeof exports == "undefined") {
|
||||
throw new Error("Please load this module using require()");
|
||||
}
|
||||
|
||||
var EXPORTED_SYMBOLS = [
|
||||
"basename",
|
||||
"dirname",
|
||||
"join",
|
||||
"normalize",
|
||||
"split",
|
||||
"toFileURI",
|
||||
"fromFileURI",
|
||||
];
|
||||
|
||||
/**
|
||||
* Return the final part of the path.
|
||||
* The final part of the path is everything after the last "/".
|
||||
*/
|
||||
var basename = function(path) {
|
||||
return path.slice(path.lastIndexOf("/") + 1);
|
||||
};
|
||||
exports.basename = basename;
|
||||
|
||||
/**
|
||||
* Return the directory part of the path.
|
||||
* The directory part of the path is everything before the last
|
||||
* "/". If the last few characters of this part are also "/",
|
||||
* they are ignored.
|
||||
*
|
||||
* If the path contains no directory, return ".".
|
||||
*/
|
||||
var dirname = function(path) {
|
||||
let index = path.lastIndexOf("/");
|
||||
if (index == -1) {
|
||||
return ".";
|
||||
}
|
||||
while (index >= 0 && path[index] == "/") {
|
||||
--index;
|
||||
}
|
||||
return path.slice(0, index + 1);
|
||||
};
|
||||
exports.dirname = dirname;
|
||||
|
||||
/**
|
||||
* Join path components.
|
||||
* This is the recommended manner of getting the path of a file/subdirectory
|
||||
* in a directory.
|
||||
*
|
||||
* Example: Obtaining $TMP/foo/bar in an OS-independent manner
|
||||
* var tmpDir = OS.Constants.Path.tmpDir;
|
||||
* var path = OS.Path.join(tmpDir, "foo", "bar");
|
||||
*
|
||||
* Under Unix, this will return "/tmp/foo/bar".
|
||||
*
|
||||
* Empty components are ignored, i.e. `OS.Path.join("foo", "", "bar)` is the
|
||||
* same as `OS.Path.join("foo", "bar")`.
|
||||
*/
|
||||
var join = function(...path) {
|
||||
// If there is a path that starts with a "/", eliminate everything before
|
||||
let paths = [];
|
||||
for (let subpath of path) {
|
||||
if (subpath == null) {
|
||||
throw new TypeError("invalid path component");
|
||||
}
|
||||
if (subpath.length == 0) {
|
||||
continue;
|
||||
} else if (subpath[0] == "/") {
|
||||
paths = [subpath];
|
||||
} else {
|
||||
paths.push(subpath);
|
||||
}
|
||||
}
|
||||
return paths.join("/");
|
||||
};
|
||||
exports.join = join;
|
||||
|
||||
/**
|
||||
* Normalize a path by removing any unneeded ".", "..", "//".
|
||||
*/
|
||||
var normalize = function(path) {
|
||||
let stack = [];
|
||||
let absolute;
|
||||
if (path.length >= 0 && path[0] == "/") {
|
||||
absolute = true;
|
||||
} else {
|
||||
absolute = false;
|
||||
}
|
||||
path.split("/").forEach(function(v) {
|
||||
switch (v) {
|
||||
case "": case ".":// fallthrough
|
||||
break;
|
||||
case "..":
|
||||
if (stack.length == 0) {
|
||||
if (absolute) {
|
||||
throw new Error("Path is ill-formed: attempting to go past root");
|
||||
} else {
|
||||
stack.push("..");
|
||||
}
|
||||
} else {
|
||||
if (stack[stack.length - 1] == "..") {
|
||||
stack.push("..");
|
||||
} else {
|
||||
stack.pop();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
stack.push(v);
|
||||
}
|
||||
});
|
||||
let string = stack.join("/");
|
||||
return absolute ? "/" + string : string;
|
||||
};
|
||||
exports.normalize = normalize;
|
||||
|
||||
/**
|
||||
* Return the components of a path.
|
||||
* You should generally apply this function to a normalized path.
|
||||
*
|
||||
* @return {{
|
||||
* {bool} absolute |true| if the path is absolute, |false| otherwise
|
||||
* {array} components the string components of the path
|
||||
* }}
|
||||
*
|
||||
* Other implementations may add additional OS-specific informations.
|
||||
*/
|
||||
var split = function(path) {
|
||||
return {
|
||||
absolute: path.length && path[0] == "/",
|
||||
components: path.split("/")
|
||||
};
|
||||
};
|
||||
exports.split = split;
|
||||
|
||||
/**
|
||||
* Returns the file:// URI file path of the given local file path.
|
||||
*/
|
||||
// The case of %3b is designed to match Services.io, but fundamentally doesn't matter.
|
||||
var toFileURIExtraEncodings = {';': '%3b', '?': '%3F', '#': '%23'};
|
||||
var toFileURI = function toFileURI(path) {
|
||||
// Per https://url.spec.whatwg.org we should not encode [] in the path
|
||||
let dontNeedEscaping = {'%5B': '[', '%5D': ']'};
|
||||
let uri = encodeURI(this.normalize(path)).replace(/%(5B|5D)/gi,
|
||||
match => dontNeedEscaping[match]);
|
||||
|
||||
// add a prefix, and encodeURI doesn't escape a few characters that we do
|
||||
// want to escape, so fix that up
|
||||
let prefix = "file://";
|
||||
uri = prefix + uri.replace(/[;?#]/g, match => toFileURIExtraEncodings[match]);
|
||||
|
||||
return uri;
|
||||
};
|
||||
exports.toFileURI = toFileURI;
|
||||
|
||||
/**
|
||||
* Returns the local file path from a given file URI.
|
||||
*/
|
||||
var fromFileURI = function fromFileURI(uri) {
|
||||
let url = new URL(uri);
|
||||
if (url.protocol != 'file:') {
|
||||
throw new Error("fromFileURI expects a file URI");
|
||||
}
|
||||
let path = this.normalize(decodeURIComponent(url.pathname));
|
||||
return path;
|
||||
};
|
||||
exports.fromFileURI = fromFileURI;
|
||||
|
||||
|
||||
//////////// Boilerplate
|
||||
if (typeof Components != "undefined") {
|
||||
this.EXPORTED_SYMBOLS = EXPORTED_SYMBOLS;
|
||||
for (let symbol of EXPORTED_SYMBOLS) {
|
||||
this[symbol] = exports[symbol];
|
||||
}
|
||||
}
|
||||
373
toolkit/components/osfile/modules/ospath_win.jsm
Normal file
373
toolkit/components/osfile/modules/ospath_win.jsm
Normal file
|
|
@ -0,0 +1,373 @@
|
|||
/**
|
||||
* Handling native paths.
|
||||
*
|
||||
* This module contains a number of functions destined to simplify
|
||||
* working with native paths through a cross-platform API. Functions
|
||||
* of this module will only work with the following assumptions:
|
||||
*
|
||||
* - paths are valid;
|
||||
* - paths are defined with one of the grammars that this module can
|
||||
* parse (see later);
|
||||
* - all path concatenations go through function |join|.
|
||||
*
|
||||
* Limitations of this implementation.
|
||||
*
|
||||
* Windows supports 6 distinct grammars for paths. For the moment, this
|
||||
* implementation supports the following subset:
|
||||
*
|
||||
* - drivename:backslash-separated components
|
||||
* - backslash-separated components
|
||||
* - \\drivename\ followed by backslash-separated components
|
||||
*
|
||||
* Additionally, |normalize| can convert a path containing slash-
|
||||
* separated components to a path containing backslash-separated
|
||||
* components.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
// Boilerplate used to be able to import this module both from the main
|
||||
// thread and from worker threads.
|
||||
if (typeof Components != "undefined") {
|
||||
Components.utils.importGlobalProperties(["URL"]);
|
||||
// Global definition of |exports|, to keep everybody happy.
|
||||
// In non-main thread, |exports| is provided by the module
|
||||
// loader.
|
||||
this.exports = {};
|
||||
} else if (typeof module == "undefined" || typeof exports == "undefined") {
|
||||
throw new Error("Please load this module using require()");
|
||||
}
|
||||
|
||||
var EXPORTED_SYMBOLS = [
|
||||
"basename",
|
||||
"dirname",
|
||||
"join",
|
||||
"normalize",
|
||||
"split",
|
||||
"winGetDrive",
|
||||
"winIsAbsolute",
|
||||
"toFileURI",
|
||||
"fromFileURI",
|
||||
];
|
||||
|
||||
/**
|
||||
* Return the final part of the path.
|
||||
* The final part of the path is everything after the last "\\".
|
||||
*/
|
||||
var basename = function(path) {
|
||||
if (path.startsWith("\\\\")) {
|
||||
// UNC-style path
|
||||
let index = path.lastIndexOf("\\");
|
||||
if (index != 1) {
|
||||
return path.slice(index + 1);
|
||||
}
|
||||
return ""; // Degenerate case
|
||||
}
|
||||
return path.slice(Math.max(path.lastIndexOf("\\"),
|
||||
path.lastIndexOf(":")) + 1);
|
||||
};
|
||||
exports.basename = basename;
|
||||
|
||||
/**
|
||||
* Return the directory part of the path.
|
||||
*
|
||||
* If the path contains no directory, return the drive letter,
|
||||
* or "." if the path contains no drive letter or if option
|
||||
* |winNoDrive| is set.
|
||||
*
|
||||
* Otherwise, return everything before the last backslash,
|
||||
* including the drive/server name.
|
||||
*
|
||||
*
|
||||
* @param {string} path The path.
|
||||
* @param {*=} options Platform-specific options controlling the behavior
|
||||
* of this function. This implementation supports the following options:
|
||||
* - |winNoDrive| If |true|, also remove the letter from the path name.
|
||||
*/
|
||||
var dirname = function(path, options) {
|
||||
let noDrive = (options && options.winNoDrive);
|
||||
|
||||
// Find the last occurrence of "\\"
|
||||
let index = path.lastIndexOf("\\");
|
||||
if (index == -1) {
|
||||
// If there is no directory component...
|
||||
if (!noDrive) {
|
||||
// Return the drive path if possible, falling back to "."
|
||||
return this.winGetDrive(path) || ".";
|
||||
} else {
|
||||
// Or just "."
|
||||
return ".";
|
||||
}
|
||||
}
|
||||
|
||||
if (index == 1 && path.charAt(0) == "\\") {
|
||||
// The path is reduced to a UNC drive
|
||||
if (noDrive) {
|
||||
return ".";
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore any occurrence of "\\: immediately before that one
|
||||
while (index >= 0 && path[index] == "\\") {
|
||||
--index;
|
||||
}
|
||||
|
||||
// Compute what is left, removing the drive name if necessary
|
||||
let start;
|
||||
if (noDrive) {
|
||||
start = (this.winGetDrive(path) || "").length;
|
||||
} else {
|
||||
start = 0;
|
||||
}
|
||||
return path.slice(start, index + 1);
|
||||
};
|
||||
exports.dirname = dirname;
|
||||
|
||||
/**
|
||||
* Join path components.
|
||||
* This is the recommended manner of getting the path of a file/subdirectory
|
||||
* in a directory.
|
||||
*
|
||||
* Example: Obtaining $TMP/foo/bar in an OS-independent manner
|
||||
* var tmpDir = OS.Constants.Path.tmpDir;
|
||||
* var path = OS.Path.join(tmpDir, "foo", "bar");
|
||||
*
|
||||
* Under Windows, this will return "$TMP\foo\bar".
|
||||
*
|
||||
* Empty components are ignored, i.e. `OS.Path.join("foo", "", "bar)` is the
|
||||
* same as `OS.Path.join("foo", "bar")`.
|
||||
*/
|
||||
var join = function(...path) {
|
||||
let paths = [];
|
||||
let root;
|
||||
let absolute = false;
|
||||
for (let subpath of path) {
|
||||
if (subpath == null) {
|
||||
throw new TypeError("invalid path component");
|
||||
}
|
||||
if (subpath == "") {
|
||||
continue;
|
||||
}
|
||||
let drive = this.winGetDrive(subpath);
|
||||
if (drive) {
|
||||
root = drive;
|
||||
let component = trimBackslashes(subpath.slice(drive.length));
|
||||
if (component) {
|
||||
paths = [component];
|
||||
} else {
|
||||
paths = [];
|
||||
}
|
||||
absolute = true;
|
||||
} else if (this.winIsAbsolute(subpath)) {
|
||||
paths = [trimBackslashes(subpath)];
|
||||
absolute = true;
|
||||
} else {
|
||||
paths.push(trimBackslashes(subpath));
|
||||
}
|
||||
}
|
||||
let result = "";
|
||||
if (root) {
|
||||
result += root;
|
||||
}
|
||||
if (absolute) {
|
||||
result += "\\";
|
||||
}
|
||||
result += paths.join("\\");
|
||||
return result;
|
||||
};
|
||||
exports.join = join;
|
||||
|
||||
/**
|
||||
* Return the drive name of a path, or |null| if the path does
|
||||
* not contain a drive name.
|
||||
*
|
||||
* Drive name appear either as "DriveName:..." (the return drive
|
||||
* name includes the ":") or "\\\\DriveName..." (the returned drive name
|
||||
* includes "\\\\").
|
||||
*/
|
||||
var winGetDrive = function(path) {
|
||||
if (path == null) {
|
||||
throw new TypeError("path is invalid");
|
||||
}
|
||||
|
||||
if (path.startsWith("\\\\")) {
|
||||
// UNC path
|
||||
if (path.length == 2) {
|
||||
return null;
|
||||
}
|
||||
let index = path.indexOf("\\", 2);
|
||||
if (index == -1) {
|
||||
return path;
|
||||
}
|
||||
return path.slice(0, index);
|
||||
}
|
||||
// Non-UNC path
|
||||
let index = path.indexOf(":");
|
||||
if (index <= 0) return null;
|
||||
return path.slice(0, index + 1);
|
||||
};
|
||||
exports.winGetDrive = winGetDrive;
|
||||
|
||||
/**
|
||||
* Return |true| if the path is absolute, |false| otherwise.
|
||||
*
|
||||
* We consider that a path is absolute if it starts with "\\"
|
||||
* or "driveletter:\\".
|
||||
*/
|
||||
var winIsAbsolute = function(path) {
|
||||
let index = path.indexOf(":");
|
||||
return path.length > index + 1 && path[index + 1] == "\\";
|
||||
};
|
||||
exports.winIsAbsolute = winIsAbsolute;
|
||||
|
||||
/**
|
||||
* Normalize a path by removing any unneeded ".", "..", "\\".
|
||||
* Also convert any "/" to a "\\".
|
||||
*/
|
||||
var normalize = function(path) {
|
||||
let stack = [];
|
||||
|
||||
if (!path.startsWith("\\\\")) {
|
||||
// Normalize "/" to "\\"
|
||||
path = path.replace(/\//g, "\\");
|
||||
}
|
||||
|
||||
// Remove the drive (we will put it back at the end)
|
||||
let root = this.winGetDrive(path);
|
||||
if (root) {
|
||||
path = path.slice(root.length);
|
||||
}
|
||||
|
||||
// Remember whether we need to restore a leading "\\" or drive name.
|
||||
let absolute = this.winIsAbsolute(path);
|
||||
|
||||
// And now, fill |stack| from the components,
|
||||
// popping whenever there is a ".."
|
||||
path.split("\\").forEach(function loop(v) {
|
||||
switch (v) {
|
||||
case "": case ".": // Ignore
|
||||
break;
|
||||
case "..":
|
||||
if (stack.length == 0) {
|
||||
if (absolute) {
|
||||
throw new Error("Path is ill-formed: attempting to go past root");
|
||||
} else {
|
||||
stack.push("..");
|
||||
}
|
||||
} else {
|
||||
if (stack[stack.length - 1] == "..") {
|
||||
stack.push("..");
|
||||
} else {
|
||||
stack.pop();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
stack.push(v);
|
||||
}
|
||||
});
|
||||
|
||||
// Put everything back together
|
||||
let result = stack.join("\\");
|
||||
if (absolute || root) {
|
||||
result = "\\" + result;
|
||||
}
|
||||
if (root) {
|
||||
result = root + result;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
exports.normalize = normalize;
|
||||
|
||||
/**
|
||||
* Return the components of a path.
|
||||
* You should generally apply this function to a normalized path.
|
||||
*
|
||||
* @return {{
|
||||
* {bool} absolute |true| if the path is absolute, |false| otherwise
|
||||
* {array} components the string components of the path
|
||||
* {string?} winDrive the drive or server for this path
|
||||
* }}
|
||||
*
|
||||
* Other implementations may add additional OS-specific informations.
|
||||
*/
|
||||
var split = function(path) {
|
||||
return {
|
||||
absolute: this.winIsAbsolute(path),
|
||||
winDrive: this.winGetDrive(path),
|
||||
components: path.split("\\")
|
||||
};
|
||||
};
|
||||
exports.split = split;
|
||||
|
||||
/**
|
||||
* Return the file:// URI file path of the given local file path.
|
||||
*/
|
||||
// The case of %3b is designed to match Services.io, but fundamentally doesn't matter.
|
||||
var toFileURIExtraEncodings = {';': '%3b', '?': '%3F', '#': '%23'};
|
||||
var toFileURI = function toFileURI(path) {
|
||||
// URI-escape forward slashes and convert backward slashes to forward
|
||||
path = this.normalize(path).replace(/[\\\/]/g, m => (m=='\\')? '/' : '%2F');
|
||||
// Per https://url.spec.whatwg.org we should not encode [] in the path
|
||||
let dontNeedEscaping = {'%5B': '[', '%5D': ']'};
|
||||
let uri = encodeURI(path).replace(/%(5B|5D)/gi,
|
||||
match => dontNeedEscaping[match]);
|
||||
|
||||
// add a prefix, and encodeURI doesn't escape a few characters that we do
|
||||
// want to escape, so fix that up
|
||||
let prefix = "file:///";
|
||||
uri = prefix + uri.replace(/[;?#]/g, match => toFileURIExtraEncodings[match]);
|
||||
|
||||
// turn e.g., file:///C: into file:///C:/
|
||||
if (uri.charAt(uri.length - 1) === ':') {
|
||||
uri += "/"
|
||||
}
|
||||
|
||||
return uri;
|
||||
};
|
||||
exports.toFileURI = toFileURI;
|
||||
|
||||
/**
|
||||
* Returns the local file path from a given file URI.
|
||||
*/
|
||||
var fromFileURI = function fromFileURI(uri) {
|
||||
let url = new URL(uri);
|
||||
if (url.protocol != 'file:') {
|
||||
throw new Error("fromFileURI expects a file URI");
|
||||
}
|
||||
|
||||
// strip leading slash, since Windows paths don't start with one
|
||||
uri = url.pathname.substr(1);
|
||||
|
||||
let path = decodeURI(uri);
|
||||
// decode a few characters where URL's parsing is overzealous
|
||||
path = path.replace(/%(3b|3f|23)/gi,
|
||||
match => decodeURIComponent(match));
|
||||
path = this.normalize(path);
|
||||
|
||||
// this.normalize() does not remove the trailing slash if the path
|
||||
// component is a drive letter. eg. 'C:\'' will not get normalized.
|
||||
if (path.endsWith(":\\")) {
|
||||
path = path.substr(0, path.length - 1);
|
||||
}
|
||||
return this.normalize(path);
|
||||
};
|
||||
exports.fromFileURI = fromFileURI;
|
||||
|
||||
/**
|
||||
* Utility function: Remove any leading/trailing backslashes
|
||||
* from a string.
|
||||
*/
|
||||
var trimBackslashes = function trimBackslashes(string) {
|
||||
return string.replace(/^\\+|\\+$/g,'');
|
||||
};
|
||||
|
||||
//////////// Boilerplate
|
||||
if (typeof Components != "undefined") {
|
||||
this.EXPORTED_SYMBOLS = EXPORTED_SYMBOLS;
|
||||
for (let symbol of EXPORTED_SYMBOLS) {
|
||||
this[symbol] = exports[symbol];
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue