1 // It is expected that, when .add() returns false, the consumer
2 // of the DirWriter will pause until a "drain" event occurs. Note
3 // that this is *almost always going to be the case*, unless the
4 // thing being written is some sort of unsupported type, and thus
7 module.exports = DirWriter
9 var Writer = require('./writer.js')
10 var inherits = require('inherits')
11 var mkdir = require('mkdirp')
12 var path = require('path')
13 var collect = require('./collect.js')
15 inherits(DirWriter, Writer)
17 function DirWriter (props) {
19 if (!(self instanceof DirWriter)) {
20 self.error('DirWriter must be called as constructor.', null, true)
23 // should already be established as a Directory type
24 if (props.type !== 'Directory' || !props.Directory) {
25 self.error('Non-directory type ' + props.type + ' ' +
26 JSON.stringify(props), null, true)
29 Writer.call(this, props)
32 DirWriter.prototype._create = function () {
34 mkdir(self._path, Writer.dirmode, function (er) {
35 if (er) return self.error(er)
36 // ready to start getting entries!
43 // a DirWriter has an add(entry) method, but its .write() doesn't
44 // do anything. Why a no-op rather than a throw? Because this
45 // leaves open the door for writing directory metadata for
46 // gnu/solaris style dumpdirs.
47 DirWriter.prototype.write = function () {
51 DirWriter.prototype.end = function () {
56 DirWriter.prototype.add = function (entry) {
59 // console.error('\tadd', entry._path, '->', self._path)
61 if (!self.ready || self._currentEntry) {
62 self._buffer.push(entry)
66 // create a new writer, and pipe the incoming entry into it.
68 return self.error('add after end')
71 self._buffer.push(entry)
74 return this._buffer.length === 0
77 DirWriter.prototype._process = function () {
80 // console.error('DW Process p=%j', self._processing, self.basename)
82 if (self._processing) return
84 var entry = self._buffer.shift()
86 // console.error("DW Drain")
88 if (self._ended) self._finish()
92 self._processing = true
93 // console.error("DW Entry", entry._path)
95 self.emit('entry', entry)
99 // don't allow recursive copying
103 pp = p._path || p.path
104 if (pp === self.root._path || pp === self._path ||
105 (pp && pp.indexOf(self._path) === 0)) {
106 // console.error('DW Exit (recursive)', entry.basename, self._path)
107 self._processing = false
108 if (entry._collected) entry.pipe()
109 return self._process()
114 // console.error("DW not recursive")
116 // chop off the entry's root dir, replace with ours
119 root: self.root || self,
121 depth: self.depth + 1
124 pp = entry._path || entry.path || entry.props.path
126 pp = pp.substr(entry.parent._path.length + 1)
128 // get rid of any ../../ shenanigans
129 props.path = path.join(self.path, path.join('/', pp))
131 // if i have a filter, the child should inherit it.
132 props.filter = self.filter
134 // all the rest of the stuff, copy over from the source.
135 Object.keys(entry.props).forEach(function (k) {
136 if (!props.hasOwnProperty(k)) {
137 props[k] = entry.props[k]
141 // not sure at this point what kind of writer this is.
142 var child = self._currentChild = new Writer(props)
143 child.on('ready', function () {
144 // console.error("DW Child Ready", child.type, child._path)
145 // console.error(" resuming", entry._path)
150 // XXX Make this work in node.
151 // Long filenames should not break stuff.
152 child.on('error', function (er) {
153 if (child._swallowErrors) {
158 self.emit('error', er)
162 // we fire _end internally *after* end, so that we don't move on
163 // until any "end" listeners have had their chance to do stuff.
164 child.on('close', onend)
169 // console.error("* DW Child end", child.basename)
170 self._currentChild = null
171 self._processing = false