[M94 Dev][Tizen] Fix for errors for generating ninja files
[platform/framework/web/chromium-efl.git] / base / command_line.cc
1 // Copyright (c) 2012 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 #include <ostream>
8
9 #include "base/containers/contains.h"
10 #include "base/containers/span.h"
11 #include "base/files/file_path.h"
12 #include "base/logging.h"
13 #include "base/notreached.h"
14 #include "base/ranges/algorithm.h"
15 #include "base/stl_util.h"
16 #include "base/strings/strcat.h"
17 #include "base/strings/string_piece.h"
18 #include "base/strings/string_split.h"
19 #include "base/strings/string_tokenizer.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "build/build_config.h"
23
24 #if defined(OS_WIN)
25 #include <windows.h>
26 #include <shellapi.h>
27
28 #include "base/strings/string_util_win.h"
29 #endif  // defined(OS_WIN)
30
31 namespace base {
32
33 CommandLine* CommandLine::current_process_commandline_ = nullptr;
34
35 namespace {
36
37 constexpr CommandLine::CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--");
38 constexpr CommandLine::CharType kSwitchValueSeparator[] =
39     FILE_PATH_LITERAL("=");
40
41 // Since we use a lazy match, make sure that longer versions (like "--") are
42 // listed before shorter versions (like "-") of similar prefixes.
43 #if defined(OS_WIN)
44 // By putting slash last, we can control whether it is treaded as a switch
45 // value by changing the value of switch_prefix_count to be one less than
46 // the array size.
47 constexpr CommandLine::StringPieceType kSwitchPrefixes[] = {L"--", L"-", L"/"};
48 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
49 // Unixes don't use slash as a switch.
50 constexpr CommandLine::StringPieceType kSwitchPrefixes[] = {"--", "-"};
51 #endif
52 size_t switch_prefix_count = base::size(kSwitchPrefixes);
53
54 #if defined(OS_WIN)
55 // Switch string that specifies the single argument to the command line.
56 // If present, everything after this switch is interpreted as a single
57 // argument regardless of whitespace, quotes, etc. Used for launches from the
58 // Windows shell, which may have arguments with unencoded quotes that could
59 // otherwise unexpectedly be split into multiple arguments
60 // (https://crbug.com/937179).
61 constexpr CommandLine::CharType kSingleArgument[] =
62     FILE_PATH_LITERAL("single-argument");
63 #endif  // defined(OS_WIN)
64
65 size_t GetSwitchPrefixLength(CommandLine::StringPieceType string) {
66   for (size_t i = 0; i < switch_prefix_count; ++i) {
67     CommandLine::StringType prefix(kSwitchPrefixes[i]);
68     if (string.substr(0, prefix.length()) == prefix)
69       return prefix.length();
70   }
71   return 0;
72 }
73
74 // Fills in |switch_string| and |switch_value| if |string| is a switch.
75 // This will preserve the input switch prefix in the output |switch_string|.
76 bool IsSwitch(const CommandLine::StringType& string,
77               CommandLine::StringType* switch_string,
78               CommandLine::StringType* switch_value) {
79   switch_string->clear();
80   switch_value->clear();
81   size_t prefix_length = GetSwitchPrefixLength(string);
82   if (prefix_length == 0 || prefix_length == string.length())
83     return false;
84
85   const size_t equals_position = string.find(kSwitchValueSeparator);
86   *switch_string = string.substr(0, equals_position);
87   if (equals_position != CommandLine::StringType::npos)
88     *switch_value = string.substr(equals_position + 1);
89   return true;
90 }
91
92 // Returns true iff |string| represents a switch with key
93 // |switch_key_without_prefix|, regardless of value.
94 bool IsSwitchWithKey(CommandLine::StringPieceType string,
95                      CommandLine::StringPieceType switch_key_without_prefix) {
96   size_t prefix_length = GetSwitchPrefixLength(string);
97   if (prefix_length == 0 || prefix_length == string.length())
98     return false;
99
100   const size_t equals_position = string.find(kSwitchValueSeparator);
101   return string.substr(prefix_length, equals_position - prefix_length) ==
102          switch_key_without_prefix;
103 }
104
105 #if defined(OS_WIN)
106 // Quote a string as necessary for CommandLineToArgvW compatibility *on
107 // Windows*.
108 std::wstring QuoteForCommandLineToArgvW(const std::wstring& arg,
109                                         bool allow_unsafe_insert_sequences) {
110   // Ensure that GetCommandLineString isn't used to generate command-line
111   // strings for the Windows shell by checking for Windows insert sequences like
112   // "%1". GetCommandLineStringForShell should be used instead to get a string
113   // with the correct placeholder format for the shell.
114   DCHECK(arg.size() != 2 || arg[0] != L'%' || allow_unsafe_insert_sequences);
115
116   // We follow the quoting rules of CommandLineToArgvW.
117   // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
118   std::wstring quotable_chars(L" \\\"");
119   if (arg.find_first_of(quotable_chars) == std::wstring::npos) {
120     // No quoting necessary.
121     return arg;
122   }
123
124   std::wstring out;
125   out.push_back('"');
126   for (size_t i = 0; i < arg.size(); ++i) {
127     if (arg[i] == '\\') {
128       // Find the extent of this run of backslashes.
129       size_t start = i, end = start + 1;
130       for (; end < arg.size() && arg[end] == '\\'; ++end) {}
131       size_t backslash_count = end - start;
132
133       // Backslashes are escapes only if the run is followed by a double quote.
134       // Since we also will end the string with a double quote, we escape for
135       // either a double quote or the end of the string.
136       if (end == arg.size() || arg[end] == '"') {
137         // To quote, we need to output 2x as many backslashes.
138         backslash_count *= 2;
139       }
140       for (size_t j = 0; j < backslash_count; ++j)
141         out.push_back('\\');
142
143       // Advance i to one before the end to balance i++ in loop.
144       i = end - 1;
145     } else if (arg[i] == '"') {
146       out.push_back('\\');
147       out.push_back('"');
148     } else {
149       out.push_back(arg[i]);
150     }
151   }
152   out.push_back('"');
153
154   return out;
155 }
156 #endif  // defined(OS_WIN)
157
158 }  // namespace
159
160 CommandLine::CommandLine(NoProgram no_program)
161     : argv_(1),
162       begin_args_(1) {
163 }
164
165 CommandLine::CommandLine(const FilePath& program)
166     : argv_(1),
167       begin_args_(1) {
168   SetProgram(program);
169 }
170
171 CommandLine::CommandLine(int argc, const CommandLine::CharType* const* argv)
172     : argv_(1),
173       begin_args_(1) {
174   InitFromArgv(argc, argv);
175 }
176
177 CommandLine::CommandLine(const StringVector& argv)
178     : argv_(1),
179       begin_args_(1) {
180   InitFromArgv(argv);
181 }
182
183 CommandLine::CommandLine(const CommandLine& other) = default;
184
185 CommandLine& CommandLine::operator=(const CommandLine& other) = default;
186
187 CommandLine::~CommandLine() = default;
188
189 #if defined(OS_WIN)
190 // static
191 void CommandLine::set_slash_is_not_a_switch() {
192   // The last switch prefix should be slash, so adjust the size to skip it.
193   static_assert(base::make_span(kSwitchPrefixes).back() == L"/",
194                 "Error: Last switch prefix is not a slash.");
195   switch_prefix_count = base::size(kSwitchPrefixes) - 1;
196 }
197
198 // static
199 void CommandLine::InitUsingArgvForTesting(int argc, const char* const* argv) {
200   DCHECK(!current_process_commandline_);
201   current_process_commandline_ = new CommandLine(NO_PROGRAM);
202   // On Windows we need to convert the command line arguments to std::wstring.
203   CommandLine::StringVector argv_vector;
204   for (int i = 0; i < argc; ++i)
205     argv_vector.push_back(UTF8ToWide(argv[i]));
206   current_process_commandline_->InitFromArgv(argv_vector);
207 }
208 #endif  // defined(OS_WIN)
209
210 // static
211 bool CommandLine::Init(int argc, const char* const* argv) {
212   if (current_process_commandline_) {
213     // If this is intentional, Reset() must be called first. If we are using
214     // the shared build mode, we have to share a single object across multiple
215     // shared libraries.
216     return false;
217   }
218
219   current_process_commandline_ = new CommandLine(NO_PROGRAM);
220 #if defined(OS_WIN)
221   current_process_commandline_->ParseFromString(::GetCommandLineW());
222 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
223   current_process_commandline_->InitFromArgv(argc, argv);
224 #else
225 #error Unsupported platform
226 #endif
227
228   return true;
229 }
230
231 // static
232 void CommandLine::Reset() {
233   DCHECK(current_process_commandline_);
234   delete current_process_commandline_;
235   current_process_commandline_ = nullptr;
236 }
237
238 // static
239 CommandLine* CommandLine::ForCurrentProcess() {
240   DCHECK(current_process_commandline_);
241   return current_process_commandline_;
242 }
243
244 // static
245 bool CommandLine::InitializedForCurrentProcess() {
246   return !!current_process_commandline_;
247 }
248
249 #if defined(OS_WIN)
250 // static
251 CommandLine CommandLine::FromString(StringPieceType command_line) {
252   CommandLine cmd(NO_PROGRAM);
253   cmd.ParseFromString(command_line);
254   return cmd;
255 }
256 #endif  // defined(OS_WIN)
257
258 void CommandLine::InitFromArgv(int argc,
259                                const CommandLine::CharType* const* argv) {
260   StringVector new_argv;
261   for (int i = 0; i < argc; ++i)
262     new_argv.push_back(argv[i]);
263   InitFromArgv(new_argv);
264 }
265
266 void CommandLine::InitFromArgv(const StringVector& argv) {
267   argv_ = StringVector(1);
268   switches_.clear();
269   begin_args_ = 1;
270   SetProgram(argv.empty() ? FilePath() : FilePath(argv[0]));
271   AppendSwitchesAndArguments(argv);
272 }
273
274 FilePath CommandLine::GetProgram() const {
275   return FilePath(argv_[0]);
276 }
277
278 void CommandLine::SetProgram(const FilePath& program) {
279 #if defined(OS_WIN)
280   argv_[0] = StringType(TrimWhitespace(program.value(), TRIM_ALL));
281 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
282   TrimWhitespaceASCII(program.value(), TRIM_ALL, &argv_[0]);
283 #else
284 #error Unsupported platform
285 #endif
286 }
287
288 bool CommandLine::HasSwitch(StringPiece switch_string) const {
289   DCHECK_EQ(ToLowerASCII(switch_string), switch_string);
290   return Contains(switches_, switch_string);
291 }
292
293 bool CommandLine::HasSwitch(const char switch_constant[]) const {
294   return HasSwitch(StringPiece(switch_constant));
295 }
296
297 std::string CommandLine::GetSwitchValueASCII(StringPiece switch_string) const {
298   StringType value = GetSwitchValueNative(switch_string);
299 #if defined(OS_WIN)
300   if (!IsStringASCII(base::AsStringPiece16(value))) {
301 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
302   if (!IsStringASCII(value)) {
303 #endif
304     DLOG(WARNING) << "Value of switch (" << switch_string << ") must be ASCII.";
305     return std::string();
306   }
307 #if defined(OS_WIN)
308   return WideToUTF8(value);
309 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
310   return value;
311 #endif
312 }
313
314 FilePath CommandLine::GetSwitchValuePath(StringPiece switch_string) const {
315   return FilePath(GetSwitchValueNative(switch_string));
316 }
317
318 CommandLine::StringType CommandLine::GetSwitchValueNative(
319     StringPiece switch_string) const {
320   DCHECK_EQ(ToLowerASCII(switch_string), switch_string);
321   auto result = switches_.find(switch_string);
322   return result == switches_.end() ? StringType() : result->second;
323 }
324
325 void CommandLine::AppendSwitch(StringPiece switch_string) {
326   AppendSwitchNative(switch_string, StringType());
327 }
328
329 void CommandLine::AppendSwitchPath(StringPiece switch_string,
330                                    const FilePath& path) {
331   AppendSwitchNative(switch_string, path.value());
332 }
333
334 void CommandLine::AppendSwitchNative(StringPiece switch_string,
335                                      CommandLine::StringPieceType value) {
336 #if defined(OS_WIN)
337   const std::string switch_key = ToLowerASCII(switch_string);
338   StringType combined_switch_string(UTF8ToWide(switch_key));
339 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
340   StringPiece switch_key = switch_string;
341   StringType combined_switch_string(switch_key);
342 #endif
343   size_t prefix_length = GetSwitchPrefixLength(combined_switch_string);
344   base::InsertOrAssign(switches_, std::string(switch_key.substr(prefix_length)),
345                        StringType(value));
346   // Preserve existing switch prefixes in |argv_|; only append one if necessary.
347   if (prefix_length == 0) {
348     combined_switch_string.insert(0, kSwitchPrefixes[0].data(),
349                                   kSwitchPrefixes[0].size());
350   }
351   if (!value.empty())
352     base::StrAppend(&combined_switch_string, {kSwitchValueSeparator, value});
353   // Append the switch and update the switches/arguments divider |begin_args_|.
354   argv_.insert(argv_.begin() + begin_args_++, combined_switch_string);
355 }
356
357 void CommandLine::AppendSwitchASCII(StringPiece switch_string,
358                                     StringPiece value_string) {
359 #if defined(OS_WIN)
360   AppendSwitchNative(switch_string, UTF8ToWide(value_string));
361 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
362   AppendSwitchNative(switch_string, value_string);
363 #else
364 #error Unsupported platform
365 #endif
366 }
367
368 void CommandLine::RemoveSwitch(base::StringPiece switch_key_without_prefix) {
369 #if defined(OS_WIN)
370   StringType switch_key_native = UTF8ToWide(switch_key_without_prefix);
371 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
372   StringType switch_key_native(switch_key_without_prefix);
373 #endif
374
375   DCHECK_EQ(ToLowerASCII(switch_key_without_prefix), switch_key_without_prefix);
376   DCHECK_EQ(0u, GetSwitchPrefixLength(switch_key_native));
377   auto it = switches_.find(switch_key_without_prefix);
378   if (it == switches_.end())
379     return;
380   switches_.erase(it);
381   // Also erase from the switches section of |argv_| and update |begin_args_|
382   // accordingly.
383   // Switches in |argv_| have indices [1, begin_args_).
384   auto argv_switches_begin = argv_.begin() + 1;
385   auto argv_switches_end = argv_.begin() + begin_args_;
386   DCHECK(argv_switches_begin <= argv_switches_end);
387   DCHECK(argv_switches_end <= argv_.end());
388   auto expell = std::remove_if(argv_switches_begin, argv_switches_end,
389                                [&switch_key_native](const StringType& arg) {
390                                  return IsSwitchWithKey(arg, switch_key_native);
391                                });
392   if (expell == argv_switches_end) {
393     NOTREACHED();
394     return;
395   }
396   begin_args_ -= argv_switches_end - expell;
397   argv_.erase(expell, argv_switches_end);
398 }
399
400 void CommandLine::CopySwitchesFrom(const CommandLine& source,
401                                    const char* const switches[],
402                                    size_t count) {
403   for (size_t i = 0; i < count; ++i) {
404     if (source.HasSwitch(switches[i]))
405       AppendSwitchNative(switches[i], source.GetSwitchValueNative(switches[i]));
406   }
407 }
408
409 CommandLine::StringVector CommandLine::GetArgs() const {
410   // Gather all arguments after the last switch (may include kSwitchTerminator).
411   StringVector args(argv_.begin() + begin_args_, argv_.end());
412   // Erase only the first kSwitchTerminator (maybe "--" is a legitimate page?)
413   auto switch_terminator = ranges::find(args, kSwitchTerminator);
414   if (switch_terminator != args.end())
415     args.erase(switch_terminator);
416   return args;
417 }
418
419 void CommandLine::AppendArg(StringPiece value) {
420 #if defined(OS_WIN)
421   DCHECK(IsStringUTF8(value));
422   AppendArgNative(UTF8ToWide(value));
423 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
424   AppendArgNative(value);
425 #else
426 #error Unsupported platform
427 #endif
428 }
429
430 void CommandLine::AppendArgPath(const FilePath& path) {
431   AppendArgNative(path.value());
432 }
433
434 void CommandLine::AppendArgNative(StringPieceType value) {
435   argv_.push_back(StringType(value));
436 }
437
438 void CommandLine::AppendArguments(const CommandLine& other,
439                                   bool include_program) {
440   if (include_program)
441     SetProgram(other.GetProgram());
442   AppendSwitchesAndArguments(other.argv());
443 }
444
445 void CommandLine::PrependWrapper(StringPieceType wrapper) {
446   if (wrapper.empty())
447     return;
448   // Split the wrapper command based on whitespace (with quoting).
449   // StringPieceType does not currently work directly with StringTokenizerT.
450   using CommandLineTokenizer =
451       StringTokenizerT<StringType, StringType::const_iterator>;
452   StringType wrapper_string(wrapper);
453   CommandLineTokenizer tokenizer(wrapper_string, FILE_PATH_LITERAL(" "));
454   tokenizer.set_quote_chars(FILE_PATH_LITERAL("'\""));
455   std::vector<StringType> wrapper_argv;
456   while (tokenizer.GetNext())
457     wrapper_argv.emplace_back(tokenizer.token());
458
459   // Prepend the wrapper and update the switches/arguments |begin_args_|.
460   argv_.insert(argv_.begin(), wrapper_argv.begin(), wrapper_argv.end());
461   begin_args_ += wrapper_argv.size();
462 }
463
464 #if defined(OS_WIN)
465 void CommandLine::ParseFromString(StringPieceType command_line) {
466   command_line = TrimWhitespace(command_line, TRIM_ALL);
467   if (command_line.empty())
468     return;
469   raw_command_line_string_ = command_line;
470
471   int num_args = 0;
472   wchar_t** args = NULL;
473   // When calling CommandLineToArgvW, use the apiset if available.
474   // Doing so will bypass loading shell32.dll on Win8+.
475   HMODULE downlevel_shell32_dll =
476       ::LoadLibraryEx(L"api-ms-win-downlevel-shell32-l1-1-0.dll", nullptr,
477                       LOAD_LIBRARY_SEARCH_SYSTEM32);
478   if (downlevel_shell32_dll) {
479     auto command_line_to_argv_w_proc =
480         reinterpret_cast<decltype(::CommandLineToArgvW)*>(
481             ::GetProcAddress(downlevel_shell32_dll, "CommandLineToArgvW"));
482     if (command_line_to_argv_w_proc)
483       args = command_line_to_argv_w_proc(command_line.data(), &num_args);
484   } else {
485     // Since the apiset is not available, allow the delayload of shell32.dll
486     // to take place.
487     args = ::CommandLineToArgvW(command_line.data(), &num_args);
488   }
489
490   DPLOG_IF(FATAL, !args) << "CommandLineToArgvW failed on command line: "
491                          << command_line;
492   StringVector argv(args, args + num_args);
493   InitFromArgv(argv);
494   raw_command_line_string_ = StringPieceType();
495   LocalFree(args);
496
497   if (downlevel_shell32_dll)
498     ::FreeLibrary(downlevel_shell32_dll);
499 }
500 #endif  // defined(OS_WIN)
501
502 void CommandLine::AppendSwitchesAndArguments(
503     const CommandLine::StringVector& argv) {
504   bool parse_switches = true;
505 #if defined(OS_WIN)
506   const bool is_parsed_from_string = !raw_command_line_string_.empty();
507 #endif
508   for (size_t i = 1; i < argv.size(); ++i) {
509     CommandLine::StringType arg = argv[i];
510 #if defined(OS_WIN)
511     arg = CommandLine::StringType(TrimWhitespace(arg, TRIM_ALL));
512 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
513     TrimWhitespaceASCII(arg, TRIM_ALL, &arg);
514 #endif
515
516     CommandLine::StringType switch_string;
517     CommandLine::StringType switch_value;
518     parse_switches &= (arg != kSwitchTerminator);
519     if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) {
520 #if defined(OS_WIN)
521       if (is_parsed_from_string &&
522           IsSwitchWithKey(switch_string, kSingleArgument)) {
523         ParseAsSingleArgument(switch_string);
524         return;
525       }
526       AppendSwitchNative(WideToUTF8(switch_string), switch_value);
527 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
528       AppendSwitchNative(switch_string, switch_value);
529 #else
530 #error Unsupported platform
531 #endif
532     } else {
533       AppendArgNative(arg);
534     }
535   }
536 }
537
538 CommandLine::StringType CommandLine::GetArgumentsStringInternal(
539     bool allow_unsafe_insert_sequences) const {
540   StringType params;
541   // Append switches and arguments.
542   bool parse_switches = true;
543   for (size_t i = 1; i < argv_.size(); ++i) {
544     StringType arg = argv_[i];
545     StringType switch_string;
546     StringType switch_value;
547     parse_switches &= arg != kSwitchTerminator;
548     if (i > 1)
549       params.append(FILE_PATH_LITERAL(" "));
550     if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) {
551       params.append(switch_string);
552       if (!switch_value.empty()) {
553 #if defined(OS_WIN)
554         switch_value = QuoteForCommandLineToArgvW(
555             switch_value, allow_unsafe_insert_sequences);
556 #endif
557         params.append(kSwitchValueSeparator + switch_value);
558       }
559     } else {
560 #if defined(OS_WIN)
561       arg = QuoteForCommandLineToArgvW(arg, allow_unsafe_insert_sequences);
562 #endif
563       params.append(arg);
564     }
565   }
566   return params;
567 }
568
569 CommandLine::StringType CommandLine::GetCommandLineString() const {
570   StringType string(argv_[0]);
571 #if defined(OS_WIN)
572   string = QuoteForCommandLineToArgvW(string,
573                                       /*allow_unsafe_insert_sequences=*/false);
574 #endif
575   StringType params(GetArgumentsString());
576   if (!params.empty()) {
577     string.append(FILE_PATH_LITERAL(" "));
578     string.append(params);
579   }
580   return string;
581 }
582
583 #if defined(OS_WIN)
584 // NOTE: this function is used to set Chrome's open command in the registry
585 // during update. Any change to the syntax must be compatible with the prior
586 // version (i.e., any new syntax must be understood by older browsers expecting
587 // the old syntax, and the new browser must still handle the old syntax), as
588 // old versions are likely to persist, e.g., immediately after background
589 // update, when parsing command lines for other channels, when uninstalling web
590 // applications installed using the old syntax, etc.
591 CommandLine::StringType CommandLine::GetCommandLineStringForShell() const {
592   DCHECK(GetArgs().empty());
593   StringType command_line_string = GetCommandLineString();
594   return command_line_string + FILE_PATH_LITERAL(" ") +
595          StringType(kSwitchPrefixes[0]) + kSingleArgument +
596          FILE_PATH_LITERAL(" %1");
597 }
598
599 CommandLine::StringType
600 CommandLine::GetCommandLineStringWithUnsafeInsertSequences() const {
601   StringType string(argv_[0]);
602   string = QuoteForCommandLineToArgvW(string,
603                                       /*allow_unsafe_insert_sequences=*/true);
604   StringType params(
605       GetArgumentsStringInternal(/*allow_unsafe_insert_sequences=*/true));
606   if (!params.empty()) {
607     string.append(FILE_PATH_LITERAL(" "));
608     string.append(params);
609   }
610   return string;
611 }
612 #endif  // defined(OS_WIN)
613
614 CommandLine::StringType CommandLine::GetArgumentsString() const {
615   return GetArgumentsStringInternal(/*allow_unsafe_insert_sequences=*/false);
616 }
617
618 #if defined(OS_WIN)
619 void CommandLine::ParseAsSingleArgument(
620     const CommandLine::StringType& single_arg_switch) {
621   DCHECK(!raw_command_line_string_.empty());
622
623   // Remove any previously parsed arguments.
624   argv_.resize(begin_args_);
625
626   // Locate "--single-argument" in the process's raw command line. Results are
627   // unpredictable if "--single-argument" appears as part of a previous
628   // argument or switch.
629   const size_t single_arg_switch_position =
630       raw_command_line_string_.find(single_arg_switch);
631   DCHECK_NE(single_arg_switch_position, StringType::npos);
632
633   // Append the portion of the raw command line that starts one character past
634   // "--single-argument" as the one and only argument, or return if no
635   // argument is present.
636   const size_t arg_position =
637       single_arg_switch_position + single_arg_switch.length() + 1;
638   if (arg_position >= raw_command_line_string_.length())
639     return;
640   const StringPieceType arg = raw_command_line_string_.substr(arg_position);
641   if (!arg.empty()) {
642     AppendArgNative(arg);
643   }
644 }
645 #endif  // defined(OS_WIN)
646
647 }  // namespace base