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__); }
78static 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
104int LZ4IO_compressFilename_Legacy(const char* input_filename, const char* output_filename, int compressionlevel);   /* hidden function */
105
106
107/*-***************************
108*  Functions
109*****************************/
110static 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
129static 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
168static 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
229static int badusage(const char* exeName)
230{
231    DISPLAYLEVEL(1, "Incorrect parameters\n");
232    if (displayLevel >= 1) usage(exeName);
233    exit(1);
234}
235
236
237static void waitEnter(void)
238{
239    DISPLAY("Press enter to continue...\n");
240    (void)getchar();
241}
242
243static 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 */
255static 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
263typedef enum { om_auto, om_compress, om_decompress, om_test, om_bench } operationMode_e;
264
265int 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