Skip to content

Commit

Permalink
Simple_wml: support embedded quotes in strings
Browse files Browse the repository at this point in the history
Fixes #3567.
  • Loading branch information
jyrkive committed Sep 19, 2018
1 parent 821f953 commit 2d6ebe7
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 5 deletions.
67 changes: 62 additions & 5 deletions src/server/simple_wml.cpp
Expand Up @@ -191,6 +191,15 @@ char* string_span::duplicate() const
return buf;
}

int string_span::count(char ch) const
{
int count = 0;
for(const char* c = begin(); c != end(); ++c) {
count += *c == ch ? 1 : 0;
}
return count;
}

error::error(const char* msg)
: game::error(msg)
{
Expand Down Expand Up @@ -276,6 +285,7 @@ node::node(document& doc, node* parent, const char** str, int depth) :
break;
default: {
const char* end = strchr(s, '=');
bool needs_escaping = false;
if(end == nullptr) {
ERR_SWML << "attribute: " << s << std::endl;
throw error("did not find '=' after attribute");
Expand Down Expand Up @@ -309,6 +319,7 @@ node::node(document& doc, node* parent, const char** str, int depth) :
#pragma warning (pop)
#endif
++end;
needs_escaping = true;
}
if(end == nullptr)
throw error("did not find end of attribute");
Expand Down Expand Up @@ -351,7 +362,13 @@ node::node(document& doc, node* parent, const char** str, int depth) :

s = end + 1;

attr_.emplace_back(name, value);
if(!needs_escaping) {
attr_.emplace_back(name, value);
} else {
char* value_buf = unescape_value(value);
doc_->take_ownership_of_buffer(value_buf);
attr_.emplace_back(name, string_span(value_buf));
}
}
}
}
Expand Down Expand Up @@ -659,6 +676,42 @@ int node::get_children(const string_span& name)
return children_.size() - 1;
}

char* node::unescape_value(const string_span& value)
{
std::string buffer;
buffer.reserve(value.size());
const char* begin = value.begin();
const char* end;
// Find the next duplicated double quote.
while((end = strstr(begin, "\"\"")) != nullptr) {
// Copy characters up to and including the first double quote, but not the second.
buffer.append(begin, end - begin + 1);
begin = end + 2;
}
// Copy the rest.
buffer.append(begin, value.end() - begin);

char* final_buffer = new char[buffer.size() + 1];
buffer.copy(final_buffer, std::string::npos);
final_buffer[buffer.size()] = '\0';
return final_buffer;
}

void node::escape_value(const string_span& value, char** buffer)
{
char* buf = *buffer;
for(const char* c = value.begin(); c != value.end(); ++c)
{
if(*c != '"') {
*buf++ = *c;
} else {
*buf++ = '"';
*buf++ = '"';
}
}
*buffer = buf;
}

node::child_map::const_iterator node::find_in_map(const child_map& m, const string_span& attr)
{
child_map::const_iterator i = m.begin();
Expand Down Expand Up @@ -702,7 +755,7 @@ int node::output_size() const

int res = 0;
for(attribute_list::const_iterator i = attr_.begin(); i != attr_.end(); ++i) {
res += i->key.size() + i->value.size() + 4;
res += i->key.size() + i->value.size() + i->value.count('"') + 4;
}

size_t count_children = 0;
Expand Down Expand Up @@ -758,9 +811,13 @@ void node::output(char*& buf, CACHE_STATUS cache_status)
buf += i->key.size();
*buf++ = '=';
*buf++ = '"';
memcpy(buf, i->value.begin(), i->value.size());
i->value = string_span(buf, i->value.size());
buf += i->value.size();
if(memchr(i->value.begin(), '"', i->value.size()) == nullptr) {
memcpy(buf, i->value.begin(), i->value.size());
i->value = string_span(buf, i->value.size());
buf += i->value.size();
} else {
escape_value(i->value, &buf);
}
*buf++ = '"';
*buf++ = '\n';
}
Expand Down
6 changes: 6 additions & 0 deletions src/server/simple_wml.hpp
Expand Up @@ -100,6 +100,9 @@ class string_span
//returns a duplicate of the string span in a new[] allocated buffer
char* duplicate() const;

//counts how many copies of the given character the string span has
int count(char ch) const;

private:
const char* str_;
unsigned int size_;
Expand Down Expand Up @@ -183,6 +186,9 @@ class node
int get_children(const string_span& name);
int get_children(const char* name);

static char* unescape_value(const string_span& value);
static void escape_value(const string_span& value, char** buffer);

void set_dirty();
void shift_buffers(ptrdiff_t offset);

Expand Down

0 comments on commit 2d6ebe7

Please sign in to comment.