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