"Fossies" - the Fresh Open Source Software Archive

Member "Atom/resources/app/apm/node_modules/tar/lib/header.js" (8 Mar 2017, 11034 Bytes) of archive /windows/misc/atom-windows.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Javascript source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 // parse a 512-byte header block to a data object, or vice-versa
    2 // If the data won't fit nicely in a simple header, then generate
    3 // the appropriate extended header file, and return that.
    4 
    5 module.exports = TarHeader
    6 
    7 var tar = require("../tar.js")
    8   , fields = tar.fields
    9   , fieldOffs = tar.fieldOffs
   10   , fieldEnds = tar.fieldEnds
   11   , fieldSize = tar.fieldSize
   12   , numeric = tar.numeric
   13   , assert = require("assert").ok
   14   , space = " ".charCodeAt(0)
   15   , slash = "/".charCodeAt(0)
   16   , bslash = process.platform === "win32" ? "\\".charCodeAt(0) : null
   17 
   18 function TarHeader (block) {
   19   if (!(this instanceof TarHeader)) return new TarHeader(block)
   20   if (block) this.decode(block)
   21 }
   22 
   23 TarHeader.prototype =
   24   { decode : decode
   25   , encode: encode
   26   , calcSum: calcSum
   27   , checkSum: checkSum
   28   }
   29 
   30 TarHeader.parseNumeric = parseNumeric
   31 TarHeader.encode = encode
   32 TarHeader.decode = decode
   33 
   34 // note that this will only do the normal ustar header, not any kind
   35 // of extended posix header file.  If something doesn't fit comfortably,
   36 // then it will set obj.needExtended = true, and set the block to
   37 // the closest approximation.
   38 function encode (obj) {
   39   if (!obj && !(this instanceof TarHeader)) throw new Error(
   40     "encode must be called on a TarHeader, or supplied an object")
   41 
   42   obj = obj || this
   43   var block = obj.block = new Buffer(512)
   44 
   45   // if the object has a "prefix", then that's actually an extension of
   46   // the path field.
   47   if (obj.prefix) {
   48     // console.error("%% header encoding, got a prefix", obj.prefix)
   49     obj.path = obj.prefix + "/" + obj.path
   50     // console.error("%% header encoding, prefixed path", obj.path)
   51     obj.prefix = ""
   52   }
   53 
   54   obj.needExtended = false
   55 
   56   if (obj.mode) {
   57     if (typeof obj.mode === "string") obj.mode = parseInt(obj.mode, 8)
   58     obj.mode = obj.mode & 0777
   59   }
   60 
   61   for (var f = 0; fields[f] !== null; f ++) {
   62     var field = fields[f]
   63       , off = fieldOffs[f]
   64       , end = fieldEnds[f]
   65       , ret
   66 
   67     switch (field) {
   68       case "cksum":
   69         // special, done below, after all the others
   70         break
   71 
   72       case "prefix":
   73         // special, this is an extension of the "path" field.
   74         // console.error("%% header encoding, skip prefix later")
   75         break
   76 
   77       case "type":
   78         // convert from long name to a single char.
   79         var type = obj.type || "0"
   80         if (type.length > 1) {
   81           type = tar.types[obj.type]
   82           if (!type) type = "0"
   83         }
   84         writeText(block, off, end, type)
   85         break
   86 
   87       case "path":
   88         // uses the "prefix" field if > 100 bytes, but <= 255
   89         var pathLen = Buffer.byteLength(obj.path)
   90           , pathFSize = fieldSize[fields.path]
   91           , prefFSize = fieldSize[fields.prefix]
   92 
   93         // paths between 100 and 255 should use the prefix field.
   94         // longer than 255
   95         if (pathLen > pathFSize &&
   96             pathLen <= pathFSize + prefFSize) {
   97           // need to find a slash somewhere in the middle so that
   98           // path and prefix both fit in their respective fields
   99           var searchStart = pathLen - 1 - pathFSize
  100             , searchEnd = prefFSize
  101             , found = false
  102             , pathBuf = new Buffer(obj.path)
  103 
  104           for ( var s = searchStart
  105               ; (s <= searchEnd)
  106               ; s ++ ) {
  107             if (pathBuf[s] === slash || pathBuf[s] === bslash) {
  108               found = s
  109               break
  110             }
  111           }
  112 
  113           if (found !== false) {
  114             prefix = pathBuf.slice(0, found).toString("utf8")
  115             path = pathBuf.slice(found + 1).toString("utf8")
  116 
  117             ret = writeText(block, off, end, path)
  118             off = fieldOffs[fields.prefix]
  119             end = fieldEnds[fields.prefix]
  120             // console.error("%% header writing prefix", off, end, prefix)
  121             ret = writeText(block, off, end, prefix) || ret
  122             break
  123           }
  124         }
  125 
  126         // paths less than 100 chars don't need a prefix
  127         // and paths longer than 255 need an extended header and will fail
  128         // on old implementations no matter what we do here.
  129         // Null out the prefix, and fallthrough to default.
  130         // console.error("%% header writing no prefix")
  131         var poff = fieldOffs[fields.prefix]
  132           , pend = fieldEnds[fields.prefix]
  133         writeText(block, poff, pend, "")
  134         // fallthrough
  135 
  136       // all other fields are numeric or text
  137       default:
  138         ret = numeric[field]
  139             ? writeNumeric(block, off, end, obj[field])
  140             : writeText(block, off, end, obj[field] || "")
  141         break
  142     }
  143     obj.needExtended = obj.needExtended || ret
  144   }
  145 
  146   var off = fieldOffs[fields.cksum]
  147     , end = fieldEnds[fields.cksum]
  148 
  149   writeNumeric(block, off, end, calcSum.call(this, block))
  150 
  151   return block
  152 }
  153 
  154 // if it's a negative number, or greater than will fit,
  155 // then use write256.
  156 var MAXNUM = { 12: 077777777777
  157              , 11: 07777777777
  158              , 8 : 07777777
  159              , 7 : 0777777 }
  160 function writeNumeric (block, off, end, num) {
  161   var writeLen = end - off
  162     , maxNum = MAXNUM[writeLen] || 0
  163 
  164   num = num || 0
  165   // console.error("  numeric", num)
  166 
  167   if (num instanceof Date ||
  168       Object.prototype.toString.call(num) === "[object Date]") {
  169     num = num.getTime() / 1000
  170   }
  171 
  172   if (num > maxNum || num < 0) {
  173     write256(block, off, end, num)
  174     // need an extended header if negative or too big.
  175     return true
  176   }
  177 
  178   // god, tar is so annoying
  179   // if the string is small enough, you should put a space
  180   // between the octal string and the \0, but if it doesn't
  181   // fit, then don't.
  182   var numStr = Math.floor(num).toString(8)
  183   if (num < MAXNUM[writeLen - 1]) numStr += " "
  184 
  185   // pad with "0" chars
  186   if (numStr.length < writeLen) {
  187     numStr = (new Array(writeLen - numStr.length).join("0")) + numStr
  188   }
  189 
  190   if (numStr.length !== writeLen - 1) {
  191     throw new Error("invalid length: " + JSON.stringify(numStr) + "\n" +
  192                     "expected: "+writeLen)
  193   }
  194   block.write(numStr, off, writeLen, "utf8")
  195   block[end - 1] = 0
  196 }
  197 
  198 function write256 (block, off, end, num) {
  199   var buf = block.slice(off, end)
  200   var positive = num >= 0
  201   buf[0] = positive ? 0x80 : 0xFF
  202 
  203   // get the number as a base-256 tuple
  204   if (!positive) num *= -1
  205   var tuple = []
  206   do {
  207     var n = num % 256
  208     tuple.push(n)
  209     num = (num - n) / 256
  210   } while (num)
  211 
  212   var bytes = tuple.length
  213 
  214   var fill = buf.length - bytes
  215   for (var i = 1; i < fill; i ++) {
  216     buf[i] = positive ? 0 : 0xFF
  217   }
  218 
  219   // tuple is a base256 number, with [0] as the *least* significant byte
  220   // if it's negative, then we need to flip all the bits once we hit the
  221   // first non-zero bit.  The 2's-complement is (0x100 - n), and the 1's-
  222   // complement is (0xFF - n).
  223   var zero = true
  224   for (i = bytes; i > 0; i --) {
  225     var byte = tuple[bytes - i]
  226     if (positive) buf[fill + i] = byte
  227     else if (zero && byte === 0) buf[fill + i] = 0
  228     else if (zero) {
  229       zero = false
  230       buf[fill + i] = 0x100 - byte
  231     } else buf[fill + i] = 0xFF - byte
  232   }
  233 }
  234 
  235 function writeText (block, off, end, str) {
  236   // strings are written as utf8, then padded with \0
  237   var strLen = Buffer.byteLength(str)
  238     , writeLen = Math.min(strLen, end - off)
  239     // non-ascii fields need extended headers
  240     // long fields get truncated
  241     , needExtended = strLen !== str.length || strLen > writeLen
  242 
  243   // write the string, and null-pad
  244   if (writeLen > 0) block.write(str, off, writeLen, "utf8")
  245   for (var i = off + writeLen; i < end; i ++) block[i] = 0
  246 
  247   return needExtended
  248 }
  249 
  250 function calcSum (block) {
  251   block = block || this.block
  252   assert(Buffer.isBuffer(block) && block.length === 512)
  253 
  254   if (!block) throw new Error("Need block to checksum")
  255 
  256   // now figure out what it would be if the cksum was "        "
  257   var sum = 0
  258     , start = fieldOffs[fields.cksum]
  259     , end = fieldEnds[fields.cksum]
  260 
  261   for (var i = 0; i < fieldOffs[fields.cksum]; i ++) {
  262     sum += block[i]
  263   }
  264 
  265   for (var i = start; i < end; i ++) {
  266     sum += space
  267   }
  268 
  269   for (var i = end; i < 512; i ++) {
  270     sum += block[i]
  271   }
  272 
  273   return sum
  274 }
  275 
  276 
  277 function checkSum (block) {
  278   var sum = calcSum.call(this, block)
  279   block = block || this.block
  280 
  281   var cksum = block.slice(fieldOffs[fields.cksum], fieldEnds[fields.cksum])
  282   cksum = parseNumeric(cksum)
  283 
  284   return cksum === sum
  285 }
  286 
  287 function decode (block) {
  288   block = block || this.block
  289   assert(Buffer.isBuffer(block) && block.length === 512)
  290 
  291   this.block = block
  292   this.cksumValid = this.checkSum()
  293 
  294   var prefix = null
  295 
  296   // slice off each field.
  297   for (var f = 0; fields[f] !== null; f ++) {
  298     var field = fields[f]
  299       , val = block.slice(fieldOffs[f], fieldEnds[f])
  300 
  301     switch (field) {
  302       case "ustar":
  303         // if not ustar, then everything after that is just padding.
  304         if (val.toString() !== "ustar\0") {
  305           this.ustar = false
  306           return
  307         } else {
  308           // console.error("ustar:", val, val.toString())
  309           this.ustar = val.toString()
  310         }
  311         break
  312 
  313       // prefix is special, since it might signal the xstar header
  314       case "prefix":
  315         var atime = parseNumeric(val.slice(131, 131 + 12))
  316           , ctime = parseNumeric(val.slice(131 + 12, 131 + 12 + 12))
  317         if ((val[130] === 0 || val[130] === space) &&
  318             typeof atime === "number" &&
  319             typeof ctime === "number" &&
  320             val[131 + 12] === space &&
  321             val[131 + 12 + 12] === space) {
  322           this.atime = atime
  323           this.ctime = ctime
  324           val = val.slice(0, 130)
  325         }
  326         prefix = val.toString("utf8").replace(/\0+$/, "")
  327         // console.error("%% header reading prefix", prefix)
  328         break
  329 
  330       // all other fields are null-padding text
  331       // or a number.
  332       default:
  333         if (numeric[field]) {
  334           this[field] = parseNumeric(val)
  335         } else {
  336           this[field] = val.toString("utf8").replace(/\0+$/, "")
  337         }
  338         break
  339     }
  340   }
  341 
  342   // if we got a prefix, then prepend it to the path.
  343   if (prefix) {
  344     this.path = prefix + "/" + this.path
  345     // console.error("%% header got a prefix", this.path)
  346   }
  347 }
  348 
  349 function parse256 (buf) {
  350   // first byte MUST be either 80 or FF
  351   // 80 for positive, FF for 2's comp
  352   var positive
  353   if (buf[0] === 0x80) positive = true
  354   else if (buf[0] === 0xFF) positive = false
  355   else return null
  356 
  357   // build up a base-256 tuple from the least sig to the highest
  358   var zero = false
  359     , tuple = []
  360   for (var i = buf.length - 1; i > 0; i --) {
  361     var byte = buf[i]
  362     if (positive) tuple.push(byte)
  363     else if (zero && byte === 0) tuple.push(0)
  364     else if (zero) {
  365       zero = false
  366       tuple.push(0x100 - byte)
  367     } else tuple.push(0xFF - byte)
  368   }
  369 
  370   for (var sum = 0, i = 0, l = tuple.length; i < l; i ++) {
  371     sum += tuple[i] * Math.pow(256, i)
  372   }
  373 
  374   return positive ? sum : -1 * sum
  375 }
  376 
  377 function parseNumeric (f) {
  378   if (f[0] & 0x80) return parse256(f)
  379 
  380   var str = f.toString("utf8").split("\0")[0].trim()
  381     , res = parseInt(str, 8)
  382 
  383   return isNaN(res) ? null : res
  384 }
  385