Compare commits
26 Commits
Author | SHA1 | Date | |
---|---|---|---|
John Shearer | b01260294e | ||
SheetJS | 5a5870eb1b | ||
SheetJS | ad93f76a36 | ||
SheetJS | 0e33eb6e13 | ||
SheetJS | c5eb7e6278 | ||
Christophe Coevoet | 99990abf64 | ||
reviewher | 4d8ef18b10 | ||
SheetJS | 1e70aae159 | ||
Ross Johnson | efb96a2c2c | ||
Ross Johnson | 4c7b78f9dc | ||
SheetJS | 76e4603fa5 | ||
Garrett Luu | 1a1920c567 | ||
SheetJS | 7c1980741a | ||
SheetJS | 6367acfd8a | ||
SheetJS | aff6d67444 | ||
SheetJS | 8756cc9b41 | ||
SheetJS | 8d85fb6e74 | ||
Ross Johnson | 62bd08588e | ||
SheetJS | d9b99d68a7 | ||
SheetJS | 8ee792f343 | ||
SheetJS | c88a98104d | ||
SheetJS | 8bb0c56acd | ||
SheetJS | 35d59c57bf | ||
SheetJS | 17b1a8de5a | ||
SheetJS | 98e9d2e641 | ||
SheetJS | 25688e28fe |
|
@ -2,7 +2,7 @@
|
|||
"env": { "shared-node-browser":true },
|
||||
"globals": {},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 3,
|
||||
"ecmaVersion": 3
|
||||
},
|
||||
"plugins": [ "html", "json" ],
|
||||
"extends": "eslint:recommended",
|
||||
|
@ -12,7 +12,13 @@
|
|||
"curly": 0,
|
||||
"no-bitwise": 0,
|
||||
"no-console": 0,
|
||||
"no-control-regex": 0,
|
||||
"no-empty": 0,
|
||||
"no-trailing-spaces": 2,
|
||||
"no-use-before-define": [ 1, {
|
||||
"functions":false, "classes":true, "variables":false
|
||||
}],
|
||||
"no-useless-escape": 0,
|
||||
"semi": [ 2, "always" ]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,3 +34,4 @@ module.file_ext=.js
|
|||
module.file_ext=.njs
|
||||
module.ignore_non_literal_requires=true
|
||||
suppress_comment= \\(.\\|\n\\)*\\$FlowIgnore
|
||||
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
name: 'Tests: node.js'
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
jobs:
|
||||
matrix:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
latest: ${{ steps.set-matrix.outputs.requireds }}
|
||||
steps:
|
||||
- uses: ljharb/actions/node/matrix@main
|
||||
id: set-matrix
|
||||
with:
|
||||
versionsAsRoot: true
|
||||
type: 'majors'
|
||||
preset: '>=4'
|
||||
|
||||
latest:
|
||||
needs: [matrix]
|
||||
name: 'latest majors'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version: ${{ fromJson(needs.matrix.outputs.latest) }}
|
||||
include:
|
||||
- node-version: '14.'
|
||||
env:
|
||||
TZ: America/New_York
|
||||
- node-version: '13.'
|
||||
env:
|
||||
TZ: Europe/London
|
||||
- node-version: '12.'
|
||||
env:
|
||||
TZ: Asia/Seoul
|
||||
- node-version: '11.'
|
||||
env:
|
||||
TZ: America/Los_Angeles
|
||||
FMTS: misc
|
||||
- node-version: '10.'
|
||||
env:
|
||||
TZ: Europe/Berlin
|
||||
FMTS: misc
|
||||
- node-version: '9.'
|
||||
env:
|
||||
TZ: Asia/Kolkata
|
||||
FMTS: misc
|
||||
- node-version: '8.'
|
||||
env:
|
||||
TZ: Asia/Shanghai
|
||||
FMTS: misc
|
||||
- node-version: '7.'
|
||||
env:
|
||||
TZ: America/Cancun
|
||||
FMTS: misc
|
||||
- node-version: '6.'
|
||||
env:
|
||||
TZ: Asia/Seoul
|
||||
FMTS: misc
|
||||
- node-version: '5.'
|
||||
env:
|
||||
TZ: America/Anchorage
|
||||
FMTS: misc
|
||||
- node-version: '4.'
|
||||
env:
|
||||
TZ: America/Barbados
|
||||
FMTS: misc
|
||||
- node-version: '4.4.7' # see GH issue #1150
|
||||
env:
|
||||
TZ: Asia/Tokyo
|
||||
FMTS: misc
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ljharb/actions/node/install@main
|
||||
name: 'nvm install ${{ matrix.node-version }} && npm install'
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: sudo curl -Lo /usr/bin/rooster https://github.com/SheetJS/rooster/releases/download/v0.2.0/rooster-v0.2.0-linux-amd64
|
||||
- run: sudo chmod a+x /usr/bin/rooster
|
||||
- run: make init
|
||||
- run: 'cd test_files; make all; cd -'
|
||||
- run: npm run test
|
||||
|
||||
node:
|
||||
name: 'node 4+'
|
||||
needs: [latest]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: 'echo tests completed'
|
|
@ -0,0 +1,45 @@
|
|||
name: 'Tests: node.js (io.js)'
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
jobs:
|
||||
matrix:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
latest: ${{ steps.set-matrix.outputs.requireds }}
|
||||
steps:
|
||||
- uses: ljharb/actions/node/matrix@main
|
||||
id: set-matrix
|
||||
with:
|
||||
type: 'majors'
|
||||
preset: 'iojs'
|
||||
|
||||
latest:
|
||||
needs: [matrix]
|
||||
name: 'latest majors'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.matrix.outputs.latest) }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ljharb/actions/node/install@main
|
||||
name: 'nvm install ${{ matrix.node-version }} && npm install'
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
skip-ls-check: true
|
||||
- run: sudo curl -Lo /usr/bin/rooster https://github.com/SheetJS/rooster/releases/download/v0.2.0/rooster-v0.2.0-linux-amd64
|
||||
- run: sudo chmod a+x /usr/bin/rooster
|
||||
- run: make init
|
||||
- run: 'cd test_files; make all; cd -'
|
||||
- run: npm run test
|
||||
#- run: 'cd packages/ssf; npm run tests-only; cd -'
|
||||
|
||||
node:
|
||||
name: 'io.js'
|
||||
needs: [latest]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: 'echo tests completed'
|
|
@ -0,0 +1,32 @@
|
|||
name: 'Tests: pretest/posttest'
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
jobs:
|
||||
pretest:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ljharb/actions/node/install@main
|
||||
name: 'nvm install lts/* && npm install'
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
- run: sudo curl -Lo /usr/bin/rooster https://github.com/SheetJS/rooster/releases/download/v0.2.0/rooster-v0.2.0-linux-amd64
|
||||
- run: sudo chmod a+x /usr/bin/rooster
|
||||
- run: make init
|
||||
- run: 'cd test_files; make all; cd -'
|
||||
#- run: npm run pretest
|
||||
|
||||
# posttest:
|
||||
# runs-on: ubuntu-latest
|
||||
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - uses: ljharb/actions/node/install@main
|
||||
# name: 'nvm install lts/* && npm install'
|
||||
# with:
|
||||
# node-version: 'lts/*'
|
||||
# - run: make init
|
||||
# - run: 'cd test_files; make all; cd -'
|
||||
# - run: npm run posttest
|
|
@ -0,0 +1,88 @@
|
|||
name: 'Tests: node.js (0.x)'
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
jobs:
|
||||
matrix:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
stable: ${{ steps.set-matrix.outputs.requireds }}
|
||||
# unstable: ${{ steps.set-matrix.outputs.optionals }}
|
||||
steps:
|
||||
- uses: ljharb/actions/node/matrix@main
|
||||
id: set-matrix
|
||||
with:
|
||||
versionsAsRoot: true
|
||||
preset: '0.x'
|
||||
|
||||
stable:
|
||||
needs: [matrix]
|
||||
name: 'stable minors'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version: ${{ fromJson(needs.matrix.outputs.stable) }}
|
||||
include:
|
||||
- node-version: '0.12.'
|
||||
env:
|
||||
TZ: America/Cayman
|
||||
FMTS: misc
|
||||
- node-version: '0.10.'
|
||||
env:
|
||||
TZ: Pacific/Honolulu
|
||||
FMTS: misc
|
||||
- node-version: '0.8.'
|
||||
env:
|
||||
TZ: America/Mexico_City
|
||||
FMTS: misc
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ljharb/actions/node/install@main
|
||||
name: 'nvm install ${{ matrix.node-version }} && npm install'
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache-node-modules-key: node_modules-${{ github.workflow }}-${{ github.action }}-${{ github.run_id }}
|
||||
skip-ls-check: true
|
||||
- run: sudo curl -Lo /usr/bin/rooster https://github.com/SheetJS/rooster/releases/download/v0.2.0/rooster-v0.2.0-linux-amd64
|
||||
- run: sudo chmod a+x /usr/bin/rooster
|
||||
- run: make init
|
||||
- run: 'cd test_files; make all; cd -'
|
||||
- run: npm run test
|
||||
#- run: 'cd packages/ssf; npm run tests-only; cd -'
|
||||
|
||||
# unstable:
|
||||
# needs: [matrix, stable]
|
||||
# name: 'unstable minors'
|
||||
# continue-on-error: true
|
||||
# if: ${{ !github.head_ref || !startsWith(github.head_ref, 'renovate') }}
|
||||
# runs-on: ubuntu-latest
|
||||
|
||||
# strategy:
|
||||
# fail-fast: false
|
||||
# matrix:
|
||||
# node-version: ${{ fromJson(needs.matrix.outputs.unstable) }}
|
||||
#
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - uses: ljharb/actions/node/install@main
|
||||
# name: 'nvm install ${{ matrix.node-version }} && npm install'
|
||||
# with:
|
||||
# node-version: ${{ matrix.node-version }}
|
||||
# cache-node-modules-key: node_modules-${{ github.workflow }}-${{ github.action }}-${{ github.run_id }}
|
||||
# skip-ls-check: true
|
||||
# - run: sudo curl -Lo /usr/bin/rooster https://github.com/SheetJS/rooster/releases/download/v0.2.0/rooster-v0.2.0-linux-amd64
|
||||
# - run: sudo chmod a+x /usr/bin/rooster
|
||||
# - run: make init
|
||||
# - run: 'cd test_files; make all; cd -'
|
||||
# - run: npm run tests-only
|
||||
|
||||
node:
|
||||
name: 'node 0.x'
|
||||
# needs: [stable, unstable]
|
||||
needs: [stable]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: 'echo tests completed'
|
|
@ -10,18 +10,27 @@ test_files_pres
|
|||
*.[cC][sS][vV]
|
||||
*.[dD][iIbB][fF]
|
||||
*.[pP][rR][nN]
|
||||
*.[pP][mM][dD]*
|
||||
*.[pP][dD][fF]
|
||||
*.[sS][lL][kK]
|
||||
*.socialcalc
|
||||
*.[xX][lL][sSwWcC]
|
||||
*.[xX][lL][sS][xXmMbB]
|
||||
*.[xX][lL][sSwWcCaAtTmM]
|
||||
*.[xX][lL][sSaAtT][xXmMbB]
|
||||
*.[oO][dD][sS]
|
||||
*.[fF][oO][dD][sS]
|
||||
*.[xX][mM][lL]
|
||||
*.[uU][oO][sS]
|
||||
*.[wW][kKqQbB][S1234567890]
|
||||
*.[qQ][pP][wW]
|
||||
*.[bB][iI][fF][fF][23458]
|
||||
*.[rR][tT][fF]
|
||||
*.[eE][tT][hH]
|
||||
*.[zZ][iI][pP]
|
||||
*.[mM][sS][iIgG]
|
||||
*.[mM][hH][tT]
|
||||
*.123
|
||||
*.htm
|
||||
*.html
|
||||
*.sheetjs
|
||||
*.exe
|
||||
*.img
|
||||
|
|
13
.travis.yml
13
.travis.yml
|
@ -1,5 +1,11 @@
|
|||
language: node_js
|
||||
dist: xenial
|
||||
node_js:
|
||||
- "14"
|
||||
- "13"
|
||||
- "12"
|
||||
- "11"
|
||||
- "10"
|
||||
- "9"
|
||||
- "8"
|
||||
- "7"
|
||||
|
@ -10,12 +16,15 @@ node_js:
|
|||
- "0.10"
|
||||
- "0.8"
|
||||
before_install:
|
||||
- "npm install -g npm@4.3.0"
|
||||
- "npm config set strict-ssl false"
|
||||
- "./misc/node_version.sh"
|
||||
- "npm install -g mocha@2.x voc"
|
||||
- "npm install blanket"
|
||||
- "npm install xlsjs crc-32"
|
||||
- "npm install word crc-32"
|
||||
- "npm install coveralls mocha-lcov-reporter"
|
||||
before_script:
|
||||
- "make init"
|
||||
install:
|
||||
- npm install
|
||||
after_success:
|
||||
- "make coveralls-spin"
|
||||
|
|
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -4,6 +4,26 @@ This log is intended to keep track of backwards-incompatible changes, including
|
|||
but not limited to API changes and file location changes. Minor behavioral
|
||||
changes may not be included if they are not expected to break existing code.
|
||||
|
||||
## 1.2.1 (2021-09-06)
|
||||
|
||||
* CFB write optimizations (h/t @rossj Ross Johnson)
|
||||
* `read` in NodeJS will treat `Buffer` input as type `"buffer"` by default
|
||||
* `deflate` / ZIP support fixed Huffman compression
|
||||
* `inflate` more aggressive reallocs
|
||||
|
||||
## 1.2.0 (2020-07-09)
|
||||
|
||||
* Support for MAD file format (MIME aggregate document)
|
||||
* Spun off the CLI tool to the `cfb-cli` module
|
||||
|
||||
## 1.1.0 (2018-09-04)
|
||||
|
||||
* Support for ZIP file format
|
||||
|
||||
## 1.0.6 (2018-04-09)
|
||||
|
||||
* `lastIndexOf` in FAT builder enables larger file parsing at minor cost
|
||||
|
||||
## 1.0.0 (2017-11-05)
|
||||
|
||||
* Actually walk mini-fat
|
||||
|
|
190
LICENSE
190
LICENSE
|
@ -1,4 +1,192 @@
|
|||
Copyright (C) 2013-present SheetJS
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright (C) 2013-present SheetJS LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
38
Makefile
38
Makefile
|
@ -4,7 +4,7 @@ FMT=xls doc ppt misc full
|
|||
REQS=
|
||||
ADDONS=
|
||||
AUXTARGETS=xlscfb.js
|
||||
CMDS=bin/cfb.njs
|
||||
CMDS=packages/cfb-cli/bin/cfb.njs
|
||||
HTMLLINT=index.html
|
||||
|
||||
ULIB=$(shell echo $(LIB) | tr a-z A-Z)
|
||||
|
@ -31,7 +31,7 @@ bits/31_version.js: package.json
|
|||
|
||||
.PHONY: clean
|
||||
clean: ## Remove targets and build artifacts
|
||||
rm -f $(TARGET) $(FLOWTARGET)
|
||||
rm -f $(TARGET) $(FLOWTARGET) xlscfb.js xlscfb.flow.js
|
||||
|
||||
.PHONY: clean-data
|
||||
clean-data:
|
||||
|
@ -39,20 +39,22 @@ clean-data:
|
|||
|
||||
.PHONY: init
|
||||
init: ## Initial setup for development
|
||||
if [ ! -e test_files ]; then git clone https://github.com/SheetJS/test_files; fi
|
||||
if [ ! -e test_files ]; then git clone --depth=1 https://github.com/SheetJS/test_files; fi
|
||||
cd test_files; git pull; make
|
||||
if [ ! -e test_files_pres ]; then git clone https://github.com/SheetJS/test_files_pres; fi
|
||||
if [ ! -e test_files_pres ]; then git clone --depth=1 https://github.com/SheetJS/test_files_pres; fi
|
||||
cd test_files_pres; git pull
|
||||
|
||||
.PHONY: dist
|
||||
dist: dist-deps $(TARGET) ## Prepare JS files for distribution
|
||||
cp $(TARGET) dist/
|
||||
mkdir -p dist
|
||||
cp LICENSE dist/
|
||||
cp $(TARGET) dist/
|
||||
uglifyjs $(TARGET) $(UGLIFYOPTS) -o dist/$(LIB).min.js --source-map dist/$(LIB).min.map --preamble "$$(head -n 1 bits/00_header.js)"
|
||||
misc/strip_sourcemap.sh dist/$(LIB).min.js
|
||||
|
||||
.PHONY: dist-deps
|
||||
dist-deps: xlscfb.js ## Copy dependencies for distribution
|
||||
mkdir -p dist
|
||||
cp xlscfb.flow.js dist/xlscfb.js
|
||||
|
||||
.PHONY: aux
|
||||
|
@ -61,15 +63,15 @@ aux: $(AUXTARGETS)
|
|||
.PHONY: xls
|
||||
xls: xlscfb.js
|
||||
|
||||
XLSSKIP=bits/08_blob.js bits/04_base64.js bits/05_buf.js
|
||||
XLSDEPS=misc/suppress_export.js $(filter-out $(XLSSKIP),$(DEPS))
|
||||
XLSSKIP=bits/08_blob.js bits/04_base64.js bits/05_buf.js bits/98_exports.js
|
||||
XLSDEPS=misc/xlscfb.js $(filter-out $(XLSSKIP),$(DEPS))
|
||||
xlscfb.flow.js: $(XLSDEPS) ## Build support library
|
||||
cat $^ | tr -d '\15\32' > $@
|
||||
cat $^ | tr -d '\15\32' | grep -v DO_NOT_EXPORT_CFB > $@
|
||||
|
||||
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
|
||||
for i in $(BYTEFILE); do npx printj "%-30s %7d %10d" $$i $$(wc -c < $$i) $$(gzip --best --stdout $$i | wc -c); done
|
||||
|
||||
|
||||
## Testing
|
||||
|
@ -93,23 +95,23 @@ 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) $(CMDS) $(HTMLLINT) package.json
|
||||
if [ -e $(CLOSURE) ]; then java -jar $(CLOSURE) $(REQS) $(FLOWTARGET) --jscomp_warning=reportUnknownTypes >/dev/null; fi
|
||||
@./node_modules/.bin/eslint --ext .js,.njs,.json,.html,.htm $(TARGET) $(CMDS) $(HTMLLINT) package.json
|
||||
@if [ -x "$(CLOSURE)" ]; then java -jar $(CLOSURE) $(REQS) $(FLOWTARGET) --jscomp_warning=reportUnknownTypes >/dev/null; fi
|
||||
|
||||
.PHONY: old-lint
|
||||
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 test.js
|
||||
@jshint --show-non-errors --extract=always $(HTMLLINT)
|
||||
@jscs $(TARGET) $(AUXTARGETS) test.js
|
||||
if [ -e $(CLOSURE) ]; then java -jar $(CLOSURE) $(REQS) $(FLOWTARGET) --jscomp_warning=reportUnknownTypes >/dev/null; fi
|
||||
@./node_modules/.bin/jscs $(TARGET) $(AUXTARGETS) test.js
|
||||
@./node_modules/.bin/jshint --show-non-errors $(TARGET) $(AUXTARGETS)
|
||||
@./node_modules/.bin/jshint --show-non-errors $(CMDS)
|
||||
@./node_modules/.bin/jshint --show-non-errors package.json test.js
|
||||
@./node_modules/.bin/jshint --show-non-errors --extract=always $(HTMLLINT)
|
||||
@if [ -x "$(CLOSURE)" ]; then java -jar $(CLOSURE) $(REQS) $(FLOWTARGET) --jscomp_warning=reportUnknownTypes >/dev/null; fi
|
||||
|
||||
.PHONY: tslint
|
||||
tslint: $(TARGET) ## Run typescript checks
|
||||
#@npm install dtslint typescript
|
||||
#@npm run-script dtslint
|
||||
dtslint types
|
||||
./node_modules/.bin/dtslint types
|
||||
|
||||
.PHONY: flow
|
||||
flow: lint ## Run flow checker
|
||||
|
|
107
README.md
107
README.md
|
@ -1,13 +1,11 @@
|
|||
# Compound File Binary Format
|
||||
# Container File Blobs
|
||||
|
||||
Pure JS implementation of MS-CFB: Compound File Binary File Format, a container
|
||||
format used in many Microsoft file types (XLS, DOC, VBA blobs in XLSX and XLSB)
|
||||
Pure JS implementation of various container file formats, including ZIP and CFB.
|
||||
|
||||
[![Build Status](https://travis-ci.org/SheetJS/js-cfb.svg?branch=master)](https://travis-ci.org/SheetJS/js-cfb)
|
||||
[![Coverage Status](http://img.shields.io/coveralls/SheetJS/js-cfb/master.svg)](https://coveralls.io/r/SheetJS/js-cfb?branch=master)
|
||||
[![Dependencies Status](https://david-dm.org/sheetjs/js-cfb/status.svg)](https://david-dm.org/sheetjs/js-cfb)
|
||||
[![NPM Downloads](https://img.shields.io/npm/dt/cfb.svg)](https://npmjs.org/package/cfb)
|
||||
[![ghit.me](https://ghit.me/badge.svg?repo=sheetjs/js-xlsx)](https://ghit.me/repo/sheetjs/js-xlsx)
|
||||
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-cfb?pixel)](https://github.com/SheetJS/js-cfb)
|
||||
|
||||
## Installation
|
||||
|
@ -24,7 +22,7 @@ With [npm](https://www.npmjs.org/package/cfb):
|
|||
$ npm install cfb
|
||||
```
|
||||
|
||||
The `xlscfb.js` file is designed to be embedded in [js-xlsx](http://git.io/xlsx)
|
||||
The `xlscfb.js` file is designed to be embedded in [js-xlsx](https://github.com/SheetJS/sheetjs)
|
||||
|
||||
|
||||
## Library Usage
|
||||
|
@ -46,28 +44,8 @@ var data = workbook.content;
|
|||
|
||||
## Command-Line Utility Usage
|
||||
|
||||
It is preferable to install the library globally with npm:
|
||||
|
||||
```bash
|
||||
$ npm install -g cfb
|
||||
```
|
||||
|
||||
The global installation adds a command `cfb` which can work with files:
|
||||
|
||||
- `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.
|
||||
The [`cfb-cli`](https://www.npmjs.com/package/cfb-cli) module ships with a CLI
|
||||
tool for manipulating and inspecting supported files.
|
||||
|
||||
|
||||
## JS API
|
||||
|
@ -79,29 +57,52 @@ The CFB object exposes the following methods and properties:
|
|||
`CFB.parse(blob)` takes a nodejs Buffer or an array of bytes and returns an
|
||||
parsed representation of the data.
|
||||
|
||||
`CFB.read(blob, opts)` wraps `parse`. `opts.type` controls the behavior:
|
||||
|
||||
| `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.read(blob, opts)` wraps `parse`.
|
||||
|
||||
`CFB.find(cfb, path)` performs a case-insensitive match for the path (or file
|
||||
name, if there are no slashes) and returns an entry object or null if not found.
|
||||
|
||||
`CFB.write(cfb, opts)` generates a file based on the container. `opts.type`
|
||||
controls the behavior:
|
||||
|
||||
| `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.write(cfb, opts)` generates a file based on the container.
|
||||
|
||||
`CFB.writeFile(cfb, filename, opts)` creates a file with the specified name.
|
||||
|
||||
### Parse Options
|
||||
|
||||
`CFB.read` takes an options argument. `opts.type` controls the behavior:
|
||||
|
||||
| `type` | expected input |
|
||||
|------------|:----------------------------------------------------------------|
|
||||
| `"base64"` | string: Base64 encoding of the file |
|
||||
| `"binary"` | string: binary string (byte `n` is `data.charCodeAt(n)`) |
|
||||
| `"buffer"` | nodejs Buffer |
|
||||
| `"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]`) |
|
||||
|
||||
|
||||
### Write Options
|
||||
|
||||
`CFB.write` and `CFB.writeFile` take options argument.
|
||||
|
||||
`opts.type` controls the behavior:
|
||||
|
||||
| `type` | output |
|
||||
|------------|:----------------------------------------------------------------|
|
||||
| `"base64"` | string: Base64 encoding of the file |
|
||||
| `"binary"` | string: binary string (byte `n` is `data.charCodeAt(n)`) |
|
||||
| `"buffer"` | nodejs Buffer |
|
||||
| `"file"` | string: path of file that will be created (nodejs only) |
|
||||
| (default) | buffer if available, array of 8-bit unsigned int otherwise |
|
||||
|
||||
`opts.fileType` controls the output file type:
|
||||
|
||||
| `fileType` | output |
|
||||
|:-------------------|:------------------------|
|
||||
| `'cfb'` (default) | CFB container |
|
||||
| `'zip'` | ZIP file |
|
||||
| `'mad'` | MIME aggregate document |
|
||||
|
||||
`opts.compression` enables DEFLATE compression for ZIP file type.
|
||||
|
||||
|
||||
## Utility Functions
|
||||
|
||||
|
@ -110,8 +111,15 @@ accept a `name` argument strictly deal with absolute file names:
|
|||
|
||||
- `.cfb_new(?opts)` creates a new container object.
|
||||
- `.cfb_add(cfb, name, ?content, ?opts)` adds a new file to the `cfb`.
|
||||
Set the option `{unsafe:true}` to skip existence checks (for bulk additions)
|
||||
- `.cfb_del(cfb, name)` deletes the specified file
|
||||
- `.cfb_mov(cfb, old_name, new_name)` moves the old file to new path and name
|
||||
- `.use_zlib(require("zlib"))` loads a nodejs `zlib` instance.
|
||||
|
||||
By default, the library uses a pure JS inflate/deflate implementation. NodeJS
|
||||
`zlib.InflateRaw` exposes the number of bytes read in versions after `8.11.0`.
|
||||
If a supplied `zlib` does not support the required features, a warning will be
|
||||
displayed in the console and the pure JS fallback will be used.
|
||||
|
||||
|
||||
## Container Object Description
|
||||
|
@ -132,6 +140,7 @@ interface CFBEntry {
|
|||
content: Buffer | number[] | Uint8Array; /** Raw Content */
|
||||
ct?: Date; /** Creation Time */
|
||||
mt?: Date; /** Modification Time */
|
||||
ctype?: String; /** Content-Type (for MAD) */
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -144,11 +153,9 @@ granted by the Apache 2.0 License are reserved by the Original Author.
|
|||
|
||||
## References
|
||||
|
||||
|
||||
<details>
|
||||
<summary><b>OSP-covered Specifications</b> (click to show)</summary>
|
||||
|
||||
- [MS-CFB]: Compound File Binary File Format
|
||||
|
||||
</details>
|
||||
- `MS-CFB`: Compound File Binary File Format
|
||||
- ZIP `APPNOTE.TXT`: .ZIP File Format Specification
|
||||
- RFC1951: https://www.ietf.org/rfc/rfc1951.txt
|
||||
- RFC2045: https://www.ietf.org/rfc/rfc2045.txt
|
||||
- RFC2557: https://www.ietf.org/rfc/rfc2557.txt
|
||||
|
||||
|
|
131
bin/cfb.njs
131
bin/cfb.njs
|
@ -1,131 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
/* 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] <file> [subfiles...]')
|
||||
.option('-l, --list-files', 'list files')
|
||||
.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('-O, --to-stdout', 'extract raw contents to stdout')
|
||||
.option('-z, --dump', 'dump internal representation but do not extract')
|
||||
.option('-q, --quiet', 'process but do not report')
|
||||
.option('--dev', 'development mode')
|
||||
.option('--read', 'read but do not print out contents');
|
||||
|
||||
program.parse(process.argv);
|
||||
|
||||
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) exit(0);
|
||||
|
||||
if(program.dump) {
|
||||
console.log("Full Paths:");
|
||||
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); }
|
||||
|
||||
function fix_string(x/*:string*/)/*:string*/ { return x.replace(/[\u0000-\u001f]/g, function($$) { return sprintf("\\u%04X", $$.charCodeAt(0)); }); }
|
||||
var format_date = function(date/*:Date*/)/*:string*/ {
|
||||
return sprintf("%02u-%02u-%02u %02u:%02u", date.getUTCMonth()+1, date.getUTCDate(), date.getUTCFullYear()%100, date.getUTCHours(), date.getUTCMinutes());
|
||||
};
|
||||
|
||||
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/*: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), fix_string(cfb.FullPaths[i])));
|
||||
filesize += file.size;
|
||||
++cnt;
|
||||
}
|
||||
});
|
||||
console.log(" -------- -------");
|
||||
console.log(sprintf("%9lu %lu file%s", rootsize || filesize, cnt, (cnt !== 1 ? "s" : "")));
|
||||
|
||||
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))*/ || new Buffer(0));
|
||||
}
|
||||
|
||||
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.replace(/\\u000\d/g,"!"));
|
||||
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];
|
||||
if(program.toStdout) return process.stdout.write(/*::new Buffer((*/data.content/*:: :any))*/);
|
||||
mkdirp(path.slice(0, path.lastIndexOf("/")));
|
||||
write(path, data);
|
||||
});
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if(program.toStdout) exit(0);
|
||||
for(var i=0; i!==cfb.FullPaths.length; ++i) {
|
||||
if(!cfb.FileIndex[i].name) continue;
|
||||
if(cfb.FullPaths[i].slice(-1) === "/") mkdirp(cfb.FullPaths[i]);
|
||||
else write(cfb.FullPaths[i], cfb.FileIndex[i]);
|
||||
}
|
|
@ -2,5 +2,5 @@
|
|||
/* vim: set ts=2: */
|
||||
/*jshint eqnull:true */
|
||||
/*exported CFB */
|
||||
/*global module, require:false, process:false, Buffer:false, Uint8Array:false */
|
||||
/*global module, require:false, process:false, Buffer:false, Uint8Array:false, Uint16Array:false */
|
||||
|
||||
|
|
|
@ -1,47 +1,36 @@
|
|||
var Base64 = (function make_b64(){
|
||||
var map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||
return {
|
||||
encode: function(input/*:string*/)/*:string*/ {
|
||||
var o = "";
|
||||
var c1/*:number*/, c2/*:number*/, c3/*:number*/;
|
||||
var e1/*:number*/, e2/*:number*/, e3/*:number*/, e4/*:number*/;
|
||||
for(var i = 0; i < input.length; ) {
|
||||
c1 = input.charCodeAt(i++);
|
||||
e1 = (c1 >> 2);
|
||||
|
||||
c2 = input.charCodeAt(i++);
|
||||
e2 = ((c1 & 3) << 4) | (c2 >> 4);
|
||||
|
||||
c3 = input.charCodeAt(i++);
|
||||
e3 = ((c2 & 15) << 2) | (c3 >> 6);
|
||||
e4 = (c3 & 63);
|
||||
if (isNaN(c2)) { e3 = e4 = 64; }
|
||||
else if (isNaN(c3)) { e4 = 64; }
|
||||
o += map.charAt(e1) + map.charAt(e2) + map.charAt(e3) + map.charAt(e4);
|
||||
}
|
||||
return o;
|
||||
},
|
||||
decode: function b64_decode(input/*:string*/)/*:string*/ {
|
||||
var o = "";
|
||||
var c1/*:number*/, c2/*:number*/, c3/*:number*/;
|
||||
var e1/*:number*/, e2/*:number*/, e3/*:number*/, e4/*:number*/;
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
input = input.replace(/[^\w\+\/\=]/g, "");
|
||||
for(var i = 0; i < input.length;) {
|
||||
e1 = map.indexOf(input.charAt(i++));
|
||||
e2 = map.indexOf(input.charAt(i++));
|
||||
c1 = (e1 << 2) | (e2 >> 4);
|
||||
o += String.fromCharCode(c1);
|
||||
|
||||
e3 = map.indexOf(input.charAt(i++));
|
||||
c2 = ((e2 & 15) << 4) | (e3 >> 2);
|
||||
if (e3 !== 64) { o += String.fromCharCode(c2); }
|
||||
|
||||
e4 = map.indexOf(input.charAt(i++));
|
||||
c3 = ((e3 & 3) << 6) | e4;
|
||||
if (e4 !== 64) { o += String.fromCharCode(c3); }
|
||||
}
|
||||
return o;
|
||||
}
|
||||
};
|
||||
})();
|
||||
var Base64_map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||
function Base64_encode(input/*:string*/)/*:string*/ {
|
||||
var o = "";
|
||||
var c1 = 0, c2 = 0, c3 = 0, e1 = 0, e2 = 0, e3 = 0, e4 = 0;
|
||||
for (var i = 0; i < input.length; ) {
|
||||
c1 = input.charCodeAt(i++);
|
||||
e1 = (c1 >> 2);
|
||||
c2 = input.charCodeAt(i++);
|
||||
e2 = ((c1 & 3) << 4) | (c2 >> 4);
|
||||
c3 = input.charCodeAt(i++);
|
||||
e3 = ((c2 & 15) << 2) | (c3 >> 6);
|
||||
e4 = (c3 & 63);
|
||||
if (isNaN(c2)) e3 = e4 = 64;
|
||||
else if (isNaN(c3)) e4 = 64;
|
||||
o += Base64_map.charAt(e1) + Base64_map.charAt(e2) + Base64_map.charAt(e3) + Base64_map.charAt(e4);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
function Base64_decode(input/*:string*/)/*:string*/ {
|
||||
var o = "";
|
||||
var c1 = 0, c2 = 0, c3 = 0, e1 = 0, e2 = 0, e3 = 0, e4 = 0;
|
||||
input = input.replace(/[^\w\+\/\=]/g, "");
|
||||
for (var i = 0; i < input.length;) {
|
||||
e1 = Base64_map.indexOf(input.charAt(i++));
|
||||
e2 = Base64_map.indexOf(input.charAt(i++));
|
||||
c1 = (e1 << 2) | (e2 >> 4);
|
||||
o += String.fromCharCode(c1);
|
||||
e3 = Base64_map.indexOf(input.charAt(i++));
|
||||
c2 = ((e2 & 15) << 4) | (e3 >> 2);
|
||||
if (e3 !== 64) o += String.fromCharCode(c2);
|
||||
e4 = Base64_map.indexOf(input.charAt(i++));
|
||||
c3 = ((e3 & 3) << 6) | e4;
|
||||
if (e4 !== 64) o += String.fromCharCode(c3);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,35 @@
|
|||
var has_buf = (typeof Buffer !== 'undefined' && typeof process !== 'undefined' && typeof process.versions !== 'undefined' && process.versions.node);
|
||||
var has_buf = /*#__PURE__*/(function() { return typeof Buffer !== 'undefined' && typeof process !== 'undefined' && typeof process.versions !== 'undefined' && !!process.versions.node; })();
|
||||
|
||||
var Buffer_from = /*#__PURE__*/(function() {
|
||||
if(typeof Buffer !== 'undefined') {
|
||||
var nbfs = !Buffer.from;
|
||||
if(!nbfs) try { Buffer.from("foo", "utf8"); } catch(e) { nbfs = true; }
|
||||
return nbfs ? function(buf, enc) { return (enc) ? new Buffer(buf, enc) : new Buffer(buf); } : Buffer.from.bind(Buffer);
|
||||
}
|
||||
return function() {};
|
||||
})();
|
||||
|
||||
|
||||
function new_raw_buf(len/*:number*/) {
|
||||
/* jshint -W056 */
|
||||
// $FlowIgnore
|
||||
return new (has_buf ? Buffer : Array)(len);
|
||||
if(has_buf) {
|
||||
if(Buffer.alloc) return Buffer.alloc(len);
|
||||
var b = new Buffer(len); b.fill(0); return b;
|
||||
}
|
||||
return typeof Uint8Array != "undefined" ? new Uint8Array(len) : new Array(len);
|
||||
/* jshint +W056 */
|
||||
}
|
||||
|
||||
var s2a = function s2a(s/*:string*/) {
|
||||
if(has_buf) return new Buffer(s, "binary");
|
||||
return s.split("").map(function(x){ return x.charCodeAt(0) & 0xff; });
|
||||
function new_unsafe_buf(len/*:number*/) {
|
||||
/* jshint -W056 */
|
||||
if(has_buf) return Buffer.allocUnsafe ? Buffer.allocUnsafe(len) : new Buffer(len);
|
||||
return typeof Uint8Array != "undefined" ? new Uint8Array(len) : new Array(len);
|
||||
/* jshint +W056 */
|
||||
}
|
||||
|
||||
var s2a = function s2a(s/*:string*/)/*:RawBytes*/ {
|
||||
if(has_buf) return Buffer_from(s, "binary");
|
||||
return s.split("").map(function(x/*:string*/)/*:number*/{ return x.charCodeAt(0) & 0xff; });
|
||||
};
|
||||
|
||||
var chr0 = /\u0000/g, chr1 = /[\u0001-\u0006]/g;
|
||||
|
|
|
@ -22,7 +22,7 @@ if(has_buf/*:: && typeof Buffer !== 'undefined'*/) {
|
|||
};
|
||||
__hexlify = function(b/*:RawBytes|CFBlob*/,s/*:number*/,l/*:number*/)/*:string*/ { return Buffer.isBuffer(b)/*:: && b instanceof Buffer*/ ? b.toString('hex',s,s+l) : ___hexlify(b,s,l); };
|
||||
__toBuffer = function(bufs/*:Array<Array<RawBytes>>*/)/*:RawBytes*/ { return (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat((bufs[0]/*:any*/)) : ___toBuffer(bufs);};
|
||||
s2a = function(s/*:string*/)/*:RawBytes*/ { return new Buffer(s, "binary"); };
|
||||
s2a = function(s/*:string*/)/*:RawBytes*/ { return Buffer_from(s, "binary"); };
|
||||
bconcat = function(bufs/*:Array<RawBytes>*/)/*:RawBytes*/ { return Buffer.isBuffer(bufs[0]) ? Buffer.concat(/*::(*/bufs/*:: :any)*/) : __bconcat(bufs); };
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
/*! crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */
|
||||
/* vim: set ts=2: */
|
||||
/*exported CRC32 */
|
||||
var CRC32 = /*#__PURE__*/(function() {
|
||||
var CRC32 = {};
|
||||
CRC32.version = '1.2.1';
|
||||
/*::
|
||||
type ABuf = Array<number> | Buffer | Uint8Array;
|
||||
type CRC32TableType = Array<number> | Int32Array;
|
||||
*/
|
||||
/*global Int32Array */
|
||||
function signed_crc_table()/*:CRC32TableType*/ {
|
||||
var c = 0, table/*:Array<number>*/ = new Array(256);
|
||||
|
||||
for(var n =0; n != 256; ++n){
|
||||
c = n;
|
||||
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
|
||||
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
|
||||
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
|
||||
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
|
||||
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
|
||||
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
|
||||
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
|
||||
c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
|
||||
table[n] = c;
|
||||
}
|
||||
|
||||
return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table;
|
||||
}
|
||||
|
||||
var T0 = signed_crc_table();
|
||||
function slice_by_16_tables(T) {
|
||||
var c = 0, v = 0, n = 0, table/*:Array<number>*/ = typeof Int32Array !== 'undefined' ? new Int32Array(4096) : new Array(4096) ;
|
||||
|
||||
for(n = 0; n != 256; ++n) table[n] = T[n];
|
||||
for(n = 0; n != 256; ++n) {
|
||||
v = T[n];
|
||||
for(c = 256 + n; c < 4096; c += 256) v = table[c] = (v >>> 8) ^ T[v & 0xFF];
|
||||
}
|
||||
var out = [];
|
||||
for(n = 1; n != 16; ++n) out[n - 1] = typeof Int32Array !== 'undefined' ? table.subarray(n * 256, n * 256 + 256) : table.slice(n * 256, n * 256 + 256);
|
||||
return out;
|
||||
}
|
||||
var TT = slice_by_16_tables(T0);
|
||||
var T1 = TT[0], T2 = TT[1], T3 = TT[2], T4 = TT[3], T5 = TT[4];
|
||||
var T6 = TT[5], T7 = TT[6], T8 = TT[7], T9 = TT[8], Ta = TT[9];
|
||||
var Tb = TT[10], Tc = TT[11], Td = TT[12], Te = TT[13], Tf = TT[14];
|
||||
function crc32_bstr(bstr/*:string*/, seed/*:?number*/)/*:number*/ {
|
||||
var C = seed/*:: ? 0 : 0 */ ^ -1;
|
||||
for(var i = 0, L = bstr.length; i < L;) C = (C>>>8) ^ T0[(C^bstr.charCodeAt(i++))&0xFF];
|
||||
return ~C;
|
||||
}
|
||||
|
||||
function crc32_buf(B/*:ABuf*/, seed/*:?number*/)/*:number*/ {
|
||||
var C = seed/*:: ? 0 : 0 */ ^ -1, L = B.length - 15, i = 0;
|
||||
for(; i < L;) C =
|
||||
Tf[B[i++] ^ (C & 255)] ^
|
||||
Te[B[i++] ^ ((C >> 8) & 255)] ^
|
||||
Td[B[i++] ^ ((C >> 16) & 255)] ^
|
||||
Tc[B[i++] ^ (C >>> 24)] ^
|
||||
Tb[B[i++]] ^ Ta[B[i++]] ^ T9[B[i++]] ^ T8[B[i++]] ^
|
||||
T7[B[i++]] ^ T6[B[i++]] ^ T5[B[i++]] ^ T4[B[i++]] ^
|
||||
T3[B[i++]] ^ T2[B[i++]] ^ T1[B[i++]] ^ T0[B[i++]];
|
||||
L += 15;
|
||||
while(i < L) C = (C>>>8) ^ T0[(C^B[i++])&0xFF];
|
||||
return ~C;
|
||||
}
|
||||
|
||||
function crc32_str(str/*:string*/, seed/*:?number*/)/*:number*/ {
|
||||
var C = seed/*:: ? 0 : 0 */ ^ -1;
|
||||
for(var i = 0, L = str.length, c = 0, d = 0; i < L;) {
|
||||
c = str.charCodeAt(i++);
|
||||
if(c < 0x80) {
|
||||
C = (C>>>8) ^ T0[(C^c)&0xFF];
|
||||
} else if(c < 0x800) {
|
||||
C = (C>>>8) ^ T0[(C ^ (192|((c>>6)&31)))&0xFF];
|
||||
C = (C>>>8) ^ T0[(C ^ (128|(c&63)))&0xFF];
|
||||
} else if(c >= 0xD800 && c < 0xE000) {
|
||||
c = (c&1023)+64; d = str.charCodeAt(i++)&1023;
|
||||
C = (C>>>8) ^ T0[(C ^ (240|((c>>8)&7)))&0xFF];
|
||||
C = (C>>>8) ^ T0[(C ^ (128|((c>>2)&63)))&0xFF];
|
||||
C = (C>>>8) ^ T0[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF];
|
||||
C = (C>>>8) ^ T0[(C ^ (128|(d&63)))&0xFF];
|
||||
} else {
|
||||
C = (C>>>8) ^ T0[(C ^ (224|((c>>12)&15)))&0xFF];
|
||||
C = (C>>>8) ^ T0[(C ^ (128|((c>>6)&63)))&0xFF];
|
||||
C = (C>>>8) ^ T0[(C ^ (128|(c&63)))&0xFF];
|
||||
}
|
||||
}
|
||||
return ~C;
|
||||
}
|
||||
CRC32.table = T0;
|
||||
CRC32.bstr = crc32_bstr;
|
||||
CRC32.buf = crc32_buf;
|
||||
CRC32.str = crc32_str;
|
||||
return CRC32;
|
||||
})();
|
|
@ -1,3 +1,3 @@
|
|||
/* [MS-CFB] v20130118 */
|
||||
var CFB = (function _CFB(){
|
||||
/* [MS-CFB] v20171201 */
|
||||
var CFB = /*#__PURE__*/(function _CFB(){
|
||||
var exports/*:CFBModule*/ = /*::(*/{}/*:: :any)*/;
|
||||
|
|
|
@ -1 +1 @@
|
|||
exports.version = '1.0.2';
|
||||
exports.version = '1.2.2';
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/* -------------------------------------------------------------------------- */
|
||||
/* DOS Date format:
|
||||
high|YYYYYYYm.mmmddddd.HHHHHMMM.MMMSSSSS|low
|
||||
add 1980 to stored year
|
||||
stored second should be doubled
|
||||
*/
|
||||
|
||||
/* write JS date to buf as a DOS date */
|
||||
function write_dos_date(buf/*:CFBlob*/, date/*:Date|string*/) {
|
||||
if(typeof date === "string") date = new Date(date);
|
||||
var hms/*:number*/ = date.getHours();
|
||||
hms = hms << 6 | date.getMinutes();
|
||||
hms = hms << 5 | (date.getSeconds()>>>1);
|
||||
buf.write_shift(2, hms);
|
||||
var ymd/*:number*/ = (date.getFullYear() - 1980);
|
||||
ymd = ymd << 4 | (date.getMonth()+1);
|
||||
ymd = ymd << 5 | date.getDate();
|
||||
buf.write_shift(2, ymd);
|
||||
}
|
||||
|
||||
/* read four bytes from buf and interpret as a DOS date */
|
||||
function parse_dos_date(buf/*:CFBlob*/)/*:Date*/ {
|
||||
var hms = buf.read_shift(2) & 0xFFFF;
|
||||
var ymd = buf.read_shift(2) & 0xFFFF;
|
||||
var val = new Date();
|
||||
var d = ymd & 0x1F; ymd >>>= 5;
|
||||
var m = ymd & 0x0F; ymd >>>= 4;
|
||||
val.setMilliseconds(0);
|
||||
val.setFullYear(ymd + 1980);
|
||||
val.setMonth(m-1);
|
||||
val.setDate(d);
|
||||
var S = hms & 0x1F; hms >>>= 5;
|
||||
var M = hms & 0x3F; hms >>>= 6;
|
||||
val.setHours(hms);
|
||||
val.setMinutes(M);
|
||||
val.setSeconds(S<<1);
|
||||
return val;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
function parse_extra_field(blob/*:CFBlob*/)/*:any*/ {
|
||||
prep_blob(blob, 0);
|
||||
var o = /*::(*/{}/*:: :any)*/;
|
||||
var flags = 0;
|
||||
while(blob.l <= blob.length - 4) {
|
||||
var type = blob.read_shift(2);
|
||||
var sz = blob.read_shift(2), tgt = blob.l + sz;
|
||||
var p = {};
|
||||
switch(type) {
|
||||
/* UNIX-style Timestamps */
|
||||
case 0x5455: {
|
||||
flags = blob.read_shift(1);
|
||||
if(flags & 1) p.mtime = blob.read_shift(4);
|
||||
/* for some reason, CD flag corresponds to LFH */
|
||||
if(sz > 5) {
|
||||
if(flags & 2) p.atime = blob.read_shift(4);
|
||||
if(flags & 4) p.ctime = blob.read_shift(4);
|
||||
}
|
||||
if(p.mtime) p.mt = new Date(p.mtime*1000);
|
||||
}
|
||||
break;
|
||||
}
|
||||
blob.l = tgt;
|
||||
o[type] = p;
|
||||
}
|
||||
return o;
|
||||
}
|
|
@ -1,4 +1,7 @@
|
|||
function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
|
||||
if(file[0] == 0x50 && file[1] == 0x4b) return parse_zip(file, options);
|
||||
if((file[0] | 0x20) == 0x6d && (file[1]|0x20) == 0x69) return parse_mad(file, options);
|
||||
if(file.length < 512) throw new Error("CFB file size " + file.length + " < 512");
|
||||
var mver = 3;
|
||||
var ssz = 512;
|
||||
var nmfs = 0; // number of mini FAT sectors
|
||||
|
@ -18,6 +21,8 @@ var mv = check_get_mver(blob);
|
|||
mver = mv[0];
|
||||
switch(mver) {
|
||||
case 3: ssz = 512; break; case 4: ssz = 4096; break;
|
||||
case 0: if(mv[1] == 0) return parse_zip(file, options);
|
||||
/* falls through */
|
||||
default: throw new Error("Major Version: Expected 3 or 4 saw " + mver);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
/* [MS-CFB] 2.2 Compound File Header -- read up to major version */
|
||||
function check_get_mver(blob/*:CFBlob*/)/*:[number, number]*/ {
|
||||
if(blob[blob.l] == 0x50 && blob[blob.l + 1] == 0x4b) return [0, 0];
|
||||
// header signature 8
|
||||
blob.chk(HEADER_SIGNATURE, 'Header Signature: ');
|
||||
|
||||
// clsid 16
|
||||
blob.chk(HEADER_CLSID, 'CLSID: ');
|
||||
//blob.chk(HEADER_CLSID, 'CLSID: ');
|
||||
blob.l += 16;
|
||||
|
||||
// minor version 2
|
||||
var mver/*:number*/ = blob.read_shift(2, 'u');
|
||||
|
|
|
@ -13,23 +13,22 @@ function build_full_paths(FI/*:CFBFileIndex*/, FP/*:Array<string>*/, Paths/*:Arr
|
|||
if(R !== -1 && dad[R] !== R) dad[i] = dad[R];
|
||||
}
|
||||
if(C !== -1 /*NOSTREAM*/) dad[C] = i;
|
||||
if(L !== -1) { dad[L] = dad[i]; q.push(L); }
|
||||
if(R !== -1) { dad[R] = dad[i]; q.push(R); }
|
||||
if(L !== -1 && i != dad[i]) { dad[L] = dad[i]; if(q.lastIndexOf(L) < j) q.push(L); }
|
||||
if(R !== -1 && i != dad[i]) { dad[R] = dad[i]; if(q.lastIndexOf(R) < j) q.push(R); }
|
||||
}
|
||||
for(i=1; i !== pl; ++i) if(dad[i] === i) {
|
||||
for(i=1; i < pl; ++i) if(dad[i] === i) {
|
||||
if(R !== -1 /*NOSTREAM*/ && dad[R] !== R) dad[i] = dad[R];
|
||||
else if(L !== -1 && dad[L] !== L) dad[i] = dad[L];
|
||||
}
|
||||
|
||||
for(i=1; i < pl; ++i) {
|
||||
if(FI[i].type === 0 /* unknown */) continue;
|
||||
j = dad[i];
|
||||
if(j === 0) FP[i] = FP[0] + "/" + FP[i];
|
||||
else while(j !== 0 && j !== dad[j]) {
|
||||
FP[i] = FP[j] + "/" + FP[i];
|
||||
j = i;
|
||||
if(j != dad[j]) do {
|
||||
j = dad[j];
|
||||
}
|
||||
dad[i] = 0;
|
||||
FP[i] = FP[j] + "/" + FP[i];
|
||||
} while (j !== 0 && -1 !== dad[j] && j != dad[j]);
|
||||
dad[i] = -1;
|
||||
}
|
||||
|
||||
FP[0] += "/";
|
||||
|
|
|
@ -11,7 +11,7 @@ function sleuth_fat(idx/*:number*/, cnt/*:number*/, sectors/*:Array<RawBytes>*/,
|
|||
if((q = __readInt32LE(sector,i*4)) === ENDOFCHAIN) break;
|
||||
fat_addrs.push(q);
|
||||
}
|
||||
sleuth_fat(__readInt32LE(sector,ssz-4),cnt - 1, sectors, ssz, fat_addrs);
|
||||
if(cnt >= 1) sleuth_fat(__readInt32LE(sector,ssz-4),cnt - 1, sectors, ssz, fat_addrs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,9 @@ function make_sector_list(sectors/*:Array<RawBytes>*/, dir_start/*:number*/, fat
|
|||
k = (i + dir_start); if(k >= sl) k-=sl;
|
||||
if(chkd[k]) continue;
|
||||
buf_chain = [];
|
||||
var seen = [];
|
||||
for(j=k; j>=0;) {
|
||||
seen[j] = true;
|
||||
chkd[j] = true;
|
||||
buf[buf.length] = j;
|
||||
buf_chain.push(sectors[j]);
|
||||
|
@ -52,6 +54,7 @@ function make_sector_list(sectors/*:Array<RawBytes>*/, dir_start/*:number*/, fat
|
|||
if(ssz < 4 + jj) throw new Error("FAT boundary crossed: " + j + " 4 "+ssz);
|
||||
if(!sectors[addr]) break;
|
||||
j = __readInt32LE(sectors[addr], jj);
|
||||
if(seen[j]) break;
|
||||
}
|
||||
sector_list[k] = ({nodes: buf, data:__toBuffer([buf_chain])}/*:SectorEntry*/);
|
||||
}
|
||||
|
|
|
@ -37,14 +37,14 @@ function read_directory(dir_start/*:number*/, sector_list/*:SectorList*/, sector
|
|||
if(sector_list[o.start] === undefined) sector_list[o.start] = get_sector_list(sectors, o.start, sector_list.fat_addrs, sector_list.ssz);
|
||||
sector_list[o.start].name = o.name;
|
||||
o.content = (sector_list[o.start].data.slice(0,o.size)/*:any*/);
|
||||
prep_blob(o.content, 0);
|
||||
} else {
|
||||
o.storage = 'minifat';
|
||||
if(minifat_store !== ENDOFCHAIN && o.start !== ENDOFCHAIN && sector_list[minifat_store]) {
|
||||
if(o.size < 0) o.size = 0;
|
||||
else if(minifat_store !== ENDOFCHAIN && o.start !== ENDOFCHAIN && sector_list[minifat_store]) {
|
||||
o.content = get_mfat_entry(o, sector_list[minifat_store].data, (sector_list[mini]||{}).data);
|
||||
prep_blob(o.content, 0);
|
||||
}
|
||||
}
|
||||
if(o.content) prep_blob(o.content, 0);
|
||||
files[name] = o;
|
||||
FileIndex.push(o);
|
||||
}
|
||||
|
|
|
@ -4,9 +4,13 @@ function read_file(filename/*:string*/, options/*:CFBReadOpts*/) {
|
|||
}
|
||||
|
||||
function read(blob/*:RawBytes|string*/, options/*:CFBReadOpts*/) {
|
||||
switch(options && options.type || "base64") {
|
||||
var type = options && options.type;
|
||||
if(!type) {
|
||||
if(has_buf && Buffer.isBuffer(blob)) type = "buffer";
|
||||
}
|
||||
switch(type || "base64") {
|
||||
case "file": /*:: if(typeof blob !== 'string') throw "Must pass a filename when type='file'"; */return read_file(blob, options);
|
||||
case "base64": /*:: if(typeof blob !== 'string') throw "Must pass a base64-encoded binary string when type='file'"; */return parse(s2a(Base64.decode(blob)), options);
|
||||
case "base64": /*:: if(typeof blob !== 'string') throw "Must pass a base64-encoded binary string when type='file'"; */return parse(s2a(Base64_decode(blob)), options);
|
||||
case "binary": /*:: if(typeof blob !== 'string') throw "Must pass a binary string when type='file'"; */return parse(s2a(blob), options);
|
||||
}
|
||||
return parse(/*::typeof blob == 'string' ? new Buffer(blob, 'utf-8') : */blob, options);
|
||||
|
|
|
@ -1,19 +1,31 @@
|
|||
var now = new Date(1987, 1, 19), j = 0;
|
||||
// Track which names exist
|
||||
var fullPaths = Object.create ? Object.create(null) : {};
|
||||
var data/*:Array<[string, CFBEntry]>*/ = [];
|
||||
for(i = 0; i < cfb.FullPaths.length; ++i) {
|
||||
fullPaths[cfb.FullPaths[i]] = true;
|
||||
if(cfb.FileIndex[i].type === 0) continue;
|
||||
data.push([cfb.FullPaths[i], cfb.FileIndex[i]]);
|
||||
}
|
||||
for(i = 0; i < data.length; ++i) {
|
||||
var dad = dirname(data[i][0]);
|
||||
s = false;
|
||||
for(j = 0; j < data.length; ++j) if(data[j][0] === dad) s = true;
|
||||
if(!s) data.push([dad, ({
|
||||
name: filename(dad).replace("/",""),
|
||||
type: 1,
|
||||
clsid: HEADER_CLSID,
|
||||
ct: now, mt: now,
|
||||
content: null
|
||||
}/*:any*/)]);
|
||||
s = fullPaths[dad];
|
||||
while(!s) {
|
||||
while(dirname(dad) && !fullPaths[dirname(dad)]) dad = dirname(dad);
|
||||
|
||||
data.push([dad, ({
|
||||
name: filename(dad).replace("/",""),
|
||||
type: 1,
|
||||
clsid: HEADER_CLSID,
|
||||
ct: now, mt: now,
|
||||
content: null
|
||||
}/*:any*/)]);
|
||||
|
||||
// Add name to set
|
||||
fullPaths[dad] = true;
|
||||
|
||||
dad = dirname(data[i][0]);
|
||||
s = fullPaths[dad];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
function _write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ {
|
||||
function _write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|string*/ {
|
||||
var _opts = options || {};
|
||||
/* MAD is order-sensitive, skip rebuild and sort */
|
||||
if(_opts.fileType == 'mad') return write_mad(cfb, _opts);
|
||||
rebuild_cfb(cfb);
|
||||
switch(_opts.fileType) {
|
||||
case 'zip': return write_zip(cfb, _opts);
|
||||
//case 'mad': return write_mad(cfb, _opts);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
for(var i = 0; i < cfb.FileIndex.length; ++i) {
|
||||
var file = cfb.FileIndex[i];
|
||||
if(!file.content) continue;
|
||||
/*:: if(file.content == null) throw new Error("unreachable"); */
|
||||
var flen = file.content.length;
|
||||
if(flen > 0){
|
||||
if(flen < 0x1000) mini_size += (flen + 0x3F) >> 6;
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
file = cfb.FileIndex[i];
|
||||
if(i === 0) file.start = file.size ? file.start - 1 : ENDOFCHAIN;
|
||||
var _nm/*:string*/ = (i === 0 && _opts.root) || file.name;
|
||||
if(_nm.length > 31) {
|
||||
console.error("Name " + _nm + " will be truncated to " + _nm.slice(0,31));
|
||||
_nm = _nm.slice(0, 31);
|
||||
}
|
||||
flen = 2*(_nm.length+1);
|
||||
o.write_shift(64, _nm, "utf16le");
|
||||
o.write_shift(2, flen);
|
||||
|
|
|
@ -3,7 +3,13 @@
|
|||
/*:: if(!file.content) throw new Error("unreachable"); */
|
||||
if(file.size >= 0x1000) {
|
||||
o.l = (file.start+1) << 9;
|
||||
for(j = 0; j < file.size; ++j) o.write_shift(1, file.content[j]);
|
||||
for(; j & 0x1FF; ++j) o.write_shift(1, 0);
|
||||
if (has_buf && Buffer.isBuffer(file.content)) {
|
||||
file.content.copy(o, o.l, 0, file.size);
|
||||
// o is a 0-filled Buffer so just set next offset
|
||||
o.l += (file.size + 511) & -512;
|
||||
} else {
|
||||
for(j = 0; j < file.size; ++j) o.write_shift(1, file.content[j]);
|
||||
for(; j & 0x1FF; ++j) o.write_shift(1, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,19 @@
|
|||
file = cfb.FileIndex[i];
|
||||
/*:: if(!file.content) throw new Error("unreachable"); */
|
||||
if(file.size > 0 && file.size < 0x1000) {
|
||||
for(j = 0; j < file.size; ++j) o.write_shift(1, file.content[j]);
|
||||
for(; j & 0x3F; ++j) o.write_shift(1, 0);
|
||||
if (has_buf && Buffer.isBuffer(file.content)) {
|
||||
file.content.copy(o, o.l, 0, file.size);
|
||||
// o is a 0-filled Buffer so just set next offset
|
||||
o.l += (file.size + 63) & -64;
|
||||
} else {
|
||||
for(j = 0; j < file.size; ++j) o.write_shift(1, file.content[j]);
|
||||
for(; j & 0x3F; ++j) o.write_shift(1, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
while(o.l < o.length) o.write_shift(1, 0);
|
||||
if (has_buf) {
|
||||
o.l = o.length;
|
||||
} else {
|
||||
// When using Buffer, already 0-filled
|
||||
while(o.l < o.length) o.write_shift(1, 0);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* [MS-CFB] 2.6.4 (Unicode 3.0.1 case conversion) */
|
||||
function find(cfb/*:CFBContainer*/, path/*:string*/)/*:?CFBEntry*/ {
|
||||
//return cfb.find(path);
|
||||
var UCFullPaths/*:Array<string>*/ = cfb.FullPaths.map(function(x) { return x.toUpperCase(); });
|
||||
var UCPaths/*:Array<string>*/ = UCFullPaths.map(function(x) { var y = x.split("/"); return y[y.length - (x.slice(-1) == "/" ? 2 : 1)]; });
|
||||
var k/*:boolean*/ = false;
|
||||
|
@ -10,10 +9,12 @@ function find(cfb/*:CFBContainer*/, path/*:string*/)/*:?CFBEntry*/ {
|
|||
var w/*:number*/ = k === true ? UCFullPaths.indexOf(UCPath) : UCPaths.indexOf(UCPath);
|
||||
if(w !== -1) return cfb.FileIndex[w];
|
||||
|
||||
UCPath = UCPath.replace(chr0,'').replace(chr1,'!');
|
||||
var m = !UCPath.match(chr1);
|
||||
UCPath = UCPath.replace(chr0,'');
|
||||
if(m) UCPath = UCPath.replace(chr1,'!');
|
||||
for(w = 0; w < UCFullPaths.length; ++w) {
|
||||
if(UCFullPaths[w].replace(chr0,'').replace(chr1,'!') == UCPath) return cfb.FileIndex[w];
|
||||
if(UCPaths[w].replace(chr0,'').replace(chr1,'!') == UCPath) return cfb.FileIndex[w];
|
||||
if((m ? UCFullPaths[w].replace(chr1,'!') : UCFullPaths[w]).replace(chr0,'') == UCPath) return cfb.FileIndex[w];
|
||||
if((m ? UCPaths[w].replace(chr1,'!') : UCPaths[w]).replace(chr0,'') == UCPath) return cfb.FileIndex[w];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -13,10 +13,13 @@ function a2s(o/*:RawBytes*/)/*:string*/ {
|
|||
|
||||
function write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|string*/ {
|
||||
var o = _write(cfb, options);
|
||||
switch(options && options.type) {
|
||||
switch(options && options.type || "buffer") {
|
||||
case "file": get_fs(); fs.writeFileSync(options.filename, (o/*:any*/)); return o;
|
||||
case "binary": return a2s(o);
|
||||
case "base64": return Base64.encode(a2s(o));
|
||||
case "binary": return typeof o == "string" ? o : a2s(o);
|
||||
case "base64": return Base64_encode(typeof o == "string" ? o : a2s(o));
|
||||
case "buffer": if(has_buf) return Buffer.isBuffer(o) ? o : Buffer_from(o);
|
||||
/* falls through */
|
||||
case "array": return typeof o == "string" ? s2a(o) : o;
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/* node < 8.1 zlib does not expose bytesRead, so default to pure JS */
|
||||
var _zlib;
|
||||
function use_zlib(zlib) { try {
|
||||
var InflateRaw = zlib.InflateRaw;
|
||||
var InflRaw = new InflateRaw();
|
||||
InflRaw._processChunk(new Uint8Array([3, 0]), InflRaw._finishFlushFlag);
|
||||
if(InflRaw.bytesRead) _zlib = zlib;
|
||||
else throw new Error("zlib does not expose bytesRead");
|
||||
} catch(e) {console.error("cannot use native zlib: " + (e.message || e)); } }
|
||||
|
||||
function _inflateRawSync(payload, usz) {
|
||||
if(!_zlib) return _inflate(payload, usz);
|
||||
var InflateRaw = _zlib.InflateRaw;
|
||||
var InflRaw = new InflateRaw();
|
||||
var out = InflRaw._processChunk(payload.slice(payload.l), InflRaw._finishFlushFlag);
|
||||
payload.l += InflRaw.bytesRead;
|
||||
return out;
|
||||
}
|
||||
|
||||
function _deflateRawSync(payload) {
|
||||
return _zlib ? _zlib.deflateRawSync(payload) : _deflate(payload);
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
var CLEN_ORDER = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];
|
||||
|
||||
/* LEN_ID = [ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285 ]; */
|
||||
var LEN_LN = [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13 , 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 ];
|
||||
|
||||
/* DST_ID = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]; */
|
||||
var DST_LN = [ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 ];
|
||||
|
||||
function bit_swap_8(n) { var t = (((((n<<1)|(n<<11)) & 0x22110) | (((n<<5)|(n<<15)) & 0x88440))); return ((t>>16) | (t>>8) |t)&0xFF; }
|
||||
|
||||
var use_typed_arrays = typeof Uint8Array !== 'undefined';
|
||||
|
||||
var bitswap8 = use_typed_arrays ? new Uint8Array(1<<8) : [];
|
||||
for(var q = 0; q < (1<<8); ++q) bitswap8[q] = bit_swap_8(q);
|
||||
|
||||
function bit_swap_n(n, b) {
|
||||
var rev = bitswap8[n & 0xFF];
|
||||
if(b <= 8) return rev >>> (8-b);
|
||||
rev = (rev << 8) | bitswap8[(n>>8)&0xFF];
|
||||
if(b <= 16) return rev >>> (16-b);
|
||||
rev = (rev << 8) | bitswap8[(n>>16)&0xFF];
|
||||
return rev >>> (24-b);
|
||||
}
|
||||
|
||||
/* helpers for unaligned bit reads */
|
||||
function read_bits_2(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 6 ? 0 : buf[h+1]<<8))>>>w)& 0x03; }
|
||||
function read_bits_3(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 5 ? 0 : buf[h+1]<<8))>>>w)& 0x07; }
|
||||
function read_bits_4(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 4 ? 0 : buf[h+1]<<8))>>>w)& 0x0F; }
|
||||
function read_bits_5(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 3 ? 0 : buf[h+1]<<8))>>>w)& 0x1F; }
|
||||
function read_bits_7(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 1 ? 0 : buf[h+1]<<8))>>>w)& 0x7F; }
|
||||
|
||||
/* works up to n = 3 * 8 + 1 = 25 */
|
||||
function read_bits_n(buf, bl, n) {
|
||||
var w = (bl&7), h = (bl>>>3), f = ((1<<n)-1);
|
||||
var v = buf[h] >>> w;
|
||||
if(n < 8 - w) return v & f;
|
||||
v |= buf[h+1]<<(8-w);
|
||||
if(n < 16 - w) return v & f;
|
||||
v |= buf[h+2]<<(16-w);
|
||||
if(n < 24 - w) return v & f;
|
||||
v |= buf[h+3]<<(24-w);
|
||||
return v & f;
|
||||
}
|
||||
|
||||
/* helpers for unaligned bit writes */
|
||||
function write_bits_3(buf, bl, v) { var w = bl & 7, h = bl >>> 3;
|
||||
if(w <= 5) buf[h] |= (v & 7) << w;
|
||||
else {
|
||||
buf[h] |= (v << w) & 0xFF;
|
||||
buf[h+1] = (v&7) >> (8-w);
|
||||
}
|
||||
return bl + 3;
|
||||
}
|
||||
|
||||
function write_bits_1(buf, bl, v) {
|
||||
var w = bl & 7, h = bl >>> 3;
|
||||
v = (v&1) << w;
|
||||
buf[h] |= v;
|
||||
return bl + 1;
|
||||
}
|
||||
function write_bits_8(buf, bl, v) {
|
||||
var w = bl & 7, h = bl >>> 3;
|
||||
v <<= w;
|
||||
buf[h] |= v & 0xFF; v >>>= 8;
|
||||
buf[h+1] = v;
|
||||
return bl + 8;
|
||||
}
|
||||
function write_bits_16(buf, bl, v) {
|
||||
var w = bl & 7, h = bl >>> 3;
|
||||
v <<= w;
|
||||
buf[h] |= v & 0xFF; v >>>= 8;
|
||||
buf[h+1] = v & 0xFF;
|
||||
buf[h+2] = v >>> 8;
|
||||
return bl + 16;
|
||||
}
|
||||
|
||||
/* until ArrayBuffer#realloc is a thing, fake a realloc */
|
||||
function realloc(b, sz/*:number*/) {
|
||||
var L = b.length, M = 2*L > sz ? 2*L : sz + 5, i = 0;
|
||||
if(L >= sz) return b;
|
||||
if(has_buf) {
|
||||
var o = new_unsafe_buf(M);
|
||||
// $FlowIgnore
|
||||
if(b.copy) b.copy(o);
|
||||
else for(; i < b.length; ++i) o[i] = b[i];
|
||||
return o;
|
||||
} else if(use_typed_arrays) {
|
||||
var a = new Uint8Array(M);
|
||||
if(a.set) a.set(b);
|
||||
else for(; i < L; ++i) a[i] = b[i];
|
||||
return a;
|
||||
}
|
||||
b.length = M;
|
||||
return b;
|
||||
}
|
||||
|
||||
/* zero-filled arrays for older browsers */
|
||||
function zero_fill_array(n) {
|
||||
var o = new Array(n);
|
||||
for(var i = 0; i < n; ++i) o[i] = 0;
|
||||
return o;
|
||||
}
|
||||
|
||||
/* build tree (used for literals and lengths) */
|
||||
function build_tree(clens, cmap, MAX/*:number*/)/*:number*/ {
|
||||
var maxlen = 1, w = 0, i = 0, j = 0, ccode = 0, L = clens.length;
|
||||
|
||||
var bl_count = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
|
||||
for(i = 0; i < 32; ++i) bl_count[i] = 0;
|
||||
|
||||
for(i = L; i < MAX; ++i) clens[i] = 0;
|
||||
L = clens.length;
|
||||
|
||||
var ctree = use_typed_arrays ? new Uint16Array(L) : zero_fill_array(L); // []
|
||||
|
||||
/* build code tree */
|
||||
for(i = 0; i < L; ++i) {
|
||||
bl_count[(w = clens[i])]++;
|
||||
if(maxlen < w) maxlen = w;
|
||||
ctree[i] = 0;
|
||||
}
|
||||
bl_count[0] = 0;
|
||||
for(i = 1; i <= maxlen; ++i) bl_count[i+16] = (ccode = (ccode + bl_count[i-1])<<1);
|
||||
for(i = 0; i < L; ++i) {
|
||||
ccode = clens[i];
|
||||
if(ccode != 0) ctree[i] = bl_count[ccode+16]++;
|
||||
}
|
||||
|
||||
/* cmap[maxlen + 4 bits] = (off&15) + (lit<<4) reverse mapping */
|
||||
var cleni = 0;
|
||||
for(i = 0; i < L; ++i) {
|
||||
cleni = clens[i];
|
||||
if(cleni != 0) {
|
||||
ccode = bit_swap_n(ctree[i], maxlen)>>(maxlen-cleni);
|
||||
for(j = (1<<(maxlen + 4 - cleni)) - 1; j>=0; --j)
|
||||
cmap[ccode|(j<<cleni)] = (cleni&15) | (i<<4);
|
||||
}
|
||||
}
|
||||
return maxlen;
|
||||
}
|
||||
|
||||
/* Fixed Huffman */
|
||||
var fix_lmap = use_typed_arrays ? new Uint16Array(512) : zero_fill_array(512);
|
||||
var fix_dmap = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
|
||||
if(!use_typed_arrays) {
|
||||
for(var i = 0; i < 512; ++i) fix_lmap[i] = 0;
|
||||
for(i = 0; i < 32; ++i) fix_dmap[i] = 0;
|
||||
}
|
||||
(function() {
|
||||
var dlens/*:Array<number>*/ = [];
|
||||
var i = 0;
|
||||
for(;i<32; i++) dlens.push(5);
|
||||
build_tree(dlens, fix_dmap, 32);
|
||||
|
||||
var clens/*:Array<number>*/ = [];
|
||||
i = 0;
|
||||
for(; i<=143; i++) clens.push(8);
|
||||
for(; i<=255; i++) clens.push(9);
|
||||
for(; i<=279; i++) clens.push(7);
|
||||
for(; i<=287; i++) clens.push(8);
|
||||
build_tree(clens, fix_lmap, 288);
|
||||
})();
|
|
@ -0,0 +1,117 @@
|
|||
var _deflateRaw = /*#__PURE__*/(function _deflateRawIIFE() {
|
||||
var DST_LN_RE = use_typed_arrays ? new Uint8Array(0x8000) : [];
|
||||
var j = 0, k = 0;
|
||||
for(; j < DST_LN.length - 1; ++j) {
|
||||
for(; k < DST_LN[j+1]; ++k) DST_LN_RE[k] = j;
|
||||
}
|
||||
for(;k < 32768; ++k) DST_LN_RE[k] = 29;
|
||||
|
||||
var LEN_LN_RE = use_typed_arrays ? new Uint8Array(0x103) : [];
|
||||
for(j = 0, k = 0; j < LEN_LN.length - 1; ++j) {
|
||||
for(; k < LEN_LN[j+1]; ++k) LEN_LN_RE[k] = j;
|
||||
}
|
||||
|
||||
function write_stored(data, out) {
|
||||
var boff = 0;
|
||||
while(boff < data.length) {
|
||||
var L = Math.min(0xFFFF, data.length - boff);
|
||||
var h = boff + L == data.length;
|
||||
out.write_shift(1, +h);
|
||||
out.write_shift(2, L);
|
||||
out.write_shift(2, (~L) & 0xFFFF);
|
||||
while(L-- > 0) out[out.l++] = data[boff++];
|
||||
}
|
||||
return out.l;
|
||||
}
|
||||
|
||||
/* Fixed Huffman */
|
||||
function write_huff_fixed(data, out) {
|
||||
var bl = 0;
|
||||
var boff = 0;
|
||||
var addrs = use_typed_arrays ? new Uint16Array(0x8000) : [];
|
||||
while(boff < data.length) {
|
||||
var L = /* data.length - boff; */ Math.min(0xFFFF, data.length - boff);
|
||||
|
||||
/* write a stored block for short data */
|
||||
if(L < 10) {
|
||||
bl = write_bits_3(out, bl, +!!(boff + L == data.length)); // jshint ignore:line
|
||||
if(bl & 7) bl += 8 - (bl & 7);
|
||||
out.l = (bl / 8) | 0;
|
||||
out.write_shift(2, L);
|
||||
out.write_shift(2, (~L) & 0xFFFF);
|
||||
while(L-- > 0) out[out.l++] = data[boff++];
|
||||
bl = out.l * 8;
|
||||
continue;
|
||||
}
|
||||
|
||||
bl = write_bits_3(out, bl, +!!(boff + L == data.length) + 2); // jshint ignore:line
|
||||
var hash = 0;
|
||||
while(L-- > 0) {
|
||||
var d = data[boff];
|
||||
hash = ((hash << 5) ^ d) & 0x7FFF;
|
||||
|
||||
var match = -1, mlen = 0;
|
||||
|
||||
if((match = addrs[hash])) {
|
||||
match |= boff & ~0x7FFF;
|
||||
if(match > boff) match -= 0x8000;
|
||||
if(match < boff) while(data[match + mlen] == data[boff + mlen] && mlen < 250) ++mlen;
|
||||
}
|
||||
|
||||
if(mlen > 2) {
|
||||
/* Copy Token */
|
||||
d = LEN_LN_RE[mlen];
|
||||
if(d <= 22) bl = write_bits_8(out, bl, bitswap8[d+1]>>1) - 1;
|
||||
else {
|
||||
write_bits_8(out, bl, 3);
|
||||
bl += 5;
|
||||
write_bits_8(out, bl, bitswap8[d-23]>>5);
|
||||
bl += 3;
|
||||
}
|
||||
var len_eb = (d < 8) ? 0 : ((d - 4)>>2);
|
||||
if(len_eb > 0) {
|
||||
write_bits_16(out, bl, mlen - LEN_LN[d]);
|
||||
bl += len_eb;
|
||||
}
|
||||
|
||||
d = DST_LN_RE[boff - match];
|
||||
bl = write_bits_8(out, bl, bitswap8[d]>>3);
|
||||
bl -= 3;
|
||||
|
||||
var dst_eb = d < 4 ? 0 : (d-2)>>1;
|
||||
if(dst_eb > 0) {
|
||||
write_bits_16(out, bl, boff - match - DST_LN[d]);
|
||||
bl += dst_eb;
|
||||
}
|
||||
for(var q = 0; q < mlen; ++q) {
|
||||
addrs[hash] = boff & 0x7FFF;
|
||||
hash = ((hash << 5) ^ data[boff]) & 0x7FFF;
|
||||
++boff;
|
||||
}
|
||||
L-= mlen - 1;
|
||||
} else {
|
||||
/* Literal Token */
|
||||
if(d <= 143) d = d + 48;
|
||||
else bl = write_bits_1(out, bl, 1);
|
||||
bl = write_bits_8(out, bl, bitswap8[d]);
|
||||
addrs[hash] = boff & 0x7FFF;
|
||||
++boff;
|
||||
}
|
||||
}
|
||||
|
||||
bl = write_bits_8(out, bl, 0) - 1;
|
||||
}
|
||||
out.l = ((bl + 7)/8)|0;
|
||||
return out.l;
|
||||
}
|
||||
return function _deflateRaw(data, out) {
|
||||
if(data.length < 8) return write_stored(data, out);
|
||||
return write_huff_fixed(data, out);
|
||||
};
|
||||
})();
|
||||
|
||||
function _deflate(data) {
|
||||
var buf = new_buf(50+Math.floor(data.length*1.1));
|
||||
var off = _deflateRaw(data, buf);
|
||||
return buf.slice(0, off);
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
/* modified inflate function also moves original read head */
|
||||
|
||||
var dyn_lmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
|
||||
var dyn_dmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
|
||||
var dyn_cmap = use_typed_arrays ? new Uint16Array(128) : zero_fill_array(128);
|
||||
var dyn_len_1 = 1, dyn_len_2 = 1;
|
||||
|
||||
/* 5.5.3 Expanding Huffman Codes */
|
||||
function dyn(data, boff/*:number*/) {
|
||||
/* nomenclature from RFC1951 refers to bit values; these are offset by the implicit constant */
|
||||
var _HLIT = read_bits_5(data, boff) + 257; boff += 5;
|
||||
var _HDIST = read_bits_5(data, boff) + 1; boff += 5;
|
||||
var _HCLEN = read_bits_4(data, boff) + 4; boff += 4;
|
||||
var w = 0;
|
||||
|
||||
/* grab and store code lengths */
|
||||
var clens = use_typed_arrays ? new Uint8Array(19) : zero_fill_array(19);
|
||||
var ctree = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
|
||||
var maxlen = 1;
|
||||
var bl_count = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
|
||||
var next_code = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
|
||||
var L = clens.length; /* 19 */
|
||||
for(var i = 0; i < _HCLEN; ++i) {
|
||||
clens[CLEN_ORDER[i]] = w = read_bits_3(data, boff);
|
||||
if(maxlen < w) maxlen = w;
|
||||
bl_count[w]++;
|
||||
boff += 3;
|
||||
}
|
||||
|
||||
/* build code tree */
|
||||
var ccode = 0;
|
||||
bl_count[0] = 0;
|
||||
for(i = 1; i <= maxlen; ++i) next_code[i] = ccode = (ccode + bl_count[i-1])<<1;
|
||||
for(i = 0; i < L; ++i) if((ccode = clens[i]) != 0) ctree[i] = next_code[ccode]++;
|
||||
/* cmap[7 bits from stream] = (off&7) + (lit<<3) */
|
||||
var cleni = 0;
|
||||
for(i = 0; i < L; ++i) {
|
||||
cleni = clens[i];
|
||||
if(cleni != 0) {
|
||||
ccode = bitswap8[ctree[i]]>>(8-cleni);
|
||||
for(var j = (1<<(7-cleni))-1; j>=0; --j) dyn_cmap[ccode|(j<<cleni)] = (cleni&7) | (i<<3);
|
||||
}
|
||||
}
|
||||
|
||||
/* read literal and dist codes at once */
|
||||
var hcodes/*:Array<number>*/ = [];
|
||||
maxlen = 1;
|
||||
for(; hcodes.length < _HLIT + _HDIST;) {
|
||||
ccode = dyn_cmap[read_bits_7(data, boff)];
|
||||
boff += ccode & 7;
|
||||
switch((ccode >>>= 3)) {
|
||||
case 16:
|
||||
w = 3 + read_bits_2(data, boff); boff += 2;
|
||||
ccode = hcodes[hcodes.length - 1];
|
||||
while(w-- > 0) hcodes.push(ccode);
|
||||
break;
|
||||
case 17:
|
||||
w = 3 + read_bits_3(data, boff); boff += 3;
|
||||
while(w-- > 0) hcodes.push(0);
|
||||
break;
|
||||
case 18:
|
||||
w = 11 + read_bits_7(data, boff); boff += 7;
|
||||
while(w -- > 0) hcodes.push(0);
|
||||
break;
|
||||
default:
|
||||
hcodes.push(ccode);
|
||||
if(maxlen < ccode) maxlen = ccode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* build literal / length trees */
|
||||
var h1 = hcodes.slice(0, _HLIT), h2 = hcodes.slice(_HLIT);
|
||||
for(i = _HLIT; i < 286; ++i) h1[i] = 0;
|
||||
for(i = _HDIST; i < 30; ++i) h2[i] = 0;
|
||||
dyn_len_1 = build_tree(h1, dyn_lmap, 286);
|
||||
dyn_len_2 = build_tree(h2, dyn_dmap, 30);
|
||||
return boff;
|
||||
}
|
||||
|
||||
/* return [ data, bytesRead ] */
|
||||
function inflate(data, usz/*:number*/) {
|
||||
/* shortcircuit for empty buffer [0x03, 0x00] */
|
||||
if(data[0] == 3 && !(data[1] & 0x3)) { return [new_raw_buf(usz), 2]; }
|
||||
|
||||
/* bit offset */
|
||||
var boff = 0;
|
||||
|
||||
/* header includes final bit and type bits */
|
||||
var header = 0;
|
||||
|
||||
var outbuf = new_unsafe_buf(usz ? usz : (1<<18));
|
||||
var woff = 0;
|
||||
var OL = outbuf.length>>>0;
|
||||
var max_len_1 = 0, max_len_2 = 0;
|
||||
|
||||
while((header&1) == 0) {
|
||||
header = read_bits_3(data, boff); boff += 3;
|
||||
if((header >>> 1) == 0) {
|
||||
/* Stored block */
|
||||
if(boff & 7) boff += 8 - (boff&7);
|
||||
/* 2 bytes sz, 2 bytes bit inverse */
|
||||
var sz = data[boff>>>3] | data[(boff>>>3)+1]<<8;
|
||||
boff += 32;
|
||||
/* push sz bytes */
|
||||
if(sz > 0) {
|
||||
if(!usz && OL < woff + sz) { outbuf = realloc(outbuf, woff + sz); OL = outbuf.length; }
|
||||
while(sz-- > 0) { outbuf[woff++] = data[boff>>>3]; boff += 8; }
|
||||
}
|
||||
continue;
|
||||
} else if((header >> 1) == 1) {
|
||||
/* Fixed Huffman */
|
||||
max_len_1 = 9; max_len_2 = 5;
|
||||
} else {
|
||||
/* Dynamic Huffman */
|
||||
boff = dyn(data, boff);
|
||||
max_len_1 = dyn_len_1; max_len_2 = dyn_len_2;
|
||||
}
|
||||
for(;;) { // while(true) is apparently out of vogue in modern JS circles
|
||||
if(!usz && (OL < woff + 32767)) { outbuf = realloc(outbuf, woff + 32767); OL = outbuf.length; }
|
||||
/* ingest code and move read head */
|
||||
var bits = read_bits_n(data, boff, max_len_1);
|
||||
var code = (header>>>1) == 1 ? fix_lmap[bits] : dyn_lmap[bits];
|
||||
boff += code & 15; code >>>= 4;
|
||||
/* 0-255 are literals, 256 is end of block token, 257+ are copy tokens */
|
||||
if(((code>>>8)&0xFF) === 0) outbuf[woff++] = code;
|
||||
else if(code == 256) break;
|
||||
else {
|
||||
code -= 257;
|
||||
var len_eb = (code < 8) ? 0 : ((code-4)>>2); if(len_eb > 5) len_eb = 0;
|
||||
var tgt = woff + LEN_LN[code];
|
||||
/* length extra bits */
|
||||
if(len_eb > 0) {
|
||||
tgt += read_bits_n(data, boff, len_eb);
|
||||
boff += len_eb;
|
||||
}
|
||||
|
||||
/* dist code */
|
||||
bits = read_bits_n(data, boff, max_len_2);
|
||||
code = (header>>>1) == 1 ? fix_dmap[bits] : dyn_dmap[bits];
|
||||
boff += code & 15; code >>>= 4;
|
||||
var dst_eb = (code < 4 ? 0 : (code-2)>>1);
|
||||
var dst = DST_LN[code];
|
||||
/* dist extra bits */
|
||||
if(dst_eb > 0) {
|
||||
dst += read_bits_n(data, boff, dst_eb);
|
||||
boff += dst_eb;
|
||||
}
|
||||
|
||||
/* in the common case, manual byte copy is faster than TA set / Buffer copy */
|
||||
if(!usz && OL < tgt) { outbuf = realloc(outbuf, tgt + 100); OL = outbuf.length; }
|
||||
while(woff < tgt) { outbuf[woff] = outbuf[woff - dst]; ++woff; }
|
||||
}
|
||||
}
|
||||
}
|
||||
if(usz) return [outbuf, (boff+7)>>>3];
|
||||
return [outbuf.slice(0, woff), (boff+7)>>>3];
|
||||
}
|
||||
|
||||
function _inflate(payload, usz) {
|
||||
var data = payload.slice(payload.l||0);
|
||||
var out = inflate(data, usz);
|
||||
payload.l += out[1];
|
||||
return out[0];
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
function warn_or_throw(wrn, msg) {
|
||||
if(wrn) { if(typeof console !== 'undefined') console.error(msg); }
|
||||
else throw new Error(msg);
|
||||
}
|
||||
|
||||
function parse_zip(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
|
||||
var blob/*:CFBlob*/ = /*::(*/file/*:: :any)*/;
|
||||
prep_blob(blob, 0);
|
||||
|
||||
var FileIndex/*:CFBFileIndex*/ = [], FullPaths/*:Array<string>*/ = [];
|
||||
var o = {
|
||||
FileIndex: FileIndex,
|
||||
FullPaths: FullPaths
|
||||
};
|
||||
init_cfb(o, { root: options.root });
|
||||
|
||||
/* find end of central directory, start just after signature */
|
||||
var i = blob.length - 4;
|
||||
while((blob[i] != 0x50 || blob[i+1] != 0x4b || blob[i+2] != 0x05 || blob[i+3] != 0x06) && i >= 0) --i;
|
||||
blob.l = i + 4;
|
||||
|
||||
/* parse end of central directory */
|
||||
blob.l += 4;
|
||||
var fcnt = blob.read_shift(2);
|
||||
blob.l += 6;
|
||||
var start_cd = blob.read_shift(4);
|
||||
|
||||
/* parse central directory */
|
||||
blob.l = start_cd;
|
||||
|
||||
for(i = 0; i < fcnt; ++i) {
|
||||
/* trust local file header instead of CD entry */
|
||||
blob.l += 20;
|
||||
var csz = blob.read_shift(4);
|
||||
var usz = blob.read_shift(4);
|
||||
var namelen = blob.read_shift(2);
|
||||
var efsz = blob.read_shift(2);
|
||||
var fcsz = blob.read_shift(2);
|
||||
blob.l += 8;
|
||||
var offset = blob.read_shift(4);
|
||||
var EF = parse_extra_field(/*::(*/blob.slice(blob.l+namelen, blob.l+namelen+efsz)/*:: :any)*/);
|
||||
blob.l += namelen + efsz + fcsz;
|
||||
|
||||
var L = blob.l;
|
||||
blob.l = offset + 4;
|
||||
parse_local_file(blob, csz, usz, o, EF);
|
||||
blob.l = L;
|
||||
}
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
/* head starts just after local file header signature */
|
||||
function parse_local_file(blob/*:CFBlob*/, csz/*:number*/, usz/*:number*/, o/*:CFBContainer*/, EF) {
|
||||
/* [local file header] */
|
||||
blob.l += 2;
|
||||
var flags = blob.read_shift(2);
|
||||
var meth = blob.read_shift(2);
|
||||
var date = parse_dos_date(blob);
|
||||
|
||||
if(flags & 0x2041) throw new Error("Unsupported ZIP encryption");
|
||||
var crc32 = blob.read_shift(4);
|
||||
var _csz = blob.read_shift(4);
|
||||
var _usz = blob.read_shift(4);
|
||||
|
||||
var namelen = blob.read_shift(2);
|
||||
var efsz = blob.read_shift(2);
|
||||
|
||||
// TODO: flags & (1<<11) // UTF8
|
||||
var name = ""; for(var i = 0; i < namelen; ++i) name += String.fromCharCode(blob[blob.l++]);
|
||||
if(efsz) {
|
||||
var ef = parse_extra_field(/*::(*/blob.slice(blob.l, blob.l + efsz)/*:: :any)*/);
|
||||
if((ef[0x5455]||{}).mt) date = ef[0x5455].mt;
|
||||
if(((EF||{})[0x5455]||{}).mt) date = EF[0x5455].mt;
|
||||
}
|
||||
blob.l += efsz;
|
||||
|
||||
/* [encryption header] */
|
||||
|
||||
/* [file data] */
|
||||
var data = blob.slice(blob.l, blob.l + _csz);
|
||||
switch(meth) {
|
||||
case 8: data = _inflateRawSync(blob, _usz); break;
|
||||
case 0: break; // TODO: scan for magic number
|
||||
default: throw new Error("Unsupported ZIP Compression method " + meth);
|
||||
}
|
||||
|
||||
/* [data descriptor] */
|
||||
var wrn = false;
|
||||
if(flags & 8) {
|
||||
crc32 = blob.read_shift(4);
|
||||
if(crc32 == 0x08074b50) { crc32 = blob.read_shift(4); wrn = true; }
|
||||
_csz = blob.read_shift(4);
|
||||
_usz = blob.read_shift(4);
|
||||
}
|
||||
|
||||
if(_csz != csz) warn_or_throw(wrn, "Bad compressed size: " + csz + " != " + _csz);
|
||||
if(_usz != usz) warn_or_throw(wrn, "Bad uncompressed size: " + usz + " != " + _usz);
|
||||
var _crc32 = CRC32.buf(data, 0);
|
||||
if((crc32>>0) != (_crc32>>0)) warn_or_throw(wrn, "Bad CRC32 checksum: " + crc32 + " != " + _crc32);
|
||||
cfb_add(o, name, data, {unsafe: true, mt: date});
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
function write_zip(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ {
|
||||
var _opts = options || {};
|
||||
var out = [], cdirs = [];
|
||||
var o/*:CFBlob*/ = new_buf(1);
|
||||
var method = (_opts.compression ? 8 : 0), flags = 0;
|
||||
var desc = false;
|
||||
if(desc) flags |= 8;
|
||||
var i = 0, j = 0;
|
||||
|
||||
var start_cd = 0, fcnt = 0;
|
||||
var root = cfb.FullPaths[0], fp = root, fi = cfb.FileIndex[0];
|
||||
var crcs = [];
|
||||
var sz_cd = 0;
|
||||
|
||||
for(i = 1; i < cfb.FullPaths.length; ++i) {
|
||||
fp = cfb.FullPaths[i].slice(root.length); fi = cfb.FileIndex[i];
|
||||
if(!fi.size || !fi.content || fp == "\u0001Sh33tJ5") continue;
|
||||
var start = start_cd;
|
||||
|
||||
/* TODO: CP437 filename */
|
||||
var namebuf = new_buf(fp.length);
|
||||
for(j = 0; j < fp.length; ++j) namebuf.write_shift(1, fp.charCodeAt(j) & 0x7F);
|
||||
namebuf = namebuf.slice(0, namebuf.l);
|
||||
crcs[fcnt] = CRC32.buf(/*::((*/fi.content/*::||[]):any)*/, 0);
|
||||
|
||||
var outbuf = fi.content/*::||[]*/;
|
||||
if(method == 8) outbuf = _deflateRawSync(outbuf);
|
||||
|
||||
/* local file header */
|
||||
o = new_buf(30);
|
||||
o.write_shift(4, 0x04034b50);
|
||||
o.write_shift(2, 20);
|
||||
o.write_shift(2, flags);
|
||||
o.write_shift(2, method);
|
||||
/* TODO: last mod file time/date */
|
||||
if(fi.mt) write_dos_date(o, fi.mt);
|
||||
else o.write_shift(4, 0);
|
||||
o.write_shift(-4, (flags & 8) ? 0 : crcs[fcnt]);
|
||||
o.write_shift(4, (flags & 8) ? 0 : outbuf.length);
|
||||
o.write_shift(4, (flags & 8) ? 0 : /*::(*/fi.content/*::||[])*/.length);
|
||||
o.write_shift(2, namebuf.length);
|
||||
o.write_shift(2, 0);
|
||||
|
||||
start_cd += o.length;
|
||||
out.push(o);
|
||||
start_cd += namebuf.length;
|
||||
out.push(namebuf);
|
||||
|
||||
/* TODO: extra fields? */
|
||||
|
||||
/* TODO: encryption header ? */
|
||||
|
||||
start_cd += outbuf.length;
|
||||
out.push(outbuf);
|
||||
|
||||
/* data descriptor */
|
||||
if(flags & 8) {
|
||||
o = new_buf(12);
|
||||
o.write_shift(-4, crcs[fcnt]);
|
||||
o.write_shift(4, outbuf.length);
|
||||
o.write_shift(4, /*::(*/fi.content/*::||[])*/.length);
|
||||
start_cd += o.l;
|
||||
out.push(o);
|
||||
}
|
||||
|
||||
/* central directory */
|
||||
o = new_buf(46);
|
||||
o.write_shift(4, 0x02014b50);
|
||||
o.write_shift(2, 0);
|
||||
o.write_shift(2, 20);
|
||||
o.write_shift(2, flags);
|
||||
o.write_shift(2, method);
|
||||
o.write_shift(4, 0); /* TODO: last mod file time/date */
|
||||
o.write_shift(-4, crcs[fcnt]);
|
||||
|
||||
o.write_shift(4, outbuf.length);
|
||||
o.write_shift(4, /*::(*/fi.content/*::||[])*/.length);
|
||||
o.write_shift(2, namebuf.length);
|
||||
o.write_shift(2, 0);
|
||||
o.write_shift(2, 0);
|
||||
o.write_shift(2, 0);
|
||||
o.write_shift(2, 0);
|
||||
o.write_shift(4, 0);
|
||||
o.write_shift(4, start);
|
||||
|
||||
sz_cd += o.l;
|
||||
cdirs.push(o);
|
||||
sz_cd += namebuf.length;
|
||||
cdirs.push(namebuf);
|
||||
++fcnt;
|
||||
}
|
||||
|
||||
/* end of central directory */
|
||||
o = new_buf(22);
|
||||
o.write_shift(4, 0x06054b50);
|
||||
o.write_shift(2, 0);
|
||||
o.write_shift(2, 0);
|
||||
o.write_shift(2, fcnt);
|
||||
o.write_shift(2, fcnt);
|
||||
o.write_shift(4, sz_cd);
|
||||
o.write_shift(4, start_cd);
|
||||
o.write_shift(2, 0);
|
||||
|
||||
return bconcat(([bconcat((out/*:any*/)), bconcat(cdirs), o]/*:any*/));
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
var ContentTypeMap = ({
|
||||
"htm": "text/html",
|
||||
"xml": "text/xml",
|
||||
|
||||
"gif": "image/gif",
|
||||
"jpg": "image/jpeg",
|
||||
"png": "image/png",
|
||||
|
||||
"mso": "application/x-mso",
|
||||
"thmx": "application/vnd.ms-officetheme",
|
||||
"sh33tj5": "application/octet-stream"
|
||||
}/*:any*/);
|
||||
|
||||
function get_content_type(fi/*:CFBEntry*/, fp/*:string*/)/*:string*/ {
|
||||
if(fi.ctype) return fi.ctype;
|
||||
|
||||
var ext = fi.name || "", m = ext.match(/\.([^\.]+)$/);
|
||||
if(m && ContentTypeMap[m[1]]) return ContentTypeMap[m[1]];
|
||||
|
||||
if(fp) {
|
||||
m = (ext = fp).match(/[\.\\]([^\.\\])+$/);
|
||||
if(m && ContentTypeMap[m[1]]) return ContentTypeMap[m[1]];
|
||||
}
|
||||
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
/* 76 character chunks TODO: intertwine encoding */
|
||||
function write_base64_76(bstr/*:string*/)/*:string*/ {
|
||||
var data = Base64_encode(bstr);
|
||||
var o = [];
|
||||
for(var i = 0; i < data.length; i+= 76) o.push(data.slice(i, i+76));
|
||||
return o.join("\r\n") + "\r\n";
|
||||
}
|
||||
|
||||
/*
|
||||
Rules for QP:
|
||||
- escape =## applies for all non-display characters and literal "="
|
||||
- space or tab at end of line must be encoded
|
||||
- \r\n newlines can be preserved, but bare \r and \n must be escaped
|
||||
- lines must not exceed 76 characters, use soft breaks =\r\n
|
||||
|
||||
TODO: Some files from word appear to write line extensions with bare equals:
|
||||
|
||||
```
|
||||
<table class=3DMsoTableGrid border=3D1 cellspacing=3D0 cellpadding=3D0 width=
|
||||
="70%"
|
||||
```
|
||||
*/
|
||||
function write_quoted_printable(text/*:string*/)/*:string*/ {
|
||||
var encoded = text.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7E-\xFF=]/g, function(c) {
|
||||
var w = c.charCodeAt(0).toString(16).toUpperCase();
|
||||
return "=" + (w.length == 1 ? "0" + w : w);
|
||||
});
|
||||
|
||||
encoded = encoded.replace(/ $/mg, "=20").replace(/\t$/mg, "=09");
|
||||
|
||||
if(encoded.charAt(0) == "\n") encoded = "=0D" + encoded.slice(1);
|
||||
encoded = encoded.replace(/\r(?!\n)/mg, "=0D").replace(/\n\n/mg, "\n=0A").replace(/([^\r\n])\n/mg, "$1=0A");
|
||||
|
||||
var o/*:Array<string>*/ = [], split = encoded.split("\r\n");
|
||||
for(var si = 0; si < split.length; ++si) {
|
||||
var str = split[si];
|
||||
if(str.length == 0) { o.push(""); continue; }
|
||||
for(var i = 0; i < str.length;) {
|
||||
var end = 76;
|
||||
var tmp = str.slice(i, i + end);
|
||||
if(tmp.charAt(end - 1) == "=") end --;
|
||||
else if(tmp.charAt(end - 2) == "=") end -= 2;
|
||||
else if(tmp.charAt(end - 3) == "=") end -= 3;
|
||||
tmp = str.slice(i, i + end);
|
||||
i += end;
|
||||
if(i < str.length) tmp += "=";
|
||||
o.push(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
return o.join("\r\n");
|
||||
}
|
||||
function parse_quoted_printable(data/*:Array<string>*/)/*:RawBytes*/ {
|
||||
var o = [];
|
||||
|
||||
/* unify long lines */
|
||||
for(var di = 0; di < data.length; ++di) {
|
||||
var line = data[di];
|
||||
while(di <= data.length && line.charAt(line.length - 1) == "=") line = line.slice(0, line.length - 1) + data[++di];
|
||||
o.push(line);
|
||||
}
|
||||
|
||||
/* decode */
|
||||
for(var oi = 0; oi < o.length; ++oi) o[oi] = o[oi].replace(/[=][0-9A-Fa-f]{2}/g, function($$) { return String.fromCharCode(parseInt($$.slice(1), 16)); });
|
||||
return s2a(o.join("\r\n"));
|
||||
}
|
||||
|
||||
|
||||
function parse_mime(cfb/*:CFBContainer*/, data/*:Array<string>*/, root/*:string*/)/*:void*/ {
|
||||
var fname = "", cte = "", ctype = "", fdata;
|
||||
var di = 0;
|
||||
for(;di < 10; ++di) {
|
||||
var line = data[di];
|
||||
if(!line || line.match(/^\s*$/)) break;
|
||||
var m = line.match(/^(.*?):\s*([^\s].*)$/);
|
||||
if(m) switch(m[1].toLowerCase()) {
|
||||
case "content-location": fname = m[2].trim(); break;
|
||||
case "content-type": ctype = m[2].trim(); break;
|
||||
case "content-transfer-encoding": cte = m[2].trim(); break;
|
||||
}
|
||||
}
|
||||
++di;
|
||||
switch(cte.toLowerCase()) {
|
||||
case 'base64': fdata = s2a(Base64_decode(data.slice(di).join(""))); break;
|
||||
case 'quoted-printable': fdata = parse_quoted_printable(data.slice(di)); break;
|
||||
default: throw new Error("Unsupported Content-Transfer-Encoding " + cte);
|
||||
}
|
||||
var file = cfb_add(cfb, fname.slice(root.length), fdata, {unsafe: true});
|
||||
if(ctype) file.ctype = ctype;
|
||||
}
|
||||
|
||||
function parse_mad(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
|
||||
if(a2s(file.slice(0,13)).toLowerCase() != "mime-version:") throw new Error("Unsupported MAD header");
|
||||
var root = (options && options.root || "");
|
||||
// $FlowIgnore
|
||||
var data = (has_buf && Buffer.isBuffer(file) ? file.toString("binary") : a2s(file)).split("\r\n");
|
||||
var di = 0, row = "";
|
||||
|
||||
/* if root is not specified, scan for the common prefix */
|
||||
for(di = 0; di < data.length; ++di) {
|
||||
row = data[di];
|
||||
if(!/^Content-Location:/i.test(row)) continue;
|
||||
row = row.slice(row.indexOf("file"));
|
||||
if(!root) root = row.slice(0, row.lastIndexOf("/") + 1);
|
||||
if(row.slice(0, root.length) == root) continue;
|
||||
while(root.length > 0) {
|
||||
root = root.slice(0, root.length - 1);
|
||||
root = root.slice(0, root.lastIndexOf("/") + 1);
|
||||
if(row.slice(0,root.length) == root) break;
|
||||
}
|
||||
}
|
||||
|
||||
var mboundary = (data[1] || "").match(/boundary="(.*?)"/);
|
||||
if(!mboundary) throw new Error("MAD cannot find boundary");
|
||||
var boundary = "--" + (mboundary[1] || "");
|
||||
|
||||
var FileIndex/*:CFBFileIndex*/ = [], FullPaths/*:Array<string>*/ = [];
|
||||
var o = {
|
||||
FileIndex: FileIndex,
|
||||
FullPaths: FullPaths
|
||||
};
|
||||
init_cfb(o);
|
||||
var start_di, fcnt = 0;
|
||||
for(di = 0; di < data.length; ++di) {
|
||||
var line = data[di];
|
||||
if(line !== boundary && line !== boundary + "--") continue;
|
||||
if(fcnt++) parse_mime(o, data.slice(start_di, di), root);
|
||||
start_di = di;
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
function write_mad(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:string*/ {
|
||||
var opts = options || {};
|
||||
var boundary = opts.boundary || "SheetJS";
|
||||
boundary = '------=' + boundary;
|
||||
|
||||
var out = [
|
||||
'MIME-Version: 1.0',
|
||||
'Content-Type: multipart/related; boundary="' + boundary.slice(2) + '"',
|
||||
'',
|
||||
'',
|
||||
''
|
||||
];
|
||||
|
||||
var root = cfb.FullPaths[0], fp = root, fi = cfb.FileIndex[0];
|
||||
for(var i = 1; i < cfb.FullPaths.length; ++i) {
|
||||
fp = cfb.FullPaths[i].slice(root.length);
|
||||
fi = cfb.FileIndex[i];
|
||||
if(!fi.size || !fi.content || fp == "\u0001Sh33tJ5") continue;
|
||||
|
||||
/* Normalize filename */
|
||||
fp = fp.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7E-\xFF]/g, function(c) {
|
||||
return "_x" + c.charCodeAt(0).toString(16) + "_";
|
||||
}).replace(/[\u0080-\uFFFF]/g, function(u) {
|
||||
return "_u" + u.charCodeAt(0).toString(16) + "_";
|
||||
});
|
||||
|
||||
/* Extract content as binary string */
|
||||
var ca = fi.content;
|
||||
// $FlowIgnore
|
||||
var cstr = has_buf && Buffer.isBuffer(ca) ? ca.toString("binary") : a2s(ca);
|
||||
|
||||
/* 4/5 of first 1024 chars ascii -> quoted printable, else base64 */
|
||||
var dispcnt = 0, L = Math.min(1024, cstr.length), cc = 0;
|
||||
for(var csl = 0; csl <= L; ++csl) if((cc=cstr.charCodeAt(csl)) >= 0x20 && cc < 0x80) ++dispcnt;
|
||||
var qp = dispcnt >= L * 4 / 5;
|
||||
|
||||
out.push(boundary);
|
||||
out.push('Content-Location: ' + (opts.root || 'file:///C:/SheetJS/') + fp);
|
||||
out.push('Content-Transfer-Encoding: ' + (qp ? 'quoted-printable' : 'base64'));
|
||||
out.push('Content-Type: ' + get_content_type(fi, fp));
|
||||
out.push('');
|
||||
|
||||
out.push(qp ? write_quoted_printable(cstr) : write_base64_76(cstr));
|
||||
}
|
||||
out.push(boundary + '--\r\n');
|
||||
return out.join("\r\n");
|
||||
}
|
|
@ -5,8 +5,9 @@ function cfb_new(opts/*:?any*/)/*:CFBContainer*/ {
|
|||
}
|
||||
|
||||
function cfb_add(cfb/*:CFBContainer*/, name/*:string*/, content/*:?RawBytes*/, opts/*:?any*/)/*:CFBEntry*/ {
|
||||
init_cfb(cfb);
|
||||
var file = CFB.find(cfb, name);
|
||||
var unsafe = opts && opts.unsafe;
|
||||
if(!unsafe) init_cfb(cfb);
|
||||
var file = !unsafe && CFB.find(cfb, name);
|
||||
if(!file) {
|
||||
var fpath/*:string*/ = cfb.FullPaths[0];
|
||||
if(name.slice(0, fpath.length) == fpath) fpath = name;
|
||||
|
@ -17,13 +18,15 @@ function cfb_add(cfb/*:CFBContainer*/, name/*:string*/, content/*:?RawBytes*/, o
|
|||
file = ({name: filename(name), type: 2}/*:any*/);
|
||||
cfb.FileIndex.push(file);
|
||||
cfb.FullPaths.push(fpath);
|
||||
CFB.utils.cfb_gc(cfb);
|
||||
if(!unsafe) CFB.utils.cfb_gc(cfb);
|
||||
}
|
||||
/*:: if(!file) throw new Error("unreachable"); */
|
||||
file.content = (content/*:any*/);
|
||||
file.size = content ? content.length : 0;
|
||||
if(opts) {
|
||||
if(opts.CLSID) file.clsid = opts.CLSID;
|
||||
if(opts.mt) file.mt = opts.mt;
|
||||
if(opts.ct) file.ct = opts.ct;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,9 @@ exports.utils = {
|
|||
CheckField: CheckField,
|
||||
prep_blob: prep_blob,
|
||||
bconcat: bconcat,
|
||||
use_zlib: use_zlib,
|
||||
_deflateRaw: _deflate,
|
||||
_inflateRaw: _inflate,
|
||||
consts: consts
|
||||
};
|
||||
|
||||
|
|
1308
cfb.flow.js
1308
cfb.flow.js
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,2 @@
|
|||
.npmignore
|
||||
*.sheetjs
|
|
@ -1,4 +1,192 @@
|
|||
Copyright (C) 2013-present SheetJS
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright (C) 2013-present SheetJS LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
11
fails.lst
11
fails.lst
|
@ -1,6 +1,13 @@
|
|||
# not CFB or ZIP
|
||||
apachepoi_testEXCEL_3.xls
|
||||
apachepoi_testEXCEL_4.xls
|
||||
xlrd_biff4_no_format_no_window2.xls
|
||||
roo_type_excelx.xls
|
||||
roo_type_openoffice.xls
|
||||
libreoffice_calc_csv-import_malformed-quotes.xls
|
||||
# file exceeding 31 chars
|
||||
apachepoi_59746_NoRowNums.xlsx
|
||||
apachepoi_WithEmbeded.xlsx
|
||||
apachepoi_picture.xlsx
|
||||
roo_name_with_leading_slash.xlsx
|
||||
spout-xlsx_sheet_with_prefixed_xml_files.xlsx
|
||||
# not a valid file
|
||||
openpyxl_r_null_archive.xlsx
|
||||
|
|
65
index.html
65
index.html
|
@ -3,6 +3,7 @@
|
|||
<!-- vim: set ts=2: -->
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>JS-CFB Live Demo</title>
|
||||
<style>
|
||||
|
@ -32,24 +33,31 @@ a { text-decoration: none }
|
|||
<b>Advanced Demo Options:</b>
|
||||
Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" checked>
|
||||
|
||||
<a id="saveit" onclick="savefile();" href="#">Export data</a>
|
||||
<b>Export Current File</b>
|
||||
- <a id="savecfb" onclick="savefile('cfb');" href="#">Export data as CFB</a> (Container File Binary Format)
|
||||
- <a id="savezip" onclick="savefile('zip');" href="#">Export data as ZIP</a>
|
||||
- <a id="savemad" onclick="savefile('mad');" href="#">Export data as MAD</a> (MIME aggregate document)
|
||||
</pre>
|
||||
<pre id="out"></pre>
|
||||
<br />
|
||||
<script src="shim.js"></script>
|
||||
<script src="https://unpkg.com/printj/dist/printj.min.js"></script>
|
||||
<script src="cfb.js"></script>
|
||||
<script src="//rawgit.com/eligrey/Blob.js/master/Blob.js"></script>
|
||||
<script src="//rawgit.com/eligrey/FileSaver.js/master/FileSaver.js"></script>
|
||||
<script src="dist/cfb.min.js"></script>
|
||||
<script>
|
||||
/*jshint browser:true */
|
||||
/* eslint-env browser */
|
||||
/*global Uint8Array, ArrayBuffer, console */
|
||||
/*global CFB, out, PRINTJ, saveAs */
|
||||
/*global Uint8Array, ArrayBuffer */
|
||||
/*global CFB, out, PRINTJ */
|
||||
/* exported savefile, download_file */
|
||||
/* eslint no-use-before-define:0 */
|
||||
var global_cfb;
|
||||
|
||||
if(!String.prototype.repeat) String.prototype.repeat = function(count) {
|
||||
var o = "";
|
||||
for(var i = 0; i < count; ++i) o += this;
|
||||
return o;
|
||||
};
|
||||
|
||||
var get_manifest = (function() {
|
||||
var sprintf = PRINTJ.sprintf;
|
||||
function fix_string(x/*:string*/)/*:string*/ { return x.replace(/[\u0000-\u001f]/, function($$) { return sprintf("\\u%04X", $$.charCodeAt(0)); }); }
|
||||
|
@ -58,6 +66,7 @@ var get_manifest = (function() {
|
|||
};
|
||||
return function get_manifest(cfb) {
|
||||
var out = [];
|
||||
var rlen = cfb.FullPaths[0].length;
|
||||
|
||||
var basetime = new Date(1980,0,1);
|
||||
var cnt = 0, rootsize = 0, filesize = 0;
|
||||
|
@ -70,7 +79,10 @@ var get_manifest = (function() {
|
|||
rootsize = file.size;
|
||||
break;
|
||||
case 2:
|
||||
out.push(sprintf("%9lu %s <a href=\"#\" onclick=\"download_file(%d);\">%s</a>", file.size, format_date(basetime), i, fix_string(cfb.FullPaths[i])));
|
||||
var fixname = fix_string(cfb.FullPaths[i]);
|
||||
if(fixname.match(/\\u0001Sh33tJ5/)) return;
|
||||
fixname = fixname.slice(rlen);
|
||||
out.push(sprintf("%9lu %s <a href=\"#\" onclick=\"download_file(%d);\">%s</a>", file.size, format_date(basetime), i, fixname));
|
||||
filesize += file.size;
|
||||
++cnt;
|
||||
}
|
||||
|
@ -92,20 +104,13 @@ var do_file = (function() {
|
|||
var domrabs = document.getElementsByName("userabs")[0];
|
||||
if(!rABS) domrabs.disabled = !(domrabs.checked = false);
|
||||
|
||||
function fixdata(data) {
|
||||
var o = "", l = 0, w = 10240;
|
||||
for(; l<data.byteLength/w; ++l) o+=String.fromCharCode.apply(null,new Uint8Array(data.slice(l*w,l*w+w)));
|
||||
o+=String.fromCharCode.apply(null, new Uint8Array(data.slice(l*w)));
|
||||
return o;
|
||||
}
|
||||
|
||||
return function do_file(files) {
|
||||
rABS = domrabs.checked;
|
||||
var f = files[0];
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
var data = e.target.result;
|
||||
var cfb = CFB.read(rABS ? data : btoa(fixdata(data)), {type: rABS ? 'binary' : 'base64'});
|
||||
var cfb = CFB.read(rABS ? data : new Uint8Array(data), {type: rABS ? 'binary' : 'buffer'});
|
||||
process_data(cfb);
|
||||
};
|
||||
if(rABS) reader.readAsBinaryString(f);
|
||||
|
@ -134,6 +139,14 @@ var do_file = (function() {
|
|||
drop.addEventListener('drop', handleDrop, false);
|
||||
})();
|
||||
|
||||
function saveAs(blob, fname) {
|
||||
var url = URL.createObjectURL(blob);
|
||||
var a = document.createElement("a");
|
||||
a.download = fname; a.href = url; document.body.appendChild(a); a.click();
|
||||
/*:: if(document.body == null) throw new Error("unreachable"); */ document.body.removeChild(a);
|
||||
if(URL.revokeObjectURL && typeof setTimeout !== 'undefined') setTimeout(function() { URL.revokeObjectURL(url); }, 60000);
|
||||
}
|
||||
|
||||
var savefile = (function() {
|
||||
var s2ab = function s2ab(s) {
|
||||
var buf, i=0;
|
||||
|
@ -149,12 +162,18 @@ var savefile = (function() {
|
|||
}
|
||||
};
|
||||
|
||||
return function savefile() {
|
||||
return function savefile(type) {
|
||||
if(!global_cfb) return alert("Must load a file first!");
|
||||
console.log(global_cfb);
|
||||
var data = CFB.write(global_cfb, {type:'binary'});
|
||||
console.log(data);
|
||||
saveAs(new Blob([s2ab(data)],{type:"application/octet-stream"}), "sheetjs.xls");
|
||||
if(typeof console !== 'undefined') console.log(global_cfb);
|
||||
var data = CFB.write(global_cfb, {type:'binary', fileType: type});
|
||||
if(typeof console !== 'undefined') console.log(data);
|
||||
var newcfb = CFB.read(data, {type:'binary'});
|
||||
console.log(newcfb);
|
||||
|
||||
var fname = "SheetJSCFBDemo." + type;
|
||||
var blob = new Blob([s2ab(data)],{type:"application/octet-stream"});
|
||||
if(typeof navigator !== 'undefined' && navigator.msSaveBlob) return navigator.msSaveBlob(blob, fname);
|
||||
saveAs(blob, fname);
|
||||
};
|
||||
})();
|
||||
|
||||
|
@ -168,9 +187,11 @@ var download_file = (function() {
|
|||
|
||||
return function download_file(i) {
|
||||
if(!global_cfb) return alert("Must load a file first!");
|
||||
console.log(global_cfb);
|
||||
if(typeof console !== 'undefined') console.log(global_cfb);
|
||||
var file = global_cfb.FileIndex[i], data = file.content;
|
||||
saveAs(new Blob([a2ab(data)],{type:"application/octet-stream"}), file.name);
|
||||
var blob = new Blob([a2ab(data)],{type:"application/octet-stream"});
|
||||
if(typeof navigator !== 'undefined' && navigator.msSaveBlob) return navigator.msSaveBlob(blob, file.name);
|
||||
saveAs(blob, file.name);
|
||||
};
|
||||
})();
|
||||
var _gaq = _gaq || [];
|
||||
|
|
|
@ -64,6 +64,7 @@ type CFBWriteOpts = any;
|
|||
|
||||
interface CFBReadOpts {
|
||||
type?:string;
|
||||
root?:string;
|
||||
};
|
||||
|
||||
type CFBFileIndex = Array<CFBEntry>;
|
||||
|
@ -94,5 +95,6 @@ type CFBEntry = {
|
|||
R: number;
|
||||
C: number;
|
||||
content?: CFBlob;
|
||||
ctype?: string;
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
/*::
|
||||
|
||||
declare module 'cfb' { declare var exports:CFBModule; };
|
||||
declare module '../' { declare var exports:CFBModule; };
|
||||
declare module './' { declare var exports:CFBModule; };
|
||||
declare module 'cfb' { declare module.exports:CFBModule; };
|
||||
declare module '../' { declare module.exports:CFBModule; };
|
||||
declare module './' { declare module.exports:CFBModule; };
|
||||
|
||||
declare module 'commander' { declare var exports:any; };
|
||||
declare module 'printj' { declare var exports:any; };
|
||||
declare module 'crc-32' { declare var exports: any; };
|
||||
declare module 'commander' { declare module.exports:any; };
|
||||
declare module 'printj' {
|
||||
declare var sprintf:(fmt:string, ...args:any)=>string;
|
||||
};
|
||||
declare module 'crc-32' { declare module.exports: any; };
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
#! /usr/bin/env bash
|
||||
|
||||
# This script will check the current version of node and install another version
|
||||
# of npm if node is version 0.8
|
||||
|
||||
version=$(node --version)
|
||||
|
||||
if [[ $version =~ v0\.8\. ]]
|
||||
then
|
||||
npm install -g npm@4.3.0
|
||||
fi
|
|
@ -1,6 +1,4 @@
|
|||
var DO_NOT_EXPORT_CFB = true;
|
||||
/*::
|
||||
declare var Base64:any;
|
||||
declare var ReadShift:any;
|
||||
declare var CheckField:any;
|
||||
declare var prep_blob:any;
|
||||
|
@ -12,5 +10,9 @@ declare var bconcat:any;
|
|||
declare var s2a:any;
|
||||
declare var chr0:any;
|
||||
declare var chr1:any;
|
||||
declare var has_buf:boolean;
|
||||
declare var new_buf:any;
|
||||
declare var new_raw_buf:any;
|
||||
declare var new_unsafe_buf:any;
|
||||
declare var Buffer_from:any;
|
||||
*/
|
27
package.json
27
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "cfb",
|
||||
"version": "1.0.2",
|
||||
"version": "1.2.2",
|
||||
"author": "sheetjs",
|
||||
"description": "Compound File Binary File Format extractor",
|
||||
"keywords": [
|
||||
|
@ -8,9 +8,6 @@
|
|||
"compression",
|
||||
"office"
|
||||
],
|
||||
"bin": {
|
||||
"cfb": "./bin/cfb.njs"
|
||||
},
|
||||
"main": "./cfb",
|
||||
"types": "types",
|
||||
"browser": {
|
||||
|
@ -19,17 +16,22 @@
|
|||
"fs": false
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": "^2.12.1",
|
||||
"printj": "~1.1.1"
|
||||
"adler-32": "~1.3.0",
|
||||
"crc-32": "~1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"crc-32": "~1.1.1",
|
||||
"mocha": "~2.5.3",
|
||||
"blanket": "~1.2.3",
|
||||
"@sheetjs/uglify-js": "~2.7.3",
|
||||
"@types/node": "^8.0.7",
|
||||
"@types/commander": "^2.9.0",
|
||||
"@types/node": "^8.10.25",
|
||||
"acorn": "7.4.1",
|
||||
"alex": "8.1.1",
|
||||
"blanket": "~1.2.3",
|
||||
"dtslint": "~0.1.2",
|
||||
"eslint": "7.23.0",
|
||||
"eslint-plugin-html": "^6.1.2",
|
||||
"eslint-plugin-json": "^2.1.2",
|
||||
"jscs": "3.0.7",
|
||||
"jshint": "2.13.4",
|
||||
"mocha": "~2.5.3",
|
||||
"typescript": "2.2.0"
|
||||
},
|
||||
"repository": {
|
||||
|
@ -49,14 +51,13 @@
|
|||
"files": [
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"bin/",
|
||||
"dist/",
|
||||
"types/index.d.ts",
|
||||
"types/tsconfig.json",
|
||||
"cfb.js",
|
||||
"xlscfb.flow.js"
|
||||
],
|
||||
"homepage": "http://sheetjs.com/opensource",
|
||||
"homepage": "http://sheetjs.com/",
|
||||
"bugs": {
|
||||
"url": "https://github.com/SheetJS/js-cfb/issues"
|
||||
},
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
*.tgz
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright (C) 2013-present SheetJS LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,48 @@
|
|||
# Container File Blobs
|
||||
|
||||
This CLI tool inspects and can manipulate supported files, leveraging the base
|
||||
[`cfb` library](https://www.npmjs.com/package/cfb).
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
It is preferable to install the library globally with npm:
|
||||
|
||||
```bash
|
||||
$ npm install -g cfb-cli
|
||||
```
|
||||
|
||||
The global installation adds a command `cfb-cli` which can work with files.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
- `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.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
Please consult the attached LICENSE file for details. All rights not explicitly
|
||||
granted by the Apache 2.0 license are reserved by the Original Author.
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
Special thanks to [Garrett Luu](https://garrettluu.com/) for spinning off the
|
||||
command from the CFB module.
|
||||
|
||||
|
||||
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-cfb?pixel)](https://github.com/SheetJS/js-cfb)
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env node
|
||||
/* cfb.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* eslint-env node */
|
||||
/* vim: set ts=2 ft=javascript: */
|
||||
var cli = require('../');
|
||||
|
||||
cli();
|
|
@ -0,0 +1,173 @@
|
|||
#!/usr/bin/env node
|
||||
/* index.js (C) 2020-present SheetJS -- http://sheetjs.com */
|
||||
/* eslint-env node */
|
||||
/* vim: set ts=2 ft=javascript: */
|
||||
|
||||
var n = "cfb-cli";
|
||||
var X = require('cfb');
|
||||
var fs = require('fs');
|
||||
var program = require('commander');
|
||||
var PRINTJ = require("printj");
|
||||
function run() {
|
||||
var sprintf = PRINTJ.sprintf;
|
||||
program
|
||||
.version(X.version)
|
||||
.usage('[options] <file> [subfiles...]')
|
||||
.option('-l, --list-files', 'list files')
|
||||
.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('-O, --to-stdout', 'extract raw contents to stdout')
|
||||
.option('-z, --dump', 'dump internal representation but do not extract')
|
||||
.option('-q, --quiet', 'process but do not report')
|
||||
.option('--here', 'skip the CFB root name when extracting')
|
||||
.option('--osx', 'use OSX-style unzip listing')
|
||||
.option('--zlib', 'use native zlib')
|
||||
.option('--local', 'print times in local timezone')
|
||||
.option('--dev', 'development mode')
|
||||
.option('--read', 'read but do not print out contents');
|
||||
program.parse(process.argv);
|
||||
|
||||
if (program.zlib) X.utils.use_zlib(require('zlib'));
|
||||
|
||||
var exit = process.exit;
|
||||
var die = function (errno/*:number*/, msg/*:string*/) { console.error(n + ": " + msg); exit(errno); };
|
||||
var logit = function (cmd/*:string*/, f/*:string*/) { console.error(sprintf("%-6s %s", cmd, f)); };
|
||||
|
||||
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) exit(0);
|
||||
|
||||
if (program.dump) {
|
||||
console.log("Full Paths:");
|
||||
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 rlen = cfb.FullPaths[0].length;
|
||||
|
||||
function fix_string(x/*:string*/)/*:string*/ { return x.replace(/[\u0000-\u001f]/g, function ($$) { return sprintf("\\u%04X", $$.charCodeAt(0)); }); }
|
||||
var format_date = function (date/*:Date*/, osx/*:?any*/)/*:string*/ {
|
||||
var datefmt = osx ? "%02u-%02u-%04u %02u:%02u" : "%02u-%02u-%02u %02u:%02u";
|
||||
var MM = program.local ? date.getMonth() + 1 : date.getUTCMonth() + 1;
|
||||
var DD = program.local ? date.getDate() : date.getUTCDate();
|
||||
var YY = (program.local ? date.getFullYear() : date.getUTCFullYear()) % (osx ? 10000 : 100);
|
||||
var hh = program.local ? date.getHours() : date.getUTCHours();
|
||||
var mm = program.local ? date.getMinutes() : date.getUTCMinutes();
|
||||
return sprintf(datefmt, MM, DD, YY, hh, mm);
|
||||
};
|
||||
|
||||
if (program.listFiles) {
|
||||
var basetime = new Date(Date.UTC(1980, 0, 1));
|
||||
var cnt = 0, rootsize = 0, filesize = 0;
|
||||
var fmtstr = "%9lu %s %s";
|
||||
if (program.osx) {
|
||||
console.log("Archive: " + program.args[0]);
|
||||
console.log(" Length Date Time Name");
|
||||
console.log("--------- ---------- ----- ----");
|
||||
fmtstr = "%9lu %s %s";
|
||||
} else {
|
||||
console.log(" Length Date Time Name");
|
||||
console.log(" -------- ---- ---- ----");
|
||||
}
|
||||
cfb.FileIndex.forEach(function (file/*:CFBEntry*/, i/*:number*/) {
|
||||
switch (file.type) {
|
||||
case 5:
|
||||
basetime = file.ct || file.mt || basetime;
|
||||
rootsize = file.size;
|
||||
break;
|
||||
case 2:
|
||||
var fixname = fix_string(cfb.FullPaths[i]);
|
||||
if (program.osx && fixname.match(/\\u0001Sh33tJ5/)) return;
|
||||
if (program.here) fixname = fixname.slice(rlen);
|
||||
console.log(sprintf(fmtstr, file.size, format_date(file.mt || basetime, program.osx), fixname));
|
||||
filesize += file.size;
|
||||
++cnt;
|
||||
}
|
||||
});
|
||||
var outfmt = "%9lu %lu file%s";
|
||||
if (program.osx) {
|
||||
console.log("--------- -------");
|
||||
outfmt = "%9lu %lu file%s";
|
||||
} else {
|
||||
console.log(" -------- -------");
|
||||
}
|
||||
console.log(sprintf(outfmt, rootsize || filesize, cnt, (cnt !== 1 ? "s" : "")));
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
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))*/ || new Buffer(0));
|
||||
}
|
||||
|
||||
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.replace(/\\u000\d/g, "!"));
|
||||
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];
|
||||
if (program.toStdout) return process.stdout.write(/*::new Buffer((*/data.content/*:: :any))*/);
|
||||
mkdirp(path.slice(0, path.lastIndexOf("/")));
|
||||
write(path, data);
|
||||
});
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (program.toStdout) exit(0);
|
||||
for (var i = program.here ? 1 : 0; i !== cfb.FullPaths.length; ++i) {
|
||||
if (!cfb.FileIndex[i].name) continue;
|
||||
var fp = cfb.FullPaths[i];
|
||||
if (program.here) fp = fp.slice(rlen);
|
||||
if (fp.slice(-1) === "/") mkdirp(fp);
|
||||
else {
|
||||
if (fp.indexOf("/") > -1) mkdirp(fp.slice(0, fp.lastIndexOf("/")));
|
||||
write(fp, cfb.FileIndex[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = run;
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "cfb-cli",
|
||||
"version": "1.0.1",
|
||||
"description": "Command-line interface for cfb",
|
||||
"bin": {
|
||||
"cfb-cli": "./bin/cfb.njs"
|
||||
},
|
||||
"main": "index.js",
|
||||
"author": "Garrett Luu",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"cfb": "^1.1.4",
|
||||
"commander": "^5.1.0",
|
||||
"printj": "^1.2.2"
|
||||
}
|
||||
}
|
475
shim.js
475
shim.js
|
@ -1,343 +1,154 @@
|
|||
/*! shim.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* ES3/5 Compatibility shims and other utilities for older browsers. */
|
||||
|
||||
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
|
||||
if (!Object.keys) {
|
||||
Object.keys = (function () {
|
||||
var hasOwnProperty = Object.prototype.hasOwnProperty,
|
||||
hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
|
||||
dontEnums = [
|
||||
'toString',
|
||||
'toLocaleString',
|
||||
'valueOf',
|
||||
'hasOwnProperty',
|
||||
'isPrototypeOf',
|
||||
'propertyIsEnumerable',
|
||||
'constructor'
|
||||
],
|
||||
dontEnumsLength = dontEnums.length;
|
||||
if(!Object.keys) Object.keys = (function() {
|
||||
var hasOwnProperty = Object.prototype.hasOwnProperty,
|
||||
hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
|
||||
dontEnums = [
|
||||
'toString',
|
||||
'toLocaleString',
|
||||
'valueOf',
|
||||
'hasOwnProperty',
|
||||
'isPrototypeOf',
|
||||
'propertyIsEnumerable',
|
||||
'constructor'
|
||||
],
|
||||
dontEnumsLength = dontEnums.length;
|
||||
|
||||
return function (obj) {
|
||||
if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) throw new TypeError('Object.keys called on non-object');
|
||||
return function(obj) {
|
||||
if(typeof obj !== 'object' && typeof obj !== 'function' || obj === null) throw new TypeError('Object.keys called on non-object');
|
||||
|
||||
var result = [];
|
||||
var result = [];
|
||||
|
||||
for (var prop in obj) {
|
||||
if (hasOwnProperty.call(obj, prop)) result.push(prop);
|
||||
}
|
||||
for(var prop in obj) if(hasOwnProperty.call(obj, prop)) result.push(prop);
|
||||
|
||||
if (hasDontEnumBug) {
|
||||
for (var i=0; i < dontEnumsLength; i++) {
|
||||
if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
}
|
||||
|
||||
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
|
||||
if (!Array.prototype.filter)
|
||||
{
|
||||
Array.prototype.filter = function(fun /*, thisp */)
|
||||
{
|
||||
"use strict";
|
||||
|
||||
if (this == null)
|
||||
throw new TypeError();
|
||||
|
||||
var t = Object(this);
|
||||
var len = t.length >>> 0;
|
||||
if (typeof fun != "function")
|
||||
throw new TypeError();
|
||||
|
||||
var res = [];
|
||||
var thisp = arguments[1];
|
||||
for (var i = 0; i < len; i++)
|
||||
{
|
||||
if (i in t)
|
||||
{
|
||||
var val = t[i]; // in case fun mutates this
|
||||
if (fun.call(thisp, val, i, t))
|
||||
res.push(val);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
if(hasDontEnumBug)
|
||||
for(var i=0; i < dontEnumsLength; ++i)
|
||||
if(hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]);
|
||||
return result;
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
||||
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim
|
||||
if (!String.prototype.trim) {
|
||||
String.prototype.trim = function () {
|
||||
return this.replace(/^\s+|\s+$/g, '');
|
||||
if(!String.prototype.trim) String.prototype.trim = function() {
|
||||
var s = this.replace(/^\s+/, '');
|
||||
for(var i = s.length - 1; i >=0 ; --i) if(!s.charAt(i).match(/^\s/)) return s.slice(0,i+1);
|
||||
return "";
|
||||
};
|
||||
|
||||
if(!Array.prototype.forEach) Array.prototype.forEach = function(cb) {
|
||||
var len = (this.length>>>0), self = (arguments[1]||void 0);
|
||||
for(var i=0; i<len; ++i) if(i in this) self ? cb.call(self, this[i], i, this) : cb(this[i], i, this);
|
||||
};
|
||||
|
||||
if(!Array.prototype.map) Array.prototype.map = function(cb) {
|
||||
var len = (this.length>>>0), self = (arguments[1]||void 0), A = new Array(len);
|
||||
for(var i=0; i<len; ++i) if(i in this) A[i] = self ? cb.call(self, this[i], i, this) : cb(this[i], i, this);
|
||||
return A;
|
||||
};
|
||||
|
||||
if(!Array.prototype.indexOf) Array.prototype.indexOf = function(needle) {
|
||||
var len = (this.length>>>0), i = ((arguments[1]|0)||0);
|
||||
for(i<0 && (i+=len)<0 && (i=0); i<len; ++i) if(this[i] === needle) return i;
|
||||
return -1;
|
||||
};
|
||||
|
||||
if(!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(needle) {
|
||||
var len = (this.length>>>0), i = len - 1;
|
||||
for(; i>=0; --i) if(this[i] === needle) return i;
|
||||
return -1;
|
||||
};
|
||||
|
||||
if(!Array.isArray) Array.isArray = function(obj) { return Object.prototype.toString.call(obj) === "[object Array]"; };
|
||||
|
||||
if(!Date.prototype.toISOString) Date.prototype.toISOString = (function() {
|
||||
function p(n,i) { return ('0000000' + n).slice(-(i||2)); }
|
||||
|
||||
return function _toISOString() {
|
||||
var y = this.getUTCFullYear(), yr = "";
|
||||
if(y>9999) yr = '+' + p( y, 6);
|
||||
else if(y<0) yr = '-' + p(-y, 6);
|
||||
else yr = p( y, 4);
|
||||
|
||||
return [
|
||||
yr, p(this.getUTCMonth()+1), p(this.getUTCDate())
|
||||
].join('-') + 'T' + [
|
||||
p(this.getUTCHours()), p(this.getUTCMinutes()), p(this.getUTCSeconds())
|
||||
].join(':') + '.' + p(this.getUTCMilliseconds(),3) + 'Z';
|
||||
};
|
||||
}
|
||||
|
||||
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
|
||||
if (!Array.prototype.forEach)
|
||||
{
|
||||
Array.prototype.forEach = function(fun /*, thisArg */)
|
||||
{
|
||||
"use strict";
|
||||
|
||||
if (this === void 0 || this === null)
|
||||
throw new TypeError();
|
||||
|
||||
var t = Object(this);
|
||||
var len = t.length >>> 0;
|
||||
if (typeof fun !== "function")
|
||||
throw new TypeError();
|
||||
|
||||
var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
|
||||
for (var i = 0; i < len; i++)
|
||||
{
|
||||
if (i in t)
|
||||
fun.call(thisArg, t[i], i, t);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Production steps of ECMA-262, Edition 5, 15.4.4.19
|
||||
// Reference: http://es5.github.com/#x15.4.4.19
|
||||
if (!Array.prototype.map) {
|
||||
Array.prototype.map = function(callback, thisArg) {
|
||||
|
||||
var T, A, k;
|
||||
|
||||
if (this == null) {
|
||||
throw new TypeError(" this is null or not defined");
|
||||
}
|
||||
|
||||
// 1. Let O be the result of calling ToObject passing the |this| value as the argument.
|
||||
var O = Object(this);
|
||||
|
||||
// 2. Let lenValue be the result of calling the Get internal method of O with the argument "length".
|
||||
// 3. Let len be ToUint32(lenValue).
|
||||
var len = O.length >>> 0;
|
||||
|
||||
// 4. If IsCallable(callback) is false, throw a TypeError exception.
|
||||
// See: http://es5.github.com/#x9.11
|
||||
if (typeof callback !== "function") {
|
||||
throw new TypeError(callback + " is not a function");
|
||||
}
|
||||
|
||||
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
|
||||
if (thisArg) {
|
||||
T = thisArg;
|
||||
}
|
||||
|
||||
// 6. Let A be a new array created as if by the expression new Array(len) where Array is
|
||||
// the standard built-in constructor with that name and len is the value of len.
|
||||
A = new Array(len);
|
||||
|
||||
// 7. Let k be 0
|
||||
k = 0;
|
||||
|
||||
// 8. Repeat, while k < len
|
||||
while(k < len) {
|
||||
|
||||
var kValue, mappedValue;
|
||||
|
||||
// a. Let Pk be ToString(k).
|
||||
// This is implicit for LHS operands of the in operator
|
||||
// b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk.
|
||||
// This step can be combined with c
|
||||
// c. If kPresent is true, then
|
||||
if (k in O) {
|
||||
|
||||
// i. Let kValue be the result of calling the Get internal method of O with argument Pk.
|
||||
kValue = O[ k ];
|
||||
|
||||
// ii. Let mappedValue be the result of calling the Call internal method of callback
|
||||
// with T as the this value and argument list containing kValue, k, and O.
|
||||
mappedValue = callback.call(T, kValue, k, O);
|
||||
|
||||
// iii. Call the DefineOwnProperty internal method of A with arguments
|
||||
// Pk, Property Descriptor {Value: mappedValue, : true, Enumerable: true, Configurable: true},
|
||||
// and false.
|
||||
|
||||
// In browsers that support Object.defineProperty, use the following:
|
||||
// Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true });
|
||||
|
||||
// For best browser support, use the following:
|
||||
A[ k ] = mappedValue;
|
||||
}
|
||||
// d. Increase k by 1.
|
||||
k++;
|
||||
}
|
||||
|
||||
// 9. return A
|
||||
return A;
|
||||
};
|
||||
}
|
||||
|
||||
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
|
||||
if (!Array.prototype.indexOf) {
|
||||
Array.prototype.indexOf = function (searchElement, fromIndex) {
|
||||
if ( this === undefined || this === null ) {
|
||||
throw new TypeError( '"this" is null or not defined' );
|
||||
}
|
||||
|
||||
var length = this.length >>> 0; // Hack to convert object.length to a UInt32
|
||||
|
||||
fromIndex = +fromIndex || 0;
|
||||
|
||||
if (Math.abs(fromIndex) === Infinity) {
|
||||
fromIndex = 0;
|
||||
}
|
||||
|
||||
if (fromIndex < 0) {
|
||||
fromIndex += length;
|
||||
if (fromIndex < 0) {
|
||||
fromIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (;fromIndex < length; fromIndex++) {
|
||||
if (this[fromIndex] === searchElement) {
|
||||
return fromIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
};
|
||||
}
|
||||
// Based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
|
||||
|
||||
if (! Array.isArray) {
|
||||
Array.isArray = function(obj) {
|
||||
return Object.prototype.toString.call(obj) === "[object Array]";
|
||||
};
|
||||
}
|
||||
|
||||
// https://github.com/ttaubert/node-arraybuffer-slice
|
||||
// (c) 2013 Tim Taubert <tim@timtaubert.de>
|
||||
// arraybuffer-slice may be freely distributed under the MIT license.
|
||||
|
||||
"use strict";
|
||||
|
||||
if (typeof ArrayBuffer !== 'undefined' && !ArrayBuffer.prototype.slice) {
|
||||
ArrayBuffer.prototype.slice = function (begin, end) {
|
||||
begin = (begin|0) || 0;
|
||||
var num = this.byteLength;
|
||||
end = end === (void 0) ? num : (end|0);
|
||||
|
||||
// Handle negative values.
|
||||
if (begin < 0) begin += num;
|
||||
if (end < 0) end += num;
|
||||
|
||||
if (num === 0 || begin >= num || begin >= end) {
|
||||
return new ArrayBuffer(0);
|
||||
}
|
||||
|
||||
var length = Math.min(num - begin, end - begin);
|
||||
var target = new ArrayBuffer(length);
|
||||
var targetArray = new Uint8Array(target);
|
||||
targetArray.set(new Uint8Array(this, begin, length));
|
||||
return target;
|
||||
};
|
||||
}
|
||||
|
||||
// https://github.com/davidchambers/Base64.js
|
||||
// (C) 2015 David Chambers
|
||||
// Base64.js may be freely distributed under the Apache 2.0 License.
|
||||
;(function () {
|
||||
|
||||
var object =
|
||||
typeof exports != 'undefined' ? exports :
|
||||
typeof self != 'undefined' ? self : // #8: web workers
|
||||
$.global; // #31: ExtendScript
|
||||
|
||||
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||
|
||||
function InvalidCharacterError(message) {
|
||||
this.message = message;
|
||||
}
|
||||
InvalidCharacterError.prototype = new Error;
|
||||
InvalidCharacterError.prototype.name = 'InvalidCharacterError';
|
||||
|
||||
// encoder
|
||||
// [https://gist.github.com/999166] by [https://github.com/nignag]
|
||||
object.btoa || (
|
||||
object.btoa = function (input) {
|
||||
var str = String(input);
|
||||
for (
|
||||
// initialize result and counter
|
||||
var block, charCode, idx = 0, map = chars, output = '';
|
||||
// if the next str index does not exist:
|
||||
// change the mapping table to "="
|
||||
// check if d has no fractional digits
|
||||
str.charAt(idx | 0) || (map = '=', idx % 1);
|
||||
// "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8
|
||||
output += map.charAt(63 & block >> 8 - idx % 1 * 8)
|
||||
) {
|
||||
charCode = str.charCodeAt(idx += 3/4);
|
||||
if (charCode > 0xFF) {
|
||||
throw new InvalidCharacterError("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");
|
||||
}
|
||||
block = block << 8 | charCode;
|
||||
}
|
||||
return output;
|
||||
});
|
||||
|
||||
// decoder
|
||||
// [https://gist.github.com/1020396] by [https://github.com/atk]
|
||||
object.atob || (
|
||||
object.atob = function (input) {
|
||||
var str = String(input).replace(/[=]+$/, ''); // #31: ExtendScript bad parse of /=
|
||||
if (str.length % 4 == 1) {
|
||||
throw new InvalidCharacterError("'atob' failed: The string to be decoded is not correctly encoded.");
|
||||
}
|
||||
for (
|
||||
// initialize result and counters
|
||||
var bc = 0, bs, buffer, idx = 0, output = '';
|
||||
// get next character
|
||||
buffer = str.charAt(idx++);
|
||||
// character found in table? initialize bit storage and add its ascii value;
|
||||
~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
|
||||
// and if not first of each 4 characters,
|
||||
// convert the first 8 bits to one ascii character
|
||||
bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
|
||||
) {
|
||||
// try to find character in table (0-63, not found => -1)
|
||||
buffer = chars.indexOf(buffer);
|
||||
}
|
||||
return output;
|
||||
});
|
||||
}());
|
||||
|
||||
|
||||
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString
|
||||
if (!Date.prototype.toISOString) {
|
||||
(function() {
|
||||
|
||||
function pad(number) {
|
||||
if (number < 10) {
|
||||
return '0' + number;
|
||||
}
|
||||
return number;
|
||||
}
|
||||
|
||||
Date.prototype.toISOString = function() {
|
||||
return this.getUTCFullYear() +
|
||||
'-' + pad(this.getUTCMonth() + 1) +
|
||||
'-' + pad(this.getUTCDate()) +
|
||||
'T' + pad(this.getUTCHours()) +
|
||||
':' + pad(this.getUTCMinutes()) +
|
||||
':' + pad(this.getUTCSeconds()) +
|
||||
'.' + (this.getUTCMilliseconds() / 1000).toFixed(3).slice(2, 5) +
|
||||
'Z';
|
||||
};
|
||||
|
||||
}());
|
||||
}
|
||||
|
||||
// note: MDN shim will not work in IE
|
||||
if(typeof Uint8Array !== 'undefined' && !Uint8Array.prototype.slice) Uint8Array.prototype.slice = function(start, end) {
|
||||
if(start < 0) { start += this.length; if(start < 0) start = 0; }
|
||||
if(start >= this.length) return new Uint8Array(0);
|
||||
if(end == null) end = this.length;
|
||||
if(end < 0) { end += this.length; if(end < 0) end = 0; }
|
||||
if(end > this.length) end = this.length;
|
||||
var out = new Uint8Array(end - start);
|
||||
while(start <= --end) out[end - start] = this[end];
|
||||
return out;
|
||||
if(typeof ArrayBuffer !== 'undefined' && !ArrayBuffer.prototype.slice) ArrayBuffer.prototype.slice = function(start, end) {
|
||||
if(start == null) start = 0;
|
||||
if(start < 0) { start += this.byteLength; if(start < 0) start = 0; }
|
||||
if(start >= this.byteLength) return new Uint8Array(0);
|
||||
if(end == null) end = this.byteLength;
|
||||
if(end < 0) { end += this.byteLength; if(end < 0) end = 0; }
|
||||
if(end > this.byteLength) end = this.byteLength;
|
||||
if(start > end) return new Uint8Array(0);
|
||||
var out = new ArrayBuffer(end - start);
|
||||
var view = new Uint8Array(out);
|
||||
var data = new Uint8Array(this, start, end - start)
|
||||
/* IE10 should have Uint8Array#set */
|
||||
if(view.set) view.set(data); else while(start <= --end) view[end - start] = data[end];
|
||||
return out;
|
||||
};
|
||||
if(typeof Uint8Array !== 'undefined' && !Uint8Array.prototype.slice) Uint8Array.prototype.slice = function(start, end) {
|
||||
if(start == null) start = 0;
|
||||
if(start < 0) { start += this.length; if(start < 0) start = 0; }
|
||||
if(start >= this.length) return new Uint8Array(0);
|
||||
if(end == null) end = this.length;
|
||||
if(end < 0) { end += this.length; if(end < 0) end = 0; }
|
||||
if(end > this.length) end = this.length;
|
||||
if(start > end) return new Uint8Array(0);
|
||||
var out = new Uint8Array(end - start);
|
||||
while(start <= --end) out[end - start] = this[end];
|
||||
return out;
|
||||
};
|
||||
|
||||
// VBScript + ActiveX fallback for IE5+
|
||||
var IE_SaveFile = (function() { try {
|
||||
if(typeof IE_SaveFile_Impl == "undefined") document.write([
|
||||
'<script type="text/vbscript" language="vbscript">',
|
||||
'IE_GetProfileAndPath_Key = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders\\"',
|
||||
'Function IE_GetProfileAndPath(key): Set wshell = CreateObject("WScript.Shell"): IE_GetProfileAndPath = wshell.RegRead(IE_GetProfileAndPath_Key & key): IE_GetProfileAndPath = wshell.ExpandEnvironmentStrings("%USERPROFILE%") & "!" & IE_GetProfileAndPath: End Function',
|
||||
'Function IE_SaveFile_Impl(FileName, payload): Dim data, plen, i, bit: data = CStr(payload): plen = Len(data): Set fso = CreateObject("Scripting.FileSystemObject"): fso.CreateTextFile FileName, True: Set f = fso.GetFile(FileName): Set stream = f.OpenAsTextStream(2, 0): For i = 1 To plen Step 3: bit = Mid(data, i, 2): stream.write Chr(CLng("&h" & bit)): Next: stream.Close: IE_SaveFile_Impl = True: End Function',
|
||||
'|/script>'.replace("|","<")
|
||||
].join("\r\n"));
|
||||
if(typeof IE_SaveFile_Impl == "undefined") return void 0;
|
||||
var IE_GetPath = (function() {
|
||||
var DDP1 = "";
|
||||
try { DDP1 = IE_GetProfileAndPath("{374DE290-123F-4565-9164-39C4925E467B}"); } catch(e) { try { DDP1 = IE_GetProfileAndPath("Personal"); } catch(e) { try { DDP1 = IE_GetProfileAndPath("Desktop"); } catch(e) { throw e; }}}
|
||||
var o = DDP1.split("!");
|
||||
DDP = o[1].replace("%USERPROFILE%", o[0]);
|
||||
return function(path) { return DDP + "\\" + path; };
|
||||
})();
|
||||
function fix_data(data) {
|
||||
var out = [];
|
||||
var T = typeof data == "string";
|
||||
for(var i = 0; i < data.length; ++i) out.push(("00"+(T ? data.charCodeAt(i) : data[i]).toString(16)).slice(-2));
|
||||
var o = out.join("|");
|
||||
return o;
|
||||
}
|
||||
return function(data, filename) { return IE_SaveFile_Impl(IE_GetPath(filename), fix_data(data)); };
|
||||
} catch(e) { return void 0; }})();
|
||||
var IE_LoadFile = (function() { try {
|
||||
if(typeof IE_LoadFile_Impl == "undefined") document.write([
|
||||
'<script type="text/vbscript" language="vbscript">',
|
||||
'Function IE_LoadFile_Impl(FileName): Dim out(), plen, i, cc: Set fso = CreateObject("Scripting.FileSystemObject"): Set f = fso.GetFile(FileName): Set stream = f.OpenAsTextStream(1, 0): plen = f.Size: ReDim out(plen): For i = 1 To plen Step 1: cc = Hex(Asc(stream.read(1))): If Len(cc) < 2 Then: cc = "0" & cc: End If: out(i) = cc: Next: IE_LoadFile_Impl = Join(out,""): End Function',
|
||||
'|/script>'.replace("|","<")
|
||||
].join("\r\n"));
|
||||
if(typeof IE_LoadFile_Impl == "undefined") return void 0;
|
||||
function fix_data(data) {
|
||||
var out = [];
|
||||
for(var i = 0; i < data.length; i+=2) out.push(String.fromCharCode(parseInt(data.slice(i, i+2), 16)));
|
||||
var o = out.join("");
|
||||
return o;
|
||||
}
|
||||
return function(filename) { return fix_data(IE_LoadFile_Impl(filename)); };
|
||||
} catch(e) { return void 0; }})();
|
||||
|
||||
// getComputedStyle polyfill from https://gist.github.com/8HNHoFtE/5891086
|
||||
if(typeof window !== 'undefined' && typeof window.getComputedStyle !== 'function') {
|
||||
window.getComputedStyle = function(e,t){return this.el=e,this.getPropertyValue=function(t){var n=/(\-([a-z]){1})/g;return t=="float"&&(t="styleFloat"),n.test(t)&&(t=t.replace(n,function(){return arguments[2].toUpperCase()})),e.currentStyle[t]?e.currentStyle[t]:null},this}
|
||||
}
|
||||
|
|
189
test.js
189
test.js
|
@ -1,6 +1,7 @@
|
|||
/* cfb.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* vim: set ts=2: */
|
||||
/*jshint mocha:true */
|
||||
/* eslint-env mocha */
|
||||
/*global process, require */
|
||||
/*::
|
||||
declare type EmptyFunc = (() => void) | null;
|
||||
|
@ -11,9 +12,11 @@ declare var before:(test:EmptyFunc)=>void;
|
|||
*/
|
||||
var CFB;
|
||||
var fs = require('fs');
|
||||
describe('source', function() { it('should load', function() { CFB = require('./'); }); });
|
||||
describe('source', function() { it('should load', function() { CFB = require('./'); if(zlibify) CFB.utils.use_zlib(require("zlib")); }); });
|
||||
var CRC32 = require('crc-32');
|
||||
|
||||
var WTF = !!process.env.WTF;
|
||||
var zlibify = !!process.env.ZLIB;
|
||||
var ex = [".xls",".doc",".ppt"];
|
||||
if(process.env.FMTS) ex=process.env.FMTS.split(":").map(function(x){return x[0]==="."?x:"."+x;});
|
||||
if(process.env.FMTS === "full") process.env.FMTS = ex.join(":");
|
||||
|
@ -25,68 +28,113 @@ var files = fs.readdirSync('test_files').filter(ffunc);
|
|||
var f2011 = fs.readdirSync('test_files/2011').filter(ffunc);
|
||||
var f2013 = fs.readdirSync('test_files/2013').filter(ffunc);
|
||||
var fpres = fs.readdirSync('test_files_pres').filter(ffunc);
|
||||
var fxlsx = fs.readdirSync('test_files').filter(function(x) { return x.slice(-5) == ".xlsx" && fails.indexOf(x) === -1; });
|
||||
|
||||
var dir = "./test_files/";
|
||||
var TYPE = "buffer";
|
||||
|
||||
var names = [
|
||||
var names/*:Array<Array<string>>*/ = [
|
||||
["!DocumentSummaryInformation", "\u0005"],
|
||||
["!SummaryInformation", "\u0005"],
|
||||
["!CompObj", "\u0001"],
|
||||
["!DataSpaces", "\u0006"],
|
||||
["/!DataSpaces/Version", "\u0006"],
|
||||
["!DRMContent", "\u0009"],
|
||||
["!DRMViewerContent", "\u0009"],
|
||||
["!Ole", "\u0001"]
|
||||
].map(function(x) { return [x[0], x[0].replace("!", x[1])]; });
|
||||
|
||||
/* [ rel, abs ] */
|
||||
var ENTRIES/*:Array<Array<string>>*/ = [
|
||||
/* DOC */
|
||||
["WordDocument", "/WordDocument"],
|
||||
/* PPT */
|
||||
["PowerPoint Document", "/PowerPoint Document"],
|
||||
/* XLS */
|
||||
["Workbook", "/Workbook"],
|
||||
["Book", "/Book"],
|
||||
/* OPC */
|
||||
["[Content_Types].xml", "/[Content_Types].xml"],
|
||||
/* ODF */
|
||||
["content.xml", "/content.xml"],
|
||||
/* XLSX */
|
||||
["workbook.xml", "/xl/workbook.xml"],
|
||||
/* Encrypted */
|
||||
["EncryptedPackage", "/EncryptedPackage"],
|
||||
["EncryptionInfo", "/EncryptionInfo"]
|
||||
];
|
||||
|
||||
var REL_FILES = ENTRIES.map(function(e) { return e[0]; });
|
||||
var ABS_FILES = ENTRIES.map(function(e) { return e[1]; });
|
||||
|
||||
function has_file(cfb, files/*:Array<string>*/)/*:string*/ {
|
||||
for(var i = 0; i < files.length; ++i) if(CFB.find(cfb, files[i])) return files[i];
|
||||
return "";
|
||||
}
|
||||
|
||||
function zero_dates(cfb) {
|
||||
cfb.FileIndex.forEach(function(f) { delete f.mt; delete f.ct; });
|
||||
}
|
||||
|
||||
function parsetest(x, cfb) {
|
||||
describe(x + ' should have basic parts', function() {
|
||||
it('should find relative path', function() {
|
||||
switch(x.substr(-4)) {
|
||||
case '.xls': if(!CFB.find(cfb, 'Workbook') && !CFB.find(cfb, 'Book')) throw new Error("Cannot find workbook for " + x); break;
|
||||
case '.ppt': if(!CFB.find(cfb, 'PowerPoint Document')) throw new Error("Cannot find presentation for " + x); break;
|
||||
case '.doc': if(!CFB.find(cfb, 'WordDocument') && !CFB.find(cfb, 'Word Document')) throw new Error("Cannot find doc for " + x); break;
|
||||
}
|
||||
if(!has_file(cfb, REL_FILES)) throw new Error("Cannot find content for " + x);
|
||||
});
|
||||
it('should find absolute path', function() {
|
||||
switch(x.substr(-4)) {
|
||||
case '.xls': if(!CFB.find(cfb, '/Workbook') && !CFB.find(cfb, '/Book')) throw new Error("Cannot find workbook for " + x); break;
|
||||
case '.ppt': if(!CFB.find(cfb, '/PowerPoint Document')) throw new Error("Cannot find presentation for " + x); break;
|
||||
case '.doc': if(!CFB.find(cfb, '/WordDocument') && !CFB.find(cfb, '/Word Document')) throw new Error("Cannot find doc for " + x); break;
|
||||
}
|
||||
if(!has_file(cfb, ABS_FILES)) throw new Error("Cannot find content for " + x);
|
||||
});
|
||||
it('should handle "!" aliases', function() {
|
||||
names.forEach(function(n) { if(CFB.find(cfb,n[0]) != CFB.find(cfb,n[1])) throw new Error("Bad name: " + n.join(" != ")); });
|
||||
});
|
||||
it('should handle size < 0', function() {
|
||||
cfb.FileIndex.forEach(function(p, i) { if(p.size < 0) throw new Error(cfb.FullPaths[i] + " size=" + p.size); });
|
||||
});
|
||||
});
|
||||
describe(x + ' should roundtrip', function() {
|
||||
var data, newcfb;
|
||||
it('should roundtrip safely', function() {
|
||||
data = CFB.write(cfb, {type:TYPE});
|
||||
newcfb = CFB.read(data, {type:TYPE});
|
||||
var datacfb, newcfb;
|
||||
var datazip, newzip;
|
||||
var datacmp, newcmp;
|
||||
before(function() {
|
||||
/* cfb */
|
||||
zero_dates(cfb);
|
||||
datacfb = CFB.write(cfb, {type:TYPE});
|
||||
newcfb = CFB.read(datacfb, {type:TYPE});
|
||||
zero_dates(newcfb);
|
||||
|
||||
/* zip */
|
||||
zero_dates(cfb);
|
||||
datazip = CFB.write(cfb, {type:TYPE, fileType:"zip"});
|
||||
newzip = CFB.read(datazip, {type:TYPE});
|
||||
zero_dates(newzip);
|
||||
|
||||
/* zip with compression */
|
||||
zero_dates(cfb);
|
||||
datacmp = CFB.write(cfb, {type:TYPE, fileType:"zip", compression:1});
|
||||
newcmp = CFB.read(datacmp, {type:TYPE});
|
||||
zero_dates(newcmp);
|
||||
});
|
||||
it('should preserve content', function() {
|
||||
var _old, _new;
|
||||
switch(x.substr(-4)) {
|
||||
case '.xls':
|
||||
_old = CFB.find(cfb, '/Workbook') || CFB.find(cfb, '/Book');
|
||||
_new = CFB.find(newcfb, '/Workbook') || CFB.find(newcfb, '/Book');
|
||||
break;
|
||||
case '.ppt':
|
||||
_old = CFB.find(cfb, '/PowerPoint Document');
|
||||
_new = CFB.find(newcfb, '/PowerPoint Document');
|
||||
break;
|
||||
case '.doc':
|
||||
_old = CFB.find(cfb, '/WordDocument') || CFB.find(cfb, '/Word Document');
|
||||
_new = CFB.find(newcfb, '/WordDocument') || CFB.find(newcfb, '/Word Document');
|
||||
break;
|
||||
}
|
||||
/*:: if(!_old || !_new) throw "unreachable"; */
|
||||
if(CRC32.buf(_old.content) != CRC32.buf(_new.content)) throw new Error(x + " failed roundtrip test");
|
||||
var path = has_file(cfb, REL_FILES);
|
||||
var _old = CFB.find(cfb, path);
|
||||
var _cfb = CFB.find(newcfb, path);
|
||||
var _zip = CFB.find(newzip, path);
|
||||
var _cmp = CFB.find(newcmp, path);
|
||||
/*:: if(!_old || !_cfb || !_zip || !_cmp) throw "unreachable"; */
|
||||
var c1 = CRC32.buf(_old.content);
|
||||
var c2 = CRC32.buf(_cfb.content);
|
||||
var c3 = CRC32.buf(_zip.content);
|
||||
var c4 = CRC32.buf(_cmp.content);
|
||||
if(c1 != c2) throw new Error(x + " failed CFB roundtrip test");
|
||||
if(c1 != c3) throw new Error(x + " failed ZIP roundtrip test");
|
||||
if(c1 != c4) throw new Error(x + " failed ZIP compression roundtrip test");
|
||||
});
|
||||
it('should be idempotent', function() {
|
||||
var dat2 = CFB.write(newcfb, {type:TYPE});
|
||||
if(CRC32.buf(data) != CRC32.buf(dat2)) throw new Error(x + " failed idempotent test");
|
||||
if(CRC32.buf(datacfb) != CRC32.buf(dat2)) throw new Error(x + " failed CFB idempotent test");
|
||||
var dat2zip = CFB.write(newzip, {type:TYPE, fileType:"zip"});
|
||||
if(CRC32.buf(datazip) != CRC32.buf(dat2zip)) throw new Error(x + " failed ZIP idempotent test");
|
||||
var dat2cmp = CFB.write(newcmp, {type:TYPE, fileType:"zip", compression:1});
|
||||
if(CRC32.buf(datacmp) != CRC32.buf(dat2cmp)) throw new Error(x + " failed ZIP idempotent test");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -94,41 +142,99 @@ function parsetest(x, cfb) {
|
|||
describe('should parse test files', function() {
|
||||
files.forEach(function(x) {
|
||||
it('should parse ' + x, function() {
|
||||
var cfb = CFB.read('./test_files/' + x, {type: "file"});
|
||||
var cfb = CFB.read('./test_files/' + x, {type: "file", WTF: WTF});
|
||||
parsetest(x, cfb);
|
||||
});
|
||||
});
|
||||
fpres.forEach(function(x) {
|
||||
it('should parse ' + x, function() {
|
||||
var cfb = CFB.read('./test_files_pres/' + x, {type: "file"});
|
||||
var cfb = CFB.read('./test_files_pres/' + x, {type: "file", WTF: WTF});
|
||||
parsetest(x, cfb);
|
||||
});
|
||||
});
|
||||
f2011.forEach(function(x) {
|
||||
it('should parse ' + x, function() {
|
||||
var cfb = CFB.read('./test_files/2011/' + x, {type: "file"});
|
||||
var cfb = CFB.read('./test_files/2011/' + x, {type: "file", WTF: WTF});
|
||||
parsetest(x, cfb);
|
||||
});
|
||||
});
|
||||
f2013.forEach(function(x) {
|
||||
it('should parse ' + x, function() {
|
||||
var cfb = CFB.read('./test_files/2013/' + x, {type: "file"});
|
||||
var cfb = CFB.read('./test_files/2013/' + x, {type: "file", WTF: WTF});
|
||||
parsetest(x, cfb);
|
||||
});
|
||||
});
|
||||
fxlsx.forEach(function(x) {
|
||||
it('should parse ' + x, function() {
|
||||
try {
|
||||
var cfb = CFB.read('./test_files/' + x, {type: "file", WTF: WTF});
|
||||
parsetest(x, cfb);
|
||||
} catch(e) {
|
||||
if(e.message.match(/CFB file size /)) return;
|
||||
if(!e.message.match(/Header Signature: Expected d0cf11e0a1b11ae1 saw /)) throw e;
|
||||
}
|
||||
});
|
||||
});
|
||||
it('should recognize correct magic number', function() {
|
||||
var cfb = CFB.read('./test_files/AutoFilter.xls', {type: "file"});
|
||||
if(!CFB.find(cfb, '!CompObj')) throw new Error("Could not find !CompObj");
|
||||
if(!CFB.find(cfb, '\u0001CompObj')) throw new Error("Could not find 1CompObj");
|
||||
if(CFB.find(cfb, '\u0005CompObj')) throw new Error("Found 5CompObj");
|
||||
|
||||
if(!CFB.find(cfb, '!DocumentSummaryInformation')) throw new Error("Could not find !DSI");
|
||||
if(!CFB.find(cfb, '\u0005DocumentSummaryInformation')) throw new Error("Could not find 5DSI");
|
||||
if(CFB.find(cfb, '\u0001DocumentSummaryInformation')) throw new Error("Found 1DSI");
|
||||
});
|
||||
});
|
||||
|
||||
var cp = 'custom_properties.xls';
|
||||
|
||||
var xl = 'custom_properties.xlsx';
|
||||
describe('input formats', function() {
|
||||
it('should read binary strings', function() {
|
||||
CFB.read(fs.readFileSync(dir + '/' + cp, 'binary'), {type: 'binary'});
|
||||
CFB.read(fs.readFileSync(dir + '/' + xl, 'binary'), {type: 'binary'});
|
||||
});
|
||||
it('should read base64 strings', function() {
|
||||
CFB.read(fs.readFileSync(dir + '/' + cp, 'base64'), {type: 'base64'});
|
||||
CFB.read(fs.readFileSync(dir + '/' + xl, 'base64'), {type: 'base64'});
|
||||
});
|
||||
it('should read buffers', function() {
|
||||
CFB.read(fs.readFileSync(dir + '/' + cp), {type: 'buffer'});
|
||||
CFB.read(fs.readFileSync(dir + '/' + xl), {type: 'buffer'});
|
||||
});
|
||||
});
|
||||
describe('output formats', function() {
|
||||
it('should write binary strings', function() {
|
||||
var t = [
|
||||
[ "CFB", CFB.write(CFB.read(fs.readFileSync(dir + '/' + cp, 'binary'), {type: 'binary'}), {type: 'binary'})],
|
||||
[ "ZIP", CFB.write(CFB.read(fs.readFileSync(dir + '/' + xl, 'binary'), {type: 'binary'}), {type: 'binary', fileType: 'zip'})]
|
||||
];
|
||||
t.forEach(function(r) {
|
||||
if(typeof r[1] != "string") throw new Error(r[0] + " binary write failed");
|
||||
var good = false;
|
||||
for(var i = 0; i < r[1].length; ++i) {
|
||||
if(/*::((*/r[1]/*:: :any):string)*/.charCodeAt(i) == 0x00) good = true;
|
||||
else if(/*::((*/r[1]/*:: :any):string)*/.charCodeAt(i) > 0xFF) { good = false; break; }
|
||||
}
|
||||
if(!good) throw new Error(r[0] + " binary write failed");
|
||||
});
|
||||
});
|
||||
it('should write base64 strings', function() {
|
||||
var t = [
|
||||
[ "CFB", CFB.write(CFB.read(fs.readFileSync(dir + '/' + cp, 'base64'), {type: 'base64'}), {type: 'base64'})],
|
||||
[ "ZIP", CFB.write(CFB.read(fs.readFileSync(dir + '/' + xl, 'base64'), {type: 'base64'}), {type: 'base64', fileType: 'zip'})]
|
||||
];
|
||||
t.forEach(function(r) {
|
||||
if(typeof r[1] != "string") throw new Error(r[0] + " base64 write failed");
|
||||
var good = false;
|
||||
if(r[1].match(/[^a-zA-Z0-9+\/\+\.=]/)) throw new Error(r[0] + " base64 write failed");
|
||||
});
|
||||
});
|
||||
it('should write buffers', function() {
|
||||
var t1 = CFB.write(CFB.read(fs.readFileSync(dir + '/' + cp), {type: 'buffer'}), {type: 'buffer'});
|
||||
var t2 = CFB.write(CFB.read(fs.readFileSync(dir + '/' + xl), {type: 'buffer'}), {type: 'buffer'});
|
||||
if(!Buffer.isBuffer(t1)) throw new Error("CFB buffer write failed");
|
||||
if(!Buffer.isBuffer(t2)) throw new Error("ZIP buffer write failed");
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -164,5 +270,8 @@ describe('api', function() {
|
|||
var file = CFB.find(cfb, '/dafuq');
|
||||
if(!file || !file.content) throw new Error("Cannot find /dafuq");
|
||||
if(file.content.length != 3) throw new Error("Bad content length " + file.content.length);
|
||||
file = CFB.find(newcfb, '/dafuq');
|
||||
if(!file || !file.content) throw new Error("Cannot find /dafuq");
|
||||
if(file.content.length != 3) throw new Error("Bad content length " + file.content.length);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* cfb.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* eslint-env node */
|
||||
/* vim: set ts=2 ft=javascript: */
|
||||
/// <reference types="../node_modules/@types/node" />
|
||||
const n = "cfb";
|
||||
import * as X from 'cfb';
|
||||
import fs = require('fs');
|
||||
|
@ -10,13 +11,14 @@ const sprintf = PRINTJ.sprintf;
|
|||
program
|
||||
.version(X.version)
|
||||
.usage('[options] <file> [subfiles...]')
|
||||
.option('-q, --quiet', 'process but do not report')
|
||||
.option('-l, --list-files', 'list files')
|
||||
.option('-z, --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('-O, --to-stdout', 'extract raw contents to stdout')
|
||||
.option('-z, --dump', 'dump internal representation but do not extract')
|
||||
.option('-q, --quiet', 'process but do not report')
|
||||
.option('--dev', 'development mode')
|
||||
.option('--read', 'read but do not print out contents');
|
||||
|
||||
|
@ -36,10 +38,10 @@ if(program.create) {
|
|||
|
||||
if(!fs.existsSync(program.args[0])) die(1, "must specify a filename");
|
||||
|
||||
const opts: X.CFBParsingOptions = {type:'file'};
|
||||
const opts: X.CFB$ParsingOptions = {type:'file'};
|
||||
if(program.dev) opts.WTF = true;
|
||||
|
||||
const cfb: X.CFBContainer = X.read(program.args[0], opts);
|
||||
const cfb: X.CFB$Container = X.read(program.args[0], opts);
|
||||
if(program.quiet) exit(0);
|
||||
|
||||
if(program.dump) {
|
||||
|
@ -61,7 +63,7 @@ if(program.listFiles) {
|
|||
let cnt = 0, rootsize = 0, filesize = 0;
|
||||
console.log(" Length Date Time Name");
|
||||
console.log(" -------- ---- ---- ----");
|
||||
cfb.FileIndex.forEach((file: X.CFBEntry, i: number) => {
|
||||
cfb.FileIndex.forEach((file: X.CFB$Entry, i: number) => {
|
||||
switch(file.type) {
|
||||
case 5:
|
||||
basetime = file.ct || file.mt || basetime;
|
||||
|
@ -85,7 +87,7 @@ const mkdirp = (path: string) => { path.split("/").reduce((acc: string, p: strin
|
|||
return acc;
|
||||
}, ""); };
|
||||
|
||||
const write = (path: string, data: X.CFBEntry) => {
|
||||
const write = (path: string, data: X.CFB$Entry) => {
|
||||
logit("write", fix_string(path));
|
||||
fs.writeFileSync(path, /*::new Buffer((*/data.content/*:: :any))*/);
|
||||
};
|
||||
|
@ -110,16 +112,18 @@ if(program.delete) {
|
|||
|
||||
if(program.args.length > 1) {
|
||||
program.args.slice(1).forEach((x: string) => {
|
||||
const data/*:?CFBEntry*/ = X.find(cfb, x);
|
||||
const data: X.CFB$Entry = X.find(cfb, x.replace(/\\u000\d/g,"!"));
|
||||
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];
|
||||
if(program.toStdout) return process.stdout.write(new Buffer(<any>data.content));
|
||||
mkdirp(path.slice(0, path.lastIndexOf("/")));
|
||||
write(path, data);
|
||||
});
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if(program.toStdout) exit(0);
|
||||
for(let i=0; i!==cfb.FullPaths.length; ++i) {
|
||||
if(!cfb.FileIndex[i].name) continue;
|
||||
if(cfb.FullPaths[i].slice(-1) === "/") mkdirp(cfb.FullPaths[i]);
|
||||
|
|
|
@ -5,100 +5,124 @@
|
|||
export const version: string;
|
||||
|
||||
/** Parse a buffer or array */
|
||||
export function parse(f: CFB$Blob, options?: CFBParsingOptions): CFBContainer;
|
||||
export function parse(f: CFB$Blob, options?: CFB$ParsingOptions): CFB$Container;
|
||||
|
||||
/** Read a blob or file or binary string */
|
||||
export function read(f: CFB$Blob | string, options?: CFBParsingOptions): CFBContainer;
|
||||
export function read(f: CFB$Blob | string, options?: CFB$ParsingOptions): CFB$Container;
|
||||
|
||||
/** Find a file entry given a path or file name */
|
||||
export function find(cfb: CFBContainer, path: string): CFBEntry | null;
|
||||
export function find(cfb: CFB$Container, path: string): CFB$Entry | null;
|
||||
|
||||
/** Generate a container file */
|
||||
export function write(cfb: CFBContainer, options?: any): any;
|
||||
export function write(cfb: CFB$Container, options?: CFB$WritingOptions): any;
|
||||
|
||||
/** Write a container file to the filesystem */
|
||||
export function writeFile(cfb: CFBContainer, filename: string, options?: any): any;
|
||||
export function writeFile(cfb: CFB$Container, filename: string, options?: CFB$WritingOptions): any;
|
||||
|
||||
/** Utility functions */
|
||||
export const utils: CFB$Utils;
|
||||
|
||||
export interface CFB$CommonOptions {
|
||||
/** Data encoding */
|
||||
type?: 'base64' | 'binary' | 'buffer' | 'file' | 'array';
|
||||
|
||||
/** Options for read and readFile */
|
||||
export interface CFBParsingOptions {
|
||||
/** Input data encoding */
|
||||
type?: 'base64' | 'binary' | 'buffer' | 'file' | 'array';
|
||||
/** If true, throw errors when features are not understood */
|
||||
WTF?: boolean;
|
||||
/** If true, include raw data in output */
|
||||
raw?: boolean;
|
||||
/** If true, throw errors when features are not understood */
|
||||
WTF?: boolean;
|
||||
}
|
||||
|
||||
export type CFB$Blob = Buffer | number[] | Uint8Array;
|
||||
/** Options for read and readFile */
|
||||
export interface CFB$ParsingOptions extends CFB$CommonOptions {
|
||||
/** If true, include raw data in output */
|
||||
raw?: boolean;
|
||||
}
|
||||
|
||||
export enum CFBEntryType { unknown, storage, stream, lockbytes, property, root }
|
||||
export enum CFBStorageType { fat, minifat }
|
||||
/** Options for write and writeFile */
|
||||
export interface CFB$WritingOptions extends CFB$CommonOptions {
|
||||
/** Output file type */
|
||||
fileType?: 'cfb' | 'zip' | 'mad';
|
||||
|
||||
/** Override default root entry name (CFB only) */
|
||||
root?: string;
|
||||
|
||||
/** Enable compression (ZIP only) */
|
||||
compression?: boolean;
|
||||
}
|
||||
|
||||
export type CFB$Blob = number[] | Uint8Array;
|
||||
|
||||
export enum CFB$EntryType { unknown, storage, stream, lockbytes, property, root }
|
||||
export enum CFB$StorageType { fat, minifat }
|
||||
|
||||
/** CFB File Entry Object */
|
||||
export interface CFBEntry {
|
||||
/** Case-sensitive internal name */
|
||||
name: string;
|
||||
export interface CFB$Entry {
|
||||
/** Case-sensitive internal name */
|
||||
name: string;
|
||||
|
||||
/** CFB type (salient types: stream, storage) -- see CFBEntryType */
|
||||
type: number;
|
||||
/** CFB type (salient types: stream, storage) -- see CFB$EntryType */
|
||||
type: number;
|
||||
|
||||
/** Raw Content (Buffer when available, Array of bytes otherwise) */
|
||||
content: CFB$Blob;
|
||||
/** Raw Content (Buffer when available, Array of bytes otherwise) */
|
||||
content: CFB$Blob;
|
||||
|
||||
/** Creation Time */
|
||||
ct?: Date;
|
||||
/** Creation Time */
|
||||
ct?: Date;
|
||||
|
||||
/** Modification Time */
|
||||
mt?: Date;
|
||||
/** Modification Time */
|
||||
mt?: Date;
|
||||
|
||||
/** Red/Black Tree color: 0 = red, 1 = black */
|
||||
color: number;
|
||||
/** Red/Black Tree color: 0 = red, 1 = black */
|
||||
color: number;
|
||||
|
||||
/** Class ID represented as hex string */
|
||||
clsid: string;
|
||||
/** Class ID represented as hex string */
|
||||
clsid: string;
|
||||
|
||||
/** User-Defined State Bits */
|
||||
state: number;
|
||||
/** User-Defined State Bits */
|
||||
state: number;
|
||||
|
||||
/** Starting Sector */
|
||||
start: number;
|
||||
/** Starting Sector */
|
||||
start: number;
|
||||
|
||||
/** Data Size */
|
||||
size: number;
|
||||
/** Data Size */
|
||||
size: number;
|
||||
|
||||
/** Storage location -- see CFBStorageType */
|
||||
storage?: string;
|
||||
/** Storage location -- see CFB$StorageType */
|
||||
storage?: string;
|
||||
|
||||
/** Content Type (used for MAD) */
|
||||
ctype?: string;
|
||||
}
|
||||
|
||||
/* File object */
|
||||
export interface CFBContainer {
|
||||
/* list of streams and storages */
|
||||
FullPaths: string[];
|
||||
export interface CFB$Container {
|
||||
/* List of streams and storages */
|
||||
FullPaths: string[];
|
||||
|
||||
/* Array of entries in the same order as FullPaths */
|
||||
FileIndex: CFBEntry[];
|
||||
/* Array of entries in the same order as FullPaths */
|
||||
FileIndex: CFB$Entry[];
|
||||
|
||||
/* Raw Content, in chunks (Buffer when available, Array of bytes otherwise) */
|
||||
raw?: {
|
||||
header: CFB$Blob,
|
||||
sectors: CFB$Blob[];
|
||||
};
|
||||
/* Raw Content, in chunks (Buffer when available, Array of bytes otherwise) */
|
||||
raw?: {
|
||||
header: CFB$Blob,
|
||||
sectors: CFB$Blob[];
|
||||
};
|
||||
}
|
||||
|
||||
/** cfb_add options */
|
||||
export interface CFB$AddOpts {
|
||||
/** Skip existence and safety checks (best for bulk write operations) */
|
||||
unsafe?: boolean;
|
||||
}
|
||||
|
||||
/** General utilities */
|
||||
export interface CFB$Utils {
|
||||
cfb_new(opts?: any): CFBContainer;
|
||||
cfb_add(cfb: CFBContainer, name: string, content: any, opts?: any): CFBEntry;
|
||||
cfb_del(cfb: CFBContainer, name: string): boolean;
|
||||
cfb_mov(cfb: CFBContainer, old_name: string, new_name: string): boolean;
|
||||
cfb_gc(cfb: CFBContainer): void;
|
||||
ReadShift(size: number, t?: string): number|string;
|
||||
WarnField(hexstr: string, fld?: string): void;
|
||||
CheckField(hexstr: string, fld?: string): void;
|
||||
prep_blob(blob: any, pos?: number): CFB$Blob;
|
||||
bconcat(bufs: any[]): any;
|
||||
cfb_new(opts?: any): CFB$Container;
|
||||
cfb_add(cfb: CFB$Container, name: string, content: any, opts?: CFB$AddOpts): CFB$Entry;
|
||||
cfb_del(cfb: CFB$Container, name: string): boolean;
|
||||
cfb_mov(cfb: CFB$Container, old_name: string, new_name: string): boolean;
|
||||
cfb_gc(cfb: CFB$Container): void;
|
||||
ReadShift(size: number, t?: string): number|string;
|
||||
WarnField(hexstr: string, fld?: string): void;
|
||||
CheckField(hexstr: string, fld?: string): void;
|
||||
prep_blob(blob: any, pos?: number): CFB$Blob;
|
||||
bconcat(bufs: any[]): any;
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ const old_cfb = CFB.read("t2.xls", {type:"file"});
|
|||
const new_cfb = CFB.utils.cfb_new({root:"R", clsid: old_cfb.FileIndex[0].clsid });
|
||||
old_cfb.FullPaths.forEach((p, i) => {
|
||||
if(p.slice(-1) === "/") return;
|
||||
CFB.utils.cfb_add(new_cfb, p.replace(/^[^/]*/,"R"), old_cfb.FileIndex[i].content);
|
||||
CFB.utils.cfb_add(new_cfb, p.replace(/^[^/]*/,"R"), old_cfb.FileIndex[i].content, {unsafe: true});
|
||||
});
|
||||
dumpit(new_cfb);
|
||||
CFB.writeFile(new_cfb, "t3.xls");
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
"paths": { "cfb": ["."] },
|
||||
"types": [],
|
||||
"noEmit": true,
|
||||
"strictFunctionTypes": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
"only-arrow-functions": false,
|
||||
"no-consecutive-blank-lines": false,
|
||||
"prefer-conditional-expression": false,
|
||||
"one-variable-per-declaration": false
|
||||
"one-variable-per-declaration": false,
|
||||
"no-implicit-dependencies": false,
|
||||
"prefer-template": false
|
||||
}
|
||||
}
|
||||
|
|
1203
xlscfb.flow.js
1203
xlscfb.flow.js
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue