+
+
+(defun concat (&rest strs)
+ (flet ((concat-two (str1 str2)
+ (concatenate-storage-vector str1 str2)))
+ (!reduce #'concat-two strs "")))
+
+
+(defun string-upcase (string &key (start 0) end)
+ (let* ((string (string string))
+ (new (make-string (length string))))
+ (dotimes (i (length string) new)
+ (aset new i
+ (if (and (or (null start) (>= i start))
+ (or (null end) (< i end)))
+ (char-upcase (char string i))
+ (char string i))))))
+
+(defun nstring-upcase (string &key (start 0) end)
+ (let ((end (or end (length string))))
+ (dotimes (i (- end start) string)
+ (aset string (+ start i)
+ (char-upcase (char string (+ start i)))))))
+
+(defun string-downcase (string &key (start 0) end)
+ (let* ((string (string string))
+ (new (make-string (length string))))
+ (dotimes (i (length string) new)
+ (aset new i
+ (if (and (or (null start) (>= i start))
+ (or (null end) (< i end)))
+ (char-downcase (char string i))
+ (char string i))))))
+
+(defun nstring-downcase (string &key (start 0) end)
+ (let ((end (or end (length string))))
+ (dotimes (i (- end start) string)
+ (aset string (+ start i)
+ (char-downcase (char string (+ start i)))))))
+
+(defun string-capitalize (string &key (start 0) end)
+ (let* ((string (string string))
+ (new (make-string (length string)))
+ (just-saw-alphanum-p nil))
+ (dotimes (i (length string) new)
+ (aset new i
+ (cond ((or (and start (< i start))
+ (and end (> i end)))
+ (char string i))
+ ((or (= i start)
+ (not just-saw-alphanum-p))
+ (char-upcase (char string i)))
+ (t
+ (char-downcase (char string i)))))
+ (setq just-saw-alphanum-p (alphanumericp (char string i))))))
+
+(defun nstring-capitalize (string &key (start 0) end)
+ (let ((end (or end (length string)))
+ (just-saw-alphanum-p nil))
+ (dotimes (i (- end start) string)
+ (aset string (+ start i)
+ (if (or (zerop i)
+ (not just-saw-alphanum-p))
+ (char-upcase (char string (+ start i)))
+ (char-downcase (char string (+ start i)))))
+ (setq just-saw-alphanum-p (alphanumericp (char string (+ start i)))))))
+
+(defun string-equal (s1 s2 &key start1 end1 start2 end2)
+ (let* ((s1 (string s1))
+ (s2 (string s2))
+ (n1 (length s1))
+ (n2 (length s2))
+ (start1 (or start1 0))
+ (end1 (or end1 n1))
+ (start2 (or start2 0))
+ (end2 (or end2 n2)))
+ (when (= (- end2 start2) (- end1 start1))
+ (dotimes (i (- end2 start2) t)
+ (unless (char-equal (char s1 (+ start1 i)) (char s2 (+ start2 i)))
+ (return-from string-equal nil))))))
+
+;; just like string/= but with char-equal instead of char=
+(defun string-not-equal (s1 s2 &key (start1 0) end1 (start2 0) end2)
+ (let* ((s1 (string s1))
+ (s2 (string s2))
+ (n1 (length s1))
+ (n2 (length s2))
+ (end1 (or end1 n1))
+ (end2 (or end2 n2)))
+ (dotimes (i (max (- end1 start1) (- end2 start2)) nil)
+ (when (or (>= (+ start1 i) n1)
+ (>= (+ start2 i) n2))
+ (return-from string-not-equal (+ start1 i)))
+ (unless (char-equal (char s1 (+ start1 i)) (char s2 (+ start2 i)))
+ (return-from string-not-equal (+ start1 i))))))
+
+(defun string-trim (character-bag string)
+ (string-left-trim character-bag (string-right-trim character-bag string)))
+
+(defun string-left-trim (character-bag string)
+ (let* ((string (string string))
+ (n (length string))
+ (start (or (position-if-not (lambda (c) (find c character-bag)) string) n)))
+ (subseq string start)))
+
+(defun string-right-trim (character-bag string)
+ (let* ((string (string string))
+ (n (length string)))
+ (dotimes (i n "")
+ (when (not (find (char string (- n i 1)) character-bag))
+ (return-from string-right-trim (subseq string 0 (- n i)))))))