@@ -14,9 +14,11 @@ import (
14
14
"path"
15
15
"path/filepath"
16
16
"runtime"
17
+ "strconv"
17
18
"strings"
18
19
"sync"
19
20
"time"
21
+ "unicode"
20
22
21
23
"github.com/klauspost/compress/s2"
22
24
"github.com/klauspost/compress/s2/cmd/internal/readahead"
31
33
goos = flag .String ("os" , runtime .GOOS , "Destination operating system" )
32
34
goarch = flag .String ("arch" , runtime .GOARCH , "Destination architecture" )
33
35
cpu = flag .Int ("cpu" , runtime .GOMAXPROCS (0 ), "Compress using this amount of threads" )
36
+ max = flag .String ("max" , "1G" , "Maximum executable size. Rest will be written to another file." )
34
37
safe = flag .Bool ("safe" , false , "Do not overwrite output files" )
35
38
stdout = flag .Bool ("c" , false , "Write all output to stdout. Multiple input files will be concatenated" )
36
39
remove = flag .Bool ("rm" , false , "Delete source file(s) after successful compression" )
@@ -48,6 +51,8 @@ var embeddedFiles embed.FS
48
51
func main () {
49
52
flag .Parse ()
50
53
args := flag .Args ()
54
+ sz , err := toSize (* max )
55
+ exitErr (err )
51
56
if len (args ) == 0 || * help {
52
57
_ , _ = fmt .Fprintf (os .Stderr , "s2sx v%v, built at %v.\n \n " , version , date )
53
58
_ , _ = fmt .Fprintf (os .Stderr , "Copyright (c) 2011 The Snappy-Go Authors. All rights reserved.\n " +
@@ -58,7 +63,8 @@ Compresses all files supplied as input separately.
58
63
Use file name - to read from stdin and write to stdout.
59
64
If input is already s2 compressed it will just have the executable wrapped.
60
65
Use s2c commandline tool for advanced option, eg 'cat file.txt||s2c -|s2sx - >out.s2sx'
61
- Output files are written as 'filename.s2sfx' and with '.exe' for windows targets.
66
+ Output files are written as 'filename.s2sx' and with '.exe' for windows targets.
67
+ If output is big, an additional file with ".more" is written. This must be included as well.
62
68
By default output files will be overwritten.
63
69
64
70
Wildcards are accepted: testdir/*.txt will compress all files in testdir ending with .txt
@@ -94,6 +100,10 @@ Options:`)
94
100
exec , err = ioutil .ReadAll (s2 .NewReader (bytes .NewBuffer (exec )))
95
101
exitErr (err )
96
102
103
+ written := int64 (0 )
104
+ if int64 (len (exec ))+ 1 >= sz {
105
+ exitErr (fmt .Errorf ("max size less than unpacker. Max size must be at least %d bytes" , len (exec )+ 1 ))
106
+ }
97
107
mode := byte (opUnpack )
98
108
if * untar {
99
109
mode = opUnTar
@@ -107,6 +117,7 @@ Options:`)
107
117
exitErr (err )
108
118
_ , err = os .Stdout .Write ([]byte {mode })
109
119
exitErr (err )
120
+ written += int64 (len (exec ) + 1 )
110
121
}
111
122
112
123
if stdIn {
@@ -139,7 +150,7 @@ Options:`)
139
150
for _ , filename := range files {
140
151
func () {
141
152
var closeOnce sync.Once
142
- dstFilename := fmt .Sprintf ("%s%s" , strings .TrimPrefix (filename , ".s2" ), ".s2sfx " )
153
+ dstFilename := fmt .Sprintf ("%s%s" , strings .TrimPrefix (filename , ".s2" ), ".s2sx " )
143
154
if * goos == "windows" {
144
155
dstFilename += ".exe"
145
156
}
@@ -172,7 +183,23 @@ Options:`)
172
183
dstFile , err := os .OpenFile (dstFilename , os .O_CREATE | os .O_WRONLY | os .O_TRUNC , 0777 )
173
184
exitErr (err )
174
185
defer dstFile .Close ()
175
- bw := bufio .NewWriterSize (dstFile , 4 << 20 * 2 )
186
+ sw := & switchWriter {w : dstFile , left : sz , close : nil }
187
+ sw .fn = func () {
188
+ dstFilename := dstFilename + ".more"
189
+ if * safe {
190
+ _ , err := os .Stat (dstFilename )
191
+ if ! os .IsNotExist (err ) {
192
+ exitErr (fmt .Errorf ("destination '%s' file exists" , dstFilename ))
193
+ }
194
+ }
195
+ dstFile , err := os .OpenFile (dstFilename , os .O_CREATE | os .O_WRONLY | os .O_TRUNC , 0777 )
196
+ exitErr (err )
197
+ sw .close = dstFile .Close
198
+ sw .w = dstFile
199
+ sw .left = (1 << 63 ) - 1
200
+ }
201
+ defer sw .Close ()
202
+ bw := bufio .NewWriterSize (sw , 4 << 20 * 2 )
176
203
defer bw .Flush ()
177
204
out = bw
178
205
_ , err = out .Write (exec )
@@ -252,3 +279,58 @@ func isS2Input(rd io.Reader) (bool, io.Reader) {
252
279
exitErr (err )
253
280
return false , nil
254
281
}
282
+
283
+ // toSize converts a size indication to bytes.
284
+ func toSize (size string ) (int64 , error ) {
285
+ size = strings .ToUpper (strings .TrimSpace (size ))
286
+ firstLetter := strings .IndexFunc (size , unicode .IsLetter )
287
+ if firstLetter == - 1 {
288
+ firstLetter = len (size )
289
+ }
290
+
291
+ bytesString , multiple := size [:firstLetter ], size [firstLetter :]
292
+ bytes , err := strconv .ParseInt (bytesString , 10 , 64 )
293
+ if err != nil {
294
+ return 0 , fmt .Errorf ("unable to parse size: %v" , err )
295
+ }
296
+
297
+ switch multiple {
298
+ case "G" , "GB" , "GIB" :
299
+ return bytes * 1 << 20 , nil
300
+ case "M" , "MB" , "MIB" :
301
+ return bytes * 1 << 20 , nil
302
+ case "K" , "KB" , "KIB" :
303
+ return bytes * 1 << 10 , nil
304
+ case "B" , "" :
305
+ return bytes , nil
306
+ default :
307
+ return 0 , fmt .Errorf ("unknown size suffix: %v" , multiple )
308
+ }
309
+ }
310
+
311
+ type switchWriter struct {
312
+ w io.Writer
313
+ left int64
314
+ fn func ()
315
+ close func () error
316
+ }
317
+
318
+ func (w * switchWriter ) Write (b []byte ) (int , error ) {
319
+ if int64 (len (b )) <= w .left {
320
+ w .left -= int64 (len (b ))
321
+ return w .w .Write (b )
322
+ }
323
+ n , err := w .w .Write (b [:w .left ])
324
+ if err != nil {
325
+ return n , err
326
+ }
327
+ w .fn ()
328
+ n2 , err := w .Write (b [n :])
329
+ return n + n2 , err
330
+ }
331
+ func (w * switchWriter ) Close () error {
332
+ if w .close == nil {
333
+ return nil
334
+ }
335
+ return w .close ()
336
+ }
0 commit comments