mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-05-02 14:57:59 +03:00
Merge branch 'detain-hash-selectively-reluctant' into 'master'
Get rid of EscapeHashX classes option 5 (attempt 2): Use boost::filesystem::path rules if the path starts with ", and consume the whole thing verbatim otherwise Closes #5804 See merge request OpenMW/openmw!1436
This commit is contained in:
commit
4883cbf590
14 changed files with 488 additions and 474 deletions
|
@ -105,8 +105,8 @@ IF(NOT WIN32 AND NOT APPLE)
|
|||
add_definitions(-DGLOBAL_CONFIG_PATH="${GLOBAL_CONFIG_PATH}")
|
||||
ENDIF()
|
||||
add_component_dir (files
|
||||
linuxpath androidpath windowspath macospath fixedpath multidircollection collections configurationmanager escape
|
||||
lowlevelfile constrainedfilestream memorystream hash
|
||||
linuxpath androidpath windowspath macospath fixedpath multidircollection collections configurationmanager
|
||||
lowlevelfile constrainedfilestream memorystream hash configfileparser
|
||||
)
|
||||
|
||||
add_component_dir (compiler
|
||||
|
|
|
@ -11,13 +11,12 @@ void Fallback::validate(boost::any& v, std::vector<std::string> const& tokens, F
|
|||
|
||||
for (const auto& token : tokens)
|
||||
{
|
||||
std::string temp = Files::EscapeHashString::processString(token);
|
||||
size_t sep = temp.find(',');
|
||||
if (sep < 1 || sep == temp.length() - 1 || sep == std::string::npos)
|
||||
size_t sep = token.find(',');
|
||||
if (sep < 1 || sep == token.length() - 1 || sep == std::string::npos)
|
||||
throw boost::program_options::validation_error(boost::program_options::validation_error::invalid_option_value);
|
||||
|
||||
std::string key(temp.substr(0, sep));
|
||||
std::string value(temp.substr(sep + 1));
|
||||
std::string key(token.substr(0, sep));
|
||||
std::string value(token.substr(sep + 1));
|
||||
|
||||
map->mMap[key] = value;
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
#include <components/files/escape.hpp>
|
||||
|
||||
// Parses and validates a fallback map from boost program_options.
|
||||
// Note: for boost to pick up the validate function, you need to pull in the namespace e.g.
|
||||
// by using namespace Fallback;
|
||||
|
|
297
components/files/configfileparser.cpp
Normal file
297
components/files/configfileparser.cpp
Normal file
|
@ -0,0 +1,297 @@
|
|||
// This file's contents is largely lifted from boost::program_options with only minor modification.
|
||||
// Its original preamble (without updated dates) from those source files is below:
|
||||
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "configfileparser.hpp"
|
||||
|
||||
#include <boost/program_options/detail/config_file.hpp>
|
||||
#include <boost/program_options/detail/convert.hpp>
|
||||
|
||||
namespace Files
|
||||
{
|
||||
namespace
|
||||
{
|
||||
/** Standalone parser for config files in ini-line format.
|
||||
The parser is a model of single-pass lvalue iterator, and
|
||||
default constructor creates past-the-end-iterator. The typical usage is:
|
||||
config_file_iterator i(is, ... set of options ...), e;
|
||||
for(; i !=e; ++i) {
|
||||
*i;
|
||||
}
|
||||
|
||||
Syntax conventions:
|
||||
|
||||
- config file can not contain positional options
|
||||
- '#' is comment character: it is ignored together with
|
||||
the rest of the line.
|
||||
- variable assignments are in the form
|
||||
name '=' value.
|
||||
spaces around '=' are trimmed.
|
||||
- Section names are given in brackets.
|
||||
|
||||
The actual option name is constructed by combining current section
|
||||
name and specified option name, with dot between. If section_name
|
||||
already contains dot at the end, new dot is not inserted. For example:
|
||||
@verbatim
|
||||
[gui.accessibility]
|
||||
visual_bell=yes
|
||||
@endverbatim
|
||||
will result in option "gui.accessibility.visual_bell" with value
|
||||
"yes" been returned.
|
||||
|
||||
TODO: maybe, we should just accept a pointer to options_description
|
||||
class.
|
||||
*/
|
||||
class common_config_file_iterator
|
||||
: public boost::eof_iterator<common_config_file_iterator, bpo::option>
|
||||
{
|
||||
public:
|
||||
common_config_file_iterator() { found_eof(); }
|
||||
common_config_file_iterator(
|
||||
const std::set<std::string>& allowed_options,
|
||||
bool allow_unregistered = false);
|
||||
|
||||
virtual ~common_config_file_iterator() {}
|
||||
|
||||
public: // Method required by eof_iterator
|
||||
|
||||
void get();
|
||||
|
||||
#if BOOST_WORKAROUND(_MSC_VER, <= 1900)
|
||||
void decrement() {}
|
||||
void advance(difference_type) {}
|
||||
#endif
|
||||
|
||||
protected: // Stubs for derived classes
|
||||
|
||||
// Obtains next line from the config file
|
||||
// Note: really, this design is a bit ugly
|
||||
// The most clean thing would be to pass 'line_iterator' to
|
||||
// constructor of this class, but to avoid templating this class
|
||||
// we'd need polymorphic iterator, which does not exist yet.
|
||||
virtual bool getline(std::string&) { return false; }
|
||||
|
||||
protected:
|
||||
/** Adds another allowed option. If the 'name' ends with
|
||||
'*', then all options with the same prefix are
|
||||
allowed. For example, if 'name' is 'foo*', then 'foo1' and
|
||||
'foo_bar' are allowed. */
|
||||
void add_option(const char* name);
|
||||
|
||||
// Returns true if 's' is a registered option name.
|
||||
bool allowed_option(const std::string& s) const;
|
||||
|
||||
// That's probably too much data for iterator, since
|
||||
// it will be copied, but let's not bother for now.
|
||||
std::set<std::string> allowed_options;
|
||||
// Invariant: no element is prefix of other element.
|
||||
std::set<std::string> allowed_prefixes;
|
||||
std::string m_prefix;
|
||||
bool m_allow_unregistered;
|
||||
};
|
||||
|
||||
common_config_file_iterator::common_config_file_iterator(
|
||||
const std::set<std::string>& allowed_options,
|
||||
bool allow_unregistered)
|
||||
: allowed_options(allowed_options),
|
||||
m_allow_unregistered(allow_unregistered)
|
||||
{
|
||||
for (std::set<std::string>::const_iterator i = allowed_options.begin();
|
||||
i != allowed_options.end();
|
||||
++i)
|
||||
{
|
||||
add_option(i->c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void common_config_file_iterator::add_option(const char* name)
|
||||
{
|
||||
std::string s(name);
|
||||
assert(!s.empty());
|
||||
if (*s.rbegin() == '*') {
|
||||
s.resize(s.size() - 1);
|
||||
bool bad_prefixes(false);
|
||||
// If 's' is a prefix of one of allowed suffix, then
|
||||
// lower_bound will return that element.
|
||||
// If some element is prefix of 's', then lower_bound will
|
||||
// return the next element.
|
||||
std::set<std::string>::iterator i = allowed_prefixes.lower_bound(s);
|
||||
if (i != allowed_prefixes.end()) {
|
||||
if (i->find(s) == 0)
|
||||
bad_prefixes = true;
|
||||
}
|
||||
if (i != allowed_prefixes.begin()) {
|
||||
--i;
|
||||
if (s.find(*i) == 0)
|
||||
bad_prefixes = true;
|
||||
}
|
||||
if (bad_prefixes)
|
||||
boost::throw_exception(bpo::error("options '" + std::string(name) + "' and '" +
|
||||
*i + "*' will both match the same "
|
||||
"arguments from the configuration file"));
|
||||
allowed_prefixes.insert(s);
|
||||
}
|
||||
}
|
||||
|
||||
std::string trim_ws(const std::string& s)
|
||||
{
|
||||
std::string::size_type n, n2;
|
||||
n = s.find_first_not_of(" \t\r\n");
|
||||
if (n == std::string::npos)
|
||||
return std::string();
|
||||
else {
|
||||
n2 = s.find_last_not_of(" \t\r\n");
|
||||
return s.substr(n, n2 - n + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void common_config_file_iterator::get()
|
||||
{
|
||||
std::string s;
|
||||
std::string::size_type n;
|
||||
bool found = false;
|
||||
|
||||
while (this->getline(s)) {
|
||||
|
||||
// strip '#' comments and whitespace
|
||||
if (s.find('#') == s.find_first_not_of(" \t\r\n"))
|
||||
continue;
|
||||
s = trim_ws(s);
|
||||
|
||||
if (!s.empty()) {
|
||||
// Handle section name
|
||||
if (*s.begin() == '[' && *s.rbegin() == ']') {
|
||||
m_prefix = s.substr(1, s.size() - 2);
|
||||
if (*m_prefix.rbegin() != '.')
|
||||
m_prefix += '.';
|
||||
}
|
||||
else if ((n = s.find('=')) != std::string::npos) {
|
||||
|
||||
std::string name = m_prefix + trim_ws(s.substr(0, n));
|
||||
std::string value = trim_ws(s.substr(n + 1));
|
||||
|
||||
bool registered = allowed_option(name);
|
||||
if (!registered && !m_allow_unregistered)
|
||||
boost::throw_exception(bpo::unknown_option(name));
|
||||
|
||||
found = true;
|
||||
this->value().string_key = name;
|
||||
this->value().value.clear();
|
||||
this->value().value.push_back(value);
|
||||
this->value().unregistered = !registered;
|
||||
this->value().original_tokens.clear();
|
||||
this->value().original_tokens.push_back(name);
|
||||
this->value().original_tokens.push_back(value);
|
||||
break;
|
||||
|
||||
} else {
|
||||
boost::throw_exception(bpo::invalid_config_file_syntax(s, bpo::invalid_syntax::unrecognized_line));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
found_eof();
|
||||
}
|
||||
|
||||
bool common_config_file_iterator::allowed_option(const std::string& s) const
|
||||
{
|
||||
std::set<std::string>::const_iterator i = allowed_options.find(s);
|
||||
if (i != allowed_options.end())
|
||||
return true;
|
||||
// If s is "pa" where "p" is allowed prefix then
|
||||
// lower_bound should find the element after "p".
|
||||
// This depends on 'allowed_prefixes' invariant.
|
||||
i = allowed_prefixes.lower_bound(s);
|
||||
if (i != allowed_prefixes.begin() && s.find(*--i) == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class charT>
|
||||
class basic_config_file_iterator : public Files::common_config_file_iterator {
|
||||
public:
|
||||
basic_config_file_iterator()
|
||||
{
|
||||
found_eof();
|
||||
}
|
||||
|
||||
/** Creates a config file parser for the specified stream.
|
||||
*/
|
||||
basic_config_file_iterator(std::basic_istream<charT>& is,
|
||||
const std::set<std::string>& allowed_options,
|
||||
bool allow_unregistered = false);
|
||||
|
||||
private: // base overrides
|
||||
|
||||
bool getline(std::string&);
|
||||
|
||||
private: // internal data
|
||||
std::shared_ptr<std::basic_istream<charT> > is;
|
||||
};
|
||||
|
||||
template<class charT>
|
||||
basic_config_file_iterator<charT>::
|
||||
basic_config_file_iterator(std::basic_istream<charT>& is,
|
||||
const std::set<std::string>& allowed_options,
|
||||
bool allow_unregistered)
|
||||
: common_config_file_iterator(allowed_options, allow_unregistered)
|
||||
{
|
||||
this->is.reset(&is, bpo::detail::null_deleter());
|
||||
get();
|
||||
}
|
||||
|
||||
template<class charT>
|
||||
bool basic_config_file_iterator<charT>::getline(std::string& s)
|
||||
{
|
||||
std::basic_string<charT> in;
|
||||
if (std::getline(*is, in)) {
|
||||
s = bpo::to_internal(in);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class charT>
|
||||
bpo::basic_parsed_options<charT>
|
||||
parse_config_file(std::basic_istream<charT>& is,
|
||||
const bpo::options_description& desc,
|
||||
bool allow_unregistered)
|
||||
{
|
||||
std::set<std::string> allowed_options;
|
||||
|
||||
const std::vector<boost::shared_ptr<bpo::option_description> >& options = desc.options();
|
||||
for (unsigned i = 0; i < options.size(); ++i)
|
||||
{
|
||||
const bpo::option_description& d = *options[i];
|
||||
|
||||
if (d.long_name().empty())
|
||||
boost::throw_exception(
|
||||
bpo::error("abbreviated option names are not permitted in options configuration files"));
|
||||
|
||||
allowed_options.insert(d.long_name());
|
||||
}
|
||||
|
||||
// Parser return char strings
|
||||
bpo::parsed_options result(&desc);
|
||||
copy(basic_config_file_iterator<charT>(
|
||||
is, allowed_options, allow_unregistered),
|
||||
basic_config_file_iterator<charT>(),
|
||||
back_inserter(result.options));
|
||||
// Convert char strings into desired type.
|
||||
return bpo::basic_parsed_options<charT>(result);
|
||||
}
|
||||
|
||||
template
|
||||
bpo::basic_parsed_options<char>
|
||||
parse_config_file(std::basic_istream<char>& is,
|
||||
const bpo::options_description& desc,
|
||||
bool allow_unregistered);
|
||||
}
|
17
components/files/configfileparser.hpp
Normal file
17
components/files/configfileparser.hpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#ifndef COMPONENTS_FILES_CONFIGFILEPARSER_HPP
|
||||
#define COMPONENTS_FILES_CONFIGFILEPARSER_HPP
|
||||
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
|
||||
namespace Files
|
||||
{
|
||||
|
||||
namespace bpo = boost::program_options;
|
||||
|
||||
template<class charT>
|
||||
bpo::basic_parsed_options<charT> parse_config_file(std::basic_istream<charT>&, const bpo::options_description&,
|
||||
bool allow_unregistered = false);
|
||||
|
||||
}
|
||||
|
||||
#endif // COMPONENTS_FILES_CONFIGFILEPARSER_HPP
|
|
@ -1,7 +1,7 @@
|
|||
#include "configurationmanager.hpp"
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/files/escape.hpp>
|
||||
#include <components/files/configfileparser.hpp>
|
||||
#include <components/fallback/validate.hpp>
|
||||
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
@ -109,7 +109,7 @@ void mergeComposingVariables(boost::program_options::variables_map& first, boost
|
|||
auto replace = second["replace"];
|
||||
if (!replace.defaulted() && !replace.empty())
|
||||
{
|
||||
std::vector<std::string> replaceVector = replace.as<Files::EscapeStringVector>().toStdStringVector();
|
||||
std::vector<std::string> replaceVector = replace.as<std::vector<std::string>>();
|
||||
replacedVariables.insert(replaceVector.begin(), replaceVector.end());
|
||||
}
|
||||
}
|
||||
|
@ -138,19 +138,19 @@ void mergeComposingVariables(boost::program_options::variables_map& first, boost
|
|||
boost::any& firstValue = firstPosition->second.value();
|
||||
const boost::any& secondValue = second[name].value();
|
||||
|
||||
if (firstValue.type() == typeid(Files::EscapePathContainer))
|
||||
if (firstValue.type() == typeid(Files::MaybeQuotedPathContainer))
|
||||
{
|
||||
auto& firstPathContainer = boost::any_cast<Files::EscapePathContainer&>(firstValue);
|
||||
const auto& secondPathContainer = boost::any_cast<const Files::EscapePathContainer&>(secondValue);
|
||||
auto& firstPathContainer = boost::any_cast<Files::MaybeQuotedPathContainer&>(firstValue);
|
||||
const auto& secondPathContainer = boost::any_cast<const Files::MaybeQuotedPathContainer&>(secondValue);
|
||||
|
||||
firstPathContainer.insert(firstPathContainer.end(), secondPathContainer.begin(), secondPathContainer.end());
|
||||
}
|
||||
else if (firstValue.type() == typeid(Files::EscapeStringVector))
|
||||
else if (firstValue.type() == typeid(std::vector<std::string>))
|
||||
{
|
||||
auto& firstVector = boost::any_cast<Files::EscapeStringVector&>(firstValue);
|
||||
const auto& secondVector = boost::any_cast<const Files::EscapeStringVector&>(secondValue);
|
||||
auto& firstVector = boost::any_cast<std::vector<std::string>&>(firstValue);
|
||||
const auto& secondVector = boost::any_cast<const std::vector<std::string>&>(secondValue);
|
||||
|
||||
firstVector.mVector.insert(firstVector.mVector.end(), secondVector.mVector.begin(), secondVector.mVector.end());
|
||||
firstVector.insert(firstVector.end(), secondVector.begin(), secondVector.end());
|
||||
}
|
||||
else if (firstValue.type() == typeid(Fallback::FallbackMap))
|
||||
{
|
||||
|
@ -235,11 +235,11 @@ bool ConfigurationManager::loadConfig(const boost::filesystem::path& path,
|
|||
if (!mSilent)
|
||||
Log(Debug::Info) << "Loading config file: " << cfgFile.string();
|
||||
|
||||
boost::filesystem::ifstream configFileStreamUnfiltered(cfgFile);
|
||||
boost::filesystem::ifstream configFileStream(cfgFile);
|
||||
|
||||
if (configFileStreamUnfiltered.is_open())
|
||||
if (configFileStream.is_open())
|
||||
{
|
||||
parseConfig(configFileStreamUnfiltered, variables, description);
|
||||
parseConfig(configFileStream, variables, description);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -311,14 +311,37 @@ void parseArgs(int argc, const char* const argv[], boost::program_options::varia
|
|||
void parseConfig(std::istream& stream, boost::program_options::variables_map& variables,
|
||||
boost::program_options::options_description& description)
|
||||
{
|
||||
boost::iostreams::filtering_istream configFileStream;
|
||||
configFileStream.push(escape_hash_filter());
|
||||
configFileStream.push(stream);
|
||||
|
||||
boost::program_options::store(
|
||||
boost::program_options::parse_config_file(configFileStream, description, true),
|
||||
Files::parse_config_file(stream, description, true),
|
||||
variables
|
||||
);
|
||||
}
|
||||
|
||||
std::istream& operator>> (std::istream& istream, MaybeQuotedPath& MaybeQuotedPath)
|
||||
{
|
||||
// If the stream starts with a double quote, read from stream using boost::filesystem::path rules, then discard anything remaining.
|
||||
// This prevents boost::program_options getting upset that we've not consumed the whole stream.
|
||||
// If it doesn't start with a double quote, read the whole thing verbatim
|
||||
if (istream.peek() == '"')
|
||||
{
|
||||
istream >> static_cast<boost::filesystem::path&>(MaybeQuotedPath);
|
||||
if (istream && !istream.eof() && istream.peek() != EOF)
|
||||
{
|
||||
std::string remainder{std::istreambuf_iterator(istream), {}};
|
||||
Log(Debug::Warning) << "Trailing data in path setting. Used '" << MaybeQuotedPath.string() << "' but '" << remainder << "' remained";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string intermediate{std::istreambuf_iterator(istream), {}};
|
||||
static_cast<boost::filesystem::path&>(MaybeQuotedPath) = intermediate;
|
||||
}
|
||||
return istream;
|
||||
}
|
||||
|
||||
PathContainer asPathContainer(const MaybeQuotedPathContainer& MaybeQuotedPathContainer)
|
||||
{
|
||||
return PathContainer(MaybeQuotedPathContainer.begin(), MaybeQuotedPathContainer.end());
|
||||
}
|
||||
|
||||
} /* namespace Cfg */
|
||||
|
|
|
@ -77,6 +77,16 @@ void parseArgs(int argc, const char* const argv[], boost::program_options::varia
|
|||
void parseConfig(std::istream& stream, boost::program_options::variables_map& variables,
|
||||
boost::program_options::options_description& description);
|
||||
|
||||
class MaybeQuotedPath : public boost::filesystem::path
|
||||
{
|
||||
};
|
||||
|
||||
std::istream& operator>> (std::istream& istream, MaybeQuotedPath& MaybeQuotedPath);
|
||||
|
||||
typedef std::vector<MaybeQuotedPath> MaybeQuotedPathContainer;
|
||||
|
||||
PathContainer asPathContainer(const MaybeQuotedPathContainer& MaybeQuotedPathContainer);
|
||||
|
||||
} /* namespace Cfg */
|
||||
|
||||
#endif /* COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP */
|
||||
|
|
|
@ -1,145 +0,0 @@
|
|||
#include "escape.hpp"
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
namespace Files
|
||||
{
|
||||
const int escape_hash_filter::sEscape = '@';
|
||||
const int escape_hash_filter::sEscapeIdentifier = 'a';
|
||||
const int escape_hash_filter::sHashIdentifier = 'h';
|
||||
|
||||
escape_hash_filter::escape_hash_filter() : mSeenNonWhitespace(false), mFinishLine(false)
|
||||
{
|
||||
}
|
||||
|
||||
escape_hash_filter::~escape_hash_filter()
|
||||
{
|
||||
}
|
||||
|
||||
unescape_hash_filter::unescape_hash_filter() : expectingIdentifier(false)
|
||||
{
|
||||
}
|
||||
|
||||
unescape_hash_filter::~unescape_hash_filter()
|
||||
{
|
||||
}
|
||||
|
||||
std::string EscapeHashString::processString(const std::string & str)
|
||||
{
|
||||
std::string temp = str;
|
||||
|
||||
static const char hash[] = { escape_hash_filter::sEscape, escape_hash_filter::sHashIdentifier };
|
||||
Misc::StringUtils::replaceAll(temp, std::string_view(hash, 2), "#");
|
||||
|
||||
static const char escape[] = { escape_hash_filter::sEscape, escape_hash_filter::sEscapeIdentifier };
|
||||
Misc::StringUtils::replaceAll(temp, std::string_view(escape, 2), "@");
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
EscapeHashString::EscapeHashString() : mData()
|
||||
{
|
||||
}
|
||||
|
||||
EscapeHashString::EscapeHashString(const std::string & str) : mData(EscapeHashString::processString(str))
|
||||
{
|
||||
}
|
||||
|
||||
EscapeHashString::EscapeHashString(const std::string & str, size_t pos, size_t len) : mData(EscapeHashString::processString(str), pos, len)
|
||||
{
|
||||
}
|
||||
|
||||
EscapeHashString::EscapeHashString(const char * s) : mData(EscapeHashString::processString(std::string(s)))
|
||||
{
|
||||
}
|
||||
|
||||
EscapeHashString::EscapeHashString(const char * s, size_t n) : mData(EscapeHashString::processString(std::string(s)), 0, n)
|
||||
{
|
||||
}
|
||||
|
||||
EscapeHashString::EscapeHashString(size_t n, char c) : mData(n, c)
|
||||
{
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
EscapeHashString::EscapeHashString(InputIterator first, InputIterator last) : mData(EscapeHashString::processString(std::string(first, last)))
|
||||
{
|
||||
}
|
||||
|
||||
std::string EscapeHashString::toStdString() const
|
||||
{
|
||||
return std::string(mData);
|
||||
}
|
||||
|
||||
std::istream & operator>> (std::istream & is, EscapeHashString & eHS)
|
||||
{
|
||||
std::string temp;
|
||||
is >> temp;
|
||||
eHS = EscapeHashString(temp);
|
||||
return is;
|
||||
}
|
||||
|
||||
std::ostream & operator<< (std::ostream & os, const EscapeHashString & eHS)
|
||||
{
|
||||
os << eHS.mData;
|
||||
return os;
|
||||
}
|
||||
|
||||
EscapeStringVector::EscapeStringVector() : mVector()
|
||||
{
|
||||
}
|
||||
|
||||
EscapeStringVector::~EscapeStringVector()
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<std::string> EscapeStringVector::toStdStringVector() const
|
||||
{
|
||||
std::vector<std::string> temp = std::vector<std::string>();
|
||||
for (std::vector<EscapeHashString>::const_iterator it = mVector.begin(); it != mVector.end(); ++it)
|
||||
{
|
||||
temp.push_back(it->toStdString());
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
// boost program options validation
|
||||
|
||||
void validate(boost::any &v, const std::vector<std::string> &tokens, Files::EscapeHashString * eHS, int a)
|
||||
{
|
||||
boost::program_options::validators::check_first_occurrence(v);
|
||||
|
||||
if (v.empty())
|
||||
v = boost::any(EscapeHashString(boost::program_options::validators::get_single_string(tokens)));
|
||||
}
|
||||
|
||||
void validate(boost::any &v, const std::vector<std::string> &tokens, EscapeStringVector *, int)
|
||||
{
|
||||
if (v.empty())
|
||||
v = boost::any(EscapeStringVector());
|
||||
|
||||
EscapeStringVector * eSV = boost::any_cast<EscapeStringVector>(&v);
|
||||
|
||||
for (std::vector<std::string>::const_iterator it = tokens.begin(); it != tokens.end(); ++it)
|
||||
eSV->mVector.emplace_back(*it);
|
||||
}
|
||||
|
||||
PathContainer EscapePath::toPathContainer(const EscapePathContainer & escapePathContainer)
|
||||
{
|
||||
PathContainer temp;
|
||||
for (EscapePathContainer::const_iterator it = escapePathContainer.begin(); it != escapePathContainer.end(); ++it)
|
||||
temp.push_back(it->mPath);
|
||||
return temp;
|
||||
}
|
||||
|
||||
std::istream & operator>> (std::istream & istream, EscapePath & escapePath)
|
||||
{
|
||||
boost::iostreams::filtering_istream filteredStream;
|
||||
filteredStream.push(unescape_hash_filter());
|
||||
filteredStream.push(istream);
|
||||
|
||||
filteredStream >> escapePath.mPath;
|
||||
|
||||
return istream;
|
||||
}
|
||||
}
|
|
@ -1,191 +0,0 @@
|
|||
#ifndef COMPONENTS_FILES_ESCAPE_HPP
|
||||
#define COMPONENTS_FILES_ESCAPE_HPP
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include <components/files/multidircollection.hpp>
|
||||
|
||||
#include <boost/iostreams/filtering_stream.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
/**
|
||||
* \namespace Files
|
||||
*/
|
||||
namespace Files
|
||||
{
|
||||
/**
|
||||
* \struct escape_hash_filter
|
||||
*/
|
||||
struct escape_hash_filter : public boost::iostreams::input_filter
|
||||
{
|
||||
static const int sEscape;
|
||||
static const int sHashIdentifier;
|
||||
static const int sEscapeIdentifier;
|
||||
|
||||
escape_hash_filter();
|
||||
virtual ~escape_hash_filter();
|
||||
|
||||
template <typename Source> int get(Source & src);
|
||||
|
||||
private:
|
||||
std::queue<int> mNext;
|
||||
|
||||
bool mSeenNonWhitespace;
|
||||
bool mFinishLine;
|
||||
};
|
||||
|
||||
template <typename Source>
|
||||
int escape_hash_filter::get(Source & src)
|
||||
{
|
||||
if (mNext.empty())
|
||||
{
|
||||
int character = boost::iostreams::get(src);
|
||||
if (character == boost::iostreams::WOULD_BLOCK)
|
||||
{
|
||||
mNext.push(character);
|
||||
}
|
||||
else if (character == EOF)
|
||||
{
|
||||
mSeenNonWhitespace = false;
|
||||
mFinishLine = false;
|
||||
mNext.push(character);
|
||||
}
|
||||
else if (character == '\n')
|
||||
{
|
||||
mSeenNonWhitespace = false;
|
||||
mFinishLine = false;
|
||||
mNext.push(character);
|
||||
}
|
||||
else if (mFinishLine)
|
||||
{
|
||||
mNext.push(character);
|
||||
}
|
||||
else if (character == '#')
|
||||
{
|
||||
if (mSeenNonWhitespace)
|
||||
{
|
||||
mNext.push(sEscape);
|
||||
mNext.push(sHashIdentifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
//it's fine being interpreted by Boost as a comment, and so is anything afterwards
|
||||
mNext.push(character);
|
||||
mFinishLine = true;
|
||||
}
|
||||
}
|
||||
else if (character == sEscape)
|
||||
{
|
||||
mNext.push(sEscape);
|
||||
mNext.push(sEscapeIdentifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
mNext.push(character);
|
||||
}
|
||||
if (!mSeenNonWhitespace && !(character >= 0 && character <= 255 && isspace(character)))
|
||||
mSeenNonWhitespace = true;
|
||||
}
|
||||
int retval = mNext.front();
|
||||
mNext.pop();
|
||||
return retval;
|
||||
}
|
||||
|
||||
struct unescape_hash_filter : public boost::iostreams::input_filter
|
||||
{
|
||||
unescape_hash_filter();
|
||||
virtual ~unescape_hash_filter();
|
||||
|
||||
template <typename Source> int get(Source & src);
|
||||
|
||||
private:
|
||||
bool expectingIdentifier;
|
||||
};
|
||||
|
||||
template <typename Source>
|
||||
int unescape_hash_filter::get(Source & src)
|
||||
{
|
||||
int character;
|
||||
if (!expectingIdentifier)
|
||||
character = boost::iostreams::get(src);
|
||||
else
|
||||
{
|
||||
character = escape_hash_filter::sEscape;
|
||||
expectingIdentifier = false;
|
||||
}
|
||||
if (character == escape_hash_filter::sEscape)
|
||||
{
|
||||
int nextChar = boost::iostreams::get(src);
|
||||
int intended;
|
||||
if (nextChar == escape_hash_filter::sEscapeIdentifier)
|
||||
intended = escape_hash_filter::sEscape;
|
||||
else if (nextChar == escape_hash_filter::sHashIdentifier)
|
||||
intended = '#';
|
||||
else if (nextChar == boost::iostreams::WOULD_BLOCK)
|
||||
{
|
||||
expectingIdentifier = true;
|
||||
intended = nextChar;
|
||||
}
|
||||
else
|
||||
intended = '?';
|
||||
return intended;
|
||||
}
|
||||
else
|
||||
return character;
|
||||
}
|
||||
|
||||
/**
|
||||
* \class EscapeHashString
|
||||
*/
|
||||
class EscapeHashString
|
||||
{
|
||||
private:
|
||||
std::string mData;
|
||||
public:
|
||||
static std::string processString(const std::string & str);
|
||||
|
||||
EscapeHashString();
|
||||
EscapeHashString(const std::string & str);
|
||||
EscapeHashString(const std::string & str, size_t pos, size_t len = std::string::npos);
|
||||
EscapeHashString(const char * s);
|
||||
EscapeHashString(const char * s, size_t n);
|
||||
EscapeHashString(size_t n, char c);
|
||||
template <class InputIterator>
|
||||
EscapeHashString(InputIterator first, InputIterator last);
|
||||
|
||||
std::string toStdString() const;
|
||||
|
||||
friend std::ostream & operator<< (std::ostream & os, const EscapeHashString & eHS);
|
||||
};
|
||||
|
||||
std::istream & operator>> (std::istream & is, EscapeHashString & eHS);
|
||||
|
||||
struct EscapeStringVector
|
||||
{
|
||||
std::vector<Files::EscapeHashString> mVector;
|
||||
|
||||
EscapeStringVector();
|
||||
virtual ~EscapeStringVector();
|
||||
|
||||
std::vector<std::string> toStdStringVector() const;
|
||||
};
|
||||
|
||||
//boost program options validation
|
||||
|
||||
void validate(boost::any &v, const std::vector<std::string> &tokens, Files::EscapeHashString * eHS, int a);
|
||||
|
||||
void validate(boost::any &v, const std::vector<std::string> &tokens, EscapeStringVector *, int);
|
||||
|
||||
struct EscapePath
|
||||
{
|
||||
boost::filesystem::path mPath;
|
||||
|
||||
static PathContainer toPathContainer(const std::vector<EscapePath> & escapePathContainer);
|
||||
};
|
||||
|
||||
typedef std::vector<EscapePath> EscapePathContainer;
|
||||
|
||||
std::istream & operator>> (std::istream & istream, EscapePath & escapePath);
|
||||
} /* namespace Files */
|
||||
#endif /* COMPONENTS_FILES_ESCAPE_HPP */
|
Loading…
Add table
Add a link
Reference in a new issue