diff --git a/lib/lz4frame.h b/lib/lz4frame.h index 742c252..8c07e86 100644 --- a/lib/lz4frame.h +++ b/lib/lz4frame.h @@ -193,6 +193,13 @@ typedef struct { #define LZ4F_INIT_PREFERENCES { LZ4F_INIT_FRAMEINFO, 0, 0u, 0u, { 0u, 0u, 0u } } /* v1.8.3+ */ +typedef struct { + LZ4F_frameInfo_t frameInfo; + const char* fileName; + unsigned long long fileSize; +} LZ4F_compFileInfo_t; + +#define LZ4F_INIT_FILEINFO { (LZ4F_frameInfo_t) LZ4F_INIT_FRAMEINFO, NULL, 0ULL } /*-********************************* * Simple compression function diff --git a/programs/lz4.1.md b/programs/lz4.1.md index ffda3ff..62f672e 100644 --- a/programs/lz4.1.md +++ b/programs/lz4.1.md @@ -113,6 +113,11 @@ only the latest one will be applied. * `-b#`: Benchmark mode, using `#` compression level. +* `--list`: + List mode. + Lists information about .lz4 files. + Useful if compressed with --content-size flag. + ### Operation modifiers * `-#`: @@ -160,6 +165,7 @@ only the latest one will be applied. Multiple input files. Compressed file names will be appended a `.lz4` suffix. This mode also reduces notification level. + Can also be used to list multiple files. `lz4 -m` has a behavior equivalent to `gzip -k` (it preserves source files by default). diff --git a/programs/lz4cli.c b/programs/lz4cli.c index 82f2fac..443e4b4 100644 --- a/programs/lz4cli.c +++ b/programs/lz4cli.c @@ -141,6 +141,7 @@ static int usage_advanced(const char* exeName) DISPLAY( " -BX : enable block checksum (default:disabled) \n"); DISPLAY( "--no-frame-crc : disable stream checksum (default:enabled) \n"); DISPLAY( "--content-size : compressed frame includes original size (default:not present)\n"); + DISPLAY( "--list : lists information about .lz4 files. Useful if compressed with --content-size flag.\n"); DISPLAY( "--[no-]sparse : sparse mode (default:enabled on file, disabled on stdout)\n"); DISPLAY( "--favor-decSpeed: compressed files decompress faster, but are less compressed \n"); DISPLAY( "--fast[=#]: switch to ultra fast compression level (default: %i)\n", 1); @@ -286,7 +287,7 @@ static int longCommandWArg(const char** stringPtr, const char* longCommand) return result; } -typedef enum { om_auto, om_compress, om_decompress, om_test, om_bench } operationMode_e; +typedef enum { om_auto, om_compress, om_decompress, om_test, om_bench, om_list } operationMode_e; /** determineOpMode() : * auto-determine operation mode, based on input filename extension @@ -383,6 +384,7 @@ int main(int argc, const char** argv) if (!strcmp(argument, "--no-frame-crc")) { LZ4IO_setStreamChecksumMode(prefs, 0); continue; } if (!strcmp(argument, "--content-size")) { LZ4IO_setContentSize(prefs, 1); continue; } if (!strcmp(argument, "--no-content-size")) { LZ4IO_setContentSize(prefs, 0); continue; } + if (!strcmp(argument, "--list")) { mode = om_list; continue; } if (!strcmp(argument, "--sparse")) { LZ4IO_setSparseFile(prefs, 2); continue; } if (!strcmp(argument, "--no-sparse")) { LZ4IO_setSparseFile(prefs, 0); continue; } if (!strcmp(argument, "--favor-decSpeed")) { LZ4IO_favorDecSpeed(prefs, 1); continue; } @@ -691,7 +693,7 @@ int main(int argc, const char** argv) break; } - if (multiple_inputs==0) assert(output_filename); + if (multiple_inputs==0 && mode != om_list) assert(output_filename); /* when multiple_inputs==1, output_filename may simply be useless, * however, output_filename must be !NULL for next strcmp() tests */ if (!output_filename) output_filename = "*\\dummy^!//"; @@ -720,6 +722,12 @@ int main(int argc, const char** argv) } else { operationResult = DEFAULT_DECOMPRESSOR(prefs, input_filename, output_filename); } + } else if (mode == om_list){ + if(!multiple_inputs){ + inFileNames[ifnIdx++] = input_filename; + } + operationResult = LZ4IO_displayCompressedFilesInfo(inFileNames, ifnIdx); + inFileNames=NULL; } else { /* compression is default action */ if (legacy_format) { DISPLAYLEVEL(3, "! Generating LZ4 Legacy format (deprecated) ! \n"); @@ -744,4 +752,4 @@ _cleanup: LZ4IO_freePreferences(prefs); free((void*)inFileNames); return operationResult; -} +} \ No newline at end of file diff --git a/programs/lz4io.c b/programs/lz4io.c index 121bd44..175157e 100644 --- a/programs/lz4io.c +++ b/programs/lz4io.c @@ -53,11 +53,11 @@ #include /* clock */ #include /* stat64 */ #include /* stat64 */ -#include "lz4io.h" #include "lz4.h" /* still required for legacy format */ #include "lz4hc.h" /* still required for legacy format */ #define LZ4F_STATIC_LINKING_ONLY #include "lz4frame.h" +#include "lz4io.h" /***************************** @@ -1214,6 +1214,62 @@ static int LZ4IO_decompressDstFile(LZ4IO_prefs_t* const prefs, dRess_t ress, con } +static int LZ4IO_getCompressedFileInfo(const char* input_filename, LZ4F_compFileInfo_t* cfinfo){ + const char *b, + *e; + char *t; + stat_t statbuf; + size_t readSize = LZ4F_HEADER_SIZE_MAX; + LZ4F_errorCode_t errorCode; + dRess_t ress; + /* Open file */ + FILE* const finput = LZ4IO_openSrcFile(input_filename); + if (finput==NULL) return 1; + + /* Get file size */ + if (!UTIL_getFileStat(input_filename, &statbuf)){ + EXM_THROW(60, "Can't stat file : %s", input_filename); + } + + cfinfo->fileSize = statbuf.st_size; + + /* Get basename without extension */ + b = strrchr(input_filename, '/'); + if (!b){ + b = strrchr(input_filename, '\\'); + } + if (b && b != input_filename){ + b++; + } else{ + b=input_filename; + } + e = strrchr(b, '.'); + + /* Allocate Memory */ + t = (char*)malloc( (e-b+1) * sizeof(char)); + ress.srcBuffer = malloc(LZ4IO_dBufferSize); + if (!t || !ress.srcBuffer) + EXM_THROW(21, "Allocation error : not enough memory"); + strncpy(t, b, (e-b)); + t[e-b] = '\0'; + cfinfo->fileName = t; + + /* init */ + errorCode = LZ4F_createDecompressionContext(&ress.dCtx, LZ4F_VERSION); + if (LZ4F_isError(errorCode)) EXM_THROW(60, "Can't create LZ4F context : %s", LZ4F_getErrorName(errorCode)); + + if (!fread(ress.srcBuffer, readSize, 1, finput)){ + EXM_THROW(30, "Error reading %s ", input_filename); + } + LZ4F_getFrameInfo(ress.dCtx, &cfinfo->frameInfo, ress.srcBuffer, &readSize); + + /* Close input/free resources */ + fclose(finput); + free(ress.srcBuffer); + return 0; +} + + int LZ4IO_decompressFilename(LZ4IO_prefs_t* const prefs, const char* input_filename, const char* output_filename) { dRess_t const ress = LZ4IO_createDResources(prefs); @@ -1265,3 +1321,30 @@ int LZ4IO_decompressMultipleFilenames(LZ4IO_prefs_t* const prefs, const char** i free(outFileName); return missingFiles + skippedFiles; } + + +int LZ4IO_displayCompressedFilesInfo(const char** inFileNames, const size_t ifnIdx){ + size_t idx; + int op_result=0; + double ratio; + LZ4F_compFileInfo_t cfinfo; + DISPLAY("%16s\t%-20s\t%-20s\t%-10s\t%s\n","BlockChecksumFlag","Compressed", "Uncompressed", "Ratio", "Filename"); + for(idx=0; idx