1 /*
2   LZ4io.c - LZ4 File/Stream Interface
3   Copyright (C) Yann Collet 2011-2017
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 code 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 source file is GPLv2.
31 */
32 
33 
34 /*-************************************
35 *  Compiler options
36 **************************************/
37 #ifdef _MSC_VER    /* Visual Studio */
38 #  pragma warning(disable : 4127)    /* disable: C4127: conditional expression is constant */
39 #endif
40 #if defined(__MINGW32__) && !defined(_POSIX_SOURCE)
41 #  define _POSIX_SOURCE 1          /* disable %llu warnings with MinGW on Windows */
42 #endif
43 
44 
45 /*****************************
46 *  Includes
47 *****************************/
48 #include "platform.h"  /* Large File Support, SET_BINARY_MODE, SET_SPARSE_FILE_MODE, PLATFORM_POSIX_VERSION, __64BIT__ */
49 #include "util.h"      /* UTIL_getFileStat, UTIL_setFileStat */
50 #include <stdio.h>     /* fprintf, fopen, fread, stdin, stdout, fflush, getchar */
51 #include <stdlib.h>    /* malloc, free */
52 #include <string.h>    /* strerror, strcmp, strlen */
53 #include <time.h>      /* clock */
54 #include <sys/types.h> /* stat64 */
55 #include <sys/stat.h>  /* stat64 */
56 #include "lz4.h"       /* still required for legacy format */
57 #include "lz4hc.h"     /* still required for legacy format */
58 #define LZ4F_STATIC_LINKING_ONLY
59 #include "lz4frame.h"
60 #include "lz4io.h"
61 
62 
63 /*****************************
64 *  Constants
65 *****************************/
66 #define KB *(1 <<10)
67 #define MB *(1 <<20)
68 #define GB *(1U<<30)
69 
70 #define _1BIT  0x01
71 #define _2BITS 0x03
72 #define _3BITS 0x07
73 #define _4BITS 0x0F
74 #define _8BITS 0xFF
75 
76 #define MAGICNUMBER_SIZE    4
77 #define LZ4IO_MAGICNUMBER   0x184D2204
78 #define LZ4IO_SKIPPABLE0    0x184D2A50
79 #define LZ4IO_SKIPPABLEMASK 0xFFFFFFF0
80 #define LEGACY_MAGICNUMBER  0x184C2102
81 
82 #define CACHELINE 64
83 #define LEGACY_BLOCKSIZE   (8 MB)
84 #define MIN_STREAM_BUFSIZE (192 KB)
85 #define LZ4IO_BLOCKSIZEID_DEFAULT 7
86 #define LZ4_MAX_DICT_SIZE (64 KB)
87 
88 
89 /**************************************
90 *  Macros
91 **************************************/
92 #define DISPLAY(...)         fprintf(stderr, __VA_ARGS__)
93 #define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); }
94 static int g_displayLevel = 0;   /* 0 : no display  ; 1: errors  ; 2 : + result + interaction + warnings ; 3 : + progression; 4 : + information */
95 
96 #define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \
97             if ( ((clock() - g_time) > refreshRate)    \
98               || (g_displayLevel>=4) ) {               \
99                 g_time = clock();                      \
100                 DISPLAY(__VA_ARGS__);                  \
101                 if (g_displayLevel>=4) fflush(stderr); \
102         }   }
103 static const clock_t refreshRate = CLOCKS_PER_SEC / 6;
104 static clock_t g_time = 0;
105 #define LZ4IO_STATIC_ASSERT(c)   { enum { LZ4IO_static_assert = 1/(int)(!!(c)) }; }   /* use after variable declarations */
106 
107 
108 /**************************************
109 *  Local Parameters
110 **************************************/
111 
112 struct LZ4IO_prefs_s {
113   int passThrough;
114   int overwrite;
115   int testMode;
116   int blockSizeId;
117   size_t blockSize;
118   int blockChecksum;
119   int streamChecksum;
120   int blockIndependence;
121   int sparseFileSupport;
122   int contentSizeFlag;
123   int useDictionary;
124   unsigned favorDecSpeed;
125   const char* dictionaryFilename;
126   int removeSrcFile;
127 };
128 
129 /**************************************
130 *  Exceptions
131 ***************************************/
132 #ifndef DEBUG
133 #  define DEBUG 0
134 #endif
135 #define DEBUGOUTPUT(...) if (DEBUG) DISPLAY(__VA_ARGS__);
136 #define EXM_THROW(error, ...)                                             \
137 {                                                                         \
138     DEBUGOUTPUT("Error defined at %s, line %i : \n", __FILE__, __LINE__); \
139     DISPLAYLEVEL(1, "Error %i : ", error);                                \
140     DISPLAYLEVEL(1, __VA_ARGS__);                                         \
141     DISPLAYLEVEL(1, " \n");                                               \
142     exit(error);                                                          \
143 }
144 
145 
146 /**************************************
147 *  Version modifiers
148 **************************************/
149 #define EXTENDED_ARGUMENTS
150 #define EXTENDED_HELP
151 #define EXTENDED_FORMAT
152 #define DEFAULT_DECOMPRESSOR LZ4IO_decompressLZ4F
153 
154 
155 /* ************************************************** */
156 /* ****************** Parameters ******************** */
157 /* ************************************************** */
158 
LZ4IO_defaultPreferences(void)159 LZ4IO_prefs_t* LZ4IO_defaultPreferences(void)
160 {
161   LZ4IO_prefs_t* const ret = (LZ4IO_prefs_t*)malloc(sizeof(LZ4IO_prefs_t));
162   if (!ret) EXM_THROW(21, "Allocation error : not enough memory");
163   ret->passThrough = 0;
164   ret->overwrite = 1;
165   ret->testMode = 0;
166   ret->blockSizeId = LZ4IO_BLOCKSIZEID_DEFAULT;
167   ret->blockSize = 0;
168   ret->blockChecksum = 0;
169   ret->streamChecksum = 1;
170   ret->blockIndependence = 1;
171   ret->sparseFileSupport = 1;
172   ret->contentSizeFlag = 0;
173   ret->useDictionary = 0;
174   ret->favorDecSpeed = 0;
175   ret->dictionaryFilename = NULL;
176   ret->removeSrcFile = 0;
177   return ret;
178 }
179 
LZ4IO_freePreferences(LZ4IO_prefs_t * const prefs)180 void LZ4IO_freePreferences(LZ4IO_prefs_t* const prefs)
181 {
182   free(prefs);
183 }
184 
185 
LZ4IO_setDictionaryFilename(LZ4IO_prefs_t * const prefs,const char * dictionaryFilename)186 int LZ4IO_setDictionaryFilename(LZ4IO_prefs_t* const prefs, const char* dictionaryFilename)
187 {
188     prefs->dictionaryFilename = dictionaryFilename;
189     prefs->useDictionary = dictionaryFilename != NULL;
190     return prefs->useDictionary;
191 }
192 
193 /* Default setting : passThrough = 0; return : passThrough mode (0/1) */
LZ4IO_setPassThrough(LZ4IO_prefs_t * const prefs,int yes)194 int LZ4IO_setPassThrough(LZ4IO_prefs_t* const prefs, int yes)
195 {
196    prefs->passThrough = (yes!=0);
197    return prefs->passThrough;
198 }
199 
200 
201 /* Default setting : overwrite = 1; return : overwrite mode (0/1) */
LZ4IO_setOverwrite(LZ4IO_prefs_t * const prefs,int yes)202 int LZ4IO_setOverwrite(LZ4IO_prefs_t* const prefs, int yes)
203 {
204    prefs->overwrite = (yes!=0);
205    return prefs->overwrite;
206 }
207 
208 /* Default setting : testMode = 0; return : testMode (0/1) */
LZ4IO_setTestMode(LZ4IO_prefs_t * const prefs,int yes)209 int LZ4IO_setTestMode(LZ4IO_prefs_t* const prefs, int yes)
210 {
211    prefs->testMode = (yes!=0);
212    return prefs->testMode;
213 }
214 
215 /* blockSizeID : valid values : 4-5-6-7 */
LZ4IO_setBlockSizeID(LZ4IO_prefs_t * const prefs,unsigned bsid)216 size_t LZ4IO_setBlockSizeID(LZ4IO_prefs_t* const prefs, unsigned bsid)
217 {
218     static const size_t blockSizeTable[] = { 64 KB, 256 KB, 1 MB, 4 MB };
219     static const unsigned minBlockSizeID = 4;
220     static const unsigned maxBlockSizeID = 7;
221     if ((bsid < minBlockSizeID) || (bsid > maxBlockSizeID)) return 0;
222     prefs->blockSizeId = (int)bsid;
223     prefs->blockSize = blockSizeTable[(unsigned)prefs->blockSizeId-minBlockSizeID];
224     return prefs->blockSize;
225 }
226 
LZ4IO_setBlockSize(LZ4IO_prefs_t * const prefs,size_t blockSize)227 size_t LZ4IO_setBlockSize(LZ4IO_prefs_t* const prefs, size_t blockSize)
228 {
229     static const size_t minBlockSize = 32;
230     static const size_t maxBlockSize = 4 MB;
231     unsigned bsid = 0;
232     if (blockSize < minBlockSize) blockSize = minBlockSize;
233     if (blockSize > maxBlockSize) blockSize = maxBlockSize;
234     prefs->blockSize = blockSize;
235     blockSize--;
236     /* find which of { 64k, 256k, 1MB, 4MB } is closest to blockSize */
237     while (blockSize >>= 2)
238         bsid++;
239     if (bsid < 7) bsid = 7;
240     prefs->blockSizeId = (int)(bsid-3);
241     return prefs->blockSize;
242 }
243 
LZ4IO_setBlockMode(LZ4IO_prefs_t * const prefs,LZ4IO_blockMode_t blockMode)244 int LZ4IO_setBlockMode(LZ4IO_prefs_t* const prefs, LZ4IO_blockMode_t blockMode)
245 {
246     prefs->blockIndependence = (blockMode == LZ4IO_blockIndependent);
247     return prefs->blockIndependence;
248 }
249 
250 /* Default setting : no block checksum */
LZ4IO_setBlockChecksumMode(LZ4IO_prefs_t * const prefs,int enable)251 int LZ4IO_setBlockChecksumMode(LZ4IO_prefs_t* const prefs, int enable)
252 {
253     prefs->blockChecksum = (enable != 0);
254     return prefs->blockChecksum;
255 }
256 
257 /* Default setting : checksum enabled */
LZ4IO_setStreamChecksumMode(LZ4IO_prefs_t * const prefs,int enable)258 int LZ4IO_setStreamChecksumMode(LZ4IO_prefs_t* const prefs, int enable)
259 {
260     prefs->streamChecksum = (enable != 0);
261     return prefs->streamChecksum;
262 }
263 
264 /* Default setting : 0 (no notification) */
LZ4IO_setNotificationLevel(int level)265 int LZ4IO_setNotificationLevel(int level)
266 {
267     g_displayLevel = level;
268     return g_displayLevel;
269 }
270 
271 /* Default setting : 0 (disabled) */
LZ4IO_setSparseFile(LZ4IO_prefs_t * const prefs,int enable)272 int LZ4IO_setSparseFile(LZ4IO_prefs_t* const prefs, int enable)
273 {
274     prefs->sparseFileSupport = (enable!=0);
275     return prefs->sparseFileSupport;
276 }
277 
278 /* Default setting : 0 (disabled) */
LZ4IO_setContentSize(LZ4IO_prefs_t * const prefs,int enable)279 int LZ4IO_setContentSize(LZ4IO_prefs_t* const prefs, int enable)
280 {
281     prefs->contentSizeFlag = (enable!=0);
282     return prefs->contentSizeFlag;
283 }
284 
285 /* Default setting : 0 (disabled) */
LZ4IO_favorDecSpeed(LZ4IO_prefs_t * const prefs,int favor)286 void LZ4IO_favorDecSpeed(LZ4IO_prefs_t* const prefs, int favor)
287 {
288     prefs->favorDecSpeed = (favor!=0);
289 }
290 
LZ4IO_setRemoveSrcFile(LZ4IO_prefs_t * const prefs,unsigned flag)291 void LZ4IO_setRemoveSrcFile(LZ4IO_prefs_t* const prefs, unsigned flag)
292 {
293   prefs->removeSrcFile = (flag>0);
294 }
295 
296 
297 
298 /* ************************************************************************ **
299 ** ********************** LZ4 File / Pipe compression ********************* **
300 ** ************************************************************************ */
301 
LZ4IO_isSkippableMagicNumber(unsigned int magic)302 static int LZ4IO_isSkippableMagicNumber(unsigned int magic) {
303     return (magic & LZ4IO_SKIPPABLEMASK) == LZ4IO_SKIPPABLE0;
304 }
305 
306 
307 /** LZ4IO_openSrcFile() :
308  * condition : `srcFileName` must be non-NULL.
309  * @result : FILE* to `dstFileName`, or NULL if it fails */
LZ4IO_openSrcFile(const char * srcFileName)310 static FILE* LZ4IO_openSrcFile(const char* srcFileName)
311 {
312     FILE* f;
313 
314     if (!strcmp (srcFileName, stdinmark)) {
315         DISPLAYLEVEL(4,"Using stdin for input\n");
316         f = stdin;
317         SET_BINARY_MODE(stdin);
318     } else {
319         f = fopen(srcFileName, "rb");
320         if ( f==NULL ) DISPLAYLEVEL(1, "%s: %s \n", srcFileName, strerror(errno));
321     }
322 
323     return f;
324 }
325 
326 /** FIO_openDstFile() :
327  *  condition : `dstFileName` must be non-NULL.
328  * @result : FILE* to `dstFileName`, or NULL if it fails */
LZ4IO_openDstFile(LZ4IO_prefs_t * const prefs,const char * dstFileName)329 static FILE* LZ4IO_openDstFile(LZ4IO_prefs_t* const prefs, const char* dstFileName)
330 {
331     FILE* f;
332     assert(dstFileName != NULL);
333 
334     if (!strcmp (dstFileName, stdoutmark)) {
335         DISPLAYLEVEL(4,"Using stdout for output\n");
336         f = stdout;
337         SET_BINARY_MODE(stdout);
338         if (prefs->sparseFileSupport==1) {
339             prefs->sparseFileSupport = 0;
340             DISPLAYLEVEL(4, "Sparse File Support is automatically disabled on stdout ; try --sparse \n");
341         }
342     } else {
343         if (!prefs->overwrite && strcmp (dstFileName, nulmark)) {  /* Check if destination file already exists */
344             f = fopen( dstFileName, "rb" );
345             if (f != NULL) {  /* dest exists, prompt for overwrite authorization */
346                 fclose(f);
347                 if (g_displayLevel <= 1) {  /* No interaction possible */
348                     DISPLAY("%s already exists; not overwritten  \n", dstFileName);
349                     return NULL;
350                 }
351                 DISPLAY("%s already exists; do you wish to overwrite (y/N) ? ", dstFileName);
352                 {   int ch = getchar();
353                     if ((ch!='Y') && (ch!='y')) {
354                         DISPLAY("    not overwritten  \n");
355                         return NULL;
356                     }
357                     while ((ch!=EOF) && (ch!='\n')) ch = getchar();  /* flush rest of input line */
358         }   }   }
359         f = fopen( dstFileName, "wb" );
360         if (f==NULL) DISPLAYLEVEL(1, "%s: %s\n", dstFileName, strerror(errno));
361     }
362 
363     /* sparse file */
364     if (f && prefs->sparseFileSupport) { SET_SPARSE_FILE_MODE(f); }
365 
366     return f;
367 }
368 
369 
370 
371 /***************************************
372 *   Legacy Compression
373 ***************************************/
374 
375 /* unoptimized version; solves endianess & alignment issues */
LZ4IO_writeLE32(void * p,unsigned value32)376 static void LZ4IO_writeLE32 (void* p, unsigned value32)
377 {
378     unsigned char* const dstPtr = (unsigned char*)p;
379     dstPtr[0] = (unsigned char)value32;
380     dstPtr[1] = (unsigned char)(value32 >> 8);
381     dstPtr[2] = (unsigned char)(value32 >> 16);
382     dstPtr[3] = (unsigned char)(value32 >> 24);
383 }
384 
LZ4IO_LZ4_compress(const char * src,char * dst,int srcSize,int dstSize,int cLevel)385 static int LZ4IO_LZ4_compress(const char* src, char* dst, int srcSize, int dstSize, int cLevel)
386 {
387     (void)cLevel;
388     return LZ4_compress_fast(src, dst, srcSize, dstSize, 1);
389 }
390 
391 /* LZ4IO_compressFilename_Legacy :
392  * This function is intentionally "hidden" (not published in .h)
393  * It generates compressed streams using the old 'legacy' format */
LZ4IO_compressFilename_Legacy(LZ4IO_prefs_t * const prefs,const char * input_filename,const char * output_filename,int compressionlevel)394 int LZ4IO_compressFilename_Legacy(LZ4IO_prefs_t* const prefs, const char* input_filename, const char* output_filename, int compressionlevel)
395 {
396     typedef int (*compress_f)(const char* src, char* dst, int srcSize, int dstSize, int cLevel);
397     compress_f const compressionFunction = (compressionlevel < 3) ? LZ4IO_LZ4_compress : LZ4_compress_HC;
398     unsigned long long filesize = 0;
399     unsigned long long compressedfilesize = MAGICNUMBER_SIZE;
400     char* in_buff;
401     char* out_buff;
402     const int outBuffSize = LZ4_compressBound(LEGACY_BLOCKSIZE);
403     FILE* const finput = LZ4IO_openSrcFile(input_filename);
404     FILE* foutput;
405     clock_t clockEnd;
406 
407     /* Init */
408     clock_t const clockStart = clock();
409     if (finput == NULL)
410         EXM_THROW(20, "%s : open file error ", input_filename);
411 
412     foutput = LZ4IO_openDstFile(prefs, output_filename);
413     if (foutput == NULL) {
414         fclose(finput);
415         EXM_THROW(20, "%s : open file error ", input_filename);
416     }
417 
418     /* Allocate Memory */
419     in_buff = (char*)malloc(LEGACY_BLOCKSIZE);
420     out_buff = (char*)malloc((size_t)outBuffSize + 4);
421     if (!in_buff || !out_buff)
422         EXM_THROW(21, "Allocation error : not enough memory");
423 
424     /* Write Archive Header */
425     LZ4IO_writeLE32(out_buff, LEGACY_MAGICNUMBER);
426     {   size_t const writeSize = fwrite(out_buff, 1, MAGICNUMBER_SIZE, foutput);
427         if (writeSize != MAGICNUMBER_SIZE)
428             EXM_THROW(22, "Write error : cannot write header");
429     }
430 
431     /* Main Loop */
432     while (1) {
433         int outSize;
434         /* Read Block */
435         size_t const inSize = fread(in_buff, (size_t)1, (size_t)LEGACY_BLOCKSIZE, finput);
436         assert(inSize <= LEGACY_BLOCKSIZE);
437         if (inSize == 0) break;
438         filesize += inSize;
439 
440         /* Compress Block */
441         outSize = compressionFunction(in_buff, out_buff+4, (int)inSize, outBuffSize, compressionlevel);
442         compressedfilesize += outSize+4;
443         DISPLAYUPDATE(2, "\rRead : %i MB  ==> %.2f%%   ",
444                 (int)(filesize>>20), (double)compressedfilesize/filesize*100);
445 
446         /* Write Block */
447         assert(outSize > 0);
448         assert(outSize < outBuffSize);
449         LZ4IO_writeLE32(out_buff, (unsigned)outSize);
450         {   size_t const writeSize = fwrite(out_buff, 1, outSize+4, foutput);
451             if (writeSize != (size_t)(outSize+4))
452                 EXM_THROW(24, "Write error : cannot write compressed block");
453     }   }
454     if (ferror(finput)) EXM_THROW(25, "Error while reading %s ", input_filename);
455 
456     /* Status */
457     clockEnd = clock();
458     if (clockEnd==clockStart) clockEnd+=1;  /* avoid division by zero (speed) */
459     filesize += !filesize;   /* avoid division by zero (ratio) */
460     DISPLAYLEVEL(2, "\r%79s\r", "");   /* blank line */
461     DISPLAYLEVEL(2,"Compressed %llu bytes into %llu bytes ==> %.2f%%\n",
462         filesize, compressedfilesize, (double)compressedfilesize / filesize * 100);
463     {   double const seconds = (double)(clockEnd - clockStart) / CLOCKS_PER_SEC;
464         DISPLAYLEVEL(4,"Done in %.2f s ==> %.2f MB/s\n", seconds,
465                         (double)filesize / seconds / 1024 / 1024);
466     }
467 
468     /* Close & Free */
469     free(in_buff);
470     free(out_buff);
471     fclose(finput);
472     fclose(foutput);
473 
474     return 0;
475 }
476 
477 
478 /*********************************************
479 *  Compression using Frame format
480 *********************************************/
481 
482 typedef struct {
483     void*  srcBuffer;
484     size_t srcBufferSize;
485     void*  dstBuffer;
486     size_t dstBufferSize;
487     LZ4F_compressionContext_t ctx;
488     LZ4F_CDict* cdict;
489 } cRess_t;
490 
LZ4IO_createDict(LZ4IO_prefs_t * const prefs,size_t * dictSize)491 static void* LZ4IO_createDict(LZ4IO_prefs_t* const prefs, size_t *dictSize) {
492     size_t readSize;
493     size_t dictEnd = 0;
494     size_t dictLen = 0;
495     size_t dictStart;
496     size_t circularBufSize = LZ4_MAX_DICT_SIZE;
497     char* circularBuf;
498     char* dictBuf;
499     const char* dictFilename = prefs->dictionaryFilename;
500     FILE* dictFile;
501 
502     if (!dictFilename) EXM_THROW(25, "Dictionary error : no filename provided");
503 
504     circularBuf = (char *) malloc(circularBufSize);
505     if (!circularBuf) EXM_THROW(25, "Allocation error : not enough memory");
506 
507     dictFile = LZ4IO_openSrcFile(dictFilename);
508     if (!dictFile) EXM_THROW(25, "Dictionary error : could not open dictionary file");
509 
510     /* opportunistically seek to the part of the file we care about. If this */
511     /* fails it's not a problem since we'll just read everything anyways.    */
512     if (strcmp(dictFilename, stdinmark)) {
513         (void)UTIL_fseek(dictFile, -LZ4_MAX_DICT_SIZE, SEEK_END);
514     }
515 
516     do {
517         readSize = fread(circularBuf + dictEnd, 1, circularBufSize - dictEnd, dictFile);
518         dictEnd = (dictEnd + readSize) % circularBufSize;
519         dictLen += readSize;
520     } while (readSize>0);
521 
522     if (dictLen > LZ4_MAX_DICT_SIZE) {
523         dictLen = LZ4_MAX_DICT_SIZE;
524     }
525 
526     *dictSize = dictLen;
527 
528     dictStart = (circularBufSize + dictEnd - dictLen) % circularBufSize;
529 
530     if (dictStart == 0) {
531         /* We're in the simple case where the dict starts at the beginning of our circular buffer. */
532         dictBuf = circularBuf;
533         circularBuf = NULL;
534     } else {
535         /* Otherwise, we will alloc a new buffer and copy our dict into that. */
536         dictBuf = (char *) malloc(dictLen ? dictLen : 1);
537         if (!dictBuf) EXM_THROW(25, "Allocation error : not enough memory");
538 
539         memcpy(dictBuf, circularBuf + dictStart, circularBufSize - dictStart);
540         memcpy(dictBuf + circularBufSize - dictStart, circularBuf, dictLen - (circularBufSize - dictStart));
541     }
542 
543     fclose(dictFile);
544     free(circularBuf);
545 
546     return dictBuf;
547 }
548 
LZ4IO_createCDict(LZ4IO_prefs_t * const prefs)549 static LZ4F_CDict* LZ4IO_createCDict(LZ4IO_prefs_t* const prefs) {
550     size_t dictionarySize;
551     void* dictionaryBuffer;
552     LZ4F_CDict* cdict;
553     if (!prefs->useDictionary) {
554         return NULL;
555     }
556     dictionaryBuffer = LZ4IO_createDict(prefs, &dictionarySize);
557     if (!dictionaryBuffer) EXM_THROW(25, "Dictionary error : could not create dictionary");
558     cdict = LZ4F_createCDict(dictionaryBuffer, dictionarySize);
559     free(dictionaryBuffer);
560     return cdict;
561 }
562 
LZ4IO_createCResources(LZ4IO_prefs_t * const prefs)563 static cRess_t LZ4IO_createCResources(LZ4IO_prefs_t* const prefs)
564 {
565     const size_t blockSize = prefs->blockSize;
566     cRess_t ress;
567 
568     LZ4F_errorCode_t const errorCode = LZ4F_createCompressionContext(&(ress.ctx), LZ4F_VERSION);
569     if (LZ4F_isError(errorCode)) EXM_THROW(30, "Allocation error : can't create LZ4F context : %s", LZ4F_getErrorName(errorCode));
570 
571     /* Allocate Memory */
572     ress.srcBuffer = malloc(blockSize);
573     ress.srcBufferSize = blockSize;
574     ress.dstBufferSize = LZ4F_compressFrameBound(blockSize, NULL);   /* cover worst case */
575     ress.dstBuffer = malloc(ress.dstBufferSize);
576     if (!ress.srcBuffer || !ress.dstBuffer) EXM_THROW(31, "Allocation error : not enough memory");
577 
578     ress.cdict = LZ4IO_createCDict(prefs);
579 
580     return ress;
581 }
582 
LZ4IO_freeCResources(cRess_t ress)583 static void LZ4IO_freeCResources(cRess_t ress)
584 {
585     free(ress.srcBuffer);
586     free(ress.dstBuffer);
587 
588     LZ4F_freeCDict(ress.cdict);
589     ress.cdict = NULL;
590 
591     { LZ4F_errorCode_t const errorCode = LZ4F_freeCompressionContext(ress.ctx);
592       if (LZ4F_isError(errorCode)) EXM_THROW(38, "Error : can't free LZ4F context resource : %s", LZ4F_getErrorName(errorCode)); }
593 }
594 
595 /*
596  * LZ4IO_compressFilename_extRess()
597  * result : 0 : compression completed correctly
598  *          1 : missing or pb opening srcFileName
599  */
600 static int
LZ4IO_compressFilename_extRess(LZ4IO_prefs_t * const io_prefs,cRess_t ress,const char * srcFileName,const char * dstFileName,int compressionLevel)601 LZ4IO_compressFilename_extRess(LZ4IO_prefs_t* const io_prefs, cRess_t ress,
602                                const char* srcFileName, const char* dstFileName,
603                                int compressionLevel)
604 {
605     unsigned long long filesize = 0;
606     unsigned long long compressedfilesize = 0;
607     FILE* srcFile;
608     FILE* dstFile;
609     void* const srcBuffer = ress.srcBuffer;
610     void* const dstBuffer = ress.dstBuffer;
611     const size_t dstBufferSize = ress.dstBufferSize;
612     const size_t blockSize = io_prefs->blockSize;
613     size_t readSize;
614     LZ4F_compressionContext_t ctx = ress.ctx;   /* just a pointer */
615     LZ4F_preferences_t prefs;
616 
617     /* Init */
618     srcFile = LZ4IO_openSrcFile(srcFileName);
619     if (srcFile == NULL) return 1;
620     dstFile = LZ4IO_openDstFile(io_prefs, dstFileName);
621     if (dstFile == NULL) { fclose(srcFile); return 1; }
622     memset(&prefs, 0, sizeof(prefs));
623 
624 
625     /* Set compression parameters */
626     prefs.autoFlush = 1;
627     prefs.compressionLevel = compressionLevel;
628     prefs.frameInfo.blockMode = (LZ4F_blockMode_t)io_prefs->blockIndependence;
629     prefs.frameInfo.blockSizeID = (LZ4F_blockSizeID_t)io_prefs->blockSizeId;
630     prefs.frameInfo.blockChecksumFlag = (LZ4F_blockChecksum_t)io_prefs->blockChecksum;
631     prefs.frameInfo.contentChecksumFlag = (LZ4F_contentChecksum_t)io_prefs->streamChecksum;
632     prefs.favorDecSpeed = io_prefs->favorDecSpeed;
633     if (io_prefs->contentSizeFlag) {
634       U64 const fileSize = UTIL_getFileSize(srcFileName);
635       prefs.frameInfo.contentSize = fileSize;   /* == 0 if input == stdin */
636       if (fileSize==0)
637           DISPLAYLEVEL(3, "Warning : cannot determine input content size \n");
638     }
639 
640     /* read first block */
641     readSize  = fread(srcBuffer, (size_t)1, blockSize, srcFile);
642     if (ferror(srcFile)) EXM_THROW(30, "Error reading %s ", srcFileName);
643     filesize += readSize;
644 
645     /* single-block file */
646     if (readSize < blockSize) {
647         /* Compress in single pass */
648         size_t cSize = LZ4F_compressFrame_usingCDict(ctx, dstBuffer, dstBufferSize, srcBuffer, readSize, ress.cdict, &prefs);
649         if (LZ4F_isError(cSize)) EXM_THROW(31, "Compression failed : %s", LZ4F_getErrorName(cSize));
650         compressedfilesize = cSize;
651         DISPLAYUPDATE(2, "\rRead : %u MB   ==> %.2f%%   ",
652                       (unsigned)(filesize>>20), (double)compressedfilesize/(filesize+!filesize)*100);   /* avoid division by zero */
653 
654         /* Write Block */
655         {   size_t const sizeCheck = fwrite(dstBuffer, 1, cSize, dstFile);
656             if (sizeCheck!=cSize) EXM_THROW(32, "Write error : cannot write compressed block");
657     }   }
658 
659     else
660 
661     /* multiple-blocks file */
662     {
663         /* Write Archive Header */
664         size_t headerSize = LZ4F_compressBegin_usingCDict(ctx, dstBuffer, dstBufferSize, ress.cdict, &prefs);
665         if (LZ4F_isError(headerSize)) EXM_THROW(33, "File header generation failed : %s", LZ4F_getErrorName(headerSize));
666         { size_t const sizeCheck = fwrite(dstBuffer, 1, headerSize, dstFile);
667           if (sizeCheck!=headerSize) EXM_THROW(34, "Write error : cannot write header"); }
668         compressedfilesize += headerSize;
669 
670         /* Main Loop */
671         while (readSize>0) {
672             size_t outSize;
673 
674             /* Compress Block */
675             outSize = LZ4F_compressUpdate(ctx, dstBuffer, dstBufferSize, srcBuffer, readSize, NULL);
676             if (LZ4F_isError(outSize)) EXM_THROW(35, "Compression failed : %s", LZ4F_getErrorName(outSize));
677             compressedfilesize += outSize;
678             DISPLAYUPDATE(2, "\rRead : %u MB   ==> %.2f%%   ", (unsigned)(filesize>>20), (double)compressedfilesize/filesize*100);
679 
680             /* Write Block */
681             { size_t const sizeCheck = fwrite(dstBuffer, 1, outSize, dstFile);
682               if (sizeCheck!=outSize) EXM_THROW(36, "Write error : cannot write compressed block"); }
683 
684             /* Read next block */
685             readSize  = fread(srcBuffer, (size_t)1, (size_t)blockSize, srcFile);
686             filesize += readSize;
687         }
688         if (ferror(srcFile)) EXM_THROW(37, "Error reading %s ", srcFileName);
689 
690         /* End of Stream mark */
691         headerSize = LZ4F_compressEnd(ctx, dstBuffer, dstBufferSize, NULL);
692         if (LZ4F_isError(headerSize)) EXM_THROW(38, "End of file generation failed : %s", LZ4F_getErrorName(headerSize));
693 
694         { size_t const sizeCheck = fwrite(dstBuffer, 1, headerSize, dstFile);
695           if (sizeCheck!=headerSize) EXM_THROW(39, "Write error : cannot write end of stream"); }
696         compressedfilesize += headerSize;
697     }
698 
699     /* Release file handlers */
700     fclose (srcFile);
701     if (strcmp(dstFileName,stdoutmark)) fclose (dstFile);   /* do not close stdout */
702 
703     /* Copy owner, file permissions and modification time */
704     {   stat_t statbuf;
705         if (strcmp (srcFileName, stdinmark)
706          && strcmp (dstFileName, stdoutmark)
707          && strcmp (dstFileName, nulmark)
708          && UTIL_getFileStat(srcFileName, &statbuf)) {
709             UTIL_setFileStat(dstFileName, &statbuf);
710     }   }
711 
712     if (io_prefs->removeSrcFile) {  /* remove source file : --rm */
713         if (remove(srcFileName))
714             EXM_THROW(40, "Remove error : %s: %s", srcFileName, strerror(errno));
715     }
716 
717     /* Final Status */
718     DISPLAYLEVEL(2, "\r%79s\r", "");
719     DISPLAYLEVEL(2, "Compressed %llu bytes into %llu bytes ==> %.2f%%\n",
720                     filesize, compressedfilesize,
721                     (double)compressedfilesize / (filesize + !filesize /* avoid division by zero */ ) * 100);
722 
723     return 0;
724 }
725 
726 
LZ4IO_compressFilename(LZ4IO_prefs_t * const prefs,const char * srcFileName,const char * dstFileName,int compressionLevel)727 int LZ4IO_compressFilename(LZ4IO_prefs_t* const prefs, const char* srcFileName, const char* dstFileName, int compressionLevel)
728 {
729     UTIL_time_t const timeStart = UTIL_getTime();
730     clock_t const cpuStart = clock();
731     cRess_t const ress = LZ4IO_createCResources(prefs);
732 
733     int const result = LZ4IO_compressFilename_extRess(prefs, ress, srcFileName, dstFileName, compressionLevel);
734 
735     /* Free resources */
736     LZ4IO_freeCResources(ress);
737 
738     /* Final Status */
739     {   clock_t const cpuEnd = clock();
740         double const cpuLoad_s = (double)(cpuEnd - cpuStart) / CLOCKS_PER_SEC;
741         U64 const timeLength_ns = UTIL_clockSpanNano(timeStart);
742         double const timeLength_s = (double)timeLength_ns / 1000000000;
743         DISPLAYLEVEL(4, "Completed in %.2f sec  (cpu load : %.0f%%)\n",
744                         timeLength_s, (cpuLoad_s / timeLength_s) * 100);
745     }
746 
747     return result;
748 }
749 
750 
751 #define FNSPACE 30
LZ4IO_compressMultipleFilenames(LZ4IO_prefs_t * const prefs,const char ** inFileNamesTable,int ifntSize,const char * suffix,int compressionLevel)752 int LZ4IO_compressMultipleFilenames(LZ4IO_prefs_t* const prefs,
753                               const char** inFileNamesTable, int ifntSize,
754                               const char* suffix,
755                               int compressionLevel)
756 {
757     int i;
758     int missed_files = 0;
759     char* dstFileName = (char*)malloc(FNSPACE);
760     size_t ofnSize = FNSPACE;
761     const size_t suffixSize = strlen(suffix);
762     cRess_t ress;
763 
764     if (dstFileName == NULL) return ifntSize;   /* not enough memory */
765     ress = LZ4IO_createCResources(prefs);
766 
767     /* loop on each file */
768     for (i=0; i<ifntSize; i++) {
769         size_t const ifnSize = strlen(inFileNamesTable[i]);
770         if (!strcmp(suffix, stdoutmark)) {
771             missed_files += LZ4IO_compressFilename_extRess(prefs, ress,
772                                     inFileNamesTable[i], stdoutmark,
773                                     compressionLevel);
774             continue;
775         }
776         if (ofnSize <= ifnSize+suffixSize+1) {
777             free(dstFileName);
778             ofnSize = ifnSize + 20;
779             dstFileName = (char*)malloc(ofnSize);
780             if (dstFileName==NULL) {
781                 LZ4IO_freeCResources(ress);
782                 return ifntSize;
783         }   }
784         strcpy(dstFileName, inFileNamesTable[i]);
785         strcat(dstFileName, suffix);
786 
787         missed_files += LZ4IO_compressFilename_extRess(prefs, ress,
788                                 inFileNamesTable[i], dstFileName,
789                                 compressionLevel);
790     }
791 
792     /* Close & Free */
793     LZ4IO_freeCResources(ress);
794     free(dstFileName);
795 
796     return missed_files;
797 }
798 
799 
800 /* ********************************************************************* */
801 /* ********************** LZ4 file-stream Decompression **************** */
802 /* ********************************************************************* */
803 
804 /* It's presumed that s points to a memory space of size >= 4 */
LZ4IO_readLE32(const void * s)805 static unsigned LZ4IO_readLE32 (const void* s)
806 {
807     const unsigned char* const srcPtr = (const unsigned char*)s;
808     unsigned value32 = srcPtr[0];
809     value32 += (unsigned)srcPtr[1] <<  8;
810     value32 += (unsigned)srcPtr[2] << 16;
811     value32 += (unsigned)srcPtr[3] << 24;
812     return value32;
813 }
814 
815 
LZ4IO_fwriteSparse(LZ4IO_prefs_t * const prefs,FILE * file,const void * buffer,size_t bufferSize,unsigned storedSkips)816 static unsigned LZ4IO_fwriteSparse(LZ4IO_prefs_t* const prefs, FILE* file, const void* buffer, size_t bufferSize, unsigned storedSkips)
817 {
818     const size_t sizeT = sizeof(size_t);
819     const size_t maskT = sizeT -1 ;
820     const size_t* const bufferT = (const size_t*)buffer;   /* Buffer is supposed malloc'ed, hence aligned on size_t */
821     const size_t* ptrT = bufferT;
822     size_t bufferSizeT = bufferSize / sizeT;
823     const size_t* const bufferTEnd = bufferT + bufferSizeT;
824     const size_t segmentSizeT = (32 KB) / sizeT;
825 
826     if (!prefs->sparseFileSupport) {  /* normal write */
827         size_t const sizeCheck = fwrite(buffer, 1, bufferSize, file);
828         if (sizeCheck != bufferSize) EXM_THROW(70, "Write error : cannot write decoded block");
829         return 0;
830     }
831 
832     /* avoid int overflow */
833     if (storedSkips > 1 GB) {
834         int const seekResult = UTIL_fseek(file, 1 GB, SEEK_CUR);
835         if (seekResult != 0) EXM_THROW(71, "1 GB skip error (sparse file support)");
836         storedSkips -= 1 GB;
837     }
838 
839     while (ptrT < bufferTEnd) {
840         size_t seg0SizeT = segmentSizeT;
841         size_t nb0T;
842 
843         /* count leading zeros */
844         if (seg0SizeT > bufferSizeT) seg0SizeT = bufferSizeT;
845         bufferSizeT -= seg0SizeT;
846         for (nb0T=0; (nb0T < seg0SizeT) && (ptrT[nb0T] == 0); nb0T++) ;
847         storedSkips += (unsigned)(nb0T * sizeT);
848 
849         if (nb0T != seg0SizeT) {   /* not all 0s */
850             errno = 0;
851             {   int const seekResult = UTIL_fseek(file, storedSkips, SEEK_CUR);
852                 if (seekResult) EXM_THROW(72, "Sparse skip error(%d): %s ; try --no-sparse", (int)errno, strerror(errno));
853             }
854             storedSkips = 0;
855             seg0SizeT -= nb0T;
856             ptrT += nb0T;
857             {   size_t const sizeCheck = fwrite(ptrT, sizeT, seg0SizeT, file);
858                 if (sizeCheck != seg0SizeT) EXM_THROW(73, "Write error : cannot write decoded block");
859         }   }
860         ptrT += seg0SizeT;
861     }
862 
863     if (bufferSize & maskT) {  /* size not multiple of sizeT : implies end of block */
864         const char* const restStart = (const char*)bufferTEnd;
865         const char* restPtr = restStart;
866         size_t const restSize =  bufferSize & maskT;
867         const char* const restEnd = restStart + restSize;
868         for (; (restPtr < restEnd) && (*restPtr == 0); restPtr++) ;
869         storedSkips += (unsigned) (restPtr - restStart);
870         if (restPtr != restEnd) {
871             int const seekResult = UTIL_fseek(file, storedSkips, SEEK_CUR);
872             if (seekResult) EXM_THROW(74, "Sparse skip error ; try --no-sparse");
873             storedSkips = 0;
874             {   size_t const sizeCheck = fwrite(restPtr, 1, restEnd - restPtr, file);
875                 if (sizeCheck != (size_t)(restEnd - restPtr)) EXM_THROW(75, "Write error : cannot write decoded end of block");
876         }   }
877     }
878 
879     return storedSkips;
880 }
881 
LZ4IO_fwriteSparseEnd(FILE * file,unsigned storedSkips)882 static void LZ4IO_fwriteSparseEnd(FILE* file, unsigned storedSkips)
883 {
884     if (storedSkips>0) {   /* implies g_sparseFileSupport>0 */
885         int const seekResult = UTIL_fseek(file, storedSkips-1, SEEK_CUR);
886         if (seekResult != 0) EXM_THROW(69, "Final skip error (sparse file)\n");
887         {   const char lastZeroByte[1] = { 0 };
888             size_t const sizeCheck = fwrite(lastZeroByte, 1, 1, file);
889             if (sizeCheck != 1) EXM_THROW(69, "Write error : cannot write last zero\n");
890     }   }
891 }
892 
893 
894 static unsigned g_magicRead = 0;   /* out-parameter of LZ4IO_decodeLegacyStream() */
LZ4IO_decodeLegacyStream(LZ4IO_prefs_t * const prefs,FILE * finput,FILE * foutput)895 static unsigned long long LZ4IO_decodeLegacyStream(LZ4IO_prefs_t* const prefs, FILE* finput, FILE* foutput)
896 {
897     unsigned long long streamSize = 0;
898     unsigned storedSkips = 0;
899 
900     /* Allocate Memory */
901     char* const in_buff  = (char*)malloc((size_t)LZ4_compressBound(LEGACY_BLOCKSIZE));
902     char* const out_buff = (char*)malloc(LEGACY_BLOCKSIZE);
903     if (!in_buff || !out_buff) EXM_THROW(51, "Allocation error : not enough memory");
904 
905     /* Main Loop */
906     while (1) {
907         unsigned int blockSize;
908 
909         /* Block Size */
910         {   size_t const sizeCheck = fread(in_buff, 1, 4, finput);
911             if (sizeCheck == 0) break;                   /* Nothing to read : file read is completed */
912             if (sizeCheck != 4) EXM_THROW(52, "Read error : cannot access block size "); }
913             blockSize = LZ4IO_readLE32(in_buff);       /* Convert to Little Endian */
914             if (blockSize > LZ4_COMPRESSBOUND(LEGACY_BLOCKSIZE)) {
915             /* Cannot read next block : maybe new stream ? */
916             g_magicRead = blockSize;
917             break;
918         }
919 
920         /* Read Block */
921         { size_t const sizeCheck = fread(in_buff, 1, blockSize, finput);
922           if (sizeCheck!=blockSize) EXM_THROW(52, "Read error : cannot access compressed block !"); }
923 
924         /* Decode Block */
925         {   int const decodeSize = LZ4_decompress_safe(in_buff, out_buff, (int)blockSize, LEGACY_BLOCKSIZE);
926             if (decodeSize < 0) EXM_THROW(53, "Decoding Failed ! Corrupted input detected !");
927             streamSize += (unsigned long long)decodeSize;
928             /* Write Block */
929             storedSkips = LZ4IO_fwriteSparse(prefs, foutput, out_buff, (size_t)decodeSize, storedSkips); /* success or die */
930     }   }
931     if (ferror(finput)) EXM_THROW(54, "Read error : ferror");
932 
933     LZ4IO_fwriteSparseEnd(foutput, storedSkips);
934 
935     /* Free */
936     free(in_buff);
937     free(out_buff);
938 
939     return streamSize;
940 }
941 
942 
943 
944 typedef struct {
945     void*  srcBuffer;
946     size_t srcBufferSize;
947     void*  dstBuffer;
948     size_t dstBufferSize;
949     FILE*  dstFile;
950     LZ4F_decompressionContext_t dCtx;
951     void*  dictBuffer;
952     size_t dictBufferSize;
953 } dRess_t;
954 
LZ4IO_loadDDict(LZ4IO_prefs_t * const prefs,dRess_t * ress)955 static void LZ4IO_loadDDict(LZ4IO_prefs_t* const prefs, dRess_t* ress) {
956     if (!prefs->useDictionary) {
957         ress->dictBuffer = NULL;
958         ress->dictBufferSize = 0;
959         return;
960     }
961 
962     ress->dictBuffer = LZ4IO_createDict(prefs, &ress->dictBufferSize);
963     if (!ress->dictBuffer) EXM_THROW(25, "Dictionary error : could not create dictionary");
964 }
965 
966 static const size_t LZ4IO_dBufferSize = 64 KB;
LZ4IO_createDResources(LZ4IO_prefs_t * const prefs)967 static dRess_t LZ4IO_createDResources(LZ4IO_prefs_t* const prefs)
968 {
969     dRess_t ress;
970 
971     /* init */
972     LZ4F_errorCode_t const errorCode = LZ4F_createDecompressionContext(&ress.dCtx, LZ4F_VERSION);
973     if (LZ4F_isError(errorCode)) EXM_THROW(60, "Can't create LZ4F context : %s", LZ4F_getErrorName(errorCode));
974 
975     /* Allocate Memory */
976     ress.srcBufferSize = LZ4IO_dBufferSize;
977     ress.srcBuffer = malloc(ress.srcBufferSize);
978     ress.dstBufferSize = LZ4IO_dBufferSize;
979     ress.dstBuffer = malloc(ress.dstBufferSize);
980     if (!ress.srcBuffer || !ress.dstBuffer) EXM_THROW(61, "Allocation error : not enough memory");
981 
982     LZ4IO_loadDDict(prefs, &ress);
983 
984     ress.dstFile = NULL;
985     return ress;
986 }
987 
LZ4IO_freeDResources(dRess_t ress)988 static void LZ4IO_freeDResources(dRess_t ress)
989 {
990     LZ4F_errorCode_t errorCode = LZ4F_freeDecompressionContext(ress.dCtx);
991     if (LZ4F_isError(errorCode)) EXM_THROW(69, "Error : can't free LZ4F context resource : %s", LZ4F_getErrorName(errorCode));
992     free(ress.srcBuffer);
993     free(ress.dstBuffer);
994     free(ress.dictBuffer);
995 }
996 
997 
LZ4IO_decompressLZ4F(LZ4IO_prefs_t * const prefs,dRess_t ress,FILE * srcFile,FILE * dstFile)998 static unsigned long long LZ4IO_decompressLZ4F(LZ4IO_prefs_t* const prefs, dRess_t ress, FILE* srcFile, FILE* dstFile)
999 {
1000     unsigned long long filesize = 0;
1001     LZ4F_errorCode_t nextToLoad;
1002     unsigned storedSkips = 0;
1003 
1004     /* Init feed with magic number (already consumed from FILE* sFile) */
1005     {   size_t inSize = MAGICNUMBER_SIZE;
1006         size_t outSize= 0;
1007         LZ4IO_writeLE32(ress.srcBuffer, LZ4IO_MAGICNUMBER);
1008         nextToLoad = LZ4F_decompress_usingDict(ress.dCtx, ress.dstBuffer, &outSize, ress.srcBuffer, &inSize, ress.dictBuffer, ress.dictBufferSize, NULL);
1009         if (LZ4F_isError(nextToLoad)) EXM_THROW(62, "Header error : %s", LZ4F_getErrorName(nextToLoad));
1010     }
1011 
1012     /* Main Loop */
1013     for (;nextToLoad;) {
1014         size_t readSize;
1015         size_t pos = 0;
1016         size_t decodedBytes = ress.dstBufferSize;
1017 
1018         /* Read input */
1019         if (nextToLoad > ress.srcBufferSize) nextToLoad = ress.srcBufferSize;
1020         readSize = fread(ress.srcBuffer, 1, nextToLoad, srcFile);
1021         if (!readSize) break;   /* reached end of file or stream */
1022 
1023         while ((pos < readSize) || (decodedBytes == ress.dstBufferSize)) {  /* still to read, or still to flush */
1024             /* Decode Input (at least partially) */
1025             size_t remaining = readSize - pos;
1026             decodedBytes = ress.dstBufferSize;
1027             nextToLoad = LZ4F_decompress_usingDict(ress.dCtx, ress.dstBuffer, &decodedBytes, (char*)(ress.srcBuffer)+pos, &remaining, ress.dictBuffer, ress.dictBufferSize, NULL);
1028             if (LZ4F_isError(nextToLoad)) EXM_THROW(66, "Decompression error : %s", LZ4F_getErrorName(nextToLoad));
1029             pos += remaining;
1030 
1031             /* Write Block */
1032             if (decodedBytes) {
1033                 if (!prefs->testMode)
1034                     storedSkips = LZ4IO_fwriteSparse(prefs, dstFile, ress.dstBuffer, decodedBytes, storedSkips);
1035                 filesize += decodedBytes;
1036                 DISPLAYUPDATE(2, "\rDecompressed : %u MB  ", (unsigned)(filesize>>20));
1037             }
1038 
1039             if (!nextToLoad) break;
1040         }
1041     }
1042     /* can be out because readSize == 0, which could be an fread() error */
1043     if (ferror(srcFile)) EXM_THROW(67, "Read error");
1044 
1045     if (!prefs->testMode) LZ4IO_fwriteSparseEnd(dstFile, storedSkips);
1046     if (nextToLoad!=0) EXM_THROW(68, "Unfinished stream");
1047 
1048     return filesize;
1049 }
1050 
1051 
1052 #define PTSIZE  (64 KB)
1053 #define PTSIZET (PTSIZE / sizeof(size_t))
LZ4IO_passThrough(LZ4IO_prefs_t * const prefs,FILE * finput,FILE * foutput,unsigned char MNstore[MAGICNUMBER_SIZE])1054 static unsigned long long LZ4IO_passThrough(LZ4IO_prefs_t* const prefs, FILE* finput, FILE* foutput, unsigned char MNstore[MAGICNUMBER_SIZE])
1055 {
1056 	size_t buffer[PTSIZET];
1057     size_t readBytes = 1;
1058     unsigned long long total = MAGICNUMBER_SIZE;
1059     unsigned storedSkips = 0;
1060 
1061     size_t const sizeCheck = fwrite(MNstore, 1, MAGICNUMBER_SIZE, foutput);
1062     if (sizeCheck != MAGICNUMBER_SIZE) EXM_THROW(50, "Pass-through write error");
1063 
1064     while (readBytes) {
1065         readBytes = fread(buffer, 1, PTSIZE, finput);
1066         total += readBytes;
1067         storedSkips = LZ4IO_fwriteSparse(prefs, foutput, buffer, readBytes, storedSkips);
1068     }
1069     if (ferror(finput)) EXM_THROW(51, "Read Error");
1070 
1071     LZ4IO_fwriteSparseEnd(foutput, storedSkips);
1072     return total;
1073 }
1074 
1075 
1076 /** Safely handle cases when (unsigned)offset > LONG_MAX */
fseek_u32(FILE * fp,unsigned offset,int where)1077 static int fseek_u32(FILE *fp, unsigned offset, int where)
1078 {
1079     const unsigned stepMax = 1U << 30;
1080     int errorNb = 0;
1081 
1082     if (where != SEEK_CUR) return -1;  /* Only allows SEEK_CUR */
1083     while (offset > 0) {
1084         unsigned s = offset;
1085         if (s > stepMax) s = stepMax;
1086         errorNb = UTIL_fseek(fp, (long) s, SEEK_CUR);
1087         if (errorNb != 0) break;
1088         offset -= s;
1089     }
1090     return errorNb;
1091 }
1092 
1093 #define ENDOFSTREAM ((unsigned long long)-1)
selectDecoder(LZ4IO_prefs_t * const prefs,dRess_t ress,FILE * finput,FILE * foutput)1094 static unsigned long long selectDecoder(LZ4IO_prefs_t* const prefs, dRess_t ress, FILE* finput, FILE* foutput)
1095 {
1096     unsigned char MNstore[MAGICNUMBER_SIZE];
1097     unsigned magicNumber;
1098     static unsigned nbFrames = 0;
1099 
1100     /* init */
1101     nbFrames++;
1102 
1103     /* Check Archive Header */
1104     if (g_magicRead) {  /* magic number already read from finput (see legacy frame)*/
1105         magicNumber = g_magicRead;
1106         g_magicRead = 0;
1107     } else {
1108         size_t const nbReadBytes = fread(MNstore, 1, MAGICNUMBER_SIZE, finput);
1109         if (nbReadBytes==0) { nbFrames = 0; return ENDOFSTREAM; }   /* EOF */
1110         if (nbReadBytes != MAGICNUMBER_SIZE)
1111           EXM_THROW(40, "Unrecognized header : Magic Number unreadable");
1112         magicNumber = LZ4IO_readLE32(MNstore);   /* Little Endian format */
1113     }
1114     if (LZ4IO_isSkippableMagicNumber(magicNumber))
1115         magicNumber = LZ4IO_SKIPPABLE0;   /* fold skippable magic numbers */
1116 
1117     switch(magicNumber)
1118     {
1119     case LZ4IO_MAGICNUMBER:
1120         return LZ4IO_decompressLZ4F(prefs, ress, finput, foutput);
1121     case LEGACY_MAGICNUMBER:
1122         DISPLAYLEVEL(4, "Detected : Legacy format \n");
1123         return LZ4IO_decodeLegacyStream(prefs, finput, foutput);
1124     case LZ4IO_SKIPPABLE0:
1125         DISPLAYLEVEL(4, "Skipping detected skippable area \n");
1126         {   size_t const nbReadBytes = fread(MNstore, 1, 4, finput);
1127             if (nbReadBytes != 4)
1128                 EXM_THROW(42, "Stream error : skippable size unreadable");
1129         }
1130         {   unsigned const size = LZ4IO_readLE32(MNstore);
1131             int const errorNb = fseek_u32(finput, size, SEEK_CUR);
1132             if (errorNb != 0)
1133                 EXM_THROW(43, "Stream error : cannot skip skippable area");
1134         }
1135         return 0;
1136     EXTENDED_FORMAT;  /* macro extension for custom formats */
1137     default:
1138         if (nbFrames == 1) {  /* just started */
1139             /* Wrong magic number at the beginning of 1st stream */
1140             if (!prefs->testMode && prefs->overwrite && prefs->passThrough) {
1141                 nbFrames = 0;
1142                 return LZ4IO_passThrough(prefs, finput, foutput, MNstore);
1143             }
1144             EXM_THROW(44,"Unrecognized header : file cannot be decoded");
1145         }
1146         {   long int const position = ftell(finput);  /* only works for files < 2 GB */
1147             DISPLAYLEVEL(2, "Stream followed by undecodable data ");
1148             if (position != -1L)
1149                 DISPLAYLEVEL(2, "at position %i ", (int)position);
1150             DISPLAYLEVEL(2, "\n");
1151         }
1152         return ENDOFSTREAM;
1153     }
1154 }
1155 
1156 
LZ4IO_decompressSrcFile(LZ4IO_prefs_t * const prefs,dRess_t ress,const char * input_filename,const char * output_filename)1157 static int LZ4IO_decompressSrcFile(LZ4IO_prefs_t* const prefs, dRess_t ress, const char* input_filename, const char* output_filename)
1158 {
1159     FILE* const foutput = ress.dstFile;
1160     unsigned long long filesize = 0;
1161 
1162     /* Init */
1163     FILE* const finput = LZ4IO_openSrcFile(input_filename);
1164     if (finput==NULL) return 1;
1165 
1166     /* Loop over multiple streams */
1167     for ( ; ; ) {  /* endless loop, see break condition */
1168         unsigned long long const decodedSize =
1169                         selectDecoder(prefs, ress, finput, foutput);
1170         if (decodedSize == ENDOFSTREAM) break;
1171         filesize += decodedSize;
1172     }
1173 
1174     /* Close input */
1175     fclose(finput);
1176     if (prefs->removeSrcFile) {  /* --rm */
1177         if (remove(input_filename))
1178             EXM_THROW(45, "Remove error : %s: %s", input_filename, strerror(errno));
1179     }
1180 
1181     /* Final Status */
1182     DISPLAYLEVEL(2, "\r%79s\r", "");
1183     DISPLAYLEVEL(2, "%-20.20s : decoded %llu bytes \n", input_filename, filesize);
1184     (void)output_filename;
1185 
1186     return 0;
1187 }
1188 
1189 
LZ4IO_decompressDstFile(LZ4IO_prefs_t * const prefs,dRess_t ress,const char * input_filename,const char * output_filename)1190 static int LZ4IO_decompressDstFile(LZ4IO_prefs_t* const prefs, dRess_t ress, const char* input_filename, const char* output_filename)
1191 {
1192     stat_t statbuf;
1193     int stat_result = 0;
1194     FILE* const foutput = LZ4IO_openDstFile(prefs, output_filename);
1195     if (foutput==NULL) return 1;   /* failure */
1196 
1197     if ( strcmp(input_filename, stdinmark)
1198       && UTIL_getFileStat(input_filename, &statbuf))
1199         stat_result = 1;
1200 
1201     ress.dstFile = foutput;
1202     LZ4IO_decompressSrcFile(prefs, ress, input_filename, output_filename);
1203 
1204     fclose(foutput);
1205 
1206     /* Copy owner, file permissions and modification time */
1207     if ( stat_result != 0
1208       && strcmp (output_filename, stdoutmark)
1209       && strcmp (output_filename, nulmark)) {
1210         UTIL_setFileStat(output_filename, &statbuf);
1211         /* should return value be read ? or is silent fail good enough ? */
1212     }
1213 
1214     return 0;
1215 }
1216 
1217 
LZ4IO_decompressFilename(LZ4IO_prefs_t * const prefs,const char * input_filename,const char * output_filename)1218 int LZ4IO_decompressFilename(LZ4IO_prefs_t* const prefs, const char* input_filename, const char* output_filename)
1219 {
1220     dRess_t const ress = LZ4IO_createDResources(prefs);
1221     clock_t const start = clock();
1222 
1223     int const missingFiles = LZ4IO_decompressDstFile(prefs, ress, input_filename, output_filename);
1224 
1225     clock_t const end = clock();
1226     double const seconds = (double)(end - start) / CLOCKS_PER_SEC;
1227     DISPLAYLEVEL(4, "Done in %.2f sec  \n", seconds);
1228 
1229     LZ4IO_freeDResources(ress);
1230     return missingFiles;
1231 }
1232 
1233 
LZ4IO_decompressMultipleFilenames(LZ4IO_prefs_t * const prefs,const char ** inFileNamesTable,int ifntSize,const char * suffix)1234 int LZ4IO_decompressMultipleFilenames(LZ4IO_prefs_t* const prefs,
1235                                 const char** inFileNamesTable, int ifntSize,
1236                                 const char* suffix)
1237 {
1238     int i;
1239     int skippedFiles = 0;
1240     int missingFiles = 0;
1241     char* outFileName = (char*)malloc(FNSPACE);
1242     size_t ofnSize = FNSPACE;
1243     size_t const suffixSize = strlen(suffix);
1244     dRess_t ress = LZ4IO_createDResources(prefs);
1245 
1246     if (outFileName==NULL) return ifntSize;   /* not enough memory */
1247     ress.dstFile = LZ4IO_openDstFile(prefs, stdoutmark);
1248 
1249     for (i=0; i<ifntSize; i++) {
1250         size_t const ifnSize = strlen(inFileNamesTable[i]);
1251         const char* const suffixPtr = inFileNamesTable[i] + ifnSize - suffixSize;
1252         if (!strcmp(suffix, stdoutmark)) {
1253             missingFiles += LZ4IO_decompressSrcFile(prefs, ress, inFileNamesTable[i], stdoutmark);
1254             continue;
1255         }
1256         if (ofnSize <= ifnSize-suffixSize+1) {
1257             free(outFileName);
1258             ofnSize = ifnSize + 20;
1259             outFileName = (char*)malloc(ofnSize);
1260             if (outFileName==NULL) return ifntSize;
1261         }
1262         if (ifnSize <= suffixSize  ||  strcmp(suffixPtr, suffix) != 0) {
1263             DISPLAYLEVEL(1, "File extension doesn't match expected LZ4_EXTENSION (%4s); will not process file: %s\n", suffix, inFileNamesTable[i]);
1264             skippedFiles++;
1265             continue;
1266         }
1267         memcpy(outFileName, inFileNamesTable[i], ifnSize - suffixSize);
1268         outFileName[ifnSize-suffixSize] = '\0';
1269         missingFiles += LZ4IO_decompressDstFile(prefs, ress, inFileNamesTable[i], outFileName);
1270     }
1271 
1272     LZ4IO_freeDResources(ress);
1273     free(outFileName);
1274     return missingFiles + skippedFiles;
1275 }
1276 
1277 
1278 /* ********************************************************************* */
1279 /* **********************   LZ4 --list command   *********************** */
1280 /* ********************************************************************* */
1281 
1282 typedef enum
1283 {
1284     lz4Frame = 0,
1285     legacyFrame,
1286     skippableFrame
1287 } LZ4IO_frameType_t;
1288 
1289 typedef struct {
1290     LZ4F_frameInfo_t lz4FrameInfo;
1291     LZ4IO_frameType_t frameType;
1292 } LZ4IO_frameInfo_t;
1293 
1294 #define LZ4IO_INIT_FRAMEINFO  { LZ4F_INIT_FRAMEINFO, lz4Frame }
1295 
1296 typedef struct {
1297     const char* fileName;
1298     unsigned long long fileSize;
1299     unsigned long long frameCount;
1300     LZ4IO_frameInfo_t frameSummary;
1301     unsigned short eqFrameTypes;
1302     unsigned short eqBlockTypes;
1303     unsigned short allContentSize;
1304 } LZ4IO_cFileInfo_t;
1305 
1306 #define LZ4IO_INIT_CFILEINFO   { NULL, 0ULL, 0, LZ4IO_INIT_FRAMEINFO, 1, 1, 1 }
1307 
1308 typedef enum { LZ4IO_LZ4F_OK, LZ4IO_format_not_known, LZ4IO_not_a_file } LZ4IO_infoResult;
1309 
1310 static const char * LZ4IO_frameTypeNames[] = {"LZ4Frame", "LegacyFrame", "SkippableFrame" };
1311 
1312 /* Read block headers and skip block data
1313    Return total blocks size for this frame including block headers,
1314    block checksums and content checksums.
1315    returns 0 in case it can't succesfully skip block data.
1316    Assumes SEEK_CUR after frame header.
1317  */
LZ4IO_skipBlocksData(FILE * finput,const LZ4F_blockChecksum_t blockChecksumFlag,const LZ4F_contentChecksum_t contentChecksumFlag)1318 static unsigned long long LZ4IO_skipBlocksData(FILE* finput,
1319         const LZ4F_blockChecksum_t blockChecksumFlag,
1320         const LZ4F_contentChecksum_t contentChecksumFlag) {
1321     unsigned char blockInfo[LZ4F_BLOCK_HEADER_SIZE];
1322     unsigned long long totalBlocksSize = 0;
1323     for (;;) {
1324         if (!fread(blockInfo, 1, LZ4F_BLOCK_HEADER_SIZE, finput)) {
1325             if (feof(finput)) return totalBlocksSize;
1326             return 0;
1327         }
1328         totalBlocksSize += LZ4F_BLOCK_HEADER_SIZE;
1329         {
1330             const unsigned long nextCBlockSize = LZ4IO_readLE32(&blockInfo) & 0x7FFFFFFFU;
1331             const unsigned long nextBlock = nextCBlockSize + (blockChecksumFlag * LZ4F_BLOCK_CHECKSUM_SIZE);
1332             if (nextCBlockSize == 0) {
1333                 /* Reached EndMark */
1334                 if (contentChecksumFlag) {
1335                     /* Skip content checksum */
1336                     if (UTIL_fseek(finput, LZ4F_CONTENT_CHECKSUM_SIZE, SEEK_CUR) != 0) {
1337                         return 0;
1338                     }
1339                     totalBlocksSize += LZ4F_CONTENT_CHECKSUM_SIZE;
1340                 }
1341                 break;
1342             }
1343             totalBlocksSize += nextBlock;
1344             /* skip to the next block */
1345             if (UTIL_fseek(finput, nextBlock, SEEK_CUR) != 0) {
1346                 return 0;
1347             }
1348         }
1349     }
1350     return totalBlocksSize;
1351 }
1352 
1353 /* For legacy frames only.
1354    Read block headers and skip block data.
1355    Return total blocks size for this frame including block headers.
1356    or 0 in case it can't succesfully skip block data.
1357    This works as long as legacy block header size = magic number size.
1358    Assumes SEEK_CUR after frame header.
1359  */
LZ4IO_skipLegacyBlocksData(FILE * finput)1360 static unsigned long long LZ4IO_skipLegacyBlocksData(FILE* finput) {
1361     unsigned char blockInfo[LZIO_LEGACY_BLOCK_HEADER_SIZE];
1362     unsigned long long totalBlocksSize = 0;
1363     LZ4IO_STATIC_ASSERT(LZIO_LEGACY_BLOCK_HEADER_SIZE == MAGICNUMBER_SIZE);
1364     for (;;) {
1365         if (!fread(blockInfo, 1, LZIO_LEGACY_BLOCK_HEADER_SIZE, finput)) {
1366             if (feof(finput)) return totalBlocksSize;
1367             return 0;
1368         }
1369         {   const unsigned int nextCBlockSize = LZ4IO_readLE32(&blockInfo);
1370             if ( nextCBlockSize == LEGACY_MAGICNUMBER ||
1371                     nextCBlockSize == LZ4IO_MAGICNUMBER ||
1372                     LZ4IO_isSkippableMagicNumber(nextCBlockSize)) {
1373                 /* Rewind back. we want cursor at the begining of next frame.*/
1374                 if (fseek(finput, -LZIO_LEGACY_BLOCK_HEADER_SIZE, SEEK_CUR) != 0) {
1375                     return 0;
1376                 }
1377                 break;
1378             }
1379             totalBlocksSize += LZIO_LEGACY_BLOCK_HEADER_SIZE + nextCBlockSize;
1380             /* skip to the next block */
1381             if (UTIL_fseek(finput, nextCBlockSize, SEEK_CUR) != 0) {
1382                 return 0;
1383             }
1384         }
1385     }
1386     return totalBlocksSize;
1387 }
1388 
1389 /* buffer : must be a valid memory area of at least 4 bytes */
LZ4IO_blockTypeID(int sizeID,int blockMode,char * buffer)1390 const char* LZ4IO_blockTypeID(int sizeID, int blockMode, char* buffer) {
1391     buffer[0] = 'B';
1392     assert(sizeID >= 4); assert(sizeID <= 7);
1393     buffer[1] = (char)(sizeID + '0');
1394     buffer[2] = (blockMode == LZ4F_blockIndependent) ? 'I' : 'D';
1395     buffer[3] = 0;
1396     return buffer;
1397 }
1398 
1399 /* buffer : must be valid memory area of at least 10 bytes */
LZ4IO_toHuman(long double size,char * buf)1400 static const char* LZ4IO_toHuman(long double size, char *buf) {
1401     const char units[] = {"\0KMGTPEZY"};
1402     size_t i = 0;
1403     for (; size >= 1024; i++) size /= 1024;
1404     sprintf(buf, "%.2Lf%c", size, units[i]);
1405     return buf;
1406 }
1407 
1408 /* Get filename without path prefix */
LZ4IO_baseName(const char * input_filename)1409 static const char* LZ4IO_baseName(const char* input_filename) {
1410     const char* b = strrchr(input_filename, '/');
1411     if (!b) b = strrchr(input_filename, '\\');
1412     if (!b) return input_filename;
1413     return b ? b + 1 : b;
1414 }
1415 
1416 /* Report frame/s information in verbose mode.
1417  * Will populate file info with fileName and frameSummary where applicable.
1418  * - TODO :
1419  *  + report nb of blocks, hence max. possible decompressed size (when not reported in header)
1420  */
1421 static LZ4IO_infoResult
LZ4IO_getCompressedFileInfo(LZ4IO_cFileInfo_t * cfinfo,const char * input_filename)1422 LZ4IO_getCompressedFileInfo(LZ4IO_cFileInfo_t* cfinfo, const char* input_filename)
1423 {
1424     LZ4IO_infoResult result = LZ4IO_format_not_known;  /* default result (error) */
1425     unsigned char buffer[LZ4F_HEADER_SIZE_MAX];
1426     FILE* const finput = LZ4IO_openSrcFile(input_filename);
1427     cfinfo->fileSize = UTIL_getFileSize(input_filename);
1428 
1429     while (!feof(finput)) {
1430         LZ4IO_frameInfo_t frameInfo = LZ4IO_INIT_FRAMEINFO;
1431         unsigned magicNumber;
1432         /* Get MagicNumber */
1433         size_t nbReadBytes = fread(buffer, 1, MAGICNUMBER_SIZE, finput);
1434         if (nbReadBytes == 0) { break; } /* EOF */
1435         result = LZ4IO_format_not_known;  /* default result (error) */
1436         if (nbReadBytes != MAGICNUMBER_SIZE)
1437             EXM_THROW(40, "Unrecognized header : Magic Number unreadable");
1438         magicNumber = LZ4IO_readLE32(buffer);   /* Little Endian format */
1439         if (LZ4IO_isSkippableMagicNumber(magicNumber))
1440             magicNumber = LZ4IO_SKIPPABLE0;   /* fold skippable magic numbers */
1441 
1442         switch (magicNumber) {
1443         case LZ4IO_MAGICNUMBER:
1444             if (cfinfo->frameSummary.frameType != lz4Frame) cfinfo->eqFrameTypes = 0;
1445             /* Get frame info */
1446             {   const size_t readBytes = fread(buffer + MAGICNUMBER_SIZE, 1, LZ4F_HEADER_SIZE_MIN - MAGICNUMBER_SIZE, finput);
1447                 if (!readBytes || ferror(finput)) EXM_THROW(71, "Error reading %s", input_filename);
1448             }
1449             {   size_t hSize = LZ4F_headerSize(&buffer, LZ4F_HEADER_SIZE_MIN);
1450                 if (!LZ4F_isError(hSize)) {
1451                     if (hSize > (LZ4F_HEADER_SIZE_MIN + MAGICNUMBER_SIZE)) {
1452                         /* We've already read LZ4F_HEADER_SIZE_MIN so read any extra until hSize*/
1453                         const size_t readBytes = fread(buffer + LZ4F_HEADER_SIZE_MIN, 1, hSize - LZ4F_HEADER_SIZE_MIN, finput);
1454                         if (!readBytes || ferror(finput)) EXM_THROW(72, "Error reading %s", input_filename);
1455                     }
1456                     /* Create decompression context */
1457                     {   LZ4F_dctx* dctx;
1458                         unsigned isError = LZ4F_isError(LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION));
1459                         if (!isError) {
1460                             isError = LZ4F_isError(LZ4F_getFrameInfo(dctx, &frameInfo.lz4FrameInfo, buffer, &hSize));
1461                             LZ4F_freeDecompressionContext(dctx);
1462                             if (!isError) {
1463                                 if ((cfinfo->frameSummary.lz4FrameInfo.blockSizeID != frameInfo.lz4FrameInfo.blockSizeID ||
1464                                         cfinfo->frameSummary.lz4FrameInfo.blockMode != frameInfo.lz4FrameInfo.blockMode)
1465                                         && cfinfo->frameCount != 0)
1466                                     cfinfo->eqBlockTypes = 0;
1467                                 {   const unsigned long long totalBlocksSize = LZ4IO_skipBlocksData(finput,
1468                                             frameInfo.lz4FrameInfo.blockChecksumFlag,
1469                                             frameInfo.lz4FrameInfo.contentChecksumFlag);
1470                                     if (totalBlocksSize) {
1471                                         char bTypeBuffer[5];
1472                                         LZ4IO_blockTypeID(frameInfo.lz4FrameInfo.blockSizeID, frameInfo.lz4FrameInfo.blockMode, bTypeBuffer);
1473                                         DISPLAYLEVEL(3, "    %6llu %14s %5s %8s",
1474                                                      cfinfo->frameCount + 1,
1475                                                      LZ4IO_frameTypeNames[frameInfo.frameType],
1476                                                      bTypeBuffer,
1477                                                      frameInfo.lz4FrameInfo.contentChecksumFlag ? "XXH32" : "-");
1478                                         if (frameInfo.lz4FrameInfo.contentSize) {
1479                                             {   double const ratio = (double)(totalBlocksSize + hSize) / frameInfo.lz4FrameInfo.contentSize * 100;
1480                                                 DISPLAYLEVEL(3, " %20llu %20llu %9.2f%%\n",
1481                                                              totalBlocksSize + hSize,
1482                                                              frameInfo.lz4FrameInfo.contentSize,
1483                                                              ratio);
1484                                             }
1485                                             /* Now we've consumed frameInfo we can use it to store the total contentSize */
1486                                             frameInfo.lz4FrameInfo.contentSize += cfinfo->frameSummary.lz4FrameInfo.contentSize;
1487                                         }
1488                                         else {
1489                                             DISPLAYLEVEL(3, " %20llu %20s %9s \n", totalBlocksSize + hSize, "-", "-");
1490                                             cfinfo->allContentSize = 0;
1491                                         }
1492                                         result = LZ4IO_LZ4F_OK;
1493                                     }
1494                                 }
1495                             }
1496                         }
1497                     }
1498                 }
1499             }
1500             break;
1501         case LEGACY_MAGICNUMBER:
1502             frameInfo.frameType = legacyFrame;
1503             if (cfinfo->frameSummary.frameType != legacyFrame && cfinfo->frameCount != 0) cfinfo->eqFrameTypes = 0;
1504             cfinfo->eqBlockTypes = 0;
1505             cfinfo->allContentSize = 0;
1506             {   const unsigned long long totalBlocksSize = LZ4IO_skipLegacyBlocksData(finput);
1507                 if (totalBlocksSize) {
1508                     DISPLAYLEVEL(3, "    %6llu %14s %5s %8s %20llu %20s %9s\n",
1509                                  cfinfo->frameCount + 1,
1510                                  LZ4IO_frameTypeNames[frameInfo.frameType],
1511                                  "-", "-",
1512                                  totalBlocksSize + 4,
1513                                  "-", "-");
1514                     result = LZ4IO_LZ4F_OK;
1515                 }
1516             }
1517             break;
1518         case LZ4IO_SKIPPABLE0:
1519             frameInfo.frameType = skippableFrame;
1520             if (cfinfo->frameSummary.frameType != skippableFrame && cfinfo->frameCount != 0) cfinfo->eqFrameTypes = 0;
1521             cfinfo->eqBlockTypes = 0;
1522             cfinfo->allContentSize = 0;
1523             {   nbReadBytes = fread(buffer, 1, 4, finput);
1524                 if (nbReadBytes != 4)
1525                     EXM_THROW(42, "Stream error : skippable size unreadable");
1526             }
1527             {   unsigned const size = LZ4IO_readLE32(buffer);
1528                 int const errorNb = fseek_u32(finput, size, SEEK_CUR);
1529                 if (errorNb != 0)
1530                     EXM_THROW(43, "Stream error : cannot skip skippable area");
1531                 DISPLAYLEVEL(3, "    %6llu %14s %5s %8s %20u %20s %9s\n",
1532                              cfinfo->frameCount + 1,
1533                              "SkippableFrame",
1534                              "-", "-", size + 8, "-", "-");
1535 
1536                 result = LZ4IO_LZ4F_OK;
1537             }
1538             break;
1539         default:
1540             {   long int const position = ftell(finput);  /* only works for files < 2 GB */
1541                 DISPLAYLEVEL(3, "Stream followed by undecodable data ");
1542                 if (position != -1L)
1543                     DISPLAYLEVEL(3, "at position %i ", (int)position);
1544                 DISPLAYLEVEL(3, "\n");
1545             }
1546         break;
1547         }
1548         if (result != LZ4IO_LZ4F_OK) {
1549             break;
1550         }
1551         cfinfo->frameSummary = frameInfo;
1552         cfinfo->frameCount++;
1553     }
1554     fclose(finput);
1555     return result;
1556 }
1557 
1558 
LZ4IO_displayCompressedFilesInfo(const char ** inFileNames,size_t ifnIdx)1559 int LZ4IO_displayCompressedFilesInfo(const char** inFileNames, size_t ifnIdx)
1560 {
1561     int result = 0;
1562     size_t idx = 0;
1563     if (g_displayLevel < 3) {
1564         DISPLAY("%10s %14s %5s %11s %13s %9s   %s\n",
1565                 "Frames", "Type", "Block", "Compressed", "Uncompressed", "Ratio", "Filename");
1566     }
1567     for (; idx < ifnIdx; idx++) {
1568         /* Get file info */
1569         LZ4IO_cFileInfo_t cfinfo = LZ4IO_INIT_CFILEINFO;
1570         cfinfo.fileName = LZ4IO_baseName(inFileNames[idx]);
1571         if (!UTIL_isRegFile(inFileNames[idx])) {
1572             DISPLAYLEVEL(1, "lz4: %s is not a regular file \n", inFileNames[idx]);
1573             return 0;
1574         }
1575         DISPLAYLEVEL(3, "%s(%llu/%llu)\n", cfinfo.fileName, (unsigned long long)idx + 1, (unsigned  long long)ifnIdx);
1576         DISPLAYLEVEL(3, "    %6s %14s %5s %8s %20s %20s %9s\n",
1577                      "Frame", "Type", "Block", "Checksum", "Compressed", "Uncompressed", "Ratio")
1578         {   LZ4IO_infoResult const op_result = LZ4IO_getCompressedFileInfo(&cfinfo, inFileNames[idx]);
1579             if (op_result != LZ4IO_LZ4F_OK) {
1580                 assert(op_result == LZ4IO_format_not_known);
1581                 DISPLAYLEVEL(1, "lz4: %s: File format not recognized \n", inFileNames[idx]);
1582                 return 0;
1583             }
1584         }
1585         DISPLAYLEVEL(3, "\n");
1586         if (g_displayLevel < 3) {
1587             /* Display Summary */
1588             {   char buffers[3][10];
1589                 DISPLAY("%10llu %14s %5s %11s %13s ",
1590                         cfinfo.frameCount,
1591                         cfinfo.eqFrameTypes ? LZ4IO_frameTypeNames[cfinfo.frameSummary.frameType] : "-" ,
1592                         cfinfo.eqBlockTypes ? LZ4IO_blockTypeID(cfinfo.frameSummary.lz4FrameInfo.blockSizeID,
1593                                                                 cfinfo.frameSummary.lz4FrameInfo.blockMode, buffers[0]) : "-",
1594                         LZ4IO_toHuman((long double)cfinfo.fileSize, buffers[1]),
1595                         cfinfo.allContentSize ? LZ4IO_toHuman((long double)cfinfo.frameSummary.lz4FrameInfo.contentSize, buffers[2]) : "-");
1596                 if (cfinfo.allContentSize) {
1597                     double const ratio = (double)cfinfo.fileSize / cfinfo.frameSummary.lz4FrameInfo.contentSize * 100;
1598                     DISPLAY("%9.2f%%  %s \n", ratio, cfinfo.fileName);
1599                 } else {
1600                     DISPLAY("%9s   %s\n",
1601                             "-",
1602                             cfinfo.fileName);
1603                 }
1604             }
1605         }
1606     }
1607 
1608     return result;
1609 }
1610