Browse Source

Rework fs.realpath, primordal windows compatibility

v0.7.4-release
Bert Belder 14 years ago
committed by Ryan Dahl
parent
commit
dea2331377
  1. 269
      lib/fs.js

269
lib/fs.js

@ -487,150 +487,169 @@ 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;
function realpathSync(p) {
if (p.charAt(0) !== '/') {
p = path.join(process.cwd(), p);
}
p = path.split(p);
var buf = [];
var seenLinks = {};
var knownHard = {};
// walk down the path, swapping out linked pathparts for their real
// values, and pushing non-link path bits onto the buffer.
// then return the buffer.
// NB: path.length changes.
for (var i = 0; i < p.length; i++) {
// skip over empty path parts.
if (p[i] === '') continue;
var part = path.join.apply(path, buf.concat(p[i]));
if (knownHard[part]) {
buf.push(p[i]);
continue;
}
var stat = fs.lstatSync(part); var path = require('path'),
if (!stat.isSymbolicLink()) { normalize = path.normalize,
// not a symlink. easy. isWindows = process.platform === 'win32';
knownHard[part] = true;
buf.push(p[i]); if (isWindows) {
continue; // Node doesn't support symlinks / lstat on windows. Hence realpatch is just
} // the same as path.resolve that fails if the path doesn't exists.
// 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
// values
// NB: p.length changes.
while (pos < p.length) {
// find the next part
nextPartRe.lastIndex = pos;
var result = nextPartRe.exec(p);
previous = current;
current += result[0];
base = previous + result[1];
pos = nextPartRe.lastIndex;
// continue if not a symlink, or if root
if (!base || knownHard[base]) {
continue;
}
var stat = fs.lstatSync(base);
if (!stat.isSymbolicLink()) {
knownHard[base] = true;
continue;
}
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); // read the link if it wasn't read before
if (!seenLinks[id]) { var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
fs.statSync(part); if (!seenLinks[id]) {
seenLinks[id] = fs.readlinkSync(part); fs.statSync(base);
} 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 knownHard = {};
// walk down the path, swapping out linked pathparts for their real
// values, and pushing non-link path bits onto the buffer.
// then return the buffer.
// NB: path.length changes.
var i = -1;
var part;
LOOP();
function LOOP() {
i++;
if (!(i < p.length)) return exit();
// skip over empty path parts.
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) { var seenLinks = {},
if (er) return cb(er); knownHard = {};
if (!stat.isSymbolicLink()) {
// not a symlink. easy. var pos = 0, // current character position in p
knownHard[part] = true; current = "", // the partial path so far, including a trailing slash if any
buf.push(p[i]); base = "", // the partial path without a trailing slash
return process.nextTick(LOOP); previous = ""; // the partial path scanned in the previous round, with slash
// walk down the path, swapping out linked pathparts for their real
// values
LOOP();
function LOOP() {
// stop if scanned past end of path
if (pos >= p.length) {
return cb(null, p);
}
// find the next part
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);
} }
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); function gotStat(err, stat) {
if (seenLinks[id]) return gotTarget(null, seenLinks[id]); if (err) return cb(err);
fs.stat(part, function(er) {
if (er) return cb(er); // if not a symlink, skip to the next path part
fs.readlink(part, function(er, target) { if (!stat.isSymbolicLink()) {
gotTarget(er, seenLinks[id] = target); knownHard[base] = true;
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);
if (seenLinks[id]) {
return gotTarget(null, seenLinks[id]);
}
fs.stat(base, function(err) {
if (err) return cb(err);
fs.readlink(base, function(err, target) {
gotTarget(err, seenLinks[id] = target);
});
}); });
}); }
}
function gotTarget(err, target) {
if (err) return cb(err);
// resolve the link, then start over
p = path.resolve(previous, target, p.slice(pos));
pos = 0;
previous = base = current = "";
function gotTarget(er, target) {
if (er) return cb(er);
if (target.charAt(0) === '/') {
// absolute. Start over.
buf = [];
p = path.normalizeArray(path.split(target).concat(p.slice(i + 1)));
i = -1;
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;
function allocNewPool() { function allocNewPool() {

Loading…
Cancel
Save