blob: 0fb4624e1adb9aa5f87bdf00815f4182245af2b5 [file] [log] [blame]
initial.commit3f4a7322008-07-27 06:49:38 +09001// Copyright 2008, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
evanm@google.com91cdff82008-08-08 05:07:32 +090030#include "base/command_line.h"
31
32#if defined(OS_WIN)
initial.commit3f4a7322008-07-27 06:49:38 +090033#include <windows.h>
34#include <shellapi.h>
evanm@google.com91cdff82008-08-08 05:07:32 +090035#endif
initial.commit3f4a7322008-07-27 06:49:38 +090036
37#include <algorithm>
38
initial.commit3f4a7322008-07-27 06:49:38 +090039#include "base/logging.h"
40#include "base/singleton.h"
deanm@google.coma65ec9f2008-08-19 22:19:24 +090041#include "base/string_piece.h"
initial.commit3f4a7322008-07-27 06:49:38 +090042#include "base/string_util.h"
pinkerton@google.comd5fb8e92008-08-08 07:28:24 +090043#include "base/sys_string_conversions.h"
initial.commit3f4a7322008-07-27 06:49:38 +090044
45using namespace std;
46
47// Since we use a lazy match, make sure that longer versions (like L"--")
48// are listed before shorter versions (like L"-") of similar prefixes.
pinkerton@google.com0c0e01c2008-08-09 05:46:21 +090049#if defined(OS_WIN)
initial.commit3f4a7322008-07-27 06:49:38 +090050const wchar_t* const CommandLine::kSwitchPrefixes[] = {L"--", L"-", L"/"};
pinkerton@google.com0c0e01c2008-08-09 05:46:21 +090051#elif defined(OS_POSIX)
evanm@google.com638e9fb2008-08-12 10:14:37 +090052// Unixes don't use slash as a switch.
pinkerton@google.com0c0e01c2008-08-09 05:46:21 +090053const wchar_t* const CommandLine::kSwitchPrefixes[] = {L"--", L"-"};
54#endif
initial.commit3f4a7322008-07-27 06:49:38 +090055
56const wchar_t CommandLine::kSwitchValueSeparator[] = L"=";
57
evanm@google.com91cdff82008-08-08 05:07:32 +090058// Needed to avoid a typecast on the tolower() function pointer in Lowercase().
59// MSVC accepts it as-is but GCC requires the typecast.
60static int ToLower(int c) {
61 return tolower(c);
62}
63
initial.commit3f4a7322008-07-27 06:49:38 +090064static void Lowercase(wstring* parameter) {
evanm@google.com91cdff82008-08-08 05:07:32 +090065 transform(parameter->begin(), parameter->end(), parameter->begin(),
66 ToLower);
initial.commit3f4a7322008-07-27 06:49:38 +090067}
68
69// CommandLine::Data
70//
71// This object holds the parsed data for a command line. We hold this in a
72// separate object from |CommandLine| so that we can share the parsed data
73// across multiple |CommandLine| objects. When we share |Data|, we might be
74// accessing this object on multiple threads. To ensure thread safety, the
75// public interface of this object is const only.
76//
77// Do NOT add any non-const methods to this object. You have been warned.
78class CommandLine::Data {
79 public:
evanm@google.com91cdff82008-08-08 05:07:32 +090080#if defined(OS_WIN)
81 Data() {
initial.commit3f4a7322008-07-27 06:49:38 +090082 Init(GetCommandLineW());
83 }
evanm@google.com638e9fb2008-08-12 10:14:37 +090084#elif defined(OS_POSIX)
evanm@google.com91cdff82008-08-08 05:07:32 +090085 Data() {
evanm@google.com638e9fb2008-08-12 10:14:37 +090086 // Owner must call Init().
evanm@google.com91cdff82008-08-08 05:07:32 +090087 }
88#endif
initial.commit3f4a7322008-07-27 06:49:38 +090089
evanm@google.com91cdff82008-08-08 05:07:32 +090090#if defined(OS_WIN)
initial.commit3f4a7322008-07-27 06:49:38 +090091 Data(const wstring& command_line) {
92 Init(command_line);
93 }
evanm@google.com91cdff82008-08-08 05:07:32 +090094#elif defined(OS_POSIX)
tc@google.com1bc8d522008-08-15 07:09:39 +090095 Data(int argc, const char* const* argv) {
evanm@google.com91cdff82008-08-08 05:07:32 +090096 Init(argc, argv);
97 }
98#endif
99
100#if defined(OS_WIN)
101 // Does the actual parsing of the command line.
102 void Init(const std::wstring& command_line) {
103 TrimWhitespace(command_line, TRIM_ALL, &command_line_string_);
104
105 if (command_line_string_.empty())
106 return;
107
108 int num_args = 0;
109 wchar_t** args = NULL;
110
111 args = CommandLineToArgvW(command_line_string_.c_str(), &num_args);
112
113 // Populate program_ with the trimmed version of the first arg.
114 TrimWhitespace(args[0], TRIM_ALL, &program_);
115
116 for (int i = 1; i < num_args; ++i) {
117 wstring arg;
118 TrimWhitespace(args[i], TRIM_ALL, &arg);
119
120 wstring switch_string;
121 wstring switch_value;
122 if (IsSwitch(arg, &switch_string, &switch_value)) {
123 switches_[switch_string] = switch_value;
124 } else {
125 loose_values_.push_back(arg);
126 }
127 }
128
129 if (args)
130 LocalFree(args);
131 }
evanm@google.com638e9fb2008-08-12 10:14:37 +0900132
133#elif defined(OS_POSIX)
134 // Does the actual parsing of the command line.
tc@google.com1bc8d522008-08-15 07:09:39 +0900135 void Init(int argc, const char* const* argv) {
pinkerton@google.com0c0e01c2008-08-09 05:46:21 +0900136 if (argc < 1)
evanm@google.com91cdff82008-08-08 05:07:32 +0900137 return;
pinkerton@google.comd5fb8e92008-08-08 07:28:24 +0900138 program_ = base::SysNativeMBToWide(argv[0]);
evanm@google.com91cdff82008-08-08 05:07:32 +0900139 command_line_string_ = program_;
140
141 for (int i = 1; i < argc; ++i) {
pinkerton@google.comd5fb8e92008-08-08 07:28:24 +0900142 std::wstring arg = base::SysNativeMBToWide(argv[i]);
evanm@google.com91cdff82008-08-08 05:07:32 +0900143 command_line_string_.append(L" ");
144 command_line_string_.append(arg);
145
146 wstring switch_string;
147 wstring switch_value;
148 if (IsSwitch(arg, &switch_string, &switch_value)) {
149 switches_[switch_string] = switch_value;
150 } else {
151 loose_values_.push_back(arg);
152 }
153 }
154 }
155#endif
initial.commit3f4a7322008-07-27 06:49:38 +0900156
157 const std::wstring& command_line_string() const {
158 return command_line_string_;
159 }
160
161 const std::wstring& program() const {
162 return program_;
163 }
164
165 const std::map<std::wstring, std::wstring>& switches() const {
166 return switches_;
167 }
168
169 const std::vector<std::wstring>& loose_values() const {
170 return loose_values_;
171 }
172
173 private:
174 // Returns true if parameter_string represents a switch. If true,
175 // switch_string and switch_value are set. (If false, both are
176 // set to the empty string.)
177 static bool IsSwitch(const wstring& parameter_string,
178 wstring* switch_string,
179 wstring* switch_value) {
180
181 *switch_string = L"";
182 *switch_value = L"";
183
184 for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) {
185 std::wstring prefix(kSwitchPrefixes[i]);
186 if (parameter_string.find(prefix) != 0) // check prefix
187 continue;
188
189 const size_t switch_start = prefix.length();
190 const size_t equals_position = parameter_string.find(
191 kSwitchValueSeparator, switch_start);
192 if (equals_position == wstring::npos) {
193 *switch_string = parameter_string.substr(switch_start);
194 } else {
195 *switch_string = parameter_string.substr(
196 switch_start, equals_position - switch_start);
197 *switch_value = parameter_string.substr(equals_position + 1);
198 }
199 Lowercase(switch_string);
200
201 return true;
202 }
203
204 return false;
205 }
206
initial.commit3f4a7322008-07-27 06:49:38 +0900207 std::wstring command_line_string_;
208 std::wstring program_;
209 std::map<std::wstring, std::wstring> switches_;
210 std::vector<std::wstring> loose_values_;
211
evanm@google.com91cdff82008-08-08 05:07:32 +0900212 DISALLOW_EVIL_CONSTRUCTORS(Data);
initial.commit3f4a7322008-07-27 06:49:38 +0900213};
214
215CommandLine::CommandLine()
216 : we_own_data_(false), // The Singleton class will manage it for us.
217 data_(Singleton<Data>::get()) {
evanm@google.com638e9fb2008-08-12 10:14:37 +0900218 DCHECK(!data_->command_line_string().empty()) <<
219 "You must call CommandLine::SetArgcArgv before making any CommandLine "
220 "calls.";
initial.commit3f4a7322008-07-27 06:49:38 +0900221}
222
evanm@google.com91cdff82008-08-08 05:07:32 +0900223#if defined(OS_WIN)
initial.commit3f4a7322008-07-27 06:49:38 +0900224CommandLine::CommandLine(const wstring& command_line)
225 : we_own_data_(true),
226 data_(new Data(command_line)) {
227}
evanm@google.com91cdff82008-08-08 05:07:32 +0900228#elif defined(OS_POSIX)
tc@google.com1bc8d522008-08-15 07:09:39 +0900229CommandLine::CommandLine(const int argc, const char* const* argv)
evanm@google.com91cdff82008-08-08 05:07:32 +0900230 : we_own_data_(true),
231 data_(new Data(argc, argv)) {
232}
233#endif
initial.commit3f4a7322008-07-27 06:49:38 +0900234
235CommandLine::~CommandLine() {
236 if (we_own_data_)
237 delete data_;
238}
239
evanm@google.com638e9fb2008-08-12 10:14:37 +0900240// static
tc@google.com1bc8d522008-08-15 07:09:39 +0900241void CommandLine::SetArgcArgv(int argc, const char* const* argv) {
evanm@google.com1c0026a2008-08-12 10:27:35 +0900242#if !defined(OS_WIN)
evanm@google.com638e9fb2008-08-12 10:14:37 +0900243 Singleton<Data>::get()->Init(argc, argv);
evanm@google.com1c0026a2008-08-12 10:27:35 +0900244#endif
evanm@google.com638e9fb2008-08-12 10:14:37 +0900245}
246
initial.commit3f4a7322008-07-27 06:49:38 +0900247bool CommandLine::HasSwitch(const wstring& switch_string) const {
248 wstring lowercased_switch(switch_string);
249 Lowercase(&lowercased_switch);
250 return data_->switches().find(lowercased_switch) != data_->switches().end();
251}
252
253wstring CommandLine::GetSwitchValue(const wstring& switch_string) const {
254 wstring lowercased_switch(switch_string);
255 Lowercase(&lowercased_switch);
256
257 const map<wstring, wstring>::const_iterator result =
258 data_->switches().find(lowercased_switch);
259
260 if (result == data_->switches().end()) {
261 return L"";
262 } else {
263 return result->second;
264 }
265}
266
267size_t CommandLine::GetLooseValueCount() const {
268 return data_->loose_values().size();
269}
270
271CommandLine::LooseValueIterator CommandLine::GetLooseValuesBegin() const {
272 return data_->loose_values().begin();
273}
274
275CommandLine::LooseValueIterator CommandLine::GetLooseValuesEnd() const {
276 return data_->loose_values().end();
277}
278
279std::wstring CommandLine::command_line_string() const {
280 return data_->command_line_string();
281}
282
283std::wstring CommandLine::program() const {
284 return data_->program();
285}
286
287// static
288void CommandLine::AppendSwitch(wstring* command_line_string,
289 const wstring& switch_string) {
290 DCHECK(command_line_string);
291 command_line_string->append(L" ");
292 command_line_string->append(kSwitchPrefixes[0]);
293 command_line_string->append(switch_string);
294}
295
296// static
297void CommandLine::AppendSwitchWithValue(wstring* command_line_string,
298 const wstring& switch_string,
299 const wstring& value_string) {
300 AppendSwitch(command_line_string, switch_string);
301
302 if (value_string.empty())
303 return;
304
305 command_line_string->append(kSwitchValueSeparator);
306 // NOTE(jhughes): If the value contains a quotation mark at one
307 // end but not both, you may get unusable output.
308 if ((value_string.find(L" ") != std::wstring::npos) &&
309 (value_string[0] != L'"') &&
310 (value_string[value_string.length() - 1] != L'"')) {
311 // need to provide quotes
mmentovai@google.comaeff9442008-08-14 09:41:45 +0900312 StringAppendF(command_line_string, L"\"%ls\"", value_string.c_str());
initial.commit3f4a7322008-07-27 06:49:38 +0900313 } else {
314 command_line_string->append(value_string);
315 }
316}