|
|
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var pathModule = require('path'); var isWindows = process.platform === 'win32'; var fs = require('fs');
// JavaScript implementation of realpath, ported from node pre-v6
var DEBUG = process.env.NODE_DEBUG && /fs/.test(process.env.NODE_DEBUG);
function rethrow() { // Only enable in debug mode. A backtrace uses ~1000 bytes of heap space and
// is fairly slow to generate.
var callback; if (DEBUG) { var backtrace = new Error; callback = debugCallback; } else callback = missingCallback;
return callback;
function debugCallback(err) { if (err) { backtrace.message = err.message; err = backtrace; missingCallback(err); } }
function missingCallback(err) { if (err) { if (process.throwDeprecation) throw err; // Forgot a callback but don't know where? Use NODE_DEBUG=fs
else if (!process.noDeprecation) { var msg = 'fs: missing callback ' + (err.stack || err.message); if (process.traceDeprecation) console.trace(msg); else console.error(msg); } } } }
function maybeCallback(cb) { return typeof cb === 'function' ? cb : rethrow(); }
var normalize = pathModule.normalize;
// Regexp that finds the next partion of a (partial) path
// result is [base_with_slash, base], e.g. ['somedir/', 'somedir']
if (isWindows) { var nextPartRe = /(.*?)(?:[\/\\]+|$)/g; } else { var nextPartRe = /(.*?)(?:[\/]+|$)/g; }
// Regex to find the device root, including trailing slash. E.g. 'c:\\'.
if (isWindows) { var splitRootRe = /^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/; } else { var splitRootRe = /^[\/]*/; }
exports.realpathSync = function realpathSync(p, cache) { // make p is absolute
p = pathModule.resolve(p);
if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { return cache[p]; }
var original = p, seenLinks = {}, knownHard = {};
// current character position in p
var pos; // the partial path so far, including a trailing slash if any
var current; // the partial path without a trailing slash (except when pointing at a root)
var base; // the partial path scanned in the previous round, with slash
var previous;
start();
function start() { // Skip over roots
var m = splitRootRe.exec(p); pos = m[0].length; current = m[0]; base = m[0]; previous = '';
// On windows, check that the root exists. On unix there is no need.
if (isWindows && !knownHard[base]) { fs.lstatSync(base); knownHard[base] = true; } }
// 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
if (knownHard[base] || (cache && cache[base] === base)) { continue; }
var resolvedLink; if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { // some known symbolic link. no need to stat again.
resolvedLink = cache[base]; } else { var stat = fs.lstatSync(base); if (!stat.isSymbolicLink()) { knownHard[base] = true; if (cache) cache[base] = base; continue; }
// read the link if it wasn't read before
// dev/ino always return 0 on windows, so skip the check.
var linkTarget = null; if (!isWindows) { var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); if (seenLinks.hasOwnProperty(id)) { linkTarget = seenLinks[id]; } } if (linkTarget === null) { fs.statSync(base); linkTarget = fs.readlinkSync(base); } resolvedLink = pathModule.resolve(previous, linkTarget); // track this, if given a cache.
if (cache) cache[base] = resolvedLink; if (!isWindows) seenLinks[id] = linkTarget; }
// resolve the link, then start over
p = pathModule.resolve(resolvedLink, p.slice(pos)); start(); }
if (cache) cache[original] = p;
return p; };
exports.realpath = function realpath(p, cache, cb) { if (typeof cb !== 'function') { cb = maybeCallback(cache); cache = null; }
// make p is absolute
p = pathModule.resolve(p);
if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { return process.nextTick(cb.bind(null, null, cache[p])); }
var original = p, seenLinks = {}, knownHard = {};
// current character position in p
var pos; // the partial path so far, including a trailing slash if any
var current; // the partial path without a trailing slash (except when pointing at a root)
var base; // the partial path scanned in the previous round, with slash
var previous;
start();
function start() { // Skip over roots
var m = splitRootRe.exec(p); pos = m[0].length; current = m[0]; base = m[0]; previous = '';
// On windows, check that the root exists. On unix there is no need.
if (isWindows && !knownHard[base]) { fs.lstat(base, function(err) { if (err) return cb(err); knownHard[base] = true; LOOP(); }); } else { process.nextTick(LOOP); } }
// walk down the path, swapping out linked pathparts for their real
// values
function LOOP() { // stop if scanned past end of path
if (pos >= p.length) { if (cache) cache[original] = p; 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 not a symlink
if (knownHard[base] || (cache && cache[base] === base)) { return process.nextTick(LOOP); }
if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { // known symbolic link. no need to stat again.
return gotResolvedLink(cache[base]); }
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()) { knownHard[base] = true; if (cache) cache[base] = base; return process.nextTick(LOOP); }
// stat & read the link if not read before
// call gotTarget as soon as the link target is known
// dev/ino always return 0 on windows, so skip the check.
if (!isWindows) { var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); if (seenLinks.hasOwnProperty(id)) { return gotTarget(null, seenLinks[id], base); } } fs.stat(base, function(err) { if (err) return cb(err);
fs.readlink(base, function(err, target) { if (!isWindows) seenLinks[id] = target; gotTarget(err, target); }); }); }
function gotTarget(err, target, base) { if (err) return cb(err);
var resolvedLink = pathModule.resolve(previous, target); if (cache) cache[base] = resolvedLink; gotResolvedLink(resolvedLink); }
function gotResolvedLink(resolvedLink) { // resolve the link, then start over
p = pathModule.resolve(resolvedLink, p.slice(pos)); start(); } };
|