// 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