You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

extract.js 2.8KB

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