xref: /freebsd/sys/contrib/zstd/programs/zstdcli.c (revision d6eb98610fa65663bf0df4574b7cb2c5c4ffda71)
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  * You may select, at your option, one of the above-listed licenses.
9  */
10 
11 
12 /*-************************************
13 *  Tuning parameters
14 **************************************/
15 #ifndef ZSTDCLI_CLEVEL_DEFAULT
16 #  define ZSTDCLI_CLEVEL_DEFAULT 3
17 #endif
18 
19 #ifndef ZSTDCLI_CLEVEL_MAX
20 #  define ZSTDCLI_CLEVEL_MAX 19   /* without using --ultra */
21 #endif
22 
23 
24 
25 /*-************************************
26 *  Dependencies
27 **************************************/
28 #include "platform.h" /* IS_CONSOLE, PLATFORM_POSIX_VERSION */
29 #include "util.h"     /* UTIL_HAS_CREATEFILELIST, UTIL_createFileList */
30 #include <stdio.h>    /* fprintf(), stdin, stdout, stderr */
31 #include <stdlib.h>   /* getenv */
32 #include <string.h>   /* strcmp, strlen */
33 #include <errno.h>    /* errno */
34 #include "fileio.h"   /* stdinmark, stdoutmark, ZSTD_EXTENSION */
35 #ifndef ZSTD_NOBENCH
36 #  include "benchzstd.h"  /* BMK_benchFiles */
37 #endif
38 #ifndef ZSTD_NODICT
39 #  include "dibio.h"  /* ZDICT_cover_params_t, DiB_trainFromFiles() */
40 #endif
41 #define ZSTD_STATIC_LINKING_ONLY   /* ZSTD_minCLevel */
42 #include "zstd.h"     /* ZSTD_VERSION_STRING, ZSTD_maxCLevel */
43 
44 
45 /*-************************************
46 *  Constants
47 **************************************/
48 #define COMPRESSOR_NAME "zstd command line interface"
49 #ifndef ZSTD_VERSION
50 #  define ZSTD_VERSION "v" ZSTD_VERSION_STRING
51 #endif
52 #define AUTHOR "Yann Collet"
53 #define WELCOME_MESSAGE "*** %s %i-bits %s, by %s ***\n", COMPRESSOR_NAME, (int)(sizeof(size_t)*8), ZSTD_VERSION, AUTHOR
54 
55 #define ZSTD_ZSTDMT "zstdmt"
56 #define ZSTD_UNZSTD "unzstd"
57 #define ZSTD_CAT "zstdcat"
58 #define ZSTD_ZCAT "zcat"
59 #define ZSTD_GZ "gzip"
60 #define ZSTD_GUNZIP "gunzip"
61 #define ZSTD_GZCAT "gzcat"
62 #define ZSTD_LZMA "lzma"
63 #define ZSTD_UNLZMA "unlzma"
64 #define ZSTD_XZ "xz"
65 #define ZSTD_UNXZ "unxz"
66 #define ZSTD_LZ4 "lz4"
67 #define ZSTD_UNLZ4 "unlz4"
68 
69 #define KB *(1 <<10)
70 #define MB *(1 <<20)
71 #define GB *(1U<<30)
72 
73 #define DISPLAY_LEVEL_DEFAULT 2
74 
75 static const char*    g_defaultDictName = "dictionary";
76 static const unsigned g_defaultMaxDictSize = 110 KB;
77 static const int      g_defaultDictCLevel = 3;
78 static const unsigned g_defaultSelectivityLevel = 9;
79 static const unsigned g_defaultMaxWindowLog = 27;
80 #define OVERLAP_LOG_DEFAULT 9999
81 #define LDM_PARAM_DEFAULT 9999  /* Default for parameters where 0 is valid */
82 static U32 g_overlapLog = OVERLAP_LOG_DEFAULT;
83 static U32 g_ldmHashLog = 0;
84 static U32 g_ldmMinMatch = 0;
85 static U32 g_ldmHashRateLog = LDM_PARAM_DEFAULT;
86 static U32 g_ldmBucketSizeLog = LDM_PARAM_DEFAULT;
87 
88 
89 #define DEFAULT_ACCEL 1
90 
91 typedef enum { cover, fastCover, legacy } dictType;
92 
93 /*-************************************
94 *  Display Macros
95 **************************************/
96 #define DISPLAY(...)         fprintf(g_displayOut, __VA_ARGS__)
97 #define DISPLAYLEVEL(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } }
98 static int g_displayLevel = DISPLAY_LEVEL_DEFAULT;   /* 0 : no display,  1: errors,  2 : + result + interaction + warnings,  3 : + progression,  4 : + information */
99 static FILE* g_displayOut;
100 
101 
102 /*-************************************
103 *  Command Line
104 **************************************/
105 static int usage(const char* programName)
106 {
107     DISPLAY( "Usage : \n");
108     DISPLAY( "      %s [args] [FILE(s)] [-o file] \n", programName);
109     DISPLAY( "\n");
110     DISPLAY( "FILE    : a filename \n");
111     DISPLAY( "          with no FILE, or when FILE is - , read standard input\n");
112     DISPLAY( "Arguments : \n");
113 #ifndef ZSTD_NOCOMPRESS
114     DISPLAY( " -#     : # compression level (1-%d, default: %d) \n", ZSTDCLI_CLEVEL_MAX, ZSTDCLI_CLEVEL_DEFAULT);
115 #endif
116 #ifndef ZSTD_NODECOMPRESS
117     DISPLAY( " -d     : decompression \n");
118 #endif
119     DISPLAY( " -D file: use `file` as Dictionary \n");
120     DISPLAY( " -o file: result stored into `file` (only if 1 input file) \n");
121     DISPLAY( " -f     : overwrite output without prompting and (de)compress links \n");
122     DISPLAY( "--rm    : remove source file(s) after successful de/compression \n");
123     DISPLAY( " -k     : preserve source file(s) (default) \n");
124     DISPLAY( " -h/-H  : display help/long help and exit \n");
125     return 0;
126 }
127 
128 static int usage_advanced(const char* programName)
129 {
130     DISPLAY(WELCOME_MESSAGE);
131     usage(programName);
132     DISPLAY( "\n");
133     DISPLAY( "Advanced arguments : \n");
134     DISPLAY( " -V     : display Version number and exit \n");
135     DISPLAY( " -v     : verbose mode; specify multiple times to increase verbosity\n");
136     DISPLAY( " -q     : suppress warnings; specify twice to suppress errors too\n");
137     DISPLAY( " -c     : force write to standard output, even if it is the console\n");
138     DISPLAY( " -l     : print information about zstd compressed files \n");
139 #ifndef ZSTD_NOCOMPRESS
140     DISPLAY( "--ultra : enable levels beyond %i, up to %i (requires more memory)\n", ZSTDCLI_CLEVEL_MAX, ZSTD_maxCLevel());
141     DISPLAY( "--long[=#]: enable long distance matching with given window log (default: %u)\n", g_defaultMaxWindowLog);
142     DISPLAY( "--fast[=#]: switch to ultra fast compression level (default: %u)\n", 1);
143     DISPLAY( "--adapt : dynamically adapt compression level to I/O conditions \n");
144 #ifdef ZSTD_MULTITHREAD
145     DISPLAY( " -T#    : spawns # compression threads (default: 1, 0==# cores) \n");
146     DISPLAY( " -B#    : select size of each job (default: 0==automatic) \n");
147     DISPLAY( " --rsyncable : compress using a rsync-friendly method (-B sets block size) \n");
148 #endif
149     DISPLAY( "--no-dictID : don't write dictID into header (dictionary compression)\n");
150     DISPLAY( "--[no-]check : integrity check (default: enabled) \n");
151     DISPLAY( "--[no-]compress-literals : force (un)compressed literals \n");
152 #endif
153 #ifdef UTIL_HAS_CREATEFILELIST
154     DISPLAY( " -r     : operate recursively on directories \n");
155 #endif
156     DISPLAY( "--format=zstd : compress files to the .zst format (default) \n");
157 #ifdef ZSTD_GZCOMPRESS
158     DISPLAY( "--format=gzip : compress files to the .gz format \n");
159 #endif
160 #ifdef ZSTD_LZMACOMPRESS
161     DISPLAY( "--format=xz : compress files to the .xz format \n");
162     DISPLAY( "--format=lzma : compress files to the .lzma format \n");
163 #endif
164 #ifdef ZSTD_LZ4COMPRESS
165     DISPLAY( "--format=lz4 : compress files to the .lz4 format \n");
166 #endif
167 #ifndef ZSTD_NODECOMPRESS
168     DISPLAY( "--test  : test compressed file integrity \n");
169 #if ZSTD_SPARSE_DEFAULT
170     DISPLAY( "--[no-]sparse : sparse mode (default: enabled on file, disabled on stdout)\n");
171 #else
172     DISPLAY( "--[no-]sparse : sparse mode (default: disabled)\n");
173 #endif
174 #endif
175     DISPLAY( " -M#    : Set a memory usage limit for decompression \n");
176     DISPLAY( "--no-progress : do not display the progress bar \n");
177     DISPLAY( "--      : All arguments after \"--\" are treated as files \n");
178 #ifndef ZSTD_NODICT
179     DISPLAY( "\n");
180     DISPLAY( "Dictionary builder : \n");
181     DISPLAY( "--train ## : create a dictionary from a training set of files \n");
182     DISPLAY( "--train-cover[=k=#,d=#,steps=#,split=#] : use the cover algorithm with optional args\n");
183     DISPLAY( "--train-fastcover[=k=#,d=#,f=#,steps=#,split=#,accel=#] : use the fast cover algorithm with optional args\n");
184     DISPLAY( "--train-legacy[=s=#] : use the legacy algorithm with selectivity (default: %u)\n", g_defaultSelectivityLevel);
185     DISPLAY( " -o file : `file` is dictionary name (default: %s) \n", g_defaultDictName);
186     DISPLAY( "--maxdict=# : limit dictionary to specified size (default: %u) \n", g_defaultMaxDictSize);
187     DISPLAY( "--dictID=# : force dictionary ID to specified value (default: random)\n");
188 #endif
189 #ifndef ZSTD_NOBENCH
190     DISPLAY( "\n");
191     DISPLAY( "Benchmark arguments : \n");
192     DISPLAY( " -b#    : benchmark file(s), using # compression level (default: %d) \n", ZSTDCLI_CLEVEL_DEFAULT);
193     DISPLAY( " -e#    : test all compression levels from -bX to # (default: 1)\n");
194     DISPLAY( " -i#    : minimum evaluation time in seconds (default: 3s) \n");
195     DISPLAY( " -B#    : cut file into independent blocks of size # (default: no block)\n");
196     DISPLAY( "--priority=rt : set process priority to real-time \n");
197 #endif
198     return 0;
199 }
200 
201 static int badusage(const char* programName)
202 {
203     DISPLAYLEVEL(1, "Incorrect parameters\n");
204     if (g_displayLevel >= 2) usage(programName);
205     return 1;
206 }
207 
208 static void waitEnter(void)
209 {
210     int unused;
211     DISPLAY("Press enter to continue...\n");
212     unused = getchar();
213     (void)unused;
214 }
215 
216 static const char* lastNameFromPath(const char* path)
217 {
218     const char* name = path;
219     if (strrchr(name, '/')) name = strrchr(name, '/') + 1;
220     if (strrchr(name, '\\')) name = strrchr(name, '\\') + 1; /* windows */
221     return name;
222 }
223 
224 /*! exeNameMatch() :
225     @return : a non-zero value if exeName matches test, excluding the extension
226    */
227 static int exeNameMatch(const char* exeName, const char* test)
228 {
229     return !strncmp(exeName, test, strlen(test)) &&
230         (exeName[strlen(test)] == '\0' || exeName[strlen(test)] == '.');
231 }
232 
233 static void errorOut(const char* msg)
234 {
235     DISPLAY("%s \n", msg); exit(1);
236 }
237 
238 /*! readU32FromCharChecked() :
239  * @return 0 if success, and store the result in *value.
240  *  allows and interprets K, KB, KiB, M, MB and MiB suffix.
241  *  Will also modify `*stringPtr`, advancing it to position where it stopped reading.
242  * @return 1 if an overflow error occurs */
243 static int readU32FromCharChecked(const char** stringPtr, unsigned* value)
244 {
245     unsigned result = 0;
246     while ((**stringPtr >='0') && (**stringPtr <='9')) {
247         unsigned const max = (((unsigned)(-1)) / 10) - 1;
248         if (result > max) return 1; /* overflow error */
249         result *= 10;
250         result += (unsigned)(**stringPtr - '0');
251         (*stringPtr)++ ;
252     }
253     if ((**stringPtr=='K') || (**stringPtr=='M')) {
254         unsigned const maxK = ((unsigned)(-1)) >> 10;
255         if (result > maxK) return 1; /* overflow error */
256         result <<= 10;
257         if (**stringPtr=='M') {
258             if (result > maxK) return 1; /* overflow error */
259             result <<= 10;
260         }
261         (*stringPtr)++;  /* skip `K` or `M` */
262         if (**stringPtr=='i') (*stringPtr)++;
263         if (**stringPtr=='B') (*stringPtr)++;
264     }
265     *value = result;
266     return 0;
267 }
268 
269 /*! readU32FromChar() :
270  * @return : unsigned integer value read from input in `char` format.
271  *  allows and interprets K, KB, KiB, M, MB and MiB suffix.
272  *  Will also modify `*stringPtr`, advancing it to position where it stopped reading.
273  *  Note : function will exit() program if digit sequence overflows */
274 static unsigned readU32FromChar(const char** stringPtr) {
275     static const char errorMsg[] = "error: numeric value too large";
276     unsigned result;
277     if (readU32FromCharChecked(stringPtr, &result)) { errorOut(errorMsg); }
278     return result;
279 }
280 
281 /** longCommandWArg() :
282  *  check if *stringPtr is the same as longCommand.
283  *  If yes, @return 1 and advances *stringPtr to the position which immediately follows longCommand.
284  * @return 0 and doesn't modify *stringPtr otherwise.
285  */
286 static unsigned longCommandWArg(const char** stringPtr, const char* longCommand)
287 {
288     size_t const comSize = strlen(longCommand);
289     int const result = !strncmp(*stringPtr, longCommand, comSize);
290     if (result) *stringPtr += comSize;
291     return result;
292 }
293 
294 
295 #ifndef ZSTD_NODICT
296 /**
297  * parseCoverParameters() :
298  * reads cover parameters from *stringPtr (e.g. "--train-cover=k=48,d=8,steps=32") into *params
299  * @return 1 means that cover parameters were correct
300  * @return 0 in case of malformed parameters
301  */
302 static unsigned parseCoverParameters(const char* stringPtr, ZDICT_cover_params_t* params)
303 {
304     memset(params, 0, sizeof(*params));
305     for (; ;) {
306         if (longCommandWArg(&stringPtr, "k=")) { params->k = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
307         if (longCommandWArg(&stringPtr, "d=")) { params->d = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
308         if (longCommandWArg(&stringPtr, "steps=")) { params->steps = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
309         if (longCommandWArg(&stringPtr, "split=")) {
310           unsigned splitPercentage = readU32FromChar(&stringPtr);
311           params->splitPoint = (double)splitPercentage / 100.0;
312           if (stringPtr[0]==',') { stringPtr++; continue; } else break;
313         }
314         return 0;
315     }
316     if (stringPtr[0] != 0) return 0;
317     DISPLAYLEVEL(4, "cover: k=%u\nd=%u\nsteps=%u\nsplit=%u\n", params->k, params->d, params->steps, (unsigned)(params->splitPoint * 100));
318     return 1;
319 }
320 
321 /**
322  * parseFastCoverParameters() :
323  * reads fastcover parameters from *stringPtr (e.g. "--train-fastcover=k=48,d=8,f=20,steps=32,accel=2") into *params
324  * @return 1 means that fastcover parameters were correct
325  * @return 0 in case of malformed parameters
326  */
327 static unsigned parseFastCoverParameters(const char* stringPtr, ZDICT_fastCover_params_t* params)
328 {
329     memset(params, 0, sizeof(*params));
330     for (; ;) {
331         if (longCommandWArg(&stringPtr, "k=")) { params->k = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
332         if (longCommandWArg(&stringPtr, "d=")) { params->d = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
333         if (longCommandWArg(&stringPtr, "f=")) { params->f = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
334         if (longCommandWArg(&stringPtr, "steps=")) { params->steps = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
335         if (longCommandWArg(&stringPtr, "accel=")) { params->accel = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
336         if (longCommandWArg(&stringPtr, "split=")) {
337           unsigned splitPercentage = readU32FromChar(&stringPtr);
338           params->splitPoint = (double)splitPercentage / 100.0;
339           if (stringPtr[0]==',') { stringPtr++; continue; } else break;
340         }
341         return 0;
342     }
343     if (stringPtr[0] != 0) return 0;
344     DISPLAYLEVEL(4, "cover: k=%u\nd=%u\nf=%u\nsteps=%u\nsplit=%u\naccel=%u\n", params->k, params->d, params->f, params->steps, (unsigned)(params->splitPoint * 100), params->accel);
345     return 1;
346 }
347 
348 /**
349  * parseLegacyParameters() :
350  * reads legacy dictionary builder parameters from *stringPtr (e.g. "--train-legacy=selectivity=8") into *selectivity
351  * @return 1 means that legacy dictionary builder parameters were correct
352  * @return 0 in case of malformed parameters
353  */
354 static unsigned parseLegacyParameters(const char* stringPtr, unsigned* selectivity)
355 {
356     if (!longCommandWArg(&stringPtr, "s=") && !longCommandWArg(&stringPtr, "selectivity=")) { return 0; }
357     *selectivity = readU32FromChar(&stringPtr);
358     if (stringPtr[0] != 0) return 0;
359     DISPLAYLEVEL(4, "legacy: selectivity=%u\n", *selectivity);
360     return 1;
361 }
362 
363 static ZDICT_cover_params_t defaultCoverParams(void)
364 {
365     ZDICT_cover_params_t params;
366     memset(&params, 0, sizeof(params));
367     params.d = 8;
368     params.steps = 4;
369     params.splitPoint = 1.0;
370     return params;
371 }
372 
373 static ZDICT_fastCover_params_t defaultFastCoverParams(void)
374 {
375     ZDICT_fastCover_params_t params;
376     memset(&params, 0, sizeof(params));
377     params.d = 8;
378     params.f = 20;
379     params.steps = 4;
380     params.splitPoint = 0.75; /* different from default splitPoint of cover */
381     params.accel = DEFAULT_ACCEL;
382     return params;
383 }
384 #endif
385 
386 
387 /** parseAdaptParameters() :
388  *  reads adapt parameters from *stringPtr (e.g. "--zstd=min=1,max=19) and store them into adaptMinPtr and adaptMaxPtr.
389  *  Both adaptMinPtr and adaptMaxPtr must be already allocated and correctly initialized.
390  *  There is no guarantee that any of these values will be updated.
391  *  @return 1 means that parsing was successful,
392  *  @return 0 in case of malformed parameters
393  */
394 static unsigned parseAdaptParameters(const char* stringPtr, int* adaptMinPtr, int* adaptMaxPtr)
395 {
396     for ( ; ;) {
397         if (longCommandWArg(&stringPtr, "min=")) { *adaptMinPtr = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
398         if (longCommandWArg(&stringPtr, "max=")) { *adaptMaxPtr = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
399         DISPLAYLEVEL(4, "invalid compression parameter \n");
400         return 0;
401     }
402     if (stringPtr[0] != 0) return 0; /* check the end of string */
403     if (*adaptMinPtr > *adaptMaxPtr) {
404         DISPLAYLEVEL(4, "incoherent adaptation limits \n");
405         return 0;
406     }
407     return 1;
408 }
409 
410 
411 /** parseCompressionParameters() :
412  *  reads compression parameters from *stringPtr (e.g. "--zstd=wlog=23,clog=23,hlog=22,slog=6,mml=3,tlen=48,strat=6") into *params
413  *  @return 1 means that compression parameters were correct
414  *  @return 0 in case of malformed parameters
415  */
416 static unsigned parseCompressionParameters(const char* stringPtr, ZSTD_compressionParameters* params)
417 {
418     for ( ; ;) {
419         if (longCommandWArg(&stringPtr, "windowLog=") || longCommandWArg(&stringPtr, "wlog=")) { params->windowLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
420         if (longCommandWArg(&stringPtr, "chainLog=") || longCommandWArg(&stringPtr, "clog=")) { params->chainLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
421         if (longCommandWArg(&stringPtr, "hashLog=") || longCommandWArg(&stringPtr, "hlog=")) { params->hashLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
422         if (longCommandWArg(&stringPtr, "searchLog=") || longCommandWArg(&stringPtr, "slog=")) { params->searchLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
423         if (longCommandWArg(&stringPtr, "minMatch=") || longCommandWArg(&stringPtr, "mml=")) { params->minMatch = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
424         if (longCommandWArg(&stringPtr, "targetLength=") || longCommandWArg(&stringPtr, "tlen=")) { params->targetLength = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
425         if (longCommandWArg(&stringPtr, "strategy=") || longCommandWArg(&stringPtr, "strat=")) { params->strategy = (ZSTD_strategy)(readU32FromChar(&stringPtr)); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
426         if (longCommandWArg(&stringPtr, "overlapLog=") || longCommandWArg(&stringPtr, "ovlog=")) { g_overlapLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
427         if (longCommandWArg(&stringPtr, "ldmHashLog=") || longCommandWArg(&stringPtr, "lhlog=")) { g_ldmHashLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
428         if (longCommandWArg(&stringPtr, "ldmMinMatch=") || longCommandWArg(&stringPtr, "lmml=")) { g_ldmMinMatch = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
429         if (longCommandWArg(&stringPtr, "ldmBucketSizeLog=") || longCommandWArg(&stringPtr, "lblog=")) { g_ldmBucketSizeLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
430         if (longCommandWArg(&stringPtr, "ldmHashRateLog=") || longCommandWArg(&stringPtr, "lhrlog=")) { g_ldmHashRateLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; }
431         DISPLAYLEVEL(4, "invalid compression parameter \n");
432         return 0;
433     }
434 
435     DISPLAYLEVEL(4, "windowLog=%d, chainLog=%d, hashLog=%d, searchLog=%d \n", params->windowLog, params->chainLog, params->hashLog, params->searchLog);
436     DISPLAYLEVEL(4, "minMatch=%d, targetLength=%d, strategy=%d \n", params->minMatch, params->targetLength, params->strategy);
437     if (stringPtr[0] != 0) return 0; /* check the end of string */
438     return 1;
439 }
440 
441 static void printVersion(void)
442 {
443     DISPLAY(WELCOME_MESSAGE);
444     /* format support */
445     DISPLAYLEVEL(3, "*** supports: zstd");
446 #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>0) && (ZSTD_LEGACY_SUPPORT<8)
447     DISPLAYLEVEL(3, ", zstd legacy v0.%d+", ZSTD_LEGACY_SUPPORT);
448 #endif
449 #ifdef ZSTD_GZCOMPRESS
450     DISPLAYLEVEL(3, ", gzip");
451 #endif
452 #ifdef ZSTD_LZ4COMPRESS
453     DISPLAYLEVEL(3, ", lz4");
454 #endif
455 #ifdef ZSTD_LZMACOMPRESS
456     DISPLAYLEVEL(3, ", lzma, xz ");
457 #endif
458     DISPLAYLEVEL(3, "\n");
459     /* posix support */
460 #ifdef _POSIX_C_SOURCE
461     DISPLAYLEVEL(4, "_POSIX_C_SOURCE defined: %ldL\n", (long) _POSIX_C_SOURCE);
462 #endif
463 #ifdef _POSIX_VERSION
464     DISPLAYLEVEL(4, "_POSIX_VERSION defined: %ldL \n", (long) _POSIX_VERSION);
465 #endif
466 #ifdef PLATFORM_POSIX_VERSION
467     DISPLAYLEVEL(4, "PLATFORM_POSIX_VERSION defined: %ldL\n", (long) PLATFORM_POSIX_VERSION);
468 #endif
469 }
470 
471 /* Environment variables for parameter setting */
472 #define ENV_CLEVEL "ZSTD_CLEVEL"
473 
474 /* functions that pick up environment variables */
475 static int init_cLevel(void) {
476     const char* const env = getenv(ENV_CLEVEL);
477     if (env) {
478         const char *ptr = env;
479         int sign = 1;
480         if (*ptr == '-') {
481             sign = -1;
482             ptr++;
483         } else if (*ptr == '+') {
484             ptr++;
485         }
486 
487         if ((*ptr>='0') && (*ptr<='9')) {
488             unsigned absLevel;
489             if (readU32FromCharChecked(&ptr, &absLevel)) {
490                 DISPLAYLEVEL(2, "Ignore environment variable setting %s=%s: numeric value too large\n", ENV_CLEVEL, env);
491                 return ZSTDCLI_CLEVEL_DEFAULT;
492             } else if (*ptr == 0) {
493                 return sign * absLevel;
494             }
495         }
496 
497         DISPLAYLEVEL(2, "Ignore environment variable setting %s=%s: not a valid integer value\n", ENV_CLEVEL, env);
498     }
499 
500     return ZSTDCLI_CLEVEL_DEFAULT;
501 }
502 
503 typedef enum { zom_compress, zom_decompress, zom_test, zom_bench, zom_train, zom_list } zstd_operation_mode;
504 
505 #define CLEAN_RETURN(i) { operationResult = (i); goto _end; }
506 
507 #ifdef ZSTD_NOCOMPRESS
508 /* symbols from compression library are not defined and should not be invoked */
509 # define MINCLEVEL  -50
510 # define MAXCLEVEL   22
511 #else
512 # define MINCLEVEL  ZSTD_minCLevel()
513 # define MAXCLEVEL  ZSTD_maxCLevel()
514 #endif
515 
516 int main(int argCount, const char* argv[])
517 {
518     int argNb,
519         followLinks = 0,
520         forceStdout = 0,
521         lastCommand = 0,
522         ldmFlag = 0,
523         main_pause = 0,
524         nbWorkers = 0,
525         adapt = 0,
526         adaptMin = MINCLEVEL,
527         adaptMax = MAXCLEVEL,
528         rsyncable = 0,
529         nextArgumentIsOutFileName = 0,
530         nextArgumentIsMaxDict = 0,
531         nextArgumentIsDictID = 0,
532         nextArgumentsAreFiles = 0,
533         nextEntryIsDictionary = 0,
534         operationResult = 0,
535         separateFiles = 0,
536         setRealTimePrio = 0,
537         singleThread = 0,
538         ultra=0;
539     double compressibility = 0.5;
540     unsigned bench_nbSeconds = 3;   /* would be better if this value was synchronized from bench */
541     size_t blockSize = 0;
542 
543     FIO_prefs_t* const prefs = FIO_createPreferences();
544     zstd_operation_mode operation = zom_compress;
545     ZSTD_compressionParameters compressionParams;
546     int cLevel;
547     int cLevelLast = -1000000000;
548     unsigned recursive = 0;
549     unsigned memLimit = 0;
550     const char** filenameTable = (const char**)malloc(argCount * sizeof(const char*));   /* argCount >= 1 */
551     unsigned filenameIdx = 0;
552     const char* programName = argv[0];
553     const char* outFileName = NULL;
554     const char* dictFileName = NULL;
555     const char* suffix = ZSTD_EXTENSION;
556     unsigned maxDictSize = g_defaultMaxDictSize;
557     unsigned dictID = 0;
558     int dictCLevel = g_defaultDictCLevel;
559     unsigned dictSelect = g_defaultSelectivityLevel;
560 #ifdef UTIL_HAS_CREATEFILELIST
561     const char** extendedFileList = NULL;
562     char* fileNamesBuf = NULL;
563     unsigned fileNamesNb;
564 #endif
565 #ifndef ZSTD_NODICT
566     ZDICT_cover_params_t coverParams = defaultCoverParams();
567     ZDICT_fastCover_params_t fastCoverParams = defaultFastCoverParams();
568     dictType dict = fastCover;
569 #endif
570 #ifndef ZSTD_NOBENCH
571     BMK_advancedParams_t benchParams = BMK_initAdvancedParams();
572 #endif
573     ZSTD_literalCompressionMode_e literalCompressionMode = ZSTD_lcm_auto;
574 
575 
576     /* init */
577     (void)recursive; (void)cLevelLast;    /* not used when ZSTD_NOBENCH set */
578     (void)memLimit;   /* not used when ZSTD_NODECOMPRESS set */
579     if (filenameTable==NULL) { DISPLAY("zstd: %s \n", strerror(errno)); exit(1); }
580     filenameTable[0] = stdinmark;
581     g_displayOut = stderr;
582     cLevel = init_cLevel();
583     programName = lastNameFromPath(programName);
584 #ifdef ZSTD_MULTITHREAD
585     nbWorkers = 1;
586 #endif
587 
588     /* preset behaviors */
589     if (exeNameMatch(programName, ZSTD_ZSTDMT)) nbWorkers=0, singleThread=0;
590     if (exeNameMatch(programName, ZSTD_UNZSTD)) operation=zom_decompress;
591     if (exeNameMatch(programName, ZSTD_CAT)) { operation=zom_decompress; forceStdout=1; FIO_overwriteMode(prefs); outFileName=stdoutmark; g_displayLevel=1; }     /* supports multiple formats */
592     if (exeNameMatch(programName, ZSTD_ZCAT)) { operation=zom_decompress; forceStdout=1; FIO_overwriteMode(prefs); outFileName=stdoutmark; g_displayLevel=1; }    /* behave like zcat, also supports multiple formats */
593     if (exeNameMatch(programName, ZSTD_GZ)) { suffix = GZ_EXTENSION; FIO_setCompressionType(prefs, FIO_gzipCompression); FIO_setRemoveSrcFile(prefs, 1); }        /* behave like gzip */
594     if (exeNameMatch(programName, ZSTD_GUNZIP)) { operation=zom_decompress; FIO_setRemoveSrcFile(prefs, 1); }                                                     /* behave like gunzip, also supports multiple formats */
595     if (exeNameMatch(programName, ZSTD_GZCAT)) { operation=zom_decompress; forceStdout=1; FIO_overwriteMode(prefs); outFileName=stdoutmark; g_displayLevel=1; }   /* behave like gzcat, also supports multiple formats */
596     if (exeNameMatch(programName, ZSTD_LZMA)) { suffix = LZMA_EXTENSION; FIO_setCompressionType(prefs, FIO_lzmaCompression); FIO_setRemoveSrcFile(prefs, 1); }    /* behave like lzma */
597     if (exeNameMatch(programName, ZSTD_UNLZMA)) { operation=zom_decompress; FIO_setCompressionType(prefs, FIO_lzmaCompression); FIO_setRemoveSrcFile(prefs, 1); } /* behave like unlzma, also supports multiple formats */
598     if (exeNameMatch(programName, ZSTD_XZ)) { suffix = XZ_EXTENSION; FIO_setCompressionType(prefs, FIO_xzCompression); FIO_setRemoveSrcFile(prefs, 1); }          /* behave like xz */
599     if (exeNameMatch(programName, ZSTD_UNXZ)) { operation=zom_decompress; FIO_setCompressionType(prefs, FIO_xzCompression); FIO_setRemoveSrcFile(prefs, 1); }     /* behave like unxz, also supports multiple formats */
600     if (exeNameMatch(programName, ZSTD_LZ4)) { suffix = LZ4_EXTENSION; FIO_setCompressionType(prefs, FIO_lz4Compression); }                                       /* behave like lz4 */
601     if (exeNameMatch(programName, ZSTD_UNLZ4)) { operation=zom_decompress; FIO_setCompressionType(prefs, FIO_lz4Compression); }                                   /* behave like unlz4, also supports multiple formats */
602     memset(&compressionParams, 0, sizeof(compressionParams));
603 
604     /* init crash handler */
605     FIO_addAbortHandler();
606 
607     /* command switches */
608     for (argNb=1; argNb<argCount; argNb++) {
609         const char* argument = argv[argNb];
610         if(!argument) continue;   /* Protection if argument empty */
611 
612         if (nextArgumentsAreFiles==0) {
613             /* "-" means stdin/stdout */
614             if (!strcmp(argument, "-")){
615                 if (!filenameIdx) {
616                     filenameIdx=1, filenameTable[0]=stdinmark;
617                     outFileName=stdoutmark;
618                     g_displayLevel-=(g_displayLevel==2);
619                     continue;
620             }   }
621 
622             /* Decode commands (note : aggregated commands are allowed) */
623             if (argument[0]=='-') {
624 
625                 if (argument[1]=='-') {
626                     /* long commands (--long-word) */
627                     if (!strcmp(argument, "--")) { nextArgumentsAreFiles=1; continue; }   /* only file names allowed from now on */
628                     if (!strcmp(argument, "--list")) { operation=zom_list; continue; }
629                     if (!strcmp(argument, "--compress")) { operation=zom_compress; continue; }
630                     if (!strcmp(argument, "--decompress")) { operation=zom_decompress; continue; }
631                     if (!strcmp(argument, "--uncompress")) { operation=zom_decompress; continue; }
632                     if (!strcmp(argument, "--force")) { FIO_overwriteMode(prefs); forceStdout=1; followLinks=1; continue; }
633                     if (!strcmp(argument, "--version")) { g_displayOut=stdout; DISPLAY(WELCOME_MESSAGE); CLEAN_RETURN(0); }
634                     if (!strcmp(argument, "--help")) { g_displayOut=stdout; CLEAN_RETURN(usage_advanced(programName)); }
635                     if (!strcmp(argument, "--verbose")) { g_displayLevel++; continue; }
636                     if (!strcmp(argument, "--quiet")) { g_displayLevel--; continue; }
637                     if (!strcmp(argument, "--stdout")) { forceStdout=1; outFileName=stdoutmark; g_displayLevel-=(g_displayLevel==2); continue; }
638                     if (!strcmp(argument, "--ultra")) { ultra=1; continue; }
639                     if (!strcmp(argument, "--check")) { FIO_setChecksumFlag(prefs, 2); continue; }
640                     if (!strcmp(argument, "--no-check")) { FIO_setChecksumFlag(prefs, 0); continue; }
641                     if (!strcmp(argument, "--sparse")) { FIO_setSparseWrite(prefs, 2); continue; }
642                     if (!strcmp(argument, "--no-sparse")) { FIO_setSparseWrite(prefs, 0); continue; }
643                     if (!strcmp(argument, "--test")) { operation=zom_test; continue; }
644                     if (!strcmp(argument, "--train")) { operation=zom_train; if (outFileName==NULL) outFileName=g_defaultDictName; continue; }
645                     if (!strcmp(argument, "--maxdict")) { nextArgumentIsMaxDict=1; lastCommand=1; continue; }  /* kept available for compatibility with old syntax ; will be removed one day */
646                     if (!strcmp(argument, "--dictID")) { nextArgumentIsDictID=1; lastCommand=1; continue; }  /* kept available for compatibility with old syntax ; will be removed one day */
647                     if (!strcmp(argument, "--no-dictID")) { FIO_setDictIDFlag(prefs, 0); continue; }
648                     if (!strcmp(argument, "--keep")) { FIO_setRemoveSrcFile(prefs, 0); continue; }
649                     if (!strcmp(argument, "--rm")) { FIO_setRemoveSrcFile(prefs, 1); continue; }
650                     if (!strcmp(argument, "--priority=rt")) { setRealTimePrio = 1; continue; }
651                     if (!strcmp(argument, "--adapt")) { adapt = 1; continue; }
652                     if (longCommandWArg(&argument, "--adapt=")) { adapt = 1; if (!parseAdaptParameters(argument, &adaptMin, &adaptMax)) CLEAN_RETURN(badusage(programName)); continue; }
653                     if (!strcmp(argument, "--single-thread")) { nbWorkers = 0; singleThread = 1; continue; }
654                     if (!strcmp(argument, "--format=zstd")) { suffix = ZSTD_EXTENSION; FIO_setCompressionType(prefs, FIO_zstdCompression); continue; }
655 #ifdef ZSTD_GZCOMPRESS
656                     if (!strcmp(argument, "--format=gzip")) { suffix = GZ_EXTENSION; FIO_setCompressionType(prefs, FIO_gzipCompression); continue; }
657 #endif
658 #ifdef ZSTD_LZMACOMPRESS
659                     if (!strcmp(argument, "--format=lzma")) { suffix = LZMA_EXTENSION; FIO_setCompressionType(prefs, FIO_lzmaCompression);  continue; }
660                     if (!strcmp(argument, "--format=xz")) { suffix = XZ_EXTENSION; FIO_setCompressionType(prefs, FIO_xzCompression);  continue; }
661 #endif
662 #ifdef ZSTD_LZ4COMPRESS
663                     if (!strcmp(argument, "--format=lz4")) { suffix = LZ4_EXTENSION; FIO_setCompressionType(prefs, FIO_lz4Compression);  continue; }
664 #endif
665                     if (!strcmp(argument, "--rsyncable")) { rsyncable = 1; continue; }
666                     if (!strcmp(argument, "--compress-literals")) { literalCompressionMode = ZSTD_lcm_huffman; continue; }
667                     if (!strcmp(argument, "--no-compress-literals")) { literalCompressionMode = ZSTD_lcm_uncompressed; continue; }
668                     if (!strcmp(argument, "--no-progress")) { FIO_setNoProgress(1); continue; }
669 
670                     /* long commands with arguments */
671 #ifndef ZSTD_NODICT
672                     if (longCommandWArg(&argument, "--train-cover")) {
673                       operation = zom_train;
674                       if (outFileName == NULL)
675                           outFileName = g_defaultDictName;
676                       dict = cover;
677                       /* Allow optional arguments following an = */
678                       if (*argument == 0) { memset(&coverParams, 0, sizeof(coverParams)); }
679                       else if (*argument++ != '=') { CLEAN_RETURN(badusage(programName)); }
680                       else if (!parseCoverParameters(argument, &coverParams)) { CLEAN_RETURN(badusage(programName)); }
681                       continue;
682                     }
683                     if (longCommandWArg(&argument, "--train-fastcover")) {
684                       operation = zom_train;
685                       if (outFileName == NULL)
686                           outFileName = g_defaultDictName;
687                       dict = fastCover;
688                       /* Allow optional arguments following an = */
689                       if (*argument == 0) { memset(&fastCoverParams, 0, sizeof(fastCoverParams)); }
690                       else if (*argument++ != '=') { CLEAN_RETURN(badusage(programName)); }
691                       else if (!parseFastCoverParameters(argument, &fastCoverParams)) { CLEAN_RETURN(badusage(programName)); }
692                       continue;
693                     }
694                     if (longCommandWArg(&argument, "--train-legacy")) {
695                       operation = zom_train;
696                       if (outFileName == NULL)
697                           outFileName = g_defaultDictName;
698                       dict = legacy;
699                       /* Allow optional arguments following an = */
700                       if (*argument == 0) { continue; }
701                       else if (*argument++ != '=') { CLEAN_RETURN(badusage(programName)); }
702                       else if (!parseLegacyParameters(argument, &dictSelect)) { CLEAN_RETURN(badusage(programName)); }
703                       continue;
704                     }
705 #endif
706                     if (longCommandWArg(&argument, "--threads=")) { nbWorkers = readU32FromChar(&argument); continue; }
707                     if (longCommandWArg(&argument, "--memlimit=")) { memLimit = readU32FromChar(&argument); continue; }
708                     if (longCommandWArg(&argument, "--memory=")) { memLimit = readU32FromChar(&argument); continue; }
709                     if (longCommandWArg(&argument, "--memlimit-decompress=")) { memLimit = readU32FromChar(&argument); continue; }
710                     if (longCommandWArg(&argument, "--block-size=")) { blockSize = readU32FromChar(&argument); continue; }
711                     if (longCommandWArg(&argument, "--maxdict=")) { maxDictSize = readU32FromChar(&argument); continue; }
712                     if (longCommandWArg(&argument, "--dictID=")) { dictID = readU32FromChar(&argument); continue; }
713                     if (longCommandWArg(&argument, "--zstd=")) { if (!parseCompressionParameters(argument, &compressionParams)) CLEAN_RETURN(badusage(programName)); continue; }
714                     if (longCommandWArg(&argument, "--long")) {
715                         unsigned ldmWindowLog = 0;
716                         ldmFlag = 1;
717                         /* Parse optional window log */
718                         if (*argument == '=') {
719                             ++argument;
720                             ldmWindowLog = readU32FromChar(&argument);
721                         } else if (*argument != 0) {
722                             /* Invalid character following --long */
723                             CLEAN_RETURN(badusage(programName));
724                         }
725                         /* Only set windowLog if not already set by --zstd */
726                         if (compressionParams.windowLog == 0)
727                             compressionParams.windowLog = ldmWindowLog;
728                         continue;
729                     }
730 #ifndef ZSTD_NOCOMPRESS   /* linking ZSTD_minCLevel() requires compression support */
731                     if (longCommandWArg(&argument, "--fast")) {
732                         /* Parse optional acceleration factor */
733                         if (*argument == '=') {
734                             U32 const maxFast = (U32)-ZSTD_minCLevel();
735                             U32 fastLevel;
736                             ++argument;
737                             fastLevel = readU32FromChar(&argument);
738                             if (fastLevel > maxFast) fastLevel = maxFast;
739                             if (fastLevel) {
740                               dictCLevel = cLevel = -(int)fastLevel;
741                             } else {
742                               CLEAN_RETURN(badusage(programName));
743                             }
744                         } else if (*argument != 0) {
745                             /* Invalid character following --fast */
746                             CLEAN_RETURN(badusage(programName));
747                         } else {
748                             cLevel = -1;  /* default for --fast */
749                         }
750                         continue;
751                     }
752 #endif
753                     /* fall-through, will trigger bad_usage() later on */
754                 }
755 
756                 argument++;
757                 while (argument[0]!=0) {
758                     if (lastCommand) {
759                         DISPLAY("error : command must be followed by argument \n");
760                         CLEAN_RETURN(1);
761                     }
762 #ifndef ZSTD_NOCOMPRESS
763                     /* compression Level */
764                     if ((*argument>='0') && (*argument<='9')) {
765                         dictCLevel = cLevel = readU32FromChar(&argument);
766                         continue;
767                     }
768 #endif
769 
770                     switch(argument[0])
771                     {
772                         /* Display help */
773                     case 'V': g_displayOut=stdout; printVersion(); CLEAN_RETURN(0);   /* Version Only */
774                     case 'H':
775                     case 'h': g_displayOut=stdout; CLEAN_RETURN(usage_advanced(programName));
776 
777                          /* Compress */
778                     case 'z': operation=zom_compress; argument++; break;
779 
780                          /* Decoding */
781                     case 'd':
782 #ifndef ZSTD_NOBENCH
783                             benchParams.mode = BMK_decodeOnly;
784                             if (operation==zom_bench) { argument++; break; }  /* benchmark decode (hidden option) */
785 #endif
786                             operation=zom_decompress; argument++; break;
787 
788                         /* Force stdout, even if stdout==console */
789                     case 'c': forceStdout=1; outFileName=stdoutmark; argument++; break;
790 
791                         /* Use file content as dictionary */
792                     case 'D': nextEntryIsDictionary = 1; lastCommand = 1; argument++; break;
793 
794                         /* Overwrite */
795                     case 'f': FIO_overwriteMode(prefs); forceStdout=1; followLinks=1; argument++; break;
796 
797                         /* Verbose mode */
798                     case 'v': g_displayLevel++; argument++; break;
799 
800                         /* Quiet mode */
801                     case 'q': g_displayLevel--; argument++; break;
802 
803                         /* keep source file (default) */
804                     case 'k': FIO_setRemoveSrcFile(prefs, 0); argument++; break;
805 
806                         /* Checksum */
807                     case 'C': FIO_setChecksumFlag(prefs, 2); argument++; break;
808 
809                         /* test compressed file */
810                     case 't': operation=zom_test; argument++; break;
811 
812                         /* destination file name */
813                     case 'o': nextArgumentIsOutFileName=1; lastCommand=1; argument++; break;
814 
815                         /* limit decompression memory */
816                     case 'M':
817                         argument++;
818                         memLimit = readU32FromChar(&argument);
819                         break;
820                     case 'l': operation=zom_list; argument++; break;
821 #ifdef UTIL_HAS_CREATEFILELIST
822                         /* recursive */
823                     case 'r': recursive=1; argument++; break;
824 #endif
825 
826 #ifndef ZSTD_NOBENCH
827                         /* Benchmark */
828                     case 'b':
829                         operation=zom_bench;
830                         argument++;
831                         break;
832 
833                         /* range bench (benchmark only) */
834                     case 'e':
835                         /* compression Level */
836                         argument++;
837                         cLevelLast = readU32FromChar(&argument);
838                         break;
839 
840                         /* Modify Nb Iterations (benchmark only) */
841                     case 'i':
842                         argument++;
843                         bench_nbSeconds = readU32FromChar(&argument);
844                         break;
845 
846                         /* cut input into blocks (benchmark only) */
847                     case 'B':
848                         argument++;
849                         blockSize = readU32FromChar(&argument);
850                         break;
851 
852                         /* benchmark files separately (hidden option) */
853                     case 'S':
854                         argument++;
855                         separateFiles = 1;
856                         break;
857 
858 #endif   /* ZSTD_NOBENCH */
859 
860                         /* nb of threads (hidden option) */
861                     case 'T':
862                         argument++;
863                         nbWorkers = readU32FromChar(&argument);
864                         break;
865 
866                         /* Dictionary Selection level */
867                     case 's':
868                         argument++;
869                         dictSelect = readU32FromChar(&argument);
870                         break;
871 
872                         /* Pause at the end (-p) or set an additional param (-p#) (hidden option) */
873                     case 'p': argument++;
874 #ifndef ZSTD_NOBENCH
875                         if ((*argument>='0') && (*argument<='9')) {
876                             benchParams.additionalParam = (int)readU32FromChar(&argument);
877                         } else
878 #endif
879                             main_pause=1;
880                         break;
881 
882                         /* Select compressibility of synthetic sample */
883                     case 'P':
884                     {   argument++;
885                         compressibility = (double)readU32FromChar(&argument) / 100;
886                     }
887                     break;
888 
889                         /* unknown command */
890                     default : CLEAN_RETURN(badusage(programName));
891                     }
892                 }
893                 continue;
894             }   /* if (argument[0]=='-') */
895 
896             if (nextArgumentIsMaxDict) {  /* kept available for compatibility with old syntax ; will be removed one day */
897                 nextArgumentIsMaxDict = 0;
898                 lastCommand = 0;
899                 maxDictSize = readU32FromChar(&argument);
900                 continue;
901             }
902 
903             if (nextArgumentIsDictID) {  /* kept available for compatibility with old syntax ; will be removed one day */
904                 nextArgumentIsDictID = 0;
905                 lastCommand = 0;
906                 dictID = readU32FromChar(&argument);
907                 continue;
908             }
909 
910         }   /* if (nextArgumentIsAFile==0) */
911 
912         if (nextEntryIsDictionary) {
913             nextEntryIsDictionary = 0;
914             lastCommand = 0;
915             dictFileName = argument;
916             continue;
917         }
918 
919         if (nextArgumentIsOutFileName) {
920             nextArgumentIsOutFileName = 0;
921             lastCommand = 0;
922             outFileName = argument;
923             if (!strcmp(outFileName, "-")) outFileName = stdoutmark;
924             continue;
925         }
926 
927         /* add filename to list */
928         filenameTable[filenameIdx++] = argument;
929     }
930 
931     if (lastCommand) { /* forgotten argument */
932         DISPLAY("error : command must be followed by argument \n");
933         CLEAN_RETURN(1);
934     }
935 
936     /* Welcome message (if verbose) */
937     DISPLAYLEVEL(3, WELCOME_MESSAGE);
938 
939 #ifdef ZSTD_MULTITHREAD
940     if ((nbWorkers==0) && (!singleThread)) {
941         /* automatically set # workers based on # of reported cpus */
942         nbWorkers = UTIL_countPhysicalCores();
943         DISPLAYLEVEL(3, "Note: %d physical core(s) detected \n", nbWorkers);
944     }
945 #else
946     (void)singleThread; (void)nbWorkers;
947 #endif
948 
949 #ifdef UTIL_HAS_CREATEFILELIST
950     g_utilDisplayLevel = g_displayLevel;
951     if (!followLinks) {
952         unsigned u;
953         for (u=0, fileNamesNb=0; u<filenameIdx; u++) {
954             if (UTIL_isLink(filenameTable[u])) {
955                 DISPLAYLEVEL(2, "Warning : %s is a symbolic link, ignoring\n", filenameTable[u]);
956             } else {
957                 filenameTable[fileNamesNb++] = filenameTable[u];
958             }
959         }
960         if (fileNamesNb == 0 && filenameIdx > 0)
961             CLEAN_RETURN(1);
962         filenameIdx = fileNamesNb;
963     }
964     if (recursive) {  /* at this stage, filenameTable is a list of paths, which can contain both files and directories */
965         extendedFileList = UTIL_createFileList(filenameTable, filenameIdx, &fileNamesBuf, &fileNamesNb, followLinks);
966         if (extendedFileList) {
967             unsigned u;
968             for (u=0; u<fileNamesNb; u++) DISPLAYLEVEL(4, "%u %s\n", u, extendedFileList[u]);
969             free((void*)filenameTable);
970             filenameTable = extendedFileList;
971             filenameIdx = fileNamesNb;
972         }
973     }
974 #else
975     (void)followLinks;
976 #endif
977 
978     if (operation == zom_list) {
979 #ifndef ZSTD_NODECOMPRESS
980         int const ret = FIO_listMultipleFiles(filenameIdx, filenameTable, g_displayLevel);
981         CLEAN_RETURN(ret);
982 #else
983         DISPLAY("file information is not supported \n");
984         CLEAN_RETURN(1);
985 #endif
986     }
987 
988     /* Check if benchmark is selected */
989     if (operation==zom_bench) {
990 #ifndef ZSTD_NOBENCH
991         benchParams.blockSize = blockSize;
992         benchParams.nbWorkers = nbWorkers;
993         benchParams.realTime = setRealTimePrio;
994         benchParams.nbSeconds = bench_nbSeconds;
995         benchParams.ldmFlag = ldmFlag;
996         benchParams.ldmMinMatch = g_ldmMinMatch;
997         benchParams.ldmHashLog = g_ldmHashLog;
998         if (g_ldmBucketSizeLog != LDM_PARAM_DEFAULT) {
999             benchParams.ldmBucketSizeLog = g_ldmBucketSizeLog;
1000         }
1001         if (g_ldmHashRateLog != LDM_PARAM_DEFAULT) {
1002             benchParams.ldmHashRateLog = g_ldmHashRateLog;
1003         }
1004         benchParams.literalCompressionMode = literalCompressionMode;
1005 
1006         if (cLevel > ZSTD_maxCLevel()) cLevel = ZSTD_maxCLevel();
1007         if (cLevelLast > ZSTD_maxCLevel()) cLevelLast = ZSTD_maxCLevel();
1008         if (cLevelLast < cLevel) cLevelLast = cLevel;
1009         if (cLevelLast > cLevel)
1010             DISPLAYLEVEL(3, "Benchmarking levels from %d to %d\n", cLevel, cLevelLast);
1011         if(filenameIdx) {
1012             if(separateFiles) {
1013                 unsigned i;
1014                 for(i = 0; i < filenameIdx; i++) {
1015                     int c;
1016                     DISPLAYLEVEL(3, "Benchmarking %s \n", filenameTable[i]);
1017                     for(c = cLevel; c <= cLevelLast; c++) {
1018                         BMK_benchFilesAdvanced(&filenameTable[i], 1, dictFileName, c, &compressionParams, g_displayLevel, &benchParams);
1019                     }
1020                 }
1021             } else {
1022                 for(; cLevel <= cLevelLast; cLevel++) {
1023                     BMK_benchFilesAdvanced(filenameTable, filenameIdx, dictFileName, cLevel, &compressionParams, g_displayLevel, &benchParams);
1024                 }
1025             }
1026         } else {
1027             for(; cLevel <= cLevelLast; cLevel++) {
1028                 BMK_syntheticTest(cLevel, compressibility, &compressionParams, g_displayLevel, &benchParams);
1029             }
1030         }
1031 
1032 #else
1033         (void)bench_nbSeconds; (void)blockSize; (void)setRealTimePrio; (void)separateFiles; (void)compressibility;
1034 #endif
1035         goto _end;
1036     }
1037 
1038     /* Check if dictionary builder is selected */
1039     if (operation==zom_train) {
1040 #ifndef ZSTD_NODICT
1041         ZDICT_params_t zParams;
1042         zParams.compressionLevel = dictCLevel;
1043         zParams.notificationLevel = g_displayLevel;
1044         zParams.dictID = dictID;
1045         if (dict == cover) {
1046             int const optimize = !coverParams.k || !coverParams.d;
1047             coverParams.nbThreads = nbWorkers;
1048             coverParams.zParams = zParams;
1049             operationResult = DiB_trainFromFiles(outFileName, maxDictSize, filenameTable, filenameIdx, blockSize, NULL, &coverParams, NULL, optimize);
1050         } else if (dict == fastCover) {
1051             int const optimize = !fastCoverParams.k || !fastCoverParams.d;
1052             fastCoverParams.nbThreads = nbWorkers;
1053             fastCoverParams.zParams = zParams;
1054             operationResult = DiB_trainFromFiles(outFileName, maxDictSize, filenameTable, filenameIdx, blockSize, NULL, NULL, &fastCoverParams, optimize);
1055         } else {
1056             ZDICT_legacy_params_t dictParams;
1057             memset(&dictParams, 0, sizeof(dictParams));
1058             dictParams.selectivityLevel = dictSelect;
1059             dictParams.zParams = zParams;
1060             operationResult = DiB_trainFromFiles(outFileName, maxDictSize, filenameTable, filenameIdx, blockSize, &dictParams, NULL, NULL, 0);
1061         }
1062 #else
1063         (void)dictCLevel; (void)dictSelect; (void)dictID;  (void)maxDictSize; /* not used when ZSTD_NODICT set */
1064         DISPLAYLEVEL(1, "training mode not available \n");
1065         operationResult = 1;
1066 #endif
1067         goto _end;
1068     }
1069 
1070 #ifndef ZSTD_NODECOMPRESS
1071     if (operation==zom_test) { outFileName=nulmark; FIO_setRemoveSrcFile(prefs, 0); }  /* test mode */
1072 #endif
1073 
1074     /* No input filename ==> use stdin and stdout */
1075     filenameIdx += !filenameIdx;   /* filenameTable[0] is stdin by default */
1076     if (!strcmp(filenameTable[0], stdinmark) && !outFileName)
1077         outFileName = stdoutmark;  /* when input is stdin, default output is stdout */
1078 
1079     /* Check if input/output defined as console; trigger an error in this case */
1080     if (!strcmp(filenameTable[0], stdinmark) && IS_CONSOLE(stdin) )
1081         CLEAN_RETURN(badusage(programName));
1082     if ( outFileName && !strcmp(outFileName, stdoutmark)
1083       && IS_CONSOLE(stdout)
1084       && !strcmp(filenameTable[0], stdinmark)
1085       && !forceStdout
1086       && operation!=zom_decompress )
1087         CLEAN_RETURN(badusage(programName));
1088 
1089 #ifndef ZSTD_NOCOMPRESS
1090     /* check compression level limits */
1091     {   int const maxCLevel = ultra ? ZSTD_maxCLevel() : ZSTDCLI_CLEVEL_MAX;
1092         if (cLevel > maxCLevel) {
1093             DISPLAYLEVEL(2, "Warning : compression level higher than max, reduced to %i \n", maxCLevel);
1094             cLevel = maxCLevel;
1095     }   }
1096 #endif
1097 
1098     /* No status message in pipe mode (stdin - stdout) or multi-files mode */
1099     if (!strcmp(filenameTable[0], stdinmark) && outFileName && !strcmp(outFileName,stdoutmark) && (g_displayLevel==2)) g_displayLevel=1;
1100     if ((filenameIdx>1) & (g_displayLevel==2)) g_displayLevel=1;
1101 
1102     /* IO Stream/File */
1103     FIO_setNotificationLevel(g_displayLevel);
1104     if (operation==zom_compress) {
1105 #ifndef ZSTD_NOCOMPRESS
1106         FIO_setNbWorkers(prefs, nbWorkers);
1107         FIO_setBlockSize(prefs, (U32)blockSize);
1108         if (g_overlapLog!=OVERLAP_LOG_DEFAULT) FIO_setOverlapLog(prefs, g_overlapLog);
1109         FIO_setLdmFlag(prefs, ldmFlag);
1110         FIO_setLdmHashLog(prefs, g_ldmHashLog);
1111         FIO_setLdmMinMatch(prefs, g_ldmMinMatch);
1112         if (g_ldmBucketSizeLog != LDM_PARAM_DEFAULT) FIO_setLdmBucketSizeLog(prefs, g_ldmBucketSizeLog);
1113         if (g_ldmHashRateLog != LDM_PARAM_DEFAULT) FIO_setLdmHashRateLog(prefs, g_ldmHashRateLog);
1114         FIO_setAdaptiveMode(prefs, adapt);
1115         FIO_setAdaptMin(prefs, adaptMin);
1116         FIO_setAdaptMax(prefs, adaptMax);
1117         FIO_setRsyncable(prefs, rsyncable);
1118         FIO_setLiteralCompressionMode(prefs, literalCompressionMode);
1119         if (adaptMin > cLevel) cLevel = adaptMin;
1120         if (adaptMax < cLevel) cLevel = adaptMax;
1121 
1122         if ((filenameIdx==1) && outFileName)
1123           operationResult = FIO_compressFilename(prefs, outFileName, filenameTable[0], dictFileName, cLevel, compressionParams);
1124         else
1125           operationResult = FIO_compressMultipleFilenames(prefs, filenameTable, filenameIdx, outFileName, suffix, dictFileName, cLevel, compressionParams);
1126 #else
1127         (void)suffix; (void)adapt; (void)rsyncable; (void)ultra; (void)cLevel; (void)ldmFlag; (void)literalCompressionMode; /* not used when ZSTD_NOCOMPRESS set */
1128         DISPLAY("Compression not supported \n");
1129 #endif
1130     } else {  /* decompression or test */
1131 #ifndef ZSTD_NODECOMPRESS
1132         if (memLimit == 0) {
1133             if (compressionParams.windowLog == 0)
1134                 memLimit = (U32)1 << g_defaultMaxWindowLog;
1135             else {
1136                 memLimit = (U32)1 << (compressionParams.windowLog & 31);
1137             }
1138         }
1139         FIO_setMemLimit(prefs, memLimit);
1140         if (filenameIdx==1 && outFileName)
1141             operationResult = FIO_decompressFilename(prefs, outFileName, filenameTable[0], dictFileName);
1142         else
1143             operationResult = FIO_decompressMultipleFilenames(prefs, filenameTable, filenameIdx, outFileName, dictFileName);
1144 #else
1145         DISPLAY("Decompression not supported \n");
1146 #endif
1147     }
1148 
1149 _end:
1150     FIO_freePreferences(prefs);
1151 
1152     if (main_pause) waitEnter();
1153 #ifdef UTIL_HAS_CREATEFILELIST
1154     if (extendedFileList)
1155         UTIL_freeFileList(extendedFileList, fileNamesBuf);
1156     else
1157 #endif
1158         free((void*)filenameTable);
1159     return operationResult;
1160 }
1161