| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 | var util = require('util')var bl = require('bl')var headers = require('./headers')var Writable = require('readable-stream').Writablevar PassThrough = require('readable-stream').PassThroughvar noop = function () {}var overflow = function (size) {  size &= 511  return size && 512 - size}var emptyStream = function (self, offset) {  var s = new Source(self, offset)  s.end()  return s}var mixinPax = function (header, pax) {  if (pax.path) header.name = pax.path  if (pax.linkpath) header.linkname = pax.linkpath  if (pax.size) header.size = parseInt(pax.size, 10)  header.pax = pax  return header}var Source = function (self, offset) {  this._parent = self  this.offset = offset  PassThrough.call(this, { autoDestroy: false })}util.inherits(Source, PassThrough)Source.prototype.destroy = function (err) {  this._parent.destroy(err)}var Extract = function (opts) {  if (!(this instanceof Extract)) return new Extract(opts)  Writable.call(this, opts)  opts = opts || {}  this._offset = 0  this._buffer = bl()  this._missing = 0  this._partial = false  this._onparse = noop  this._header = null  this._stream = null  this._overflow = null  this._cb = null  this._locked = false  this._destroyed = false  this._pax = null  this._paxGlobal = null  this._gnuLongPath = null  this._gnuLongLinkPath = null  var self = this  var b = self._buffer  var oncontinue = function () {    self._continue()  }  var onunlock = function (err) {    self._locked = false    if (err) return self.destroy(err)    if (!self._stream) oncontinue()  }  var onstreamend = function () {    self._stream = null    var drain = overflow(self._header.size)    if (drain) self._parse(drain, ondrain)    else self._parse(512, onheader)    if (!self._locked) oncontinue()  }  var ondrain = function () {    self._buffer.consume(overflow(self._header.size))    self._parse(512, onheader)    oncontinue()  }  var onpaxglobalheader = function () {    var size = self._header.size    self._paxGlobal = headers.decodePax(b.slice(0, size))    b.consume(size)    onstreamend()  }  var onpaxheader = function () {    var size = self._header.size    self._pax = headers.decodePax(b.slice(0, size))    if (self._paxGlobal) self._pax = Object.assign({}, self._paxGlobal, self._pax)    b.consume(size)    onstreamend()  }  var ongnulongpath = function () {    var size = self._header.size    this._gnuLongPath = headers.decodeLongPath(b.slice(0, size), opts.filenameEncoding)    b.consume(size)    onstreamend()  }  var ongnulonglinkpath = function () {    var size = self._header.size    this._gnuLongLinkPath = headers.decodeLongPath(b.slice(0, size), opts.filenameEncoding)    b.consume(size)    onstreamend()  }  var onheader = function () {    var offset = self._offset    var header    try {      header = self._header = headers.decode(b.slice(0, 512), opts.filenameEncoding, opts.allowUnknownFormat)    } catch (err) {      self.emit('error', err)    }    b.consume(512)    if (!header) {      self._parse(512, onheader)      oncontinue()      return    }    if (header.type === 'gnu-long-path') {      self._parse(header.size, ongnulongpath)      oncontinue()      return    }    if (header.type === 'gnu-long-link-path') {      self._parse(header.size, ongnulonglinkpath)      oncontinue()      return    }    if (header.type === 'pax-global-header') {      self._parse(header.size, onpaxglobalheader)      oncontinue()      return    }    if (header.type === 'pax-header') {      self._parse(header.size, onpaxheader)      oncontinue()      return    }    if (self._gnuLongPath) {      header.name = self._gnuLongPath      self._gnuLongPath = null    }    if (self._gnuLongLinkPath) {      header.linkname = self._gnuLongLinkPath      self._gnuLongLinkPath = null    }    if (self._pax) {      self._header = header = mixinPax(header, self._pax)      self._pax = null    }    self._locked = true    if (!header.size || header.type === 'directory') {      self._parse(512, onheader)      self.emit('entry', header, emptyStream(self, offset), onunlock)      return    }    self._stream = new Source(self, offset)    self.emit('entry', header, self._stream, onunlock)    self._parse(header.size, onstreamend)    oncontinue()  }  this._onheader = onheader  this._parse(512, onheader)}util.inherits(Extract, Writable)Extract.prototype.destroy = function (err) {  if (this._destroyed) return  this._destroyed = true  if (err) this.emit('error', err)  this.emit('close')  if (this._stream) this._stream.emit('close')}Extract.prototype._parse = function (size, onparse) {  if (this._destroyed) return  this._offset += size  this._missing = size  if (onparse === this._onheader) this._partial = false  this._onparse = onparse}Extract.prototype._continue = function () {  if (this._destroyed) return  var cb = this._cb  this._cb = noop  if (this._overflow) this._write(this._overflow, undefined, cb)  else cb()}Extract.prototype._write = function (data, enc, cb) {  if (this._destroyed) return  var s = this._stream  var b = this._buffer  var missing = this._missing  if (data.length) this._partial = true  // we do not reach end-of-chunk now. just forward it  if (data.length < missing) {    this._missing -= data.length    this._overflow = null    if (s) return s.write(data, cb)    b.append(data)    return cb()  }  // end-of-chunk. the parser should call cb.  this._cb = cb  this._missing = 0  var overflow = null  if (data.length > missing) {    overflow = data.slice(missing)    data = data.slice(0, missing)  }  if (s) s.end(data)  else b.append(data)  this._overflow = overflow  this._onparse()}Extract.prototype._final = function (cb) {  if (this._partial) return this.destroy(new Error('Unexpected end of data'))  cb()}module.exports = Extract
 |