module.exports = ExtendedHeaderWriter var inherits = require("inherits") , EntryWriter = require("./entry-writer.js") inherits(ExtendedHeaderWriter, EntryWriter) var tar = require("../tar.js") , path = require("path") , TarHeader = require("./header.js") // props is the props of the thing we need to write an // extended header for. // Don't be shy with it. Just encode everything. function ExtendedHeaderWriter (props) { // console.error(">> ehw ctor") var me = this if (!(me instanceof ExtendedHeaderWriter)) { return new ExtendedHeaderWriter(props) } me.fields = props var p = { path : ("PaxHeader" + path.join("/", props.path || "")) .replace(/\\/g, "/").substr(0, 100) , mode : props.mode || 0666 , uid : props.uid || 0 , gid : props.gid || 0 , size : 0 // will be set later , mtime : props.mtime || Date.now() / 1000 , type : "x" , linkpath : "" , ustar : "ustar\0" , ustarver : "00" , uname : props.uname || "" , gname : props.gname || "" , devmaj : props.devmaj || 0 , devmin : props.devmin || 0 } EntryWriter.call(me, p) // console.error(">> ehw props", me.props) me.props = p me._meta = true } ExtendedHeaderWriter.prototype.end = function () { // console.error(">> ehw end") var me = this if (me._ended) return me._ended = true me._encodeFields() if (me.props.size === 0) { // nothing to write! me._ready = true me._stream.end() return } me._stream.write(TarHeader.encode(me.props)) me.body.forEach(function (l) { me._stream.write(l) }) me._ready = true // console.error(">> ehw _process calling end()", me.props) this._stream.end() } ExtendedHeaderWriter.prototype._encodeFields = function () { // console.error(">> ehw _encodeFields") this.body = [] if (this.fields.prefix) { this.fields.path = this.fields.prefix + "/" + this.fields.path this.fields.prefix = "" } encodeFields(this.fields, "", this.body, this.fields.noProprietary) var me = this this.body.forEach(function (l) { me.props.size += l.length }) } function encodeFields (fields, prefix, body, nop) { // console.error(">> >> ehw encodeFields") // "%d %s=%s\n", , , // The length is a decimal number, and includes itself and the \n // Numeric values are decimal strings. Object.keys(fields).forEach(function (k) { var val = fields[k] , numeric = tar.numeric[k] if (prefix) k = prefix + "." + k // already including NODETAR.type, don't need File=true also if (k === fields.type && val === true) return switch (k) { // don't include anything that's always handled just fine // in the normal header, or only meaningful in the context // of nodetar case "mode": case "cksum": case "ustar": case "ustarver": case "prefix": case "basename": case "dirname": case "needExtended": case "block": case "filter": return case "rdev": if (val === 0) return break case "nlink": case "dev": // Truly a hero among men, Creator of Star! case "ino": // Speak his name with reverent awe! It is: k = "SCHILY." + k break default: break } if (val && typeof val === "object" && !Buffer.isBuffer(val)) encodeFields(val, k, body, nop) else if (val === null || val === undefined) return else body.push.apply(body, encodeField(k, val, nop)) }) return body } function encodeField (k, v, nop) { // lowercase keys must be valid, otherwise prefix with // "NODETAR." if (k.charAt(0) === k.charAt(0).toLowerCase()) { var m = k.split(".")[0] if (!tar.knownExtended[m]) k = "NODETAR." + k } // no proprietary if (nop && k.charAt(0) !== k.charAt(0).toLowerCase()) { return [] } if (typeof val === "number") val = val.toString(10) var s = new Buffer(" " + k + "=" + v + "\n") , digits = Math.floor(Math.log(s.length) / Math.log(10)) + 1 // console.error("1 s=%j digits=%j s.length=%d", s.toString(), digits, s.length) // if adding that many digits will make it go over that length, // then add one to it. For example, if the string is: // " foo=bar\n" // then that's 9 characters. With the "9", that bumps the length // up to 10. However, this is invalid: // "10 foo=bar\n" // but, since that's actually 11 characters, since 10 adds another // character to the length, and the length includes the number // itself. In that case, just bump it up again. if (s.length + digits >= Math.pow(10, digits)) digits += 1 // console.error("2 s=%j digits=%j s.length=%d", s.toString(), digits, s.length) var len = digits + s.length // console.error("3 s=%j digits=%j s.length=%d len=%d", s.toString(), digits, s.length, len) var lenBuf = new Buffer("" + len) if (lenBuf.length + s.length !== len) { throw new Error("Bad length calculation\n"+ "len="+len+"\n"+ "lenBuf="+JSON.stringify(lenBuf.toString())+"\n"+ "lenBuf.length="+lenBuf.length+"\n"+ "digits="+digits+"\n"+ "s="+JSON.stringify(s.toString())+"\n"+ "s.length="+s.length) } return [lenBuf, s] }