• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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