| 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 |
| } |
| } |