77#include " to_string.hpp"
88#include " inspect.hpp"
99#include " eval.hpp"
10+ #include " utf8_string.hpp"
1011
1112#include < cmath>
1213#include < cctype>
@@ -695,7 +696,6 @@ namespace Sass {
695696 {
696697 String_Constant* s = ARG (" $string" , String_Constant);
697698 string str = s->value ();
698- size_t len = 0 ;
699699 size_t length_of_s = str.size ();
700700 size_t i = 0 ;
701701
@@ -704,27 +704,128 @@ namespace Sass {
704704 --length_of_s;
705705 }
706706
707- while (i < length_of_s) {
708- unsigned char c = static_cast <unsigned char >(str[i]);
709- if (c < 128 ) {
710- // it's a single-byte character
711- ++len;
712- ++i;
713- }
714- // it's a multi bit sequence and presumably it's a leading bit
715- else {
716- ++i; // go to the next byte
717- // see if it's still part of the sequence
718- while ((i < length_of_s) && ((static_cast <unsigned char >(str[i]) & 0b11000000 ) == 0b10000000 )) {
719- ++i;
720- }
721- // when it's not [aka a new leading bit], increment and move on
722- ++len;
723- }
724- }
707+ size_t len = UTF_8::code_point_count (str, i, length_of_s);
708+
725709 return new (ctx.mem ) Number (path, position, len);
726710 }
727711
712+ Signature str_insert_sig = " str-insert($string, $insert, $index)" ;
713+ BUILT_IN (str_insert)
714+ {
715+ String_Constant* s = ARG (" $string" , String_Constant);
716+ string str = s->value ();
717+ char quotemark = s->quote_mark ();
718+ str = unquote (str);
719+ String_Constant* i = ARG (" $insert" , String_Constant);
720+ string ins = i->value ();
721+ ins = unquote (ins);
722+ Number* ind = ARG (" $index" , Number);
723+ double index = ind->value ();
724+ size_t len = UTF_8::code_point_count (str, 0 , str.size ());
725+
726+ if (index > 0 && index <= len) {
727+ // positive and within string length
728+ str.insert (UTF_8::code_point_offset_to_byte_offset (str, index-1 ), ins);
729+ }
730+ else if (index > len) {
731+ // positive and past string length
732+ str += ins;
733+ }
734+ else if (index == 0 ) {
735+ str = ins + str;
736+ }
737+ else if (std::abs (index) <= len) {
738+ // negative and within string length
739+ index += len + 1 ;
740+ str.insert (UTF_8::code_point_offset_to_byte_offset (str, index), ins);
741+ }
742+ else {
743+ // negative and past string length
744+ str = ins + str;
745+ }
746+
747+ if (quotemark) {
748+ str = quote (str, quotemark);
749+ }
750+
751+ return new (ctx.mem ) String_Constant (path, position, str);
752+
753+ }
754+
755+ Signature str_index_sig = " str-index($string, $substring)" ;
756+ BUILT_IN (str_index)
757+ {
758+ String_Constant* s = ARG (" $string" , String_Constant);
759+ String_Constant* t = ARG (" $substring" , String_Constant);
760+ string str = s->value ();
761+ str = unquote (str);
762+ string substr = t->value ();
763+ substr = unquote (substr);
764+
765+ size_t c_index = str.find (substr);
766+ if (c_index == string::npos) {
767+ return new (ctx.mem ) Null (path, position);
768+ }
769+ size_t index = UTF_8::code_point_count (str, 0 , c_index + 1 );
770+
771+ return new (ctx.mem ) Number (path, position, index);
772+ }
773+
774+ Signature str_slice_sig = " str-slice($string, $start-at, $end-at:-1)" ;
775+ BUILT_IN (str_slice)
776+ {
777+ String_Constant* s = ARG (" $string" , String_Constant);
778+ Number* n = ARG (" $start-at" , Number);
779+ Number* m = ARG (" $end-at" , Number);
780+
781+ string str = s->value ();
782+ char quotemark = s->quote_mark ();
783+ str = unquote (str);
784+
785+ // normalize into 0-based indices
786+ size_t start = UTF_8::code_point_offset_to_byte_offset (str, UTF_8::normalize_index (n->value (), UTF_8::code_point_count (str)));
787+ size_t end = UTF_8::code_point_offset_to_byte_offset (str, UTF_8::normalize_index (m->value (), UTF_8::code_point_count (str)));
788+
789+ string newstr;
790+ if (start - end == 0 ) {
791+ newstr = str.substr (start, end - start);
792+ } else {
793+ newstr = str.substr (start, end - start + UTF_8::length_of_code_point_at (str, end));
794+ }
795+ if (quotemark) {
796+ newstr = quote (newstr, quotemark);
797+ }
798+
799+ return new (ctx.mem ) String_Constant (path, position, newstr);
800+
801+ }
802+
803+ Signature to_upper_case_sig = " to-upper-case($string)" ;
804+ BUILT_IN (to_upper_case)
805+ {
806+ String_Constant* s = ARG (" $string" , String_Constant);
807+ string str = s->value ();
808+
809+ for (size_t i = 0 , L = str.length (); i < L; ++i) {
810+ str[i] = std::toupper (str[i]);
811+ }
812+
813+ return new (ctx.mem ) String_Constant (path, position, str);
814+ }
815+
816+ Signature to_lower_case_sig = " to-lower-case($string)" ;
817+ BUILT_IN (to_lower_case)
818+ {
819+ String_Constant* s = ARG (" $string" , String_Constant);
820+ string str = s->value ();
821+
822+ for (size_t i = 0 , L = str.length (); i < L; ++i) {
823+ str[i] = std::tolower (str[i]);
824+ }
825+
826+ return new (ctx.mem ) String_Constant (path, position, str);
827+ }
828+
728829 // /////////////////
729830 // NUMBER FUNCTIONS
730831 // /////////////////
0 commit comments