1 /*
2 LZ4cli - LZ4 Command Line Interface
3 Copyright (C) Yann Collet 2011-2014
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 : http://code.google.com/p/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 * Compiler Options
44 ***************************************/
45 /* Disable some Visual warning messages */
46 #ifdef _MSC_VER
47 # define _CRT_SECURE_NO_WARNINGS
48 # define _CRT_SECURE_NO_DEPRECATE /* VS2005 */
49 # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
50 #endif
51
52 #define _POSIX_SOURCE 1 /* for fileno() within <stdio.h> on unix */
53
54
55 /****************************
56 * Includes
57 *****************************/
58 #include <stdio.h> /* fprintf, getchar */
59 #include <stdlib.h> /* exit, calloc, free */
60 #include <string.h> /* strcmp, strlen */
61 #include "bench.h" /* BMK_benchFile, BMK_SetNbIterations, BMK_SetBlocksize, BMK_SetPause */
62 #include "lz4io.h"
63
64
65 /****************************
66 * OS-specific Includes
67 *****************************/
68 #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__)
69 # include <fcntl.h> /* _O_BINARY */
70 # include <io.h> /* _setmode, _isatty */
71 # ifdef __MINGW32__
72 int _fileno(FILE *stream); /* MINGW somehow forgets to include this prototype into <stdio.h> */
73 # endif
74 # define SET_BINARY_MODE(file) _setmode(_fileno(file), _O_BINARY)
75 # define IS_CONSOLE(stdStream) _isatty(_fileno(stdStream))
76 #else
77 # include <unistd.h> /* isatty */
78 # define SET_BINARY_MODE(file)
79 # define IS_CONSOLE(stdStream) isatty(fileno(stdStream))
80 #endif
81
82
83 /*****************************
84 * Constants
85 ******************************/
86 #define COMPRESSOR_NAME "LZ4 command line interface"
87 #ifndef LZ4_VERSION
88 # define LZ4_VERSION "r126"
89 #endif
90 #define AUTHOR "Yann Collet"
91 #define WELCOME_MESSAGE "*** %s %i-bits %s, by %s (%s) ***\n", COMPRESSOR_NAME, (int)(sizeof(void*)*8), LZ4_VERSION, AUTHOR, __DATE__
92 #define LZ4_EXTENSION ".lz4"
93 #define LZ4_CAT "lz4cat"
94
95 #define KB *(1U<<10)
96 #define MB *(1U<<20)
97 #define GB *(1U<<30)
98
99 #define LZ4_BLOCKSIZEID_DEFAULT 7
100
101
102 /**************************************
103 * Macros
104 ***************************************/
105 #define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
106 #define DISPLAYLEVEL(l, ...) if (displayLevel>=l) { DISPLAY(__VA_ARGS__); }
107 static unsigned displayLevel = 2; /* 0 : no display ; 1: errors ; 2 : + result + interaction + warnings ; 3 : + progression; 4 : + information */
108
109
110 /**************************************
111 * Local Variables
112 ***************************************/
113 static char* programName;
114
115
116 /**************************************
117 * Exceptions
118 ***************************************/
119 #define DEBUG 0
120 #define DEBUGOUTPUT(...) if (DEBUG) DISPLAY(__VA_ARGS__);
121 #define EXM_THROW(error, ...) \
122 { \
123 DEBUGOUTPUT("Error defined at %s, line %i : \n", __FILE__, __LINE__); \
124 DISPLAYLEVEL(1, "Error %i : ", error); \
125 DISPLAYLEVEL(1, __VA_ARGS__); \
126 DISPLAYLEVEL(1, "\n"); \
127 exit(error); \
128 }
129
130
131 /**************************************
132 * Version modifiers
133 ***************************************/
134 #define EXTENDED_ARGUMENTS
135 #define EXTENDED_HELP
136 #define EXTENDED_FORMAT
137 #define DEFAULT_COMPRESSOR LZ4IO_compressFilename
138 #define DEFAULT_DECOMPRESSOR LZ4IO_decompressFilename
139 int LZ4IO_compressFilename_Legacy(char* input_filename, char* output_filename, int compressionlevel); /* hidden function */
140
141
142 /****************************
143 * Functions
144 *****************************/
usage(void)145 static int usage(void)
146 {
147 DISPLAY( "Usage :\n");
148 DISPLAY( " %s [arg] [input] [output]\n", programName);
149 DISPLAY( "\n");
150 DISPLAY( "input : a filename\n");
151 DISPLAY( " with no FILE, or when FILE is - or %s, read standard input\n", stdinmark);
152 DISPLAY( "Arguments :\n");
153 DISPLAY( " -1 : Fast compression (default) \n");
154 DISPLAY( " -9 : High compression \n");
155 DISPLAY( " -d : decompression (default for %s extension)\n", LZ4_EXTENSION);
156 DISPLAY( " -z : force compression\n");
157 DISPLAY( " -f : overwrite output without prompting \n");
158 DISPLAY( " -h/-H : display help/long help and exit\n");
159 return 0;
160 }
161
usage_advanced(void)162 static int usage_advanced(void)
163 {
164 DISPLAY(WELCOME_MESSAGE);
165 usage();
166 DISPLAY( "\n");
167 DISPLAY( "Advanced arguments :\n");
168 DISPLAY( " -V : display Version number and exit\n");
169 DISPLAY( " -v : verbose mode\n");
170 DISPLAY( " -q : suppress warnings; specify twice to suppress errors too\n");
171 DISPLAY( " -c : force write to standard output, even if it is the console\n");
172 DISPLAY( " -t : test compressed file integrity\n");
173 DISPLAY( " -l : compress using Legacy format (Linux kernel compression)\n");
174 DISPLAY( " -B# : Block size [4-7](default : 7)\n");
175 DISPLAY( " -BD : Block dependency (improve compression ratio)\n");
176 /* DISPLAY( " -BX : enable block checksum (default:disabled)\n"); *//* Option currently inactive */
177 DISPLAY( " -Sx : disable stream checksum (default:enabled)\n");
178 DISPLAY( "Benchmark arguments :\n");
179 DISPLAY( " -b : benchmark file(s)\n");
180 DISPLAY( " -i# : iteration loops [1-9](default : 3), benchmark mode only\n");
181 #if defined(ENABLE_LZ4C_LEGACY_OPTIONS)
182 DISPLAY( "Legacy arguments :\n");
183 DISPLAY( " -c0 : fast compression\n");
184 DISPLAY( " -c1 : high compression\n");
185 DISPLAY( " -hc : high compression\n");
186 DISPLAY( " -y : overwrite output without prompting \n");
187 DISPLAY( " -s : suppress warnings \n");
188 #endif /* ENABLE_LZ4C_LEGACY_OPTIONS */
189 EXTENDED_HELP;
190 return 0;
191 }
192
usage_longhelp(void)193 static int usage_longhelp(void)
194 {
195 DISPLAY( "\n");
196 DISPLAY( "Which values can get [output] ? \n");
197 DISPLAY( "[output] : a filename\n");
198 DISPLAY( " '%s', or '-' for standard output (pipe mode)\n", stdoutmark);
199 DISPLAY( " '%s' to discard output (test mode)\n", NULL_OUTPUT);
200 DISPLAY( "[output] can be left empty. In this case, it receives the following value : \n");
201 DISPLAY( " - if stdout is not the console, then [output] = stdout \n");
202 DISPLAY( " - if stdout is console : \n");
203 DISPLAY( " + if compression selected, output to filename%s \n", LZ4_EXTENSION);
204 DISPLAY( " + if decompression selected, output to filename without '%s'\n", LZ4_EXTENSION);
205 DISPLAY( " > if input filename has no '%s' extension : error\n", LZ4_EXTENSION);
206 DISPLAY( "\n");
207 DISPLAY( "Compression levels : \n");
208 DISPLAY( "There are technically 2 accessible compression levels.\n");
209 DISPLAY( "-0 ... -2 => Fast compression\n");
210 DISPLAY( "-3 ... -9 => High compression\n");
211 DISPLAY( "\n");
212 DISPLAY( "stdin, stdout and the console : \n");
213 DISPLAY( "To protect the console from binary flooding (bad argument mistake)\n");
214 DISPLAY( "%s will refuse to read from console, or write to console \n", programName);
215 DISPLAY( "except if '-c' command is specified, to force output to console \n");
216 DISPLAY( "\n");
217 DISPLAY( "Simple example :\n");
218 DISPLAY( "1 : compress 'filename' fast, using default output name 'filename.lz4'\n");
219 DISPLAY( " %s filename\n", programName);
220 DISPLAY( "\n");
221 DISPLAY( "Arguments can be appended together, or provided independently. For example :\n");
222 DISPLAY( "2 : compress 'filename' in high compression mode, overwrite output if exists\n");
223 DISPLAY( " %s -f9 filename \n", programName);
224 DISPLAY( " is equivalent to :\n");
225 DISPLAY( " %s -f -9 filename \n", programName);
226 DISPLAY( "\n");
227 DISPLAY( "%s can be used in 'pure pipe mode', for example :\n", programName);
228 DISPLAY( "3 : compress data stream from 'generator', send result to 'consumer'\n");
229 DISPLAY( " generator | %s | consumer \n", programName);
230 #if defined(ENABLE_LZ4C_LEGACY_OPTIONS)
231 DISPLAY( "\n");
232 DISPLAY( "Warning :\n");
233 DISPLAY( "Legacy arguments take precedence. Therefore : \n");
234 DISPLAY( " %s -hc filename\n", programName);
235 DISPLAY( "means 'compress filename in high compression mode'\n");
236 DISPLAY( "It is not equivalent to :\n");
237 DISPLAY( " %s -h -c filename\n", programName);
238 DISPLAY( "which would display help text and exit\n");
239 #endif /* ENABLE_LZ4C_LEGACY_OPTIONS */
240 return 0;
241 }
242
badusage(void)243 static int badusage(void)
244 {
245 DISPLAYLEVEL(1, "Incorrect parameters\n");
246 if (displayLevel >= 1) usage();
247 exit(1);
248 }
249
250
waitEnter(void)251 static void waitEnter(void)
252 {
253 DISPLAY("Press enter to continue...\n");
254 getchar();
255 }
256
257
main(int argc,char ** argv)258 int main(int argc, char** argv)
259 {
260 int i,
261 cLevel=0,
262 decode=0,
263 bench=0,
264 filenamesStart=2,
265 legacy_format=0,
266 forceStdout=0,
267 forceCompress=0,
268 main_pause=0;
269 char* input_filename=0;
270 char* output_filename=0;
271 char* dynNameSpace=0;
272 char nullOutput[] = NULL_OUTPUT;
273 char extension[] = LZ4_EXTENSION;
274 int blockSize;
275
276 /* Init */
277 programName = argv[0];
278 LZ4IO_setOverwrite(0);
279 blockSize = LZ4IO_setBlockSizeID(LZ4_BLOCKSIZEID_DEFAULT);
280
281 /* lz4cat behavior */
282 if (!strcmp(programName, LZ4_CAT)) { decode=1; forceStdout=1; output_filename=stdoutmark; displayLevel=1; }
283
284 /* command switches */
285 for(i=1; i<argc; i++)
286 {
287 char* argument = argv[i];
288
289 if(!argument) continue; /* Protection if argument empty */
290
291 /* Decode command (note : aggregated commands are allowed) */
292 if (argument[0]=='-')
293 {
294 /* '-' means stdin/stdout */
295 if (argument[1]==0)
296 {
297 if (!input_filename) input_filename=stdinmark;
298 else output_filename=stdoutmark;
299 }
300
301 while (argument[1]!=0)
302 {
303 argument ++;
304
305 #if defined(ENABLE_LZ4C_LEGACY_OPTIONS)
306 /* Legacy arguments (-c0, -c1, -hc, -y, -s) */
307 if ((argument[0]=='c') && (argument[1]=='0')) { cLevel=0; argument++; continue; } /* -c0 (fast compression) */
308 if ((argument[0]=='c') && (argument[1]=='1')) { cLevel=9; argument++; continue; } /* -c1 (high compression) */
309 if ((argument[0]=='h') && (argument[1]=='c')) { cLevel=9; argument++; continue; } /* -hc (high compression) */
310 if (*argument=='y') { LZ4IO_setOverwrite(1); continue; } /* -y (answer 'yes' to overwrite permission) */
311 if (*argument=='s') { displayLevel=1; continue; } /* -s (silent mode) */
312 #endif /* ENABLE_LZ4C_LEGACY_OPTIONS */
313
314 if ((*argument>='0') && (*argument<='9'))
315 {
316 cLevel = 0;
317 while ((*argument >= '0') && (*argument <= '9'))
318 {
319 cLevel *= 10;
320 cLevel += *argument - '0';
321 argument++;
322 }
323 argument--;
324 continue;
325 }
326
327 switch(argument[0])
328 {
329 /* Display help */
330 case 'V': DISPLAY(WELCOME_MESSAGE); return 0; /* Version */
331 case 'h': usage_advanced(); return 0;
332 case 'H': usage_advanced(); usage_longhelp(); return 0;
333
334 /* Compression (default) */
335 case 'z': forceCompress = 1; break;
336
337 /* Use Legacy format (ex : Linux kernel compression) */
338 case 'l': legacy_format = 1; blockSize = 8 MB; break;
339
340 /* Decoding */
341 case 'd': decode=1; break;
342
343 /* Force stdout, even if stdout==console */
344 case 'c': forceStdout=1; output_filename=stdoutmark; displayLevel=1; break;
345
346 /* Test integrity */
347 case 't': decode=1; LZ4IO_setOverwrite(1); output_filename=nulmark; break;
348
349 /* Overwrite */
350 case 'f': LZ4IO_setOverwrite(1); break;
351
352 /* Verbose mode */
353 case 'v': displayLevel=4; break;
354
355 /* Quiet mode */
356 case 'q': displayLevel--; break;
357
358 /* keep source file (default anyway, so useless) (for xz/lzma compatibility) */
359 case 'k': break;
360
361 /* Modify Block Properties */
362 case 'B':
363 while (argument[1]!=0)
364 {
365 int exitBlockProperties=0;
366 switch(argument[1])
367 {
368 case '4':
369 case '5':
370 case '6':
371 case '7':
372 {
373 int B = argument[1] - '0';
374 blockSize = LZ4IO_setBlockSizeID(B);
375 BMK_SetBlocksize(blockSize);
376 argument++;
377 break;
378 }
379 case 'D': LZ4IO_setBlockMode(LZ4IO_blockLinked); argument++; break;
380 case 'X': LZ4IO_setBlockChecksumMode(1); argument ++; break; /* currently disables */
381 default : exitBlockProperties=1;
382 }
383 if (exitBlockProperties) break;
384 }
385 break;
386
387 /* Modify Stream properties */
388 case 'S': if (argument[1]=='x') { LZ4IO_setStreamChecksumMode(0); argument++; break; } else { badusage(); }
389
390 /* Benchmark */
391 case 'b': bench=1; break;
392
393 /* Modify Nb Iterations (benchmark only) */
394 case 'i':
395 if ((argument[1] >='1') && (argument[1] <='9'))
396 {
397 int iters = argument[1] - '0';
398 BMK_SetNbIterations(iters);
399 argument++;
400 }
401 break;
402
403 /* Pause at the end (hidden option) */
404 case 'p': main_pause=1; BMK_SetPause(); break;
405
406 /* Specific commands for customized versions */
407 EXTENDED_ARGUMENTS;
408
409 /* Unrecognised command */
410 default : badusage();
411 }
412 }
413 continue;
414 }
415
416 /* first provided filename is input */
417 if (!input_filename) { input_filename=argument; filenamesStart=i; continue; }
418
419 /* second provided filename is output */
420 if (!output_filename)
421 {
422 output_filename=argument;
423 if (!strcmp (output_filename, nullOutput)) output_filename = nulmark;
424 continue;
425 }
426 }
427
428 DISPLAYLEVEL(3, WELCOME_MESSAGE);
429 if (!decode) DISPLAYLEVEL(4, "Blocks size : %i KB\n", blockSize>>10);
430
431 /* No input filename ==> use stdin */
432 if(!input_filename) { input_filename=stdinmark; }
433
434 /* Check if input or output are defined as console; trigger an error in this case */
435 if (!strcmp(input_filename, stdinmark) && IS_CONSOLE(stdin) ) badusage();
436
437 /* Check if benchmark is selected */
438 if (bench) return BMK_benchFile(argv+filenamesStart, argc-filenamesStart, cLevel);
439
440 /* No output filename ==> try to select one automatically (when possible) */
441 while (!output_filename)
442 {
443 if (!IS_CONSOLE(stdout)) { output_filename=stdoutmark; break; } /* Default to stdout whenever possible (i.e. not a console) */
444 if ((!decode) && !(forceCompress)) /* auto-determine compression or decompression, based on file extension */
445 {
446 size_t l = strlen(input_filename);
447 if (!strcmp(input_filename+(l-4), LZ4_EXTENSION)) decode=1;
448 }
449 if (!decode) /* compression to file */
450 {
451 size_t l = strlen(input_filename);
452 dynNameSpace = (char*)calloc(1,l+5);
453 output_filename = dynNameSpace;
454 strcpy(output_filename, input_filename);
455 strcpy(output_filename+l, LZ4_EXTENSION);
456 DISPLAYLEVEL(2, "Compressed filename will be : %s \n", output_filename);
457 break;
458 }
459 /* decompression to file (automatic name will work only if input filename has correct format extension) */
460 {
461 size_t outl;
462 size_t inl = strlen(input_filename);
463 dynNameSpace = (char*)calloc(1,inl+1);
464 output_filename = dynNameSpace;
465 strcpy(output_filename, input_filename);
466 outl = inl;
467 if (inl>4)
468 while ((outl >= inl-4) && (input_filename[outl] == extension[outl-inl+4])) output_filename[outl--]=0;
469 if (outl != inl-5) { DISPLAYLEVEL(1, "Cannot determine an output filename\n"); badusage(); }
470 DISPLAYLEVEL(2, "Decoding file %s \n", output_filename);
471 }
472 }
473
474 /* Check if output is defined as console; trigger an error in this case */
475 if (!strcmp(output_filename,stdoutmark) && IS_CONSOLE(stdout) && !forceStdout) badusage();
476
477 /* No warning message in pure pipe mode (stdin + stdout) */
478 if (!strcmp(input_filename, stdinmark) && !strcmp(output_filename,stdoutmark) && (displayLevel==2)) displayLevel=1;
479
480
481 /* IO Stream/File */
482 LZ4IO_setNotificationLevel(displayLevel);
483 if (decode) DEFAULT_DECOMPRESSOR(input_filename, output_filename);
484 else
485 {
486 /* compression is default action */
487 if (legacy_format)
488 {
489 DISPLAYLEVEL(3, "! Generating compressed LZ4 using Legacy format (deprecated) ! \n");
490 LZ4IO_compressFilename_Legacy(input_filename, output_filename, cLevel);
491 }
492 else
493 {
494 DEFAULT_COMPRESSOR(input_filename, output_filename, cLevel);
495 }
496 }
497
498 if (main_pause) waitEnter();
499 free(dynNameSpace);
500 return 0;
501 }
502