diff --git a/ast.hpp b/ast.hpp index 0eea9a9451..10c3159724 100644 --- a/ast.hpp +++ b/ast.hpp @@ -1439,22 +1439,23 @@ namespace Sass { // Flat strings -- the lowest level of raw textual data. //////////////////////////////////////////////////////// class String_Constant : public String { + ADD_PROPERTY(bool, from_schema); ADD_PROPERTY(char, quote_mark); ADD_PROPERTY(string, value); protected: size_t hash_; public: String_Constant(ParserState pstate, string val) - : String(pstate), quote_mark_(0), value_(read_css_string(val)), hash_(0) + : String(pstate), from_schema_(false), quote_mark_(0), value_(read_css_string(val)), hash_(0) { } String_Constant(ParserState pstate, const char* beg) - : String(pstate), quote_mark_(0), value_(read_css_string(string(beg))), hash_(0) + : String(pstate), from_schema_(false), quote_mark_(0), value_(read_css_string(string(beg))), hash_(0) { } String_Constant(ParserState pstate, const char* beg, const char* end) - : String(pstate), quote_mark_(0), value_(read_css_string(string(beg, end-beg))), hash_(0) + : String(pstate), from_schema_(false), quote_mark_(0), value_(read_css_string(string(beg, end-beg))), hash_(0) { } String_Constant(ParserState pstate, const Token& tok) - : String(pstate), quote_mark_(0), value_(read_css_string(string(tok.begin, tok.end))), hash_(0) + : String(pstate), from_schema_(false), quote_mark_(0), value_(read_css_string(string(tok.begin, tok.end))), hash_(0) { } string type() { return "string"; } static string type_name() { return "string"; } diff --git a/eval.cpp b/eval.cpp index 2bc78af573..d99eb2805b 100644 --- a/eval.cpp +++ b/eval.cpp @@ -888,6 +888,7 @@ namespace Sass { if (!str->quote_mark()) { str->value(string_unescape(str->value())); } else if (str->quote_mark()) { + str->from_schema(true); str->quote_mark('*'); } return str; diff --git a/output.cpp b/output.cpp index 66729b4305..1c2b8b8403 100644 --- a/output.cpp +++ b/output.cpp @@ -378,7 +378,12 @@ namespace Sass { void Output::operator()(String_Quoted* s) { if (s->quote_mark()) { - append_token(quote(s->value(), s->quote_mark()), s); + if (s->from_schema()) { + append_token(quote(unevacuate_escapes(s->value()), s->quote_mark()), s); + } else { + append_token(quote(s->value(), s->quote_mark()), s); + } + } else if (!in_comment) { append_token(string_to_output(s->value()), s); } else { diff --git a/util.cpp b/util.cpp index d938d0f4e9..410197c325 100644 --- a/util.cpp +++ b/util.cpp @@ -159,6 +159,61 @@ namespace Sass { return out; } + // unescape all double escape sequences + // needed after all interpolations are done + string unevacuate_escapes(const string& s) + { + string out(""); + bool esc = false; + + for (size_t i = 0, L = s.length(); i < L; ++i) { + + // implement the same strange ruby sass behavior + // an escape sequence can also mean a unicode char + if (s[i] == '\\' && !esc) { + // remember + esc = true; + + // escape length + size_t len = 1; + + // parse as many sequence chars as possible + // ToDo: Check if ruby aborts after possible max + while (i + len < L && s[i + len] && isxdigit(s[i + len])) ++ len; + + // hex string? + if (len > 1) { + + // convert the extracted hex string to code point value + // ToDo: Maybe we could do this without creating a substring + uint32_t cp = strtol(s.substr (i + 1, len - 1).c_str(), nullptr, 16); + + // assert invalid code points + if (cp >= 16) { + + // use a very simple approach to convert via utf8 lib + // maybe there is a more elegant way; maybe we shoud + // convert the whole output from string to a stream!? + // allocate memory for utf8 char and convert to utf8 + unsigned char u[5] = {0,0,0,0,0}; utf8::append(cp, u); + for(size_t m = 0; u[m] && m < 5; m++) out.push_back(u[m]); + + // skip some more chars? + i += len - 1; esc = false; + + } + + } + + } else { + if (esc) out += '\\'; + esc = false; + out += s[i]; + } + } + return out; + } + // bell character is replaces with space string string_to_output(const string& str) { diff --git a/util.hpp b/util.hpp index 576d0f40c7..2b8aad45ac 100644 --- a/util.hpp +++ b/util.hpp @@ -15,6 +15,7 @@ namespace Sass { string read_css_string(const string& str); string evacuate_quotes(const string& str); string evacuate_escapes(const string& str); + string unevacuate_escapes(const string& str); string string_to_output(const string& str); string comment_to_string(const string& text); string normalize_wspace(const string& str);