From: Ryan Dahl Date: Fri, 5 Mar 2010 19:11:23 +0000 (-0800) Subject: Merge remote branch 'felixge/file2' X-Git-Tag: v0.1.31~6 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=5217eda1ae3ff02e8ac32af464f21ad13eea91a4;p=platform%2Fupstream%2Fnodejs.git Merge remote branch 'felixge/file2' --- 5217eda1ae3ff02e8ac32af464f21ad13eea91a4 diff --cc lib/fs.js index 3fd95de,fa8ba89..74e1e3e --- a/lib/fs.js +++ b/lib/fs.js @@@ -292,132 -296,286 +296,332 @@@ exports.unwatchFile = function (filenam // Realpath var path = require('path'); -var dirname = path.dirname, - basename = path.basename, - normalize = path.normalize; - -function readlinkDeepSync(path, stats) { - var seen_links = {}, resolved_link, stats, file_id; - while (true) { - stats = stats || exports.lstatSync(path); - file_id = stats.dev.toString(32)+":"+stats.ino.toString(32); - if (file_id in seen_links) { - throw new Error("cyclic symbolic link at "+path); - } else { - seen_links[file_id] = 1; - if (stats.isSymbolicLink()) { - var newpath = exports.readlinkSync(path); - if (newpath.charAt(0) === '/') { - path = newpath; +var normalize = path.normalize + normalizeArray = path.normalizeArray; + +exports.realpathSync = function (path) { + var seen_links = {}, knownHards = {}, buf, i = 0, part, x, stats; + if (path.charAt(0) !== '/') { + var cwd = process.cwd().split('/'); + path = cwd.concat(path.split('/')); + path = normalizeArray(path); + i = cwd.length; + buf = [].concat(cwd); + } else { + path = normalizeArray(path.split('/')); + buf = ['']; + } + for (; i 0) buf.splice(y, delta); + } else { + i--; + } + } } else { - var dir = dirname(path); - path = (dir !== '') ? dir + '/' + newpath : newpath; + buf.push(path[i]); + knownHards[buf.join('/')] = true; } - } else { - return normalize(path); } } - stats = null; } + return buf.join('/'); } -function readlinkDeep(path, stats, callback) { - var seen_links = {}, resolved_link, file_id; - function next(stats) { - file_id = stats.dev.toString(32)+":"+stats.ino.toString(32); - if (file_id in seen_links) { - callback(new Error("cyclic symbolic link at "+path)); - } else { - seen_links[file_id] = 1; - if (stats.isSymbolicLink()) { - exports.readlink(path, function(err, newpath) { - if (err) callback(err); - if (newpath.charAt(0) === '/') { - path = newpath; - } else { - var dir = dirname(path); - path = (dir !== '') ? dir + '/' + newpath : newpath; - } - _next(); - }); - } else { - callback(null, normalize(path)); - } + +exports.realpath = function (path, callback) { + var seen_links = {}, knownHards = {}, buf = [''], i = 0, part, x; + if (path.charAt(0) !== '/') { + // assumes cwd is canonical + var cwd = process.cwd().split('/'); + path = cwd.concat(path.split('/')); + path = normalizeArray(path); + i = cwd.length-1; + buf = [].concat(cwd); + } else { + path = normalizeArray(path.split('/')); + } + function done(err) { + if (callback) { + if (!err) callback(err, buf.join('/')); + else callback(err); } } - function _next() { - exports.lstat(path, function(err, stats){ - if (err) callback(err); - else next(stats); - }); + function next() { + if (++i === path.length) return done(); + part = path.slice(0, i+1).join('/'); + if (part.length === 0) return next(); + if (part in knownHards) { + buf.push(path[i]); + next(); + } else { + exports.lstat(part, function(err, stats){ + if (err) return done(err); + if (stats.isSymbolicLink()) { + x = stats.dev.toString(32)+":"+stats.ino.toString(32); + if (x in seen_links) + return done(new Error("cyclic link at "+part)); + seen_links[x] = true; + exports.readlink(part, function(err, npart){ + if (err) return done(err); + part = npart; + if (part.charAt(0) === '/') { + // absolute + path = normalizeArray(part.split('/')); + buf = ['']; + i = 0; + } else { + // relative + Array.prototype.splice.apply(path, [i, 1].concat(part.split('/'))); + part = normalizeArray(path); + var y = 0, L = Math.max(path.length, part.length), delta; + for (; y 0) buf.splice(y, delta); + } + else { + i--; // resolve new node if needed + } + } + next(); + }); // fs.readlink + } + else { + buf.push(path[i]); + knownHards[buf.join('/')] = true; + next(); + } + }); // fs.lstat + } } - if (stats) next(stats); - else _next(); -} - -exports.realpathSync = function(path) { - var stats = exports.lstatSync(path); - if (stats.isSymbolicLink()) - return readlinkDeepSync(path, stats); - else - return normalize(path); -} - -exports.realpath = function(path, callback) { - var resolved_path = path; - if (!callback) return; - exports.lstat(path, function(err, stats){ - if (err) - callback(err); - else if (stats.isSymbolicLink()) - readlinkDeep(path, stats, callback); - else - callback(null, normalize(path)); - }); + next(); } + + exports.createReadStream = function(path, options) { + return new FileReadStream(path, options); + }; + + var FileReadStream = exports.FileReadStream = function(path, options) { + events.EventEmitter.call(this); + + this.path = path; + this.fd = null; + this.readable = true; + this.paused = false; + + this.flags = 'r'; + this.encoding = 'binary'; + this.mode = 0666; + this.bufferSize = 4 * 1024; + + process.mixin(this, options || {}); + + var + self = this, + buffer = null; + + function read() { + if (!self.readable || self.paused) { + return; + } + + fs.read(self.fd, self.bufferSize, undefined, self.encoding, function(err, data, bytesRead) { + if (err) { + self.emit('error', err); + self.readable = false; + return; + } + + if (bytesRead === 0) { + self.emit('end'); + self.forceClose(); + return; + } + + // do not emit events if the stream is paused + if (self.paused) { + buffer = data; + return; + } + + // do not emit events anymore after we declared the stream unreadable + if (!self.readable) { + return; + } + + self.emit('data', data); + read(); + }); + } + + fs.open(this.path, this.flags, this.mode, function(err, fd) { + if (err) { + self.emit('error', err); + self.readable = false; + return; + } + + self.fd = fd; + self.emit('open', fd); + read(); + }); + + this.forceClose = function() { + this.readable = false; + fs.close(this.fd, function(err) { + if (err) { + self.emit('error', err); + return; + } + + self.emit('close'); + }); + }; + + this.pause = function() { + this.paused = true; + }; + + this.resume = function() { + this.paused = false; + + if (buffer !== null) { + self.emit('data', buffer); + buffer = null; + } + + read(); + }; + }; + sys.inherits(FileReadStream, events.EventEmitter); + + exports.createWriteStream = function(path, options) { + return new FileWriteStream(path, options); + }; + + var FileWriteStream = exports.FileWriteStream = function(path, options) { + events.EventEmitter.call(this); + + this.path = path; + this.fd = null; + this.writeable = true; + + this.flags = 'w'; + this.encoding = 'binary'; + this.mode = 0666; + + process.mixin(this, options || {}); + + var + self = this, + queue = [], + busy = false; + + queue.push([fs.open, this.path, this.flags, this.mode]); + + function flush() { + if (busy) { + return; + } + + var args = queue.shift(); + if (!args) { + return self.emit('drain'); + } + + busy = true; + + var method = args.shift(); + + args.push(function(err) { + busy = false; + + if (err) { + self.writeable = false; + self.emit('error', err); + return; + } + + // save reference for file pointer + if (method === fs.open) { + self.fd = arguments[1]; + self.emit('open', self.fd); + } + + // stop flushing after close + if (method === fs.close) { + self.emit('close'); + return; + } + + flush(); + }); + + // Inject the file pointer + if (method !== fs.open) { + args.unshift(self.fd); + } + + method.apply(null, args); + }; + + this.write = function(data) { + if (!this.writeable) { + throw new Error('stream not writeable'); + } + + queue.push([fs.write, data, undefined, this.encoding]); + flush(); + return false; + }; + + this.close = function() { + this.writeable = false; + queue.push([fs.close,]); + flush(); + }; + + this.forceClose = function() { + this.writeable = false; + fs.close(self.fd, function(err) { + if (err) { + self.emit('error', err); + return; + } + + self.emit('close'); + }); + }; + + flush(); + }; + sys.inherits(FileWriteStream, events.EventEmitter);