| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385 | 
							- // parse a 512-byte header block to a data object, or vice-versa
 
- // If the data won't fit nicely in a simple header, then generate
 
- // the appropriate extended header file, and return that.
 
- module.exports = TarHeader
 
- var tar = require("../tar.js")
 
-   , fields = tar.fields
 
-   , fieldOffs = tar.fieldOffs
 
-   , fieldEnds = tar.fieldEnds
 
-   , fieldSize = tar.fieldSize
 
-   , numeric = tar.numeric
 
-   , assert = require("assert").ok
 
-   , space = " ".charCodeAt(0)
 
-   , slash = "/".charCodeAt(0)
 
-   , bslash = process.platform === "win32" ? "\\".charCodeAt(0) : null
 
- function TarHeader (block) {
 
-   if (!(this instanceof TarHeader)) return new TarHeader(block)
 
-   if (block) this.decode(block)
 
- }
 
- TarHeader.prototype =
 
-   { decode : decode
 
-   , encode: encode
 
-   , calcSum: calcSum
 
-   , checkSum: checkSum
 
-   }
 
- TarHeader.parseNumeric = parseNumeric
 
- TarHeader.encode = encode
 
- TarHeader.decode = decode
 
- // note that this will only do the normal ustar header, not any kind
 
- // of extended posix header file.  If something doesn't fit comfortably,
 
- // then it will set obj.needExtended = true, and set the block to
 
- // the closest approximation.
 
- function encode (obj) {
 
-   if (!obj && !(this instanceof TarHeader)) throw new Error(
 
-     "encode must be called on a TarHeader, or supplied an object")
 
-   obj = obj || this
 
-   var block = obj.block = new Buffer(512)
 
-   // if the object has a "prefix", then that's actually an extension of
 
-   // the path field.
 
-   if (obj.prefix) {
 
-     // console.error("%% header encoding, got a prefix", obj.prefix)
 
-     obj.path = obj.prefix + "/" + obj.path
 
-     // console.error("%% header encoding, prefixed path", obj.path)
 
-     obj.prefix = ""
 
-   }
 
-   obj.needExtended = false
 
-   if (obj.mode) {
 
-     if (typeof obj.mode === "string") obj.mode = parseInt(obj.mode, 8)
 
-     obj.mode = obj.mode & 0777
 
-   }
 
-   for (var f = 0; fields[f] !== null; f ++) {
 
-     var field = fields[f]
 
-       , off = fieldOffs[f]
 
-       , end = fieldEnds[f]
 
-       , ret
 
-     switch (field) {
 
-       case "cksum":
 
-         // special, done below, after all the others
 
-         break
 
-       case "prefix":
 
-         // special, this is an extension of the "path" field.
 
-         // console.error("%% header encoding, skip prefix later")
 
-         break
 
-       case "type":
 
-         // convert from long name to a single char.
 
-         var type = obj.type || "0"
 
-         if (type.length > 1) {
 
-           type = tar.types[obj.type]
 
-           if (!type) type = "0"
 
-         }
 
-         writeText(block, off, end, type)
 
-         break
 
-       case "path":
 
-         // uses the "prefix" field if > 100 bytes, but <= 255
 
-         var pathLen = Buffer.byteLength(obj.path)
 
-           , pathFSize = fieldSize[fields.path]
 
-           , prefFSize = fieldSize[fields.prefix]
 
-         // paths between 100 and 255 should use the prefix field.
 
-         // longer than 255
 
-         if (pathLen > pathFSize &&
 
-             pathLen <= pathFSize + prefFSize) {
 
-           // need to find a slash somewhere in the middle so that
 
-           // path and prefix both fit in their respective fields
 
-           var searchStart = pathLen - 1 - pathFSize
 
-             , searchEnd = prefFSize
 
-             , found = false
 
-             , pathBuf = new Buffer(obj.path)
 
-           for ( var s = searchStart
 
-               ; (s <= searchEnd)
 
-               ; s ++ ) {
 
-             if (pathBuf[s] === slash || pathBuf[s] === bslash) {
 
-               found = s
 
-               break
 
-             }
 
-           }
 
-           if (found !== false) {
 
-             prefix = pathBuf.slice(0, found).toString("utf8")
 
-             path = pathBuf.slice(found + 1).toString("utf8")
 
-             ret = writeText(block, off, end, path)
 
-             off = fieldOffs[fields.prefix]
 
-             end = fieldEnds[fields.prefix]
 
-             // console.error("%% header writing prefix", off, end, prefix)
 
-             ret = writeText(block, off, end, prefix) || ret
 
-             break
 
-           }
 
-         }
 
-         // paths less than 100 chars don't need a prefix
 
-         // and paths longer than 255 need an extended header and will fail
 
-         // on old implementations no matter what we do here.
 
-         // Null out the prefix, and fallthrough to default.
 
-         // console.error("%% header writing no prefix")
 
-         var poff = fieldOffs[fields.prefix]
 
-           , pend = fieldEnds[fields.prefix]
 
-         writeText(block, poff, pend, "")
 
-         // fallthrough
 
-       // all other fields are numeric or text
 
-       default:
 
-         ret = numeric[field]
 
-             ? writeNumeric(block, off, end, obj[field])
 
-             : writeText(block, off, end, obj[field] || "")
 
-         break
 
-     }
 
-     obj.needExtended = obj.needExtended || ret
 
-   }
 
-   var off = fieldOffs[fields.cksum]
 
-     , end = fieldEnds[fields.cksum]
 
-   writeNumeric(block, off, end, calcSum.call(this, block))
 
-   return block
 
- }
 
- // if it's a negative number, or greater than will fit,
 
- // then use write256.
 
- var MAXNUM = { 12: 077777777777
 
-              , 11: 07777777777
 
-              , 8 : 07777777
 
-              , 7 : 0777777 }
 
- function writeNumeric (block, off, end, num) {
 
-   var writeLen = end - off
 
-     , maxNum = MAXNUM[writeLen] || 0
 
-   num = num || 0
 
-   // console.error("  numeric", num)
 
-   if (num instanceof Date ||
 
-       Object.prototype.toString.call(num) === "[object Date]") {
 
-     num = num.getTime() / 1000
 
-   }
 
-   if (num > maxNum || num < 0) {
 
-     write256(block, off, end, num)
 
-     // need an extended header if negative or too big.
 
-     return true
 
-   }
 
-   // god, tar is so annoying
 
-   // if the string is small enough, you should put a space
 
-   // between the octal string and the \0, but if it doesn't
 
-   // fit, then don't.
 
-   var numStr = Math.floor(num).toString(8)
 
-   if (num < MAXNUM[writeLen - 1]) numStr += " "
 
-   // pad with "0" chars
 
-   if (numStr.length < writeLen) {
 
-     numStr = (new Array(writeLen - numStr.length).join("0")) + numStr
 
-   }
 
-   if (numStr.length !== writeLen - 1) {
 
-     throw new Error("invalid length: " + JSON.stringify(numStr) + "\n" +
 
-                     "expected: "+writeLen)
 
-   }
 
-   block.write(numStr, off, writeLen, "utf8")
 
-   block[end - 1] = 0
 
- }
 
- function write256 (block, off, end, num) {
 
-   var buf = block.slice(off, end)
 
-   var positive = num >= 0
 
-   buf[0] = positive ? 0x80 : 0xFF
 
-   // get the number as a base-256 tuple
 
-   if (!positive) num *= -1
 
-   var tuple = []
 
-   do {
 
-     var n = num % 256
 
-     tuple.push(n)
 
-     num = (num - n) / 256
 
-   } while (num)
 
-   var bytes = tuple.length
 
-   var fill = buf.length - bytes
 
-   for (var i = 1; i < fill; i ++) {
 
-     buf[i] = positive ? 0 : 0xFF
 
-   }
 
-   // tuple is a base256 number, with [0] as the *least* significant byte
 
-   // if it's negative, then we need to flip all the bits once we hit the
 
-   // first non-zero bit.  The 2's-complement is (0x100 - n), and the 1's-
 
-   // complement is (0xFF - n).
 
-   var zero = true
 
-   for (i = bytes; i > 0; i --) {
 
-     var byte = tuple[bytes - i]
 
-     if (positive) buf[fill + i] = byte
 
-     else if (zero && byte === 0) buf[fill + i] = 0
 
-     else if (zero) {
 
-       zero = false
 
-       buf[fill + i] = 0x100 - byte
 
-     } else buf[fill + i] = 0xFF - byte
 
-   }
 
- }
 
- function writeText (block, off, end, str) {
 
-   // strings are written as utf8, then padded with \0
 
-   var strLen = Buffer.byteLength(str)
 
-     , writeLen = Math.min(strLen, end - off)
 
-     // non-ascii fields need extended headers
 
-     // long fields get truncated
 
-     , needExtended = strLen !== str.length || strLen > writeLen
 
-   // write the string, and null-pad
 
-   if (writeLen > 0) block.write(str, off, writeLen, "utf8")
 
-   for (var i = off + writeLen; i < end; i ++) block[i] = 0
 
-   return needExtended
 
- }
 
- function calcSum (block) {
 
-   block = block || this.block
 
-   assert(Buffer.isBuffer(block) && block.length === 512)
 
-   if (!block) throw new Error("Need block to checksum")
 
-   // now figure out what it would be if the cksum was "        "
 
-   var sum = 0
 
-     , start = fieldOffs[fields.cksum]
 
-     , end = fieldEnds[fields.cksum]
 
-   for (var i = 0; i < fieldOffs[fields.cksum]; i ++) {
 
-     sum += block[i]
 
-   }
 
-   for (var i = start; i < end; i ++) {
 
-     sum += space
 
-   }
 
-   for (var i = end; i < 512; i ++) {
 
-     sum += block[i]
 
-   }
 
-   return sum
 
- }
 
- function checkSum (block) {
 
-   var sum = calcSum.call(this, block)
 
-   block = block || this.block
 
-   var cksum = block.slice(fieldOffs[fields.cksum], fieldEnds[fields.cksum])
 
-   cksum = parseNumeric(cksum)
 
-   return cksum === sum
 
- }
 
- function decode (block) {
 
-   block = block || this.block
 
-   assert(Buffer.isBuffer(block) && block.length === 512)
 
-   this.block = block
 
-   this.cksumValid = this.checkSum()
 
-   var prefix = null
 
-   // slice off each field.
 
-   for (var f = 0; fields[f] !== null; f ++) {
 
-     var field = fields[f]
 
-       , val = block.slice(fieldOffs[f], fieldEnds[f])
 
-     switch (field) {
 
-       case "ustar":
 
-         // if not ustar, then everything after that is just padding.
 
-         if (val.toString() !== "ustar\0") {
 
-           this.ustar = false
 
-           return
 
-         } else {
 
-           // console.error("ustar:", val, val.toString())
 
-           this.ustar = val.toString()
 
-         }
 
-         break
 
-       // prefix is special, since it might signal the xstar header
 
-       case "prefix":
 
-         var atime = parseNumeric(val.slice(131, 131 + 12))
 
-           , ctime = parseNumeric(val.slice(131 + 12, 131 + 12 + 12))
 
-         if ((val[130] === 0 || val[130] === space) &&
 
-             typeof atime === "number" &&
 
-             typeof ctime === "number" &&
 
-             val[131 + 12] === space &&
 
-             val[131 + 12 + 12] === space) {
 
-           this.atime = atime
 
-           this.ctime = ctime
 
-           val = val.slice(0, 130)
 
-         }
 
-         prefix = val.toString("utf8").replace(/\0+$/, "")
 
-         // console.error("%% header reading prefix", prefix)
 
-         break
 
-       // all other fields are null-padding text
 
-       // or a number.
 
-       default:
 
-         if (numeric[field]) {
 
-           this[field] = parseNumeric(val)
 
-         } else {
 
-           this[field] = val.toString("utf8").replace(/\0+$/, "")
 
-         }
 
-         break
 
-     }
 
-   }
 
-   // if we got a prefix, then prepend it to the path.
 
-   if (prefix) {
 
-     this.path = prefix + "/" + this.path
 
-     // console.error("%% header got a prefix", this.path)
 
-   }
 
- }
 
- function parse256 (buf) {
 
-   // first byte MUST be either 80 or FF
 
-   // 80 for positive, FF for 2's comp
 
-   var positive
 
-   if (buf[0] === 0x80) positive = true
 
-   else if (buf[0] === 0xFF) positive = false
 
-   else return null
 
-   // build up a base-256 tuple from the least sig to the highest
 
-   var zero = false
 
-     , tuple = []
 
-   for (var i = buf.length - 1; i > 0; i --) {
 
-     var byte = buf[i]
 
-     if (positive) tuple.push(byte)
 
-     else if (zero && byte === 0) tuple.push(0)
 
-     else if (zero) {
 
-       zero = false
 
-       tuple.push(0x100 - byte)
 
-     } else tuple.push(0xFF - byte)
 
-   }
 
-   for (var sum = 0, i = 0, l = tuple.length; i < l; i ++) {
 
-     sum += tuple[i] * Math.pow(256, i)
 
-   }
 
-   return positive ? sum : -1 * sum
 
- }
 
- function parseNumeric (f) {
 
-   if (f[0] & 0x80) return parse256(f)
 
-   var str = f.toString("utf8").split("\0")[0].trim()
 
-     , res = parseInt(str, 8)
 
-   return isNaN(res) ? null : res
 
- }
 
 
  |