tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

usage.cc (16992B)


      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/internal/usage.h"
     17 
     18 #include <stdint.h>
     19 
     20 #include <algorithm>
     21 #include <cstdlib>
     22 #include <functional>
     23 #include <iterator>
     24 #include <map>
     25 #include <ostream>
     26 #include <string>
     27 #include <utility>
     28 #include <vector>
     29 
     30 #include "absl/base/attributes.h"
     31 #include "absl/base/config.h"
     32 #include "absl/base/no_destructor.h"
     33 #include "absl/base/thread_annotations.h"
     34 #include "absl/flags/commandlineflag.h"
     35 #include "absl/flags/flag.h"
     36 #include "absl/flags/internal/flag.h"
     37 #include "absl/flags/internal/path_util.h"
     38 #include "absl/flags/internal/private_handle_accessor.h"
     39 #include "absl/flags/internal/program_name.h"
     40 #include "absl/flags/internal/registry.h"
     41 #include "absl/flags/usage_config.h"
     42 #include "absl/strings/match.h"
     43 #include "absl/strings/str_cat.h"
     44 #include "absl/strings/str_split.h"
     45 #include "absl/strings/string_view.h"
     46 #include "absl/strings/strip.h"
     47 #include "absl/synchronization/mutex.h"
     48 
     49 // Dummy global variables to prevent anyone else defining these.
     50 bool FLAGS_help = false;
     51 bool FLAGS_helpfull = false;
     52 bool FLAGS_helpshort = false;
     53 bool FLAGS_helppackage = false;
     54 bool FLAGS_version = false;
     55 bool FLAGS_only_check_args = false;
     56 bool FLAGS_helpon = false;
     57 bool FLAGS_helpmatch = false;
     58 
     59 namespace absl {
     60 ABSL_NAMESPACE_BEGIN
     61 namespace flags_internal {
     62 namespace {
     63 
     64 using PerFlagFilter = std::function<bool(const absl::CommandLineFlag&)>;
     65 
     66 // Maximum length size in a human readable format.
     67 constexpr size_t kHrfMaxLineLength = 80;
     68 
     69 // This class is used to emit an XML element with `tag` and `text`.
     70 // It adds opening and closing tags and escapes special characters in the text.
     71 // For example:
     72 // std::cout << XMLElement("title", "Milk & Cookies");
     73 // prints "<title>Milk &amp; Cookies</title>"
     74 class XMLElement {
     75 public:
     76  XMLElement(absl::string_view tag, absl::string_view txt)
     77      : tag_(tag), txt_(txt) {}
     78 
     79  friend std::ostream& operator<<(std::ostream& out,
     80                                  const XMLElement& xml_elem) {
     81    out << "<" << xml_elem.tag_ << ">";
     82 
     83    for (auto c : xml_elem.txt_) {
     84      switch (c) {
     85        case '"':
     86          out << "&quot;";
     87          break;
     88        case '\'':
     89          out << "&apos;";
     90          break;
     91        case '&':
     92          out << "&amp;";
     93          break;
     94        case '<':
     95          out << "&lt;";
     96          break;
     97        case '>':
     98          out << "&gt;";
     99          break;
    100        case '\n':
    101        case '\v':
    102        case '\f':
    103        case '\t':
    104          out << " ";
    105          break;
    106        default:
    107          if (IsValidXmlCharacter(static_cast<unsigned char>(c))) {
    108            out << c;
    109          }
    110          break;
    111      }
    112    }
    113 
    114    return out << "</" << xml_elem.tag_ << ">";
    115  }
    116 
    117 private:
    118  static bool IsValidXmlCharacter(unsigned char c) { return c >= 0x20; }
    119  absl::string_view tag_;
    120  absl::string_view txt_;
    121 };
    122 
    123 // --------------------------------------------------------------------
    124 // Helper class to pretty-print info about a flag.
    125 
    126 class FlagHelpPrettyPrinter {
    127 public:
    128  // Pretty printer holds on to the std::ostream& reference to direct an output
    129  // to that stream.
    130  FlagHelpPrettyPrinter(size_t max_line_len, size_t min_line_len,
    131                        size_t wrapped_line_indent, std::ostream& out)
    132      : out_(out),
    133        max_line_len_(max_line_len),
    134        min_line_len_(min_line_len),
    135        wrapped_line_indent_(wrapped_line_indent),
    136        line_len_(0),
    137        first_line_(true) {}
    138 
    139  void Write(absl::string_view str, bool wrap_line = false) {
    140    // Empty string - do nothing.
    141    if (str.empty()) return;
    142 
    143    std::vector<absl::string_view> tokens;
    144    if (wrap_line) {
    145      for (auto line : absl::StrSplit(str, absl::ByAnyChar("\n\r"))) {
    146        if (!tokens.empty()) {
    147          // Keep line separators in the input string.
    148          tokens.emplace_back("\n");
    149        }
    150        for (auto token :
    151             absl::StrSplit(line, absl::ByAnyChar(" \t"), absl::SkipEmpty())) {
    152          tokens.push_back(token);
    153        }
    154      }
    155    } else {
    156      tokens.push_back(str);
    157    }
    158 
    159    for (auto token : tokens) {
    160      bool new_line = (line_len_ == 0);
    161 
    162      // Respect line separators in the input string.
    163      if (token == "\n") {
    164        EndLine();
    165        continue;
    166      }
    167 
    168      // Write the token, ending the string first if necessary/possible.
    169      if (!new_line && (line_len_ + token.size() >= max_line_len_)) {
    170        EndLine();
    171        new_line = true;
    172      }
    173 
    174      if (new_line) {
    175        StartLine();
    176      } else {
    177        out_ << ' ';
    178        ++line_len_;
    179      }
    180 
    181      out_ << token;
    182      line_len_ += token.size();
    183    }
    184  }
    185 
    186  void StartLine() {
    187    if (first_line_) {
    188      line_len_ = min_line_len_;
    189      first_line_ = false;
    190    } else {
    191      line_len_ = min_line_len_ + wrapped_line_indent_;
    192    }
    193    out_ << std::string(line_len_, ' ');
    194  }
    195  void EndLine() {
    196    out_ << '\n';
    197    line_len_ = 0;
    198  }
    199 
    200 private:
    201  std::ostream& out_;
    202  const size_t max_line_len_;
    203  const size_t min_line_len_;
    204  const size_t wrapped_line_indent_;
    205  size_t line_len_;
    206  bool first_line_;
    207 };
    208 
    209 void FlagHelpHumanReadable(const CommandLineFlag& flag, std::ostream& out) {
    210  FlagHelpPrettyPrinter printer(kHrfMaxLineLength, 4, 2, out);
    211 
    212  // Flag name.
    213  printer.Write(absl::StrCat("--", flag.Name()));
    214 
    215  // Flag help.
    216  printer.Write(absl::StrCat("(", flag.Help(), ");"), /*wrap_line=*/true);
    217 
    218  // The listed default value will be the actual default from the flag
    219  // definition in the originating source file, unless the value has
    220  // subsequently been modified using SetCommandLineOption() with mode
    221  // SET_FLAGS_DEFAULT.
    222  std::string dflt_val = flag.DefaultValue();
    223  std::string curr_val = flag.CurrentValue();
    224  bool is_modified = curr_val != dflt_val;
    225 
    226  if (flag.IsOfType<std::string>()) {
    227    dflt_val = absl::StrCat("\"", dflt_val, "\"");
    228  }
    229  printer.Write(absl::StrCat("default: ", dflt_val, ";"));
    230 
    231  if (is_modified) {
    232    if (flag.IsOfType<std::string>()) {
    233      curr_val = absl::StrCat("\"", curr_val, "\"");
    234    }
    235    printer.Write(absl::StrCat("currently: ", curr_val, ";"));
    236  }
    237 
    238  printer.EndLine();
    239 }
    240 
    241 // Shows help for every filename which matches any of the filters
    242 // If filters are empty, shows help for every file.
    243 // If a flag's help message has been stripped (e.g. by adding '#define
    244 // STRIP_FLAG_HELP 1' then this flag will not be displayed by '--help'
    245 // and its variants.
    246 void FlagsHelpImpl(std::ostream& out, PerFlagFilter filter_cb,
    247                   HelpFormat format, absl::string_view program_usage_message) {
    248  if (format == HelpFormat::kHumanReadable) {
    249    out << flags_internal::ShortProgramInvocationName() << ": "
    250        << program_usage_message << "\n\n";
    251  } else {
    252    // XML schema is not a part of our public API for now.
    253    out << "<?xml version=\"1.0\"?>\n"
    254        << "<!-- This output should be used with care. We do not report type "
    255           "names for flags with user defined types -->\n"
    256        << "<!-- Prefer flag only_check_args for validating flag inputs -->\n"
    257        // The document.
    258        << "<AllFlags>\n"
    259        // The program name and usage.
    260        << XMLElement("program", flags_internal::ShortProgramInvocationName())
    261        << '\n'
    262        << XMLElement("usage", program_usage_message) << '\n';
    263  }
    264 
    265  // Ordered map of package name to
    266  //   map of file name to
    267  //     vector of flags in the file.
    268  // This map is used to output matching flags grouped by package and file
    269  // name.
    270  std::map<std::string,
    271           std::map<std::string, std::vector<const absl::CommandLineFlag*>>>
    272      matching_flags;
    273 
    274  flags_internal::ForEachFlag([&](absl::CommandLineFlag& flag) {
    275    // Ignore retired flags.
    276    if (flag.IsRetired()) return;
    277 
    278    // If the flag has been stripped, pretend that it doesn't exist.
    279    if (flag.Help() == flags_internal::kStrippedFlagHelp) return;
    280 
    281    // Make sure flag satisfies the filter
    282    if (!filter_cb(flag)) return;
    283 
    284    std::string flag_filename = flag.Filename();
    285 
    286    matching_flags[std::string(flags_internal::Package(flag_filename))]
    287                  [flag_filename]
    288                      .push_back(&flag);
    289  });
    290 
    291  absl::string_view package_separator;  // controls blank lines between packages
    292  absl::string_view file_separator;     // controls blank lines between files
    293  for (auto& package : matching_flags) {
    294    if (format == HelpFormat::kHumanReadable) {
    295      out << package_separator;
    296      package_separator = "\n\n";
    297    }
    298 
    299    file_separator = "";
    300    for (auto& flags_in_file : package.second) {
    301      if (format == HelpFormat::kHumanReadable) {
    302        out << file_separator << "  Flags from " << flags_in_file.first
    303            << ":\n";
    304        file_separator = "\n";
    305      }
    306 
    307      std::sort(std::begin(flags_in_file.second),
    308                std::end(flags_in_file.second),
    309                [](const CommandLineFlag* lhs, const CommandLineFlag* rhs) {
    310                  return lhs->Name() < rhs->Name();
    311                });
    312 
    313      for (const auto* flag : flags_in_file.second) {
    314        flags_internal::FlagHelp(out, *flag, format);
    315      }
    316    }
    317  }
    318 
    319  if (format == HelpFormat::kHumanReadable) {
    320    FlagHelpPrettyPrinter printer(kHrfMaxLineLength, 0, 0, out);
    321 
    322    if (filter_cb && matching_flags.empty()) {
    323      printer.Write("No flags matched.\n", true);
    324    }
    325    printer.EndLine();
    326    printer.Write(
    327        "Try --helpfull to get a list of all flags or --help=substring "
    328        "shows help for flags which include specified substring in either "
    329        "in the name, or description or path.\n",
    330        true);
    331  } else {
    332    // The end of the document.
    333    out << "</AllFlags>\n";
    334  }
    335 }
    336 
    337 void FlagsHelpImpl(std::ostream& out,
    338                   flags_internal::FlagKindFilter filename_filter_cb,
    339                   HelpFormat format, absl::string_view program_usage_message) {
    340  FlagsHelpImpl(
    341      out,
    342      [&](const absl::CommandLineFlag& flag) {
    343        return filename_filter_cb && filename_filter_cb(flag.Filename());
    344      },
    345      format, program_usage_message);
    346 }
    347 
    348 }  // namespace
    349 
    350 // --------------------------------------------------------------------
    351 // Produces the help message describing specific flag.
    352 void FlagHelp(std::ostream& out, const CommandLineFlag& flag,
    353              HelpFormat format) {
    354  if (format == HelpFormat::kHumanReadable)
    355    flags_internal::FlagHelpHumanReadable(flag, out);
    356 }
    357 
    358 // --------------------------------------------------------------------
    359 // Produces the help messages for all flags matching the filename filter.
    360 // If filter is empty produces help messages for all flags.
    361 void FlagsHelp(std::ostream& out, absl::string_view filter, HelpFormat format,
    362               absl::string_view program_usage_message) {
    363  flags_internal::FlagKindFilter filter_cb = [&](absl::string_view filename) {
    364    return filter.empty() || absl::StrContains(filename, filter);
    365  };
    366  flags_internal::FlagsHelpImpl(out, filter_cb, format, program_usage_message);
    367 }
    368 
    369 // --------------------------------------------------------------------
    370 // Checks all the 'usage' command line flags to see if any have been set.
    371 // If so, handles them appropriately.
    372 HelpMode HandleUsageFlags(std::ostream& out,
    373                          absl::string_view program_usage_message) {
    374  switch (GetFlagsHelpMode()) {
    375    case HelpMode::kNone:
    376      break;
    377    case HelpMode::kImportant:
    378      flags_internal::FlagsHelpImpl(
    379          out, flags_internal::GetUsageConfig().contains_help_flags,
    380          GetFlagsHelpFormat(), program_usage_message);
    381      break;
    382 
    383    case HelpMode::kShort:
    384      flags_internal::FlagsHelpImpl(
    385          out, flags_internal::GetUsageConfig().contains_helpshort_flags,
    386          GetFlagsHelpFormat(), program_usage_message);
    387      break;
    388 
    389    case HelpMode::kFull:
    390      flags_internal::FlagsHelp(out, "", GetFlagsHelpFormat(),
    391                                program_usage_message);
    392      break;
    393 
    394    case HelpMode::kPackage:
    395      flags_internal::FlagsHelpImpl(
    396          out, flags_internal::GetUsageConfig().contains_helppackage_flags,
    397          GetFlagsHelpFormat(), program_usage_message);
    398      break;
    399 
    400    case HelpMode::kMatch: {
    401      std::string substr = GetFlagsHelpMatchSubstr();
    402      if (substr.empty()) {
    403        // show all options
    404        flags_internal::FlagsHelp(out, substr, GetFlagsHelpFormat(),
    405                                  program_usage_message);
    406      } else {
    407        auto filter_cb = [&substr](const absl::CommandLineFlag& flag) {
    408          if (absl::StrContains(flag.Name(), substr)) return true;
    409          if (absl::StrContains(flag.Filename(), substr)) return true;
    410          if (absl::StrContains(flag.Help(), substr)) return true;
    411 
    412          return false;
    413        };
    414        flags_internal::FlagsHelpImpl(
    415            out, filter_cb, HelpFormat::kHumanReadable, program_usage_message);
    416      }
    417      break;
    418    }
    419    case HelpMode::kVersion:
    420      if (flags_internal::GetUsageConfig().version_string)
    421        out << flags_internal::GetUsageConfig().version_string();
    422      // Unlike help, we may be asking for version in a script, so return 0
    423      break;
    424 
    425    case HelpMode::kOnlyCheckArgs:
    426      break;
    427  }
    428 
    429  return GetFlagsHelpMode();
    430 }
    431 
    432 // --------------------------------------------------------------------
    433 // Globals representing usage reporting flags
    434 
    435 namespace {
    436 
    437 absl::Mutex* HelpAttributesMutex() {
    438  static absl::NoDestructor<absl::Mutex> mutex;
    439  return mutex.get();
    440 }
    441 ABSL_CONST_INIT std::string* match_substr ABSL_GUARDED_BY(HelpAttributesMutex())
    442    ABSL_PT_GUARDED_BY(HelpAttributesMutex()) = nullptr;
    443 ABSL_CONST_INIT HelpMode help_mode ABSL_GUARDED_BY(HelpAttributesMutex()) =
    444    HelpMode::kNone;
    445 ABSL_CONST_INIT HelpFormat help_format ABSL_GUARDED_BY(HelpAttributesMutex()) =
    446    HelpFormat::kHumanReadable;
    447 
    448 }  // namespace
    449 
    450 std::string GetFlagsHelpMatchSubstr() {
    451  absl::MutexLock l(HelpAttributesMutex());
    452  if (match_substr == nullptr) return "";
    453  return *match_substr;
    454 }
    455 
    456 void SetFlagsHelpMatchSubstr(absl::string_view substr) {
    457  absl::MutexLock l(HelpAttributesMutex());
    458  if (match_substr == nullptr) match_substr = new std::string;
    459  match_substr->assign(substr.data(), substr.size());
    460 }
    461 
    462 HelpMode GetFlagsHelpMode() {
    463  absl::MutexLock l(HelpAttributesMutex());
    464  return help_mode;
    465 }
    466 
    467 void SetFlagsHelpMode(HelpMode mode) {
    468  absl::MutexLock l(HelpAttributesMutex());
    469  help_mode = mode;
    470 }
    471 
    472 HelpFormat GetFlagsHelpFormat() {
    473  absl::MutexLock l(HelpAttributesMutex());
    474  return help_format;
    475 }
    476 
    477 void SetFlagsHelpFormat(HelpFormat format) {
    478  absl::MutexLock l(HelpAttributesMutex());
    479  help_format = format;
    480 }
    481 
    482 // Deduces usage flags from the input argument in a form --name=value or
    483 // --name. argument is already split into name and value before we call this
    484 // function.
    485 bool DeduceUsageFlags(absl::string_view name, absl::string_view value) {
    486  if (absl::ConsumePrefix(&name, "help")) {
    487    if (name.empty()) {
    488      if (value.empty()) {
    489        SetFlagsHelpMode(HelpMode::kImportant);
    490      } else {
    491        SetFlagsHelpMode(HelpMode::kMatch);
    492        SetFlagsHelpMatchSubstr(value);
    493      }
    494      return true;
    495    }
    496 
    497    if (name == "match") {
    498      SetFlagsHelpMode(HelpMode::kMatch);
    499      SetFlagsHelpMatchSubstr(value);
    500      return true;
    501    }
    502 
    503    if (name == "on") {
    504      SetFlagsHelpMode(HelpMode::kMatch);
    505      SetFlagsHelpMatchSubstr(absl::StrCat("/", value, "."));
    506      return true;
    507    }
    508 
    509    if (name == "full") {
    510      SetFlagsHelpMode(HelpMode::kFull);
    511      return true;
    512    }
    513 
    514    if (name == "short") {
    515      SetFlagsHelpMode(HelpMode::kShort);
    516      return true;
    517    }
    518 
    519    if (name == "package") {
    520      SetFlagsHelpMode(HelpMode::kPackage);
    521      return true;
    522    }
    523 
    524    return false;
    525  }
    526 
    527  if (name == "version") {
    528    SetFlagsHelpMode(HelpMode::kVersion);
    529    return true;
    530  }
    531 
    532  if (name == "only_check_args") {
    533    SetFlagsHelpMode(HelpMode::kOnlyCheckArgs);
    534    return true;
    535  }
    536 
    537  return false;
    538 }
    539 
    540 // --------------------------------------------------------------------
    541 
    542 void MaybeExit(HelpMode mode) {
    543  switch (mode) {
    544    case flags_internal::HelpMode::kNone:
    545      return;
    546    case flags_internal::HelpMode::kOnlyCheckArgs:
    547    case flags_internal::HelpMode::kVersion:
    548      std::exit(0);
    549    default:  // For all the other modes we exit with 1
    550      std::exit(1);
    551  }
    552 }
    553 
    554 // --------------------------------------------------------------------
    555 
    556 }  // namespace flags_internal
    557 ABSL_NAMESPACE_END
    558 }  // namespace absl