old.js
  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  
 22  var pathModule = require('path');
 23  var isWindows = process.platform === 'win32';
 24  var fs = require('fs');
 25  
 26  // JavaScript implementation of realpath, ported from node pre-v6
 27  
 28  var DEBUG = process.env.NODE_DEBUG && /fs/.test(process.env.NODE_DEBUG);
 29  
 30  function 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  
 65  function maybeCallback(cb) {
 66    return typeof cb === 'function' ? cb : rethrow();
 67  }
 68  
 69  var 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']
 73  if (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:\\'.
 80  if (isWindows) {
 81    var splitRootRe = /^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/;
 82  } else {
 83    var splitRootRe = /^[\/]*/;
 84  }
 85  
 86  exports.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  
183  exports.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  };