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.
		
		
		
		
		
			
		
			
				
					
					
						
							151 lines
						
					
					
						
							4.0 KiB
						
					
					
				
			
		
		
	
	
							151 lines
						
					
					
						
							4.0 KiB
						
					
					
				'use strict'
 | 
						|
const Header = require('./header.js')
 | 
						|
const path = require('path')
 | 
						|
 | 
						|
class Pax {
 | 
						|
  constructor (obj, global) {
 | 
						|
    this.atime = obj.atime || null
 | 
						|
    this.charset = obj.charset || null
 | 
						|
    this.comment = obj.comment || null
 | 
						|
    this.ctime = obj.ctime || null
 | 
						|
    this.gid = obj.gid || null
 | 
						|
    this.gname = obj.gname || null
 | 
						|
    this.linkpath = obj.linkpath || null
 | 
						|
    this.mtime = obj.mtime || null
 | 
						|
    this.path = obj.path || null
 | 
						|
    this.size = obj.size || null
 | 
						|
    this.uid = obj.uid || null
 | 
						|
    this.uname = obj.uname || null
 | 
						|
    this.dev = obj.dev || null
 | 
						|
    this.ino = obj.ino || null
 | 
						|
    this.nlink = obj.nlink || null
 | 
						|
    this.global = global || false
 | 
						|
  }
 | 
						|
 | 
						|
  encode () {
 | 
						|
    const body = this.encodeBody()
 | 
						|
    if (body === '') {
 | 
						|
      return null
 | 
						|
    }
 | 
						|
 | 
						|
    const bodyLen = Buffer.byteLength(body)
 | 
						|
    // round up to 512 bytes
 | 
						|
    // add 512 for header
 | 
						|
    const bufLen = 512 * Math.ceil(1 + bodyLen / 512)
 | 
						|
    const buf = Buffer.allocUnsafe(bufLen)
 | 
						|
 | 
						|
    // 0-fill the header section, it might not hit every field
 | 
						|
    for (let i = 0; i < 512; i++) {
 | 
						|
      buf[i] = 0
 | 
						|
    }
 | 
						|
 | 
						|
    new Header({
 | 
						|
      // XXX split the path
 | 
						|
      // then the path should be PaxHeader + basename, but less than 99,
 | 
						|
      // prepend with the dirname
 | 
						|
      path: ('PaxHeader/' + path.basename(this.path)).slice(0, 99),
 | 
						|
      mode: this.mode || 0o644,
 | 
						|
      uid: this.uid || null,
 | 
						|
      gid: this.gid || null,
 | 
						|
      size: bodyLen,
 | 
						|
      mtime: this.mtime || null,
 | 
						|
      type: this.global ? 'GlobalExtendedHeader' : 'ExtendedHeader',
 | 
						|
      linkpath: '',
 | 
						|
      uname: this.uname || '',
 | 
						|
      gname: this.gname || '',
 | 
						|
      devmaj: 0,
 | 
						|
      devmin: 0,
 | 
						|
      atime: this.atime || null,
 | 
						|
      ctime: this.ctime || null,
 | 
						|
    }).encode(buf)
 | 
						|
 | 
						|
    buf.write(body, 512, bodyLen, 'utf8')
 | 
						|
 | 
						|
    // null pad after the body
 | 
						|
    for (let i = bodyLen + 512; i < buf.length; i++) {
 | 
						|
      buf[i] = 0
 | 
						|
    }
 | 
						|
 | 
						|
    return buf
 | 
						|
  }
 | 
						|
 | 
						|
  encodeBody () {
 | 
						|
    return (
 | 
						|
      this.encodeField('path') +
 | 
						|
      this.encodeField('ctime') +
 | 
						|
      this.encodeField('atime') +
 | 
						|
      this.encodeField('dev') +
 | 
						|
      this.encodeField('ino') +
 | 
						|
      this.encodeField('nlink') +
 | 
						|
      this.encodeField('charset') +
 | 
						|
      this.encodeField('comment') +
 | 
						|
      this.encodeField('gid') +
 | 
						|
      this.encodeField('gname') +
 | 
						|
      this.encodeField('linkpath') +
 | 
						|
      this.encodeField('mtime') +
 | 
						|
      this.encodeField('size') +
 | 
						|
      this.encodeField('uid') +
 | 
						|
      this.encodeField('uname')
 | 
						|
    )
 | 
						|
  }
 | 
						|
 | 
						|
  encodeField (field) {
 | 
						|
    if (this[field] === null || this[field] === undefined) {
 | 
						|
      return ''
 | 
						|
    }
 | 
						|
    const v = this[field] instanceof Date ? this[field].getTime() / 1000
 | 
						|
      : this[field]
 | 
						|
    const s = ' ' +
 | 
						|
      (field === 'dev' || field === 'ino' || field === 'nlink'
 | 
						|
        ? 'SCHILY.' : '') +
 | 
						|
      field + '=' + v + '\n'
 | 
						|
    const byteLen = Buffer.byteLength(s)
 | 
						|
    // the digits includes the length of the digits in ascii base-10
 | 
						|
    // so if it's 9 characters, then adding 1 for the 9 makes it 10
 | 
						|
    // which makes it 11 chars.
 | 
						|
    let digits = Math.floor(Math.log(byteLen) / Math.log(10)) + 1
 | 
						|
    if (byteLen + digits >= Math.pow(10, digits)) {
 | 
						|
      digits += 1
 | 
						|
    }
 | 
						|
    const len = digits + byteLen
 | 
						|
    return len + s
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
Pax.parse = (string, ex, g) => new Pax(merge(parseKV(string), ex), g)
 | 
						|
 | 
						|
const merge = (a, b) =>
 | 
						|
  b ? Object.keys(a).reduce((s, k) => (s[k] = a[k], s), b) : a
 | 
						|
 | 
						|
const parseKV = string =>
 | 
						|
  string
 | 
						|
    .replace(/\n$/, '')
 | 
						|
    .split('\n')
 | 
						|
    .reduce(parseKVLine, Object.create(null))
 | 
						|
 | 
						|
const parseKVLine = (set, line) => {
 | 
						|
  const n = parseInt(line, 10)
 | 
						|
 | 
						|
  // XXX Values with \n in them will fail this.
 | 
						|
  // Refactor to not be a naive line-by-line parse.
 | 
						|
  if (n !== Buffer.byteLength(line) + 1) {
 | 
						|
    return set
 | 
						|
  }
 | 
						|
 | 
						|
  line = line.slice((n + ' ').length)
 | 
						|
  const kv = line.split('=')
 | 
						|
  const k = kv.shift().replace(/^SCHILY\.(dev|ino|nlink)/, '$1')
 | 
						|
  if (!k) {
 | 
						|
    return set
 | 
						|
  }
 | 
						|
 | 
						|
  const v = kv.join('=')
 | 
						|
  set[k] = /^([A-Z]+\.)?([mac]|birth|creation)time$/.test(k)
 | 
						|
    ? new Date(v * 1000)
 | 
						|
    : /^[0-9]+$/.test(v) ? +v
 | 
						|
    : v
 | 
						|
  return set
 | 
						|
}
 | 
						|
 | 
						|
module.exports = Pax
 |