summaryrefslogtreecommitdiff
path: root/deps/npm/node_modules/npm-lifecycle/node_modules/node-gyp/node_modules/tar/lib/entry.js
blob: 5f5dd3c2688a8b9bfe88c7f4b0a54e77a9142ca9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
// 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 = Entry

var TarHeader = require("./header.js")
  , tar = require("../tar")
  , assert = require("assert").ok
  , Stream = require("stream").Stream
  , inherits = require("inherits")
  , fstream = require("fstream").Abstract

function 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 instance
Entry.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.warn
Entry.prototype.error = fstream.error