entry-writer.js 3.66 KB
module.exports = EntryWriter

var tar = require("../tar.js")
  , TarHeader = require("./header.js")
  , Entry = require("./entry.js")
  , inherits = require("inherits")
  , BlockStream = require("block-stream")
  , ExtendedHeaderWriter
  , Stream = require("stream").Stream
  , EOF = {}

inherits(EntryWriter, Stream)

function EntryWriter (props) {
  var me = this

  if (!(me instanceof EntryWriter)) {
    return new EntryWriter(props)
  }

  Stream.apply(this)

  me.writable = true
  me.readable = true

  me._stream = new BlockStream(512)

  me._stream.on("data", function (c) {
    me.emit("data", c)
  })

  me._stream.on("drain", function () {
    me.emit("drain")
  })

  me._stream.on("end", function () {
    me.emit("end")
    me.emit("close")
  })

  me.props = props
  if (props.type === "Directory") {
    props.size = 0
  }
  props.ustar = "ustar\0"
  props.ustarver = "00"
  me.path = props.path

  me._buffer = []
  me._didHeader = false
  me._meta = false

  me.on("pipe", function () {
    me._process()
  })
}

EntryWriter.prototype.write = function (c) {
  // console.error(".. ew write")
  if (this._ended) return this.emit("error", new Error("write after end"))
  this._buffer.push(c)
  this._process()
  this._needDrain = this._buffer.length > 0
  return !this._needDrain
}

EntryWriter.prototype.end = function (c) {
  // console.error(".. ew end")
  if (c) this._buffer.push(c)
  this._buffer.push(EOF)
  this._ended = true
  this._process()
  this._needDrain = this._buffer.length > 0
}

EntryWriter.prototype.pause = function () {
  // console.error(".. ew pause")
  this._paused = true
  this.emit("pause")
}

EntryWriter.prototype.resume = function () {
  // console.error(".. ew resume")
  this._paused = false
  this.emit("resume")
  this._process()
}

EntryWriter.prototype.add = function (entry) {
  // console.error(".. ew add")
  if (!this.parent) return this.emit("error", new Error("no parent"))

  // make sure that the _header and such is emitted, and clear out
  // the _currentEntry link on the parent.
  if (!this._ended) this.end()

  return this.parent.add(entry)
}

EntryWriter.prototype._header = function () {
  // console.error(".. ew header")
  if (this._didHeader) return
  this._didHeader = true

  var headerBlock = TarHeader.encode(this.props)

  if (this.props.needExtended && !this._meta) {
    var me = this

    ExtendedHeaderWriter = ExtendedHeaderWriter ||
      require("./extended-header-writer.js")

    ExtendedHeaderWriter(this.props)
      .on("data", function (c) {
        me.emit("data", c)
      })
      .on("error", function (er) {
        me.emit("error", er)
      })
      .end()
  }

  // console.error(".. .. ew headerBlock emitting")
  this.emit("data", headerBlock)
  this.emit("header")
}

EntryWriter.prototype._process = function () {
  // console.error(".. .. ew process")
  if (!this._didHeader && !this._meta) {
    this._header()
  }

  if (this._paused || this._processing) {
    // console.error(".. .. .. paused=%j, processing=%j", this._paused, this._processing)
    return
  }

  this._processing = true

  var buf = this._buffer
  for (var i = 0; i < buf.length; i ++) {
    // console.error(".. .. .. i=%d", i)

    var c = buf[i]

    if (c === EOF) this._stream.end()
    else this._stream.write(c)

    if (this._paused) {
      // console.error(".. .. .. paused mid-emission")
      this._processing = false
      if (i < buf.length) {
        this._needDrain = true
        this._buffer = buf.slice(i + 1)
      }
      return
    }
  }

  // console.error(".. .. .. emitted")
  this._buffer.length = 0
  this._processing = false

  // console.error(".. .. .. emitting drain")
  this.emit("drain")
}

EntryWriter.prototype.destroy = function () {}