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

};