Skip to content

Commit d9b99d6

Browse files
committed
version bump 1.1.0: zip support
1 parent 8ee792f commit d9b99d6

39 files changed

+5384
-124
lines changed

.gitignore

+10-2
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,26 @@ test_files_pres
1010
*.[cC][sS][vV]
1111
*.[dD][iIbB][fF]
1212
*.[pP][rR][nN]
13+
*.[pP][mM][dD]*
14+
*.[pP][dD][fF]
1315
*.[sS][lL][kK]
1416
*.socialcalc
15-
*.[xX][lL][sSwWcC]
16-
*.[xX][lL][sS][xXmMbB]
17+
*.[xX][lL][sSwWcCaAtTmM]
18+
*.[xX][lL][sSaAtT][xXmMbB]
1719
*.[oO][dD][sS]
1820
*.[fF][oO][dD][sS]
1921
*.[xX][mM][lL]
2022
*.[uU][oO][sS]
2123
*.[wW][kKqQbB][S1234567890]
2224
*.[qQ][pP][wW]
25+
*.[bB][iI][fF][fF][23458]
26+
*.[rR][tT][fF]
27+
*.[eE][tT][hH]
28+
*.[zZ][iI][pP]
29+
*.[mM][sS][iI]
2330
*.123
2431
*.htm
2532
*.html
2633
*.sheetjs
2734
*.exe
35+
*.img

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ This log is intended to keep track of backwards-incompatible changes, including
44
but not limited to API changes and file location changes. Minor behavioral
55
changes may not be included if they are not expected to break existing code.
66

7+
## 1.1.0 (2018-09-04)
8+
9+
* Support for ZIP file format
10+
711
## 1.0.6 (2018-04-09)
812

913
* `lastIndexOf` in FAT builder enables larger file parsing at minor cost

README.md

+37-18
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
# Compound File Binary Format
1+
# Container File Blobs
22

3-
Pure JS implementation of MS-CFB: Compound File Binary File Format, a container
4-
format used in many Microsoft file types (XLS, DOC, VBA blobs in XLSX and XLSB)
3+
Pure JS implementation of various container file formats, including ZIP and CFB.
54

65
[![Build Status](https://travis-ci.org/SheetJS/js-cfb.svg?branch=master)](https://travis-ci.org/SheetJS/js-cfb)
76
[![Coverage Status](http://img.shields.io/coveralls/SheetJS/js-cfb/master.svg)](https://coveralls.io/r/SheetJS/js-cfb?branch=master)
87
[![Dependencies Status](https://david-dm.org/sheetjs/js-cfb/status.svg)](https://david-dm.org/sheetjs/js-cfb)
98
[![NPM Downloads](https://img.shields.io/npm/dt/cfb.svg)](https://npmjs.org/package/cfb)
10-
[![ghit.me](https://ghit.me/badge.svg?repo=sheetjs/js-xlsx)](https://ghit.me/repo/sheetjs/js-xlsx)
119
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-cfb?pixel)](https://github.com/SheetJS/js-cfb)
1210

1311
## Installation
@@ -79,29 +77,48 @@ The CFB object exposes the following methods and properties:
7977
`CFB.parse(blob)` takes a nodejs Buffer or an array of bytes and returns an
8078
parsed representation of the data.
8179

82-
`CFB.read(blob, opts)` wraps `parse`. `opts.type` controls the behavior:
80+
`CFB.read(blob, opts)` wraps `parse`.
81+
82+
`CFB.find(cfb, path)` performs a case-insensitive match for the path (or file
83+
name, if there are no slashes) and returns an entry object or null if not found.
84+
85+
`CFB.write(cfb, opts)` generates a file based on the container.
86+
87+
`CFB.writeFile(cfb, filename, opts)` creates a file with the specified name.
88+
89+
### Parse Options
90+
91+
`CFB.read` takes an options argument. `opts.type` controls the behavior:
8392

8493
| `type` | expected input |
85-
|------------|-----------------------------------------------------------------|
94+
|------------|:----------------------------------------------------------------|
8695
| `"base64"` | string: Base64 encoding of the file |
8796
| `"binary"` | string: binary string (byte `n` is `data.charCodeAt(n)`) |
8897
| `"file"` | string: path of file that will be read (nodejs only) |
8998
| (default) | buffer or array of 8-bit unsigned int (byte `n` is `data[n]`) |
9099

91-
`CFB.find(cfb, path)` performs a case-insensitive match for the path (or file
92-
name, if there are no slashes) and returns an entry object or null if not found.
93100

94-
`CFB.write(cfb, opts)` generates a file based on the container. `opts.type`
95-
controls the behavior:
101+
### Write Options
102+
103+
`CFB.write` and `CFB.writeFile` take options argument.
104+
105+
`opts.type` controls the behavior:
96106

97107
| `type` | output |
98-
|------------|-----------------------------------------------------------------|
108+
|------------|:----------------------------------------------------------------|
99109
| `"base64"` | string: Base64 encoding of the file |
100110
| `"binary"` | string: binary string (byte `n` is `data.charCodeAt(n)`) |
101111
| `"file"` | string: path of file that will be created (nodejs only) |
102112
| (default) | buffer if available, array of 8-bit unsigned int otherwise |
103113

104-
`CFB.writeFile(cfb, filename, opts)` creates a file with the specified name.
114+
`opts.fileType` controls the output file type:
115+
116+
| `fileType` | output |
117+
|:-------------------|:--------------|
118+
| `'cfb'` (default) | CFB container |
119+
| `'zip'` | ZIP file |
120+
121+
`opts.compression` enables DEFLATE compression for ZIP file type.
105122

106123

107124
## Utility Functions
@@ -114,6 +131,12 @@ accept a `name` argument strictly deal with absolute file names:
114131
Set the option `{unsafe:true}` to skip existence checks (for bulk additions)
115132
- `.cfb_del(cfb, name)` deletes the specified file
116133
- `.cfb_mov(cfb, old_name, new_name)` moves the old file to new path and name
134+
- `.use_zlib(require("zlib"))` loads a nodejs zlib instance.
135+
136+
By default, the library uses a pure JS inflate/deflate implementation. NodeJS
137+
`zlib.InflateRaw` exposes the number of bytes read in versions after `8.11.0`.
138+
If a supplied `zlib` does not support the required features, a warning will be
139+
displayed in the console and the pure JS fallback will be used.
117140

118141

119142
## Container Object Description
@@ -146,11 +169,7 @@ granted by the Apache 2.0 License are reserved by the Original Author.
146169

147170
## References
148171

149-
150-
<details>
151-
<summary><b>OSP-covered Specifications</b> (click to show)</summary>
152-
153172
- [MS-CFB]: Compound File Binary File Format
154-
155-
</details>
173+
- ZIP `APPNOTE.TXT`: https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.3.4.TXT
174+
- RFC1951: https://www.ietf.org/rfc/rfc1951.txt
156175

bin/cfb.njs

+47-12
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,16 @@ program
1919
.option('-O, --to-stdout', 'extract raw contents to stdout')
2020
.option('-z, --dump', 'dump internal representation but do not extract')
2121
.option('-q, --quiet', 'process but do not report')
22+
.option('--here', 'skip the CFB root name when extracting')
23+
.option('--osx', 'use OSX-style unzip listing')
24+
.option('--zlib', 'use native zlib')
25+
.option('--local', 'print times in local timezone')
2226
.option('--dev', 'development mode')
2327
.option('--read', 'read but do not print out contents');
24-
2528
program.parse(process.argv);
2629

30+
if(program.zlib) X.utils.use_zlib(require('zlib'));
31+
2732
var exit = process.exit;
2833
var die = function(errno/*:number*/, msg/*:string*/) { console.error(n + ": " + msg); exit(errno); };
2934
var logit = function(cmd/*:string*/, f/*:string*/) { console.error(sprintf("%-6s %s", cmd, f)); };
@@ -53,30 +58,55 @@ if(program.dump) {
5358
}
5459
if(program.repair) { X.writeFile(cfb, program.args[0]); exit(0); }
5560

61+
var rlen = cfb.FullPaths[0].length;
62+
5663
function fix_string(x/*:string*/)/*:string*/ { return x.replace(/[\u0000-\u001f]/g, function($$) { return sprintf("\\u%04X", $$.charCodeAt(0)); }); }
57-
var format_date = function(date/*:Date*/)/*:string*/ {
58-
return sprintf("%02u-%02u-%02u %02u:%02u", date.getUTCMonth()+1, date.getUTCDate(), date.getUTCFullYear()%100, date.getUTCHours(), date.getUTCMinutes());
64+
var format_date = function(date/*:Date*/, osx/*:?any*/)/*:string*/ {
65+
var datefmt = osx ? "%02u-%02u-%04u %02u:%02u": "%02u-%02u-%02u %02u:%02u";
66+
var MM = program.local ? date.getMonth() + 1 : date.getUTCMonth() + 1;
67+
var DD = program.local ? date.getDate() : date.getUTCDate();
68+
var YY = (program.local ? date.getFullYear() : date.getUTCFullYear())%(osx ? 10000 : 100);
69+
var hh = program.local ? date.getHours() : date.getUTCHours();
70+
var mm = program.local ? date.getMinutes() : date.getUTCMinutes();
71+
return sprintf(datefmt, MM, DD, YY, hh, mm);
5972
};
6073

6174
if(program.listFiles) {
62-
var basetime = new Date(1980,0,1);
75+
var basetime = new Date(Date.UTC(1980,0,1));
6376
var cnt = 0, rootsize = 0, filesize = 0;
64-
console.log(" Length Date Time Name");
65-
console.log(" -------- ---- ---- ----");
77+
var fmtstr = "%9lu %s %s";
78+
if(program.osx) {
79+
console.log("Archive: " + program.args[0]);
80+
console.log(" Length Date Time Name");
81+
console.log("--------- ---------- ----- ----");
82+
fmtstr = "%9lu %s %s";
83+
} else {
84+
console.log(" Length Date Time Name");
85+
console.log(" -------- ---- ---- ----");
86+
}
6687
cfb.FileIndex.forEach(function(file/*:CFBEntry*/, i/*:number*/) {
6788
switch(file.type) {
6889
case 5:
6990
basetime = file.ct || file.mt || basetime;
7091
rootsize = file.size;
7192
break;
7293
case 2:
73-
console.log(sprintf("%9lu %s %s", file.size, format_date(basetime), fix_string(cfb.FullPaths[i])));
94+
var fixname = fix_string(cfb.FullPaths[i]);
95+
if(program.osx && fixname.match(/\\u0001Sh33tJ5/)) return;
96+
if(program.here) fixname = fixname.slice(rlen);
97+
console.log(sprintf(fmtstr, file.size, format_date(file.mt || basetime, program.osx), fixname));
7498
filesize += file.size;
7599
++cnt;
76100
}
77101
});
78-
console.log(" -------- -------");
79-
console.log(sprintf("%9lu %lu file%s", rootsize || filesize, cnt, (cnt !== 1 ? "s" : "")));
102+
var outfmt = "%9lu %lu file%s";
103+
if(program.osx) {
104+
console.log("--------- -------");
105+
outfmt = "%9lu %lu file%s";
106+
} else {
107+
console.log(" -------- -------");
108+
}
109+
console.log(sprintf(outfmt, rootsize || filesize, cnt, (cnt !== 1 ? "s" : "")));
80110

81111
exit(0);
82112
}
@@ -124,8 +154,13 @@ if(program.args.length > 1) {
124154
}
125155

126156
if(program.toStdout) exit(0);
127-
for(var i=0; i!==cfb.FullPaths.length; ++i) {
157+
for(var i=program.here ? 1 : 0; i!==cfb.FullPaths.length; ++i) {
128158
if(!cfb.FileIndex[i].name) continue;
129-
if(cfb.FullPaths[i].slice(-1) === "/") mkdirp(cfb.FullPaths[i]);
130-
else write(cfb.FullPaths[i], cfb.FileIndex[i]);
159+
var fp = cfb.FullPaths[i];
160+
if(program.here) fp = fp.slice(rlen);
161+
if(fp.slice(-1) === "/") mkdirp(fp);
162+
else {
163+
if(fp.indexOf("/") > -1) mkdirp(fp.slice(0, fp.lastIndexOf("/")));
164+
write(fp, cfb.FileIndex[i]);
165+
}
131166
}

bits/00_header.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
/* vim: set ts=2: */
33
/*jshint eqnull:true */
44
/*exported CFB */
5-
/*global module, require:false, process:false, Buffer:false, Uint8Array:false */
5+
/*global module, require:false, process:false, Buffer:false, Uint8Array:false, Uint16Array:false */
66

bits/05_buf.js

+11-3
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ var Buffer_from = /*::(*/function(){}/*:: :any)*/;
55
if(typeof Buffer !== 'undefined') {
66
var nbfs = !Buffer.from;
77
if(!nbfs) try { Buffer.from("foo", "utf8"); } catch(e) { nbfs = true; }
8-
Buffer_from = nbfs ? function(buf, enc) { return (enc) ? new Buffer(buf, enc) : new Buffer(buf); } : Buffer.from.bind(Buffer);
8+
Buffer_from = /*::((*/nbfs ? function(buf, enc) { return (enc) ? new Buffer(buf, enc) : new Buffer(buf); } : Buffer.from.bind(Buffer)/*::) :any)*/;
99
// $FlowIgnore
1010
if(!Buffer.alloc) Buffer.alloc = function(n) { return new Buffer(n); };
11+
// $FlowIgnore
12+
if(!Buffer.allocUnsafe) Buffer.allocUnsafe = function(n) { return new Buffer(n); };
1113
}
1214

1315
function new_raw_buf(len/*:number*/) {
@@ -16,8 +18,14 @@ function new_raw_buf(len/*:number*/) {
1618
/* jshint +W056 */
1719
}
1820

19-
var s2a = function s2a(s/*:string*/) {
20-
if(has_buf) return Buffer.from(s, "binary");
21+
function new_unsafe_buf(len/*:number*/) {
22+
/* jshint -W056 */
23+
return has_buf ? Buffer.allocUnsafe(len) : new Array(len);
24+
/* jshint +W056 */
25+
}
26+
27+
var s2a = function s2a(s/*:string*/)/*:any*/ {
28+
if(has_buf) return Buffer_from(s, "binary");
2129
return s.split("").map(function(x/*:string*/)/*:number*/{ return x.charCodeAt(0) & 0xff; });
2230
};
2331

bits/08_blob.js

-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ if(has_buf/*:: && typeof Buffer !== 'undefined'*/) {
2222
};
2323
__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); };
2424
__toBuffer = function(bufs/*:Array<Array<RawBytes>>*/)/*:RawBytes*/ { return (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat((bufs[0]/*:any*/)) : ___toBuffer(bufs);};
25-
// $FlowIgnore
2625
s2a = function(s/*:string*/)/*:RawBytes*/ { return Buffer_from(s, "binary"); };
2726
bconcat = function(bufs/*:Array<RawBytes>*/)/*:RawBytes*/ { return Buffer.isBuffer(bufs[0]) ? Buffer.concat(/*::(*/bufs/*:: :any)*/) : __bconcat(bufs); };
2827
}

bits/21_crc32.js

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/* crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */
2+
/* vim: set ts=2: */
3+
/*exported CRC32 */
4+
var CRC32;
5+
(function (factory) {
6+
/*jshint ignore:start */
7+
/*eslint-disable */
8+
factory(CRC32 = {});
9+
/*eslint-enable */
10+
/*jshint ignore:end */
11+
}(function(CRC32) {
12+
CRC32.version = '1.2.0';
13+
/* see perf/crc32table.js */
14+
/*global Int32Array */
15+
function signed_crc_table()/*:any*/ {
16+
var c = 0, table/*:Array<number>*/ = new Array(256);
17+
18+
for(var n =0; n != 256; ++n){
19+
c = n;
20+
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
21+
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
22+
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
23+
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
24+
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
25+
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
26+
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
27+
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
28+
table[n] = c;
29+
}
30+
31+
return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table;
32+
}
33+
34+
var T = signed_crc_table();
35+
function crc32_bstr(bstr/*:string*/, seed/*:number*/)/*:number*/ {
36+
var C = seed ^ -1, L = bstr.length - 1;
37+
for(var i = 0; i < L;) {
38+
C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
39+
C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF];
40+
}
41+
if(i === L) C = (C>>>8) ^ T[(C ^ bstr.charCodeAt(i))&0xFF];
42+
return C ^ -1;
43+
}
44+
45+
function crc32_buf(buf/*:Uint8Array|Array<number>*/, seed/*:number*/)/*:number*/ {
46+
if(buf.length > 10000) return crc32_buf_8(buf, seed);
47+
var C = seed ^ -1, L = buf.length - 3;
48+
for(var i = 0; i < L;) {
49+
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
50+
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
51+
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
52+
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
53+
}
54+
while(i < L+3) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
55+
return C ^ -1;
56+
}
57+
58+
function crc32_buf_8(buf/*:Uint8Array|Array<number>*/, seed/*:number*/)/*:number*/ {
59+
var C = seed ^ -1, L = buf.length - 7;
60+
for(var i = 0; i < L;) {
61+
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
62+
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
63+
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
64+
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
65+
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
66+
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
67+
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
68+
C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
69+
}
70+
while(i < L+7) C = (C>>>8) ^ T[(C^buf[i++])&0xFF];
71+
return C ^ -1;
72+
}
73+
74+
function crc32_str(str/*:string*/, seed/*:number*/)/*:number*/ {
75+
var C = seed ^ -1;
76+
for(var i = 0, L=str.length, c, d; i < L;) {
77+
c = str.charCodeAt(i++);
78+
if(c < 0x80) {
79+
C = (C>>>8) ^ T[(C ^ c)&0xFF];
80+
} else if(c < 0x800) {
81+
C = (C>>>8) ^ T[(C ^ (192|((c>>6)&31)))&0xFF];
82+
C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
83+
} else if(c >= 0xD800 && c < 0xE000) {
84+
c = (c&1023)+64; d = str.charCodeAt(i++)&1023;
85+
C = (C>>>8) ^ T[(C ^ (240|((c>>8)&7)))&0xFF];
86+
C = (C>>>8) ^ T[(C ^ (128|((c>>2)&63)))&0xFF];
87+
C = (C>>>8) ^ T[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF];
88+
C = (C>>>8) ^ T[(C ^ (128|(d&63)))&0xFF];
89+
} else {
90+
C = (C>>>8) ^ T[(C ^ (224|((c>>12)&15)))&0xFF];
91+
C = (C>>>8) ^ T[(C ^ (128|((c>>6)&63)))&0xFF];
92+
C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF];
93+
}
94+
}
95+
return C ^ -1;
96+
}
97+
CRC32.table = T;
98+
CRC32.bstr = crc32_bstr;
99+
CRC32.buf = crc32_buf;
100+
CRC32.str = crc32_str;
101+
}));

bits/31_version.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
exports.version = '1.0.8';
1+
exports.version = '1.1.0';

0 commit comments

Comments
 (0)