1 // A passthrough read/write stream that sets its properties
2 // based on a header, extendedHeader, and globalHeader
4 // Can be either a file system object of some sort, or
5 // a pax/ustar metadata entry.
9 var TarHeader = require("./header.js")
10 , tar = require("../tar")
11 , assert = require("assert").ok
12 , Stream = require("stream").Stream
13 , inherits = require("inherits")
14 , fstream = require("fstream").Abstract
16 function Entry (header, extended, global) {
21 this._needDrain = false
32 this._read = this._read.bind(this)
36 this._extended = extended || {}
38 // globals can change throughout the course of
39 // a file parse operation. Freeze it at its current state.
42 Object.keys(global || {}).forEach(function (g) {
43 me._global[g] = global[g]
49 inherits(Entry, Stream)
51 Entry.prototype.write = function (c) {
52 if (this._ending) this.error("write() after end()", null, true)
53 if (this._remaining === 0) {
54 this.error("invalid bytes past eof")
57 // often we'll get a bunch of \0 at the end of the last write,
58 // since chunks will always be 512 bytes when reading a tarball.
59 if (c.length > this._remaining) {
60 c = c.slice(0, this._remaining)
62 this._remaining -= c.length
64 // put it on the stack.
65 var ql = this._queueLen
71 // either paused, or buffered
72 if (this._paused || ql > 0) {
73 this._needDrain = true
80 Entry.prototype.end = function (c) {
86 Entry.prototype.pause = function () {
91 Entry.prototype.resume = function () {
92 // console.error(" Tar Entry resume", this.path)
96 return this._queueLen - this._index > 1
99 // This is bound to the instance
100 Entry.prototype._read = function () {
101 // console.error(" Tar Entry _read", this.path)
103 if (this._paused || this._reading || this._ended) return
105 // set this flag so that event handlers don't inadvertently
106 // get multiple _read() calls running.
109 // have any data to emit?
110 while (this._index < this._queueLen && !this._paused) {
111 var chunk = this._queue[this._index ++]
112 this.emit("data", chunk)
115 // check if we're drained
116 if (this._index >= this._queueLen) {
117 this._queue.length = this._queueLen = this._index = 0
118 if (this._needDrain) {
119 this._needDrain = false
128 // if the queue gets too big, then pluck off whatever we can.
129 // this should be fairly rare.
130 var mql = this._maxQueueLen
131 if (this._queueLen > mql && this._index > 0) {
132 mql = Math.min(this._index, mql)
134 this._queueLen -= mql
135 this._queue = this._queue.slice(mql)
138 this._reading = false
141 Entry.prototype._setProps = function () {
142 // props = extended->global->header->{}
143 var header = this._header
144 , extended = this._extended
145 , global = this._global
148 // first get the values from the normal header.
149 var fields = tar.fields
150 for (var f = 0; fields[f] !== null; f ++) {
151 var field = fields[f]
152 , val = header[field]
153 if (typeof val !== "undefined") props[field] = val
156 // next, the global header for this file.
157 // numeric values, etc, will have already been parsed.
158 ;[global, extended].forEach(function (p) {
159 Object.keys(p).forEach(function (f) {
160 if (typeof p[f] !== "undefined") props[f] = p[f]
164 // no nulls allowed in path or linkpath
165 ;["path", "linkpath"].forEach(function (p) {
166 if (props.hasOwnProperty(p)) {
167 props[p] = props[p].split("\0")[0]
172 // set date fields to be a proper date
173 ;["mtime", "ctime", "atime"].forEach(function (p) {
174 if (props.hasOwnProperty(p)) {
175 props[p] = new Date(props[p] * 1000)
179 // set the type so that we know what kind of file to create
181 switch (tar.types[props.type]) {
183 case "ContiguousFile":
197 case "CharacterDevice":
202 type = tar.types[props.type]
206 this.path = props.path
207 this.size = props.size
209 // size is special, since it signals when the file needs to end.
210 this._remaining = props.size
213 // the parser may not call write if _abort is true.
214 // useful for skipping data from some files quickly.
215 Entry.prototype.abort = function(){
219 Entry.prototype.warn = fstream.warn
220 Entry.prototype.error = fstream.error