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