pax_global_header00006660000000000000000000000064127310411140014504gustar00rootroot0000000000000052 comment=6f3d3bcd85ba2a723cf23e045f4dc6976f654771 fstream-1.0.10/000077500000000000000000000000001273104111400132245ustar00rootroot00000000000000fstream-1.0.10/.gitignore000066400000000000000000000001161273104111400152120ustar00rootroot00000000000000.*.swp node_modules/ examples/deep-copy/ examples/path/ examples/filter-copy/ fstream-1.0.10/.travis.yml000066400000000000000000000002131273104111400153310ustar00rootroot00000000000000language: node_js node_js: - iojs - 0.12 - 0.10 - 0.8 before_install: - "npm config set spin false" - "npm install -g npm/npm" fstream-1.0.10/LICENSE000066400000000000000000000013751273104111400142370ustar00rootroot00000000000000The ISC License Copyright (c) Isaac Z. Schlueter and Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. fstream-1.0.10/README.md000066400000000000000000000044511273104111400145070ustar00rootroot00000000000000Like FS streams, but with stat on them, and supporting directories and symbolic links, as well as normal files. Also, you can use this to set the stats on a file, even if you don't change its contents, or to create a symlink, etc. So, for example, you can "write" a directory, and it'll call `mkdir`. You can specify a uid and gid, and it'll call `chown`. You can specify a `mtime` and `atime`, and it'll call `utimes`. You can call it a symlink and provide a `linkpath` and it'll call `symlink`. Note that it won't automatically resolve symbolic links. So, if you call `fstream.Reader('/some/symlink')` then you'll get an object that stats and then ends immediately (since it has no data). To follow symbolic links, do this: `fstream.Reader({path:'/some/symlink', follow: true })`. There are various checks to make sure that the bytes emitted are the same as the intended size, if the size is set. ## Examples ```javascript fstream .Writer({ path: "path/to/file" , mode: 0755 , size: 6 }) .write("hello\n") .end() ``` This will create the directories if they're missing, and then write `hello\n` into the file, chmod it to 0755, and assert that 6 bytes have been written when it's done. ```javascript fstream .Writer({ path: "path/to/file" , mode: 0755 , size: 6 , flags: "a" }) .write("hello\n") .end() ``` You can pass flags in, if you want to append to a file. ```javascript fstream .Writer({ path: "path/to/symlink" , linkpath: "./file" , SymbolicLink: true , mode: "0755" // octal strings supported }) .end() ``` If isSymbolicLink is a function, it'll be called, and if it returns true, then it'll treat it as a symlink. If it's not a function, then any truish value will make a symlink, or you can set `type: 'SymbolicLink'`, which does the same thing. Note that the linkpath is relative to the symbolic link location, not the parent dir or cwd. ```javascript fstream .Reader("path/to/dir") .pipe(fstream.Writer("path/to/other/dir")) ``` This will do like `cp -Rp path/to/dir path/to/other/dir`. If the other dir exists and isn't a directory, then it'll emit an error. It'll also set the uid, gid, mode, etc. to be identical. In this way, it's more like `rsync -a` than simply a copy. fstream-1.0.10/examples/000077500000000000000000000000001273104111400150425ustar00rootroot00000000000000fstream-1.0.10/examples/filter-pipe.js000066400000000000000000000066651273104111400176350ustar00rootroot00000000000000var fstream = require('../fstream.js') var path = require('path') var r = fstream.Reader({ path: path.dirname(__dirname), filter: function () { return !this.basename.match(/^\./) && !this.basename.match(/^node_modules$/) && !this.basename.match(/^deep-copy$/) && !this.basename.match(/^filter-copy$/) } }) // this writer will only write directories var w = fstream.Writer({ path: path.resolve(__dirname, 'filter-copy'), type: 'Directory', filter: function () { return this.type === 'Directory' } }) var indent = '' r.on('entry', appears) r.on('ready', function () { console.error('ready to begin!', r.path) }) function appears (entry) { console.error(indent + 'a %s appears!', entry.type, entry.basename, typeof entry.basename) if (foggy) { console.error('FOGGY!') var p = entry do { console.error(p.depth, p.path, p._paused) p = p.parent } while (p) throw new Error('\u001b[mshould not have entries while foggy') } indent += '\t' entry.on('data', missile(entry)) entry.on('end', runaway(entry)) entry.on('entry', appears) } var foggy function missile (entry) { function liftFog (who) { if (!foggy) return if (who) { console.error('%s breaks the spell!', who && who.path) } else { console.error('the spell expires!') } console.error('\u001b[mthe fog lifts!\n') clearTimeout(foggy) foggy = null if (entry._paused) entry.resume() } if (entry.type === 'Directory') { var ended = false entry.once('end', function () { ended = true }) return function (c) { // throw in some pathological pause()/resume() behavior // just for extra fun. process.nextTick(function () { if (!foggy && !ended) { // && Math.random() < 0.3) { console.error(indent + '%s casts a spell', entry.basename) console.error('\na slowing fog comes over the battlefield...\n\u001b[32m') entry.pause() entry.once('resume', liftFog) foggy = setTimeout(liftFog, 1000) } }) } } return function (c) { var e = Math.random() < 0.5 console.error(indent + '%s %s for %d damage!', entry.basename, e ? 'is struck' : 'fires a chunk', c.length) } } function runaway (entry) { return function () { var e = Math.random() < 0.5 console.error(indent + '%s %s', entry.basename, e ? 'turns to flee' : 'is vanquished!') indent = indent.slice(0, -1) } } w.on('entry', attacks) // w.on('ready', function () { attacks(w) }) function attacks (entry) { console.error(indent + '%s %s!', entry.basename, entry.type === 'Directory' ? 'calls for backup' : 'attacks') entry.on('entry', attacks) } var ended = false var i = 1 r.on('end', function () { if (foggy) clearTimeout(foggy) console.error("\u001b[mIT'S OVER!!") console.error('A WINNAR IS YOU!') console.log('ok ' + (i++) + ' A WINNAR IS YOU') ended = true // now go through and verify that everything in there is a dir. var p = path.resolve(__dirname, 'filter-copy') var checker = fstream.Reader({ path: p }) checker.checker = true checker.on('child', function (e) { var ok = e.type === 'Directory' console.log((ok ? '' : 'not ') + 'ok ' + (i++) + ' should be a dir: ' + e.path.substr(checker.path.length + 1)) }) }) process.on('exit', function () { console.log((ended ? '' : 'not ') + 'ok ' + (i) + ' ended') console.log('1..' + i) }) r.pipe(w) fstream-1.0.10/examples/pipe.js000066400000000000000000000055631273104111400163460ustar00rootroot00000000000000var fstream = require('../fstream.js') var path = require('path') var r = fstream.Reader({ path: path.dirname(__dirname), filter: function () { return !this.basename.match(/^\./) && !this.basename.match(/^node_modules$/) && !this.basename.match(/^deep-copy$/) } }) var w = fstream.Writer({ path: path.resolve(__dirname, 'deep-copy'), type: 'Directory' }) var indent = '' r.on('entry', appears) r.on('ready', function () { console.error('ready to begin!', r.path) }) function appears (entry) { console.error(indent + 'a %s appears!', entry.type, entry.basename, typeof entry.basename, entry) if (foggy) { console.error('FOGGY!') var p = entry do { console.error(p.depth, p.path, p._paused) p = p.parent } while (p) throw new Error('\u001b[mshould not have entries while foggy') } indent += '\t' entry.on('data', missile(entry)) entry.on('end', runaway(entry)) entry.on('entry', appears) } var foggy function missile (entry) { function liftFog (who) { if (!foggy) return if (who) { console.error('%s breaks the spell!', who && who.path) } else { console.error('the spell expires!') } console.error('\u001b[mthe fog lifts!\n') clearTimeout(foggy) foggy = null if (entry._paused) entry.resume() } if (entry.type === 'Directory') { var ended = false entry.once('end', function () { ended = true }) return function (c) { // throw in some pathological pause()/resume() behavior // just for extra fun. process.nextTick(function () { if (!foggy && !ended) { // && Math.random() < 0.3) { console.error(indent + '%s casts a spell', entry.basename) console.error('\na slowing fog comes over the battlefield...\n\u001b[32m') entry.pause() entry.once('resume', liftFog) foggy = setTimeout(liftFog, 10) } }) } } return function (c) { var e = Math.random() < 0.5 console.error(indent + '%s %s for %d damage!', entry.basename, e ? 'is struck' : 'fires a chunk', c.length) } } function runaway (entry) { return function () { var e = Math.random() < 0.5 console.error(indent + '%s %s', entry.basename, e ? 'turns to flee' : 'is vanquished!') indent = indent.slice(0, -1) } } w.on('entry', attacks) // w.on('ready', function () { attacks(w) }) function attacks (entry) { console.error(indent + '%s %s!', entry.basename, entry.type === 'Directory' ? 'calls for backup' : 'attacks') entry.on('entry', attacks) } var ended = false r.on('end', function () { if (foggy) clearTimeout(foggy) console.error("\u001b[mIT'S OVER!!") console.error('A WINNAR IS YOU!') console.log('ok 1 A WINNAR IS YOU') ended = true }) process.on('exit', function () { console.log((ended ? '' : 'not ') + 'ok 2 ended') console.log('1..2') }) r.pipe(w) fstream-1.0.10/examples/reader.js000066400000000000000000000027101273104111400166420ustar00rootroot00000000000000var fstream = require('../fstream.js') var tap = require('tap') var fs = require('fs') var path = require('path') var dir = path.dirname(__dirname) tap.test('reader test', function (t) { var children = -1 var gotReady = false var ended = false var r = fstream.Reader({ path: dir, filter: function () { // return this.parent === r return this.parent === r || this === r } }) r.on('ready', function () { gotReady = true children = fs.readdirSync(dir).length console.error('Setting expected children to ' + children) t.equal(r.type, 'Directory', 'should be a directory') }) r.on('entry', function (entry) { children-- if (!gotReady) { t.fail('children before ready!') } t.equal(entry.dirname, r.path, 'basename is parent dir') }) r.on('error', function (er) { t.fail(er) t.end() process.exit(1) }) r.on('end', function () { t.equal(children, 0, 'should have seen all children') ended = true }) var closed = false r.on('close', function () { t.ok(ended, 'saw end before close') t.notOk(closed, 'close should only happen once') closed = true t.end() }) }) tap.test('reader error test', function (t) { // assumes non-root on a *nix system var r = fstream.Reader({ path: '/etc/shadow' }) r.once('error', function (er) { t.ok(true) t.end() }) r.on('end', function () { t.fail('reader ended without error') t.end() }) }) fstream-1.0.10/examples/symlink-write.js000066400000000000000000000013511273104111400202160ustar00rootroot00000000000000var fstream = require('../fstream.js') var notOpen = false process.chdir(__dirname) fstream .Writer({ path: 'path/to/symlink', linkpath: './file', isSymbolicLink: true, mode: '0755' // octal strings supported }) .on('close', function () { notOpen = true var fs = require('fs') var s = fs.lstatSync('path/to/symlink') var isSym = s.isSymbolicLink() console.log((isSym ? '' : 'not ') + 'ok 1 should be symlink') var t = fs.readlinkSync('path/to/symlink') var isTarget = t === './file' console.log((isTarget ? '' : 'not ') + 'ok 2 should link to ./file') }) .end() process.on('exit', function () { console.log((notOpen ? '' : 'not ') + 'ok 3 should be closed') console.log('1..3') }) fstream-1.0.10/fstream.js000066400000000000000000000021251273104111400152230ustar00rootroot00000000000000exports.Abstract = require('./lib/abstract.js') exports.Reader = require('./lib/reader.js') exports.Writer = require('./lib/writer.js') exports.File = { Reader: require('./lib/file-reader.js'), Writer: require('./lib/file-writer.js') } exports.Dir = { Reader: require('./lib/dir-reader.js'), Writer: require('./lib/dir-writer.js') } exports.Link = { Reader: require('./lib/link-reader.js'), Writer: require('./lib/link-writer.js') } exports.Proxy = { Reader: require('./lib/proxy-reader.js'), Writer: require('./lib/proxy-writer.js') } exports.Reader.Dir = exports.DirReader = exports.Dir.Reader exports.Reader.File = exports.FileReader = exports.File.Reader exports.Reader.Link = exports.LinkReader = exports.Link.Reader exports.Reader.Proxy = exports.ProxyReader = exports.Proxy.Reader exports.Writer.Dir = exports.DirWriter = exports.Dir.Writer exports.Writer.File = exports.FileWriter = exports.File.Writer exports.Writer.Link = exports.LinkWriter = exports.Link.Writer exports.Writer.Proxy = exports.ProxyWriter = exports.Proxy.Writer exports.collect = require('./lib/collect.js') fstream-1.0.10/lib/000077500000000000000000000000001273104111400137725ustar00rootroot00000000000000fstream-1.0.10/lib/abstract.js000066400000000000000000000037601273104111400161410ustar00rootroot00000000000000// the parent class for all fstreams. module.exports = Abstract var Stream = require('stream').Stream var inherits = require('inherits') function Abstract () { Stream.call(this) } inherits(Abstract, Stream) Abstract.prototype.on = function (ev, fn) { if (ev === 'ready' && this.ready) { process.nextTick(fn.bind(this)) } else { Stream.prototype.on.call(this, ev, fn) } return this } Abstract.prototype.abort = function () { this._aborted = true this.emit('abort') } Abstract.prototype.destroy = function () {} Abstract.prototype.warn = function (msg, code) { var self = this var er = decorate(msg, code, self) if (!self.listeners('warn')) { console.error('%s %s\n' + 'path = %s\n' + 'syscall = %s\n' + 'fstream_type = %s\n' + 'fstream_path = %s\n' + 'fstream_unc_path = %s\n' + 'fstream_class = %s\n' + 'fstream_stack =\n%s\n', code || 'UNKNOWN', er.stack, er.path, er.syscall, er.fstream_type, er.fstream_path, er.fstream_unc_path, er.fstream_class, er.fstream_stack.join('\n')) } else { self.emit('warn', er) } } Abstract.prototype.info = function (msg, code) { this.emit('info', msg, code) } Abstract.prototype.error = function (msg, code, th) { var er = decorate(msg, code, this) if (th) throw er else this.emit('error', er) } function decorate (er, code, self) { if (!(er instanceof Error)) er = new Error(er) er.code = er.code || code er.path = er.path || self.path er.fstream_type = er.fstream_type || self.type er.fstream_path = er.fstream_path || self.path if (self._path !== self.path) { er.fstream_unc_path = er.fstream_unc_path || self._path } if (self.linkpath) { er.fstream_linkpath = er.fstream_linkpath || self.linkpath } er.fstream_class = er.fstream_class || self.constructor.name er.fstream_stack = er.fstream_stack || new Error().stack.split(/\n/).slice(3).map(function (s) { return s.replace(/^ {4}at /, '') }) return er } fstream-1.0.10/lib/collect.js000066400000000000000000000033741273104111400157640ustar00rootroot00000000000000module.exports = collect function collect (stream) { if (stream._collected) return if (stream._paused) return stream.on('resume', collect.bind(null, stream)) stream._collected = true stream.pause() stream.on('data', save) stream.on('end', save) var buf = [] function save (b) { if (typeof b === 'string') b = new Buffer(b) if (Buffer.isBuffer(b) && !b.length) return buf.push(b) } stream.on('entry', saveEntry) var entryBuffer = [] function saveEntry (e) { collect(e) entryBuffer.push(e) } stream.on('proxy', proxyPause) function proxyPause (p) { p.pause() } // replace the pipe method with a new version that will // unlock the buffered stuff. if you just call .pipe() // without a destination, then it'll re-play the events. stream.pipe = (function (orig) { return function (dest) { // console.error(' === open the pipes', dest && dest.path) // let the entries flow through one at a time. // Once they're all done, then we can resume completely. var e = 0 ;(function unblockEntry () { var entry = entryBuffer[e++] // console.error(" ==== unblock entry", entry && entry.path) if (!entry) return resume() entry.on('end', unblockEntry) if (dest) dest.add(entry) else stream.emit('entry', entry) })() function resume () { stream.removeListener('entry', saveEntry) stream.removeListener('data', save) stream.removeListener('end', save) stream.pipe = orig if (dest) stream.pipe(dest) buf.forEach(function (b) { if (b) stream.emit('data', b) else stream.emit('end') }) stream.resume() } return dest } })(stream.pipe) } fstream-1.0.10/lib/dir-reader.js000066400000000000000000000145451273104111400163570ustar00rootroot00000000000000// A thing that emits "entry" events with Reader objects // Pausing it causes it to stop emitting entry events, and also // pauses the current entry if there is one. module.exports = DirReader var fs = require('graceful-fs') var inherits = require('inherits') var path = require('path') var Reader = require('./reader.js') var assert = require('assert').ok inherits(DirReader, Reader) function DirReader (props) { var self = this if (!(self instanceof DirReader)) { throw new Error('DirReader must be called as constructor.') } // should already be established as a Directory type if (props.type !== 'Directory' || !props.Directory) { throw new Error('Non-directory type ' + props.type) } self.entries = null self._index = -1 self._paused = false self._length = -1 if (props.sort) { this.sort = props.sort } Reader.call(this, props) } DirReader.prototype._getEntries = function () { var self = this // race condition. might pause() before calling _getEntries, // and then resume, and try to get them a second time. if (self._gotEntries) return self._gotEntries = true fs.readdir(self._path, function (er, entries) { if (er) return self.error(er) self.entries = entries self.emit('entries', entries) if (self._paused) self.once('resume', processEntries) else processEntries() function processEntries () { self._length = self.entries.length if (typeof self.sort === 'function') { self.entries = self.entries.sort(self.sort.bind(self)) } self._read() } }) } // start walking the dir, and emit an "entry" event for each one. DirReader.prototype._read = function () { var self = this if (!self.entries) return self._getEntries() if (self._paused || self._currentEntry || self._aborted) { // console.error('DR paused=%j, current=%j, aborted=%j', self._paused, !!self._currentEntry, self._aborted) return } self._index++ if (self._index >= self.entries.length) { if (!self._ended) { self._ended = true self.emit('end') self.emit('close') } return } // ok, handle this one, then. // save creating a proxy, by stat'ing the thing now. var p = path.resolve(self._path, self.entries[self._index]) assert(p !== self._path) assert(self.entries[self._index]) // set this to prevent trying to _read() again in the stat time. self._currentEntry = p fs[ self.props.follow ? 'stat' : 'lstat' ](p, function (er, stat) { if (er) return self.error(er) var who = self._proxy || self stat.path = p stat.basename = path.basename(p) stat.dirname = path.dirname(p) var childProps = self.getChildProps.call(who, stat) childProps.path = p childProps.basename = path.basename(p) childProps.dirname = path.dirname(p) var entry = Reader(childProps, stat) // console.error("DR Entry", p, stat.size) self._currentEntry = entry // "entry" events are for direct entries in a specific dir. // "child" events are for any and all children at all levels. // This nomenclature is not completely final. entry.on('pause', function (who) { if (!self._paused && !entry._disowned) { self.pause(who) } }) entry.on('resume', function (who) { if (self._paused && !entry._disowned) { self.resume(who) } }) entry.on('stat', function (props) { self.emit('_entryStat', entry, props) if (entry._aborted) return if (entry._paused) { entry.once('resume', function () { self.emit('entryStat', entry, props) }) } else self.emit('entryStat', entry, props) }) entry.on('ready', function EMITCHILD () { // console.error("DR emit child", entry._path) if (self._paused) { // console.error(" DR emit child - try again later") // pause the child, and emit the "entry" event once we drain. // console.error("DR pausing child entry") entry.pause(self) return self.once('resume', EMITCHILD) } // skip over sockets. they can't be piped around properly, // so there's really no sense even acknowledging them. // if someone really wants to see them, they can listen to // the "socket" events. if (entry.type === 'Socket') { self.emit('socket', entry) } else { self.emitEntry(entry) } }) var ended = false entry.on('close', onend) entry.on('disown', onend) function onend () { if (ended) return ended = true self.emit('childEnd', entry) self.emit('entryEnd', entry) self._currentEntry = null if (!self._paused) { self._read() } } // XXX Remove this. Works in node as of 0.6.2 or so. // Long filenames should not break stuff. entry.on('error', function (er) { if (entry._swallowErrors) { self.warn(er) entry.emit('end') entry.emit('close') } else { self.emit('error', er) } }) // proxy up some events. ;[ 'child', 'childEnd', 'warn' ].forEach(function (ev) { entry.on(ev, self.emit.bind(self, ev)) }) }) } DirReader.prototype.disown = function (entry) { entry.emit('beforeDisown') entry._disowned = true entry.parent = entry.root = null if (entry === this._currentEntry) { this._currentEntry = null } entry.emit('disown') } DirReader.prototype.getChildProps = function () { return { depth: this.depth + 1, root: this.root || this, parent: this, follow: this.follow, filter: this.filter, sort: this.props.sort, hardlinks: this.props.hardlinks } } DirReader.prototype.pause = function (who) { var self = this if (self._paused) return who = who || self self._paused = true if (self._currentEntry && self._currentEntry.pause) { self._currentEntry.pause(who) } self.emit('pause', who) } DirReader.prototype.resume = function (who) { var self = this if (!self._paused) return who = who || self self._paused = false // console.error('DR Emit Resume', self._path) self.emit('resume', who) if (self._paused) { // console.error('DR Re-paused', self._path) return } if (self._currentEntry) { if (self._currentEntry.resume) self._currentEntry.resume(who) } else self._read() } DirReader.prototype.emitEntry = function (entry) { this.emit('entry', entry) this.emit('child', entry) } fstream-1.0.10/lib/dir-writer.js000066400000000000000000000106251273104111400164240ustar00rootroot00000000000000// It is expected that, when .add() returns false, the consumer // of the DirWriter will pause until a "drain" event occurs. Note // that this is *almost always going to be the case*, unless the // thing being written is some sort of unsupported type, and thus // skipped over. module.exports = DirWriter var Writer = require('./writer.js') var inherits = require('inherits') var mkdir = require('mkdirp') var path = require('path') var collect = require('./collect.js') inherits(DirWriter, Writer) function DirWriter (props) { var self = this if (!(self instanceof DirWriter)) { self.error('DirWriter must be called as constructor.', null, true) } // should already be established as a Directory type if (props.type !== 'Directory' || !props.Directory) { self.error('Non-directory type ' + props.type + ' ' + JSON.stringify(props), null, true) } Writer.call(this, props) } DirWriter.prototype._create = function () { var self = this mkdir(self._path, Writer.dirmode, function (er) { if (er) return self.error(er) // ready to start getting entries! self.ready = true self.emit('ready') self._process() }) } // a DirWriter has an add(entry) method, but its .write() doesn't // do anything. Why a no-op rather than a throw? Because this // leaves open the door for writing directory metadata for // gnu/solaris style dumpdirs. DirWriter.prototype.write = function () { return true } DirWriter.prototype.end = function () { this._ended = true this._process() } DirWriter.prototype.add = function (entry) { var self = this // console.error('\tadd', entry._path, '->', self._path) collect(entry) if (!self.ready || self._currentEntry) { self._buffer.push(entry) return false } // create a new writer, and pipe the incoming entry into it. if (self._ended) { return self.error('add after end') } self._buffer.push(entry) self._process() return this._buffer.length === 0 } DirWriter.prototype._process = function () { var self = this // console.error('DW Process p=%j', self._processing, self.basename) if (self._processing) return var entry = self._buffer.shift() if (!entry) { // console.error("DW Drain") self.emit('drain') if (self._ended) self._finish() return } self._processing = true // console.error("DW Entry", entry._path) self.emit('entry', entry) // ok, add this entry // // don't allow recursive copying var p = entry var pp do { pp = p._path || p.path if (pp === self.root._path || pp === self._path || (pp && pp.indexOf(self._path) === 0)) { // console.error('DW Exit (recursive)', entry.basename, self._path) self._processing = false if (entry._collected) entry.pipe() return self._process() } p = p.parent } while (p) // console.error("DW not recursive") // chop off the entry's root dir, replace with ours var props = { parent: self, root: self.root || self, type: entry.type, depth: self.depth + 1 } pp = entry._path || entry.path || entry.props.path if (entry.parent) { pp = pp.substr(entry.parent._path.length + 1) } // get rid of any ../../ shenanigans props.path = path.join(self.path, path.join('/', pp)) // if i have a filter, the child should inherit it. props.filter = self.filter // all the rest of the stuff, copy over from the source. Object.keys(entry.props).forEach(function (k) { if (!props.hasOwnProperty(k)) { props[k] = entry.props[k] } }) // not sure at this point what kind of writer this is. var child = self._currentChild = new Writer(props) child.on('ready', function () { // console.error("DW Child Ready", child.type, child._path) // console.error(" resuming", entry._path) entry.pipe(child) entry.resume() }) // XXX Make this work in node. // Long filenames should not break stuff. child.on('error', function (er) { if (child._swallowErrors) { self.warn(er) child.emit('end') child.emit('close') } else { self.emit('error', er) } }) // we fire _end internally *after* end, so that we don't move on // until any "end" listeners have had their chance to do stuff. child.on('close', onend) var ended = false function onend () { if (ended) return ended = true // console.error("* DW Child end", child.basename) self._currentChild = null self._processing = false self._process() } } fstream-1.0.10/lib/file-reader.js000066400000000000000000000076201273104111400165140ustar00rootroot00000000000000// Basically just a wrapper around an fs.ReadStream module.exports = FileReader var fs = require('graceful-fs') var inherits = require('inherits') var Reader = require('./reader.js') var EOF = {EOF: true} var CLOSE = {CLOSE: true} inherits(FileReader, Reader) function FileReader (props) { // console.error(" FR create", props.path, props.size, new Error().stack) var self = this if (!(self instanceof FileReader)) { throw new Error('FileReader must be called as constructor.') } // should already be established as a File type // XXX Todo: preserve hardlinks by tracking dev+inode+nlink, // with a HardLinkReader class. if (!((props.type === 'Link' && props.Link) || (props.type === 'File' && props.File))) { throw new Error('Non-file type ' + props.type) } self._buffer = [] self._bytesEmitted = 0 Reader.call(self, props) } FileReader.prototype._getStream = function () { var self = this var stream = self._stream = fs.createReadStream(self._path, self.props) if (self.props.blksize) { stream.bufferSize = self.props.blksize } stream.on('open', self.emit.bind(self, 'open')) stream.on('data', function (c) { // console.error('\t\t%d %s', c.length, self.basename) self._bytesEmitted += c.length // no point saving empty chunks if (!c.length) { return } else if (self._paused || self._buffer.length) { self._buffer.push(c) self._read() } else self.emit('data', c) }) stream.on('end', function () { if (self._paused || self._buffer.length) { // console.error('FR Buffering End', self._path) self._buffer.push(EOF) self._read() } else { self.emit('end') } if (self._bytesEmitted !== self.props.size) { self.error("Didn't get expected byte count\n" + 'expect: ' + self.props.size + '\n' + 'actual: ' + self._bytesEmitted) } }) stream.on('close', function () { if (self._paused || self._buffer.length) { // console.error('FR Buffering Close', self._path) self._buffer.push(CLOSE) self._read() } else { // console.error('FR close 1', self._path) self.emit('close') } }) stream.on('error', function (e) { self.emit('error', e) }) self._read() } FileReader.prototype._read = function () { var self = this // console.error('FR _read', self._path) if (self._paused) { // console.error('FR _read paused', self._path) return } if (!self._stream) { // console.error('FR _getStream calling', self._path) return self._getStream() } // clear out the buffer, if there is one. if (self._buffer.length) { // console.error('FR _read has buffer', self._buffer.length, self._path) var buf = self._buffer for (var i = 0, l = buf.length; i < l; i++) { var c = buf[i] if (c === EOF) { // console.error('FR Read emitting buffered end', self._path) self.emit('end') } else if (c === CLOSE) { // console.error('FR Read emitting buffered close', self._path) self.emit('close') } else { // console.error('FR Read emitting buffered data', self._path) self.emit('data', c) } if (self._paused) { // console.error('FR Read Re-pausing at '+i, self._path) self._buffer = buf.slice(i) return } } self._buffer.length = 0 } // console.error("FR _read done") // that's about all there is to it. } FileReader.prototype.pause = function (who) { var self = this // console.error('FR Pause', self._path) if (self._paused) return who = who || self self._paused = true if (self._stream) self._stream.pause() self.emit('pause', who) } FileReader.prototype.resume = function (who) { var self = this // console.error('FR Resume', self._path) if (!self._paused) return who = who || self self.emit('resume', who) self._paused = false if (self._stream) self._stream.resume() self._read() } fstream-1.0.10/lib/file-writer.js000066400000000000000000000050411273104111400165610ustar00rootroot00000000000000module.exports = FileWriter var fs = require('graceful-fs') var Writer = require('./writer.js') var inherits = require('inherits') var EOF = {} inherits(FileWriter, Writer) function FileWriter (props) { var self = this if (!(self instanceof FileWriter)) { throw new Error('FileWriter must be called as constructor.') } // should already be established as a File type if (props.type !== 'File' || !props.File) { throw new Error('Non-file type ' + props.type) } self._buffer = [] self._bytesWritten = 0 Writer.call(this, props) } FileWriter.prototype._create = function () { var self = this if (self._stream) return var so = {} if (self.props.flags) so.flags = self.props.flags so.mode = Writer.filemode if (self._old && self._old.blksize) so.bufferSize = self._old.blksize self._stream = fs.createWriteStream(self._path, so) self._stream.on('open', function () { // console.error("FW open", self._buffer, self._path) self.ready = true self._buffer.forEach(function (c) { if (c === EOF) self._stream.end() else self._stream.write(c) }) self.emit('ready') // give this a kick just in case it needs it. self.emit('drain') }) self._stream.on('error', function (er) { self.emit('error', er) }) self._stream.on('drain', function () { self.emit('drain') }) self._stream.on('close', function () { // console.error('\n\nFW Stream Close', self._path, self.size) self._finish() }) } FileWriter.prototype.write = function (c) { var self = this self._bytesWritten += c.length if (!self.ready) { if (!Buffer.isBuffer(c) && typeof c !== 'string') { throw new Error('invalid write data') } self._buffer.push(c) return false } var ret = self._stream.write(c) // console.error('\t-- fw wrote, _stream says', ret, self._stream._queue.length) // allow 2 buffered writes, because otherwise there's just too // much stop and go bs. if (ret === false && self._stream._queue) { return self._stream._queue.length <= 2 } else { return ret } } FileWriter.prototype.end = function (c) { var self = this if (c) self.write(c) if (!self.ready) { self._buffer.push(EOF) return false } return self._stream.end() } FileWriter.prototype._finish = function () { var self = this if (typeof self.size === 'number' && self._bytesWritten !== self.size) { self.error( 'Did not get expected byte count.\n' + 'expect: ' + self.size + '\n' + 'actual: ' + self._bytesWritten) } Writer.prototype._finish.call(self) } fstream-1.0.10/lib/get-type.js000066400000000000000000000011421273104111400160640ustar00rootroot00000000000000module.exports = getType function getType (st) { var types = [ 'Directory', 'File', 'SymbolicLink', 'Link', // special for hardlinks from tarballs 'BlockDevice', 'CharacterDevice', 'FIFO', 'Socket' ] var type if (st.type && types.indexOf(st.type) !== -1) { st[st.type] = true return st.type } for (var i = 0, l = types.length; i < l; i++) { type = types[i] var is = st[type] || st['is' + type] if (typeof is === 'function') is = is.call(st) if (is) { st[type] = true st.type = type return type } } return null } fstream-1.0.10/lib/link-reader.js000066400000000000000000000027431273104111400165330ustar00rootroot00000000000000// Basically just a wrapper around an fs.readlink // // XXX: Enhance this to support the Link type, by keeping // a lookup table of {:}, so that hardlinks // can be preserved in tarballs. module.exports = LinkReader var fs = require('graceful-fs') var inherits = require('inherits') var Reader = require('./reader.js') inherits(LinkReader, Reader) function LinkReader (props) { var self = this if (!(self instanceof LinkReader)) { throw new Error('LinkReader must be called as constructor.') } if (!((props.type === 'Link' && props.Link) || (props.type === 'SymbolicLink' && props.SymbolicLink))) { throw new Error('Non-link type ' + props.type) } Reader.call(self, props) } // When piping a LinkReader into a LinkWriter, we have to // already have the linkpath property set, so that has to // happen *before* the "ready" event, which means we need to // override the _stat method. LinkReader.prototype._stat = function (currentStat) { var self = this fs.readlink(self._path, function (er, linkpath) { if (er) return self.error(er) self.linkpath = self.props.linkpath = linkpath self.emit('linkpath', linkpath) Reader.prototype._stat.call(self, currentStat) }) } LinkReader.prototype._read = function () { var self = this if (self._paused) return // basically just a no-op, since we got all the info we need // from the _stat method if (!self._ended) { self.emit('end') self.emit('close') self._ended = true } } fstream-1.0.10/lib/link-writer.js000066400000000000000000000054361273104111400166070ustar00rootroot00000000000000module.exports = LinkWriter var fs = require('graceful-fs') var Writer = require('./writer.js') var inherits = require('inherits') var path = require('path') var rimraf = require('rimraf') inherits(LinkWriter, Writer) function LinkWriter (props) { var self = this if (!(self instanceof LinkWriter)) { throw new Error('LinkWriter must be called as constructor.') } // should already be established as a Link type if (!((props.type === 'Link' && props.Link) || (props.type === 'SymbolicLink' && props.SymbolicLink))) { throw new Error('Non-link type ' + props.type) } if (props.linkpath === '') props.linkpath = '.' if (!props.linkpath) { self.error('Need linkpath property to create ' + props.type) } Writer.call(this, props) } LinkWriter.prototype._create = function () { // console.error(" LW _create") var self = this var hard = self.type === 'Link' || process.platform === 'win32' var link = hard ? 'link' : 'symlink' var lp = hard ? path.resolve(self.dirname, self.linkpath) : self.linkpath // can only change the link path by clobbering // For hard links, let's just assume that's always the case, since // there's no good way to read them if we don't already know. if (hard) return clobber(self, lp, link) fs.readlink(self._path, function (er, p) { // only skip creation if it's exactly the same link if (p && p === lp) return finish(self) clobber(self, lp, link) }) } function clobber (self, lp, link) { rimraf(self._path, function (er) { if (er) return self.error(er) create(self, lp, link) }) } function create (self, lp, link) { fs[link](lp, self._path, function (er) { // if this is a hard link, and we're in the process of writing out a // directory, it's very possible that the thing we're linking to // doesn't exist yet (especially if it was intended as a symlink), // so swallow ENOENT errors here and just soldier in. // Additionally, an EPERM or EACCES can happen on win32 if it's trying // to make a link to a directory. Again, just skip it. // A better solution would be to have fs.symlink be supported on // windows in some nice fashion. if (er) { if ((er.code === 'ENOENT' || er.code === 'EACCES' || er.code === 'EPERM') && process.platform === 'win32') { self.ready = true self.emit('ready') self.emit('end') self.emit('close') self.end = self._finish = function () {} } else return self.error(er) } finish(self) }) } function finish (self) { self.ready = true self.emit('ready') if (self._ended && !self._finished) self._finish() } LinkWriter.prototype.end = function () { // console.error("LW finish in end") this._ended = true if (this.ready) { this._finished = true this._finish() } } fstream-1.0.10/lib/proxy-reader.js000066400000000000000000000037241273104111400167570ustar00rootroot00000000000000// A reader for when we don't yet know what kind of thing // the thing is. module.exports = ProxyReader var Reader = require('./reader.js') var getType = require('./get-type.js') var inherits = require('inherits') var fs = require('graceful-fs') inherits(ProxyReader, Reader) function ProxyReader (props) { var self = this if (!(self instanceof ProxyReader)) { throw new Error('ProxyReader must be called as constructor.') } self.props = props self._buffer = [] self.ready = false Reader.call(self, props) } ProxyReader.prototype._stat = function () { var self = this var props = self.props // stat the thing to see what the proxy should be. var stat = props.follow ? 'stat' : 'lstat' fs[stat](props.path, function (er, current) { var type if (er || !current) { type = 'File' } else { type = getType(current) } props[type] = true props.type = self.type = type self._old = current self._addProxy(Reader(props, current)) }) } ProxyReader.prototype._addProxy = function (proxy) { var self = this if (self._proxyTarget) { return self.error('proxy already set') } self._proxyTarget = proxy proxy._proxy = self ;[ 'error', 'data', 'end', 'close', 'linkpath', 'entry', 'entryEnd', 'child', 'childEnd', 'warn', 'stat' ].forEach(function (ev) { // console.error('~~ proxy event', ev, self.path) proxy.on(ev, self.emit.bind(self, ev)) }) self.emit('proxy', proxy) proxy.on('ready', function () { // console.error("~~ proxy is ready!", self.path) self.ready = true self.emit('ready') }) var calls = self._buffer self._buffer.length = 0 calls.forEach(function (c) { proxy[c[0]].apply(proxy, c[1]) }) } ProxyReader.prototype.pause = function () { return this._proxyTarget ? this._proxyTarget.pause() : false } ProxyReader.prototype.resume = function () { return this._proxyTarget ? this._proxyTarget.resume() : false } fstream-1.0.10/lib/proxy-writer.js000066400000000000000000000046161273104111400170320ustar00rootroot00000000000000// A writer for when we don't know what kind of thing // the thing is. That is, it's not explicitly set, // so we're going to make it whatever the thing already // is, or "File" // // Until then, collect all events. module.exports = ProxyWriter var Writer = require('./writer.js') var getType = require('./get-type.js') var inherits = require('inherits') var collect = require('./collect.js') var fs = require('fs') inherits(ProxyWriter, Writer) function ProxyWriter (props) { var self = this if (!(self instanceof ProxyWriter)) { throw new Error('ProxyWriter must be called as constructor.') } self.props = props self._needDrain = false Writer.call(self, props) } ProxyWriter.prototype._stat = function () { var self = this var props = self.props // stat the thing to see what the proxy should be. var stat = props.follow ? 'stat' : 'lstat' fs[stat](props.path, function (er, current) { var type if (er || !current) { type = 'File' } else { type = getType(current) } props[type] = true props.type = self.type = type self._old = current self._addProxy(Writer(props, current)) }) } ProxyWriter.prototype._addProxy = function (proxy) { // console.error("~~ set proxy", this.path) var self = this if (self._proxy) { return self.error('proxy already set') } self._proxy = proxy ;[ 'ready', 'error', 'close', 'pipe', 'drain', 'warn' ].forEach(function (ev) { proxy.on(ev, self.emit.bind(self, ev)) }) self.emit('proxy', proxy) var calls = self._buffer calls.forEach(function (c) { // console.error("~~ ~~ proxy buffered call", c[0], c[1]) proxy[c[0]].apply(proxy, c[1]) }) self._buffer.length = 0 if (self._needsDrain) self.emit('drain') } ProxyWriter.prototype.add = function (entry) { // console.error("~~ proxy add") collect(entry) if (!this._proxy) { this._buffer.push(['add', [entry]]) this._needDrain = true return false } return this._proxy.add(entry) } ProxyWriter.prototype.write = function (c) { // console.error('~~ proxy write') if (!this._proxy) { this._buffer.push(['write', [c]]) this._needDrain = true return false } return this._proxy.write(c) } ProxyWriter.prototype.end = function (c) { // console.error('~~ proxy end') if (!this._proxy) { this._buffer.push(['end', [c]]) return false } return this._proxy.end(c) } fstream-1.0.10/lib/reader.js000066400000000000000000000156541273104111400156050ustar00rootroot00000000000000module.exports = Reader var fs = require('graceful-fs') var Stream = require('stream').Stream var inherits = require('inherits') var path = require('path') var getType = require('./get-type.js') var hardLinks = Reader.hardLinks = {} var Abstract = require('./abstract.js') // Must do this *before* loading the child classes inherits(Reader, Abstract) var LinkReader = require('./link-reader.js') function Reader (props, currentStat) { var self = this if (!(self instanceof Reader)) return new Reader(props, currentStat) if (typeof props === 'string') { props = { path: props } } if (!props.path) { self.error('Must provide a path', null, true) } // polymorphism. // call fstream.Reader(dir) to get a DirReader object, etc. // Note that, unlike in the Writer case, ProxyReader is going // to be the *normal* state of affairs, since we rarely know // the type of a file prior to reading it. var type var ClassType if (props.type && typeof props.type === 'function') { type = props.type ClassType = type } else { type = getType(props) ClassType = Reader } if (currentStat && !type) { type = getType(currentStat) props[type] = true props.type = type } switch (type) { case 'Directory': ClassType = require('./dir-reader.js') break case 'Link': // XXX hard links are just files. // However, it would be good to keep track of files' dev+inode // and nlink values, and create a HardLinkReader that emits // a linkpath value of the original copy, so that the tar // writer can preserve them. // ClassType = HardLinkReader // break case 'File': ClassType = require('./file-reader.js') break case 'SymbolicLink': ClassType = LinkReader break case 'Socket': ClassType = require('./socket-reader.js') break case null: ClassType = require('./proxy-reader.js') break } if (!(self instanceof ClassType)) { return new ClassType(props) } Abstract.call(self) self.readable = true self.writable = false self.type = type self.props = props self.depth = props.depth = props.depth || 0 self.parent = props.parent || null self.root = props.root || (props.parent && props.parent.root) || self self._path = self.path = path.resolve(props.path) if (process.platform === 'win32') { self.path = self._path = self.path.replace(/\?/g, '_') if (self._path.length >= 260) { // how DOES one create files on the moon? // if the path has spaces in it, then UNC will fail. self._swallowErrors = true // if (self._path.indexOf(" ") === -1) { self._path = '\\\\?\\' + self.path.replace(/\//g, '\\') // } } } self.basename = props.basename = path.basename(self.path) self.dirname = props.dirname = path.dirname(self.path) // these have served their purpose, and are now just noisy clutter props.parent = props.root = null // console.error("\n\n\n%s setting size to", props.path, props.size) self.size = props.size self.filter = typeof props.filter === 'function' ? props.filter : null if (props.sort === 'alpha') props.sort = alphasort // start the ball rolling. // this will stat the thing, and then call self._read() // to start reading whatever it is. // console.error("calling stat", props.path, currentStat) self._stat(currentStat) } function alphasort (a, b) { return a === b ? 0 : a.toLowerCase() > b.toLowerCase() ? 1 : a.toLowerCase() < b.toLowerCase() ? -1 : a > b ? 1 : -1 } Reader.prototype._stat = function (currentStat) { var self = this var props = self.props var stat = props.follow ? 'stat' : 'lstat' // console.error("Reader._stat", self._path, currentStat) if (currentStat) process.nextTick(statCb.bind(null, null, currentStat)) else fs[stat](self._path, statCb) function statCb (er, props_) { // console.error("Reader._stat, statCb", self._path, props_, props_.nlink) if (er) return self.error(er) Object.keys(props_).forEach(function (k) { props[k] = props_[k] }) // if it's not the expected size, then abort here. if (undefined !== self.size && props.size !== self.size) { return self.error('incorrect size') } self.size = props.size var type = getType(props) var handleHardlinks = props.hardlinks !== false // special little thing for handling hardlinks. if (handleHardlinks && type !== 'Directory' && props.nlink && props.nlink > 1) { var k = props.dev + ':' + props.ino // console.error("Reader has nlink", self._path, k) if (hardLinks[k] === self._path || !hardLinks[k]) { hardLinks[k] = self._path } else { // switch into hardlink mode. type = self.type = self.props.type = 'Link' self.Link = self.props.Link = true self.linkpath = self.props.linkpath = hardLinks[k] // console.error("Hardlink detected, switching mode", self._path, self.linkpath) // Setting __proto__ would arguably be the "correct" // approach here, but that just seems too wrong. self._stat = self._read = LinkReader.prototype._read } } if (self.type && self.type !== type) { self.error('Unexpected type: ' + type) } // if the filter doesn't pass, then just skip over this one. // still have to emit end so that dir-walking can move on. if (self.filter) { var who = self._proxy || self // special handling for ProxyReaders if (!self.filter.call(who, who, props)) { if (!self._disowned) { self.abort() self.emit('end') self.emit('close') } return } } // last chance to abort or disown before the flow starts! var events = ['_stat', 'stat', 'ready'] var e = 0 ;(function go () { if (self._aborted) { self.emit('end') self.emit('close') return } if (self._paused && self.type !== 'Directory') { self.once('resume', go) return } var ev = events[e++] if (!ev) { return self._read() } self.emit(ev, props) go() })() } } Reader.prototype.pipe = function (dest) { var self = this if (typeof dest.add === 'function') { // piping to a multi-compatible, and we've got directory entries. self.on('entry', function (entry) { var ret = dest.add(entry) if (ret === false) { self.pause() } }) } // console.error("R Pipe apply Stream Pipe") return Stream.prototype.pipe.apply(this, arguments) } Reader.prototype.pause = function (who) { this._paused = true who = who || this this.emit('pause', who) if (this._stream) this._stream.pause(who) } Reader.prototype.resume = function (who) { this._paused = false who = who || this this.emit('resume', who) if (this._stream) this._stream.resume(who) this._read() } Reader.prototype._read = function () { this.error('Cannot read unknown type: ' + this.type) } fstream-1.0.10/lib/socket-reader.js000066400000000000000000000016231273104111400170620ustar00rootroot00000000000000// Just get the stats, and then don't do anything. // You can't really "read" from a socket. You "connect" to it. // Mostly, this is here so that reading a dir with a socket in it // doesn't blow up. module.exports = SocketReader var inherits = require('inherits') var Reader = require('./reader.js') inherits(SocketReader, Reader) function SocketReader (props) { var self = this if (!(self instanceof SocketReader)) { throw new Error('SocketReader must be called as constructor.') } if (!(props.type === 'Socket' && props.Socket)) { throw new Error('Non-socket type ' + props.type) } Reader.call(self, props) } SocketReader.prototype._read = function () { var self = this if (self._paused) return // basically just a no-op, since we got all the info we have // from the _stat method if (!self._ended) { self.emit('end') self.emit('close') self._ended = true } } fstream-1.0.10/lib/writer.js000066400000000000000000000253671273104111400156610ustar00rootroot00000000000000module.exports = Writer var fs = require('graceful-fs') var inherits = require('inherits') var rimraf = require('rimraf') var mkdir = require('mkdirp') var path = require('path') var umask = process.platform === 'win32' ? 0 : process.umask() var getType = require('./get-type.js') var Abstract = require('./abstract.js') // Must do this *before* loading the child classes inherits(Writer, Abstract) Writer.dirmode = parseInt('0777', 8) & (~umask) Writer.filemode = parseInt('0666', 8) & (~umask) var DirWriter = require('./dir-writer.js') var LinkWriter = require('./link-writer.js') var FileWriter = require('./file-writer.js') var ProxyWriter = require('./proxy-writer.js') // props is the desired state. current is optionally the current stat, // provided here so that subclasses can avoid statting the target // more than necessary. function Writer (props, current) { var self = this if (typeof props === 'string') { props = { path: props } } if (!props.path) self.error('Must provide a path', null, true) // polymorphism. // call fstream.Writer(dir) to get a DirWriter object, etc. var type = getType(props) var ClassType = Writer switch (type) { case 'Directory': ClassType = DirWriter break case 'File': ClassType = FileWriter break case 'Link': case 'SymbolicLink': ClassType = LinkWriter break case null: default: // Don't know yet what type to create, so we wrap in a proxy. ClassType = ProxyWriter break } if (!(self instanceof ClassType)) return new ClassType(props) // now get down to business. Abstract.call(self) // props is what we want to set. // set some convenience properties as well. self.type = props.type self.props = props self.depth = props.depth || 0 self.clobber = props.clobber === false ? props.clobber : true self.parent = props.parent || null self.root = props.root || (props.parent && props.parent.root) || self self._path = self.path = path.resolve(props.path) if (process.platform === 'win32') { self.path = self._path = self.path.replace(/\?/g, '_') if (self._path.length >= 260) { self._swallowErrors = true self._path = '\\\\?\\' + self.path.replace(/\//g, '\\') } } self.basename = path.basename(props.path) self.dirname = path.dirname(props.path) self.linkpath = props.linkpath || null props.parent = props.root = null // console.error("\n\n\n%s setting size to", props.path, props.size) self.size = props.size if (typeof props.mode === 'string') { props.mode = parseInt(props.mode, 8) } self.readable = false self.writable = true // buffer until ready, or while handling another entry self._buffer = [] self.ready = false self.filter = typeof props.filter === 'function' ? props.filter : null // start the ball rolling. // this checks what's there already, and then calls // self._create() to call the impl-specific creation stuff. self._stat(current) } // Calling this means that it's something we can't create. // Just assert that it's already there, otherwise raise a warning. Writer.prototype._create = function () { var self = this fs[self.props.follow ? 'stat' : 'lstat'](self._path, function (er) { if (er) { return self.warn('Cannot create ' + self._path + '\n' + 'Unsupported type: ' + self.type, 'ENOTSUP') } self._finish() }) } Writer.prototype._stat = function (current) { var self = this var props = self.props var stat = props.follow ? 'stat' : 'lstat' var who = self._proxy || self if (current) statCb(null, current) else fs[stat](self._path, statCb) function statCb (er, current) { if (self.filter && !self.filter.call(who, who, current)) { self._aborted = true self.emit('end') self.emit('close') return } // if it's not there, great. We'll just create it. // if it is there, then we'll need to change whatever differs if (er || !current) { return create(self) } self._old = current var currentType = getType(current) // if it's a type change, then we need to clobber or error. // if it's not a type change, then let the impl take care of it. if (currentType !== self.type) { return rimraf(self._path, function (er) { if (er) return self.error(er) self._old = null create(self) }) } // otherwise, just handle in the app-specific way // this creates a fs.WriteStream, or mkdir's, or whatever create(self) } } function create (self) { // console.error("W create", self._path, Writer.dirmode) // XXX Need to clobber non-dirs that are in the way, // unless { clobber: false } in the props. mkdir(path.dirname(self._path), Writer.dirmode, function (er, made) { // console.error("W created", path.dirname(self._path), er) if (er) return self.error(er) // later on, we have to set the mode and owner for these self._madeDir = made return self._create() }) } function endChmod (self, want, current, path, cb) { var wantMode = want.mode var chmod = want.follow || self.type !== 'SymbolicLink' ? 'chmod' : 'lchmod' if (!fs[chmod]) return cb() if (typeof wantMode !== 'number') return cb() var curMode = current.mode & parseInt('0777', 8) wantMode = wantMode & parseInt('0777', 8) if (wantMode === curMode) return cb() fs[chmod](path, wantMode, cb) } function endChown (self, want, current, path, cb) { // Don't even try it unless root. Too easy to EPERM. if (process.platform === 'win32') return cb() if (!process.getuid || process.getuid() !== 0) return cb() if (typeof want.uid !== 'number' && typeof want.gid !== 'number') return cb() if (current.uid === want.uid && current.gid === want.gid) return cb() var chown = (self.props.follow || self.type !== 'SymbolicLink') ? 'chown' : 'lchown' if (!fs[chown]) return cb() if (typeof want.uid !== 'number') want.uid = current.uid if (typeof want.gid !== 'number') want.gid = current.gid fs[chown](path, want.uid, want.gid, cb) } function endUtimes (self, want, current, path, cb) { if (!fs.utimes || process.platform === 'win32') return cb() var utimes = (want.follow || self.type !== 'SymbolicLink') ? 'utimes' : 'lutimes' if (utimes === 'lutimes' && !fs[utimes]) { utimes = 'utimes' } if (!fs[utimes]) return cb() var curA = current.atime var curM = current.mtime var meA = want.atime var meM = want.mtime if (meA === undefined) meA = curA if (meM === undefined) meM = curM if (!isDate(meA)) meA = new Date(meA) if (!isDate(meM)) meA = new Date(meM) if (meA.getTime() === curA.getTime() && meM.getTime() === curM.getTime()) return cb() fs[utimes](path, meA, meM, cb) } // XXX This function is beastly. Break it up! Writer.prototype._finish = function () { var self = this if (self._finishing) return self._finishing = true // console.error(" W Finish", self._path, self.size) // set up all the things. // At this point, we're already done writing whatever we've gotta write, // adding files to the dir, etc. var todo = 0 var errState = null var done = false if (self._old) { // the times will almost *certainly* have changed. // adds the utimes syscall, but remove another stat. self._old.atime = new Date(0) self._old.mtime = new Date(0) // console.error(" W Finish Stale Stat", self._path, self.size) setProps(self._old) } else { var stat = self.props.follow ? 'stat' : 'lstat' // console.error(" W Finish Stating", self._path, self.size) fs[stat](self._path, function (er, current) { // console.error(" W Finish Stated", self._path, self.size, current) if (er) { // if we're in the process of writing out a // directory, it's very possible that the thing we're linking to // doesn't exist yet (especially if it was intended as a symlink), // so swallow ENOENT errors here and just soldier on. if (er.code === 'ENOENT' && (self.type === 'Link' || self.type === 'SymbolicLink') && process.platform === 'win32') { self.ready = true self.emit('ready') self.emit('end') self.emit('close') self.end = self._finish = function () {} return } else return self.error(er) } setProps(self._old = current) }) } return function setProps (current) { todo += 3 endChmod(self, self.props, current, self._path, next('chmod')) endChown(self, self.props, current, self._path, next('chown')) endUtimes(self, self.props, current, self._path, next('utimes')) } function next (what) { return function (er) { // console.error(" W Finish", what, todo) if (errState) return if (er) { er.fstream_finish_call = what return self.error(errState = er) } if (--todo > 0) return if (done) return done = true // we may still need to set the mode/etc. on some parent dirs // that were created previously. delay end/close until then. if (!self._madeDir) return end() else endMadeDir(self, self._path, end) function end (er) { if (er) { er.fstream_finish_call = 'setupMadeDir' return self.error(er) } // all the props have been set, so we're completely done. self.emit('end') self.emit('close') } } } } function endMadeDir (self, p, cb) { var made = self._madeDir // everything *between* made and path.dirname(self._path) // needs to be set up. Note that this may just be one dir. var d = path.dirname(p) endMadeDir_(self, d, function (er) { if (er) return cb(er) if (d === made) { return cb() } endMadeDir(self, d, cb) }) } function endMadeDir_ (self, p, cb) { var dirProps = {} Object.keys(self.props).forEach(function (k) { dirProps[k] = self.props[k] // only make non-readable dirs if explicitly requested. if (k === 'mode' && self.type !== 'Directory') { dirProps[k] = dirProps[k] | parseInt('0111', 8) } }) var todo = 3 var errState = null fs.stat(p, function (er, current) { if (er) return cb(errState = er) endChmod(self, dirProps, current, p, next) endChown(self, dirProps, current, p, next) endUtimes(self, dirProps, current, p, next) }) function next (er) { if (errState) return if (er) return cb(errState = er) if (--todo === 0) return cb() } } Writer.prototype.pipe = function () { this.error("Can't pipe from writable stream") } Writer.prototype.add = function () { this.error("Can't add to non-Directory type") } Writer.prototype.write = function () { return true } function objectToString (d) { return Object.prototype.toString.call(d) } function isDate (d) { return typeof d === 'object' && objectToString(d) === '[object Date]' } fstream-1.0.10/package.json000066400000000000000000000011341273104111400155110ustar00rootroot00000000000000{ "author": "Isaac Z. Schlueter (http://blog.izs.me/)", "name": "fstream", "description": "Advanced file system stream things", "version": "1.0.10", "repository": { "type": "git", "url": "https://github.com/npm/fstream.git" }, "main": "fstream.js", "engines": { "node": ">=0.6" }, "dependencies": { "graceful-fs": "^4.1.2", "inherits": "~2.0.0", "mkdirp": ">=0.5 0", "rimraf": "2" }, "devDependencies": { "standard": "^4.0.0", "tap": "^1.2.0" }, "scripts": { "test": "standard && tap examples/*.js" }, "license": "ISC" }