| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 | // A passthrough read/write stream that sets its properties// based on a header, extendedHeader, and globalHeader//// Can be either a file system object of some sort, or// a pax/ustar metadata entry.module.exports = Entryvar TarHeader = require("./header.js")  , tar = require("../tar")  , assert = require("assert").ok  , Stream = require("stream").Stream  , inherits = require("inherits")  , fstream = require("fstream").Abstractfunction Entry (header, extended, global) {  Stream.call(this)  this.readable = true  this.writable = true  this._needDrain = false  this._paused = false  this._reading = false  this._ending = false  this._ended = false  this._remaining = 0  this._abort = false  this._queue = []  this._index = 0  this._queueLen = 0  this._read = this._read.bind(this)  this.props = {}  this._header = header  this._extended = extended || {}  // globals can change throughout the course of  // a file parse operation.  Freeze it at its current state.  this._global = {}  var me = this  Object.keys(global || {}).forEach(function (g) {    me._global[g] = global[g]  })  this._setProps()}inherits(Entry, Stream)Entry.prototype.write = function (c) {  if (this._ending) this.error("write() after end()", null, true)  if (this._remaining === 0) {    this.error("invalid bytes past eof")  }  // often we'll get a bunch of \0 at the end of the last write,  // since chunks will always be 512 bytes when reading a tarball.  if (c.length > this._remaining) {    c = c.slice(0, this._remaining)  }  this._remaining -= c.length  // put it on the stack.  var ql = this._queueLen  this._queue.push(c)  this._queueLen ++  this._read()  // either paused, or buffered  if (this._paused || ql > 0) {    this._needDrain = true    return false  }  return true}Entry.prototype.end = function (c) {  if (c) this.write(c)  this._ending = true  this._read()}Entry.prototype.pause = function () {  this._paused = true  this.emit("pause")}Entry.prototype.resume = function () {  // console.error("    Tar Entry resume", this.path)  this.emit("resume")  this._paused = false  this._read()  return this._queueLen - this._index > 1}  // This is bound to the instanceEntry.prototype._read = function () {  // console.error("    Tar Entry _read", this.path)  if (this._paused || this._reading || this._ended) return  // set this flag so that event handlers don't inadvertently  // get multiple _read() calls running.  this._reading = true  // have any data to emit?  while (this._index < this._queueLen && !this._paused) {    var chunk = this._queue[this._index ++]    this.emit("data", chunk)  }  // check if we're drained  if (this._index >= this._queueLen) {    this._queue.length = this._queueLen = this._index = 0    if (this._needDrain) {      this._needDrain = false      this.emit("drain")    }    if (this._ending) {      this._ended = true      this.emit("end")    }  }  // if the queue gets too big, then pluck off whatever we can.  // this should be fairly rare.  var mql = this._maxQueueLen  if (this._queueLen > mql && this._index > 0) {    mql = Math.min(this._index, mql)    this._index -= mql    this._queueLen -= mql    this._queue = this._queue.slice(mql)  }  this._reading = false}Entry.prototype._setProps = function () {  // props = extended->global->header->{}  var header = this._header    , extended = this._extended    , global = this._global    , props = this.props  // first get the values from the normal header.  var fields = tar.fields  for (var f = 0; fields[f] !== null; f ++) {    var field = fields[f]      , val = header[field]    if (typeof val !== "undefined") props[field] = val  }  // next, the global header for this file.  // numeric values, etc, will have already been parsed.  ;[global, extended].forEach(function (p) {    Object.keys(p).forEach(function (f) {      if (typeof p[f] !== "undefined") props[f] = p[f]    })  })  // no nulls allowed in path or linkpath  ;["path", "linkpath"].forEach(function (p) {    if (props.hasOwnProperty(p)) {      props[p] = props[p].split("\0")[0]    }  })  // set date fields to be a proper date  ;["mtime", "ctime", "atime"].forEach(function (p) {    if (props.hasOwnProperty(p)) {      props[p] = new Date(props[p] * 1000)    }  })  // set the type so that we know what kind of file to create  var type  switch (tar.types[props.type]) {    case "OldFile":    case "ContiguousFile":      type = "File"      break    case "GNUDumpDir":      type = "Directory"      break    case undefined:      type = "Unknown"      break    case "Link":    case "SymbolicLink":    case "CharacterDevice":    case "BlockDevice":    case "Directory":    case "FIFO":    default:      type = tar.types[props.type]  }  this.type = type  this.path = props.path  this.size = props.size  // size is special, since it signals when the file needs to end.  this._remaining = props.size}// the parser may not call write if _abort is true. // useful for skipping data from some files quickly.Entry.prototype.abort = function(){  this._abort = true}Entry.prototype.warn = fstream.warnEntry.prototype.error = fstream.error
 |