1 // Copyright 2014 The Chromium OS Authors. All rights reserved.
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 "brillo/flag_helper.h"
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <sysexits.h>
10
11 #include <memory>
12 #include <string>
13 #include <utility>
14
15 #include <base/base_switches.h>
16 #include <base/command_line.h>
17 #include <base/logging.h>
18 #include <base/strings/stringprintf.h>
19 #include <base/strings/string_number_conversions.h>
20
21 namespace brillo {
22
Flag(const char * name,const char * default_value,const char * help,bool visible)23 Flag::Flag(const char* name,
24 const char* default_value,
25 const char* help,
26 bool visible)
27 : name_(name),
28 default_value_(default_value),
29 help_(help),
30 visible_(visible) {
31 }
32
33 class HelpFlag : public brillo::Flag {
34 public:
HelpFlag()35 HelpFlag() : Flag("help", "false", "Show this help message", true) {}
36
SetValue(const std::string &)37 bool SetValue(const std::string& /* value */) override { return true; };
GetType() const38 const char* GetType() const override { return "bool"; }
39 };
40
BoolFlag(const char * name,bool * value,bool * no_value,const char * default_value,const char * help,bool visible)41 BoolFlag::BoolFlag(const char* name,
42 bool* value,
43 bool* no_value,
44 const char* default_value,
45 const char* help,
46 bool visible)
47 : Flag(name, default_value, help, visible),
48 value_(value),
49 no_value_(no_value) {
50 }
51
SetValue(const std::string & value)52 bool BoolFlag::SetValue(const std::string& value) {
53 if (value.empty()) {
54 *value_ = true;
55 } else {
56 if (!value.compare("true"))
57 *value_ = true;
58 else if (!value.compare("false"))
59 *value_ = false;
60 else
61 return false;
62 }
63
64 *no_value_ = !*value_;
65
66 return true;
67 }
68
GetType() const69 const char* BoolFlag::GetType() const {
70 return "bool";
71 }
72
Int32Flag(const char * name,int * value,const char * default_value,const char * help,bool visible)73 Int32Flag::Int32Flag(const char* name,
74 int* value,
75 const char* default_value,
76 const char* help,
77 bool visible)
78 : Flag(name, default_value, help, visible), value_(value) {
79 }
80
SetValue(const std::string & value)81 bool Int32Flag::SetValue(const std::string& value) {
82 return base::StringToInt(value, value_);
83 }
84
GetType() const85 const char* Int32Flag::GetType() const {
86 return "int";
87 }
88
UInt32Flag(const char * name,uint32_t * value,const char * default_value,const char * help,bool visible)89 UInt32Flag::UInt32Flag(const char* name,
90 uint32_t* value,
91 const char* default_value,
92 const char* help,
93 bool visible)
94 : Flag(name, default_value, help, visible), value_(value) {
95 }
96
SetValue(const std::string & value)97 bool UInt32Flag::SetValue(const std::string& value) {
98 return base::StringToUint(value, value_);
99 }
100
GetType() const101 const char* UInt32Flag::GetType() const {
102 return "uint32";
103 }
104
Int64Flag(const char * name,int64_t * value,const char * default_value,const char * help,bool visible)105 Int64Flag::Int64Flag(const char* name,
106 int64_t* value,
107 const char* default_value,
108 const char* help,
109 bool visible)
110 : Flag(name, default_value, help, visible), value_(value) {
111 }
112
SetValue(const std::string & value)113 bool Int64Flag::SetValue(const std::string& value) {
114 return base::StringToInt64(value, value_);
115 }
116
GetType() const117 const char* Int64Flag::GetType() const {
118 return "int64";
119 }
120
UInt64Flag(const char * name,uint64_t * value,const char * default_value,const char * help,bool visible)121 UInt64Flag::UInt64Flag(const char* name,
122 uint64_t* value,
123 const char* default_value,
124 const char* help,
125 bool visible)
126 : Flag(name, default_value, help, visible), value_(value) {
127 }
128
SetValue(const std::string & value)129 bool UInt64Flag::SetValue(const std::string& value) {
130 return base::StringToUint64(value, value_);
131 }
132
GetType() const133 const char* UInt64Flag::GetType() const {
134 return "uint64";
135 }
136
DoubleFlag(const char * name,double * value,const char * default_value,const char * help,bool visible)137 DoubleFlag::DoubleFlag(const char* name,
138 double* value,
139 const char* default_value,
140 const char* help,
141 bool visible)
142 : Flag(name, default_value, help, visible), value_(value) {
143 }
144
SetValue(const std::string & value)145 bool DoubleFlag::SetValue(const std::string& value) {
146 return base::StringToDouble(value, value_);
147 }
148
GetType() const149 const char* DoubleFlag::GetType() const {
150 return "double";
151 }
152
StringFlag(const char * name,std::string * value,const char * default_value,const char * help,bool visible)153 StringFlag::StringFlag(const char* name,
154 std::string* value,
155 const char* default_value,
156 const char* help,
157 bool visible)
158 : Flag(name, default_value, help, visible), value_(value) {
159 }
160
SetValue(const std::string & value)161 bool StringFlag::SetValue(const std::string& value) {
162 value_->assign(value);
163
164 return true;
165 }
166
GetType() const167 const char* StringFlag::GetType() const {
168 return "string";
169 }
170
171 namespace {
172 brillo::FlagHelper* instance_ = nullptr;
173 } // namespace
174
FlagHelper()175 FlagHelper::FlagHelper() : command_line_(nullptr) {
176 AddFlag(std::unique_ptr<Flag>(new HelpFlag()));
177 }
178
~FlagHelper()179 FlagHelper::~FlagHelper() {
180 }
181
GetInstance()182 brillo::FlagHelper* FlagHelper::GetInstance() {
183 if (!instance_)
184 instance_ = new FlagHelper();
185
186 return instance_;
187 }
188
ResetForTesting()189 void FlagHelper::ResetForTesting() {
190 delete instance_;
191 instance_ = nullptr;
192 }
193
Init(int argc,const char * const * argv,std::string help_usage)194 void FlagHelper::Init(int argc,
195 const char* const* argv,
196 std::string help_usage) {
197 brillo::FlagHelper* helper = GetInstance();
198 if (!helper->command_line_) {
199 if (!base::CommandLine::InitializedForCurrentProcess())
200 base::CommandLine::Init(argc, argv);
201 helper->command_line_ = base::CommandLine::ForCurrentProcess();
202 }
203
204 GetInstance()->SetUsageMessage(help_usage);
205
206 GetInstance()->UpdateFlagValues();
207 }
208
UpdateFlagValues()209 void FlagHelper::UpdateFlagValues() {
210 std::string error_msg;
211 int error_code = EX_OK;
212
213 // Check that base::CommandLine has been initialized.
214 CHECK(base::CommandLine::InitializedForCurrentProcess());
215
216 // If the --help flag exists, print out help message and exit.
217 if (command_line_->HasSwitch("help")) {
218 puts(GetHelpMessage().c_str());
219 exit(EX_OK);
220 }
221
222 // Iterate over the base::CommandLine switches. Update the value
223 // of the corresponding Flag if it exists, or output an error message
224 // if the flag wasn't defined.
225 const base::CommandLine::SwitchMap& switch_map = command_line_->GetSwitches();
226
227 for (const auto& pair : switch_map) {
228 const std::string& key = pair.first;
229 // Make sure we allow the standard logging switches (--v and --vmodule).
230 if (key == switches::kV || key == switches::kVModule)
231 continue;
232
233 const std::string& value = pair.second;
234
235 auto df_it = defined_flags_.find(key);
236 if (df_it != defined_flags_.end()) {
237 Flag* flag = df_it->second.get();
238 if (!flag->SetValue(value)) {
239 base::StringAppendF(
240 &error_msg,
241 "ERROR: illegal value '%s' specified for %s flag '%s'\n",
242 value.c_str(),
243 flag->GetType(),
244 flag->name_);
245 error_code = EX_DATAERR;
246 }
247 } else {
248 base::StringAppendF(
249 &error_msg, "ERROR: unknown command line flag '%s'\n", key.c_str());
250 error_code = EX_USAGE;
251 }
252 }
253
254 if (error_code != EX_OK) {
255 puts(error_msg.c_str());
256 exit(error_code);
257 }
258 }
259
AddFlag(std::unique_ptr<Flag> flag)260 void FlagHelper::AddFlag(std::unique_ptr<Flag> flag) {
261 defined_flags_.emplace(flag->name_, std::move(flag));
262 }
263
SetUsageMessage(std::string help_usage)264 void FlagHelper::SetUsageMessage(std::string help_usage) {
265 help_usage_.assign(std::move(help_usage));
266 }
267
GetHelpMessage() const268 std::string FlagHelper::GetHelpMessage() const {
269 std::string help = help_usage_;
270 help.append("\n\n");
271 for (const auto& pair : defined_flags_) {
272 const Flag* flag = pair.second.get();
273 if (flag->visible_) {
274 base::StringAppendF(&help,
275 " --%s (%s) type: %s default: %s\n",
276 flag->name_,
277 flag->help_,
278 flag->GetType(),
279 flag->default_value_);
280 }
281 }
282 return help;
283 }
284
285 } // namespace brillo
286