| 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 | 5x
5x
5x
5x
5x
 
5x
34x
1x
33x
1x
 
34x
5x
 
34x
31x
 
3x
 
34x
 
34x
1x
 
33x
1x
 
32x
3x
 
32x
22x
 
32x
 
 
 
 
5x
22x
22x
255x
255x
72x
 
 
 
 
5x
6x
3x
 
3x
125x
125x
 
 
 
125x
125x
 
 
3x
32x
64x
 
 
5x
13x
13x
13x
 
13x
13x
12x
12x
10x
 
2x
2x
2x
2x
28x
27x
27x
 
1x
 
11x
 
13x
1x
 
 
 
5x
16x
16x
 
16x
16x
16x
16x
 
16x
16x
2x
 
14x
 
 
 
14x
14x
 
 
 
16x
 
 
16x | 'use strict'
 
// XXX: This shares a lot in common with extract.js
// maybe some DRY opportunity here?
 
// tar -t
const hlo = require('./high-level-opt.js')
const Parser = require('./parse.js')
const fs = require('fs')
const fsm = require('fs-minipass')
const path = require('path')
 
const t = module.exports = (opt_, files, cb) => {
  if (typeof opt_ === 'function')
    cb = opt_, files = null, opt_ = {}
  else if (Array.isArray(opt_))
    files = opt_, opt_ = {}
 
  if (typeof files === 'function')
    cb = files, files = null
 
  if (!files)
    files = []
  else
    files = Array.from(files)
 
  const opt = hlo(opt_)
 
  if (opt.sync && typeof cb === 'function')
    throw new TypeError('callback not supported for sync tar functions')
 
  if (!opt.file && typeof cb === 'function')
    throw new TypeError('callback only supported with file option')
 
  if (files.length)
    filesFilter(opt, files)
 
  if (!opt.noResume)
    onentryFunction(opt)
 
  return opt.file && opt.sync ? listFileSync(opt)
    : opt.file ? listFile(opt, cb)
    : list(opt)
}
 
const onentryFunction = opt => {
  const onentry = opt.onentry
  opt.onentry = onentry ? e => {
    onentry(e)
    e.resume()
  } : e => e.resume()
}
 
// construct a filter that limits the file entries listed
// include child entries if a dir is included
const filesFilter = (opt, files) => {
  const map = new Map(files.map(f => [f.replace(/\/+$/, ''), true]))
  const filter = opt.filter
 
  const mapHas = (file, r) => {
    const root = r || path.parse(file).root || '.'
    const ret = file === root ? false
      : map.has(file) ? map.get(file)
      : mapHas(path.dirname(file), root)
 
    map.set(file, ret)
    return ret
  }
 
  opt.filter = filter
    ? (file, entry) => filter(file, entry) && mapHas(file.replace(/\/+$/, ''))
    : file => mapHas(file.replace(/\/+$/, ''))
}
 
const listFileSync = opt => {
  const p = list(opt)
  const file = opt.file
  let threw = true
  let fd
  try {
    const stat = fs.statSync(file)
    const readSize = opt.maxReadSize || 16*1024*1024
    if (stat.size < readSize) {
      p.end(fs.readFileSync(file))
    } else {
      let pos = 0
      const buf = Buffer.allocUnsafe(readSize)
      fd = fs.openSync(file, 'r')
      while (pos < stat.size) {
        let bytesRead = fs.readSync(fd, buf, 0, readSize, pos)
        pos += bytesRead
        p.write(buf.slice(0, bytesRead))
      }
      p.end()
    }
    threw = false
  } finally {
    if (threw && fd)
      try { fs.closeSync(fd) } catch (er) {}
  }
}
 
const listFile = (opt, cb) => {
  const parse = new Parser(opt)
  const readSize = opt.maxReadSize || 16*1024*1024
 
  const file = opt.file
  const p = new Promise((resolve, reject) => {
    parse.on('error', reject)
    parse.on('end', resolve)
 
    fs.stat(file, (er, stat) => {
      if (er)
        reject(er)
      else {
        const stream = new fsm.ReadStream(file, {
          readSize: readSize,
          size: stat.size
        })
        stream.on('error', reject)
        stream.pipe(parse)
      }
    })
  })
  return cb ? p.then(cb, cb) : p
}
 
const list = opt => new Parser(opt)
  |