You're Invited:Meet the Socket Team at BlackHat and DEF CON in Las Vegas, Aug 7-8.RSVP
Socket
Socket
Sign inDemoInstall

fstream

Package Overview
Dependencies
Maintainers
1
Versions
46
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.0.0 to 0.0.1

examples/deep-copy/examples/symlink-write.js

81

examples/deep-copy/examples/pipe.js

@@ -16,14 +16,81 @@ var fstream = require("../fstream.js")

r.on("entry", function (entry) {
console.error("a %s appears!", entry.type, entry.path)
})
var indent = ""
var escape = {}
w.on("entry", function (entry) {
console.error("%s attacks!", entry.type, entry.path)
})
r.on("entry", appears)
//r.on("ready", function () {
// appears(r)
//})
function appears (entry) {
console.error(indent + "a %s appears!", entry.type, entry.basename)
indent += "\t"
entry.on("data", missile(entry))
entry.on("end", runaway(entry))
entry.on("entry", appears)
}
var foggy
function missile (entry) { return function (c) {
if (entry.type === "Directory") {
if (!foggy && Math.random() < 0.3) {
console.error(indent +"%s casts a spell", entry.basename)
console.error("\na fog comes over the battlefield...\n\033[32m")
r.pause()
r.__resume = r.resume
r.resume = r.pause
w.once("drain", function f () {
if (foggy) {
console.error("\ntrying to lift the fog!\n")
r.pause()
w.once("drain", f)
}
})
foggy = setTimeout(function () {
console.error("\n\033[mthe fog lifts!\n")
setTimeout(function () {
foggy = null
r.resume = r.__resume
r.resume()
}, 300)
}, 1000)
}
return
}
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 () {
console.error("IT'S OVER!!")
if (foggy) clearTimeout(foggy)
console.error("\033[mIT'S OVER!!")
console.error("A WINNAR IS YOU!")
ended = true
})
process.on("exit", function () {
console.error("ended? "+ended)
})
r.pipe(w)

@@ -10,8 +10,17 @@ var fstream = require("../fstream.js")

console.error(r instanceof fstream.Reader)
console.error(r instanceof require("stream").Stream)
console.error(r instanceof require("events").EventEmitter)
console.error(r.on)
r.on("stat", function () {
console.error("a %s !!!\t", r.type, r.path)
})
r.on("entries", function (entries) {
console.error("the entries", entries)
console.error("\t" + entries.join("\n\t"))
})
r.on("entry", function (entry) {
console.error("a %s appears!", entry.type, entry.path)
console.error("a %s !!!\t", entry.type, entry.path)
})

@@ -18,0 +27,0 @@

537

examples/deep-copy/fstream.js

@@ -0,520 +1,29 @@

exports.Abstract = require("./lib/abstract.js")
exports.Reader = require("./lib/reader.js")
exports.Writer = require("./lib/writer.js")
exports.Reader = Reader
exports.Writer = Writer
exports.File =
{ Reader: require("./lib/file-reader.js")
, Writer: require("./lib/file-writer.js") }
var fs = require("graceful-fs")
, Stream = require("stream").Stream
, inherits = require("inherits")
, rimraf = require("rimraf")
, mkdir = require("mkdirp")
, path = require("path")
, umask = process.umask()
, dirmode = 0777 & (~umask)
, filemode = 0644 & (~umask)
exports.Dir =
{ Reader : require("./lib/dir-reader.js")
, Writer : require("./lib/dir-writer.js") }
function Reader (opts) {
if (typeof opts === "string") opts = { path: opts }
exports.Link =
{ Reader : require("./lib/link-reader.js")
, Writer : require("./lib/link-writer.js") }
var me = this
if (!(me instanceof Reader)) return new Reader(opts)
Stream.call(me)
me.readable = true
me.writable = false
exports.Proxy =
{ Reader : require("./lib/proxy-reader.js")
, Writer : require("./lib/proxy-writer.js") }
if (!opts.path) {
return me.emit("error", new Error("path option is required"))
}
exports.Reader.Dir = exports.Dir.Reader
exports.Reader.File = exports.File.Reader
exports.Reader.Link = exports.Link.Reader
exports.Reader.Proxy = exports.Proxy.Reader
var stat = opts.follow ? "stat" : "lstat"
me.filter = typeof opts.filter === "function" ? opts.filter : null
me.depth = opts.depth || 0
me.parent = opts.parent || null
me.root = opts.root || (opts.parent && opts.parent.root) || me
me.path = opts.path
me.basename = path.basename(opts.path)
me.dirname = path.dirname(opts.path)
fs[stat](me.path, function (er, props) {
if (er) return me.emit("error", er)
me.props = props
me.type = getType(props)
// 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 ret = me.filter()
if (!ret) return me.emit("end")
}
me.emit("stat", props)
// if it's a directory, then we'll be emitting "file" events.
if (props.isDirectory()) {
dirWalk(me)
return
} else if (props.isSymbolicLink()) {
readlink(me)
return
}
// TODO: special handling for character devices, block devices,
// FIFO's, sockets. They stat as size=0, but emit all sorts of data.
// if it's got a zero-size, then just emit "end" right now
if (props.size === 0) {
me.emit("end")
return
}
// otherwise, set up a readable stream for it.
// TODO: Check that the bytesEmitted at the end matches
// the expected size.
var s = me._stream = fs.createReadStream(me.path, opts)
proxyEvents(["open", "error", "data", "end", "close"], s, me)
})
}
inherits(Reader, Stream)
function readlink (me) {
fs.readlink(me.path, function (er, lp) {
if (er) return me.emit("error", er)
me.emit("linkpath", lp)
me.emit("end")
})
}
function dirWalk (me) {
fs.readdir(me.path, function (er, entries) {
if (er) return me.emit("error", er)
me.emit("entries", entries)
// now, go through all the entries and create Readers for them
// Use the same filter, if one was provided, so that we don't
// dive into directories that are excluded.
var len = entries.length
, f = 0
;(function walk () {
if (f === len) return me.emit("end")
var fst = Reader({ path: path.resolve(me.path, entries[f])
, filter: me.filter
, depth: me.depth + 1
, root: me.root
, parent: me })
fst.on("error", function (e) {
me.emit("error", e)
})
fst.on("stat", function (p) {
me.emit("entry", fst)
})
// bubble up
fst.on("entry", function (entry) {
me.emit("entry", entry)
})
fst.on("end", function () {
f ++
walk()
})
})()
})
}
function proxyEvents (evs, from, to) {
evs.forEach(function (ev) {
from.on(ev, function () {
var l = arguments.length
, args = new Array(l + 1)
args[0] = ev
for (var i = 0, l = arguments.length; i < l; i ++) {
args[i + 1] = arguments[i]
}
to.emit.apply(to, args)
})
})
}
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) {
dest.add(entry)
})
}
Stream.prototype.pipe.apply(this, arguments)
}
Reader.prototype.pause = function () {
if (this._stream) this._stream.pause()
}
Reader.prototype.resume = function () {
if (this._stream) this._stream.resume()
}
function Writer (props) {
var me = this
if (!(me instanceof Writer)) return new Writer(props)
Stream.call(me)
if (typeof props === "string") {
props = { path: props }
}
if (!props.path) {
me.emit("error", new Error("Must provide a path"))
return
}
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 = props.path
me.basename = path.basename(props.path)
me.dirname = path.dirname(props.path)
me.linkpath = props.linkpath || null
me.readable = false
me.writable = true
// buffer until stat
me._buffer = []
me._ready = false
var stat = props.follow ? "stat" : "lstat"
fs[stat](props.path, function (er, current) {
// if it's not there, great. We'll just create it.
// if it is there, then we'll need to change whatever differs
// if we can't modify what needs modifying, then error on it.
// try to figure out what kind of type it is.
// We can actually only create directories, files, and symlinks,
// so if it's a block/character device or fifo, then abort.
// However, if it's a fifo or device or socket, and we're not
// *changing* it, then that's fine, since we might just be writing
// data into it.
//
// If it wants to be a directory or file or symlink, and is currently
// anything else, then clobber it.
var changeType = true
, currentType = current ? getType(current) : null
, wantedType = me.type = getType(props) || currentType || "File"
, creatable = wantedType === "Directory" ||
wantedType === "File" ||
wantedType === "SymbolicLink"
, recreate = wantedType !== currentType
Object.keys(current || {}).forEach(function (k) {
if (!props.hasOwnProperty(k)) props[k] = current[k]
})
me._old = current
if (!recreate) {
if (wantedType == "File") create(me)
else setProps(me, current)
return
}
if (wantedType === "SymbolicLink" && !me.linkpath) {
return me.emit("error", new Error(
"Cannot create symlink without linkpath"))
}
if (!creatable) {
return me.emit("error", new Error(
"Cannot create filetype: " + wantedType))
}
clobber(me)
})
}
inherits(Writer, Stream)
function clobber (me) {
rimraf(me.path, function (er) {
if (er) return me.emit("error", er)
create(me)
})
}
function create (me) {
if (typeof me.props.mode !== "number") {
me.props.mode = (me.type === "Directory" ? 0777 : 0666) & (~umask)
}
mkdir(me.dirname, dirmode, function (er) {
if (er) return me.emit("error", me)
switch (me.type) {
case "Directory":
mkdir(me.path, me.props.mode & 0777, next)
break
case "SymbolicLink":
fs.symlink(me.path, me.linkpath, next)
break
case "File":
var s = me._stream = fs.createWriteStream(me.path, me.props)
proxyEvents(["open", "error", "drain", "close"], s, me)
me._stream.on("open", function (fd) {
next()
})
break
default:
return me.emit("error", new Error("Cannot create type: "+me.type))
}
})
function next (er) {
if (er) return me.emit("error", er)
fs[me.follow ? "stat" : "lstat"](me.path, function (er, stat) {
if (er) return me.emit("error", er)
me._old = stat
setProps(me, stat)
})
}
}
function setProps (me, current) {
// set up all the things.
var todo = 0
var errState = null
var done = false
// mode
var wantMode = me.props.mode
if (typeof wantMode === "number") {
wantMode = wantMode & 0777
var curMode = current.mode & 0777
if (wantMode !== curMode) {
todo ++
if (me._stream && me._stream.fd && fs.fchmod) {
fs.fchmod(me._stream.fd, wantMode, next)
} else {
fs.chmod(me.path, wantMode, next)
}
}
}
// uid, gid
if (typeof me.props.uid === "number" || typeof me.props.gid === "number") {
if (typeof me.props.uid !== "number") me.props.uid = current.uid
if (typeof me.props.gid !== "number") me.props.gid = current.gid
if (me.props.uid !== current.uid || me.props.gid !== current.gid) {
todo ++
if (me._stream && me._stream.fd && fs.fchown) {
fs.fchown(me._stream.fd, me.props.uid, me.props.gid, next)
} else {
fs.chown(me.path, me.props.uid, me.props.gid, next)
}
}
}
// atime, mtime.
if (fs.utimes) {
var curA = current.atime
, curM = current.mtime
, meA = me.props.atime
, meM = me.props.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()) {
if (me._stream) {
// can't set them now, since we'll be touching the file.
me._stream.on("end", setTimes)
me._stream.on("close", setTimes)
} else {
todo ++
setTimes()
}
}
function setTimes () {
fs.utimes(me.path, meA, meM, next)
}
}
// finally, handle the case if there was nothing to do.
if (todo === 0) next()
function next (er) {
if (errState) return
if (er) return me.emit("error", errState = er)
if (--todo > 0) return
if (done) return
done = true
// all the props have been set, now see if we got any pending writes
// in the meantime.
me._ready = true
var buffer = me._buffer
if (buffer.length) {
me._buffer = []
buffer.forEach(function (c) {
me[c[0]](c[1])
})
}
}
}
function objectToString (d) {
return Object.prototype.toString.call(d)
}
function isDate(d) {
return typeof d === 'object' && objectToString(d) === '[object Date]';
}
Writer.prototype.pipe = function () {
this.emit("error", new Error("Can't pipe from writable stream"))
}
Writer.prototype.write = function (c) {
var me = this
if (me._ended) {
me.emit("error", new Error("write after end"))
return false
}
if (!me._ready || me._buffer.length) {
me._buffer.push(["write", c])
me._needsDrain = true
return false
}
if (me.type !== "File") {
// just ignore writes to non-File types.
// some tar entries contain metadata for directories
return true
}
return me._stream.write(c)
}
// create a subfolder underneath this one
Writer.prototype.add = function (entry) {
var me = this
if (me._ended) {
me.emit("error", new Error("add after end"))
return false
}
if (!me._ready || me._buffer.length) {
me._buffer.push(["add", entry])
me._needsDrain = true
return false
}
if (me._ended) {
me.emit("error", new Error("write after end"))
return false
}
if (me.type !== "Directory") {
// can't add sub-entries to a non-dir type
me.emit("error", new Error("can't add to non-Directory type"))
return
}
// don't allow recursive copying!
var p = entry
do {
if (p.path === me.path) return
} while (p = p.parent)
// chop the entry's parent's dir
var opts = { parent: me
, root: me.root || me
, type: entry.type
, depth: me.depth + 1 }
var p = entry.path
, root = entry.root || entry.parent
if (root) {
p = p.substr(root.path.length + 1)
}
opts.path = path.join(me.path, p)
// all the rest of the stuff, copy over from the source.
Object.keys(entry.props).forEach(function (k) {
if (!opts.hasOwnProperty(k)) {
opts[k] = entry.props[k]
}
})
var child = new Writer(opts)
// directories are already handled.
if (entry.type !== "Directory") {
entry.pipe(child)
}
child.on("entry", function (entry) {
me.emit("entry", entry)
})
child.on("stat", function () {
me.emit("entry", child)
})
}
Writer.prototype.end = function (c) {
var me = this
if (c) me.write(c)
if (me._stream) me._stream.end()
me._ended = true
}
function getType (st) {
var types =
[ "Directory"
, "File"
, "SymbolicLink"
, "BlockDevice"
, "CharacterDevice"
, "FIFO"
, "Socket" ]
, type
if (st.type && -1 !== types.indexOf(st.type)) {
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
}
exports.Writer.Dir = exports.Dir.Writer
exports.Writer.File = exports.File.Writer
exports.Writer.Link = exports.Link.Writer
exports.Writer.Proxy = exports.Proxy.Writer
{
"author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
"name": "fstream",
"description": "Advanced FS Streams",
"description": "Advanced file system stream things",
"version": "0.0.0",

@@ -6,0 +6,0 @@ "repository": {

@@ -1,11 +0,16 @@

Like FS streams, but with stats on them, and supporting every kind of
filesystem object.
Like 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`.
`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 `filestream.ReadStream('/some/symlink')` then you'll get an object
that stats and then ends immediately (since it has no data).
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 })`.

@@ -15,3 +20,3 @@ There are various checks to make sure that the bytes emitted are the

## Example
## Examples

@@ -23,3 +28,2 @@ ```javascript

, size: 6
, flags: 'w'
})

@@ -36,6 +40,19 @@ .write("hello\n")

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"
, isSymbolicLink: true
, mode: '0755'
, SymbolicLink: true
, mode: "0755" // octal strings supported
})

@@ -47,6 +64,9 @@ .end()

true, then it'll treat it as a symlink. If it's not a function, then
any truish value will make a symlink.
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
// NOT YET IMPLEMENTED
fstream

@@ -53,0 +73,0 @@ .Reader("path/to/dir")

@@ -16,14 +16,81 @@ var fstream = require("../fstream.js")

r.on("entry", function (entry) {
console.error("a %s appears!", entry.type, entry.path)
})
var indent = ""
var escape = {}
w.on("entry", function (entry) {
console.error("%s attacks!", entry.type, entry.path)
})
r.on("entry", appears)
//r.on("ready", function () {
// appears(r)
//})
function appears (entry) {
console.error(indent + "a %s appears!", entry.type, entry.basename)
indent += "\t"
entry.on("data", missile(entry))
entry.on("end", runaway(entry))
entry.on("entry", appears)
}
var foggy
function missile (entry) { return function (c) {
if (entry.type === "Directory") {
if (!foggy && Math.random() < 0.3) {
console.error(indent +"%s casts a spell", entry.basename)
console.error("\na fog comes over the battlefield...\n\033[32m")
r.pause()
r.__resume = r.resume
r.resume = r.pause
w.once("drain", function f () {
if (foggy) {
console.error("\ntrying to lift the fog!\n")
r.pause()
w.once("drain", f)
}
})
foggy = setTimeout(function () {
console.error("\n\033[mthe fog lifts!\n")
setTimeout(function () {
foggy = null
r.resume = r.__resume
r.resume()
}, 300)
}, 1000)
}
return
}
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 () {
console.error("IT'S OVER!!")
if (foggy) clearTimeout(foggy)
console.error("\033[mIT'S OVER!!")
console.error("A WINNAR IS YOU!")
ended = true
})
process.on("exit", function () {
console.error("ended? "+ended)
})
r.pipe(w)

@@ -10,8 +10,17 @@ var fstream = require("../fstream.js")

console.error(r instanceof fstream.Reader)
console.error(r instanceof require("stream").Stream)
console.error(r instanceof require("events").EventEmitter)
console.error(r.on)
r.on("stat", function () {
console.error("a %s !!!\t", r.type, r.path)
})
r.on("entries", function (entries) {
console.error("the entries", entries)
console.error("\t" + entries.join("\n\t"))
})
r.on("entry", function (entry) {
console.error("a %s appears!", entry.type, entry.path)
console.error("a %s !!!\t", entry.type, entry.path)
})

@@ -18,0 +27,0 @@

@@ -0,540 +1,29 @@

exports.Abstract = require("./lib/abstract.js")
exports.Reader = require("./lib/reader.js")
exports.Writer = require("./lib/writer.js")
exports.Reader = Reader
exports.Writer = Writer
exports.File =
{ Reader: require("./lib/file-reader.js")
, Writer: require("./lib/file-writer.js") }
var fs = require("graceful-fs")
, Stream = require("stream").Stream
, inherits = require("inherits")
, rimraf = require("rimraf")
, mkdir = require("mkdirp")
, path = require("path")
, umask = process.umask()
, dirmode = 0777 & (~umask)
, filemode = 0644 & (~umask)
exports.Dir =
{ Reader : require("./lib/dir-reader.js")
, Writer : require("./lib/dir-writer.js") }
function Reader (opts) {
if (typeof opts === "string") opts = { path: opts }
exports.Link =
{ Reader : require("./lib/link-reader.js")
, Writer : require("./lib/link-writer.js") }
var me = this
if (!(me instanceof Reader)) return new Reader(opts)
Stream.call(me)
me.readable = true
me.writable = false
exports.Proxy =
{ Reader : require("./lib/proxy-reader.js")
, Writer : require("./lib/proxy-writer.js") }
if (!opts.path) {
return me.emit("error", new Error("path option is required"))
}
exports.Reader.Dir = exports.Dir.Reader
exports.Reader.File = exports.File.Reader
exports.Reader.Link = exports.Link.Reader
exports.Reader.Proxy = exports.Proxy.Reader
var stat = opts.follow ? "stat" : "lstat"
me.filter = typeof opts.filter === "function" ? opts.filter : null
me.depth = opts.depth || 0
me.parent = opts.parent || null
me.root = opts.root || (opts.parent && opts.parent.root) || me
me.path = opts.path
me.basename = path.basename(opts.path)
me.dirname = path.dirname(opts.path)
fs[stat](me.path, function (er, props) {
if (er) return me.emit("error", er)
me.props = props
me.type = getType(props)
// 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 ret = me.filter()
if (!ret) return me.emit("end")
}
me.emit("stat", props)
// if it's a directory, then we'll be emitting "file" events.
if (props.isDirectory()) {
dirWalk(me)
return
} else if (props.isSymbolicLink()) {
readlink(me)
return
}
// TODO: special handling for character devices, block devices,
// FIFO's, sockets. They stat as size=0, but emit all sorts of data.
// if it's got a zero-size, then just emit "end" right now
if (props.size === 0) {
me.emit("end")
return
}
// otherwise, set up a readable stream for it.
// TODO: Check that the bytesEmitted at the end matches
// the expected size.
var s = me._stream = fs.createReadStream(me.path, opts)
proxyEvents(["open", "error", "data", "end", "close"], s, me)
})
}
inherits(Reader, Stream)
function readlink (me) {
fs.readlink(me.path, function (er, lp) {
if (er) return me.emit("error", er)
me.emit("linkpath", lp)
me.emit("end")
})
}
function dirWalk (me) {
fs.readdir(me.path, function (er, entries) {
if (er) return me.emit("error", er)
me.emit("entries", entries)
// now, go through all the entries and create Readers for them
// Use the same filter, if one was provided, so that we don't
// dive into directories that are excluded.
var len = entries.length
, f = 0
;(function walk () {
if (f === len) return me.emit("end")
var fst = Reader({ path: path.resolve(me.path, entries[f])
, filter: me.filter
, depth: me.depth + 1
, root: me.root
, parent: me })
fst.on("error", function (e) {
me.emit("error", e)
})
fst.on("stat", function (p) {
me.emit("entry", fst)
})
// bubble up
fst.on("entry", function (entry) {
me.emit("entry", entry)
})
fst.on("end", function () {
f ++
walk()
})
})()
})
}
function proxyEvents (evs, from, to) {
evs.forEach(function (ev) {
from.on(ev, function () {
var l = arguments.length
, args = new Array(l + 1)
args[0] = ev
for (var i = 0, l = arguments.length; i < l; i ++) {
args[i + 1] = arguments[i]
}
to.emit.apply(to, args)
})
})
}
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) {
dest.add(entry)
})
}
Stream.prototype.pipe.apply(this, arguments)
}
Reader.prototype.pause = function () {
if (this._stream) this._stream.pause()
}
Reader.prototype.resume = function () {
if (this._stream) this._stream.resume()
}
function Writer (props) {
var me = this
if (!(me instanceof Writer)) return new Writer(props)
Stream.call(me)
if (typeof props === "string") {
props = { path: props }
}
if (!props.path) {
me.emit("error", new Error("Must provide a path"))
return
}
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 = props.path
me.basename = path.basename(props.path)
me.dirname = path.dirname(props.path)
me.linkpath = props.linkpath || null
me.size = props.size
me._bytesWritten = 0
if (typeof props.mode === "string") props.mode = parseInt(props.mode, 8)
me.readable = false
me.writable = true
// buffer until stat
me._buffer = []
me._ready = false
var stat = props.follow ? "stat" : "lstat"
fs[stat](props.path, function (er, current) {
// if it's not there, great. We'll just create it.
// if it is there, then we'll need to change whatever differs
// if we can't modify what needs modifying, then error on it.
// try to figure out what kind of type it is.
// We can actually only create directories, files, and symlinks,
// so if it's a block/character device or fifo, then abort.
// However, if it's a fifo or device or socket, and we're not
// *changing* it, then that's fine, since we might just be writing
// data into it.
//
// If it wants to be a directory or file or symlink, and is currently
// anything else, then clobber it.
var changeType = true
, currentType = current ? getType(current) : null
, wantedType = me.type = getType(props) || currentType || "File"
, creatable = wantedType === "Directory" ||
wantedType === "File" ||
wantedType === "SymbolicLink"
, recreate = wantedType !== currentType
Object.keys(current || {}).forEach(function (k) {
if (!props.hasOwnProperty(k)) props[k] = current[k]
})
me._old = current
if (!recreate) {
if (wantedType == "File") create(me)
else setProps(me, current)
return
}
if (wantedType === "SymbolicLink" && !me.linkpath) {
return me.emit("error", new Error(
"Cannot create symlink without linkpath"))
}
if (!creatable) {
return me.emit("error", new Error(
"Cannot create filetype: " + wantedType))
}
clobber(me)
})
}
inherits(Writer, Stream)
function clobber (me) {
rimraf(me.path, function (er) {
if (er) return me.emit("error", er)
create(me)
})
}
function create (me) {
if (typeof me.props.mode !== "number") {
me.props.mode = me.type === "Directory" ? dirmode : filemode
}
mkdir(me.dirname, dirmode, function (er) {
if (er) return me.emit("error", me)
switch (me.type) {
case "Directory":
mkdir(me.path, me.props.mode & 0777, next)
break
case "SymbolicLink":
fs.symlink(me.linkpath, me.path, next)
break
case "File":
var s = me._stream = fs.createWriteStream(me.path, me.props)
proxyEvents(["open", "error", "drain", "close"], s, me)
me._stream.on("open", function (fd) {
next()
})
if (typeof me.size === "number") {
me._stream.on("close", function () {
if (me._bytesWritten !== me.size) {
me.emit("error", new Error("Wrong byte count\n" +
"Expected: " + me.size + "\n" +
"Actual: " + me._byteCount))
}
})
}
break
default:
return me.emit("error", new Error("Cannot create type: "+me.type))
}
})
function next (er) {
if (er) return me.emit("error", er)
fs[me.follow ? "stat" : "lstat"](me.path, function (er, stat) {
if (er) return me.emit("error", er)
me._old = stat
setProps(me, stat)
})
}
}
function setProps (me, current) {
// set up all the things.
var todo = 0
var errState = null
var done = false
// mode
var wantMode = me.props.mode
if (typeof wantMode === "number") {
wantMode = wantMode & 0777
var curMode = current.mode & 0777
if (wantMode !== curMode) {
todo ++
if (me._stream && me._stream.fd && fs.fchmod) {
fs.fchmod(me._stream.fd, wantMode, next)
} else {
fs.chmod(me.path, wantMode, next)
}
}
}
// uid, gid
if (typeof me.props.uid === "number" || typeof me.props.gid === "number") {
if (typeof me.props.uid !== "number") me.props.uid = current.uid
if (typeof me.props.gid !== "number") me.props.gid = current.gid
if (me.props.uid !== current.uid || me.props.gid !== current.gid) {
todo ++
if (me._stream && me._stream.fd && fs.fchown) {
fs.fchown(me._stream.fd, me.props.uid, me.props.gid, next)
} else {
fs.chown(me.path, me.props.uid, me.props.gid, next)
}
}
}
// atime, mtime.
if (fs.utimes) {
var curA = current.atime
, curM = current.mtime
, meA = me.props.atime
, meM = me.props.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()) {
if (me._stream) {
// can't set them now, since we'll be touching the file.
me._stream.on("end", setTimes)
me._stream.on("close", setTimes)
} else {
todo ++
setTimes()
}
}
function setTimes () {
fs.utimes(me.path, meA, meM, next)
}
}
// finally, handle the case if there was nothing to do.
if (todo === 0) next()
function next (er) {
if (errState) return
if (er) return me.emit("error", errState = er)
if (--todo > 0) return
if (done) return
done = true
// all the props have been set, now see if we got any pending writes
// in the meantime.
me._ready = true
var buffer = me._buffer
if (buffer.length) {
me._buffer = []
buffer.forEach(function (c) {
me[c[0]](c[1])
})
}
}
}
function objectToString (d) {
return Object.prototype.toString.call(d)
}
function isDate(d) {
return typeof d === 'object' && objectToString(d) === '[object Date]';
}
Writer.prototype.pipe = function () {
this.emit("error", new Error("Can't pipe from writable stream"))
}
Writer.prototype.write = function (c) {
var me = this
if (me._ended) {
me.emit("error", new Error("write after end"))
return false
}
if (!me._ready || me._buffer.length) {
me._buffer.push(["write", c])
me._needsDrain = true
return false
}
if (me.type !== "File") {
// just ignore writes to non-File types.
// some tar entries contain metadata for directories
return true
}
if (typeof c === "string") c = new Buffer(c)
if (!Buffer.isBuffer(c)) {
me.emit("error", new Error("can only write strings or buffers"))
return
}
me._bytesWritten += c.length
return me._stream.write(c)
}
// create a subfolder underneath this one
Writer.prototype.add = function (entry) {
var me = this
if (me._ended) {
me.emit("error", new Error("add after end"))
return false
}
if (!me._ready || me._buffer.length) {
me._buffer.push(["add", entry])
me._needsDrain = true
return false
}
if (me._ended) {
me.emit("error", new Error("write after end"))
return false
}
if (me.type !== "Directory") {
// can't add sub-entries to a non-dir type
me.emit("error", new Error("can't add to non-Directory type"))
return
}
// don't allow recursive copying!
var p = entry
do {
if (p.path === me.path) return
} while (p = p.parent)
// chop the entry's parent's dir
var opts = { parent: me
, root: me.root || me
, type: entry.type
, depth: me.depth + 1 }
var p = entry.path
, root = entry.root || entry.parent
if (root) {
p = p.substr(root.path.length + 1)
}
opts.path = path.join(me.path, p)
// all the rest of the stuff, copy over from the source.
Object.keys(entry.props).forEach(function (k) {
if (!opts.hasOwnProperty(k)) {
opts[k] = entry.props[k]
}
})
var child = new Writer(opts)
// directories are already handled.
if (entry.type !== "Directory") {
entry.pipe(child)
}
child.on("entry", function (entry) {
me.emit("entry", entry)
})
child.on("stat", function () {
me.emit("entry", child)
})
}
Writer.prototype.end = function (c) {
var me = this
if (c) me.write(c)
if (me._stream) me._stream.end()
me._ended = true
}
function getType (st) {
var types =
[ "Directory"
, "File"
, "SymbolicLink"
, "BlockDevice"
, "CharacterDevice"
, "FIFO"
, "Socket" ]
, type
if (st.type && -1 !== types.indexOf(st.type)) {
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
}
exports.Writer.Dir = exports.Dir.Writer
exports.Writer.File = exports.File.Writer
exports.Writer.Link = exports.Link.Writer
exports.Writer.Proxy = exports.Proxy.Writer

@@ -5,3 +5,3 @@ {

"description": "Advanced file system stream things",
"version": "0.0.0",
"version": "0.0.1",
"repository": {

@@ -17,3 +17,3 @@ "type": "git",

"rimraf": "~1.0.8",
"mkdirp": "0.0.7",
"mkdirp": "~0.1.0",
"graceful-fs": "~1.0.1",

@@ -20,0 +20,0 @@ "inherits": "~1.0.0"

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc