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