| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 | 
							
- // A writable stream.
 
- // It emits "entry" events, which provide a readable stream that has
 
- // header info attached.
 
- module.exports = Parse.create = Parse
 
- var stream = require("stream")
 
-   , Stream = stream.Stream
 
-   , BlockStream = require("block-stream")
 
-   , tar = require("../tar.js")
 
-   , TarHeader = require("./header.js")
 
-   , Entry = require("./entry.js")
 
-   , BufferEntry = require("./buffer-entry.js")
 
-   , ExtendedHeader = require("./extended-header.js")
 
-   , assert = require("assert").ok
 
-   , inherits = require("inherits")
 
-   , fstream = require("fstream")
 
- // reading a tar is a lot like reading a directory
 
- // However, we're actually not going to run the ctor,
 
- // since it does a stat and various other stuff.
 
- // This inheritance gives us the pause/resume/pipe
 
- // behavior that is desired.
 
- inherits(Parse, fstream.Reader)
 
- function Parse () {
 
-   var me = this
 
-   if (!(me instanceof Parse)) return new Parse()
 
-   // doesn't apply fstream.Reader ctor?
 
-   // no, becasue we don't want to stat/etc, we just
 
-   // want to get the entry/add logic from .pipe()
 
-   Stream.apply(me)
 
-   me.writable = true
 
-   me.readable = true
 
-   me._stream = new BlockStream(512)
 
-   me.position = 0
 
-   me._ended = false
 
-   me._hardLinks = {}
 
-   me._stream.on("error", function (e) {
 
-     me.emit("error", e)
 
-   })
 
-   me._stream.on("data", function (c) {
 
-     me._process(c)
 
-   })
 
-   me._stream.on("end", function () {
 
-     me._streamEnd()
 
-   })
 
-   me._stream.on("drain", function () {
 
-     me.emit("drain")
 
-   })
 
- }
 
- // overridden in Extract class, since it needs to
 
- // wait for its DirWriter part to finish before
 
- // emitting "end"
 
- Parse.prototype._streamEnd = function () {
 
-   var me = this
 
-   if (!me._ended || me._entry) me.error("unexpected eof")
 
-   me.emit("end")
 
- }
 
- // a tar reader is actually a filter, not just a readable stream.
 
- // So, you should pipe a tarball stream into it, and it needs these
 
- // write/end methods to do that.
 
- Parse.prototype.write = function (c) {
 
-   if (this._ended) {
 
-     // gnutar puts a LOT of nulls at the end.
 
-     // you can keep writing these things forever.
 
-     // Just ignore them.
 
-     for (var i = 0, l = c.length; i > l; i ++) {
 
-       if (c[i] !== 0) return this.error("write() after end()")
 
-     }
 
-     return
 
-   }
 
-   return this._stream.write(c)
 
- }
 
- Parse.prototype.end = function (c) {
 
-   this._ended = true
 
-   return this._stream.end(c)
 
- }
 
- // don't need to do anything, since we're just
 
- // proxying the data up from the _stream.
 
- // Just need to override the parent's "Not Implemented"
 
- // error-thrower.
 
- Parse.prototype._read = function () {}
 
- Parse.prototype._process = function (c) {
 
-   assert(c && c.length === 512, "block size should be 512")
 
-   // one of three cases.
 
-   // 1. A new header
 
-   // 2. A part of a file/extended header
 
-   // 3. One of two or more EOF null blocks
 
-   if (this._entry) {
 
-     var entry = this._entry
 
-     if(!entry._abort) entry.write(c)
 
-     else {
 
-       entry._remaining -= c.length
 
-       if(entry._remaining < 0) entry._remaining = 0
 
-     }
 
-     if (entry._remaining === 0) {
 
-       entry.end()
 
-       this._entry = null
 
-     }
 
-   } else {
 
-     // either zeroes or a header
 
-     var zero = true
 
-     for (var i = 0; i < 512 && zero; i ++) {
 
-       zero = c[i] === 0
 
-     }
 
-     // eof is *at least* 2 blocks of nulls, and then the end of the
 
-     // file.  you can put blocks of nulls between entries anywhere,
 
-     // so appending one tarball to another is technically valid.
 
-     // ending without the eof null blocks is not allowed, however.
 
-     if (zero) {
 
-       if (this._eofStarted)
 
-         this._ended = true
 
-       this._eofStarted = true
 
-     } else {
 
-       this._eofStarted = false
 
-       this._startEntry(c)
 
-     }
 
-   }
 
-   this.position += 512
 
- }
 
- // take a header chunk, start the right kind of entry.
 
- Parse.prototype._startEntry = function (c) {
 
-   var header = new TarHeader(c)
 
-     , self = this
 
-     , entry
 
-     , ev
 
-     , EntryType
 
-     , onend
 
-     , meta = false
 
-   if (null === header.size || !header.cksumValid) {
 
-     var e = new Error("invalid tar file")
 
-     e.header = header
 
-     e.tar_file_offset = this.position
 
-     e.tar_block = this.position / 512
 
-     return this.emit("error", e)
 
-   }
 
-   switch (tar.types[header.type]) {
 
-     case "File":
 
-     case "OldFile":
 
-     case "Link":
 
-     case "SymbolicLink":
 
-     case "CharacterDevice":
 
-     case "BlockDevice":
 
-     case "Directory":
 
-     case "FIFO":
 
-     case "ContiguousFile":
 
-     case "GNUDumpDir":
 
-       // start a file.
 
-       // pass in any extended headers
 
-       // These ones consumers are typically most interested in.
 
-       EntryType = Entry
 
-       ev = "entry"
 
-       break
 
-     case "GlobalExtendedHeader":
 
-       // extended headers that apply to the rest of the tarball
 
-       EntryType = ExtendedHeader
 
-       onend = function () {
 
-         self._global = self._global || {}
 
-         Object.keys(entry.fields).forEach(function (k) {
 
-           self._global[k] = entry.fields[k]
 
-         })
 
-       }
 
-       ev = "globalExtendedHeader"
 
-       meta = true
 
-       break
 
-     case "ExtendedHeader":
 
-     case "OldExtendedHeader":
 
-       // extended headers that apply to the next entry
 
-       EntryType = ExtendedHeader
 
-       onend = function () {
 
-         self._extended = entry.fields
 
-       }
 
-       ev = "extendedHeader"
 
-       meta = true
 
-       break
 
-     case "NextFileHasLongLinkpath":
 
-       // set linkpath=<contents> in extended header
 
-       EntryType = BufferEntry
 
-       onend = function () {
 
-         self._extended = self._extended || {}
 
-         self._extended.linkpath = entry.body
 
-       }
 
-       ev = "longLinkpath"
 
-       meta = true
 
-       break
 
-     case "NextFileHasLongPath":
 
-     case "OldGnuLongPath":
 
-       // set path=<contents> in file-extended header
 
-       EntryType = BufferEntry
 
-       onend = function () {
 
-         self._extended = self._extended || {}
 
-         self._extended.path = entry.body
 
-       }
 
-       ev = "longPath"
 
-       meta = true
 
-       break
 
-     default:
 
-       // all the rest we skip, but still set the _entry
 
-       // member, so that we can skip over their data appropriately.
 
-       // emit an event to say that this is an ignored entry type?
 
-       EntryType = Entry
 
-       ev = "ignoredEntry"
 
-       break
 
-   }
 
-   var global, extended
 
-   if (meta) {
 
-     global = extended = null
 
-   } else {
 
-     var global = this._global
 
-     var extended = this._extended
 
-     // extendedHeader only applies to one entry, so once we start
 
-     // an entry, it's over.
 
-     this._extended = null
 
-   }
 
-   entry = new EntryType(header, extended, global)
 
-   entry.meta = meta
 
-   // only proxy data events of normal files.
 
-   if (!meta) {
 
-     entry.on("data", function (c) {
 
-       me.emit("data", c)
 
-     })
 
-   }
 
-   if (onend) entry.on("end", onend)
 
-   this._entry = entry
 
-   if (entry.type === "Link") {
 
-     this._hardLinks[entry.path] = entry
 
-   }
 
-   var me = this
 
-   entry.on("pause", function () {
 
-     me.pause()
 
-   })
 
-   entry.on("resume", function () {
 
-     me.resume()
 
-   })
 
-   if (this.listeners("*").length) {
 
-     this.emit("*", ev, entry)
 
-   }
 
-   this.emit(ev, entry)
 
-   // Zero-byte entry.  End immediately.
 
-   if (entry.props.size === 0) {
 
-     entry.end()
 
-     this._entry = null
 
-   }
 
- }
 
 
  |