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