All files / tar/lib list.js

100% Statements 82/82
100% Branches 55/55
100% Functions 15/15
100% Lines 79/79
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            2x 2x 2x 2x   2x 20x 1x 19x 1x   20x 5x   20x 11x   20x   20x 1x   19x 1x   18x 3x   18x   18x         2x 18x 18x 233x 233x 72x         2x 6x 3x   3x 125x 125x       125x 125x     3x 32x 64x     2x 4x 4x 4x   4x 4x 3x 3x 1x   2x 2x 2x 2x 28x 27x 27x   1x   2x   4x 1x       2x 11x 11x   11x 11x 11x 11x   11x 11x 2x 9x 7x 7x 2x 5x     2x     2x 2x       11x     7x  
'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 path = require('path')
 
const t = module.exports = (opt_, files, cb) => {
  if (typeof opt_ === 'function')
    cb = opt_, files = [], opt_ = {}
  else if (Array.isArray(opt_))
    files = opt_, opt_ = {}
 
  if (typeof files === 'function')
    cb = files, files = []
 
  if (!files)
    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)
 
  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 if (stat.size < readSize)
        fs.readFile(file, (er, data) => {
          if (er)
            return reject(er)
          parse.end(data)
        })
      else {
        const stream = fs.createReadStream(file, {
          highWaterMark: readSize
        })
        stream.on('error', reject)
        stream.pipe(parse)
      }
    })
  })
  return cb ? p.then(cb, cb) : p
}
 
const list = opt => new Parser(opt)