11cb0ef41Sopenharmony_ci'use strict'
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ci// this[BUFFER] is the remainder of a chunk if we're waiting for
41cb0ef41Sopenharmony_ci// the full 512 bytes of a header to come in.  We will Buffer.concat()
51cb0ef41Sopenharmony_ci// it to the next write(), which is a mem copy, but a small one.
61cb0ef41Sopenharmony_ci//
71cb0ef41Sopenharmony_ci// this[QUEUE] is a Yallist of entries that haven't been emitted
81cb0ef41Sopenharmony_ci// yet this can only get filled up if the user keeps write()ing after
91cb0ef41Sopenharmony_ci// a write() returns false, or does a write() with more than one entry
101cb0ef41Sopenharmony_ci//
111cb0ef41Sopenharmony_ci// We don't buffer chunks, we always parse them and either create an
121cb0ef41Sopenharmony_ci// entry, or push it into the active entry.  The ReadEntry class knows
131cb0ef41Sopenharmony_ci// to throw data away if .ignore=true
141cb0ef41Sopenharmony_ci//
151cb0ef41Sopenharmony_ci// Shift entry off the buffer when it emits 'end', and emit 'entry' for
161cb0ef41Sopenharmony_ci// the next one in the list.
171cb0ef41Sopenharmony_ci//
181cb0ef41Sopenharmony_ci// At any time, we're pushing body chunks into the entry at WRITEENTRY,
191cb0ef41Sopenharmony_ci// and waiting for 'end' on the entry at READENTRY
201cb0ef41Sopenharmony_ci//
211cb0ef41Sopenharmony_ci// ignored entries get .resume() called on them straight away
221cb0ef41Sopenharmony_ci
231cb0ef41Sopenharmony_ciconst warner = require('./warn-mixin.js')
241cb0ef41Sopenharmony_ciconst Header = require('./header.js')
251cb0ef41Sopenharmony_ciconst EE = require('events')
261cb0ef41Sopenharmony_ciconst Yallist = require('yallist')
271cb0ef41Sopenharmony_ciconst maxMetaEntrySize = 1024 * 1024
281cb0ef41Sopenharmony_ciconst Entry = require('./read-entry.js')
291cb0ef41Sopenharmony_ciconst Pax = require('./pax.js')
301cb0ef41Sopenharmony_ciconst zlib = require('minizlib')
311cb0ef41Sopenharmony_ciconst { nextTick } = require('process')
321cb0ef41Sopenharmony_ci
331cb0ef41Sopenharmony_ciconst gzipHeader = Buffer.from([0x1f, 0x8b])
341cb0ef41Sopenharmony_ciconst STATE = Symbol('state')
351cb0ef41Sopenharmony_ciconst WRITEENTRY = Symbol('writeEntry')
361cb0ef41Sopenharmony_ciconst READENTRY = Symbol('readEntry')
371cb0ef41Sopenharmony_ciconst NEXTENTRY = Symbol('nextEntry')
381cb0ef41Sopenharmony_ciconst PROCESSENTRY = Symbol('processEntry')
391cb0ef41Sopenharmony_ciconst EX = Symbol('extendedHeader')
401cb0ef41Sopenharmony_ciconst GEX = Symbol('globalExtendedHeader')
411cb0ef41Sopenharmony_ciconst META = Symbol('meta')
421cb0ef41Sopenharmony_ciconst EMITMETA = Symbol('emitMeta')
431cb0ef41Sopenharmony_ciconst BUFFER = Symbol('buffer')
441cb0ef41Sopenharmony_ciconst QUEUE = Symbol('queue')
451cb0ef41Sopenharmony_ciconst ENDED = Symbol('ended')
461cb0ef41Sopenharmony_ciconst EMITTEDEND = Symbol('emittedEnd')
471cb0ef41Sopenharmony_ciconst EMIT = Symbol('emit')
481cb0ef41Sopenharmony_ciconst UNZIP = Symbol('unzip')
491cb0ef41Sopenharmony_ciconst CONSUMECHUNK = Symbol('consumeChunk')
501cb0ef41Sopenharmony_ciconst CONSUMECHUNKSUB = Symbol('consumeChunkSub')
511cb0ef41Sopenharmony_ciconst CONSUMEBODY = Symbol('consumeBody')
521cb0ef41Sopenharmony_ciconst CONSUMEMETA = Symbol('consumeMeta')
531cb0ef41Sopenharmony_ciconst CONSUMEHEADER = Symbol('consumeHeader')
541cb0ef41Sopenharmony_ciconst CONSUMING = Symbol('consuming')
551cb0ef41Sopenharmony_ciconst BUFFERCONCAT = Symbol('bufferConcat')
561cb0ef41Sopenharmony_ciconst MAYBEEND = Symbol('maybeEnd')
571cb0ef41Sopenharmony_ciconst WRITING = Symbol('writing')
581cb0ef41Sopenharmony_ciconst ABORTED = Symbol('aborted')
591cb0ef41Sopenharmony_ciconst DONE = Symbol('onDone')
601cb0ef41Sopenharmony_ciconst SAW_VALID_ENTRY = Symbol('sawValidEntry')
611cb0ef41Sopenharmony_ciconst SAW_NULL_BLOCK = Symbol('sawNullBlock')
621cb0ef41Sopenharmony_ciconst SAW_EOF = Symbol('sawEOF')
631cb0ef41Sopenharmony_ciconst CLOSESTREAM = Symbol('closeStream')
641cb0ef41Sopenharmony_ci
651cb0ef41Sopenharmony_ciconst noop = _ => true
661cb0ef41Sopenharmony_ci
671cb0ef41Sopenharmony_cimodule.exports = warner(class Parser extends EE {
681cb0ef41Sopenharmony_ci  constructor (opt) {
691cb0ef41Sopenharmony_ci    opt = opt || {}
701cb0ef41Sopenharmony_ci    super(opt)
711cb0ef41Sopenharmony_ci
721cb0ef41Sopenharmony_ci    this.file = opt.file || ''
731cb0ef41Sopenharmony_ci
741cb0ef41Sopenharmony_ci    // set to boolean false when an entry starts.  1024 bytes of \0
751cb0ef41Sopenharmony_ci    // is technically a valid tarball, albeit a boring one.
761cb0ef41Sopenharmony_ci    this[SAW_VALID_ENTRY] = null
771cb0ef41Sopenharmony_ci
781cb0ef41Sopenharmony_ci    // these BADARCHIVE errors can't be detected early. listen on DONE.
791cb0ef41Sopenharmony_ci    this.on(DONE, _ => {
801cb0ef41Sopenharmony_ci      if (this[STATE] === 'begin' || this[SAW_VALID_ENTRY] === false) {
811cb0ef41Sopenharmony_ci        // either less than 1 block of data, or all entries were invalid.
821cb0ef41Sopenharmony_ci        // Either way, probably not even a tarball.
831cb0ef41Sopenharmony_ci        this.warn('TAR_BAD_ARCHIVE', 'Unrecognized archive format')
841cb0ef41Sopenharmony_ci      }
851cb0ef41Sopenharmony_ci    })
861cb0ef41Sopenharmony_ci
871cb0ef41Sopenharmony_ci    if (opt.ondone) {
881cb0ef41Sopenharmony_ci      this.on(DONE, opt.ondone)
891cb0ef41Sopenharmony_ci    } else {
901cb0ef41Sopenharmony_ci      this.on(DONE, _ => {
911cb0ef41Sopenharmony_ci        this.emit('prefinish')
921cb0ef41Sopenharmony_ci        this.emit('finish')
931cb0ef41Sopenharmony_ci        this.emit('end')
941cb0ef41Sopenharmony_ci      })
951cb0ef41Sopenharmony_ci    }
961cb0ef41Sopenharmony_ci
971cb0ef41Sopenharmony_ci    this.strict = !!opt.strict
981cb0ef41Sopenharmony_ci    this.maxMetaEntrySize = opt.maxMetaEntrySize || maxMetaEntrySize
991cb0ef41Sopenharmony_ci    this.filter = typeof opt.filter === 'function' ? opt.filter : noop
1001cb0ef41Sopenharmony_ci    // Unlike gzip, brotli doesn't have any magic bytes to identify it
1011cb0ef41Sopenharmony_ci    // Users need to explicitly tell us they're extracting a brotli file
1021cb0ef41Sopenharmony_ci    // Or we infer from the file extension
1031cb0ef41Sopenharmony_ci    const isTBR = (opt.file && (
1041cb0ef41Sopenharmony_ci        opt.file.endsWith('.tar.br') || opt.file.endsWith('.tbr')))
1051cb0ef41Sopenharmony_ci    // if it's a tbr file it MIGHT be brotli, but we don't know until
1061cb0ef41Sopenharmony_ci    // we look at it and verify it's not a valid tar file.
1071cb0ef41Sopenharmony_ci    this.brotli = !opt.gzip && opt.brotli !== undefined ? opt.brotli
1081cb0ef41Sopenharmony_ci      : isTBR ? undefined
1091cb0ef41Sopenharmony_ci      : false
1101cb0ef41Sopenharmony_ci
1111cb0ef41Sopenharmony_ci    // have to set this so that streams are ok piping into it
1121cb0ef41Sopenharmony_ci    this.writable = true
1131cb0ef41Sopenharmony_ci    this.readable = false
1141cb0ef41Sopenharmony_ci
1151cb0ef41Sopenharmony_ci    this[QUEUE] = new Yallist()
1161cb0ef41Sopenharmony_ci    this[BUFFER] = null
1171cb0ef41Sopenharmony_ci    this[READENTRY] = null
1181cb0ef41Sopenharmony_ci    this[WRITEENTRY] = null
1191cb0ef41Sopenharmony_ci    this[STATE] = 'begin'
1201cb0ef41Sopenharmony_ci    this[META] = ''
1211cb0ef41Sopenharmony_ci    this[EX] = null
1221cb0ef41Sopenharmony_ci    this[GEX] = null
1231cb0ef41Sopenharmony_ci    this[ENDED] = false
1241cb0ef41Sopenharmony_ci    this[UNZIP] = null
1251cb0ef41Sopenharmony_ci    this[ABORTED] = false
1261cb0ef41Sopenharmony_ci    this[SAW_NULL_BLOCK] = false
1271cb0ef41Sopenharmony_ci    this[SAW_EOF] = false
1281cb0ef41Sopenharmony_ci
1291cb0ef41Sopenharmony_ci    this.on('end', () => this[CLOSESTREAM]())
1301cb0ef41Sopenharmony_ci
1311cb0ef41Sopenharmony_ci    if (typeof opt.onwarn === 'function') {
1321cb0ef41Sopenharmony_ci      this.on('warn', opt.onwarn)
1331cb0ef41Sopenharmony_ci    }
1341cb0ef41Sopenharmony_ci    if (typeof opt.onentry === 'function') {
1351cb0ef41Sopenharmony_ci      this.on('entry', opt.onentry)
1361cb0ef41Sopenharmony_ci    }
1371cb0ef41Sopenharmony_ci  }
1381cb0ef41Sopenharmony_ci
1391cb0ef41Sopenharmony_ci  [CONSUMEHEADER] (chunk, position) {
1401cb0ef41Sopenharmony_ci    if (this[SAW_VALID_ENTRY] === null) {
1411cb0ef41Sopenharmony_ci      this[SAW_VALID_ENTRY] = false
1421cb0ef41Sopenharmony_ci    }
1431cb0ef41Sopenharmony_ci    let header
1441cb0ef41Sopenharmony_ci    try {
1451cb0ef41Sopenharmony_ci      header = new Header(chunk, position, this[EX], this[GEX])
1461cb0ef41Sopenharmony_ci    } catch (er) {
1471cb0ef41Sopenharmony_ci      return this.warn('TAR_ENTRY_INVALID', er)
1481cb0ef41Sopenharmony_ci    }
1491cb0ef41Sopenharmony_ci
1501cb0ef41Sopenharmony_ci    if (header.nullBlock) {
1511cb0ef41Sopenharmony_ci      if (this[SAW_NULL_BLOCK]) {
1521cb0ef41Sopenharmony_ci        this[SAW_EOF] = true
1531cb0ef41Sopenharmony_ci        // ending an archive with no entries.  pointless, but legal.
1541cb0ef41Sopenharmony_ci        if (this[STATE] === 'begin') {
1551cb0ef41Sopenharmony_ci          this[STATE] = 'header'
1561cb0ef41Sopenharmony_ci        }
1571cb0ef41Sopenharmony_ci        this[EMIT]('eof')
1581cb0ef41Sopenharmony_ci      } else {
1591cb0ef41Sopenharmony_ci        this[SAW_NULL_BLOCK] = true
1601cb0ef41Sopenharmony_ci        this[EMIT]('nullBlock')
1611cb0ef41Sopenharmony_ci      }
1621cb0ef41Sopenharmony_ci    } else {
1631cb0ef41Sopenharmony_ci      this[SAW_NULL_BLOCK] = false
1641cb0ef41Sopenharmony_ci      if (!header.cksumValid) {
1651cb0ef41Sopenharmony_ci        this.warn('TAR_ENTRY_INVALID', 'checksum failure', { header })
1661cb0ef41Sopenharmony_ci      } else if (!header.path) {
1671cb0ef41Sopenharmony_ci        this.warn('TAR_ENTRY_INVALID', 'path is required', { header })
1681cb0ef41Sopenharmony_ci      } else {
1691cb0ef41Sopenharmony_ci        const type = header.type
1701cb0ef41Sopenharmony_ci        if (/^(Symbolic)?Link$/.test(type) && !header.linkpath) {
1711cb0ef41Sopenharmony_ci          this.warn('TAR_ENTRY_INVALID', 'linkpath required', { header })
1721cb0ef41Sopenharmony_ci        } else if (!/^(Symbolic)?Link$/.test(type) && header.linkpath) {
1731cb0ef41Sopenharmony_ci          this.warn('TAR_ENTRY_INVALID', 'linkpath forbidden', { header })
1741cb0ef41Sopenharmony_ci        } else {
1751cb0ef41Sopenharmony_ci          const entry = this[WRITEENTRY] = new Entry(header, this[EX], this[GEX])
1761cb0ef41Sopenharmony_ci
1771cb0ef41Sopenharmony_ci          // we do this for meta & ignored entries as well, because they
1781cb0ef41Sopenharmony_ci          // are still valid tar, or else we wouldn't know to ignore them
1791cb0ef41Sopenharmony_ci          if (!this[SAW_VALID_ENTRY]) {
1801cb0ef41Sopenharmony_ci            if (entry.remain) {
1811cb0ef41Sopenharmony_ci              // this might be the one!
1821cb0ef41Sopenharmony_ci              const onend = () => {
1831cb0ef41Sopenharmony_ci                if (!entry.invalid) {
1841cb0ef41Sopenharmony_ci                  this[SAW_VALID_ENTRY] = true
1851cb0ef41Sopenharmony_ci                }
1861cb0ef41Sopenharmony_ci              }
1871cb0ef41Sopenharmony_ci              entry.on('end', onend)
1881cb0ef41Sopenharmony_ci            } else {
1891cb0ef41Sopenharmony_ci              this[SAW_VALID_ENTRY] = true
1901cb0ef41Sopenharmony_ci            }
1911cb0ef41Sopenharmony_ci          }
1921cb0ef41Sopenharmony_ci
1931cb0ef41Sopenharmony_ci          if (entry.meta) {
1941cb0ef41Sopenharmony_ci            if (entry.size > this.maxMetaEntrySize) {
1951cb0ef41Sopenharmony_ci              entry.ignore = true
1961cb0ef41Sopenharmony_ci              this[EMIT]('ignoredEntry', entry)
1971cb0ef41Sopenharmony_ci              this[STATE] = 'ignore'
1981cb0ef41Sopenharmony_ci              entry.resume()
1991cb0ef41Sopenharmony_ci            } else if (entry.size > 0) {
2001cb0ef41Sopenharmony_ci              this[META] = ''
2011cb0ef41Sopenharmony_ci              entry.on('data', c => this[META] += c)
2021cb0ef41Sopenharmony_ci              this[STATE] = 'meta'
2031cb0ef41Sopenharmony_ci            }
2041cb0ef41Sopenharmony_ci          } else {
2051cb0ef41Sopenharmony_ci            this[EX] = null
2061cb0ef41Sopenharmony_ci            entry.ignore = entry.ignore || !this.filter(entry.path, entry)
2071cb0ef41Sopenharmony_ci
2081cb0ef41Sopenharmony_ci            if (entry.ignore) {
2091cb0ef41Sopenharmony_ci              // probably valid, just not something we care about
2101cb0ef41Sopenharmony_ci              this[EMIT]('ignoredEntry', entry)
2111cb0ef41Sopenharmony_ci              this[STATE] = entry.remain ? 'ignore' : 'header'
2121cb0ef41Sopenharmony_ci              entry.resume()
2131cb0ef41Sopenharmony_ci            } else {
2141cb0ef41Sopenharmony_ci              if (entry.remain) {
2151cb0ef41Sopenharmony_ci                this[STATE] = 'body'
2161cb0ef41Sopenharmony_ci              } else {
2171cb0ef41Sopenharmony_ci                this[STATE] = 'header'
2181cb0ef41Sopenharmony_ci                entry.end()
2191cb0ef41Sopenharmony_ci              }
2201cb0ef41Sopenharmony_ci
2211cb0ef41Sopenharmony_ci              if (!this[READENTRY]) {
2221cb0ef41Sopenharmony_ci                this[QUEUE].push(entry)
2231cb0ef41Sopenharmony_ci                this[NEXTENTRY]()
2241cb0ef41Sopenharmony_ci              } else {
2251cb0ef41Sopenharmony_ci                this[QUEUE].push(entry)
2261cb0ef41Sopenharmony_ci              }
2271cb0ef41Sopenharmony_ci            }
2281cb0ef41Sopenharmony_ci          }
2291cb0ef41Sopenharmony_ci        }
2301cb0ef41Sopenharmony_ci      }
2311cb0ef41Sopenharmony_ci    }
2321cb0ef41Sopenharmony_ci  }
2331cb0ef41Sopenharmony_ci
2341cb0ef41Sopenharmony_ci  [CLOSESTREAM] () {
2351cb0ef41Sopenharmony_ci    nextTick(() => this.emit('close'))
2361cb0ef41Sopenharmony_ci  }
2371cb0ef41Sopenharmony_ci
2381cb0ef41Sopenharmony_ci  [PROCESSENTRY] (entry) {
2391cb0ef41Sopenharmony_ci    let go = true
2401cb0ef41Sopenharmony_ci
2411cb0ef41Sopenharmony_ci    if (!entry) {
2421cb0ef41Sopenharmony_ci      this[READENTRY] = null
2431cb0ef41Sopenharmony_ci      go = false
2441cb0ef41Sopenharmony_ci    } else if (Array.isArray(entry)) {
2451cb0ef41Sopenharmony_ci      this.emit.apply(this, entry)
2461cb0ef41Sopenharmony_ci    } else {
2471cb0ef41Sopenharmony_ci      this[READENTRY] = entry
2481cb0ef41Sopenharmony_ci      this.emit('entry', entry)
2491cb0ef41Sopenharmony_ci      if (!entry.emittedEnd) {
2501cb0ef41Sopenharmony_ci        entry.on('end', _ => this[NEXTENTRY]())
2511cb0ef41Sopenharmony_ci        go = false
2521cb0ef41Sopenharmony_ci      }
2531cb0ef41Sopenharmony_ci    }
2541cb0ef41Sopenharmony_ci
2551cb0ef41Sopenharmony_ci    return go
2561cb0ef41Sopenharmony_ci  }
2571cb0ef41Sopenharmony_ci
2581cb0ef41Sopenharmony_ci  [NEXTENTRY] () {
2591cb0ef41Sopenharmony_ci    do {} while (this[PROCESSENTRY](this[QUEUE].shift()))
2601cb0ef41Sopenharmony_ci
2611cb0ef41Sopenharmony_ci    if (!this[QUEUE].length) {
2621cb0ef41Sopenharmony_ci      // At this point, there's nothing in the queue, but we may have an
2631cb0ef41Sopenharmony_ci      // entry which is being consumed (readEntry).
2641cb0ef41Sopenharmony_ci      // If we don't, then we definitely can handle more data.
2651cb0ef41Sopenharmony_ci      // If we do, and either it's flowing, or it has never had any data
2661cb0ef41Sopenharmony_ci      // written to it, then it needs more.
2671cb0ef41Sopenharmony_ci      // The only other possibility is that it has returned false from a
2681cb0ef41Sopenharmony_ci      // write() call, so we wait for the next drain to continue.
2691cb0ef41Sopenharmony_ci      const re = this[READENTRY]
2701cb0ef41Sopenharmony_ci      const drainNow = !re || re.flowing || re.size === re.remain
2711cb0ef41Sopenharmony_ci      if (drainNow) {
2721cb0ef41Sopenharmony_ci        if (!this[WRITING]) {
2731cb0ef41Sopenharmony_ci          this.emit('drain')
2741cb0ef41Sopenharmony_ci        }
2751cb0ef41Sopenharmony_ci      } else {
2761cb0ef41Sopenharmony_ci        re.once('drain', _ => this.emit('drain'))
2771cb0ef41Sopenharmony_ci      }
2781cb0ef41Sopenharmony_ci    }
2791cb0ef41Sopenharmony_ci  }
2801cb0ef41Sopenharmony_ci
2811cb0ef41Sopenharmony_ci  [CONSUMEBODY] (chunk, position) {
2821cb0ef41Sopenharmony_ci    // write up to but no  more than writeEntry.blockRemain
2831cb0ef41Sopenharmony_ci    const entry = this[WRITEENTRY]
2841cb0ef41Sopenharmony_ci    const br = entry.blockRemain
2851cb0ef41Sopenharmony_ci    const c = (br >= chunk.length && position === 0) ? chunk
2861cb0ef41Sopenharmony_ci      : chunk.slice(position, position + br)
2871cb0ef41Sopenharmony_ci
2881cb0ef41Sopenharmony_ci    entry.write(c)
2891cb0ef41Sopenharmony_ci
2901cb0ef41Sopenharmony_ci    if (!entry.blockRemain) {
2911cb0ef41Sopenharmony_ci      this[STATE] = 'header'
2921cb0ef41Sopenharmony_ci      this[WRITEENTRY] = null
2931cb0ef41Sopenharmony_ci      entry.end()
2941cb0ef41Sopenharmony_ci    }
2951cb0ef41Sopenharmony_ci
2961cb0ef41Sopenharmony_ci    return c.length
2971cb0ef41Sopenharmony_ci  }
2981cb0ef41Sopenharmony_ci
2991cb0ef41Sopenharmony_ci  [CONSUMEMETA] (chunk, position) {
3001cb0ef41Sopenharmony_ci    const entry = this[WRITEENTRY]
3011cb0ef41Sopenharmony_ci    const ret = this[CONSUMEBODY](chunk, position)
3021cb0ef41Sopenharmony_ci
3031cb0ef41Sopenharmony_ci    // if we finished, then the entry is reset
3041cb0ef41Sopenharmony_ci    if (!this[WRITEENTRY]) {
3051cb0ef41Sopenharmony_ci      this[EMITMETA](entry)
3061cb0ef41Sopenharmony_ci    }
3071cb0ef41Sopenharmony_ci
3081cb0ef41Sopenharmony_ci    return ret
3091cb0ef41Sopenharmony_ci  }
3101cb0ef41Sopenharmony_ci
3111cb0ef41Sopenharmony_ci  [EMIT] (ev, data, extra) {
3121cb0ef41Sopenharmony_ci    if (!this[QUEUE].length && !this[READENTRY]) {
3131cb0ef41Sopenharmony_ci      this.emit(ev, data, extra)
3141cb0ef41Sopenharmony_ci    } else {
3151cb0ef41Sopenharmony_ci      this[QUEUE].push([ev, data, extra])
3161cb0ef41Sopenharmony_ci    }
3171cb0ef41Sopenharmony_ci  }
3181cb0ef41Sopenharmony_ci
3191cb0ef41Sopenharmony_ci  [EMITMETA] (entry) {
3201cb0ef41Sopenharmony_ci    this[EMIT]('meta', this[META])
3211cb0ef41Sopenharmony_ci    switch (entry.type) {
3221cb0ef41Sopenharmony_ci      case 'ExtendedHeader':
3231cb0ef41Sopenharmony_ci      case 'OldExtendedHeader':
3241cb0ef41Sopenharmony_ci        this[EX] = Pax.parse(this[META], this[EX], false)
3251cb0ef41Sopenharmony_ci        break
3261cb0ef41Sopenharmony_ci
3271cb0ef41Sopenharmony_ci      case 'GlobalExtendedHeader':
3281cb0ef41Sopenharmony_ci        this[GEX] = Pax.parse(this[META], this[GEX], true)
3291cb0ef41Sopenharmony_ci        break
3301cb0ef41Sopenharmony_ci
3311cb0ef41Sopenharmony_ci      case 'NextFileHasLongPath':
3321cb0ef41Sopenharmony_ci      case 'OldGnuLongPath':
3331cb0ef41Sopenharmony_ci        this[EX] = this[EX] || Object.create(null)
3341cb0ef41Sopenharmony_ci        this[EX].path = this[META].replace(/\0.*/, '')
3351cb0ef41Sopenharmony_ci        break
3361cb0ef41Sopenharmony_ci
3371cb0ef41Sopenharmony_ci      case 'NextFileHasLongLinkpath':
3381cb0ef41Sopenharmony_ci        this[EX] = this[EX] || Object.create(null)
3391cb0ef41Sopenharmony_ci        this[EX].linkpath = this[META].replace(/\0.*/, '')
3401cb0ef41Sopenharmony_ci        break
3411cb0ef41Sopenharmony_ci
3421cb0ef41Sopenharmony_ci      /* istanbul ignore next */
3431cb0ef41Sopenharmony_ci      default: throw new Error('unknown meta: ' + entry.type)
3441cb0ef41Sopenharmony_ci    }
3451cb0ef41Sopenharmony_ci  }
3461cb0ef41Sopenharmony_ci
3471cb0ef41Sopenharmony_ci  abort (error) {
3481cb0ef41Sopenharmony_ci    this[ABORTED] = true
3491cb0ef41Sopenharmony_ci    this.emit('abort', error)
3501cb0ef41Sopenharmony_ci    // always throws, even in non-strict mode
3511cb0ef41Sopenharmony_ci    this.warn('TAR_ABORT', error, { recoverable: false })
3521cb0ef41Sopenharmony_ci  }
3531cb0ef41Sopenharmony_ci
3541cb0ef41Sopenharmony_ci  write (chunk) {
3551cb0ef41Sopenharmony_ci    if (this[ABORTED]) {
3561cb0ef41Sopenharmony_ci      return
3571cb0ef41Sopenharmony_ci    }
3581cb0ef41Sopenharmony_ci
3591cb0ef41Sopenharmony_ci    // first write, might be gzipped
3601cb0ef41Sopenharmony_ci    const needSniff = this[UNZIP] === null ||
3611cb0ef41Sopenharmony_ci      this.brotli === undefined && this[UNZIP] === false
3621cb0ef41Sopenharmony_ci    if (needSniff && chunk) {
3631cb0ef41Sopenharmony_ci      if (this[BUFFER]) {
3641cb0ef41Sopenharmony_ci        chunk = Buffer.concat([this[BUFFER], chunk])
3651cb0ef41Sopenharmony_ci        this[BUFFER] = null
3661cb0ef41Sopenharmony_ci      }
3671cb0ef41Sopenharmony_ci      if (chunk.length < gzipHeader.length) {
3681cb0ef41Sopenharmony_ci        this[BUFFER] = chunk
3691cb0ef41Sopenharmony_ci        return true
3701cb0ef41Sopenharmony_ci      }
3711cb0ef41Sopenharmony_ci
3721cb0ef41Sopenharmony_ci      // look for gzip header
3731cb0ef41Sopenharmony_ci      for (let i = 0; this[UNZIP] === null && i < gzipHeader.length; i++) {
3741cb0ef41Sopenharmony_ci        if (chunk[i] !== gzipHeader[i]) {
3751cb0ef41Sopenharmony_ci          this[UNZIP] = false
3761cb0ef41Sopenharmony_ci        }
3771cb0ef41Sopenharmony_ci      }
3781cb0ef41Sopenharmony_ci
3791cb0ef41Sopenharmony_ci      const maybeBrotli = this.brotli === undefined
3801cb0ef41Sopenharmony_ci      if (this[UNZIP] === false && maybeBrotli) {
3811cb0ef41Sopenharmony_ci        // read the first header to see if it's a valid tar file. If so,
3821cb0ef41Sopenharmony_ci        // we can safely assume that it's not actually brotli, despite the
3831cb0ef41Sopenharmony_ci        // .tbr or .tar.br file extension.
3841cb0ef41Sopenharmony_ci        // if we ended before getting a full chunk, yes, def brotli
3851cb0ef41Sopenharmony_ci        if (chunk.length < 512) {
3861cb0ef41Sopenharmony_ci          if (this[ENDED]) {
3871cb0ef41Sopenharmony_ci            this.brotli = true
3881cb0ef41Sopenharmony_ci          } else {
3891cb0ef41Sopenharmony_ci            this[BUFFER] = chunk
3901cb0ef41Sopenharmony_ci            return true
3911cb0ef41Sopenharmony_ci          }
3921cb0ef41Sopenharmony_ci        } else {
3931cb0ef41Sopenharmony_ci          // if it's tar, it's pretty reliably not brotli, chances of
3941cb0ef41Sopenharmony_ci          // that happening are astronomical.
3951cb0ef41Sopenharmony_ci          try {
3961cb0ef41Sopenharmony_ci            new Header(chunk.slice(0, 512))
3971cb0ef41Sopenharmony_ci            this.brotli = false
3981cb0ef41Sopenharmony_ci          } catch (_) {
3991cb0ef41Sopenharmony_ci            this.brotli = true
4001cb0ef41Sopenharmony_ci          }
4011cb0ef41Sopenharmony_ci        }
4021cb0ef41Sopenharmony_ci      }
4031cb0ef41Sopenharmony_ci
4041cb0ef41Sopenharmony_ci      if (this[UNZIP] === null || (this[UNZIP] === false && this.brotli)) {
4051cb0ef41Sopenharmony_ci        const ended = this[ENDED]
4061cb0ef41Sopenharmony_ci        this[ENDED] = false
4071cb0ef41Sopenharmony_ci        this[UNZIP] = this[UNZIP] === null
4081cb0ef41Sopenharmony_ci          ? new zlib.Unzip()
4091cb0ef41Sopenharmony_ci          : new zlib.BrotliDecompress()
4101cb0ef41Sopenharmony_ci        this[UNZIP].on('data', chunk => this[CONSUMECHUNK](chunk))
4111cb0ef41Sopenharmony_ci        this[UNZIP].on('error', er => this.abort(er))
4121cb0ef41Sopenharmony_ci        this[UNZIP].on('end', _ => {
4131cb0ef41Sopenharmony_ci          this[ENDED] = true
4141cb0ef41Sopenharmony_ci          this[CONSUMECHUNK]()
4151cb0ef41Sopenharmony_ci        })
4161cb0ef41Sopenharmony_ci        this[WRITING] = true
4171cb0ef41Sopenharmony_ci        const ret = this[UNZIP][ended ? 'end' : 'write'](chunk)
4181cb0ef41Sopenharmony_ci        this[WRITING] = false
4191cb0ef41Sopenharmony_ci        return ret
4201cb0ef41Sopenharmony_ci      }
4211cb0ef41Sopenharmony_ci    }
4221cb0ef41Sopenharmony_ci
4231cb0ef41Sopenharmony_ci    this[WRITING] = true
4241cb0ef41Sopenharmony_ci    if (this[UNZIP]) {
4251cb0ef41Sopenharmony_ci      this[UNZIP].write(chunk)
4261cb0ef41Sopenharmony_ci    } else {
4271cb0ef41Sopenharmony_ci      this[CONSUMECHUNK](chunk)
4281cb0ef41Sopenharmony_ci    }
4291cb0ef41Sopenharmony_ci    this[WRITING] = false
4301cb0ef41Sopenharmony_ci
4311cb0ef41Sopenharmony_ci    // return false if there's a queue, or if the current entry isn't flowing
4321cb0ef41Sopenharmony_ci    const ret =
4331cb0ef41Sopenharmony_ci      this[QUEUE].length ? false :
4341cb0ef41Sopenharmony_ci      this[READENTRY] ? this[READENTRY].flowing :
4351cb0ef41Sopenharmony_ci      true
4361cb0ef41Sopenharmony_ci
4371cb0ef41Sopenharmony_ci    // if we have no queue, then that means a clogged READENTRY
4381cb0ef41Sopenharmony_ci    if (!ret && !this[QUEUE].length) {
4391cb0ef41Sopenharmony_ci      this[READENTRY].once('drain', _ => this.emit('drain'))
4401cb0ef41Sopenharmony_ci    }
4411cb0ef41Sopenharmony_ci
4421cb0ef41Sopenharmony_ci    return ret
4431cb0ef41Sopenharmony_ci  }
4441cb0ef41Sopenharmony_ci
4451cb0ef41Sopenharmony_ci  [BUFFERCONCAT] (c) {
4461cb0ef41Sopenharmony_ci    if (c && !this[ABORTED]) {
4471cb0ef41Sopenharmony_ci      this[BUFFER] = this[BUFFER] ? Buffer.concat([this[BUFFER], c]) : c
4481cb0ef41Sopenharmony_ci    }
4491cb0ef41Sopenharmony_ci  }
4501cb0ef41Sopenharmony_ci
4511cb0ef41Sopenharmony_ci  [MAYBEEND] () {
4521cb0ef41Sopenharmony_ci    if (this[ENDED] &&
4531cb0ef41Sopenharmony_ci        !this[EMITTEDEND] &&
4541cb0ef41Sopenharmony_ci        !this[ABORTED] &&
4551cb0ef41Sopenharmony_ci        !this[CONSUMING]) {
4561cb0ef41Sopenharmony_ci      this[EMITTEDEND] = true
4571cb0ef41Sopenharmony_ci      const entry = this[WRITEENTRY]
4581cb0ef41Sopenharmony_ci      if (entry && entry.blockRemain) {
4591cb0ef41Sopenharmony_ci        // truncated, likely a damaged file
4601cb0ef41Sopenharmony_ci        const have = this[BUFFER] ? this[BUFFER].length : 0
4611cb0ef41Sopenharmony_ci        this.warn('TAR_BAD_ARCHIVE', `Truncated input (needed ${
4621cb0ef41Sopenharmony_ci          entry.blockRemain} more bytes, only ${have} available)`, { entry })
4631cb0ef41Sopenharmony_ci        if (this[BUFFER]) {
4641cb0ef41Sopenharmony_ci          entry.write(this[BUFFER])
4651cb0ef41Sopenharmony_ci        }
4661cb0ef41Sopenharmony_ci        entry.end()
4671cb0ef41Sopenharmony_ci      }
4681cb0ef41Sopenharmony_ci      this[EMIT](DONE)
4691cb0ef41Sopenharmony_ci    }
4701cb0ef41Sopenharmony_ci  }
4711cb0ef41Sopenharmony_ci
4721cb0ef41Sopenharmony_ci  [CONSUMECHUNK] (chunk) {
4731cb0ef41Sopenharmony_ci    if (this[CONSUMING]) {
4741cb0ef41Sopenharmony_ci      this[BUFFERCONCAT](chunk)
4751cb0ef41Sopenharmony_ci    } else if (!chunk && !this[BUFFER]) {
4761cb0ef41Sopenharmony_ci      this[MAYBEEND]()
4771cb0ef41Sopenharmony_ci    } else {
4781cb0ef41Sopenharmony_ci      this[CONSUMING] = true
4791cb0ef41Sopenharmony_ci      if (this[BUFFER]) {
4801cb0ef41Sopenharmony_ci        this[BUFFERCONCAT](chunk)
4811cb0ef41Sopenharmony_ci        const c = this[BUFFER]
4821cb0ef41Sopenharmony_ci        this[BUFFER] = null
4831cb0ef41Sopenharmony_ci        this[CONSUMECHUNKSUB](c)
4841cb0ef41Sopenharmony_ci      } else {
4851cb0ef41Sopenharmony_ci        this[CONSUMECHUNKSUB](chunk)
4861cb0ef41Sopenharmony_ci      }
4871cb0ef41Sopenharmony_ci
4881cb0ef41Sopenharmony_ci      while (this[BUFFER] &&
4891cb0ef41Sopenharmony_ci          this[BUFFER].length >= 512 &&
4901cb0ef41Sopenharmony_ci          !this[ABORTED] &&
4911cb0ef41Sopenharmony_ci          !this[SAW_EOF]) {
4921cb0ef41Sopenharmony_ci        const c = this[BUFFER]
4931cb0ef41Sopenharmony_ci        this[BUFFER] = null
4941cb0ef41Sopenharmony_ci        this[CONSUMECHUNKSUB](c)
4951cb0ef41Sopenharmony_ci      }
4961cb0ef41Sopenharmony_ci      this[CONSUMING] = false
4971cb0ef41Sopenharmony_ci    }
4981cb0ef41Sopenharmony_ci
4991cb0ef41Sopenharmony_ci    if (!this[BUFFER] || this[ENDED]) {
5001cb0ef41Sopenharmony_ci      this[MAYBEEND]()
5011cb0ef41Sopenharmony_ci    }
5021cb0ef41Sopenharmony_ci  }
5031cb0ef41Sopenharmony_ci
5041cb0ef41Sopenharmony_ci  [CONSUMECHUNKSUB] (chunk) {
5051cb0ef41Sopenharmony_ci    // we know that we are in CONSUMING mode, so anything written goes into
5061cb0ef41Sopenharmony_ci    // the buffer.  Advance the position and put any remainder in the buffer.
5071cb0ef41Sopenharmony_ci    let position = 0
5081cb0ef41Sopenharmony_ci    const length = chunk.length
5091cb0ef41Sopenharmony_ci    while (position + 512 <= length && !this[ABORTED] && !this[SAW_EOF]) {
5101cb0ef41Sopenharmony_ci      switch (this[STATE]) {
5111cb0ef41Sopenharmony_ci        case 'begin':
5121cb0ef41Sopenharmony_ci        case 'header':
5131cb0ef41Sopenharmony_ci          this[CONSUMEHEADER](chunk, position)
5141cb0ef41Sopenharmony_ci          position += 512
5151cb0ef41Sopenharmony_ci          break
5161cb0ef41Sopenharmony_ci
5171cb0ef41Sopenharmony_ci        case 'ignore':
5181cb0ef41Sopenharmony_ci        case 'body':
5191cb0ef41Sopenharmony_ci          position += this[CONSUMEBODY](chunk, position)
5201cb0ef41Sopenharmony_ci          break
5211cb0ef41Sopenharmony_ci
5221cb0ef41Sopenharmony_ci        case 'meta':
5231cb0ef41Sopenharmony_ci          position += this[CONSUMEMETA](chunk, position)
5241cb0ef41Sopenharmony_ci          break
5251cb0ef41Sopenharmony_ci
5261cb0ef41Sopenharmony_ci        /* istanbul ignore next */
5271cb0ef41Sopenharmony_ci        default:
5281cb0ef41Sopenharmony_ci          throw new Error('invalid state: ' + this[STATE])
5291cb0ef41Sopenharmony_ci      }
5301cb0ef41Sopenharmony_ci    }
5311cb0ef41Sopenharmony_ci
5321cb0ef41Sopenharmony_ci    if (position < length) {
5331cb0ef41Sopenharmony_ci      if (this[BUFFER]) {
5341cb0ef41Sopenharmony_ci        this[BUFFER] = Buffer.concat([chunk.slice(position), this[BUFFER]])
5351cb0ef41Sopenharmony_ci      } else {
5361cb0ef41Sopenharmony_ci        this[BUFFER] = chunk.slice(position)
5371cb0ef41Sopenharmony_ci      }
5381cb0ef41Sopenharmony_ci    }
5391cb0ef41Sopenharmony_ci  }
5401cb0ef41Sopenharmony_ci
5411cb0ef41Sopenharmony_ci  end (chunk) {
5421cb0ef41Sopenharmony_ci    if (!this[ABORTED]) {
5431cb0ef41Sopenharmony_ci      if (this[UNZIP]) {
5441cb0ef41Sopenharmony_ci        this[UNZIP].end(chunk)
5451cb0ef41Sopenharmony_ci      } else {
5461cb0ef41Sopenharmony_ci        this[ENDED] = true
5471cb0ef41Sopenharmony_ci        if (this.brotli === undefined) chunk = chunk || Buffer.alloc(0)
5481cb0ef41Sopenharmony_ci        this.write(chunk)
5491cb0ef41Sopenharmony_ci      }
5501cb0ef41Sopenharmony_ci    }
5511cb0ef41Sopenharmony_ci  }
5521cb0ef41Sopenharmony_ci})
553