xref: /aosp_15_r20/external/libchrome-gestures/src/command_line.cc (revision aed3e5085e770be5b69ce25295ecf6ddf906af95)
1 // Copyright 2014 The ChromiumOS Authors
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 "include/command_line.h"
6 
7 #include <algorithm>
8 #include <ostream>
9 #include <string>
10 
11 #include "include/macros.h"
12 #include "include/string_util.h"
13 
14 namespace gestures {
15 
16 CommandLine* CommandLine::current_process_commandline_ = nullptr;
17 
18 namespace {
19 
20 const CommandLine::CharType kSwitchTerminator[] = "--";
21 const CommandLine::CharType kSwitchValueSeparator[] = "=";
22 
23 // Since we use a lazy match, make sure that longer versions (like "--") are
24 // listed before shorter versions (like "-") of similar prefixes.
25 // Unixes don't use slash as a switch.
26 const CommandLine::CharType* const kSwitchPrefixes[] = {"--", "-"};
27 size_t switch_prefix_count = arraysize(kSwitchPrefixes);
28 
GetSwitchPrefixLength(const std::string & string)29 size_t GetSwitchPrefixLength(const std::string& string) {
30   for (size_t i = 0; i < switch_prefix_count; ++i) {
31     std::string prefix(kSwitchPrefixes[i]);
32     if (string.compare(0, prefix.length(), prefix) == 0)
33       return prefix.length();
34   }
35   return 0;
36 }
37 
38 // Fills in |switch_string| and |switch_value| if |string| is a switch.
39 // This will preserve the input switch prefix in the output |switch_string|.
IsSwitch(const std::string & string,std::string * switch_string,std::string * switch_value)40 bool IsSwitch(const std::string& string,
41               std::string* switch_string,
42               std::string* switch_value) {
43   switch_string->clear();
44   switch_value->clear();
45   size_t prefix_length = GetSwitchPrefixLength(string);
46   if (prefix_length == 0 || prefix_length == string.length())
47     return false;
48 
49   const size_t equals_position = string.find(kSwitchValueSeparator);
50   *switch_string = string.substr(0, equals_position);
51   if (equals_position != std::string::npos)
52     *switch_value = string.substr(equals_position + 1);
53   return true;
54 }
55 
56 // Append switches and arguments, keeping switches before arguments.
AppendSwitchesAndArguments(CommandLine & command_line,const CommandLine::StringVector & argv)57 void AppendSwitchesAndArguments(CommandLine& command_line,
58                                 const CommandLine::StringVector& argv) {
59   bool parse_switches = true;
60   for (size_t i = 1; i < argv.size(); ++i) {
61     std::string arg = argv[i];
62     arg = TrimWhitespaceASCII(arg);
63 
64     std::string switch_string;
65     std::string switch_value;
66     parse_switches &= (arg != kSwitchTerminator);
67     if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) {
68       command_line.AppendSwitchASCII(switch_string, switch_value);
69     } else {
70       command_line.AppendArgASCII(arg);
71     }
72   }
73 }
74 
75 }  // namespace
76 
CommandLine()77 CommandLine::CommandLine()
78     : argv_(1),
79       begin_args_(1) {}
80 
~CommandLine()81 CommandLine::~CommandLine() {}
82 
83 // static
Init(int argc,const char * const * argv)84 bool CommandLine::Init(int argc, const char* const* argv) {
85   if (current_process_commandline_) {
86     // If this is intentional, Reset() must be called first. If we are using
87     // the shared build mode, we have to share a single object across multiple
88     // shared libraries.
89     return false;
90   }
91 
92   current_process_commandline_ = new CommandLine();
93   current_process_commandline_->InitFromArgv(argc, argv);
94   return true;
95 }
96 
97 // static
Reset()98 void CommandLine::Reset() {
99   delete current_process_commandline_;
100   current_process_commandline_ = nullptr;
101 }
102 
103 // static
ForCurrentProcess()104 CommandLine* CommandLine::ForCurrentProcess() {
105   if (!current_process_commandline_) {
106     fprintf(stderr, "FATAL: Command line not initialized!\n");
107     abort();
108   }
109   return current_process_commandline_;
110 }
111 
112 
InitFromArgv(int argc,const CommandLine::CharType * const * argv)113 void CommandLine::InitFromArgv(int argc,
114                                const CommandLine::CharType* const* argv) {
115   StringVector new_argv;
116   for (int i = 0; i < argc; ++i)
117     new_argv.push_back(argv[i]);
118   InitFromArgv(new_argv);
119 }
120 
InitFromArgv(const StringVector & argv)121 void CommandLine::InitFromArgv(const StringVector& argv) {
122   argv_ = StringVector(1);
123   switches_.clear();
124   begin_args_ = 1;
125   SetProgram(argv.empty() ? "" : argv[0]);
126   AppendSwitchesAndArguments(*this, argv);
127 }
128 
GetProgram() const129 std::string CommandLine::GetProgram() const {
130   return argv_[0];
131 }
132 
SetProgram(const std::string & program)133 void CommandLine::SetProgram(const std::string& program) {
134   argv_[0] = TrimWhitespaceASCII(program);
135 }
136 
HasSwitch(const std::string & switch_string) const137 bool CommandLine::HasSwitch(const std::string& switch_string) const {
138   return switches_.find(switch_string) != switches_.end();
139 }
140 
GetSwitchValueASCII(const std::string & switch_string) const141 std::string CommandLine::GetSwitchValueASCII(
142     const std::string& switch_string) const {
143   SwitchMap::const_iterator result =
144     switches_.find(switch_string);
145   return result == switches_.end() ? std::string() : result->second;
146 }
147 
AppendSwitch(const std::string & switch_string)148 void CommandLine::AppendSwitch(const std::string& switch_string) {
149   AppendSwitchASCII(switch_string, std::string());
150 }
151 
AppendSwitchASCII(const std::string & switch_string,const std::string & value)152 void CommandLine::AppendSwitchASCII(const std::string& switch_string,
153                                      const std::string& value) {
154   std::string switch_key(switch_string);
155   std::string combined_switch_string(switch_string);
156   size_t prefix_length = GetSwitchPrefixLength(combined_switch_string);
157   switches_[switch_key.substr(prefix_length)] = value;
158   // Preserve existing switch prefixes in |argv_|; only append one if necessary.
159   if (prefix_length == 0)
160     combined_switch_string = kSwitchPrefixes[0] + combined_switch_string;
161   if (!value.empty())
162     combined_switch_string += kSwitchValueSeparator + value;
163   // Append the switch and update the switches/arguments divider |begin_args_|.
164   argv_.insert(argv_.begin() + begin_args_++, combined_switch_string);
165 }
166 
GetArgs() const167 CommandLine::StringVector CommandLine::GetArgs() const {
168   // Gather all arguments after the last switch (may include kSwitchTerminator).
169   StringVector args(argv_.begin() + begin_args_, argv_.end());
170   // Erase only the first kSwitchTerminator (maybe "--" is a legitimate page?)
171   StringVector::iterator switch_terminator =
172       std::find(args.begin(), args.end(), kSwitchTerminator);
173   if (switch_terminator != args.end())
174     args.erase(switch_terminator);
175   return args;
176 }
177 
AppendArgASCII(const std::string & value)178 void CommandLine::AppendArgASCII(const std::string& value) {
179   argv_.push_back(value);
180 }
181 
182 }  // namespace gestures
183