# HG changeset patch
# User Marshall <marshall@cognifire.com>
# Date 1583243856 -7200
#      Tue Mar 03 15:57:36 2020 +0200
# Node ID c6d8d70466f19a1b3cf1be392c0f0e6418760e5c
# Parent  b02d9109fb6e753c62ff8957c52ab7b6ce6bc2d0
Add new function "contains.m" (bug #56065).

* scripts/strings/contains.m: Add new function.
* scripts/strings/module.mk: Add new function to build system.
* NEWS: Add note about new function.

diff -r b02d9109fb6e -r c6d8d70466f1 NEWS
--- a/NEWS	Tue Oct 20 01:29:00 2020 -0400
+++ b/NEWS	Tue Mar 03 15:57:36 2020 +0200
@@ -157,6 +157,7 @@
 
 ### Alphabetical list of new functions added in Octave 7
 
+* `contains`
 * `getpixelposition`
 * `endsWith`
 * `jsondecode`
diff -r b02d9109fb6e -r c6d8d70466f1 scripts/strings/contains.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/strings/contains.m	Tue Mar 03 15:57:36 2020 +0200
@@ -0,0 +1,123 @@
+########################################################################
+##
+## Copyright (C) 2020 The Octave Project Developers
+##
+## See the file COPYRIGHT.md in the top-level directory of this
+## distribution or <https://octave.org/copyright/>.
+##
+## This file is part of Octave.
+##
+## Octave is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## Octave is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with Octave; see the file COPYING.  If not, see
+## <https://www.gnu.org/licenses/>.
+##
+########################################################################
+
+## -*- texinfo -*-
+## @deftypefn  {} {[@var{retval}] =} contains (@var{str}, @var{pattern})
+## @deftypefnx {} {[@var{retval}] =} contains (@var{str}, @var{pattern}, "IgnoreCase", @var{ignore_case})
+## Check for @var{pattern} within @var{str}.
+##
+## One or both of @var{str} and @var{pattern} may be character arrays or a cell
+## arrays of strings.  They do not have to be the same size.
+##
+## The return value @var{retval} is the same size as @var{str} and is true for
+## each element in @var{str} that contains any of the pattern strings specified.
+## 
+## If @var{ignore_case} is @qcode{true}, the match is case insensitive.  The
+## default value for @var{ignore_case} is @qcode{false}.
+##
+## Examples:
+##
+## @example
+## @group
+## contains ("data.tar.gz", "tar")
+##   @result{} [1]
+## contains ({"abstracts.docx", "data.tar.gz"}, "tar")
+##   @result{} [0 1]
+## contains ("data.tar.gz", {"docx", "tar"})
+##   @result{} [1]
+## contains ({"DATA.TAR.GZ", "SUMMARY.PPT"}, "tar", "ignorecase", true) 
+##   @result{} [1 0]
+## @end group
+## @end example
+## @end deftypefn
+
+function retval = contains (str, pattern, IgnoreCase, ignore_case)
+
+  if (nargin != 2 && nargin != 4)
+    print_usage ();
+  endif
+
+  ## Validate input str and pattern
+  if (! (iscellstr (str) || ischar (str)))
+    error ("Octave:contains:invalid_str", ...
+           "contains: STR must be a string or cell array of strings");
+  endif
+  if (! (iscellstr (pattern) || ischar (pattern)))
+    error ("Octave:contains:invalid_pattern", ...
+           "contains: PATTERN must be a string or cell array of strings");
+  endif
+
+  str = cellstr (str);
+  pattern = cellstr (pattern);
+
+  if (nargin == 2)
+    ignore_case = false;
+  else
+    ## For Matlab compatibility accept any abbreviation of 3rd argument
+    if (! ischar (IgnoreCase) || isempty (IgnoreCase)
+        || ! strncmpi (IgnoreCase, "IgnoreCase", length (IgnoreCase)))
+      error ("Octave:contains:invalid_property", ...
+             'contains: third input must be "IgnoreCase"');
+    endif
+
+    if (! isscalar (ignore_case) || ! isreal (ignore_case))
+      error ("Octave:contains:invalid_ignore_case", ...
+             'contains: "IgnoreCase" value must be a logical scalar');
+    endif
+    ignore_case = logical (ignore_case);
+  endif
+
+  if (ignore_case)
+    str = lower (str);
+    pattern = lower (pattern);
+  endif
+
+  retval = cellfun (@(s) any (cellfun (@(p) ! isempty (p), ...
+                                       strfind (s, pattern))), str);
+
+endfunction
+
+
+%!assert (contains ("data.tar.gz", "tar"), true)
+%!assert (contains ("peppers, onions, and mushrooms", "onion"), true)
+%!assert (contains ("peppers, onions, and mushrooms", "pineapple"), false)
+
+%!assert (contains({"abstracts.docx", "data.tar.gz"}, "tar"), logical ([0, 1]))
+%!assert (contains({"abstracts.docx", "data.tar.gz"}, "zip"), logical ([0, 0]))
+
+%!assert (contains ("data.tar.gz", {"docx", "tar"}), true)
+%!assert (contains ("data.tar.gz", {"7z", "zip"}), false)
+
+%!test
+%! str = {"Mary Ann Jones", 'Christopher Matthew Burns', 'John Paul Smith'};
+%! pattern = {'Ann', 'Paul'};
+%! assert (contains (str, pattern), logical ([1 0 1]))
+%! assert (str(contains (str, pattern)), {'Mary Ann Jones', 'John Paul Smith'})
+
+%!test
+%! str = {'DATA.TAR.GZ','SUMMARY.PPT'};
+%! P = 'tar';
+%! assert (contains (str, P, 'IgnoreCase', true), logical([1, 0]))
+%! assert (contains (str, P, 'IgnoreCase', false), logical([0, 0]))
diff -r b02d9109fb6e -r c6d8d70466f1 scripts/strings/module.mk
--- a/scripts/strings/module.mk	Tue Oct 20 01:29:00 2020 -0400
+++ b/scripts/strings/module.mk	Tue Mar 03 15:57:36 2020 +0200
@@ -5,6 +5,7 @@
   %reldir%/base2dec.m \
   %reldir%/bin2dec.m \
   %reldir%/blanks.m \
+  %reldir%/contains.m \
   %reldir%/cstrcat.m \
   %reldir%/deblank.m \
   %reldir%/dec2base.m \
