xref: /aosp_15_r20/external/libbrillo/brillo/flag_helper.cc (revision 1a96fba65179ea7d3f56207137718607415c5953)
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