old.js (8542B)
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 };