1
+ #!/usr/bin/env node
2
+ /* index.js (C) 2020-present SheetJS -- http://sheetjs.com */
3
+ /* eslint-env node */
4
+ /* vim: set ts=2 ft=javascript: */
5
+
6
+ var n = "cfb-cli" ;
7
+ var X = require ( 'cfb' ) ;
8
+ var fs = require ( 'fs' ) ;
9
+ var program = require ( 'commander' ) ;
10
+ var PRINTJ = require ( "printj" ) ;
11
+ function run ( ) {
12
+ var sprintf = PRINTJ . sprintf ;
13
+ program
14
+ . version ( X . version )
15
+ . usage ( '[options] <file> [subfiles...]' )
16
+ . option ( '-l, --list-files' , 'list files' )
17
+ . option ( '-r, --repair' , 'attempt to repair and garbage-collect archive' )
18
+ . option ( '-c, --create' , 'create file' )
19
+ . option ( '-a, --append' , 'add files to CFB (overwrite existing data)' )
20
+ . option ( '-d, --delete' , 'delete files from CFB' )
21
+ . option ( '-O, --to-stdout' , 'extract raw contents to stdout' )
22
+ . option ( '-z, --dump' , 'dump internal representation but do not extract' )
23
+ . option ( '-q, --quiet' , 'process but do not report' )
24
+ . option ( '--here' , 'skip the CFB root name when extracting' )
25
+ . option ( '--osx' , 'use OSX-style unzip listing' )
26
+ . option ( '--zlib' , 'use native zlib' )
27
+ . option ( '--local' , 'print times in local timezone' )
28
+ . option ( '--dev' , 'development mode' )
29
+ . option ( '--read' , 'read but do not print out contents' ) ;
30
+ program . parse ( process . argv ) ;
31
+
32
+ if ( program . zlib ) X . utils . use_zlib ( require ( 'zlib' ) ) ;
33
+
34
+ var exit = process . exit ;
35
+ var die = function ( errno /*:number*/ , msg /*:string*/ ) { console . error ( n + ": " + msg ) ; exit ( errno ) ; } ;
36
+ var logit = function ( cmd /*:string*/ , f /*:string*/ ) { console . error ( sprintf ( "%-6s %s" , cmd , f ) ) ; } ;
37
+
38
+ if ( program . args . length === 0 ) die ( 1 , "must specify a filename" ) ;
39
+
40
+ if ( program . create ) {
41
+ logit ( "create" , program . args [ 0 ] ) ;
42
+ var newcfb = X . utils . cfb_new ( ) ;
43
+ X . writeFile ( newcfb , program . args [ 0 ] ) ;
44
+ }
45
+
46
+ if ( ! fs . existsSync ( program . args [ 0 ] ) ) die ( 1 , "must specify a filename" ) ;
47
+
48
+ var opts = ( { type : 'file' } /*:any*/ ) ;
49
+ if ( program . dev ) opts . WTF = true ;
50
+
51
+ var cfb = X . read ( program . args [ 0 ] , opts ) ;
52
+ if ( program . quiet ) exit ( 0 ) ;
53
+
54
+ if ( program . dump ) {
55
+ console . log ( "Full Paths:" ) ;
56
+ console . log ( cfb . FullPaths . map ( function ( x /*:string*/ ) { return " " + x ; } ) . join ( "\n" ) ) ;
57
+ console . log ( "File Index:" ) ;
58
+ console . log ( cfb . FileIndex ) ;
59
+ exit ( 0 ) ;
60
+ }
61
+ if ( program . repair ) { X . writeFile ( cfb , program . args [ 0 ] ) ; exit ( 0 ) ; }
62
+
63
+ var rlen = cfb . FullPaths [ 0 ] . length ;
64
+
65
+ function fix_string ( x /*:string*/ ) /*:string*/ { return x . replace ( / [ \u0000 - \u001f ] / g, function ( $$ ) { return sprintf ( "\\u%04X" , $$ . charCodeAt ( 0 ) ) ; } ) ; }
66
+ var format_date = function ( date /*:Date*/ , osx /*:?any*/ ) /*:string*/ {
67
+ var datefmt = osx ? "%02u-%02u-%04u %02u:%02u" : "%02u-%02u-%02u %02u:%02u" ;
68
+ var MM = program . local ? date . getMonth ( ) + 1 : date . getUTCMonth ( ) + 1 ;
69
+ var DD = program . local ? date . getDate ( ) : date . getUTCDate ( ) ;
70
+ var YY = ( program . local ? date . getFullYear ( ) : date . getUTCFullYear ( ) ) % ( osx ? 10000 : 100 ) ;
71
+ var hh = program . local ? date . getHours ( ) : date . getUTCHours ( ) ;
72
+ var mm = program . local ? date . getMinutes ( ) : date . getUTCMinutes ( ) ;
73
+ return sprintf ( datefmt , MM , DD , YY , hh , mm ) ;
74
+ } ;
75
+
76
+ if ( program . listFiles ) {
77
+ var basetime = new Date ( Date . UTC ( 1980 , 0 , 1 ) ) ;
78
+ var cnt = 0 , rootsize = 0 , filesize = 0 ;
79
+ var fmtstr = "%9lu %s %s" ;
80
+ if ( program . osx ) {
81
+ console . log ( "Archive: " + program . args [ 0 ] ) ;
82
+ console . log ( " Length Date Time Name" ) ;
83
+ console . log ( "--------- ---------- ----- ----" ) ;
84
+ fmtstr = "%9lu %s %s" ;
85
+ } else {
86
+ console . log ( " Length Date Time Name" ) ;
87
+ console . log ( " -------- ---- ---- ----" ) ;
88
+ }
89
+ cfb . FileIndex . forEach ( function ( file /*:CFBEntry*/ , i /*:number*/ ) {
90
+ switch ( file . type ) {
91
+ case 5 :
92
+ basetime = file . ct || file . mt || basetime ;
93
+ rootsize = file . size ;
94
+ break ;
95
+ case 2 :
96
+ var fixname = fix_string ( cfb . FullPaths [ i ] ) ;
97
+ if ( program . osx && fixname . match ( / \\ u 0 0 0 1 S h 3 3 t J 5 / ) ) return ;
98
+ if ( program . here ) fixname = fixname . slice ( rlen ) ;
99
+ console . log ( sprintf ( fmtstr , file . size , format_date ( file . mt || basetime , program . osx ) , fixname ) ) ;
100
+ filesize += file . size ;
101
+ ++ cnt ;
102
+ }
103
+ } ) ;
104
+ var outfmt = "%9lu %lu file%s" ;
105
+ if ( program . osx ) {
106
+ console . log ( "--------- -------" ) ;
107
+ outfmt = "%9lu %lu file%s" ;
108
+ } else {
109
+ console . log ( " -------- -------" ) ;
110
+ }
111
+ console . log ( sprintf ( outfmt , rootsize || filesize , cnt , ( cnt !== 1 ? "s" : "" ) ) ) ;
112
+
113
+ exit ( 0 ) ;
114
+ }
115
+
116
+ function mkdirp ( path /*:string*/ ) {
117
+ path . split ( "/" ) . reduce ( function ( acc /*:string*/ , p /*:string*/ ) {
118
+ acc += p + "/" ;
119
+ if ( ! fs . existsSync ( acc ) ) { logit ( "mkdir" , acc ) ; fs . mkdirSync ( acc ) ; }
120
+ return acc ;
121
+ } , "" ) ;
122
+ }
123
+
124
+ function write ( path /*:string*/ , data /*:CFBEntry*/ ) {
125
+ logit ( "write" , fix_string ( path ) ) ;
126
+ fs . writeFileSync ( path , /*::new Buffer((*/ data . content /*:: :any))*/ || new Buffer ( 0 ) ) ;
127
+ }
128
+
129
+ if ( program . create || program . append ) {
130
+ program . args . slice ( 1 ) . forEach ( function ( x /*:string*/ ) {
131
+ logit ( "append" , x ) ;
132
+ X . utils . cfb_add ( cfb , "/" + x , fs . readFileSync ( x ) ) ;
133
+ } ) ;
134
+ X . writeFile ( cfb , program . args [ 0 ] ) ;
135
+ exit ( 0 ) ;
136
+ }
137
+
138
+ if ( program . delete ) {
139
+ program . args . slice ( 1 ) . forEach ( function ( x /*:string*/ ) {
140
+ logit ( "delete" , x ) ;
141
+ X . utils . cfb_del ( cfb , "/" + x ) ;
142
+ } ) ;
143
+ X . writeFile ( cfb , program . args [ 0 ] ) ;
144
+ exit ( 0 ) ;
145
+ }
146
+
147
+ if ( program . args . length > 1 ) {
148
+ program . args . slice ( 1 ) . forEach ( function ( x /*:string*/ ) {
149
+ var data /*:?CFBEntry*/ = X . find ( cfb , x . replace ( / \\ u 0 0 0 \d / g, "!" ) ) ;
150
+ if ( ! data ) { console . error ( x + ": file not found" ) ; return ; }
151
+ if ( data . type !== 2 ) { console . error ( x + ": not a file" ) ; return ; }
152
+ var idx = cfb . FileIndex . indexOf ( data ) , path = cfb . FullPaths [ idx ] ;
153
+ if ( program . toStdout ) return process . stdout . write ( /*::new Buffer((*/ data . content /*:: :any))*/ ) ;
154
+ mkdirp ( path . slice ( 0 , path . lastIndexOf ( "/" ) ) ) ;
155
+ write ( path , data ) ;
156
+ } ) ;
157
+ exit ( 0 ) ;
158
+ }
159
+
160
+ if ( program . toStdout ) exit ( 0 ) ;
161
+ for ( var i = program . here ? 1 : 0 ; i !== cfb . FullPaths . length ; ++ i ) {
162
+ if ( ! cfb . FileIndex [ i ] . name ) continue ;
163
+ var fp = cfb . FullPaths [ i ] ;
164
+ if ( program . here ) fp = fp . slice ( rlen ) ;
165
+ if ( fp . slice ( - 1 ) === "/" ) mkdirp ( fp ) ;
166
+ else {
167
+ if ( fp . indexOf ( "/" ) > - 1 ) mkdirp ( fp . slice ( 0 , fp . lastIndexOf ( "/" ) ) ) ;
168
+ write ( fp , cfb . FileIndex [ i ] ) ;
169
+ }
170
+ }
171
+ }
172
+
173
+ module . exports = run ;
0 commit comments