pax_global_header00006660000000000000000000000064121736461400014516gustar00rootroot0000000000000052 comment=695b85efbdfd895d783c83de166bd4b6441d043c fstream-0.1.24/000077500000000000000000000000001217364614000132435ustar00rootroot00000000000000fstream-0.1.24/.gitignore000066400000000000000000000001161217364614000152310ustar00rootroot00000000000000.*.swp node_modules/ examples/deep-copy/ examples/path/ examples/filter-copy/ fstream-0.1.24/.travis.yml000066400000000000000000000000431217364614000153510ustar00rootroot00000000000000language: node_js node_js: - 0.6 fstream-0.1.24/LICENSE000066400000000000000000000024361217364614000142550ustar00rootroot00000000000000Copyright (c) Isaac Z. Schlueter ("Author") All rights reserved. The BSD License Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. fstream-0.1.24/README.md000066400000000000000000000044511217364614000145260ustar00rootroot00000000000000Like 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-0.1.24/examples/000077500000000000000000000000001217364614000150615ustar00rootroot00000000000000fstream-0.1.24/examples/filter-pipe.js000066400000000000000000000075301217364614000176440ustar00rootroot00000000000000var 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 = "" var escape = {} 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) } while (p = p.parent) throw new Error("\033[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) { 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\033[32m") entry.pause() entry.once("resume", liftFog) foggy = setTimeout(liftFog, 1000) 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("\033[mthe fog lifts!\n") clearTimeout(foggy) foggy = null if (entry._paused) entry.resume() } } }) } } 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) } ended = false var i = 1 r.on("end", function () { if (foggy) clearTimeout(foggy) console.error("\033[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") }) r.pipe(w) fstream-0.1.24/examples/pipe.js000066400000000000000000000062431217364614000163610ustar00rootroot00000000000000var 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 = "" var escape = {} 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) } while (p = p.parent) throw new Error("\033[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) { 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\033[32m") entry.pause() entry.once("resume", liftFog) foggy = setTimeout(liftFog, 10) 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("\033[mthe fog lifts!\n") clearTimeout(foggy) foggy = null if (entry._paused) entry.resume() } } }) } } 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) } ended = false r.on("end", function () { if (foggy) clearTimeout(foggy) console.error("\033[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") }) r.pipe(w) fstream-0.1.24/examples/reader.js000066400000000000000000000024151217364614000166630ustar00rootroot00000000000000var fstream = require("../fstream.js") var tap = require("tap") var fs = require("fs") var path = require("path") var children = -1 var dir = path.dirname(__dirname) var gotReady = false var ended = false tap.test("reader test", function (t) { 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() }) }) fstream-0.1.24/examples/symlink-write.js000066400000000000000000000013001217364614000202270ustar00rootroot00000000000000var fstream = require("../fstream.js") , closed = false fstream .Writer({ path: "path/to/symlink" , linkpath: "./file" , isSymbolicLink: true , mode: "0755" // octal strings supported }) .on("close", function () { closed = 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((closed?"":"not ")+"ok 3 should be closed") }) fstream-0.1.24/fstream.js000066400000000000000000000021401217364614000152370ustar00rootroot00000000000000exports.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-0.1.24/lib/000077500000000000000000000000001217364614000140115ustar00rootroot00000000000000fstream-0.1.24/lib/abstract.js000066400000000000000000000042421217364614000161540ustar00rootroot00000000000000// the parent class for all fstreams. module.exports = Abstract var Stream = require("stream").Stream , 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 me = this , er = decorate(msg, code, me) if (!me.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 { me.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, me) { if (!(er instanceof Error)) er = new Error(er) er.code = er.code || code er.path = er.path || me.path er.fstream_type = er.fstream_type || me.type er.fstream_path = er.fstream_path || me.path if (me._path !== me.path) { er.fstream_unc_path = er.fstream_unc_path || me._path } if (me.linkpath) { er.fstream_linkpath = er.fstream_linkpath || me.linkpath } er.fstream_class = er.fstream_class || me.constructor.name er.fstream_stack = er.fstream_stack || new Error().stack.split(/\n/).slice(3).map(function (s) { return s.replace(/^ at /, "") }) return er } fstream-0.1.24/lib/collect.js000066400000000000000000000031641217364614000160000ustar00rootroot00000000000000module.exports = collect function collect (stream) { if (stream._collected) return 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-0.1.24/lib/dir-reader.js000066400000000000000000000143721217364614000163740ustar00rootroot00000000000000// 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") , fstream = require("../fstream.js") , Reader = fstream.Reader , inherits = require("inherits") , mkdir = require("mkdirp") , path = require("path") , Reader = require("./reader.js") , assert = require("assert").ok inherits(DirReader, Reader) function DirReader (props) { var me = this if (!(me 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) } me.entries = null me._index = -1 me._paused = false me._length = -1 if (props.sort) { this.sort = props.sort } Reader.call(this, props) } DirReader.prototype._getEntries = function () { var me = this // race condition. might pause() before calling _getEntries, // and then resume, and try to get them a second time. if (me._gotEntries) return me._gotEntries = true fs.readdir(me._path, function (er, entries) { if (er) return me.error(er) me.entries = entries me.emit("entries", entries) if (me._paused) me.once("resume", processEntries) else processEntries() function processEntries () { me._length = me.entries.length if (typeof me.sort === "function") { me.entries = me.entries.sort(me.sort.bind(me)) } me._read() } }) } // start walking the dir, and emit an "entry" event for each one. DirReader.prototype._read = function () { var me = this if (!me.entries) return me._getEntries() if (me._paused || me._currentEntry || me._aborted) { // console.error("DR paused=%j, current=%j, aborted=%j", me._paused, !!me._currentEntry, me._aborted) return } me._index ++ if (me._index >= me.entries.length) { if (!me._ended) { me._ended = true me.emit("end") me.emit("close") } return } // ok, handle this one, then. // save creating a proxy, by stat'ing the thing now. var p = path.resolve(me._path, me.entries[me._index]) assert(p !== me._path) assert(me.entries[me._index]) // set this to prevent trying to _read() again in the stat time. me._currentEntry = p fs[ me.props.follow ? "stat" : "lstat" ](p, function (er, stat) { if (er) return me.error(er) var who = me._proxy || me stat.path = p stat.basename = path.basename(p) stat.dirname = path.dirname(p) var childProps = me.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) me._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 (!me._paused && !entry._disowned) { me.pause(who) } }) entry.on("resume", function (who) { if (me._paused && !entry._disowned) { me.resume(who) } }) entry.on("stat", function (props) { me.emit("_entryStat", entry, props) if (entry._aborted) return if (entry._paused) entry.once("resume", function () { me.emit("entryStat", entry, props) }) else me.emit("entryStat", entry, props) }) entry.on("ready", function EMITCHILD () { // console.error("DR emit child", entry._path) if (me._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(me) return me.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") { me.emit("socket", entry) } else { me.emitEntry(entry) } }) var ended = false entry.on("close", onend) entry.on("disown", onend) function onend () { if (ended) return ended = true me.emit("childEnd", entry) me.emit("entryEnd", entry) me._currentEntry = null if (!me._paused) { me._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) { me.warn(er) entry.emit("end") entry.emit("close") } else { me.emit("error", er) } }) // proxy up some events. ; [ "child" , "childEnd" , "warn" ].forEach(function (ev) { entry.on(ev, me.emit.bind(me, 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 (stat) { return { depth: this.depth + 1 , root: this.root || this , parent: this , follow: this.follow , filter: this.filter , sort: this.props.sort } } DirReader.prototype.pause = function (who) { var me = this if (me._paused) return who = who || me me._paused = true if (me._currentEntry && me._currentEntry.pause) { me._currentEntry.pause(who) } me.emit("pause", who) } DirReader.prototype.resume = function (who) { var me = this if (!me._paused) return who = who || me me._paused = false // console.error("DR Emit Resume", me._path) me.emit("resume", who) if (me._paused) { // console.error("DR Re-paused", me._path) return } if (me._currentEntry) { if (me._currentEntry.resume) me._currentEntry.resume(who) } else me._read() } DirReader.prototype.emitEntry = function (entry) { this.emit("entry", entry) this.emit("child", entry) } fstream-0.1.24/lib/dir-writer.js000066400000000000000000000106301217364614000164370ustar00rootroot00000000000000// 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 fs = require("graceful-fs") , fstream = require("../fstream.js") , Writer = require("./writer.js") , inherits = require("inherits") , mkdir = require("mkdirp") , path = require("path") , collect = require("./collect.js") inherits(DirWriter, Writer) function DirWriter (props) { var me = this if (!(me instanceof DirWriter)) me.error( "DirWriter must be called as constructor.", null, true) // should already be established as a Directory type if (props.type !== "Directory" || !props.Directory) { me.error("Non-directory type "+ props.type + " " + JSON.stringify(props), null, true) } Writer.call(this, props) } DirWriter.prototype._create = function () { var me = this mkdir(me._path, Writer.dirmode, function (er) { if (er) return me.error(er) // ready to start getting entries! me.ready = true me.emit("ready") me._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 me = this // console.error("\tadd", entry._path, "->", me._path) collect(entry) if (!me.ready || me._currentEntry) { me._buffer.push(entry) return false } // create a new writer, and pipe the incoming entry into it. if (me._ended) { return me.error("add after end") } me._buffer.push(entry) me._process() return 0 === this._buffer.length } DirWriter.prototype._process = function () { var me = this // console.error("DW Process p=%j", me._processing, me.basename) if (me._processing) return var entry = me._buffer.shift() if (!entry) { // console.error("DW Drain") me.emit("drain") if (me._ended) me._finish() return } me._processing = true // console.error("DW Entry", entry._path) me.emit("entry", entry) // ok, add this entry // // don't allow recursive copying var p = entry do { var pp = p._path || p.path if (pp === me.root._path || pp === me._path || (pp && pp.indexOf(me._path) === 0)) { // console.error("DW Exit (recursive)", entry.basename, me._path) me._processing = false if (entry._collected) entry.pipe() return me._process() } } while (p = p.parent) // console.error("DW not recursive") // chop off the entry's root dir, replace with ours var props = { parent: me , root: me.root || me , type: entry.type , depth: me.depth + 1 } var p = entry._path || entry.path || entry.props.path if (entry.parent) { p = p.substr(entry.parent._path.length + 1) } // get rid of any ../../ shenanigans props.path = path.join(me.path, path.join("/", p)) // if i have a filter, the child should inherit it. props.filter = me.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 = me._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) { me.warn(er) child.emit("end") child.emit("close") } else { me.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) me._currentChild = null me._processing = false me._process() } } fstream-0.1.24/lib/file-reader.js000066400000000000000000000074251217364614000165360ustar00rootroot00000000000000// Basically just a wrapper around an fs.ReadStream module.exports = FileReader var fs = require("graceful-fs") , fstream = require("../fstream.js") , Reader = fstream.Reader , inherits = require("inherits") , mkdir = require("mkdirp") , Reader = require("./reader.js") , EOF = {EOF: true} , CLOSE = {CLOSE: true} inherits(FileReader, Reader) function FileReader (props) { // console.error(" FR create", props.path, props.size, new Error().stack) var me = this if (!(me 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) } me._buffer = [] me._bytesEmitted = 0 Reader.call(me, props) } FileReader.prototype._getStream = function () { var me = this , stream = me._stream = fs.createReadStream(me._path, me.props) if (me.props.blksize) { stream.bufferSize = me.props.blksize } stream.on("open", me.emit.bind(me, "open")) stream.on("data", function (c) { // console.error("\t\t%d %s", c.length, me.basename) me._bytesEmitted += c.length // no point saving empty chunks if (!c.length) return else if (me._paused || me._buffer.length) { me._buffer.push(c) me._read() } else me.emit("data", c) }) stream.on("end", function () { if (me._paused || me._buffer.length) { // console.error("FR Buffering End", me._path) me._buffer.push(EOF) me._read() } else { me.emit("end") } if (me._bytesEmitted !== me.props.size) { me.error("Didn't get expected byte count\n"+ "expect: "+me.props.size + "\n" + "actual: "+me._bytesEmitted) } }) stream.on("close", function () { if (me._paused || me._buffer.length) { // console.error("FR Buffering Close", me._path) me._buffer.push(CLOSE) me._read() } else { // console.error("FR close 1", me._path) me.emit("close") } }) me._read() } FileReader.prototype._read = function () { var me = this // console.error("FR _read", me._path) if (me._paused) { // console.error("FR _read paused", me._path) return } if (!me._stream) { // console.error("FR _getStream calling", me._path) return me._getStream() } // clear out the buffer, if there is one. if (me._buffer.length) { // console.error("FR _read has buffer", me._buffer.length, me._path) var buf = me._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", me._path) me.emit("end") } else if (c === CLOSE) { // console.error("FR Read emitting buffered close", me._path) me.emit("close") } else { // console.error("FR Read emitting buffered data", me._path) me.emit("data", c) } if (me._paused) { // console.error("FR Read Re-pausing at "+i, me._path) me._buffer = buf.slice(i) return } } me._buffer.length = 0 } // console.error("FR _read done") // that's about all there is to it. } FileReader.prototype.pause = function (who) { var me = this // console.error("FR Pause", me._path) if (me._paused) return who = who || me me._paused = true if (me._stream) me._stream.pause() me.emit("pause", who) } FileReader.prototype.resume = function (who) { var me = this // console.error("FR Resume", me._path) if (!me._paused) return who = who || me me.emit("resume", who) me._paused = false if (me._stream) me._stream.resume() me._read() } fstream-0.1.24/lib/file-writer.js000066400000000000000000000045341217364614000166060ustar00rootroot00000000000000module.exports = FileWriter var fs = require("graceful-fs") , mkdir = require("mkdirp") , Writer = require("./writer.js") , inherits = require("inherits") , EOF = {} inherits(FileWriter, Writer) function FileWriter (props) { var me = this if (!(me 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) } me._buffer = [] me._bytesWritten = 0 Writer.call(this, props) } FileWriter.prototype._create = function () { var me = this if (me._stream) return var so = {} if (me.props.flags) so.flags = me.props.flags so.mode = Writer.filemode if (me._old && me._old.blksize) so.bufferSize = me._old.blksize me._stream = fs.createWriteStream(me._path, so) me._stream.on("open", function (fd) { // console.error("FW open", me._buffer, me._path) me.ready = true me._buffer.forEach(function (c) { if (c === EOF) me._stream.end() else me._stream.write(c) }) me.emit("ready") // give this a kick just in case it needs it. me.emit("drain") }) me._stream.on("drain", function () { me.emit("drain") }) me._stream.on("close", function () { // console.error("\n\nFW Stream Close", me._path, me.size) me._finish() }) } FileWriter.prototype.write = function (c) { var me = this me._bytesWritten += c.length if (!me.ready) { if (!Buffer.isBuffer(c) && typeof c !== 'string') throw new Error('invalid write data') me._buffer.push(c) return false } var ret = me._stream.write(c) // console.error("\t-- fw wrote, _stream says", ret, me._stream._queue.length) // allow 2 buffered writes, because otherwise there's just too // much stop and go bs. return ret || (me._stream._queue && me._stream._queue.length <= 2) } FileWriter.prototype.end = function (c) { var me = this if (c) me.write(c) if (!me.ready) { me._buffer.push(EOF) return false } return me._stream.end() } FileWriter.prototype._finish = function () { var me = this if (typeof me.size === "number" && me._bytesWritten != me.size) { me.error( "Did not get expected byte count.\n" + "expect: " + me.size + "\n" + "actual: " + me._bytesWritten) } Writer.prototype._finish.call(me) } fstream-0.1.24/lib/get-type.js000066400000000000000000000011701217364614000161040ustar00rootroot00000000000000module.exports = getType function getType (st) { var types = [ "Directory" , "File" , "SymbolicLink" , "Link" // special for hardlinks from tarballs , "BlockDevice" , "CharacterDevice" , "FIFO" , "Socket" ] , type if (st.type && -1 !== types.indexOf(st.type)) { 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-0.1.24/lib/link-reader.js000066400000000000000000000030061217364614000165430ustar00rootroot00000000000000// 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") , fstream = require("../fstream.js") , inherits = require("inherits") , mkdir = require("mkdirp") , Reader = require("./reader.js") inherits(LinkReader, Reader) function LinkReader (props) { var me = this if (!(me 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(me, 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 me = this fs.readlink(me._path, function (er, linkpath) { if (er) return me.error(er) me.linkpath = me.props.linkpath = linkpath me.emit("linkpath", linkpath) Reader.prototype._stat.call(me, currentStat) }) } LinkReader.prototype._read = function () { var me = this if (me._paused) return // basically just a no-op, since we got all the info we need // from the _stat method if (!me._ended) { me.emit("end") me.emit("close") me._ended = true } } fstream-0.1.24/lib/link-writer.js000066400000000000000000000053441217364614000166240ustar00rootroot00000000000000 module.exports = LinkWriter var fs = require("graceful-fs") , Writer = require("./writer.js") , inherits = require("inherits") , path = require("path") , rimraf = require("rimraf") inherits(LinkWriter, Writer) function LinkWriter (props) { var me = this if (!(me 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) { me.error("Need linkpath property to create " + props.type) } Writer.call(this, props) } LinkWriter.prototype._create = function () { // console.error(" LW _create") var me = this , hard = me.type === "Link" || process.platform === "win32" , link = hard ? "link" : "symlink" , lp = hard ? path.resolve(me.dirname, me.linkpath) : me.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(me, lp, link) fs.readlink(me._path, function (er, p) { // only skip creation if it's exactly the same link if (p && p === lp) return finish(me) clobber(me, lp, link) }) } function clobber (me, lp, link) { rimraf(me._path, function (er) { if (er) return me.error(er) create(me, lp, link) }) } function create (me, lp, link) { fs[link](lp, me._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") { me.ready = true me.emit("ready") me.emit("end") me.emit("close") me.end = me._finish = function () {} } else return me.error(er) } finish(me) }) } function finish (me) { me.ready = true me.emit("ready") if (me._ended && !me._finished) me._finish() } LinkWriter.prototype.end = function () { // console.error("LW finish in end") this._ended = true if (this.ready) { this._finished = true this._finish() } } fstream-0.1.24/lib/proxy-reader.js000066400000000000000000000036561217364614000170020ustar00rootroot00000000000000// A reader for when we don't yet know what kind of thing // the thing is. module.exports = ProxyReader var Reader = require("./reader.js") , getType = require("./get-type.js") , inherits = require("inherits") , fs = require("graceful-fs") inherits(ProxyReader, Reader) function ProxyReader (props) { var me = this if (!(me instanceof ProxyReader)) throw new Error( "ProxyReader must be called as constructor.") me.props = props me._buffer = [] me.ready = false Reader.call(me, props) } ProxyReader.prototype._stat = function () { var me = this , props = me.props // stat the thing to see what the proxy should be. , 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 = me.type = type me._old = current me._addProxy(Reader(props, current)) }) } ProxyReader.prototype._addProxy = function (proxy) { var me = this if (me._proxyTarget) { return me.error("proxy already set") } me._proxyTarget = proxy proxy._proxy = me ; [ "error" , "data" , "end" , "close" , "linkpath" , "entry" , "entryEnd" , "child" , "childEnd" , "warn" , "stat" ].forEach(function (ev) { // console.error("~~ proxy event", ev, me.path) proxy.on(ev, me.emit.bind(me, ev)) }) me.emit("proxy", proxy) proxy.on("ready", function () { // console.error("~~ proxy is ready!", me.path) me.ready = true me.emit("ready") }) var calls = me._buffer me._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-0.1.24/lib/proxy-writer.js000066400000000000000000000045511217364614000170470ustar00rootroot00000000000000// 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") , getType = require("./get-type.js") , inherits = require("inherits") , collect = require("./collect.js") , fs = require("fs") inherits(ProxyWriter, Writer) function ProxyWriter (props) { var me = this if (!(me instanceof ProxyWriter)) throw new Error( "ProxyWriter must be called as constructor.") me.props = props me._needDrain = false Writer.call(me, props) } ProxyWriter.prototype._stat = function () { var me = this , props = me.props // stat the thing to see what the proxy should be. , 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 = me.type = type me._old = current me._addProxy(Writer(props, current)) }) } ProxyWriter.prototype._addProxy = function (proxy) { // console.error("~~ set proxy", this.path) var me = this if (me._proxy) { return me.error("proxy already set") } me._proxy = proxy ; [ "ready" , "error" , "close" , "pipe" , "drain" , "warn" ].forEach(function (ev) { proxy.on(ev, me.emit.bind(me, ev)) }) me.emit("proxy", proxy) var calls = me._buffer calls.forEach(function (c) { // console.error("~~ ~~ proxy buffered call", c[0], c[1]) proxy[c[0]].apply(proxy, c[1]) }) me._buffer.length = 0 if (me._needsDrain) me.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-0.1.24/lib/reader.js000066400000000000000000000154411217364614000156160ustar00rootroot00000000000000 module.exports = Reader var fs = require("graceful-fs") , Stream = require("stream").Stream , inherits = require("inherits") , path = require("path") , getType = require("./get-type.js") , hardLinks = Reader.hardLinks = {} , Abstract = require("./abstract.js") // Must do this *before* loading the child classes inherits(Reader, Abstract) var DirReader = require("./dir-reader.js") , FileReader = require("./file-reader.js") , LinkReader = require("./link-reader.js") , SocketReader = require("./socket-reader.js") , ProxyReader = require("./proxy-reader.js") function Reader (props, currentStat) { var me = this if (!(me instanceof Reader)) return new Reader(props, currentStat) if (typeof props === "string") { props = { path: props } } if (!props.path) { me.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 , 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 = DirReader 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 = FileReader break case "SymbolicLink": ClassType = LinkReader break case "Socket": ClassType = SocketReader break case null: ClassType = ProxyReader break } if (!(me instanceof ClassType)) { return new ClassType(props) } Abstract.call(me) me.readable = true me.writable = false me.type = type me.props = props me.depth = props.depth = props.depth || 0 me.parent = props.parent || null me.root = props.root || (props.parent && props.parent.root) || me me._path = me.path = path.resolve(props.path) if (process.platform === "win32") { me.path = me._path = me.path.replace(/\?/g, "_") if (me._path.length >= 260) { // how DOES one create files on the moon? // if the path has spaces in it, then UNC will fail. me._swallowErrors = true //if (me._path.indexOf(" ") === -1) { me._path = "\\\\?\\" + me.path.replace(/\//g, "\\") //} } } me.basename = props.basename = path.basename(me.path) me.dirname = props.dirname = path.dirname(me.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) me.size = props.size me.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 me._read() // to start reading whatever it is. // console.error("calling stat", props.path, currentStat) me._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 me = this , props = me.props , stat = props.follow ? "stat" : "lstat" // console.error("Reader._stat", me._path, currentStat) if (currentStat) process.nextTick(statCb.bind(null, null, currentStat)) else fs[stat](me._path, statCb) function statCb (er, props_) { // console.error("Reader._stat, statCb", me._path, props_, props_.nlink) if (er) return me.error(er) Object.keys(props_).forEach(function (k) { props[k] = props_[k] }) // if it's not the expected size, then abort here. if (undefined !== me.size && props.size !== me.size) { return me.error("incorrect size") } me.size = props.size var type = getType(props) // special little thing for handling hardlinks. if (type !== "Directory" && props.nlink && props.nlink > 1) { var k = props.dev + ":" + props.ino // console.error("Reader has nlink", me._path, k) if (hardLinks[k] === me._path || !hardLinks[k]) hardLinks[k] = me._path else { // switch into hardlink mode. type = me.type = me.props.type = "Link" me.Link = me.props.Link = true me.linkpath = me.props.linkpath = hardLinks[k] // console.error("Hardlink detected, switching mode", me._path, me.linkpath) // Setting __proto__ would arguably be the "correct" // approach here, but that just seems too wrong. me._stat = me._read = LinkReader.prototype._read } } if (me.type && me.type !== type) { me.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 (me.filter) { var who = me._proxy || me // special handling for ProxyReaders if (!me.filter.call(who, who, props)) { if (!me._disowned) { me.abort() me.emit("end") me.emit("close") } return } } // last chance to abort or disown before the flow starts! var events = ["_stat", "stat", "ready"] var e = 0 ;(function go () { if (me._aborted) { me.emit("end") me.emit("close") return } if (me._paused) { me.once("resume", go) return } var ev = events[e ++] if (!ev) return me._read() me.emit(ev, props) go() })() } } Reader.prototype.pipe = function (dest, opts) { var me = this if (typeof dest.add === "function") { // piping to a multi-compatible, and we've got directory entries. me.on("entry", function (entry) { var ret = dest.add(entry) if (false === ret) { me.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-0.1.24/lib/socket-reader.js000066400000000000000000000017401217364614000171010ustar00rootroot00000000000000// 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 fs = require("graceful-fs") , fstream = require("../fstream.js") , inherits = require("inherits") , mkdir = require("mkdirp") , Reader = require("./reader.js") inherits(SocketReader, Reader) function SocketReader (props) { var me = this if (!(me 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(me, props) } SocketReader.prototype._read = function () { var me = this if (me._paused) return // basically just a no-op, since we got all the info we have // from the _stat method if (!me._ended) { me.emit("end") me.emit("close") me._ended = true } } fstream-0.1.24/lib/writer.js000066400000000000000000000246621217364614000156750ustar00rootroot00000000000000 module.exports = Writer var fs = require("graceful-fs") , inherits = require("inherits") , rimraf = require("rimraf") , mkdir = require("mkdirp") , path = require("path") , umask = process.platform === "win32" ? 0 : process.umask() , getType = require("./get-type.js") , Abstract = require("./abstract.js") // Must do this *before* loading the child classes inherits(Writer, Abstract) Writer.dirmode = 0777 & (~umask) Writer.filemode = 0666 & (~umask) var DirWriter = require("./dir-writer.js") , LinkWriter = require("./link-writer.js") , FileWriter = require("./file-writer.js") , 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 me = this if (typeof props === "string") { props = { path: props } } if (!props.path) me.error("Must provide a path", null, true) // polymorphism. // call fstream.Writer(dir) to get a DirWriter object, etc. var type = getType(props) , ClassType = Writer switch (type) { case "Directory": ClassType = DirWriter break case "File": ClassType = FileWriter break case "Link": case "SymbolicLink": ClassType = LinkWriter break case null: // Don't know yet what type to create, so we wrap in a proxy. ClassType = ProxyWriter break } if (!(me instanceof ClassType)) return new ClassType(props) // now get down to business. Abstract.call(me) // props is what we want to set. // set some convenience properties as well. me.type = props.type me.props = props me.depth = props.depth || 0 me.clobber = false === props.clobber ? props.clobber : true me.parent = props.parent || null me.root = props.root || (props.parent && props.parent.root) || me me._path = me.path = path.resolve(props.path) if (process.platform === "win32") { me.path = me._path = me.path.replace(/\?/g, "_") if (me._path.length >= 260) { me._swallowErrors = true me._path = "\\\\?\\" + me.path.replace(/\//g, "\\") } } me.basename = path.basename(props.path) me.dirname = path.dirname(props.path) me.linkpath = props.linkpath || null props.parent = props.root = null // console.error("\n\n\n%s setting size to", props.path, props.size) me.size = props.size if (typeof props.mode === "string") { props.mode = parseInt(props.mode, 8) } me.readable = false me.writable = true // buffer until ready, or while handling another entry me._buffer = [] me.ready = false me.filter = typeof props.filter === "function" ? props.filter: null // start the ball rolling. // this checks what's there already, and then calls // me._create() to call the impl-specific creation stuff. me._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 me = this fs[me.props.follow ? "stat" : "lstat"](me._path, function (er, current) { if (er) { return me.warn("Cannot create " + me._path + "\n" + "Unsupported type: "+me.type, "ENOTSUP") } me._finish() }) } Writer.prototype._stat = function (current) { var me = this , props = me.props , stat = props.follow ? "stat" : "lstat" , who = me._proxy || me if (current) statCb(null, current) else fs[stat](me._path, statCb) function statCb (er, current) { if (me.filter && !me.filter.call(who, who, current)) { me._aborted = true me.emit("end") me.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(me) } me._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 !== me.type) { return rimraf(me._path, function (er) { if (er) return me.error(er) me._old = null create(me) }) } // otherwise, just handle in the app-specific way // this creates a fs.WriteStream, or mkdir's, or whatever create(me) } } function create (me) { // console.error("W create", me._path, Writer.dirmode) // XXX Need to clobber non-dirs that are in the way, // unless { clobber: false } in the props. mkdir(path.dirname(me._path), Writer.dirmode, function (er, made) { // console.error("W created", path.dirname(me._path), er) if (er) return me.error(er) // later on, we have to set the mode and owner for these me._madeDir = made return me._create() }) } function endChmod (me, want, current, path, cb) { var wantMode = want.mode , chmod = want.follow || me.type !== "SymbolicLink" ? "chmod" : "lchmod" if (!fs[chmod]) return cb() if (typeof wantMode !== "number") return cb() var curMode = current.mode & 0777 wantMode = wantMode & 0777 if (wantMode === curMode) return cb() fs[chmod](path, wantMode, cb) } function endChown (me, 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 = (me.props.follow || me.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 (me, want, current, path, cb) { if (!fs.utimes || process.platform === "win32") return cb() var utimes = (want.follow || me.type !== "SymbolicLink") ? "utimes" : "lutimes" if (utimes === "lutimes" && !fs[utimes]) { utimes = "utimes" } if (!fs[utimes]) return cb() var curA = current.atime , curM = current.mtime , meA = want.atime , 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 me = this // console.error(" W Finish", me._path, me.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 (me._old) { // the times will almost *certainly* have changed. // adds the utimes syscall, but remove another stat. me._old.atime = new Date(0) me._old.mtime = new Date(0) // console.error(" W Finish Stale Stat", me._path, me.size) setProps(me._old) } else { var stat = me.props.follow ? "stat" : "lstat" // console.error(" W Finish Stating", me._path, me.size) fs[stat](me._path, function (er, current) { // console.error(" W Finish Stated", me._path, me.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" && (me.type === "Link" || me.type === "SymbolicLink") && process.platform === "win32") { me.ready = true me.emit("ready") me.emit("end") me.emit("close") me.end = me._finish = function () {} return } else return me.error(er) } setProps(me._old = current) }) } return function setProps (current) { todo += 3 endChmod(me, me.props, current, me._path, next("chmod")) endChown(me, me.props, current, me._path, next("chown")) endUtimes(me, me.props, current, me._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 me.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 (!me._madeDir) return end() else endMadeDir(me, me._path, end) function end (er) { if (er) { er.fstream_finish_call = "setupMadeDir" return me.error(er) } // all the props have been set, so we're completely done. me.emit("end") me.emit("close") } } } } function endMadeDir (me, p, cb) { var made = me._madeDir // everything *between* made and path.dirname(me._path) // needs to be set up. Note that this may just be one dir. var d = path.dirname(p) endMadeDir_(me, d, function (er) { if (er) return cb(er) if (d === made) { return cb() } endMadeDir(me, d, cb) }) } function endMadeDir_ (me, p, cb) { var dirProps = {} Object.keys(me.props).forEach(function (k) { dirProps[k] = me.props[k] // only make non-readable dirs if explicitly requested. if (k === "mode" && me.type !== "Directory") { dirProps[k] = dirProps[k] | 0111 } }) var todo = 3 , errState = null fs.stat(p, function (er, current) { if (er) return cb(errState = er) endChmod(me, dirProps, current, p, next) endChown(me, dirProps, current, p, next) endUtimes(me, 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("Cannot 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-0.1.24/package.json000066400000000000000000000010551217364614000155320ustar00rootroot00000000000000{ "author": "Isaac Z. Schlueter (http://blog.izs.me/)", "name": "fstream", "description": "Advanced file system stream things", "version": "0.1.24", "repository": { "type": "git", "url": "git://github.com/isaacs/fstream.git" }, "main": "fstream.js", "engines": { "node": ">=0.6" }, "dependencies": { "rimraf": "2", "mkdirp": "0.3", "graceful-fs": "~2.0.0", "inherits": "~2.0.0" }, "devDependencies": { "tap": "" }, "scripts": { "test": "tap examples/*.js" }, "license": "BSD" }