version bump 0.13.2

- file writing includes `fs` as appropriate
- CLI modify and write options
- infrastructure update
This commit is contained in:
SheetJS 2017-10-20 16:36:54 -04:00
parent e6d1c8b06e
commit 9e22a4425e
25 changed files with 306 additions and 144 deletions

@ -3,7 +3,6 @@
.*/dist/.* .*/dist/.*
.*/test_files/.* .*/test_files/.*
.*/test_files_pres/.* .*/test_files_pres/.*
.*/test.js
.*/bits/.* .*/bits/.*
.*/ctest/.* .*/ctest/.*
@ -23,6 +22,7 @@
cfb.flow.js cfb.flow.js
xlscfb.flow.js xlscfb.flow.js
.*/bin/.*.njs .*/bin/.*.njs
test.js
[libs] [libs]
bits/10_types.js bits/10_types.js

16
.spelling Normal file

@ -0,0 +1,16 @@
# cfb.js (C) 2013-present SheetJS -- http://sheetjs.com
SheetJS
js-xlsx
# CFB-related terms
CFB
storages
# Third-party
NPM
nodejs
npm
# Other terms
Base64
metadata

@ -66,6 +66,11 @@ XLSDEPS=misc/suppress_export.js $(filter-out $(XLSSKIP),$(DEPS))
xlscfb.flow.js: $(XLSDEPS) ## Build support library xlscfb.flow.js: $(XLSDEPS) ## Build support library
cat $^ | tr -d '\15\32' > $@ cat $^ | tr -d '\15\32' > $@
BYTEFILE=dist/cfb.min.js dist/xlscfb.js
.PHONY: bytes
bytes: ## Display minified and gzipped file sizes
for i in $(BYTEFILE); do printj "%-30s %7d %10d" $$i $$(wc -c < $$i) $$(gzip --best --stdout $$i | wc -c); done
## Testing ## Testing
@ -74,6 +79,7 @@ test mocha: test.js $(TARGET) ## Run test suite
mocha -R spec -t 20000 mocha -R spec -t 20000
#* To run tests for one format, make test_<fmt> #* To run tests for one format, make test_<fmt>
#* To run the core test suite, make test_misc
TESTFMT=$(patsubst %,test_%,$(FMT)) TESTFMT=$(patsubst %,test_%,$(FMT))
.PHONY: $(TESTFMT) .PHONY: $(TESTFMT)
$(TESTFMT): test_%: $(TESTFMT): test_%:
@ -82,6 +88,9 @@ $(TESTFMT): test_%:
## Code Checking ## Code Checking
.PHONY: fullint
fullint: lint old-lint tslint flow mdlint ## Run all checks
.PHONY: lint .PHONY: lint
lint: $(TARGET) $(AUXTARGETS) ## Run eslint checks lint: $(TARGET) $(AUXTARGETS) ## Run eslint checks
@eslint --ext .js,.njs,.json,.html,.htm $(TARGET) $(AUXTARGETS) $(CMDS) $(HTMLLINT) package.json @eslint --ext .js,.njs,.json,.html,.htm $(TARGET) $(AUXTARGETS) $(CMDS) $(HTMLLINT) package.json
@ -91,9 +100,9 @@ lint: $(TARGET) $(AUXTARGETS) ## Run eslint checks
old-lint: $(TARGET) $(AUXTARGETS) ## Run jshint and jscs checks old-lint: $(TARGET) $(AUXTARGETS) ## Run jshint and jscs checks
@jshint --show-non-errors $(TARGET) $(AUXTARGETS) @jshint --show-non-errors $(TARGET) $(AUXTARGETS)
@jshint --show-non-errors $(CMDS) @jshint --show-non-errors $(CMDS)
@jshint --show-non-errors package.json @jshint --show-non-errors package.json test.js
@jshint --show-non-errors --extract=always $(HTMLLINT) @jshint --show-non-errors --extract=always $(HTMLLINT)
@jscs $(TARGET) $(AUXTARGETS) @jscs $(TARGET) $(AUXTARGETS) test.js
if [ -e $(CLOSURE) ]; then java -jar $(CLOSURE) $(REQS) $(FLOWTARGET) --jscomp_warning=reportUnknownTypes >/dev/null; fi if [ -e $(CLOSURE) ]; then java -jar $(CLOSURE) $(REQS) $(FLOWTARGET) --jscomp_warning=reportUnknownTypes >/dev/null; fi
.PHONY: tslint .PHONY: tslint
@ -115,6 +124,11 @@ misc/coverage.html: $(TARGET) test.js
coveralls: ## Coverage Test + Send to coveralls.io coveralls: ## Coverage Test + Send to coveralls.io
mocha --require blanket --reporter mocha-lcov-reporter -t 20000 | node ./node_modules/coveralls/bin/coveralls.js mocha --require blanket --reporter mocha-lcov-reporter -t 20000 | node ./node_modules/coveralls/bin/coveralls.js
MDLINT=README.md
.PHONY: mdlint
mdlint: $(MDLINT) ## Check markdown documents
alex $^
mdspell -a -n -x -r --en-us $^
.PHONY: help .PHONY: help
help: help:

@ -1,6 +1,6 @@
# Compound File Binary Format # Compound File Binary Format
Pure-JS implementation of MS-CFB: Compound File Binary File Format, a container 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) format used in many Microsoft file types (XLS, DOC, VBA blobs in XLSX and XLSB)
[![Build Status](https://travis-ci.org/SheetJS/js-cfb.svg?branch=master)](https://travis-ci.org/SheetJS/js-cfb) [![Build Status](https://travis-ci.org/SheetJS/js-cfb.svg?branch=master)](https://travis-ci.org/SheetJS/js-cfb)
@ -35,7 +35,7 @@ In node:
var CFB = require('cfb'); var CFB = require('cfb');
``` ```
For example, to get the Workbook content from an XLS file: For example, to get the Workbook content from an Excel 2003 XLS file:
```js ```js
var cfb = CFB.read(filename, {type: 'file'}); var cfb = CFB.read(filename, {type: 'file'});
@ -52,15 +52,23 @@ It is preferable to install the library globally with npm:
$ npm install -g cfb $ npm install -g cfb
``` ```
The global installation adds a command `cfb` which can work with existing files: The global installation adds a command `cfb` which can work with files:
- `cfb file` will extract the contents of the file to the current directory. - `cfb file [names...]` extracts the contents of the file. If additional names
It will make the corresponding subdirectories. are supplied, only the listed files will be extracted.
- `cfb --list-files file` will show a listing of the contained files.
The format follows the `unzip -l` "short format". - `cfb -l file` lists the contained files (following `unzip -l` "short format")
- `cfb --repair file` will attempt to repair by reading and re-writing the file.
- `cfb -r file` attempts to repair by reading and re-writing the file.
This fixes some issues with files generated by non-standard tools. This fixes some issues with files generated by non-standard tools.
- `cfb -c file [files...]` creates a new file containing the listed files.
The default root entry name is `Root Entry`.
- `cfb -a file [files...]` adds the listed files to the original file.
- `cfb -d file [files...]` deletes the listed files from the original file.
## JS API ## JS API
@ -73,10 +81,12 @@ parsed representation of the data.
`CFB.read(blob, opts)` wraps `parse`. `opts.type` controls the behavior: `CFB.read(blob, opts)` wraps `parse`. `opts.type` controls the behavior:
- `file`: `blob` is interpreted as a file name that will be read | `type` | expected input |
- `base64`: `blob` is interpreted as base64 string |------------|-----------------------------------------------------------------|
- `binary`: `blob` is interpreted as binary string | `"base64"` | string: Base64 encoding of the file |
- default: `blob` is interpreted as nodejs buffer or array of bytes | `"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 `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. name, if there are no slashes) and returns an entry object or null if not found.
@ -84,9 +94,11 @@ 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` `CFB.write(cfb, opts)` generates a file based on the container. `opts.type`
controls the behavior: controls the behavior:
- `base64`: returns a base64 string | `type` | output |
- `binary`: returns a binary string |------------|-----------------------------------------------------------------|
- default: returns a nodejs buffer or array of bytes | `"base64"` | string: Base64 encoding of the file |
| `"binary"` | string: binary string (byte `n` is `data.charCodeAt(n)`) |
| (default) | buffer if available, array of 8-bit unsigned int otherwise |
`CFB.writeFile(cfb, filename, opts)` creates a file with the specified name. `CFB.writeFile(cfb, filename, opts)` creates a file with the specified name.
@ -110,19 +122,8 @@ The objects returned by `parse` and `read` have the following properties:
storages (directories) in the container. The paths are properly prefixed from storages (directories) in the container. The paths are properly prefixed from
the root entry (so the entries are unique) the root entry (so the entries are unique)
- `.FullPathDir` is an object whose keys are entries in `.FullPaths` and whose - `.FileIndex` is an array, in the same order as `.FullPaths`, whose values are
values are objects with metadata and content (described below) objects following the schema:
- `.FileIndex` is an array of the objects from `.FullPathDir`, in the same order
as `.FullPaths`.
- `.raw` contains the raw header and sectors
## Entry Object Description
The entry objects are available from `FullPathDir` and `FileIndex` elements of
the container object:
```typescript ```typescript
interface CFBEntry { interface CFBEntry {
@ -151,4 +152,3 @@ granted by the Apache 2.0 License are reserved by the Original Author.
</details> </details>

@ -2,57 +2,67 @@
/* cfb.js (C) 2013-present SheetJS -- http://sheetjs.com */ /* cfb.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* eslint-env node */ /* eslint-env node */
/* vim: set ts=2 ft=javascript: */ /* vim: set ts=2 ft=javascript: */
var n = "cfb";
var X = require('../'); var X = require('../');
var fs = require('fs'); var fs = require('fs');
var program = require('commander'); var program = require('commander');
var PRINTJ = require("printj"); var PRINTJ = require("printj");
var sprintf = PRINTJ.sprintf;
program program
.version(X.version) .version(X.version)
.usage('[options] <file>') .usage('[options] <file> [subfiles...]')
.option('-q, --quiet', 'process but do not report')
.option('-l, --list-files', 'list files') .option('-l, --list-files', 'list files')
.option('-d, --dump', 'dump internal representation but do not extract')
.option('-r, --repair', 'attempt to repair and garbage-collect archive') .option('-r, --repair', 'attempt to repair and garbage-collect archive')
.option('-c, --create', 'create file')
.option('-a, --append', 'add files to CFB (overwrite existing data)')
.option('-d, --delete', 'delete files from CFB')
.option('-z, --dump', 'dump internal representation but do not extract')
.option('-q, --quiet', 'process but do not report')
.option('--dev', 'development mode') .option('--dev', 'development mode')
.option('--read', 'read but do not print out contents'); .option('--read', 'read but do not print out contents');
program.parse(process.argv); program.parse(process.argv);
if(program.args.length === 0 || !fs.existsSync(program.args[0])) { var exit = process.exit;
console.error("Usage: " + process.argv[1] + " [-q] <cfb_file>"); var die = function(errno/*:number*/, msg/*:string*/) { console.error(n + ": " + msg); exit(errno); };
process.exit(1); var logit = function(cmd/*:string*/, f/*:string*/) { console.error(sprintf("%-6s %s", cmd, f)); };
if(program.args.length === 0) die(1, "must specify a filename");
if(program.create) {
logit("create", program.args[0]);
var newcfb = X.utils.cfb_new();
X.writeFile(newcfb, program.args[0]);
} }
if(!fs.existsSync(program.args[0])) die(1, "must specify a filename");
var opts = ({type:'file'}/*:any*/); var opts = ({type:'file'}/*:any*/);
if(program.dev) opts.WTF = true; if(program.dev) opts.WTF = true;
var cfb = X.read(program.args[0], opts); var cfb = X.read(program.args[0], opts);
if(program.quiet) process.exit(0); if(program.quiet) exit(0);
if(program.dump) { if(program.dump) {
console.log("Full Paths:"); console.log("Full Paths:");
console.log(cfb.FullPaths.map(function(x) { return " " + x; }).join("\n")); console.log(cfb.FullPaths.map(function(x/*:string*/) { return " " + x; }).join("\n"));
console.log("Full Path Directory:"); console.log("File Index:");
console.log(cfb.FullPathDir); console.log(cfb.FileIndex);
process.exit(0); exit(0);
}
if(program.repair) {
X.writeFile(cfb, program.args[0]);
process.exit(0);
} }
if(program.repair) { X.writeFile(cfb, program.args[0]); exit(0); }
var sprintf = PRINTJ.sprintf;
function fix_string(x/*:string*/)/*:string*/ { return x.replace(/[\u0000-\u001f]/, function($$) { return sprintf("\\u%04X", $$.charCodeAt(0)); }); } function fix_string(x/*:string*/)/*:string*/ { return x.replace(/[\u0000-\u001f]/, function($$) { return sprintf("\\u%04X", $$.charCodeAt(0)); }); }
if(program.listFiles) { var format_date = function(date/*:Date*/)/*:string*/ {
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());
return sprintf("%02u-%02u-%02u %02u:%02u", date.getUTCMonth()+1, date.getUTCDate(), date.getUTCFullYear()%100, date.getUTCHours(), date.getUTCMinutes()); };
};
if(program.listFiles) {
var basetime = new Date(1980,0,1); var basetime = new Date(1980,0,1);
var cnt = 0, rootsize = 0, filesize = 0; var cnt = 0, rootsize = 0, filesize = 0;
console.log(" Length Date Time Name"); console.log(" Length Date Time Name");
console.log(" -------- ---- ---- ----"); console.log(" -------- ---- ---- ----");
cfb.FileIndex.forEach(function(file, i/*:number*/) { cfb.FileIndex.forEach(function(file/*:CFBEntry*/, i/*:number*/) {
switch(file.type) { switch(file.type) {
case 5: case 5:
basetime = file.ct || file.mt || basetime; basetime = file.ct || file.mt || basetime;
@ -67,14 +77,52 @@ if(program.listFiles) {
console.log(" -------- -------"); console.log(" -------- -------");
console.log(sprintf("%9lu %lu file%s", rootsize || filesize, cnt, (cnt !== 1 ? "s" : ""))); console.log(sprintf("%9lu %lu file%s", rootsize || filesize, cnt, (cnt !== 1 ? "s" : "")));
process.exit(0); exit(0);
} }
function mkdirp(path/*:string*/) { path.split("/").reduce(function(acc/*:string*/, p/*:string*/) {
acc += p + "/";
if(!fs.existsSync(acc)) { logit("mkdir", acc); fs.mkdirSync(acc); }
return acc;
}, ""); }
function write(path/*:string*/, data/*:CFBEntry*/) {
logit("write", fix_string(path));
fs.writeFileSync(path, /*::new Buffer((*/data.content/*:: :any))*/);
}
if(program.create || program.append) {
program.args.slice(1).forEach(function(x/*:string*/) {
logit("append", x);
X.utils.cfb_add(cfb, "/" + x, fs.readFileSync(x));
});
X.writeFile(cfb, program.args[0]);
exit(0);
}
if(program.delete) {
program.args.slice(1).forEach(function(x/*:string*/) {
logit("delete", x);
X.utils.cfb_del(cfb, "/" + x);
});
X.writeFile(cfb, program.args[0]);
exit(0);
}
if(program.args.length > 1) {
program.args.slice(1).forEach(function(x/*:string*/) {
var data/*:?CFBEntry*/ = X.find(cfb, x);
if(!data) { console.error(x + ": file not found"); return; }
if(data.type !== 2) { console.error(x + ": not a file"); return; }
var idx = cfb.FileIndex.indexOf(data), path = cfb.FullPaths[idx];
mkdirp(path.slice(0, path.lastIndexOf("/")));
write(path, data);
});
exit(0);
}
for(var i=0; i!==cfb.FullPaths.length; ++i) { for(var i=0; i!==cfb.FullPaths.length; ++i) {
if(cfb.FullPaths[i].slice(-1) === "/") { if(!cfb.FileIndex[i].name) continue;
console.error("mkdir " + fix_string(cfb.FullPaths[i])); if(cfb.FullPaths[i].slice(-1) === "/") mkdirp(cfb.FullPaths[i]);
fs.mkdirSync(cfb.FullPaths[i]); else write(cfb.FullPaths[i], cfb.FileIndex[i]);
} else {
console.error("write " + fix_string(cfb.FullPaths[i]));
fs.writeFileSync(cfb.FullPaths[i], /*::new Buffer((*/cfb.FileIndex[i].content/*:: :any))*/);
}
} }

@ -1 +1 @@
exports.version = '0.13.1'; exports.version = '0.13.2';

2
bits/39_fs.js Normal file

@ -0,0 +1,2 @@
var fs/*:: = require('fs'); */;
function get_fs() { return fs || (fs = require('fs')); }

@ -1,6 +1,5 @@
var fs/*:: = require('fs'); */;
function read_file(filename/*:string*/, options/*:CFBReadOpts*/) { function read_file(filename/*:string*/, options/*:CFBReadOpts*/) {
if(fs == null) fs = require('fs'); get_fs();
return parse(fs.readFileSync(filename), options); return parse(fs.readFileSync(filename), options);
} }

@ -1,4 +1,5 @@
function write_file(cfb/*:CFBContainer*/, filename/*:string*/, options/*:CFBWriteOpts*/)/*:void*/ { function write_file(cfb/*:CFBContainer*/, filename/*:string*/, options/*:CFBWriteOpts*/)/*:void*/ {
get_fs();
var o = _write(cfb, options); var o = _write(cfb, options);
/*:: if(typeof Buffer == 'undefined' || !Buffer.isBuffer(o) || !(o instanceof Buffer)) throw new Error("unreachable"); */ /*:: if(typeof Buffer == 'undefined' || !Buffer.isBuffer(o) || !(o instanceof Buffer)) throw new Error("unreachable"); */
fs.writeFileSync(filename, o); fs.writeFileSync(filename, o);
@ -13,7 +14,7 @@ function a2s(o/*:RawBytes*/)/*:string*/ {
function write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|string*/ { function write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|string*/ {
var o = _write(cfb, options); var o = _write(cfb, options);
switch(options && options.type) { switch(options && options.type) {
case "file": fs.writeFileSync(options.filename, (o/*:any*/)); return o; case "file": get_fs(); fs.writeFileSync(options.filename, (o/*:any*/)); return o;
case "binary": return a2s(o); case "binary": return a2s(o);
case "base64": return Base64.encode(a2s(o)); case "base64": return Base64.encode(a2s(o));
} }

@ -8,13 +8,13 @@ function cfb_add(cfb/*:CFBContainer*/, name/*:string*/, content/*:?RawBytes*/, o
init_cfb(cfb); init_cfb(cfb);
var file = CFB.find(cfb, name); var file = CFB.find(cfb, name);
if(!file) { if(!file) {
var fpath = cfb.FullPaths[0]; var fpath/*:string*/ = cfb.FullPaths[0];
if(name.slice(0, fpath.length) == fpath) fpath = name; if(name.slice(0, fpath.length) == fpath) fpath = name;
else { else {
if(fpath.slice(-1) != "/") fpath += "/"; if(fpath.slice(-1) != "/") fpath += "/";
fpath = (fpath + name).replace("//","/"); fpath = (fpath + name).replace("//","/");
} }
file = ({name: filename(name)}/*:any*/); file = ({name: filename(name), type: 2}/*:any*/);
cfb.FileIndex.push(file); cfb.FileIndex.push(file);
cfb.FullPaths.push(fpath); cfb.FullPaths.push(fpath);
CFB.utils.cfb_gc(cfb); CFB.utils.cfb_gc(cfb);

@ -179,7 +179,7 @@ type CFBFiles = {[n:string]:CFBEntry};
/* [MS-CFB] v20130118 */ /* [MS-CFB] v20130118 */
var CFB = (function _CFB(){ var CFB = (function _CFB(){
var exports/*:CFBModule*/ = /*::(*/{}/*:: :any)*/; var exports/*:CFBModule*/ = /*::(*/{}/*:: :any)*/;
exports.version = '0.13.1'; exports.version = '0.13.2';
/* [MS-CFB] 2.6.4 */ /* [MS-CFB] 2.6.4 */
function namecmp(l/*:string*/, r/*:string*/)/*:number*/ { function namecmp(l/*:string*/, r/*:string*/)/*:number*/ {
var L = l.split("/"), R = r.split("/"); var L = l.split("/"), R = r.split("/");
@ -200,6 +200,8 @@ function filename(p/*:string*/)/*:string*/ {
var c = p.lastIndexOf("/"); var c = p.lastIndexOf("/");
return (c === -1) ? p : p.slice(c+1); return (c === -1) ? p : p.slice(c+1);
} }
var fs/*:: = require('fs'); */;
function get_fs() { return fs || (fs = require('fs')); }
function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ { function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
var mver = 3; var mver = 3;
var ssz = 512; var ssz = 512;
@ -497,9 +499,8 @@ function read_date(blob/*:RawBytes|CFBlob*/, offset/*:number*/)/*:Date*/ {
return new Date(( ( (__readUInt32LE(blob,offset+4)/1e7)*Math.pow(2,32)+__readUInt32LE(blob,offset)/1e7 ) - 11644473600)*1000); return new Date(( ( (__readUInt32LE(blob,offset+4)/1e7)*Math.pow(2,32)+__readUInt32LE(blob,offset)/1e7 ) - 11644473600)*1000);
} }
var fs/*:: = require('fs'); */;
function read_file(filename/*:string*/, options/*:CFBReadOpts*/) { function read_file(filename/*:string*/, options/*:CFBReadOpts*/) {
if(fs == null) fs = require('fs'); get_fs();
return parse(fs.readFileSync(filename), options); return parse(fs.readFileSync(filename), options);
} }
@ -779,6 +780,7 @@ var consts = {
}; };
function write_file(cfb/*:CFBContainer*/, filename/*:string*/, options/*:CFBWriteOpts*/)/*:void*/ { function write_file(cfb/*:CFBContainer*/, filename/*:string*/, options/*:CFBWriteOpts*/)/*:void*/ {
get_fs();
var o = _write(cfb, options); var o = _write(cfb, options);
/*:: if(typeof Buffer == 'undefined' || !Buffer.isBuffer(o) || !(o instanceof Buffer)) throw new Error("unreachable"); */ /*:: if(typeof Buffer == 'undefined' || !Buffer.isBuffer(o) || !(o instanceof Buffer)) throw new Error("unreachable"); */
fs.writeFileSync(filename, o); fs.writeFileSync(filename, o);
@ -793,7 +795,7 @@ function a2s(o/*:RawBytes*/)/*:string*/ {
function write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|string*/ { function write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|string*/ {
var o = _write(cfb, options); var o = _write(cfb, options);
switch(options && options.type) { switch(options && options.type) {
case "file": fs.writeFileSync(options.filename, (o/*:any*/)); return o; case "file": get_fs(); fs.writeFileSync(options.filename, (o/*:any*/)); return o;
case "binary": return a2s(o); case "binary": return a2s(o);
case "base64": return Base64.encode(a2s(o)); case "base64": return Base64.encode(a2s(o));
} }
@ -809,13 +811,13 @@ function cfb_add(cfb/*:CFBContainer*/, name/*:string*/, content/*:?RawBytes*/, o
init_cfb(cfb); init_cfb(cfb);
var file = CFB.find(cfb, name); var file = CFB.find(cfb, name);
if(!file) { if(!file) {
var fpath = cfb.FullPaths[0]; var fpath/*:string*/ = cfb.FullPaths[0];
if(name.slice(0, fpath.length) == fpath) fpath = name; if(name.slice(0, fpath.length) == fpath) fpath = name;
else { else {
if(fpath.slice(-1) != "/") fpath += "/"; if(fpath.slice(-1) != "/") fpath += "/";
fpath = (fpath + name).replace("//","/"); fpath = (fpath + name).replace("//","/");
} }
file = ({name: filename(name)}/*:any*/); file = ({name: filename(name), type: 2}/*:any*/);
cfb.FileIndex.push(file); cfb.FileIndex.push(file);
cfb.FullPaths.push(fpath); cfb.FullPaths.push(fpath);
CFB.utils.cfb_gc(cfb); CFB.utils.cfb_gc(cfb);

12
cfb.js

@ -161,7 +161,7 @@ function new_buf(sz) {
/* [MS-CFB] v20130118 */ /* [MS-CFB] v20130118 */
var CFB = (function _CFB(){ var CFB = (function _CFB(){
var exports = {}; var exports = {};
exports.version = '0.13.1'; exports.version = '0.13.2';
/* [MS-CFB] 2.6.4 */ /* [MS-CFB] 2.6.4 */
function namecmp(l, r) { function namecmp(l, r) {
var L = l.split("/"), R = r.split("/"); var L = l.split("/"), R = r.split("/");
@ -182,6 +182,8 @@ function filename(p) {
var c = p.lastIndexOf("/"); var c = p.lastIndexOf("/");
return (c === -1) ? p : p.slice(c+1); return (c === -1) ? p : p.slice(c+1);
} }
var fs;
function get_fs() { return fs || (fs = require('fs')); }
function parse(file, options) { function parse(file, options) {
var mver = 3; var mver = 3;
var ssz = 512; var ssz = 512;
@ -479,9 +481,8 @@ function read_date(blob, offset) {
return new Date(( ( (__readUInt32LE(blob,offset+4)/1e7)*Math.pow(2,32)+__readUInt32LE(blob,offset)/1e7 ) - 11644473600)*1000); return new Date(( ( (__readUInt32LE(blob,offset+4)/1e7)*Math.pow(2,32)+__readUInt32LE(blob,offset)/1e7 ) - 11644473600)*1000);
} }
var fs;
function read_file(filename, options) { function read_file(filename, options) {
if(fs == null) fs = require('fs'); get_fs();
return parse(fs.readFileSync(filename), options); return parse(fs.readFileSync(filename), options);
} }
@ -756,6 +757,7 @@ var consts = {
}; };
function write_file(cfb, filename, options) { function write_file(cfb, filename, options) {
get_fs();
var o = _write(cfb, options); var o = _write(cfb, options);
fs.writeFileSync(filename, o); fs.writeFileSync(filename, o);
} }
@ -769,7 +771,7 @@ function a2s(o) {
function write(cfb, options) { function write(cfb, options) {
var o = _write(cfb, options); var o = _write(cfb, options);
switch(options && options.type) { switch(options && options.type) {
case "file": fs.writeFileSync(options.filename, (o)); return o; case "file": get_fs(); fs.writeFileSync(options.filename, (o)); return o;
case "binary": return a2s(o); case "binary": return a2s(o);
case "base64": return Base64.encode(a2s(o)); case "base64": return Base64.encode(a2s(o));
} }
@ -791,7 +793,7 @@ function cfb_add(cfb, name, content, opts) {
if(fpath.slice(-1) != "/") fpath += "/"; if(fpath.slice(-1) != "/") fpath += "/";
fpath = (fpath + name).replace("//","/"); fpath = (fpath + name).replace("//","/");
} }
file = ({name: filename(name)}); file = ({name: filename(name), type: 2});
cfb.FileIndex.push(file); cfb.FileIndex.push(file);
cfb.FullPaths.push(fpath); cfb.FullPaths.push(fpath);
CFB.utils.cfb_gc(cfb); CFB.utils.cfb_gc(cfb);

12
dist/cfb.js vendored

@ -161,7 +161,7 @@ function new_buf(sz) {
/* [MS-CFB] v20130118 */ /* [MS-CFB] v20130118 */
var CFB = (function _CFB(){ var CFB = (function _CFB(){
var exports = {}; var exports = {};
exports.version = '0.13.1'; exports.version = '0.13.2';
/* [MS-CFB] 2.6.4 */ /* [MS-CFB] 2.6.4 */
function namecmp(l, r) { function namecmp(l, r) {
var L = l.split("/"), R = r.split("/"); var L = l.split("/"), R = r.split("/");
@ -182,6 +182,8 @@ function filename(p) {
var c = p.lastIndexOf("/"); var c = p.lastIndexOf("/");
return (c === -1) ? p : p.slice(c+1); return (c === -1) ? p : p.slice(c+1);
} }
var fs;
function get_fs() { return fs || (fs = require('fs')); }
function parse(file, options) { function parse(file, options) {
var mver = 3; var mver = 3;
var ssz = 512; var ssz = 512;
@ -479,9 +481,8 @@ function read_date(blob, offset) {
return new Date(( ( (__readUInt32LE(blob,offset+4)/1e7)*Math.pow(2,32)+__readUInt32LE(blob,offset)/1e7 ) - 11644473600)*1000); return new Date(( ( (__readUInt32LE(blob,offset+4)/1e7)*Math.pow(2,32)+__readUInt32LE(blob,offset)/1e7 ) - 11644473600)*1000);
} }
var fs;
function read_file(filename, options) { function read_file(filename, options) {
if(fs == null) fs = require('fs'); get_fs();
return parse(fs.readFileSync(filename), options); return parse(fs.readFileSync(filename), options);
} }
@ -756,6 +757,7 @@ var consts = {
}; };
function write_file(cfb, filename, options) { function write_file(cfb, filename, options) {
get_fs();
var o = _write(cfb, options); var o = _write(cfb, options);
fs.writeFileSync(filename, o); fs.writeFileSync(filename, o);
} }
@ -769,7 +771,7 @@ function a2s(o) {
function write(cfb, options) { function write(cfb, options) {
var o = _write(cfb, options); var o = _write(cfb, options);
switch(options && options.type) { switch(options && options.type) {
case "file": fs.writeFileSync(options.filename, (o)); return o; case "file": get_fs(); fs.writeFileSync(options.filename, (o)); return o;
case "binary": return a2s(o); case "binary": return a2s(o);
case "base64": return Base64.encode(a2s(o)); case "base64": return Base64.encode(a2s(o));
} }
@ -791,7 +793,7 @@ function cfb_add(cfb, name, content, opts) {
if(fpath.slice(-1) != "/") fpath += "/"; if(fpath.slice(-1) != "/") fpath += "/";
fpath = (fpath + name).replace("//","/"); fpath = (fpath + name).replace("//","/");
} }
file = ({name: filename(name)}); file = ({name: filename(name), type: 2});
cfb.FileIndex.push(file); cfb.FileIndex.push(file);
cfb.FullPaths.push(fpath); cfb.FullPaths.push(fpath);
CFB.utils.cfb_gc(cfb); CFB.utils.cfb_gc(cfb);

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

14
dist/xlscfb.js vendored

@ -38,7 +38,7 @@ type CFBFiles = {[n:string]:CFBEntry};
/* [MS-CFB] v20130118 */ /* [MS-CFB] v20130118 */
var CFB = (function _CFB(){ var CFB = (function _CFB(){
var exports/*:CFBModule*/ = /*::(*/{}/*:: :any)*/; var exports/*:CFBModule*/ = /*::(*/{}/*:: :any)*/;
exports.version = '0.13.1'; exports.version = '0.13.2';
/* [MS-CFB] 2.6.4 */ /* [MS-CFB] 2.6.4 */
function namecmp(l/*:string*/, r/*:string*/)/*:number*/ { function namecmp(l/*:string*/, r/*:string*/)/*:number*/ {
var L = l.split("/"), R = r.split("/"); var L = l.split("/"), R = r.split("/");
@ -59,6 +59,8 @@ function filename(p/*:string*/)/*:string*/ {
var c = p.lastIndexOf("/"); var c = p.lastIndexOf("/");
return (c === -1) ? p : p.slice(c+1); return (c === -1) ? p : p.slice(c+1);
} }
var fs/*:: = require('fs'); */;
function get_fs() { return fs || (fs = require('fs')); }
function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ { function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
var mver = 3; var mver = 3;
var ssz = 512; var ssz = 512;
@ -356,9 +358,8 @@ function read_date(blob/*:RawBytes|CFBlob*/, offset/*:number*/)/*:Date*/ {
return new Date(( ( (__readUInt32LE(blob,offset+4)/1e7)*Math.pow(2,32)+__readUInt32LE(blob,offset)/1e7 ) - 11644473600)*1000); return new Date(( ( (__readUInt32LE(blob,offset+4)/1e7)*Math.pow(2,32)+__readUInt32LE(blob,offset)/1e7 ) - 11644473600)*1000);
} }
var fs/*:: = require('fs'); */;
function read_file(filename/*:string*/, options/*:CFBReadOpts*/) { function read_file(filename/*:string*/, options/*:CFBReadOpts*/) {
if(fs == null) fs = require('fs'); get_fs();
return parse(fs.readFileSync(filename), options); return parse(fs.readFileSync(filename), options);
} }
@ -638,6 +639,7 @@ var consts = {
}; };
function write_file(cfb/*:CFBContainer*/, filename/*:string*/, options/*:CFBWriteOpts*/)/*:void*/ { function write_file(cfb/*:CFBContainer*/, filename/*:string*/, options/*:CFBWriteOpts*/)/*:void*/ {
get_fs();
var o = _write(cfb, options); var o = _write(cfb, options);
/*:: if(typeof Buffer == 'undefined' || !Buffer.isBuffer(o) || !(o instanceof Buffer)) throw new Error("unreachable"); */ /*:: if(typeof Buffer == 'undefined' || !Buffer.isBuffer(o) || !(o instanceof Buffer)) throw new Error("unreachable"); */
fs.writeFileSync(filename, o); fs.writeFileSync(filename, o);
@ -652,7 +654,7 @@ function a2s(o/*:RawBytes*/)/*:string*/ {
function write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|string*/ { function write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|string*/ {
var o = _write(cfb, options); var o = _write(cfb, options);
switch(options && options.type) { switch(options && options.type) {
case "file": fs.writeFileSync(options.filename, (o/*:any*/)); return o; case "file": get_fs(); fs.writeFileSync(options.filename, (o/*:any*/)); return o;
case "binary": return a2s(o); case "binary": return a2s(o);
case "base64": return Base64.encode(a2s(o)); case "base64": return Base64.encode(a2s(o));
} }
@ -668,13 +670,13 @@ function cfb_add(cfb/*:CFBContainer*/, name/*:string*/, content/*:?RawBytes*/, o
init_cfb(cfb); init_cfb(cfb);
var file = CFB.find(cfb, name); var file = CFB.find(cfb, name);
if(!file) { if(!file) {
var fpath = cfb.FullPaths[0]; var fpath/*:string*/ = cfb.FullPaths[0];
if(name.slice(0, fpath.length) == fpath) fpath = name; if(name.slice(0, fpath.length) == fpath) fpath = name;
else { else {
if(fpath.slice(-1) != "/") fpath += "/"; if(fpath.slice(-1) != "/") fpath += "/";
fpath = (fpath + name).replace("//","/"); fpath = (fpath + name).replace("//","/");
} }
file = ({name: filename(name)}/*:any*/); file = ({name: filename(name), type: 2}/*:any*/);
cfb.FileIndex.push(file); cfb.FileIndex.push(file);
cfb.FullPaths.push(fpath); cfb.FullPaths.push(fpath);
CFB.utils.cfb_gc(cfb); CFB.utils.cfb_gc(cfb);

@ -32,7 +32,7 @@ a { text-decoration: none }
<b>Advanced Demo Options:</b> <b>Advanced Demo Options:</b>
Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" checked> Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" checked>
<a id="saveit" onclick="savefile();" href="#">Export loaded data</a> <a id="saveit" onclick="savefile();" href="#">Export data</a>
</pre> </pre>
<pre id="out"></pre> <pre id="out"></pre>
<br /> <br />
@ -61,14 +61,14 @@ var get_manifest = (function() {
var cnt = 0, rootsize = 0, filesize = 0; var cnt = 0, rootsize = 0, filesize = 0;
out.push(" Length Date Time Name"); out.push(" Length Date Time Name");
out.push(" -------- ---- ---- ----"); out.push(" -------- ---- ---- ----");
cfb.FileIndex.forEach(function(file, i/*:number*/) { cfb.FileIndex.forEach(function(file/*:CFBEntry*/, i/*:number*/) {
switch(file.type) { switch(file.type) {
case 5: case 5:
basetime = file.ct || file.mt || basetime; basetime = file.ct || file.mt || basetime;
rootsize = file.size; rootsize = file.size;
break; break;
case 2: case 2:
out.push(sprintf("%9lu %s %s", file.size, format_date(basetime), fix_string(cfb.FullPaths[i]))); out.push(sprintf("%9lu %s <a href=\"#\" onclick=\"download_file(%d);\">%s</a>", file.size, format_date(basetime), i, fix_string(cfb.FullPaths[i])));
filesize += file.size; filesize += file.size;
++cnt; ++cnt;
} }
@ -82,8 +82,7 @@ var get_manifest = (function() {
function process_data(cfb) { function process_data(cfb) {
global_cfb = cfb; global_cfb = cfb;
var output = get_manifest(cfb); var output = get_manifest(cfb);
if(out.innerText === undefined) out.textContent = output; out.innerHTML = output;
else out.innerText = output;
} }
var do_file = (function() { var do_file = (function() {
@ -156,6 +155,22 @@ var savefile = (function() {
saveAs(new Blob([s2ab(data)],{type:"application/octet-stream"}), "sheetjs.xls"); saveAs(new Blob([s2ab(data)],{type:"application/octet-stream"}), "sheetjs.xls");
}; };
})(); })();
var download_file = (function() {
var a2ab = function a2ab(a) {
var o = new ArrayBuffer(a.length);
var view = new Uint8Array(o);
for (var i = 0; i!=a.length; ++i) view[i] = a[i];
return o;
};
return function download_file(i) {
if(!global_cfb) return alert("Must load a file first!");
console.log(global_cfb);
var file = global_cfb.FileIndex[i], data = file.content;
saveAs(new Blob([a2ab(data)],{type:"application/octet-stream"}), file.name);
};
})();
</script> </script>
<script type="text/javascript"> <script type="text/javascript">
/* eslint no-use-before-define:0 */ /* eslint no-use-before-define:0 */

@ -67,7 +67,7 @@ type CFBlob = CFBlobArray | CFBlobBuffer | CFBlobUint8;
type CFBWriteOpts = any; type CFBWriteOpts = any;
interface CFBReadOpts { interface CFBReadOpts {
type:?string; type?:string;
}; };
type CFBFileIndex = Array<CFBEntry>; type CFBFileIndex = Array<CFBEntry>;

@ -6,4 +6,5 @@ declare module './' { declare var exports:CFBModule; };
declare module 'commander' { declare var exports:any; }; declare module 'commander' { declare var exports:any; };
declare module 'printj' { declare var exports:any; }; declare module 'printj' { declare var exports:any; };
declare module 'crc-32' { declare var exports: any; };
*/ */

@ -1,5 +0,0 @@
var describe = function(m,cb){if(cb) cb();};
describe.skip = function(m,cb){};
var it = function(m,cb){if(cb) cb();};
it.skip = function(m,cb){};
var before = function(cb){if(cb) cb();};

@ -1,6 +1,6 @@
{ {
"name": "cfb", "name": "cfb",
"version": "0.13.1", "version": "0.13.2",
"author": "sheetjs", "author": "sheetjs",
"description": "Compound File Binary File Format extractor", "description": "Compound File Binary File Format extractor",
"keywords": [ "cfb", "compression", "office" ], "keywords": [ "cfb", "compression", "office" ],

15
test.js

@ -1,8 +1,18 @@
/* cfb.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */ /* vim: set ts=2: */
/*jshint mocha:true */
/*global process, require */
/*::
declare type EmptyFunc = (() => void) | null;
declare type DescribeIt = { (desc:string, test:EmptyFunc):void; skip(desc:string, test:EmptyFunc):void; };
declare var describe : DescribeIt;
declare var it: DescribeIt;
declare var before:(test:EmptyFunc)=>void;
*/
var CFB; var CFB;
var fs = require('fs'); var fs = require('fs');
describe('source', function() { it('should load', function() { CFB = require('./'); }); }); describe('source', function() { it('should load', function() { CFB = require('./'); }); });
if(typeof CRC32 === 'undefined') CRC32 = require('crc-32'); var CRC32 = require('crc-32');
var ex = [".xls",".doc",".ppt"]; 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) ex=process.env.FMTS.split(":").map(function(x){return x[0]==="."?x:"."+x;});
@ -71,6 +81,7 @@ function parsetest(x, cfb) {
_new = CFB.find(newcfb, '/WordDocument') || CFB.find(newcfb, '/Word Document'); _new = CFB.find(newcfb, '/WordDocument') || CFB.find(newcfb, '/Word Document');
break; break;
} }
/*:: if(!_old || !_new) throw "unreachable"; */
if(CRC32.buf(_old.content) != CRC32.buf(_new.content)) throw new Error(x + " failed roundtrip test"); if(CRC32.buf(_old.content) != CRC32.buf(_new.content)) throw new Error(x + " failed roundtrip test");
}); });
it('should be idempotent', function() { it('should be idempotent', function() {
@ -107,7 +118,7 @@ describe('should parse test files', function() {
}); });
}); });
var cp = 'custom_properties.xls' var cp = 'custom_properties.xls';
describe('input formats', function() { describe('input formats', function() {
it('should read binary strings', function() { it('should read binary strings', function() {

@ -1,57 +1,67 @@
/* cfb.js (C) 2013-present SheetJS -- http://sheetjs.com */ /* cfb.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* eslint-env node */ /* eslint-env node */
/* vim: set ts=2 ft=javascript: */ /* vim: set ts=2 ft=javascript: */
const n = "cfb";
import * as X from 'cfb'; import * as X from 'cfb';
import fs = require('fs'); import fs = require('fs');
import program = require('commander'); import program = require('commander');
import PRINTJ = require("printj"); import PRINTJ = require("printj");
const sprintf = PRINTJ.sprintf;
program program
.version(X.version) .version(X.version)
.usage('[options] <file>') .usage('[options] <file> [subfiles...]')
.option('-q, --quiet', 'process but do not report') .option('-q, --quiet', 'process but do not report')
.option('-l, --list-files', 'list files') .option('-l, --list-files', 'list files')
.option('-d, --dump', 'dump internal representation but do not extract') .option('-z, --dump', 'dump internal representation but do not extract')
.option('-r, --repair', 'attempt to repair and garbage-collect archive') .option('-r, --repair', 'attempt to repair and garbage-collect archive')
.option('-c, --create', 'create file')
.option('-a, --append', 'add files to CFB (overwrite existing data)')
.option('-d, --delete', 'delete files from CFB')
.option('--dev', 'development mode') .option('--dev', 'development mode')
.option('--read', 'read but do not print out contents'); .option('--read', 'read but do not print out contents');
program.parse(process.argv); program.parse(process.argv);
if(program.args.length === 0 || !fs.existsSync(program.args[0])) { const exit = process.exit;
console.error("Usage: " + process.argv[1] + " [-q] <cfb_file>"); const die = (errno: number, msg: string) => { console.error(n + ": " + msg); exit(errno); };
process.exit(1); const logit = (cmd: string, f: string) => { console.error(sprintf("%-6s %s", cmd, f)); };
if(program.args.length === 0) die(1, "must specify a filename");
if(program.create) {
logit("create", program.args[0]);
const newcfb = X.utils.cfb_new();
X.writeFile(newcfb, program.args[0]);
} }
if(!fs.existsSync(program.args[0])) die(1, "must specify a filename");
const opts: X.CFBParsingOptions = {type:'file'}; const opts: X.CFBParsingOptions = {type:'file'};
if(program.dev) opts.WTF = true; if(program.dev) opts.WTF = true;
const cfb: X.CFBContainer = X.read(program.args[0], opts); const cfb: X.CFBContainer = X.read(program.args[0], opts);
if(program.quiet) process.exit(0); if(program.quiet) exit(0);
if(program.dump) { if(program.dump) {
console.log("Full Paths:"); console.log("Full Paths:");
console.log(cfb.FullPaths.map((x) => " " + x).join("\n")); console.log(cfb.FullPaths.map((x) => " " + x).join("\n"));
console.log("Full Path Directory:"); console.log("File Index:");
console.log(cfb.FullPathDir); console.log(cfb.FileIndex);
process.exit(0); exit(0);
}
if(program.repair) {
X.writeFile(cfb, program.args[0]);
process.exit(0);
} }
if(program.repair) { X.writeFile(cfb, program.args[0]); exit(0); }
const fix_string = (x: string): string => x.replace(/[\u0000-\u001f]/, ($$) => sprintf("\\u%04X", $$.charCodeAt(0)));
const format_date = (date: Date): string => {
return sprintf("%02u-%02u-%02u %02u:%02u", date.getUTCMonth()+1, date.getUTCDate(), date.getUTCFullYear()%100, date.getUTCHours(), date.getUTCMinutes());
};
const sprintf = PRINTJ.sprintf;
function fix_string(x: string): string { return x.replace(/[\u0000-\u001f]/, function($$) { return sprintf("\\u%04X", $$.charCodeAt(0)); }); }
if(program.listFiles) { if(program.listFiles) {
const 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());
};
let basetime = new Date(1980,0,1); let basetime = new Date(1980,0,1);
let cnt = 0, rootsize = 0, filesize = 0; let cnt = 0, rootsize = 0, filesize = 0;
console.log(" Length Date Time Name"); console.log(" Length Date Time Name");
console.log(" -------- ---- ---- ----"); console.log(" -------- ---- ---- ----");
cfb.FileIndex.forEach(function(file: X.CFBEntry, i: number) { cfb.FileIndex.forEach((file: X.CFBEntry, i: number) => {
switch(file.type) { switch(file.type) {
case 5: case 5:
basetime = file.ct || file.mt || basetime; basetime = file.ct || file.mt || basetime;
@ -66,14 +76,52 @@ if(program.listFiles) {
console.log(" -------- -------"); console.log(" -------- -------");
console.log(sprintf("%9lu %lu file%s", rootsize || filesize, cnt, (cnt !== 1 ? "s" : ""))); console.log(sprintf("%9lu %lu file%s", rootsize || filesize, cnt, (cnt !== 1 ? "s" : "")));
process.exit(0); exit(0);
} }
const mkdirp = (path: string) => { path.split("/").reduce((acc: string, p: string): string => {
acc += p + "/";
if(!fs.existsSync(acc)) { logit("mkdir", acc); fs.mkdirSync(acc); }
return acc;
}, ""); };
const write = (path: string, data: X.CFBEntry) => {
logit("write", fix_string(path));
fs.writeFileSync(path, /*::new Buffer((*/data.content/*:: :any))*/);
};
if(program.create || program.append) {
program.args.slice(1).forEach((x: string) => {
logit("append", x);
X.utils.cfb_add(cfb, "/" + x, fs.readFileSync(x));
});
X.writeFile(cfb, program.args[0]);
exit(0);
}
if(program.delete) {
program.args.slice(1).forEach((x: string) => {
logit("delete", x);
X.utils.cfb_del(cfb, "/" + x);
});
X.writeFile(cfb, program.args[0]);
exit(0);
}
if(program.args.length > 1) {
program.args.slice(1).forEach((x: string) => {
const data/*:?CFBEntry*/ = X.find(cfb, x);
if(!data) { console.error(x + ": file not found"); return; }
if(data.type !== 2) { console.error(x + ": not a file"); return; }
const idx = cfb.FileIndex.indexOf(data), path = cfb.FullPaths[idx];
mkdirp(path.slice(0, path.lastIndexOf("/")));
write(path, data);
});
exit(0);
}
for(let i=0; i!==cfb.FullPaths.length; ++i) { for(let i=0; i!==cfb.FullPaths.length; ++i) {
if(cfb.FullPaths[i].slice(-1) === "/") { if(!cfb.FileIndex[i].name) continue;
console.error("mkdir " + fix_string(cfb.FullPaths[i])); if(cfb.FullPaths[i].slice(-1) === "/") mkdirp(cfb.FullPaths[i]);
fs.mkdirSync(cfb.FullPaths[i]); else write(cfb.FullPaths[i], cfb.FileIndex[i]);
} else {
console.error("write " + fix_string(cfb.FullPaths[i]));
fs.writeFileSync(cfb.FullPaths[i], cfb.FileIndex[i].content);
}
} }

@ -38,7 +38,7 @@ type CFBFiles = {[n:string]:CFBEntry};
/* [MS-CFB] v20130118 */ /* [MS-CFB] v20130118 */
var CFB = (function _CFB(){ var CFB = (function _CFB(){
var exports/*:CFBModule*/ = /*::(*/{}/*:: :any)*/; var exports/*:CFBModule*/ = /*::(*/{}/*:: :any)*/;
exports.version = '0.13.1'; exports.version = '0.13.2';
/* [MS-CFB] 2.6.4 */ /* [MS-CFB] 2.6.4 */
function namecmp(l/*:string*/, r/*:string*/)/*:number*/ { function namecmp(l/*:string*/, r/*:string*/)/*:number*/ {
var L = l.split("/"), R = r.split("/"); var L = l.split("/"), R = r.split("/");
@ -59,6 +59,8 @@ function filename(p/*:string*/)/*:string*/ {
var c = p.lastIndexOf("/"); var c = p.lastIndexOf("/");
return (c === -1) ? p : p.slice(c+1); return (c === -1) ? p : p.slice(c+1);
} }
var fs/*:: = require('fs'); */;
function get_fs() { return fs || (fs = require('fs')); }
function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ { function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
var mver = 3; var mver = 3;
var ssz = 512; var ssz = 512;
@ -356,9 +358,8 @@ function read_date(blob/*:RawBytes|CFBlob*/, offset/*:number*/)/*:Date*/ {
return new Date(( ( (__readUInt32LE(blob,offset+4)/1e7)*Math.pow(2,32)+__readUInt32LE(blob,offset)/1e7 ) - 11644473600)*1000); return new Date(( ( (__readUInt32LE(blob,offset+4)/1e7)*Math.pow(2,32)+__readUInt32LE(blob,offset)/1e7 ) - 11644473600)*1000);
} }
var fs/*:: = require('fs'); */;
function read_file(filename/*:string*/, options/*:CFBReadOpts*/) { function read_file(filename/*:string*/, options/*:CFBReadOpts*/) {
if(fs == null) fs = require('fs'); get_fs();
return parse(fs.readFileSync(filename), options); return parse(fs.readFileSync(filename), options);
} }
@ -638,6 +639,7 @@ var consts = {
}; };
function write_file(cfb/*:CFBContainer*/, filename/*:string*/, options/*:CFBWriteOpts*/)/*:void*/ { function write_file(cfb/*:CFBContainer*/, filename/*:string*/, options/*:CFBWriteOpts*/)/*:void*/ {
get_fs();
var o = _write(cfb, options); var o = _write(cfb, options);
/*:: if(typeof Buffer == 'undefined' || !Buffer.isBuffer(o) || !(o instanceof Buffer)) throw new Error("unreachable"); */ /*:: if(typeof Buffer == 'undefined' || !Buffer.isBuffer(o) || !(o instanceof Buffer)) throw new Error("unreachable"); */
fs.writeFileSync(filename, o); fs.writeFileSync(filename, o);
@ -652,7 +654,7 @@ function a2s(o/*:RawBytes*/)/*:string*/ {
function write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|string*/ { function write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|string*/ {
var o = _write(cfb, options); var o = _write(cfb, options);
switch(options && options.type) { switch(options && options.type) {
case "file": fs.writeFileSync(options.filename, (o/*:any*/)); return o; case "file": get_fs(); fs.writeFileSync(options.filename, (o/*:any*/)); return o;
case "binary": return a2s(o); case "binary": return a2s(o);
case "base64": return Base64.encode(a2s(o)); case "base64": return Base64.encode(a2s(o));
} }
@ -668,13 +670,13 @@ function cfb_add(cfb/*:CFBContainer*/, name/*:string*/, content/*:?RawBytes*/, o
init_cfb(cfb); init_cfb(cfb);
var file = CFB.find(cfb, name); var file = CFB.find(cfb, name);
if(!file) { if(!file) {
var fpath = cfb.FullPaths[0]; var fpath/*:string*/ = cfb.FullPaths[0];
if(name.slice(0, fpath.length) == fpath) fpath = name; if(name.slice(0, fpath.length) == fpath) fpath = name;
else { else {
if(fpath.slice(-1) != "/") fpath += "/"; if(fpath.slice(-1) != "/") fpath += "/";
fpath = (fpath + name).replace("//","/"); fpath = (fpath + name).replace("//","/");
} }
file = ({name: filename(name)}/*:any*/); file = ({name: filename(name), type: 2}/*:any*/);
cfb.FileIndex.push(file); cfb.FileIndex.push(file);
cfb.FullPaths.push(fpath); cfb.FullPaths.push(fpath);
CFB.utils.cfb_gc(cfb); CFB.utils.cfb_gc(cfb);

@ -8,7 +8,7 @@ var DO_NOT_EXPORT_CFB = true;
/* [MS-CFB] v20130118 */ /* [MS-CFB] v20130118 */
var CFB = (function _CFB(){ var CFB = (function _CFB(){
var exports = {}; var exports = {};
exports.version = '0.13.1'; exports.version = '0.13.2';
/* [MS-CFB] 2.6.4 */ /* [MS-CFB] 2.6.4 */
function namecmp(l, r) { function namecmp(l, r) {
var L = l.split("/"), R = r.split("/"); var L = l.split("/"), R = r.split("/");
@ -29,6 +29,8 @@ function filename(p) {
var c = p.lastIndexOf("/"); var c = p.lastIndexOf("/");
return (c === -1) ? p : p.slice(c+1); return (c === -1) ? p : p.slice(c+1);
} }
var fs;
function get_fs() { return fs || (fs = require('fs')); }
function parse(file, options) { function parse(file, options) {
var mver = 3; var mver = 3;
var ssz = 512; var ssz = 512;
@ -326,9 +328,8 @@ function read_date(blob, offset) {
return new Date(( ( (__readUInt32LE(blob,offset+4)/1e7)*Math.pow(2,32)+__readUInt32LE(blob,offset)/1e7 ) - 11644473600)*1000); return new Date(( ( (__readUInt32LE(blob,offset+4)/1e7)*Math.pow(2,32)+__readUInt32LE(blob,offset)/1e7 ) - 11644473600)*1000);
} }
var fs;
function read_file(filename, options) { function read_file(filename, options) {
if(fs == null) fs = require('fs'); get_fs();
return parse(fs.readFileSync(filename), options); return parse(fs.readFileSync(filename), options);
} }
@ -603,6 +604,7 @@ var consts = {
}; };
function write_file(cfb, filename, options) { function write_file(cfb, filename, options) {
get_fs();
var o = _write(cfb, options); var o = _write(cfb, options);
fs.writeFileSync(filename, o); fs.writeFileSync(filename, o);
} }
@ -616,7 +618,7 @@ function a2s(o) {
function write(cfb, options) { function write(cfb, options) {
var o = _write(cfb, options); var o = _write(cfb, options);
switch(options && options.type) { switch(options && options.type) {
case "file": fs.writeFileSync(options.filename, (o)); return o; case "file": get_fs(); fs.writeFileSync(options.filename, (o)); return o;
case "binary": return a2s(o); case "binary": return a2s(o);
case "base64": return Base64.encode(a2s(o)); case "base64": return Base64.encode(a2s(o));
} }
@ -638,7 +640,7 @@ function cfb_add(cfb, name, content, opts) {
if(fpath.slice(-1) != "/") fpath += "/"; if(fpath.slice(-1) != "/") fpath += "/";
fpath = (fpath + name).replace("//","/"); fpath = (fpath + name).replace("//","/");
} }
file = ({name: filename(name)}); file = ({name: filename(name), type: 2});
cfb.FileIndex.push(file); cfb.FileIndex.push(file);
cfb.FullPaths.push(fpath); cfb.FullPaths.push(fpath);
CFB.utils.cfb_gc(cfb); CFB.utils.cfb_gc(cfb);