blob: 7f309a26eb2fc67b2b40d95499e9dd8d3c04391e [file] [log] [blame]
Harry Cutts44402922022-10-19 17:42:27 +00001// Copyright 2014 The ChromiumOS Authors
Michael Spang2b1a8fc2014-04-04 13:29:13 -04002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Harry Cutts2cb5dba2022-10-19 17:25:54 +00005#include "include/command_line.h"
Michael Spang2b1a8fc2014-04-04 13:29:13 -04006
7#include <algorithm>
8#include <ostream>
9#include <string>
10
Harry Cutts2cb5dba2022-10-19 17:25:54 +000011#include "include/macros.h"
12#include "include/string_util.h"
Michael Spang2b1a8fc2014-04-04 13:29:13 -040013
14namespace gestures {
15
Harry Cutts91d989b2023-06-02 15:38:45 +000016CommandLine* CommandLine::current_process_commandline_ = nullptr;
Michael Spang2b1a8fc2014-04-04 13:29:13 -040017
18namespace {
19
20const CommandLine::CharType kSwitchTerminator[] = "--";
21const 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.
26const CommandLine::CharType* const kSwitchPrefixes[] = {"--", "-"};
27size_t switch_prefix_count = arraysize(kSwitchPrefixes);
28
29size_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|.
40bool 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.
57void 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 TrimWhitespaceASCII(arg, TRIM_ALL, &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
77CommandLine::CommandLine()
78 : argv_(1),
79 begin_args_(1) {}
80
81CommandLine::~CommandLine() {}
82
83// static
84bool 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
98void CommandLine::Reset() {
99 delete current_process_commandline_;
Harry Cutts91d989b2023-06-02 15:38:45 +0000100 current_process_commandline_ = nullptr;
Michael Spang2b1a8fc2014-04-04 13:29:13 -0400101}
102
103// static
104CommandLine* 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
113void 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
121void 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
129std::string CommandLine::GetProgram() const {
130 return argv_[0];
131}
132
133void CommandLine::SetProgram(const std::string& program) {
134 TrimWhitespaceASCII(program, TRIM_ALL, &argv_[0]);
135}
136
137bool CommandLine::HasSwitch(const std::string& switch_string) const {
138 return switches_.find(switch_string) != switches_.end();
139}
140
141std::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
148void CommandLine::AppendSwitch(const std::string& switch_string) {
149 AppendSwitchASCII(switch_string, std::string());
150}
151
152void 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
167CommandLine::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
178void CommandLine::AppendArgASCII(const std::string& value) {
179 argv_.push_back(value);
180}
181
182} // namespace gestures