1 //
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>
19
20 #include <algorithm>
21 #include <cstdint>
22 #include <cstdlib>
23 #include <fstream>
24 #include <iostream>
25 #include <ostream>
26 #include <string>
27 #include <tuple>
28 #include <utility>
29 #include <vector>
30
31 #ifdef _WIN32
32 #include <windows.h>
33 #endif
34
35 #include "absl/algorithm/container.h"
36 #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"
40 #include "absl/flags/commandlineflag.h"
41 #include "absl/flags/config.h"
42 #include "absl/flags/flag.h"
43 #include "absl/flags/internal/commandlineflag.h"
44 #include "absl/flags/internal/flag.h"
45 #include "absl/flags/internal/parse.h"
46 #include "absl/flags/internal/private_handle_accessor.h"
47 #include "absl/flags/internal/program_name.h"
48 #include "absl/flags/internal/usage.h"
49 #include "absl/flags/reflection.h"
50 #include "absl/flags/usage.h"
51 #include "absl/flags/usage_config.h"
52 #include "absl/strings/ascii.h"
53 #include "absl/strings/internal/damerau_levenshtein_distance.h"
54 #include "absl/strings/str_cat.h"
55 #include "absl/strings/str_join.h"
56 #include "absl/strings/string_view.h"
57 #include "absl/strings/strip.h"
58 #include "absl/synchronization/mutex.h"
59
60 // --------------------------------------------------------------------
61
62 namespace absl {
63 ABSL_NAMESPACE_BEGIN
64 namespace flags_internal {
65 namespace {
66
67 ABSL_CONST_INIT absl::Mutex processing_checks_guard(absl::kConstInit);
68
69 ABSL_CONST_INIT bool flagfile_needs_processing
70 ABSL_GUARDED_BY(processing_checks_guard) = false;
71 ABSL_CONST_INIT bool fromenv_needs_processing
72 ABSL_GUARDED_BY(processing_checks_guard) = false;
73 ABSL_CONST_INIT bool tryfromenv_needs_processing
74 ABSL_GUARDED_BY(processing_checks_guard) = false;
75
76 ABSL_CONST_INIT absl::Mutex specified_flags_guard(absl::kConstInit);
77 ABSL_CONST_INIT std::vector<const CommandLineFlag*>* specified_flags
78 ABSL_GUARDED_BY(specified_flags_guard) = nullptr;
79
80 // Suggesting at most kMaxHints flags in case of misspellings.
81 ABSL_CONST_INIT const size_t kMaxHints = 100;
82 // Suggesting only flags which have a smaller distance than kMaxDistance.
83 ABSL_CONST_INIT const size_t kMaxDistance = 3;
84
85 struct SpecifiedFlagsCompare {
operator ()absl::flags_internal::__anon89871b300111::SpecifiedFlagsCompare86 bool operator()(const CommandLineFlag* a, const CommandLineFlag* b) const {
87 return a->Name() < b->Name();
88 }
operator ()absl::flags_internal::__anon89871b300111::SpecifiedFlagsCompare89 bool operator()(const CommandLineFlag* a, absl::string_view b) const {
90 return a->Name() < b;
91 }
operator ()absl::flags_internal::__anon89871b300111::SpecifiedFlagsCompare92 bool operator()(absl::string_view a, const CommandLineFlag* b) const {
93 return a < b->Name();
94 }
95 };
96
97 } // namespace
98 } // namespace flags_internal
99 ABSL_NAMESPACE_END
100 } // namespace absl
101
102 // 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.
104 ABSL_FLAG(std::vector<std::string>, flagfile, {},
105 "comma-separated list of files to load flags from")
__anon89871b300202() 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 });
119 ABSL_FLAG(std::vector<std::string>, fromenv, {},
120 "comma-separated list of flags to set from the environment"
121 " [use 'export FLAGS_flag1=value']")
__anon89871b300302() 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 });
135 ABSL_FLAG(std::vector<std::string>, tryfromenv, {},
136 "comma-separated list of flags to try to set from the environment if "
137 "present")
__anon89871b300402() 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
153 // Rather than reading or setting --undefok from C++ code, please consider using
154 // ABSL_RETIRED_FLAG instead.
155 ABSL_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
160 namespace absl {
161 ABSL_NAMESPACE_BEGIN
162 namespace flags_internal {
163
164 namespace {
165
166 class ArgsList {
167 public:
ArgsList()168 ArgsList() : next_arg_(0) {}
ArgsList(int argc,char * argv[])169 ArgsList(int argc, char* argv[]) : args_(argv, argv + argc), next_arg_(0) {}
ArgsList(const std::vector<std::string> & args)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
Size() const176 size_t Size() const { return args_.size() - next_arg_; }
FrontIndex() const177 size_t FrontIndex() const { return next_arg_; }
Front() const178 absl::string_view Front() const { return args_[next_arg_]; }
PopFront()179 void PopFront() { next_arg_++; }
180
181 private:
182 std::vector<std::string> args_;
183 size_t next_arg_;
184 };
185
ReadFromFlagfile(const std::string & flag_file_name)186 bool 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.
198 args_.emplace_back("");
199
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
220 args_.emplace_back(stripped);
221 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.
GetEnvVar(const char * var_name,std::string & var_value)240 bool GetEnvVar(const char* var_name, std::string& var_value) {
241 #ifdef _WIN32
242 char buf[1024];
243 auto get_res = GetEnvironmentVariableA(var_name, buf, sizeof(buf));
244 if (get_res >= sizeof(buf)) {
245 return false;
246 }
247
248 if (get_res == 0) {
249 return false;
250 }
251
252 var_value = std::string(buf, get_res);
253 #else
254 const char* val = ::getenv(var_name);
255 if (val == nullptr) {
256 return false;
257 }
258
259 var_value = val;
260 #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}.
SplitNameAndValue(absl::string_view arg)277 std::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
286 auto equal_sign_pos = arg.find('=');
287
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
LocateFlag(absl::string_view flag_name)306 std::tuple<CommandLineFlag*, bool> LocateFlag(absl::string_view flag_name) {
307 CommandLineFlag* flag = absl::FindCommandLineFlag(flag_name);
308 bool is_negative = false;
309
310 if (!flag && absl::ConsumePrefix(&flag_name, "no")) {
311 flag = absl::FindCommandLineFlag(flag_name);
312 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.
CheckDefaultValuesParsingRoundtrip()322 void CheckDefaultValuesParsingRoundtrip() {
323 #ifndef NDEBUG
324 flags_internal::ForEachFlag([&](CommandLineFlag& flag) {
325 if (flag.IsRetired()) return;
326
327 #define ABSL_FLAGS_INTERNAL_IGNORE_TYPE(T, _) \
328 if (flag.IsOfType<T>()) return;
329
330 ABSL_FLAGS_INTERNAL_SUPPORTED_TYPES(ABSL_FLAGS_INTERNAL_IGNORE_TYPE)
331 #undef ABSL_FLAGS_INTERNAL_IGNORE_TYPE
332
333 flags_internal::PrivateHandleAccessor::CheckDefaultValueParsingRoundtrip(
334 flag);
335 });
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.
ReadFlagfiles(const std::vector<std::string> & flagfiles,std::vector<ArgsList> & input_args)346 bool ReadFlagfiles(const std::vector<std::string>& flagfiles,
347 std::vector<ArgsList>& input_args) {
348 bool success = true;
349 for (auto it = flagfiles.rbegin(); it != flagfiles.rend(); ++it) {
350 ArgsList al;
351
352 if (al.ReadFromFlagfile(*it)) {
353 input_args.push_back(al);
354 } 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.
ReadFlagsFromEnv(const std::vector<std::string> & flag_names,std::vector<ArgsList> & input_args,bool fail_on_absent_in_env)367 bool ReadFlagsFromEnv(const std::vector<std::string>& flag_names,
368 std::vector<ArgsList>& input_args,
369 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.
375 args.emplace_back("");
376
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;
389 if (!GetEnvVar(envname.c_str(), envval)) {
390 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) {
404 input_args.emplace_back(args);
405 }
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.
HandleGeneratorFlags(std::vector<ArgsList> & input_args,std::vector<std::string> & flagfile_value)414 bool HandleGeneratorFlags(std::vector<ArgsList>& input_args,
415 std::vector<std::string>& flagfile_value) {
416 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
424 // of the command line arguments. Since we always the process most recently
425 // 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
440 if (input_args.size() == 1) {
441 flagfile_value.insert(flagfile_value.end(), flagfiles.begin(),
442 flagfiles.end());
443 }
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
ResetGeneratorFlags(const std::vector<std::string> & flagfile_value)474 void 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.
DeduceFlagValue(const CommandLineFlag & flag,absl::string_view value,bool is_negative,bool is_empty_value,ArgsList * curr_list)505 std::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
569 // Heuristic to detect the case where someone treats a string arg
570 // like a bool or just forgets to pass a value:
571 // --my_string_var --foo=bar
572 // We look for a flag of string type, whose value begins with a
573 // dash and corresponds to known flag or standalone --.
574 if (!value.empty() && value[0] == '-' && flag.IsOfType<std::string>()) {
575 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
CanIgnoreUndefinedFlag(absl::string_view flag_name)593 bool 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
607 // --------------------------------------------------------------------
608
ReportUnrecognizedFlags(const std::vector<UnrecognizedFlag> & unrecognized_flags,bool report_as_fatal_error)609 void 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
635 } // namespace
636
637 // --------------------------------------------------------------------
638
WasPresentOnCommandLine(absl::string_view flag_name)639 bool WasPresentOnCommandLine(absl::string_view flag_name) {
640 absl::MutexLock l(&specified_flags_guard);
641 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
650 struct BestHints {
BestHintsabsl::flags_internal::BestHints651 explicit BestHints(uint8_t _max) : best_distance(_max + 1) {}
AddHintabsl::flags_internal::BestHints652 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.
GetMisspellingHints(const absl::string_view flag)670 std::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));
674 flags_internal::ForEachFlag([&](const CommandLineFlag& f) {
675 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
ParseCommandLineImpl(int argc,char * argv[],UsageFlagsAction usage_flag_action,OnUndefinedFlag undef_flag_action,std::ostream & error_help_output)699 std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
700 UsageFlagsAction usage_flag_action,
701 OnUndefinedFlag undef_flag_action,
702 std::ostream& error_help_output) {
703 std::vector<char*> positional_args;
704 std::vector<UnrecognizedFlag> unrecognized_flags;
705
706 auto help_mode = flags_internal::ParseAbseilFlagsOnlyImpl(
707 argc, argv, positional_args, unrecognized_flags, usage_flag_action);
708
709 if (undef_flag_action != OnUndefinedFlag::kIgnoreUndefined) {
710 flags_internal::ReportUnrecognizedFlags(
711 unrecognized_flags,
712 (undef_flag_action == OnUndefinedFlag::kAbortIfUndefined));
713
714 if (undef_flag_action == OnUndefinedFlag::kAbortIfUndefined) {
715 if (!unrecognized_flags.empty()) {
716 flags_internal::HandleUsageFlags(error_help_output,
717 ProgramUsageMessage()); std::exit(1);
718 }
719 }
720 }
721
722 flags_internal::MaybeExit(help_mode);
723
724 return positional_args;
725 }
726
727 // --------------------------------------------------------------------
728
729 // 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
ParseAbseilFlagsOnlyImpl(int argc,char * argv[],std::vector<char * > & positional_args,std::vector<UnrecognizedFlag> & unrecognized_flags,UsageFlagsAction usage_flag_action)741 HelpMode ParseAbseilFlagsOnlyImpl(
742 int argc, char* argv[], std::vector<char*>& positional_args,
743 std::vector<UnrecognizedFlag>& unrecognized_flags,
744 UsageFlagsAction usage_flag_action) {
745 ABSL_INTERNAL_CHECK(argc > 0, "Missing argv[0]");
746
747 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.
754 flags_internal::FinalizeRegistry();
755
756 // This routine does not return anything since we abort on failure.
757 flags_internal::CheckDefaultValuesParsingRoundtrip();
758
759 input_args.push_back(ArgsList(argc, argv));
760
761 // Set program invocation name if it is not set before.
762 if (flags_internal::ProgramInvocationName() == "UNKNOWN") {
763 flags_internal::SetProgramInvocationName(argv[0]);
764 }
765 positional_args.push_back(argv[0]);
766
767 absl::MutexLock l(&flags_internal::specified_flags_guard);
768 if (specified_flags == nullptr) {
769 specified_flags = new std::vector<const CommandLineFlag*>;
770 } else {
771 specified_flags->clear();
772 }
773
774 // 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).
777 bool success = true;
778 while (!input_args.empty()) {
779 // First we process the built-in generator flags.
780 success &= flags_internal::HandleGeneratorFlags(input_args, flagfile_value);
781
782 // Select top-most (most recent) arguments list. If it is empty drop it
783 // and re-try.
784 ArgsList& curr_list = input_args.back();
785
786 // Every ArgsList starts with real or fake program name, so we can always
787 // start by skipping it.
788 curr_list.PopFront();
789
790 if (curr_list.Size() == 0) {
791 input_args.pop_back();
792 continue;
793 }
794
795 // Handle the next argument in the current list. If the stack of argument
796 // lists contains only one element - we are processing an argument from
797 // the original argv.
798 absl::string_view arg(curr_list.Front());
799 bool arg_from_argv = input_args.size() == 1;
800
801 // If argument does not start with '-' or is just "-" - this is
802 // 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
811 // 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
813 // can be empty either if there were no '=' in argument string at all or
814 // 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
820 std::tie(flag_name, value, is_empty_value) =
821 flags_internal::SplitNameAndValue(arg);
822
823 // Standalone "--" argument indicates that the rest of the arguments are
824 // positional. We do not support positional arguments in flagfiles.
825 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
833 // Locate the flag based on flag name. Handle both --foo and --nofoo.
834 CommandLineFlag* flag = nullptr;
835 bool is_negative = false;
836 std::tie(flag, is_negative) = flags_internal::LocateFlag(flag_name);
837
838 if (flag == nullptr) {
839 // Usage flags are not modeled as Abseil flags. Locate them separately.
840 if (flags_internal::DeduceUsageFlags(flag_name, value)) {
841 continue;
842 }
843 unrecognized_flags.emplace_back(arg_from_argv
844 ? UnrecognizedFlag::kFromArgv
845 : UnrecognizedFlag::kFromFlagfile,
846 flag_name);
847 continue;
848 }
849
850 // Deduce flag's value (from this or next argument).
851 bool value_success = true;
852 std::tie(value_success, value) = flags_internal::DeduceFlagValue(
853 *flag, value, is_negative, is_empty_value, &curr_list);
854 success &= value_success;
855
856 // 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.
859 std::string error;
860 if (!flags_internal::PrivateHandleAccessor::ParseFrom(
861 *flag, value, flags_internal::SET_FLAGS_VALUE,
862 flags_internal::kCommandLine, error)) {
863 if (flag->IsRetired()) continue;
864
865 flags_internal::ReportUsageError(error, true);
866 success = false;
867 } else {
868 specified_flags->push_back(flag);
869 }
870 }
871
872 flags_internal::ResetGeneratorFlags(flagfile_value);
873
874 // All the remaining arguments are positional.
875 if (!input_args.empty()) {
876 for (size_t arg_index = input_args.back().FrontIndex();
877 arg_index < static_cast<size_t>(argc); ++arg_index) {
878 positional_args.push_back(argv[arg_index]);
879 }
880 }
881
882 // Trim and sort the vector.
883 specified_flags->shrink_to_fit();
884 std::sort(specified_flags->begin(), specified_flags->end(),
885 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
898 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
903 flags_internal::HandleUsageFlags(std::cerr, ProgramUsageMessage());
904 #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
ParseAbseilFlagsOnly(int argc,char * argv[],std::vector<char * > & positional_args,std::vector<UnrecognizedFlag> & unrecognized_flags)917 void 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);
925 }
926
927 // --------------------------------------------------------------------
928
ReportUnrecognizedFlags(const std::vector<UnrecognizedFlag> & unrecognized_flags)929 void ReportUnrecognizedFlags(
930 const std::vector<UnrecognizedFlag>& unrecognized_flags) {
931 flags_internal::ReportUnrecognizedFlags(unrecognized_flags, true);
932 }
933
934 // --------------------------------------------------------------------
935
ParseCommandLine(int argc,char * argv[])936 std::vector<char*> ParseCommandLine(int argc, char* argv[]) {
937 return flags_internal::ParseCommandLineImpl(
938 argc, argv, flags_internal::UsageFlagsAction::kHandleUsage,
939 flags_internal::OnUndefinedFlag::kAbortIfUndefined);
940 }
941
942 ABSL_NAMESPACE_END
943 } // namespace absl
944