• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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