changeset 25698:ac386820f2b6

Return true when strncmp matches, but number of characters exceeds strlen (bug #54373) * NEWS: Announce change in behavior. * oct-string.cc (strncmp, strncmpi): Change templates to check length of strings against neff (N effective) which is the smaller of the actual string length or N itself. * strfns.cc: Add new BIST tests, marked as regressions, for this behavior. Change existing BIST tests to pass. * publish.m (is_publish_markup): New nested function replacing an anonymous function. This version does not index past the length of the string when checking if the N+1 character is a space.
author Rik <rik@octave.org>
date Wed, 25 Jul 2018 10:55:46 -0700
parents 91f0416c2ba7
children 825e1d0dfa8e
files NEWS libinterp/corefcn/strfns.cc liboctave/util/oct-string.cc scripts/miscellaneous/publish.m
diffstat 4 files changed, 58 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Fri Jul 27 18:15:17 2018 -0400
+++ b/NEWS	Wed Jul 25 10:55:46 2018 -0700
@@ -10,6 +10,14 @@
     with Matlab.  Change all uses of "ascending" and "descending" in
     existing code to the new options.
 
+ ** The strncmp and strncmpi functions now return true if the two input
+    strings match, even though the number of characters specified by N
+    exceeds the string length.  This behavior more closely matches
+    common sense and is Matlab compatible.  Example:
+
+      Octave 5.0 : strncmp ("abc", "abc", 100) => true
+      Previously : strncmp ("abc", "abc", 100) => false
+
  ** Figure graphic objects have a new property "Number" which is
     read-only and will return the handle (number) of the figure.
     However, if the property "IntegerHandle" has been set to "off" then
--- a/libinterp/corefcn/strfns.cc	Fri Jul 27 18:15:17 2018 -0400
+++ b/libinterp/corefcn/strfns.cc	Wed Jul 25 10:55:46 2018 -0700
@@ -658,11 +658,13 @@
 %!assert (strncmp ("abce", "aBc", 3), false)
 %!assert (strncmp (100, 100, 1), false)
 %!assert (strncmp ("abce", {"abcd", "bca", "abc"}, 3), logical ([1, 0, 1]))
-%!assert (strncmp ("abc",  {"abcd", "bca", "abc"}, 4), logical ([0, 0, 0]))
+%!assert (strncmp ("abc",  {"abcd", "bca", "abc"}, 4), logical ([0, 0, 1]))
 %!assert (strncmp ({"abcd", "bca", "abc"},"abce", 3), logical ([1, 0, 1]))
 %!assert (strncmp ({"abcd", "bca", "abc"},{"abcd", "bca", "abe"}, 3), logical ([1, 1, 0]))
 %!assert (strncmp ("abc", {"abcd", 10}, 2), logical ([1, 0]))
 
+%!assert <*54373> (strncmp ("abc", "abc", 100))
+
 %!error strncmp ()
 %!error strncmp ("abc", "def")
 */
@@ -733,6 +735,8 @@
 
 /*
 %!assert (strncmpi ("abc123", "ABC456", 3), true)
+
+%!assert <*54373> (strncmpi ("abc", "abC", 100))
 */
 
 DEFUN (__native2unicode__, args, ,
--- a/liboctave/util/oct-string.cc	Fri Jul 27 18:15:17 2018 -0400
+++ b/liboctave/util/oct-string.cc	Wed Jul 25 10:55:46 2018 -0700
@@ -146,8 +146,13 @@
 octave::string::strncmp (const T& str_a, const T& str_b,
                          const typename T::size_type n)
 {
-  return (numel (str_a) >= n && numel (str_b) >= n
-          && str_data_cmp<T> (str_a.data (), str_b.data (), n));
+  typename T::size_type neff;
+  auto len_a = numel (str_a);
+  auto len_b = numel (str_b);
+  neff = std::min (std::max (len_a, len_b), n); 
+
+  return (len_a >= neff && len_b >= neff
+          && str_data_cmp<T> (str_a.data (), str_b.data (), neff));
 }
 
 template<typename T>
@@ -155,8 +160,13 @@
 octave::string::strncmp (const T& str_a, const typename T::value_type *str_b,
                          const typename T::size_type n)
 {
-  return (numel (str_a) >= n && strlen<T> (str_b) >= n
-          && str_data_cmp<T> (str_a.data (), str_b, n));
+  typename T::size_type neff;
+  auto len_a = numel (str_a);
+  auto len_b = strlen<T> (str_b);
+  neff = std::min (std::max (len_a, len_b), n); 
+
+  return (len_a >= neff && len_b >= neff
+          && str_data_cmp<T> (str_a.data (), str_b, neff));
 }
 
 
@@ -165,8 +175,13 @@
 octave::string::strncmpi (const T& str_a, const T& str_b,
                           const typename T::size_type n)
 {
-  return (numel (str_a) >= n && numel (str_b) >= n
-          && str_data_cmpi<T> (str_a.data (), str_b.data (), n));
+  typename T::size_type neff;
+  auto len_a = numel (str_a);
+  auto len_b = numel (str_b);
+  neff = std::min (std::max (len_a, len_b), n); 
+
+  return (len_a >= neff && len_b >= neff
+          && str_data_cmpi<T> (str_a.data (), str_b.data (), neff));
 }
 
 template<typename T>
@@ -174,8 +189,13 @@
 octave::string::strncmpi (const T& str_a, const typename T::value_type *str_b,
                           const typename T::size_type n)
 {
-  return (numel (str_a) >= n && strlen<T> (str_b) >= n
-          && str_data_cmpi<T> (str_a.data (), str_b, n));
+  typename T::size_type neff;
+  auto len_a = numel (str_a);
+  auto len_b = strlen<T> (str_b);
+  neff = std::min (std::max (len_a, len_b), n); 
+
+  return (len_a >= neff && len_b >= neff
+          && str_data_cmpi<T> (str_a.data (), str_b, neff));
 }
 
 
--- a/scripts/miscellaneous/publish.m	Fri Jul 27 18:15:17 2018 -0400
+++ b/scripts/miscellaneous/publish.m	Wed Jul 25 10:55:46 2018 -0700
@@ -425,9 +425,23 @@
   ##
   ## Checks line to have N "%" or "#" lines
   ## followed either by a space or end of string
-  is_publish_markup = @(cstr, N) ...
-    any (strncmp (char (cstr), {"%%%", "##"}, N)) ...
-    && ((length (char (cstr)) == N) || ((char (cstr))(N + 1) == " "));
+  function r = is_publish_markup (cstr, N)
+    str = char (cstr);
+
+    r = any (strncmp (str, {"%%%", "##"}, N));
+    if (r)
+      len = length (str);
+      if (len == N)
+        r = true;
+      elseif (len > N && str(N+1) == " ")
+        r = true;
+      else
+        r = false;
+      endif
+    endif
+
+    return;
+  endfunction
   ## Checks line of cellstring to be a paragraph line
   is_paragraph = @(cstr) is_publish_markup (cstr, 1);
   ## Checks line of cellstring to be a section headline