# All files / tar/lib extract.js

100% Statements 61/61
100% Branches 45/45
100% Functions 12/12
100% Lines 60/60

    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

    2x
    2x
    2x
    2x
    2x

    2x
    21x
    1x
    20x
    1x

    21x
    3x

    21x
    13x

    8x

    21x

    21x
    2x

    19x
    1x

    18x
    7x

    18x

    2x
    13x
    7x

    7x
    87x
    87x

    87x
    87x

    7x
    9x
    9x

    2x
    6x

    6x
    6x

    6x

    5x
    5x

    4x

    2x
    9x
    9x

    9x
    9x
    9x
    9x

    9x
    9x
    1x

    8x

    8x
    8x

    9x

    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 fsm = require('fs-minipass')
    const path = require('path')

    const x = 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)

      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
      const stat = fs.statSync(file)
      // This trades a zero-byte read() syscall for a stat
      // However, it will usually result in less memory allocation
      const readSize = opt.maxReadSize || 16*1024*1024
      const stream = new fsm.ReadStreamSync(file, {
        readSize: readSize,
        size: stat.size
      })
      stream.pipe(u)
    }

    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)

        // This trades a zero-byte read() syscall for a stat
        // However, it will usually result in less memory allocation
        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(u)
          }
        })
      })
      return cb ? p.then(cb, cb) : p
    }

    const extractSync = opt => {
      return new Unpack.Sync(opt)
    }

    const extract = opt => {
      return new Unpack(opt)
    }