|
|
|
// An Entry consisting of:
|
|
|
|
//
|
|
|
|
// "%d %s=%s\n", <length>, <keyword>, <value>
|
|
|
|
//
|
|
|
|
// The length is a decimal number, and includes itself and the \n
|
|
|
|
// \0 does not terminate anything. Only the length terminates the string.
|
|
|
|
// Numeric values are decimal strings.
|
|
|
|
|
|
|
|
module.exports = ExtendedHeader
|
|
|
|
|
|
|
|
var Entry = require("./entry.js")
|
|
|
|
, inherits = require("inherits")
|
|
|
|
, tar = require("../tar.js")
|
|
|
|
, numeric = tar.numeric
|
|
|
|
, keyTrans = { "SCHILY.dev": "dev"
|
|
|
|
, "SCHILY.ino": "ino"
|
|
|
|
, "SCHILY.nlink": "nlink" }
|
|
|
|
|
|
|
|
function ExtendedHeader () {
|
|
|
|
Entry.apply(this, arguments)
|
|
|
|
this.on("data", this._parse)
|
|
|
|
this.fields = {}
|
|
|
|
this._position = 0
|
|
|
|
this._fieldPos = 0
|
|
|
|
this._state = SIZE
|
|
|
|
this._sizeBuf = []
|
|
|
|
this._keyBuf = []
|
|
|
|
this._valBuf = []
|
|
|
|
this._size = -1
|
|
|
|
this._key = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
inherits(ExtendedHeader, Entry)
|
|
|
|
ExtendedHeader.prototype._parse = parse
|
|
|
|
|
|
|
|
var s = 0
|
|
|
|
, states = ExtendedHeader.states = {}
|
|
|
|
, SIZE = states.SIZE = s++
|
|
|
|
, KEY = states.KEY = s++
|
|
|
|
, VAL = states.VAL = s++
|
|
|
|
, ERR = states.ERR = s++
|
|
|
|
|
|
|
|
Object.keys(states).forEach(function (s) {
|
|
|
|
states[states[s]] = states[s]
|
|
|
|
})
|
|
|
|
|
|
|
|
states[s] = null
|
|
|
|
|
|
|
|
// char code values for comparison
|
|
|
|
var _0 = "0".charCodeAt(0)
|
|
|
|
, _9 = "9".charCodeAt(0)
|
|
|
|
, point = ".".charCodeAt(0)
|
|
|
|
, a = "a".charCodeAt(0)
|
|
|
|
, Z = "Z".charCodeAt(0)
|
|
|
|
, a = "a".charCodeAt(0)
|
|
|
|
, z = "z".charCodeAt(0)
|
|
|
|
, space = " ".charCodeAt(0)
|
|
|
|
, eq = "=".charCodeAt(0)
|
|
|
|
, cr = "\n".charCodeAt(0)
|
|
|
|
|
|
|
|
function parse (c) {
|
|
|
|
if (this._state === ERR) return
|
|
|
|
|
|
|
|
for ( var i = 0, l = c.length
|
|
|
|
; i < l
|
|
|
|
; this._position++, this._fieldPos++, i++) {
|
|
|
|
// console.error("top of loop, size="+this._size)
|
|
|
|
|
|
|
|
var b = c[i]
|
|
|
|
|
|
|
|
if (this._size >= 0 && this._fieldPos > this._size) {
|
|
|
|
error(this, "field exceeds length="+this._size)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (this._state) {
|
|
|
|
case ERR: return
|
|
|
|
|
|
|
|
case SIZE:
|
|
|
|
// console.error("parsing size, b=%d, rest=%j", b, c.slice(i).toString())
|
|
|
|
if (b === space) {
|
|
|
|
this._state = KEY
|
|
|
|
// this._fieldPos = this._sizeBuf.length
|
|
|
|
this._size = parseInt(new Buffer(this._sizeBuf).toString(), 10)
|
|
|
|
this._sizeBuf.length = 0
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if (b < _0 || b > _9) {
|
|
|
|
error(this, "expected [" + _0 + ".." + _9 + "], got " + b)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
this._sizeBuf.push(b)
|
|
|
|
continue
|
|
|
|
|
|
|
|
case KEY:
|
|
|
|
// can be any char except =, not > size.
|
|
|
|
if (b === eq) {
|
|
|
|
this._state = VAL
|
|
|
|
this._key = new Buffer(this._keyBuf).toString()
|
|
|
|
if (keyTrans[this._key]) this._key = keyTrans[this._key]
|
|
|
|
this._keyBuf.length = 0
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
this._keyBuf.push(b)
|
|
|
|
continue
|
|
|
|
|
|
|
|
case VAL:
|
|
|
|
// field must end with cr
|
|
|
|
if (this._fieldPos === this._size - 1) {
|
|
|
|
// console.error("finished with "+this._key)
|
|
|
|
if (b !== cr) {
|
|
|
|
error(this, "expected \\n at end of field")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var val = new Buffer(this._valBuf).toString()
|
|
|
|
if (numeric[this._key]) {
|
|
|
|
val = parseFloat(val)
|
|
|
|
}
|
|
|
|
this.fields[this._key] = val
|
|
|
|
|
|
|
|
this._valBuf.length = 0
|
|
|
|
this._state = SIZE
|
|
|
|
this._size = -1
|
|
|
|
this._fieldPos = -1
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
this._valBuf.push(b)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function error (me, msg) {
|
|
|
|
msg = "invalid header: " + msg
|
|
|
|
+ "\nposition=" + me._position
|
|
|
|
+ "\nfield position=" + me._fieldPos
|
|
|
|
|
|
|
|
me.error(msg)
|
|
|
|
me.state = ERR
|
|
|
|
}
|