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