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 identicals\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 main_pause=0,
318 multiple_inputs=0,
319 all_arguments_are_files=0,
320 operationResult=0;
321 operationMode_e mode = om_auto;
322 const char* input_filename = NULL;
323 const char* output_filename= NULL;
324 const char* dictionary_filename = NULL;
325 char* dynNameSpace = NULL;
326 const char** inFileNames = (const char**)calloc((size_t)argc, sizeof(char*));
327 unsigned ifnIdx=0;
328 LZ4IO_prefs_t* const prefs = LZ4IO_defaultPreferences();
329 const char nullOutput[] = NULL_OUTPUT;
330 const char extension[] = LZ4_EXTENSION;
331 size_t blockSize = LZ4IO_setBlockSizeID(prefs, LZ4_BLOCKSIZEID_DEFAULT);
332 const char* const exeName = lastNameFromPath(argv[0]);
333 #ifdef UTIL_HAS_CREATEFILELIST
334 const char** extendedFileList = NULL;
335 char* fileNamesBuf = NULL;
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"))) { mode = om_decompress; continue; }
382 if (!strcmp(argument, "--multiple")) { multiple_inputs = 1; continue; }
383 if (!strcmp(argument, "--test")) { mode = om_test; continue; }
384 if (!strcmp(argument, "--force")) { LZ4IO_setOverwrite(prefs, 1); continue; }
385 if (!strcmp(argument, "--no-force")) { LZ4IO_setOverwrite(prefs, 0); continue; }
386 if ((!strcmp(argument, "--stdout"))
387 || (!strcmp(argument, "--to-stdout"))) { forceStdout=1; output_filename=stdoutmark; continue; }
388 if (!strcmp(argument, "--frame-crc")) { LZ4IO_setStreamChecksumMode(prefs, 1); continue; }
389 if (!strcmp(argument, "--no-frame-crc")) { LZ4IO_setStreamChecksumMode(prefs, 0); continue; }
390 if (!strcmp(argument, "--content-size")) { LZ4IO_setContentSize(prefs, 1); continue; }
391 if (!strcmp(argument, "--no-content-size")) { LZ4IO_setContentSize(prefs, 0); continue; }
392 if (!strcmp(argument, "--list")) { mode = om_list; continue; }
393 if (!strcmp(argument, "--sparse")) { LZ4IO_setSparseFile(prefs, 2); continue; }
394 if (!strcmp(argument, "--no-sparse")) { LZ4IO_setSparseFile(prefs, 0); continue; }
395 if (!strcmp(argument, "--favor-decSpeed")) { LZ4IO_favorDecSpeed(prefs, 1); continue; }
396 if (!strcmp(argument, "--verbose")) { displayLevel++; continue; }
397 if (!strcmp(argument, "--quiet")) { if (displayLevel) displayLevel--; continue; }
398 if (!strcmp(argument, "--version")) { DISPLAYOUT(WELCOME_MESSAGE); goto _cleanup; }
399 if (!strcmp(argument, "--help")) { usage_advanced(exeName); goto _cleanup; }
400 if (!strcmp(argument, "--keep")) { LZ4IO_setRemoveSrcFile(prefs, 0); continue; } /* keep source file (default) */
401 if (!strcmp(argument, "--rm")) { LZ4IO_setRemoveSrcFile(prefs, 1); continue; }
402 if (longCommandWArg(&argument, "--fast")) {
403 /* Parse optional acceleration factor */
404 if (*argument == '=') {
405 U32 fastLevel;
406 ++argument;
407 fastLevel = readU32FromChar(&argument);
408 if (fastLevel) {
409 cLevel = -(int)fastLevel;
410 } else {
411 badusage(exeName);
412 }
413 } else if (*argument != 0) {
414 /* Invalid character following --fast */
415 badusage(exeName);
416 } else {
417 cLevel = -1; /* default for --fast */
418 }
419 continue;
420 }
421
422 /* For gzip(1) compatibility */
423 if (!strcmp(argument, "--best")) { cLevel=LZ4HC_CLEVEL_MAX; continue; }
424 }
425
426 while (argument[1]!=0) {
427 argument ++;
428
429 if (g_lz4c_legacy_commands) {
430 /* Legacy commands (-c0, -c1, -hc, -y) */
431 if (!strcmp(argument, "c0")) { cLevel=0; argument++; continue; } /* -c0 (fast compression) */
432 if (!strcmp(argument, "c1")) { cLevel=9; argument++; continue; } /* -c1 (high compression) */
433 if (!strcmp(argument, "c2")) { cLevel=12; argument++; continue; } /* -c2 (very high compression) */
434 if (!strcmp(argument, "hc")) { cLevel=12; argument++; continue; } /* -hc (very high compression) */
435 if (!strcmp(argument, "y")) { LZ4IO_setOverwrite(prefs, 1); continue; } /* -y (answer 'yes' to overwrite permission) */
436 }
437
438 if ((*argument>='0') && (*argument<='9')) {
439 cLevel = (int)readU32FromChar(&argument);
440 argument--;
441 continue;
442 }
443
444
445 switch(argument[0])
446 {
447 /* Display help */
448 case 'V': DISPLAYOUT(WELCOME_MESSAGE); goto _cleanup; /* Version */
449 case 'h': usage_advanced(exeName); goto _cleanup;
450 case 'H': usage_longhelp(exeName); goto _cleanup;
451
452 case 'e':
453 argument++;
454 cLevelLast = (int)readU32FromChar(&argument);
455 argument--;
456 break;
457
458 /* Compression (default) */
459 case 'z': mode = om_compress; break;
460
461 case 'D':
462 if (argument[1] == '\0') {
463 /* path is next arg */
464 if (i + 1 == argc) {
465 /* there is no next arg */
466 badusage(exeName);
467 }
468 dictionary_filename = argv[++i];
469 } else {
470 /* path follows immediately */
471 dictionary_filename = argument + 1;
472 }
473 /* skip to end of argument so that we jump to parsing next argument */
474 argument += strlen(argument) - 1;
475 break;
476
477 /* Use Legacy format (ex : Linux kernel compression) */
478 case 'l': legacy_format = 1; blockSize = 8 MB; break;
479
480 /* Decoding */
481 case 'd': mode = om_decompress; break;
482
483 /* Force stdout, even if stdout==console */
484 case 'c':
485 forceStdout=1;
486 output_filename=stdoutmark;
487 LZ4IO_setPassThrough(prefs, 1);
488 break;
489
490 /* Test integrity */
491 case 't': mode = om_test; break;
492
493 /* Overwrite */
494 case 'f': LZ4IO_setOverwrite(prefs, 1); break;
495
496 /* Verbose mode */
497 case 'v': displayLevel++; break;
498
499 /* Quiet mode */
500 case 'q': if (displayLevel) displayLevel--; break;
501
502 /* keep source file (default anyway, so useless) (for xz/lzma compatibility) */
503 case 'k': LZ4IO_setRemoveSrcFile(prefs, 0); break;
504
505 /* Modify Block Properties */
506 case 'B':
507 while (argument[1]!=0) {
508 int exitBlockProperties=0;
509 switch(argument[1])
510 {
511 case 'D': LZ4IO_setBlockMode(prefs, LZ4IO_blockLinked); argument++; break;
512 case 'I': LZ4IO_setBlockMode(prefs, LZ4IO_blockIndependent); argument++; break;
513 case 'X': LZ4IO_setBlockChecksumMode(prefs, 1); argument ++; break; /* disabled by default */
514 default :
515 if (argument[1] < '0' || argument[1] > '9') {
516 exitBlockProperties=1;
517 break;
518 } else {
519 unsigned B;
520 argument++;
521 B = readU32FromChar(&argument);
522 argument--;
523 if (B < 4) badusage(exeName);
524 if (B <= 7) {
525 blockSize = LZ4IO_setBlockSizeID(prefs, B);
526 BMK_setBlockSize(blockSize);
527 DISPLAYLEVEL(2, "using blocks of size %u KB \n", (U32)(blockSize>>10));
528 } else {
529 if (B < 32) badusage(exeName);
530 blockSize = LZ4IO_setBlockSize(prefs, B);
531 BMK_setBlockSize(blockSize);
532 if (blockSize >= 1024) {
533 DISPLAYLEVEL(2, "using blocks of size %u KB \n", (U32)(blockSize>>10));
534 } else {
535 DISPLAYLEVEL(2, "using blocks of size %u bytes \n", (U32)(blockSize));
536 }
537 }
538 break;
539 }
540 }
541 if (exitBlockProperties) break;
542 }
543 break;
544
545 /* Benchmark */
546 case 'b': mode = om_bench; multiple_inputs=1;
547 break;
548
549 /* hidden command : benchmark files, but do not fuse result */
550 case 'S': BMK_setBenchSeparately(1);
551 break;
552
553 #ifdef UTIL_HAS_CREATEFILELIST
554 /* recursive */
555 case 'r': recursive=1;
556 #endif
557 /* fall-through */
558 /* Treat non-option args as input files. See https://code.google.com/p/lz4/issues/detail?id=151 */
559 case 'm': multiple_inputs=1;
560 break;
561
562 /* Modify Nb Seconds (benchmark only) */
563 case 'i':
564 { unsigned iters;
565 argument++;
566 iters = readU32FromChar(&argument);
567 argument--;
568 BMK_setNotificationLevel(displayLevel);
569 BMK_setNbSeconds(iters); /* notification if displayLevel >= 3 */
570 }
571 break;
572
573 /* Pause at the end (hidden option) */
574 case 'p': main_pause=1; break;
575
576 /* Unrecognised command */
577 default : badusage(exeName);
578 }
579 }
580 continue;
581 }
582
583 /* Store in *inFileNames[] if -m is used. */
584 if (multiple_inputs) { inFileNames[ifnIdx++]=argument; continue; }
585
586 /* Store first non-option arg in input_filename to preserve original cli logic. */
587 if (!input_filename) { input_filename=argument; continue; }
588
589 /* Second non-option arg in output_filename to preserve original cli logic. */
590 if (!output_filename) {
591 output_filename=argument;
592 if (!strcmp (output_filename, nullOutput)) output_filename = nulmark;
593 continue;
594 }
595
596 /* 3rd non-option arg should not exist */
597 DISPLAYLEVEL(1, "Warning : %s won't be used ! Do you want multiple input files (-m) ? \n", argument);
598 }
599
600 DISPLAYLEVEL(3, WELCOME_MESSAGE);
601 #ifdef _POSIX_C_SOURCE
602 DISPLAYLEVEL(4, "_POSIX_C_SOURCE defined: %ldL\n", (long) _POSIX_C_SOURCE);
603 #endif
604 #ifdef _POSIX_VERSION
605 DISPLAYLEVEL(4, "_POSIX_VERSION defined: %ldL\n", (long) _POSIX_VERSION);
606 #endif
607 #ifdef PLATFORM_POSIX_VERSION
608 DISPLAYLEVEL(4, "PLATFORM_POSIX_VERSION defined: %ldL\n", (long) PLATFORM_POSIX_VERSION);
609 #endif
610 #ifdef _FILE_OFFSET_BITS
611 DISPLAYLEVEL(4, "_FILE_OFFSET_BITS defined: %ldL\n", (long) _FILE_OFFSET_BITS);
612 #endif
613 if ((mode == om_compress) || (mode == om_bench))
614 DISPLAYLEVEL(4, "Blocks size : %u KB\n", (U32)(blockSize>>10));
615
616 if (multiple_inputs) {
617 input_filename = inFileNames[0];
618 #ifdef UTIL_HAS_CREATEFILELIST
619 if (recursive) { /* at this stage, filenameTable is a list of paths, which can contain both files and directories */
620 extendedFileList = UTIL_createFileList(inFileNames, ifnIdx, &fileNamesBuf, &fileNamesNb);
621 if (extendedFileList) {
622 unsigned u;
623 for (u=0; u<fileNamesNb; u++) DISPLAYLEVEL(4, "%u %s\n", u, extendedFileList[u]);
624 free((void*)inFileNames);
625 inFileNames = extendedFileList;
626 ifnIdx = fileNamesNb;
627 } }
628 #endif
629 }
630
631 if (dictionary_filename) {
632 if (!strcmp(dictionary_filename, stdinmark) && IS_CONSOLE(stdin)) {
633 DISPLAYLEVEL(1, "refusing to read from a console\n");
634 exit(1);
635 }
636 LZ4IO_setDictionaryFilename(prefs, dictionary_filename);
637 }
638
639 /* benchmark and test modes */
640 if (mode == om_bench) {
641 BMK_setNotificationLevel(displayLevel);
642 operationResult = BMK_benchFiles(inFileNames, ifnIdx, cLevel, cLevelLast, dictionary_filename);
643 goto _cleanup;
644 }
645
646 if (mode == om_test) {
647 LZ4IO_setTestMode(prefs, 1);
648 output_filename = nulmark;
649 mode = om_decompress; /* defer to decompress */
650 }
651
652 /* compress or decompress */
653 if (!input_filename) input_filename = stdinmark;
654 /* Check if input is defined as console; trigger an error in this case */
655 if (!strcmp(input_filename, stdinmark) && IS_CONSOLE(stdin) ) {
656 DISPLAYLEVEL(1, "refusing to read from a console\n");
657 exit(1);
658 }
659 if (!strcmp(input_filename, stdinmark)) {
660 /* if input==stdin and no output defined, stdout becomes default output */
661 if (!output_filename) output_filename = stdoutmark;
662 }
663 else{
664 #ifdef UTIL_HAS_CREATEFILELIST
665 if (!recursive && !UTIL_isRegFile(input_filename)) {
666 #else
667 if (!UTIL_isRegFile(input_filename)) {
668 #endif
669 DISPLAYLEVEL(1, "%s: is not a regular file \n", input_filename);
670 exit(1);
671 }
672 }
673
674 /* No output filename ==> try to select one automatically (when possible) */
675 while ((!output_filename) && (multiple_inputs==0)) {
676 if (!IS_CONSOLE(stdout) && mode != om_list) {
677 /* Default to stdout whenever stdout is not the console.
678 * Note : this policy may change in the future, therefore don't rely on it !
679 * To ensure `stdout` is explicitly selected, use `-c` command flag.
680 * Conversely, to ensure output will not become `stdout`, use `-m` command flag */
681 DISPLAYLEVEL(1, "Warning : using stdout as default output. Do not rely on this behavior: use explicit `-c` instead ! \n");
682 output_filename=stdoutmark;
683 break;
684 }
685 if (mode == om_auto) { /* auto-determine compression or decompression, based on file extension */
686 mode = determineOpMode(input_filename);
687 }
688 if (mode == om_compress) { /* compression to file */
689 size_t const l = strlen(input_filename);
690 dynNameSpace = (char*)calloc(1,l+5);
691 if (dynNameSpace==NULL) { perror(exeName); exit(1); }
692 strcpy(dynNameSpace, input_filename);
693 strcat(dynNameSpace, LZ4_EXTENSION);
694 output_filename = dynNameSpace;
695 DISPLAYLEVEL(2, "Compressed filename will be : %s \n", output_filename);
696 break;
697 }
698 if (mode == om_decompress) {/* decompression to file (automatic name will work only if input filename has correct format extension) */
699 size_t outl;
700 size_t const inl = strlen(input_filename);
701 dynNameSpace = (char*)calloc(1,inl+1);
702 if (dynNameSpace==NULL) { perror(exeName); exit(1); }
703 strcpy(dynNameSpace, input_filename);
704 outl = inl;
705 if (inl>4)
706 while ((outl >= inl-4) && (input_filename[outl] == extension[outl-inl+4])) dynNameSpace[outl--]=0;
707 if (outl != inl-5) { DISPLAYLEVEL(1, "Cannot determine an output filename\n"); badusage(exeName); }
708 output_filename = dynNameSpace;
709 DISPLAYLEVEL(2, "Decoding file %s \n", output_filename);
710 }
711 break;
712 }
713
714 if (mode == om_list){
715 /* Exit if trying to read from stdin as this isn't supported in this mode */
716 if(!strcmp(input_filename, stdinmark)){
717 DISPLAYLEVEL(1, "refusing to read from standard input in --list mode\n");
718 exit(1);
719 }
720 if(!multiple_inputs){
721 inFileNames[ifnIdx++] = input_filename;
722 }
723 }
724 else{
725 if (multiple_inputs==0) assert(output_filename);
726 }
727 /* when multiple_inputs==1, output_filename may simply be useless,
728 * however, output_filename must be !NULL for next strcmp() tests */
729 if (!output_filename) output_filename = "*\\dummy^!//";
730
731 /* Check if output is defined as console; trigger an error in this case */
732 if (!strcmp(output_filename,stdoutmark) && IS_CONSOLE(stdout) && !forceStdout) {
733 DISPLAYLEVEL(1, "refusing to write to console without -c \n");
734 exit(1);
735 }
736 /* Downgrade notification level in stdout and multiple file mode */
737 if (!strcmp(output_filename,stdoutmark) && (displayLevel==2)) displayLevel=1;
738 if ((multiple_inputs) && (displayLevel==2)) displayLevel=1;
739
740 /* Auto-determine compression or decompression, based on file extension */
741 if (mode == om_auto) {
742 mode = determineOpMode(input_filename);
743 }
744
745 /* IO Stream/File */
746 LZ4IO_setNotificationLevel((int)displayLevel);
747 if (ifnIdx == 0) multiple_inputs = 0;
748 if (mode == om_decompress) {
749 if (multiple_inputs) {
750 const char* const dec_extension = !strcmp(output_filename,stdoutmark) ? stdoutmark : LZ4_EXTENSION;
751 assert(ifnIdx <= INT_MAX);
752 operationResult = LZ4IO_decompressMultipleFilenames(inFileNames, (int)ifnIdx, dec_extension, prefs);
753 } else {
754 operationResult = DEFAULT_DECOMPRESSOR(input_filename, output_filename, prefs);
755 }
756 } else if (mode == om_list){
757 operationResult = LZ4IO_displayCompressedFilesInfo(inFileNames, ifnIdx);
758 } else { /* compression is default action */
759 if (legacy_format) {
760 DISPLAYLEVEL(3, "! Generating LZ4 Legacy format (deprecated) ! \n");
761 if(multiple_inputs){
762 const char* const leg_extension = !strcmp(output_filename,stdoutmark) ? stdoutmark : LZ4_EXTENSION;
763 LZ4IO_compressMultipleFilenames_Legacy(inFileNames, (int)ifnIdx, leg_extension, cLevel, prefs);
764 } else {
765 LZ4IO_compressFilename_Legacy(input_filename, output_filename, cLevel, prefs);
766 }
767 } else {
768 if (multiple_inputs) {
769 const char* const comp_extension = !strcmp(output_filename,stdoutmark) ? stdoutmark : LZ4_EXTENSION;
770 assert(ifnIdx <= INT_MAX);
771 operationResult = LZ4IO_compressMultipleFilenames(inFileNames, (int)ifnIdx, comp_extension, cLevel, prefs);
772 } else {
773 operationResult = DEFAULT_COMPRESSOR(input_filename, output_filename, cLevel, prefs);
774 } } }
775
776 _cleanup:
777 if (main_pause) waitEnter();
778 free(dynNameSpace);
779 #ifdef UTIL_HAS_CREATEFILELIST
780 if (extendedFileList) {
781 UTIL_freeFileList(extendedFileList, fileNamesBuf);
782 inFileNames = NULL;
783 }
784 #endif
785 LZ4IO_freePreferences(prefs);
786 free((void*)inFileNames);
787 return operationResult;
788 }
789