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