diff --git a/src/ast.cpp b/src/ast.cpp index d50d9cc77..bf2ea961a 100644 --- a/src/ast.cpp +++ b/src/ast.cpp @@ -3,8 +3,6 @@ #include "sass.hpp" #include "ast.hpp" -#include -#include namespace Sass { diff --git a/src/ast_selectors.cpp b/src/ast_selectors.cpp index dd2113712..b55f24b02 100644 --- a/src/ast_selectors.cpp +++ b/src/ast_selectors.cpp @@ -652,15 +652,17 @@ namespace Sass { CssMediaQuery_Obj CssMediaQuery::merge(CssMediaQuery_Obj& other) { - std::string ourType(this->type()); - std::string theirType(other->type()); - std::string ourModifier(this->modifier()); - std::string theirModifier(other->modifier()); - - std::transform(ourType.begin(), ourType.end(), ourType.begin(), ::tolower); - std::transform(theirType.begin(), theirType.end(), theirType.begin(), ::tolower); - std::transform(ourModifier.begin(), ourModifier.end(), ourModifier.begin(), ::tolower); - std::transform(theirModifier.begin(), theirModifier.end(), theirModifier.begin(), ::tolower); + std::string ourType = this->type(); + Util::ascii_str_tolower(&ourType); + + std::string theirType = other->type(); + Util::ascii_str_tolower(&theirType); + + std::string ourModifier = this->modifier(); + Util::ascii_str_tolower(&ourModifier); + + std::string theirModifier = other->modifier(); + Util::ascii_str_tolower(&theirModifier); std::string type; std::string modifier; diff --git a/src/color_maps.cpp b/src/color_maps.cpp index 8d147329c..be38fcb08 100644 --- a/src/color_maps.cpp +++ b/src/color_maps.cpp @@ -4,6 +4,7 @@ #include "ast.hpp" #include "color_maps.hpp" +#include "util_string.hpp" namespace Sass { @@ -616,8 +617,8 @@ namespace Sass { const Color_RGBA* name_to_color(const std::string& key) { // case insensitive lookup. See #2462 - std::string lower{key}; - std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower); + std::string lower = key; + Util::ascii_str_tolower(&lower); auto p = names_to_colors->find(lower); if (p != names_to_colors->end()) { diff --git a/src/file.cpp b/src/file.cpp index 3ccdf3a66..02e728031 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -13,7 +13,6 @@ #else # include #endif -#include #include #include #include @@ -25,6 +24,7 @@ #include "sass_functions.hpp" #include "error_handling.hpp" #include "util.hpp" +#include "util_string.hpp" #include "sass2scss.h" #ifdef _WIN32 @@ -106,13 +106,13 @@ namespace Sass { bool is_absolute_path(const std::string& path) { #ifdef _WIN32 - if (path.length() >= 2 && isalpha(path[0]) && path[1] == ':') return true; + if (path.length() >= 2 && Util::ascii_isalpha(path[0]) && path[1] == ':') return true; #endif size_t i = 0; // check if we have a protocol - if (path[i] && Prelexer::is_alpha(path[i])) { + if (path[i] && Util::ascii_isalpha(static_cast(path[i]))) { // skip over all alphanumeric characters - while (path[i] && Prelexer::is_alnum(path[i])) ++i; + while (path[i] && Util::ascii_isalnum(static_cast(path[i]))) ++i; i = i && path[i] == ':' ? i + 1 : 0; } return path[i] == '/'; @@ -179,9 +179,9 @@ namespace Sass { size_t proto = 0; // check if we have a protocol - if (path[proto] && Prelexer::is_alpha(path[proto])) { + if (path[proto] && Util::ascii_isalpha(static_cast(path[proto]))) { // skip over all alphanumeric characters - while (path[proto] && Prelexer::is_alnum(path[proto++])) {} + while (path[proto] && Util::ascii_isalnum(static_cast(path[proto++]))) {} // then skip over the mandatory colon if (proto && path[proto] == ':') ++ proto; } @@ -260,9 +260,9 @@ namespace Sass { size_t proto = 0; // check if we have a protocol - if (path[proto] && Prelexer::is_alpha(path[proto])) { + if (path[proto] && Util::ascii_isalpha(static_cast(path[proto]))) { // skip over all alphanumeric characters - while (path[proto] && Prelexer::is_alnum(path[proto++])) {} + while (path[proto] && Util::ascii_isalnum(static_cast(path[proto++]))) {} // then skip over the mandatory colon if (proto && path[proto] == ':') ++ proto; } @@ -288,7 +288,8 @@ namespace Sass { #else // compare the charactes in a case insensitive manner // windows fs is only case insensitive in ascii ranges - if (tolower(abs_path[i]) != tolower(abs_base[i])) break; + if (Util::ascii_tolower(static_cast(abs_path[i])) != + Util::ascii_tolower(static_cast(abs_base[i]))) break; #endif if (abs_path[i] == '/') index = i + 1; } @@ -488,8 +489,7 @@ namespace Sass { if (path.length() > 5) { extension = path.substr(path.length() - 5, 5); } - for(size_t i=0; i #include #include "ast.hpp" #include "fn_utils.hpp" #include "fn_colors.hpp" #include "util.hpp" +#include "util_string.hpp" namespace Sass { @@ -582,10 +582,8 @@ namespace Sass { ss << std::hex << std::setw(2) << static_cast(Sass::round(g, ctx.c_options.precision)); ss << std::hex << std::setw(2) << static_cast(Sass::round(b, ctx.c_options.precision)); - std::string result(ss.str()); - for (size_t i = 0, L = result.length(); i < L; ++i) { - result[i] = std::toupper(result[i]); - } + std::string result = ss.str(); + Util::ascii_str_toupper(&result); return SASS_MEMORY_NEW(String_Quoted, pstate, result); } diff --git a/src/fn_numbers.cpp b/src/fn_numbers.cpp index 28befb5ac..8f4784af2 100644 --- a/src/fn_numbers.cpp +++ b/src/fn_numbers.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include diff --git a/src/fn_strings.cpp b/src/fn_strings.cpp index 2af000a4a..b09fce9a6 100644 --- a/src/fn_strings.cpp +++ b/src/fn_strings.cpp @@ -2,11 +2,11 @@ // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" -#include #include "utf8.h" #include "ast.hpp" #include "fn_utils.hpp" #include "fn_strings.hpp" +#include "util_string.hpp" namespace Sass { @@ -212,12 +212,7 @@ namespace Sass { { String_Constant* s = ARG("$string", String_Constant); std::string str = s->value(); - - for (size_t i = 0, L = str.length(); i < L; ++i) { - if (Sass::Util::isAscii(str[i])) { - str[i] = std::toupper(str[i]); - } - } + Util::ascii_str_toupper(&str); if (String_Quoted* ss = Cast(s)) { String_Quoted* cpy = SASS_MEMORY_COPY(ss); @@ -233,12 +228,7 @@ namespace Sass { { String_Constant* s = ARG("$string", String_Constant); std::string str = s->value(); - - for (size_t i = 0, L = str.length(); i < L; ++i) { - if (Sass::Util::isAscii(str[i])) { - str[i] = std::tolower(str[i]); - } - } + Util::ascii_str_tolower(&str); if (String_Quoted* ss = Cast(s)) { String_Quoted* cpy = SASS_MEMORY_COPY(ss); diff --git a/src/lexer.cpp b/src/lexer.cpp index 593fc9c9e..fd9101192 100644 --- a/src/lexer.cpp +++ b/src/lexer.cpp @@ -2,7 +2,6 @@ // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" -#include #include #include #include "lexer.hpp" diff --git a/src/prelexer.cpp b/src/prelexer.cpp index c8dbe34c1..d2e8e06f0 100644 --- a/src/prelexer.cpp +++ b/src/prelexer.cpp @@ -2,10 +2,10 @@ // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" -#include #include #include #include "util.hpp" +#include "util_string.hpp" #include "position.hpp" #include "prelexer.hpp" #include "constants.hpp" @@ -1400,7 +1400,7 @@ namespace Sass { }*/ const char* H(const char* src) { - return std::isxdigit(static_cast(*src)) ? src+1 : 0; + return Util::ascii_isxdigit(static_cast(*src)) ? src+1 : 0; } const char* W(const char* src) { diff --git a/src/util.cpp b/src/util.cpp index cf45a75db..272cc9669 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -2,6 +2,7 @@ #include "sass.h" #include "ast.hpp" #include "util.hpp" +#include "util_string.hpp" #include "lexer.hpp" #include "prelexer.hpp" #include "constants.hpp" @@ -289,7 +290,7 @@ namespace Sass { // 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; + while (i + len < L && s[i + len] && Util::ascii_isxdigit(static_cast(s[i + len]))) ++ len; if (len > 1) { @@ -375,7 +376,7 @@ namespace Sass { // 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; + while (i + len < L && s[i + len] && Util::ascii_isxdigit(static_cast(s[i + len]))) ++ len; // hex string? if (keep_utf8_sequences) { @@ -718,9 +719,5 @@ namespace Sass { return false; } - bool isAscii(const char chr) { - return unsigned(chr) < 128; - } - } } diff --git a/src/util.hpp b/src/util.hpp index f7a3ed34b..61652074b 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -100,7 +100,6 @@ namespace Sass { bool isPrintable(String_Constant* s, Sass_Output_Style style = NESTED); bool isPrintable(String_Quoted* s, Sass_Output_Style style = NESTED); bool isPrintable(Declaration* d, Sass_Output_Style style = NESTED); - bool isAscii(const char chr); } } diff --git a/src/util_string.cpp b/src/util_string.cpp index a92a2666c..65f48da9e 100644 --- a/src/util_string.cpp +++ b/src/util_string.cpp @@ -26,7 +26,24 @@ namespace Sass { // If not test was too long return *lit == 0; } - // EO equalsLiteral + + void ascii_str_tolower(std::string* s) { + for (auto& ch : *s) { + ch = ascii_tolower(static_cast(ch)); + } + } + + void ascii_str_toupper(std::string* s) { + for (auto& ch : *s) { + ch = ascii_toupper(static_cast(ch)); + } + } + + std::string rtrim(std::string str) { + auto it = std::find_if_not(str.rbegin(), str.rend(), ascii_isspace); + str.erase(str.rend() - it); + return str; + } // ########################################################################### // Returns [name] without a vendor prefix. diff --git a/src/util_string.hpp b/src/util_string.hpp index 72e3b9b1b..dd0abc3cc 100644 --- a/src/util_string.hpp +++ b/src/util_string.hpp @@ -29,9 +29,44 @@ namespace Sass { char opening_bracket_for(char closing_bracket); char closing_bracket_for(char opening_bracket); - } - // namespace Util -} -// namespace Sass + // Locale-independent ASCII character routines. + inline bool ascii_isalpha(unsigned char c) { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); + } + + inline bool ascii_isdigit(unsigned char c) { + return (c >= '0' && c <= '9'); + } + + inline bool ascii_isalnum(unsigned char c) { + return ascii_isalpha(c) || ascii_isdigit(c); + } + + inline bool ascii_isascii(unsigned char c) { return c < 128; } + + inline bool ascii_isxdigit(unsigned char c) { + return ascii_isdigit(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); + } + + inline bool ascii_isspace(unsigned char c) { + return c == ' ' || c == '\t' || c == '\v' || c == '\f' || c == '\r' || c == '\n'; + } + + inline char ascii_tolower(unsigned char c) { + if (c >= 'A' && c <= 'Z') return c + 32; + return c; + } + + void ascii_str_tolower(std::string* s); + + inline char ascii_toupper(unsigned char c) { + if (c >= 'a' && c <= 'z') return c - 32; + return c; + } + + void ascii_str_toupper(std::string* s); + + } // namespace Sass +} // namespace Util #endif // SASS_UTIL_STRING_H diff --git a/test/test_util_string.cpp b/test/test_util_string.cpp index 3f110af20..b0df8af2c 100644 --- a/test/test_util_string.cpp +++ b/test/test_util_string.cpp @@ -142,6 +142,45 @@ bool TestUnvendor() { return true; } +bool Test_ascii_str_to_lower() { + std::string str = "A B"; + Sass::Util::ascii_str_tolower(&str); + ASSERT_STR_EQ("a b", str); + return true; +} + +bool Test_ascii_str_to_upper() { + std::string str = "a b"; + Sass::Util::ascii_str_toupper(&str); + ASSERT_STR_EQ("A B", str); + return true; +} + +bool Test_ascii_isalpha() { + ASSERT_TRUE(Sass::Util::ascii_isalpha('a')); + ASSERT_FALSE(Sass::Util::ascii_isalpha('3')); + return true; +} + +bool Test_ascii_isxdigit() { + ASSERT_TRUE(Sass::Util::ascii_isxdigit('a')); + ASSERT_TRUE(Sass::Util::ascii_isxdigit('F')); + ASSERT_TRUE(Sass::Util::ascii_isxdigit('3')); + ASSERT_FALSE(Sass::Util::ascii_isxdigit('G')); + return true; +} + +bool Test_ascii_isspace() { + ASSERT_TRUE(Sass::Util::ascii_isspace(' ')); + ASSERT_TRUE(Sass::Util::ascii_isspace('\t')); + ASSERT_TRUE(Sass::Util::ascii_isspace('\v')); + ASSERT_TRUE(Sass::Util::ascii_isspace('\f')); + ASSERT_TRUE(Sass::Util::ascii_isspace('\r')); + ASSERT_TRUE(Sass::Util::ascii_isspace('\n')); + ASSERT_FALSE(Sass::Util::ascii_isspace('G')); + return true; +} + } // namespace #define TEST(fn) \ @@ -166,6 +205,11 @@ int main(int argc, char **argv) { TEST(TestNormalizeDecimalsNoLeadingZero); TEST(testEqualsLiteral); TEST(TestUnvendor); + TEST(Test_ascii_str_to_lower); + TEST(Test_ascii_str_to_upper); + TEST(Test_ascii_isalpha); + TEST(Test_ascii_isxdigit); + TEST(Test_ascii_isspace); std::cerr << argv[0] << ": Passed: " << passed.size() << ", failed: " << failed.size() << "." << std::endl;