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
11namespace 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

source code of gtk/subprojects/libsass/src/fn_strings.cpp