• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "quiche_platform_impl/quiche_command_line_flags_impl.h"
6 
7 #include <initializer_list>
8 #include <iostream>
9 #include <set>
10 #include <string>
11 #include <vector>
12 
13 #include "base/command_line.h"
14 #include "base/export_template.h"
15 #include "base/logging.h"
16 #include "base/strings/strcat.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "build/build_config.h"
21 #include "net/third_party/quiche/src/quiche/quic/platform/api/quic_logging.h"
22 
23 namespace quiche {
24 
25 namespace {
26 
FindLineWrapPosition(const std::string & s,size_t desired_len)27 size_t FindLineWrapPosition(const std::string& s, size_t desired_len) {
28   if (s.length() <= desired_len) {
29     return std::string::npos;
30   }
31   size_t pos = s.find_last_of(base::kWhitespaceASCII, desired_len);
32   if (pos != std::string::npos) {
33     return pos;
34   }
35   pos = s.find_first_of(base::kWhitespaceASCII, desired_len);
36   if (pos != std::string::npos) {
37     return pos;
38   }
39   return std::string::npos;
40 }
41 
42 // Pretty-print a flag description in the format:
43 //
44 // --flag_name      Some text describing the flag that can
45 //                  wrap around to the next line.
AppendFlagDescription(const std::string & name,std::string help,std::string * out)46 void AppendFlagDescription(const std::string& name,
47                            std::string help,
48                            std::string* out) {
49   const int kStartCol = 20;
50   const int kEndCol = 80;
51   const int kMinPadding = 2;
52   static const char kDashes[] = "--";
53 
54   base::StrAppend(out, {kDashes, name});
55   int col = strlen(kDashes) + name.length();
56   if (col + kMinPadding < kEndCol) {
57     // Start help text on same line
58     int pad_len = std::max(kMinPadding, kStartCol - col);
59     base::StrAppend(out, {std::string(pad_len, ' ')});
60     col += pad_len;
61   } else {
62     // Start help text on next line
63     base::StrAppend(out, {"\n", std::string(kStartCol, ' ')});
64     col = kStartCol;
65   }
66 
67   while (!help.empty()) {
68     size_t desired_len = kEndCol - col;
69     size_t wrap_pos = FindLineWrapPosition(help, desired_len);
70     if (wrap_pos == std::string::npos) {
71       base::StrAppend(out, {help});
72       break;
73     }
74     base::StrAppend(
75         out, {help.substr(0, wrap_pos), "\n", std::string(kStartCol, ' ')});
76     help = help.substr(wrap_pos + 1);
77     col = kStartCol;
78   }
79   base::StrAppend(out, {"\n"});
80 }
81 
82 // Overload for platforms where base::CommandLine::StringType == std::string.
ToQuicheStringVector(const std::vector<std::string> & v)83 [[maybe_unused]] std::vector<std::string> ToQuicheStringVector(
84     const std::vector<std::string>& v) {
85   return v;
86 }
87 
88 #if defined(WCHAR_T_IS_16_BIT)
89 // Overload for platforms where base::CommandLine::StringType == std::wstring.
ToQuicheStringVector(const std::vector<std::wstring> & v)90 [[maybe_unused]] std::vector<std::string> ToQuicheStringVector(
91     const std::vector<std::wstring>& v) {
92   std::vector<std::string> qsv;
93   for (const auto& s : v) {
94     if (!base::IsStringASCII(s)) {
95       QUIC_LOG(ERROR) << "Unable to convert to ASCII: " << s;
96       continue;
97     }
98     qsv.push_back(base::WideToASCII(s));
99   }
100   return qsv;
101 }
102 #endif  // defined(WCHAR_T_IS_16_BIT)
103 
104 }  // namespace
105 
106 // static
GetInstance()107 QuicheFlagRegistry& QuicheFlagRegistry::GetInstance() {
108   static base::NoDestructor<QuicheFlagRegistry> instance;
109   return *instance;
110 }
111 
RegisterFlag(const char * name,std::unique_ptr<QuicheFlagHelper> helper)112 void QuicheFlagRegistry::RegisterFlag(
113     const char* name,
114     std::unique_ptr<QuicheFlagHelper> helper) {
115   flags_.emplace(std::string(name), std::move(helper));
116 }
117 
SetFlags(const base::CommandLine & command_line,std::string * error_msg) const118 bool QuicheFlagRegistry::SetFlags(const base::CommandLine& command_line,
119                                   std::string* error_msg) const {
120   for (const auto& kv : flags_) {
121     const std::string& name = kv.first;
122     const QuicheFlagHelper* helper = kv.second.get();
123     if (!command_line.HasSwitch(name)) {
124       continue;
125     }
126     std::string value = command_line.GetSwitchValueASCII(name);
127     if (!helper->SetFlag(value)) {
128       *error_msg =
129           base::StrCat({"Invalid value \"", value, "\" for flag --", name});
130       return false;
131     }
132     QUIC_LOG(INFO) << "Set flag --" << name << " = " << value;
133   }
134   return true;
135 }
136 
ResetFlags() const137 void QuicheFlagRegistry::ResetFlags() const {
138   for (const auto& kv : flags_) {
139     kv.second->ResetFlag();
140     QUIC_LOG(INFO) << "Reset flag --" << kv.first;
141   }
142 }
143 
GetHelp() const144 std::string QuicheFlagRegistry::GetHelp() const {
145   std::string help;
146   AppendFlagDescription("help", "Print this help message.", &help);
147   for (const auto& kv : flags_) {
148     AppendFlagDescription(kv.first, kv.second->GetHelp(), &help);
149   }
150   return help;
151 }
152 
153 template <>
SetFlag(const std::string & s) const154 bool TypedQuicheFlagHelper<bool>::SetFlag(const std::string& s) const {
155   static const base::NoDestructor<std::set<std::string>> kTrueValues(
156       std::initializer_list<std::string>({"", "1", "t", "true", "y", "yes"}));
157   static const base::NoDestructor<std::set<std::string>> kFalseValues(
158       std::initializer_list<std::string>({"0", "f", "false", "n", "no"}));
159   if (kTrueValues->find(base::ToLowerASCII(s)) != kTrueValues->end()) {
160     *flag_ = true;
161     return true;
162   }
163   if (kFalseValues->find(base::ToLowerASCII(s)) != kFalseValues->end()) {
164     *flag_ = false;
165     return true;
166   }
167   return false;
168 }
169 
170 template <>
SetFlag(const std::string & s) const171 bool TypedQuicheFlagHelper<uint16_t>::SetFlag(const std::string& s) const {
172   int value;
173   if (!base::StringToInt(s, &value) ||
174       value < std::numeric_limits<uint16_t>::min() ||
175       value > std::numeric_limits<uint16_t>::max()) {
176     return false;
177   }
178   *flag_ = static_cast<uint16_t>(value);
179   return true;
180 }
181 
182 template <>
SetFlag(const std::string & s) const183 bool TypedQuicheFlagHelper<int32_t>::SetFlag(const std::string& s) const {
184   int32_t value;
185   if (!base::StringToInt(s, &value)) {
186     return false;
187   }
188   *flag_ = value;
189   return true;
190 }
191 
192 template <>
SetFlag(const std::string & s) const193 bool TypedQuicheFlagHelper<std::string>::SetFlag(const std::string& s) const {
194   *flag_ = s;
195   return true;
196 }
197 
198 template class TypedQuicheFlagHelper<bool>;
199 template class TypedQuicheFlagHelper<uint16_t>;
200 template class TypedQuicheFlagHelper<int32_t>;
201 template class TypedQuicheFlagHelper<std::string>;
202 
203 QuicheFlagRegistry::QuicheFlagRegistry() = default;
204 QuicheFlagRegistry::~QuicheFlagRegistry() = default;
205 
QuicheParseCommandLineFlagsImpl(const char * usage,int argc,const char * const * argv)206 std::vector<std::string> QuicheParseCommandLineFlagsImpl(
207     const char* usage,
208     int argc,
209     const char* const* argv) {
210   base::CommandLine::Init(argc, argv);
211   auto result = QuicheParseCommandLineFlagsHelper(
212       usage, *base::CommandLine::ForCurrentProcess());
213   if (result.exit_status.has_value()) {
214     exit(*result.exit_status);
215   }
216 
217   logging::LoggingSettings settings;
218   settings.logging_dest = logging::LOG_TO_STDERR;
219   CHECK(logging::InitLogging(settings));
220 
221   return result.non_flag_args;
222 }
223 
QuicheParseCommandLineFlagsHelper(const char * usage,const base::CommandLine & command_line)224 QuicheParseCommandLineFlagsResult QuicheParseCommandLineFlagsHelper(
225     const char* usage,
226     const base::CommandLine& command_line) {
227   QuicheParseCommandLineFlagsResult result;
228   result.non_flag_args = ToQuicheStringVector(command_line.GetArgs());
229   if (command_line.HasSwitch("h") || command_line.HasSwitch("help")) {
230     QuichePrintCommandLineFlagHelpImpl(usage);
231     result.exit_status = 0;
232   } else {
233     std::string msg;
234     if (!QuicheFlagRegistry::GetInstance().SetFlags(command_line, &msg)) {
235       std::cerr << msg << std::endl;
236       result.exit_status = 1;
237     }
238   }
239   return result;
240 }
241 
QuichePrintCommandLineFlagHelpImpl(const char * usage)242 void QuichePrintCommandLineFlagHelpImpl(const char* usage) {
243   std::cout << usage << std::endl
244             << "Options:" << std::endl
245             << QuicheFlagRegistry::GetInstance().GetHelp() << std::endl;
246 }
247 
248 QuicheParseCommandLineFlagsResult::QuicheParseCommandLineFlagsResult() =
249     default;
250 QuicheParseCommandLineFlagsResult::QuicheParseCommandLineFlagsResult(
251     const QuicheParseCommandLineFlagsResult&) = default;
252 QuicheParseCommandLineFlagsResult::~QuicheParseCommandLineFlagsResult() =
253     default;
254 
255 }  // namespace quiche
256