parse.cc (32733B)
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/no_destructor.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::Mutex* ProcessingChecksMutex() { 68 static absl::NoDestructor<absl::Mutex> mutex; 69 return mutex.get(); 70 } 71 72 ABSL_CONST_INIT bool flagfile_needs_processing 73 ABSL_GUARDED_BY(ProcessingChecksMutex()) = false; 74 ABSL_CONST_INIT bool fromenv_needs_processing 75 ABSL_GUARDED_BY(ProcessingChecksMutex()) = false; 76 ABSL_CONST_INIT bool tryfromenv_needs_processing 77 ABSL_GUARDED_BY(ProcessingChecksMutex()) = false; 78 79 absl::Mutex* SpecifiedFlagsMutex() { 80 static absl::NoDestructor<absl::Mutex> mutex; 81 return mutex.get(); 82 } 83 84 ABSL_CONST_INIT std::vector<const CommandLineFlag*>* specified_flags 85 ABSL_GUARDED_BY(SpecifiedFlagsMutex()) = nullptr; 86 87 // Suggesting at most kMaxHints flags in case of misspellings. 88 ABSL_CONST_INIT const size_t kMaxHints = 100; 89 // Suggesting only flags which have a smaller distance than kMaxDistance. 90 ABSL_CONST_INIT const size_t kMaxDistance = 3; 91 92 struct SpecifiedFlagsCompare { 93 bool operator()(const CommandLineFlag* a, const CommandLineFlag* b) const { 94 return a->Name() < b->Name(); 95 } 96 bool operator()(const CommandLineFlag* a, absl::string_view b) const { 97 return a->Name() < b; 98 } 99 bool operator()(absl::string_view a, const CommandLineFlag* b) const { 100 return a < b->Name(); 101 } 102 }; 103 104 } // namespace 105 } // namespace flags_internal 106 ABSL_NAMESPACE_END 107 } // namespace absl 108 109 // These flags influence how command line flags are parsed and are only intended 110 // to be set on the command line. Avoid reading or setting them from C++ code. 111 ABSL_FLAG(std::vector<std::string>, flagfile, {}, 112 "comma-separated list of files to load flags from") 113 .OnUpdate([]() { 114 if (absl::GetFlag(FLAGS_flagfile).empty()) return; 115 116 absl::MutexLock l(absl::flags_internal::ProcessingChecksMutex()); 117 118 // Setting this flag twice before it is handled most likely an internal 119 // error and should be reviewed by developers. 120 if (absl::flags_internal::flagfile_needs_processing) { 121 ABSL_INTERNAL_LOG(WARNING, "flagfile set twice before it is handled"); 122 } 123 124 absl::flags_internal::flagfile_needs_processing = true; 125 }); 126 ABSL_FLAG(std::vector<std::string>, fromenv, {}, 127 "comma-separated list of flags to set from the environment" 128 " [use 'export FLAGS_flag1=value']") 129 .OnUpdate([]() { 130 if (absl::GetFlag(FLAGS_fromenv).empty()) return; 131 132 absl::MutexLock l(absl::flags_internal::ProcessingChecksMutex()); 133 134 // Setting this flag twice before it is handled most likely an internal 135 // error and should be reviewed by developers. 136 if (absl::flags_internal::fromenv_needs_processing) { 137 ABSL_INTERNAL_LOG(WARNING, "fromenv set twice before it is handled."); 138 } 139 140 absl::flags_internal::fromenv_needs_processing = true; 141 }); 142 ABSL_FLAG(std::vector<std::string>, tryfromenv, {}, 143 "comma-separated list of flags to try to set from the environment if " 144 "present") 145 .OnUpdate([]() { 146 if (absl::GetFlag(FLAGS_tryfromenv).empty()) return; 147 148 absl::MutexLock l(absl::flags_internal::ProcessingChecksMutex()); 149 150 // Setting this flag twice before it is handled most likely an internal 151 // error and should be reviewed by developers. 152 if (absl::flags_internal::tryfromenv_needs_processing) { 153 ABSL_INTERNAL_LOG(WARNING, 154 "tryfromenv set twice before it is handled."); 155 } 156 157 absl::flags_internal::tryfromenv_needs_processing = true; 158 }); 159 160 // Rather than reading or setting --undefok from C++ code, please consider using 161 // ABSL_RETIRED_FLAG instead. 162 ABSL_FLAG(std::vector<std::string>, undefok, {}, 163 "comma-separated list of flag names that it is okay to specify " 164 "on the command line even if the program does not define a flag " 165 "with that name"); 166 167 namespace absl { 168 ABSL_NAMESPACE_BEGIN 169 namespace flags_internal { 170 171 namespace { 172 173 class ArgsList { 174 public: 175 ArgsList() : next_arg_(0) {} 176 ArgsList(int argc, char* argv[]) : args_(argv, argv + argc), next_arg_(0) {} 177 explicit ArgsList(const std::vector<std::string>& args) 178 : args_(args), next_arg_(0) {} 179 180 // Returns success status: true if parsing successful, false otherwise. 181 bool ReadFromFlagfile(const std::string& flag_file_name); 182 183 size_t Size() const { return args_.size() - next_arg_; } 184 size_t FrontIndex() const { return next_arg_; } 185 absl::string_view Front() const { return args_[next_arg_]; } 186 void PopFront() { next_arg_++; } 187 188 private: 189 std::vector<std::string> args_; 190 size_t next_arg_; 191 }; 192 193 bool ArgsList::ReadFromFlagfile(const std::string& flag_file_name) { 194 std::ifstream flag_file(flag_file_name); 195 196 if (!flag_file) { 197 flags_internal::ReportUsageError( 198 absl::StrCat("Can't open flagfile ", flag_file_name), true); 199 200 return false; 201 } 202 203 // This argument represents fake argv[0], which should be present in all arg 204 // lists. 205 args_.emplace_back(""); 206 207 std::string line; 208 bool success = true; 209 210 while (std::getline(flag_file, line)) { 211 absl::string_view stripped = absl::StripLeadingAsciiWhitespace(line); 212 213 if (stripped.empty() || stripped[0] == '#') { 214 // Comment or empty line; just ignore. 215 continue; 216 } 217 218 if (stripped[0] == '-') { 219 if (stripped == "--") { 220 flags_internal::ReportUsageError( 221 "Flagfile can't contain position arguments or --", true); 222 223 success = false; 224 break; 225 } 226 227 args_.emplace_back(stripped); 228 continue; 229 } 230 231 flags_internal::ReportUsageError( 232 absl::StrCat("Unexpected line in the flagfile ", flag_file_name, ": ", 233 line), 234 true); 235 236 success = false; 237 } 238 239 return success; 240 } 241 242 // -------------------------------------------------------------------- 243 244 // Reads the environment variable with name `name` and stores results in 245 // `value`. If variable is not present in environment returns false, otherwise 246 // returns true. 247 bool GetEnvVar(const char* var_name, std::string& var_value) { 248 #ifdef _WIN32 249 char buf[1024]; 250 auto get_res = GetEnvironmentVariableA(var_name, buf, sizeof(buf)); 251 if (get_res >= sizeof(buf)) { 252 return false; 253 } 254 255 if (get_res == 0) { 256 return false; 257 } 258 259 var_value = std::string(buf, get_res); 260 #else 261 const char* val = ::getenv(var_name); 262 if (val == nullptr) { 263 return false; 264 } 265 266 var_value = val; 267 #endif 268 269 return true; 270 } 271 272 // -------------------------------------------------------------------- 273 274 // Returns: 275 // Flag name or empty if arg= -- 276 // Flag value after = in --flag=value (empty if --foo) 277 // "Is empty value" status. True if arg= --foo=, false otherwise. This is 278 // required to separate --foo from --foo=. 279 // For example: 280 // arg return values 281 // "--foo=bar" -> {"foo", "bar", false}. 282 // "--foo" -> {"foo", "", false}. 283 // "--foo=" -> {"foo", "", true}. 284 std::tuple<absl::string_view, absl::string_view, bool> SplitNameAndValue( 285 absl::string_view arg) { 286 // Allow -foo and --foo 287 absl::ConsumePrefix(&arg, "-"); 288 289 if (arg.empty()) { 290 return std::make_tuple("", "", false); 291 } 292 293 auto equal_sign_pos = arg.find('='); 294 295 absl::string_view flag_name = arg.substr(0, equal_sign_pos); 296 297 absl::string_view value; 298 bool is_empty_value = false; 299 300 if (equal_sign_pos != absl::string_view::npos) { 301 value = arg.substr(equal_sign_pos + 1); 302 is_empty_value = value.empty(); 303 } 304 305 return std::make_tuple(flag_name, value, is_empty_value); 306 } 307 308 // -------------------------------------------------------------------- 309 310 // Returns: 311 // found flag or nullptr 312 // is negative in case of --nofoo 313 std::tuple<CommandLineFlag*, bool> LocateFlag(absl::string_view flag_name) { 314 CommandLineFlag* flag = absl::FindCommandLineFlag(flag_name); 315 bool is_negative = false; 316 317 if (!flag && absl::ConsumePrefix(&flag_name, "no")) { 318 flag = absl::FindCommandLineFlag(flag_name); 319 is_negative = true; 320 } 321 322 return std::make_tuple(flag, is_negative); 323 } 324 325 // -------------------------------------------------------------------- 326 327 // Verify that default values of typed flags must be convertible to string and 328 // back. 329 void CheckDefaultValuesParsingRoundtrip() { 330 #ifndef NDEBUG 331 flags_internal::ForEachFlag([&](CommandLineFlag& flag) { 332 if (flag.IsRetired()) return; 333 334 #define ABSL_FLAGS_INTERNAL_IGNORE_TYPE(T, _) \ 335 if (flag.IsOfType<T>()) return; 336 337 ABSL_FLAGS_INTERNAL_SUPPORTED_TYPES(ABSL_FLAGS_INTERNAL_IGNORE_TYPE) 338 #undef ABSL_FLAGS_INTERNAL_IGNORE_TYPE 339 340 flags_internal::PrivateHandleAccessor::CheckDefaultValueParsingRoundtrip( 341 flag); 342 }); 343 #endif 344 } 345 346 // -------------------------------------------------------------------- 347 348 // Returns success status, which is true if we successfully read all flag files, 349 // in which case new ArgLists are appended to the input_args in a reverse order 350 // of file names in the input flagfiles list. This order ensures that flags from 351 // the first flagfile in the input list are processed before the second flagfile 352 // etc. 353 bool ReadFlagfiles(const std::vector<std::string>& flagfiles, 354 std::vector<ArgsList>& input_args) { 355 bool success = true; 356 for (auto it = flagfiles.rbegin(); it != flagfiles.rend(); ++it) { 357 ArgsList al; 358 359 if (al.ReadFromFlagfile(*it)) { 360 input_args.push_back(al); 361 } else { 362 success = false; 363 } 364 } 365 366 return success; 367 } 368 369 // Returns success status, which is true if were able to locate all environment 370 // variables correctly or if fail_on_absent_in_env is false. The environment 371 // variable names are expected to be of the form `FLAGS_<flag_name>`, where 372 // `flag_name` is a string from the input flag_names list. If successful we 373 // append a single ArgList at the end of the input_args. 374 bool ReadFlagsFromEnv(const std::vector<std::string>& flag_names, 375 std::vector<ArgsList>& input_args, 376 bool fail_on_absent_in_env) { 377 bool success = true; 378 std::vector<std::string> args; 379 380 // This argument represents fake argv[0], which should be present in all arg 381 // lists. 382 args.emplace_back(""); 383 384 for (const auto& flag_name : flag_names) { 385 // Avoid infinite recursion. 386 if (flag_name == "fromenv" || flag_name == "tryfromenv") { 387 flags_internal::ReportUsageError( 388 absl::StrCat("Infinite recursion on flag ", flag_name), true); 389 390 success = false; 391 continue; 392 } 393 394 const std::string envname = absl::StrCat("FLAGS_", flag_name); 395 std::string envval; 396 if (!GetEnvVar(envname.c_str(), envval)) { 397 if (fail_on_absent_in_env) { 398 flags_internal::ReportUsageError( 399 absl::StrCat(envname, " not found in environment"), true); 400 401 success = false; 402 } 403 404 continue; 405 } 406 407 args.push_back(absl::StrCat("--", flag_name, "=", envval)); 408 } 409 410 if (success) { 411 input_args.emplace_back(args); 412 } 413 414 return success; 415 } 416 417 // -------------------------------------------------------------------- 418 419 // Returns success status, which is true if were able to handle all generator 420 // flags (flagfile, fromenv, tryfromemv) successfully. 421 bool HandleGeneratorFlags(std::vector<ArgsList>& input_args, 422 std::vector<std::string>& flagfile_value) { 423 bool success = true; 424 425 absl::MutexLock l(flags_internal::ProcessingChecksMutex()); 426 427 // flagfile could have been set either on a command line or 428 // programmatically before invoking ParseCommandLine. Note that we do not 429 // actually process arguments specified in the flagfile, but instead 430 // create a secondary arguments list to be processed along with the rest 431 // of the command line arguments. Since we always the process most recently 432 // created list of arguments first, this will result in flagfile argument 433 // being processed before any other argument in the command line. If 434 // FLAGS_flagfile contains more than one file name we create multiple new 435 // levels of arguments in a reverse order of file names. Thus we always 436 // process arguments from first file before arguments containing in a 437 // second file, etc. If flagfile contains another 438 // --flagfile inside of it, it will produce new level of arguments and 439 // processed before the rest of the flagfile. We are also collecting all 440 // flagfiles set on original command line. Unlike the rest of the flags, 441 // this flag can be set multiple times and is expected to be handled 442 // multiple times. We are collecting them all into a single list and set 443 // the value of FLAGS_flagfile to that value at the end of the parsing. 444 if (flags_internal::flagfile_needs_processing) { 445 auto flagfiles = absl::GetFlag(FLAGS_flagfile); 446 447 if (input_args.size() == 1) { 448 flagfile_value.insert(flagfile_value.end(), flagfiles.begin(), 449 flagfiles.end()); 450 } 451 452 success &= ReadFlagfiles(flagfiles, input_args); 453 454 flags_internal::flagfile_needs_processing = false; 455 } 456 457 // Similar to flagfile fromenv/tryfromemv can be set both 458 // programmatically and at runtime on a command line. Unlike flagfile these 459 // can't be recursive. 460 if (flags_internal::fromenv_needs_processing) { 461 auto flags_list = absl::GetFlag(FLAGS_fromenv); 462 463 success &= ReadFlagsFromEnv(flags_list, input_args, true); 464 465 flags_internal::fromenv_needs_processing = false; 466 } 467 468 if (flags_internal::tryfromenv_needs_processing) { 469 auto flags_list = absl::GetFlag(FLAGS_tryfromenv); 470 471 success &= ReadFlagsFromEnv(flags_list, input_args, false); 472 473 flags_internal::tryfromenv_needs_processing = false; 474 } 475 476 return success; 477 } 478 479 // -------------------------------------------------------------------- 480 481 void ResetGeneratorFlags(const std::vector<std::string>& flagfile_value) { 482 // Setting flagfile to the value which collates all the values set on a 483 // command line and programmatically. So if command line looked like 484 // --flagfile=f1 --flagfile=f2 the final value of the FLAGS_flagfile flag is 485 // going to be {"f1", "f2"} 486 if (!flagfile_value.empty()) { 487 absl::SetFlag(&FLAGS_flagfile, flagfile_value); 488 absl::MutexLock l(flags_internal::ProcessingChecksMutex()); 489 flags_internal::flagfile_needs_processing = false; 490 } 491 492 // fromenv/tryfromenv are set to <undefined> value. 493 if (!absl::GetFlag(FLAGS_fromenv).empty()) { 494 absl::SetFlag(&FLAGS_fromenv, {}); 495 } 496 if (!absl::GetFlag(FLAGS_tryfromenv).empty()) { 497 absl::SetFlag(&FLAGS_tryfromenv, {}); 498 } 499 500 absl::MutexLock l(flags_internal::ProcessingChecksMutex()); 501 flags_internal::fromenv_needs_processing = false; 502 flags_internal::tryfromenv_needs_processing = false; 503 } 504 505 // -------------------------------------------------------------------- 506 507 // Returns: 508 // success status 509 // deduced value 510 // We are also mutating curr_list in case if we need to get a hold of next 511 // argument in the input. 512 std::tuple<bool, absl::string_view> DeduceFlagValue(const CommandLineFlag& flag, 513 absl::string_view value, 514 bool is_negative, 515 bool is_empty_value, 516 ArgsList* curr_list) { 517 // Value is either an argument suffix after `=` in "--foo=<value>" 518 // or separate argument in case of "--foo" "<value>". 519 520 // boolean flags have these forms: 521 // --foo 522 // --nofoo 523 // --foo=true 524 // --foo=false 525 // --nofoo=<value> is not supported 526 // --foo <value> is not supported 527 528 // non boolean flags have these forms: 529 // --foo=<value> 530 // --foo <value> 531 // --nofoo is not supported 532 533 if (flag.IsOfType<bool>()) { 534 if (value.empty()) { 535 if (is_empty_value) { 536 // "--bool_flag=" case 537 flags_internal::ReportUsageError( 538 absl::StrCat( 539 "Missing the value after assignment for the boolean flag '", 540 flag.Name(), "'"), 541 true); 542 return std::make_tuple(false, ""); 543 } 544 545 // "--bool_flag" case 546 value = is_negative ? "0" : "1"; 547 } else if (is_negative) { 548 // "--nobool_flag=Y" case 549 flags_internal::ReportUsageError( 550 absl::StrCat("Negative form with assignment is not valid for the " 551 "boolean flag '", 552 flag.Name(), "'"), 553 true); 554 return std::make_tuple(false, ""); 555 } 556 } else if (is_negative) { 557 // "--noint_flag=1" case 558 flags_internal::ReportUsageError( 559 absl::StrCat("Negative form is not valid for the flag '", flag.Name(), 560 "'"), 561 true); 562 return std::make_tuple(false, ""); 563 } else if (value.empty() && (!is_empty_value)) { 564 if (curr_list->Size() == 1) { 565 // "--int_flag" case 566 flags_internal::ReportUsageError( 567 absl::StrCat("Missing the value for the flag '", flag.Name(), "'"), 568 true); 569 return std::make_tuple(false, ""); 570 } 571 572 // "--int_flag" "10" case 573 curr_list->PopFront(); 574 value = curr_list->Front(); 575 576 // Heuristic to detect the case where someone treats a string arg 577 // like a bool or just forgets to pass a value: 578 // --my_string_var --foo=bar 579 // We look for a flag of string type, whose value begins with a 580 // dash and corresponds to known flag or standalone --. 581 if (!value.empty() && value[0] == '-' && flag.IsOfType<std::string>()) { 582 auto maybe_flag_name = std::get<0>(SplitNameAndValue(value.substr(1))); 583 584 if (maybe_flag_name.empty() || 585 std::get<0>(LocateFlag(maybe_flag_name)) != nullptr) { 586 // "--string_flag" "--known_flag" case 587 ABSL_INTERNAL_LOG( 588 WARNING, 589 absl::StrCat("Did you really mean to set flag '", flag.Name(), 590 "' to the value '", value, "'?")); 591 } 592 } 593 } 594 595 return std::make_tuple(true, value); 596 } 597 598 // -------------------------------------------------------------------- 599 600 bool CanIgnoreUndefinedFlag(absl::string_view flag_name) { 601 auto undefok = absl::GetFlag(FLAGS_undefok); 602 if (std::find(undefok.begin(), undefok.end(), flag_name) != undefok.end()) { 603 return true; 604 } 605 606 if (absl::ConsumePrefix(&flag_name, "no") && 607 std::find(undefok.begin(), undefok.end(), flag_name) != undefok.end()) { 608 return true; 609 } 610 611 return false; 612 } 613 614 // -------------------------------------------------------------------- 615 616 void ReportUnrecognizedFlags( 617 const std::vector<UnrecognizedFlag>& unrecognized_flags, 618 bool report_as_fatal_error) { 619 for (const auto& unrecognized : unrecognized_flags) { 620 // Verify if flag_name has the "no" already removed 621 std::vector<std::string> misspelling_hints; 622 if (unrecognized.source == UnrecognizedFlag::kFromArgv) { 623 misspelling_hints = 624 flags_internal::GetMisspellingHints(unrecognized.flag_name); 625 } 626 627 if (misspelling_hints.empty()) { 628 flags_internal::ReportUsageError( 629 absl::StrCat("Unknown command line flag '", unrecognized.flag_name, 630 "'"), 631 report_as_fatal_error); 632 } else { 633 flags_internal::ReportUsageError( 634 absl::StrCat("Unknown command line flag '", unrecognized.flag_name, 635 "'. Did you mean: ", 636 absl::StrJoin(misspelling_hints, ", "), " ?"), 637 report_as_fatal_error); 638 } 639 } 640 } 641 642 } // namespace 643 644 // -------------------------------------------------------------------- 645 646 bool WasPresentOnCommandLine(absl::string_view flag_name) { 647 absl::ReaderMutexLock l(SpecifiedFlagsMutex()); 648 ABSL_INTERNAL_CHECK(specified_flags != nullptr, 649 "ParseCommandLine is not invoked yet"); 650 651 return std::binary_search(specified_flags->begin(), specified_flags->end(), 652 flag_name, SpecifiedFlagsCompare{}); 653 } 654 655 // -------------------------------------------------------------------- 656 657 struct BestHints { 658 explicit BestHints(uint8_t _max) : best_distance(_max + 1) {} 659 bool AddHint(absl::string_view hint, uint8_t distance) { 660 if (hints.size() >= kMaxHints) return false; 661 if (distance == best_distance) { 662 hints.emplace_back(hint); 663 } 664 if (distance < best_distance) { 665 best_distance = distance; 666 hints = std::vector<std::string>{std::string(hint)}; 667 } 668 return true; 669 } 670 671 uint8_t best_distance; 672 std::vector<std::string> hints; 673 }; 674 675 // Return the list of flags with the smallest Damerau-Levenshtein distance to 676 // the given flag. 677 std::vector<std::string> GetMisspellingHints(const absl::string_view flag) { 678 const size_t maxCutoff = std::min(flag.size() / 2 + 1, kMaxDistance); 679 auto undefok = absl::GetFlag(FLAGS_undefok); 680 BestHints best_hints(static_cast<uint8_t>(maxCutoff)); 681 flags_internal::ForEachFlag([&](const CommandLineFlag& f) { 682 if (best_hints.hints.size() >= kMaxHints) return; 683 uint8_t distance = strings_internal::CappedDamerauLevenshteinDistance( 684 flag, f.Name(), best_hints.best_distance); 685 best_hints.AddHint(f.Name(), distance); 686 // For boolean flags, also calculate distance to the negated form. 687 if (f.IsOfType<bool>()) { 688 const std::string negated_flag = absl::StrCat("no", f.Name()); 689 distance = strings_internal::CappedDamerauLevenshteinDistance( 690 flag, negated_flag, best_hints.best_distance); 691 best_hints.AddHint(negated_flag, distance); 692 } 693 }); 694 // Finally calculate distance to flags in "undefok". 695 absl::c_for_each(undefok, [&](const absl::string_view f) { 696 if (best_hints.hints.size() >= kMaxHints) return; 697 uint8_t distance = strings_internal::CappedDamerauLevenshteinDistance( 698 flag, f, best_hints.best_distance); 699 best_hints.AddHint(absl::StrCat(f, " (undefok)"), distance); 700 }); 701 return best_hints.hints; 702 } 703 704 // -------------------------------------------------------------------- 705 706 std::vector<char*> ParseCommandLineImpl(int argc, char* argv[], 707 UsageFlagsAction usage_flag_action, 708 OnUndefinedFlag undef_flag_action, 709 std::ostream& error_help_output) { 710 std::vector<char*> positional_args; 711 std::vector<UnrecognizedFlag> unrecognized_flags; 712 713 auto help_mode = flags_internal::ParseAbseilFlagsOnlyImpl( 714 argc, argv, positional_args, unrecognized_flags, usage_flag_action); 715 716 if (undef_flag_action != OnUndefinedFlag::kIgnoreUndefined) { 717 flags_internal::ReportUnrecognizedFlags( 718 unrecognized_flags, 719 (undef_flag_action == OnUndefinedFlag::kAbortIfUndefined)); 720 721 if (undef_flag_action == OnUndefinedFlag::kAbortIfUndefined) { 722 if (!unrecognized_flags.empty()) { 723 flags_internal::HandleUsageFlags(error_help_output, 724 ProgramUsageMessage()); std::exit(1); 725 } 726 } 727 } 728 729 flags_internal::MaybeExit(help_mode); 730 731 return positional_args; 732 } 733 734 // -------------------------------------------------------------------- 735 736 // This function handles all Abseil Flags and built-in usage flags and, if any 737 // help mode was handled, it returns that help mode. The caller of this function 738 // can decide to exit based on the returned help mode. 739 // The caller may decide to handle unrecognized positional arguments and 740 // unrecognized flags first before exiting. 741 // 742 // Returns: 743 // * HelpMode::kFull if parsing errors were detected in recognized arguments 744 // * The HelpMode that was handled in case when `usage_flag_action` is 745 // UsageFlagsAction::kHandleUsage and a usage flag was specified on the 746 // commandline 747 // * Otherwise it returns HelpMode::kNone 748 HelpMode ParseAbseilFlagsOnlyImpl( 749 int argc, char* argv[], std::vector<char*>& positional_args, 750 std::vector<UnrecognizedFlag>& unrecognized_flags, 751 UsageFlagsAction usage_flag_action) { 752 ABSL_INTERNAL_CHECK(argc > 0, "Missing argv[0]"); 753 754 using flags_internal::ArgsList; 755 using flags_internal::specified_flags; 756 757 std::vector<std::string> flagfile_value; 758 std::vector<ArgsList> input_args; 759 760 // Once parsing has started we will not allow more flag registrations. 761 flags_internal::FinalizeRegistry(); 762 763 // This routine does not return anything since we abort on failure. 764 flags_internal::CheckDefaultValuesParsingRoundtrip(); 765 766 input_args.push_back(ArgsList(argc, argv)); 767 768 // Set program invocation name if it is not set before. 769 if (flags_internal::ProgramInvocationName() == "UNKNOWN") { 770 flags_internal::SetProgramInvocationName(argv[0]); 771 } 772 positional_args.push_back(argv[0]); 773 774 absl::MutexLock l(flags_internal::SpecifiedFlagsMutex()); 775 if (specified_flags == nullptr) { 776 specified_flags = new std::vector<const CommandLineFlag*>; 777 } else { 778 specified_flags->clear(); 779 } 780 781 // Iterate through the list of the input arguments. First level are 782 // arguments originated from argc/argv. Following levels are arguments 783 // originated from recursive parsing of flagfile(s). 784 bool success = true; 785 while (!input_args.empty()) { 786 // First we process the built-in generator flags. 787 success &= flags_internal::HandleGeneratorFlags(input_args, flagfile_value); 788 789 // Select top-most (most recent) arguments list. If it is empty drop it 790 // and re-try. 791 ArgsList& curr_list = input_args.back(); 792 793 // Every ArgsList starts with real or fake program name, so we can always 794 // start by skipping it. 795 curr_list.PopFront(); 796 797 if (curr_list.Size() == 0) { 798 input_args.pop_back(); 799 continue; 800 } 801 802 // Handle the next argument in the current list. If the stack of argument 803 // lists contains only one element - we are processing an argument from 804 // the original argv. 805 absl::string_view arg(curr_list.Front()); 806 bool arg_from_argv = input_args.size() == 1; 807 808 // If argument does not start with '-' or is just "-" - this is 809 // positional argument. 810 if (!absl::ConsumePrefix(&arg, "-") || arg.empty()) { 811 ABSL_INTERNAL_CHECK(arg_from_argv, 812 "Flagfile cannot contain positional argument"); 813 814 positional_args.push_back(argv[curr_list.FrontIndex()]); 815 continue; 816 } 817 818 // Split the current argument on '=' to deduce the argument flag name and 819 // value. If flag name is empty it means we've got an "--" argument. Value 820 // can be empty either if there were no '=' in argument string at all or 821 // an argument looked like "--foo=". In a latter case is_empty_value is 822 // true. 823 absl::string_view flag_name; 824 absl::string_view value; 825 bool is_empty_value = false; 826 827 std::tie(flag_name, value, is_empty_value) = 828 flags_internal::SplitNameAndValue(arg); 829 830 // Standalone "--" argument indicates that the rest of the arguments are 831 // positional. We do not support positional arguments in flagfiles. 832 if (flag_name.empty()) { 833 ABSL_INTERNAL_CHECK(arg_from_argv, 834 "Flagfile cannot contain positional argument"); 835 836 curr_list.PopFront(); 837 break; 838 } 839 840 // Locate the flag based on flag name. Handle both --foo and --nofoo. 841 CommandLineFlag* flag = nullptr; 842 bool is_negative = false; 843 std::tie(flag, is_negative) = flags_internal::LocateFlag(flag_name); 844 845 if (flag == nullptr) { 846 // Usage flags are not modeled as Abseil flags. Locate them separately. 847 if (flags_internal::DeduceUsageFlags(flag_name, value)) { 848 continue; 849 } 850 unrecognized_flags.emplace_back(arg_from_argv 851 ? UnrecognizedFlag::kFromArgv 852 : UnrecognizedFlag::kFromFlagfile, 853 flag_name); 854 continue; 855 } 856 857 // Deduce flag's value (from this or next argument). 858 bool value_success = true; 859 std::tie(value_success, value) = flags_internal::DeduceFlagValue( 860 *flag, value, is_negative, is_empty_value, &curr_list); 861 success &= value_success; 862 863 // Set the located flag to a new value, unless it is retired. Setting 864 // retired flag fails, but we ignoring it here while also reporting access 865 // to retired flag. 866 std::string error; 867 if (!flags_internal::PrivateHandleAccessor::ParseFrom( 868 *flag, value, flags_internal::SET_FLAGS_VALUE, 869 flags_internal::kCommandLine, error)) { 870 if (flag->IsRetired()) continue; 871 872 flags_internal::ReportUsageError(error, true); 873 success = false; 874 } else { 875 specified_flags->push_back(flag); 876 } 877 } 878 879 flags_internal::ResetGeneratorFlags(flagfile_value); 880 881 // All the remaining arguments are positional. 882 if (!input_args.empty()) { 883 for (size_t arg_index = input_args.back().FrontIndex(); 884 arg_index < static_cast<size_t>(argc); ++arg_index) { 885 positional_args.push_back(argv[arg_index]); 886 } 887 } 888 889 // Trim and sort the vector. 890 specified_flags->shrink_to_fit(); 891 std::sort(specified_flags->begin(), specified_flags->end(), 892 flags_internal::SpecifiedFlagsCompare{}); 893 894 // Filter out unrecognized flags, which are ok to ignore. 895 std::vector<UnrecognizedFlag> filtered; 896 filtered.reserve(unrecognized_flags.size()); 897 for (const auto& unrecognized : unrecognized_flags) { 898 if (flags_internal::CanIgnoreUndefinedFlag(unrecognized.flag_name)) 899 continue; 900 filtered.push_back(unrecognized); 901 } 902 903 std::swap(unrecognized_flags, filtered); 904 905 if (!success) { 906 #if ABSL_FLAGS_STRIP_NAMES 907 flags_internal::ReportUsageError( 908 "NOTE: command line flags are disabled in this build", true); 909 #else 910 flags_internal::HandleUsageFlags(std::cerr, ProgramUsageMessage()); 911 #endif 912 return HelpMode::kFull; // We just need to make sure the exit with 913 // code 1. 914 } 915 916 return usage_flag_action == UsageFlagsAction::kHandleUsage 917 ? flags_internal::HandleUsageFlags(std::cout, 918 ProgramUsageMessage()) 919 : HelpMode::kNone; 920 } 921 922 } // namespace flags_internal 923 924 void ParseAbseilFlagsOnly(int argc, char* argv[], 925 std::vector<char*>& positional_args, 926 std::vector<UnrecognizedFlag>& unrecognized_flags) { 927 auto help_mode = flags_internal::ParseAbseilFlagsOnlyImpl( 928 argc, argv, positional_args, unrecognized_flags, 929 flags_internal::UsageFlagsAction::kHandleUsage); 930 931 flags_internal::MaybeExit(help_mode); 932 } 933 934 // -------------------------------------------------------------------- 935 936 void ReportUnrecognizedFlags( 937 const std::vector<UnrecognizedFlag>& unrecognized_flags) { 938 flags_internal::ReportUnrecognizedFlags(unrecognized_flags, true); 939 } 940 941 // -------------------------------------------------------------------- 942 943 std::vector<char*> ParseCommandLine(int argc, char* argv[]) { 944 return flags_internal::ParseCommandLineImpl( 945 argc, argv, flags_internal::UsageFlagsAction::kHandleUsage, 946 flags_internal::OnUndefinedFlag::kAbortIfUndefined); 947 } 948 949 ABSL_NAMESPACE_END 950 } // namespace absl