|  | 
|  | 1 | +#include "ast.hpp" | 
|  | 2 | +#include "fn_utils.hpp" | 
|  | 3 | +#include "fn_strings.hpp" | 
|  | 4 | + | 
|  | 5 | +namespace Sass { | 
|  | 6 | + | 
|  | 7 | +  namespace Functions { | 
|  | 8 | + | 
|  | 9 | +    void handle_utf8_error (const ParserState& pstate, Backtraces traces) | 
|  | 10 | +    { | 
|  | 11 | +      try { | 
|  | 12 | +       throw; | 
|  | 13 | +      } | 
|  | 14 | +      catch (utf8::invalid_code_point) { | 
|  | 15 | +        std::string msg("utf8::invalid_code_point"); | 
|  | 16 | +        error(msg, pstate, traces); | 
|  | 17 | +      } | 
|  | 18 | +      catch (utf8::not_enough_room) { | 
|  | 19 | +        std::string msg("utf8::not_enough_room"); | 
|  | 20 | +        error(msg, pstate, traces); | 
|  | 21 | +      } | 
|  | 22 | +      catch (utf8::invalid_utf8) { | 
|  | 23 | +        std::string msg("utf8::invalid_utf8"); | 
|  | 24 | +        error(msg, pstate, traces); | 
|  | 25 | +      } | 
|  | 26 | +      catch (...) { throw; } | 
|  | 27 | +    } | 
|  | 28 | + | 
|  | 29 | +    /////////////////// | 
|  | 30 | +    // STRING FUNCTIONS | 
|  | 31 | +    /////////////////// | 
|  | 32 | + | 
|  | 33 | +    Signature unquote_sig = "unquote($string)"; | 
|  | 34 | +    BUILT_IN(sass_unquote) | 
|  | 35 | +    { | 
|  | 36 | +      AST_Node_Obj arg = env["$string"]; | 
|  | 37 | +      if (String_Quoted_Ptr string_quoted = Cast<String_Quoted>(arg)) { | 
|  | 38 | +        String_Constant_Ptr result = SASS_MEMORY_NEW(String_Constant, pstate, string_quoted->value()); | 
|  | 39 | +        // remember if the string was quoted (color tokens) | 
|  | 40 | +        result->is_delayed(true); // delay colors | 
|  | 41 | +        return result; | 
|  | 42 | +      } | 
|  | 43 | +      else if (String_Constant_Ptr str = Cast<String_Constant>(arg)) { | 
|  | 44 | +        return str; | 
|  | 45 | +      } | 
|  | 46 | +      else if (Value_Ptr ex = Cast<Value>(arg)) { | 
|  | 47 | +        Sass_Output_Style oldstyle = ctx.c_options.output_style; | 
|  | 48 | +        ctx.c_options.output_style = SASS_STYLE_NESTED; | 
|  | 49 | +        std::string val(arg->to_string(ctx.c_options)); | 
|  | 50 | +        val = Cast<Null>(arg) ? "null" : val; | 
|  | 51 | +        ctx.c_options.output_style = oldstyle; | 
|  | 52 | + | 
|  | 53 | +        deprecated_function("Passing " + val + ", a non-string value, to unquote()", pstate); | 
|  | 54 | +        return ex; | 
|  | 55 | +      } | 
|  | 56 | +      throw std::runtime_error("Invalid Data Type for unquote"); | 
|  | 57 | +    } | 
|  | 58 | + | 
|  | 59 | +    Signature quote_sig = "quote($string)"; | 
|  | 60 | +    BUILT_IN(sass_quote) | 
|  | 61 | +    { | 
|  | 62 | +      AST_Node_Obj arg = env["$string"]; | 
|  | 63 | +      // only set quote mark to true if already a string | 
|  | 64 | +      if (String_Quoted_Ptr qstr = Cast<String_Quoted>(arg)) { | 
|  | 65 | +        qstr->quote_mark('*'); | 
|  | 66 | +        return qstr; | 
|  | 67 | +      } | 
|  | 68 | +      // all other nodes must be converted to a string node | 
|  | 69 | +      std::string str(quote(arg->to_string(ctx.c_options), String_Constant::double_quote())); | 
|  | 70 | +      String_Quoted_Ptr result = SASS_MEMORY_NEW(String_Quoted, pstate, str); | 
|  | 71 | +      result->quote_mark('*'); | 
|  | 72 | +      return result; | 
|  | 73 | +    } | 
|  | 74 | + | 
|  | 75 | + | 
|  | 76 | +    Signature str_length_sig = "str-length($string)"; | 
|  | 77 | +    BUILT_IN(str_length) | 
|  | 78 | +    { | 
|  | 79 | +      size_t len = std::string::npos; | 
|  | 80 | +      try { | 
|  | 81 | +        String_Constant_Ptr s = ARG("$string", String_Constant); | 
|  | 82 | +        len = UTF_8::code_point_count(s->value(), 0, s->value().size()); | 
|  | 83 | + | 
|  | 84 | +      } | 
|  | 85 | +      // handle any invalid utf8 errors | 
|  | 86 | +      // other errors will be re-thrown | 
|  | 87 | +      catch (...) { handle_utf8_error(pstate, traces); } | 
|  | 88 | +      // return something even if we had an error (-1) | 
|  | 89 | +      return SASS_MEMORY_NEW(Number, pstate, (double)len); | 
|  | 90 | +    } | 
|  | 91 | + | 
|  | 92 | +    Signature str_insert_sig = "str-insert($string, $insert, $index)"; | 
|  | 93 | +    BUILT_IN(str_insert) | 
|  | 94 | +    { | 
|  | 95 | +      std::string str; | 
|  | 96 | +      try { | 
|  | 97 | +        String_Constant_Ptr s = ARG("$string", String_Constant); | 
|  | 98 | +        str = s->value(); | 
|  | 99 | +        str = unquote(str); | 
|  | 100 | +        String_Constant_Ptr i = ARG("$insert", String_Constant); | 
|  | 101 | +        std::string ins = i->value(); | 
|  | 102 | +        ins = unquote(ins); | 
|  | 103 | +        double index = ARGVAL("$index"); | 
|  | 104 | +        size_t len = UTF_8::code_point_count(str, 0, str.size()); | 
|  | 105 | + | 
|  | 106 | +        if (index > 0 && index <= len) { | 
|  | 107 | +          // positive and within string length | 
|  | 108 | +          str.insert(UTF_8::offset_at_position(str, static_cast<size_t>(index) - 1), ins); | 
|  | 109 | +        } | 
|  | 110 | +        else if (index > len) { | 
|  | 111 | +          // positive and past string length | 
|  | 112 | +          str += ins; | 
|  | 113 | +        } | 
|  | 114 | +        else if (index == 0) { | 
|  | 115 | +          str = ins + str; | 
|  | 116 | +        } | 
|  | 117 | +        else if (std::abs(index) <= len) { | 
|  | 118 | +          // negative and within string length | 
|  | 119 | +          index += len + 1; | 
|  | 120 | +          str.insert(UTF_8::offset_at_position(str, static_cast<size_t>(index)), ins); | 
|  | 121 | +        } | 
|  | 122 | +        else { | 
|  | 123 | +          // negative and past string length | 
|  | 124 | +          str = ins + str; | 
|  | 125 | +        } | 
|  | 126 | + | 
|  | 127 | +        if (String_Quoted_Ptr ss = Cast<String_Quoted>(s)) { | 
|  | 128 | +          if (ss->quote_mark()) str = quote(str); | 
|  | 129 | +        } | 
|  | 130 | +      } | 
|  | 131 | +      // handle any invalid utf8 errors | 
|  | 132 | +      // other errors will be re-thrown | 
|  | 133 | +      catch (...) { handle_utf8_error(pstate, traces); } | 
|  | 134 | +      return SASS_MEMORY_NEW(String_Quoted, pstate, str); | 
|  | 135 | +    } | 
|  | 136 | + | 
|  | 137 | +    Signature str_index_sig = "str-index($string, $substring)"; | 
|  | 138 | +    BUILT_IN(str_index) | 
|  | 139 | +    { | 
|  | 140 | +      size_t index = std::string::npos; | 
|  | 141 | +      try { | 
|  | 142 | +        String_Constant_Ptr s = ARG("$string", String_Constant); | 
|  | 143 | +        String_Constant_Ptr t = ARG("$substring", String_Constant); | 
|  | 144 | +        std::string str = s->value(); | 
|  | 145 | +        str = unquote(str); | 
|  | 146 | +        std::string substr = t->value(); | 
|  | 147 | +        substr = unquote(substr); | 
|  | 148 | + | 
|  | 149 | +        size_t c_index = str.find(substr); | 
|  | 150 | +        if(c_index == std::string::npos) { | 
|  | 151 | +          return SASS_MEMORY_NEW(Null, pstate); | 
|  | 152 | +        } | 
|  | 153 | +        index = UTF_8::code_point_count(str, 0, c_index) + 1; | 
|  | 154 | +      } | 
|  | 155 | +      // handle any invalid utf8 errors | 
|  | 156 | +      // other errors will be re-thrown | 
|  | 157 | +      catch (...) { handle_utf8_error(pstate, traces); } | 
|  | 158 | +      // return something even if we had an error (-1) | 
|  | 159 | +      return SASS_MEMORY_NEW(Number, pstate, (double)index); | 
|  | 160 | +    } | 
|  | 161 | + | 
|  | 162 | +    Signature str_slice_sig = "str-slice($string, $start-at, $end-at:-1)"; | 
|  | 163 | +    BUILT_IN(str_slice) | 
|  | 164 | +    { | 
|  | 165 | +      std::string newstr; | 
|  | 166 | +      try { | 
|  | 167 | +        String_Constant_Ptr s = ARG("$string", String_Constant); | 
|  | 168 | +        double start_at = ARGVAL("$start-at"); | 
|  | 169 | +        double end_at = ARGVAL("$end-at"); | 
|  | 170 | +        String_Quoted_Ptr ss = Cast<String_Quoted>(s); | 
|  | 171 | + | 
|  | 172 | +        std::string str = unquote(s->value()); | 
|  | 173 | + | 
|  | 174 | +        size_t size = utf8::distance(str.begin(), str.end()); | 
|  | 175 | + | 
|  | 176 | +        if (!Cast<Number>(env["$end-at"])) { | 
|  | 177 | +          end_at = -1; | 
|  | 178 | +        } | 
|  | 179 | + | 
|  | 180 | +        if (end_at == 0 || (end_at + size) < 0) { | 
|  | 181 | +          if (ss && ss->quote_mark()) newstr = quote(""); | 
|  | 182 | +          return SASS_MEMORY_NEW(String_Quoted, pstate, newstr); | 
|  | 183 | +        } | 
|  | 184 | + | 
|  | 185 | +        if (end_at < 0) { | 
|  | 186 | +          end_at += size + 1; | 
|  | 187 | +          if (end_at == 0) end_at = 1; | 
|  | 188 | +        } | 
|  | 189 | +        if (end_at > size) { end_at = (double)size; } | 
|  | 190 | +        if (start_at < 0) { | 
|  | 191 | +          start_at += size + 1; | 
|  | 192 | +          if (start_at < 0)  start_at = 0; | 
|  | 193 | +        } | 
|  | 194 | +        else if (start_at == 0) { ++ start_at; } | 
|  | 195 | + | 
|  | 196 | +        if (start_at <= end_at) | 
|  | 197 | +        { | 
|  | 198 | +          std::string::iterator start = str.begin(); | 
|  | 199 | +          utf8::advance(start, start_at - 1, str.end()); | 
|  | 200 | +          std::string::iterator end = start; | 
|  | 201 | +          utf8::advance(end, end_at - start_at + 1, str.end()); | 
|  | 202 | +          newstr = std::string(start, end); | 
|  | 203 | +        } | 
|  | 204 | +        if (ss) { | 
|  | 205 | +          if(ss->quote_mark()) newstr = quote(newstr); | 
|  | 206 | +        } | 
|  | 207 | +      } | 
|  | 208 | +      // handle any invalid utf8 errors | 
|  | 209 | +      // other errors will be re-thrown | 
|  | 210 | +      catch (...) { handle_utf8_error(pstate, traces); } | 
|  | 211 | +      return SASS_MEMORY_NEW(String_Quoted, pstate, newstr); | 
|  | 212 | +    } | 
|  | 213 | + | 
|  | 214 | +    Signature to_upper_case_sig = "to-upper-case($string)"; | 
|  | 215 | +    BUILT_IN(to_upper_case) | 
|  | 216 | +    { | 
|  | 217 | +      String_Constant_Ptr s = ARG("$string", String_Constant); | 
|  | 218 | +      std::string str = s->value(); | 
|  | 219 | + | 
|  | 220 | +      for (size_t i = 0, L = str.length(); i < L; ++i) { | 
|  | 221 | +        if (Sass::Util::isAscii(str[i])) { | 
|  | 222 | +          str[i] = std::toupper(str[i]); | 
|  | 223 | +        } | 
|  | 224 | +      } | 
|  | 225 | + | 
|  | 226 | +      if (String_Quoted_Ptr ss = Cast<String_Quoted>(s)) { | 
|  | 227 | +        String_Quoted_Ptr cpy = SASS_MEMORY_COPY(ss); | 
|  | 228 | +        cpy->value(str); | 
|  | 229 | +        return cpy; | 
|  | 230 | +      } else { | 
|  | 231 | +        return SASS_MEMORY_NEW(String_Quoted, pstate, str); | 
|  | 232 | +      } | 
|  | 233 | +    } | 
|  | 234 | + | 
|  | 235 | +    Signature to_lower_case_sig = "to-lower-case($string)"; | 
|  | 236 | +    BUILT_IN(to_lower_case) | 
|  | 237 | +    { | 
|  | 238 | +      String_Constant_Ptr s = ARG("$string", String_Constant); | 
|  | 239 | +      std::string str = s->value(); | 
|  | 240 | + | 
|  | 241 | +      for (size_t i = 0, L = str.length(); i < L; ++i) { | 
|  | 242 | +        if (Sass::Util::isAscii(str[i])) { | 
|  | 243 | +          str[i] = std::tolower(str[i]); | 
|  | 244 | +        } | 
|  | 245 | +      } | 
|  | 246 | + | 
|  | 247 | +      if (String_Quoted_Ptr ss = Cast<String_Quoted>(s)) { | 
|  | 248 | +        String_Quoted_Ptr cpy = SASS_MEMORY_COPY(ss); | 
|  | 249 | +        cpy->value(str); | 
|  | 250 | +        return cpy; | 
|  | 251 | +      } else { | 
|  | 252 | +        return SASS_MEMORY_NEW(String_Quoted, pstate, str); | 
|  | 253 | +      } | 
|  | 254 | +    } | 
|  | 255 | + | 
|  | 256 | +  } | 
|  | 257 | + | 
|  | 258 | +} | 
0 commit comments