blob: 0649ee852c6d34dd9777a6449175363105a8f163 [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"
41#include "base/string_util.h"
pinkerton@google.comd5fb8e92008-08-08 07:28:24 +090042#include "base/sys_string_conversions.h"
initial.commit3f4a7322008-07-27 06:49:38 +090043
evanm@google.com91cdff82008-08-08 05:07:32 +090044extern "C" {
45#if defined(OS_MACOSX)
46const char** NXArgv;
47int NXArgc;
48#elif defined(OS_LINUX)
evanm@google.com91cdff82008-08-08 05:07:32 +090049const char** __libc_argv;
pinkerton@google.comd5fb8e92008-08-08 07:28:24 +090050int __libc_argc;
evanm@google.com91cdff82008-08-08 05:07:32 +090051#endif
52} // extern "C"
53
initial.commit3f4a7322008-07-27 06:49:38 +090054using namespace std;
55
56// Since we use a lazy match, make sure that longer versions (like L"--")
57// are listed before shorter versions (like L"-") of similar prefixes.
58const wchar_t* const CommandLine::kSwitchPrefixes[] = {L"--", L"-", L"/"};
59
60const wchar_t CommandLine::kSwitchValueSeparator[] = L"=";
61
evanm@google.com91cdff82008-08-08 05:07:32 +090062// Needed to avoid a typecast on the tolower() function pointer in Lowercase().
63// MSVC accepts it as-is but GCC requires the typecast.
64static int ToLower(int c) {
65 return tolower(c);
66}
67
initial.commit3f4a7322008-07-27 06:49:38 +090068static void Lowercase(wstring* parameter) {
evanm@google.com91cdff82008-08-08 05:07:32 +090069 transform(parameter->begin(), parameter->end(), parameter->begin(),
70 ToLower);
initial.commit3f4a7322008-07-27 06:49:38 +090071}
72
73// CommandLine::Data
74//
75// This object holds the parsed data for a command line. We hold this in a
76// separate object from |CommandLine| so that we can share the parsed data
77// across multiple |CommandLine| objects. When we share |Data|, we might be
78// accessing this object on multiple threads. To ensure thread safety, the
79// public interface of this object is const only.
80//
81// Do NOT add any non-const methods to this object. You have been warned.
82class CommandLine::Data {
83 public:
evanm@google.com91cdff82008-08-08 05:07:32 +090084#if defined(OS_WIN)
85 Data() {
initial.commit3f4a7322008-07-27 06:49:38 +090086 Init(GetCommandLineW());
87 }
evanm@google.com91cdff82008-08-08 05:07:32 +090088#elif defined(OS_MACOSX)
89 Data() {
90 Init(NXArgc, NXArgv);
91 }
92#elif defined(OS_LINUX)
93 Data() {
94 Init(__gnuc_argc, __gnuc_argv);
95 }
96#endif
initial.commit3f4a7322008-07-27 06:49:38 +090097
evanm@google.com91cdff82008-08-08 05:07:32 +090098#if defined(OS_WIN)
initial.commit3f4a7322008-07-27 06:49:38 +090099 Data(const wstring& command_line) {
100 Init(command_line);
101 }
evanm@google.com91cdff82008-08-08 05:07:32 +0900102#elif defined(OS_POSIX)
103 Data(const int argc, const char* argv[]) {
104 Init(argc, argv);
105 }
106#endif
107
108#if defined(OS_WIN)
109 // Does the actual parsing of the command line.
110 void Init(const std::wstring& command_line) {
111 TrimWhitespace(command_line, TRIM_ALL, &command_line_string_);
112
113 if (command_line_string_.empty())
114 return;
115
116 int num_args = 0;
117 wchar_t** args = NULL;
118
119 args = CommandLineToArgvW(command_line_string_.c_str(), &num_args);
120
121 // Populate program_ with the trimmed version of the first arg.
122 TrimWhitespace(args[0], TRIM_ALL, &program_);
123
124 for (int i = 1; i < num_args; ++i) {
125 wstring arg;
126 TrimWhitespace(args[i], TRIM_ALL, &arg);
127
128 wstring switch_string;
129 wstring switch_value;
130 if (IsSwitch(arg, &switch_string, &switch_value)) {
131 switches_[switch_string] = switch_value;
132 } else {
133 loose_values_.push_back(arg);
134 }
135 }
136
137 if (args)
138 LocalFree(args);
139 }
140#elif defined(OS_POSIX) // Does the actual parsing of the command line.
141 void Init(int argc, const char* argv[]) {
142 if (argc <= 1)
143 return;
144
pinkerton@google.comd5fb8e92008-08-08 07:28:24 +0900145 program_ = base::SysNativeMBToWide(argv[0]);
evanm@google.com91cdff82008-08-08 05:07:32 +0900146 command_line_string_ = program_;
147
148 for (int i = 1; i < argc; ++i) {
pinkerton@google.comd5fb8e92008-08-08 07:28:24 +0900149 std::wstring arg = base::SysNativeMBToWide(argv[i]);
evanm@google.com91cdff82008-08-08 05:07:32 +0900150 command_line_string_.append(L" ");
151 command_line_string_.append(arg);
152
153 wstring switch_string;
154 wstring switch_value;
155 if (IsSwitch(arg, &switch_string, &switch_value)) {
156 switches_[switch_string] = switch_value;
157 } else {
158 loose_values_.push_back(arg);
159 }
160 }
161 }
162#endif
initial.commit3f4a7322008-07-27 06:49:38 +0900163
164 const std::wstring& command_line_string() const {
165 return command_line_string_;
166 }
167
168 const std::wstring& program() const {
169 return program_;
170 }
171
172 const std::map<std::wstring, std::wstring>& switches() const {
173 return switches_;
174 }
175
176 const std::vector<std::wstring>& loose_values() const {
177 return loose_values_;
178 }
179
180 private:
181 // Returns true if parameter_string represents a switch. If true,
182 // switch_string and switch_value are set. (If false, both are
183 // set to the empty string.)
184 static bool IsSwitch(const wstring& parameter_string,
185 wstring* switch_string,
186 wstring* switch_value) {
187
188 *switch_string = L"";
189 *switch_value = L"";
190
191 for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) {
192 std::wstring prefix(kSwitchPrefixes[i]);
193 if (parameter_string.find(prefix) != 0) // check prefix
194 continue;
195
196 const size_t switch_start = prefix.length();
197 const size_t equals_position = parameter_string.find(
198 kSwitchValueSeparator, switch_start);
199 if (equals_position == wstring::npos) {
200 *switch_string = parameter_string.substr(switch_start);
201 } else {
202 *switch_string = parameter_string.substr(
203 switch_start, equals_position - switch_start);
204 *switch_value = parameter_string.substr(equals_position + 1);
205 }
206 Lowercase(switch_string);
207
208 return true;
209 }
210
211 return false;
212 }
213
initial.commit3f4a7322008-07-27 06:49:38 +0900214 std::wstring command_line_string_;
215 std::wstring program_;
216 std::map<std::wstring, std::wstring> switches_;
217 std::vector<std::wstring> loose_values_;
218
evanm@google.com91cdff82008-08-08 05:07:32 +0900219 DISALLOW_EVIL_CONSTRUCTORS(Data);
initial.commit3f4a7322008-07-27 06:49:38 +0900220};
221
222CommandLine::CommandLine()
223 : we_own_data_(false), // The Singleton class will manage it for us.
224 data_(Singleton<Data>::get()) {
225}
226
evanm@google.com91cdff82008-08-08 05:07:32 +0900227#if defined(OS_WIN)
initial.commit3f4a7322008-07-27 06:49:38 +0900228CommandLine::CommandLine(const wstring& command_line)
229 : we_own_data_(true),
230 data_(new Data(command_line)) {
231}
evanm@google.com91cdff82008-08-08 05:07:32 +0900232#elif defined(OS_POSIX)
233CommandLine::CommandLine(const int argc, const char* argv[])
234 : we_own_data_(true),
235 data_(new Data(argc, argv)) {
236}
237#endif
initial.commit3f4a7322008-07-27 06:49:38 +0900238
239CommandLine::~CommandLine() {
240 if (we_own_data_)
241 delete data_;
242}
243
244bool CommandLine::HasSwitch(const wstring& switch_string) const {
245 wstring lowercased_switch(switch_string);
246 Lowercase(&lowercased_switch);
247 return data_->switches().find(lowercased_switch) != data_->switches().end();
248}
249
250wstring CommandLine::GetSwitchValue(const wstring& switch_string) const {
251 wstring lowercased_switch(switch_string);
252 Lowercase(&lowercased_switch);
253
254 const map<wstring, wstring>::const_iterator result =
255 data_->switches().find(lowercased_switch);
256
257 if (result == data_->switches().end()) {
258 return L"";
259 } else {
260 return result->second;
261 }
262}
263
264size_t CommandLine::GetLooseValueCount() const {
265 return data_->loose_values().size();
266}
267
268CommandLine::LooseValueIterator CommandLine::GetLooseValuesBegin() const {
269 return data_->loose_values().begin();
270}
271
272CommandLine::LooseValueIterator CommandLine::GetLooseValuesEnd() const {
273 return data_->loose_values().end();
274}
275
276std::wstring CommandLine::command_line_string() const {
277 return data_->command_line_string();
278}
279
280std::wstring CommandLine::program() const {
281 return data_->program();
282}
283
284// static
285void CommandLine::AppendSwitch(wstring* command_line_string,
286 const wstring& switch_string) {
287 DCHECK(command_line_string);
288 command_line_string->append(L" ");
289 command_line_string->append(kSwitchPrefixes[0]);
290 command_line_string->append(switch_string);
291}
292
293// static
294void CommandLine::AppendSwitchWithValue(wstring* command_line_string,
295 const wstring& switch_string,
296 const wstring& value_string) {
297 AppendSwitch(command_line_string, switch_string);
298
299 if (value_string.empty())
300 return;
301
302 command_line_string->append(kSwitchValueSeparator);
303 // NOTE(jhughes): If the value contains a quotation mark at one
304 // end but not both, you may get unusable output.
305 if ((value_string.find(L" ") != std::wstring::npos) &&
306 (value_string[0] != L'"') &&
307 (value_string[value_string.length() - 1] != L'"')) {
308 // need to provide quotes
309 StringAppendF(command_line_string, L"\"%s\"", value_string.c_str());
310 } else {
311 command_line_string->append(value_string);
312 }
313}