Home | History | Annotate | Download | only in brillo
      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