version bump 0.12.1: CFB.find

- exported object includes static find method
- CLI option displays file manifest
- ignore mid-table free sectors
- ignore lazily-deleted files (orphan nodes)
This commit is contained in:
SheetJS 2017-08-09 02:50:59 -04:00
parent 17f35153f8
commit 2d0c71ac88
19 changed files with 224 additions and 71 deletions

@ -5,7 +5,7 @@ format used in many Microsoft file types (such as XLS and DOC)
# Utility Installation and Usage
The package is available on NPM:
With [npm](https://www.npmjs.org/package/cfb):
```bash
$ npm install -g cfb
@ -14,7 +14,7 @@ $ cfb path/to/CFB/file
The command will extract the storages and streams in the container, generating
files that line up with the tree-based structure of the storage. Metadata such
as the red-black tree are discarded.
as the red-black tree are discarded. The `-l` option displays a manifest.
# Library Installation and Usage
@ -34,9 +34,12 @@ For example, to get the Workbook content from an XLS file:
```js
var cfb = CFB.read(filename, {type: 'file'});
var workbook = cfb.find('Workbook')
var workbook = cfb.find('Workbook');
var data = workbook.content;
```
The `xlscfb.js` file is designed to be embedded in [js-xlsx](http://git.io/xlsx)
# API
Typescript definitions are maintained in `types/index.d.ts`.
@ -52,14 +55,15 @@ parsed representation of the data.
- `base64`: `blob` should be a base64 string
- `binary`: `blob` should be a binary string
`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.
## Container Object Description
The object returned by `parse` and `read` can be found in the source (`rval`).
It has the following properties and methods:
- `.find(path)` performs a case-insensitive match for the path (or file name, if
there are no slashes) and returns an entry object (described later) or null if
not found
- `.find(path)` is equivalent to `CFB.find(cfb, path)` and should not be used.
- `.FullPaths` is an array of the names of all of the streams (files) and
storages (directories) in the container. The paths are properly prefixed from
@ -84,14 +88,6 @@ the container object.
- `.content` is a Buffer/Array with the raw content
- `.ct`/`.mt` are the creation and modification time (if provided in file)
# Notes
Case comparison has not been verified for non-ASCII characters
Writing is not supported. It is in the works, but it has not yet been released.
The `xlscfb.js` file is designed to be embedded in [js-xlsx](http://git.io/xlsx)
# License
This implementation is covered under Apache 2.0 license. It complies with the

@ -1,14 +1,19 @@
#!/usr/bin/env node
var CFB = require('../');
/* cfb.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* eslint-env node */
/* vim: set ts=2 ft=javascript: */
var X = require('../');
var fs = require('fs'), program = require('commander');
program
.version(CFB.version)
.version(X.version)
.usage('[options] <file>')
.option('-q, --quiet', 'process but do not report')
.option('-l, --list-files', 'list files')
.option('-d, --dump', 'dump internal representation but do not extract')
.option('--dev', 'development mode')
.parse(process.argv);
.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] <cfb_file>");
@ -18,14 +23,46 @@ if(program.args.length === 0 || !fs.existsSync(program.args[0])) {
var opts = ({type:'file'}/*:any*/);
if(program.dev) opts.WTF = true;
var cfb = CFB.read(program.args[0], opts);
var cfb = X.read(program.args[0], opts);
if(program.quiet) process.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.quiet && !program.dump) for(var i=0; i!==cfb.FullPaths.length; ++i) {
if(program.listFiles) {
var PRINTJ = require("printj"), sprintf = PRINTJ.sprintf;
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 basetime = new Date(1980,0,1);
var cnt = 0;
var rootsize = 0, filesize = 0;
console.log(" Length Date Time Name");
console.log(" -------- ---- ---- ----");
cfb.FileIndex.forEach(function(file, i) {
switch(file.type) {
case 5:
basetime = file.ct || file.mt || basetime;
rootsize = file.size;
break;
case 2:
console.log(sprintf("%9lu %s %s", file.size, format_date(basetime), cfb.FullPaths[i]));
filesize += file.size;
++cnt;
}
});
console.log(" -------- -------");
console.log(sprintf("%9lu %lu file%s", rootsize || filesize, cnt, (cnt !== 1 ? "s" : "")));
process.exit(0);
}
for(var i=0; i!==cfb.FullPaths.length; ++i) {
if(cfb.FullPaths[i].slice(-1) === "/") {
console.error("mkdir " + cfb.FullPaths[i]);
fs.mkdirSync(cfb.FullPaths[i]);

@ -1 +1 @@
exports.version = '0.12.0';
exports.version = '0.12.1';

@ -25,7 +25,7 @@ function build_full_paths(FI/*:CFBFileIndex*/, FPD/*:CFBFullPathDir*/, FP/*:Arra
if(FI[i].type === 0 /* unknown */) continue;
j = dad[i];
if(j === 0) FP[i] = FP[0] + "/" + FP[i];
else while(j !== 0) {
else while(j !== 0 && j !== dad[j]) {
FP[i] = FP[j] + "/" + FP[i];
j = dad[j];
}

@ -7,7 +7,6 @@ function read_directory(dir_start/*:number*/, sector_list/*:SectorList*/, sector
var blob/*:CFBlob*/ = /*::(*/sector.slice(i, i+128)/*:: :any)*/;
prep_blob(blob, 64);
namelen = blob.read_shift(2);
if(namelen === 0) continue;
name = __utf16le(blob,0,namelen-pl);
Paths.push(name);
var o/*:CFBEntry*/ = ({
@ -28,6 +27,7 @@ function read_directory(dir_start/*:number*/, sector_list/*:SectorList*/, sector
if(mtime !== 0) o.mt = read_date(blob, blob.l-8);
o.start = blob.read_shift(4, 'i');
o.size = blob.read_shift(4, 'i');
if(o.size < 0 && o.start < 0) { o.size = o.type = 0; o.start = ENDOFCHAIN; o.name = ""; }
if(o.type === 5) { /* root */
minifat_store = o.start;
if(nmfs > 0 && minifat_store !== ENDOFCHAIN) sector_list[minifat_store].name = "!StreamData";
@ -40,7 +40,7 @@ function read_directory(dir_start/*:number*/, sector_list/*:SectorList*/, sector
prep_blob(o.content, 0);
} else {
o.storage = 'minifat';
if(minifat_store !== ENDOFCHAIN && o.start !== ENDOFCHAIN) {
if(minifat_store !== ENDOFCHAIN && o.start !== ENDOFCHAIN && sector_list[minifat_store]) {
o.content = (sector_list[minifat_store].data.slice(o.start*MSSZ,o.start*MSSZ+o.size)/*:any*/);
prep_blob(o.content, 0);
}

3
bits/70_find.js Normal file

@ -0,0 +1,3 @@
function find(cfb/*:CFBContainer*/, path/*:string*/)/*:?CFBEntry*/ {
return cfb.find(path);
}

@ -1,3 +1,4 @@
exports.find = find;
exports.read = readSync;
exports.parse = parse;
exports.utils = {

@ -109,7 +109,7 @@ type CFBFiles = {[n:string]:CFBEntry};
/* [MS-CFB] v20130118 */
var CFB = (function _CFB(){
var exports/*:CFBModule*/ = /*::(*/{}/*:: :any)*/;
exports.version = '0.12.0';
exports.version = '0.12.1';
function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
var mver = 3; // major version
var ssz = 512; // sector size
@ -280,7 +280,7 @@ function build_full_paths(FI/*:CFBFileIndex*/, FPD/*:CFBFullPathDir*/, FP/*:Arra
if(FI[i].type === 0 /* unknown */) continue;
j = dad[i];
if(j === 0) FP[i] = FP[0] + "/" + FP[i];
else while(j !== 0) {
else while(j !== 0 && j !== dad[j]) {
FP[i] = FP[j] + "/" + FP[i];
j = dad[j];
}
@ -381,7 +381,6 @@ function read_directory(dir_start/*:number*/, sector_list/*:SectorList*/, sector
var blob/*:CFBlob*/ = /*::(*/sector.slice(i, i+128)/*:: :any)*/;
prep_blob(blob, 64);
namelen = blob.read_shift(2);
if(namelen === 0) continue;
name = __utf16le(blob,0,namelen-pl);
Paths.push(name);
var o/*:CFBEntry*/ = ({
@ -402,6 +401,7 @@ function read_directory(dir_start/*:number*/, sector_list/*:SectorList*/, sector
if(mtime !== 0) o.mt = read_date(blob, blob.l-8);
o.start = blob.read_shift(4, 'i');
o.size = blob.read_shift(4, 'i');
if(o.size < 0 && o.start < 0) { o.size = o.type = 0; o.start = ENDOFCHAIN; o.name = ""; }
if(o.type === 5) { /* root */
minifat_store = o.start;
if(nmfs > 0 && minifat_store !== ENDOFCHAIN) sector_list[minifat_store].name = "!StreamData";
@ -414,7 +414,7 @@ function read_directory(dir_start/*:number*/, sector_list/*:SectorList*/, sector
prep_blob(o.content, 0);
} else {
o.storage = 'minifat';
if(minifat_store !== ENDOFCHAIN && o.start !== ENDOFCHAIN) {
if(minifat_store !== ENDOFCHAIN && o.start !== ENDOFCHAIN && sector_list[minifat_store]) {
o.content = (sector_list[minifat_store].data.slice(o.start*MSSZ,o.start*MSSZ+o.size)/*:any*/);
prep_blob(o.content, 0);
}
@ -443,6 +443,9 @@ function readSync(blob/*:RawBytes|string*/, options/*:CFBReadOpts*/) {
return parse(/*::typeof blob == 'string' ? new Buffer(blob, 'utf-8') : */blob, options);
}
function find(cfb/*:CFBContainer*/, path/*:string*/)/*:?CFBEntry*/ {
return cfb.find(path);
}
/** CFB Constants */
var MSSZ = 64; /* Mini Sector Size = 1<<6 */
//var MSCSZ = 4096; /* Mini Stream Cutoff Size */
@ -468,6 +471,7 @@ var consts = {
EntryTypes: ['unknown','storage','stream','lockbytes','property','root']
};
exports.find = find;
exports.read = readSync;
exports.parse = parse;
exports.utils = {

12
cfb.js

@ -94,7 +94,7 @@ function prep_blob(blob, pos) {
/* [MS-CFB] v20130118 */
var CFB = (function _CFB(){
var exports = {};
exports.version = '0.12.0';
exports.version = '0.12.1';
function parse(file, options) {
var mver = 3; // major version
var ssz = 512; // sector size
@ -265,7 +265,7 @@ function build_full_paths(FI, FPD, FP, Paths) {
if(FI[i].type === 0 /* unknown */) continue;
j = dad[i];
if(j === 0) FP[i] = FP[0] + "/" + FP[i];
else while(j !== 0) {
else while(j !== 0 && j !== dad[j]) {
FP[i] = FP[j] + "/" + FP[i];
j = dad[j];
}
@ -366,7 +366,6 @@ function read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, Fil
var blob = sector.slice(i, i+128);
prep_blob(blob, 64);
namelen = blob.read_shift(2);
if(namelen === 0) continue;
name = __utf16le(blob,0,namelen-pl);
Paths.push(name);
var o = ({
@ -387,6 +386,7 @@ function read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, Fil
if(mtime !== 0) o.mt = read_date(blob, blob.l-8);
o.start = blob.read_shift(4, 'i');
o.size = blob.read_shift(4, 'i');
if(o.size < 0 && o.start < 0) { o.size = o.type = 0; o.start = ENDOFCHAIN; o.name = ""; }
if(o.type === 5) { /* root */
minifat_store = o.start;
if(nmfs > 0 && minifat_store !== ENDOFCHAIN) sector_list[minifat_store].name = "!StreamData";
@ -399,7 +399,7 @@ function read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, Fil
prep_blob(o.content, 0);
} else {
o.storage = 'minifat';
if(minifat_store !== ENDOFCHAIN && o.start !== ENDOFCHAIN) {
if(minifat_store !== ENDOFCHAIN && o.start !== ENDOFCHAIN && sector_list[minifat_store]) {
o.content = (sector_list[minifat_store].data.slice(o.start*MSSZ,o.start*MSSZ+o.size));
prep_blob(o.content, 0);
}
@ -428,6 +428,9 @@ function readSync(blob, options) {
return parse(blob, options);
}
function find(cfb, path) {
return cfb.find(path);
}
/** CFB Constants */
var MSSZ = 64; /* Mini Sector Size = 1<<6 */
//var MSCSZ = 4096; /* Mini Stream Cutoff Size */
@ -453,6 +456,7 @@ var consts = {
EntryTypes: ['unknown','storage','stream','lockbytes','property','root']
};
exports.find = find;
exports.read = readSync;
exports.parse = parse;
exports.utils = {

@ -5,18 +5,3 @@ xlrd_biff4_no_format_no_window2.xls
roo_type_excelx.xls
roo_type_openoffice.xls
libreoffice_calc_csv-import_malformed-quotes.xls
ootest_cellformat_import_biff2.xls
ootest_cellformat_import_biff3.xls
ootest_cells_import_biff2.xls
ootest_cells_import_biff3.xls
ootest_cells_import_biff4.xls
ootest_drawing_import_biff3.xls
ootest_externallink_import_biff2.xls
ootest_externallink_import_biff3.xls
ootest_formula_import_biff2.xls
ootest_formula_import_biff3.xls
ootest_oleobject_import_biff3.xls
ootest_oleobject_import_biff8_12.xls
ootest_sheettypes_import_biff8_12.xls
ootest_writeprotection_import_biff3.xls
ootest_writeprotection_import_biff4.xls

@ -18,19 +18,28 @@
#b64data{
width:100%;
}
a { text-decoration: none }
</style>
</head>
<body>
<b>JS-CFB Live Demo</b><br />
<pre>
<b><a href="http://sheetjs.com">SheetJS CFB Preview Live Demo</a></b>
<a href="https://github.com/SheetJS/js-cfb">Source Code Repo</a>
<a href="https://github.com/SheetJS/js-cfb/issues">Issues? Something look weird? Click here and report an issue</a>
<div id="drop">Drop an XLS file here to see the CFB structure.</div>
Advanced Demo Options: <br />
Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" checked><br />
<b>Advanced Demo Options:</b>
Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" checked>
</pre>
<pre id="out"></pre>
<br />
<script src="https://unpkg.com/printj@1.1.0/dist/printj.min.js"></script>
<script src="cfb.js"></script>
<script>
/*jshint browser:true */
/* eslint-env browser */
/* eslint no-use-before-define:0 */
/*global CFB, out */
var rABS = typeof FileReader !== "undefined" && typeof FileReader.prototype !== "undefined" && typeof FileReader.prototype.readAsBinaryString !== "undefined";
if(!rABS) {
@ -45,9 +54,41 @@ function fixdata(data) {
return o;
}
function process_data(data) {
if(out.innerText === undefined) out.textContent = data.FullPaths;
else out.innerText = data.FullPaths.join("\n");
function get_manifest(cfb) {
var out = [];
var sprintf = PRINTJ.sprintf;
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 basetime = new Date(1980,0,1);
var cnt = 0;
var rootsize = 0, filesize = 0;
out.push(" Length Date Time Name");
out.push(" -------- ---- ---- ----");
cfb.FileIndex.forEach(function(file, i) {
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), cfb.FullPaths[i]));
filesize += file.size;
++cnt;
}
});
out.push(" -------- -------");
out.push(sprintf("%9lu %lu file%s", rootsize || filesize, cnt, (cnt !== 1 ? "s" : "")));
return out.join("\n");
}
function process_data(cfb) {
var output = get_manifest(cfb);
if(out.innerText === undefined) out.textContent = output
else out.innerText = output;
}
var drop = document.getElementById('drop');
@ -88,5 +129,16 @@ if(drop.addEventListener) {
drop.addEventListener('drop', handleDrop, false);
}
</script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-36810333-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</body>
</html>

@ -2,6 +2,7 @@
type CFBModule = {
version:string;
find:(cfb:CFBContainer, path:string)=>?CFBEntry;
read:(blob:RawBytes|string, opts:CFBReadOpts)=>CFBContainer;
parse:(file:RawBytes, opts:CFBReadOpts)=>CFBContainer;
utils:CFBUtils;

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

@ -1,6 +1,6 @@
{
"name": "cfb",
"version": "0.12.0",
"version": "0.12.1",
"author": "sheetjs",
"description": "Compound File Binary File Format extractor",
"keywords": [ "cfb", "compression", "office" ],
@ -15,6 +15,7 @@
"fs": false
},
"dependencies": {
"printj":"~1.1.0",
"commander":"~2.11.0"
},
"devDependencies": {

21
test.js

@ -19,20 +19,37 @@ var dir = "./test_files/";
function parsetest(x, cfb) {
describe(x + ' should have basic parts', function() {
it('should find relative path', function() {
/* cfb.find interface */
it('should find relative path using cfb#find', function() {
switch(x.substr(-4)) {
case '.xls': if(!cfb.find('Workbook') && !cfb.find('Book')) throw new Error("Cannot find workbook for " + x); break;
case '.ppt': if(!cfb.find('PowerPoint Document')) throw new Error("Cannot find presentation for " + x); break;
case '.doc': if(!cfb.find('WordDocument') && !cfb.find('Word Document')) throw new Error("Cannot find doc for " + x); break;
}
});
it('should find absolute path', function() {
it('should find absolute path using cfb#find', function() {
switch(x.substr(-4)) {
case '.xls': if(!cfb.find('/Workbook') && !cfb.find('/Book')) throw new Error("Cannot find workbook for " + x); break;
case '.ppt': if(!cfb.find('/PowerPoint Document')) throw new Error("Cannot find presentation for " + x); break;
case '.doc': if(!cfb.find('/WordDocument') && !cfb.find('/Word Document')) throw new Error("Cannot find doc for " + x); break;
}
});
/* CFB.find function */
it('should find relative path using CFB.find', function() {
switch(x.substr(-4)) {
case '.xls': if(!CFB.find(cfb, 'Workbook') && !CFB.find(cfb, 'Book')) throw new Error("Cannot find workbook for " + x); break;
case '.ppt': if(!CFB.find(cfb, 'PowerPoint Document')) throw new Error("Cannot find presentation for " + x); break;
case '.doc': if(!CFB.find(cfb, 'WordDocument') && !CFB.find(cfb, 'Word Document')) throw new Error("Cannot find doc for " + x); break;
}
});
it('should find absolute path using CFB.find', function() {
switch(x.substr(-4)) {
case '.xls': if(!CFB.find(cfb, '/Workbook') && !CFB.find(cfb, '/Book')) throw new Error("Cannot find workbook for " + x); break;
case '.ppt': if(!CFB.find(cfb, '/PowerPoint Document')) throw new Error("Cannot find presentation for " + x); break;
case '.doc': if(!CFB.find(cfb, '/WordDocument') && !CFB.find(cfb, '/Word Document')) throw new Error("Cannot find doc for " + x); break;
}
});
});
}

@ -1,13 +1,20 @@
/* vim: set ts=2: */
/* cfb.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* eslint-env node */
/* vim: set ts=2 ft=javascript: */
import * as CFB from 'cfb';
import fs = require('fs');
import program = require('commander');
import PRINTJ = require("printj");
program
.version(CFB.version)
.usage('[options] <file>')
.option('-q, --quiet', 'process but do not report')
.option('-l, --list-files', 'list files')
.option('-d, --dump', 'dump internal representation but do not extract')
.parse(process.argv);
.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] <cfb_file>");
@ -15,16 +22,48 @@ if(program.args.length === 0 || !fs.existsSync(program.args[0])) {
}
const opts: CFB.CFBParsingOptions = {type:'file'};
if(program.dev) opts.WTF = true;
const cfb: CFB.CFBContainer = CFB.read(program.args[0], opts);
if(program.quiet) process.exit(0);
if(program.dump) {
console.log("Full Paths:");
console.log(cfb.FullPaths.map((x) => " " + x).join("\n"));
console.log("Full Path Directory:");
console.log(cfb.FullPathDir);
process.exit(0);
}
if(!program.quiet && !program.dump) for(let i=0; i!==cfb.FullPaths.length; ++i) {
if(program.listFiles) {
const sprintf = PRINTJ.sprintf;
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 cnt = 0;
let rootsize = 0, filesize = 0;
console.log(" Length Date Time Name");
console.log(" -------- ---- ---- ----");
cfb.FileIndex.forEach(function(file: CFB.CFBEntry, i: number) {
switch(file.type) {
case 5:
basetime = file.ct || file.mt || basetime;
rootsize = file.size;
break;
case 2:
console.log(sprintf("%9lu %s %s", file.size, format_date(basetime), cfb.FullPaths[i]));
filesize += file.size;
++cnt;
}
});
console.log(" -------- -------");
console.log(sprintf("%9lu %lu file%s", rootsize || filesize, cnt, (cnt !== 1 ? "s" : "")));
process.exit(0);
}
for(let i=0; i!==cfb.FullPaths.length; ++i) {
if(cfb.FullPaths[i].slice(-1) === "/") {
console.error("mkdir " + cfb.FullPaths[i]);
fs.mkdirSync(cfb.FullPaths[i]);

11
types/index.d.ts vendored

@ -10,6 +10,9 @@ export function parse(f: CFB$Blob, options?: CFBParsingOptions): CFBContainer;
/** Read a blob or file or binary string */
export function read(f: CFB$Blob | string, options?: CFBParsingOptions): CFBContainer;
/** Find a file entry given a path or file name */
export function find(cfb: CFBContainer, path: string): CFBEntry | null;
/** Utility functions */
export const utils: CFB$Utils;
@ -18,6 +21,8 @@ export const utils: CFB$Utils;
export interface CFBParsingOptions {
/** Input data encoding */
type?: 'base64' | 'binary' | 'buffer' | 'file' | 'array';
/** If true, throw errors when features are not understood */
WTF?: boolean;
}
export type CFB$Blob = Buffer | number[] | Uint8Array;
@ -85,9 +90,9 @@ export interface CFBContainer {
/* Raw Content, in chunks (Buffer when available, Array of bytes otherwise) */
raw: {
header: CFB$Blob,
sectors: CFB$Blob[];
};
header: CFB$Blob,
sectors: CFB$Blob[];
};
}
/** General utilities */

@ -35,7 +35,7 @@ type CFBFiles = {[n:string]:CFBEntry};
/* [MS-CFB] v20130118 */
var CFB = (function _CFB(){
var exports/*:CFBModule*/ = /*::(*/{}/*:: :any)*/;
exports.version = '0.12.0';
exports.version = '0.12.1';
function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
var mver = 3; // major version
var ssz = 512; // sector size
@ -206,7 +206,7 @@ function build_full_paths(FI/*:CFBFileIndex*/, FPD/*:CFBFullPathDir*/, FP/*:Arra
if(FI[i].type === 0 /* unknown */) continue;
j = dad[i];
if(j === 0) FP[i] = FP[0] + "/" + FP[i];
else while(j !== 0) {
else while(j !== 0 && j !== dad[j]) {
FP[i] = FP[j] + "/" + FP[i];
j = dad[j];
}
@ -307,7 +307,6 @@ function read_directory(dir_start/*:number*/, sector_list/*:SectorList*/, sector
var blob/*:CFBlob*/ = /*::(*/sector.slice(i, i+128)/*:: :any)*/;
prep_blob(blob, 64);
namelen = blob.read_shift(2);
if(namelen === 0) continue;
name = __utf16le(blob,0,namelen-pl);
Paths.push(name);
var o/*:CFBEntry*/ = ({
@ -328,6 +327,7 @@ function read_directory(dir_start/*:number*/, sector_list/*:SectorList*/, sector
if(mtime !== 0) o.mt = read_date(blob, blob.l-8);
o.start = blob.read_shift(4, 'i');
o.size = blob.read_shift(4, 'i');
if(o.size < 0 && o.start < 0) { o.size = o.type = 0; o.start = ENDOFCHAIN; o.name = ""; }
if(o.type === 5) { /* root */
minifat_store = o.start;
if(nmfs > 0 && minifat_store !== ENDOFCHAIN) sector_list[minifat_store].name = "!StreamData";
@ -340,7 +340,7 @@ function read_directory(dir_start/*:number*/, sector_list/*:SectorList*/, sector
prep_blob(o.content, 0);
} else {
o.storage = 'minifat';
if(minifat_store !== ENDOFCHAIN && o.start !== ENDOFCHAIN) {
if(minifat_store !== ENDOFCHAIN && o.start !== ENDOFCHAIN && sector_list[minifat_store]) {
o.content = (sector_list[minifat_store].data.slice(o.start*MSSZ,o.start*MSSZ+o.size)/*:any*/);
prep_blob(o.content, 0);
}
@ -369,6 +369,9 @@ function readSync(blob/*:RawBytes|string*/, options/*:CFBReadOpts*/) {
return parse(/*::typeof blob == 'string' ? new Buffer(blob, 'utf-8') : */blob, options);
}
function find(cfb/*:CFBContainer*/, path/*:string*/)/*:?CFBEntry*/ {
return cfb.find(path);
}
/** CFB Constants */
var MSSZ = 64; /* Mini Sector Size = 1<<6 */
//var MSCSZ = 4096; /* Mini Stream Cutoff Size */
@ -394,6 +397,7 @@ var consts = {
EntryTypes: ['unknown','storage','stream','lockbytes','property','root']
};
exports.find = find;
exports.read = readSync;
exports.parse = parse;
exports.utils = {

@ -6,7 +6,7 @@ var DO_NOT_EXPORT_CFB = true;
/* [MS-CFB] v20130118 */
var CFB = (function _CFB(){
var exports = {};
exports.version = '0.12.0';
exports.version = '0.12.1';
function parse(file, options) {
var mver = 3; // major version
var ssz = 512; // sector size
@ -177,7 +177,7 @@ function build_full_paths(FI, FPD, FP, Paths) {
if(FI[i].type === 0 /* unknown */) continue;
j = dad[i];
if(j === 0) FP[i] = FP[0] + "/" + FP[i];
else while(j !== 0) {
else while(j !== 0 && j !== dad[j]) {
FP[i] = FP[j] + "/" + FP[i];
j = dad[j];
}
@ -278,7 +278,6 @@ function read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, Fil
var blob = sector.slice(i, i+128);
prep_blob(blob, 64);
namelen = blob.read_shift(2);
if(namelen === 0) continue;
name = __utf16le(blob,0,namelen-pl);
Paths.push(name);
var o = ({
@ -299,6 +298,7 @@ function read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, Fil
if(mtime !== 0) o.mt = read_date(blob, blob.l-8);
o.start = blob.read_shift(4, 'i');
o.size = blob.read_shift(4, 'i');
if(o.size < 0 && o.start < 0) { o.size = o.type = 0; o.start = ENDOFCHAIN; o.name = ""; }
if(o.type === 5) { /* root */
minifat_store = o.start;
if(nmfs > 0 && minifat_store !== ENDOFCHAIN) sector_list[minifat_store].name = "!StreamData";
@ -311,7 +311,7 @@ function read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, Fil
prep_blob(o.content, 0);
} else {
o.storage = 'minifat';
if(minifat_store !== ENDOFCHAIN && o.start !== ENDOFCHAIN) {
if(minifat_store !== ENDOFCHAIN && o.start !== ENDOFCHAIN && sector_list[minifat_store]) {
o.content = (sector_list[minifat_store].data.slice(o.start*MSSZ,o.start*MSSZ+o.size));
prep_blob(o.content, 0);
}
@ -340,6 +340,9 @@ function readSync(blob, options) {
return parse(blob, options);
}
function find(cfb, path) {
return cfb.find(path);
}
/** CFB Constants */
var MSSZ = 64; /* Mini Sector Size = 1<<6 */
//var MSCSZ = 4096; /* Mini Stream Cutoff Size */
@ -365,6 +368,7 @@ var consts = {
EntryTypes: ['unknown','storage','stream','lockbytes','property','root']
};
exports.find = find;
exports.read = readSync;
exports.parse = parse;
exports.utils = {