// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "utf8.h"
#include "ast.hpp"
#include "fn_utils.hpp"
#include "fn_strings.hpp"
#include "util_string.hpp"
namespace Sass {
namespace Functions {
void handle_utf8_error (const SourceSpan& pstate, Backtraces traces)
{
try {
throw;
}
catch (utf8::invalid_code_point&) {
sass::string msg("utf8::invalid_code_point");
error(msg, pstate, traces);
}
catch (utf8::not_enough_room&) {
sass::string msg("utf8::not_enough_room");
error(msg, pstate, traces);
}
catch (utf8::invalid_utf8&) {
sass::string msg("utf8::invalid_utf8");
error(msg, pstate, traces);
}
catch (...) { throw; }
}
///////////////////
// STRING FUNCTIONS
///////////////////
Signature unquote_sig = "unquote($string)";
BUILT_IN(sass_unquote)
{
AST_Node_Obj arg = env["$string"];
if (String_Quoted* string_quoted = Cast<String_Quoted>(arg)) {
String_Constant* result = SASS_MEMORY_NEW(String_Constant, pstate, string_quoted->value());
// remember if the string was quoted (color tokens)
result->is_delayed(true); // delay colors
return result;
}
else if (String_Constant* str = Cast<String_Constant>(arg)) {
return str;
}
else if (Value* ex = Cast<Value>(arg)) {
Sass_Output_Style oldstyle = ctx.c_options.output_style;
ctx.c_options.output_style = SASS_STYLE_NESTED;
sass::string val(arg->to_string(ctx.c_options));
val = Cast<Null>(arg) ? "null" : val;
ctx.c_options.output_style = oldstyle;
deprecated_function("Passing " + val + ", a non-string value, to unquote()", pstate);
return ex;
}
throw std::runtime_error("Invalid Data Type for unquote");
}
Signature quote_sig = "quote($string)";
BUILT_IN(sass_quote)
{
const String_Constant* s = ARG("$string", String_Constant);
String_Quoted *result = SASS_MEMORY_NEW(
String_Quoted, pstate, s->value(),
/*q=*/'\0', /*keep_utf8_escapes=*/false, /*skip_unquoting=*/true);
result->quote_mark('*');
return result;
}
Signature str_length_sig = "str-length($string)";
BUILT_IN(str_length)
{
size_t len = sass::string::npos;
try {
String_Constant* s = ARG("$string", String_Constant);
len = UTF_8::code_point_count(s->value(), 0, s->value().size());
}
// handle any invalid utf8 errors
// other errors will be re-thrown
catch (...) { handle_utf8_error(pstate, traces); }
// return something even if we had an error (-1)
return SASS_MEMORY_NEW(Number, pstate, (double)len);
}
Signature str_insert_sig = "str-insert($string, $insert, $index)";
BUILT_IN(str_insert)
{
sass::string str;
try {
String_Constant* s = ARG("$string", String_Constant);
str = s->value();
String_Constant* i = ARG("$insert", String_Constant);
sass::string ins = i->value();
double index = ARGVAL("$index");
if (index != (int)index) {
sass::ostream strm;
strm << "$index: ";
strm << std::to_string(index);
strm << " is not an int";
error(strm.str(), pstate, traces);
}
size_t len = UTF_8::code_point_count(str, 0, str.size());
if (index > 0 && index <= len) {
// positive and within string length
str.insert(UTF_8::offset_at_position(str, static_cast<size_t>(index) - 1), ins);
}
else if (index > len) {
// positive and past string length
str += ins;
}
else if (index == 0) {
str = ins + str;
}
else if (std::abs(index) <= len) {
// negative and within string length
index += len + 1;
str.insert(UTF_8::offset_at_position(str, static_cast<size_t>(index)), ins);
}
else {
// negative and past string length
str = ins + str;
}
if (String_Quoted* ss = Cast<String_Quoted>(s)) {
if (ss->quote_mark()) str = quote(str);
}
}
// handle any invalid utf8 errors
// other errors will be re-thrown
catch (...) { handle_utf8_error(pstate, traces); }
return SASS_MEMORY_NEW(String_Quoted, pstate, str);
}
Signature str_index_sig = "str-index($string, $substring)";
BUILT_IN(str_index)
{
size_t index = sass::string::npos;
try {
String_Constant* s = ARG("$string", String_Constant);
String_Constant* t = ARG("$substring", String_Constant);
sass::string str = s->value();
sass::string substr = t->value();
size_t c_index = str.find(substr);
if(c_index == sass::string::npos) {
return SASS_MEMORY_NEW(Null, pstate);
}
index = UTF_8::code_point_count(str, 0, c_index) + 1;
}
// handle any invalid utf8 errors
// other errors will be re-thrown
catch (...) { handle_utf8_error(pstate, traces); }
// return something even if we had an error (-1)
return SASS_MEMORY_NEW(Number, pstate, (double)index);
}
Signature str_slice_sig = "str-slice($string, $start-at, $end-at:-1)";
BUILT_IN(str_slice)
{
sass::string newstr;
try {
String_Constant* s = ARG("$string", String_Constant);
double start_at = ARGVAL("$start-at");
double end_at = ARGVAL("$end-at");
if (start_at != (int)start_at) {
sass::ostream strm;
strm << "$start-at: ";
strm << std::to_string(start_at);
strm << " is not an int";
error(strm.str(), pstate, traces);
}
String_Quoted* ss = Cast<String_Quoted>(s);
sass::string str(s->value());
size_t size = utf8::distance(str.begin(), str.end());
if (!Cast<Number>(env["$end-at"])) {
end_at = -1;
}
if (end_at != (int)end_at) {
sass::ostream strm;
strm << "$end-at: ";
strm << std::to_string(end_at);
strm << " is not an int";
error(strm.str(), pstate, traces);
}
if (end_at == 0 || (end_at + size) < 0) {
if (ss && ss->quote_mark()) newstr = quote("");
return SASS_MEMORY_NEW(String_Quoted, pstate, newstr);
}
if (end_at < 0) {
end_at += size + 1;
if (end_at == 0) end_at = 1;
}
if (end_at > size) { end_at = (double)size; }
if (start_at < 0) {
start_at += size + 1;
if (start_at <= 0) start_at = 1;
}
else if (start_at == 0) { ++ start_at; }
if (start_at <= end_at)
{
sass::string::iterator start = str.begin();
utf8::advance(start, start_at - 1, str.end());
sass::string::iterator end = start;
utf8::advance(end, end_at - start_at + 1, str.end());
newstr = sass::string(start, end);
}
if (ss) {
if(ss->quote_mark()) newstr = quote(newstr);
}
}
// handle any invalid utf8 errors
// other errors will be re-thrown
catch (...) { handle_utf8_error(pstate, traces); }
return SASS_MEMORY_NEW(String_Quoted, pstate, newstr);
}
Signature to_upper_case_sig = "to-upper-case($string)";
BUILT_IN(to_upper_case)
{
String_Constant* s = ARG("$string", String_Constant);
sass::string str = s->value();
Util::ascii_str_toupper(&str);
if (String_Quoted* ss = Cast<String_Quoted>(s)) {
String_Quoted* cpy = SASS_MEMORY_COPY(ss);
cpy->value(str);
return cpy;
} else {
return SASS_MEMORY_NEW(String_Quoted, pstate, str);
}
}
Signature to_lower_case_sig = "to-lower-case($string)";
BUILT_IN(to_lower_case)
{
String_Constant* s = ARG("$string", String_Constant);
sass::string str = s->value();
Util::ascii_str_tolower(&str);
if (String_Quoted* ss = Cast<String_Quoted>(s)) {
String_Quoted* cpy = SASS_MEMORY_COPY(ss);
cpy->value(str);
return cpy;
} else {
return SASS_MEMORY_NEW(String_Quoted, pstate, str);
}
}
}
}