| // 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(); |
| } |
| }; |