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__); } 107static unsigned displayLevel = 2; /* 0 : no display ; 1: errors ; 2 : + result + interaction + warnings ; 3 : + progression; 4 : + information */ 108 109 110/************************************** 111* Local Variables 112***************************************/ 113static 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 139int LZ4IO_compressFilename_Legacy(char* input_filename, char* output_filename, int compressionlevel); /* hidden function */ 140 141 142/**************************** 143* Functions 144*****************************/ 145static 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 162static 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 193static 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 243static int badusage(void) 244{ 245 DISPLAYLEVEL(1, "Incorrect parameters\n"); 246 if (displayLevel >= 1) usage(); 247 exit(1); 248} 249 250 251static void waitEnter(void) 252{ 253 DISPLAY("Press enter to continue...\n"); 254 getchar(); 255} 256 257 258int 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