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