blob: f4a8459d4e92c72ce38f91d028ddc8684f04c27c [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "starboard/shared/starboard/command_line.h"
#include <algorithm>
#include <ostream>
#include "starboard/configuration.h"
#include "starboard/log.h"
namespace starboard {
namespace shared {
namespace starboard {
namespace {
// From base/string_util.cc
const char kWhitespaceASCII[] = {
0x09, // <control-0009> to <control-000D>
0x0A,
0x0B,
0x0C,
0x0D,
0x20, // Space
0
};
const CommandLine::CharType kSwitchTerminator[] = "--";
const CommandLine::CharType kSwitchValueSeparator[] = "=";
// Since we use a lazy match, make sure that longer versions (like "--") are
// listed before shorter versions (like "-") of similar prefixes.
// Unixes don't use slash as a switch.
const CommandLine::CharType* const kSwitchPrefixes[] = {"--", "-"};
size_t GetSwitchPrefixLength(const CommandLine::StringType& string) {
for (size_t i = 0; i < SB_ARRAY_SIZE_INT(kSwitchPrefixes); ++i) {
CommandLine::StringType prefix(kSwitchPrefixes[i]);
if (string.compare(0, prefix.length(), prefix) == 0)
return prefix.length();
}
return 0;
}
// Fills in |switch_string| and |switch_value| if |string| is a switch.
// This will preserve the input switch prefix in the output |switch_string|.
bool IsSwitch(const CommandLine::StringType& string,
CommandLine::StringType* switch_string,
CommandLine::StringType* switch_value) {
switch_string->clear();
switch_value->clear();
size_t prefix_length = GetSwitchPrefixLength(string);
if (prefix_length == 0 || prefix_length == string.length())
return false;
const size_t equals_position = string.find(kSwitchValueSeparator);
*switch_string = string.substr(0, equals_position);
if (equals_position != CommandLine::StringType::npos)
*switch_value = string.substr(equals_position + 1);
return true;
}
// Append switches and arguments, keeping switches before arguments.
void AppendSwitchesAndArguments(CommandLine& command_line,
const CommandLine::StringVector& argv) {
bool parse_switches = true;
for (size_t i = 1; i < argv.size(); ++i) {
CommandLine::StringType arg = argv[i];
// Begin inlined TrimWhitespace TRIM_ALL from base/string_util.cc
CommandLine::StringType::size_type last_char = arg.length() - 1;
CommandLine::StringType::size_type first_good_char =
arg.find_first_not_of(kWhitespaceASCII);
CommandLine::StringType::size_type last_good_char =
arg.find_last_not_of(kWhitespaceASCII);
if (arg.empty() ||
(first_good_char == CommandLine::StringType::npos) ||
(last_good_char == CommandLine::StringType::npos)) {
arg.clear();
} else {
// Trim the whitespace.
arg = arg.substr(first_good_char, last_good_char - first_good_char + 1);
}
// End inlined TrimWhitespace TRIM_ALL from base/string_util.cc
CommandLine::StringType switch_string;
CommandLine::StringType switch_value;
parse_switches &= (arg != kSwitchTerminator);
if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) {
command_line.AppendSwitch(switch_string, switch_value);
} else {
command_line.AppendArg(arg);
}
}
}
// Lowercase switches for backwards compatiblity *on Windows*.
std::string LowerASCIIOnWindows(const std::string& string) {
return string;
}
} // namespace
CommandLine::CommandLine(int argc, const CommandLine::CharType* const* argv)
: argv_(1),
begin_args_(1),
original_argument_count_(argc),
original_argument_vector_(argv) {
InitFromArgv(argc, argv);
}
CommandLine::~CommandLine() {
}
void CommandLine::InitFromArgv(int argc,
const CommandLine::CharType* const* argv) {
StringVector new_argv;
for (int i = 0; i < argc; ++i)
new_argv.push_back(argv[i]);
InitFromArgv(new_argv);
}
void CommandLine::InitFromArgv(const StringVector& argv) {
argv_ = StringVector(1);
begin_args_ = 1;
AppendSwitchesAndArguments(*this, argv);
}
bool CommandLine::HasSwitch(const std::string& switch_string) const {
return switches_.find(LowerASCIIOnWindows(switch_string)) != switches_.end();
}
CommandLine::StringType CommandLine::GetSwitchValue(
const std::string& switch_string) const {
SwitchMap::const_iterator result = switches_.end();
result = switches_.find(LowerASCIIOnWindows(switch_string));
return result == switches_.end() ? StringType() : result->second;
}
void CommandLine::AppendSwitch(const std::string& switch_string,
const CommandLine::StringType& value) {
std::string switch_key(LowerASCIIOnWindows(switch_string));
StringType combined_switch_string(switch_string);
size_t prefix_length = GetSwitchPrefixLength(combined_switch_string);
switches_[switch_key.substr(prefix_length)] = value;
// Preserve existing switch prefixes in |argv_|; only append one if necessary.
if (prefix_length == 0)
combined_switch_string = kSwitchPrefixes[0] + combined_switch_string;
if (!value.empty())
combined_switch_string += kSwitchValueSeparator + value;
// Append the switch and update the switches/arguments divider |begin_args_|.
argv_.insert(argv_.begin() + begin_args_++, combined_switch_string);
}
CommandLine::StringVector CommandLine::GetArgs() const {
// Gather all arguments after the last switch (may include kSwitchTerminator).
StringVector args(argv_.begin() + begin_args_, argv_.end());
// Erase only the first kSwitchTerminator (maybe "--" is a legitimate page?)
StringVector::iterator switch_terminator =
std::find(args.begin(), args.end(), kSwitchTerminator);
if (switch_terminator != args.end())
args.erase(switch_terminator);
return args;
}
void CommandLine::AppendArg(const CommandLine::StringType& value) {
argv_.push_back(value);
}
void CommandLine::AppendArguments(const CommandLine& other,
bool include_program) {
AppendSwitchesAndArguments(*this, other.argv());
}
} // namespace starboard
} // namespace shared
} // namespace starboard