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
31
this._read = this._read.bind(this)
35
this._extended = extended || {}
37
// globals can change throughout the course of
38
// a file parse operation. Freeze it at its current state.
41
Object.keys(global || {}).forEach(function (g) {
42
me._global[g] = global[g]
48
inherits(Entry, Stream,
49
{ write: function (c) {
50
if (this._ending) this.error("write() after end()", null, true)
51
if (this._remaining === 0) {
52
this.error("invalid bytes past eof")
55
// often we'll get a bunch of \0 at the end of the last write,
56
// since chunks will always be 512 bytes when reading a tarball.
57
if (c.length > this._remaining) {
58
c = c.slice(0, this._remaining)
60
this._remaining -= c.length
62
// put it on the stack.
63
var ql = this._queueLen
69
// either paused, or buffered
70
if (this._paused || ql > 0) {
71
this._needDrain = true
84
, pause: function () {
89
, resume: function () {
90
// console.error(" Tar Entry resume", this.path)
94
return this._queueLen - this._index > 1
97
// This is bound to the instance
98
, _read: function () {
99
// console.error(" Tar Entry _read", this.path)
101
if (this._paused || this._reading || this._ended) return
103
// set this flag so that event handlers don't inadvertently
104
// get multiple _read() calls running.
107
// have any data to emit?
108
if (this._index < this._queueLen) {
109
var chunk = this._queue[this._index ++]
110
this.emit("data", chunk)
113
// check if we're drained
114
if (this._index >= this._queueLen) {
115
this._queue.length = this._queueLen = this._index = 0
116
if (this._needDrain) {
117
this._needDrain = false
126
// if the queue gets too big, then pluck off whatever we can.
127
// this should be fairly rare.
128
var mql = this._maxQueueLen
129
if (this._queueLen > mql && this._index > 0) {
130
mql = Math.min(this._index, mql)
132
this._queueLen -= mql
133
this._queue = this._queue.slice(mql)
136
this._reading = false
139
, _setProps: function () {
140
// props = extended->global->header->{}
141
var header = this._header
142
, extended = this._extended
143
, global = this._global
146
// first get the values from the normal header.
147
var fields = tar.fields
148
for (var f = 0; fields[f] !== null; f ++) {
149
var field = fields[f]
150
, val = header[field]
151
if (typeof val !== "undefined") props[field] = val
154
// next, the global header for this file.
155
// numeric values, etc, will have already been parsed.
156
;[global, extended].forEach(function (p) {
157
Object.keys(p).forEach(function (f) {
158
if (typeof p[f] !== "undefined") props[f] = p[f]
162
// no nulls allowed in path or linkpath
163
;["path", "linkpath"].forEach(function (p) {
164
if (props.hasOwnProperty(p)) {
165
props[p] = props[p].split("\0")[0]
170
// set date fields to be a proper date
171
;["mtime", "ctime", "atime"].forEach(function (p) {
172
if (props.hasOwnProperty(p)) {
173
props[p] = new Date(props[p] * 1000)
177
// set the type so that we know what kind of file to create
179
switch (tar.types[props.type]) {
181
case "ContiguousFile":
195
case "CharacterDevice":
200
type = tar.types[props.type]
204
this.path = props.path
205
this.size = props.size
207
// size is special, since it signals when the file needs to end.
208
this._remaining = props.size
211
, error: fstream.error