version bump 1.1.0: zip support

This commit is contained in:
SheetJS 2018-09-04 03:14:20 -04:00
parent 8ee792f343
commit d9b99d68a7
39 changed files with 5384 additions and 124 deletions

12
.gitignore vendored

@ -10,18 +10,26 @@ test_files_pres
*.[cC][sS][vV]
*.[dD][iIbB][fF]
*.[pP][rR][nN]
*.[pP][mM][dD]*
*.[pP][dD][fF]
*.[sS][lL][kK]
*.socialcalc
*.[xX][lL][sSwWcC]
*.[xX][lL][sS][xXmMbB]
*.[xX][lL][sSwWcCaAtTmM]
*.[xX][lL][sSaAtT][xXmMbB]
*.[oO][dD][sS]
*.[fF][oO][dD][sS]
*.[xX][mM][lL]
*.[uU][oO][sS]
*.[wW][kKqQbB][S1234567890]
*.[qQ][pP][wW]
*.[bB][iI][fF][fF][23458]
*.[rR][tT][fF]
*.[eE][tT][hH]
*.[zZ][iI][pP]
*.[mM][sS][iI]
*.123
*.htm
*.html
*.sheetjs
*.exe
*.img

@ -4,6 +4,10 @@ This log is intended to keep track of backwards-incompatible changes, including
but not limited to API changes and file location changes. Minor behavioral
changes may not be included if they are not expected to break existing code.
## 1.1.0 (2018-09-04)
* Support for ZIP file format
## 1.0.6 (2018-04-09)
* `lastIndexOf` in FAT builder enables larger file parsing at minor cost

@ -1,13 +1,11 @@
# Compound File Binary Format
# Container File Blobs
Pure JS implementation of MS-CFB: Compound File Binary File Format, a container
format used in many Microsoft file types (XLS, DOC, VBA blobs in XLSX and XLSB)
Pure JS implementation of various container file formats, including ZIP and CFB.
[![Build Status](https://travis-ci.org/SheetJS/js-cfb.svg?branch=master)](https://travis-ci.org/SheetJS/js-cfb)
[![Coverage Status](http://img.shields.io/coveralls/SheetJS/js-cfb/master.svg)](https://coveralls.io/r/SheetJS/js-cfb?branch=master)
[![Dependencies Status](https://david-dm.org/sheetjs/js-cfb/status.svg)](https://david-dm.org/sheetjs/js-cfb)
[![NPM Downloads](https://img.shields.io/npm/dt/cfb.svg)](https://npmjs.org/package/cfb)
[![ghit.me](https://ghit.me/badge.svg?repo=sheetjs/js-xlsx)](https://ghit.me/repo/sheetjs/js-xlsx)
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-cfb?pixel)](https://github.com/SheetJS/js-cfb)
## Installation
@ -79,29 +77,48 @@ The CFB object exposes the following methods and properties:
`CFB.parse(blob)` takes a nodejs Buffer or an array of bytes and returns an
parsed representation of the data.
`CFB.read(blob, opts)` wraps `parse`. `opts.type` controls the behavior:
`CFB.read(blob, opts)` wraps `parse`.
`CFB.find(cfb, path)` performs a case-insensitive match for the path (or file
name, if there are no slashes) and returns an entry object or null if not found.
`CFB.write(cfb, opts)` generates a file based on the container.
`CFB.writeFile(cfb, filename, opts)` creates a file with the specified name.
### Parse Options
`CFB.read` takes an options argument. `opts.type` controls the behavior:
| `type` | expected input |
|------------|-----------------------------------------------------------------|
|------------|:----------------------------------------------------------------|
| `"base64"` | string: Base64 encoding of the file |
| `"binary"` | string: binary string (byte `n` is `data.charCodeAt(n)`) |
| `"file"` | string: path of file that will be read (nodejs only) |
| (default) | buffer or array of 8-bit unsigned int (byte `n` is `data[n]`) |
`CFB.find(cfb, path)` performs a case-insensitive match for the path (or file
name, if there are no slashes) and returns an entry object or null if not found.
`CFB.write(cfb, opts)` generates a file based on the container. `opts.type`
controls the behavior:
### Write Options
`CFB.write` and `CFB.writeFile` take options argument.
`opts.type` controls the behavior:
| `type` | output |
|------------|-----------------------------------------------------------------|
|------------|:----------------------------------------------------------------|
| `"base64"` | string: Base64 encoding of the file |
| `"binary"` | string: binary string (byte `n` is `data.charCodeAt(n)`) |
| `"file"` | string: path of file that will be created (nodejs only) |
| (default) | buffer if available, array of 8-bit unsigned int otherwise |
`CFB.writeFile(cfb, filename, opts)` creates a file with the specified name.
`opts.fileType` controls the output file type:
| `fileType` | output |
|:-------------------|:--------------|
| `'cfb'` (default) | CFB container |
| `'zip'` | ZIP file |
`opts.compression` enables DEFLATE compression for ZIP file type.
## Utility Functions
@ -114,6 +131,12 @@ accept a `name` argument strictly deal with absolute file names:
Set the option `{unsafe:true}` to skip existence checks (for bulk additions)
- `.cfb_del(cfb, name)` deletes the specified file
- `.cfb_mov(cfb, old_name, new_name)` moves the old file to new path and name
- `.use_zlib(require("zlib"))` loads a nodejs zlib instance.
By default, the library uses a pure JS inflate/deflate implementation. NodeJS
`zlib.InflateRaw` exposes the number of bytes read in versions after `8.11.0`.
If a supplied `zlib` does not support the required features, a warning will be
displayed in the console and the pure JS fallback will be used.
## Container Object Description
@ -146,11 +169,7 @@ granted by the Apache 2.0 License are reserved by the Original Author.
## References
<details>
<summary><b>OSP-covered Specifications</b> (click to show)</summary>
- [MS-CFB]: Compound File Binary File Format
</details>
- ZIP `APPNOTE.TXT`: https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.3.4.TXT
- RFC1951: https://www.ietf.org/rfc/rfc1951.txt

@ -19,11 +19,16 @@ program
.option('-O, --to-stdout', 'extract raw contents to stdout')
.option('-z, --dump', 'dump internal representation but do not extract')
.option('-q, --quiet', 'process but do not report')
.option('--here', 'skip the CFB root name when extracting')
.option('--osx', 'use OSX-style unzip listing')
.option('--zlib', 'use native zlib')
.option('--local', 'print times in local timezone')
.option('--dev', 'development mode')
.option('--read', 'read but do not print out contents');
program.parse(process.argv);
if(program.zlib) X.utils.use_zlib(require('zlib'));
var exit = process.exit;
var die = function(errno/*:number*/, msg/*:string*/) { console.error(n + ": " + msg); exit(errno); };
var logit = function(cmd/*:string*/, f/*:string*/) { console.error(sprintf("%-6s %s", cmd, f)); };
@ -53,16 +58,32 @@ if(program.dump) {
}
if(program.repair) { X.writeFile(cfb, program.args[0]); exit(0); }
var rlen = cfb.FullPaths[0].length;
function fix_string(x/*:string*/)/*:string*/ { return x.replace(/[\u0000-\u001f]/g, function($$) { return sprintf("\\u%04X", $$.charCodeAt(0)); }); }
var format_date = function(date/*:Date*/)/*:string*/ {
return sprintf("%02u-%02u-%02u %02u:%02u", date.getUTCMonth()+1, date.getUTCDate(), date.getUTCFullYear()%100, date.getUTCHours(), date.getUTCMinutes());
var format_date = function(date/*:Date*/, osx/*:?any*/)/*:string*/ {
var datefmt = osx ? "%02u-%02u-%04u %02u:%02u": "%02u-%02u-%02u %02u:%02u";
var MM = program.local ? date.getMonth() + 1 : date.getUTCMonth() + 1;
var DD = program.local ? date.getDate() : date.getUTCDate();
var YY = (program.local ? date.getFullYear() : date.getUTCFullYear())%(osx ? 10000 : 100);
var hh = program.local ? date.getHours() : date.getUTCHours();
var mm = program.local ? date.getMinutes() : date.getUTCMinutes();
return sprintf(datefmt, MM, DD, YY, hh, mm);
};
if(program.listFiles) {
var basetime = new Date(1980,0,1);
var basetime = new Date(Date.UTC(1980,0,1));
var cnt = 0, rootsize = 0, filesize = 0;
console.log(" Length Date Time Name");
console.log(" -------- ---- ---- ----");
var fmtstr = "%9lu %s %s";
if(program.osx) {
console.log("Archive: " + program.args[0]);
console.log(" Length Date Time Name");
console.log("--------- ---------- ----- ----");
fmtstr = "%9lu %s %s";
} else {
console.log(" Length Date Time Name");
console.log(" -------- ---- ---- ----");
}
cfb.FileIndex.forEach(function(file/*:CFBEntry*/, i/*:number*/) {
switch(file.type) {
case 5:
@ -70,13 +91,22 @@ if(program.listFiles) {
rootsize = file.size;
break;
case 2:
console.log(sprintf("%9lu %s %s", file.size, format_date(basetime), fix_string(cfb.FullPaths[i])));
var fixname = fix_string(cfb.FullPaths[i]);
if(program.osx && fixname.match(/\\u0001Sh33tJ5/)) return;
if(program.here) fixname = fixname.slice(rlen);
console.log(sprintf(fmtstr, file.size, format_date(file.mt || basetime, program.osx), fixname));
filesize += file.size;
++cnt;
}
});
console.log(" -------- -------");
console.log(sprintf("%9lu %lu file%s", rootsize || filesize, cnt, (cnt !== 1 ? "s" : "")));
var outfmt = "%9lu %lu file%s";
if(program.osx) {
console.log("--------- -------");
outfmt = "%9lu %lu file%s";
} else {
console.log(" -------- -------");
}
console.log(sprintf(outfmt, rootsize || filesize, cnt, (cnt !== 1 ? "s" : "")));
exit(0);
}
@ -124,8 +154,13 @@ if(program.args.length > 1) {
}
if(program.toStdout) exit(0);
for(var i=0; i!==cfb.FullPaths.length; ++i) {
for(var i=program.here ? 1 : 0; i!==cfb.FullPaths.length; ++i) {
if(!cfb.FileIndex[i].name) continue;
if(cfb.FullPaths[i].slice(-1) === "/") mkdirp(cfb.FullPaths[i]);
else write(cfb.FullPaths[i], cfb.FileIndex[i]);
var fp = cfb.FullPaths[i];
if(program.here) fp = fp.slice(rlen);
if(fp.slice(-1) === "/") mkdirp(fp);
else {
if(fp.indexOf("/") > -1) mkdirp(fp.slice(0, fp.lastIndexOf("/")));
write(fp, cfb.FileIndex[i]);
}
}

@ -2,5 +2,5 @@
/* vim: set ts=2: */
/*jshint eqnull:true */
/*exported CFB */
/*global module, require:false, process:false, Buffer:false, Uint8Array:false */
/*global module, require:false, process:false, Buffer:false, Uint8Array:false, Uint16Array:false */

@ -5,9 +5,11 @@ var Buffer_from = /*::(*/function(){}/*:: :any)*/;
if(typeof Buffer !== 'undefined') {
var nbfs = !Buffer.from;
if(!nbfs) try { Buffer.from("foo", "utf8"); } catch(e) { nbfs = true; }
Buffer_from = nbfs ? function(buf, enc) { return (enc) ? new Buffer(buf, enc) : new Buffer(buf); } : Buffer.from.bind(Buffer);
Buffer_from = /*::((*/nbfs ? function(buf, enc) { return (enc) ? new Buffer(buf, enc) : new Buffer(buf); } : Buffer.from.bind(Buffer)/*::) :any)*/;
// $FlowIgnore
if(!Buffer.alloc) Buffer.alloc = function(n) { return new Buffer(n); };
// $FlowIgnore
if(!Buffer.allocUnsafe) Buffer.allocUnsafe = function(n) { return new Buffer(n); };
}
function new_raw_buf(len/*:number*/) {
@ -16,8 +18,14 @@ function new_raw_buf(len/*:number*/) {
/* jshint +W056 */
}
var s2a = function s2a(s/*:string*/) {
if(has_buf) return Buffer.from(s, "binary");
function new_unsafe_buf(len/*:number*/) {
/* jshint -W056 */
return has_buf ? Buffer.allocUnsafe(len) : new Array(len);
/* jshint +W056 */
}
var s2a = function s2a(s/*:string*/)/*:any*/ {
if(has_buf) return Buffer_from(s, "binary");
return s.split("").map(function(x/*:string*/)/*:number*/{ return x.charCodeAt(0) & 0xff; });
};

@ -22,7 +22,6 @@ if(has_buf/*:: && typeof Buffer !== 'undefined'*/) {
};
__hexlify = function(b/*:RawBytes|CFBlob*/,s/*:number*/,l/*:number*/)/*:string*/ { return Buffer.isBuffer(b)/*:: && b instanceof Buffer*/ ? b.toString('hex',s,s+l) : ___hexlify(b,s,l); };
__toBuffer = function(bufs/*:Array<Array<RawBytes>>*/)/*:RawBytes*/ { return (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat((bufs[0]/*:any*/)) : ___toBuffer(bufs);};
// $FlowIgnore
s2a = function(s/*:string*/)/*:RawBytes*/ { return Buffer_from(s, "binary"); };
bconcat = function(bufs/*:Array<RawBytes>*/)/*:RawBytes*/ { return Buffer.isBuffer(bufs[0]) ? Buffer.concat(/*::(*/bufs/*:: :any)*/) : __bconcat(bufs); };
}

101
bits/21_crc32.js Normal file

@ -0,0 +1,101 @@
/* crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
/*exported CRC32 */
var CRC32;
(function (factory) {
/*jshint ignore:start */
/*eslint-disable */
factory(CRC32 = {});
/*eslint-enable */
/*jshint ignore:end */
}(function(CRC32) {
CRC32.version = '1.2.0';
/* see perf/crc32table.js */
/*global Int32Array */
function signed_crc_table()/*:any*/ {
var c = 0, table/*:Array<number>*/ = new Array(256);
for(var n =0; n != 256; ++n){
c = n;
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
table[n] = c;
}
return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table;
}
var T = signed_crc_table();
function crc32_bstr(bstr/*:string*/, seed/*:number*/)/*:number*/ {
var C = seed ^ -1, L = bstr.length - 1;
for(var i = 0; i < L;) {
C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
}
if(i === L) C = (C>>>8) ^ T[(C ^ bstr.charCodeAt(i))&0xFF];
return C ^ -1;
}
function crc32_buf(buf/*:Uint8Array|Array<number>*/, seed/*:number*/)/*:number*/ {
if(buf.length > 10000) return crc32_buf_8(buf, seed);
var C = seed ^ -1, L = buf.length - 3;
for(var i = 0; i < L;) {
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
}
while(i < L+3) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
return C ^ -1;
}
function crc32_buf_8(buf/*:Uint8Array|Array<number>*/, seed/*:number*/)/*:number*/ {
var C = seed ^ -1, L = buf.length - 7;
for(var i = 0; i < L;) {
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
}
while(i < L+7) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
return C ^ -1;
}
function crc32_str(str/*:string*/, seed/*:number*/)/*:number*/ {
var C = seed ^ -1;
for(var i = 0, L=str.length, c, d; i < L;) {
c = str.charCodeAt(i++);
if(c < 0x80) {
C = (C>>>8) ^ T[(C ^ c)&0xFF];
} else if(c < 0x800) {
C = (C>>>8) ^ T[(C ^ (192|((c>>6)&31)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
} else if(c >= 0xD800 && c < 0xE000) {
c = (c&1023)+64; d = str.charCodeAt(i++)&1023;
C = (C>>>8) ^ T[(C ^ (240|((c>>8)&7)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|((c>>2)&63)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|(d&63)))&0xFF];
} else {
C = (C>>>8) ^ T[(C ^ (224|((c>>12)&15)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|((c>>6)&63)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
}
}
return C ^ -1;
}
CRC32.table = T;
CRC32.bstr = crc32_bstr;
CRC32.buf = crc32_buf;
CRC32.str = crc32_str;
}));

@ -1 +1 @@
exports.version = '1.0.8';
exports.version = '1.1.0';

38
bits/37_dosdates.js Normal file

@ -0,0 +1,38 @@
/* -------------------------------------------------------------------------- */
/* DOS Date format:
high|YYYYYYYm.mmmddddd.HHHHHMMM.MMMSSSSS|low
add 1980 to stored year
stored second should be doubled
*/
/* write JS date to buf as a DOS date */
function write_dos_date(buf/*:CFBlob*/, date/*:Date|string*/) {
if(typeof date === "string") date = new Date(date);
var hms/*:number*/ = date.getHours();
hms = hms << 6 | date.getMinutes();
hms = hms << 5 | (date.getSeconds()>>>1);
buf.write_shift(2, hms);
var ymd/*:number*/ = (date.getFullYear() - 1980);
ymd = ymd << 4 | (date.getMonth()+1);
ymd = ymd << 5 | date.getDate();
buf.write_shift(2, ymd);
}
/* read four bytes from buf and interpret as a DOS date */
function parse_dos_date(buf/*:CFBlob*/)/*:Date*/ {
var hms = buf.read_shift(2) & 0xFFFF;
var ymd = buf.read_shift(2) & 0xFFFF;
var val = new Date();
var d = ymd & 0x1F; ymd >>>= 5;
var m = ymd & 0x0F; ymd >>>= 4;
val.setMilliseconds(0);
val.setFullYear(ymd + 1980);
val.setMonth(m-1);
val.setDate(d);
var S = hms & 0x1F; hms >>>= 5;
var M = hms & 0x3F; hms >>>= 6;
val.setHours(hms);
val.setMinutes(M);
val.setSeconds(S<<1);
return val;
}

27
bits/38_extrafield.js Normal file

@ -0,0 +1,27 @@
function parse_extra_field(blob/*:CFBlob*/)/*:any*/ {
prep_blob(blob, 0);
var o = /*::(*/{}/*:: :any)*/;
var flags = 0;
while(blob.l <= blob.length - 4) {
var type = blob.read_shift(2);
var sz = blob.read_shift(2), tgt = blob.l + sz;
var p = {};
switch(type) {
/* UNIX-style Timestamps */
case 0x5455: {
flags = blob.read_shift(1);
if(flags & 1) p.mtime = blob.read_shift(4);
/* for some reason, CD flag corresponds to LFH */
if(sz > 5) {
if(flags & 2) p.atime = blob.read_shift(4);
if(flags & 4) p.ctime = blob.read_shift(4);
}
if(p.mtime) p.mt = new Date(p.mtime*1000);
}
break;
}
blob.l = tgt;
o[type] = p;
}
return o;
}

@ -1,4 +1,5 @@
function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
if(file[0] == 0x50 && file[1] == 0x4b) return parse_zip(file, options);
if(file.length < 512) throw new Error("CFB file size " + file.length + " < 512");
var mver = 3;
var ssz = 512;
@ -19,6 +20,8 @@ var mv = check_get_mver(blob);
mver = mv[0];
switch(mver) {
case 3: ssz = 512; break; case 4: ssz = 4096; break;
case 0: if(mv[1] == 0) return parse_zip(file, options);
/* falls through */
default: throw new Error("Major Version: Expected 3 or 4 saw " + mver);
}

@ -1,5 +1,6 @@
/* [MS-CFB] 2.2 Compound File Header -- read up to major version */
function check_get_mver(blob/*:CFBlob*/)/*:[number, number]*/ {
if(blob[blob.l] == 0x50 && blob[blob.l + 1] == 0x4b) return [0, 0];
// header signature 8
blob.chk(HEADER_SIGNATURE, 'Header Signature: ');

@ -1,3 +1,4 @@
function _write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ {
var _opts = options || {};
rebuild_cfb(cfb);
if(_opts.fileType == 'zip') return write_zip(cfb, _opts);

22
bits/78_zlib.js Normal file

@ -0,0 +1,22 @@
/* node < 8.1 zlib does not expose bytesRead, so default to pure JS */
var _zlib;
function use_zlib(zlib) { try {
var InflateRaw = zlib.InflateRaw;
var InflRaw = new InflateRaw();
InflRaw._processChunk(new Uint8Array([3, 0]), InflRaw._finishFlushFlag);
if(InflRaw.bytesRead) _zlib = zlib;
else throw new Error("zlib does not expose bytesRead");
} catch(e) {console.error("cannot use native zlib: " + (e.message || e)); } }
function _inflateRawSync(payload, usz) {
if(!_zlib) return _inflate(payload, usz);
var InflateRaw = _zlib.InflateRaw;
var InflRaw = new InflateRaw();
var out = InflRaw._processChunk(payload.slice(payload.l), InflRaw._finishFlushFlag);
payload.l += InflRaw.bytesRead;
return out;
}
function _deflateRawSync(payload) {
return _zlib ? _zlib.deflateRawSync(payload) : _deflate(payload);
}

70
bits/79_flate.js Normal file

@ -0,0 +1,70 @@
var CLEN_ORDER = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];
/* LEN_ID = [ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285 ]; */
var LEN_LN = [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13 , 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 ];
/* DST_ID = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]; */
var DST_LN = [ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 ];
function bit_swap_8(n) { var t = (((((n<<1)|(n<<11)) & 0x22110) | (((n<<5)|(n<<15)) & 0x88440))); return ((t>>16) | (t>>8) |t)&0xFF; }
var use_typed_arrays = typeof Uint8Array !== 'undefined';
var bitswap8 = use_typed_arrays ? new Uint8Array(1<<8) : [];
for(var q = 0; q < (1<<8); ++q) bitswap8[q] = bit_swap_8(q);
function bit_swap_n(n, b) {
var rev = bitswap8[n & 0xFF];
if(b <= 8) return rev >>> (8-b);
rev = (rev << 8) | bitswap8[(n>>8)&0xFF];
if(b <= 16) return rev >>> (16-b);
rev = (rev << 8) | bitswap8[(n>>16)&0xFF];
return rev >>> (24-b);
}
/* helpers for unaligned bit reads */
function read_bits_2(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 6 ? 0 : buf[h+1]<<8))>>>w)& 0x03; }
function read_bits_3(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 5 ? 0 : buf[h+1]<<8))>>>w)& 0x07; }
function read_bits_4(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 4 ? 0 : buf[h+1]<<8))>>>w)& 0x0F; }
function read_bits_5(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 3 ? 0 : buf[h+1]<<8))>>>w)& 0x1F; }
function read_bits_7(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 1 ? 0 : buf[h+1]<<8))>>>w)& 0x7F; }
/* works up to n = 3 * 8 + 1 = 25 */
function read_bits_n(buf, bl, n) {
var w = (bl&7), h = (bl>>>3), f = ((1<<n)-1);
var v = buf[h] >>> w;
if(n < 8 - w) return v & f;
v |= buf[h+1]<<(8-w);
if(n < 16 - w) return v & f;
v |= buf[h+2]<<(16-w);
if(n < 24 - w) return v & f;
v |= buf[h+3]<<(24-w);
return v & f;
}
/* until ArrayBuffer#realloc is a thing, fake a realloc */
function realloc(b, sz/*:number*/) {
var L = b.length, M = 2*L > sz ? 2*L : sz + 5, i = 0;
if(L >= sz) return b;
if(has_buf) {
var o = new_unsafe_buf(M);
// $FlowIgnore
if(b.copy) b.copy(o);
else for(; i < b.length; ++i) o[i] = b[i];
return o;
} else if(use_typed_arrays) {
var a = new Uint8Array(M);
if(a.set) a.set(b);
else for(; i < b.length; ++i) a[i] = b[i];
return a;
}
b.length = M;
return b;
}
/* zero-filled arrays for older browsers */
function zero_fill_array(n) {
var o = new Array(n);
for(var i = 0; i < n; ++i) o[i] = 0;
return o;
}

23
bits/80_deflate.js Normal file

@ -0,0 +1,23 @@
var _deflate = (function() {
var _deflateRaw = (function() {
return function deflateRaw(data, out) {
var boff = 0;
while(boff < data.length) {
var L = Math.min(0xFFFF, data.length - boff);
var h = boff + L == data.length;
/* TODO: this is only type 0 stored */
out.write_shift(1, +h);
out.write_shift(2, L);
out.write_shift(2, (~L) & 0xFFFF);
while(L-- > 0) out[out.l++] = data[boff++];
}
return out.l;
};
})();
return function(data) {
var buf = new_buf(50+Math.floor(data.length*1.1));
var off = _deflateRaw(data, buf);
return buf.slice(0, off);
};
})();

226
bits/81_inflate.js Normal file

@ -0,0 +1,226 @@
/* modified inflate function also moves original read head */
/* build tree (used for literals and lengths) */
function build_tree(clens, cmap, MAX/*:number*/)/*:number*/ {
var maxlen = 1, w = 0, i = 0, j = 0, ccode = 0, L = clens.length;
var bl_count = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
for(i = 0; i < 32; ++i) bl_count[i] = 0;
for(i = L; i < MAX; ++i) clens[i] = 0;
L = clens.length;
var ctree = use_typed_arrays ? new Uint16Array(L) : zero_fill_array(L); // []
/* build code tree */
for(i = 0; i < L; ++i) {
bl_count[(w = clens[i])]++;
if(maxlen < w) maxlen = w;
ctree[i] = 0;
}
bl_count[0] = 0;
for(i = 1; i <= maxlen; ++i) bl_count[i+16] = (ccode = (ccode + bl_count[i-1])<<1);
for(i = 0; i < L; ++i) {
ccode = clens[i];
if(ccode != 0) ctree[i] = bl_count[ccode+16]++;
}
/* cmap[maxlen + 4 bits] = (off&15) + (lit<<4) reverse mapping */
var cleni = 0;
for(i = 0; i < L; ++i) {
cleni = clens[i];
if(cleni != 0) {
ccode = bit_swap_n(ctree[i], maxlen)>>(maxlen-cleni);
for(j = (1<<(maxlen + 4 - cleni)) - 1; j>=0; --j)
cmap[ccode|(j<<cleni)] = (cleni&15) | (i<<4);
}
}
return maxlen;
}
var fix_lmap = use_typed_arrays ? new Uint16Array(512) : zero_fill_array(512);
var fix_dmap = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
if(!use_typed_arrays) {
for(var i = 0; i < 512; ++i) fix_lmap[i] = 0;
for(i = 0; i < 32; ++i) fix_dmap[i] = 0;
}
(function() {
var dlens/*:Array<number>*/ = [];
var i = 0;
for(;i<32; i++) dlens.push(5);
build_tree(dlens, fix_dmap, 32);
var clens/*:Array<number>*/ = [];
i = 0;
for(; i<=143; i++) clens.push(8);
for(; i<=255; i++) clens.push(9);
for(; i<=279; i++) clens.push(7);
for(; i<=287; i++) clens.push(8);
build_tree(clens, fix_lmap, 288);
})();
var dyn_lmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
var dyn_dmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
var dyn_cmap = use_typed_arrays ? new Uint16Array(128) : zero_fill_array(128);
var dyn_len_1 = 1, dyn_len_2 = 1;
/* 5.5.3 Expanding Huffman Codes */
function dyn(data, boff/*:number*/) {
/* nomenclature from RFC1951 refers to bit values; these are offset by the implicit constant */
var _HLIT = read_bits_5(data, boff) + 257; boff += 5;
var _HDIST = read_bits_5(data, boff) + 1; boff += 5;
var _HCLEN = read_bits_4(data, boff) + 4; boff += 4;
var w = 0;
/* grab and store code lengths */
var clens = use_typed_arrays ? new Uint8Array(19) : zero_fill_array(19);
var ctree = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
var maxlen = 1;
var bl_count = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
var next_code = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
var L = clens.length; /* 19 */
for(var i = 0; i < _HCLEN; ++i) {
clens[CLEN_ORDER[i]] = w = read_bits_3(data, boff);
if(maxlen < w) maxlen = w;
bl_count[w]++;
boff += 3;
}
/* build code tree */
var ccode = 0;
bl_count[0] = 0;
for(i = 1; i <= maxlen; ++i) next_code[i] = ccode = (ccode + bl_count[i-1])<<1;
for(i = 0; i < L; ++i) if((ccode = clens[i]) != 0) ctree[i] = next_code[ccode]++;
/* cmap[7 bits from stream] = (off&7) + (lit<<3) */
var cleni = 0;
for(i = 0; i < L; ++i) {
cleni = clens[i];
if(cleni != 0) {
ccode = bitswap8[ctree[i]]>>(8-cleni);
for(var j = (1<<(7-cleni))-1; j>=0; --j) dyn_cmap[ccode|(j<<cleni)] = (cleni&7) | (i<<3);
}
}
/* read literal and dist codes at once */
var hcodes/*:Array<number>*/ = [];
maxlen = 1;
for(; hcodes.length < _HLIT + _HDIST;) {
ccode = dyn_cmap[read_bits_7(data, boff)];
boff += ccode & 7;
switch((ccode >>>= 3)) {
case 16:
w = 3 + read_bits_2(data, boff); boff += 2;
ccode = hcodes[hcodes.length - 1];
while(w-- > 0) hcodes.push(ccode);
break;
case 17:
w = 3 + read_bits_3(data, boff); boff += 3;
while(w-- > 0) hcodes.push(0);
break;
case 18:
w = 11 + read_bits_7(data, boff); boff += 7;
while(w -- > 0) hcodes.push(0);
break;
default:
hcodes.push(ccode);
if(maxlen < ccode) maxlen = ccode;
break;
}
}
/* build literal / length trees */
var h1 = hcodes.slice(0, _HLIT), h2 = hcodes.slice(_HLIT);
for(i = _HLIT; i < 286; ++i) h1[i] = 0;
for(i = _HDIST; i < 30; ++i) h2[i] = 0;
dyn_len_1 = build_tree(h1, dyn_lmap, 286);
dyn_len_2 = build_tree(h2, dyn_dmap, 30);
return boff;
}
/* return [ data, bytesRead ] */
function inflate(data, usz/*:number*/) {
/* shortcircuit for empty buffer [0x03, 0x00] */
if(data[0] == 3 && !(data[1] & 0x3)) { return [new_raw_buf(usz), 2]; }
/* bit offset */
var boff = 0;
/* header includes final bit and type bits */
var header = 0;
var outbuf = new_unsafe_buf(usz ? usz : (1<<18));
var woff = 0;
var OL = outbuf.length>>>0;
var max_len_1 = 0, max_len_2 = 0;
while((header&1) == 0) {
header = read_bits_3(data, boff); boff += 3;
if((header >>> 1) == 0) {
/* Stored block */
if(boff & 7) boff += 8 - (boff&7);
/* 2 bytes sz, 2 bytes bit inverse */
var sz = data[boff>>>3] | data[(boff>>>3)+1]<<8;
boff += 32;
/* push sz bytes */
if(!usz && OL < woff + sz) { outbuf = realloc(outbuf, woff + sz); OL = outbuf.length; }
if(typeof data.copy === 'function') {
// $FlowIgnore
data.copy(outbuf, woff, boff>>>3, (boff>>>3)+sz);
woff += sz; boff += 8*sz;
} else while(sz-- > 0) { outbuf[woff++] = data[boff>>>3]; boff += 8; }
continue;
} else if((header >>> 1) == 1) {
/* Fixed Huffman */
max_len_1 = 9; max_len_2 = 5;
} else {
/* Dynamic Huffman */
boff = dyn(data, boff);
max_len_1 = dyn_len_1; max_len_2 = dyn_len_2;
}
if(!usz && (OL < woff + 32767)) { outbuf = realloc(outbuf, woff + 32767); OL = outbuf.length; }
for(;;) { // while(true) is apparently out of vogue in modern JS circles
/* ingest code and move read head */
var bits = read_bits_n(data, boff, max_len_1);
var code = (header>>>1) == 1 ? fix_lmap[bits] : dyn_lmap[bits];
boff += code & 15; code >>>= 4;
/* 0-255 are literals, 256 is end of block token, 257+ are copy tokens */
if(((code>>>8)&0xFF) === 0) outbuf[woff++] = code;
else if(code == 256) break;
else {
code -= 257;
var len_eb = (code < 8) ? 0 : ((code-4)>>2); if(len_eb > 5) len_eb = 0;
var tgt = woff + LEN_LN[code];
/* length extra bits */
if(len_eb > 0) {
tgt += read_bits_n(data, boff, len_eb);
boff += len_eb;
}
/* dist code */
bits = read_bits_n(data, boff, max_len_2);
code = (header>>>1) == 1 ? fix_dmap[bits] : dyn_dmap[bits];
boff += code & 15; code >>>= 4;
var dst_eb = (code < 4 ? 0 : (code-2)>>1);
var dst = DST_LN[code];
/* dist extra bits */
if(dst_eb > 0) {
dst += read_bits_n(data, boff, dst_eb);
boff += dst_eb;
}
/* in the common case, manual byte copy is faster than TA set / Buffer copy */
if(!usz && OL < tgt) { outbuf = realloc(outbuf, tgt); OL = outbuf.length; }
while(woff < tgt) { outbuf[woff] = outbuf[woff - dst]; ++woff; }
}
}
}
return [usz ? outbuf : outbuf.slice(0, woff), (boff+7)>>>3];
}
function _inflate(payload, usz) {
var data = payload.slice(payload.l||0);
var out = inflate(data, usz);
payload.l += out[1];
return out[0];
}

103
bits/82_zparse.js Normal file

@ -0,0 +1,103 @@
function warn_or_throw(wrn, msg) {
if(wrn) { if(typeof console !== 'undefined') console.error(msg); }
else throw new Error(msg);
}
function parse_zip(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
var blob/*:CFBlob*/ = /*::(*/file/*:: :any)*/;
prep_blob(blob, 0);
var FileIndex/*:CFBFileIndex*/ = [], FullPaths/*:Array<string>*/ = [];
var o = {
FileIndex: FileIndex,
FullPaths: FullPaths
};
init_cfb(o, { root: options.root });
/* find end of central directory, start just after signature */
var i = blob.length - 4;
while((blob[i] != 0x50 || blob[i+1] != 0x4b || blob[i+2] != 0x05 || blob[i+3] != 0x06) && i >= 0) --i;
blob.l = i + 4;
/* parse end of central directory */
blob.l += 4;
var fcnt = blob.read_shift(2);
blob.l += 6;
var start_cd = blob.read_shift(4);
/* parse central directory */
blob.l = start_cd;
for(i = 0; i < fcnt; ++i) {
/* trust local file header instead of CD entry */
blob.l += 20;
var csz = blob.read_shift(4);
var usz = blob.read_shift(4);
var namelen = blob.read_shift(2);
var efsz = blob.read_shift(2);
var fcsz = blob.read_shift(2);
blob.l += 8;
var offset = blob.read_shift(4);
var EF = parse_extra_field(/*::(*/blob.slice(blob.l+namelen, blob.l+namelen+efsz)/*:: :any)*/);
blob.l += namelen + efsz + fcsz;
var L = blob.l;
blob.l = offset + 4;
parse_local_file(blob, csz, usz, o, EF);
blob.l = L;
}
return o;
}
/* head starts just after local file header signature */
function parse_local_file(blob/*:CFBlob*/, csz/*:number*/, usz/*:number*/, o/*:CFBContainer*/, EF) {
/* [local file header] */
blob.l += 2;
var flags = blob.read_shift(2);
var meth = blob.read_shift(2);
var date = parse_dos_date(blob);
if(flags & 0x2041) throw new Error("Unsupported ZIP encryption");
var crc32 = blob.read_shift(4);
var _csz = blob.read_shift(4);
var _usz = blob.read_shift(4);
var namelen = blob.read_shift(2);
var efsz = blob.read_shift(2);
// TODO: flags & (1<<11) // UTF8
var name = ""; for(var i = 0; i < namelen; ++i) name += String.fromCharCode(blob[blob.l++]);
if(efsz) {
var ef = parse_extra_field(/*::(*/blob.slice(blob.l, blob.l + efsz)/*:: :any)*/);
if((ef[0x5455]||{}).mt) date = ef[0x5455].mt;
if(((EF||{})[0x5455]||{}).mt) date = EF[0x5455].mt;
}
blob.l += efsz;
/* [encryption header] */
/* [file data] */
var data = blob.slice(blob.l, blob.l + _csz);
switch(meth) {
case 8: data = _inflateRawSync(blob, _usz); break;
case 0: break;
default: throw new Error("Unsupported ZIP Compression method " + meth);
}
/* [data descriptor] */
var wrn = false;
if(flags & 8) {
crc32 = blob.read_shift(4);
if(crc32 == 0x08074b50) { crc32 = blob.read_shift(4); wrn = true; }
_csz = blob.read_shift(4);
_usz = blob.read_shift(4);
}
if(_csz != csz) warn_or_throw(wrn, "Bad compressed size: " + csz + " != " + _csz);
if(_usz != usz) warn_or_throw(wrn, "Bad uncompressed size: " + usz + " != " + _usz);
var _crc32 = CRC32.buf(data, 0);
if(crc32 != _crc32) warn_or_throw(wrn, "Bad CRC32 checksum: " + crc32 + " != " + _crc32);
cfb_add(o, name, data, {unsafe: true, mt: date});
}

102
bits/83_zwrite.js Normal file

@ -0,0 +1,102 @@
function write_zip(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ {
var _opts = options || {};
var out = [], cdirs = [];
var o/*:CFBlob*/ = new_buf(1);
var method = (_opts.compression ? 8 : 0), flags = 0;
var desc = false;
if(desc) flags |= 8;
var i = 0, j = 0;
var start_cd = 0, fcnt = 0;
var root = cfb.FullPaths[0], fp = root, fi = cfb.FileIndex[0];
var crcs = [];
var sz_cd = 0;
for(i = 1; i < cfb.FullPaths.length; ++i) {
fp = cfb.FullPaths[i].slice(root.length); fi = cfb.FileIndex[i];
if(!fi.size || !fi.content || fp == "\u0001Sh33tJ5") continue;
var start = start_cd;
/* TODO: CP437 filename */
var namebuf = new_buf(fp.length);
for(j = 0; j < fp.length; ++j) namebuf.write_shift(1, fp.charCodeAt(j) & 0x7F);
namebuf = namebuf.slice(0, namebuf.l);
crcs[fcnt] = CRC32.buf(/*::((*/fi.content/*::||[]):any)*/, 0);
var outbuf = fi.content/*::||[]*/;
if(method == 8) outbuf = _deflateRawSync(outbuf);
/* local file header */
o = new_buf(30);
o.write_shift(4, 0x04034b50);
o.write_shift(2, 20);
o.write_shift(2, flags);
o.write_shift(2, method);
/* TODO: last mod file time/date */
if(fi.mt) write_dos_date(o, fi.mt);
else o.write_shift(4, 0);
o.write_shift(-4, (flags & 8) ? 0 : crcs[fcnt]);
o.write_shift(4, (flags & 8) ? 0 : outbuf.length);
o.write_shift(4, (flags & 8) ? 0 : /*::(*/fi.content/*::||[])*/.length);
o.write_shift(2, namebuf.length);
o.write_shift(2, 0);
start_cd += o.length;
out.push(o);
start_cd += namebuf.length;
out.push(namebuf);
/* TODO: encryption header ? */
start_cd += outbuf.length;
out.push(outbuf);
/* data descriptor */
if(flags & 8) {
o = new_buf(12);
o.write_shift(-4, crcs[fcnt]);
o.write_shift(4, outbuf.length);
o.write_shift(4, /*::(*/fi.content/*::||[])*/.length);
start_cd += o.l;
out.push(o);
}
/* central directory */
o = new_buf(46);
o.write_shift(4, 0x02014b50);
o.write_shift(2, 0);
o.write_shift(2, 20);
o.write_shift(2, flags);
o.write_shift(2, method);
o.write_shift(4, 0); /* TODO: last mod file time/date */
o.write_shift(-4, crcs[fcnt]);
o.write_shift(4, outbuf.length);
o.write_shift(4, /*::(*/fi.content/*::||[])*/.length);
o.write_shift(2, namebuf.length);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(4, 0);
o.write_shift(4, start);
sz_cd += o.l;
cdirs.push(o);
sz_cd += namebuf.length;
cdirs.push(namebuf);
++fcnt;
}
/* end of central directory */
o = new_buf(22);
o.write_shift(4, 0x06054b50);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, fcnt);
o.write_shift(2, fcnt);
o.write_shift(4, sz_cd);
o.write_shift(4, start_cd);
o.write_shift(2, 0);
return bconcat(([bconcat((out/*:any*/)), bconcat(cdirs), o]/*:any*/));
}

@ -25,6 +25,8 @@ function cfb_add(cfb/*:CFBContainer*/, name/*:string*/, content/*:?RawBytes*/, o
file.size = content ? content.length : 0;
if(opts) {
if(opts.CLSID) file.clsid = opts.CLSID;
if(opts.mt) file.mt = opts.mt;
if(opts.ct) file.ct = opts.ct;
}
return file;
}

@ -13,6 +13,9 @@ exports.utils = {
CheckField: CheckField,
prep_blob: prep_blob,
bconcat: bconcat,
use_zlib: use_zlib,
_deflateRaw: _deflate,
_inflateRaw: _inflate,
consts: consts
};

@ -2,7 +2,7 @@
/* vim: set ts=2: */
/*jshint eqnull:true */
/*exported CFB */
/*global module, require:false, process:false, Buffer:false, Uint8Array:false */
/*global module, require:false, process:false, Buffer:false, Uint8Array:false, Uint16Array:false */
var Base64 = (function make_b64(){
var map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
@ -55,9 +55,11 @@ var Buffer_from = /*::(*/function(){}/*:: :any)*/;
if(typeof Buffer !== 'undefined') {
var nbfs = !Buffer.from;
if(!nbfs) try { Buffer.from("foo", "utf8"); } catch(e) { nbfs = true; }
Buffer_from = nbfs ? function(buf, enc) { return (enc) ? new Buffer(buf, enc) : new Buffer(buf); } : Buffer.from.bind(Buffer);
Buffer_from = /*::((*/nbfs ? function(buf, enc) { return (enc) ? new Buffer(buf, enc) : new Buffer(buf); } : Buffer.from.bind(Buffer)/*::) :any)*/;
// $FlowIgnore
if(!Buffer.alloc) Buffer.alloc = function(n) { return new Buffer(n); };
// $FlowIgnore
if(!Buffer.allocUnsafe) Buffer.allocUnsafe = function(n) { return new Buffer(n); };
}
function new_raw_buf(len/*:number*/) {
@ -66,8 +68,14 @@ function new_raw_buf(len/*:number*/) {
/* jshint +W056 */
}
var s2a = function s2a(s/*:string*/) {
if(has_buf) return Buffer.from(s, "binary");
function new_unsafe_buf(len/*:number*/) {
/* jshint -W056 */
return has_buf ? Buffer.allocUnsafe(len) : new Array(len);
/* jshint +W056 */
}
var s2a = function s2a(s/*:string*/)/*:any*/ {
if(has_buf) return Buffer_from(s, "binary");
return s.split("").map(function(x/*:string*/)/*:number*/{ return x.charCodeAt(0) & 0xff; });
};
@ -96,7 +104,6 @@ if(has_buf/*:: && typeof Buffer !== 'undefined'*/) {
};
__hexlify = function(b/*:RawBytes|CFBlob*/,s/*:number*/,l/*:number*/)/*:string*/ { return Buffer.isBuffer(b)/*:: && b instanceof Buffer*/ ? b.toString('hex',s,s+l) : ___hexlify(b,s,l); };
__toBuffer = function(bufs/*:Array<Array<RawBytes>>*/)/*:RawBytes*/ { return (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat((bufs[0]/*:any*/)) : ___toBuffer(bufs);};
// $FlowIgnore
s2a = function(s/*:string*/)/*:RawBytes*/ { return Buffer_from(s, "binary"); };
bconcat = function(bufs/*:Array<RawBytes>*/)/*:RawBytes*/ { return Buffer.isBuffer(bufs[0]) ? Buffer.concat(/*::(*/bufs/*:: :any)*/) : __bconcat(bufs); };
}
@ -184,10 +191,111 @@ type SectorList = {
}
type CFBFiles = {[n:string]:CFBEntry};
*/
/* crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
/*exported CRC32 */
var CRC32;
(function (factory) {
/*jshint ignore:start */
/*eslint-disable */
factory(CRC32 = {});
/*eslint-enable */
/*jshint ignore:end */
}(function(CRC32) {
CRC32.version = '1.2.0';
/* see perf/crc32table.js */
/*global Int32Array */
function signed_crc_table()/*:any*/ {
var c = 0, table/*:Array<number>*/ = new Array(256);
for(var n =0; n != 256; ++n){
c = n;
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
table[n] = c;
}
return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table;
}
var T = signed_crc_table();
function crc32_bstr(bstr/*:string*/, seed/*:number*/)/*:number*/ {
var C = seed ^ -1, L = bstr.length - 1;
for(var i = 0; i < L;) {
C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
}
if(i === L) C = (C>>>8) ^ T[(C ^ bstr.charCodeAt(i))&0xFF];
return C ^ -1;
}
function crc32_buf(buf/*:Uint8Array|Array<number>*/, seed/*:number*/)/*:number*/ {
if(buf.length > 10000) return crc32_buf_8(buf, seed);
var C = seed ^ -1, L = buf.length - 3;
for(var i = 0; i < L;) {
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
}
while(i < L+3) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
return C ^ -1;
}
function crc32_buf_8(buf/*:Uint8Array|Array<number>*/, seed/*:number*/)/*:number*/ {
var C = seed ^ -1, L = buf.length - 7;
for(var i = 0; i < L;) {
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
}
while(i < L+7) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
return C ^ -1;
}
function crc32_str(str/*:string*/, seed/*:number*/)/*:number*/ {
var C = seed ^ -1;
for(var i = 0, L=str.length, c, d; i < L;) {
c = str.charCodeAt(i++);
if(c < 0x80) {
C = (C>>>8) ^ T[(C ^ c)&0xFF];
} else if(c < 0x800) {
C = (C>>>8) ^ T[(C ^ (192|((c>>6)&31)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
} else if(c >= 0xD800 && c < 0xE000) {
c = (c&1023)+64; d = str.charCodeAt(i++)&1023;
C = (C>>>8) ^ T[(C ^ (240|((c>>8)&7)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|((c>>2)&63)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|(d&63)))&0xFF];
} else {
C = (C>>>8) ^ T[(C ^ (224|((c>>12)&15)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|((c>>6)&63)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
}
}
return C ^ -1;
}
CRC32.table = T;
CRC32.bstr = crc32_bstr;
CRC32.buf = crc32_buf;
CRC32.str = crc32_str;
}));
/* [MS-CFB] v20171201 */
var CFB = (function _CFB(){
var exports/*:CFBModule*/ = /*::(*/{}/*:: :any)*/;
exports.version = '1.0.8';
exports.version = '1.1.0';
/* [MS-CFB] 2.6.4 */
function namecmp(l/*:string*/, r/*:string*/)/*:number*/ {
var L = l.split("/"), R = r.split("/");
@ -208,9 +316,75 @@ function filename(p/*:string*/)/*:string*/ {
var c = p.lastIndexOf("/");
return (c === -1) ? p : p.slice(c+1);
}
/* -------------------------------------------------------------------------- */
/* DOS Date format:
high|YYYYYYYm.mmmddddd.HHHHHMMM.MMMSSSSS|low
add 1980 to stored year
stored second should be doubled
*/
/* write JS date to buf as a DOS date */
function write_dos_date(buf/*:CFBlob*/, date/*:Date|string*/) {
if(typeof date === "string") date = new Date(date);
var hms/*:number*/ = date.getHours();
hms = hms << 6 | date.getMinutes();
hms = hms << 5 | (date.getSeconds()>>>1);
buf.write_shift(2, hms);
var ymd/*:number*/ = (date.getFullYear() - 1980);
ymd = ymd << 4 | (date.getMonth()+1);
ymd = ymd << 5 | date.getDate();
buf.write_shift(2, ymd);
}
/* read four bytes from buf and interpret as a DOS date */
function parse_dos_date(buf/*:CFBlob*/)/*:Date*/ {
var hms = buf.read_shift(2) & 0xFFFF;
var ymd = buf.read_shift(2) & 0xFFFF;
var val = new Date();
var d = ymd & 0x1F; ymd >>>= 5;
var m = ymd & 0x0F; ymd >>>= 4;
val.setMilliseconds(0);
val.setFullYear(ymd + 1980);
val.setMonth(m-1);
val.setDate(d);
var S = hms & 0x1F; hms >>>= 5;
var M = hms & 0x3F; hms >>>= 6;
val.setHours(hms);
val.setMinutes(M);
val.setSeconds(S<<1);
return val;
}
function parse_extra_field(blob/*:CFBlob*/)/*:any*/ {
prep_blob(blob, 0);
var o = /*::(*/{}/*:: :any)*/;
var flags = 0;
while(blob.l <= blob.length - 4) {
var type = blob.read_shift(2);
var sz = blob.read_shift(2), tgt = blob.l + sz;
var p = {};
switch(type) {
/* UNIX-style Timestamps */
case 0x5455: {
flags = blob.read_shift(1);
if(flags & 1) p.mtime = blob.read_shift(4);
/* for some reason, CD flag corresponds to LFH */
if(sz > 5) {
if(flags & 2) p.atime = blob.read_shift(4);
if(flags & 4) p.ctime = blob.read_shift(4);
}
if(p.mtime) p.mt = new Date(p.mtime*1000);
}
break;
}
blob.l = tgt;
o[type] = p;
}
return o;
}
var fs/*:: = require('fs'); */;
function get_fs() { return fs || (fs = require('fs')); }
function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
if(file[0] == 0x50 && file[1] == 0x4b) return parse_zip(file, options);
if(file.length < 512) throw new Error("CFB file size " + file.length + " < 512");
var mver = 3;
var ssz = 512;
@ -231,6 +405,8 @@ var mv = check_get_mver(blob);
mver = mv[0];
switch(mver) {
case 3: ssz = 512; break; case 4: ssz = 4096; break;
case 0: if(mv[1] == 0) return parse_zip(file, options);
/* falls through */
default: throw new Error("Major Version: Expected 3 or 4 saw " + mver);
}
@ -309,6 +485,7 @@ return o;
/* [MS-CFB] 2.2 Compound File Header -- read up to major version */
function check_get_mver(blob/*:CFBlob*/)/*:[number, number]*/ {
if(blob[blob.l] == 0x50 && blob[blob.l + 1] == 0x4b) return [0, 0];
// header signature 8
blob.chk(HEADER_SIGNATURE, 'Header Signature: ');
@ -626,6 +803,7 @@ function rebuild_cfb(cfb/*:CFBContainer*/, f/*:?boolean*/)/*:void*/ {
function _write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ {
var _opts = options || {};
rebuild_cfb(cfb);
if(_opts.fileType == 'zip') return write_zip(cfb, _opts);
var L = (function(cfb/*:CFBContainer*/)/*:Array<number>*/{
var mini_size = 0, fat_size = 0;
for(var i = 0; i < cfb.FileIndex.length; ++i) {
@ -826,6 +1004,551 @@ function write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|string
}
return o;
}
/* node < 8.1 zlib does not expose bytesRead, so default to pure JS */
var _zlib;
function use_zlib(zlib) { try {
var InflateRaw = zlib.InflateRaw;
var InflRaw = new InflateRaw();
InflRaw._processChunk(new Uint8Array([3, 0]), InflRaw._finishFlushFlag);
if(InflRaw.bytesRead) _zlib = zlib;
else throw new Error("zlib does not expose bytesRead");
} catch(e) {console.error("cannot use native zlib: " + (e.message || e)); } }
function _inflateRawSync(payload, usz) {
if(!_zlib) return _inflate(payload, usz);
var InflateRaw = _zlib.InflateRaw;
var InflRaw = new InflateRaw();
var out = InflRaw._processChunk(payload.slice(payload.l), InflRaw._finishFlushFlag);
payload.l += InflRaw.bytesRead;
return out;
}
function _deflateRawSync(payload) {
return _zlib ? _zlib.deflateRawSync(payload) : _deflate(payload);
}
var CLEN_ORDER = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];
/* LEN_ID = [ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285 ]; */
var LEN_LN = [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13 , 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 ];
/* DST_ID = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]; */
var DST_LN = [ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 ];
function bit_swap_8(n) { var t = (((((n<<1)|(n<<11)) & 0x22110) | (((n<<5)|(n<<15)) & 0x88440))); return ((t>>16) | (t>>8) |t)&0xFF; }
var use_typed_arrays = typeof Uint8Array !== 'undefined';
var bitswap8 = use_typed_arrays ? new Uint8Array(1<<8) : [];
for(var q = 0; q < (1<<8); ++q) bitswap8[q] = bit_swap_8(q);
function bit_swap_n(n, b) {
var rev = bitswap8[n & 0xFF];
if(b <= 8) return rev >>> (8-b);
rev = (rev << 8) | bitswap8[(n>>8)&0xFF];
if(b <= 16) return rev >>> (16-b);
rev = (rev << 8) | bitswap8[(n>>16)&0xFF];
return rev >>> (24-b);
}
/* helpers for unaligned bit reads */
function read_bits_2(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 6 ? 0 : buf[h+1]<<8))>>>w)& 0x03; }
function read_bits_3(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 5 ? 0 : buf[h+1]<<8))>>>w)& 0x07; }
function read_bits_4(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 4 ? 0 : buf[h+1]<<8))>>>w)& 0x0F; }
function read_bits_5(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 3 ? 0 : buf[h+1]<<8))>>>w)& 0x1F; }
function read_bits_7(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 1 ? 0 : buf[h+1]<<8))>>>w)& 0x7F; }
/* works up to n = 3 * 8 + 1 = 25 */
function read_bits_n(buf, bl, n) {
var w = (bl&7), h = (bl>>>3), f = ((1<<n)-1);
var v = buf[h] >>> w;
if(n < 8 - w) return v & f;
v |= buf[h+1]<<(8-w);
if(n < 16 - w) return v & f;
v |= buf[h+2]<<(16-w);
if(n < 24 - w) return v & f;
v |= buf[h+3]<<(24-w);
return v & f;
}
/* until ArrayBuffer#realloc is a thing, fake a realloc */
function realloc(b, sz/*:number*/) {
var L = b.length, M = 2*L > sz ? 2*L : sz + 5, i = 0;
if(L >= sz) return b;
if(has_buf) {
var o = new_unsafe_buf(M);
// $FlowIgnore
if(b.copy) b.copy(o);
else for(; i < b.length; ++i) o[i] = b[i];
return o;
} else if(use_typed_arrays) {
var a = new Uint8Array(M);
if(a.set) a.set(b);
else for(; i < b.length; ++i) a[i] = b[i];
return a;
}
b.length = M;
return b;
}
/* zero-filled arrays for older browsers */
function zero_fill_array(n) {
var o = new Array(n);
for(var i = 0; i < n; ++i) o[i] = 0;
return o;
}var _deflate = (function() {
var _deflateRaw = (function() {
return function deflateRaw(data, out) {
var boff = 0;
while(boff < data.length) {
var L = Math.min(0xFFFF, data.length - boff);
var h = boff + L == data.length;
/* TODO: this is only type 0 stored */
out.write_shift(1, +h);
out.write_shift(2, L);
out.write_shift(2, (~L) & 0xFFFF);
while(L-- > 0) out[out.l++] = data[boff++];
}
return out.l;
};
})();
return function(data) {
var buf = new_buf(50+Math.floor(data.length*1.1));
var off = _deflateRaw(data, buf);
return buf.slice(0, off);
};
})();
/* modified inflate function also moves original read head */
/* build tree (used for literals and lengths) */
function build_tree(clens, cmap, MAX/*:number*/)/*:number*/ {
var maxlen = 1, w = 0, i = 0, j = 0, ccode = 0, L = clens.length;
var bl_count = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
for(i = 0; i < 32; ++i) bl_count[i] = 0;
for(i = L; i < MAX; ++i) clens[i] = 0;
L = clens.length;
var ctree = use_typed_arrays ? new Uint16Array(L) : zero_fill_array(L); // []
/* build code tree */
for(i = 0; i < L; ++i) {
bl_count[(w = clens[i])]++;
if(maxlen < w) maxlen = w;
ctree[i] = 0;
}
bl_count[0] = 0;
for(i = 1; i <= maxlen; ++i) bl_count[i+16] = (ccode = (ccode + bl_count[i-1])<<1);
for(i = 0; i < L; ++i) {
ccode = clens[i];
if(ccode != 0) ctree[i] = bl_count[ccode+16]++;
}
/* cmap[maxlen + 4 bits] = (off&15) + (lit<<4) reverse mapping */
var cleni = 0;
for(i = 0; i < L; ++i) {
cleni = clens[i];
if(cleni != 0) {
ccode = bit_swap_n(ctree[i], maxlen)>>(maxlen-cleni);
for(j = (1<<(maxlen + 4 - cleni)) - 1; j>=0; --j)
cmap[ccode|(j<<cleni)] = (cleni&15) | (i<<4);
}
}
return maxlen;
}
var fix_lmap = use_typed_arrays ? new Uint16Array(512) : zero_fill_array(512);
var fix_dmap = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
if(!use_typed_arrays) {
for(var i = 0; i < 512; ++i) fix_lmap[i] = 0;
for(i = 0; i < 32; ++i) fix_dmap[i] = 0;
}
(function() {
var dlens/*:Array<number>*/ = [];
var i = 0;
for(;i<32; i++) dlens.push(5);
build_tree(dlens, fix_dmap, 32);
var clens/*:Array<number>*/ = [];
i = 0;
for(; i<=143; i++) clens.push(8);
for(; i<=255; i++) clens.push(9);
for(; i<=279; i++) clens.push(7);
for(; i<=287; i++) clens.push(8);
build_tree(clens, fix_lmap, 288);
})();
var dyn_lmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
var dyn_dmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
var dyn_cmap = use_typed_arrays ? new Uint16Array(128) : zero_fill_array(128);
var dyn_len_1 = 1, dyn_len_2 = 1;
/* 5.5.3 Expanding Huffman Codes */
function dyn(data, boff/*:number*/) {
/* nomenclature from RFC1951 refers to bit values; these are offset by the implicit constant */
var _HLIT = read_bits_5(data, boff) + 257; boff += 5;
var _HDIST = read_bits_5(data, boff) + 1; boff += 5;
var _HCLEN = read_bits_4(data, boff) + 4; boff += 4;
var w = 0;
/* grab and store code lengths */
var clens = use_typed_arrays ? new Uint8Array(19) : zero_fill_array(19);
var ctree = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
var maxlen = 1;
var bl_count = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
var next_code = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
var L = clens.length; /* 19 */
for(var i = 0; i < _HCLEN; ++i) {
clens[CLEN_ORDER[i]] = w = read_bits_3(data, boff);
if(maxlen < w) maxlen = w;
bl_count[w]++;
boff += 3;
}
/* build code tree */
var ccode = 0;
bl_count[0] = 0;
for(i = 1; i <= maxlen; ++i) next_code[i] = ccode = (ccode + bl_count[i-1])<<1;
for(i = 0; i < L; ++i) if((ccode = clens[i]) != 0) ctree[i] = next_code[ccode]++;
/* cmap[7 bits from stream] = (off&7) + (lit<<3) */
var cleni = 0;
for(i = 0; i < L; ++i) {
cleni = clens[i];
if(cleni != 0) {
ccode = bitswap8[ctree[i]]>>(8-cleni);
for(var j = (1<<(7-cleni))-1; j>=0; --j) dyn_cmap[ccode|(j<<cleni)] = (cleni&7) | (i<<3);
}
}
/* read literal and dist codes at once */
var hcodes/*:Array<number>*/ = [];
maxlen = 1;
for(; hcodes.length < _HLIT + _HDIST;) {
ccode = dyn_cmap[read_bits_7(data, boff)];
boff += ccode & 7;
switch((ccode >>>= 3)) {
case 16:
w = 3 + read_bits_2(data, boff); boff += 2;
ccode = hcodes[hcodes.length - 1];
while(w-- > 0) hcodes.push(ccode);
break;
case 17:
w = 3 + read_bits_3(data, boff); boff += 3;
while(w-- > 0) hcodes.push(0);
break;
case 18:
w = 11 + read_bits_7(data, boff); boff += 7;
while(w -- > 0) hcodes.push(0);
break;
default:
hcodes.push(ccode);
if(maxlen < ccode) maxlen = ccode;
break;
}
}
/* build literal / length trees */
var h1 = hcodes.slice(0, _HLIT), h2 = hcodes.slice(_HLIT);
for(i = _HLIT; i < 286; ++i) h1[i] = 0;
for(i = _HDIST; i < 30; ++i) h2[i] = 0;
dyn_len_1 = build_tree(h1, dyn_lmap, 286);
dyn_len_2 = build_tree(h2, dyn_dmap, 30);
return boff;
}
/* return [ data, bytesRead ] */
function inflate(data, usz/*:number*/) {
/* shortcircuit for empty buffer [0x03, 0x00] */
if(data[0] == 3 && !(data[1] & 0x3)) { return [new_raw_buf(usz), 2]; }
/* bit offset */
var boff = 0;
/* header includes final bit and type bits */
var header = 0;
var outbuf = new_unsafe_buf(usz ? usz : (1<<18));
var woff = 0;
var OL = outbuf.length>>>0;
var max_len_1 = 0, max_len_2 = 0;
while((header&1) == 0) {
header = read_bits_3(data, boff); boff += 3;
if((header >>> 1) == 0) {
/* Stored block */
if(boff & 7) boff += 8 - (boff&7);
/* 2 bytes sz, 2 bytes bit inverse */
var sz = data[boff>>>3] | data[(boff>>>3)+1]<<8;
boff += 32;
/* push sz bytes */
if(!usz && OL < woff + sz) { outbuf = realloc(outbuf, woff + sz); OL = outbuf.length; }
if(typeof data.copy === 'function') {
// $FlowIgnore
data.copy(outbuf, woff, boff>>>3, (boff>>>3)+sz);
woff += sz; boff += 8*sz;
} else while(sz-- > 0) { outbuf[woff++] = data[boff>>>3]; boff += 8; }
continue;
} else if((header >>> 1) == 1) {
/* Fixed Huffman */
max_len_1 = 9; max_len_2 = 5;
} else {
/* Dynamic Huffman */
boff = dyn(data, boff);
max_len_1 = dyn_len_1; max_len_2 = dyn_len_2;
}
if(!usz && (OL < woff + 32767)) { outbuf = realloc(outbuf, woff + 32767); OL = outbuf.length; }
for(;;) { // while(true) is apparently out of vogue in modern JS circles
/* ingest code and move read head */
var bits = read_bits_n(data, boff, max_len_1);
var code = (header>>>1) == 1 ? fix_lmap[bits] : dyn_lmap[bits];
boff += code & 15; code >>>= 4;
/* 0-255 are literals, 256 is end of block token, 257+ are copy tokens */
if(((code>>>8)&0xFF) === 0) outbuf[woff++] = code;
else if(code == 256) break;
else {
code -= 257;
var len_eb = (code < 8) ? 0 : ((code-4)>>2); if(len_eb > 5) len_eb = 0;
var tgt = woff + LEN_LN[code];
/* length extra bits */
if(len_eb > 0) {
tgt += read_bits_n(data, boff, len_eb);
boff += len_eb;
}
/* dist code */
bits = read_bits_n(data, boff, max_len_2);
code = (header>>>1) == 1 ? fix_dmap[bits] : dyn_dmap[bits];
boff += code & 15; code >>>= 4;
var dst_eb = (code < 4 ? 0 : (code-2)>>1);
var dst = DST_LN[code];
/* dist extra bits */
if(dst_eb > 0) {
dst += read_bits_n(data, boff, dst_eb);
boff += dst_eb;
}
/* in the common case, manual byte copy is faster than TA set / Buffer copy */
if(!usz && OL < tgt) { outbuf = realloc(outbuf, tgt); OL = outbuf.length; }
while(woff < tgt) { outbuf[woff] = outbuf[woff - dst]; ++woff; }
}
}
}
return [usz ? outbuf : outbuf.slice(0, woff), (boff+7)>>>3];
}
function _inflate(payload, usz) {
var data = payload.slice(payload.l||0);
var out = inflate(data, usz);
payload.l += out[1];
return out[0];
}
function warn_or_throw(wrn, msg) {
if(wrn) { if(typeof console !== 'undefined') console.error(msg); }
else throw new Error(msg);
}
function parse_zip(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
var blob/*:CFBlob*/ = /*::(*/file/*:: :any)*/;
prep_blob(blob, 0);
var FileIndex/*:CFBFileIndex*/ = [], FullPaths/*:Array<string>*/ = [];
var o = {
FileIndex: FileIndex,
FullPaths: FullPaths
};
init_cfb(o, { root: options.root });
/* find end of central directory, start just after signature */
var i = blob.length - 4;
while((blob[i] != 0x50 || blob[i+1] != 0x4b || blob[i+2] != 0x05 || blob[i+3] != 0x06) && i >= 0) --i;
blob.l = i + 4;
/* parse end of central directory */
blob.l += 4;
var fcnt = blob.read_shift(2);
blob.l += 6;
var start_cd = blob.read_shift(4);
/* parse central directory */
blob.l = start_cd;
for(i = 0; i < fcnt; ++i) {
/* trust local file header instead of CD entry */
blob.l += 20;
var csz = blob.read_shift(4);
var usz = blob.read_shift(4);
var namelen = blob.read_shift(2);
var efsz = blob.read_shift(2);
var fcsz = blob.read_shift(2);
blob.l += 8;
var offset = blob.read_shift(4);
var EF = parse_extra_field(/*::(*/blob.slice(blob.l+namelen, blob.l+namelen+efsz)/*:: :any)*/);
blob.l += namelen + efsz + fcsz;
var L = blob.l;
blob.l = offset + 4;
parse_local_file(blob, csz, usz, o, EF);
blob.l = L;
}
return o;
}
/* head starts just after local file header signature */
function parse_local_file(blob/*:CFBlob*/, csz/*:number*/, usz/*:number*/, o/*:CFBContainer*/, EF) {
/* [local file header] */
blob.l += 2;
var flags = blob.read_shift(2);
var meth = blob.read_shift(2);
var date = parse_dos_date(blob);
if(flags & 0x2041) throw new Error("Unsupported ZIP encryption");
var crc32 = blob.read_shift(4);
var _csz = blob.read_shift(4);
var _usz = blob.read_shift(4);
var namelen = blob.read_shift(2);
var efsz = blob.read_shift(2);
// TODO: flags & (1<<11) // UTF8
var name = ""; for(var i = 0; i < namelen; ++i) name += String.fromCharCode(blob[blob.l++]);
if(efsz) {
var ef = parse_extra_field(/*::(*/blob.slice(blob.l, blob.l + efsz)/*:: :any)*/);
if((ef[0x5455]||{}).mt) date = ef[0x5455].mt;
if(((EF||{})[0x5455]||{}).mt) date = EF[0x5455].mt;
}
blob.l += efsz;
/* [encryption header] */
/* [file data] */
var data = blob.slice(blob.l, blob.l + _csz);
switch(meth) {
case 8: data = _inflateRawSync(blob, _usz); break;
case 0: break;
default: throw new Error("Unsupported ZIP Compression method " + meth);
}
/* [data descriptor] */
var wrn = false;
if(flags & 8) {
crc32 = blob.read_shift(4);
if(crc32 == 0x08074b50) { crc32 = blob.read_shift(4); wrn = true; }
_csz = blob.read_shift(4);
_usz = blob.read_shift(4);
}
if(_csz != csz) warn_or_throw(wrn, "Bad compressed size: " + csz + " != " + _csz);
if(_usz != usz) warn_or_throw(wrn, "Bad uncompressed size: " + usz + " != " + _usz);
var _crc32 = CRC32.buf(data, 0);
if(crc32 != _crc32) warn_or_throw(wrn, "Bad CRC32 checksum: " + crc32 + " != " + _crc32);
cfb_add(o, name, data, {unsafe: true, mt: date});
}
function write_zip(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ {
var _opts = options || {};
var out = [], cdirs = [];
var o/*:CFBlob*/ = new_buf(1);
var method = (_opts.compression ? 8 : 0), flags = 0;
var desc = false;
if(desc) flags |= 8;
var i = 0, j = 0;
var start_cd = 0, fcnt = 0;
var root = cfb.FullPaths[0], fp = root, fi = cfb.FileIndex[0];
var crcs = [];
var sz_cd = 0;
for(i = 1; i < cfb.FullPaths.length; ++i) {
fp = cfb.FullPaths[i].slice(root.length); fi = cfb.FileIndex[i];
if(!fi.size || !fi.content || fp == "\u0001Sh33tJ5") continue;
var start = start_cd;
/* TODO: CP437 filename */
var namebuf = new_buf(fp.length);
for(j = 0; j < fp.length; ++j) namebuf.write_shift(1, fp.charCodeAt(j) & 0x7F);
namebuf = namebuf.slice(0, namebuf.l);
crcs[fcnt] = CRC32.buf(/*::((*/fi.content/*::||[]):any)*/, 0);
var outbuf = fi.content/*::||[]*/;
if(method == 8) outbuf = _deflateRawSync(outbuf);
/* local file header */
o = new_buf(30);
o.write_shift(4, 0x04034b50);
o.write_shift(2, 20);
o.write_shift(2, flags);
o.write_shift(2, method);
/* TODO: last mod file time/date */
if(fi.mt) write_dos_date(o, fi.mt);
else o.write_shift(4, 0);
o.write_shift(-4, (flags & 8) ? 0 : crcs[fcnt]);
o.write_shift(4, (flags & 8) ? 0 : outbuf.length);
o.write_shift(4, (flags & 8) ? 0 : /*::(*/fi.content/*::||[])*/.length);
o.write_shift(2, namebuf.length);
o.write_shift(2, 0);
start_cd += o.length;
out.push(o);
start_cd += namebuf.length;
out.push(namebuf);
/* TODO: encryption header ? */
start_cd += outbuf.length;
out.push(outbuf);
/* data descriptor */
if(flags & 8) {
o = new_buf(12);
o.write_shift(-4, crcs[fcnt]);
o.write_shift(4, outbuf.length);
o.write_shift(4, /*::(*/fi.content/*::||[])*/.length);
start_cd += o.l;
out.push(o);
}
/* central directory */
o = new_buf(46);
o.write_shift(4, 0x02014b50);
o.write_shift(2, 0);
o.write_shift(2, 20);
o.write_shift(2, flags);
o.write_shift(2, method);
o.write_shift(4, 0); /* TODO: last mod file time/date */
o.write_shift(-4, crcs[fcnt]);
o.write_shift(4, outbuf.length);
o.write_shift(4, /*::(*/fi.content/*::||[])*/.length);
o.write_shift(2, namebuf.length);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(4, 0);
o.write_shift(4, start);
sz_cd += o.l;
cdirs.push(o);
sz_cd += namebuf.length;
cdirs.push(namebuf);
++fcnt;
}
/* end of central directory */
o = new_buf(22);
o.write_shift(4, 0x06054b50);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, fcnt);
o.write_shift(2, fcnt);
o.write_shift(4, sz_cd);
o.write_shift(4, start_cd);
o.write_shift(2, 0);
return bconcat(([bconcat((out/*:any*/)), bconcat(cdirs), o]/*:any*/));
}
function cfb_new(opts/*:?any*/)/*:CFBContainer*/ {
var o/*:CFBContainer*/ = ({}/*:any*/);
init_cfb(o, opts);
@ -853,6 +1576,8 @@ function cfb_add(cfb/*:CFBContainer*/, name/*:string*/, content/*:?RawBytes*/, o
file.size = content ? content.length : 0;
if(opts) {
if(opts.CLSID) file.clsid = opts.CLSID;
if(opts.mt) file.mt = opts.mt;
if(opts.ct) file.ct = opts.ct;
}
return file;
}
@ -896,6 +1621,9 @@ exports.utils = {
CheckField: CheckField,
prep_blob: prep_blob,
bconcat: bconcat,
use_zlib: use_zlib,
_deflateRaw: _deflate,
_inflateRaw: _inflate,
consts: consts
};

736
cfb.js

@ -2,7 +2,7 @@
/* vim: set ts=2: */
/*jshint eqnull:true */
/*exported CFB */
/*global module, require:false, process:false, Buffer:false, Uint8Array:false */
/*global module, require:false, process:false, Buffer:false, Uint8Array:false, Uint16Array:false */
var Base64 = (function make_b64(){
var map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
@ -58,6 +58,8 @@ if(typeof Buffer !== 'undefined') {
Buffer_from = nbfs ? function(buf, enc) { return (enc) ? new Buffer(buf, enc) : new Buffer(buf); } : Buffer.from.bind(Buffer);
// $FlowIgnore
if(!Buffer.alloc) Buffer.alloc = function(n) { return new Buffer(n); };
// $FlowIgnore
if(!Buffer.allocUnsafe) Buffer.allocUnsafe = function(n) { return new Buffer(n); };
}
function new_raw_buf(len) {
@ -66,8 +68,14 @@ function new_raw_buf(len) {
/* jshint +W056 */
}
function new_unsafe_buf(len) {
/* jshint -W056 */
return has_buf ? Buffer.allocUnsafe(len) : new Array(len);
/* jshint +W056 */
}
var s2a = function s2a(s) {
if(has_buf) return Buffer.from(s, "binary");
if(has_buf) return Buffer_from(s, "binary");
return s.split("").map(function(x){ return x.charCodeAt(0) & 0xff; });
};
@ -96,7 +104,6 @@ if(has_buf) {
};
__hexlify = function(b,s,l) { return Buffer.isBuffer(b) ? b.toString('hex',s,s+l) : ___hexlify(b,s,l); };
__toBuffer = function(bufs) { return (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat((bufs[0])) : ___toBuffer(bufs);};
// $FlowIgnore
s2a = function(s) { return Buffer_from(s, "binary"); };
bconcat = function(bufs) { return Buffer.isBuffer(bufs[0]) ? Buffer.concat(bufs) : __bconcat(bufs); };
}
@ -166,10 +173,111 @@ function new_buf(sz) {
return o;
}
/* crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
/*exported CRC32 */
var CRC32;
(function (factory) {
/*jshint ignore:start */
/*eslint-disable */
factory(CRC32 = {});
/*eslint-enable */
/*jshint ignore:end */
}(function(CRC32) {
CRC32.version = '1.2.0';
/* see perf/crc32table.js */
/*global Int32Array */
function signed_crc_table() {
var c = 0, table = new Array(256);
for(var n =0; n != 256; ++n){
c = n;
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
table[n] = c;
}
return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table;
}
var T = signed_crc_table();
function crc32_bstr(bstr, seed) {
var C = seed ^ -1, L = bstr.length - 1;
for(var i = 0; i < L;) {
C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
}
if(i === L) C = (C>>>8) ^ T[(C ^ bstr.charCodeAt(i))&0xFF];
return C ^ -1;
}
function crc32_buf(buf, seed) {
if(buf.length > 10000) return crc32_buf_8(buf, seed);
var C = seed ^ -1, L = buf.length - 3;
for(var i = 0; i < L;) {
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
}
while(i < L+3) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
return C ^ -1;
}
function crc32_buf_8(buf, seed) {
var C = seed ^ -1, L = buf.length - 7;
for(var i = 0; i < L;) {
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
}
while(i < L+7) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
return C ^ -1;
}
function crc32_str(str, seed) {
var C = seed ^ -1;
for(var i = 0, L=str.length, c, d; i < L;) {
c = str.charCodeAt(i++);
if(c < 0x80) {
C = (C>>>8) ^ T[(C ^ c)&0xFF];
} else if(c < 0x800) {
C = (C>>>8) ^ T[(C ^ (192|((c>>6)&31)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
} else if(c >= 0xD800 && c < 0xE000) {
c = (c&1023)+64; d = str.charCodeAt(i++)&1023;
C = (C>>>8) ^ T[(C ^ (240|((c>>8)&7)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|((c>>2)&63)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|(d&63)))&0xFF];
} else {
C = (C>>>8) ^ T[(C ^ (224|((c>>12)&15)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|((c>>6)&63)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
}
}
return C ^ -1;
}
CRC32.table = T;
CRC32.bstr = crc32_bstr;
CRC32.buf = crc32_buf;
CRC32.str = crc32_str;
}));
/* [MS-CFB] v20171201 */
var CFB = (function _CFB(){
var exports = {};
exports.version = '1.0.8';
exports.version = '1.1.0';
/* [MS-CFB] 2.6.4 */
function namecmp(l, r) {
var L = l.split("/"), R = r.split("/");
@ -190,9 +298,75 @@ function filename(p) {
var c = p.lastIndexOf("/");
return (c === -1) ? p : p.slice(c+1);
}
/* -------------------------------------------------------------------------- */
/* DOS Date format:
high|YYYYYYYm.mmmddddd.HHHHHMMM.MMMSSSSS|low
add 1980 to stored year
stored second should be doubled
*/
/* write JS date to buf as a DOS date */
function write_dos_date(buf, date) {
if(typeof date === "string") date = new Date(date);
var hms = date.getHours();
hms = hms << 6 | date.getMinutes();
hms = hms << 5 | (date.getSeconds()>>>1);
buf.write_shift(2, hms);
var ymd = (date.getFullYear() - 1980);
ymd = ymd << 4 | (date.getMonth()+1);
ymd = ymd << 5 | date.getDate();
buf.write_shift(2, ymd);
}
/* read four bytes from buf and interpret as a DOS date */
function parse_dos_date(buf) {
var hms = buf.read_shift(2) & 0xFFFF;
var ymd = buf.read_shift(2) & 0xFFFF;
var val = new Date();
var d = ymd & 0x1F; ymd >>>= 5;
var m = ymd & 0x0F; ymd >>>= 4;
val.setMilliseconds(0);
val.setFullYear(ymd + 1980);
val.setMonth(m-1);
val.setDate(d);
var S = hms & 0x1F; hms >>>= 5;
var M = hms & 0x3F; hms >>>= 6;
val.setHours(hms);
val.setMinutes(M);
val.setSeconds(S<<1);
return val;
}
function parse_extra_field(blob) {
prep_blob(blob, 0);
var o = {};
var flags = 0;
while(blob.l <= blob.length - 4) {
var type = blob.read_shift(2);
var sz = blob.read_shift(2), tgt = blob.l + sz;
var p = {};
switch(type) {
/* UNIX-style Timestamps */
case 0x5455: {
flags = blob.read_shift(1);
if(flags & 1) p.mtime = blob.read_shift(4);
/* for some reason, CD flag corresponds to LFH */
if(sz > 5) {
if(flags & 2) p.atime = blob.read_shift(4);
if(flags & 4) p.ctime = blob.read_shift(4);
}
if(p.mtime) p.mt = new Date(p.mtime*1000);
}
break;
}
blob.l = tgt;
o[type] = p;
}
return o;
}
var fs;
function get_fs() { return fs || (fs = require('fs')); }
function parse(file, options) {
if(file[0] == 0x50 && file[1] == 0x4b) return parse_zip(file, options);
if(file.length < 512) throw new Error("CFB file size " + file.length + " < 512");
var mver = 3;
var ssz = 512;
@ -213,6 +387,8 @@ var mv = check_get_mver(blob);
mver = mv[0];
switch(mver) {
case 3: ssz = 512; break; case 4: ssz = 4096; break;
case 0: if(mv[1] == 0) return parse_zip(file, options);
/* falls through */
default: throw new Error("Major Version: Expected 3 or 4 saw " + mver);
}
@ -291,6 +467,7 @@ return o;
/* [MS-CFB] 2.2 Compound File Header -- read up to major version */
function check_get_mver(blob) {
if(blob[blob.l] == 0x50 && blob[blob.l + 1] == 0x4b) return [0, 0];
// header signature 8
blob.chk(HEADER_SIGNATURE, 'Header Signature: ');
@ -608,6 +785,7 @@ function rebuild_cfb(cfb, f) {
function _write(cfb, options) {
var _opts = options || {};
rebuild_cfb(cfb);
if(_opts.fileType == 'zip') return write_zip(cfb, _opts);
var L = (function(cfb){
var mini_size = 0, fat_size = 0;
for(var i = 0; i < cfb.FileIndex.length; ++i) {
@ -802,6 +980,551 @@ function write(cfb, options) {
}
return o;
}
/* node < 8.1 zlib does not expose bytesRead, so default to pure JS */
var _zlib;
function use_zlib(zlib) { try {
var InflateRaw = zlib.InflateRaw;
var InflRaw = new InflateRaw();
InflRaw._processChunk(new Uint8Array([3, 0]), InflRaw._finishFlushFlag);
if(InflRaw.bytesRead) _zlib = zlib;
else throw new Error("zlib does not expose bytesRead");
} catch(e) {console.error("cannot use native zlib: " + (e.message || e)); } }
function _inflateRawSync(payload, usz) {
if(!_zlib) return _inflate(payload, usz);
var InflateRaw = _zlib.InflateRaw;
var InflRaw = new InflateRaw();
var out = InflRaw._processChunk(payload.slice(payload.l), InflRaw._finishFlushFlag);
payload.l += InflRaw.bytesRead;
return out;
}
function _deflateRawSync(payload) {
return _zlib ? _zlib.deflateRawSync(payload) : _deflate(payload);
}
var CLEN_ORDER = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];
/* LEN_ID = [ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285 ]; */
var LEN_LN = [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13 , 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 ];
/* DST_ID = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]; */
var DST_LN = [ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 ];
function bit_swap_8(n) { var t = (((((n<<1)|(n<<11)) & 0x22110) | (((n<<5)|(n<<15)) & 0x88440))); return ((t>>16) | (t>>8) |t)&0xFF; }
var use_typed_arrays = typeof Uint8Array !== 'undefined';
var bitswap8 = use_typed_arrays ? new Uint8Array(1<<8) : [];
for(var q = 0; q < (1<<8); ++q) bitswap8[q] = bit_swap_8(q);
function bit_swap_n(n, b) {
var rev = bitswap8[n & 0xFF];
if(b <= 8) return rev >>> (8-b);
rev = (rev << 8) | bitswap8[(n>>8)&0xFF];
if(b <= 16) return rev >>> (16-b);
rev = (rev << 8) | bitswap8[(n>>16)&0xFF];
return rev >>> (24-b);
}
/* helpers for unaligned bit reads */
function read_bits_2(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 6 ? 0 : buf[h+1]<<8))>>>w)& 0x03; }
function read_bits_3(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 5 ? 0 : buf[h+1]<<8))>>>w)& 0x07; }
function read_bits_4(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 4 ? 0 : buf[h+1]<<8))>>>w)& 0x0F; }
function read_bits_5(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 3 ? 0 : buf[h+1]<<8))>>>w)& 0x1F; }
function read_bits_7(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 1 ? 0 : buf[h+1]<<8))>>>w)& 0x7F; }
/* works up to n = 3 * 8 + 1 = 25 */
function read_bits_n(buf, bl, n) {
var w = (bl&7), h = (bl>>>3), f = ((1<<n)-1);
var v = buf[h] >>> w;
if(n < 8 - w) return v & f;
v |= buf[h+1]<<(8-w);
if(n < 16 - w) return v & f;
v |= buf[h+2]<<(16-w);
if(n < 24 - w) return v & f;
v |= buf[h+3]<<(24-w);
return v & f;
}
/* until ArrayBuffer#realloc is a thing, fake a realloc */
function realloc(b, sz) {
var L = b.length, M = 2*L > sz ? 2*L : sz + 5, i = 0;
if(L >= sz) return b;
if(has_buf) {
var o = new_unsafe_buf(M);
// $FlowIgnore
if(b.copy) b.copy(o);
else for(; i < b.length; ++i) o[i] = b[i];
return o;
} else if(use_typed_arrays) {
var a = new Uint8Array(M);
if(a.set) a.set(b);
else for(; i < b.length; ++i) a[i] = b[i];
return a;
}
b.length = M;
return b;
}
/* zero-filled arrays for older browsers */
function zero_fill_array(n) {
var o = new Array(n);
for(var i = 0; i < n; ++i) o[i] = 0;
return o;
}var _deflate = (function() {
var _deflateRaw = (function() {
return function deflateRaw(data, out) {
var boff = 0;
while(boff < data.length) {
var L = Math.min(0xFFFF, data.length - boff);
var h = boff + L == data.length;
/* TODO: this is only type 0 stored */
out.write_shift(1, +h);
out.write_shift(2, L);
out.write_shift(2, (~L) & 0xFFFF);
while(L-- > 0) out[out.l++] = data[boff++];
}
return out.l;
};
})();
return function(data) {
var buf = new_buf(50+Math.floor(data.length*1.1));
var off = _deflateRaw(data, buf);
return buf.slice(0, off);
};
})();
/* modified inflate function also moves original read head */
/* build tree (used for literals and lengths) */
function build_tree(clens, cmap, MAX) {
var maxlen = 1, w = 0, i = 0, j = 0, ccode = 0, L = clens.length;
var bl_count = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
for(i = 0; i < 32; ++i) bl_count[i] = 0;
for(i = L; i < MAX; ++i) clens[i] = 0;
L = clens.length;
var ctree = use_typed_arrays ? new Uint16Array(L) : zero_fill_array(L); // []
/* build code tree */
for(i = 0; i < L; ++i) {
bl_count[(w = clens[i])]++;
if(maxlen < w) maxlen = w;
ctree[i] = 0;
}
bl_count[0] = 0;
for(i = 1; i <= maxlen; ++i) bl_count[i+16] = (ccode = (ccode + bl_count[i-1])<<1);
for(i = 0; i < L; ++i) {
ccode = clens[i];
if(ccode != 0) ctree[i] = bl_count[ccode+16]++;
}
/* cmap[maxlen + 4 bits] = (off&15) + (lit<<4) reverse mapping */
var cleni = 0;
for(i = 0; i < L; ++i) {
cleni = clens[i];
if(cleni != 0) {
ccode = bit_swap_n(ctree[i], maxlen)>>(maxlen-cleni);
for(j = (1<<(maxlen + 4 - cleni)) - 1; j>=0; --j)
cmap[ccode|(j<<cleni)] = (cleni&15) | (i<<4);
}
}
return maxlen;
}
var fix_lmap = use_typed_arrays ? new Uint16Array(512) : zero_fill_array(512);
var fix_dmap = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
if(!use_typed_arrays) {
for(var i = 0; i < 512; ++i) fix_lmap[i] = 0;
for(i = 0; i < 32; ++i) fix_dmap[i] = 0;
}
(function() {
var dlens = [];
var i = 0;
for(;i<32; i++) dlens.push(5);
build_tree(dlens, fix_dmap, 32);
var clens = [];
i = 0;
for(; i<=143; i++) clens.push(8);
for(; i<=255; i++) clens.push(9);
for(; i<=279; i++) clens.push(7);
for(; i<=287; i++) clens.push(8);
build_tree(clens, fix_lmap, 288);
})();
var dyn_lmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
var dyn_dmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
var dyn_cmap = use_typed_arrays ? new Uint16Array(128) : zero_fill_array(128);
var dyn_len_1 = 1, dyn_len_2 = 1;
/* 5.5.3 Expanding Huffman Codes */
function dyn(data, boff) {
/* nomenclature from RFC1951 refers to bit values; these are offset by the implicit constant */
var _HLIT = read_bits_5(data, boff) + 257; boff += 5;
var _HDIST = read_bits_5(data, boff) + 1; boff += 5;
var _HCLEN = read_bits_4(data, boff) + 4; boff += 4;
var w = 0;
/* grab and store code lengths */
var clens = use_typed_arrays ? new Uint8Array(19) : zero_fill_array(19);
var ctree = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
var maxlen = 1;
var bl_count = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
var next_code = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
var L = clens.length; /* 19 */
for(var i = 0; i < _HCLEN; ++i) {
clens[CLEN_ORDER[i]] = w = read_bits_3(data, boff);
if(maxlen < w) maxlen = w;
bl_count[w]++;
boff += 3;
}
/* build code tree */
var ccode = 0;
bl_count[0] = 0;
for(i = 1; i <= maxlen; ++i) next_code[i] = ccode = (ccode + bl_count[i-1])<<1;
for(i = 0; i < L; ++i) if((ccode = clens[i]) != 0) ctree[i] = next_code[ccode]++;
/* cmap[7 bits from stream] = (off&7) + (lit<<3) */
var cleni = 0;
for(i = 0; i < L; ++i) {
cleni = clens[i];
if(cleni != 0) {
ccode = bitswap8[ctree[i]]>>(8-cleni);
for(var j = (1<<(7-cleni))-1; j>=0; --j) dyn_cmap[ccode|(j<<cleni)] = (cleni&7) | (i<<3);
}
}
/* read literal and dist codes at once */
var hcodes = [];
maxlen = 1;
for(; hcodes.length < _HLIT + _HDIST;) {
ccode = dyn_cmap[read_bits_7(data, boff)];
boff += ccode & 7;
switch((ccode >>>= 3)) {
case 16:
w = 3 + read_bits_2(data, boff); boff += 2;
ccode = hcodes[hcodes.length - 1];
while(w-- > 0) hcodes.push(ccode);
break;
case 17:
w = 3 + read_bits_3(data, boff); boff += 3;
while(w-- > 0) hcodes.push(0);
break;
case 18:
w = 11 + read_bits_7(data, boff); boff += 7;
while(w -- > 0) hcodes.push(0);
break;
default:
hcodes.push(ccode);
if(maxlen < ccode) maxlen = ccode;
break;
}
}
/* build literal / length trees */
var h1 = hcodes.slice(0, _HLIT), h2 = hcodes.slice(_HLIT);
for(i = _HLIT; i < 286; ++i) h1[i] = 0;
for(i = _HDIST; i < 30; ++i) h2[i] = 0;
dyn_len_1 = build_tree(h1, dyn_lmap, 286);
dyn_len_2 = build_tree(h2, dyn_dmap, 30);
return boff;
}
/* return [ data, bytesRead ] */
function inflate(data, usz) {
/* shortcircuit for empty buffer [0x03, 0x00] */
if(data[0] == 3 && !(data[1] & 0x3)) { return [new_raw_buf(usz), 2]; }
/* bit offset */
var boff = 0;
/* header includes final bit and type bits */
var header = 0;
var outbuf = new_unsafe_buf(usz ? usz : (1<<18));
var woff = 0;
var OL = outbuf.length>>>0;
var max_len_1 = 0, max_len_2 = 0;
while((header&1) == 0) {
header = read_bits_3(data, boff); boff += 3;
if((header >>> 1) == 0) {
/* Stored block */
if(boff & 7) boff += 8 - (boff&7);
/* 2 bytes sz, 2 bytes bit inverse */
var sz = data[boff>>>3] | data[(boff>>>3)+1]<<8;
boff += 32;
/* push sz bytes */
if(!usz && OL < woff + sz) { outbuf = realloc(outbuf, woff + sz); OL = outbuf.length; }
if(typeof data.copy === 'function') {
// $FlowIgnore
data.copy(outbuf, woff, boff>>>3, (boff>>>3)+sz);
woff += sz; boff += 8*sz;
} else while(sz-- > 0) { outbuf[woff++] = data[boff>>>3]; boff += 8; }
continue;
} else if((header >>> 1) == 1) {
/* Fixed Huffman */
max_len_1 = 9; max_len_2 = 5;
} else {
/* Dynamic Huffman */
boff = dyn(data, boff);
max_len_1 = dyn_len_1; max_len_2 = dyn_len_2;
}
if(!usz && (OL < woff + 32767)) { outbuf = realloc(outbuf, woff + 32767); OL = outbuf.length; }
for(;;) { // while(true) is apparently out of vogue in modern JS circles
/* ingest code and move read head */
var bits = read_bits_n(data, boff, max_len_1);
var code = (header>>>1) == 1 ? fix_lmap[bits] : dyn_lmap[bits];
boff += code & 15; code >>>= 4;
/* 0-255 are literals, 256 is end of block token, 257+ are copy tokens */
if(((code>>>8)&0xFF) === 0) outbuf[woff++] = code;
else if(code == 256) break;
else {
code -= 257;
var len_eb = (code < 8) ? 0 : ((code-4)>>2); if(len_eb > 5) len_eb = 0;
var tgt = woff + LEN_LN[code];
/* length extra bits */
if(len_eb > 0) {
tgt += read_bits_n(data, boff, len_eb);
boff += len_eb;
}
/* dist code */
bits = read_bits_n(data, boff, max_len_2);
code = (header>>>1) == 1 ? fix_dmap[bits] : dyn_dmap[bits];
boff += code & 15; code >>>= 4;
var dst_eb = (code < 4 ? 0 : (code-2)>>1);
var dst = DST_LN[code];
/* dist extra bits */
if(dst_eb > 0) {
dst += read_bits_n(data, boff, dst_eb);
boff += dst_eb;
}
/* in the common case, manual byte copy is faster than TA set / Buffer copy */
if(!usz && OL < tgt) { outbuf = realloc(outbuf, tgt); OL = outbuf.length; }
while(woff < tgt) { outbuf[woff] = outbuf[woff - dst]; ++woff; }
}
}
}
return [usz ? outbuf : outbuf.slice(0, woff), (boff+7)>>>3];
}
function _inflate(payload, usz) {
var data = payload.slice(payload.l||0);
var out = inflate(data, usz);
payload.l += out[1];
return out[0];
}
function warn_or_throw(wrn, msg) {
if(wrn) { if(typeof console !== 'undefined') console.error(msg); }
else throw new Error(msg);
}
function parse_zip(file, options) {
var blob = file;
prep_blob(blob, 0);
var FileIndex = [], FullPaths = [];
var o = {
FileIndex: FileIndex,
FullPaths: FullPaths
};
init_cfb(o, { root: options.root });
/* find end of central directory, start just after signature */
var i = blob.length - 4;
while((blob[i] != 0x50 || blob[i+1] != 0x4b || blob[i+2] != 0x05 || blob[i+3] != 0x06) && i >= 0) --i;
blob.l = i + 4;
/* parse end of central directory */
blob.l += 4;
var fcnt = blob.read_shift(2);
blob.l += 6;
var start_cd = blob.read_shift(4);
/* parse central directory */
blob.l = start_cd;
for(i = 0; i < fcnt; ++i) {
/* trust local file header instead of CD entry */
blob.l += 20;
var csz = blob.read_shift(4);
var usz = blob.read_shift(4);
var namelen = blob.read_shift(2);
var efsz = blob.read_shift(2);
var fcsz = blob.read_shift(2);
blob.l += 8;
var offset = blob.read_shift(4);
var EF = parse_extra_field(blob.slice(blob.l+namelen, blob.l+namelen+efsz));
blob.l += namelen + efsz + fcsz;
var L = blob.l;
blob.l = offset + 4;
parse_local_file(blob, csz, usz, o, EF);
blob.l = L;
}
return o;
}
/* head starts just after local file header signature */
function parse_local_file(blob, csz, usz, o, EF) {
/* [local file header] */
blob.l += 2;
var flags = blob.read_shift(2);
var meth = blob.read_shift(2);
var date = parse_dos_date(blob);
if(flags & 0x2041) throw new Error("Unsupported ZIP encryption");
var crc32 = blob.read_shift(4);
var _csz = blob.read_shift(4);
var _usz = blob.read_shift(4);
var namelen = blob.read_shift(2);
var efsz = blob.read_shift(2);
// TODO: flags & (1<<11) // UTF8
var name = ""; for(var i = 0; i < namelen; ++i) name += String.fromCharCode(blob[blob.l++]);
if(efsz) {
var ef = parse_extra_field(blob.slice(blob.l, blob.l + efsz));
if((ef[0x5455]||{}).mt) date = ef[0x5455].mt;
if(((EF||{})[0x5455]||{}).mt) date = EF[0x5455].mt;
}
blob.l += efsz;
/* [encryption header] */
/* [file data] */
var data = blob.slice(blob.l, blob.l + _csz);
switch(meth) {
case 8: data = _inflateRawSync(blob, _usz); break;
case 0: break;
default: throw new Error("Unsupported ZIP Compression method " + meth);
}
/* [data descriptor] */
var wrn = false;
if(flags & 8) {
crc32 = blob.read_shift(4);
if(crc32 == 0x08074b50) { crc32 = blob.read_shift(4); wrn = true; }
_csz = blob.read_shift(4);
_usz = blob.read_shift(4);
}
if(_csz != csz) warn_or_throw(wrn, "Bad compressed size: " + csz + " != " + _csz);
if(_usz != usz) warn_or_throw(wrn, "Bad uncompressed size: " + usz + " != " + _usz);
var _crc32 = CRC32.buf(data, 0);
if(crc32 != _crc32) warn_or_throw(wrn, "Bad CRC32 checksum: " + crc32 + " != " + _crc32);
cfb_add(o, name, data, {unsafe: true, mt: date});
}
function write_zip(cfb, options) {
var _opts = options || {};
var out = [], cdirs = [];
var o = new_buf(1);
var method = (_opts.compression ? 8 : 0), flags = 0;
var desc = false;
if(desc) flags |= 8;
var i = 0, j = 0;
var start_cd = 0, fcnt = 0;
var root = cfb.FullPaths[0], fp = root, fi = cfb.FileIndex[0];
var crcs = [];
var sz_cd = 0;
for(i = 1; i < cfb.FullPaths.length; ++i) {
fp = cfb.FullPaths[i].slice(root.length); fi = cfb.FileIndex[i];
if(!fi.size || !fi.content || fp == "\u0001Sh33tJ5") continue;
var start = start_cd;
/* TODO: CP437 filename */
var namebuf = new_buf(fp.length);
for(j = 0; j < fp.length; ++j) namebuf.write_shift(1, fp.charCodeAt(j) & 0x7F);
namebuf = namebuf.slice(0, namebuf.l);
crcs[fcnt] = CRC32.buf(fi.content, 0);
var outbuf = fi.content;
if(method == 8) outbuf = _deflateRawSync(outbuf);
/* local file header */
o = new_buf(30);
o.write_shift(4, 0x04034b50);
o.write_shift(2, 20);
o.write_shift(2, flags);
o.write_shift(2, method);
/* TODO: last mod file time/date */
if(fi.mt) write_dos_date(o, fi.mt);
else o.write_shift(4, 0);
o.write_shift(-4, (flags & 8) ? 0 : crcs[fcnt]);
o.write_shift(4, (flags & 8) ? 0 : outbuf.length);
o.write_shift(4, (flags & 8) ? 0 : fi.content.length);
o.write_shift(2, namebuf.length);
o.write_shift(2, 0);
start_cd += o.length;
out.push(o);
start_cd += namebuf.length;
out.push(namebuf);
/* TODO: encryption header ? */
start_cd += outbuf.length;
out.push(outbuf);
/* data descriptor */
if(flags & 8) {
o = new_buf(12);
o.write_shift(-4, crcs[fcnt]);
o.write_shift(4, outbuf.length);
o.write_shift(4, fi.content.length);
start_cd += o.l;
out.push(o);
}
/* central directory */
o = new_buf(46);
o.write_shift(4, 0x02014b50);
o.write_shift(2, 0);
o.write_shift(2, 20);
o.write_shift(2, flags);
o.write_shift(2, method);
o.write_shift(4, 0); /* TODO: last mod file time/date */
o.write_shift(-4, crcs[fcnt]);
o.write_shift(4, outbuf.length);
o.write_shift(4, fi.content.length);
o.write_shift(2, namebuf.length);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(4, 0);
o.write_shift(4, start);
sz_cd += o.l;
cdirs.push(o);
sz_cd += namebuf.length;
cdirs.push(namebuf);
++fcnt;
}
/* end of central directory */
o = new_buf(22);
o.write_shift(4, 0x06054b50);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, fcnt);
o.write_shift(2, fcnt);
o.write_shift(4, sz_cd);
o.write_shift(4, start_cd);
o.write_shift(2, 0);
return bconcat(([bconcat((out)), bconcat(cdirs), o]));
}
function cfb_new(opts) {
var o = ({});
init_cfb(o, opts);
@ -828,6 +1551,8 @@ file.content = (content);
file.size = content ? content.length : 0;
if(opts) {
if(opts.CLSID) file.clsid = opts.CLSID;
if(opts.mt) file.mt = opts.mt;
if(opts.ct) file.ct = opts.ct;
}
return file;
}
@ -871,6 +1596,9 @@ exports.utils = {
CheckField: CheckField,
prep_blob: prep_blob,
bconcat: bconcat,
use_zlib: use_zlib,
_deflateRaw: _deflate,
_inflateRaw: _inflate,
consts: consts
};

736
dist/cfb.js vendored

@ -2,7 +2,7 @@
/* vim: set ts=2: */
/*jshint eqnull:true */
/*exported CFB */
/*global module, require:false, process:false, Buffer:false, Uint8Array:false */
/*global module, require:false, process:false, Buffer:false, Uint8Array:false, Uint16Array:false */
var Base64 = (function make_b64(){
var map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
@ -58,6 +58,8 @@ if(typeof Buffer !== 'undefined') {
Buffer_from = nbfs ? function(buf, enc) { return (enc) ? new Buffer(buf, enc) : new Buffer(buf); } : Buffer.from.bind(Buffer);
// $FlowIgnore
if(!Buffer.alloc) Buffer.alloc = function(n) { return new Buffer(n); };
// $FlowIgnore
if(!Buffer.allocUnsafe) Buffer.allocUnsafe = function(n) { return new Buffer(n); };
}
function new_raw_buf(len) {
@ -66,8 +68,14 @@ function new_raw_buf(len) {
/* jshint +W056 */
}
function new_unsafe_buf(len) {
/* jshint -W056 */
return has_buf ? Buffer.allocUnsafe(len) : new Array(len);
/* jshint +W056 */
}
var s2a = function s2a(s) {
if(has_buf) return Buffer.from(s, "binary");
if(has_buf) return Buffer_from(s, "binary");
return s.split("").map(function(x){ return x.charCodeAt(0) & 0xff; });
};
@ -96,7 +104,6 @@ if(has_buf) {
};
__hexlify = function(b,s,l) { return Buffer.isBuffer(b) ? b.toString('hex',s,s+l) : ___hexlify(b,s,l); };
__toBuffer = function(bufs) { return (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat((bufs[0])) : ___toBuffer(bufs);};
// $FlowIgnore
s2a = function(s) { return Buffer_from(s, "binary"); };
bconcat = function(bufs) { return Buffer.isBuffer(bufs[0]) ? Buffer.concat(bufs) : __bconcat(bufs); };
}
@ -166,10 +173,111 @@ function new_buf(sz) {
return o;
}
/* crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
/*exported CRC32 */
var CRC32;
(function (factory) {
/*jshint ignore:start */
/*eslint-disable */
factory(CRC32 = {});
/*eslint-enable */
/*jshint ignore:end */
}(function(CRC32) {
CRC32.version = '1.2.0';
/* see perf/crc32table.js */
/*global Int32Array */
function signed_crc_table() {
var c = 0, table = new Array(256);
for(var n =0; n != 256; ++n){
c = n;
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
table[n] = c;
}
return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table;
}
var T = signed_crc_table();
function crc32_bstr(bstr, seed) {
var C = seed ^ -1, L = bstr.length - 1;
for(var i = 0; i < L;) {
C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
}
if(i === L) C = (C>>>8) ^ T[(C ^ bstr.charCodeAt(i))&0xFF];
return C ^ -1;
}
function crc32_buf(buf, seed) {
if(buf.length > 10000) return crc32_buf_8(buf, seed);
var C = seed ^ -1, L = buf.length - 3;
for(var i = 0; i < L;) {
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
}
while(i < L+3) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
return C ^ -1;
}
function crc32_buf_8(buf, seed) {
var C = seed ^ -1, L = buf.length - 7;
for(var i = 0; i < L;) {
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
}
while(i < L+7) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
return C ^ -1;
}
function crc32_str(str, seed) {
var C = seed ^ -1;
for(var i = 0, L=str.length, c, d; i < L;) {
c = str.charCodeAt(i++);
if(c < 0x80) {
C = (C>>>8) ^ T[(C ^ c)&0xFF];
} else if(c < 0x800) {
C = (C>>>8) ^ T[(C ^ (192|((c>>6)&31)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
} else if(c >= 0xD800 && c < 0xE000) {
c = (c&1023)+64; d = str.charCodeAt(i++)&1023;
C = (C>>>8) ^ T[(C ^ (240|((c>>8)&7)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|((c>>2)&63)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|(d&63)))&0xFF];
} else {
C = (C>>>8) ^ T[(C ^ (224|((c>>12)&15)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|((c>>6)&63)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
}
}
return C ^ -1;
}
CRC32.table = T;
CRC32.bstr = crc32_bstr;
CRC32.buf = crc32_buf;
CRC32.str = crc32_str;
}));
/* [MS-CFB] v20171201 */
var CFB = (function _CFB(){
var exports = {};
exports.version = '1.0.8';
exports.version = '1.1.0';
/* [MS-CFB] 2.6.4 */
function namecmp(l, r) {
var L = l.split("/"), R = r.split("/");
@ -190,9 +298,75 @@ function filename(p) {
var c = p.lastIndexOf("/");
return (c === -1) ? p : p.slice(c+1);
}
/* -------------------------------------------------------------------------- */
/* DOS Date format:
high|YYYYYYYm.mmmddddd.HHHHHMMM.MMMSSSSS|low
add 1980 to stored year
stored second should be doubled
*/
/* write JS date to buf as a DOS date */
function write_dos_date(buf, date) {
if(typeof date === "string") date = new Date(date);
var hms = date.getHours();
hms = hms << 6 | date.getMinutes();
hms = hms << 5 | (date.getSeconds()>>>1);
buf.write_shift(2, hms);
var ymd = (date.getFullYear() - 1980);
ymd = ymd << 4 | (date.getMonth()+1);
ymd = ymd << 5 | date.getDate();
buf.write_shift(2, ymd);
}
/* read four bytes from buf and interpret as a DOS date */
function parse_dos_date(buf) {
var hms = buf.read_shift(2) & 0xFFFF;
var ymd = buf.read_shift(2) & 0xFFFF;
var val = new Date();
var d = ymd & 0x1F; ymd >>>= 5;
var m = ymd & 0x0F; ymd >>>= 4;
val.setMilliseconds(0);
val.setFullYear(ymd + 1980);
val.setMonth(m-1);
val.setDate(d);
var S = hms & 0x1F; hms >>>= 5;
var M = hms & 0x3F; hms >>>= 6;
val.setHours(hms);
val.setMinutes(M);
val.setSeconds(S<<1);
return val;
}
function parse_extra_field(blob) {
prep_blob(blob, 0);
var o = {};
var flags = 0;
while(blob.l <= blob.length - 4) {
var type = blob.read_shift(2);
var sz = blob.read_shift(2), tgt = blob.l + sz;
var p = {};
switch(type) {
/* UNIX-style Timestamps */
case 0x5455: {
flags = blob.read_shift(1);
if(flags & 1) p.mtime = blob.read_shift(4);
/* for some reason, CD flag corresponds to LFH */
if(sz > 5) {
if(flags & 2) p.atime = blob.read_shift(4);
if(flags & 4) p.ctime = blob.read_shift(4);
}
if(p.mtime) p.mt = new Date(p.mtime*1000);
}
break;
}
blob.l = tgt;
o[type] = p;
}
return o;
}
var fs;
function get_fs() { return fs || (fs = require('fs')); }
function parse(file, options) {
if(file[0] == 0x50 && file[1] == 0x4b) return parse_zip(file, options);
if(file.length < 512) throw new Error("CFB file size " + file.length + " < 512");
var mver = 3;
var ssz = 512;
@ -213,6 +387,8 @@ var mv = check_get_mver(blob);
mver = mv[0];
switch(mver) {
case 3: ssz = 512; break; case 4: ssz = 4096; break;
case 0: if(mv[1] == 0) return parse_zip(file, options);
/* falls through */
default: throw new Error("Major Version: Expected 3 or 4 saw " + mver);
}
@ -291,6 +467,7 @@ return o;
/* [MS-CFB] 2.2 Compound File Header -- read up to major version */
function check_get_mver(blob) {
if(blob[blob.l] == 0x50 && blob[blob.l + 1] == 0x4b) return [0, 0];
// header signature 8
blob.chk(HEADER_SIGNATURE, 'Header Signature: ');
@ -608,6 +785,7 @@ function rebuild_cfb(cfb, f) {
function _write(cfb, options) {
var _opts = options || {};
rebuild_cfb(cfb);
if(_opts.fileType == 'zip') return write_zip(cfb, _opts);
var L = (function(cfb){
var mini_size = 0, fat_size = 0;
for(var i = 0; i < cfb.FileIndex.length; ++i) {
@ -802,6 +980,551 @@ function write(cfb, options) {
}
return o;
}
/* node < 8.1 zlib does not expose bytesRead, so default to pure JS */
var _zlib;
function use_zlib(zlib) { try {
var InflateRaw = zlib.InflateRaw;
var InflRaw = new InflateRaw();
InflRaw._processChunk(new Uint8Array([3, 0]), InflRaw._finishFlushFlag);
if(InflRaw.bytesRead) _zlib = zlib;
else throw new Error("zlib does not expose bytesRead");
} catch(e) {console.error("cannot use native zlib: " + (e.message || e)); } }
function _inflateRawSync(payload, usz) {
if(!_zlib) return _inflate(payload, usz);
var InflateRaw = _zlib.InflateRaw;
var InflRaw = new InflateRaw();
var out = InflRaw._processChunk(payload.slice(payload.l), InflRaw._finishFlushFlag);
payload.l += InflRaw.bytesRead;
return out;
}
function _deflateRawSync(payload) {
return _zlib ? _zlib.deflateRawSync(payload) : _deflate(payload);
}
var CLEN_ORDER = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];
/* LEN_ID = [ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285 ]; */
var LEN_LN = [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13 , 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 ];
/* DST_ID = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]; */
var DST_LN = [ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 ];
function bit_swap_8(n) { var t = (((((n<<1)|(n<<11)) & 0x22110) | (((n<<5)|(n<<15)) & 0x88440))); return ((t>>16) | (t>>8) |t)&0xFF; }
var use_typed_arrays = typeof Uint8Array !== 'undefined';
var bitswap8 = use_typed_arrays ? new Uint8Array(1<<8) : [];
for(var q = 0; q < (1<<8); ++q) bitswap8[q] = bit_swap_8(q);
function bit_swap_n(n, b) {
var rev = bitswap8[n & 0xFF];
if(b <= 8) return rev >>> (8-b);
rev = (rev << 8) | bitswap8[(n>>8)&0xFF];
if(b <= 16) return rev >>> (16-b);
rev = (rev << 8) | bitswap8[(n>>16)&0xFF];
return rev >>> (24-b);
}
/* helpers for unaligned bit reads */
function read_bits_2(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 6 ? 0 : buf[h+1]<<8))>>>w)& 0x03; }
function read_bits_3(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 5 ? 0 : buf[h+1]<<8))>>>w)& 0x07; }
function read_bits_4(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 4 ? 0 : buf[h+1]<<8))>>>w)& 0x0F; }
function read_bits_5(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 3 ? 0 : buf[h+1]<<8))>>>w)& 0x1F; }
function read_bits_7(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 1 ? 0 : buf[h+1]<<8))>>>w)& 0x7F; }
/* works up to n = 3 * 8 + 1 = 25 */
function read_bits_n(buf, bl, n) {
var w = (bl&7), h = (bl>>>3), f = ((1<<n)-1);
var v = buf[h] >>> w;
if(n < 8 - w) return v & f;
v |= buf[h+1]<<(8-w);
if(n < 16 - w) return v & f;
v |= buf[h+2]<<(16-w);
if(n < 24 - w) return v & f;
v |= buf[h+3]<<(24-w);
return v & f;
}
/* until ArrayBuffer#realloc is a thing, fake a realloc */
function realloc(b, sz) {
var L = b.length, M = 2*L > sz ? 2*L : sz + 5, i = 0;
if(L >= sz) return b;
if(has_buf) {
var o = new_unsafe_buf(M);
// $FlowIgnore
if(b.copy) b.copy(o);
else for(; i < b.length; ++i) o[i] = b[i];
return o;
} else if(use_typed_arrays) {
var a = new Uint8Array(M);
if(a.set) a.set(b);
else for(; i < b.length; ++i) a[i] = b[i];
return a;
}
b.length = M;
return b;
}
/* zero-filled arrays for older browsers */
function zero_fill_array(n) {
var o = new Array(n);
for(var i = 0; i < n; ++i) o[i] = 0;
return o;
}var _deflate = (function() {
var _deflateRaw = (function() {
return function deflateRaw(data, out) {
var boff = 0;
while(boff < data.length) {
var L = Math.min(0xFFFF, data.length - boff);
var h = boff + L == data.length;
/* TODO: this is only type 0 stored */
out.write_shift(1, +h);
out.write_shift(2, L);
out.write_shift(2, (~L) & 0xFFFF);
while(L-- > 0) out[out.l++] = data[boff++];
}
return out.l;
};
})();
return function(data) {
var buf = new_buf(50+Math.floor(data.length*1.1));
var off = _deflateRaw(data, buf);
return buf.slice(0, off);
};
})();
/* modified inflate function also moves original read head */
/* build tree (used for literals and lengths) */
function build_tree(clens, cmap, MAX) {
var maxlen = 1, w = 0, i = 0, j = 0, ccode = 0, L = clens.length;
var bl_count = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
for(i = 0; i < 32; ++i) bl_count[i] = 0;
for(i = L; i < MAX; ++i) clens[i] = 0;
L = clens.length;
var ctree = use_typed_arrays ? new Uint16Array(L) : zero_fill_array(L); // []
/* build code tree */
for(i = 0; i < L; ++i) {
bl_count[(w = clens[i])]++;
if(maxlen < w) maxlen = w;
ctree[i] = 0;
}
bl_count[0] = 0;
for(i = 1; i <= maxlen; ++i) bl_count[i+16] = (ccode = (ccode + bl_count[i-1])<<1);
for(i = 0; i < L; ++i) {
ccode = clens[i];
if(ccode != 0) ctree[i] = bl_count[ccode+16]++;
}
/* cmap[maxlen + 4 bits] = (off&15) + (lit<<4) reverse mapping */
var cleni = 0;
for(i = 0; i < L; ++i) {
cleni = clens[i];
if(cleni != 0) {
ccode = bit_swap_n(ctree[i], maxlen)>>(maxlen-cleni);
for(j = (1<<(maxlen + 4 - cleni)) - 1; j>=0; --j)
cmap[ccode|(j<<cleni)] = (cleni&15) | (i<<4);
}
}
return maxlen;
}
var fix_lmap = use_typed_arrays ? new Uint16Array(512) : zero_fill_array(512);
var fix_dmap = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
if(!use_typed_arrays) {
for(var i = 0; i < 512; ++i) fix_lmap[i] = 0;
for(i = 0; i < 32; ++i) fix_dmap[i] = 0;
}
(function() {
var dlens = [];
var i = 0;
for(;i<32; i++) dlens.push(5);
build_tree(dlens, fix_dmap, 32);
var clens = [];
i = 0;
for(; i<=143; i++) clens.push(8);
for(; i<=255; i++) clens.push(9);
for(; i<=279; i++) clens.push(7);
for(; i<=287; i++) clens.push(8);
build_tree(clens, fix_lmap, 288);
})();
var dyn_lmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
var dyn_dmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
var dyn_cmap = use_typed_arrays ? new Uint16Array(128) : zero_fill_array(128);
var dyn_len_1 = 1, dyn_len_2 = 1;
/* 5.5.3 Expanding Huffman Codes */
function dyn(data, boff) {
/* nomenclature from RFC1951 refers to bit values; these are offset by the implicit constant */
var _HLIT = read_bits_5(data, boff) + 257; boff += 5;
var _HDIST = read_bits_5(data, boff) + 1; boff += 5;
var _HCLEN = read_bits_4(data, boff) + 4; boff += 4;
var w = 0;
/* grab and store code lengths */
var clens = use_typed_arrays ? new Uint8Array(19) : zero_fill_array(19);
var ctree = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
var maxlen = 1;
var bl_count = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
var next_code = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
var L = clens.length; /* 19 */
for(var i = 0; i < _HCLEN; ++i) {
clens[CLEN_ORDER[i]] = w = read_bits_3(data, boff);
if(maxlen < w) maxlen = w;
bl_count[w]++;
boff += 3;
}
/* build code tree */
var ccode = 0;
bl_count[0] = 0;
for(i = 1; i <= maxlen; ++i) next_code[i] = ccode = (ccode + bl_count[i-1])<<1;
for(i = 0; i < L; ++i) if((ccode = clens[i]) != 0) ctree[i] = next_code[ccode]++;
/* cmap[7 bits from stream] = (off&7) + (lit<<3) */
var cleni = 0;
for(i = 0; i < L; ++i) {
cleni = clens[i];
if(cleni != 0) {
ccode = bitswap8[ctree[i]]>>(8-cleni);
for(var j = (1<<(7-cleni))-1; j>=0; --j) dyn_cmap[ccode|(j<<cleni)] = (cleni&7) | (i<<3);
}
}
/* read literal and dist codes at once */
var hcodes = [];
maxlen = 1;
for(; hcodes.length < _HLIT + _HDIST;) {
ccode = dyn_cmap[read_bits_7(data, boff)];
boff += ccode & 7;
switch((ccode >>>= 3)) {
case 16:
w = 3 + read_bits_2(data, boff); boff += 2;
ccode = hcodes[hcodes.length - 1];
while(w-- > 0) hcodes.push(ccode);
break;
case 17:
w = 3 + read_bits_3(data, boff); boff += 3;
while(w-- > 0) hcodes.push(0);
break;
case 18:
w = 11 + read_bits_7(data, boff); boff += 7;
while(w -- > 0) hcodes.push(0);
break;
default:
hcodes.push(ccode);
if(maxlen < ccode) maxlen = ccode;
break;
}
}
/* build literal / length trees */
var h1 = hcodes.slice(0, _HLIT), h2 = hcodes.slice(_HLIT);
for(i = _HLIT; i < 286; ++i) h1[i] = 0;
for(i = _HDIST; i < 30; ++i) h2[i] = 0;
dyn_len_1 = build_tree(h1, dyn_lmap, 286);
dyn_len_2 = build_tree(h2, dyn_dmap, 30);
return boff;
}
/* return [ data, bytesRead ] */
function inflate(data, usz) {
/* shortcircuit for empty buffer [0x03, 0x00] */
if(data[0] == 3 && !(data[1] & 0x3)) { return [new_raw_buf(usz), 2]; }
/* bit offset */
var boff = 0;
/* header includes final bit and type bits */
var header = 0;
var outbuf = new_unsafe_buf(usz ? usz : (1<<18));
var woff = 0;
var OL = outbuf.length>>>0;
var max_len_1 = 0, max_len_2 = 0;
while((header&1) == 0) {
header = read_bits_3(data, boff); boff += 3;
if((header >>> 1) == 0) {
/* Stored block */
if(boff & 7) boff += 8 - (boff&7);
/* 2 bytes sz, 2 bytes bit inverse */
var sz = data[boff>>>3] | data[(boff>>>3)+1]<<8;
boff += 32;
/* push sz bytes */
if(!usz && OL < woff + sz) { outbuf = realloc(outbuf, woff + sz); OL = outbuf.length; }
if(typeof data.copy === 'function') {
// $FlowIgnore
data.copy(outbuf, woff, boff>>>3, (boff>>>3)+sz);
woff += sz; boff += 8*sz;
} else while(sz-- > 0) { outbuf[woff++] = data[boff>>>3]; boff += 8; }
continue;
} else if((header >>> 1) == 1) {
/* Fixed Huffman */
max_len_1 = 9; max_len_2 = 5;
} else {
/* Dynamic Huffman */
boff = dyn(data, boff);
max_len_1 = dyn_len_1; max_len_2 = dyn_len_2;
}
if(!usz && (OL < woff + 32767)) { outbuf = realloc(outbuf, woff + 32767); OL = outbuf.length; }
for(;;) { // while(true) is apparently out of vogue in modern JS circles
/* ingest code and move read head */
var bits = read_bits_n(data, boff, max_len_1);
var code = (header>>>1) == 1 ? fix_lmap[bits] : dyn_lmap[bits];
boff += code & 15; code >>>= 4;
/* 0-255 are literals, 256 is end of block token, 257+ are copy tokens */
if(((code>>>8)&0xFF) === 0) outbuf[woff++] = code;
else if(code == 256) break;
else {
code -= 257;
var len_eb = (code < 8) ? 0 : ((code-4)>>2); if(len_eb > 5) len_eb = 0;
var tgt = woff + LEN_LN[code];
/* length extra bits */
if(len_eb > 0) {
tgt += read_bits_n(data, boff, len_eb);
boff += len_eb;
}
/* dist code */
bits = read_bits_n(data, boff, max_len_2);
code = (header>>>1) == 1 ? fix_dmap[bits] : dyn_dmap[bits];
boff += code & 15; code >>>= 4;
var dst_eb = (code < 4 ? 0 : (code-2)>>1);
var dst = DST_LN[code];
/* dist extra bits */
if(dst_eb > 0) {
dst += read_bits_n(data, boff, dst_eb);
boff += dst_eb;
}
/* in the common case, manual byte copy is faster than TA set / Buffer copy */
if(!usz && OL < tgt) { outbuf = realloc(outbuf, tgt); OL = outbuf.length; }
while(woff < tgt) { outbuf[woff] = outbuf[woff - dst]; ++woff; }
}
}
}
return [usz ? outbuf : outbuf.slice(0, woff), (boff+7)>>>3];
}
function _inflate(payload, usz) {
var data = payload.slice(payload.l||0);
var out = inflate(data, usz);
payload.l += out[1];
return out[0];
}
function warn_or_throw(wrn, msg) {
if(wrn) { if(typeof console !== 'undefined') console.error(msg); }
else throw new Error(msg);
}
function parse_zip(file, options) {
var blob = file;
prep_blob(blob, 0);
var FileIndex = [], FullPaths = [];
var o = {
FileIndex: FileIndex,
FullPaths: FullPaths
};
init_cfb(o, { root: options.root });
/* find end of central directory, start just after signature */
var i = blob.length - 4;
while((blob[i] != 0x50 || blob[i+1] != 0x4b || blob[i+2] != 0x05 || blob[i+3] != 0x06) && i >= 0) --i;
blob.l = i + 4;
/* parse end of central directory */
blob.l += 4;
var fcnt = blob.read_shift(2);
blob.l += 6;
var start_cd = blob.read_shift(4);
/* parse central directory */
blob.l = start_cd;
for(i = 0; i < fcnt; ++i) {
/* trust local file header instead of CD entry */
blob.l += 20;
var csz = blob.read_shift(4);
var usz = blob.read_shift(4);
var namelen = blob.read_shift(2);
var efsz = blob.read_shift(2);
var fcsz = blob.read_shift(2);
blob.l += 8;
var offset = blob.read_shift(4);
var EF = parse_extra_field(blob.slice(blob.l+namelen, blob.l+namelen+efsz));
blob.l += namelen + efsz + fcsz;
var L = blob.l;
blob.l = offset + 4;
parse_local_file(blob, csz, usz, o, EF);
blob.l = L;
}
return o;
}
/* head starts just after local file header signature */
function parse_local_file(blob, csz, usz, o, EF) {
/* [local file header] */
blob.l += 2;
var flags = blob.read_shift(2);
var meth = blob.read_shift(2);
var date = parse_dos_date(blob);
if(flags & 0x2041) throw new Error("Unsupported ZIP encryption");
var crc32 = blob.read_shift(4);
var _csz = blob.read_shift(4);
var _usz = blob.read_shift(4);
var namelen = blob.read_shift(2);
var efsz = blob.read_shift(2);
// TODO: flags & (1<<11) // UTF8
var name = ""; for(var i = 0; i < namelen; ++i) name += String.fromCharCode(blob[blob.l++]);
if(efsz) {
var ef = parse_extra_field(blob.slice(blob.l, blob.l + efsz));
if((ef[0x5455]||{}).mt) date = ef[0x5455].mt;
if(((EF||{})[0x5455]||{}).mt) date = EF[0x5455].mt;
}
blob.l += efsz;
/* [encryption header] */
/* [file data] */
var data = blob.slice(blob.l, blob.l + _csz);
switch(meth) {
case 8: data = _inflateRawSync(blob, _usz); break;
case 0: break;
default: throw new Error("Unsupported ZIP Compression method " + meth);
}
/* [data descriptor] */
var wrn = false;
if(flags & 8) {
crc32 = blob.read_shift(4);
if(crc32 == 0x08074b50) { crc32 = blob.read_shift(4); wrn = true; }
_csz = blob.read_shift(4);
_usz = blob.read_shift(4);
}
if(_csz != csz) warn_or_throw(wrn, "Bad compressed size: " + csz + " != " + _csz);
if(_usz != usz) warn_or_throw(wrn, "Bad uncompressed size: " + usz + " != " + _usz);
var _crc32 = CRC32.buf(data, 0);
if(crc32 != _crc32) warn_or_throw(wrn, "Bad CRC32 checksum: " + crc32 + " != " + _crc32);
cfb_add(o, name, data, {unsafe: true, mt: date});
}
function write_zip(cfb, options) {
var _opts = options || {};
var out = [], cdirs = [];
var o = new_buf(1);
var method = (_opts.compression ? 8 : 0), flags = 0;
var desc = false;
if(desc) flags |= 8;
var i = 0, j = 0;
var start_cd = 0, fcnt = 0;
var root = cfb.FullPaths[0], fp = root, fi = cfb.FileIndex[0];
var crcs = [];
var sz_cd = 0;
for(i = 1; i < cfb.FullPaths.length; ++i) {
fp = cfb.FullPaths[i].slice(root.length); fi = cfb.FileIndex[i];
if(!fi.size || !fi.content || fp == "\u0001Sh33tJ5") continue;
var start = start_cd;
/* TODO: CP437 filename */
var namebuf = new_buf(fp.length);
for(j = 0; j < fp.length; ++j) namebuf.write_shift(1, fp.charCodeAt(j) & 0x7F);
namebuf = namebuf.slice(0, namebuf.l);
crcs[fcnt] = CRC32.buf(fi.content, 0);
var outbuf = fi.content;
if(method == 8) outbuf = _deflateRawSync(outbuf);
/* local file header */
o = new_buf(30);
o.write_shift(4, 0x04034b50);
o.write_shift(2, 20);
o.write_shift(2, flags);
o.write_shift(2, method);
/* TODO: last mod file time/date */
if(fi.mt) write_dos_date(o, fi.mt);
else o.write_shift(4, 0);
o.write_shift(-4, (flags & 8) ? 0 : crcs[fcnt]);
o.write_shift(4, (flags & 8) ? 0 : outbuf.length);
o.write_shift(4, (flags & 8) ? 0 : fi.content.length);
o.write_shift(2, namebuf.length);
o.write_shift(2, 0);
start_cd += o.length;
out.push(o);
start_cd += namebuf.length;
out.push(namebuf);
/* TODO: encryption header ? */
start_cd += outbuf.length;
out.push(outbuf);
/* data descriptor */
if(flags & 8) {
o = new_buf(12);
o.write_shift(-4, crcs[fcnt]);
o.write_shift(4, outbuf.length);
o.write_shift(4, fi.content.length);
start_cd += o.l;
out.push(o);
}
/* central directory */
o = new_buf(46);
o.write_shift(4, 0x02014b50);
o.write_shift(2, 0);
o.write_shift(2, 20);
o.write_shift(2, flags);
o.write_shift(2, method);
o.write_shift(4, 0); /* TODO: last mod file time/date */
o.write_shift(-4, crcs[fcnt]);
o.write_shift(4, outbuf.length);
o.write_shift(4, fi.content.length);
o.write_shift(2, namebuf.length);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(4, 0);
o.write_shift(4, start);
sz_cd += o.l;
cdirs.push(o);
sz_cd += namebuf.length;
cdirs.push(namebuf);
++fcnt;
}
/* end of central directory */
o = new_buf(22);
o.write_shift(4, 0x06054b50);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, fcnt);
o.write_shift(2, fcnt);
o.write_shift(4, sz_cd);
o.write_shift(4, start_cd);
o.write_shift(2, 0);
return bconcat(([bconcat((out)), bconcat(cdirs), o]));
}
function cfb_new(opts) {
var o = ({});
init_cfb(o, opts);
@ -828,6 +1551,8 @@ file.content = (content);
file.size = content ? content.length : 0;
if(opts) {
if(opts.CLSID) file.clsid = opts.CLSID;
if(opts.mt) file.mt = opts.mt;
if(opts.ct) file.ct = opts.ct;
}
return file;
}
@ -871,6 +1596,9 @@ exports.utils = {
CheckField: CheckField,
prep_blob: prep_blob,
bconcat: bconcat,
use_zlib: use_zlib,
_deflateRaw: _deflate,
_inflateRaw: _inflate,
consts: consts
};

2
dist/cfb.min.js vendored

File diff suppressed because one or more lines are too long

2
dist/cfb.min.map vendored

File diff suppressed because one or more lines are too long

728
dist/xlscfb.js vendored

@ -12,13 +12,16 @@ declare var bconcat:any;
declare var s2a:any;
declare var chr0:any;
declare var chr1:any;
declare var has_buf:boolean;
declare var new_buf:any;
declare var new_raw_buf:any;
declare var new_unsafe_buf:any;
*/
/* cfb.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
/*jshint eqnull:true */
/*exported CFB */
/*global module, require:false, process:false, Buffer:false, Uint8Array:false */
/*global module, require:false, process:false, Buffer:false, Uint8Array:false, Uint16Array:false */
/*::
declare var DO_NOT_EXPORT_CFB:?boolean;
@ -35,10 +38,111 @@ type SectorList = {
}
type CFBFiles = {[n:string]:CFBEntry};
*/
/* crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
/*exported CRC32 */
var CRC32;
(function (factory) {
/*jshint ignore:start */
/*eslint-disable */
factory(CRC32 = {});
/*eslint-enable */
/*jshint ignore:end */
}(function(CRC32) {
CRC32.version = '1.2.0';
/* see perf/crc32table.js */
/*global Int32Array */
function signed_crc_table()/*:any*/ {
var c = 0, table/*:Array<number>*/ = new Array(256);
for(var n =0; n != 256; ++n){
c = n;
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
table[n] = c;
}
return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table;
}
var T = signed_crc_table();
function crc32_bstr(bstr/*:string*/, seed/*:number*/)/*:number*/ {
var C = seed ^ -1, L = bstr.length - 1;
for(var i = 0; i < L;) {
C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
}
if(i === L) C = (C>>>8) ^ T[(C ^ bstr.charCodeAt(i))&0xFF];
return C ^ -1;
}
function crc32_buf(buf/*:Uint8Array|Array<number>*/, seed/*:number*/)/*:number*/ {
if(buf.length > 10000) return crc32_buf_8(buf, seed);
var C = seed ^ -1, L = buf.length - 3;
for(var i = 0; i < L;) {
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
}
while(i < L+3) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
return C ^ -1;
}
function crc32_buf_8(buf/*:Uint8Array|Array<number>*/, seed/*:number*/)/*:number*/ {
var C = seed ^ -1, L = buf.length - 7;
for(var i = 0; i < L;) {
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
}
while(i < L+7) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
return C ^ -1;
}
function crc32_str(str/*:string*/, seed/*:number*/)/*:number*/ {
var C = seed ^ -1;
for(var i = 0, L=str.length, c, d; i < L;) {
c = str.charCodeAt(i++);
if(c < 0x80) {
C = (C>>>8) ^ T[(C ^ c)&0xFF];
} else if(c < 0x800) {
C = (C>>>8) ^ T[(C ^ (192|((c>>6)&31)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
} else if(c >= 0xD800 && c < 0xE000) {
c = (c&1023)+64; d = str.charCodeAt(i++)&1023;
C = (C>>>8) ^ T[(C ^ (240|((c>>8)&7)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|((c>>2)&63)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|(d&63)))&0xFF];
} else {
C = (C>>>8) ^ T[(C ^ (224|((c>>12)&15)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|((c>>6)&63)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
}
}
return C ^ -1;
}
CRC32.table = T;
CRC32.bstr = crc32_bstr;
CRC32.buf = crc32_buf;
CRC32.str = crc32_str;
}));
/* [MS-CFB] v20171201 */
var CFB = (function _CFB(){
var exports/*:CFBModule*/ = /*::(*/{}/*:: :any)*/;
exports.version = '1.0.8';
exports.version = '1.1.0';
/* [MS-CFB] 2.6.4 */
function namecmp(l/*:string*/, r/*:string*/)/*:number*/ {
var L = l.split("/"), R = r.split("/");
@ -59,9 +163,75 @@ function filename(p/*:string*/)/*:string*/ {
var c = p.lastIndexOf("/");
return (c === -1) ? p : p.slice(c+1);
}
/* -------------------------------------------------------------------------- */
/* DOS Date format:
high|YYYYYYYm.mmmddddd.HHHHHMMM.MMMSSSSS|low
add 1980 to stored year
stored second should be doubled
*/
/* write JS date to buf as a DOS date */
function write_dos_date(buf/*:CFBlob*/, date/*:Date|string*/) {
if(typeof date === "string") date = new Date(date);
var hms/*:number*/ = date.getHours();
hms = hms << 6 | date.getMinutes();
hms = hms << 5 | (date.getSeconds()>>>1);
buf.write_shift(2, hms);
var ymd/*:number*/ = (date.getFullYear() - 1980);
ymd = ymd << 4 | (date.getMonth()+1);
ymd = ymd << 5 | date.getDate();
buf.write_shift(2, ymd);
}
/* read four bytes from buf and interpret as a DOS date */
function parse_dos_date(buf/*:CFBlob*/)/*:Date*/ {
var hms = buf.read_shift(2) & 0xFFFF;
var ymd = buf.read_shift(2) & 0xFFFF;
var val = new Date();
var d = ymd & 0x1F; ymd >>>= 5;
var m = ymd & 0x0F; ymd >>>= 4;
val.setMilliseconds(0);
val.setFullYear(ymd + 1980);
val.setMonth(m-1);
val.setDate(d);
var S = hms & 0x1F; hms >>>= 5;
var M = hms & 0x3F; hms >>>= 6;
val.setHours(hms);
val.setMinutes(M);
val.setSeconds(S<<1);
return val;
}
function parse_extra_field(blob/*:CFBlob*/)/*:any*/ {
prep_blob(blob, 0);
var o = /*::(*/{}/*:: :any)*/;
var flags = 0;
while(blob.l <= blob.length - 4) {
var type = blob.read_shift(2);
var sz = blob.read_shift(2), tgt = blob.l + sz;
var p = {};
switch(type) {
/* UNIX-style Timestamps */
case 0x5455: {
flags = blob.read_shift(1);
if(flags & 1) p.mtime = blob.read_shift(4);
/* for some reason, CD flag corresponds to LFH */
if(sz > 5) {
if(flags & 2) p.atime = blob.read_shift(4);
if(flags & 4) p.ctime = blob.read_shift(4);
}
if(p.mtime) p.mt = new Date(p.mtime*1000);
}
break;
}
blob.l = tgt;
o[type] = p;
}
return o;
}
var fs/*:: = require('fs'); */;
function get_fs() { return fs || (fs = require('fs')); }
function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
if(file[0] == 0x50 && file[1] == 0x4b) return parse_zip(file, options);
if(file.length < 512) throw new Error("CFB file size " + file.length + " < 512");
var mver = 3;
var ssz = 512;
@ -82,6 +252,8 @@ var mv = check_get_mver(blob);
mver = mv[0];
switch(mver) {
case 3: ssz = 512; break; case 4: ssz = 4096; break;
case 0: if(mv[1] == 0) return parse_zip(file, options);
/* falls through */
default: throw new Error("Major Version: Expected 3 or 4 saw " + mver);
}
@ -160,6 +332,7 @@ return o;
/* [MS-CFB] 2.2 Compound File Header -- read up to major version */
function check_get_mver(blob/*:CFBlob*/)/*:[number, number]*/ {
if(blob[blob.l] == 0x50 && blob[blob.l + 1] == 0x4b) return [0, 0];
// header signature 8
blob.chk(HEADER_SIGNATURE, 'Header Signature: ');
@ -477,6 +650,7 @@ function rebuild_cfb(cfb/*:CFBContainer*/, f/*:?boolean*/)/*:void*/ {
function _write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ {
var _opts = options || {};
rebuild_cfb(cfb);
if(_opts.fileType == 'zip') return write_zip(cfb, _opts);
var L = (function(cfb/*:CFBContainer*/)/*:Array<number>*/{
var mini_size = 0, fat_size = 0;
for(var i = 0; i < cfb.FileIndex.length; ++i) {
@ -677,6 +851,551 @@ function write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|string
}
return o;
}
/* node < 8.1 zlib does not expose bytesRead, so default to pure JS */
var _zlib;
function use_zlib(zlib) { try {
var InflateRaw = zlib.InflateRaw;
var InflRaw = new InflateRaw();
InflRaw._processChunk(new Uint8Array([3, 0]), InflRaw._finishFlushFlag);
if(InflRaw.bytesRead) _zlib = zlib;
else throw new Error("zlib does not expose bytesRead");
} catch(e) {console.error("cannot use native zlib: " + (e.message || e)); } }
function _inflateRawSync(payload, usz) {
if(!_zlib) return _inflate(payload, usz);
var InflateRaw = _zlib.InflateRaw;
var InflRaw = new InflateRaw();
var out = InflRaw._processChunk(payload.slice(payload.l), InflRaw._finishFlushFlag);
payload.l += InflRaw.bytesRead;
return out;
}
function _deflateRawSync(payload) {
return _zlib ? _zlib.deflateRawSync(payload) : _deflate(payload);
}
var CLEN_ORDER = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];
/* LEN_ID = [ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285 ]; */
var LEN_LN = [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13 , 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 ];
/* DST_ID = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]; */
var DST_LN = [ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 ];
function bit_swap_8(n) { var t = (((((n<<1)|(n<<11)) & 0x22110) | (((n<<5)|(n<<15)) & 0x88440))); return ((t>>16) | (t>>8) |t)&0xFF; }
var use_typed_arrays = typeof Uint8Array !== 'undefined';
var bitswap8 = use_typed_arrays ? new Uint8Array(1<<8) : [];
for(var q = 0; q < (1<<8); ++q) bitswap8[q] = bit_swap_8(q);
function bit_swap_n(n, b) {
var rev = bitswap8[n & 0xFF];
if(b <= 8) return rev >>> (8-b);
rev = (rev << 8) | bitswap8[(n>>8)&0xFF];
if(b <= 16) return rev >>> (16-b);
rev = (rev << 8) | bitswap8[(n>>16)&0xFF];
return rev >>> (24-b);
}
/* helpers for unaligned bit reads */
function read_bits_2(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 6 ? 0 : buf[h+1]<<8))>>>w)& 0x03; }
function read_bits_3(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 5 ? 0 : buf[h+1]<<8))>>>w)& 0x07; }
function read_bits_4(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 4 ? 0 : buf[h+1]<<8))>>>w)& 0x0F; }
function read_bits_5(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 3 ? 0 : buf[h+1]<<8))>>>w)& 0x1F; }
function read_bits_7(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 1 ? 0 : buf[h+1]<<8))>>>w)& 0x7F; }
/* works up to n = 3 * 8 + 1 = 25 */
function read_bits_n(buf, bl, n) {
var w = (bl&7), h = (bl>>>3), f = ((1<<n)-1);
var v = buf[h] >>> w;
if(n < 8 - w) return v & f;
v |= buf[h+1]<<(8-w);
if(n < 16 - w) return v & f;
v |= buf[h+2]<<(16-w);
if(n < 24 - w) return v & f;
v |= buf[h+3]<<(24-w);
return v & f;
}
/* until ArrayBuffer#realloc is a thing, fake a realloc */
function realloc(b, sz/*:number*/) {
var L = b.length, M = 2*L > sz ? 2*L : sz + 5, i = 0;
if(L >= sz) return b;
if(has_buf) {
var o = new_unsafe_buf(M);
// $FlowIgnore
if(b.copy) b.copy(o);
else for(; i < b.length; ++i) o[i] = b[i];
return o;
} else if(use_typed_arrays) {
var a = new Uint8Array(M);
if(a.set) a.set(b);
else for(; i < b.length; ++i) a[i] = b[i];
return a;
}
b.length = M;
return b;
}
/* zero-filled arrays for older browsers */
function zero_fill_array(n) {
var o = new Array(n);
for(var i = 0; i < n; ++i) o[i] = 0;
return o;
}var _deflate = (function() {
var _deflateRaw = (function() {
return function deflateRaw(data, out) {
var boff = 0;
while(boff < data.length) {
var L = Math.min(0xFFFF, data.length - boff);
var h = boff + L == data.length;
/* TODO: this is only type 0 stored */
out.write_shift(1, +h);
out.write_shift(2, L);
out.write_shift(2, (~L) & 0xFFFF);
while(L-- > 0) out[out.l++] = data[boff++];
}
return out.l;
};
})();
return function(data) {
var buf = new_buf(50+Math.floor(data.length*1.1));
var off = _deflateRaw(data, buf);
return buf.slice(0, off);
};
})();
/* modified inflate function also moves original read head */
/* build tree (used for literals and lengths) */
function build_tree(clens, cmap, MAX/*:number*/)/*:number*/ {
var maxlen = 1, w = 0, i = 0, j = 0, ccode = 0, L = clens.length;
var bl_count = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
for(i = 0; i < 32; ++i) bl_count[i] = 0;
for(i = L; i < MAX; ++i) clens[i] = 0;
L = clens.length;
var ctree = use_typed_arrays ? new Uint16Array(L) : zero_fill_array(L); // []
/* build code tree */
for(i = 0; i < L; ++i) {
bl_count[(w = clens[i])]++;
if(maxlen < w) maxlen = w;
ctree[i] = 0;
}
bl_count[0] = 0;
for(i = 1; i <= maxlen; ++i) bl_count[i+16] = (ccode = (ccode + bl_count[i-1])<<1);
for(i = 0; i < L; ++i) {
ccode = clens[i];
if(ccode != 0) ctree[i] = bl_count[ccode+16]++;
}
/* cmap[maxlen + 4 bits] = (off&15) + (lit<<4) reverse mapping */
var cleni = 0;
for(i = 0; i < L; ++i) {
cleni = clens[i];
if(cleni != 0) {
ccode = bit_swap_n(ctree[i], maxlen)>>(maxlen-cleni);
for(j = (1<<(maxlen + 4 - cleni)) - 1; j>=0; --j)
cmap[ccode|(j<<cleni)] = (cleni&15) | (i<<4);
}
}
return maxlen;
}
var fix_lmap = use_typed_arrays ? new Uint16Array(512) : zero_fill_array(512);
var fix_dmap = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
if(!use_typed_arrays) {
for(var i = 0; i < 512; ++i) fix_lmap[i] = 0;
for(i = 0; i < 32; ++i) fix_dmap[i] = 0;
}
(function() {
var dlens/*:Array<number>*/ = [];
var i = 0;
for(;i<32; i++) dlens.push(5);
build_tree(dlens, fix_dmap, 32);
var clens/*:Array<number>*/ = [];
i = 0;
for(; i<=143; i++) clens.push(8);
for(; i<=255; i++) clens.push(9);
for(; i<=279; i++) clens.push(7);
for(; i<=287; i++) clens.push(8);
build_tree(clens, fix_lmap, 288);
})();
var dyn_lmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
var dyn_dmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
var dyn_cmap = use_typed_arrays ? new Uint16Array(128) : zero_fill_array(128);
var dyn_len_1 = 1, dyn_len_2 = 1;
/* 5.5.3 Expanding Huffman Codes */
function dyn(data, boff/*:number*/) {
/* nomenclature from RFC1951 refers to bit values; these are offset by the implicit constant */
var _HLIT = read_bits_5(data, boff) + 257; boff += 5;
var _HDIST = read_bits_5(data, boff) + 1; boff += 5;
var _HCLEN = read_bits_4(data, boff) + 4; boff += 4;
var w = 0;
/* grab and store code lengths */
var clens = use_typed_arrays ? new Uint8Array(19) : zero_fill_array(19);
var ctree = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
var maxlen = 1;
var bl_count = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
var next_code = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
var L = clens.length; /* 19 */
for(var i = 0; i < _HCLEN; ++i) {
clens[CLEN_ORDER[i]] = w = read_bits_3(data, boff);
if(maxlen < w) maxlen = w;
bl_count[w]++;
boff += 3;
}
/* build code tree */
var ccode = 0;
bl_count[0] = 0;
for(i = 1; i <= maxlen; ++i) next_code[i] = ccode = (ccode + bl_count[i-1])<<1;
for(i = 0; i < L; ++i) if((ccode = clens[i]) != 0) ctree[i] = next_code[ccode]++;
/* cmap[7 bits from stream] = (off&7) + (lit<<3) */
var cleni = 0;
for(i = 0; i < L; ++i) {
cleni = clens[i];
if(cleni != 0) {
ccode = bitswap8[ctree[i]]>>(8-cleni);
for(var j = (1<<(7-cleni))-1; j>=0; --j) dyn_cmap[ccode|(j<<cleni)] = (cleni&7) | (i<<3);
}
}
/* read literal and dist codes at once */
var hcodes/*:Array<number>*/ = [];
maxlen = 1;
for(; hcodes.length < _HLIT + _HDIST;) {
ccode = dyn_cmap[read_bits_7(data, boff)];
boff += ccode & 7;
switch((ccode >>>= 3)) {
case 16:
w = 3 + read_bits_2(data, boff); boff += 2;
ccode = hcodes[hcodes.length - 1];
while(w-- > 0) hcodes.push(ccode);
break;
case 17:
w = 3 + read_bits_3(data, boff); boff += 3;
while(w-- > 0) hcodes.push(0);
break;
case 18:
w = 11 + read_bits_7(data, boff); boff += 7;
while(w -- > 0) hcodes.push(0);
break;
default:
hcodes.push(ccode);
if(maxlen < ccode) maxlen = ccode;
break;
}
}
/* build literal / length trees */
var h1 = hcodes.slice(0, _HLIT), h2 = hcodes.slice(_HLIT);
for(i = _HLIT; i < 286; ++i) h1[i] = 0;
for(i = _HDIST; i < 30; ++i) h2[i] = 0;
dyn_len_1 = build_tree(h1, dyn_lmap, 286);
dyn_len_2 = build_tree(h2, dyn_dmap, 30);
return boff;
}
/* return [ data, bytesRead ] */
function inflate(data, usz/*:number*/) {
/* shortcircuit for empty buffer [0x03, 0x00] */
if(data[0] == 3 && !(data[1] & 0x3)) { return [new_raw_buf(usz), 2]; }
/* bit offset */
var boff = 0;
/* header includes final bit and type bits */
var header = 0;
var outbuf = new_unsafe_buf(usz ? usz : (1<<18));
var woff = 0;
var OL = outbuf.length>>>0;
var max_len_1 = 0, max_len_2 = 0;
while((header&1) == 0) {
header = read_bits_3(data, boff); boff += 3;
if((header >>> 1) == 0) {
/* Stored block */
if(boff & 7) boff += 8 - (boff&7);
/* 2 bytes sz, 2 bytes bit inverse */
var sz = data[boff>>>3] | data[(boff>>>3)+1]<<8;
boff += 32;
/* push sz bytes */
if(!usz && OL < woff + sz) { outbuf = realloc(outbuf, woff + sz); OL = outbuf.length; }
if(typeof data.copy === 'function') {
// $FlowIgnore
data.copy(outbuf, woff, boff>>>3, (boff>>>3)+sz);
woff += sz; boff += 8*sz;
} else while(sz-- > 0) { outbuf[woff++] = data[boff>>>3]; boff += 8; }
continue;
} else if((header >>> 1) == 1) {
/* Fixed Huffman */
max_len_1 = 9; max_len_2 = 5;
} else {
/* Dynamic Huffman */
boff = dyn(data, boff);
max_len_1 = dyn_len_1; max_len_2 = dyn_len_2;
}
if(!usz && (OL < woff + 32767)) { outbuf = realloc(outbuf, woff + 32767); OL = outbuf.length; }
for(;;) { // while(true) is apparently out of vogue in modern JS circles
/* ingest code and move read head */
var bits = read_bits_n(data, boff, max_len_1);
var code = (header>>>1) == 1 ? fix_lmap[bits] : dyn_lmap[bits];
boff += code & 15; code >>>= 4;
/* 0-255 are literals, 256 is end of block token, 257+ are copy tokens */
if(((code>>>8)&0xFF) === 0) outbuf[woff++] = code;
else if(code == 256) break;
else {
code -= 257;
var len_eb = (code < 8) ? 0 : ((code-4)>>2); if(len_eb > 5) len_eb = 0;
var tgt = woff + LEN_LN[code];
/* length extra bits */
if(len_eb > 0) {
tgt += read_bits_n(data, boff, len_eb);
boff += len_eb;
}
/* dist code */
bits = read_bits_n(data, boff, max_len_2);
code = (header>>>1) == 1 ? fix_dmap[bits] : dyn_dmap[bits];
boff += code & 15; code >>>= 4;
var dst_eb = (code < 4 ? 0 : (code-2)>>1);
var dst = DST_LN[code];
/* dist extra bits */
if(dst_eb > 0) {
dst += read_bits_n(data, boff, dst_eb);
boff += dst_eb;
}
/* in the common case, manual byte copy is faster than TA set / Buffer copy */
if(!usz && OL < tgt) { outbuf = realloc(outbuf, tgt); OL = outbuf.length; }
while(woff < tgt) { outbuf[woff] = outbuf[woff - dst]; ++woff; }
}
}
}
return [usz ? outbuf : outbuf.slice(0, woff), (boff+7)>>>3];
}
function _inflate(payload, usz) {
var data = payload.slice(payload.l||0);
var out = inflate(data, usz);
payload.l += out[1];
return out[0];
}
function warn_or_throw(wrn, msg) {
if(wrn) { if(typeof console !== 'undefined') console.error(msg); }
else throw new Error(msg);
}
function parse_zip(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
var blob/*:CFBlob*/ = /*::(*/file/*:: :any)*/;
prep_blob(blob, 0);
var FileIndex/*:CFBFileIndex*/ = [], FullPaths/*:Array<string>*/ = [];
var o = {
FileIndex: FileIndex,
FullPaths: FullPaths
};
init_cfb(o, { root: options.root });
/* find end of central directory, start just after signature */
var i = blob.length - 4;
while((blob[i] != 0x50 || blob[i+1] != 0x4b || blob[i+2] != 0x05 || blob[i+3] != 0x06) && i >= 0) --i;
blob.l = i + 4;
/* parse end of central directory */
blob.l += 4;
var fcnt = blob.read_shift(2);
blob.l += 6;
var start_cd = blob.read_shift(4);
/* parse central directory */
blob.l = start_cd;
for(i = 0; i < fcnt; ++i) {
/* trust local file header instead of CD entry */
blob.l += 20;
var csz = blob.read_shift(4);
var usz = blob.read_shift(4);
var namelen = blob.read_shift(2);
var efsz = blob.read_shift(2);
var fcsz = blob.read_shift(2);
blob.l += 8;
var offset = blob.read_shift(4);
var EF = parse_extra_field(/*::(*/blob.slice(blob.l+namelen, blob.l+namelen+efsz)/*:: :any)*/);
blob.l += namelen + efsz + fcsz;
var L = blob.l;
blob.l = offset + 4;
parse_local_file(blob, csz, usz, o, EF);
blob.l = L;
}
return o;
}
/* head starts just after local file header signature */
function parse_local_file(blob/*:CFBlob*/, csz/*:number*/, usz/*:number*/, o/*:CFBContainer*/, EF) {
/* [local file header] */
blob.l += 2;
var flags = blob.read_shift(2);
var meth = blob.read_shift(2);
var date = parse_dos_date(blob);
if(flags & 0x2041) throw new Error("Unsupported ZIP encryption");
var crc32 = blob.read_shift(4);
var _csz = blob.read_shift(4);
var _usz = blob.read_shift(4);
var namelen = blob.read_shift(2);
var efsz = blob.read_shift(2);
// TODO: flags & (1<<11) // UTF8
var name = ""; for(var i = 0; i < namelen; ++i) name += String.fromCharCode(blob[blob.l++]);
if(efsz) {
var ef = parse_extra_field(/*::(*/blob.slice(blob.l, blob.l + efsz)/*:: :any)*/);
if((ef[0x5455]||{}).mt) date = ef[0x5455].mt;
if(((EF||{})[0x5455]||{}).mt) date = EF[0x5455].mt;
}
blob.l += efsz;
/* [encryption header] */
/* [file data] */
var data = blob.slice(blob.l, blob.l + _csz);
switch(meth) {
case 8: data = _inflateRawSync(blob, _usz); break;
case 0: break;
default: throw new Error("Unsupported ZIP Compression method " + meth);
}
/* [data descriptor] */
var wrn = false;
if(flags & 8) {
crc32 = blob.read_shift(4);
if(crc32 == 0x08074b50) { crc32 = blob.read_shift(4); wrn = true; }
_csz = blob.read_shift(4);
_usz = blob.read_shift(4);
}
if(_csz != csz) warn_or_throw(wrn, "Bad compressed size: " + csz + " != " + _csz);
if(_usz != usz) warn_or_throw(wrn, "Bad uncompressed size: " + usz + " != " + _usz);
var _crc32 = CRC32.buf(data, 0);
if(crc32 != _crc32) warn_or_throw(wrn, "Bad CRC32 checksum: " + crc32 + " != " + _crc32);
cfb_add(o, name, data, {unsafe: true, mt: date});
}
function write_zip(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ {
var _opts = options || {};
var out = [], cdirs = [];
var o/*:CFBlob*/ = new_buf(1);
var method = (_opts.compression ? 8 : 0), flags = 0;
var desc = false;
if(desc) flags |= 8;
var i = 0, j = 0;
var start_cd = 0, fcnt = 0;
var root = cfb.FullPaths[0], fp = root, fi = cfb.FileIndex[0];
var crcs = [];
var sz_cd = 0;
for(i = 1; i < cfb.FullPaths.length; ++i) {
fp = cfb.FullPaths[i].slice(root.length); fi = cfb.FileIndex[i];
if(!fi.size || !fi.content || fp == "\u0001Sh33tJ5") continue;
var start = start_cd;
/* TODO: CP437 filename */
var namebuf = new_buf(fp.length);
for(j = 0; j < fp.length; ++j) namebuf.write_shift(1, fp.charCodeAt(j) & 0x7F);
namebuf = namebuf.slice(0, namebuf.l);
crcs[fcnt] = CRC32.buf(/*::((*/fi.content/*::||[]):any)*/, 0);
var outbuf = fi.content/*::||[]*/;
if(method == 8) outbuf = _deflateRawSync(outbuf);
/* local file header */
o = new_buf(30);
o.write_shift(4, 0x04034b50);
o.write_shift(2, 20);
o.write_shift(2, flags);
o.write_shift(2, method);
/* TODO: last mod file time/date */
if(fi.mt) write_dos_date(o, fi.mt);
else o.write_shift(4, 0);
o.write_shift(-4, (flags & 8) ? 0 : crcs[fcnt]);
o.write_shift(4, (flags & 8) ? 0 : outbuf.length);
o.write_shift(4, (flags & 8) ? 0 : /*::(*/fi.content/*::||[])*/.length);
o.write_shift(2, namebuf.length);
o.write_shift(2, 0);
start_cd += o.length;
out.push(o);
start_cd += namebuf.length;
out.push(namebuf);
/* TODO: encryption header ? */
start_cd += outbuf.length;
out.push(outbuf);
/* data descriptor */
if(flags & 8) {
o = new_buf(12);
o.write_shift(-4, crcs[fcnt]);
o.write_shift(4, outbuf.length);
o.write_shift(4, /*::(*/fi.content/*::||[])*/.length);
start_cd += o.l;
out.push(o);
}
/* central directory */
o = new_buf(46);
o.write_shift(4, 0x02014b50);
o.write_shift(2, 0);
o.write_shift(2, 20);
o.write_shift(2, flags);
o.write_shift(2, method);
o.write_shift(4, 0); /* TODO: last mod file time/date */
o.write_shift(-4, crcs[fcnt]);
o.write_shift(4, outbuf.length);
o.write_shift(4, /*::(*/fi.content/*::||[])*/.length);
o.write_shift(2, namebuf.length);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(4, 0);
o.write_shift(4, start);
sz_cd += o.l;
cdirs.push(o);
sz_cd += namebuf.length;
cdirs.push(namebuf);
++fcnt;
}
/* end of central directory */
o = new_buf(22);
o.write_shift(4, 0x06054b50);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, fcnt);
o.write_shift(2, fcnt);
o.write_shift(4, sz_cd);
o.write_shift(4, start_cd);
o.write_shift(2, 0);
return bconcat(([bconcat((out/*:any*/)), bconcat(cdirs), o]/*:any*/));
}
function cfb_new(opts/*:?any*/)/*:CFBContainer*/ {
var o/*:CFBContainer*/ = ({}/*:any*/);
init_cfb(o, opts);
@ -704,6 +1423,8 @@ function cfb_add(cfb/*:CFBContainer*/, name/*:string*/, content/*:?RawBytes*/, o
file.size = content ? content.length : 0;
if(opts) {
if(opts.CLSID) file.clsid = opts.CLSID;
if(opts.mt) file.mt = opts.mt;
if(opts.ct) file.ct = opts.ct;
}
return file;
}
@ -747,6 +1468,9 @@ exports.utils = {
CheckField: CheckField,
prep_blob: prep_blob,
bconcat: bconcat,
use_zlib: use_zlib,
_deflateRaw: _deflate,
_inflateRaw: _inflate,
consts: consts
};

@ -1,6 +1,13 @@
# not CFB or ZIP
apachepoi_testEXCEL_3.xls
apachepoi_testEXCEL_4.xls
xlrd_biff4_no_format_no_window2.xls
roo_type_excelx.xls
roo_type_openoffice.xls
libreoffice_calc_csv-import_malformed-quotes.xls
# file exceeding 31 chars
apachepoi_59746_NoRowNums.xlsx
apachepoi_WithEmbeded.xlsx
apachepoi_picture.xlsx
roo_name_with_leading_slash.xlsx
spout-xlsx_sheet_with_prefixed_xml_files.xlsx
# not a valid file
openpyxl_r_null_archive.xlsx

@ -32,7 +32,9 @@ a { text-decoration: none }
<b>Advanced Demo Options:</b>
Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" checked>
<a id="saveit" onclick="savefile();" href="#">Export data</a>
<b>Export Current File</b>
- <a id="savecfb" onclick="savefile('cfb');" href="#">Export data as CFB</a>
- <a id="savezip" onclick="savefile('zip');" href="#">Export data as ZIP</a>
</pre>
<pre id="out"></pre>
<br />
@ -40,7 +42,7 @@ Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" c
<script src="https://unpkg.com/printj/dist/printj.min.js"></script>
<script src="cfb.js"></script>
<script src="//rawgit.com/eligrey/Blob.js/master/Blob.js"></script>
<script src="//rawgit.com/eligrey/FileSaver.js/master/FileSaver.js"></script>
<script src="//unpkg.com/filesaver.js/FileSaver.js"></script>
<script>
/*jshint browser:true */
/* eslint-env browser */
@ -58,6 +60,7 @@ var get_manifest = (function() {
};
return function get_manifest(cfb) {
var out = [];
var rlen = cfb.FullPaths[0].length;
var basetime = new Date(1980,0,1);
var cnt = 0, rootsize = 0, filesize = 0;
@ -70,7 +73,10 @@ var get_manifest = (function() {
rootsize = file.size;
break;
case 2:
out.push(sprintf("%9lu %s <a href=\"#\" onclick=\"download_file(%d);\">%s</a>", file.size, format_date(basetime), i, fix_string(cfb.FullPaths[i])));
var fixname = fix_string(cfb.FullPaths[i]);
if(fixname.match(/\\u0001Sh33tJ5/)) return;
fixname = fixname.slice(rlen);
out.push(sprintf("%9lu %s <a href=\"#\" onclick=\"download_file(%d);\">%s</a>", file.size, format_date(basetime), i, fixname));
filesize += file.size;
++cnt;
}
@ -149,12 +155,12 @@ var savefile = (function() {
}
};
return function savefile() {
return function savefile(type) {
if(!global_cfb) return alert("Must load a file first!");
if(typeof console !== 'undefined') console.log(global_cfb);
var data = CFB.write(global_cfb, {type:'binary'});
var data = CFB.write(global_cfb, {type:'binary', fileType: type});
if(typeof console !== 'undefined') console.log(data);
saveAs(new Blob([s2ab(data)],{type:"application/octet-stream"}), "sheetjs.xls");
saveAs(new Blob([s2ab(data)],{type:"application/octet-stream"}), "SheetJSCFBDemo." + type);
};
})();

@ -64,6 +64,7 @@ type CFBWriteOpts = any;
interface CFBReadOpts {
type?:string;
root?:string;
};
type CFBFileIndex = Array<CFBEntry>;

@ -12,5 +12,8 @@ declare var bconcat:any;
declare var s2a:any;
declare var chr0:any;
declare var chr1:any;
declare var has_buf:boolean;
declare var new_buf:any;
declare var new_raw_buf:any;
declare var new_unsafe_buf:any;
*/

@ -1,6 +1,6 @@
{
"name": "cfb",
"version": "1.0.8",
"version": "1.1.0",
"author": "sheetjs",
"description": "Compound File Binary File Format extractor",
"keywords": [
@ -19,17 +19,18 @@
"fs": false
},
"dependencies": {
"commander": "^2.14.1",
"adler-32": "~1.2.0",
"commander": "^2.16.0",
"crc-32": "~1.2.0",
"printj": "~1.1.2"
},
"devDependencies": {
"crc-32": "~1.2.0",
"mocha": "~2.5.3",
"blanket": "~1.2.3",
"@sheetjs/uglify-js": "~2.7.3",
"@types/node": "^8.5.9",
"@types/commander": "^2.9.0",
"@types/node": "^8.10.25",
"blanket": "~1.2.3",
"dtslint": "~0.1.2",
"mocha": "~2.5.3",
"typescript": "2.2.0"
},
"repository": {

166
test.js

@ -12,9 +12,11 @@ declare var before:(test:EmptyFunc)=>void;
*/
var CFB;
var fs = require('fs');
describe('source', function() { it('should load', function() { CFB = require('./'); }); });
describe('source', function() { it('should load', function() { CFB = require('./'); if(zlibify) CFB.utils.use_zlib(require("zlib")); }); });
var CRC32 = require('crc-32');
var WTF = !!process.env.WTF;
var zlibify = !!process.env.ZLIB;
var ex = [".xls",".doc",".ppt"];
if(process.env.FMTS) ex=process.env.FMTS.split(":").map(function(x){return x[0]==="."?x:"."+x;});
if(process.env.FMTS === "full") process.env.FMTS = ex.join(":");
@ -26,12 +28,12 @@ var files = fs.readdirSync('test_files').filter(ffunc);
var f2011 = fs.readdirSync('test_files/2011').filter(ffunc);
var f2013 = fs.readdirSync('test_files/2013').filter(ffunc);
var fpres = fs.readdirSync('test_files_pres').filter(ffunc);
var fxlsx = fs.readdirSync('test_files').filter(function(x) { return x.slice(-5) == ".xlsx"; });
var fxlsx = fs.readdirSync('test_files').filter(function(x) { return x.slice(-5) == ".xlsx" && fails.indexOf(x) === -1; });
var dir = "./test_files/";
var TYPE = "buffer";
var names = [
var names/*:Array<Array<string>>*/ = [
["!DocumentSummaryInformation", "\u0005"],
["!SummaryInformation", "\u0005"],
["!CompObj", "\u0001"],
@ -41,23 +43,45 @@ var names = [
["!Ole", "\u0001"]
].map(function(x) { return [x[0], x[0].replace("!", x[1])]; });
/* [ rel, abs ] */
var ENTRIES/*:Array<Array<string>>*/ = [
/* DOC */
["WordDocument", "/WordDocument"],
/* PPT */
["PowerPoint Document", "/PowerPoint Document"],
/* XLS */
["Workbook", "/Workbook"],
["Book", "/Book"],
/* OPC */
["[Content_Types].xml", "/[Content_Types].xml"],
/* ODF */
["content.xml", "/content.xml"],
/* XLSX */
["workbook.xml", "/xl/workbook.xml"],
/* Encrypted */
["EncryptedPackage", "/EncryptedPackage"],
["EncryptionInfo", "/EncryptionInfo"]
];
var REL_FILES = ENTRIES.map(function(e) { return e[0]; });
var ABS_FILES = ENTRIES.map(function(e) { return e[1]; });
function has_file(cfb, files/*:Array<string>*/)/*:string*/ {
for(var i = 0; i < files.length; ++i) if(CFB.find(cfb, files[i])) return files[i];
return "";
}
function zero_dates(cfb) {
cfb.FileIndex.forEach(function(f) { delete f.mt; delete f.ct; });
}
function parsetest(x, cfb) {
describe(x + ' should have basic parts', function() {
it('should find relative path', function() {
switch(x.substr(-4)) {
case '.xls': if(!CFB.find(cfb, 'Workbook') && !CFB.find(cfb, 'Book')) throw new Error("Cannot find workbook for " + x); break;
case '.ppt': if(!CFB.find(cfb, 'PowerPoint Document')) throw new Error("Cannot find presentation for " + x); break;
case '.doc': if(!CFB.find(cfb, 'WordDocument') && !CFB.find(cfb, 'Word Document')) throw new Error("Cannot find doc for " + x); break;
case 'xlsx': if(!CFB.find(cfb, 'Workbook') && !CFB.find(cfb, 'Book') && !CFB.find(cfb, 'WordDocument') && !CFB.find(cfb, 'EncryptedPackage') && !CFB.find(cfb, 'EncryptionInfo')) throw new Error("Cannot find workbook or encryption for " + x); break;
}
if(!has_file(cfb, REL_FILES)) throw new Error("Cannot find content for " + x);
});
it('should find absolute path', function() {
switch(x.substr(-4)) {
case '.xls': if(!CFB.find(cfb, '/Workbook') && !CFB.find(cfb, '/Book')) throw new Error("Cannot find workbook for " + x); break;
case '.ppt': if(!CFB.find(cfb, '/PowerPoint Document')) throw new Error("Cannot find presentation for " + x); break;
case '.doc': if(!CFB.find(cfb, '/WordDocument') && !CFB.find(cfb, '/Word Document')) throw new Error("Cannot find doc for " + x); break;
case 'xlsx': if(!CFB.find(cfb, '/Workbook') && !CFB.find(cfb, '/Book') && !CFB.find(cfb, '/WordDocument') && !CFB.find(cfb, '/EncryptedPackage') && !CFB.find(cfb, '/EncryptionInfo')) throw new Error("Cannot find workbook or encryption for " + x); break;
}
if(!has_file(cfb, ABS_FILES)) throw new Error("Cannot find content for " + x);
});
it('should handle "!" aliases', function() {
names.forEach(function(n) { if(CFB.find(cfb,n[0]) != CFB.find(cfb,n[1])) throw new Error("Bad name: " + n.join(" != ")); });
@ -67,33 +91,50 @@ function parsetest(x, cfb) {
});
});
describe(x + ' should roundtrip', function() {
var data, newcfb;
it('should roundtrip safely', function() {
data = CFB.write(cfb, {type:TYPE});
newcfb = CFB.read(data, {type:TYPE});
var datacfb, newcfb;
var datazip, newzip;
var datacmp, newcmp;
before(function() {
/* cfb */
zero_dates(cfb);
datacfb = CFB.write(cfb, {type:TYPE});
newcfb = CFB.read(datacfb, {type:TYPE});
zero_dates(newcfb);
/* zip */
zero_dates(cfb);
datazip = CFB.write(cfb, {type:TYPE, fileType:"zip"});
newzip = CFB.read(datazip, {type:TYPE});
zero_dates(newzip);
/* zip with compression */
zero_dates(cfb);
datacmp = CFB.write(cfb, {type:TYPE, fileType:"zip", compression:1});
newcmp = CFB.read(datacmp, {type:TYPE});
zero_dates(newcmp);
});
if(x.substr(-4) !== "xlsx") it('should preserve content', function() {
var _old, _new;
switch(x.substr(-4)) {
case '.xls':
_old = CFB.find(cfb, '/Workbook') || CFB.find(cfb, '/Book');
_new = CFB.find(newcfb, '/Workbook') || CFB.find(newcfb, '/Book');
break;
case '.ppt':
_old = CFB.find(cfb, '/PowerPoint Document');
_new = CFB.find(newcfb, '/PowerPoint Document');
break;
case '.doc':
_old = CFB.find(cfb, '/WordDocument') || CFB.find(cfb, '/Word Document');
_new = CFB.find(newcfb, '/WordDocument') || CFB.find(newcfb, '/Word Document');
break;
}
/*:: if(!_old || !_new) throw "unreachable"; */
if(CRC32.buf(_old.content) != CRC32.buf(_new.content)) throw new Error(x + " failed roundtrip test");
it('should preserve content', function() {
var path = has_file(cfb, REL_FILES);
var _old = CFB.find(cfb, path);
var _cfb = CFB.find(newcfb, path);
var _zip = CFB.find(newzip, path);
var _cmp = CFB.find(newcmp, path);
/*:: if(!_old || !_cfb || !_zip || !_cmp) throw "unreachable"; */
var c1 = CRC32.buf(_old.content);
var c2 = CRC32.buf(_cfb.content);
var c3 = CRC32.buf(_zip.content);
var c4 = CRC32.buf(_cmp.content);
if(c1 != c2) throw new Error(x + " failed CFB roundtrip test");
if(c1 != c3) throw new Error(x + " failed ZIP roundtrip test");
if(c1 != c4) throw new Error(x + " failed ZIP compression roundtrip test");
});
it('should be idempotent', function() {
var dat2 = CFB.write(newcfb, {type:TYPE});
if(CRC32.buf(data) != CRC32.buf(dat2)) throw new Error(x + " failed idempotent test");
if(CRC32.buf(datacfb) != CRC32.buf(dat2)) throw new Error(x + " failed CFB idempotent test");
var dat2zip = CFB.write(newzip, {type:TYPE, fileType:"zip"});
if(CRC32.buf(datazip) != CRC32.buf(dat2zip)) throw new Error(x + " failed ZIP idempotent test");
var dat2cmp = CFB.write(newcmp, {type:TYPE, fileType:"zip", compression:1});
if(CRC32.buf(datacmp) != CRC32.buf(dat2cmp)) throw new Error(x + " failed ZIP idempotent test");
});
});
}
@ -101,32 +142,32 @@ function parsetest(x, cfb) {
describe('should parse test files', function() {
files.forEach(function(x) {
it('should parse ' + x, function() {
var cfb = CFB.read('./test_files/' + x, {type: "file"});
var cfb = CFB.read('./test_files/' + x, {type: "file", WTF: WTF});
parsetest(x, cfb);
});
});
fpres.forEach(function(x) {
it('should parse ' + x, function() {
var cfb = CFB.read('./test_files_pres/' + x, {type: "file"});
var cfb = CFB.read('./test_files_pres/' + x, {type: "file", WTF: WTF});
parsetest(x, cfb);
});
});
f2011.forEach(function(x) {
it('should parse ' + x, function() {
var cfb = CFB.read('./test_files/2011/' + x, {type: "file"});
var cfb = CFB.read('./test_files/2011/' + x, {type: "file", WTF: WTF});
parsetest(x, cfb);
});
});
f2013.forEach(function(x) {
it('should parse ' + x, function() {
var cfb = CFB.read('./test_files/2013/' + x, {type: "file"});
var cfb = CFB.read('./test_files/2013/' + x, {type: "file", WTF: WTF});
parsetest(x, cfb);
});
});
fxlsx.forEach(function(x) {
it('should parse ' + x, function() {
try {
var cfb = CFB.read('./test_files/' + x, {type: "file"});
var cfb = CFB.read('./test_files/' + x, {type: "file", WTF: WTF});
parsetest(x, cfb);
} catch(e) {
if(e.message.match(/CFB file size /)) return;
@ -147,16 +188,53 @@ describe('should parse test files', function() {
});
var cp = 'custom_properties.xls';
var xl = 'custom_properties.xlsx';
describe('input formats', function() {
it('should read binary strings', function() {
CFB.read(fs.readFileSync(dir + '/' + cp, 'binary'), {type: 'binary'});
CFB.read(fs.readFileSync(dir + '/' + xl, 'binary'), {type: 'binary'});
});
it('should read base64 strings', function() {
CFB.read(fs.readFileSync(dir + '/' + cp, 'base64'), {type: 'base64'});
CFB.read(fs.readFileSync(dir + '/' + xl, 'base64'), {type: 'base64'});
});
it('should read buffers', function() {
CFB.read(fs.readFileSync(dir + '/' + cp), {type: 'buffer'});
CFB.read(fs.readFileSync(dir + '/' + xl), {type: 'buffer'});
});
});
describe('output formats', function() {
it('should write binary strings', function() {
var t = [
[ "CFB", CFB.write(CFB.read(fs.readFileSync(dir + '/' + cp, 'binary'), {type: 'binary'}), {type: 'binary'})],
[ "ZIP", CFB.write(CFB.read(fs.readFileSync(dir + '/' + xl, 'binary'), {type: 'binary'}), {type: 'binary', fileType: 'zip'})]
];
t.forEach(function(r) {
if(typeof r[1] != "string") throw new Error(r[0] + " binary write failed");
var good = false;
for(var i = 0; i < r[1].length; ++i) {
if(r[1].charCodeAt(i) == 0x00) good = true;
else if(r[1].charCodeAt(i) > 0xFF) { good = false; break; }
}
if(!good) throw new Error(r[0] + " binary write failed");
});
});
it('should write base64 strings', function() {
var t = [
[ "CFB", CFB.write(CFB.read(fs.readFileSync(dir + '/' + cp, 'base64'), {type: 'base64'}), {type: 'base64'})],
[ "ZIP", CFB.write(CFB.read(fs.readFileSync(dir + '/' + xl, 'base64'), {type: 'base64'}), {type: 'base64', fileType: 'zip'})]
];
t.forEach(function(r) {
if(typeof r[1] != "string") throw new Error(r[0] + " base64 write failed");
var good = false;
if(r[1].match(/[^a-zA-Z0-9+\/\+\.=]/)) throw new Error(r[0] + " base64 write failed");
});
});
it('should write buffers', function() {
var t1 = CFB.write(CFB.read(fs.readFileSync(dir + '/' + cp), {type: 'buffer'}), {type: 'buffer'});
var t2 = CFB.write(CFB.read(fs.readFileSync(dir + '/' + xl), {type: 'buffer'}), {type: 'buffer'});
if(!Buffer.isBuffer(t1)) throw new Error("CFB buffer write failed");
if(!Buffer.isBuffer(t2)) throw new Error("ZIP buffer write failed");
});
});

@ -1,6 +1,7 @@
/* cfb.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* eslint-env node */
/* vim: set ts=2 ft=javascript: */
/// <reference types="../node_modules/@types/node" />
const n = "cfb";
import * as X from 'cfb';
import fs = require('fs');

23
types/index.d.ts vendored

@ -14,27 +14,40 @@ export function read(f: CFB$Blob | string, options?: CFB$ParsingOptions): CFB$Co
export function find(cfb: CFB$Container, path: string): CFB$Entry | null;
/** Generate a container file */
export function write(cfb: CFB$Container, options?: any): any;
export function write(cfb: CFB$Container, options?: CFB$WritingOptions): any;
/** Write a container file to the filesystem */
export function writeFile(cfb: CFB$Container, filename: string, options?: any): any;
export function writeFile(cfb: CFB$Container, filename: string, options?: CFB$WritingOptions): any;
/** Utility functions */
export const utils: CFB$Utils;
/** Options for read and readFile */
export interface CFB$ParsingOptions {
export interface CFB$CommonOptions {
/** Input data encoding */
type?: 'base64' | 'binary' | 'buffer' | 'file' | 'array';
/** If true, throw errors when features are not understood */
WTF?: boolean;
}
/** Options for read and readFile */
export interface CFB$ParsingOptions extends CFB$CommonOptions {
/** If true, include raw data in output */
raw?: boolean;
}
/** Options for write and writeFile */
export interface CFB$WritingOptions extends CFB$CommonOptions {
/** Output file type */
fileType?: 'cfb' | 'zip';
/** Override default root entry name (CFB only) */
root?: string;
/** Enable compression (ZIP only) */
compression?: boolean;
}
export type CFB$Blob = number[] | Uint8Array;
export enum CFB$EntryType { unknown, storage, stream, lockbytes, property, root }

@ -6,6 +6,8 @@
"only-arrow-functions": false,
"no-consecutive-blank-lines": false,
"prefer-conditional-expression": false,
"one-variable-per-declaration": false
"one-variable-per-declaration": false,
"no-implicit-dependencies": false,
"prefer-template": false
}
}

@ -12,13 +12,16 @@ declare var bconcat:any;
declare var s2a:any;
declare var chr0:any;
declare var chr1:any;
declare var has_buf:boolean;
declare var new_buf:any;
declare var new_raw_buf:any;
declare var new_unsafe_buf:any;
*/
/* cfb.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
/*jshint eqnull:true */
/*exported CFB */
/*global module, require:false, process:false, Buffer:false, Uint8Array:false */
/*global module, require:false, process:false, Buffer:false, Uint8Array:false, Uint16Array:false */
/*::
declare var DO_NOT_EXPORT_CFB:?boolean;
@ -35,10 +38,111 @@ type SectorList = {
}
type CFBFiles = {[n:string]:CFBEntry};
*/
/* crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
/*exported CRC32 */
var CRC32;
(function (factory) {
/*jshint ignore:start */
/*eslint-disable */
factory(CRC32 = {});
/*eslint-enable */
/*jshint ignore:end */
}(function(CRC32) {
CRC32.version = '1.2.0';
/* see perf/crc32table.js */
/*global Int32Array */
function signed_crc_table()/*:any*/ {
var c = 0, table/*:Array<number>*/ = new Array(256);
for(var n =0; n != 256; ++n){
c = n;
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
table[n] = c;
}
return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table;
}
var T = signed_crc_table();
function crc32_bstr(bstr/*:string*/, seed/*:number*/)/*:number*/ {
var C = seed ^ -1, L = bstr.length - 1;
for(var i = 0; i < L;) {
C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
}
if(i === L) C = (C>>>8) ^ T[(C ^ bstr.charCodeAt(i))&0xFF];
return C ^ -1;
}
function crc32_buf(buf/*:Uint8Array|Array<number>*/, seed/*:number*/)/*:number*/ {
if(buf.length > 10000) return crc32_buf_8(buf, seed);
var C = seed ^ -1, L = buf.length - 3;
for(var i = 0; i < L;) {
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
}
while(i < L+3) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
return C ^ -1;
}
function crc32_buf_8(buf/*:Uint8Array|Array<number>*/, seed/*:number*/)/*:number*/ {
var C = seed ^ -1, L = buf.length - 7;
for(var i = 0; i < L;) {
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
}
while(i < L+7) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
return C ^ -1;
}
function crc32_str(str/*:string*/, seed/*:number*/)/*:number*/ {
var C = seed ^ -1;
for(var i = 0, L=str.length, c, d; i < L;) {
c = str.charCodeAt(i++);
if(c < 0x80) {
C = (C>>>8) ^ T[(C ^ c)&0xFF];
} else if(c < 0x800) {
C = (C>>>8) ^ T[(C ^ (192|((c>>6)&31)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
} else if(c >= 0xD800 && c < 0xE000) {
c = (c&1023)+64; d = str.charCodeAt(i++)&1023;
C = (C>>>8) ^ T[(C ^ (240|((c>>8)&7)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|((c>>2)&63)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|(d&63)))&0xFF];
} else {
C = (C>>>8) ^ T[(C ^ (224|((c>>12)&15)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|((c>>6)&63)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
}
}
return C ^ -1;
}
CRC32.table = T;
CRC32.bstr = crc32_bstr;
CRC32.buf = crc32_buf;
CRC32.str = crc32_str;
}));
/* [MS-CFB] v20171201 */
var CFB = (function _CFB(){
var exports/*:CFBModule*/ = /*::(*/{}/*:: :any)*/;
exports.version = '1.0.8';
exports.version = '1.1.0';
/* [MS-CFB] 2.6.4 */
function namecmp(l/*:string*/, r/*:string*/)/*:number*/ {
var L = l.split("/"), R = r.split("/");
@ -59,9 +163,75 @@ function filename(p/*:string*/)/*:string*/ {
var c = p.lastIndexOf("/");
return (c === -1) ? p : p.slice(c+1);
}
/* -------------------------------------------------------------------------- */
/* DOS Date format:
high|YYYYYYYm.mmmddddd.HHHHHMMM.MMMSSSSS|low
add 1980 to stored year
stored second should be doubled
*/
/* write JS date to buf as a DOS date */
function write_dos_date(buf/*:CFBlob*/, date/*:Date|string*/) {
if(typeof date === "string") date = new Date(date);
var hms/*:number*/ = date.getHours();
hms = hms << 6 | date.getMinutes();
hms = hms << 5 | (date.getSeconds()>>>1);
buf.write_shift(2, hms);
var ymd/*:number*/ = (date.getFullYear() - 1980);
ymd = ymd << 4 | (date.getMonth()+1);
ymd = ymd << 5 | date.getDate();
buf.write_shift(2, ymd);
}
/* read four bytes from buf and interpret as a DOS date */
function parse_dos_date(buf/*:CFBlob*/)/*:Date*/ {
var hms = buf.read_shift(2) & 0xFFFF;
var ymd = buf.read_shift(2) & 0xFFFF;
var val = new Date();
var d = ymd & 0x1F; ymd >>>= 5;
var m = ymd & 0x0F; ymd >>>= 4;
val.setMilliseconds(0);
val.setFullYear(ymd + 1980);
val.setMonth(m-1);
val.setDate(d);
var S = hms & 0x1F; hms >>>= 5;
var M = hms & 0x3F; hms >>>= 6;
val.setHours(hms);
val.setMinutes(M);
val.setSeconds(S<<1);
return val;
}
function parse_extra_field(blob/*:CFBlob*/)/*:any*/ {
prep_blob(blob, 0);
var o = /*::(*/{}/*:: :any)*/;
var flags = 0;
while(blob.l <= blob.length - 4) {
var type = blob.read_shift(2);
var sz = blob.read_shift(2), tgt = blob.l + sz;
var p = {};
switch(type) {
/* UNIX-style Timestamps */
case 0x5455: {
flags = blob.read_shift(1);
if(flags & 1) p.mtime = blob.read_shift(4);
/* for some reason, CD flag corresponds to LFH */
if(sz > 5) {
if(flags & 2) p.atime = blob.read_shift(4);
if(flags & 4) p.ctime = blob.read_shift(4);
}
if(p.mtime) p.mt = new Date(p.mtime*1000);
}
break;
}
blob.l = tgt;
o[type] = p;
}
return o;
}
var fs/*:: = require('fs'); */;
function get_fs() { return fs || (fs = require('fs')); }
function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
if(file[0] == 0x50 && file[1] == 0x4b) return parse_zip(file, options);
if(file.length < 512) throw new Error("CFB file size " + file.length + " < 512");
var mver = 3;
var ssz = 512;
@ -82,6 +252,8 @@ var mv = check_get_mver(blob);
mver = mv[0];
switch(mver) {
case 3: ssz = 512; break; case 4: ssz = 4096; break;
case 0: if(mv[1] == 0) return parse_zip(file, options);
/* falls through */
default: throw new Error("Major Version: Expected 3 or 4 saw " + mver);
}
@ -160,6 +332,7 @@ return o;
/* [MS-CFB] 2.2 Compound File Header -- read up to major version */
function check_get_mver(blob/*:CFBlob*/)/*:[number, number]*/ {
if(blob[blob.l] == 0x50 && blob[blob.l + 1] == 0x4b) return [0, 0];
// header signature 8
blob.chk(HEADER_SIGNATURE, 'Header Signature: ');
@ -477,6 +650,7 @@ function rebuild_cfb(cfb/*:CFBContainer*/, f/*:?boolean*/)/*:void*/ {
function _write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ {
var _opts = options || {};
rebuild_cfb(cfb);
if(_opts.fileType == 'zip') return write_zip(cfb, _opts);
var L = (function(cfb/*:CFBContainer*/)/*:Array<number>*/{
var mini_size = 0, fat_size = 0;
for(var i = 0; i < cfb.FileIndex.length; ++i) {
@ -677,6 +851,551 @@ function write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|string
}
return o;
}
/* node < 8.1 zlib does not expose bytesRead, so default to pure JS */
var _zlib;
function use_zlib(zlib) { try {
var InflateRaw = zlib.InflateRaw;
var InflRaw = new InflateRaw();
InflRaw._processChunk(new Uint8Array([3, 0]), InflRaw._finishFlushFlag);
if(InflRaw.bytesRead) _zlib = zlib;
else throw new Error("zlib does not expose bytesRead");
} catch(e) {console.error("cannot use native zlib: " + (e.message || e)); } }
function _inflateRawSync(payload, usz) {
if(!_zlib) return _inflate(payload, usz);
var InflateRaw = _zlib.InflateRaw;
var InflRaw = new InflateRaw();
var out = InflRaw._processChunk(payload.slice(payload.l), InflRaw._finishFlushFlag);
payload.l += InflRaw.bytesRead;
return out;
}
function _deflateRawSync(payload) {
return _zlib ? _zlib.deflateRawSync(payload) : _deflate(payload);
}
var CLEN_ORDER = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];
/* LEN_ID = [ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285 ]; */
var LEN_LN = [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13 , 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 ];
/* DST_ID = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]; */
var DST_LN = [ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 ];
function bit_swap_8(n) { var t = (((((n<<1)|(n<<11)) & 0x22110) | (((n<<5)|(n<<15)) & 0x88440))); return ((t>>16) | (t>>8) |t)&0xFF; }
var use_typed_arrays = typeof Uint8Array !== 'undefined';
var bitswap8 = use_typed_arrays ? new Uint8Array(1<<8) : [];
for(var q = 0; q < (1<<8); ++q) bitswap8[q] = bit_swap_8(q);
function bit_swap_n(n, b) {
var rev = bitswap8[n & 0xFF];
if(b <= 8) return rev >>> (8-b);
rev = (rev << 8) | bitswap8[(n>>8)&0xFF];
if(b <= 16) return rev >>> (16-b);
rev = (rev << 8) | bitswap8[(n>>16)&0xFF];
return rev >>> (24-b);
}
/* helpers for unaligned bit reads */
function read_bits_2(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 6 ? 0 : buf[h+1]<<8))>>>w)& 0x03; }
function read_bits_3(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 5 ? 0 : buf[h+1]<<8))>>>w)& 0x07; }
function read_bits_4(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 4 ? 0 : buf[h+1]<<8))>>>w)& 0x0F; }
function read_bits_5(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 3 ? 0 : buf[h+1]<<8))>>>w)& 0x1F; }
function read_bits_7(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 1 ? 0 : buf[h+1]<<8))>>>w)& 0x7F; }
/* works up to n = 3 * 8 + 1 = 25 */
function read_bits_n(buf, bl, n) {
var w = (bl&7), h = (bl>>>3), f = ((1<<n)-1);
var v = buf[h] >>> w;
if(n < 8 - w) return v & f;
v |= buf[h+1]<<(8-w);
if(n < 16 - w) return v & f;
v |= buf[h+2]<<(16-w);
if(n < 24 - w) return v & f;
v |= buf[h+3]<<(24-w);
return v & f;
}
/* until ArrayBuffer#realloc is a thing, fake a realloc */
function realloc(b, sz/*:number*/) {
var L = b.length, M = 2*L > sz ? 2*L : sz + 5, i = 0;
if(L >= sz) return b;
if(has_buf) {
var o = new_unsafe_buf(M);
// $FlowIgnore
if(b.copy) b.copy(o);
else for(; i < b.length; ++i) o[i] = b[i];
return o;
} else if(use_typed_arrays) {
var a = new Uint8Array(M);
if(a.set) a.set(b);
else for(; i < b.length; ++i) a[i] = b[i];
return a;
}
b.length = M;
return b;
}
/* zero-filled arrays for older browsers */
function zero_fill_array(n) {
var o = new Array(n);
for(var i = 0; i < n; ++i) o[i] = 0;
return o;
}var _deflate = (function() {
var _deflateRaw = (function() {
return function deflateRaw(data, out) {
var boff = 0;
while(boff < data.length) {
var L = Math.min(0xFFFF, data.length - boff);
var h = boff + L == data.length;
/* TODO: this is only type 0 stored */
out.write_shift(1, +h);
out.write_shift(2, L);
out.write_shift(2, (~L) & 0xFFFF);
while(L-- > 0) out[out.l++] = data[boff++];
}
return out.l;
};
})();
return function(data) {
var buf = new_buf(50+Math.floor(data.length*1.1));
var off = _deflateRaw(data, buf);
return buf.slice(0, off);
};
})();
/* modified inflate function also moves original read head */
/* build tree (used for literals and lengths) */
function build_tree(clens, cmap, MAX/*:number*/)/*:number*/ {
var maxlen = 1, w = 0, i = 0, j = 0, ccode = 0, L = clens.length;
var bl_count = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
for(i = 0; i < 32; ++i) bl_count[i] = 0;
for(i = L; i < MAX; ++i) clens[i] = 0;
L = clens.length;
var ctree = use_typed_arrays ? new Uint16Array(L) : zero_fill_array(L); // []
/* build code tree */
for(i = 0; i < L; ++i) {
bl_count[(w = clens[i])]++;
if(maxlen < w) maxlen = w;
ctree[i] = 0;
}
bl_count[0] = 0;
for(i = 1; i <= maxlen; ++i) bl_count[i+16] = (ccode = (ccode + bl_count[i-1])<<1);
for(i = 0; i < L; ++i) {
ccode = clens[i];
if(ccode != 0) ctree[i] = bl_count[ccode+16]++;
}
/* cmap[maxlen + 4 bits] = (off&15) + (lit<<4) reverse mapping */
var cleni = 0;
for(i = 0; i < L; ++i) {
cleni = clens[i];
if(cleni != 0) {
ccode = bit_swap_n(ctree[i], maxlen)>>(maxlen-cleni);
for(j = (1<<(maxlen + 4 - cleni)) - 1; j>=0; --j)
cmap[ccode|(j<<cleni)] = (cleni&15) | (i<<4);
}
}
return maxlen;
}
var fix_lmap = use_typed_arrays ? new Uint16Array(512) : zero_fill_array(512);
var fix_dmap = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
if(!use_typed_arrays) {
for(var i = 0; i < 512; ++i) fix_lmap[i] = 0;
for(i = 0; i < 32; ++i) fix_dmap[i] = 0;
}
(function() {
var dlens/*:Array<number>*/ = [];
var i = 0;
for(;i<32; i++) dlens.push(5);
build_tree(dlens, fix_dmap, 32);
var clens/*:Array<number>*/ = [];
i = 0;
for(; i<=143; i++) clens.push(8);
for(; i<=255; i++) clens.push(9);
for(; i<=279; i++) clens.push(7);
for(; i<=287; i++) clens.push(8);
build_tree(clens, fix_lmap, 288);
})();
var dyn_lmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
var dyn_dmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
var dyn_cmap = use_typed_arrays ? new Uint16Array(128) : zero_fill_array(128);
var dyn_len_1 = 1, dyn_len_2 = 1;
/* 5.5.3 Expanding Huffman Codes */
function dyn(data, boff/*:number*/) {
/* nomenclature from RFC1951 refers to bit values; these are offset by the implicit constant */
var _HLIT = read_bits_5(data, boff) + 257; boff += 5;
var _HDIST = read_bits_5(data, boff) + 1; boff += 5;
var _HCLEN = read_bits_4(data, boff) + 4; boff += 4;
var w = 0;
/* grab and store code lengths */
var clens = use_typed_arrays ? new Uint8Array(19) : zero_fill_array(19);
var ctree = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
var maxlen = 1;
var bl_count = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
var next_code = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
var L = clens.length; /* 19 */
for(var i = 0; i < _HCLEN; ++i) {
clens[CLEN_ORDER[i]] = w = read_bits_3(data, boff);
if(maxlen < w) maxlen = w;
bl_count[w]++;
boff += 3;
}
/* build code tree */
var ccode = 0;
bl_count[0] = 0;
for(i = 1; i <= maxlen; ++i) next_code[i] = ccode = (ccode + bl_count[i-1])<<1;
for(i = 0; i < L; ++i) if((ccode = clens[i]) != 0) ctree[i] = next_code[ccode]++;
/* cmap[7 bits from stream] = (off&7) + (lit<<3) */
var cleni = 0;
for(i = 0; i < L; ++i) {
cleni = clens[i];
if(cleni != 0) {
ccode = bitswap8[ctree[i]]>>(8-cleni);
for(var j = (1<<(7-cleni))-1; j>=0; --j) dyn_cmap[ccode|(j<<cleni)] = (cleni&7) | (i<<3);
}
}
/* read literal and dist codes at once */
var hcodes/*:Array<number>*/ = [];
maxlen = 1;
for(; hcodes.length < _HLIT + _HDIST;) {
ccode = dyn_cmap[read_bits_7(data, boff)];
boff += ccode & 7;
switch((ccode >>>= 3)) {
case 16:
w = 3 + read_bits_2(data, boff); boff += 2;
ccode = hcodes[hcodes.length - 1];
while(w-- > 0) hcodes.push(ccode);
break;
case 17:
w = 3 + read_bits_3(data, boff); boff += 3;
while(w-- > 0) hcodes.push(0);
break;
case 18:
w = 11 + read_bits_7(data, boff); boff += 7;
while(w -- > 0) hcodes.push(0);
break;
default:
hcodes.push(ccode);
if(maxlen < ccode) maxlen = ccode;
break;
}
}
/* build literal / length trees */
var h1 = hcodes.slice(0, _HLIT), h2 = hcodes.slice(_HLIT);
for(i = _HLIT; i < 286; ++i) h1[i] = 0;
for(i = _HDIST; i < 30; ++i) h2[i] = 0;
dyn_len_1 = build_tree(h1, dyn_lmap, 286);
dyn_len_2 = build_tree(h2, dyn_dmap, 30);
return boff;
}
/* return [ data, bytesRead ] */
function inflate(data, usz/*:number*/) {
/* shortcircuit for empty buffer [0x03, 0x00] */
if(data[0] == 3 && !(data[1] & 0x3)) { return [new_raw_buf(usz), 2]; }
/* bit offset */
var boff = 0;
/* header includes final bit and type bits */
var header = 0;
var outbuf = new_unsafe_buf(usz ? usz : (1<<18));
var woff = 0;
var OL = outbuf.length>>>0;
var max_len_1 = 0, max_len_2 = 0;
while((header&1) == 0) {
header = read_bits_3(data, boff); boff += 3;
if((header >>> 1) == 0) {
/* Stored block */
if(boff & 7) boff += 8 - (boff&7);
/* 2 bytes sz, 2 bytes bit inverse */
var sz = data[boff>>>3] | data[(boff>>>3)+1]<<8;
boff += 32;
/* push sz bytes */
if(!usz && OL < woff + sz) { outbuf = realloc(outbuf, woff + sz); OL = outbuf.length; }
if(typeof data.copy === 'function') {
// $FlowIgnore
data.copy(outbuf, woff, boff>>>3, (boff>>>3)+sz);
woff += sz; boff += 8*sz;
} else while(sz-- > 0) { outbuf[woff++] = data[boff>>>3]; boff += 8; }
continue;
} else if((header >>> 1) == 1) {
/* Fixed Huffman */
max_len_1 = 9; max_len_2 = 5;
} else {
/* Dynamic Huffman */
boff = dyn(data, boff);
max_len_1 = dyn_len_1; max_len_2 = dyn_len_2;
}
if(!usz && (OL < woff + 32767)) { outbuf = realloc(outbuf, woff + 32767); OL = outbuf.length; }
for(;;) { // while(true) is apparently out of vogue in modern JS circles
/* ingest code and move read head */
var bits = read_bits_n(data, boff, max_len_1);
var code = (header>>>1) == 1 ? fix_lmap[bits] : dyn_lmap[bits];
boff += code & 15; code >>>= 4;
/* 0-255 are literals, 256 is end of block token, 257+ are copy tokens */
if(((code>>>8)&0xFF) === 0) outbuf[woff++] = code;
else if(code == 256) break;
else {
code -= 257;
var len_eb = (code < 8) ? 0 : ((code-4)>>2); if(len_eb > 5) len_eb = 0;
var tgt = woff + LEN_LN[code];
/* length extra bits */
if(len_eb > 0) {
tgt += read_bits_n(data, boff, len_eb);
boff += len_eb;
}
/* dist code */
bits = read_bits_n(data, boff, max_len_2);
code = (header>>>1) == 1 ? fix_dmap[bits] : dyn_dmap[bits];
boff += code & 15; code >>>= 4;
var dst_eb = (code < 4 ? 0 : (code-2)>>1);
var dst = DST_LN[code];
/* dist extra bits */
if(dst_eb > 0) {
dst += read_bits_n(data, boff, dst_eb);
boff += dst_eb;
}
/* in the common case, manual byte copy is faster than TA set / Buffer copy */
if(!usz && OL < tgt) { outbuf = realloc(outbuf, tgt); OL = outbuf.length; }
while(woff < tgt) { outbuf[woff] = outbuf[woff - dst]; ++woff; }
}
}
}
return [usz ? outbuf : outbuf.slice(0, woff), (boff+7)>>>3];
}
function _inflate(payload, usz) {
var data = payload.slice(payload.l||0);
var out = inflate(data, usz);
payload.l += out[1];
return out[0];
}
function warn_or_throw(wrn, msg) {
if(wrn) { if(typeof console !== 'undefined') console.error(msg); }
else throw new Error(msg);
}
function parse_zip(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
var blob/*:CFBlob*/ = /*::(*/file/*:: :any)*/;
prep_blob(blob, 0);
var FileIndex/*:CFBFileIndex*/ = [], FullPaths/*:Array<string>*/ = [];
var o = {
FileIndex: FileIndex,
FullPaths: FullPaths
};
init_cfb(o, { root: options.root });
/* find end of central directory, start just after signature */
var i = blob.length - 4;
while((blob[i] != 0x50 || blob[i+1] != 0x4b || blob[i+2] != 0x05 || blob[i+3] != 0x06) && i >= 0) --i;
blob.l = i + 4;
/* parse end of central directory */
blob.l += 4;
var fcnt = blob.read_shift(2);
blob.l += 6;
var start_cd = blob.read_shift(4);
/* parse central directory */
blob.l = start_cd;
for(i = 0; i < fcnt; ++i) {
/* trust local file header instead of CD entry */
blob.l += 20;
var csz = blob.read_shift(4);
var usz = blob.read_shift(4);
var namelen = blob.read_shift(2);
var efsz = blob.read_shift(2);
var fcsz = blob.read_shift(2);
blob.l += 8;
var offset = blob.read_shift(4);
var EF = parse_extra_field(/*::(*/blob.slice(blob.l+namelen, blob.l+namelen+efsz)/*:: :any)*/);
blob.l += namelen + efsz + fcsz;
var L = blob.l;
blob.l = offset + 4;
parse_local_file(blob, csz, usz, o, EF);
blob.l = L;
}
return o;
}
/* head starts just after local file header signature */
function parse_local_file(blob/*:CFBlob*/, csz/*:number*/, usz/*:number*/, o/*:CFBContainer*/, EF) {
/* [local file header] */
blob.l += 2;
var flags = blob.read_shift(2);
var meth = blob.read_shift(2);
var date = parse_dos_date(blob);
if(flags & 0x2041) throw new Error("Unsupported ZIP encryption");
var crc32 = blob.read_shift(4);
var _csz = blob.read_shift(4);
var _usz = blob.read_shift(4);
var namelen = blob.read_shift(2);
var efsz = blob.read_shift(2);
// TODO: flags & (1<<11) // UTF8
var name = ""; for(var i = 0; i < namelen; ++i) name += String.fromCharCode(blob[blob.l++]);
if(efsz) {
var ef = parse_extra_field(/*::(*/blob.slice(blob.l, blob.l + efsz)/*:: :any)*/);
if((ef[0x5455]||{}).mt) date = ef[0x5455].mt;
if(((EF||{})[0x5455]||{}).mt) date = EF[0x5455].mt;
}
blob.l += efsz;
/* [encryption header] */
/* [file data] */
var data = blob.slice(blob.l, blob.l + _csz);
switch(meth) {
case 8: data = _inflateRawSync(blob, _usz); break;
case 0: break;
default: throw new Error("Unsupported ZIP Compression method " + meth);
}
/* [data descriptor] */
var wrn = false;
if(flags & 8) {
crc32 = blob.read_shift(4);
if(crc32 == 0x08074b50) { crc32 = blob.read_shift(4); wrn = true; }
_csz = blob.read_shift(4);
_usz = blob.read_shift(4);
}
if(_csz != csz) warn_or_throw(wrn, "Bad compressed size: " + csz + " != " + _csz);
if(_usz != usz) warn_or_throw(wrn, "Bad uncompressed size: " + usz + " != " + _usz);
var _crc32 = CRC32.buf(data, 0);
if(crc32 != _crc32) warn_or_throw(wrn, "Bad CRC32 checksum: " + crc32 + " != " + _crc32);
cfb_add(o, name, data, {unsafe: true, mt: date});
}
function write_zip(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ {
var _opts = options || {};
var out = [], cdirs = [];
var o/*:CFBlob*/ = new_buf(1);
var method = (_opts.compression ? 8 : 0), flags = 0;
var desc = false;
if(desc) flags |= 8;
var i = 0, j = 0;
var start_cd = 0, fcnt = 0;
var root = cfb.FullPaths[0], fp = root, fi = cfb.FileIndex[0];
var crcs = [];
var sz_cd = 0;
for(i = 1; i < cfb.FullPaths.length; ++i) {
fp = cfb.FullPaths[i].slice(root.length); fi = cfb.FileIndex[i];
if(!fi.size || !fi.content || fp == "\u0001Sh33tJ5") continue;
var start = start_cd;
/* TODO: CP437 filename */
var namebuf = new_buf(fp.length);
for(j = 0; j < fp.length; ++j) namebuf.write_shift(1, fp.charCodeAt(j) & 0x7F);
namebuf = namebuf.slice(0, namebuf.l);
crcs[fcnt] = CRC32.buf(/*::((*/fi.content/*::||[]):any)*/, 0);
var outbuf = fi.content/*::||[]*/;
if(method == 8) outbuf = _deflateRawSync(outbuf);
/* local file header */
o = new_buf(30);
o.write_shift(4, 0x04034b50);
o.write_shift(2, 20);
o.write_shift(2, flags);
o.write_shift(2, method);
/* TODO: last mod file time/date */
if(fi.mt) write_dos_date(o, fi.mt);
else o.write_shift(4, 0);
o.write_shift(-4, (flags & 8) ? 0 : crcs[fcnt]);
o.write_shift(4, (flags & 8) ? 0 : outbuf.length);
o.write_shift(4, (flags & 8) ? 0 : /*::(*/fi.content/*::||[])*/.length);
o.write_shift(2, namebuf.length);
o.write_shift(2, 0);
start_cd += o.length;
out.push(o);
start_cd += namebuf.length;
out.push(namebuf);
/* TODO: encryption header ? */
start_cd += outbuf.length;
out.push(outbuf);
/* data descriptor */
if(flags & 8) {
o = new_buf(12);
o.write_shift(-4, crcs[fcnt]);
o.write_shift(4, outbuf.length);
o.write_shift(4, /*::(*/fi.content/*::||[])*/.length);
start_cd += o.l;
out.push(o);
}
/* central directory */
o = new_buf(46);
o.write_shift(4, 0x02014b50);
o.write_shift(2, 0);
o.write_shift(2, 20);
o.write_shift(2, flags);
o.write_shift(2, method);
o.write_shift(4, 0); /* TODO: last mod file time/date */
o.write_shift(-4, crcs[fcnt]);
o.write_shift(4, outbuf.length);
o.write_shift(4, /*::(*/fi.content/*::||[])*/.length);
o.write_shift(2, namebuf.length);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(4, 0);
o.write_shift(4, start);
sz_cd += o.l;
cdirs.push(o);
sz_cd += namebuf.length;
cdirs.push(namebuf);
++fcnt;
}
/* end of central directory */
o = new_buf(22);
o.write_shift(4, 0x06054b50);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, fcnt);
o.write_shift(2, fcnt);
o.write_shift(4, sz_cd);
o.write_shift(4, start_cd);
o.write_shift(2, 0);
return bconcat(([bconcat((out/*:any*/)), bconcat(cdirs), o]/*:any*/));
}
function cfb_new(opts/*:?any*/)/*:CFBContainer*/ {
var o/*:CFBContainer*/ = ({}/*:any*/);
init_cfb(o, opts);
@ -704,6 +1423,8 @@ function cfb_add(cfb/*:CFBContainer*/, name/*:string*/, content/*:?RawBytes*/, o
file.size = content ? content.length : 0;
if(opts) {
if(opts.CLSID) file.clsid = opts.CLSID;
if(opts.mt) file.mt = opts.mt;
if(opts.ct) file.ct = opts.ct;
}
return file;
}
@ -747,6 +1468,9 @@ exports.utils = {
CheckField: CheckField,
prep_blob: prep_blob,
bconcat: bconcat,
use_zlib: use_zlib,
_deflateRaw: _deflate,
_inflateRaw: _inflate,
consts: consts
};

725
xlscfb.js

@ -3,12 +3,113 @@ var DO_NOT_EXPORT_CFB = true;
/* vim: set ts=2: */
/*jshint eqnull:true */
/*exported CFB */
/*global module, require:false, process:false, Buffer:false, Uint8Array:false */
/*global module, require:false, process:false, Buffer:false, Uint8Array:false, Uint16Array:false */
/* crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
/*exported CRC32 */
var CRC32;
(function (factory) {
/*jshint ignore:start */
/*eslint-disable */
factory(CRC32 = {});
/*eslint-enable */
/*jshint ignore:end */
}(function(CRC32) {
CRC32.version = '1.2.0';
/* see perf/crc32table.js */
/*global Int32Array */
function signed_crc_table() {
var c = 0, table = new Array(256);
for(var n =0; n != 256; ++n){
c = n;
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
table[n] = c;
}
return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table;
}
var T = signed_crc_table();
function crc32_bstr(bstr, seed) {
var C = seed ^ -1, L = bstr.length - 1;
for(var i = 0; i < L;) {
C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
}
if(i === L) C = (C>>>8) ^ T[(C ^ bstr.charCodeAt(i))&0xFF];
return C ^ -1;
}
function crc32_buf(buf, seed) {
if(buf.length > 10000) return crc32_buf_8(buf, seed);
var C = seed ^ -1, L = buf.length - 3;
for(var i = 0; i < L;) {
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
}
while(i < L+3) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
return C ^ -1;
}
function crc32_buf_8(buf, seed) {
var C = seed ^ -1, L = buf.length - 7;
for(var i = 0; i < L;) {
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
}
while(i < L+7) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
return C ^ -1;
}
function crc32_str(str, seed) {
var C = seed ^ -1;
for(var i = 0, L=str.length, c, d; i < L;) {
c = str.charCodeAt(i++);
if(c < 0x80) {
C = (C>>>8) ^ T[(C ^ c)&0xFF];
} else if(c < 0x800) {
C = (C>>>8) ^ T[(C ^ (192|((c>>6)&31)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
} else if(c >= 0xD800 && c < 0xE000) {
c = (c&1023)+64; d = str.charCodeAt(i++)&1023;
C = (C>>>8) ^ T[(C ^ (240|((c>>8)&7)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|((c>>2)&63)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|(d&63)))&0xFF];
} else {
C = (C>>>8) ^ T[(C ^ (224|((c>>12)&15)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|((c>>6)&63)))&0xFF];
C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
}
}
return C ^ -1;
}
CRC32.table = T;
CRC32.bstr = crc32_bstr;
CRC32.buf = crc32_buf;
CRC32.str = crc32_str;
}));
/* [MS-CFB] v20171201 */
var CFB = (function _CFB(){
var exports = {};
exports.version = '1.0.8';
exports.version = '1.1.0';
/* [MS-CFB] 2.6.4 */
function namecmp(l, r) {
var L = l.split("/"), R = r.split("/");
@ -29,9 +130,75 @@ function filename(p) {
var c = p.lastIndexOf("/");
return (c === -1) ? p : p.slice(c+1);
}
/* -------------------------------------------------------------------------- */
/* DOS Date format:
high|YYYYYYYm.mmmddddd.HHHHHMMM.MMMSSSSS|low
add 1980 to stored year
stored second should be doubled
*/
/* write JS date to buf as a DOS date */
function write_dos_date(buf, date) {
if(typeof date === "string") date = new Date(date);
var hms = date.getHours();
hms = hms << 6 | date.getMinutes();
hms = hms << 5 | (date.getSeconds()>>>1);
buf.write_shift(2, hms);
var ymd = (date.getFullYear() - 1980);
ymd = ymd << 4 | (date.getMonth()+1);
ymd = ymd << 5 | date.getDate();
buf.write_shift(2, ymd);
}
/* read four bytes from buf and interpret as a DOS date */
function parse_dos_date(buf) {
var hms = buf.read_shift(2) & 0xFFFF;
var ymd = buf.read_shift(2) & 0xFFFF;
var val = new Date();
var d = ymd & 0x1F; ymd >>>= 5;
var m = ymd & 0x0F; ymd >>>= 4;
val.setMilliseconds(0);
val.setFullYear(ymd + 1980);
val.setMonth(m-1);
val.setDate(d);
var S = hms & 0x1F; hms >>>= 5;
var M = hms & 0x3F; hms >>>= 6;
val.setHours(hms);
val.setMinutes(M);
val.setSeconds(S<<1);
return val;
}
function parse_extra_field(blob) {
prep_blob(blob, 0);
var o = {};
var flags = 0;
while(blob.l <= blob.length - 4) {
var type = blob.read_shift(2);
var sz = blob.read_shift(2), tgt = blob.l + sz;
var p = {};
switch(type) {
/* UNIX-style Timestamps */
case 0x5455: {
flags = blob.read_shift(1);
if(flags & 1) p.mtime = blob.read_shift(4);
/* for some reason, CD flag corresponds to LFH */
if(sz > 5) {
if(flags & 2) p.atime = blob.read_shift(4);
if(flags & 4) p.ctime = blob.read_shift(4);
}
if(p.mtime) p.mt = new Date(p.mtime*1000);
}
break;
}
blob.l = tgt;
o[type] = p;
}
return o;
}
var fs;
function get_fs() { return fs || (fs = require('fs')); }
function parse(file, options) {
if(file[0] == 0x50 && file[1] == 0x4b) return parse_zip(file, options);
if(file.length < 512) throw new Error("CFB file size " + file.length + " < 512");
var mver = 3;
var ssz = 512;
@ -52,6 +219,8 @@ var mv = check_get_mver(blob);
mver = mv[0];
switch(mver) {
case 3: ssz = 512; break; case 4: ssz = 4096; break;
case 0: if(mv[1] == 0) return parse_zip(file, options);
/* falls through */
default: throw new Error("Major Version: Expected 3 or 4 saw " + mver);
}
@ -130,6 +299,7 @@ return o;
/* [MS-CFB] 2.2 Compound File Header -- read up to major version */
function check_get_mver(blob) {
if(blob[blob.l] == 0x50 && blob[blob.l + 1] == 0x4b) return [0, 0];
// header signature 8
blob.chk(HEADER_SIGNATURE, 'Header Signature: ');
@ -447,6 +617,7 @@ function rebuild_cfb(cfb, f) {
function _write(cfb, options) {
var _opts = options || {};
rebuild_cfb(cfb);
if(_opts.fileType == 'zip') return write_zip(cfb, _opts);
var L = (function(cfb){
var mini_size = 0, fat_size = 0;
for(var i = 0; i < cfb.FileIndex.length; ++i) {
@ -641,6 +812,551 @@ function write(cfb, options) {
}
return o;
}
/* node < 8.1 zlib does not expose bytesRead, so default to pure JS */
var _zlib;
function use_zlib(zlib) { try {
var InflateRaw = zlib.InflateRaw;
var InflRaw = new InflateRaw();
InflRaw._processChunk(new Uint8Array([3, 0]), InflRaw._finishFlushFlag);
if(InflRaw.bytesRead) _zlib = zlib;
else throw new Error("zlib does not expose bytesRead");
} catch(e) {console.error("cannot use native zlib: " + (e.message || e)); } }
function _inflateRawSync(payload, usz) {
if(!_zlib) return _inflate(payload, usz);
var InflateRaw = _zlib.InflateRaw;
var InflRaw = new InflateRaw();
var out = InflRaw._processChunk(payload.slice(payload.l), InflRaw._finishFlushFlag);
payload.l += InflRaw.bytesRead;
return out;
}
function _deflateRawSync(payload) {
return _zlib ? _zlib.deflateRawSync(payload) : _deflate(payload);
}
var CLEN_ORDER = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];
/* LEN_ID = [ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285 ]; */
var LEN_LN = [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13 , 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 ];
/* DST_ID = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]; */
var DST_LN = [ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 ];
function bit_swap_8(n) { var t = (((((n<<1)|(n<<11)) & 0x22110) | (((n<<5)|(n<<15)) & 0x88440))); return ((t>>16) | (t>>8) |t)&0xFF; }
var use_typed_arrays = typeof Uint8Array !== 'undefined';
var bitswap8 = use_typed_arrays ? new Uint8Array(1<<8) : [];
for(var q = 0; q < (1<<8); ++q) bitswap8[q] = bit_swap_8(q);
function bit_swap_n(n, b) {
var rev = bitswap8[n & 0xFF];
if(b <= 8) return rev >>> (8-b);
rev = (rev << 8) | bitswap8[(n>>8)&0xFF];
if(b <= 16) return rev >>> (16-b);
rev = (rev << 8) | bitswap8[(n>>16)&0xFF];
return rev >>> (24-b);
}
/* helpers for unaligned bit reads */
function read_bits_2(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 6 ? 0 : buf[h+1]<<8))>>>w)& 0x03; }
function read_bits_3(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 5 ? 0 : buf[h+1]<<8))>>>w)& 0x07; }
function read_bits_4(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 4 ? 0 : buf[h+1]<<8))>>>w)& 0x0F; }
function read_bits_5(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 3 ? 0 : buf[h+1]<<8))>>>w)& 0x1F; }
function read_bits_7(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 1 ? 0 : buf[h+1]<<8))>>>w)& 0x7F; }
/* works up to n = 3 * 8 + 1 = 25 */
function read_bits_n(buf, bl, n) {
var w = (bl&7), h = (bl>>>3), f = ((1<<n)-1);
var v = buf[h] >>> w;
if(n < 8 - w) return v & f;
v |= buf[h+1]<<(8-w);
if(n < 16 - w) return v & f;
v |= buf[h+2]<<(16-w);
if(n < 24 - w) return v & f;
v |= buf[h+3]<<(24-w);
return v & f;
}
/* until ArrayBuffer#realloc is a thing, fake a realloc */
function realloc(b, sz) {
var L = b.length, M = 2*L > sz ? 2*L : sz + 5, i = 0;
if(L >= sz) return b;
if(has_buf) {
var o = new_unsafe_buf(M);
// $FlowIgnore
if(b.copy) b.copy(o);
else for(; i < b.length; ++i) o[i] = b[i];
return o;
} else if(use_typed_arrays) {
var a = new Uint8Array(M);
if(a.set) a.set(b);
else for(; i < b.length; ++i) a[i] = b[i];
return a;
}
b.length = M;
return b;
}
/* zero-filled arrays for older browsers */
function zero_fill_array(n) {
var o = new Array(n);
for(var i = 0; i < n; ++i) o[i] = 0;
return o;
}var _deflate = (function() {
var _deflateRaw = (function() {
return function deflateRaw(data, out) {
var boff = 0;
while(boff < data.length) {
var L = Math.min(0xFFFF, data.length - boff);
var h = boff + L == data.length;
/* TODO: this is only type 0 stored */
out.write_shift(1, +h);
out.write_shift(2, L);
out.write_shift(2, (~L) & 0xFFFF);
while(L-- > 0) out[out.l++] = data[boff++];
}
return out.l;
};
})();
return function(data) {
var buf = new_buf(50+Math.floor(data.length*1.1));
var off = _deflateRaw(data, buf);
return buf.slice(0, off);
};
})();
/* modified inflate function also moves original read head */
/* build tree (used for literals and lengths) */
function build_tree(clens, cmap, MAX) {
var maxlen = 1, w = 0, i = 0, j = 0, ccode = 0, L = clens.length;
var bl_count = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
for(i = 0; i < 32; ++i) bl_count[i] = 0;
for(i = L; i < MAX; ++i) clens[i] = 0;
L = clens.length;
var ctree = use_typed_arrays ? new Uint16Array(L) : zero_fill_array(L); // []
/* build code tree */
for(i = 0; i < L; ++i) {
bl_count[(w = clens[i])]++;
if(maxlen < w) maxlen = w;
ctree[i] = 0;
}
bl_count[0] = 0;
for(i = 1; i <= maxlen; ++i) bl_count[i+16] = (ccode = (ccode + bl_count[i-1])<<1);
for(i = 0; i < L; ++i) {
ccode = clens[i];
if(ccode != 0) ctree[i] = bl_count[ccode+16]++;
}
/* cmap[maxlen + 4 bits] = (off&15) + (lit<<4) reverse mapping */
var cleni = 0;
for(i = 0; i < L; ++i) {
cleni = clens[i];
if(cleni != 0) {
ccode = bit_swap_n(ctree[i], maxlen)>>(maxlen-cleni);
for(j = (1<<(maxlen + 4 - cleni)) - 1; j>=0; --j)
cmap[ccode|(j<<cleni)] = (cleni&15) | (i<<4);
}
}
return maxlen;
}
var fix_lmap = use_typed_arrays ? new Uint16Array(512) : zero_fill_array(512);
var fix_dmap = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
if(!use_typed_arrays) {
for(var i = 0; i < 512; ++i) fix_lmap[i] = 0;
for(i = 0; i < 32; ++i) fix_dmap[i] = 0;
}
(function() {
var dlens = [];
var i = 0;
for(;i<32; i++) dlens.push(5);
build_tree(dlens, fix_dmap, 32);
var clens = [];
i = 0;
for(; i<=143; i++) clens.push(8);
for(; i<=255; i++) clens.push(9);
for(; i<=279; i++) clens.push(7);
for(; i<=287; i++) clens.push(8);
build_tree(clens, fix_lmap, 288);
})();
var dyn_lmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
var dyn_dmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
var dyn_cmap = use_typed_arrays ? new Uint16Array(128) : zero_fill_array(128);
var dyn_len_1 = 1, dyn_len_2 = 1;
/* 5.5.3 Expanding Huffman Codes */
function dyn(data, boff) {
/* nomenclature from RFC1951 refers to bit values; these are offset by the implicit constant */
var _HLIT = read_bits_5(data, boff) + 257; boff += 5;
var _HDIST = read_bits_5(data, boff) + 1; boff += 5;
var _HCLEN = read_bits_4(data, boff) + 4; boff += 4;
var w = 0;
/* grab and store code lengths */
var clens = use_typed_arrays ? new Uint8Array(19) : zero_fill_array(19);
var ctree = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
var maxlen = 1;
var bl_count = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
var next_code = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
var L = clens.length; /* 19 */
for(var i = 0; i < _HCLEN; ++i) {
clens[CLEN_ORDER[i]] = w = read_bits_3(data, boff);
if(maxlen < w) maxlen = w;
bl_count[w]++;
boff += 3;
}
/* build code tree */
var ccode = 0;
bl_count[0] = 0;
for(i = 1; i <= maxlen; ++i) next_code[i] = ccode = (ccode + bl_count[i-1])<<1;
for(i = 0; i < L; ++i) if((ccode = clens[i]) != 0) ctree[i] = next_code[ccode]++;
/* cmap[7 bits from stream] = (off&7) + (lit<<3) */
var cleni = 0;
for(i = 0; i < L; ++i) {
cleni = clens[i];
if(cleni != 0) {
ccode = bitswap8[ctree[i]]>>(8-cleni);
for(var j = (1<<(7-cleni))-1; j>=0; --j) dyn_cmap[ccode|(j<<cleni)] = (cleni&7) | (i<<3);
}
}
/* read literal and dist codes at once */
var hcodes = [];
maxlen = 1;
for(; hcodes.length < _HLIT + _HDIST;) {
ccode = dyn_cmap[read_bits_7(data, boff)];
boff += ccode & 7;
switch((ccode >>>= 3)) {
case 16:
w = 3 + read_bits_2(data, boff); boff += 2;
ccode = hcodes[hcodes.length - 1];
while(w-- > 0) hcodes.push(ccode);
break;
case 17:
w = 3 + read_bits_3(data, boff); boff += 3;
while(w-- > 0) hcodes.push(0);
break;
case 18:
w = 11 + read_bits_7(data, boff); boff += 7;
while(w -- > 0) hcodes.push(0);
break;
default:
hcodes.push(ccode);
if(maxlen < ccode) maxlen = ccode;
break;
}
}
/* build literal / length trees */
var h1 = hcodes.slice(0, _HLIT), h2 = hcodes.slice(_HLIT);
for(i = _HLIT; i < 286; ++i) h1[i] = 0;
for(i = _HDIST; i < 30; ++i) h2[i] = 0;
dyn_len_1 = build_tree(h1, dyn_lmap, 286);
dyn_len_2 = build_tree(h2, dyn_dmap, 30);
return boff;
}
/* return [ data, bytesRead ] */
function inflate(data, usz) {
/* shortcircuit for empty buffer [0x03, 0x00] */
if(data[0] == 3 && !(data[1] & 0x3)) { return [new_raw_buf(usz), 2]; }
/* bit offset */
var boff = 0;
/* header includes final bit and type bits */
var header = 0;
var outbuf = new_unsafe_buf(usz ? usz : (1<<18));
var woff = 0;
var OL = outbuf.length>>>0;
var max_len_1 = 0, max_len_2 = 0;
while((header&1) == 0) {
header = read_bits_3(data, boff); boff += 3;
if((header >>> 1) == 0) {
/* Stored block */
if(boff & 7) boff += 8 - (boff&7);
/* 2 bytes sz, 2 bytes bit inverse */
var sz = data[boff>>>3] | data[(boff>>>3)+1]<<8;
boff += 32;
/* push sz bytes */
if(!usz && OL < woff + sz) { outbuf = realloc(outbuf, woff + sz); OL = outbuf.length; }
if(typeof data.copy === 'function') {
// $FlowIgnore
data.copy(outbuf, woff, boff>>>3, (boff>>>3)+sz);
woff += sz; boff += 8*sz;
} else while(sz-- > 0) { outbuf[woff++] = data[boff>>>3]; boff += 8; }
continue;
} else if((header >>> 1) == 1) {
/* Fixed Huffman */
max_len_1 = 9; max_len_2 = 5;
} else {
/* Dynamic Huffman */
boff = dyn(data, boff);
max_len_1 = dyn_len_1; max_len_2 = dyn_len_2;
}
if(!usz && (OL < woff + 32767)) { outbuf = realloc(outbuf, woff + 32767); OL = outbuf.length; }
for(;;) { // while(true) is apparently out of vogue in modern JS circles
/* ingest code and move read head */
var bits = read_bits_n(data, boff, max_len_1);
var code = (header>>>1) == 1 ? fix_lmap[bits] : dyn_lmap[bits];
boff += code & 15; code >>>= 4;
/* 0-255 are literals, 256 is end of block token, 257+ are copy tokens */
if(((code>>>8)&0xFF) === 0) outbuf[woff++] = code;
else if(code == 256) break;
else {
code -= 257;
var len_eb = (code < 8) ? 0 : ((code-4)>>2); if(len_eb > 5) len_eb = 0;
var tgt = woff + LEN_LN[code];
/* length extra bits */
if(len_eb > 0) {
tgt += read_bits_n(data, boff, len_eb);
boff += len_eb;
}
/* dist code */
bits = read_bits_n(data, boff, max_len_2);
code = (header>>>1) == 1 ? fix_dmap[bits] : dyn_dmap[bits];
boff += code & 15; code >>>= 4;
var dst_eb = (code < 4 ? 0 : (code-2)>>1);
var dst = DST_LN[code];
/* dist extra bits */
if(dst_eb > 0) {
dst += read_bits_n(data, boff, dst_eb);
boff += dst_eb;
}
/* in the common case, manual byte copy is faster than TA set / Buffer copy */
if(!usz && OL < tgt) { outbuf = realloc(outbuf, tgt); OL = outbuf.length; }
while(woff < tgt) { outbuf[woff] = outbuf[woff - dst]; ++woff; }
}
}
}
return [usz ? outbuf : outbuf.slice(0, woff), (boff+7)>>>3];
}
function _inflate(payload, usz) {
var data = payload.slice(payload.l||0);
var out = inflate(data, usz);
payload.l += out[1];
return out[0];
}
function warn_or_throw(wrn, msg) {
if(wrn) { if(typeof console !== 'undefined') console.error(msg); }
else throw new Error(msg);
}
function parse_zip(file, options) {
var blob = file;
prep_blob(blob, 0);
var FileIndex = [], FullPaths = [];
var o = {
FileIndex: FileIndex,
FullPaths: FullPaths
};
init_cfb(o, { root: options.root });
/* find end of central directory, start just after signature */
var i = blob.length - 4;
while((blob[i] != 0x50 || blob[i+1] != 0x4b || blob[i+2] != 0x05 || blob[i+3] != 0x06) && i >= 0) --i;
blob.l = i + 4;
/* parse end of central directory */
blob.l += 4;
var fcnt = blob.read_shift(2);
blob.l += 6;
var start_cd = blob.read_shift(4);
/* parse central directory */
blob.l = start_cd;
for(i = 0; i < fcnt; ++i) {
/* trust local file header instead of CD entry */
blob.l += 20;
var csz = blob.read_shift(4);
var usz = blob.read_shift(4);
var namelen = blob.read_shift(2);
var efsz = blob.read_shift(2);
var fcsz = blob.read_shift(2);
blob.l += 8;
var offset = blob.read_shift(4);
var EF = parse_extra_field(blob.slice(blob.l+namelen, blob.l+namelen+efsz));
blob.l += namelen + efsz + fcsz;
var L = blob.l;
blob.l = offset + 4;
parse_local_file(blob, csz, usz, o, EF);
blob.l = L;
}
return o;
}
/* head starts just after local file header signature */
function parse_local_file(blob, csz, usz, o, EF) {
/* [local file header] */
blob.l += 2;
var flags = blob.read_shift(2);
var meth = blob.read_shift(2);
var date = parse_dos_date(blob);
if(flags & 0x2041) throw new Error("Unsupported ZIP encryption");
var crc32 = blob.read_shift(4);
var _csz = blob.read_shift(4);
var _usz = blob.read_shift(4);
var namelen = blob.read_shift(2);
var efsz = blob.read_shift(2);
// TODO: flags & (1<<11) // UTF8
var name = ""; for(var i = 0; i < namelen; ++i) name += String.fromCharCode(blob[blob.l++]);
if(efsz) {
var ef = parse_extra_field(blob.slice(blob.l, blob.l + efsz));
if((ef[0x5455]||{}).mt) date = ef[0x5455].mt;
if(((EF||{})[0x5455]||{}).mt) date = EF[0x5455].mt;
}
blob.l += efsz;
/* [encryption header] */
/* [file data] */
var data = blob.slice(blob.l, blob.l + _csz);
switch(meth) {
case 8: data = _inflateRawSync(blob, _usz); break;
case 0: break;
default: throw new Error("Unsupported ZIP Compression method " + meth);
}
/* [data descriptor] */
var wrn = false;
if(flags & 8) {
crc32 = blob.read_shift(4);
if(crc32 == 0x08074b50) { crc32 = blob.read_shift(4); wrn = true; }
_csz = blob.read_shift(4);
_usz = blob.read_shift(4);
}
if(_csz != csz) warn_or_throw(wrn, "Bad compressed size: " + csz + " != " + _csz);
if(_usz != usz) warn_or_throw(wrn, "Bad uncompressed size: " + usz + " != " + _usz);
var _crc32 = CRC32.buf(data, 0);
if(crc32 != _crc32) warn_or_throw(wrn, "Bad CRC32 checksum: " + crc32 + " != " + _crc32);
cfb_add(o, name, data, {unsafe: true, mt: date});
}
function write_zip(cfb, options) {
var _opts = options || {};
var out = [], cdirs = [];
var o = new_buf(1);
var method = (_opts.compression ? 8 : 0), flags = 0;
var desc = false;
if(desc) flags |= 8;
var i = 0, j = 0;
var start_cd = 0, fcnt = 0;
var root = cfb.FullPaths[0], fp = root, fi = cfb.FileIndex[0];
var crcs = [];
var sz_cd = 0;
for(i = 1; i < cfb.FullPaths.length; ++i) {
fp = cfb.FullPaths[i].slice(root.length); fi = cfb.FileIndex[i];
if(!fi.size || !fi.content || fp == "\u0001Sh33tJ5") continue;
var start = start_cd;
/* TODO: CP437 filename */
var namebuf = new_buf(fp.length);
for(j = 0; j < fp.length; ++j) namebuf.write_shift(1, fp.charCodeAt(j) & 0x7F);
namebuf = namebuf.slice(0, namebuf.l);
crcs[fcnt] = CRC32.buf(fi.content, 0);
var outbuf = fi.content;
if(method == 8) outbuf = _deflateRawSync(outbuf);
/* local file header */
o = new_buf(30);
o.write_shift(4, 0x04034b50);
o.write_shift(2, 20);
o.write_shift(2, flags);
o.write_shift(2, method);
/* TODO: last mod file time/date */
if(fi.mt) write_dos_date(o, fi.mt);
else o.write_shift(4, 0);
o.write_shift(-4, (flags & 8) ? 0 : crcs[fcnt]);
o.write_shift(4, (flags & 8) ? 0 : outbuf.length);
o.write_shift(4, (flags & 8) ? 0 : fi.content.length);
o.write_shift(2, namebuf.length);
o.write_shift(2, 0);
start_cd += o.length;
out.push(o);
start_cd += namebuf.length;
out.push(namebuf);
/* TODO: encryption header ? */
start_cd += outbuf.length;
out.push(outbuf);
/* data descriptor */
if(flags & 8) {
o = new_buf(12);
o.write_shift(-4, crcs[fcnt]);
o.write_shift(4, outbuf.length);
o.write_shift(4, fi.content.length);
start_cd += o.l;
out.push(o);
}
/* central directory */
o = new_buf(46);
o.write_shift(4, 0x02014b50);
o.write_shift(2, 0);
o.write_shift(2, 20);
o.write_shift(2, flags);
o.write_shift(2, method);
o.write_shift(4, 0); /* TODO: last mod file time/date */
o.write_shift(-4, crcs[fcnt]);
o.write_shift(4, outbuf.length);
o.write_shift(4, fi.content.length);
o.write_shift(2, namebuf.length);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(4, 0);
o.write_shift(4, start);
sz_cd += o.l;
cdirs.push(o);
sz_cd += namebuf.length;
cdirs.push(namebuf);
++fcnt;
}
/* end of central directory */
o = new_buf(22);
o.write_shift(4, 0x06054b50);
o.write_shift(2, 0);
o.write_shift(2, 0);
o.write_shift(2, fcnt);
o.write_shift(2, fcnt);
o.write_shift(4, sz_cd);
o.write_shift(4, start_cd);
o.write_shift(2, 0);
return bconcat(([bconcat((out)), bconcat(cdirs), o]));
}
function cfb_new(opts) {
var o = ({});
init_cfb(o, opts);
@ -667,6 +1383,8 @@ file.content = (content);
file.size = content ? content.length : 0;
if(opts) {
if(opts.CLSID) file.clsid = opts.CLSID;
if(opts.mt) file.mt = opts.mt;
if(opts.ct) file.ct = opts.ct;
}
return file;
}
@ -710,6 +1428,9 @@ exports.utils = {
CheckField: CheckField,
prep_blob: prep_blob,
bconcat: bconcat,
use_zlib: use_zlib,
_deflateRaw: _deflate,
_inflateRaw: _inflate,
consts: consts
};