// Copyright 2014 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "brillo/flag_helper.h"
#include <memory>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <sysexits.h>
#include <base/base_switches.h>
#include <base/command_line.h>
#include <base/logging.h>
#include <base/strings/stringprintf.h>
#include <base/strings/string_number_conversions.h>
namespace brillo {
Flag::Flag(const char* name,
const char* default_value,
const char* help,
bool visible)
: name_(name),
default_value_(default_value),
help_(help),
visible_(visible) {
}
class HelpFlag : public brillo::Flag {
public:
HelpFlag() : Flag("help", "false", "Show this help message", true) {}
bool SetValue(const std::string& /* value */) override { return true; };
const char* GetType() const override { return "bool"; }
};
BoolFlag::BoolFlag(const char* name,
bool* value,
bool* no_value,
const char* default_value,
const char* help,
bool visible)
: Flag(name, default_value, help, visible),
value_(value),
no_value_(no_value) {
}
bool BoolFlag::SetValue(const std::string& value) {
if (value.empty()) {
*value_ = true;
} else {
if (!value.compare("true"))
*value_ = true;
else if (!value.compare("false"))
*value_ = false;
else
return false;
}
*no_value_ = !*value_;
return true;
}
const char* BoolFlag::GetType() const {
return "bool";
}
Int32Flag::Int32Flag(const char* name,
int* value,
const char* default_value,
const char* help,
bool visible)
: Flag(name, default_value, help, visible), value_(value) {
}
bool Int32Flag::SetValue(const std::string& value) {
return base::StringToInt(value, value_);
}
const char* Int32Flag::GetType() const {
return "int";
}
Int64Flag::Int64Flag(const char* name,
int64_t* value,
const char* default_value,
const char* help,
bool visible)
: Flag(name, default_value, help, visible), value_(value) {
}
bool Int64Flag::SetValue(const std::string& value) {
return base::StringToInt64(value, value_);
}
const char* Int64Flag::GetType() const {
return "int64";
}
UInt64Flag::UInt64Flag(const char* name,
uint64_t* value,
const char* default_value,
const char* help,
bool visible)
: Flag(name, default_value, help, visible), value_(value) {
}
bool UInt64Flag::SetValue(const std::string& value) {
return base::StringToUint64(value, value_);
}
const char* UInt64Flag::GetType() const {
return "uint64";
}
DoubleFlag::DoubleFlag(const char* name,
double* value,
const char* default_value,
const char* help,
bool visible)
: Flag(name, default_value, help, visible), value_(value) {
}
bool DoubleFlag::SetValue(const std::string& value) {
return base::StringToDouble(value, value_);
}
const char* DoubleFlag::GetType() const {
return "double";
}
StringFlag::StringFlag(const char* name,
std::string* value,
const char* default_value,
const char* help,
bool visible)
: Flag(name, default_value, help, visible), value_(value) {
}
bool StringFlag::SetValue(const std::string& value) {
value_->assign(value);
return true;
}
const char* StringFlag::GetType() const {
return "string";
}
namespace {
brillo::FlagHelper* instance_ = nullptr;
} // namespace
FlagHelper::FlagHelper() : command_line_(nullptr) {
AddFlag(std::unique_ptr<Flag>(new HelpFlag()));
}
FlagHelper::~FlagHelper() {
}
brillo::FlagHelper* FlagHelper::GetInstance() {
if (!instance_)
instance_ = new FlagHelper();
return instance_;
}
void FlagHelper::ResetForTesting() {
delete instance_;
instance_ = nullptr;
}
void FlagHelper::Init(int argc,
const char* const* argv,
std::string help_usage) {
brillo::FlagHelper* helper = GetInstance();
if (!helper->command_line_) {
if (!base::CommandLine::InitializedForCurrentProcess())
base::CommandLine::Init(argc, argv);
helper->command_line_ = base::CommandLine::ForCurrentProcess();
}
GetInstance()->SetUsageMessage(help_usage);
GetInstance()->UpdateFlagValues();
}
void FlagHelper::UpdateFlagValues() {
std::string error_msg;
int error_code = EX_OK;
// Check that base::CommandLine has been initialized.
CHECK(base::CommandLine::InitializedForCurrentProcess());
// If the --help flag exists, print out help message and exit.
if (command_line_->HasSwitch("help")) {
puts(GetHelpMessage().c_str());
exit(EX_OK);
}
// Iterate over the base::CommandLine switches. Update the value
// of the corresponding Flag if it exists, or output an error message
// if the flag wasn't defined.
const base::CommandLine::SwitchMap& switch_map = command_line_->GetSwitches();
for (const auto& pair : switch_map) {
const std::string& key = pair.first;
// Make sure we allow the standard logging switches (--v and --vmodule).
if (key == switches::kV || key == switches::kVModule)
continue;
const std::string& value = pair.second;
auto df_it = defined_flags_.find(key);
if (df_it != defined_flags_.end()) {
Flag* flag = df_it->second.get();
if (!flag->SetValue(value)) {
base::StringAppendF(
&error_msg,
"ERROR: illegal value '%s' specified for %s flag '%s'\n",
value.c_str(),
flag->GetType(),
flag->name_);
error_code = EX_DATAERR;
}
} else {
base::StringAppendF(
&error_msg, "ERROR: unknown command line flag '%s'\n", key.c_str());
error_code = EX_USAGE;
}
}
if (error_code != EX_OK) {
puts(error_msg.c_str());
exit(error_code);
}
}
void FlagHelper::AddFlag(std::unique_ptr<Flag> flag) {
defined_flags_.emplace(flag->name_, std::move(flag));
}
void FlagHelper::SetUsageMessage(std::string help_usage) {
help_usage_.assign(std::move(help_usage));
}
std::string FlagHelper::GetHelpMessage() const {
std::string help = help_usage_;
help.append("\n\n");
for (const auto& pair : defined_flags_) {
const Flag* flag = pair.second.get();
if (flag->visible_) {
base::StringAppendF(&help,
" --%s (%s) type: %s default: %s\n",
flag->name_,
flag->help_,
flag->GetType(),
flag->default_value_);
}
}
return help;
}
} // namespace brillo