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