/src/libsass/src/util.cpp
Line | Count | Source |
1 | | #include "sass.hpp" |
2 | | #include "sass.h" |
3 | | #include "ast.hpp" |
4 | | #include "util.hpp" |
5 | | #include "util_string.hpp" |
6 | | #include "lexer.hpp" |
7 | | #include "prelexer.hpp" |
8 | | #include "constants.hpp" |
9 | | #include "utf8/checked.h" |
10 | | |
11 | | #include <cmath> |
12 | | #include <stdint.h> |
13 | | #if defined(_MSC_VER) && _MSC_VER >= 1800 && _MSC_VER < 1900 && defined(_M_X64) |
14 | | #include <mutex> |
15 | | #endif |
16 | | |
17 | | namespace Sass { |
18 | | |
19 | | double round(double val, size_t precision) |
20 | 0 | { |
21 | | // Disable FMA3-optimized implementation when compiling with VS2013 for x64 targets |
22 | | // See https://github.com/sass/node-sass/issues/1854 for details |
23 | | // FIXME: Remove this workaround when we switch to VS2015+ |
24 | | #if defined(_MSC_VER) && _MSC_VER >= 1800 && _MSC_VER < 1900 && defined(_M_X64) |
25 | | static std::once_flag flag; |
26 | | std::call_once(flag, []() { _set_FMA3_enable(0); }); |
27 | | #endif |
28 | | |
29 | | // https://github.com/sass/sass/commit/4e3e1d5684cc29073a507578fc977434ff488c93 |
30 | 0 | if (std::fmod(val, 1) - 0.5 > - std::pow(0.1, precision + 1)) return std::ceil(val); |
31 | 0 | else if (std::fmod(val, 1) - 0.5 > std::pow(0.1, precision)) return std::floor(val); |
32 | | // work around some compiler issue |
33 | | // cygwin has it not defined in std |
34 | 0 | using namespace std; |
35 | 0 | return ::round(val); |
36 | 0 | } |
37 | | |
38 | | /* Locale unspecific atof function. */ |
39 | | double sass_strtod(const char *str) |
40 | 0 | { |
41 | 0 | char separator = *(localeconv()->decimal_point); |
42 | 0 | if(separator != '.'){ |
43 | | // The current locale specifies another |
44 | | // separator. convert the separator to the |
45 | | // one understood by the locale if needed |
46 | 0 | const char *found = strchr(str, '.'); |
47 | 0 | if(found != NULL){ |
48 | | // substitution is required. perform the substitution on a copy |
49 | | // of the string. This is slower but it is thread safe. |
50 | 0 | char *copy = sass_copy_c_string(str); |
51 | 0 | *(copy + (found - str)) = separator; |
52 | 0 | double res = strtod(copy, NULL); |
53 | 0 | free(copy); |
54 | 0 | return res; |
55 | 0 | } |
56 | 0 | } |
57 | | |
58 | 0 | return strtod(str, NULL); |
59 | 0 | } |
60 | | |
61 | | // helper for safe access to c_ctx |
62 | 0 | const char* safe_str (const char* str, const char* alt) { |
63 | 0 | return str == NULL ? alt : str; |
64 | 0 | } |
65 | | |
66 | 0 | void free_string_array(char ** arr) { |
67 | 0 | if(!arr) |
68 | 0 | return; |
69 | | |
70 | 0 | char **it = arr; |
71 | 0 | while (it && (*it)) { |
72 | 0 | free(*it); |
73 | 0 | ++it; |
74 | 0 | } |
75 | |
|
76 | 0 | free(arr); |
77 | 0 | } |
78 | | |
79 | 0 | char **copy_strings(const sass::vector<sass::string>& strings, char*** array, int skip) { |
80 | 0 | int num = static_cast<int>(strings.size()) - skip; |
81 | 0 | char** arr = (char**) calloc(num + 1, sizeof(char*)); |
82 | 0 | if (arr == 0) |
83 | 0 | return *array = (char **)NULL; |
84 | | |
85 | 0 | for(int i = 0; i < num; i++) { |
86 | 0 | arr[i] = (char*) malloc(sizeof(char) * (strings[i + skip].size() + 1)); |
87 | 0 | if (arr[i] == 0) { |
88 | 0 | free_string_array(arr); |
89 | 0 | return *array = (char **)NULL; |
90 | 0 | } |
91 | 0 | std::copy(strings[i + skip].begin(), strings[i + skip].end(), arr[i]); |
92 | 0 | arr[i][strings[i + skip].size()] = '\0'; |
93 | 0 | } |
94 | | |
95 | 0 | arr[num] = 0; |
96 | 0 | return *array = arr; |
97 | 0 | } |
98 | | |
99 | | // read css string (handle multiline DELIM) |
100 | | sass::string read_css_string(const sass::string& str, bool css) |
101 | 0 | { |
102 | 0 | if (!css) return str; |
103 | 0 | sass::string out(""); |
104 | 0 | bool esc = false; |
105 | 0 | for (auto i : str) { |
106 | 0 | if (i == '\\') { |
107 | 0 | esc = ! esc; |
108 | 0 | } else if (esc && i == '\r') { |
109 | 0 | continue; |
110 | 0 | } else if (esc && i == '\n') { |
111 | 0 | out.resize (out.size () - 1); |
112 | 0 | esc = false; |
113 | 0 | continue; |
114 | 0 | } else { |
115 | 0 | esc = false; |
116 | 0 | } |
117 | 0 | out.push_back(i); |
118 | 0 | } |
119 | | // happens when parsing does not correctly skip |
120 | | // over escaped sequences for ie. interpolations |
121 | | // one example: foo\#{interpolate} |
122 | | // if (esc) out += '\\'; |
123 | 0 | return out; |
124 | 0 | } |
125 | | |
126 | | // double escape all escape sequences |
127 | | // keep unescaped quotes and backslashes |
128 | | sass::string evacuate_escapes(const sass::string& str) |
129 | 0 | { |
130 | 0 | sass::string out(""); |
131 | 0 | bool esc = false; |
132 | 0 | for (auto i : str) { |
133 | 0 | if (i == '\\' && !esc) { |
134 | 0 | out += '\\'; |
135 | 0 | out += '\\'; |
136 | 0 | esc = true; |
137 | 0 | } else if (esc && i == '"') { |
138 | 0 | out += '\\'; |
139 | 0 | out += i; |
140 | 0 | esc = false; |
141 | 0 | } else if (esc && i == '\'') { |
142 | 0 | out += '\\'; |
143 | 0 | out += i; |
144 | 0 | esc = false; |
145 | 0 | } else if (esc && i == '\\') { |
146 | 0 | out += '\\'; |
147 | 0 | out += i; |
148 | 0 | esc = false; |
149 | 0 | } else { |
150 | 0 | esc = false; |
151 | 0 | out += i; |
152 | 0 | } |
153 | 0 | } |
154 | | // happens when parsing does not correctly skip |
155 | | // over escaped sequences for ie. interpolations |
156 | | // one example: foo\#{interpolate} |
157 | | // if (esc) out += '\\'; |
158 | 0 | return out; |
159 | 0 | } |
160 | | |
161 | | // bell characters are replaced with spaces |
162 | | void newline_to_space(sass::string& str) |
163 | 0 | { |
164 | 0 | std::replace(str.begin(), str.end(), '\n', ' '); |
165 | 0 | } |
166 | | |
167 | | // 1. Removes whitespace after newlines. |
168 | | // 2. Replaces newlines with spaces. |
169 | | // |
170 | | // This method only considers LF and CRLF as newlines. |
171 | | sass::string string_to_output(const sass::string& str) |
172 | 0 | { |
173 | 0 | sass::string result; |
174 | 0 | result.reserve(str.size()); |
175 | 0 | std::size_t pos = 0; |
176 | 0 | while (true) { |
177 | 0 | const std::size_t newline = str.find_first_of("\n\r", pos); |
178 | 0 | if (newline == sass::string::npos) break; |
179 | 0 | result.append(str, pos, newline - pos); |
180 | 0 | if (str[newline] == '\r') { |
181 | 0 | if (str[newline + 1] == '\n') { |
182 | 0 | pos = newline + 2; |
183 | 0 | } else { |
184 | | // CR without LF: append as-is and continue. |
185 | 0 | result += '\r'; |
186 | 0 | pos = newline + 1; |
187 | 0 | continue; |
188 | 0 | } |
189 | 0 | } else { |
190 | 0 | pos = newline + 1; |
191 | 0 | } |
192 | 0 | result += ' '; |
193 | 0 | const std::size_t non_space = str.find_first_not_of(" \f\n\r\t\v", pos); |
194 | 0 | if (non_space != sass::string::npos) { |
195 | 0 | pos = non_space; |
196 | 0 | } |
197 | 0 | } |
198 | 0 | result.append(str, pos, sass::string::npos); |
199 | 0 | return result; |
200 | 0 | } |
201 | | |
202 | | sass::string escape_string(const sass::string& str) |
203 | 0 | { |
204 | 0 | sass::string out; |
205 | 0 | out.reserve(str.size()); |
206 | 0 | for (char c : str) { |
207 | 0 | switch (c) { |
208 | 0 | case '\n': |
209 | 0 | out.append("\\n"); |
210 | 0 | break; |
211 | 0 | case '\r': |
212 | 0 | out.append("\\r"); |
213 | 0 | break; |
214 | 0 | case '\f': |
215 | 0 | out.append("\\f"); |
216 | 0 | break; |
217 | 0 | default: |
218 | 0 | out += c; |
219 | 0 | } |
220 | 0 | } |
221 | 0 | return out; |
222 | 0 | } |
223 | | |
224 | | sass::string comment_to_compact_string(const sass::string& text) |
225 | 0 | { |
226 | 0 | sass::string str = ""; |
227 | 0 | size_t has = 0; |
228 | 0 | char prev = 0; |
229 | 0 | bool clean = false; |
230 | 0 | for (auto i : text) { |
231 | 0 | if (clean) { |
232 | 0 | if (i == '\n') { has = 0; } |
233 | 0 | else if (i == '\t') { ++ has; } |
234 | 0 | else if (i == ' ') { ++ has; } |
235 | 0 | else if (i == '*') {} |
236 | 0 | else { |
237 | 0 | clean = false; |
238 | 0 | str += ' '; |
239 | 0 | if (prev == '*' && i == '/') str += "*/"; |
240 | 0 | else str += i; |
241 | 0 | } |
242 | 0 | } else if (i == '\n') { |
243 | 0 | clean = true; |
244 | 0 | } else { |
245 | 0 | str += i; |
246 | 0 | } |
247 | 0 | prev = i; |
248 | 0 | } |
249 | 0 | if (has) return str; |
250 | 0 | else return text; |
251 | 0 | } |
252 | | |
253 | | // find best quote_mark by detecting if the string contains any single |
254 | | // or double quotes. When a single quote is found, we not we want a double |
255 | | // quote as quote_mark. Otherwise we check if the string cotains any double |
256 | | // quotes, which will trigger the use of single quotes as best quote_mark. |
257 | | char detect_best_quotemark(const char* s, char qm) |
258 | 0 | { |
259 | | // ensure valid fallback quote_mark |
260 | 0 | char quote_mark = qm && qm != '*' ? qm : '"'; |
261 | 0 | while (*s) { |
262 | | // force double quotes as soon |
263 | | // as one single quote is found |
264 | 0 | if (*s == '\'') { return '"'; } |
265 | | // a single does not force quote_mark |
266 | | // maybe we see a double quote later |
267 | 0 | else if (*s == '"') { quote_mark = '\''; } |
268 | 0 | ++ s; |
269 | 0 | } |
270 | 0 | return quote_mark; |
271 | 0 | } |
272 | | |
273 | | sass::string read_hex_escapes(const sass::string& s) |
274 | 0 | { |
275 | |
|
276 | 0 | sass::string result; |
277 | 0 | bool skipped = false; |
278 | |
|
279 | 0 | for (size_t i = 0, L = s.length(); i < L; ++i) { |
280 | | |
281 | | // implement the same strange ruby sass behavior |
282 | | // an escape sequence can also mean a unicode char |
283 | 0 | if (s[i] == '\\' && !skipped) { |
284 | | |
285 | | // remember |
286 | 0 | skipped = true; |
287 | | |
288 | | // escape length |
289 | 0 | size_t len = 1; |
290 | | |
291 | | // parse as many sequence chars as possible |
292 | | // ToDo: Check if ruby aborts after possible max |
293 | 0 | while (i + len < L && s[i + len] && Util::ascii_isxdigit(static_cast<unsigned char>(s[i + len]))) ++ len; |
294 | |
|
295 | 0 | if (len > 1) { |
296 | | |
297 | | // convert the extracted hex string to code point value |
298 | | // ToDo: Maybe we could do this without creating a substring |
299 | 0 | uint32_t cp = strtol(s.substr (i + 1, len - 1).c_str(), NULL, 16); |
300 | |
|
301 | 0 | if (s[i + len] == ' ') ++ len; |
302 | | |
303 | | // assert invalid code points |
304 | 0 | if (cp == 0) cp = 0xFFFD; |
305 | | // replace bell character |
306 | | // if (cp == '\n') cp = 32; |
307 | | |
308 | | // use a very simple approach to convert via utf8 lib |
309 | | // maybe there is a more elegant way; maybe we should |
310 | | // convert the whole output from string to a stream!? |
311 | | // allocate memory for utf8 char and convert to utf8 |
312 | 0 | unsigned char u[5] = {0,0,0,0,0}; utf8::append(cp, u); |
313 | 0 | for(size_t m = 0; m < 5 && u[m]; m++) result.push_back(u[m]); |
314 | | |
315 | | // skip some more chars? |
316 | 0 | i += len - 1; skipped = false; |
317 | |
|
318 | 0 | } |
319 | | |
320 | 0 | else { |
321 | |
|
322 | 0 | skipped = false; |
323 | |
|
324 | 0 | result.push_back(s[i]); |
325 | |
|
326 | 0 | } |
327 | |
|
328 | 0 | } |
329 | | |
330 | 0 | else { |
331 | |
|
332 | 0 | result.push_back(s[i]); |
333 | |
|
334 | 0 | } |
335 | |
|
336 | 0 | } |
337 | |
|
338 | 0 | return result; |
339 | |
|
340 | 0 | } |
341 | | |
342 | | sass::string unquote(const sass::string& s, char* qd, bool keep_utf8_sequences, bool strict) |
343 | 0 | { |
344 | | |
345 | | // not enough room for quotes |
346 | | // no possibility to unquote |
347 | 0 | if (s.length() < 2) return s; |
348 | | |
349 | 0 | char q; |
350 | 0 | bool skipped = false; |
351 | | |
352 | | // this is no guarantee that the unquoting will work |
353 | | // what about whitespace before/after the quote_mark? |
354 | 0 | if (*s.begin() == '"' && *s.rbegin() == '"') q = '"'; |
355 | 0 | else if (*s.begin() == '\'' && *s.rbegin() == '\'') q = '\''; |
356 | 0 | else return s; |
357 | | |
358 | 0 | sass::string unq; |
359 | 0 | unq.reserve(s.length()-2); |
360 | |
|
361 | 0 | for (size_t i = 1, L = s.length() - 1; i < L; ++i) { |
362 | | |
363 | | // implement the same strange ruby sass behavior |
364 | | // an escape sequence can also mean a unicode char |
365 | 0 | if (s[i] == '\\' && !skipped) { |
366 | | // remember |
367 | 0 | skipped = true; |
368 | | |
369 | | // skip it |
370 | | // ++ i; |
371 | | |
372 | | // if (i == L) break; |
373 | | |
374 | | // escape length |
375 | 0 | size_t len = 1; |
376 | | |
377 | | // parse as many sequence chars as possible |
378 | | // ToDo: Check if ruby aborts after possible max |
379 | 0 | while (i + len < L && s[i + len] && Util::ascii_isxdigit(static_cast<unsigned char>(s[i + len]))) ++ len; |
380 | | |
381 | | // hex string? |
382 | 0 | if (keep_utf8_sequences) { |
383 | 0 | unq.push_back(s[i]); |
384 | 0 | } else if (len > 1) { |
385 | | |
386 | | // convert the extracted hex string to code point value |
387 | | // ToDo: Maybe we could do this without creating a substring |
388 | 0 | uint32_t cp = strtol(s.substr (i + 1, len - 1).c_str(), NULL, 16); |
389 | |
|
390 | 0 | if (s[i + len] == ' ') ++ len; |
391 | | |
392 | | // assert invalid code points |
393 | 0 | if (cp == 0) cp = 0xFFFD; |
394 | | // replace bell character |
395 | | // if (cp == '\n') cp = 32; |
396 | | |
397 | | // use a very simple approach to convert via utf8 lib |
398 | | // maybe there is a more elegant way; maybe we should |
399 | | // convert the whole output from string to a stream!? |
400 | | // allocate memory for utf8 char and convert to utf8 |
401 | 0 | unsigned char u[5] = {0,0,0,0,0}; utf8::append(cp, u); |
402 | 0 | for(size_t m = 0; m < 5 && u[m]; m++) unq.push_back(u[m]); |
403 | | |
404 | | // skip some more chars? |
405 | 0 | i += len - 1; skipped = false; |
406 | |
|
407 | 0 | } |
408 | | |
409 | |
|
410 | 0 | } |
411 | | // check for unexpected delimiter |
412 | | // be strict and throw error back |
413 | | // else if (!skipped && q == s[i]) { |
414 | | // // don't be that strict |
415 | | // return s; |
416 | | // // this basically always means an internal error and not users fault |
417 | | // error("Unescaped delimiter in string to unquote found. [" + s + "]", SourceSpan("[UNQUOTE]")); |
418 | | // } |
419 | 0 | else { |
420 | 0 | if (strict && !skipped) { |
421 | 0 | if (s[i] == q) return s; |
422 | 0 | } |
423 | 0 | skipped = false; |
424 | 0 | unq.push_back(s[i]); |
425 | 0 | } |
426 | |
|
427 | 0 | } |
428 | 0 | if (skipped) { return s; } |
429 | 0 | if (qd) *qd = q; |
430 | 0 | return unq; |
431 | |
|
432 | 0 | } |
433 | | |
434 | | sass::string quote(const sass::string& s, char q) |
435 | 0 | { |
436 | | |
437 | | // autodetect with fallback to given quote |
438 | 0 | q = detect_best_quotemark(s.c_str(), q); |
439 | | |
440 | | // return an empty quoted string |
441 | 0 | if (s.empty()) return sass::string(2, q ? q : '"'); |
442 | | |
443 | 0 | sass::string quoted; |
444 | 0 | quoted.reserve(s.length()+2); |
445 | 0 | quoted.push_back(q); |
446 | |
|
447 | 0 | const char* it = s.c_str(); |
448 | 0 | const char* end = it + strlen(it) + 1; |
449 | 0 | while (*it && it < end) { |
450 | 0 | const char* now = it; |
451 | |
|
452 | 0 | if (*it == q) { |
453 | 0 | quoted.push_back('\\'); |
454 | 0 | } else if (*it == '\\') { |
455 | 0 | quoted.push_back('\\'); |
456 | 0 | } |
457 | |
|
458 | 0 | int cp = utf8::next(it, end); |
459 | | |
460 | | // in case of \r, check if the next in sequence |
461 | | // is \n and then advance the iterator and skip \r |
462 | 0 | if (cp == '\r' && it < end && utf8::peek_next(it, end) == '\n') { |
463 | 0 | cp = utf8::next(it, end); |
464 | 0 | } |
465 | |
|
466 | 0 | if (cp == '\n') { |
467 | 0 | quoted.push_back('\\'); |
468 | 0 | quoted.push_back('a'); |
469 | | // we hope we can remove this flag once we figure out |
470 | | // why ruby sass has these different output behaviors |
471 | | // gsub(/\n(?![a-fA-F0-9\s])/, "\\a").gsub("\n", "\\a ") |
472 | 0 | using namespace Prelexer; |
473 | 0 | if (alternatives < |
474 | 0 | Prelexer::char_range<'a', 'f'>, |
475 | 0 | Prelexer::char_range<'A', 'F'>, |
476 | 0 | Prelexer::char_range<'0', '9'>, |
477 | 0 | space |
478 | 0 | >(it) != NULL) { |
479 | 0 | quoted.push_back(' '); |
480 | 0 | } |
481 | 0 | } else if (cp < 127) { |
482 | 0 | quoted.push_back((char) cp); |
483 | 0 | } else { |
484 | 0 | while (now < it) { |
485 | 0 | quoted.push_back(*now); |
486 | 0 | ++ now; |
487 | 0 | } |
488 | 0 | } |
489 | 0 | } |
490 | |
|
491 | 0 | quoted.push_back(q); |
492 | 0 | return quoted; |
493 | 0 | } |
494 | | |
495 | | bool is_hex_doublet(double n) |
496 | 0 | { |
497 | 0 | return n == 0x00 || n == 0x11 || n == 0x22 || n == 0x33 || |
498 | 0 | n == 0x44 || n == 0x55 || n == 0x66 || n == 0x77 || |
499 | 0 | n == 0x88 || n == 0x99 || n == 0xAA || n == 0xBB || |
500 | 0 | n == 0xCC || n == 0xDD || n == 0xEE || n == 0xFF ; |
501 | 0 | } |
502 | | |
503 | | bool is_color_doublet(double r, double g, double b) |
504 | 0 | { |
505 | 0 | return is_hex_doublet(r) && is_hex_doublet(g) && is_hex_doublet(b); |
506 | 0 | } |
507 | | |
508 | | bool peek_linefeed(const char* start) |
509 | 0 | { |
510 | 0 | using namespace Prelexer; |
511 | 0 | using namespace Constants; |
512 | 0 | return sequence < |
513 | 0 | zero_plus < |
514 | 0 | alternatives < |
515 | 0 | exactly <' '>, |
516 | 0 | exactly <'\t'>, |
517 | 0 | line_comment, |
518 | 0 | block_comment, |
519 | 0 | delimited_by < |
520 | 0 | slash_star, |
521 | 0 | star_slash, |
522 | 0 | false |
523 | 0 | > |
524 | 0 | > |
525 | 0 | >, |
526 | 0 | re_linebreak |
527 | 0 | >(start) != 0; |
528 | 0 | } |
529 | | |
530 | | namespace Util { |
531 | | |
532 | 0 | bool isPrintable(StyleRule* r, Sass_Output_Style style) { |
533 | 0 | if (r == NULL) { |
534 | 0 | return false; |
535 | 0 | } |
536 | | |
537 | 0 | Block_Obj b = r->block(); |
538 | |
|
539 | 0 | SelectorList* sl = r->selector(); |
540 | 0 | bool hasSelectors = sl ? sl->length() > 0 : false; |
541 | |
|
542 | 0 | if (!hasSelectors) { |
543 | 0 | return false; |
544 | 0 | } |
545 | | |
546 | 0 | bool hasDeclarations = false; |
547 | 0 | bool hasPrintableChildBlocks = false; |
548 | 0 | for (size_t i = 0, L = b->length(); i < L; ++i) { |
549 | 0 | Statement_Obj stm = b->at(i); |
550 | 0 | if (Cast<AtRule>(stm)) { |
551 | 0 | return true; |
552 | 0 | } else if (Declaration* d = Cast<Declaration>(stm)) { |
553 | 0 | return isPrintable(d, style); |
554 | 0 | } else if (ParentStatement* p = Cast<ParentStatement>(stm)) { |
555 | 0 | Block_Obj pChildBlock = p->block(); |
556 | 0 | if (isPrintable(pChildBlock, style)) { |
557 | 0 | hasPrintableChildBlocks = true; |
558 | 0 | } |
559 | 0 | } else if (Comment* c = Cast<Comment>(stm)) { |
560 | | // keep for uncompressed |
561 | 0 | if (style != COMPRESSED) { |
562 | 0 | hasDeclarations = true; |
563 | 0 | } |
564 | | // output style compressed |
565 | 0 | if (c->is_important()) { |
566 | 0 | hasDeclarations = c->is_important(); |
567 | 0 | } |
568 | 0 | } else { |
569 | 0 | hasDeclarations = true; |
570 | 0 | } |
571 | | |
572 | 0 | if (hasDeclarations || hasPrintableChildBlocks) { |
573 | 0 | return true; |
574 | 0 | } |
575 | 0 | } |
576 | | |
577 | 0 | return false; |
578 | 0 | } |
579 | | |
580 | | bool isPrintable(String_Constant* s, Sass_Output_Style style) |
581 | 0 | { |
582 | 0 | return ! s->value().empty(); |
583 | 0 | } |
584 | | |
585 | | bool isPrintable(String_Quoted* s, Sass_Output_Style style) |
586 | 0 | { |
587 | 0 | return true; |
588 | 0 | } |
589 | | |
590 | | bool isPrintable(Declaration* d, Sass_Output_Style style) |
591 | 0 | { |
592 | 0 | ExpressionObj val = d->value(); |
593 | 0 | if (String_Quoted_Obj sq = Cast<String_Quoted>(val)) return isPrintable(sq.ptr(), style); |
594 | 0 | if (String_Constant_Obj sc = Cast<String_Constant>(val)) return isPrintable(sc.ptr(), style); |
595 | 0 | return true; |
596 | 0 | } |
597 | | |
598 | 0 | bool isPrintable(SupportsRule* f, Sass_Output_Style style) { |
599 | 0 | if (f == NULL) { |
600 | 0 | return false; |
601 | 0 | } |
602 | | |
603 | 0 | Block_Obj b = f->block(); |
604 | |
|
605 | 0 | bool hasDeclarations = false; |
606 | 0 | bool hasPrintableChildBlocks = false; |
607 | 0 | for (size_t i = 0, L = b->length(); i < L; ++i) { |
608 | 0 | Statement_Obj stm = b->at(i); |
609 | 0 | if (Cast<Declaration>(stm) || Cast<AtRule>(stm)) { |
610 | 0 | hasDeclarations = true; |
611 | 0 | } |
612 | 0 | else if (ParentStatement* b = Cast<ParentStatement>(stm)) { |
613 | 0 | Block_Obj pChildBlock = b->block(); |
614 | 0 | if (!b->is_invisible()) { |
615 | 0 | if (isPrintable(pChildBlock, style)) { |
616 | 0 | hasPrintableChildBlocks = true; |
617 | 0 | } |
618 | 0 | } |
619 | 0 | } |
620 | |
|
621 | 0 | if (hasDeclarations || hasPrintableChildBlocks) { |
622 | 0 | return true; |
623 | 0 | } |
624 | 0 | } |
625 | | |
626 | 0 | return false; |
627 | 0 | } |
628 | | |
629 | | bool isPrintable(CssMediaRule* m, Sass_Output_Style style) |
630 | 0 | { |
631 | 0 | if (m == nullptr) return false; |
632 | 0 | Block_Obj b = m->block(); |
633 | 0 | if (b == nullptr) return false; |
634 | 0 | if (m->empty()) return false; |
635 | 0 | for (size_t i = 0, L = b->length(); i < L; ++i) { |
636 | 0 | Statement_Obj stm = b->at(i); |
637 | 0 | if (Cast<AtRule>(stm)) return true; |
638 | 0 | else if (Cast<Declaration>(stm)) return true; |
639 | 0 | else if (Comment* c = Cast<Comment>(stm)) { |
640 | 0 | if (isPrintable(c, style)) { |
641 | 0 | return true; |
642 | 0 | } |
643 | 0 | } |
644 | 0 | else if (StyleRule* r = Cast<StyleRule>(stm)) { |
645 | 0 | if (isPrintable(r, style)) { |
646 | 0 | return true; |
647 | 0 | } |
648 | 0 | } |
649 | 0 | else if (SupportsRule* f = Cast<SupportsRule>(stm)) { |
650 | 0 | if (isPrintable(f, style)) { |
651 | 0 | return true; |
652 | 0 | } |
653 | 0 | } |
654 | 0 | else if (CssMediaRule* mb = Cast<CssMediaRule>(stm)) { |
655 | 0 | if (isPrintable(mb, style)) { |
656 | 0 | return true; |
657 | 0 | } |
658 | 0 | } |
659 | 0 | else if (ParentStatement* b = Cast<ParentStatement>(stm)) { |
660 | 0 | if (isPrintable(b->block(), style)) { |
661 | 0 | return true; |
662 | 0 | } |
663 | 0 | } |
664 | 0 | } |
665 | 0 | return false; |
666 | 0 | } |
667 | | |
668 | | bool isPrintable(Comment* c, Sass_Output_Style style) |
669 | 0 | { |
670 | | // keep for uncompressed |
671 | 0 | if (style != COMPRESSED) { |
672 | 0 | return true; |
673 | 0 | } |
674 | | // output style compressed |
675 | 0 | if (c->is_important()) { |
676 | 0 | return true; |
677 | 0 | } |
678 | | // not printable |
679 | 0 | return false; |
680 | 0 | }; |
681 | | |
682 | 0 | bool isPrintable(Block_Obj b, Sass_Output_Style style) { |
683 | 0 | if (!b) { |
684 | 0 | return false; |
685 | 0 | } |
686 | | |
687 | 0 | for (size_t i = 0, L = b->length(); i < L; ++i) { |
688 | 0 | Statement_Obj stm = b->at(i); |
689 | 0 | if (Cast<Declaration>(stm) || Cast<AtRule>(stm)) { |
690 | 0 | return true; |
691 | 0 | } |
692 | 0 | else if (Comment* c = Cast<Comment>(stm)) { |
693 | 0 | if (isPrintable(c, style)) { |
694 | 0 | return true; |
695 | 0 | } |
696 | 0 | } |
697 | 0 | else if (StyleRule* r = Cast<StyleRule>(stm)) { |
698 | 0 | if (isPrintable(r, style)) { |
699 | 0 | return true; |
700 | 0 | } |
701 | 0 | } |
702 | 0 | else if (SupportsRule* f = Cast<SupportsRule>(stm)) { |
703 | 0 | if (isPrintable(f, style)) { |
704 | 0 | return true; |
705 | 0 | } |
706 | 0 | } |
707 | 0 | else if (CssMediaRule * m = Cast<CssMediaRule>(stm)) { |
708 | 0 | if (isPrintable(m, style)) { |
709 | 0 | return true; |
710 | 0 | } |
711 | 0 | } |
712 | 0 | else if (ParentStatement* b = Cast<ParentStatement>(stm)) { |
713 | 0 | if (isPrintable(b->block(), style)) { |
714 | 0 | return true; |
715 | 0 | } |
716 | 0 | } |
717 | 0 | } |
718 | | |
719 | 0 | return false; |
720 | 0 | } |
721 | | |
722 | | } |
723 | | } |