]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/zstd/programs/zstdcli.c
Import zstandard 1.3.1
[FreeBSD/FreeBSD.git] / contrib / zstd / programs / zstdcli.c
1 /*
2  * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
3  * All rights reserved.
4  *
5  * This source code is licensed under both the BSD-style license (found in the
6  * LICENSE file in the root directory of this source tree) and the GPLv2 (found
7  * in the COPYING file in the root directory of this source tree).
8  */
9
10
11 /*-************************************
12 *  Tuning parameters
13 **************************************/
14 #ifndef ZSTDCLI_CLEVEL_DEFAULT
15 #  define ZSTDCLI_CLEVEL_DEFAULT 3
16 #endif
17
18 #ifndef ZSTDCLI_CLEVEL_MAX
19 #  define ZSTDCLI_CLEVEL_MAX 19   /* without using --ultra */
20 #endif
21
22
23
24 /*-************************************
25 *  Dependencies
26 **************************************/
27 #include "platform.h" /* IS_CONSOLE, PLATFORM_POSIX_VERSION */
28 #include "util.h"     /* UTIL_HAS_CREATEFILELIST, UTIL_createFileList */
29 #include <stdio.h>    /* fprintf(), stdin, stdout, stderr */
30 #include <string.h>   /* strcmp, strlen */
31 #include <errno.h>    /* errno */
32 #include "fileio.h"   /* stdinmark, stdoutmark, ZSTD_EXTENSION */
33 #ifndef ZSTD_NOBENCH
34 #  include "bench.h"  /* BMK_benchFiles, BMK_SetNbSeconds */
35 #endif
36 #ifndef ZSTD_NODICT
37 #  include "dibio.h"  /* ZDICT_cover_params_t, DiB_trainFromFiles() */
38 #endif
39 #define ZSTD_STATIC_LINKING_ONLY   /* ZSTD_maxCLevel */
40 #include "zstd.h"     /* ZSTD_VERSION_STRING */
41
42
43 /*-************************************
44 *  Constants
45 **************************************/
46 #define COMPRESSOR_NAME "zstd command line interface"
47 #ifndef ZSTD_VERSION
48 #  define ZSTD_VERSION "v" ZSTD_VERSION_STRING
49 #endif
50 #define AUTHOR "Yann Collet"
51 #define WELCOME_MESSAGE "*** %s %i-bits %s, by %s ***\n", COMPRESSOR_NAME, (int)(sizeof(size_t)*8), ZSTD_VERSION, AUTHOR
52
53 #define ZSTD_ZSTDMT "zstdmt"
54 #define ZSTD_UNZSTD "unzstd"
55 #define ZSTD_CAT "zstdcat"
56 #define ZSTD_GZ "gzip"
57 #define ZSTD_GUNZIP "gunzip"
58 #define ZSTD_GZCAT "gzcat"
59 #define ZSTD_LZMA "lzma"
60 #define ZSTD_UNLZMA "unlzma"
61 #define ZSTD_XZ "xz"
62 #define ZSTD_UNXZ "unxz"
63
64 #define KB *(1 <<10)
65 #define MB *(1 <<20)
66 #define GB *(1U<<30)
67
68 #define DISPLAY_LEVEL_DEFAULT 2
69
70 static const char*    g_defaultDictName = "dictionary";
71 static const unsigned g_defaultMaxDictSize = 110 KB;
72 static const int      g_defaultDictCLevel = 3;
73 static const unsigned g_defaultSelectivityLevel = 9;
74 #define OVERLAP_LOG_DEFAULT 9999
75 static U32 g_overlapLog = OVERLAP_LOG_DEFAULT;
76
77
78 /*-************************************
79 *  Display Macros
80 **************************************/
81 #define DISPLAY(...)         fprintf(g_displayOut, __VA_ARGS__)
82 #define DISPLAYLEVEL(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } }
83 static int g_displayLevel = DISPLAY_LEVEL_DEFAULT;   /* 0 : no display,  1: errors,  2 : + result + interaction + warnings,  3 : + progression,  4 : + information */
84 static FILE* g_displayOut;
85
86
87 /*-************************************
88 *  Command Line
89 **************************************/
90 static int usage(const char* programName)
91 {
92     DISPLAY( "Usage : \n");
93     DISPLAY( "      %s [args] [FILE(s)] [-o file] \n", programName);
94     DISPLAY( "\n");
95     DISPLAY( "FILE    : a filename \n");
96     DISPLAY( "          with no FILE, or when FILE is - , read standard input\n");
97     DISPLAY( "Arguments : \n");
98 #ifndef ZSTD_NOCOMPRESS
99     DISPLAY( " -#     : # compression level (1-%d, default:%d) \n", ZSTDCLI_CLEVEL_MAX, ZSTDCLI_CLEVEL_DEFAULT);
100 #endif
101 #ifndef ZSTD_NODECOMPRESS
102     DISPLAY( " -d     : decompression \n");
103 #endif
104     DISPLAY( " -D file: use `file` as Dictionary \n");
105     DISPLAY( " -o file: result stored into `file` (only if 1 input file) \n");
106     DISPLAY( " -f     : overwrite output without prompting and (de)compress links \n");
107     DISPLAY( "--rm    : remove source file(s) after successful de/compression \n");
108     DISPLAY( " -k     : preserve source file(s) (default) \n");
109     DISPLAY( " -h/-H  : display help/long help and exit \n");
110     return 0;
111 }
112
113 static int usage_advanced(const char* programName)
114 {
115     DISPLAY(WELCOME_MESSAGE);
116     usage(programName);
117     DISPLAY( "\n");
118     DISPLAY( "Advanced arguments : \n");
119     DISPLAY( " -V     : display Version number and exit \n");
120     DISPLAY( " -v     : verbose mode; specify multiple times to increase verbosity\n");
121     DISPLAY( " -q     : suppress warnings; specify twice to suppress errors too\n");
122     DISPLAY( " -c     : force write to standard output, even if it is the console\n");
123     DISPLAY( " -l     : print information about zstd compressed files \n");
124 #ifndef ZSTD_NOCOMPRESS
125     DISPLAY( "--ultra : enable levels beyond %i, up to %i (requires more memory)\n", ZSTDCLI_CLEVEL_MAX, ZSTD_maxCLevel());
126 #ifdef ZSTD_MULTITHREAD
127     DISPLAY( " -T#    : use # threads for compression (default:1) \n");
128     DISPLAY( " -B#    : select size of each job (default:0==automatic) \n");
129 #endif
130     DISPLAY( "--no-dictID : don't write dictID into header (dictionary compression)\n");
131     DISPLAY( "--[no-]check : integrity check (default:enabled) \n");
132 #endif
133 #ifdef UTIL_HAS_CREATEFILELIST
134     DISPLAY( " -r     : operate recursively on directories \n");
135 #endif
136 #ifdef ZSTD_GZCOMPRESS
137     DISPLAY( "--format=gzip : compress files to the .gz format \n");
138 #endif
139 #ifdef ZSTD_LZMACOMPRESS
140     DISPLAY( "--format=xz : compress files to the .xz format \n");
141     DISPLAY( "--format=lzma : compress files to the .lzma format \n");
142 #endif
143 #ifdef ZSTD_LZ4COMPRESS
144     DISPLAY( "--format=lz4 : compress files to the .lz4 format \n");
145 #endif
146 #ifndef ZSTD_NODECOMPRESS
147     DISPLAY( "--test  : test compressed file integrity \n");
148 #if ZSTD_SPARSE_DEFAULT
149     DISPLAY( "--[no-]sparse : sparse mode (default:enabled on file, disabled on stdout)\n");
150 #else
151     DISPLAY( "--[no-]sparse : sparse mode (default:disabled)\n");
152 #endif
153 #endif
154     DISPLAY( " -M#    : Set a memory usage limit for decompression \n");
155     DISPLAY( "--      : All arguments after \"--\" are treated as files \n");
156 #ifndef ZSTD_NODICT
157     DISPLAY( "\n");
158     DISPLAY( "Dictionary builder : \n");
159     DISPLAY( "--train ## : create a dictionary from a training set of files \n");
160     DISPLAY( "--train-cover[=k=#,d=#,steps=#] : use the cover algorithm with optional args\n");
161     DISPLAY( "--train-legacy[=s=#] : use the legacy algorithm with selectivity (default: %u)\n", g_defaultSelectivityLevel);
162     DISPLAY( " -o file : `file` is dictionary name (default: %s) \n", g_defaultDictName);
163     DISPLAY( "--maxdict=# : limit dictionary to specified size (default : %u) \n", g_defaultMaxDictSize);
164     DISPLAY( "--dictID=# : force dictionary ID to specified value (default: random)\n");
165 #endif
166 #ifndef ZSTD_NOBENCH
167     DISPLAY( "\n");
168     DISPLAY( "Benchmark arguments : \n");
169     DISPLAY( " -b#    : benchmark file(s), using # compression level (default : 1) \n");
170     DISPLAY( " -e#    : test all compression levels from -bX to # (default: 1)\n");
171     DISPLAY( " -i#    : minimum evaluation time in seconds (default : 3s) \n");
172     DISPLAY( " -B#    : cut file into independent blocks of size # (default: no block)\n");
173     DISPLAY( "--priority=rt : set process priority to real-time \n");
174 #endif
175     return 0;
176 }
177
178 static int badusage(const char* programName)
179 {
180     DISPLAYLEVEL(1, "Incorrect parameters\n");
181     if (g_displayLevel >= 2) usage(programName);
182     return 1;
183 }
184
185 static void waitEnter(void)
186 {
187     int unused;
188     DISPLAY("Press enter to continue...\n");
189     unused = getchar();
190     (void)unused;
191 }
192
193 static const char* lastNameFromPath(const char* path)
194 {
195     const char* name = path;
196     if (strrchr(name, '/')) name = strrchr(name, '/') + 1;
197     if (strrchr(name, '\\')) name = strrchr(name, '\\') + 1; /* windows */
198     return name;
199 }
200
201 /*! exeNameMatch() :
202     @return : a non-zero value if exeName matches test, excluding the extension
203    */
204 static int exeNameMatch(const char* exeName, const char* test)
205 {
206     return !strncmp(exeName, test, strlen(test)) &&
207         (exeName[strlen(test)] == '\0' || exeName[strlen(test)] == '.');
208 }
209
210 /*! readU32FromChar() :
211     @return : unsigned integer value read from input in `char` format
212     allows and interprets K, KB, KiB, M, MB and MiB suffix.
213     Will also modify `*stringPtr`, advancing it to position where it stopped reading.
214     Note : function result can overflow if digit string > MAX_UINT */
215 static unsigned readU32FromChar(const char** stringPtr)
216 {
217     unsigned result = 0;
218     while ((**stringPtr >='0') && (**stringPtr <='9'))
219         result *= 10, result += **stringPtr - '0', (*stringPtr)++ ;
220     if ((**stringPtr=='K') || (**stringPtr=='M')) {
221         result <<= 10;
222         if (**stringPtr=='M') result <<= 10;
223         (*stringPtr)++ ;
224         if (**stringPtr=='i') (*stringPtr)++;
225         if (**stringPtr=='B') (*stringPtr)++;
226     }
227     return result;
228 }
229
230 /** longCommandWArg() :
231  *  check if *stringPtr is the same as longCommand.
232  *  If yes, @return 1 and advances *stringPtr to the position which immediately follows longCommand.
233  *  @return 0 and doesn't modify *stringPtr otherwise.
234  */
235 static unsigned longCommandWArg(const char** stringPtr, const char* longCommand)
236 {
237     size_t const comSize = strlen(longCommand);
238     int const result = !strncmp(*stringPtr, longCommand, comSize);
239     if (result) *stringPtr += comSize;
240     return result;
241 }
242
243
244 #ifndef ZSTD_NODICT
245 /**
246  * parseCoverParameters() :
247  * reads cover parameters from *stringPtr (e.g. "--train-cover=k=48,d=8,steps=32") into *params
248  * @return 1 means that cover parameters were correct
249  * @return 0 in case of malformed parameters
250  */
251 static unsigned parseCoverParameters(const char* stringPtr, ZDICT_cover_params_t* params)
252 {
253     memset(params, 0, sizeof(*params));
254     for (; ;) {
255         if (longCommandWArg(&stringPtr, "k=")) { params->k = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
256         if (longCommandWArg(&stringPtr, "d=")) { params->d = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
257         if (longCommandWArg(&stringPtr, "steps=")) { params->steps = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
258         return 0;
259     }
260     if (stringPtr[0] != 0) return 0;
261     DISPLAYLEVEL(4, "cover: k=%u\nd=%u\nsteps=%u\n", params->k, params->d, params->steps);
262     return 1;
263 }
264
265 /**
266  * parseLegacyParameters() :
267  * reads legacy dictioanry builter parameters from *stringPtr (e.g. "--train-legacy=selectivity=8") into *selectivity
268  * @return 1 means that legacy dictionary builder parameters were correct
269  * @return 0 in case of malformed parameters
270  */
271 static unsigned parseLegacyParameters(const char* stringPtr, unsigned* selectivity)
272 {
273     if (!longCommandWArg(&stringPtr, "s=") && !longCommandWArg(&stringPtr, "selectivity=")) { return 0; }
274     *selectivity = readU32FromChar(&stringPtr);
275     if (stringPtr[0] != 0) return 0;
276     DISPLAYLEVEL(4, "legacy: selectivity=%u\n", *selectivity);
277     return 1;
278 }
279
280 static ZDICT_cover_params_t defaultCoverParams(void)
281 {
282     ZDICT_cover_params_t params;
283     memset(&params, 0, sizeof(params));
284     params.d = 8;
285     params.steps = 4;
286     return params;
287 }
288 #endif
289
290
291 /** parseCompressionParameters() :
292  *  reads compression parameters from *stringPtr (e.g. "--zstd=wlog=23,clog=23,hlog=22,slog=6,slen=3,tlen=48,strat=6") into *params
293  *  @return 1 means that compression parameters were correct
294  *  @return 0 in case of malformed parameters
295  */
296 static unsigned parseCompressionParameters(const char* stringPtr, ZSTD_compressionParameters* params)
297 {
298     for ( ; ;) {
299         if (longCommandWArg(&stringPtr, "windowLog=") || longCommandWArg(&stringPtr, "wlog=")) { params->windowLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
300         if (longCommandWArg(&stringPtr, "chainLog=") || longCommandWArg(&stringPtr, "clog=")) { params->chainLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
301         if (longCommandWArg(&stringPtr, "hashLog=") || longCommandWArg(&stringPtr, "hlog=")) { params->hashLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
302         if (longCommandWArg(&stringPtr, "searchLog=") || longCommandWArg(&stringPtr, "slog=")) { params->searchLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
303         if (longCommandWArg(&stringPtr, "searchLength=") || longCommandWArg(&stringPtr, "slen=")) { params->searchLength = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
304         if (longCommandWArg(&stringPtr, "targetLength=") || longCommandWArg(&stringPtr, "tlen=")) { params->targetLength = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
305         if (longCommandWArg(&stringPtr, "strategy=") || longCommandWArg(&stringPtr, "strat=")) { params->strategy = (ZSTD_strategy)(readU32FromChar(&stringPtr)); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
306         if (longCommandWArg(&stringPtr, "overlapLog=") || longCommandWArg(&stringPtr, "ovlog=")) { g_overlapLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
307         return 0;
308     }
309
310     if (stringPtr[0] != 0) return 0; /* check the end of string */
311     DISPLAYLEVEL(4, "windowLog=%d\nchainLog=%d\nhashLog=%d\nsearchLog=%d\n", params->windowLog, params->chainLog, params->hashLog, params->searchLog);
312     DISPLAYLEVEL(4, "searchLength=%d\ntargetLength=%d\nstrategy=%d\n", params->searchLength, params->targetLength, params->strategy);
313     return 1;
314 }
315
316 static void printVersion(void)
317 {
318     DISPLAY(WELCOME_MESSAGE);
319     /* format support */
320     DISPLAYLEVEL(3, "*** supports: zstd");
321 #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>0) && (ZSTD_LEGACY_SUPPORT<8)
322     DISPLAYLEVEL(3, ", zstd legacy v0.%d+", ZSTD_LEGACY_SUPPORT);
323 #endif
324 #ifdef ZSTD_GZCOMPRESS
325     DISPLAYLEVEL(3, ", gzip");
326 #endif
327 #ifdef ZSTD_LZ4COMPRESS
328     DISPLAYLEVEL(3, ", lz4");
329 #endif
330 #ifdef ZSTD_LZMACOMPRESS
331     DISPLAYLEVEL(3, ", lzma, xz ");
332 #endif
333     DISPLAYLEVEL(3, "\n");
334     /* posix support */
335 #ifdef _POSIX_C_SOURCE
336     DISPLAYLEVEL(4, "_POSIX_C_SOURCE defined: %ldL\n", (long) _POSIX_C_SOURCE);
337 #endif
338 #ifdef _POSIX_VERSION
339     DISPLAYLEVEL(4, "_POSIX_VERSION defined: %ldL \n", (long) _POSIX_VERSION);
340 #endif
341 #ifdef PLATFORM_POSIX_VERSION
342     DISPLAYLEVEL(4, "PLATFORM_POSIX_VERSION defined: %ldL\n", (long) PLATFORM_POSIX_VERSION);
343 #endif
344 }
345
346 typedef enum { zom_compress, zom_decompress, zom_test, zom_bench, zom_train, zom_list } zstd_operation_mode;
347
348 #define CLEAN_RETURN(i) { operationResult = (i); goto _end; }
349
350 int main(int argCount, const char* argv[])
351 {
352     int argNb,
353         forceStdout=0,
354         followLinks=0,
355         main_pause=0,
356         nextEntryIsDictionary=0,
357         operationResult=0,
358         nextArgumentIsOutFileName=0,
359         nextArgumentIsMaxDict=0,
360         nextArgumentIsDictID=0,
361         nextArgumentsAreFiles=0,
362         ultra=0,
363         lastCommand = 0,
364         nbThreads = 1,
365         setRealTimePrio = 0;
366     unsigned bench_nbSeconds = 3;   /* would be better if this value was synchronized from bench */
367     size_t blockSize = 0;
368     zstd_operation_mode operation = zom_compress;
369     ZSTD_compressionParameters compressionParams;
370     int cLevel = ZSTDCLI_CLEVEL_DEFAULT;
371     int cLevelLast = 1;
372     unsigned recursive = 0;
373     unsigned memLimit = 0;
374     const char** filenameTable = (const char**)malloc(argCount * sizeof(const char*));   /* argCount >= 1 */
375     unsigned filenameIdx = 0;
376     const char* programName = argv[0];
377     const char* outFileName = NULL;
378     const char* dictFileName = NULL;
379     const char* suffix = ZSTD_EXTENSION;
380     unsigned maxDictSize = g_defaultMaxDictSize;
381     unsigned dictID = 0;
382     int dictCLevel = g_defaultDictCLevel;
383     unsigned dictSelect = g_defaultSelectivityLevel;
384 #ifdef UTIL_HAS_CREATEFILELIST
385     const char** extendedFileList = NULL;
386     char* fileNamesBuf = NULL;
387     unsigned fileNamesNb;
388 #endif
389 #ifndef ZSTD_NODICT
390     ZDICT_cover_params_t coverParams = defaultCoverParams();
391     int cover = 1;
392 #endif
393
394     /* init */
395     (void)recursive; (void)cLevelLast;    /* not used when ZSTD_NOBENCH set */
396     (void)dictCLevel; (void)dictSelect; (void)dictID;  (void)maxDictSize; /* not used when ZSTD_NODICT set */
397     (void)ultra; (void)cLevel; /* not used when ZSTD_NOCOMPRESS set */
398     (void)memLimit;   /* not used when ZSTD_NODECOMPRESS set */
399     if (filenameTable==NULL) { DISPLAY("zstd: %s \n", strerror(errno)); exit(1); }
400     filenameTable[0] = stdinmark;
401     g_displayOut = stderr;
402
403     programName = lastNameFromPath(programName);
404
405     /* preset behaviors */
406     if (exeNameMatch(programName, ZSTD_ZSTDMT)) nbThreads=0;
407     if (exeNameMatch(programName, ZSTD_UNZSTD)) operation=zom_decompress;
408     if (exeNameMatch(programName, ZSTD_CAT)) { operation=zom_decompress; forceStdout=1; FIO_overwriteMode(); outFileName=stdoutmark; g_displayLevel=1; }
409     if (exeNameMatch(programName, ZSTD_GZ)) { suffix = GZ_EXTENSION; FIO_setCompressionType(FIO_gzipCompression); FIO_setRemoveSrcFile(1); }    /* behave like gzip */
410     if (exeNameMatch(programName, ZSTD_GUNZIP)) { operation=zom_decompress; FIO_setRemoveSrcFile(1); }                                          /* behave like gunzip */
411     if (exeNameMatch(programName, ZSTD_GZCAT)) { operation=zom_decompress; forceStdout=1; FIO_overwriteMode(); outFileName=stdoutmark; g_displayLevel=1; }  /* behave like gzcat */
412     if (exeNameMatch(programName, ZSTD_LZMA)) { suffix = LZMA_EXTENSION; FIO_setCompressionType(FIO_lzmaCompression); FIO_setRemoveSrcFile(1); }    /* behave like lzma */
413     if (exeNameMatch(programName, ZSTD_UNLZMA)) { operation=zom_decompress; FIO_setCompressionType(FIO_lzmaCompression); FIO_setRemoveSrcFile(1); }    /* behave like unlzma */
414     if (exeNameMatch(programName, ZSTD_XZ)) { suffix = XZ_EXTENSION; FIO_setCompressionType(FIO_xzCompression); FIO_setRemoveSrcFile(1); }    /* behave like xz */
415     if (exeNameMatch(programName, ZSTD_UNXZ)) { operation=zom_decompress; FIO_setCompressionType(FIO_xzCompression); FIO_setRemoveSrcFile(1); }    /* behave like unxz */
416     memset(&compressionParams, 0, sizeof(compressionParams));
417
418     /* command switches */
419     for (argNb=1; argNb<argCount; argNb++) {
420         const char* argument = argv[argNb];
421         if(!argument) continue;   /* Protection if argument empty */
422
423         if (nextArgumentsAreFiles==0) {
424             /* "-" means stdin/stdout */
425             if (!strcmp(argument, "-")){
426                 if (!filenameIdx) {
427                     filenameIdx=1, filenameTable[0]=stdinmark;
428                     outFileName=stdoutmark;
429                     g_displayLevel-=(g_displayLevel==2);
430                     continue;
431             }   }
432
433             /* Decode commands (note : aggregated commands are allowed) */
434             if (argument[0]=='-') {
435
436                 if (argument[1]=='-') {
437                     /* long commands (--long-word) */
438                     if (!strcmp(argument, "--")) { nextArgumentsAreFiles=1; continue; }   /* only file names allowed from now on */
439                     if (!strcmp(argument, "--list")) { operation=zom_list; continue; }
440                     if (!strcmp(argument, "--compress")) { operation=zom_compress; continue; }
441                     if (!strcmp(argument, "--decompress")) { operation=zom_decompress; continue; }
442                     if (!strcmp(argument, "--uncompress")) { operation=zom_decompress; continue; }
443                     if (!strcmp(argument, "--force")) { FIO_overwriteMode(); forceStdout=1; followLinks=1; continue; }
444                     if (!strcmp(argument, "--version")) { g_displayOut=stdout; DISPLAY(WELCOME_MESSAGE); CLEAN_RETURN(0); }
445                     if (!strcmp(argument, "--help")) { g_displayOut=stdout; CLEAN_RETURN(usage_advanced(programName)); }
446                     if (!strcmp(argument, "--verbose")) { g_displayLevel++; continue; }
447                     if (!strcmp(argument, "--quiet")) { g_displayLevel--; continue; }
448                     if (!strcmp(argument, "--stdout")) { forceStdout=1; outFileName=stdoutmark; g_displayLevel-=(g_displayLevel==2); continue; }
449                     if (!strcmp(argument, "--ultra")) { ultra=1; continue; }
450                     if (!strcmp(argument, "--check")) { FIO_setChecksumFlag(2); continue; }
451                     if (!strcmp(argument, "--no-check")) { FIO_setChecksumFlag(0); continue; }
452                     if (!strcmp(argument, "--sparse")) { FIO_setSparseWrite(2); continue; }
453                     if (!strcmp(argument, "--no-sparse")) { FIO_setSparseWrite(0); continue; }
454                     if (!strcmp(argument, "--test")) { operation=zom_test; continue; }
455                     if (!strcmp(argument, "--train")) { operation=zom_train; outFileName=g_defaultDictName; continue; }
456                     if (!strcmp(argument, "--maxdict")) { nextArgumentIsMaxDict=1; lastCommand=1; continue; }  /* kept available for compatibility with old syntax ; will be removed one day */
457                     if (!strcmp(argument, "--dictID")) { nextArgumentIsDictID=1; lastCommand=1; continue; }  /* kept available for compatibility with old syntax ; will be removed one day */
458                     if (!strcmp(argument, "--no-dictID")) { FIO_setDictIDFlag(0); continue; }
459                     if (!strcmp(argument, "--keep")) { FIO_setRemoveSrcFile(0); continue; }
460                     if (!strcmp(argument, "--rm")) { FIO_setRemoveSrcFile(1); continue; }
461                     if (!strcmp(argument, "--priority=rt")) { setRealTimePrio = 1; continue; }
462 #ifdef ZSTD_GZCOMPRESS
463                     if (!strcmp(argument, "--format=gzip")) { suffix = GZ_EXTENSION; FIO_setCompressionType(FIO_gzipCompression); continue; }
464 #endif
465 #ifdef ZSTD_LZMACOMPRESS
466                     if (!strcmp(argument, "--format=lzma")) { suffix = LZMA_EXTENSION; FIO_setCompressionType(FIO_lzmaCompression);  continue; }
467                     if (!strcmp(argument, "--format=xz")) { suffix = XZ_EXTENSION; FIO_setCompressionType(FIO_xzCompression);  continue; }
468 #endif
469 #ifdef ZSTD_LZ4COMPRESS
470                     if (!strcmp(argument, "--format=lz4")) { suffix = LZ4_EXTENSION; FIO_setCompressionType(FIO_lz4Compression);  continue; }
471 #endif
472
473                     /* long commands with arguments */
474 #ifndef ZSTD_NODICT
475                     if (longCommandWArg(&argument, "--train-cover")) {
476                       operation = zom_train;
477                       outFileName = g_defaultDictName;
478                       cover = 1;
479                       /* Allow optional arguments following an = */
480                       if (*argument == 0) { memset(&coverParams, 0, sizeof(coverParams)); }
481                       else if (*argument++ != '=') { CLEAN_RETURN(badusage(programName)); }
482                       else if (!parseCoverParameters(argument, &coverParams)) { CLEAN_RETURN(badusage(programName)); }
483                       continue;
484                     }
485                     if (longCommandWArg(&argument, "--train-legacy")) {
486                       operation = zom_train;
487                       outFileName = g_defaultDictName;
488                       cover = 0;
489                       /* Allow optional arguments following an = */
490                       if (*argument == 0) { continue; }
491                       else if (*argument++ != '=') { CLEAN_RETURN(badusage(programName)); }
492                       else if (!parseLegacyParameters(argument, &dictSelect)) { CLEAN_RETURN(badusage(programName)); }
493                       continue;
494                     }
495 #endif
496                     if (longCommandWArg(&argument, "--threads=")) { nbThreads = readU32FromChar(&argument); continue; }
497                     if (longCommandWArg(&argument, "--memlimit=")) { memLimit = readU32FromChar(&argument); continue; }
498                     if (longCommandWArg(&argument, "--memory=")) { memLimit = readU32FromChar(&argument); continue; }
499                     if (longCommandWArg(&argument, "--memlimit-decompress=")) { memLimit = readU32FromChar(&argument); continue; }
500                     if (longCommandWArg(&argument, "--block-size=")) { blockSize = readU32FromChar(&argument); continue; }
501                     if (longCommandWArg(&argument, "--maxdict=")) { maxDictSize = readU32FromChar(&argument); continue; }
502                     if (longCommandWArg(&argument, "--dictID=")) { dictID = readU32FromChar(&argument); continue; }
503                     if (longCommandWArg(&argument, "--zstd=")) { if (!parseCompressionParameters(argument, &compressionParams)) CLEAN_RETURN(badusage(programName)); continue; }
504                     /* fall-through, will trigger bad_usage() later on */
505                 }
506
507                 argument++;
508                 while (argument[0]!=0) {
509                     if (lastCommand) {
510                         DISPLAY("error : command must be followed by argument \n");
511                         CLEAN_RETURN(1);
512                     }
513 #ifndef ZSTD_NOCOMPRESS
514                     /* compression Level */
515                     if ((*argument>='0') && (*argument<='9')) {
516                         dictCLevel = cLevel = readU32FromChar(&argument);
517                         continue;
518                     }
519 #endif
520
521                     switch(argument[0])
522                     {
523                         /* Display help */
524                     case 'V': g_displayOut=stdout; printVersion(); CLEAN_RETURN(0);   /* Version Only */
525                     case 'H':
526                     case 'h': g_displayOut=stdout; CLEAN_RETURN(usage_advanced(programName));
527
528                          /* Compress */
529                     case 'z': operation=zom_compress; argument++; break;
530
531                          /* Decoding */
532                     case 'd':
533 #ifndef ZSTD_NOBENCH
534                             if (operation==zom_bench) { BMK_setDecodeOnlyMode(1); argument++; break; }  /* benchmark decode (hidden option) */
535 #endif
536                             operation=zom_decompress; argument++; break;
537
538                         /* Force stdout, even if stdout==console */
539                     case 'c': forceStdout=1; outFileName=stdoutmark; argument++; break;
540
541                         /* Use file content as dictionary */
542                     case 'D': nextEntryIsDictionary = 1; lastCommand = 1; argument++; break;
543
544                         /* Overwrite */
545                     case 'f': FIO_overwriteMode(); forceStdout=1; followLinks=1; argument++; break;
546
547                         /* Verbose mode */
548                     case 'v': g_displayLevel++; argument++; break;
549
550                         /* Quiet mode */
551                     case 'q': g_displayLevel--; argument++; break;
552
553                         /* keep source file (default) */
554                     case 'k': FIO_setRemoveSrcFile(0); argument++; break;
555
556                         /* Checksum */
557                     case 'C': FIO_setChecksumFlag(2); argument++; break;
558
559                         /* test compressed file */
560                     case 't': operation=zom_test; argument++; break;
561
562                         /* destination file name */
563                     case 'o': nextArgumentIsOutFileName=1; lastCommand=1; argument++; break;
564
565                         /* limit decompression memory */
566                     case 'M':
567                         argument++;
568                         memLimit = readU32FromChar(&argument);
569                         break;
570                     case 'l': operation=zom_list; argument++; break;
571 #ifdef UTIL_HAS_CREATEFILELIST
572                         /* recursive */
573                     case 'r': recursive=1; argument++; break;
574 #endif
575
576 #ifndef ZSTD_NOBENCH
577                         /* Benchmark */
578                     case 'b':
579                         operation=zom_bench;
580                         argument++;
581                         break;
582
583                         /* range bench (benchmark only) */
584                     case 'e':
585                         /* compression Level */
586                         argument++;
587                         cLevelLast = readU32FromChar(&argument);
588                         break;
589
590                         /* Modify Nb Iterations (benchmark only) */
591                     case 'i':
592                         argument++;
593                         bench_nbSeconds = readU32FromChar(&argument);
594                         break;
595
596                         /* cut input into blocks (benchmark only) */
597                     case 'B':
598                         argument++;
599                         blockSize = readU32FromChar(&argument);
600                         break;
601
602 #endif   /* ZSTD_NOBENCH */
603
604                         /* nb of threads (hidden option) */
605                     case 'T':
606                         argument++;
607                         nbThreads = readU32FromChar(&argument);
608                         break;
609
610                         /* Dictionary Selection level */
611                     case 's':
612                         argument++;
613                         dictSelect = readU32FromChar(&argument);
614                         break;
615
616                         /* Pause at the end (-p) or set an additional param (-p#) (hidden option) */
617                     case 'p': argument++;
618 #ifndef ZSTD_NOBENCH
619                         if ((*argument>='0') && (*argument<='9')) {
620                             BMK_setAdditionalParam(readU32FromChar(&argument));
621                         } else
622 #endif
623                             main_pause=1;
624                         break;
625                         /* unknown command */
626                     default : CLEAN_RETURN(badusage(programName));
627                     }
628                 }
629                 continue;
630             }   /* if (argument[0]=='-') */
631
632             if (nextArgumentIsMaxDict) {  /* kept available for compatibility with old syntax ; will be removed one day */
633                 nextArgumentIsMaxDict = 0;
634                 lastCommand = 0;
635                 maxDictSize = readU32FromChar(&argument);
636                 continue;
637             }
638
639             if (nextArgumentIsDictID) {  /* kept available for compatibility with old syntax ; will be removed one day */
640                 nextArgumentIsDictID = 0;
641                 lastCommand = 0;
642                 dictID = readU32FromChar(&argument);
643                 continue;
644             }
645
646         }   /* if (nextArgumentIsAFile==0) */
647
648         if (nextEntryIsDictionary) {
649             nextEntryIsDictionary = 0;
650             lastCommand = 0;
651             dictFileName = argument;
652             continue;
653         }
654
655         if (nextArgumentIsOutFileName) {
656             nextArgumentIsOutFileName = 0;
657             lastCommand = 0;
658             outFileName = argument;
659             if (!strcmp(outFileName, "-")) outFileName = stdoutmark;
660             continue;
661         }
662
663         /* add filename to list */
664         filenameTable[filenameIdx++] = argument;
665     }
666
667     if (lastCommand) { /* forgotten argument */
668         DISPLAY("error : command must be followed by argument \n");
669         CLEAN_RETURN(1);
670     }
671
672     /* Welcome message (if verbose) */
673     DISPLAYLEVEL(3, WELCOME_MESSAGE);
674
675     if (nbThreads == 0) {
676         /* try to guess */
677         nbThreads = UTIL_countPhysicalCores();
678         DISPLAYLEVEL(3, "Note: %d physical core(s) detected \n", nbThreads);
679     }
680
681     g_utilDisplayLevel = g_displayLevel;
682     if (!followLinks) {
683         unsigned u;
684         for (u=0, fileNamesNb=0; u<filenameIdx; u++) {
685             if (UTIL_isLink(filenameTable[u])) {
686                 DISPLAYLEVEL(2, "Warning : %s is a symbolic link, ignoring\n", filenameTable[u]);
687             } else {
688                 filenameTable[fileNamesNb++] = filenameTable[u];
689             }
690         }
691         filenameIdx = fileNamesNb;
692     }
693 #ifdef UTIL_HAS_CREATEFILELIST
694     if (recursive) {  /* at this stage, filenameTable is a list of paths, which can contain both files and directories */
695         extendedFileList = UTIL_createFileList(filenameTable, filenameIdx, &fileNamesBuf, &fileNamesNb, followLinks);
696         if (extendedFileList) {
697             unsigned u;
698             for (u=0; u<fileNamesNb; u++) DISPLAYLEVEL(4, "%u %s\n", u, extendedFileList[u]);
699             free((void*)filenameTable);
700             filenameTable = extendedFileList;
701             filenameIdx = fileNamesNb;
702         }
703     }
704 #endif
705
706     if (operation == zom_list) {
707 #ifndef ZSTD_NODECOMPRESS
708         int const ret = FIO_listMultipleFiles(filenameIdx, filenameTable, g_displayLevel);
709         CLEAN_RETURN(ret);
710 #else
711         DISPLAY("file information is not supported \n");
712         CLEAN_RETURN(1);
713 #endif
714     }
715
716     /* Check if benchmark is selected */
717     if (operation==zom_bench) {
718 #ifndef ZSTD_NOBENCH
719         BMK_setNotificationLevel(g_displayLevel);
720         BMK_setBlockSize(blockSize);
721         BMK_setNbThreads(nbThreads);
722         BMK_setNbSeconds(bench_nbSeconds);
723         BMK_benchFiles(filenameTable, filenameIdx, dictFileName, cLevel, cLevelLast, &compressionParams, setRealTimePrio);
724 #endif
725         (void)bench_nbSeconds; (void)blockSize; (void)setRealTimePrio;
726         goto _end;
727     }
728
729     /* Check if dictionary builder is selected */
730     if (operation==zom_train) {
731 #ifndef ZSTD_NODICT
732         ZDICT_params_t zParams;
733         zParams.compressionLevel = dictCLevel;
734         zParams.notificationLevel = g_displayLevel;
735         zParams.dictID = dictID;
736         if (cover) {
737             int const optimize = !coverParams.k || !coverParams.d;
738             coverParams.nbThreads = nbThreads;
739             coverParams.zParams = zParams;
740             operationResult = DiB_trainFromFiles(outFileName, maxDictSize, filenameTable, filenameIdx, NULL, &coverParams, optimize);
741         } else {
742             ZDICT_legacy_params_t dictParams;
743             memset(&dictParams, 0, sizeof(dictParams));
744             dictParams.selectivityLevel = dictSelect;
745             dictParams.zParams = zParams;
746             operationResult = DiB_trainFromFiles(outFileName, maxDictSize, filenameTable, filenameIdx, &dictParams, NULL, 0);
747         }
748 #endif
749         goto _end;
750     }
751
752 #ifndef ZSTD_NODECOMPRESS
753     if (operation==zom_test) { outFileName=nulmark; FIO_setRemoveSrcFile(0); } /* test mode */
754 #endif
755
756     /* No input filename ==> use stdin and stdout */
757     filenameIdx += !filenameIdx;   /* filenameTable[0] is stdin by default */
758     if (!strcmp(filenameTable[0], stdinmark) && !outFileName) outFileName = stdoutmark;   /* when input is stdin, default output is stdout */
759
760     /* Check if input/output defined as console; trigger an error in this case */
761     if (!strcmp(filenameTable[0], stdinmark) && IS_CONSOLE(stdin) ) CLEAN_RETURN(badusage(programName));
762     if (outFileName && !strcmp(outFileName, stdoutmark) && IS_CONSOLE(stdout) && !strcmp(filenameTable[0], stdinmark) && !forceStdout && operation!=zom_decompress)
763         CLEAN_RETURN(badusage(programName));
764
765     /* user-selected output filename, only possible with a single file */
766     if (outFileName && strcmp(outFileName,stdoutmark) && strcmp(outFileName,nulmark) && (filenameIdx>1)) {
767         DISPLAY("Too many files (%u) on the command line. \n", filenameIdx);
768         CLEAN_RETURN(filenameIdx);
769     }
770
771 #ifndef ZSTD_NOCOMPRESS
772     /* check compression level limits */
773     {   int const maxCLevel = ultra ? ZSTD_maxCLevel() : ZSTDCLI_CLEVEL_MAX;
774         if (cLevel > maxCLevel) {
775             DISPLAYLEVEL(2, "Warning : compression level higher than max, reduced to %i \n", maxCLevel);
776             cLevel = maxCLevel;
777     }   }
778 #endif
779
780     /* No status message in pipe mode (stdin - stdout) or multi-files mode */
781     if (!strcmp(filenameTable[0], stdinmark) && outFileName && !strcmp(outFileName,stdoutmark) && (g_displayLevel==2)) g_displayLevel=1;
782     if ((filenameIdx>1) & (g_displayLevel==2)) g_displayLevel=1;
783
784     /* IO Stream/File */
785     FIO_setNotificationLevel(g_displayLevel);
786     if (operation==zom_compress) {
787 #ifndef ZSTD_NOCOMPRESS
788         FIO_setNbThreads(nbThreads);
789         FIO_setBlockSize((U32)blockSize);
790         if (g_overlapLog!=OVERLAP_LOG_DEFAULT) FIO_setOverlapLog(g_overlapLog);
791         if ((filenameIdx==1) && outFileName)
792           operationResult = FIO_compressFilename(outFileName, filenameTable[0], dictFileName, cLevel, &compressionParams);
793         else
794           operationResult = FIO_compressMultipleFilenames(filenameTable, filenameIdx, outFileName ? outFileName : suffix, dictFileName, cLevel, &compressionParams);
795 #else
796         (void)suffix;
797         DISPLAY("Compression not supported\n");
798 #endif
799     } else {  /* decompression or test */
800 #ifndef ZSTD_NODECOMPRESS
801         FIO_setMemLimit(memLimit);
802         if (filenameIdx==1 && outFileName)
803             operationResult = FIO_decompressFilename(outFileName, filenameTable[0], dictFileName);
804         else
805             operationResult = FIO_decompressMultipleFilenames(filenameTable, filenameIdx, outFileName ? outFileName : ZSTD_EXTENSION, dictFileName);
806 #else
807         DISPLAY("Decompression not supported\n");
808 #endif
809     }
810
811 _end:
812     if (main_pause) waitEnter();
813 #ifdef UTIL_HAS_CREATEFILELIST
814     if (extendedFileList)
815         UTIL_freeFileList(extendedFileList, fileNamesBuf);
816     else
817 #endif
818         free((void*)filenameTable);
819     return operationResult;
820 }