command_line.cc revision 5e3f23d412006dc4db4e659864679f29341e113f
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/command_line.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <algorithm> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <ostream> 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/basictypes.h" 112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h" 132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_split.h" 145e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/string_util.h" 15868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h" 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "build/build_config.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN) 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <windows.h> 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <shellapi.h> 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using base::FilePath; 242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandLine* CommandLine::current_process_commandline_ = NULL; 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const CommandLine::CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--"); 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const CommandLine::CharType kSwitchValueSeparator[] = FILE_PATH_LITERAL("="); 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Since we use a lazy match, make sure that longer versions (like "--") are 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// listed before shorter versions (like "-") of similar prefixes. 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN) 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const CommandLine::CharType* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif defined(OS_POSIX) 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Unixes don't use slash as a switch. 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const CommandLine::CharType* const kSwitchPrefixes[] = {"--", "-"}; 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)size_t GetSwitchPrefixLength(const CommandLine::StringType& string) { 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) { 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CommandLine::StringType prefix(kSwitchPrefixes[i]); 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (string.compare(0, prefix.length(), prefix) == 0) 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return prefix.length(); 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 0; 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Fills in |switch_string| and |switch_value| if |string| is a switch. 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This will preserve the input switch prefix in the output |switch_string|. 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool IsSwitch(const CommandLine::StringType& string, 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CommandLine::StringType* switch_string, 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CommandLine::StringType* switch_value) { 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) switch_string->clear(); 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) switch_value->clear(); 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) size_t prefix_length = GetSwitchPrefixLength(string); 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (prefix_length == 0 || prefix_length == string.length()) 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const size_t equals_position = string.find(kSwitchValueSeparator); 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *switch_string = string.substr(0, equals_position); 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (equals_position != CommandLine::StringType::npos) 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *switch_value = string.substr(equals_position + 1); 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Append switches and arguments, keeping switches before arguments. 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void AppendSwitchesAndArguments(CommandLine& command_line, 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const CommandLine::StringVector& argv) { 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool parse_switches = true; 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (size_t i = 1; i < argv.size(); ++i) { 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CommandLine::StringType arg = argv[i]; 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) TrimWhitespace(arg, TRIM_ALL, &arg); 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CommandLine::StringType switch_string; 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CommandLine::StringType switch_value; 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parse_switches &= (arg != kSwitchTerminator); 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) { 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN) 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) command_line.AppendSwitchNative(WideToASCII(switch_string), switch_value); 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif defined(OS_POSIX) 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) command_line.AppendSwitchNative(switch_string, switch_value); 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) command_line.AppendArgNative(arg); 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Lowercase switches for backwards compatiblity *on Windows*. 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string LowerASCIIOnWindows(const std::string& string) { 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN) 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return StringToLowerASCII(string); 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif defined(OS_POSIX) 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return string; 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN) 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Quote a string as necessary for CommandLineToArgvW compatiblity *on Windows*. 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::wstring QuoteForCommandLineToArgvW(const std::wstring& arg) { 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We follow the quoting rules of CommandLineToArgvW. 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (arg.find_first_of(L" \\\"") == std::wstring::npos) { 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // No quoting necessary. 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return arg; 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::wstring out; 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) out.push_back(L'"'); 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (size_t i = 0; i < arg.size(); ++i) { 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (arg[i] == '\\') { 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Find the extent of this run of backslashes. 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) size_t start = i, end = start + 1; 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (; end < arg.size() && arg[end] == '\\'; ++end) 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /* empty */; 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) size_t backslash_count = end - start; 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Backslashes are escapes only if the run is followed by a double quote. 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Since we also will end the string with a double quote, we escape for 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // either a double quote or the end of the string. 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (end == arg.size() || arg[end] == '"') { 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // To quote, we need to output 2x as many backslashes. 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) backslash_count *= 2; 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (size_t j = 0; j < backslash_count; ++j) 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) out.push_back('\\'); 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Advance i to one before the end to balance i++ in loop. 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) i = end - 1; 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (arg[i] == '"') { 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) out.push_back('\\'); 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) out.push_back('"'); 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) out.push_back(arg[i]); 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) out.push_back('"'); 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return out; 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandLine::CommandLine(NoProgram no_program) 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : argv_(1), 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) begin_args_(1) { 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandLine::CommandLine(const FilePath& program) 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : argv_(1), 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) begin_args_(1) { 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetProgram(program); 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandLine::CommandLine(int argc, const CommandLine::CharType* const* argv) 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : argv_(1), 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) begin_args_(1) { 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) InitFromArgv(argc, argv); 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandLine::CommandLine(const StringVector& argv) 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : argv_(1), 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) begin_args_(1) { 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) InitFromArgv(argv); 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandLine::~CommandLine() { 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool CommandLine::Init(int argc, const char* const* argv) { 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (current_process_commandline_) { 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If this is intentional, Reset() must be called first. If we are using 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the shared build mode, we have to share a single object across multiple 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // shared libraries. 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) current_process_commandline_ = new CommandLine(NO_PROGRAM); 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN) 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) current_process_commandline_->ParseFromString(::GetCommandLineW()); 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif defined(OS_POSIX) 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) current_process_commandline_->InitFromArgv(argc, argv); 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CommandLine::Reset() { 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(current_process_commandline_); 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delete current_process_commandline_; 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) current_process_commandline_ = NULL; 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandLine* CommandLine::ForCurrentProcess() { 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(current_process_commandline_); 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return current_process_commandline_; 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN) 2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandLine CommandLine::FromString(const std::wstring& command_line) { 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CommandLine cmd(NO_PROGRAM); 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cmd.ParseFromString(command_line); 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return cmd; 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CommandLine::InitFromArgv(int argc, 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const CommandLine::CharType* const* argv) { 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StringVector new_argv; 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (int i = 0; i < argc; ++i) 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) new_argv.push_back(argv[i]); 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) InitFromArgv(new_argv); 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CommandLine::InitFromArgv(const StringVector& argv) { 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) argv_ = StringVector(1); 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) begin_args_ = 1; 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetProgram(argv.empty() ? FilePath() : FilePath(argv[0])); 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) AppendSwitchesAndArguments(*this, argv); 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandLine::StringType CommandLine::GetCommandLineString() const { 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StringType string(argv_[0]); 2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN) 2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) string = QuoteForCommandLineToArgvW(string); 2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StringType params(GetArgumentsString()); 2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!params.empty()) { 2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) string.append(StringType(FILE_PATH_LITERAL(" "))); 2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) string.append(params); 2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return string; 2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandLine::StringType CommandLine::GetArgumentsString() const { 2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StringType params; 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Append switches and arguments. 2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool parse_switches = true; 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (size_t i = 1; i < argv_.size(); ++i) { 2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StringType arg = argv_[i]; 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StringType switch_string; 2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StringType switch_value; 2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parse_switches &= arg != kSwitchTerminator; 2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (i > 1) 2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) params.append(StringType(FILE_PATH_LITERAL(" "))); 2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) { 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) params.append(switch_string); 2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!switch_value.empty()) { 2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN) 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) switch_value = QuoteForCommandLineToArgvW(switch_value); 2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) params.append(kSwitchValueSeparator + switch_value); 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else { 2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN) 2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) arg = QuoteForCommandLineToArgvW(arg); 2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) params.append(arg); 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return params; 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FilePath CommandLine::GetProgram() const { 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return FilePath(argv_[0]); 2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CommandLine::SetProgram(const FilePath& program) { 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) TrimWhitespace(program.value(), TRIM_ALL, &argv_[0]); 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool CommandLine::HasSwitch(const std::string& switch_string) const { 2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return switches_.find(LowerASCIIOnWindows(switch_string)) != switches_.end(); 2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string CommandLine::GetSwitchValueASCII( 2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& switch_string) const { 2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StringType value = GetSwitchValueNative(switch_string); 2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!IsStringASCII(value)) { 2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DLOG(WARNING) << "Value of switch (" << switch_string << ") must be ASCII."; 2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return std::string(); 2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN) 2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return WideToASCII(value); 2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else 2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return value; 2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FilePath CommandLine::GetSwitchValuePath( 2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& switch_string) const { 2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return FilePath(GetSwitchValueNative(switch_string)); 3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandLine::StringType CommandLine::GetSwitchValueNative( 3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& switch_string) const { 3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SwitchMap::const_iterator result = switches_.end(); 3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) result = switches_.find(LowerASCIIOnWindows(switch_string)); 3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return result == switches_.end() ? StringType() : result->second; 3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CommandLine::AppendSwitch(const std::string& switch_string) { 3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) AppendSwitchNative(switch_string, StringType()); 3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CommandLine::AppendSwitchPath(const std::string& switch_string, 3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const FilePath& path) { 3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) AppendSwitchNative(switch_string, path.value()); 3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CommandLine::AppendSwitchNative(const std::string& switch_string, 3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const CommandLine::StringType& value) { 3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string switch_key(LowerASCIIOnWindows(switch_string)); 3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN) 3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StringType combined_switch_string(ASCIIToWide(switch_key)); 3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif defined(OS_POSIX) 3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StringType combined_switch_string(switch_string); 3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) size_t prefix_length = GetSwitchPrefixLength(combined_switch_string); 3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) switches_[switch_key.substr(prefix_length)] = value; 3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Preserve existing switch prefixes in |argv_|; only append one if necessary. 3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (prefix_length == 0) 3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) combined_switch_string = kSwitchPrefixes[0] + combined_switch_string; 3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!value.empty()) 3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) combined_switch_string += kSwitchValueSeparator + value; 3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Append the switch and update the switches/arguments divider |begin_args_|. 3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) argv_.insert(argv_.begin() + begin_args_++, combined_switch_string); 3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CommandLine::AppendSwitchASCII(const std::string& switch_string, 3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& value_string) { 3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN) 3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) AppendSwitchNative(switch_string, ASCIIToWide(value_string)); 3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif defined(OS_POSIX) 3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) AppendSwitchNative(switch_string, value_string); 3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CommandLine::CopySwitchesFrom(const CommandLine& source, 3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const char* const switches[], 3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) size_t count) { 3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (size_t i = 0; i < count; ++i) { 3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (source.HasSwitch(switches[i])) 3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) AppendSwitchNative(switches[i], source.GetSwitchValueNative(switches[i])); 3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CommandLine::StringVector CommandLine::GetArgs() const { 3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Gather all arguments after the last switch (may include kSwitchTerminator). 3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StringVector args(argv_.begin() + begin_args_, argv_.end()); 3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Erase only the first kSwitchTerminator (maybe "--" is a legitimate page?) 3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StringVector::iterator switch_terminator = 3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::find(args.begin(), args.end(), kSwitchTerminator); 3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (switch_terminator != args.end()) 3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) args.erase(switch_terminator); 3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return args; 3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CommandLine::AppendArg(const std::string& value) { 3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN) 3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(IsStringUTF8(value)); 3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) AppendArgNative(UTF8ToWide(value)); 3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif defined(OS_POSIX) 3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) AppendArgNative(value); 3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CommandLine::AppendArgPath(const FilePath& path) { 3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) AppendArgNative(path.value()); 3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CommandLine::AppendArgNative(const CommandLine::StringType& value) { 3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) argv_.push_back(value); 3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CommandLine::AppendArguments(const CommandLine& other, 3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool include_program) { 3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (include_program) 3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetProgram(other.GetProgram()); 3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) AppendSwitchesAndArguments(*this, other.argv()); 3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CommandLine::PrependWrapper(const CommandLine::StringType& wrapper) { 3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (wrapper.empty()) 3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The wrapper may have embedded arguments (like "gdb --args"). In this case, 3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // we don't pretend to do anything fancy, we just split on spaces. 3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) StringVector wrapper_argv; 3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::SplitString(wrapper, FILE_PATH_LITERAL(' '), &wrapper_argv); 3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Prepend the wrapper and update the switches/arguments |begin_args_|. 3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) argv_.insert(argv_.begin(), wrapper_argv.begin(), wrapper_argv.end()); 3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) begin_args_ += wrapper_argv.size(); 4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN) 4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CommandLine::ParseFromString(const std::wstring& command_line) { 4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::wstring command_line_string; 4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) TrimWhitespace(command_line, TRIM_ALL, &command_line_string); 4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (command_line_string.empty()) 4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int num_args = 0; 4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) wchar_t** args = NULL; 4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) args = ::CommandLineToArgvW(command_line_string.c_str(), &num_args); 4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DPLOG_IF(FATAL, !args) << "CommandLineToArgvW failed on command line: " 4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) << command_line; 4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) InitFromArgv(num_args, args); 4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LocalFree(args); 4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 419