]> git.eshelyaron.com Git - emacs.git/commitdiff
string-search robustness and documentation improvement (bug#43598)
authorMattias Engdegård <mattiase@acm.org>
Fri, 25 Sep 2020 15:00:17 +0000 (17:00 +0200)
committerMattias Engdegård <mattiase@acm.org>
Fri, 25 Sep 2020 15:08:00 +0000 (17:08 +0200)
* src/fns.c (Fstring_search): Check START-POS argument range.
Simplify logic.  Improve doc string.
* test/src/fns-tests.el (string-search): Add test cases.
* doc/lispref/strings.texi (Text Comparison): Elaborate.
* lisp/emacs-lisp/byte-opt.el (pure-fns): Mark string-search as pure.

doc/lispref/strings.texi
lisp/emacs-lisp/byte-opt.el
src/fns.c
test/src/fns-tests.el

index 6eb3d6f3100313cb2be6db7930f4a63b82035c40..0f157c39d636e66a01f1b890ed6d20ddf13628b7 100644 (file)
@@ -660,8 +660,10 @@ ignores case differences.
 Return the position of the first instance of @var{needle} in
 @var{haystack}, both of which are strings.  If @var{start-pos} is
 non-@code{nil}, start searching from that position in @var{needle}.
+Return @code{nil} if no match was found.
 This function only considers the characters in the strings when doing
-the comparison; text properties are ignored.
+the comparison; text properties are ignored.  Matching is always
+case-sensitive.
 @end defun
 
 @defun compare-strings string1 start1 end1 string2 start2 end2 &optional ignore-case
index 2c95c870606de86d21c3804a6680c9c41638891d..8a6c0b9a7fabafc096a85d3e0dfaa4bf40320c39 100644 (file)
          floor ceiling round truncate
          ffloor fceiling fround ftruncate
          string= string-equal string< string-lessp
+         string-search
          consp atom listp nlistp propert-list-p
          sequencep arrayp vectorp stringp bool-vector-p hash-table-p
          null not
index 3927e4306e68914beb7860fbf3ed0be420ed37c8..2f64d955760d907e5c69d4331b94d5a8a6d2cd1b 100644 (file)
--- a/src/fns.c
+++ b/src/fns.c
@@ -5456,15 +5456,18 @@ It should not be used for anything security-related.  See
 
 DEFUN ("string-search", Fstring_search, Sstring_search, 2, 3, 0,
        doc: /* Search for the string NEEDLE in the string HAYSTACK.
-The return value is the position of the first instance of NEEDLE in
-HAYSTACK.
+The return value is the position of the first occurrence of NEEDLE in
+HAYSTACK, or nil if no match was found.
 
 The optional START-POS argument says where to start searching in
-HAYSTACK.  If not given, start at the beginning. */)
+HAYSTACK and defaults to zero (start at the beginning).
+It must be between zero and the length of HAYSTACK, inclusive.
+
+Case is always significant and text properties are ignored. */)
   (register Lisp_Object needle, Lisp_Object haystack, Lisp_Object start_pos)
 {
   ptrdiff_t start_byte = 0, haybytes;
-  char *res = NULL, *haystart;
+  char *res, *haystart;
 
   CHECK_STRING (needle);
   CHECK_STRING (haystack);
@@ -5472,7 +5475,10 @@ HAYSTACK.  If not given, start at the beginning. */)
   if (!NILP (start_pos))
     {
       CHECK_FIXNUM (start_pos);
-      start_byte = string_char_to_byte (haystack, XFIXNUM (start_pos));
+      EMACS_INT start = XFIXNUM (start_pos);
+      if (start < 0 || start > SCHARS (haystack))
+        xsignal1 (Qargs_out_of_range, start_pos);
+      start_byte = string_char_to_byte (haystack, start);
     }
 
   haystart = SSDATA (haystack) + start_byte;
@@ -5481,13 +5487,13 @@ HAYSTACK.  If not given, start at the beginning. */)
   if (STRING_MULTIBYTE (haystack) == STRING_MULTIBYTE (needle))
     res = memmem (haystart, haybytes,
                  SSDATA (needle), SBYTES (needle));
-  else if (STRING_MULTIBYTE (haystack) && !STRING_MULTIBYTE (needle))
+  else if (STRING_MULTIBYTE (haystack))  /* unibyte needle */
     {
       Lisp_Object multi_needle = string_to_multibyte (needle);
       res = memmem (haystart, haybytes,
                    SSDATA (multi_needle), SBYTES (multi_needle));
     }
-  else if (!STRING_MULTIBYTE (haystack) && STRING_MULTIBYTE (needle))
+  else                        /* unibyte haystack, multibyte needle */
     {
       Lisp_Object uni_needle = Fstring_as_unibyte (needle);
       res = memmem (haystart, haybytes,
index 8c2b1300dceac9383d8aac6b9a5b1ccbaf07df76..323743d84201c0b3dec6674278ece04e9319056e 100644 (file)
   (should (equal (string-search "foo" "foobarzot") 0))
   (should (not (string-search "fooz" "foobarzot")))
   (should (not (string-search "zot" "foobarzo")))
+  (should (equal (string-search "ab" "ab") 0))
+  (should (equal (string-search "ab\0" "ab") nil))
+  (should (equal (string-search "ab" "abababab" 3) 4))
+  (should (equal (string-search "ab" "ababac" 3) nil))
+  (let ((case-fold-search t))
+    (should (equal (string-search "ab" "AB") nil)))
 
   (should (equal
            (string-search (make-string 2 130)
   (should (not (string-search (make-string 1 255) "a\377ø")))
   (should (not (string-search (make-string 1 255) "a\377a")))
 
-  (should (equal (string-search "fóo" "zotfóo") 3)))
+  (should (equal (string-search "fóo" "zotfóo") 3))
+
+  (should (equal (string-search (string-to-multibyte "\377") "ab\377c") 2))
+  (should (equal (string-search "\303" "aøb") nil))
+  (should (equal (string-search "\270" "aøb") nil))
+  ;; This test currently fails, but it shouldn't!
+  ;;(should (equal (string-search "ø" "\303\270") nil))
+
+  (should-error (string-search "a" "abc" -1))
+  (should-error (string-search "a" "abc" 4))
+  (should-error (string-search "a" "abc" 100000000000))
+
+  (should (equal (string-search "a" "aaa" 3) nil))
+  (should (equal (string-search "\0" "") nil))
+
+  (should (equal (string-search "" "") 0))
+  (should-error (string-search "" "" 1))
+  (should (equal (string-search "" "abc") 0))
+  (should (equal (string-search "" "abc" 2) 2))
+  (should (equal (string-search "" "abc" 3) 3))
+  (should-error (string-search "" "abc" 4))
+  (should-error (string-search "" "abc" -1))
+  )