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