std::vector ParseCommandLineImpl()

in absl/flags/parse.cc [608:809]


std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
                                        ArgvListAction arg_list_act,
                                        UsageFlagsAction usage_flag_act,
                                        OnUndefinedFlag on_undef_flag) {
  ABSL_INTERNAL_CHECK(argc > 0, "Missing argv[0]");

  // Once parsing has started we will not have more flag registrations.
  // If we did, they would be missing during parsing, which is a problem on
  // itself.
  flags_internal::FinalizeRegistry();

  // This routine does not return anything since we abort on failure.
  CheckDefaultValuesParsingRoundtrip();

  std::vector<std::string> flagfile_value;

  std::vector<ArgsList> input_args;
  input_args.push_back(ArgsList(argc, argv));

  std::vector<char*> output_args;
  std::vector<char*> positional_args;
  output_args.reserve(argc);

  // This is the list of undefined flags. The element of the list is the pair
  // consisting of boolean indicating if flag came from command line (vs from
  // some flag file we've read) and flag name.
  // TODO(rogeeff): Eliminate the first element in the pair after cleanup.
  std::vector<std::pair<bool, std::string>> undefined_flag_names;

  // Set program invocation name if it is not set before.
  if (ProgramInvocationName() == "UNKNOWN") {
    flags_internal::SetProgramInvocationName(argv[0]);
  }
  output_args.push_back(argv[0]);

  absl::MutexLock l(&specified_flags_guard);
  if (specified_flags == nullptr) {
    specified_flags = new std::vector<const CommandLineFlag*>;
  } else {
    specified_flags->clear();
  }

  // Iterate through the list of the input arguments. First level are arguments
  // originated from argc/argv. Following levels are arguments originated from
  // recursive parsing of flagfile(s).
  bool success = true;
  while (!input_args.empty()) {
    // 10. First we process the built-in generator flags.
    success &= HandleGeneratorFlags(input_args, flagfile_value);

    // 30. Select top-most (most recent) arguments list. If it is empty drop it
    // and re-try.
    ArgsList& curr_list = input_args.back();

    curr_list.PopFront();

    if (curr_list.Size() == 0) {
      input_args.pop_back();
      continue;
    }

    // 40. Pick up the front remaining argument in the current list. If current
    // stack of argument lists contains only one element - we are processing an
    // argument from the original argv.
    absl::string_view arg(curr_list.Front());
    bool arg_from_argv = input_args.size() == 1;

    // 50. If argument does not start with - or is just "-" - this is
    // positional argument.
    if (!absl::ConsumePrefix(&arg, "-") || arg.empty()) {
      ABSL_INTERNAL_CHECK(arg_from_argv,
                          "Flagfile cannot contain positional argument");

      positional_args.push_back(argv[curr_list.FrontIndex()]);
      continue;
    }

    if (arg_from_argv && (arg_list_act == ArgvListAction::kKeepParsedArgs)) {
      output_args.push_back(argv[curr_list.FrontIndex()]);
    }

    // 60. Split the current argument on '=' to figure out the argument
    // name and value. If flag name is empty it means we've got "--". value
    // can be empty either if there were no '=' in argument string at all or
    // an argument looked like "--foo=". In a latter case is_empty_value is
    // true.
    absl::string_view flag_name;
    absl::string_view value;
    bool is_empty_value = false;

    std::tie(flag_name, value, is_empty_value) = SplitNameAndValue(arg);

    // 70. "--" alone means what it does for GNU: stop flags parsing. We do
    // not support positional arguments in flagfiles, so we just drop them.
    if (flag_name.empty()) {
      ABSL_INTERNAL_CHECK(arg_from_argv,
                          "Flagfile cannot contain positional argument");

      curr_list.PopFront();
      break;
    }

    // 80. Locate the flag based on flag name. Handle both --foo and --nofoo
    CommandLineFlag* flag = nullptr;
    bool is_negative = false;
    std::tie(flag, is_negative) = LocateFlag(flag_name);

    if (flag == nullptr) {
      // Usage flags are not modeled as Abseil flags. Locate them separately.
      if (flags_internal::DeduceUsageFlags(flag_name, value)) {
        continue;
      }

      if (on_undef_flag != OnUndefinedFlag::kIgnoreUndefined) {
        undefined_flag_names.emplace_back(arg_from_argv,
                                          std::string(flag_name));
      }
      continue;
    }

    // 90. Deduce flag's value (from this or next argument)
    auto curr_index = curr_list.FrontIndex();
    bool value_success = true;
    std::tie(value_success, value) =
        DeduceFlagValue(*flag, value, is_negative, is_empty_value, &curr_list);
    success &= value_success;

    // If above call consumed an argument, it was a standalone value
    if (arg_from_argv && (arg_list_act == ArgvListAction::kKeepParsedArgs) &&
        (curr_index != curr_list.FrontIndex())) {
      output_args.push_back(argv[curr_list.FrontIndex()]);
    }

    // 100. Set the located flag to a new new value, unless it is retired.
    // Setting retired flag fails, but we ignoring it here while also reporting
    // access to retired flag.
    std::string error;
    if (!flags_internal::PrivateHandleAccessor::ParseFrom(
            *flag, value, SET_FLAGS_VALUE, kCommandLine, error)) {
      if (flag->IsRetired()) continue;

      flags_internal::ReportUsageError(error, true);
      success = false;
    } else {
      specified_flags->push_back(flag);
    }
  }

  for (const auto& flag_name : undefined_flag_names) {
    if (CanIgnoreUndefinedFlag(flag_name.second)) continue;

    flags_internal::ReportUsageError(
        absl::StrCat("Unknown command line flag '", flag_name.second, "'"),
        true);

    success = false;
  }

#if ABSL_FLAGS_STRIP_NAMES
  if (!success) {
    flags_internal::ReportUsageError(
        "NOTE: command line flags are disabled in this build", true);
  }
#endif

  if (!success) {
    flags_internal::HandleUsageFlags(std::cout,
                                     ProgramUsageMessage());
    std::exit(1);
  }

  if (usage_flag_act == UsageFlagsAction::kHandleUsage) {
    int exit_code = flags_internal::HandleUsageFlags(
        std::cout, ProgramUsageMessage());

    if (exit_code != -1) {
      std::exit(exit_code);
    }
  }

  ResetGeneratorFlags(flagfile_value);

  // Reinstate positional args which were intermixed with flags in the arguments
  // list.
  for (auto arg : positional_args) {
    output_args.push_back(arg);
  }

  // All the remaining arguments are positional.
  if (!input_args.empty()) {
    for (int arg_index = input_args.back().FrontIndex(); arg_index < argc;
         ++arg_index) {
      output_args.push_back(argv[arg_index]);
    }
  }

  // Trim and sort the vector.
  specified_flags->shrink_to_fit();
  std::sort(specified_flags->begin(), specified_flags->end(),
            SpecifiedFlagsCompare{});
  return output_args;
}