list.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. 'use strict'
  2. const Buffer = require('./buffer.js')
  3. // XXX: This shares a lot in common with extract.js
  4. // maybe some DRY opportunity here?
  5. // tar -t
  6. const hlo = require('./high-level-opt.js')
  7. const Parser = require('./parse.js')
  8. const fs = require('fs')
  9. const fsm = require('fs-minipass')
  10. const path = require('path')
  11. const stripSlash = require('./strip-trailing-slashes.js')
  12. const t = module.exports = (opt_, files, cb) => {
  13. if (typeof opt_ === 'function')
  14. cb = opt_, files = null, opt_ = {}
  15. else if (Array.isArray(opt_))
  16. files = opt_, opt_ = {}
  17. if (typeof files === 'function')
  18. cb = files, files = null
  19. if (!files)
  20. files = []
  21. else
  22. files = Array.from(files)
  23. const opt = hlo(opt_)
  24. if (opt.sync && typeof cb === 'function')
  25. throw new TypeError('callback not supported for sync tar functions')
  26. if (!opt.file && typeof cb === 'function')
  27. throw new TypeError('callback only supported with file option')
  28. if (files.length)
  29. filesFilter(opt, files)
  30. if (!opt.noResume)
  31. onentryFunction(opt)
  32. return opt.file && opt.sync ? listFileSync(opt)
  33. : opt.file ? listFile(opt, cb)
  34. : list(opt)
  35. }
  36. const onentryFunction = opt => {
  37. const onentry = opt.onentry
  38. opt.onentry = onentry ? e => {
  39. onentry(e)
  40. e.resume()
  41. } : e => e.resume()
  42. }
  43. // construct a filter that limits the file entries listed
  44. // include child entries if a dir is included
  45. const filesFilter = (opt, files) => {
  46. const map = new Map(files.map(f => [stripSlash(f), true]))
  47. const filter = opt.filter
  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. map.set(file, ret)
  54. return ret
  55. }
  56. opt.filter = filter
  57. ? (file, entry) => filter(file, entry) && mapHas(stripSlash(file))
  58. : file => mapHas(stripSlash(file))
  59. }
  60. const listFileSync = opt => {
  61. const p = list(opt)
  62. const file = opt.file
  63. let threw = true
  64. let fd
  65. try {
  66. const stat = fs.statSync(file)
  67. const readSize = opt.maxReadSize || 16*1024*1024
  68. if (stat.size < readSize) {
  69. p.end(fs.readFileSync(file))
  70. } else {
  71. let pos = 0
  72. const buf = Buffer.allocUnsafe(readSize)
  73. fd = fs.openSync(file, 'r')
  74. while (pos < stat.size) {
  75. let bytesRead = fs.readSync(fd, buf, 0, readSize, pos)
  76. pos += bytesRead
  77. p.write(buf.slice(0, bytesRead))
  78. }
  79. p.end()
  80. }
  81. threw = false
  82. } finally {
  83. if (threw && fd)
  84. try { fs.closeSync(fd) } catch (er) {}
  85. }
  86. }
  87. const listFile = (opt, cb) => {
  88. const parse = new Parser(opt)
  89. const readSize = opt.maxReadSize || 16*1024*1024
  90. const file = opt.file
  91. const p = new Promise((resolve, reject) => {
  92. parse.on('error', reject)
  93. parse.on('end', resolve)
  94. fs.stat(file, (er, stat) => {
  95. if (er)
  96. reject(er)
  97. else {
  98. const stream = new fsm.ReadStream(file, {
  99. readSize: readSize,
  100. size: stat.size
  101. })
  102. stream.on('error', reject)
  103. stream.pipe(parse)
  104. }
  105. })
  106. })
  107. return cb ? p.then(cb, cb) : p
  108. }
  109. const list = opt => new Parser(opt)