You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
303 lines
8.3 KiB
303 lines
8.3 KiB
// 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(); |
|
} |
|
};
|
|
|