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