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 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: 33 HelpFlag() : Flag("help", "false", "Show this help message", true) {} 34 35 bool SetValue(const std::string& /* value */) override { return true; }; 36 const char* GetType() const override { return "bool"; } 37 }; 38 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 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 67 const char* BoolFlag::GetType() const { 68 return "bool"; 69 } 70 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 79 bool Int32Flag::SetValue(const std::string& value) { 80 return base::StringToInt(value, value_); 81 } 82 83 const char* Int32Flag::GetType() const { 84 return "int"; 85 } 86 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 95 bool Int64Flag::SetValue(const std::string& value) { 96 return base::StringToInt64(value, value_); 97 } 98 99 const char* Int64Flag::GetType() const { 100 return "int64"; 101 } 102 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 111 bool UInt64Flag::SetValue(const std::string& value) { 112 return base::StringToUint64(value, value_); 113 } 114 115 const char* UInt64Flag::GetType() const { 116 return "uint64"; 117 } 118 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 127 bool DoubleFlag::SetValue(const std::string& value) { 128 return base::StringToDouble(value, value_); 129 } 130 131 const char* DoubleFlag::GetType() const { 132 return "double"; 133 } 134 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 143 bool StringFlag::SetValue(const std::string& value) { 144 value_->assign(value); 145 146 return true; 147 } 148 149 const char* StringFlag::GetType() const { 150 return "string"; 151 } 152 153 namespace { 154 brillo::FlagHelper* instance_ = nullptr; 155 } // namespace 156 157 FlagHelper::FlagHelper() : command_line_(nullptr) { 158 AddFlag(std::unique_ptr<Flag>(new HelpFlag())); 159 } 160 161 FlagHelper::~FlagHelper() { 162 } 163 164 brillo::FlagHelper* FlagHelper::GetInstance() { 165 if (!instance_) 166 instance_ = new FlagHelper(); 167 168 return instance_; 169 } 170 171 void FlagHelper::ResetForTesting() { 172 delete instance_; 173 instance_ = nullptr; 174 } 175 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 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 242 void FlagHelper::AddFlag(std::unique_ptr<Flag> flag) { 243 defined_flags_.emplace(flag->name_, std::move(flag)); 244 } 245 246 void FlagHelper::SetUsageMessage(std::string help_usage) { 247 help_usage_.assign(std::move(help_usage)); 248 } 249 250 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