All files / tar/lib list.js

100% Statements 80/80
100% Branches 53/53
100% Functions 14/14
100% Lines 77/77
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)