1 // Copyright (c) 2006-2008 The Chromium 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 "base/command_line.h"
6
7 #if defined(OS_WIN)
8 #include <windows.h>
9 #include <shellapi.h>
10 #elif defined(OS_POSIX)
11 #include <limits.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #endif
15 #if defined(OS_LINUX)
16 #include <sys/prctl.h>
17 #endif
18
19 #include <algorithm>
20
21 #include "base/file_path.h"
22 #include "base/logging.h"
23 #include "base/singleton.h"
24 #include "base/string_piece.h"
25 #include "base/string_util.h"
26 #include "base/sys_string_conversions.h"
27
28 #if defined(OS_LINUX)
29 // Linux/glibc doesn't natively have setproctitle().
30 #include "base/setproctitle_linux.h"
31 #endif
32
33 CommandLine* CommandLine::current_process_commandline_ = NULL;
34
35 // Since we use a lazy match, make sure that longer versions (like L"--")
36 // are listed before shorter versions (like L"-") of similar prefixes.
37 #if defined(OS_WIN)
38 const wchar_t* const kSwitchPrefixes[] = {L"--", L"-", L"/"};
39 const wchar_t kSwitchTerminator[] = L"--";
40 const wchar_t kSwitchValueSeparator[] = L"=";
41 #elif defined(OS_POSIX)
42 // Unixes don't use slash as a switch.
43 const char* const kSwitchPrefixes[] = {"--", "-"};
44 const char kSwitchTerminator[] = "--";
45 const char kSwitchValueSeparator[] = "=";
46 #endif
47
48 #if defined(OS_WIN)
49 // Lowercase a string. This is used to lowercase switch names.
50 // Is this what we really want? It seems crazy to me. I've left it in
51 // for backwards compatibility on Windows.
Lowercase(std::string * parameter)52 static void Lowercase(std::string* parameter) {
53 transform(parameter->begin(), parameter->end(), parameter->begin(),
54 tolower);
55 }
56 #endif
57
58 #if defined(OS_WIN)
CommandLine(ArgumentsOnly args_only)59 CommandLine::CommandLine(ArgumentsOnly args_only) {
60 }
61
ParseFromString(const std::wstring & command_line)62 void CommandLine::ParseFromString(const std::wstring& command_line) {
63 TrimWhitespace(command_line, TRIM_ALL, &command_line_string_);
64
65 if (command_line_string_.empty())
66 return;
67
68 int num_args = 0;
69 wchar_t** args = NULL;
70
71 args = CommandLineToArgvW(command_line_string_.c_str(), &num_args);
72
73 // Populate program_ with the trimmed version of the first arg.
74 TrimWhitespace(args[0], TRIM_ALL, &program_);
75
76 bool parse_switches = true;
77 for (int i = 1; i < num_args; ++i) {
78 std::wstring arg;
79 TrimWhitespace(args[i], TRIM_ALL, &arg);
80
81 if (!parse_switches) {
82 loose_values_.push_back(arg);
83 continue;
84 }
85
86 if (arg == kSwitchTerminator) {
87 parse_switches = false;
88 continue;
89 }
90
91 std::string switch_string;
92 std::wstring switch_value;
93 if (IsSwitch(arg, &switch_string, &switch_value)) {
94 switches_[switch_string] = switch_value;
95 } else {
96 loose_values_.push_back(arg);
97 }
98 }
99
100 if (args)
101 LocalFree(args);
102 }
103
CommandLine(const FilePath & program)104 CommandLine::CommandLine(const FilePath& program) {
105 if (!program.empty()) {
106 program_ = program.value();
107 command_line_string_ = L'"' + program.value() + L'"';
108 }
109 }
110
111 #elif defined(OS_POSIX)
CommandLine(ArgumentsOnly args_only)112 CommandLine::CommandLine(ArgumentsOnly args_only) {
113 // Push an empty argument, because we always assume argv_[0] is a program.
114 argv_.push_back("");
115 }
116
InitFromArgv(int argc,const char * const * argv)117 void CommandLine::InitFromArgv(int argc, const char* const* argv) {
118 for (int i = 0; i < argc; ++i)
119 argv_.push_back(argv[i]);
120 InitFromArgv(argv_);
121 }
122
InitFromArgv(const std::vector<std::string> & argv)123 void CommandLine::InitFromArgv(const std::vector<std::string>& argv) {
124 argv_ = argv;
125 bool parse_switches = true;
126 for (size_t i = 1; i < argv_.size(); ++i) {
127 const std::string& arg = argv_[i];
128
129 if (!parse_switches) {
130 loose_values_.push_back(arg);
131 continue;
132 }
133
134 if (arg == kSwitchTerminator) {
135 parse_switches = false;
136 continue;
137 }
138
139 std::string switch_string;
140 std::string switch_value;
141 if (IsSwitch(arg, &switch_string, &switch_value)) {
142 switches_[switch_string] = switch_value;
143 } else {
144 loose_values_.push_back(arg);
145 }
146 }
147 }
148
CommandLine(const FilePath & program)149 CommandLine::CommandLine(const FilePath& program) {
150 argv_.push_back(program.value());
151 }
152
153 #endif
154
155 // static
IsSwitch(const StringType & parameter_string,std::string * switch_string,StringType * switch_value)156 bool CommandLine::IsSwitch(const StringType& parameter_string,
157 std::string* switch_string,
158 StringType* switch_value) {
159 switch_string->clear();
160 switch_value->clear();
161
162 for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) {
163 StringType prefix(kSwitchPrefixes[i]);
164 if (parameter_string.find(prefix) != 0)
165 continue;
166
167 const size_t switch_start = prefix.length();
168 const size_t equals_position = parameter_string.find(
169 kSwitchValueSeparator, switch_start);
170 StringType switch_native;
171 if (equals_position == StringType::npos) {
172 switch_native = parameter_string.substr(switch_start);
173 } else {
174 switch_native = parameter_string.substr(
175 switch_start, equals_position - switch_start);
176 *switch_value = parameter_string.substr(equals_position + 1);
177 }
178 #if defined(OS_WIN)
179 *switch_string = WideToASCII(switch_native);
180 Lowercase(switch_string);
181 #else
182 *switch_string = switch_native;
183 #endif
184
185 return true;
186 }
187
188 return false;
189 }
190
191 // static
Init(int argc,const char * const * argv)192 void CommandLine::Init(int argc, const char* const* argv) {
193 delete current_process_commandline_;
194 current_process_commandline_ = new CommandLine;
195 #if defined(OS_WIN)
196 current_process_commandline_->ParseFromString(::GetCommandLineW());
197 #elif defined(OS_POSIX)
198 current_process_commandline_->InitFromArgv(argc, argv);
199 #endif
200
201 #if defined(OS_LINUX)
202 if (argv)
203 setproctitle_init(const_cast<char**>(argv));
204 #endif
205 }
206
207 #if defined(OS_POSIX) && !defined(OS_MACOSX)
208 // static
SetProcTitle()209 void CommandLine::SetProcTitle() {
210 // Build a single string which consists of all the arguments separated
211 // by spaces. We can't actually keep them separate due to the way the
212 // setproctitle() function works.
213 std::string title;
214 bool have_argv0 = false;
215 #if defined(OS_LINUX)
216 // In Linux we sometimes exec ourselves from /proc/self/exe, but this makes us
217 // show up as "exe" in process listings. Read the symlink /proc/self/exe and
218 // use the path it points at for our process title. Note that this is only for
219 // display purposes and has no TOCTTOU security implications.
220 char buffer[PATH_MAX];
221 // Note: readlink() does not append a null byte to terminate the string.
222 ssize_t length = readlink("/proc/self/exe", buffer, sizeof(buffer));
223 DCHECK(length <= static_cast<ssize_t>(sizeof(buffer)));
224 if (length > 0) {
225 have_argv0 = true;
226 title.assign(buffer, length);
227 // If the binary has since been deleted, Linux appends " (deleted)" to the
228 // symlink target. Remove it, since this is not really part of our name.
229 const std::string kDeletedSuffix = " (deleted)";
230 if (EndsWith(title, kDeletedSuffix, true))
231 title.resize(title.size() - kDeletedSuffix.size());
232 #if defined(PR_SET_NAME)
233 // If PR_SET_NAME is available at compile time, we try using it. We ignore
234 // any errors if the kernel does not support it at runtime though. When
235 // available, this lets us set the short process name that shows when the
236 // full command line is not being displayed in most process listings.
237 prctl(PR_SET_NAME, FilePath(title).BaseName().value().c_str());
238 #endif
239 }
240 #endif
241 for (size_t i = 1; i < current_process_commandline_->argv_.size(); ++i) {
242 if (!title.empty())
243 title += " ";
244 title += current_process_commandline_->argv_[i];
245 }
246 // Disable prepending argv[0] with '-' if we prepended it ourselves above.
247 setproctitle(have_argv0 ? "-%s" : "%s", title.c_str());
248 }
249 #endif
250
Reset()251 void CommandLine::Reset() {
252 DCHECK(current_process_commandline_ != NULL);
253 delete current_process_commandline_;
254 current_process_commandline_ = NULL;
255 }
256
HasSwitch(const std::string & switch_string) const257 bool CommandLine::HasSwitch(const std::string& switch_string) const {
258 std::string lowercased_switch(switch_string);
259 #if defined(OS_WIN)
260 Lowercase(&lowercased_switch);
261 #endif
262 return switches_.find(lowercased_switch) != switches_.end();
263 }
264
GetSwitchValue(const std::string & switch_string) const265 std::wstring CommandLine::GetSwitchValue(
266 const std::string& switch_string) const {
267 std::string lowercased_switch(switch_string);
268 #if defined(OS_WIN)
269 Lowercase(&lowercased_switch);
270 #endif
271
272 std::map<std::string, StringType>::const_iterator result =
273 switches_.find(lowercased_switch);
274
275 if (result == switches_.end()) {
276 return L"";
277 } else {
278 #if defined(OS_WIN)
279 return result->second;
280 #else
281 return base::SysNativeMBToWide(result->second);
282 #endif
283 }
284 }
285
286 #if defined(OS_WIN)
GetLooseValues() const287 std::vector<std::wstring> CommandLine::GetLooseValues() const {
288 return loose_values_;
289 }
program() const290 std::wstring CommandLine::program() const {
291 return program_;
292 }
293 #else
GetLooseValues() const294 std::vector<std::wstring> CommandLine::GetLooseValues() const {
295 std::vector<std::wstring> values;
296 for (size_t i = 0; i < loose_values_.size(); ++i)
297 values.push_back(base::SysNativeMBToWide(loose_values_[i]));
298 return values;
299 }
program() const300 std::wstring CommandLine::program() const {
301 DCHECK_GT(argv_.size(), 0U);
302 return base::SysNativeMBToWide(argv_[0]);
303 }
304 #endif
305
306
307 // static
PrefixedSwitchString(const std::string & switch_string)308 std::wstring CommandLine::PrefixedSwitchString(
309 const std::string& switch_string) {
310 #if defined(OS_WIN)
311 return kSwitchPrefixes[0] + ASCIIToWide(switch_string);
312 #else
313 return ASCIIToWide(kSwitchPrefixes[0] + switch_string);
314 #endif
315 }
316
317 // static
PrefixedSwitchStringWithValue(const std::string & switch_string,const std::wstring & value_string)318 std::wstring CommandLine::PrefixedSwitchStringWithValue(
319 const std::string& switch_string, const std::wstring& value_string) {
320 if (value_string.empty()) {
321 return PrefixedSwitchString(switch_string);
322 }
323
324 return PrefixedSwitchString(switch_string +
325 #if defined(OS_WIN)
326 WideToASCII(kSwitchValueSeparator)
327 #else
328 kSwitchValueSeparator
329 #endif
330 ) + value_string;
331 }
332
333 #if defined(OS_WIN)
AppendSwitch(const std::string & switch_string)334 void CommandLine::AppendSwitch(const std::string& switch_string) {
335 std::wstring prefixed_switch_string = PrefixedSwitchString(switch_string);
336 command_line_string_.append(L" ");
337 command_line_string_.append(prefixed_switch_string);
338 switches_[switch_string] = L"";
339 }
340
AppendSwitchWithValue(const std::string & switch_string,const std::wstring & value_string)341 void CommandLine::AppendSwitchWithValue(const std::string& switch_string,
342 const std::wstring& value_string) {
343 std::wstring value_string_edit;
344
345 // NOTE(jhughes): If the value contains a quotation mark at one
346 // end but not both, you may get unusable output.
347 if (!value_string.empty() &&
348 (value_string.find(L" ") != std::wstring::npos) &&
349 (value_string[0] != L'"') &&
350 (value_string[value_string.length() - 1] != L'"')) {
351 // need to provide quotes
352 value_string_edit = StringPrintf(L"\"%ls\"", value_string.c_str());
353 } else {
354 value_string_edit = value_string;
355 }
356
357 std::wstring combined_switch_string =
358 PrefixedSwitchStringWithValue(switch_string, value_string_edit);
359
360 command_line_string_.append(L" ");
361 command_line_string_.append(combined_switch_string);
362
363 switches_[switch_string] = value_string;
364 }
365
AppendLooseValue(const std::wstring & value)366 void CommandLine::AppendLooseValue(const std::wstring& value) {
367 // TODO(evan): quoting?
368 command_line_string_.append(L" ");
369 command_line_string_.append(value);
370 loose_values_.push_back(value);
371 }
372
AppendArguments(const CommandLine & other,bool include_program)373 void CommandLine::AppendArguments(const CommandLine& other,
374 bool include_program) {
375 // Verify include_program is used correctly.
376 // Logic could be shorter but this is clearer.
377 DCHECK(include_program ? !other.program().empty() : other.program().empty());
378 command_line_string_ += L" " + other.command_line_string_;
379 std::map<std::string, StringType>::const_iterator i;
380 for (i = other.switches_.begin(); i != other.switches_.end(); ++i)
381 switches_[i->first] = i->second;
382 }
383
PrependWrapper(const std::wstring & wrapper)384 void CommandLine::PrependWrapper(const std::wstring& wrapper) {
385 // The wrapper may have embedded arguments (like "gdb --args"). In this case,
386 // we don't pretend to do anything fancy, we just split on spaces.
387 std::vector<std::wstring> wrapper_and_args;
388 SplitString(wrapper, ' ', &wrapper_and_args);
389 program_ = wrapper_and_args[0];
390 command_line_string_ = wrapper + L" " + command_line_string_;
391 }
392
393 #elif defined(OS_POSIX)
AppendSwitch(const std::string & switch_string)394 void CommandLine::AppendSwitch(const std::string& switch_string) {
395 argv_.push_back(kSwitchPrefixes[0] + switch_string);
396 switches_[switch_string] = "";
397 }
398
AppendSwitchWithValue(const std::string & switch_string,const std::wstring & value_string)399 void CommandLine::AppendSwitchWithValue(const std::string& switch_string,
400 const std::wstring& value_string) {
401 std::string mb_value = base::SysWideToNativeMB(value_string);
402
403 argv_.push_back(kSwitchPrefixes[0] + switch_string +
404 kSwitchValueSeparator + mb_value);
405 switches_[switch_string] = mb_value;
406 }
407
AppendLooseValue(const std::wstring & value)408 void CommandLine::AppendLooseValue(const std::wstring& value) {
409 argv_.push_back(base::SysWideToNativeMB(value));
410 }
411
AppendArguments(const CommandLine & other,bool include_program)412 void CommandLine::AppendArguments(const CommandLine& other,
413 bool include_program) {
414 // Verify include_program is used correctly.
415 // Logic could be shorter but this is clearer.
416 DCHECK(include_program ? !other.program().empty() : other.program().empty());
417
418 size_t first_arg = include_program ? 0 : 1;
419 for (size_t i = first_arg; i < other.argv_.size(); ++i)
420 argv_.push_back(other.argv_[i]);
421 std::map<std::string, StringType>::const_iterator i;
422 for (i = other.switches_.begin(); i != other.switches_.end(); ++i)
423 switches_[i->first] = i->second;
424 }
425
PrependWrapper(const std::wstring & wrapper_wide)426 void CommandLine::PrependWrapper(const std::wstring& wrapper_wide) {
427 // The wrapper may have embedded arguments (like "gdb --args"). In this case,
428 // we don't pretend to do anything fancy, we just split on spaces.
429 const std::string wrapper = base::SysWideToNativeMB(wrapper_wide);
430 std::vector<std::string> wrapper_and_args;
431 SplitString(wrapper, ' ', &wrapper_and_args);
432 argv_.insert(argv_.begin(), wrapper_and_args.begin(), wrapper_and_args.end());
433 }
434
435 #endif
436