From 9e22a4425e1a51715de8f21ac1e6800cc5bf5c02 Mon Sep 17 00:00:00 2001 From: SheetJS Date: Fri, 20 Oct 2017 16:36:54 -0400 Subject: [PATCH] version bump 0.13.2 - file writing includes `fs` as appropriate - CLI modify and write options - infrastructure update --- .flowconfig | 2 +- .spelling | 16 +++++++ Makefile | 18 ++++++- README.md | 58 +++++++++++------------ bin/cfb.njs | 106 ++++++++++++++++++++++++++++++------------ bits/31_version.js | 2 +- bits/39_fs.js | 2 + bits/49_readutils.js | 3 +- bits/77_writeutils.js | 3 +- bits/85_api.js | 4 +- cfb.flow.js | 14 +++--- cfb.js | 12 +++-- dist/cfb.js | 12 +++-- dist/cfb.min.js | 2 +- dist/cfb.min.map | 2 +- dist/xlscfb.js | 14 +++--- index.html | 25 ++++++++-- misc/flow.js | 2 +- misc/flowdeps.js | 1 + misc/prof.js | 5 -- package.json | 2 +- test.js | 15 +++++- types/bin_cfb.ts | 104 ++++++++++++++++++++++++++++++----------- xlscfb.flow.js | 14 +++--- xlscfb.js | 12 +++-- 25 files changed, 306 insertions(+), 144 deletions(-) create mode 100644 .spelling create mode 100644 bits/39_fs.js delete mode 100644 misc/prof.js diff --git a/.flowconfig b/.flowconfig index 191e085..0e7334d 100644 --- a/.flowconfig +++ b/.flowconfig @@ -3,7 +3,6 @@ .*/dist/.* .*/test_files/.* .*/test_files_pres/.* -.*/test.js .*/bits/.* .*/ctest/.* @@ -23,6 +22,7 @@ cfb.flow.js xlscfb.flow.js .*/bin/.*.njs +test.js [libs] bits/10_types.js diff --git a/.spelling b/.spelling new file mode 100644 index 0000000..6b322e1 --- /dev/null +++ b/.spelling @@ -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 diff --git a/Makefile b/Makefile index 1a65142..675e33a 100644 --- a/Makefile +++ b/Makefile @@ -66,6 +66,11 @@ XLSDEPS=misc/suppress_export.js $(filter-out $(XLSSKIP),$(DEPS)) xlscfb.flow.js: $(XLSDEPS) ## Build support library 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 @@ -74,6 +79,7 @@ test mocha: test.js $(TARGET) ## Run test suite mocha -R spec -t 20000 #* To run tests for one format, make test_ +#* To run the core test suite, make test_misc TESTFMT=$(patsubst %,test_%,$(FMT)) .PHONY: $(TESTFMT) $(TESTFMT): test_%: @@ -82,6 +88,9 @@ $(TESTFMT): test_%: ## Code Checking +.PHONY: fullint +fullint: lint old-lint tslint flow mdlint ## Run all checks + .PHONY: lint lint: $(TARGET) $(AUXTARGETS) ## Run eslint checks @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 @jshint --show-non-errors $(TARGET) $(AUXTARGETS) @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) - @jscs $(TARGET) $(AUXTARGETS) + @jscs $(TARGET) $(AUXTARGETS) test.js if [ -e $(CLOSURE) ]; then java -jar $(CLOSURE) $(REQS) $(FLOWTARGET) --jscomp_warning=reportUnknownTypes >/dev/null; fi .PHONY: tslint @@ -115,6 +124,11 @@ misc/coverage.html: $(TARGET) test.js coveralls: ## Coverage Test + Send to coveralls.io 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 help: diff --git a/README.md b/README.md index 541512b..877839d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 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) [![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'); ``` -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 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 ``` -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. - It will make the corresponding subdirectories. -- `cfb --list-files file` will show a listing of the contained files. - The format follows the `unzip -l` "short format". -- `cfb --repair file` will attempt to repair by reading and re-writing the file. +- `cfb file [names...]` extracts the contents of the file. If additional names + are supplied, only the listed files will be extracted. + +- `cfb -l file` lists the contained files (following `unzip -l` "short format") + +- `cfb -r file` attempts to repair by reading and re-writing the file. 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 @@ -73,10 +81,12 @@ parsed representation of the data. `CFB.read(blob, opts)` wraps `parse`. `opts.type` controls the behavior: -- `file`: `blob` is interpreted as a file name that will be read -- `base64`: `blob` is interpreted as base64 string -- `binary`: `blob` is interpreted as binary string -- default: `blob` is interpreted as nodejs buffer or array of bytes +| `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. @@ -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` controls the behavior: -- `base64`: returns a base64 string -- `binary`: returns a binary string -- default: returns a nodejs buffer or array of bytes +| `type` | output | +|------------|-----------------------------------------------------------------| +| `"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. @@ -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 the root entry (so the entries are unique) -- `.FullPathDir` is an object whose keys are entries in `.FullPaths` and whose - values are objects with metadata and content (described below) - -- `.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: +- `.FileIndex` is an array, in the same order as `.FullPaths`, whose values are + objects following the schema: ```typescript interface CFBEntry { @@ -151,4 +152,3 @@ granted by the Apache 2.0 License are reserved by the Original Author. - diff --git a/bin/cfb.njs b/bin/cfb.njs index ca7e778..eb9ebc3 100755 --- a/bin/cfb.njs +++ b/bin/cfb.njs @@ -2,57 +2,67 @@ /* cfb.js (C) 2013-present SheetJS -- http://sheetjs.com */ /* eslint-env node */ /* vim: set ts=2 ft=javascript: */ +var n = "cfb"; var X = require('../'); var fs = require('fs'); var program = require('commander'); var PRINTJ = require("printj"); +var sprintf = PRINTJ.sprintf; program .version(X.version) - .usage('[options] ') - .option('-q, --quiet', 'process but do not report') + .usage('[options] [subfiles...]') .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('-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('--read', 'read but do not print out contents'); program.parse(process.argv); -if(program.args.length === 0 || !fs.existsSync(program.args[0])) { - console.error("Usage: " + process.argv[1] + " [-q] "); - process.exit(1); +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)); }; + +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*/); if(program.dev) opts.WTF = true; var cfb = X.read(program.args[0], opts); -if(program.quiet) process.exit(0); +if(program.quiet) exit(0); if(program.dump) { console.log("Full Paths:"); - console.log(cfb.FullPaths.map(function(x) { return " " + x; }).join("\n")); - console.log("Full Path Directory:"); - console.log(cfb.FullPathDir); - process.exit(0); -} -if(program.repair) { - X.writeFile(cfb, program.args[0]); - process.exit(0); + console.log(cfb.FullPaths.map(function(x/*:string*/) { return " " + x; }).join("\n")); + console.log("File Index:"); + console.log(cfb.FileIndex); + 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)); }); } -if(program.listFiles) { - 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*/)/*:string*/ { + 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 cnt = 0, rootsize = 0, filesize = 0; console.log(" Length Date Time Name"); console.log(" -------- ---- ---- ----"); - cfb.FileIndex.forEach(function(file, i/*:number*/) { + cfb.FileIndex.forEach(function(file/*:CFBEntry*/, i/*:number*/) { switch(file.type) { case 5: basetime = file.ct || file.mt || basetime; @@ -67,14 +77,52 @@ if(program.listFiles) { console.log(" -------- -------"); 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) { - if(cfb.FullPaths[i].slice(-1) === "/") { - console.error("mkdir " + fix_string(cfb.FullPaths[i])); - fs.mkdirSync(cfb.FullPaths[i]); - } else { - console.error("write " + fix_string(cfb.FullPaths[i])); - fs.writeFileSync(cfb.FullPaths[i], /*::new Buffer((*/cfb.FileIndex[i].content/*:: :any))*/); - } + if(!cfb.FileIndex[i].name) continue; + if(cfb.FullPaths[i].slice(-1) === "/") mkdirp(cfb.FullPaths[i]); + else write(cfb.FullPaths[i], cfb.FileIndex[i]); } diff --git a/bits/31_version.js b/bits/31_version.js index 29e2371..726dd00 100644 --- a/bits/31_version.js +++ b/bits/31_version.js @@ -1 +1 @@ -exports.version = '0.13.1'; +exports.version = '0.13.2'; diff --git a/bits/39_fs.js b/bits/39_fs.js new file mode 100644 index 0000000..0c95673 --- /dev/null +++ b/bits/39_fs.js @@ -0,0 +1,2 @@ +var fs/*:: = require('fs'); */; +function get_fs() { return fs || (fs = require('fs')); } diff --git a/bits/49_readutils.js b/bits/49_readutils.js index 42db2e5..a80b454 100644 --- a/bits/49_readutils.js +++ b/bits/49_readutils.js @@ -1,6 +1,5 @@ -var fs/*:: = require('fs'); */; function read_file(filename/*:string*/, options/*:CFBReadOpts*/) { - if(fs == null) fs = require('fs'); + get_fs(); return parse(fs.readFileSync(filename), options); } diff --git a/bits/77_writeutils.js b/bits/77_writeutils.js index 2b8382b..9527cda 100644 --- a/bits/77_writeutils.js +++ b/bits/77_writeutils.js @@ -1,4 +1,5 @@ function write_file(cfb/*:CFBContainer*/, filename/*:string*/, options/*:CFBWriteOpts*/)/*:void*/ { + get_fs(); var o = _write(cfb, options); /*:: if(typeof Buffer == 'undefined' || !Buffer.isBuffer(o) || !(o instanceof Buffer)) throw new Error("unreachable"); */ fs.writeFileSync(filename, o); @@ -13,7 +14,7 @@ function a2s(o/*:RawBytes*/)/*:string*/ { function write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|string*/ { var o = _write(cfb, options); 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 "base64": return Base64.encode(a2s(o)); } diff --git a/bits/85_api.js b/bits/85_api.js index da077e4..0d2cb4c 100644 --- a/bits/85_api.js +++ b/bits/85_api.js @@ -8,13 +8,13 @@ function cfb_add(cfb/*:CFBContainer*/, name/*:string*/, content/*:?RawBytes*/, o init_cfb(cfb); var file = CFB.find(cfb, name); if(!file) { - var fpath = cfb.FullPaths[0]; + var fpath/*:string*/ = cfb.FullPaths[0]; if(name.slice(0, fpath.length) == fpath) fpath = name; else { if(fpath.slice(-1) != "/") fpath += "/"; fpath = (fpath + name).replace("//","/"); } - file = ({name: filename(name)}/*:any*/); + file = ({name: filename(name), type: 2}/*:any*/); cfb.FileIndex.push(file); cfb.FullPaths.push(fpath); CFB.utils.cfb_gc(cfb); diff --git a/cfb.flow.js b/cfb.flow.js index 317aa94..1f01792 100644 --- a/cfb.flow.js +++ b/cfb.flow.js @@ -179,7 +179,7 @@ type CFBFiles = {[n:string]:CFBEntry}; /* [MS-CFB] v20130118 */ var CFB = (function _CFB(){ var exports/*:CFBModule*/ = /*::(*/{}/*:: :any)*/; -exports.version = '0.13.1'; +exports.version = '0.13.2'; /* [MS-CFB] 2.6.4 */ function namecmp(l/*:string*/, r/*:string*/)/*:number*/ { var L = l.split("/"), R = r.split("/"); @@ -200,6 +200,8 @@ function filename(p/*:string*/)/*:string*/ { var c = p.lastIndexOf("/"); 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*/ { var mver = 3; 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); } -var fs/*:: = require('fs'); */; function read_file(filename/*:string*/, options/*:CFBReadOpts*/) { - if(fs == null) fs = require('fs'); + get_fs(); return parse(fs.readFileSync(filename), options); } @@ -779,6 +780,7 @@ var consts = { }; function write_file(cfb/*:CFBContainer*/, filename/*:string*/, options/*:CFBWriteOpts*/)/*:void*/ { + get_fs(); var o = _write(cfb, options); /*:: if(typeof Buffer == 'undefined' || !Buffer.isBuffer(o) || !(o instanceof Buffer)) throw new Error("unreachable"); */ fs.writeFileSync(filename, o); @@ -793,7 +795,7 @@ function a2s(o/*:RawBytes*/)/*:string*/ { function write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|string*/ { var o = _write(cfb, options); 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 "base64": return Base64.encode(a2s(o)); } @@ -809,13 +811,13 @@ function cfb_add(cfb/*:CFBContainer*/, name/*:string*/, content/*:?RawBytes*/, o init_cfb(cfb); var file = CFB.find(cfb, name); if(!file) { - var fpath = cfb.FullPaths[0]; + var fpath/*:string*/ = cfb.FullPaths[0]; if(name.slice(0, fpath.length) == fpath) fpath = name; else { if(fpath.slice(-1) != "/") fpath += "/"; fpath = (fpath + name).replace("//","/"); } - file = ({name: filename(name)}/*:any*/); + file = ({name: filename(name), type: 2}/*:any*/); cfb.FileIndex.push(file); cfb.FullPaths.push(fpath); CFB.utils.cfb_gc(cfb); diff --git a/cfb.js b/cfb.js index f292341..393e7df 100644 --- a/cfb.js +++ b/cfb.js @@ -161,7 +161,7 @@ function new_buf(sz) { /* [MS-CFB] v20130118 */ var CFB = (function _CFB(){ var exports = {}; -exports.version = '0.13.1'; +exports.version = '0.13.2'; /* [MS-CFB] 2.6.4 */ function namecmp(l, r) { var L = l.split("/"), R = r.split("/"); @@ -182,6 +182,8 @@ function filename(p) { var c = p.lastIndexOf("/"); return (c === -1) ? p : p.slice(c+1); } +var fs; +function get_fs() { return fs || (fs = require('fs')); } function parse(file, options) { var mver = 3; 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); } -var fs; function read_file(filename, options) { - if(fs == null) fs = require('fs'); + get_fs(); return parse(fs.readFileSync(filename), options); } @@ -756,6 +757,7 @@ var consts = { }; function write_file(cfb, filename, options) { + get_fs(); var o = _write(cfb, options); fs.writeFileSync(filename, o); } @@ -769,7 +771,7 @@ function a2s(o) { function write(cfb, options) { var o = _write(cfb, options); 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 "base64": return Base64.encode(a2s(o)); } @@ -791,7 +793,7 @@ function cfb_add(cfb, name, content, opts) { if(fpath.slice(-1) != "/") fpath += "/"; fpath = (fpath + name).replace("//","/"); } - file = ({name: filename(name)}); + file = ({name: filename(name), type: 2}); cfb.FileIndex.push(file); cfb.FullPaths.push(fpath); CFB.utils.cfb_gc(cfb); diff --git a/dist/cfb.js b/dist/cfb.js index f292341..393e7df 100644 --- a/dist/cfb.js +++ b/dist/cfb.js @@ -161,7 +161,7 @@ function new_buf(sz) { /* [MS-CFB] v20130118 */ var CFB = (function _CFB(){ var exports = {}; -exports.version = '0.13.1'; +exports.version = '0.13.2'; /* [MS-CFB] 2.6.4 */ function namecmp(l, r) { var L = l.split("/"), R = r.split("/"); @@ -182,6 +182,8 @@ function filename(p) { var c = p.lastIndexOf("/"); return (c === -1) ? p : p.slice(c+1); } +var fs; +function get_fs() { return fs || (fs = require('fs')); } function parse(file, options) { var mver = 3; 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); } -var fs; function read_file(filename, options) { - if(fs == null) fs = require('fs'); + get_fs(); return parse(fs.readFileSync(filename), options); } @@ -756,6 +757,7 @@ var consts = { }; function write_file(cfb, filename, options) { + get_fs(); var o = _write(cfb, options); fs.writeFileSync(filename, o); } @@ -769,7 +771,7 @@ function a2s(o) { function write(cfb, options) { var o = _write(cfb, options); 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 "base64": return Base64.encode(a2s(o)); } @@ -791,7 +793,7 @@ function cfb_add(cfb, name, content, opts) { if(fpath.slice(-1) != "/") fpath += "/"; fpath = (fpath + name).replace("//","/"); } - file = ({name: filename(name)}); + file = ({name: filename(name), type: 2}); cfb.FileIndex.push(file); cfb.FullPaths.push(fpath); CFB.utils.cfb_gc(cfb); diff --git a/dist/cfb.min.js b/dist/cfb.min.js index 005ea73..a5c553d 100644 --- a/dist/cfb.min.js +++ b/dist/cfb.min.js @@ -1,2 +1,2 @@ /* cfb.js (C) 2013-present SheetJS -- http://sheetjs.com */ -var Base64=function e(){var e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";return{encode:function(r){var t="";var i,n,a;var f,s,h,l;for(var o=0;o>2;n=r.charCodeAt(o++);s=(i&3)<<4|n>>4;a=r.charCodeAt(o++);h=(n&15)<<2|a>>6;l=a&63;if(isNaN(n)){h=l=64}else if(isNaN(a)){l=64}t+=e.charAt(f)+e.charAt(s)+e.charAt(h)+e.charAt(l)}return t},decode:function r(t){var i="";var n,a,f;var s,h,l,o;t=t.replace(/[^\w\+\/\=]/g,"");for(var c=0;c>4;i+=String.fromCharCode(n);l=e.indexOf(t.charAt(c++));a=(h&15)<<4|l>>2;if(l!==64){i+=String.fromCharCode(a)}o=e.indexOf(t.charAt(c++));f=(l&3)<<6|o;if(o!==64){i+=String.fromCharCode(f)}}return i}}}();var has_buf=typeof Buffer!=="undefined"&&typeof process!=="undefined"&&typeof process.versions!=="undefined"&&process.versions.node;function new_raw_buf(e){return new(has_buf?Buffer:Array)(e)}var s2a=function r(e){if(has_buf)return new Buffer(e,"binary");return e.split("").map(function(e){return e.charCodeAt(0)&255})};var chr0=/\u0000/g,chr1=/[\u0001-\u0006]/;var __toBuffer=function(e){var r=[];for(var t=0;t0&&Buffer.isBuffer(e[0][0])?Buffer.concat(e[0]):___toBuffer(e)};s2a=function(e){return new Buffer(e,"binary")};bconcat=function(e){return Buffer.isBuffer(e[0])?Buffer.concat(e):__bconcat(e)}}var __readUInt8=function(e,r){return e[r]};var __readUInt16LE=function(e,r){return e[r+1]*(1<<8)+e[r]};var __readInt16LE=function(e,r){var t=e[r+1]*(1<<8)+e[r];return t<32768?t:(65535-t+1)*-1};var __readUInt32LE=function(e,r){return e[r+3]*(1<<24)+(e[r+2]<<16)+(e[r+1]<<8)+e[r]};var __readInt32LE=function(e,r){return(e[r+3]<<24)+(e[r+2]<<16)+(e[r+1]<<8)+e[r]};function ReadShift(e,r){var t,i,n=0;switch(e){case 1:t=__readUInt8(this,this.l);break;case 2:t=(r!=="i"?__readUInt16LE:__readInt16LE)(this,this.l);break;case 4:t=__readInt32LE(this,this.l);break;case 16:n=2;i=__hexlify(this,this.l,e);}this.l+=e;if(n===0)return t;return i}var __writeUInt32LE=function(e,r,t){e[t]=r&255;e[t+1]=r>>>8&255;e[t+2]=r>>>16&255;e[t+3]=r>>>24&255};var __writeInt32LE=function(e,r,t){e[t]=r&255;e[t+1]=r>>8&255;e[t+2]=r>>16&255;e[t+3]=r>>24&255};function WriteShift(e,r,t){var i=0,n=0;switch(t){case"hex":for(;n>8}while(this.l>>=8;this[this.l+1]=r&255;break;case 4:i=4;__writeUInt32LE(this,r,this.l);break;case-4:i=4;__writeInt32LE(this,r,this.l);break;}this.l+=i;return this}function CheckField(e,r){var t=__hexlify(this,this.l,e.length>>1);if(t!==e)throw new Error(r+"Expected "+e+" saw "+t);this.l+=e.length>>1}function prep_blob(e,r){e.l=r;e.read_shift=ReadShift;e.chk=CheckField;e.write_shift=WriteShift}function new_buf(e){var r=new_raw_buf(e);prep_blob(r,0);return r}var CFB=function t(){var e={};e.version="0.13.1";function r(e,r){var t=e.split("/"),i=r.split("/");for(var n=0,a=0,f=Math.min(t.length,i.length);n0&&d!==E)y[d].name="!MiniFAT";y[w[0]].name="!FAT";y.fat_addrs=w;y.ssz=i;var S={},A=[],m=[],B=[],k={};u(_,y,C,A,n,S,m);h(m,k,B,A);A.shift();var L={FileIndex:m,FullPaths:B,FullPathDir:k};if(r&&r.raw)L.raw={header:g,sectors:C};return L}function a(e){e.chk(C,"Header Signature: ");e.chk(S,"CLSID: ");var r=e.read_shift(2,"u");return[e.read_shift(2,"u"),r]}function f(e,r){var t=9;e.l+=2;switch(t=e.read_shift(2)){case 9:if(r!=3)throw new Error("Sector Shift: Expected 9 saw "+t);break;case 12:if(r!=4)throw new Error("Sector Shift: Expected 12 saw "+t);break;default:throw new Error("Sector Shift: Expected 9 or 12 saw "+t);}e.chk("0600","Mini Sector Shift: ");e.chk("000000000000","Reserved: ")}function s(e,r){var t=Math.ceil(e.length/r)-1;var i=[];for(var n=1;n>>2)-1;if(!f)return;for(var h=0;h=0;){n[h]=true;a[a.length]=h;f.push(e[h]);var o=t[Math.floor(h*4/i)];l=h*4&s;if(i<4+l)throw new Error("FAT boundary crossed: "+h+" 4 "+i);if(!e[o])break;h=__readInt32LE(e[o],l)}return{nodes:a,data:__toBuffer([f])}}function c(e,r,t,i){var n=e.length,a=[];var f=[],s=[],h=[];var l=i-1,o=0,c=0,u=0,_=0;for(o=0;o=n)u-=n;if(f[u])continue;h=[];for(c=u;c>=0;){f[c]=true;s[s.length]=c;h.push(e[c]);var d=t[Math.floor(c*4/i)];_=c*4&l;if(i<4+_)throw new Error("FAT boundary crossed: "+c+" 4 "+i);if(!e[d])break;c=__readInt32LE(e[d],_)}a[u]={nodes:s,data:__toBuffer([h])}}return a}function u(e,r,t,i,n,a,f){var s=0,h=i.length?2:0;var l=r[e].data;var c=0,u=0,d;for(;c0&&s!==E)r[s].name="!StreamData"}else if(w.size>=4096){w.storage="fat";if(r[w.start]===undefined)r[w.start]=o(t,w.start,r.fat_addrs,r.ssz);r[w.start].name=w.name;w.content=r[w.start].data.slice(0,w.size);prep_blob(w.content,0)}else{w.storage="minifat";if(s!==E&&w.start!==E&&r[s]){w.content=r[s].data.slice(w.start*x,w.start*x+w.size);prep_blob(w.content,0)}}a[d]=w;f.push(w)}}function _(e,r){return new Date((__readUInt32LE(e,r+4)/1e7*Math.pow(2,32)+__readUInt32LE(e,r)/1e7-11644473600)*1e3)}var d;function v(e,r){if(d==null)d=require("fs");return n(d.readFileSync(e),r)}function w(e,r){switch(r&&r.type||"base64"){case"file":return v(e,r);case"base64":return n(s2a(Base64.decode(e)),r);case"binary":return n(s2a(e),r);}return n(e,r)}function p(e,r){var t=r||{},i=t.root||"Root Entry";if(!e.FullPaths)e.FullPaths=[];if(!e.FileIndex)e.FileIndex=[];if(e.FullPaths.length!==e.FileIndex.length)throw new Error("inconsistent CFB structure");if(e.FullPaths.length===0){e.FullPaths[0]=i+"/";e.FileIndex[0]={name:i,type:5}}if(t.CLSID)e.FileIndex[0].clsid=t.CLSID;F(e)}function F(e){var r="Sh33tJ5";if(CFB.find(e,"/"+r))return;var t=new_buf(4);t[0]=55;t[1]=t[3]=50;t[2]=54;e.FileIndex.push({name:r,type:2,content:t,size:4,L:69,R:69,C:69});e.FullPaths.push(e.FullPaths[0]+r);g(e)}function g(e,n){p(e);var a=false,f=false;for(var s=e.FullPaths.length-1;s>=0;--s){var h=e.FileIndex[s];switch(h.type){case 0:if(f)a=true;else{e.FileIndex.pop();e.FullPaths.pop()}break;case 1:;case 2:;case 5:f=true;if(isNaN(h.R*h.L*h.C))a=true;if(h.R>-1&&h.L>-1&&h.R==h.L)a=true;break;default:a=true;break;}}if(!a&&!n)return;var l=new Date(1987,1,19),o=0;var c=[];for(s=0;s1?1:-1;_.size=0;_.type=5}else if(d.slice(-1)=="/"){for(o=s+1;o=c.length?-1:o;for(o=s+1;o=c.length?-1:o;_.type=1}else{if(t(e.FullPaths[s+1]||"")==t(d))_.R=s+1;_.type=2}}}function I(e,r){g(e);var t=function(e){var r=0,t=0;for(var i=0;i>6;else t+=a+511>>9}var f=e.FullPaths.length+3>>2;var s=r+7>>3;var h=r+127>>7;var l=s+t+f+h;var o=l+127>>7;var c=o<=109?0:Math.ceil((o-109)/127);while(l+o+c+127>>7>o)c=++o<=109?0:Math.ceil((o-109)/127);var u=[1,c,o,h,f,t,r,0];e.FileIndex[0].size=r<<6;u[7]=(e.FileIndex[0].start=u[0]+u[1]+u[2]+u[3]+u[4]+u[5])+(u[6]+7>>3);return u}(e);var i=new_buf(t[7]<<9);var n=0,a=0;{for(n=0;n<8;++n)i.write_shift(1,y[n]);for(n=0;n<8;++n)i.write_shift(2,0);i.write_shift(2,62);i.write_shift(2,3);i.write_shift(2,65534);i.write_shift(2,9);i.write_shift(2,6);for(n=0;n<3;++n)i.write_shift(2,0);i.write_shift(4,0);i.write_shift(4,t[2]);i.write_shift(4,t[0]+t[1]+t[2]+t[3]-1);i.write_shift(4,0);i.write_shift(4,1<<12);i.write_shift(4,t[3]?t[0]+t[1]+t[2]-1:E);i.write_shift(4,t[3]);i.write_shift(-4,t[1]?t[0]-1:E);i.write_shift(4,t[1]);for(n=0;n<109;++n)i.write_shift(-4,n>9)}f(t[6]+7>>3);while(i.l&511)i.write_shift(-4,A.ENDOFCHAIN);a=n=0;for(s=0;s=4096)continue;l.start=a;f(h+63>>6)}while(i.l&511)i.write_shift(-4,A.ENDOFCHAIN);for(n=0;n=4096){i.l=l.start+1<<9;for(s=0;s0&&l.size<4096){for(s=0;s>2;n=r.charCodeAt(o++);s=(i&3)<<4|n>>4;a=r.charCodeAt(o++);h=(n&15)<<2|a>>6;l=a&63;if(isNaN(n)){h=l=64}else if(isNaN(a)){l=64}t+=e.charAt(f)+e.charAt(s)+e.charAt(h)+e.charAt(l)}return t},decode:function r(t){var i="";var n,a,f;var s,h,l,o;t=t.replace(/[^\w\+\/\=]/g,"");for(var c=0;c>4;i+=String.fromCharCode(n);l=e.indexOf(t.charAt(c++));a=(h&15)<<4|l>>2;if(l!==64){i+=String.fromCharCode(a)}o=e.indexOf(t.charAt(c++));f=(l&3)<<6|o;if(o!==64){i+=String.fromCharCode(f)}}return i}}}();var has_buf=typeof Buffer!=="undefined"&&typeof process!=="undefined"&&typeof process.versions!=="undefined"&&process.versions.node;function new_raw_buf(e){return new(has_buf?Buffer:Array)(e)}var s2a=function r(e){if(has_buf)return new Buffer(e,"binary");return e.split("").map(function(e){return e.charCodeAt(0)&255})};var chr0=/\u0000/g,chr1=/[\u0001-\u0006]/;var __toBuffer=function(e){var r=[];for(var t=0;t0&&Buffer.isBuffer(e[0][0])?Buffer.concat(e[0]):___toBuffer(e)};s2a=function(e){return new Buffer(e,"binary")};bconcat=function(e){return Buffer.isBuffer(e[0])?Buffer.concat(e):__bconcat(e)}}var __readUInt8=function(e,r){return e[r]};var __readUInt16LE=function(e,r){return e[r+1]*(1<<8)+e[r]};var __readInt16LE=function(e,r){var t=e[r+1]*(1<<8)+e[r];return t<32768?t:(65535-t+1)*-1};var __readUInt32LE=function(e,r){return e[r+3]*(1<<24)+(e[r+2]<<16)+(e[r+1]<<8)+e[r]};var __readInt32LE=function(e,r){return(e[r+3]<<24)+(e[r+2]<<16)+(e[r+1]<<8)+e[r]};function ReadShift(e,r){var t,i,n=0;switch(e){case 1:t=__readUInt8(this,this.l);break;case 2:t=(r!=="i"?__readUInt16LE:__readInt16LE)(this,this.l);break;case 4:t=__readInt32LE(this,this.l);break;case 16:n=2;i=__hexlify(this,this.l,e);}this.l+=e;if(n===0)return t;return i}var __writeUInt32LE=function(e,r,t){e[t]=r&255;e[t+1]=r>>>8&255;e[t+2]=r>>>16&255;e[t+3]=r>>>24&255};var __writeInt32LE=function(e,r,t){e[t]=r&255;e[t+1]=r>>8&255;e[t+2]=r>>16&255;e[t+3]=r>>24&255};function WriteShift(e,r,t){var i=0,n=0;switch(t){case"hex":for(;n>8}while(this.l>>=8;this[this.l+1]=r&255;break;case 4:i=4;__writeUInt32LE(this,r,this.l);break;case-4:i=4;__writeInt32LE(this,r,this.l);break;}this.l+=i;return this}function CheckField(e,r){var t=__hexlify(this,this.l,e.length>>1);if(t!==e)throw new Error(r+"Expected "+e+" saw "+t);this.l+=e.length>>1}function prep_blob(e,r){e.l=r;e.read_shift=ReadShift;e.chk=CheckField;e.write_shift=WriteShift}function new_buf(e){var r=new_raw_buf(e);prep_blob(r,0);return r}var CFB=function t(){var e={};e.version="0.13.2";function r(e,r){var t=e.split("/"),i=r.split("/");for(var n=0,a=0,f=Math.min(t.length,i.length);n0&&u!==C)y[u].name="!MiniFAT";y[w[0]].name="!FAT";y.fat_addrs=w;y.ssz=i;var S={},A=[],m=[],B=[],k={};d(f,y,E,A,n,S,m);o(m,k,B,A);A.shift();var L={FileIndex:m,FullPaths:B,FullPathDir:k};if(r&&r.raw)L.raw={header:g,sectors:E};return L}function s(e){e.chk(y,"Header Signature: ");e.chk(A,"CLSID: ");var r=e.read_shift(2,"u");return[e.read_shift(2,"u"),r]}function h(e,r){var t=9;e.l+=2;switch(t=e.read_shift(2)){case 9:if(r!=3)throw new Error("Sector Shift: Expected 9 saw "+t);break;case 12:if(r!=4)throw new Error("Sector Shift: Expected 12 saw "+t);break;default:throw new Error("Sector Shift: Expected 9 or 12 saw "+t);}e.chk("0600","Mini Sector Shift: ");e.chk("000000000000","Reserved: ")}function l(e,r){var t=Math.ceil(e.length/r)-1;var i=[];for(var n=1;n>>2)-1;if(!f)return;for(var h=0;h=0;){n[h]=true;a[a.length]=h;f.push(e[h]);var o=t[Math.floor(h*4/i)];l=h*4&s;if(i<4+l)throw new Error("FAT boundary crossed: "+h+" 4 "+i);if(!e[o])break;h=__readInt32LE(e[o],l)}return{nodes:a,data:__toBuffer([f])}}function _(e,r,t,i){var n=e.length,a=[];var f=[],s=[],h=[];var l=i-1,o=0,c=0,u=0,_=0;for(o=0;o=n)u-=n;if(f[u])continue;h=[];for(c=u;c>=0;){f[c]=true;s[s.length]=c;h.push(e[c]);var d=t[Math.floor(c*4/i)];_=c*4&l;if(i<4+_)throw new Error("FAT boundary crossed: "+c+" 4 "+i);if(!e[d])break;c=__readInt32LE(e[d],_)}a[u]={nodes:s,data:__toBuffer([h])}}return a}function d(e,r,t,i,n,a,f){var s=0,h=i.length?2:0;var l=r[e].data;var o=0,c=0,_;for(;o0&&s!==C)r[s].name="!StreamData"}else if(w.size>=4096){w.storage="fat";if(r[w.start]===undefined)r[w.start]=u(t,w.start,r.fat_addrs,r.ssz);r[w.start].name=w.name;w.content=r[w.start].data.slice(0,w.size);prep_blob(w.content,0)}else{w.storage="minifat";if(s!==C&&w.start!==C&&r[s]){w.content=r[s].data.slice(w.start*E,w.start*E+w.size);prep_blob(w.content,0)}}a[_]=w;f.push(w)}}function v(e,r){return new Date((__readUInt32LE(e,r+4)/1e7*Math.pow(2,32)+__readUInt32LE(e,r)/1e7-11644473600)*1e3)}function w(e,r){a();return f(n.readFileSync(e),r)}function p(e,r){switch(r&&r.type||"base64"){case"file":return w(e,r);case"base64":return f(s2a(Base64.decode(e)),r);case"binary":return f(s2a(e),r);}return f(e,r)}function F(e,r){var t=r||{},i=t.root||"Root Entry";if(!e.FullPaths)e.FullPaths=[];if(!e.FileIndex)e.FileIndex=[];if(e.FullPaths.length!==e.FileIndex.length)throw new Error("inconsistent CFB structure");if(e.FullPaths.length===0){e.FullPaths[0]=i+"/";e.FileIndex[0]={name:i,type:5}}if(t.CLSID)e.FileIndex[0].clsid=t.CLSID;g(e)}function g(e){var r="Sh33tJ5";if(CFB.find(e,"/"+r))return;var t=new_buf(4);t[0]=55;t[1]=t[3]=50;t[2]=54;e.FileIndex.push({name:r,type:2,content:t,size:4,L:69,R:69,C:69});e.FullPaths.push(e.FullPaths[0]+r);I(e)}function I(e,n){F(e);var a=false,f=false;for(var s=e.FullPaths.length-1;s>=0;--s){var h=e.FileIndex[s];switch(h.type){case 0:if(f)a=true;else{e.FileIndex.pop();e.FullPaths.pop()}break;case 1:;case 2:;case 5:f=true;if(isNaN(h.R*h.L*h.C))a=true;if(h.R>-1&&h.L>-1&&h.R==h.L)a=true;break;default:a=true;break;}}if(!a&&!n)return;var l=new Date(1987,1,19),o=0;var c=[];for(s=0;s1?1:-1;_.size=0;_.type=5}else if(d.slice(-1)=="/"){for(o=s+1;o=c.length?-1:o;for(o=s+1;o=c.length?-1:o;_.type=1}else{if(t(e.FullPaths[s+1]||"")==t(d))_.R=s+1;_.type=2}}}function b(e,r){I(e);var t=function(e){var r=0,t=0;for(var i=0;i>6;else t+=a+511>>9}var f=e.FullPaths.length+3>>2;var s=r+7>>3;var h=r+127>>7;var l=s+t+f+h;var o=l+127>>7;var c=o<=109?0:Math.ceil((o-109)/127);while(l+o+c+127>>7>o)c=++o<=109?0:Math.ceil((o-109)/127);var u=[1,c,o,h,f,t,r,0];e.FileIndex[0].size=r<<6;u[7]=(e.FileIndex[0].start=u[0]+u[1]+u[2]+u[3]+u[4]+u[5])+(u[6]+7>>3);return u}(e);var i=new_buf(t[7]<<9);var n=0,a=0;{for(n=0;n<8;++n)i.write_shift(1,S[n]);for(n=0;n<8;++n)i.write_shift(2,0);i.write_shift(2,62);i.write_shift(2,3);i.write_shift(2,65534);i.write_shift(2,9);i.write_shift(2,6);for(n=0;n<3;++n)i.write_shift(2,0);i.write_shift(4,0);i.write_shift(4,t[2]);i.write_shift(4,t[0]+t[1]+t[2]+t[3]-1);i.write_shift(4,0);i.write_shift(4,1<<12);i.write_shift(4,t[3]?t[0]+t[1]+t[2]-1:C);i.write_shift(4,t[3]);i.write_shift(-4,t[1]?t[0]-1:C);i.write_shift(4,t[1]);for(n=0;n<109;++n)i.write_shift(-4,n>9)}f(t[6]+7>>3);while(i.l&511)i.write_shift(-4,m.ENDOFCHAIN);a=n=0;for(s=0;s=4096)continue;l.start=a;f(h+63>>6)}while(i.l&511)i.write_shift(-4,m.ENDOFCHAIN);for(n=0;n=4096){i.l=l.start+1<<9;for(s=0;s0&&l.size<4096){for(s=0;sAdvanced Demo Options: Use readAsBinaryString: (when available) -Export loaded data +Export data

 
@@ -61,14 +61,14 @@ var get_manifest = (function() { var cnt = 0, rootsize = 0, filesize = 0; out.push(" Length Date Time Name"); out.push(" -------- ---- ---- ----"); - cfb.FileIndex.forEach(function(file, i/*:number*/) { + cfb.FileIndex.forEach(function(file/*:CFBEntry*/, i/*:number*/) { switch(file.type) { case 5: basetime = file.ct || file.mt || basetime; rootsize = file.size; break; case 2: - out.push(sprintf("%9lu %s %s", file.size, format_date(basetime), fix_string(cfb.FullPaths[i]))); + out.push(sprintf("%9lu %s %s", file.size, format_date(basetime), i, fix_string(cfb.FullPaths[i]))); filesize += file.size; ++cnt; } @@ -82,8 +82,7 @@ var get_manifest = (function() { function process_data(cfb) { global_cfb = cfb; var output = get_manifest(cfb); - if(out.innerText === undefined) out.textContent = output; - else out.innerText = output; + out.innerHTML = output; } var do_file = (function() { @@ -156,6 +155,22 @@ var savefile = (function() { 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); + }; +})();