Rework fs.realpath, primordal windows compatibility
This commit is contained in:
parent
7c731ec9dd
commit
dea2331377
233
lib/fs.js
233
lib/fs.js
@ -487,149 +487,168 @@ fs.unwatchFile = function(filename) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Realpath
|
// Realpath
|
||||||
|
|
||||||
var path = require('path');
|
|
||||||
var normalize = path.normalize;
|
|
||||||
var normalizeArray = path.normalizeArray;
|
|
||||||
|
|
||||||
// realpath
|
|
||||||
// Not using realpath(2) because it's bad.
|
// Not using realpath(2) because it's bad.
|
||||||
// See: http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
|
// See: http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
|
||||||
fs.realpathSync = realpathSync;
|
|
||||||
fs.realpath = realpath;
|
var path = require('path'),
|
||||||
function realpathSync(p) {
|
normalize = path.normalize,
|
||||||
if (p.charAt(0) !== '/') {
|
isWindows = process.platform === 'win32';
|
||||||
p = path.join(process.cwd(), p);
|
|
||||||
}
|
if (isWindows) {
|
||||||
p = path.split(p);
|
// Node doesn't support symlinks / lstat on windows. Hence realpatch is just
|
||||||
var buf = [];
|
// the same as path.resolve that fails if the path doesn't exists.
|
||||||
var seenLinks = {};
|
|
||||||
var knownHard = {};
|
// windows version
|
||||||
|
fs.realpathSync = function realpathSync(p) {
|
||||||
|
var p = path.resolve(p);
|
||||||
|
fs.statSync(p);
|
||||||
|
return p;
|
||||||
|
};
|
||||||
|
|
||||||
|
// windows version
|
||||||
|
fs.realpath = function(p, cb) {
|
||||||
|
var p = path.resolve(p);
|
||||||
|
fs.stat(p, function(err) {
|
||||||
|
if (err) cb(err);
|
||||||
|
cb(null, p);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} else /* posix */ {
|
||||||
|
|
||||||
|
// Regexp that finds the next partion of a (partial) path
|
||||||
|
// result is [base_with_slash, base], e.g. ['somedir/', 'somedir']
|
||||||
|
var nextPartRe = /(.*?)(?:[\/]+|$)/g;
|
||||||
|
|
||||||
|
// posix version
|
||||||
|
fs.realpathSync = function realpathSync(p) {
|
||||||
|
// make p is absolute
|
||||||
|
p = path.resolve(p);
|
||||||
|
|
||||||
|
var seenLinks = {},
|
||||||
|
knownHard = {};
|
||||||
|
|
||||||
|
var pos = 0, // current character position in p
|
||||||
|
current = "", // the partial path so far, including a trailing slash if any
|
||||||
|
base = "", // the partial path without a trailing slash
|
||||||
|
previous = ""; // the partial path scanned in the previous round, with slash
|
||||||
|
|
||||||
// walk down the path, swapping out linked pathparts for their real
|
// walk down the path, swapping out linked pathparts for their real
|
||||||
// values, and pushing non-link path bits onto the buffer.
|
// values
|
||||||
// then return the buffer.
|
// NB: p.length changes.
|
||||||
// NB: path.length changes.
|
while (pos < p.length) {
|
||||||
for (var i = 0; i < p.length; i++) {
|
// find the next part
|
||||||
// skip over empty path parts.
|
nextPartRe.lastIndex = pos;
|
||||||
if (p[i] === '') continue;
|
var result = nextPartRe.exec(p);
|
||||||
|
previous = current;
|
||||||
|
current += result[0];
|
||||||
|
base = previous + result[1];
|
||||||
|
pos = nextPartRe.lastIndex;
|
||||||
|
|
||||||
var part = path.join.apply(path, buf.concat(p[i]));
|
// continue if not a symlink, or if root
|
||||||
|
if (!base || knownHard[base]) {
|
||||||
if (knownHard[part]) {
|
|
||||||
buf.push(p[i]);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
var stat = fs.lstatSync(base);
|
||||||
var stat = fs.lstatSync(part);
|
|
||||||
if (!stat.isSymbolicLink()) {
|
if (!stat.isSymbolicLink()) {
|
||||||
// not a symlink. easy.
|
knownHard[base] = true;
|
||||||
knownHard[part] = true;
|
|
||||||
buf.push(p[i]);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// read the link if it wasn't read before
|
||||||
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
|
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
|
||||||
if (!seenLinks[id]) {
|
if (!seenLinks[id]) {
|
||||||
fs.statSync(part);
|
fs.statSync(base);
|
||||||
seenLinks[id] = fs.readlinkSync(part);
|
seenLinks[id] = fs.readlinkSync(base);
|
||||||
}
|
}
|
||||||
|
|
||||||
var target = seenLinks[id];
|
// resolve the link, then start over
|
||||||
if (target.charAt(0) === '/') {
|
p = path.resolve(previous, seenLinks[id], p.slice(pos));
|
||||||
// absolute. Start over.
|
pos = 0;
|
||||||
buf = [];
|
previous = base = current = "";
|
||||||
p = path.normalizeArray(path.split(target).concat(p.slice(i + 1)));
|
|
||||||
i = -1;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// not absolute. join and splice.
|
return p;
|
||||||
if (i === 0 && p[i].charAt(0) === '/') {
|
};
|
||||||
target = '/' + target;
|
|
||||||
}
|
|
||||||
target = path.split(target);
|
|
||||||
Array.prototype.splice.apply(p, [i, 1].concat(target));
|
|
||||||
p = path.normalizeArray(p);
|
|
||||||
i = -1;
|
|
||||||
buf = [];
|
|
||||||
}
|
|
||||||
return path.join(buf.join('/') || '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function realpath(p, cb) {
|
// posix version
|
||||||
if (p.charAt(0) !== '/') {
|
fs.realpath = function realpath(p, cb) {
|
||||||
p = path.join(process.cwd(), p);
|
// make p is absolute
|
||||||
}
|
p = path.resolve(p);
|
||||||
p = path.split(p);
|
|
||||||
var buf = [];
|
var seenLinks = {},
|
||||||
var seenLinks = {};
|
knownHard = {};
|
||||||
var knownHard = {};
|
|
||||||
|
var pos = 0, // current character position in p
|
||||||
|
current = "", // the partial path so far, including a trailing slash if any
|
||||||
|
base = "", // the partial path without a trailing slash
|
||||||
|
previous = ""; // the partial path scanned in the previous round, with slash
|
||||||
|
|
||||||
// walk down the path, swapping out linked pathparts for their real
|
// walk down the path, swapping out linked pathparts for their real
|
||||||
// values, and pushing non-link path bits onto the buffer.
|
// values
|
||||||
// then return the buffer.
|
|
||||||
// NB: path.length changes.
|
|
||||||
var i = -1;
|
|
||||||
var part;
|
|
||||||
|
|
||||||
LOOP();
|
LOOP();
|
||||||
function LOOP() {
|
function LOOP() {
|
||||||
i++;
|
// stop if scanned past end of path
|
||||||
if (!(i < p.length)) return exit();
|
if (pos >= p.length) {
|
||||||
// skip over empty path parts.
|
return cb(null, p);
|
||||||
if (p[i] === '') return process.nextTick(LOOP);
|
|
||||||
part = path.join(buf.join('/') + '/' + p[i]);
|
|
||||||
if (knownHard[part]) {
|
|
||||||
buf.push(p[i]);
|
|
||||||
return process.nextTick(LOOP);
|
|
||||||
}
|
|
||||||
return fs.lstat(part, gotStat);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function gotStat(er, stat) {
|
// find the next part
|
||||||
if (er) return cb(er);
|
nextPartRe.lastIndex = pos;
|
||||||
|
var result = nextPartRe.exec(p);
|
||||||
|
previous = current;
|
||||||
|
current += result[0];
|
||||||
|
base = previous + result[1];
|
||||||
|
pos = nextPartRe.lastIndex;
|
||||||
|
|
||||||
|
// continue if known to be hard or if root
|
||||||
|
if (!base || knownHard[base]) {
|
||||||
|
return process.nextTick(LOOP);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fs.lstat(base, gotStat);
|
||||||
|
}
|
||||||
|
|
||||||
|
function gotStat(err, stat) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
// if not a symlink, skip to the next path part
|
||||||
if (!stat.isSymbolicLink()) {
|
if (!stat.isSymbolicLink()) {
|
||||||
// not a symlink. easy.
|
knownHard[base] = true;
|
||||||
knownHard[part] = true;
|
|
||||||
buf.push(p[i]);
|
|
||||||
return process.nextTick(LOOP);
|
return process.nextTick(LOOP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stat & read the link if not read before
|
||||||
|
// call gotTarget as soon as the link target is known
|
||||||
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
|
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
|
||||||
if (seenLinks[id]) return gotTarget(null, seenLinks[id]);
|
if (seenLinks[id]) {
|
||||||
fs.stat(part, function(er) {
|
return gotTarget(null, seenLinks[id]);
|
||||||
if (er) return cb(er);
|
}
|
||||||
fs.readlink(part, function(er, target) {
|
fs.stat(base, function(err) {
|
||||||
gotTarget(er, seenLinks[id] = target);
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
fs.readlink(base, function(err, target) {
|
||||||
|
gotTarget(err, seenLinks[id] = target);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function gotTarget(er, target) {
|
function gotTarget(err, target) {
|
||||||
if (er) return cb(er);
|
if (err) return cb(err);
|
||||||
if (target.charAt(0) === '/') {
|
|
||||||
// absolute. Start over.
|
// resolve the link, then start over
|
||||||
buf = [];
|
p = path.resolve(previous, target, p.slice(pos));
|
||||||
p = path.normalizeArray(path.split(target).concat(p.slice(i + 1)));
|
pos = 0;
|
||||||
i = -1;
|
previous = base = current = "";
|
||||||
|
|
||||||
return process.nextTick(LOOP);
|
return process.nextTick(LOOP);
|
||||||
}
|
}
|
||||||
// not absolute. join and splice.
|
};
|
||||||
if (i === 0 && p[i].charAt(0) === '/') {
|
|
||||||
target = '/' + target;
|
|
||||||
}
|
|
||||||
target = path.split(target);
|
|
||||||
Array.prototype.splice.apply(p, [i, 1].concat(target));
|
|
||||||
p = path.normalizeArray(p);
|
|
||||||
i = -1;
|
|
||||||
buf = [];
|
|
||||||
return process.nextTick(LOOP);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function exit() {
|
|
||||||
cb(null, path.join(buf.join('/') || '/'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var pool;
|
var pool;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user