123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 |
- // 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()
- }
- }
|