1 /*
2 LZ4cli - LZ4 Command Line Interface
3 Copyright (C) Yann Collet 2011-2020
4
5 GPL v2 License
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21 You can contact the author at :
22 - LZ4 source repository : https://github.com/lz4/lz4
23 - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
24 */
25 /*
26 Note : this is stand-alone program.
27 It is not part of LZ4 compression library, it is a user program of the LZ4 library.
28 The license of LZ4 library is BSD.
29 The license of xxHash library is BSD.
30 The license of this compression CLI program is GPLv2.
31 */
32
33
34 /****************************
35 * Includes
36 *****************************/
37 #include "platform.h" /* Compiler options, IS_CONSOLE */
38 #include "util.h" /* UTIL_HAS_CREATEFILELIST, UTIL_createFileList */
39 #include <stdio.h> /* fprintf, getchar */
40 #include <stdlib.h> /* exit, calloc, free */
41 #include <string.h> /* strcmp, strlen */
42 #include "bench.h" /* BMK_benchFile, BMK_SetNbIterations, BMK_SetBlocksize, BMK_SetPause */
43 #include "lz4io.h" /* LZ4IO_compressFilename, LZ4IO_decompressFilename, LZ4IO_compressMultipleFilenames */
44 #include "lz4hc.h" /* LZ4HC_CLEVEL_MAX */
45 #include "lz4.h" /* LZ4_VERSION_STRING */
46
47
48 /*****************************
49 * Constants
50 ******************************/
51 #define COMPRESSOR_NAME "LZ4 command line interface"
52 #define AUTHOR "Yann Collet"
53 #define WELCOME_MESSAGE "*** %s %i-bits v%s, by %s ***\n", COMPRESSOR_NAME, (int)(sizeof(void*)*8), LZ4_versionString(), AUTHOR
54 #define LZ4_EXTENSION ".lz4"
55 #define LZ4CAT "lz4cat"
56 #define UNLZ4 "unlz4"
57 #define LZ4_LEGACY "lz4c"
58 static int g_lz4c_legacy_commands = 0;
59
60 #define KB *(1U<<10)
61 #define MB *(1U<<20)
62 #define GB *(1U<<30)
63
64 #define LZ4_BLOCKSIZEID_DEFAULT 7
65
66
67 /*-************************************
68 * Macros
69 ***************************************/
70 #define DISPLAYOUT(...) fprintf(stdout, __VA_ARGS__)
71 #define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
72 #define DISPLAYLEVEL(l, ...) if (displayLevel>=l) { DISPLAY(__VA_ARGS__); }
73 static unsigned displayLevel = 2; /* 0 : no display ; 1: errors only ; 2 : downgradable normal ; 3 : non-downgradable normal; 4 : + information */
74
75
76 /*-************************************
77 * Exceptions
78 ***************************************/
79 #define DEBUG 0
80 #define DEBUGOUTPUT(...) if (DEBUG) DISPLAY(__VA_ARGS__);
81 #define EXM_THROW(error, ...) \
82 { \
83 DEBUGOUTPUT("Error defined at %s, line %i : \n", __FILE__, __LINE__); \
84 DISPLAYLEVEL(1, "Error %i : ", error); \
85 DISPLAYLEVEL(1, __VA_ARGS__); \
86 DISPLAYLEVEL(1, "\n"); \
87 exit(error); \
88 }
89
90
91 /*-************************************
92 * Version modifiers
93 ***************************************/
94 #define DEFAULT_COMPRESSOR LZ4IO_compressFilename
95 #define DEFAULT_DECOMPRESSOR LZ4IO_decompressFilename
96 int LZ4IO_compressFilename_Legacy(const char* input_filename, const char* output_filename, int compressionlevel, const LZ4IO_prefs_t* prefs); /* hidden function */
97 int LZ4IO_compressMultipleFilenames_Legacy(
98 const char** inFileNamesTable, int ifntSize,
99 const char* suffix,
100 int compressionLevel, const LZ4IO_prefs_t* prefs);
101
102 /*-***************************
103 * Functions
104 *****************************/
usage(const char * exeName)105 static int usage(const char* exeName)
106 {
107 DISPLAY( "Usage : \n");
108 DISPLAY( " %s [arg] [input] [output] \n", exeName);
109 DISPLAY( "\n");
110 DISPLAY( "input : a filename \n");
111 DISPLAY( " with no FILE, or when FILE is - or %s, read standard input\n", stdinmark);
112 DISPLAY( "Arguments : \n");
113 DISPLAY( " -1 : Fast compression (default) \n");
114 DISPLAY( " -9 : High compression \n");
115 DISPLAY( " -d : decompression (default for %s extension)\n", LZ4_EXTENSION);
116 DISPLAY( " -z : force compression \n");
117 DISPLAY( " -D FILE: use FILE as dictionary \n");
118 DISPLAY( " -f : overwrite output without prompting \n");
119 DISPLAY( " -k : preserve source files(s) (default) \n");
120 DISPLAY( "--rm : remove source file(s) after successful de/compression \n");
121 DISPLAY( " -h/-H : display help/long help and exit \n");
122 return 0;
123 }
124
usage_advanced(const char * exeName)125 static int usage_advanced(const char* exeName)
126 {
127 DISPLAY(WELCOME_MESSAGE);
128 usage(exeName);
129 DISPLAY( "\n");
130 DISPLAY( "Advanced arguments :\n");
131 DISPLAY( " -V : display Version number and exit \n");
132 DISPLAY( " -v : verbose mode \n");
133 DISPLAY( " -q : suppress warnings; specify twice to suppress errors too\n");
134 DISPLAY( " -c : force write to standard output, even if it is the console\n");
135 DISPLAY( " -t : test compressed file integrity\n");
136 DISPLAY( " -m : multiple input files (implies automatic output filenames)\n");
137 #ifdef UTIL_HAS_CREATEFILELIST
138 DISPLAY( " -r : operate recursively on directories (sets also -m) \n");
139 #endif
140 DISPLAY( " -l : compress using Legacy format (Linux kernel compression)\n");
141 DISPLAY( " -B# : cut file into blocks of size # bytes [32+] \n");
142 DISPLAY( " or predefined block size [4-7] (default: 7) \n");
143 DISPLAY( " -BI : Block Independence (default) \n");
144 DISPLAY( " -BD : Block dependency (improves compression ratio) \n");
145 DISPLAY( " -BX : enable block checksum (default:disabled) \n");
146 DISPLAY( "--no-frame-crc : disable stream checksum (default:enabled) \n");
147 DISPLAY( "--content-size : compressed frame includes original size (default:not present)\n");
148 DISPLAY( "--list FILE : lists information about .lz4 files (useful for files compressed with --content-size flag)\n");
149 DISPLAY( "--[no-]sparse : sparse mode (default:enabled on file, disabled on stdout)\n");
150 DISPLAY( "--favor-decSpeed: compressed files decompress faster, but are less compressed \n");
151 DISPLAY( "--fast[=#]: switch to ultra fast compression level (default: %i)\n", 1);
152 DISPLAY( "--best : same as -%d\n", LZ4HC_CLEVEL_MAX);
153 DISPLAY( "Benchmark arguments : \n");
154 DISPLAY( " -b# : benchmark file(s), using # compression level (default : 1) \n");
155 DISPLAY( " -e# : test all compression levels from -bX to # (default : 1)\n");
156 DISPLAY( " -i# : minimum evaluation time in seconds (default : 3s) \n");
157 if (g_lz4c_legacy_commands) {
158 DISPLAY( "Legacy arguments : \n");
159 DISPLAY( " -c0 : fast compression \n");
160 DISPLAY( " -c1 : high compression \n");
161 DISPLAY( " -c2,-hc: very high compression \n");
162 DISPLAY( " -y : overwrite output without prompting \n");
163 }
164 return 0;
165 }
166
usage_longhelp(const char * exeName)167 static int usage_longhelp(const char* exeName)
168 {
169 usage_advanced(exeName);
170 DISPLAY( "\n");
171 DISPLAY( "****************************\n");
172 DISPLAY( "***** Advanced comment *****\n");
173 DISPLAY( "****************************\n");
174 DISPLAY( "\n");
175 DISPLAY( "Which values can [output] have ? \n");
176 DISPLAY( "---------------------------------\n");
177 DISPLAY( "[output] : a filename \n");
178 DISPLAY( " '%s', or '-' for standard output (pipe mode)\n", stdoutmark);
179 DISPLAY( " '%s' to discard output (test mode) \n", NULL_OUTPUT);
180 DISPLAY( "[output] can be left empty. In this case, it receives the following value :\n");
181 DISPLAY( " - if stdout is not the console, then [output] = stdout \n");
182 DISPLAY( " - if stdout is console : \n");
183 DISPLAY( " + for compression, output to filename%s \n", LZ4_EXTENSION);
184 DISPLAY( " + for decompression, output to filename without '%s'\n", LZ4_EXTENSION);
185 DISPLAY( " > if input filename has no '%s' extension : error \n", LZ4_EXTENSION);
186 DISPLAY( "\n");
187 DISPLAY( "Compression levels : \n");
188 DISPLAY( "---------------------\n");
189 DISPLAY( "-0 ... -2 => Fast compression, all identical\n");
190 DISPLAY( "-3 ... -%d => High compression; higher number == more compression but slower\n", LZ4HC_CLEVEL_MAX);
191 DISPLAY( "\n");
192 DISPLAY( "stdin, stdout and the console : \n");
193 DISPLAY( "--------------------------------\n");
194 DISPLAY( "To protect the console from binary flooding (bad argument mistake)\n");
195 DISPLAY( "%s will refuse to read from console, or write to console \n", exeName);
196 DISPLAY( "except if '-c' command is specified, to force output to console \n");
197 DISPLAY( "\n");
198 DISPLAY( "Simple example :\n");
199 DISPLAY( "----------------\n");
200 DISPLAY( "1 : compress 'filename' fast, using default output name 'filename.lz4'\n");
201 DISPLAY( " %s filename\n", exeName);
202 DISPLAY( "\n");
203 DISPLAY( "Short arguments can be aggregated. For example :\n");
204 DISPLAY( "----------------------------------\n");
205 DISPLAY( "2 : compress 'filename' in high compression mode, overwrite output if exists\n");
206 DISPLAY( " %s -9 -f filename \n", exeName);
207 DISPLAY( " is equivalent to :\n");
208 DISPLAY( " %s -9f filename \n", exeName);
209 DISPLAY( "\n");
210 DISPLAY( "%s can be used in 'pure pipe mode'. For example :\n", exeName);
211 DISPLAY( "-------------------------------------\n");
212 DISPLAY( "3 : compress data stream from 'generator', send result to 'consumer'\n");
213 DISPLAY( " generator | %s | consumer \n", exeName);
214 if (g_lz4c_legacy_commands) {
215 DISPLAY( "\n");
216 DISPLAY( "***** Warning ***** \n");
217 DISPLAY( "Legacy arguments take precedence. Therefore : \n");
218 DISPLAY( "--------------------------------- \n");
219 DISPLAY( " %s -hc filename \n", exeName);
220 DISPLAY( "means 'compress filename in high compression mode' \n");
221 DISPLAY( "It is not equivalent to : \n");
222 DISPLAY( " %s -h -c filename \n", exeName);
223 DISPLAY( "which displays help text and exits \n");
224 }
225 return 0;
226 }
227
badusage(const char * exeName)228 static int badusage(const char* exeName)
229 {
230 DISPLAYLEVEL(1, "Incorrect parameters\n");
231 if (displayLevel >= 1) usage(exeName);
232 exit(1);
233 }
234
235
waitEnter(void)236 static void waitEnter(void)
237 {
238 DISPLAY("Press enter to continue...\n");
239 (void)getchar();
240 }
241
lastNameFromPath(const char * path)242 static const char* lastNameFromPath(const char* path)
243 {
244 const char* name = path;
245 if (strrchr(name, '/')) name = strrchr(name, '/') + 1;
246 if (strrchr(name, '\\')) name = strrchr(name, '\\') + 1; /* windows */
247 return name;
248 }
249
250 /*! exeNameMatch() :
251 @return : a non-zero value if exeName matches test, excluding the extension
252 */
exeNameMatch(const char * exeName,const char * test)253 static int exeNameMatch(const char* exeName, const char* test)
254 {
255 return !strncmp(exeName, test, strlen(test)) &&
256 (exeName[strlen(test)] == '\0' || exeName[strlen(test)] == '.');
257 }
258
259 /*! readU32FromChar() :
260 * @return : unsigned integer value read from input in `char` format
261 * allows and interprets K, KB, KiB, M, MB and MiB suffix.
262 * Will also modify `*stringPtr`, advancing it to position where it stopped reading.
263 * Note : function result can overflow if digit string > MAX_UINT */
readU32FromChar(const char ** stringPtr)264 static unsigned readU32FromChar(const char** stringPtr)
265 {
266 unsigned result = 0;
267 while ((**stringPtr >='0') && (**stringPtr <='9')) {
268 result *= 10;
269 result += (unsigned)(**stringPtr - '0');
270 (*stringPtr)++ ;
271 }
272 if ((**stringPtr=='K') || (**stringPtr=='M')) {
273 result <<= 10;
274 if (**stringPtr=='M') result <<= 10;
275 (*stringPtr)++ ;
276 if (**stringPtr=='i') (*stringPtr)++;
277 if (**stringPtr=='B') (*stringPtr)++;
278 }
279 return result;
280 }
281
282 /** longCommandWArg() :
283 * check if *stringPtr is the same as longCommand.
284 * If yes, @return 1 and advances *stringPtr to the position which immediately follows longCommand.
285 * @return 0 and doesn't modify *stringPtr otherwise.
286 */
longCommandWArg(const char ** stringPtr,const char * longCommand)287 static int longCommandWArg(const char** stringPtr, const char* longCommand)
288 {
289 size_t const comSize = strlen(longCommand);
290 int const result = !strncmp(*stringPtr, longCommand, comSize);
291 if (result) *stringPtr += comSize;
292 return result;
293 }
294
295 typedef enum { om_auto, om_compress, om_decompress, om_test, om_bench, om_list } operationMode_e;
296
297 /** determineOpMode() :
298 * auto-determine operation mode, based on input filename extension
299 * @return `om_decompress` if input filename has .lz4 extension and `om_compress` otherwise.
300 */
determineOpMode(const char * inputFilename)301 static operationMode_e determineOpMode(const char* inputFilename)
302 {
303 size_t const inSize = strlen(inputFilename);
304 size_t const extSize = strlen(LZ4_EXTENSION);
305 size_t const extStart= (inSize > extSize) ? inSize-extSize : 0;
306 if (!strcmp(inputFilename+extStart, LZ4_EXTENSION)) return om_decompress;
307 else return om_compress;
308 }
309
main(int argc,const char ** argv)310 int main(int argc, const char** argv)
311 {
312 int i,
313 cLevel=1,
314 cLevelLast=-10000,
315 legacy_format=0,
316 forceStdout=0,
317 forceOverwrite=0,
318 main_pause=0,
319 multiple_inputs=0,
320 all_arguments_are_files=0,
321 operationResult=0;
322 operationMode_e mode = om_auto;
323 const char* input_filename = NULL;
324 const char* output_filename= NULL;
325 const char* dictionary_filename = NULL;
326 char* dynNameSpace = NULL;
327 const char** inFileNames = (const char**)calloc((size_t)argc, sizeof(char*));
328 unsigned ifnIdx=0;
329 LZ4IO_prefs_t* const prefs = LZ4IO_defaultPreferences();
330 const char nullOutput[] = NULL_OUTPUT;
331 const char extension[] = LZ4_EXTENSION;
332 size_t blockSize = LZ4IO_setBlockSizeID(prefs, LZ4_BLOCKSIZEID_DEFAULT);
333 const char* const exeName = lastNameFromPath(argv[0]);
334 char* fileNamesBuf = NULL;
335 #ifdef UTIL_HAS_CREATEFILELIST
336 unsigned fileNamesNb, recursive=0;
337 #endif
338
339 /* Init */
340 if (inFileNames==NULL) {
341 DISPLAY("Allocation error : not enough memory \n");
342 return 1;
343 }
344 inFileNames[0] = stdinmark;
345 LZ4IO_setOverwrite(prefs, 0);
346
347 /* predefined behaviors, based on binary/link name */
348 if (exeNameMatch(exeName, LZ4CAT)) {
349 mode = om_decompress;
350 LZ4IO_setOverwrite(prefs, 1);
351 LZ4IO_setPassThrough(prefs, 1);
352 LZ4IO_setRemoveSrcFile(prefs, 0);
353 forceStdout=1;
354 output_filename=stdoutmark;
355 displayLevel=1;
356 multiple_inputs=1;
357 }
358 if (exeNameMatch(exeName, UNLZ4)) { mode = om_decompress; }
359 if (exeNameMatch(exeName, LZ4_LEGACY)) { g_lz4c_legacy_commands=1; }
360
361 /* command switches */
362 for(i=1; i<argc; i++) {
363 const char* argument = argv[i];
364
365 if(!argument) continue; /* Protection if argument empty */
366
367 /* Short commands (note : aggregated short commands are allowed) */
368 if (!all_arguments_are_files && argument[0]=='-') {
369 /* '-' means stdin/stdout */
370 if (argument[1]==0) {
371 if (!input_filename) input_filename=stdinmark;
372 else output_filename=stdoutmark;
373 continue;
374 }
375
376 /* long commands (--long-word) */
377 if (argument[1]=='-') {
378 if (!strcmp(argument, "--")) { all_arguments_are_files = 1; continue; }
379 if (!strcmp(argument, "--compress")) { mode = om_compress; continue; }
380 if ( (!strcmp(argument, "--decompress"))
381 || (!strcmp(argument, "--uncompress"))) {
382 if (mode != om_bench) mode = om_decompress;
383 BMK_setDecodeOnlyMode(1);
384 continue;
385 }
386 if (!strcmp(argument, "--multiple")) { multiple_inputs = 1; continue; }
387 if (!strcmp(argument, "--test")) { mode = om_test; continue; }
388 if (!strcmp(argument, "--force")) { LZ4IO_setOverwrite(prefs, 1); continue; }
389 if (!strcmp(argument, "--no-force")) { LZ4IO_setOverwrite(prefs, 0); continue; }
390 if ((!strcmp(argument, "--stdout"))
391 || (!strcmp(argument, "--to-stdout"))) { forceStdout=1; output_filename=stdoutmark; continue; }
392 if (!strcmp(argument, "--frame-crc")) { LZ4IO_setStreamChecksumMode(prefs, 1); BMK_skipChecksums(0); continue; }
393 if (!strcmp(argument, "--no-frame-crc")) { LZ4IO_setStreamChecksumMode(prefs, 0); BMK_skipChecksums(1); continue; }
394 if (!strcmp(argument, "--no-crc")) { LZ4IO_setStreamChecksumMode(prefs, 0); LZ4IO_setBlockChecksumMode(prefs, 0); BMK_skipChecksums(1); continue; }
395 if (!strcmp(argument, "--content-size")) { LZ4IO_setContentSize(prefs, 1); continue; }
396 if (!strcmp(argument, "--no-content-size")) { LZ4IO_setContentSize(prefs, 0); continue; }
397 if (!strcmp(argument, "--list")) { mode = om_list; continue; }
398 if (!strcmp(argument, "--sparse")) { LZ4IO_setSparseFile(prefs, 2); continue; }
399 if (!strcmp(argument, "--no-sparse")) { LZ4IO_setSparseFile(prefs, 0); continue; }
400 if (!strcmp(argument, "--favor-decSpeed")) { LZ4IO_favorDecSpeed(prefs, 1); continue; }
401 if (!strcmp(argument, "--verbose")) { displayLevel++; continue; }
402 if (!strcmp(argument, "--quiet")) { if (displayLevel) displayLevel--; continue; }
403 if (!strcmp(argument, "--version")) { DISPLAYOUT(WELCOME_MESSAGE); goto _cleanup; }
404 if (!strcmp(argument, "--help")) { usage_advanced(exeName); goto _cleanup; }
405 if (!strcmp(argument, "--keep")) { LZ4IO_setRemoveSrcFile(prefs, 0); continue; } /* keep source file (default) */
406 if (!strcmp(argument, "--rm")) { LZ4IO_setRemoveSrcFile(prefs, 1); continue; }
407 if (longCommandWArg(&argument, "--fast")) {
408 /* Parse optional acceleration factor */
409 if (*argument == '=') {
410 U32 fastLevel;
411 ++argument;
412 fastLevel = readU32FromChar(&argument);
413 if (fastLevel) {
414 cLevel = -(int)fastLevel;
415 } else {
416 badusage(exeName);
417 }
418 } else if (*argument != 0) {
419 /* Invalid character following --fast */
420 badusage(exeName);
421 } else {
422 cLevel = -1; /* default for --fast */
423 }
424 continue;
425 }
426
427 /* For gzip(1) compatibility */
428 if (!strcmp(argument, "--best")) { cLevel=LZ4HC_CLEVEL_MAX; continue; }
429 }
430
431 while (argument[1]!=0) {
432 argument ++;
433
434 if (g_lz4c_legacy_commands) {
435 /* Legacy commands (-c0, -c1, -hc, -y) */
436 if (!strcmp(argument, "c0")) { cLevel=0; argument++; continue; } /* -c0 (fast compression) */
437 if (!strcmp(argument, "c1")) { cLevel=9; argument++; continue; } /* -c1 (high compression) */
438 if (!strcmp(argument, "c2")) { cLevel=12; argument++; continue; } /* -c2 (very high compression) */
439 if (!strcmp(argument, "hc")) { cLevel=12; argument++; continue; } /* -hc (very high compression) */
440 if (!strcmp(argument, "y")) { LZ4IO_setOverwrite(prefs, 1); continue; } /* -y (answer 'yes' to overwrite permission) */
441 }
442
443 if ((*argument>='0') && (*argument<='9')) {
444 cLevel = (int)readU32FromChar(&argument);
445 argument--;
446 continue;
447 }
448
449
450 switch(argument[0])
451 {
452 /* Display help */
453 case 'V': DISPLAYOUT(WELCOME_MESSAGE); goto _cleanup; /* Version */
454 case 'h': usage_advanced(exeName); goto _cleanup;
455 case 'H': usage_longhelp(exeName); goto _cleanup;
456
457 case 'e':
458 argument++;
459 cLevelLast = (int)readU32FromChar(&argument);
460 argument--;
461 break;
462
463 /* Compression (default) */
464 case 'z': mode = om_compress; break;
465
466 case 'D':
467 if (argument[1] == '\0') {
468 /* path is next arg */
469 if (i + 1 == argc) {
470 /* there is no next arg */
471 badusage(exeName);
472 }
473 dictionary_filename = argv[++i];
474 } else {
475 /* path follows immediately */
476 dictionary_filename = argument + 1;
477 }
478 /* skip to end of argument so that we jump to parsing next argument */
479 argument += strlen(argument) - 1;
480 break;
481
482 /* Use Legacy format (ex : Linux kernel compression) */
483 case 'l': legacy_format = 1; blockSize = 8 MB; break;
484
485 /* Decoding */
486 case 'd':
487 if (mode != om_bench) mode = om_decompress;
488 BMK_setDecodeOnlyMode(1);
489 break;
490
491 /* Force stdout, even if stdout==console */
492 case 'c':
493 forceStdout=1;
494 output_filename=stdoutmark;
495 LZ4IO_setPassThrough(prefs, 1);
496 break;
497
498 /* Test integrity */
499 case 't': mode = om_test; break;
500
501 /* Overwrite */
502 case 'f': forceOverwrite=1; LZ4IO_setOverwrite(prefs, 1); break;
503
504 /* Verbose mode */
505 case 'v': displayLevel++; break;
506
507 /* Quiet mode */
508 case 'q': if (displayLevel) displayLevel--; break;
509
510 /* keep source file (default anyway, so useless) (for xz/lzma compatibility) */
511 case 'k': LZ4IO_setRemoveSrcFile(prefs, 0); break;
512
513 /* Modify Block Properties */
514 case 'B':
515 while (argument[1]!=0) {
516 int exitBlockProperties=0;
517 switch(argument[1])
518 {
519 case 'D': LZ4IO_setBlockMode(prefs, LZ4IO_blockLinked); argument++; break;
520 case 'I': LZ4IO_setBlockMode(prefs, LZ4IO_blockIndependent); argument++; break;
521 case 'X': LZ4IO_setBlockChecksumMode(prefs, 1); argument ++; break; /* disabled by default */
522 default :
523 if (argument[1] < '0' || argument[1] > '9') {
524 exitBlockProperties=1;
525 break;
526 } else {
527 unsigned B;
528 argument++;
529 B = readU32FromChar(&argument);
530 argument--;
531 if (B < 4) badusage(exeName);
532 if (B <= 7) {
533 blockSize = LZ4IO_setBlockSizeID(prefs, B);
534 BMK_setBlockSize(blockSize);
535 DISPLAYLEVEL(2, "using blocks of size %u KB \n", (U32)(blockSize>>10));
536 } else {
537 if (B < 32) badusage(exeName);
538 blockSize = LZ4IO_setBlockSize(prefs, B);
539 BMK_setBlockSize(blockSize);
540 if (blockSize >= 1024) {
541 DISPLAYLEVEL(2, "using blocks of size %u KB \n", (U32)(blockSize>>10));
542 } else {
543 DISPLAYLEVEL(2, "using blocks of size %u bytes \n", (U32)(blockSize));
544 }
545 }
546 break;
547 }
548 }
549 if (exitBlockProperties) break;
550 }
551 break;
552
553 /* Benchmark */
554 case 'b': mode = om_bench; multiple_inputs=1;
555 break;
556
557 /* hidden command : benchmark files, but do not fuse result */
558 case 'S': BMK_setBenchSeparately(1);
559 break;
560
561 #ifdef UTIL_HAS_CREATEFILELIST
562 /* recursive */
563 case 'r': recursive=1;
564 #endif
565 /* fall-through */
566 /* Treat non-option args as input files. See https://code.google.com/p/lz4/issues/detail?id=151 */
567 case 'm': multiple_inputs=1;
568 break;
569
570 /* Modify Nb Seconds (benchmark only) */
571 case 'i':
572 { unsigned iters;
573 argument++;
574 iters = readU32FromChar(&argument);
575 argument--;
576 BMK_setNotificationLevel(displayLevel);
577 BMK_setNbSeconds(iters); /* notification if displayLevel >= 3 */
578 }
579 break;
580
581 /* Pause at the end (hidden option) */
582 case 'p': main_pause=1; break;
583
584 /* Unrecognised command */
585 default : badusage(exeName);
586 }
587 }
588 continue;
589 }
590
591 /* Store in *inFileNames[] if -m is used. */
592 if (multiple_inputs) { inFileNames[ifnIdx++] = argument; continue; }
593
594 /* original cli logic : lz4 input output */
595 /* First non-option arg is input_filename. */
596 if (!input_filename) { input_filename = argument; continue; }
597
598 /* Second non-option arg is output_filename */
599 if (!output_filename) {
600 output_filename = argument;
601 if (!strcmp (output_filename, nullOutput)) output_filename = nulmark;
602 continue;
603 }
604
605 /* 3rd+ non-option arg should not exist */
606 DISPLAYLEVEL(1, "%s : %s won't be used ! Do you want multiple input files (-m) ? \n",
607 forceOverwrite ? "Warning" : "Error",
608 argument);
609 if (!forceOverwrite) exit(1);
610 }
611
612 DISPLAYLEVEL(3, WELCOME_MESSAGE);
613 #ifdef _POSIX_C_SOURCE
614 DISPLAYLEVEL(4, "_POSIX_C_SOURCE defined: %ldL\n", (long) _POSIX_C_SOURCE);
615 #endif
616 #ifdef _POSIX_VERSION
617 DISPLAYLEVEL(4, "_POSIX_VERSION defined: %ldL\n", (long) _POSIX_VERSION);
618 #endif
619 #ifdef PLATFORM_POSIX_VERSION
620 DISPLAYLEVEL(4, "PLATFORM_POSIX_VERSION defined: %ldL\n", (long) PLATFORM_POSIX_VERSION);
621 #endif
622 #ifdef _FILE_OFFSET_BITS
623 DISPLAYLEVEL(4, "_FILE_OFFSET_BITS defined: %ldL\n", (long) _FILE_OFFSET_BITS);
624 #endif
625 if ((mode == om_compress) || (mode == om_bench))
626 DISPLAYLEVEL(4, "Blocks size : %u KB\n", (U32)(blockSize>>10));
627
628 if (multiple_inputs) {
629 input_filename = inFileNames[0];
630 #ifdef UTIL_HAS_CREATEFILELIST
631 if (recursive) { /* at this stage, filenameTable is a list of paths, which can contain both files and directories */
632 const char** extendedFileList = UTIL_createFileList(inFileNames, ifnIdx, &fileNamesBuf, &fileNamesNb);
633 if (extendedFileList) {
634 unsigned u;
635 for (u=0; u<fileNamesNb; u++) DISPLAYLEVEL(4, "%u %s\n", u, extendedFileList[u]);
636 free((void*)inFileNames);
637 inFileNames = extendedFileList;
638 ifnIdx = fileNamesNb;
639 } }
640 #endif
641 }
642
643 if (dictionary_filename) {
644 if (!strcmp(dictionary_filename, stdinmark) && IS_CONSOLE(stdin)) {
645 DISPLAYLEVEL(1, "refusing to read from a console\n");
646 exit(1);
647 }
648 LZ4IO_setDictionaryFilename(prefs, dictionary_filename);
649 }
650
651 /* benchmark and test modes */
652 if (mode == om_bench) {
653 BMK_setNotificationLevel(displayLevel);
654 operationResult = BMK_benchFiles(inFileNames, ifnIdx, cLevel, cLevelLast, dictionary_filename);
655 goto _cleanup;
656 }
657
658 if (mode == om_test) {
659 LZ4IO_setTestMode(prefs, 1);
660 output_filename = nulmark;
661 mode = om_decompress; /* defer to decompress */
662 }
663
664 /* No input provided => use stdin */
665 if (!input_filename) input_filename = stdinmark;
666
667 /* Refuse to use the console as input */
668 if (!strcmp(input_filename, stdinmark) && IS_CONSOLE(stdin) ) {
669 DISPLAYLEVEL(1, "refusing to read from a console\n");
670 exit(1);
671 }
672
673 if (!strcmp(input_filename, stdinmark)) {
674 /* if input==stdin and no output defined, stdout becomes default output */
675 if (!output_filename) output_filename = stdoutmark;
676 }
677
678 /* No output filename ==> try to select one automatically (when possible) */
679 while ((!output_filename) && (multiple_inputs==0)) {
680 if (!IS_CONSOLE(stdout) && mode != om_list) {
681 /* Default to stdout whenever stdout is not the console.
682 * Note : this policy may change in the future, therefore don't rely on it !
683 * To ensure `stdout` is explicitly selected, use `-c` command flag.
684 * Conversely, to ensure output will not become `stdout`, use `-m` command flag */
685 DISPLAYLEVEL(1, "Warning : using stdout as default output. Do not rely on this behavior: use explicit `-c` instead ! \n");
686 output_filename = stdoutmark;
687 break;
688 }
689 if (mode == om_auto) { /* auto-determine compression or decompression, based on file extension */
690 mode = determineOpMode(input_filename);
691 }
692 if (mode == om_compress) { /* compression to file */
693 size_t const l = strlen(input_filename);
694 dynNameSpace = (char*)calloc(1,l+5);
695 if (dynNameSpace==NULL) { perror(exeName); exit(1); }
696 strcpy(dynNameSpace, input_filename);
697 strcat(dynNameSpace, LZ4_EXTENSION);
698 output_filename = dynNameSpace;
699 DISPLAYLEVEL(2, "Compressed filename will be : %s \n", output_filename);
700 break;
701 }
702 if (mode == om_decompress) {/* decompress to file (automatic output name only works if input filename has correct format extension) */
703 size_t outl;
704 size_t const inl = strlen(input_filename);
705 dynNameSpace = (char*)calloc(1,inl+1);
706 if (dynNameSpace==NULL) { perror(exeName); exit(1); }
707 strcpy(dynNameSpace, input_filename);
708 outl = inl;
709 if (inl>4)
710 while ((outl >= inl-4) && (input_filename[outl] == extension[outl-inl+4])) dynNameSpace[outl--]=0;
711 if (outl != inl-5) { DISPLAYLEVEL(1, "Cannot determine an output filename \n"); badusage(exeName); }
712 output_filename = dynNameSpace;
713 DISPLAYLEVEL(2, "Decoding file %s \n", output_filename);
714 }
715 break;
716 }
717
718 if (mode == om_list) {
719 if (!multiple_inputs) inFileNames[ifnIdx++] = input_filename;
720 } else {
721 if (!multiple_inputs) assert(output_filename != NULL);
722 }
723 /* when multiple_inputs==1, output_filename may simply be useless,
724 * however, output_filename must be !NULL for next strcmp() tests */
725 if (!output_filename) output_filename = "*\\dummy^!//";
726
727 /* Check if output is defined as console; trigger an error in this case */
728 if ( !strcmp(output_filename,stdoutmark)
729 && mode != om_list
730 && IS_CONSOLE(stdout)
731 && !forceStdout) {
732 DISPLAYLEVEL(1, "refusing to write to console without -c \n");
733 exit(1);
734 }
735 /* Downgrade notification level in stdout and multiple file mode */
736 if (!strcmp(output_filename,stdoutmark) && (displayLevel==2)) displayLevel=1;
737 if ((multiple_inputs) && (displayLevel==2)) displayLevel=1;
738
739 /* Auto-determine compression or decompression, based on file extension */
740 if (mode == om_auto) {
741 mode = determineOpMode(input_filename);
742 }
743
744 /* IO Stream/File */
745 LZ4IO_setNotificationLevel((int)displayLevel);
746 if (ifnIdx == 0) multiple_inputs = 0;
747 if (mode == om_decompress) {
748 if (multiple_inputs) {
749 const char* dec_extension = LZ4_EXTENSION;
750 if (!strcmp(output_filename, stdoutmark)) dec_extension = stdoutmark;
751 if (!strcmp(output_filename, nulmark)) dec_extension = nulmark;
752 assert(ifnIdx < INT_MAX);
753 operationResult = LZ4IO_decompressMultipleFilenames(inFileNames, (int)ifnIdx, dec_extension, prefs);
754 } else {
755 operationResult = DEFAULT_DECOMPRESSOR(input_filename, output_filename, prefs);
756 }
757 } else if (mode == om_list){
758 operationResult = LZ4IO_displayCompressedFilesInfo(inFileNames, ifnIdx);
759 } else { /* compression is default action */
760 if (legacy_format) {
761 DISPLAYLEVEL(3, "! Generating LZ4 Legacy format (deprecated) ! \n");
762 if(multiple_inputs){
763 const char* const leg_extension = !strcmp(output_filename,stdoutmark) ? stdoutmark : LZ4_EXTENSION;
764 LZ4IO_compressMultipleFilenames_Legacy(inFileNames, (int)ifnIdx, leg_extension, cLevel, prefs);
765 } else {
766 LZ4IO_compressFilename_Legacy(input_filename, output_filename, cLevel, prefs);
767 }
768 } else {
769 if (multiple_inputs) {
770 const char* const comp_extension = !strcmp(output_filename,stdoutmark) ? stdoutmark : LZ4_EXTENSION;
771 assert(ifnIdx <= INT_MAX);
772 operationResult = LZ4IO_compressMultipleFilenames(inFileNames, (int)ifnIdx, comp_extension, cLevel, prefs);
773 } else {
774 operationResult = DEFAULT_COMPRESSOR(input_filename, output_filename, cLevel, prefs);
775 } } }
776
777 _cleanup:
778 if (main_pause) waitEnter();
779 free(dynNameSpace);
780 free(fileNamesBuf);
781 LZ4IO_freePreferences(prefs);
782 free((void*)inFileNames);
783 return operationResult;
784 }
785