1########################################################################
2##
3## Copyright (C) 1996-2021 The Octave Project Developers
4##
5## See the file COPYRIGHT.md in the top-level directory of this
6## distribution or <https://octave.org/copyright/>.
7##
8## This file is part of Octave.
9##
10## Octave is free software: you can redistribute it and/or modify it
11## under the terms of the GNU General Public License as published by
12## the Free Software Foundation, either version 3 of the License, or
13## (at your option) any later version.
14##
15## Octave is distributed in the hope that it will be useful, but
16## WITHOUT ANY WARRANTY; without even the implied warranty of
17## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18## GNU General Public License for more details.
19##
20## You should have received a copy of the GNU General Public License
21## along with Octave; see the file COPYING.  If not, see
22## <https://www.gnu.org/licenses/>.
23##
24########################################################################
25
26## -*- texinfo -*-
27## @deftypefn  {} {} substr (@var{s}, @var{offset})
28## @deftypefnx {} {} substr (@var{s}, @var{offset}, @var{len})
29## Return the substring of @var{s} which starts at character number
30## @var{offset} and is @var{len} characters long.
31##
32## Position numbering for offsets begins with 1.  If @var{offset} is negative,
33## extraction starts that far from the end of the string.
34##
35## If @var{len} is omitted, the substring extends to the end of @var{s}.  A
36## negative value for @var{len} extracts to within @var{len} characters of
37## the end of the string
38##
39## Examples:
40##
41## @example
42## @group
43## substr ("This is a test string", 6, 9)
44##      @result{} "is a test"
45## substr ("This is a test string", -11)
46##      @result{} "test string"
47## substr ("This is a test string", -11, -7)
48##      @result{} "test"
49## @end group
50## @end example
51##
52## This function is patterned after the equivalent function in Perl.
53## @end deftypefn
54
55function t = substr (s, offset, len)
56
57  if (nargin < 2 || nargin > 3)
58    print_usage ();
59  endif
60
61  if (! ischar (s))
62    error ("substr: S must be a string or string array");
63  elseif (! isscalar (offset) || (nargin == 3 && ! isscalar (len)))
64    error ("substr: OFFSET and LEN must be scalar integers");
65  endif
66
67  offset = fix (offset);
68  nc = columns (s);
69  if (abs (offset) > nc || offset == 0)
70    error ("substr: OFFSET = %d out of range", offset);
71  endif
72
73  if (offset <= 0)
74    offset += nc + 1;
75  endif
76
77  if (nargin == 2)
78    eos = nc;
79  else
80    len = fix (len);
81    if (len < 0)
82      eos = nc + len;
83    else
84      eos = offset + len - 1;
85    endif
86  endif
87
88  if (eos > nc)
89    error ("substr: length LEN = %d out of range", len);
90  elseif (offset > eos && len != 0)
91    error ("substr: No overlap with chosen values of OFFSET and LEN");
92  endif
93
94  t = s(:, offset:eos);
95
96endfunction
97
98
99%!assert (substr ("This is a test string", 6, 9), "is a test")
100%!assert (substr ("This is a test string", -11), "test string")
101%!assert (substr ("This is a test string", -11, 4), "test")
102%!assert (substr ("This is a test string", -11, -7), "test")
103%!assert (substr ("This is a test string", 1, -7), "This is a test")
104%!assert (isempty (substr ("This is a test string", 1, 0)))
105
106## Test input validation
107%!error substr ()
108%!error substr ("foo", 2, 3, 4)
109%!error substr (ones (5, 1), 1, 1)
110%!error substr ("foo", ones (2,2))
111%!error substr ("foo", 1, ones (2,2))
112%!error substr ("foo", 0)
113%!error substr ("foo", 5)
114%!error substr ("foo", 1, 5)
115%!error substr ("foo", -1, 5)
116%!error substr ("foo", 2, -5)
117