diff options
author | David Kalnischkies <david@kalnischkies.de> | 2017-07-17 10:52:09 +0200 |
---|---|---|
committer | David Kalnischkies <david@kalnischkies.de> | 2017-12-13 23:56:29 +0100 |
commit | 07cd99066c30e70a9f41851c80e7c51f4e507163 (patch) | |
tree | 5bdaf316c93482f8b77a2b16e3467c6d679d57f1 /apt-pkg | |
parent | 99813a2eaa7c0cce1d7d8c811827733ed66458de (diff) |
support multiline values in LookupTag
LookupTag is a little helper to deal with rfc822-style strings we use in
apt e.g. to pass acquire messages around for cases in which our usual
rfc822 parser is too heavy. All the fields it had to deal with so far
were single line, but if they aren't it should really produce the right
output and not just return the first line. Error messages are a prime
candidate for becoming multiline as at the moment they are stripped of
potential newlines due to the previous insufficiency of LookupTag.
Diffstat (limited to 'apt-pkg')
-rw-r--r-- | apt-pkg/contrib/strutl.cc | 92 |
1 files changed, 69 insertions, 23 deletions
diff --git a/apt-pkg/contrib/strutl.cc b/apt-pkg/contrib/strutl.cc index f2fc0b6a6..638f09e9d 100644 --- a/apt-pkg/contrib/strutl.cc +++ b/apt-pkg/contrib/strutl.cc @@ -694,34 +694,80 @@ int stringcasecmp(string::const_iterator A,string::const_iterator AEnd, /*}}}*/ // LookupTag - Lookup the value of a tag in a taged string /*{{{*/ // --------------------------------------------------------------------- -/* The format is like those used in package files and the method +/* The format is like those used in package files and the method communication system */ -string LookupTag(const string &Message,const char *Tag,const char *Default) +std::string LookupTag(const std::string &Message, const char *TagC, const char *Default) { - // Look for a matching tag. - int Length = strlen(Tag); - for (string::const_iterator I = Message.begin(); I + Length < Message.end(); ++I) + std::string tag = std::string("\n") + TagC + ":"; + if (Default == nullptr) + Default = ""; + if (Message.length() < tag.length()) + return Default; + std::transform(tag.begin(), tag.end(), tag.begin(), tolower_ascii); + auto valuestart = Message.cbegin(); + // maybe the message starts directly with tag + if (Message[tag.length() - 2] == ':') { - // Found the tag - if (I[Length] == ':' && stringcasecmp(I,I+Length,Tag) == 0) + std::string lowstart = std::string("\n") + Message.substr(0, tag.length() - 1); + std::transform(lowstart.begin(), lowstart.end(), lowstart.begin(), tolower_ascii); + if (lowstart == tag) + valuestart = std::next(valuestart, tag.length() - 1); + } + // the tag is somewhere in the message + if (valuestart == Message.cbegin()) + { + auto const tagbegin = std::search(Message.cbegin(), Message.cend(), tag.cbegin(), tag.cend(), + [](char const a, char const b) { return tolower_ascii(a) == b; }); + if (tagbegin == Message.cend()) + return Default; + valuestart = std::next(tagbegin, tag.length()); + } + auto const is_whitespace = [](char const c) { return isspace_ascii(c) != 0 && c != '\n'; }; + auto const is_newline = [](char const c) { return c == '\n'; }; + std::string result; + valuestart = std::find_if_not(valuestart, Message.cend(), is_whitespace); + // is the first line of the value empty? + if (valuestart != Message.cend() && *valuestart == '\n') + { + valuestart = std::next(valuestart); + if (valuestart != Message.cend() && *valuestart == ' ') + valuestart = std::next(valuestart); + } + // extract the value over multiple lines removing trailing whitespace + while (valuestart < Message.cend()) + { + auto const linebreak = std::find_if(valuestart, Message.cend(), is_newline); + auto valueend = std::prev(linebreak); + // skip spaces at the end of the line + while (valueend > valuestart && is_whitespace(*valueend)) + valueend = std::prev(valueend); + // append found line to result { - // Find the end of line and strip the leading/trailing spaces - string::const_iterator J; - I += Length + 1; - for (; isspace_ascii(*I) != 0 && I < Message.end(); ++I); - for (J = I; *J != '\n' && J < Message.end(); ++J); - for (; J > I && isspace_ascii(J[-1]) != 0; --J); - - return string(I,J); + std::string tmp(valuestart, std::next(valueend)); + if (tmp != ".") + { + if (result.empty()) + result.assign(std::move(tmp)); + else + result.append(tmp); + } } - - for (; *I != '\n' && I < Message.end(); ++I); - } - - // Failed to find a match - if (Default == 0) - return string(); - return Default; + // see if the value is multiline + if (linebreak == Message.cend()) + break; + valuestart = std::next(linebreak); + if (valuestart == Message.cend() || *valuestart != ' ') + break; + result.append("\n"); + // skip the space leading a multiline (Keep all other whitespaces in the value) + valuestart = std::next(valuestart); + } + auto const valueend = result.find_last_not_of("\n"); + if (valueend == std::string::npos) + result.clear(); + else + result.erase(valueend + 1); + return result; } /*}}}*/ // StringToBool - Converts a string into a boolean /*{{{*/ |