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