| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 | //// Copyright 2019 The Abseil Authors.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at////      https://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.#include "absl/flags/internal/usage.h"#include <map>#include <string>#include "absl/flags/flag.h"#include "absl/flags/internal/path_util.h"#include "absl/flags/internal/program_name.h"#include "absl/flags/usage_config.h"#include "absl/strings/ascii.h"#include "absl/strings/str_cat.h"#include "absl/strings/str_split.h"#include "absl/strings/string_view.h"#include "absl/synchronization/mutex.h"ABSL_FLAG(bool, help, false,          "show help on important flags for this binary [tip: all flags can "          "have two dashes]");ABSL_FLAG(bool, helpfull, false, "show help on all flags");ABSL_FLAG(bool, helpshort, false,          "show help on only the main module for this program");ABSL_FLAG(bool, helppackage, false,          "show help on all modules in the main package");ABSL_FLAG(bool, version, false, "show version and build info and exit");ABSL_FLAG(bool, only_check_args, false, "exit after checking all flags");ABSL_FLAG(std::string, helpon, "",          "show help on the modules named by this flag value");ABSL_FLAG(std::string, helpmatch, "",          "show help on modules whose name contains the specified substr");namespace absl {namespace flags_internal {namespace {// This class is used to emit an XML element with `tag` and `text`.// It adds opening and closing tags and escapes special characters in the text.// For example:// std::cout << XMLElement("title", "Milk & Cookies");// prints "<title>Milk & Cookies</title>"class XMLElement { public:  XMLElement(absl::string_view tag, absl::string_view txt)      : tag_(tag), txt_(txt) {}  friend std::ostream& operator<<(std::ostream& out,                                  const XMLElement& xml_elem) {    out << "<" << xml_elem.tag_ << ">";    for (auto c : xml_elem.txt_) {      switch (c) {        case '"':          out << """;          break;        case '\'':          out << "'";          break;        case '&':          out << "&";          break;        case '<':          out << "<";          break;        case '>':          out << ">";          break;        default:          out << c;          break;      }    }    return out << "</" << xml_elem.tag_ << ">";  } private:  absl::string_view tag_;  absl::string_view txt_;};// --------------------------------------------------------------------// Helper class to pretty-print info about a flag.class FlagHelpPrettyPrinter { public:  // Pretty printer holds on to the std::ostream& reference to direct an output  // to that stream.  FlagHelpPrettyPrinter(int max_line_len, std::ostream* out)      : out_(*out),        max_line_len_(max_line_len),        line_len_(0),        first_line_(true) {}  void Write(absl::string_view str, bool wrap_line = false) {    // Empty std::string - do nothing.    if (str.empty()) return;    std::vector<absl::string_view> tokens;    if (wrap_line) {      for (auto line : absl::StrSplit(str, absl::ByAnyChar("\n\r"))) {        if (!tokens.empty()) {          // Keep line separators in the input std::string.          tokens.push_back("\n");        }        for (auto token :             absl::StrSplit(line, absl::ByAnyChar(" \t"), absl::SkipEmpty())) {          tokens.push_back(token);        }      }    } else {      tokens.push_back(str);    }    for (auto token : tokens) {      bool new_line = (line_len_ == 0);      // Respect line separators in the input std::string.      if (token == "\n") {        EndLine();        continue;      }      // Write the token, ending the std::string first if necessary/possible.      if (!new_line && (line_len_ + token.size() >= max_line_len_)) {        EndLine();        new_line = true;      }      if (new_line) {        StartLine();      } else {        out_ << ' ';        ++line_len_;      }      out_ << token;      line_len_ += token.size();    }  }  void StartLine() {    if (first_line_) {      out_ << "    ";      line_len_ = 4;      first_line_ = false;    } else {      out_ << "      ";      line_len_ = 6;    }  }  void EndLine() {    out_ << '\n';    line_len_ = 0;  } private:  std::ostream& out_;  const int max_line_len_;  int line_len_;  bool first_line_;};void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag,                           std::ostream* out) {  FlagHelpPrettyPrinter printer(80, out);  // Max line length is 80.  // Flag name.  printer.Write(absl::StrCat("-", flag.Name()));  // Flag help.  printer.Write(absl::StrCat("(", flag.Help(), ");"), /*wrap_line=*/true);  // Flag data type (for V1 flags only).  if (!flag.IsAbseilFlag() && !flag.IsRetired()) {    printer.Write(absl::StrCat("type: ", flag.Typename(), ";"));  }  // The listed default value will be the actual default from the flag  // definition in the originating source file, unless the value has  // subsequently been modified using SetCommandLineOption() with mode  // SET_FLAGS_DEFAULT.  std::string dflt_val = flag.DefaultValue();  if (flag.IsOfType<std::string>()) {    dflt_val = absl::StrCat("\"", dflt_val, "\"");  }  printer.Write(absl::StrCat("default: ", dflt_val, ";"));  if (flag.IsModified()) {    std::string curr_val = flag.CurrentValue();    if (flag.IsOfType<std::string>()) {      curr_val = absl::StrCat("\"", curr_val, "\"");    }    printer.Write(absl::StrCat("currently: ", curr_val, ";"));  }  printer.EndLine();}// Shows help for every filename which matches any of the filters// If filters are empty, shows help for every file.// If a flag's help message has been stripped (e.g. by adding '#define// STRIP_FLAG_HELP 1' then this flag will not be displayed by '--help'// and its variants.void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,                   HelpFormat format, absl::string_view program_usage_message) {  if (format == HelpFormat::kHumanReadable) {    out << flags_internal::ShortProgramInvocationName() << ": "        << program_usage_message << "\n\n";  } else {    // XML schema is not a part of our public API for now.    out << "<?xml version=\"1.0\"?>\n"        // The document.        << "<AllFlags>\n"        // The program name and usage.        << XMLElement("program", flags_internal::ShortProgramInvocationName())        << '\n'        << XMLElement("usage", program_usage_message) << '\n';  }  // Map of package name to  //   map of file name to  //     vector of flags in the file.  // This map is used to output matching flags grouped by package and file  // name.  std::map<std::string,           std::map<std::string,                    std::vector<const flags_internal::CommandLineFlag*>>>      matching_flags;  flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) {    std::string flag_filename = flag->Filename();    // Ignore retired flags.    if (flag->IsRetired()) return;    // If the flag has been stripped, pretend that it doesn't exist.    if (flag->Help() == flags_internal::kStrippedFlagHelp) return;    // Make sure flag satisfies the filter    if (!filter_cb || !filter_cb(flag_filename)) return;    matching_flags[std::string(flags_internal::Package(flag_filename))]                  [flag_filename]                      .push_back(flag);  });  absl::string_view      package_separator;             // controls blank lines between packages.  absl::string_view file_separator;  // controls blank lines between files.  for (const auto& package : matching_flags) {    if (format == HelpFormat::kHumanReadable) {      out << package_separator;      package_separator = "\n\n";    }    file_separator = "";    for (const auto& flags_in_file : package.second) {      if (format == HelpFormat::kHumanReadable) {        out << file_separator << "  Flags from " << flags_in_file.first            << ":\n";        file_separator = "\n";      }      for (const auto* flag : flags_in_file.second) {        flags_internal::FlagHelp(out, *flag, format);      }    }  }  if (format == HelpFormat::kHumanReadable) {    if (filter_cb && matching_flags.empty()) {      out << "  No modules matched: use -helpfull\n";    }  } else {    // The end of the document.    out << "</AllFlags>\n";  }}}  // namespace// --------------------------------------------------------------------// Produces the help message describing specific flag.void FlagHelp(std::ostream& out, const flags_internal::CommandLineFlag& flag,              HelpFormat format) {  if (format == HelpFormat::kHumanReadable)    flags_internal::FlagHelpHumanReadable(flag, &out);}// --------------------------------------------------------------------// Produces the help messages for all flags matching the filter.// If filter is empty produces help messages for all flags.void FlagsHelp(std::ostream& out, absl::string_view filter, HelpFormat format,               absl::string_view program_usage_message) {  flags_internal::FlagKindFilter filter_cb = [&](absl::string_view filename) {    return filter.empty() || filename.find(filter) != absl::string_view::npos;  };  flags_internal::FlagsHelpImpl(out, filter_cb, format, program_usage_message);}// --------------------------------------------------------------------// Checks all the 'usage' command line flags to see if any have been set.// If so, handles them appropriately.int HandleUsageFlags(std::ostream& out,                     absl::string_view program_usage_message) {  if (absl::GetFlag(FLAGS_helpshort)) {    flags_internal::FlagsHelpImpl(        out, flags_internal::GetUsageConfig().contains_helpshort_flags,        HelpFormat::kHumanReadable, program_usage_message);    return 1;  }  if (absl::GetFlag(FLAGS_helpfull)) {    // show all options    flags_internal::FlagsHelp(out, "", HelpFormat::kHumanReadable,                              program_usage_message);    return 1;  }  if (!absl::GetFlag(FLAGS_helpon).empty()) {    flags_internal::FlagsHelp(        out, absl::StrCat("/", absl::GetFlag(FLAGS_helpon), "."),        HelpFormat::kHumanReadable, program_usage_message);    return 1;  }  if (!absl::GetFlag(FLAGS_helpmatch).empty()) {    flags_internal::FlagsHelp(out, absl::GetFlag(FLAGS_helpmatch),                              HelpFormat::kHumanReadable,                              program_usage_message);    return 1;  }  if (absl::GetFlag(FLAGS_help)) {    flags_internal::FlagsHelpImpl(        out, flags_internal::GetUsageConfig().contains_help_flags,        HelpFormat::kHumanReadable, program_usage_message);    out << "\nTry --helpfull to get a list of all flags.\n";    return 1;  }  if (absl::GetFlag(FLAGS_helppackage)) {    flags_internal::FlagsHelpImpl(        out, flags_internal::GetUsageConfig().contains_helppackage_flags,        HelpFormat::kHumanReadable, program_usage_message);    out << "\nTry --helpfull to get a list of all flags.\n";    return 1;  }  if (absl::GetFlag(FLAGS_version)) {    if (flags_internal::GetUsageConfig().version_string)      out << flags_internal::GetUsageConfig().version_string();    // Unlike help, we may be asking for version in a script, so return 0    return 0;  }  if (absl::GetFlag(FLAGS_only_check_args)) {    return 0;  }  return -1;}}  // namespace flags_internal}  // namespace absl
 |