• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22var pathModule = require('path');
23var isWindows = process.platform === 'win32';
24var fs = require('fs');
25
26// JavaScript implementation of realpath, ported from node pre-v6
27
28var DEBUG = process.env.NODE_DEBUG && /fs/.test(process.env.NODE_DEBUG);
29
30function rethrow() {
31  // Only enable in debug mode. A backtrace uses ~1000 bytes of heap space and
32  // is fairly slow to generate.
33  var callback;
34  if (DEBUG) {
35    var backtrace = new Error;
36    callback = debugCallback;
37  } else
38    callback = missingCallback;
39
40  return callback;
41
42  function debugCallback(err) {
43    if (err) {
44      backtrace.message = err.message;
45      err = backtrace;
46      missingCallback(err);
47    }
48  }
49
50  function missingCallback(err) {
51    if (err) {
52      if (process.throwDeprecation)
53        throw err;  // Forgot a callback but don't know where? Use NODE_DEBUG=fs
54      else if (!process.noDeprecation) {
55        var msg = 'fs: missing callback ' + (err.stack || err.message);
56        if (process.traceDeprecation)
57          console.trace(msg);
58        else
59          console.error(msg);
60      }
61    }
62  }
63}
64
65function maybeCallback(cb) {
66  return typeof cb === 'function' ? cb : rethrow();
67}
68
69var normalize = pathModule.normalize;
70
71// Regexp that finds the next partion of a (partial) path
72// result is [base_with_slash, base], e.g. ['somedir/', 'somedir']
73if (isWindows) {
74  var nextPartRe = /(.*?)(?:[\/\\]+|$)/g;
75} else {
76  var nextPartRe = /(.*?)(?:[\/]+|$)/g;
77}
78
79// Regex to find the device root, including trailing slash. E.g. 'c:\\'.
80if (isWindows) {
81  var splitRootRe = /^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/;
82} else {
83  var splitRootRe = /^[\/]*/;
84}
85
86exports.realpathSync = function realpathSync(p, cache) {
87  // make p is absolute
88  p = pathModule.resolve(p);
89
90  if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
91    return cache[p];
92  }
93
94  var original = p,
95      seenLinks = {},
96      knownHard = {};
97
98  // current character position in p
99  var pos;
100  // the partial path so far, including a trailing slash if any
101  var current;
102  // the partial path without a trailing slash (except when pointing at a root)
103  var base;
104  // the partial path scanned in the previous round, with slash
105  var previous;
106
107  start();
108
109  function start() {
110    // Skip over roots
111    var m = splitRootRe.exec(p);
112    pos = m[0].length;
113    current = m[0];
114    base = m[0];
115    previous = '';
116
117    // On windows, check that the root exists. On unix there is no need.
118    if (isWindows && !knownHard[base]) {
119      fs.lstatSync(base);
120      knownHard[base] = true;
121    }
122  }
123
124  // walk down the path, swapping out linked pathparts for their real
125  // values
126  // NB: p.length changes.
127  while (pos < p.length) {
128    // find the next part
129    nextPartRe.lastIndex = pos;
130    var result = nextPartRe.exec(p);
131    previous = current;
132    current += result[0];
133    base = previous + result[1];
134    pos = nextPartRe.lastIndex;
135
136    // continue if not a symlink
137    if (knownHard[base] || (cache && cache[base] === base)) {
138      continue;
139    }
140
141    var resolvedLink;
142    if (cache && Object.prototype.hasOwnProperty.call(cache, base)) {
143      // some known symbolic link.  no need to stat again.
144      resolvedLink = cache[base];
145    } else {
146      var stat = fs.lstatSync(base);
147      if (!stat.isSymbolicLink()) {
148        knownHard[base] = true;
149        if (cache) cache[base] = base;
150        continue;
151      }
152
153      // read the link if it wasn't read before
154      // dev/ino always return 0 on windows, so skip the check.
155      var linkTarget = null;
156      if (!isWindows) {
157        var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
158        if (seenLinks.hasOwnProperty(id)) {
159          linkTarget = seenLinks[id];
160        }
161      }
162      if (linkTarget === null) {
163        fs.statSync(base);
164        linkTarget = fs.readlinkSync(base);
165      }
166      resolvedLink = pathModule.resolve(previous, linkTarget);
167      // track this, if given a cache.
168      if (cache) cache[base] = resolvedLink;
169      if (!isWindows) seenLinks[id] = linkTarget;
170    }
171
172    // resolve the link, then start over
173    p = pathModule.resolve(resolvedLink, p.slice(pos));
174    start();
175  }
176
177  if (cache) cache[original] = p;
178
179  return p;
180};
181
182
183exports.realpath = function realpath(p, cache, cb) {
184  if (typeof cb !== 'function') {
185    cb = maybeCallback(cache);
186    cache = null;
187  }
188
189  // make p is absolute
190  p = pathModule.resolve(p);
191
192  if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
193    return process.nextTick(cb.bind(null, null, cache[p]));
194  }
195
196  var original = p,
197      seenLinks = {},
198      knownHard = {};
199
200  // current character position in p
201  var pos;
202  // the partial path so far, including a trailing slash if any
203  var current;
204  // the partial path without a trailing slash (except when pointing at a root)
205  var base;
206  // the partial path scanned in the previous round, with slash
207  var previous;
208
209  start();
210
211  function start() {
212    // Skip over roots
213    var m = splitRootRe.exec(p);
214    pos = m[0].length;
215    current = m[0];
216    base = m[0];
217    previous = '';
218
219    // On windows, check that the root exists. On unix there is no need.
220    if (isWindows && !knownHard[base]) {
221      fs.lstat(base, function(err) {
222        if (err) return cb(err);
223        knownHard[base] = true;
224        LOOP();
225      });
226    } else {
227      process.nextTick(LOOP);
228    }
229  }
230
231  // walk down the path, swapping out linked pathparts for their real
232  // values
233  function LOOP() {
234    // stop if scanned past end of path
235    if (pos >= p.length) {
236      if (cache) cache[original] = p;
237      return cb(null, p);
238    }
239
240    // find the next part
241    nextPartRe.lastIndex = pos;
242    var result = nextPartRe.exec(p);
243    previous = current;
244    current += result[0];
245    base = previous + result[1];
246    pos = nextPartRe.lastIndex;
247
248    // continue if not a symlink
249    if (knownHard[base] || (cache && cache[base] === base)) {
250      return process.nextTick(LOOP);
251    }
252
253    if (cache && Object.prototype.hasOwnProperty.call(cache, base)) {
254      // known symbolic link.  no need to stat again.
255      return gotResolvedLink(cache[base]);
256    }
257
258    return fs.lstat(base, gotStat);
259  }
260
261  function gotStat(err, stat) {
262    if (err) return cb(err);
263
264    // if not a symlink, skip to the next path part
265    if (!stat.isSymbolicLink()) {
266      knownHard[base] = true;
267      if (cache) cache[base] = base;
268      return process.nextTick(LOOP);
269    }
270
271    // stat & read the link if not read before
272    // call gotTarget as soon as the link target is known
273    // dev/ino always return 0 on windows, so skip the check.
274    if (!isWindows) {
275      var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
276      if (seenLinks.hasOwnProperty(id)) {
277        return gotTarget(null, seenLinks[id], base);
278      }
279    }
280    fs.stat(base, function(err) {
281      if (err) return cb(err);
282
283      fs.readlink(base, function(err, target) {
284        if (!isWindows) seenLinks[id] = target;
285        gotTarget(err, target);
286      });
287    });
288  }
289
290  function gotTarget(err, target, base) {
291    if (err) return cb(err);
292
293    var resolvedLink = pathModule.resolve(previous, target);
294    if (cache) cache[base] = resolvedLink;
295    gotResolvedLink(resolvedLink);
296  }
297
298  function gotResolvedLink(resolvedLink) {
299    // resolve the link, then start over
300    p = pathModule.resolve(resolvedLink, p.slice(pos));
301    start();
302  }
303};
304