1'use strict' 2 3// tar -x 4const hlo = require('./high-level-opt.js') 5const Unpack = require('./unpack.js') 6const fs = require('fs') 7const fsm = require('fs-minipass') 8const path = require('path') 9 10const x = module.exports = (opt_, files, cb) => { 11 if (typeof opt_ === 'function') 12 cb = opt_, files = null, opt_ = {} 13 else if (Array.isArray(opt_)) 14 files = opt_, opt_ = {} 15 16 if (typeof files === 'function') 17 cb = files, files = null 18 19 if (!files) 20 files = [] 21 else 22 files = Array.from(files) 23 24 const opt = hlo(opt_) 25 26 if (opt.sync && typeof cb === 'function') 27 throw new TypeError('callback not supported for sync tar functions') 28 29 if (!opt.file && typeof cb === 'function') 30 throw new TypeError('callback only supported with file option') 31 32 if (files.length) 33 filesFilter(opt, files) 34 35 return opt.file && opt.sync ? extractFileSync(opt) 36 : opt.file ? extractFile(opt, cb) 37 : opt.sync ? extractSync(opt) 38 : extract(opt) 39} 40 41// construct a filter that limits the file entries listed 42// include child entries if a dir is included 43const filesFilter = (opt, files) => { 44 const map = new Map(files.map(f => [f.replace(/\/+$/, ''), true])) 45 const filter = opt.filter 46 47 const mapHas = (file, r) => { 48 const root = r || path.parse(file).root || '.' 49 const ret = file === root ? false 50 : map.has(file) ? map.get(file) 51 : mapHas(path.dirname(file), root) 52 53 map.set(file, ret) 54 return ret 55 } 56 57 opt.filter = filter 58 ? (file, entry) => filter(file, entry) && mapHas(file.replace(/\/+$/, '')) 59 : file => mapHas(file.replace(/\/+$/, '')) 60} 61 62const extractFileSync = opt => { 63 const u = new Unpack.Sync(opt) 64 65 const file = opt.file 66 let threw = true 67 let fd 68 const stat = fs.statSync(file) 69 // This trades a zero-byte read() syscall for a stat 70 // However, it will usually result in less memory allocation 71 const readSize = opt.maxReadSize || 16*1024*1024 72 const stream = new fsm.ReadStreamSync(file, { 73 readSize: readSize, 74 size: stat.size 75 }) 76 stream.pipe(u) 77} 78 79const extractFile = (opt, cb) => { 80 const u = new Unpack(opt) 81 const readSize = opt.maxReadSize || 16*1024*1024 82 83 const file = opt.file 84 const p = new Promise((resolve, reject) => { 85 u.on('error', reject) 86 u.on('close', resolve) 87 88 // This trades a zero-byte read() syscall for a stat 89 // However, it will usually result in less memory allocation 90 fs.stat(file, (er, stat) => { 91 if (er) 92 reject(er) 93 else { 94 const stream = new fsm.ReadStream(file, { 95 readSize: readSize, 96 size: stat.size 97 }) 98 stream.on('error', reject) 99 stream.pipe(u) 100 } 101 }) 102 }) 103 return cb ? p.then(cb, cb) : p 104} 105 106const extractSync = opt => { 107 return new Unpack.Sync(opt) 108} 109 110const extract = opt => { 111 return new Unpack(opt) 112} 113