blob: 526b61d1d18c236f8748821e80bd32de7e23ad19 [file] [log] [blame]
Abseil Teamaa468ad2019-05-07 12:56:42 -07001//
2// Copyright 2019 The Abseil Authors.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// https://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16#include "absl/flags/parse.h"
17
18#include <stdlib.h>
Abseil Teamc6c3c1b2019-07-17 16:35:47 -040019
Abseil Team564001a2020-01-10 08:42:35 -080020#include <algorithm>
Abseil Team74d8b4d2023-01-04 04:10:37 -080021#include <cstdint>
Gennadiy Rozental297efcc2023-03-08 10:52:30 -080022#include <cstdlib>
Abseil Teamaa468ad2019-05-07 12:56:42 -070023#include <fstream>
24#include <iostream>
Gennadiy Rozentale73b9132023-04-06 11:43:21 -070025#include <ostream>
Abseil Team564001a2020-01-10 08:42:35 -080026#include <string>
Abseil Teamaa468ad2019-05-07 12:56:42 -070027#include <tuple>
Abseil Team564001a2020-01-10 08:42:35 -080028#include <utility>
29#include <vector>
Abseil Teamaa468ad2019-05-07 12:56:42 -070030
31#ifdef _WIN32
32#include <windows.h>
33#endif
34
Valery Mironov9ccc6102023-01-06 01:20:58 +030035#include "absl/algorithm/container.h"
Abseil Team564001a2020-01-10 08:42:35 -080036#include "absl/base/attributes.h"
37#include "absl/base/config.h"
38#include "absl/base/const_init.h"
39#include "absl/base/thread_annotations.h"
Abseil Teamcf1a02e2020-05-20 00:23:37 -070040#include "absl/flags/commandlineflag.h"
Abseil Team564001a2020-01-10 08:42:35 -080041#include "absl/flags/config.h"
Abseil Teamaa468ad2019-05-07 12:56:42 -070042#include "absl/flags/flag.h"
Abseil Teamda3a8762020-06-02 11:09:12 -070043#include "absl/flags/internal/commandlineflag.h"
Abseil Team564001a2020-01-10 08:42:35 -080044#include "absl/flags/internal/flag.h"
45#include "absl/flags/internal/parse.h"
Abseil Teama35ef8a2020-05-07 10:42:26 -070046#include "absl/flags/internal/private_handle_accessor.h"
Abseil Teamaa468ad2019-05-07 12:56:42 -070047#include "absl/flags/internal/program_name.h"
Abseil Teamaa468ad2019-05-07 12:56:42 -070048#include "absl/flags/internal/usage.h"
Abseil Team1d31b5c2020-06-03 14:03:13 -070049#include "absl/flags/reflection.h"
Abseil Teamc6c3c1b2019-07-17 16:35:47 -040050#include "absl/flags/usage.h"
Abseil Teamaa468ad2019-05-07 12:56:42 -070051#include "absl/flags/usage_config.h"
Abseil Team564001a2020-01-10 08:42:35 -080052#include "absl/strings/ascii.h"
Abseil Team74d8b4d2023-01-04 04:10:37 -080053#include "absl/strings/internal/damerau_levenshtein_distance.h"
Abseil Teamaa468ad2019-05-07 12:56:42 -070054#include "absl/strings/str_cat.h"
Abseil Team74d8b4d2023-01-04 04:10:37 -080055#include "absl/strings/str_join.h"
Abseil Team564001a2020-01-10 08:42:35 -080056#include "absl/strings/string_view.h"
Abseil Teamaa468ad2019-05-07 12:56:42 -070057#include "absl/strings/strip.h"
58#include "absl/synchronization/mutex.h"
59
60// --------------------------------------------------------------------
61
62namespace absl {
Abseil Team12bc53e2019-12-12 10:36:03 -080063ABSL_NAMESPACE_BEGIN
Abseil Teamaa468ad2019-05-07 12:56:42 -070064namespace flags_internal {
65namespace {
66
67ABSL_CONST_INIT absl::Mutex processing_checks_guard(absl::kConstInit);
68
69ABSL_CONST_INIT bool flagfile_needs_processing
Abseil Team83c1d652019-09-05 02:54:58 -070070 ABSL_GUARDED_BY(processing_checks_guard) = false;
Abseil Teamaa468ad2019-05-07 12:56:42 -070071ABSL_CONST_INIT bool fromenv_needs_processing
Abseil Team83c1d652019-09-05 02:54:58 -070072 ABSL_GUARDED_BY(processing_checks_guard) = false;
Abseil Teamaa468ad2019-05-07 12:56:42 -070073ABSL_CONST_INIT bool tryfromenv_needs_processing
Abseil Team83c1d652019-09-05 02:54:58 -070074 ABSL_GUARDED_BY(processing_checks_guard) = false;
Abseil Teamaa468ad2019-05-07 12:56:42 -070075
Abseil Teamc45d1c02020-05-08 10:36:00 -070076ABSL_CONST_INIT absl::Mutex specified_flags_guard(absl::kConstInit);
77ABSL_CONST_INIT std::vector<const CommandLineFlag*>* specified_flags
78 ABSL_GUARDED_BY(specified_flags_guard) = nullptr;
79
Abseil Team74d8b4d2023-01-04 04:10:37 -080080// Suggesting at most kMaxHints flags in case of misspellings.
81ABSL_CONST_INIT const size_t kMaxHints = 100;
82// Suggesting only flags which have a smaller distance than kMaxDistance.
83ABSL_CONST_INIT const size_t kMaxDistance = 3;
84
Abseil Teamc45d1c02020-05-08 10:36:00 -070085struct SpecifiedFlagsCompare {
86 bool operator()(const CommandLineFlag* a, const CommandLineFlag* b) const {
87 return a->Name() < b->Name();
88 }
89 bool operator()(const CommandLineFlag* a, absl::string_view b) const {
90 return a->Name() < b;
91 }
92 bool operator()(absl::string_view a, const CommandLineFlag* b) const {
93 return a < b->Name();
94 }
95};
96
Abseil Teamaa468ad2019-05-07 12:56:42 -070097} // namespace
98} // namespace flags_internal
Abseil Team12bc53e2019-12-12 10:36:03 -080099ABSL_NAMESPACE_END
Abseil Teamaa468ad2019-05-07 12:56:42 -0700100} // namespace absl
101
Mark Barolak25800da2023-07-11 12:30:02 -0700102// These flags influence how command line flags are parsed and are only intended
103// to be set on the command line. Avoid reading or setting them from C++ code.
Abseil Teamaa468ad2019-05-07 12:56:42 -0700104ABSL_FLAG(std::vector<std::string>, flagfile, {},
105 "comma-separated list of files to load flags from")
106 .OnUpdate([]() {
107 if (absl::GetFlag(FLAGS_flagfile).empty()) return;
108
109 absl::MutexLock l(&absl::flags_internal::processing_checks_guard);
110
111 // Setting this flag twice before it is handled most likely an internal
112 // error and should be reviewed by developers.
113 if (absl::flags_internal::flagfile_needs_processing) {
114 ABSL_INTERNAL_LOG(WARNING, "flagfile set twice before it is handled");
115 }
116
117 absl::flags_internal::flagfile_needs_processing = true;
118 });
119ABSL_FLAG(std::vector<std::string>, fromenv, {},
120 "comma-separated list of flags to set from the environment"
121 " [use 'export FLAGS_flag1=value']")
122 .OnUpdate([]() {
123 if (absl::GetFlag(FLAGS_fromenv).empty()) return;
124
125 absl::MutexLock l(&absl::flags_internal::processing_checks_guard);
126
127 // Setting this flag twice before it is handled most likely an internal
128 // error and should be reviewed by developers.
129 if (absl::flags_internal::fromenv_needs_processing) {
130 ABSL_INTERNAL_LOG(WARNING, "fromenv set twice before it is handled.");
131 }
132
133 absl::flags_internal::fromenv_needs_processing = true;
134 });
135ABSL_FLAG(std::vector<std::string>, tryfromenv, {},
136 "comma-separated list of flags to try to set from the environment if "
137 "present")
138 .OnUpdate([]() {
139 if (absl::GetFlag(FLAGS_tryfromenv).empty()) return;
140
141 absl::MutexLock l(&absl::flags_internal::processing_checks_guard);
142
143 // Setting this flag twice before it is handled most likely an internal
144 // error and should be reviewed by developers.
145 if (absl::flags_internal::tryfromenv_needs_processing) {
146 ABSL_INTERNAL_LOG(WARNING,
147 "tryfromenv set twice before it is handled.");
148 }
149
150 absl::flags_internal::tryfromenv_needs_processing = true;
151 });
152
Mark Barolak25800da2023-07-11 12:30:02 -0700153// Rather than reading or setting --undefok from C++ code, please consider using
154// ABSL_RETIRED_FLAG instead.
Abseil Teamaa468ad2019-05-07 12:56:42 -0700155ABSL_FLAG(std::vector<std::string>, undefok, {},
156 "comma-separated list of flag names that it is okay to specify "
157 "on the command line even if the program does not define a flag "
158 "with that name");
159
160namespace absl {
Abseil Team12bc53e2019-12-12 10:36:03 -0800161ABSL_NAMESPACE_BEGIN
Abseil Teamaa468ad2019-05-07 12:56:42 -0700162namespace flags_internal {
163
164namespace {
165
166class ArgsList {
167 public:
168 ArgsList() : next_arg_(0) {}
169 ArgsList(int argc, char* argv[]) : args_(argv, argv + argc), next_arg_(0) {}
170 explicit ArgsList(const std::vector<std::string>& args)
171 : args_(args), next_arg_(0) {}
172
173 // Returns success status: true if parsing successful, false otherwise.
174 bool ReadFromFlagfile(const std::string& flag_file_name);
175
Abseil Team69199fc2022-09-08 12:21:40 -0700176 size_t Size() const { return args_.size() - next_arg_; }
177 size_t FrontIndex() const { return next_arg_; }
Abseil Teamaa468ad2019-05-07 12:56:42 -0700178 absl::string_view Front() const { return args_[next_arg_]; }
179 void PopFront() { next_arg_++; }
180
181 private:
182 std::vector<std::string> args_;
Abseil Team69199fc2022-09-08 12:21:40 -0700183 size_t next_arg_;
Abseil Teamaa468ad2019-05-07 12:56:42 -0700184};
185
186bool ArgsList::ReadFromFlagfile(const std::string& flag_file_name) {
187 std::ifstream flag_file(flag_file_name);
188
189 if (!flag_file) {
190 flags_internal::ReportUsageError(
191 absl::StrCat("Can't open flagfile ", flag_file_name), true);
192
193 return false;
194 }
195
196 // This argument represents fake argv[0], which should be present in all arg
197 // lists.
Rose6089a042023-02-16 11:45:51 -0500198 args_.emplace_back("");
Abseil Teamaa468ad2019-05-07 12:56:42 -0700199
200 std::string line;
201 bool success = true;
202
203 while (std::getline(flag_file, line)) {
204 absl::string_view stripped = absl::StripLeadingAsciiWhitespace(line);
205
206 if (stripped.empty() || stripped[0] == '#') {
207 // Comment or empty line; just ignore.
208 continue;
209 }
210
211 if (stripped[0] == '-') {
212 if (stripped == "--") {
213 flags_internal::ReportUsageError(
214 "Flagfile can't contain position arguments or --", true);
215
216 success = false;
217 break;
218 }
219
Rose6089a042023-02-16 11:45:51 -0500220 args_.emplace_back(stripped);
Abseil Teamaa468ad2019-05-07 12:56:42 -0700221 continue;
222 }
223
224 flags_internal::ReportUsageError(
225 absl::StrCat("Unexpected line in the flagfile ", flag_file_name, ": ",
226 line),
227 true);
228
229 success = false;
230 }
231
232 return success;
233}
234
235// --------------------------------------------------------------------
236
237// Reads the environment variable with name `name` and stores results in
238// `value`. If variable is not present in environment returns false, otherwise
239// returns true.
Abseil Team33caf102020-05-26 10:57:33 -0700240bool GetEnvVar(const char* var_name, std::string& var_value) {
Abseil Teamaa468ad2019-05-07 12:56:42 -0700241#ifdef _WIN32
242 char buf[1024];
243 auto get_res = GetEnvironmentVariableA(var_name, buf, sizeof(buf));
244 if (get_res >= sizeof(buf)) {
Abseil Teamce65f5a2019-05-22 05:06:50 -0700245 return false;
Abseil Teamaa468ad2019-05-07 12:56:42 -0700246 }
247
248 if (get_res == 0) {
249 return false;
250 }
251
Abseil Team33caf102020-05-26 10:57:33 -0700252 var_value = std::string(buf, get_res);
Abseil Teamaa468ad2019-05-07 12:56:42 -0700253#else
254 const char* val = ::getenv(var_name);
255 if (val == nullptr) {
256 return false;
257 }
258
Abseil Team33caf102020-05-26 10:57:33 -0700259 var_value = val;
Abseil Teamaa468ad2019-05-07 12:56:42 -0700260#endif
261
262 return true;
263}
264
265// --------------------------------------------------------------------
266
267// Returns:
268// Flag name or empty if arg= --
269// Flag value after = in --flag=value (empty if --foo)
270// "Is empty value" status. True if arg= --foo=, false otherwise. This is
271// required to separate --foo from --foo=.
272// For example:
273// arg return values
274// "--foo=bar" -> {"foo", "bar", false}.
275// "--foo" -> {"foo", "", false}.
276// "--foo=" -> {"foo", "", true}.
277std::tuple<absl::string_view, absl::string_view, bool> SplitNameAndValue(
278 absl::string_view arg) {
279 // Allow -foo and --foo
280 absl::ConsumePrefix(&arg, "-");
281
282 if (arg.empty()) {
283 return std::make_tuple("", "", false);
284 }
285
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800286 auto equal_sign_pos = arg.find('=');
Abseil Teamaa468ad2019-05-07 12:56:42 -0700287
288 absl::string_view flag_name = arg.substr(0, equal_sign_pos);
289
290 absl::string_view value;
291 bool is_empty_value = false;
292
293 if (equal_sign_pos != absl::string_view::npos) {
294 value = arg.substr(equal_sign_pos + 1);
295 is_empty_value = value.empty();
296 }
297
298 return std::make_tuple(flag_name, value, is_empty_value);
299}
300
301// --------------------------------------------------------------------
302
303// Returns:
304// found flag or nullptr
305// is negative in case of --nofoo
306std::tuple<CommandLineFlag*, bool> LocateFlag(absl::string_view flag_name) {
Abseil Team1d31b5c2020-06-03 14:03:13 -0700307 CommandLineFlag* flag = absl::FindCommandLineFlag(flag_name);
Abseil Teamaa468ad2019-05-07 12:56:42 -0700308 bool is_negative = false;
309
310 if (!flag && absl::ConsumePrefix(&flag_name, "no")) {
Abseil Team1d31b5c2020-06-03 14:03:13 -0700311 flag = absl::FindCommandLineFlag(flag_name);
Abseil Teamaa468ad2019-05-07 12:56:42 -0700312 is_negative = true;
313 }
314
315 return std::make_tuple(flag, is_negative);
316}
317
318// --------------------------------------------------------------------
319
320// Verify that default values of typed flags must be convertible to string and
321// back.
322void CheckDefaultValuesParsingRoundtrip() {
323#ifndef NDEBUG
Abseil Team33caf102020-05-26 10:57:33 -0700324 flags_internal::ForEachFlag([&](CommandLineFlag& flag) {
325 if (flag.IsRetired()) return;
Abseil Teamaa468ad2019-05-07 12:56:42 -0700326
Abseil Team768eb2c2020-05-18 17:53:14 -0700327#define ABSL_FLAGS_INTERNAL_IGNORE_TYPE(T, _) \
Abseil Team33caf102020-05-26 10:57:33 -0700328 if (flag.IsOfType<T>()) return;
Abseil Teamaa468ad2019-05-07 12:56:42 -0700329
Abseil Team768eb2c2020-05-18 17:53:14 -0700330 ABSL_FLAGS_INTERNAL_SUPPORTED_TYPES(ABSL_FLAGS_INTERNAL_IGNORE_TYPE)
331#undef ABSL_FLAGS_INTERNAL_IGNORE_TYPE
Abseil Teamaa468ad2019-05-07 12:56:42 -0700332
Abseil Teama35ef8a2020-05-07 10:42:26 -0700333 flags_internal::PrivateHandleAccessor::CheckDefaultValueParsingRoundtrip(
Abseil Team33caf102020-05-26 10:57:33 -0700334 flag);
Abseil Teamaa468ad2019-05-07 12:56:42 -0700335 });
336#endif
337}
338
339// --------------------------------------------------------------------
340
341// Returns success status, which is true if we successfully read all flag files,
342// in which case new ArgLists are appended to the input_args in a reverse order
343// of file names in the input flagfiles list. This order ensures that flags from
344// the first flagfile in the input list are processed before the second flagfile
345// etc.
346bool ReadFlagfiles(const std::vector<std::string>& flagfiles,
Abseil Team33caf102020-05-26 10:57:33 -0700347 std::vector<ArgsList>& input_args) {
Abseil Teamaa468ad2019-05-07 12:56:42 -0700348 bool success = true;
349 for (auto it = flagfiles.rbegin(); it != flagfiles.rend(); ++it) {
350 ArgsList al;
351
352 if (al.ReadFromFlagfile(*it)) {
Abseil Team33caf102020-05-26 10:57:33 -0700353 input_args.push_back(al);
Abseil Teamaa468ad2019-05-07 12:56:42 -0700354 } else {
355 success = false;
356 }
357 }
358
359 return success;
360}
361
362// Returns success status, which is true if were able to locate all environment
363// variables correctly or if fail_on_absent_in_env is false. The environment
364// variable names are expected to be of the form `FLAGS_<flag_name>`, where
365// `flag_name` is a string from the input flag_names list. If successful we
366// append a single ArgList at the end of the input_args.
367bool ReadFlagsFromEnv(const std::vector<std::string>& flag_names,
Abseil Team33caf102020-05-26 10:57:33 -0700368 std::vector<ArgsList>& input_args,
Abseil Teamaa468ad2019-05-07 12:56:42 -0700369 bool fail_on_absent_in_env) {
370 bool success = true;
371 std::vector<std::string> args;
372
373 // This argument represents fake argv[0], which should be present in all arg
374 // lists.
Rose6089a042023-02-16 11:45:51 -0500375 args.emplace_back("");
Abseil Teamaa468ad2019-05-07 12:56:42 -0700376
377 for (const auto& flag_name : flag_names) {
378 // Avoid infinite recursion.
379 if (flag_name == "fromenv" || flag_name == "tryfromenv") {
380 flags_internal::ReportUsageError(
381 absl::StrCat("Infinite recursion on flag ", flag_name), true);
382
383 success = false;
384 continue;
385 }
386
387 const std::string envname = absl::StrCat("FLAGS_", flag_name);
388 std::string envval;
Abseil Team33caf102020-05-26 10:57:33 -0700389 if (!GetEnvVar(envname.c_str(), envval)) {
Abseil Teamaa468ad2019-05-07 12:56:42 -0700390 if (fail_on_absent_in_env) {
391 flags_internal::ReportUsageError(
392 absl::StrCat(envname, " not found in environment"), true);
393
394 success = false;
395 }
396
397 continue;
398 }
399
400 args.push_back(absl::StrCat("--", flag_name, "=", envval));
401 }
402
403 if (success) {
Abseil Team33caf102020-05-26 10:57:33 -0700404 input_args.emplace_back(args);
Abseil Teamaa468ad2019-05-07 12:56:42 -0700405 }
406
407 return success;
408}
409
410// --------------------------------------------------------------------
411
412// Returns success status, which is true if were able to handle all generator
413// flags (flagfile, fromenv, tryfromemv) successfully.
Abseil Team33caf102020-05-26 10:57:33 -0700414bool HandleGeneratorFlags(std::vector<ArgsList>& input_args,
415 std::vector<std::string>& flagfile_value) {
Abseil Teamaa468ad2019-05-07 12:56:42 -0700416 bool success = true;
417
418 absl::MutexLock l(&flags_internal::processing_checks_guard);
419
420 // flagfile could have been set either on a command line or
421 // programmatically before invoking ParseCommandLine. Note that we do not
422 // actually process arguments specified in the flagfile, but instead
423 // create a secondary arguments list to be processed along with the rest
Vertexwahn9c32e502023-04-27 22:10:09 +0200424 // of the command line arguments. Since we always the process most recently
Abseil Teamaa468ad2019-05-07 12:56:42 -0700425 // created list of arguments first, this will result in flagfile argument
426 // being processed before any other argument in the command line. If
427 // FLAGS_flagfile contains more than one file name we create multiple new
428 // levels of arguments in a reverse order of file names. Thus we always
429 // process arguments from first file before arguments containing in a
430 // second file, etc. If flagfile contains another
431 // --flagfile inside of it, it will produce new level of arguments and
432 // processed before the rest of the flagfile. We are also collecting all
433 // flagfiles set on original command line. Unlike the rest of the flags,
434 // this flag can be set multiple times and is expected to be handled
435 // multiple times. We are collecting them all into a single list and set
436 // the value of FLAGS_flagfile to that value at the end of the parsing.
437 if (flags_internal::flagfile_needs_processing) {
438 auto flagfiles = absl::GetFlag(FLAGS_flagfile);
439
Abseil Team33caf102020-05-26 10:57:33 -0700440 if (input_args.size() == 1) {
441 flagfile_value.insert(flagfile_value.end(), flagfiles.begin(),
442 flagfiles.end());
Abseil Teamaa468ad2019-05-07 12:56:42 -0700443 }
444
445 success &= ReadFlagfiles(flagfiles, input_args);
446
447 flags_internal::flagfile_needs_processing = false;
448 }
449
450 // Similar to flagfile fromenv/tryfromemv can be set both
451 // programmatically and at runtime on a command line. Unlike flagfile these
452 // can't be recursive.
453 if (flags_internal::fromenv_needs_processing) {
454 auto flags_list = absl::GetFlag(FLAGS_fromenv);
455
456 success &= ReadFlagsFromEnv(flags_list, input_args, true);
457
458 flags_internal::fromenv_needs_processing = false;
459 }
460
461 if (flags_internal::tryfromenv_needs_processing) {
462 auto flags_list = absl::GetFlag(FLAGS_tryfromenv);
463
464 success &= ReadFlagsFromEnv(flags_list, input_args, false);
465
466 flags_internal::tryfromenv_needs_processing = false;
467 }
468
469 return success;
470}
471
472// --------------------------------------------------------------------
473
474void ResetGeneratorFlags(const std::vector<std::string>& flagfile_value) {
475 // Setting flagfile to the value which collates all the values set on a
476 // command line and programmatically. So if command line looked like
477 // --flagfile=f1 --flagfile=f2 the final value of the FLAGS_flagfile flag is
478 // going to be {"f1", "f2"}
479 if (!flagfile_value.empty()) {
480 absl::SetFlag(&FLAGS_flagfile, flagfile_value);
481 absl::MutexLock l(&flags_internal::processing_checks_guard);
482 flags_internal::flagfile_needs_processing = false;
483 }
484
485 // fromenv/tryfromenv are set to <undefined> value.
486 if (!absl::GetFlag(FLAGS_fromenv).empty()) {
487 absl::SetFlag(&FLAGS_fromenv, {});
488 }
489 if (!absl::GetFlag(FLAGS_tryfromenv).empty()) {
490 absl::SetFlag(&FLAGS_tryfromenv, {});
491 }
492
493 absl::MutexLock l(&flags_internal::processing_checks_guard);
494 flags_internal::fromenv_needs_processing = false;
495 flags_internal::tryfromenv_needs_processing = false;
496}
497
498// --------------------------------------------------------------------
499
500// Returns:
501// success status
502// deduced value
503// We are also mutating curr_list in case if we need to get a hold of next
504// argument in the input.
505std::tuple<bool, absl::string_view> DeduceFlagValue(const CommandLineFlag& flag,
506 absl::string_view value,
507 bool is_negative,
508 bool is_empty_value,
509 ArgsList* curr_list) {
510 // Value is either an argument suffix after `=` in "--foo=<value>"
511 // or separate argument in case of "--foo" "<value>".
512
513 // boolean flags have these forms:
514 // --foo
515 // --nofoo
516 // --foo=true
517 // --foo=false
518 // --nofoo=<value> is not supported
519 // --foo <value> is not supported
520
521 // non boolean flags have these forms:
522 // --foo=<value>
523 // --foo <value>
524 // --nofoo is not supported
525
526 if (flag.IsOfType<bool>()) {
527 if (value.empty()) {
528 if (is_empty_value) {
529 // "--bool_flag=" case
530 flags_internal::ReportUsageError(
531 absl::StrCat(
532 "Missing the value after assignment for the boolean flag '",
533 flag.Name(), "'"),
534 true);
535 return std::make_tuple(false, "");
536 }
537
538 // "--bool_flag" case
539 value = is_negative ? "0" : "1";
540 } else if (is_negative) {
541 // "--nobool_flag=Y" case
542 flags_internal::ReportUsageError(
543 absl::StrCat("Negative form with assignment is not valid for the "
544 "boolean flag '",
545 flag.Name(), "'"),
546 true);
547 return std::make_tuple(false, "");
548 }
549 } else if (is_negative) {
550 // "--noint_flag=1" case
551 flags_internal::ReportUsageError(
552 absl::StrCat("Negative form is not valid for the flag '", flag.Name(),
553 "'"),
554 true);
555 return std::make_tuple(false, "");
556 } else if (value.empty() && (!is_empty_value)) {
557 if (curr_list->Size() == 1) {
558 // "--int_flag" case
559 flags_internal::ReportUsageError(
560 absl::StrCat("Missing the value for the flag '", flag.Name(), "'"),
561 true);
562 return std::make_tuple(false, "");
563 }
564
565 // "--int_flag" "10" case
566 curr_list->PopFront();
567 value = curr_list->Front();
568
Abseil Teama877af12020-03-10 09:28:06 -0700569 // Heuristic to detect the case where someone treats a string arg
Abseil Teamaa468ad2019-05-07 12:56:42 -0700570 // like a bool or just forgets to pass a value:
571 // --my_string_var --foo=bar
Abseil Teama877af12020-03-10 09:28:06 -0700572 // We look for a flag of string type, whose value begins with a
Abseil Teamaa468ad2019-05-07 12:56:42 -0700573 // dash and corresponds to known flag or standalone --.
Abseil Team29235132019-12-18 11:46:26 -0800574 if (!value.empty() && value[0] == '-' && flag.IsOfType<std::string>()) {
Abseil Teamaa468ad2019-05-07 12:56:42 -0700575 auto maybe_flag_name = std::get<0>(SplitNameAndValue(value.substr(1)));
576
577 if (maybe_flag_name.empty() ||
578 std::get<0>(LocateFlag(maybe_flag_name)) != nullptr) {
579 // "--string_flag" "--known_flag" case
580 ABSL_INTERNAL_LOG(
581 WARNING,
582 absl::StrCat("Did you really mean to set flag '", flag.Name(),
583 "' to the value '", value, "'?"));
584 }
585 }
586 }
587
588 return std::make_tuple(true, value);
589}
590
591// --------------------------------------------------------------------
592
593bool CanIgnoreUndefinedFlag(absl::string_view flag_name) {
594 auto undefok = absl::GetFlag(FLAGS_undefok);
595 if (std::find(undefok.begin(), undefok.end(), flag_name) != undefok.end()) {
596 return true;
597 }
598
599 if (absl::ConsumePrefix(&flag_name, "no") &&
600 std::find(undefok.begin(), undefok.end(), flag_name) != undefok.end()) {
601 return true;
602 }
603
604 return false;
605}
606
Gennadiy Rozental6db185d2023-03-13 21:22:11 -0700607// --------------------------------------------------------------------
608
609void ReportUnrecognizedFlags(
610 const std::vector<UnrecognizedFlag>& unrecognized_flags,
611 bool report_as_fatal_error) {
612 for (const auto& unrecognized : unrecognized_flags) {
613 // Verify if flag_name has the "no" already removed
614 std::vector<std::string> misspelling_hints;
615 if (unrecognized.source == UnrecognizedFlag::kFromArgv) {
616 misspelling_hints =
617 flags_internal::GetMisspellingHints(unrecognized.flag_name);
618 }
619
620 if (misspelling_hints.empty()) {
621 flags_internal::ReportUsageError(
622 absl::StrCat("Unknown command line flag '", unrecognized.flag_name,
623 "'"),
624 report_as_fatal_error);
625 } else {
626 flags_internal::ReportUsageError(
627 absl::StrCat("Unknown command line flag '", unrecognized.flag_name,
628 "'. Did you mean: ",
629 absl::StrJoin(misspelling_hints, ", "), " ?"),
630 report_as_fatal_error);
631 }
632 }
633}
634
Abseil Teamaa468ad2019-05-07 12:56:42 -0700635} // namespace
636
637// --------------------------------------------------------------------
638
Abseil Teamc45d1c02020-05-08 10:36:00 -0700639bool WasPresentOnCommandLine(absl::string_view flag_name) {
Abseil Teamd2de5312023-08-02 11:40:36 -0700640 absl::ReaderMutexLock l(&specified_flags_guard);
Abseil Teamc45d1c02020-05-08 10:36:00 -0700641 ABSL_INTERNAL_CHECK(specified_flags != nullptr,
642 "ParseCommandLine is not invoked yet");
643
644 return std::binary_search(specified_flags->begin(), specified_flags->end(),
645 flag_name, SpecifiedFlagsCompare{});
646}
647
648// --------------------------------------------------------------------
649
Abseil Team74d8b4d2023-01-04 04:10:37 -0800650struct BestHints {
651 explicit BestHints(uint8_t _max) : best_distance(_max + 1) {}
652 bool AddHint(absl::string_view hint, uint8_t distance) {
653 if (hints.size() >= kMaxHints) return false;
654 if (distance == best_distance) {
655 hints.emplace_back(hint);
656 }
657 if (distance < best_distance) {
658 best_distance = distance;
659 hints = std::vector<std::string>{std::string(hint)};
660 }
661 return true;
662 }
663
664 uint8_t best_distance;
665 std::vector<std::string> hints;
666};
667
668// Return the list of flags with the smallest Damerau-Levenshtein distance to
669// the given flag.
670std::vector<std::string> GetMisspellingHints(const absl::string_view flag) {
671 const size_t maxCutoff = std::min(flag.size() / 2 + 1, kMaxDistance);
672 auto undefok = absl::GetFlag(FLAGS_undefok);
673 BestHints best_hints(static_cast<uint8_t>(maxCutoff));
Gennadiy Rozental81927242023-03-21 01:04:30 -0700674 flags_internal::ForEachFlag([&](const CommandLineFlag& f) {
Abseil Team74d8b4d2023-01-04 04:10:37 -0800675 if (best_hints.hints.size() >= kMaxHints) return;
676 uint8_t distance = strings_internal::CappedDamerauLevenshteinDistance(
677 flag, f.Name(), best_hints.best_distance);
678 best_hints.AddHint(f.Name(), distance);
679 // For boolean flags, also calculate distance to the negated form.
680 if (f.IsOfType<bool>()) {
681 const std::string negated_flag = absl::StrCat("no", f.Name());
682 distance = strings_internal::CappedDamerauLevenshteinDistance(
683 flag, negated_flag, best_hints.best_distance);
684 best_hints.AddHint(negated_flag, distance);
685 }
686 });
687 // Finally calculate distance to flags in "undefok".
688 absl::c_for_each(undefok, [&](const absl::string_view f) {
689 if (best_hints.hints.size() >= kMaxHints) return;
690 uint8_t distance = strings_internal::CappedDamerauLevenshteinDistance(
691 flag, f, best_hints.best_distance);
692 best_hints.AddHint(absl::StrCat(f, " (undefok)"), distance);
693 });
694 return best_hints.hints;
695}
696
697// --------------------------------------------------------------------
698
Abseil Teamaa468ad2019-05-07 12:56:42 -0700699std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800700 UsageFlagsAction usage_flag_action,
Gennadiy Rozentale73b9132023-04-06 11:43:21 -0700701 OnUndefinedFlag undef_flag_action,
702 std::ostream& error_help_output) {
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800703 std::vector<char*> positional_args;
704 std::vector<UnrecognizedFlag> unrecognized_flags;
705
Gennadiy Rozental81927242023-03-21 01:04:30 -0700706 auto help_mode = flags_internal::ParseAbseilFlagsOnlyImpl(
707 argc, argv, positional_args, unrecognized_flags, usage_flag_action);
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800708
709 if (undef_flag_action != OnUndefinedFlag::kIgnoreUndefined) {
Gennadiy Rozental6db185d2023-03-13 21:22:11 -0700710 flags_internal::ReportUnrecognizedFlags(
711 unrecognized_flags,
Gennadiy Rozental81927242023-03-21 01:04:30 -0700712 (undef_flag_action == OnUndefinedFlag::kAbortIfUndefined));
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800713
Gennadiy Rozental81927242023-03-21 01:04:30 -0700714 if (undef_flag_action == OnUndefinedFlag::kAbortIfUndefined) {
Gennadiy Rozental1bd60dc2023-03-24 02:24:00 -0700715 if (!unrecognized_flags.empty()) {
Gennadiy Rozentale73b9132023-04-06 11:43:21 -0700716 flags_internal::HandleUsageFlags(error_help_output,
717 ProgramUsageMessage()); std::exit(1);
Gennadiy Rozental1bd60dc2023-03-24 02:24:00 -0700718 }
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800719 }
720 }
721
Gennadiy Rozental81927242023-03-21 01:04:30 -0700722 flags_internal::MaybeExit(help_mode);
723
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800724 return positional_args;
725}
726
727// --------------------------------------------------------------------
728
Gennadiy Rozental81927242023-03-21 01:04:30 -0700729// This function handles all Abseil Flags and built-in usage flags and, if any
730// help mode was handled, it returns that help mode. The caller of this function
731// can decide to exit based on the returned help mode.
732// The caller may decide to handle unrecognized positional arguments and
733// unrecognized flags first before exiting.
734//
735// Returns:
736// * HelpMode::kFull if parsing errors were detected in recognized arguments
737// * The HelpMode that was handled in case when `usage_flag_action` is
738// UsageFlagsAction::kHandleUsage and a usage flag was specified on the
739// commandline
740// * Otherwise it returns HelpMode::kNone
741HelpMode ParseAbseilFlagsOnlyImpl(
742 int argc, char* argv[], std::vector<char*>& positional_args,
743 std::vector<UnrecognizedFlag>& unrecognized_flags,
744 UsageFlagsAction usage_flag_action) {
Abseil Teamaa468ad2019-05-07 12:56:42 -0700745 ABSL_INTERNAL_CHECK(argc > 0, "Missing argv[0]");
746
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800747 using flags_internal::ArgsList;
748 using flags_internal::specified_flags;
749
750 std::vector<std::string> flagfile_value;
751 std::vector<ArgsList> input_args;
752
753 // Once parsing has started we will not allow more flag registrations.
Abseil Team4b2fbb42020-10-12 10:33:47 -0700754 flags_internal::FinalizeRegistry();
755
Abseil Teamaa468ad2019-05-07 12:56:42 -0700756 // This routine does not return anything since we abort on failure.
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800757 flags_internal::CheckDefaultValuesParsingRoundtrip();
Abseil Teamaa468ad2019-05-07 12:56:42 -0700758
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800759 input_args.push_back(ArgsList(argc, argv));
Abseil Teamaa468ad2019-05-07 12:56:42 -0700760
761 // Set program invocation name if it is not set before.
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800762 if (flags_internal::ProgramInvocationName() == "UNKNOWN") {
Abseil Teamaa468ad2019-05-07 12:56:42 -0700763 flags_internal::SetProgramInvocationName(argv[0]);
764 }
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800765 positional_args.push_back(argv[0]);
Abseil Teamaa468ad2019-05-07 12:56:42 -0700766
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800767 absl::MutexLock l(&flags_internal::specified_flags_guard);
Abseil Teamc45d1c02020-05-08 10:36:00 -0700768 if (specified_flags == nullptr) {
769 specified_flags = new std::vector<const CommandLineFlag*>;
770 } else {
771 specified_flags->clear();
772 }
773
Gennadiy Rozental81927242023-03-21 01:04:30 -0700774 // Iterate through the list of the input arguments. First level are
775 // arguments originated from argc/argv. Following levels are arguments
776 // originated from recursive parsing of flagfile(s).
Abseil Teamaa468ad2019-05-07 12:56:42 -0700777 bool success = true;
778 while (!input_args.empty()) {
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800779 // First we process the built-in generator flags.
780 success &= flags_internal::HandleGeneratorFlags(input_args, flagfile_value);
Abseil Teamaa468ad2019-05-07 12:56:42 -0700781
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800782 // Select top-most (most recent) arguments list. If it is empty drop it
Abseil Teamaa468ad2019-05-07 12:56:42 -0700783 // and re-try.
784 ArgsList& curr_list = input_args.back();
785
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800786 // Every ArgsList starts with real or fake program name, so we can always
787 // start by skipping it.
Abseil Teamaa468ad2019-05-07 12:56:42 -0700788 curr_list.PopFront();
789
790 if (curr_list.Size() == 0) {
791 input_args.pop_back();
792 continue;
793 }
794
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800795 // Handle the next argument in the current list. If the stack of argument
Gennadiy Rozental81927242023-03-21 01:04:30 -0700796 // lists contains only one element - we are processing an argument from
797 // the original argv.
Abseil Teamaa468ad2019-05-07 12:56:42 -0700798 absl::string_view arg(curr_list.Front());
799 bool arg_from_argv = input_args.size() == 1;
800
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800801 // If argument does not start with '-' or is just "-" - this is
Abseil Teamaa468ad2019-05-07 12:56:42 -0700802 // positional argument.
803 if (!absl::ConsumePrefix(&arg, "-") || arg.empty()) {
804 ABSL_INTERNAL_CHECK(arg_from_argv,
805 "Flagfile cannot contain positional argument");
806
807 positional_args.push_back(argv[curr_list.FrontIndex()]);
808 continue;
809 }
810
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800811 // Split the current argument on '=' to deduce the argument flag name and
812 // value. If flag name is empty it means we've got an "--" argument. Value
Abseil Teama877af12020-03-10 09:28:06 -0700813 // can be empty either if there were no '=' in argument string at all or
Abseil Teamaa468ad2019-05-07 12:56:42 -0700814 // an argument looked like "--foo=". In a latter case is_empty_value is
815 // true.
816 absl::string_view flag_name;
817 absl::string_view value;
818 bool is_empty_value = false;
819
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800820 std::tie(flag_name, value, is_empty_value) =
821 flags_internal::SplitNameAndValue(arg);
Abseil Teamaa468ad2019-05-07 12:56:42 -0700822
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800823 // Standalone "--" argument indicates that the rest of the arguments are
824 // positional. We do not support positional arguments in flagfiles.
Abseil Teamaa468ad2019-05-07 12:56:42 -0700825 if (flag_name.empty()) {
826 ABSL_INTERNAL_CHECK(arg_from_argv,
827 "Flagfile cannot contain positional argument");
828
829 curr_list.PopFront();
830 break;
831 }
832
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800833 // Locate the flag based on flag name. Handle both --foo and --nofoo.
Abseil Teamaa468ad2019-05-07 12:56:42 -0700834 CommandLineFlag* flag = nullptr;
835 bool is_negative = false;
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800836 std::tie(flag, is_negative) = flags_internal::LocateFlag(flag_name);
Abseil Teamaa468ad2019-05-07 12:56:42 -0700837
838 if (flag == nullptr) {
Abseil Team4fd9a1e2020-11-19 11:40:19 -0800839 // Usage flags are not modeled as Abseil flags. Locate them separately.
840 if (flags_internal::DeduceUsageFlags(flag_name, value)) {
841 continue;
842 }
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800843 unrecognized_flags.emplace_back(arg_from_argv
844 ? UnrecognizedFlag::kFromArgv
845 : UnrecognizedFlag::kFromFlagfile,
846 flag_name);
Abseil Teamaa468ad2019-05-07 12:56:42 -0700847 continue;
848 }
849
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800850 // Deduce flag's value (from this or next argument).
Abseil Teamaa468ad2019-05-07 12:56:42 -0700851 bool value_success = true;
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800852 std::tie(value_success, value) = flags_internal::DeduceFlagValue(
853 *flag, value, is_negative, is_empty_value, &curr_list);
Abseil Teamaa468ad2019-05-07 12:56:42 -0700854 success &= value_success;
855
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800856 // Set the located flag to a new value, unless it is retired. Setting
857 // retired flag fails, but we ignoring it here while also reporting access
858 // to retired flag.
Abseil Teamaa468ad2019-05-07 12:56:42 -0700859 std::string error;
Abseil Teama35ef8a2020-05-07 10:42:26 -0700860 if (!flags_internal::PrivateHandleAccessor::ParseFrom(
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800861 *flag, value, flags_internal::SET_FLAGS_VALUE,
862 flags_internal::kCommandLine, error)) {
Abseil Team81f34df2020-06-30 13:08:50 -0700863 if (flag->IsRetired()) continue;
864
Abseil Teamaa468ad2019-05-07 12:56:42 -0700865 flags_internal::ReportUsageError(error, true);
866 success = false;
Abseil Teamc45d1c02020-05-08 10:36:00 -0700867 } else {
868 specified_flags->push_back(flag);
Abseil Teamaa468ad2019-05-07 12:56:42 -0700869 }
870 }
871
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800872 flags_internal::ResetGeneratorFlags(flagfile_value);
Abseil Teamaa468ad2019-05-07 12:56:42 -0700873
874 // All the remaining arguments are positional.
875 if (!input_args.empty()) {
Abseil Team69199fc2022-09-08 12:21:40 -0700876 for (size_t arg_index = input_args.back().FrontIndex();
877 arg_index < static_cast<size_t>(argc); ++arg_index) {
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800878 positional_args.push_back(argv[arg_index]);
Abseil Teamaa468ad2019-05-07 12:56:42 -0700879 }
880 }
881
Abseil Teamc45d1c02020-05-08 10:36:00 -0700882 // Trim and sort the vector.
883 specified_flags->shrink_to_fit();
884 std::sort(specified_flags->begin(), specified_flags->end(),
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800885 flags_internal::SpecifiedFlagsCompare{});
886
887 // Filter out unrecognized flags, which are ok to ignore.
888 std::vector<UnrecognizedFlag> filtered;
889 filtered.reserve(unrecognized_flags.size());
890 for (const auto& unrecognized : unrecognized_flags) {
891 if (flags_internal::CanIgnoreUndefinedFlag(unrecognized.flag_name))
892 continue;
893 filtered.push_back(unrecognized);
894 }
895
896 std::swap(unrecognized_flags, filtered);
897
Gennadiy Rozental81927242023-03-21 01:04:30 -0700898 if (!success) {
899#if ABSL_FLAGS_STRIP_NAMES
900 flags_internal::ReportUsageError(
901 "NOTE: command line flags are disabled in this build", true);
902#else
Gennadiy Rozental1bd60dc2023-03-24 02:24:00 -0700903 flags_internal::HandleUsageFlags(std::cerr, ProgramUsageMessage());
Gennadiy Rozental81927242023-03-21 01:04:30 -0700904#endif
905 return HelpMode::kFull; // We just need to make sure the exit with
906 // code 1.
907 }
908
909 return usage_flag_action == UsageFlagsAction::kHandleUsage
910 ? flags_internal::HandleUsageFlags(std::cout,
911 ProgramUsageMessage())
912 : HelpMode::kNone;
913}
914
915} // namespace flags_internal
916
917void ParseAbseilFlagsOnly(int argc, char* argv[],
918 std::vector<char*>& positional_args,
919 std::vector<UnrecognizedFlag>& unrecognized_flags) {
920 auto help_mode = flags_internal::ParseAbseilFlagsOnlyImpl(
921 argc, argv, positional_args, unrecognized_flags,
922 flags_internal::UsageFlagsAction::kHandleUsage);
923
924 flags_internal::MaybeExit(help_mode);
Abseil Teamaa468ad2019-05-07 12:56:42 -0700925}
926
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800927// --------------------------------------------------------------------
928
929void ReportUnrecognizedFlags(
Gennadiy Rozental6db185d2023-03-13 21:22:11 -0700930 const std::vector<UnrecognizedFlag>& unrecognized_flags) {
931 flags_internal::ReportUnrecognizedFlags(unrecognized_flags, true);
Gennadiy Rozental297efcc2023-03-08 10:52:30 -0800932}
Abseil Teamaa468ad2019-05-07 12:56:42 -0700933
934// --------------------------------------------------------------------
935
936std::vector<char*> ParseCommandLine(int argc, char* argv[]) {
937 return flags_internal::ParseCommandLineImpl(
Gennadiy Rozental0c1114c2023-02-27 12:18:10 -0800938 argc, argv, flags_internal::UsageFlagsAction::kHandleUsage,
Abseil Teamaa468ad2019-05-07 12:56:42 -0700939 flags_internal::OnUndefinedFlag::kAbortIfUndefined);
940}
941
Abseil Team12bc53e2019-12-12 10:36:03 -0800942ABSL_NAMESPACE_END
Abseil Teamaa468ad2019-05-07 12:56:42 -0700943} // namespace absl