|
Packit |
bfcc33 |
#include "sass.hpp"
|
|
Packit |
bfcc33 |
#include "sass.h"
|
|
Packit |
bfcc33 |
#include "ast.hpp"
|
|
Packit |
bfcc33 |
#include "util.hpp"
|
|
Packit |
bfcc33 |
#include "lexer.hpp"
|
|
Packit |
bfcc33 |
#include "prelexer.hpp"
|
|
Packit |
bfcc33 |
#include "constants.hpp"
|
|
Packit |
bfcc33 |
#include "utf8/checked.h"
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
#include <cmath>
|
|
Packit |
bfcc33 |
#include <stdint.h>
|
|
Packit |
bfcc33 |
#if defined(_MSC_VER) && _MSC_VER >= 1800 && _MSC_VER < 1900 && defined(_M_X64)
|
|
Packit |
bfcc33 |
#include <mutex>
|
|
Packit |
bfcc33 |
#endif
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
namespace Sass {
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
double round(double val, size_t precision)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
// Disable FMA3-optimized implementation when compiling with VS2013 for x64 targets
|
|
Packit |
bfcc33 |
// See https://github.com/sass/node-sass/issues/1854 for details
|
|
Packit |
bfcc33 |
// FIXME: Remove this workaround when we switch to VS2015+
|
|
Packit |
bfcc33 |
#if defined(_MSC_VER) && _MSC_VER >= 1800 && _MSC_VER < 1900 && defined(_M_X64)
|
|
Packit |
bfcc33 |
static std::once_flag flag;
|
|
Packit |
bfcc33 |
std::call_once(flag, []() { _set_FMA3_enable(0); });
|
|
Packit |
bfcc33 |
#endif
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// https://github.com/sass/sass/commit/4e3e1d5684cc29073a507578fc977434ff488c93
|
|
Packit |
bfcc33 |
if (fmod(val, 1) - 0.5 > - std::pow(0.1, precision + 1)) return std::ceil(val);
|
|
Packit |
bfcc33 |
else if (fmod(val, 1) - 0.5 > std::pow(0.1, precision)) return std::floor(val);
|
|
Packit |
bfcc33 |
// work around some compiler issue
|
|
Packit |
bfcc33 |
// cygwin has it not defined in std
|
|
Packit |
bfcc33 |
using namespace std;
|
|
Packit |
bfcc33 |
return ::round(val);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
/* Locale unspecific atof function. */
|
|
Packit |
bfcc33 |
double sass_atof(const char *str)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
char separator = *(localeconv()->decimal_point);
|
|
Packit |
bfcc33 |
if(separator != '.'){
|
|
Packit |
bfcc33 |
// The current locale specifies another
|
|
Packit |
bfcc33 |
// separator. convert the separator to the
|
|
Packit |
bfcc33 |
// one understood by the locale if needed
|
|
Packit |
bfcc33 |
const char *found = strchr(str, '.');
|
|
Packit |
bfcc33 |
if(found != NULL){
|
|
Packit |
bfcc33 |
// substitution is required. perform the substitution on a copy
|
|
Packit |
bfcc33 |
// of the string. This is slower but it is thread safe.
|
|
Packit |
bfcc33 |
char *copy = sass_copy_c_string(str);
|
|
Packit |
bfcc33 |
*(copy + (found - str)) = separator;
|
|
Packit |
bfcc33 |
double res = atof(copy);
|
|
Packit |
bfcc33 |
free(copy);
|
|
Packit |
bfcc33 |
return res;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
return atof(str);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// helper for safe access to c_ctx
|
|
Packit |
bfcc33 |
const char* safe_str (const char* str, const char* alt) {
|
|
Packit |
bfcc33 |
return str == NULL ? alt : str;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
void free_string_array(char ** arr) {
|
|
Packit |
bfcc33 |
if(!arr)
|
|
Packit |
bfcc33 |
return;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
char **it = arr;
|
|
Packit |
bfcc33 |
while (it && (*it)) {
|
|
Packit |
bfcc33 |
free(*it);
|
|
Packit |
bfcc33 |
++it;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
free(arr);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
char **copy_strings(const std::vector<std::string>& strings, char*** array, int skip) {
|
|
Packit |
bfcc33 |
int num = static_cast<int>(strings.size()) - skip;
|
|
Packit |
bfcc33 |
char** arr = (char**) calloc(num + 1, sizeof(char*));
|
|
Packit |
bfcc33 |
if (arr == 0)
|
|
Packit |
bfcc33 |
return *array = (char **)NULL;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
for(int i = 0; i < num; i++) {
|
|
Packit |
bfcc33 |
arr[i] = (char*) malloc(sizeof(char) * (strings[i + skip].size() + 1));
|
|
Packit |
bfcc33 |
if (arr[i] == 0) {
|
|
Packit |
bfcc33 |
free_string_array(arr);
|
|
Packit |
bfcc33 |
return *array = (char **)NULL;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
std::copy(strings[i + skip].begin(), strings[i + skip].end(), arr[i]);
|
|
Packit |
bfcc33 |
arr[i][strings[i + skip].size()] = '\0';
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
arr[num] = 0;
|
|
Packit |
bfcc33 |
return *array = arr;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// read css string (handle multiline DELIM)
|
|
Packit |
bfcc33 |
std::string read_css_string(const std::string& str)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
std::string out("");
|
|
Packit |
bfcc33 |
bool esc = false;
|
|
Packit |
bfcc33 |
for (auto i : str) {
|
|
Packit |
bfcc33 |
if (i == '\\') {
|
|
Packit |
bfcc33 |
esc = ! esc;
|
|
Packit |
bfcc33 |
} else if (esc && i == '\r') {
|
|
Packit |
bfcc33 |
continue;
|
|
Packit |
bfcc33 |
} else if (esc && i == '\n') {
|
|
Packit |
bfcc33 |
out.resize (out.size () - 1);
|
|
Packit |
bfcc33 |
esc = false;
|
|
Packit |
bfcc33 |
continue;
|
|
Packit |
bfcc33 |
} else {
|
|
Packit |
bfcc33 |
esc = false;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
out.push_back(i);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
// happens when parsing does not correctly skip
|
|
Packit |
bfcc33 |
// over escaped sequences for ie. interpolations
|
|
Packit |
bfcc33 |
// one example: foo\#{interpolate}
|
|
Packit |
bfcc33 |
// if (esc) out += '\\';
|
|
Packit |
bfcc33 |
return out;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// double escape all escape sequences
|
|
Packit |
bfcc33 |
// keep unescaped quotes and backslashes
|
|
Packit |
bfcc33 |
std::string evacuate_escapes(const std::string& str)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
std::string out("");
|
|
Packit |
bfcc33 |
bool esc = false;
|
|
Packit |
bfcc33 |
for (auto i : str) {
|
|
Packit |
bfcc33 |
if (i == '\\' && !esc) {
|
|
Packit |
bfcc33 |
out += '\\';
|
|
Packit |
bfcc33 |
out += '\\';
|
|
Packit |
bfcc33 |
esc = true;
|
|
Packit |
bfcc33 |
} else if (esc && i == '"') {
|
|
Packit |
bfcc33 |
out += '\\';
|
|
Packit |
bfcc33 |
out += i;
|
|
Packit |
bfcc33 |
esc = false;
|
|
Packit |
bfcc33 |
} else if (esc && i == '\'') {
|
|
Packit |
bfcc33 |
out += '\\';
|
|
Packit |
bfcc33 |
out += i;
|
|
Packit |
bfcc33 |
esc = false;
|
|
Packit |
bfcc33 |
} else if (esc && i == '\\') {
|
|
Packit |
bfcc33 |
out += '\\';
|
|
Packit |
bfcc33 |
out += i;
|
|
Packit |
bfcc33 |
esc = false;
|
|
Packit |
bfcc33 |
} else {
|
|
Packit |
bfcc33 |
esc = false;
|
|
Packit |
bfcc33 |
out += i;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
// happens when parsing does not correctly skip
|
|
Packit |
bfcc33 |
// over escaped sequences for ie. interpolations
|
|
Packit |
bfcc33 |
// one example: foo\#{interpolate}
|
|
Packit |
bfcc33 |
// if (esc) out += '\\';
|
|
Packit |
bfcc33 |
return out;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// bell characters are replaced with spaces
|
|
Packit |
bfcc33 |
void newline_to_space(std::string& str)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
std::replace(str.begin(), str.end(), '\n', ' ');
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// bell characters are replaced with spaces
|
|
Packit |
bfcc33 |
// also eats spaces after line-feeds (ltrim)
|
|
Packit |
bfcc33 |
std::string string_to_output(const std::string& str)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
std::string out("");
|
|
Packit |
bfcc33 |
bool lf = false;
|
|
Packit |
bfcc33 |
for (auto i : str) {
|
|
Packit |
bfcc33 |
if (i == '\n') {
|
|
Packit |
bfcc33 |
out += ' ';
|
|
Packit |
bfcc33 |
lf = true;
|
|
Packit |
bfcc33 |
} else if (!(lf && isspace(i))) {
|
|
Packit |
bfcc33 |
out += i;
|
|
Packit |
bfcc33 |
lf = false;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
return out;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
std::string comment_to_string(const std::string& text)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
std::string str = "";
|
|
Packit |
bfcc33 |
size_t has = 0;
|
|
Packit |
bfcc33 |
char prev = 0;
|
|
Packit |
bfcc33 |
bool clean = false;
|
|
Packit |
bfcc33 |
for (auto i : text) {
|
|
Packit |
bfcc33 |
if (clean) {
|
|
Packit |
bfcc33 |
if (i == '\n') { has = 0; }
|
|
Packit |
bfcc33 |
else if (i == '\r') { has = 0; }
|
|
Packit |
bfcc33 |
else if (i == '\t') { ++ has; }
|
|
Packit |
bfcc33 |
else if (i == ' ') { ++ has; }
|
|
Packit |
bfcc33 |
else if (i == '*') {}
|
|
Packit |
bfcc33 |
else {
|
|
Packit |
bfcc33 |
clean = false;
|
|
Packit |
bfcc33 |
str += ' ';
|
|
Packit |
bfcc33 |
if (prev == '*' && i == '/') str += "*/";
|
|
Packit |
bfcc33 |
else str += i;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
} else if (i == '\n') {
|
|
Packit |
bfcc33 |
clean = true;
|
|
Packit |
bfcc33 |
} else if (i == '\r') {
|
|
Packit |
bfcc33 |
clean = true;
|
|
Packit |
bfcc33 |
} else {
|
|
Packit |
bfcc33 |
str += i;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
prev = i;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
if (has) return str;
|
|
Packit |
bfcc33 |
else return text;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// find best quote_mark by detecting if the string contains any single
|
|
Packit |
bfcc33 |
// or double quotes. When a single quote is found, we not we want a double
|
|
Packit |
bfcc33 |
// quote as quote_mark. Otherwise we check if the string cotains any double
|
|
Packit |
bfcc33 |
// quotes, which will trigger the use of single quotes as best quote_mark.
|
|
Packit |
bfcc33 |
char detect_best_quotemark(const char* s, char qm)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
// ensure valid fallback quote_mark
|
|
Packit |
bfcc33 |
char quote_mark = qm && qm != '*' ? qm : '"';
|
|
Packit |
bfcc33 |
while (*s) {
|
|
Packit |
bfcc33 |
// force double quotes as soon
|
|
Packit |
bfcc33 |
// as one single quote is found
|
|
Packit |
bfcc33 |
if (*s == '\'') { return '"'; }
|
|
Packit |
bfcc33 |
// a single does not force quote_mark
|
|
Packit |
bfcc33 |
// maybe we see a double quote later
|
|
Packit |
bfcc33 |
else if (*s == '"') { quote_mark = '\''; }
|
|
Packit |
bfcc33 |
++ s;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
return quote_mark;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
std::string read_hex_escapes(const std::string& s)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
std::string result;
|
|
Packit |
bfcc33 |
bool skipped = false;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
for (size_t i = 0, L = s.length(); i < L; ++i) {
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// implement the same strange ruby sass behavior
|
|
Packit |
bfcc33 |
// an escape sequence can also mean a unicode char
|
|
Packit |
bfcc33 |
if (s[i] == '\\' && !skipped) {
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// remember
|
|
Packit |
bfcc33 |
skipped = true;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// escape length
|
|
Packit |
bfcc33 |
size_t len = 1;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// parse as many sequence chars as possible
|
|
Packit |
bfcc33 |
// ToDo: Check if ruby aborts after possible max
|
|
Packit |
bfcc33 |
while (i + len < L && s[i + len] && isxdigit(s[i + len])) ++ len;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (len > 1) {
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// convert the extracted hex string to code point value
|
|
Packit |
bfcc33 |
// ToDo: Maybe we could do this without creating a substring
|
|
Packit |
bfcc33 |
uint32_t cp = strtol(s.substr (i + 1, len - 1).c_str(), NULL, 16);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (s[i + len] == ' ') ++ len;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// assert invalid code points
|
|
Packit |
bfcc33 |
if (cp == 0) cp = 0xFFFD;
|
|
Packit |
bfcc33 |
// replace bell character
|
|
Packit |
bfcc33 |
// if (cp == '\n') cp = 32;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// use a very simple approach to convert via utf8 lib
|
|
Packit |
bfcc33 |
// maybe there is a more elegant way; maybe we shoud
|
|
Packit |
bfcc33 |
// convert the whole output from string to a stream!?
|
|
Packit |
bfcc33 |
// allocate memory for utf8 char and convert to utf8
|
|
Packit |
bfcc33 |
unsigned char u[5] = {0,0,0,0,0}; utf8::append(cp, u);
|
|
Packit |
bfcc33 |
for(size_t m = 0; m < 5 && u[m]; m++) result.push_back(u[m]);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// skip some more chars?
|
|
Packit |
bfcc33 |
i += len - 1; skipped = false;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
else {
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
skipped = false;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
result.push_back(s[i]);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
else {
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
result.push_back(s[i]);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
return result;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
std::string unquote(const std::string& s, char* qd, bool keep_utf8_sequences, bool strict)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// not enough room for quotes
|
|
Packit |
bfcc33 |
// no possibility to unquote
|
|
Packit |
bfcc33 |
if (s.length() < 2) return s;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
char q;
|
|
Packit |
bfcc33 |
bool skipped = false;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// this is no guarantee that the unquoting will work
|
|
Packit |
bfcc33 |
// what about whitespace before/after the quote_mark?
|
|
Packit |
bfcc33 |
if (*s.begin() == '"' && *s.rbegin() == '"') q = '"';
|
|
Packit |
bfcc33 |
else if (*s.begin() == '\'' && *s.rbegin() == '\'') q = '\'';
|
|
Packit |
bfcc33 |
else return s;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
std::string unq;
|
|
Packit |
bfcc33 |
unq.reserve(s.length()-2);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
for (size_t i = 1, L = s.length() - 1; i < L; ++i) {
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// implement the same strange ruby sass behavior
|
|
Packit |
bfcc33 |
// an escape sequence can also mean a unicode char
|
|
Packit |
bfcc33 |
if (s[i] == '\\' && !skipped) {
|
|
Packit |
bfcc33 |
// remember
|
|
Packit |
bfcc33 |
skipped = true;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// skip it
|
|
Packit |
bfcc33 |
// ++ i;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// if (i == L) break;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// escape length
|
|
Packit |
bfcc33 |
size_t len = 1;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// parse as many sequence chars as possible
|
|
Packit |
bfcc33 |
// ToDo: Check if ruby aborts after possible max
|
|
Packit |
bfcc33 |
while (i + len < L && s[i + len] && isxdigit(s[i + len])) ++ len;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// hex string?
|
|
Packit |
bfcc33 |
if (keep_utf8_sequences) {
|
|
Packit |
bfcc33 |
unq.push_back(s[i]);
|
|
Packit |
bfcc33 |
} else if (len > 1) {
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// convert the extracted hex string to code point value
|
|
Packit |
bfcc33 |
// ToDo: Maybe we could do this without creating a substring
|
|
Packit |
bfcc33 |
uint32_t cp = strtol(s.substr (i + 1, len - 1).c_str(), NULL, 16);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (s[i + len] == ' ') ++ len;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// assert invalid code points
|
|
Packit |
bfcc33 |
if (cp == 0) cp = 0xFFFD;
|
|
Packit |
bfcc33 |
// replace bell character
|
|
Packit |
bfcc33 |
// if (cp == '\n') cp = 32;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// use a very simple approach to convert via utf8 lib
|
|
Packit |
bfcc33 |
// maybe there is a more elegant way; maybe we shoud
|
|
Packit |
bfcc33 |
// convert the whole output from string to a stream!?
|
|
Packit |
bfcc33 |
// allocate memory for utf8 char and convert to utf8
|
|
Packit |
bfcc33 |
unsigned char u[5] = {0,0,0,0,0}; utf8::append(cp, u);
|
|
Packit |
bfcc33 |
for(size_t m = 0; m < 5 && u[m]; m++) unq.push_back(u[m]);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// skip some more chars?
|
|
Packit |
bfcc33 |
i += len - 1; skipped = false;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
// check for unexpected delimiter
|
|
Packit |
bfcc33 |
// be strict and throw error back
|
|
Packit |
bfcc33 |
// else if (!skipped && q == s[i]) {
|
|
Packit |
bfcc33 |
// // don't be that strict
|
|
Packit |
bfcc33 |
// return s;
|
|
Packit |
bfcc33 |
// // this basically always means an internal error and not users fault
|
|
Packit |
bfcc33 |
// error("Unescaped delimiter in string to unquote found. [" + s + "]", ParserState("[UNQUOTE]"));
|
|
Packit |
bfcc33 |
// }
|
|
Packit |
bfcc33 |
else {
|
|
Packit |
bfcc33 |
if (strict && !skipped) {
|
|
Packit |
bfcc33 |
if (s[i] == q) return s;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
skipped = false;
|
|
Packit |
bfcc33 |
unq.push_back(s[i]);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
if (skipped) { return s; }
|
|
Packit |
bfcc33 |
if (qd) *qd = q;
|
|
Packit |
bfcc33 |
return unq;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
std::string quote(const std::string& s, char q)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// autodetect with fallback to given quote
|
|
Packit |
bfcc33 |
q = detect_best_quotemark(s.c_str(), q);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// return an empty quoted string
|
|
Packit |
bfcc33 |
if (s.empty()) return std::string(2, q ? q : '"');
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
std::string quoted;
|
|
Packit |
bfcc33 |
quoted.reserve(s.length()+2);
|
|
Packit |
bfcc33 |
quoted.push_back(q);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
const char* it = s.c_str();
|
|
Packit |
bfcc33 |
const char* end = it + strlen(it) + 1;
|
|
Packit |
bfcc33 |
while (*it && it < end) {
|
|
Packit |
bfcc33 |
const char* now = it;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (*it == q) {
|
|
Packit |
bfcc33 |
quoted.push_back('\\');
|
|
Packit |
bfcc33 |
} else if (*it == '\\') {
|
|
Packit |
bfcc33 |
quoted.push_back('\\');
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
int cp = utf8::next(it, end);
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
// in case of \r, check if the next in sequence
|
|
Packit |
bfcc33 |
// is \n and then advance the iterator and skip \r
|
|
Packit |
bfcc33 |
if (cp == '\r' && it < end && utf8::peek_next(it, end) == '\n') {
|
|
Packit |
bfcc33 |
cp = utf8::next(it, end);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (cp == '\n') {
|
|
Packit |
bfcc33 |
quoted.push_back('\\');
|
|
Packit |
bfcc33 |
quoted.push_back('a');
|
|
Packit |
bfcc33 |
// we hope we can remove this flag once we figure out
|
|
Packit |
bfcc33 |
// why ruby sass has these different output behaviors
|
|
Packit |
bfcc33 |
// gsub(/\n(?![a-fA-F0-9\s])/, "\\a").gsub("\n", "\\a ")
|
|
Packit |
bfcc33 |
using namespace Prelexer;
|
|
Packit |
bfcc33 |
if (alternatives <
|
|
Packit |
bfcc33 |
Prelexer::char_range<'a', 'f'>,
|
|
Packit |
bfcc33 |
Prelexer::char_range<'A', 'F'>,
|
|
Packit |
bfcc33 |
Prelexer::char_range<'0', '9'>,
|
|
Packit |
bfcc33 |
space
|
|
Packit |
bfcc33 |
>(it) != NULL) {
|
|
Packit |
bfcc33 |
quoted.push_back(' ');
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
} else if (cp < 127) {
|
|
Packit |
bfcc33 |
quoted.push_back((char) cp);
|
|
Packit |
bfcc33 |
} else {
|
|
Packit |
bfcc33 |
while (now < it) {
|
|
Packit |
bfcc33 |
quoted.push_back(*now);
|
|
Packit |
bfcc33 |
++ now;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
quoted.push_back(q);
|
|
Packit |
bfcc33 |
return quoted;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
bool is_hex_doublet(double n)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
return n == 0x00 || n == 0x11 || n == 0x22 || n == 0x33 ||
|
|
Packit |
bfcc33 |
n == 0x44 || n == 0x55 || n == 0x66 || n == 0x77 ||
|
|
Packit |
bfcc33 |
n == 0x88 || n == 0x99 || n == 0xAA || n == 0xBB ||
|
|
Packit |
bfcc33 |
n == 0xCC || n == 0xDD || n == 0xEE || n == 0xFF ;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
bool is_color_doublet(double r, double g, double b)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
return is_hex_doublet(r) && is_hex_doublet(g) && is_hex_doublet(b);
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
bool peek_linefeed(const char* start)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
using namespace Prelexer;
|
|
Packit |
bfcc33 |
using namespace Constants;
|
|
Packit |
bfcc33 |
return sequence <
|
|
Packit |
bfcc33 |
zero_plus <
|
|
Packit |
bfcc33 |
alternatives <
|
|
Packit |
bfcc33 |
exactly <' '>,
|
|
Packit |
bfcc33 |
exactly <'\t'>,
|
|
Packit |
bfcc33 |
line_comment,
|
|
Packit |
bfcc33 |
block_comment,
|
|
Packit |
bfcc33 |
delimited_by <
|
|
Packit |
bfcc33 |
slash_star,
|
|
Packit |
bfcc33 |
star_slash,
|
|
Packit |
bfcc33 |
false
|
|
Packit |
bfcc33 |
>
|
|
Packit |
bfcc33 |
>
|
|
Packit |
bfcc33 |
>,
|
|
Packit |
bfcc33 |
re_linebreak
|
|
Packit |
bfcc33 |
>(start) != 0;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
namespace Util {
|
|
Packit |
bfcc33 |
using std::string;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
std::string rtrim(const std::string &str) {
|
|
Packit |
bfcc33 |
std::string trimmed = str;
|
|
Packit |
bfcc33 |
size_t pos_ws = trimmed.find_last_not_of(" \t\n\v\f\r");
|
|
Packit |
bfcc33 |
if (pos_ws != std::string::npos)
|
|
Packit |
bfcc33 |
{ trimmed.erase(pos_ws + 1); }
|
|
Packit |
bfcc33 |
else { trimmed.clear(); }
|
|
Packit |
bfcc33 |
return trimmed;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
std::string normalize_underscores(const std::string& str) {
|
|
Packit |
bfcc33 |
std::string normalized = str;
|
|
Packit |
bfcc33 |
for(size_t i = 0, L = normalized.length(); i < L; ++i) {
|
|
Packit |
bfcc33 |
if(normalized[i] == '_') {
|
|
Packit |
bfcc33 |
normalized[i] = '-';
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
return normalized;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
std::string normalize_decimals(const std::string& str) {
|
|
Packit |
bfcc33 |
std::string prefix = "0";
|
|
Packit |
bfcc33 |
std::string normalized = str;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
return normalized[0] == '.' ? normalized.insert(0, prefix) : normalized;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
bool isPrintable(Ruleset_Ptr r, Sass_Output_Style style) {
|
|
Packit |
bfcc33 |
if (r == NULL) {
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
Block_Obj b = r->block();
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
Selector_List_Ptr sl = Cast<Selector_List>(r->selector());
|
|
Packit |
bfcc33 |
bool hasSelectors = sl ? sl->length() > 0 : false;
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (!hasSelectors) {
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
bool hasDeclarations = false;
|
|
Packit |
bfcc33 |
bool hasPrintableChildBlocks = false;
|
|
Packit |
bfcc33 |
for (size_t i = 0, L = b->length(); i < L; ++i) {
|
|
Packit |
bfcc33 |
Statement_Obj stm = b->at(i);
|
|
Packit |
bfcc33 |
if (Cast<Directive>(stm)) {
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
} else if (Declaration_Ptr d = Cast<Declaration>(stm)) {
|
|
Packit |
bfcc33 |
return isPrintable(d, style);
|
|
Packit |
bfcc33 |
} else if (Has_Block_Ptr p = Cast<Has_Block>(stm)) {
|
|
Packit |
bfcc33 |
Block_Obj pChildBlock = p->block();
|
|
Packit |
bfcc33 |
if (isPrintable(pChildBlock, style)) {
|
|
Packit |
bfcc33 |
hasPrintableChildBlocks = true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
} else if (Comment_Ptr c = Cast<Comment>(stm)) {
|
|
Packit |
bfcc33 |
// keep for uncompressed
|
|
Packit |
bfcc33 |
if (style != COMPRESSED) {
|
|
Packit |
bfcc33 |
hasDeclarations = true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
// output style compressed
|
|
Packit |
bfcc33 |
if (c->is_important()) {
|
|
Packit |
bfcc33 |
hasDeclarations = c->is_important();
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
} else {
|
|
Packit |
bfcc33 |
hasDeclarations = true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (hasDeclarations || hasPrintableChildBlocks) {
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
bool isPrintable(String_Constant_Ptr s, Sass_Output_Style style)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
return ! s->value().empty();
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
bool isPrintable(String_Quoted_Ptr s, Sass_Output_Style style)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
bool isPrintable(Declaration_Ptr d, Sass_Output_Style style)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
Expression_Obj val = d->value();
|
|
Packit |
bfcc33 |
if (String_Quoted_Obj sq = Cast<String_Quoted>(val)) return isPrintable(sq.ptr(), style);
|
|
Packit |
bfcc33 |
if (String_Constant_Obj sc = Cast<String_Constant>(val)) return isPrintable(sc.ptr(), style);
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
bool isPrintable(Supports_Block_Ptr f, Sass_Output_Style style) {
|
|
Packit |
bfcc33 |
if (f == NULL) {
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
Block_Obj b = f->block();
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
bool hasDeclarations = false;
|
|
Packit |
bfcc33 |
bool hasPrintableChildBlocks = false;
|
|
Packit |
bfcc33 |
for (size_t i = 0, L = b->length(); i < L; ++i) {
|
|
Packit |
bfcc33 |
Statement_Obj stm = b->at(i);
|
|
Packit |
bfcc33 |
if (Cast<Declaration>(stm) || Cast<Directive>(stm)) {
|
|
Packit |
bfcc33 |
hasDeclarations = true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
else if (Has_Block_Ptr b = Cast<Has_Block>(stm)) {
|
|
Packit |
bfcc33 |
Block_Obj pChildBlock = b->block();
|
|
Packit |
bfcc33 |
if (isPrintable(pChildBlock, style)) {
|
|
Packit |
bfcc33 |
hasPrintableChildBlocks = true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
if (hasDeclarations || hasPrintableChildBlocks) {
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
bool isPrintable(Media_Block_Ptr m, Sass_Output_Style style)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
if (m == 0) return false;
|
|
Packit |
bfcc33 |
Block_Obj b = m->block();
|
|
Packit |
bfcc33 |
if (b == 0) return false;
|
|
Packit |
bfcc33 |
for (size_t i = 0, L = b->length(); i < L; ++i) {
|
|
Packit |
bfcc33 |
Statement_Obj stm = b->at(i);
|
|
Packit |
bfcc33 |
if (Cast<Directive>(stm)) return true;
|
|
Packit |
bfcc33 |
else if (Cast<Declaration>(stm)) return true;
|
|
Packit |
bfcc33 |
else if (Comment_Ptr c = Cast<Comment>(stm)) {
|
|
Packit |
bfcc33 |
if (isPrintable(c, style)) {
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
else if (Ruleset_Ptr r = Cast<Ruleset>(stm)) {
|
|
Packit |
bfcc33 |
if (isPrintable(r, style)) {
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
else if (Supports_Block_Ptr f = Cast<Supports_Block>(stm)) {
|
|
Packit |
bfcc33 |
if (isPrintable(f, style)) {
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
else if (Media_Block_Ptr mb = Cast<Media_Block>(stm)) {
|
|
Packit |
bfcc33 |
if (isPrintable(mb, style)) {
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
else if (Has_Block_Ptr b = Cast<Has_Block>(stm)) {
|
|
Packit |
bfcc33 |
if (isPrintable(b->block(), style)) {
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
bool isPrintable(Comment_Ptr c, Sass_Output_Style style)
|
|
Packit |
bfcc33 |
{
|
|
Packit |
bfcc33 |
// keep for uncompressed
|
|
Packit |
bfcc33 |
if (style != COMPRESSED) {
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
// output style compressed
|
|
Packit |
bfcc33 |
if (c->is_important()) {
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
// not printable
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
};
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
bool isPrintable(Block_Obj b, Sass_Output_Style style) {
|
|
Packit |
bfcc33 |
if (!b) {
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
for (size_t i = 0, L = b->length(); i < L; ++i) {
|
|
Packit |
bfcc33 |
Statement_Obj stm = b->at(i);
|
|
Packit |
bfcc33 |
if (Cast<Declaration>(stm) || Cast<Directive>(stm)) {
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
else if (Comment_Ptr c = Cast<Comment>(stm)) {
|
|
Packit |
bfcc33 |
if (isPrintable(c, style)) {
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
else if (Ruleset_Ptr r = Cast<Ruleset>(stm)) {
|
|
Packit |
bfcc33 |
if (isPrintable(r, style)) {
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
else if (Supports_Block_Ptr f = Cast<Supports_Block>(stm)) {
|
|
Packit |
bfcc33 |
if (isPrintable(f, style)) {
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
else if (Media_Block_Ptr m = Cast<Media_Block>(stm)) {
|
|
Packit |
bfcc33 |
if (isPrintable(m, style)) {
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
else if (Has_Block_Ptr b = Cast<Has_Block>(stm)) {
|
|
Packit |
bfcc33 |
if (isPrintable(b->block(), style)) {
|
|
Packit |
bfcc33 |
return true;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
return false;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
bool isAscii(const char chr) {
|
|
Packit |
bfcc33 |
return unsigned(chr) < 128;
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
|
|
Packit |
bfcc33 |
}
|
|
Packit |
bfcc33 |
}
|