(in-package :c2ffi) (defun string-replace (source from to &key all) "Returns a new string where substrings of SOURCE that match FROM are replaced with TO. All arguments should be strings, case-sensitive." (let* ((match (string-find source from))) (if match (let ((pre (subseq source 0 match)) (post (subseq source (+ match (length from))))) (str+ pre to (if all (string-replace post from to :all t) post))) source))) (defun string-find (source target &key all (case-sensitive t) (test (if case-sensitive #'string= #'string-equal))) "Returns the index of SOURCE where the substring TARGET first appears. Arguments should be strings, case-sensitive. If ALL is true, then a list of all such indices is returned. This is currently a non-optimized implementation (TODO: replace with Boyer-moore algorithm)." (loop with target-length = (length target) for i upto (- (length source) target-length) if (funcall test (subseq source i (+ i target-length)) target) if all collect i else return i)) (defun str+ (&rest strings) "Returns a concatenation of every argument coerced into strings as by princ-to-string." (reduce #'(lambda (combined str) (concatenate 'string combined (princ-to-string str))) strings :initial-value "")) (defun list-to-delimited-string (strings &optional (delim ", ")) "Returns a string where each element of strings has been coerced into a string by princ-to-string and concatenated with delim between each item." (reduce #'str+ (cdr (mapcan #'(lambda (str) (list delim str)) strings)) :initial-value ""))