blob: 87f4628ba840f2987f6c0395067e44ccb6350d34 [file] [log] [blame]
var ZEROS = '0000000000000000000'
var ZERO_OFFSET = '0'.charCodeAt(0)
var USTAR = 'ustar\x0000'
var clamp = function(index, len, defaultValue) {
if (typeof index !== 'number') return defaultValue
index = ~~index // Coerce to integer.
if (index >= len) return len
if (index >= 0) return index
index += len
if (index >= 0) return index
return 0
}
var toType = function(flag) {
switch (flag) {
case 0:
return 'file'
case 1:
return 'link'
case 2:
return 'symlink'
case 3:
return 'character-device'
case 4:
return 'block-device'
case 5:
return 'directory'
case 6:
return 'fifo'
case 7:
return 'contiguous-file'
case 72:
return 'pax-header'
case 55:
return 'pax-global-header'
}
return null
}
var toTypeflag = function(flag) {
switch (flag) {
case 'file':
return 0
case 'link':
return 1
case 'symlink':
return 2
case 'character-device':
return 3
case 'block-device':
return 4
case 'directory':
return 5
case 'fifo':
return 6
case 'contiguous-file':
return 7
case 'pax-header':
return 72
}
return 0
}
var alloc = function(size) {
var buf = new Buffer(size)
buf.fill(0)
return buf
}
var indexOf = function(block, num, offset, end) {
for (; offset < end; offset++) {
if (block[offset] === num) return offset
}
return end
}
var cksum = function(block) {
var sum = 8 * 32
for (var i = 0; i < 148; i++) sum += block[i]
for (var i = 156; i < 512; i++) sum += block[i]
return sum
}
var encodeOct = function(val, n) {
val = val.toString(8)
return ZEROS.slice(0, n-val.length)+val+' '
}
var decodeOct = function(val, offset) {
// Older versions of tar can prefix with spaces
while (offset < val.length && val[offset] === 32) offset++
return parseInt(val.slice(offset, clamp(indexOf(val, 32, offset, val.length), val.length, val.length)).toString(), 8)
}
var decodeStr = function(val, offset, length) {
return val.slice(offset, indexOf(val, 0, offset, offset+length)).toString();
}
var addLength = function(str) {
var len = Buffer.byteLength(str)
var digits = Math.floor(Math.log(len) / Math.log(10)) + 1
if (len + digits > Math.pow(10, digits)) digits++
return (len+digits)+str
}
exports.encodePax = function(opts) { // TODO: encode more stuff in pax
var result = ''
if (opts.name) result += addLength(' path='+opts.name+'\n')
if (opts.linkname) result += addLength(' linkpath='+opts.linkname+'\n')
return new Buffer(result)
}
exports.decodePax = function(buf) {
var result = {}
while (buf.length) {
var i = 0
while (i < buf.length && buf[i] !== 32) i++
var len = parseInt(buf.slice(0, i).toString())
if (!len) return result
var b = buf.slice(i+1, len-1).toString()
var keyIndex = b.indexOf('=')
if (keyIndex === -1) return result
result[b.slice(0, keyIndex)] = b.slice(keyIndex+1)
buf = buf.slice(len)
}
return result
}
exports.encode = function(opts) {
var buf = alloc(512)
var name = opts.name
var prefix = ''
if (opts.typeflag === 5 && name[name.length-1] !== '/') name += '/'
if (Buffer.byteLength(name) !== name.length) return null // utf-8
while (Buffer.byteLength(name) > 100) {
var i = name.indexOf('/')
if (i === -1) return null
prefix += prefix ? '/' + name.slice(0, i) : name.slice(0, i)
name = name.slice(i+1)
}
if (Buffer.byteLength(name) > 100 || Buffer.byteLength(prefix) > 155) return null
if (opts.linkname && Buffer.byteLength(opts.linkname) > 100) return null
buf.write(name)
buf.write(encodeOct(opts.mode & 07777, 6), 100)
buf.write(encodeOct(opts.uid, 6), 108)
buf.write(encodeOct(opts.gid, 6), 116)
buf.write(encodeOct(opts.size, 11), 124)
buf.write(encodeOct((opts.mtime.getTime() / 1000) | 0, 11), 136)
buf[156] = ZERO_OFFSET + toTypeflag(opts.type)
if (opts.linkname) buf.write(opts.linkname, 157)
buf.write(USTAR, 257)
if (opts.uname) buf.write(opts.uname, 265)
if (opts.gname) buf.write(opts.gname, 297)
buf.write(encodeOct(opts.devmajor || 0, 6), 329)
buf.write(encodeOct(opts.devminor || 0, 6), 337)
if (prefix) buf.write(prefix, 345)
buf.write(encodeOct(cksum(buf), 6), 148)
return buf
}
exports.decode = function(buf) {
var typeflag = buf[156] === 0 ? 0 : buf[156] - ZERO_OFFSET
var type = toType(typeflag)
var name = decodeStr(buf, 0, 100)
var mode = decodeOct(buf, 100)
var uid = decodeOct(buf, 108)
var gid = decodeOct(buf, 116)
var size = decodeOct(buf, 124)
var mtime = decodeOct(buf, 136)
var linkname = buf[157] === 0 ? null : decodeStr(buf, 157, 100)
var uname = decodeStr(buf, 265, 32)
var gname = decodeStr(buf, 297, 32)
var devmajor = decodeOct(buf, 329)
var devminor = decodeOct(buf, 337)
if (buf[345]) name = decodeStr(buf, 345, 155)+'/'+name
var c = cksum(buf)
//checksum is still initial value if header was null.
if (c === 8*32) return null
//valid checksum
if (c !== decodeOct(buf, 148)) throw new Error('invalid header')
return {
name: name,
mode: mode,
uid: uid,
gid: gid,
size: size,
mtime: new Date(1000 * mtime),
type: toType(typeflag),
linkname: linkname,
uname: uname,
gname: gname,
devmajor: devmajor,
devminor: devminor
}
}