xref: /aosp_15_r20/external/zstd/contrib/pzstd/Options.cpp (revision 01826a4963a0d8a59bc3812d29bdf0fb76416722)
1*01826a49SYabin Cui /*
2*01826a49SYabin Cui  * Copyright (c) Meta Platforms, Inc. and affiliates.
3*01826a49SYabin Cui  * All rights reserved.
4*01826a49SYabin Cui  *
5*01826a49SYabin Cui  * This source code is licensed under both the BSD-style license (found in the
6*01826a49SYabin Cui  * LICENSE file in the root directory of this source tree) and the GPLv2 (found
7*01826a49SYabin Cui  * in the COPYING file in the root directory of this source tree).
8*01826a49SYabin Cui  */
9*01826a49SYabin Cui #include "Options.h"
10*01826a49SYabin Cui #include "util.h"
11*01826a49SYabin Cui #include "utils/ScopeGuard.h"
12*01826a49SYabin Cui 
13*01826a49SYabin Cui #include <algorithm>
14*01826a49SYabin Cui #include <cassert>
15*01826a49SYabin Cui #include <cstdio>
16*01826a49SYabin Cui #include <cstring>
17*01826a49SYabin Cui #include <iterator>
18*01826a49SYabin Cui #include <thread>
19*01826a49SYabin Cui #include <vector>
20*01826a49SYabin Cui 
21*01826a49SYabin Cui 
22*01826a49SYabin Cui namespace pzstd {
23*01826a49SYabin Cui 
24*01826a49SYabin Cui namespace {
defaultNumThreads()25*01826a49SYabin Cui unsigned defaultNumThreads() {
26*01826a49SYabin Cui #ifdef PZSTD_NUM_THREADS
27*01826a49SYabin Cui   return PZSTD_NUM_THREADS;
28*01826a49SYabin Cui #else
29*01826a49SYabin Cui   return std::thread::hardware_concurrency();
30*01826a49SYabin Cui #endif
31*01826a49SYabin Cui }
32*01826a49SYabin Cui 
parseUnsigned(const char ** arg)33*01826a49SYabin Cui unsigned parseUnsigned(const char **arg) {
34*01826a49SYabin Cui   unsigned result = 0;
35*01826a49SYabin Cui   while (**arg >= '0' && **arg <= '9') {
36*01826a49SYabin Cui     result *= 10;
37*01826a49SYabin Cui     result += **arg - '0';
38*01826a49SYabin Cui     ++(*arg);
39*01826a49SYabin Cui   }
40*01826a49SYabin Cui   return result;
41*01826a49SYabin Cui }
42*01826a49SYabin Cui 
getArgument(const char * options,const char ** argv,int & i,int argc)43*01826a49SYabin Cui const char *getArgument(const char *options, const char **argv, int &i,
44*01826a49SYabin Cui                         int argc) {
45*01826a49SYabin Cui   if (options[1] != 0) {
46*01826a49SYabin Cui     return options + 1;
47*01826a49SYabin Cui   }
48*01826a49SYabin Cui   ++i;
49*01826a49SYabin Cui   if (i == argc) {
50*01826a49SYabin Cui     std::fprintf(stderr, "Option -%c requires an argument, but none provided\n",
51*01826a49SYabin Cui                  *options);
52*01826a49SYabin Cui     return nullptr;
53*01826a49SYabin Cui   }
54*01826a49SYabin Cui   return argv[i];
55*01826a49SYabin Cui }
56*01826a49SYabin Cui 
57*01826a49SYabin Cui const std::string kZstdExtension = ".zst";
58*01826a49SYabin Cui constexpr char kStdIn[] = "-";
59*01826a49SYabin Cui constexpr char kStdOut[] = "-";
60*01826a49SYabin Cui constexpr unsigned kDefaultCompressionLevel = 3;
61*01826a49SYabin Cui constexpr unsigned kMaxNonUltraCompressionLevel = 19;
62*01826a49SYabin Cui 
63*01826a49SYabin Cui #ifdef _WIN32
64*01826a49SYabin Cui const char nullOutput[] = "nul";
65*01826a49SYabin Cui #else
66*01826a49SYabin Cui const char nullOutput[] = "/dev/null";
67*01826a49SYabin Cui #endif
68*01826a49SYabin Cui 
notSupported(const char * option)69*01826a49SYabin Cui void notSupported(const char *option) {
70*01826a49SYabin Cui   std::fprintf(stderr, "Operation not supported: %s\n", option);
71*01826a49SYabin Cui }
72*01826a49SYabin Cui 
usage()73*01826a49SYabin Cui void usage() {
74*01826a49SYabin Cui   std::fprintf(stderr, "Usage:\n");
75*01826a49SYabin Cui   std::fprintf(stderr, "  pzstd [args] [FILE(s)]\n");
76*01826a49SYabin Cui   std::fprintf(stderr, "Parallel ZSTD options:\n");
77*01826a49SYabin Cui   std::fprintf(stderr, "  -p, --processes   #    : number of threads to use for (de)compression (default:<numcpus>)\n");
78*01826a49SYabin Cui 
79*01826a49SYabin Cui   std::fprintf(stderr, "ZSTD options:\n");
80*01826a49SYabin Cui   std::fprintf(stderr, "  -#                     : # compression level (1-%d, default:%d)\n", kMaxNonUltraCompressionLevel, kDefaultCompressionLevel);
81*01826a49SYabin Cui   std::fprintf(stderr, "  -d, --decompress       : decompression\n");
82*01826a49SYabin Cui   std::fprintf(stderr, "  -o                file : result stored into `file` (only if 1 input file)\n");
83*01826a49SYabin Cui   std::fprintf(stderr, "  -f, --force            : overwrite output without prompting, (de)compress links\n");
84*01826a49SYabin Cui   std::fprintf(stderr, "      --rm               : remove source file(s) after successful (de)compression\n");
85*01826a49SYabin Cui   std::fprintf(stderr, "  -k, --keep             : preserve source file(s) (default)\n");
86*01826a49SYabin Cui   std::fprintf(stderr, "  -h, --help             : display help and exit\n");
87*01826a49SYabin Cui   std::fprintf(stderr, "  -V, --version          : display version number and exit\n");
88*01826a49SYabin Cui   std::fprintf(stderr, "  -v, --verbose          : verbose mode; specify multiple times to increase log level (default:2)\n");
89*01826a49SYabin Cui   std::fprintf(stderr, "  -q, --quiet            : suppress warnings; specify twice to suppress errors too\n");
90*01826a49SYabin Cui   std::fprintf(stderr, "  -c, --stdout           : write to standard output (even if it is the console)\n");
91*01826a49SYabin Cui #ifdef UTIL_HAS_CREATEFILELIST
92*01826a49SYabin Cui   std::fprintf(stderr, "  -r                     : operate recursively on directories\n");
93*01826a49SYabin Cui #endif
94*01826a49SYabin Cui   std::fprintf(stderr, "      --ultra            : enable levels beyond %i, up to %i (requires more memory)\n", kMaxNonUltraCompressionLevel, ZSTD_maxCLevel());
95*01826a49SYabin Cui   std::fprintf(stderr, "  -C, --check            : integrity check (default)\n");
96*01826a49SYabin Cui   std::fprintf(stderr, "      --no-check         : no integrity check\n");
97*01826a49SYabin Cui   std::fprintf(stderr, "  -t, --test             : test compressed file integrity\n");
98*01826a49SYabin Cui   std::fprintf(stderr, "  --                     : all arguments after \"--\" are treated as files\n");
99*01826a49SYabin Cui }
100*01826a49SYabin Cui } // anonymous namespace
101*01826a49SYabin Cui 
Options()102*01826a49SYabin Cui Options::Options()
103*01826a49SYabin Cui     : numThreads(defaultNumThreads()), maxWindowLog(23),
104*01826a49SYabin Cui       compressionLevel(kDefaultCompressionLevel), decompress(false),
105*01826a49SYabin Cui       overwrite(false), keepSource(true), writeMode(WriteMode::Auto),
106*01826a49SYabin Cui       checksum(true), verbosity(2) {}
107*01826a49SYabin Cui 
parse(int argc,const char ** argv)108*01826a49SYabin Cui Options::Status Options::parse(int argc, const char **argv) {
109*01826a49SYabin Cui   bool test = false;
110*01826a49SYabin Cui   bool recursive = false;
111*01826a49SYabin Cui   bool ultra = false;
112*01826a49SYabin Cui   bool forceStdout = false;
113*01826a49SYabin Cui   bool followLinks = false;
114*01826a49SYabin Cui   // Local copy of input files, which are pointers into argv.
115*01826a49SYabin Cui   std::vector<const char *> localInputFiles;
116*01826a49SYabin Cui   for (int i = 1; i < argc; ++i) {
117*01826a49SYabin Cui     const char *arg = argv[i];
118*01826a49SYabin Cui     // Protect against empty arguments
119*01826a49SYabin Cui     if (arg[0] == 0) {
120*01826a49SYabin Cui       continue;
121*01826a49SYabin Cui     }
122*01826a49SYabin Cui     // Everything after "--" is an input file
123*01826a49SYabin Cui     if (!std::strcmp(arg, "--")) {
124*01826a49SYabin Cui       ++i;
125*01826a49SYabin Cui       std::copy(argv + i, argv + argc, std::back_inserter(localInputFiles));
126*01826a49SYabin Cui       break;
127*01826a49SYabin Cui     }
128*01826a49SYabin Cui     // Long arguments that don't have a short option
129*01826a49SYabin Cui     {
130*01826a49SYabin Cui       bool isLongOption = true;
131*01826a49SYabin Cui       if (!std::strcmp(arg, "--rm")) {
132*01826a49SYabin Cui         keepSource = false;
133*01826a49SYabin Cui       } else if (!std::strcmp(arg, "--ultra")) {
134*01826a49SYabin Cui         ultra = true;
135*01826a49SYabin Cui         maxWindowLog = 0;
136*01826a49SYabin Cui       } else if (!std::strcmp(arg, "--no-check")) {
137*01826a49SYabin Cui         checksum = false;
138*01826a49SYabin Cui       } else if (!std::strcmp(arg, "--sparse")) {
139*01826a49SYabin Cui         writeMode = WriteMode::Sparse;
140*01826a49SYabin Cui         notSupported("Sparse mode");
141*01826a49SYabin Cui         return Status::Failure;
142*01826a49SYabin Cui       } else if (!std::strcmp(arg, "--no-sparse")) {
143*01826a49SYabin Cui         writeMode = WriteMode::Regular;
144*01826a49SYabin Cui         notSupported("Sparse mode");
145*01826a49SYabin Cui         return Status::Failure;
146*01826a49SYabin Cui       } else if (!std::strcmp(arg, "--dictID")) {
147*01826a49SYabin Cui         notSupported(arg);
148*01826a49SYabin Cui         return Status::Failure;
149*01826a49SYabin Cui       } else if (!std::strcmp(arg, "--no-dictID")) {
150*01826a49SYabin Cui         notSupported(arg);
151*01826a49SYabin Cui         return Status::Failure;
152*01826a49SYabin Cui       } else {
153*01826a49SYabin Cui         isLongOption = false;
154*01826a49SYabin Cui       }
155*01826a49SYabin Cui       if (isLongOption) {
156*01826a49SYabin Cui         continue;
157*01826a49SYabin Cui       }
158*01826a49SYabin Cui     }
159*01826a49SYabin Cui     // Arguments with a short option simply set their short option.
160*01826a49SYabin Cui     const char *options = nullptr;
161*01826a49SYabin Cui     if (!std::strcmp(arg, "--processes")) {
162*01826a49SYabin Cui       options = "p";
163*01826a49SYabin Cui     } else if (!std::strcmp(arg, "--version")) {
164*01826a49SYabin Cui       options = "V";
165*01826a49SYabin Cui     } else if (!std::strcmp(arg, "--help")) {
166*01826a49SYabin Cui       options = "h";
167*01826a49SYabin Cui     } else if (!std::strcmp(arg, "--decompress")) {
168*01826a49SYabin Cui       options = "d";
169*01826a49SYabin Cui     } else if (!std::strcmp(arg, "--force")) {
170*01826a49SYabin Cui       options = "f";
171*01826a49SYabin Cui     } else if (!std::strcmp(arg, "--stdout")) {
172*01826a49SYabin Cui       options = "c";
173*01826a49SYabin Cui     } else if (!std::strcmp(arg, "--keep")) {
174*01826a49SYabin Cui       options = "k";
175*01826a49SYabin Cui     } else if (!std::strcmp(arg, "--verbose")) {
176*01826a49SYabin Cui       options = "v";
177*01826a49SYabin Cui     } else if (!std::strcmp(arg, "--quiet")) {
178*01826a49SYabin Cui       options = "q";
179*01826a49SYabin Cui     } else if (!std::strcmp(arg, "--check")) {
180*01826a49SYabin Cui       options = "C";
181*01826a49SYabin Cui     } else if (!std::strcmp(arg, "--test")) {
182*01826a49SYabin Cui       options = "t";
183*01826a49SYabin Cui     } else if (arg[0] == '-' && arg[1] != 0) {
184*01826a49SYabin Cui       options = arg + 1;
185*01826a49SYabin Cui     } else {
186*01826a49SYabin Cui       localInputFiles.emplace_back(arg);
187*01826a49SYabin Cui       continue;
188*01826a49SYabin Cui     }
189*01826a49SYabin Cui     assert(options != nullptr);
190*01826a49SYabin Cui 
191*01826a49SYabin Cui     bool finished = false;
192*01826a49SYabin Cui     while (!finished && *options != 0) {
193*01826a49SYabin Cui       // Parse the compression level
194*01826a49SYabin Cui       if (*options >= '0' && *options <= '9') {
195*01826a49SYabin Cui         compressionLevel = parseUnsigned(&options);
196*01826a49SYabin Cui         continue;
197*01826a49SYabin Cui       }
198*01826a49SYabin Cui 
199*01826a49SYabin Cui       switch (*options) {
200*01826a49SYabin Cui       case 'h':
201*01826a49SYabin Cui       case 'H':
202*01826a49SYabin Cui         usage();
203*01826a49SYabin Cui         return Status::Message;
204*01826a49SYabin Cui       case 'V':
205*01826a49SYabin Cui         std::fprintf(stderr, "PZSTD version: %s.\n", ZSTD_VERSION_STRING);
206*01826a49SYabin Cui         return Status::Message;
207*01826a49SYabin Cui       case 'p': {
208*01826a49SYabin Cui         finished = true;
209*01826a49SYabin Cui         const char *optionArgument = getArgument(options, argv, i, argc);
210*01826a49SYabin Cui         if (optionArgument == nullptr) {
211*01826a49SYabin Cui           return Status::Failure;
212*01826a49SYabin Cui         }
213*01826a49SYabin Cui         if (*optionArgument < '0' || *optionArgument > '9') {
214*01826a49SYabin Cui           std::fprintf(stderr, "Option -p expects a number, but %s provided\n",
215*01826a49SYabin Cui                        optionArgument);
216*01826a49SYabin Cui           return Status::Failure;
217*01826a49SYabin Cui         }
218*01826a49SYabin Cui         numThreads = parseUnsigned(&optionArgument);
219*01826a49SYabin Cui         if (*optionArgument != 0) {
220*01826a49SYabin Cui           std::fprintf(stderr,
221*01826a49SYabin Cui                        "Option -p expects a number, but %u%s provided\n",
222*01826a49SYabin Cui                        numThreads, optionArgument);
223*01826a49SYabin Cui           return Status::Failure;
224*01826a49SYabin Cui         }
225*01826a49SYabin Cui         break;
226*01826a49SYabin Cui       }
227*01826a49SYabin Cui       case 'o': {
228*01826a49SYabin Cui         finished = true;
229*01826a49SYabin Cui         const char *optionArgument = getArgument(options, argv, i, argc);
230*01826a49SYabin Cui         if (optionArgument == nullptr) {
231*01826a49SYabin Cui           return Status::Failure;
232*01826a49SYabin Cui         }
233*01826a49SYabin Cui         outputFile = optionArgument;
234*01826a49SYabin Cui         break;
235*01826a49SYabin Cui       }
236*01826a49SYabin Cui       case 'C':
237*01826a49SYabin Cui         checksum = true;
238*01826a49SYabin Cui         break;
239*01826a49SYabin Cui       case 'k':
240*01826a49SYabin Cui         keepSource = true;
241*01826a49SYabin Cui         break;
242*01826a49SYabin Cui       case 'd':
243*01826a49SYabin Cui         decompress = true;
244*01826a49SYabin Cui         break;
245*01826a49SYabin Cui       case 'f':
246*01826a49SYabin Cui         overwrite = true;
247*01826a49SYabin Cui         forceStdout = true;
248*01826a49SYabin Cui         followLinks = true;
249*01826a49SYabin Cui         break;
250*01826a49SYabin Cui       case 't':
251*01826a49SYabin Cui         test = true;
252*01826a49SYabin Cui         decompress = true;
253*01826a49SYabin Cui         break;
254*01826a49SYabin Cui #ifdef UTIL_HAS_CREATEFILELIST
255*01826a49SYabin Cui       case 'r':
256*01826a49SYabin Cui         recursive = true;
257*01826a49SYabin Cui         break;
258*01826a49SYabin Cui #endif
259*01826a49SYabin Cui       case 'c':
260*01826a49SYabin Cui         outputFile = kStdOut;
261*01826a49SYabin Cui         forceStdout = true;
262*01826a49SYabin Cui         break;
263*01826a49SYabin Cui       case 'v':
264*01826a49SYabin Cui         ++verbosity;
265*01826a49SYabin Cui         break;
266*01826a49SYabin Cui       case 'q':
267*01826a49SYabin Cui         --verbosity;
268*01826a49SYabin Cui         // Ignore them for now
269*01826a49SYabin Cui         break;
270*01826a49SYabin Cui       // Unsupported options from Zstd
271*01826a49SYabin Cui       case 'D':
272*01826a49SYabin Cui       case 's':
273*01826a49SYabin Cui         notSupported("Zstd dictionaries.");
274*01826a49SYabin Cui         return Status::Failure;
275*01826a49SYabin Cui       case 'b':
276*01826a49SYabin Cui       case 'e':
277*01826a49SYabin Cui       case 'i':
278*01826a49SYabin Cui       case 'B':
279*01826a49SYabin Cui         notSupported("Zstd benchmarking options.");
280*01826a49SYabin Cui         return Status::Failure;
281*01826a49SYabin Cui       default:
282*01826a49SYabin Cui         std::fprintf(stderr, "Invalid argument: %s\n", arg);
283*01826a49SYabin Cui         return Status::Failure;
284*01826a49SYabin Cui       }
285*01826a49SYabin Cui       if (!finished) {
286*01826a49SYabin Cui         ++options;
287*01826a49SYabin Cui       }
288*01826a49SYabin Cui     } // while (*options != 0);
289*01826a49SYabin Cui   }   // for (int i = 1; i < argc; ++i);
290*01826a49SYabin Cui 
291*01826a49SYabin Cui   // Set options for test mode
292*01826a49SYabin Cui   if (test) {
293*01826a49SYabin Cui     outputFile = nullOutput;
294*01826a49SYabin Cui     keepSource = true;
295*01826a49SYabin Cui   }
296*01826a49SYabin Cui 
297*01826a49SYabin Cui   // Input file defaults to standard input if not provided.
298*01826a49SYabin Cui   if (localInputFiles.empty()) {
299*01826a49SYabin Cui     localInputFiles.emplace_back(kStdIn);
300*01826a49SYabin Cui   }
301*01826a49SYabin Cui 
302*01826a49SYabin Cui   // Check validity of input files
303*01826a49SYabin Cui   if (localInputFiles.size() > 1) {
304*01826a49SYabin Cui     const auto it = std::find(localInputFiles.begin(), localInputFiles.end(),
305*01826a49SYabin Cui                               std::string{kStdIn});
306*01826a49SYabin Cui     if (it != localInputFiles.end()) {
307*01826a49SYabin Cui       std::fprintf(
308*01826a49SYabin Cui           stderr,
309*01826a49SYabin Cui           "Cannot specify standard input when handling multiple files\n");
310*01826a49SYabin Cui       return Status::Failure;
311*01826a49SYabin Cui     }
312*01826a49SYabin Cui   }
313*01826a49SYabin Cui   if (localInputFiles.size() > 1 || recursive) {
314*01826a49SYabin Cui     if (!outputFile.empty() && outputFile != nullOutput) {
315*01826a49SYabin Cui       std::fprintf(
316*01826a49SYabin Cui           stderr,
317*01826a49SYabin Cui           "Cannot specify an output file when handling multiple inputs\n");
318*01826a49SYabin Cui       return Status::Failure;
319*01826a49SYabin Cui     }
320*01826a49SYabin Cui   }
321*01826a49SYabin Cui 
322*01826a49SYabin Cui   g_utilDisplayLevel = verbosity;
323*01826a49SYabin Cui   // Remove local input files that are symbolic links
324*01826a49SYabin Cui   if (!followLinks) {
325*01826a49SYabin Cui       std::remove_if(localInputFiles.begin(), localInputFiles.end(),
326*01826a49SYabin Cui                      [&](const char *path) {
327*01826a49SYabin Cui                         bool isLink = UTIL_isLink(path);
328*01826a49SYabin Cui                         if (isLink && verbosity >= 2) {
329*01826a49SYabin Cui                             std::fprintf(
330*01826a49SYabin Cui                                     stderr,
331*01826a49SYabin Cui                                     "Warning : %s is symbolic link, ignoring\n",
332*01826a49SYabin Cui                                     path);
333*01826a49SYabin Cui                         }
334*01826a49SYabin Cui                         return isLink;
335*01826a49SYabin Cui                     });
336*01826a49SYabin Cui   }
337*01826a49SYabin Cui 
338*01826a49SYabin Cui   // Translate input files/directories into files to (de)compress
339*01826a49SYabin Cui   if (recursive) {
340*01826a49SYabin Cui     FileNamesTable* const files = UTIL_createExpandedFNT(localInputFiles.data(), localInputFiles.size(), followLinks);
341*01826a49SYabin Cui     if (files == nullptr) {
342*01826a49SYabin Cui       std::fprintf(stderr, "Error traversing directories\n");
343*01826a49SYabin Cui       return Status::Failure;
344*01826a49SYabin Cui     }
345*01826a49SYabin Cui     auto guard =
346*01826a49SYabin Cui         makeScopeGuard([&] { UTIL_freeFileNamesTable(files); });
347*01826a49SYabin Cui     if (files->tableSize == 0) {
348*01826a49SYabin Cui       std::fprintf(stderr, "No files found\n");
349*01826a49SYabin Cui       return Status::Failure;
350*01826a49SYabin Cui     }
351*01826a49SYabin Cui     inputFiles.resize(files->tableSize);
352*01826a49SYabin Cui     std::copy(files->fileNames, files->fileNames + files->tableSize, inputFiles.begin());
353*01826a49SYabin Cui   } else {
354*01826a49SYabin Cui     inputFiles.resize(localInputFiles.size());
355*01826a49SYabin Cui     std::copy(localInputFiles.begin(), localInputFiles.end(),
356*01826a49SYabin Cui               inputFiles.begin());
357*01826a49SYabin Cui   }
358*01826a49SYabin Cui   localInputFiles.clear();
359*01826a49SYabin Cui   assert(!inputFiles.empty());
360*01826a49SYabin Cui 
361*01826a49SYabin Cui   // If reading from standard input, default to standard output
362*01826a49SYabin Cui   if (inputFiles[0] == kStdIn && outputFile.empty()) {
363*01826a49SYabin Cui     assert(inputFiles.size() == 1);
364*01826a49SYabin Cui     outputFile = "-";
365*01826a49SYabin Cui   }
366*01826a49SYabin Cui 
367*01826a49SYabin Cui   if (inputFiles[0] == kStdIn && IS_CONSOLE(stdin)) {
368*01826a49SYabin Cui     assert(inputFiles.size() == 1);
369*01826a49SYabin Cui     std::fprintf(stderr, "Cannot read input from interactive console\n");
370*01826a49SYabin Cui     return Status::Failure;
371*01826a49SYabin Cui   }
372*01826a49SYabin Cui   if (outputFile == "-" && IS_CONSOLE(stdout) && !(forceStdout && decompress)) {
373*01826a49SYabin Cui     std::fprintf(stderr, "Will not write to console stdout unless -c or -f is "
374*01826a49SYabin Cui                          "specified and decompressing\n");
375*01826a49SYabin Cui     return Status::Failure;
376*01826a49SYabin Cui   }
377*01826a49SYabin Cui 
378*01826a49SYabin Cui   // Check compression level
379*01826a49SYabin Cui   {
380*01826a49SYabin Cui     unsigned maxCLevel =
381*01826a49SYabin Cui         ultra ? ZSTD_maxCLevel() : kMaxNonUltraCompressionLevel;
382*01826a49SYabin Cui     if (compressionLevel > maxCLevel || compressionLevel == 0) {
383*01826a49SYabin Cui       std::fprintf(stderr, "Invalid compression level %u.\n", compressionLevel);
384*01826a49SYabin Cui       return Status::Failure;
385*01826a49SYabin Cui     }
386*01826a49SYabin Cui   }
387*01826a49SYabin Cui 
388*01826a49SYabin Cui   // Check that numThreads is set
389*01826a49SYabin Cui   if (numThreads == 0) {
390*01826a49SYabin Cui     std::fprintf(stderr, "Invalid arguments: # of threads not specified "
391*01826a49SYabin Cui                          "and unable to determine hardware concurrency.\n");
392*01826a49SYabin Cui     return Status::Failure;
393*01826a49SYabin Cui   }
394*01826a49SYabin Cui 
395*01826a49SYabin Cui   // Modify verbosity
396*01826a49SYabin Cui   // If we are piping input and output, turn off interaction
397*01826a49SYabin Cui   if (inputFiles[0] == kStdIn && outputFile == kStdOut && verbosity == 2) {
398*01826a49SYabin Cui     verbosity = 1;
399*01826a49SYabin Cui   }
400*01826a49SYabin Cui   // If we are in multi-file mode, turn off interaction
401*01826a49SYabin Cui   if (inputFiles.size() > 1 && verbosity == 2) {
402*01826a49SYabin Cui     verbosity = 1;
403*01826a49SYabin Cui   }
404*01826a49SYabin Cui 
405*01826a49SYabin Cui   return Status::Success;
406*01826a49SYabin Cui }
407*01826a49SYabin Cui 
getOutputFile(const std::string & inputFile) const408*01826a49SYabin Cui std::string Options::getOutputFile(const std::string &inputFile) const {
409*01826a49SYabin Cui   if (!outputFile.empty()) {
410*01826a49SYabin Cui     return outputFile;
411*01826a49SYabin Cui   }
412*01826a49SYabin Cui   // Attempt to add/remove zstd extension from the input file
413*01826a49SYabin Cui   if (decompress) {
414*01826a49SYabin Cui     int stemSize = inputFile.size() - kZstdExtension.size();
415*01826a49SYabin Cui     if (stemSize > 0 && inputFile.substr(stemSize) == kZstdExtension) {
416*01826a49SYabin Cui       return inputFile.substr(0, stemSize);
417*01826a49SYabin Cui     } else {
418*01826a49SYabin Cui       return "";
419*01826a49SYabin Cui     }
420*01826a49SYabin Cui   } else {
421*01826a49SYabin Cui     return inputFile + kZstdExtension;
422*01826a49SYabin Cui   }
423*01826a49SYabin Cui }
424*01826a49SYabin Cui }
425