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