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