All files / tar/lib extract.js

100% Statements 78/78
100% Branches 55/55
100% Functions 13/13
100% Lines 76/76
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      2x 2x 2x 2x   2x 23x 1x 22x 1x   23x 4x   23x 10x   23x   23x 2x   21x 1x   20x 7x   20x               2x 13x 7x   7x 87x 87x       87x 87x     7x 9x 9x     2x 7x   7x 7x   7x 7x 6x 6x 4x   2x 2x 2x 2x 7x 6x 6x   1x 1x   4x   7x 1x       2x 10x 10x   10x 10x 10x 10x   10x 10x 1x 9x 7x 7x 1x 6x     2x     2x 2x       10x     2x 1x     2x 2x    
'use strict'
 
// tar -x
const hlo = require('./high-level-opt.js')
const Unpack = require('./unpack.js')
const fs = require('fs')
const path = require('path')
 
const x = 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)
 
  return opt.file && opt.sync ? extractFileSync(opt)
    : opt.file ? extractFile(opt, cb)
    : opt.sync ? extractSync(opt)
    : extract(opt)
}
 
// 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 extractFileSync = opt => {
  const u = new Unpack.Sync(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)
      u.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
        u.write(buf.slice(0, bytesRead))
      }
      u.end()
      fs.closeSync(fd)
    }
    threw = false
  } finally {
    if (threw && fd)
      try { fs.closeSync(fd) } catch (er) {}
  }
}
 
const extractFile = (opt, cb) => {
  const u = new Unpack(opt)
  const readSize = opt.maxReadSize || 16*1024*1024
 
  const file = opt.file
  const p = new Promise((resolve, reject) => {
    u.on('error', reject)
    u.on('close', 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)
          u.end(data)
        })
      else {
        const stream = fs.createReadStream(file, {
          highWaterMark: readSize
        })
        stream.on('error', reject)
        stream.pipe(u)
      }
    })
  })
  return cb ? p.then(cb, cb) : p
}
 
const extractSync = opt => {
  return new Unpack.Sync(opt)
}
 
const extract = opt => {
  return new Unpack(opt)
}