Skip to content

Commit 25bd947

Browse files
committed
Improve hex escape handling in interpolation
1 parent 2c1fd97 commit 25bd947

File tree

3 files changed

+74
-1
lines changed

3 files changed

+74
-1
lines changed

src/eval.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1185,6 +1185,7 @@ namespace Sass {
11851185
if (l->size() > 1) {
11861186
// string_to_output would fail "#{'_\a' '_\a'}";
11871187
std::string str(ll->to_string(ctx.c_options));
1188+
str = read_hex_escapes(str); // read escapes
11881189
newline_to_space(str); // replace directly
11891190
res += str; // append to result string
11901191
} else {
@@ -1205,7 +1206,9 @@ namespace Sass {
12051206
if (into_quotes && ex->is_interpolant()) {
12061207
res += evacuate_escapes(ex ? ex->to_string(ctx.c_options) : "");
12071208
} else {
1208-
res += ex ? ex->to_string(ctx.c_options) : "";
1209+
std::string str(ex ? ex->to_string(ctx.c_options) : "");
1210+
if (into_quotes) str = read_hex_escapes(str);
1211+
res += str; // append to result string
12091212
}
12101213
}
12111214

src/util.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,75 @@ namespace Sass {
232232
return quote_mark;
233233
}
234234

235+
std::string read_hex_escapes(const std::string& s)
236+
{
237+
238+
std::string result;
239+
bool skipped = false;
240+
241+
for (size_t i = 0, L = s.length(); i < L; ++i) {
242+
243+
// implement the same strange ruby sass behavior
244+
// an escape sequence can also mean a unicode char
245+
if (s[i] == '\\' && !skipped) {
246+
247+
// remember
248+
skipped = true;
249+
250+
// escape length
251+
size_t len = 1;
252+
253+
// parse as many sequence chars as possible
254+
// ToDo: Check if ruby aborts after possible max
255+
while (i + len < L && s[i + len] && isxdigit(s[i + len])) ++ len;
256+
257+
if (len > 1) {
258+
259+
// convert the extracted hex string to code point value
260+
// ToDo: Maybe we could do this without creating a substring
261+
uint32_t cp = strtol(s.substr (i + 1, len - 1).c_str(), NULL, 16);
262+
263+
if (s[i + len] == ' ') ++ len;
264+
265+
// assert invalid code points
266+
if (cp == 0) cp = 0xFFFD;
267+
// replace bell character
268+
// if (cp == '\n') cp = 32;
269+
270+
// use a very simple approach to convert via utf8 lib
271+
// maybe there is a more elegant way; maybe we shoud
272+
// convert the whole output from string to a stream!?
273+
// allocate memory for utf8 char and convert to utf8
274+
unsigned char u[5] = {0,0,0,0,0}; utf8::append(cp, u);
275+
for(size_t m = 0; m < 5 && u[m]; m++) result.push_back(u[m]);
276+
277+
// skip some more chars?
278+
i += len - 1; skipped = false;
279+
280+
}
281+
282+
else {
283+
284+
skipped = false;
285+
286+
result.push_back(s[i]);
287+
288+
}
289+
290+
}
291+
292+
else {
293+
294+
result.push_back(s[i]);
295+
296+
}
297+
298+
}
299+
300+
return result;
301+
302+
}
303+
235304
std::string unquote(const std::string& s, char* qd, bool keep_utf8_sequences, bool strict)
236305
{
237306

src/util.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ namespace Sass {
2626
std::string evacuate_escapes(const std::string& str);
2727
std::string string_to_output(const std::string& str);
2828
std::string comment_to_string(const std::string& text);
29+
std::string read_hex_escapes(const std::string& str);
2930
void newline_to_space(std::string& str);
3031

3132
std::string quote(const std::string&, char q = 0);

0 commit comments

Comments
 (0)