diff --git a/src/backends/firebird/session.cpp b/src/backends/firebird/session.cpp index dd18fe2b3..d8d163a37 100644 --- a/src/backends/firebird/session.cpp +++ b/src/backends/firebird/session.cpp @@ -9,6 +9,7 @@ #include "soci-firebird.h" #include "error-firebird.h" #include "session.h" +#include #include #include #include @@ -19,32 +20,160 @@ using namespace soci::details::firebird; namespace { -// retrieves parameters from the uniform connect string -void explodeISCConnectString(std::string const &connectString, - std::map ¶meters) +// Helpers of explodeISCConnectString() for reading words from a string. "Word" +// here is defined very loosely as just a sequence of non-space characters. +// +// All these helper functions update the input iterator to point to the first +// character not consumed by them. + +// Advance the input iterator until the first non-space character or end of the +// string. +void skipWhiteSpace(std::string::const_iterator& i, std::string::const_iterator const &end) { - std::string tmp; - for (std::string::const_iterator i = connectString.begin(), - end = connectString.end(); i != end; ++i) + std::locale const loc; + for (; i != end; ++i) { - if (*i == '=') + if (!std::isspace(*i, loc)) + break; + } +} + +// Return the string of all characters until the first space or the specified +// delimiter. +// +// Throws if the first non-space character after the end of the word is not the +// delimiter. However just returns en empty string, without throwing, if +// nothing is left at all in the string except for white space. +std::string +getWordUntil(std::string const &s, std::string::const_iterator &i, char delim) +{ + std::string::const_iterator const end = s.end(); + skipWhiteSpace(i, end); + + // We need to handle this case specially because it's not an error if + // nothing at all remains in the string. But if anything does remain, then + // we must have the delimiter. + if (i == end) + return std::string(); + + // Simply put anything until the delimiter into the word, stopping at the + // first white space character. + std::string word; + std::locale const loc; + for (; i != end; ++i) + { + if (*i == delim) + break; + + if (std::isspace(*i, loc)) { - tmp += ' '; + skipWhiteSpace(i, end); + if (i == end || *i != delim) + { + std::ostringstream os; + os << "Expected '" << delim << "' at position " + << (i - s.begin() + 1) + << " in Firebird connection string \"" + << s << "\"."; + + throw soci_error(os.str()); + } + + break; } - else + + word += *i; + } + + if (i == end) + { + std::ostringstream os; + os << "Expected '" << delim + << "' not found before the end of the string " + << "in Firebird connection string \"" + << s << "\"."; + + throw soci_error(os.str()); + } + + ++i; // Skip the delimiter itself. + + return word; +} + +// Return a possibly quoted word, i.e. either just a sequence of non-space +// characters or everything inside a double-quoted string. +// +// Throws if the word is quoted and the closing quote is not found. However +// doesn't throw, just returns an empty string if there is nothing left. +std::string +getPossiblyQuotedWord(std::string const &s, std::string::const_iterator &i) +{ + std::string::const_iterator const end = s.end(); + skipWhiteSpace(i, end); + + std::string word; + + if (i != end && *i == '"') + { + for (;;) { - tmp += *i; + if (++i == end) + { + std::ostringstream os; + os << "Expected '\"' not found before the end of the string " + "in Firebird connection string \"" + << s << "\"."; + + throw soci_error(os.str()); + } + + if (*i == '"') + { + ++i; + break; + } + + word += *i; } } + else // Not quoted. + { + std::locale const loc; + for (; i != end; ++i) + { + if (std::isspace(*i, loc)) + break; - parameters.clear(); + word += *i; + } + } + + return word; +} + +// retrieves parameters from the uniform connect string which is supposed to be +// in the form "key=value[ key2=value2 ...]" and the values may be quoted to +// allow including spaces into them. Notice that currently there is no way to +// include both a space and a double quote in a value. +std::map +explodeISCConnectString(std::string const &connectString) +{ + std::map parameters; - std::istringstream iss(tmp); std::string key, value; - while (iss >> key >> value) + for (std::string::const_iterator i = connectString.begin(); ; ) { + key = getWordUntil(connectString, i, '='); + if (key.empty()) + break; + + value = getPossiblyQuotedWord(connectString, i); + parameters.insert(std::pair(key, value)); } + + return parameters; } // extracts given parameter from map previusly build with explodeISCConnectString @@ -74,8 +203,8 @@ firebird_session_backend::firebird_session_backend( , decimals_as_strings_(false) { // extract connection parameters - std::map params; - explodeISCConnectString(connectString, params); + std::map + params(explodeISCConnectString(connectString)); ISC_STATUS stat[stat_size]; std::string param;