(********************************************************************)
(*                                                                  *)
(*  wildcard.s7i  Wild card match and find matching files.          *)
(*  Copyright (C) 2010 - 2014, 2017  Thomas Mertes                  *)
(*                                                                  *)
(*  This file is part of the Seed7 Runtime Library.                 *)
(*                                                                  *)
(*  The Seed7 Runtime Library is free software; you can             *)
(*  redistribute it and/or modify it under the terms of the GNU     *)
(*  Lesser General Public License as published by the Free Software *)
(*  Foundation; either version 2.1 of the License, or (at your      *)
(*  option) any later version.                                      *)
(*                                                                  *)
(*  The Seed7 Runtime Library 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 Lesser General Public License for more    *)
(*  details.                                                        *)
(*                                                                  *)
(*  You should have received a copy of the GNU Lesser General       *)
(*  Public License along with this program; if not, write to the    *)
(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
(*                                                                  *)
(********************************************************************)


(**
 *  Check if the [[string]] stri matches with ''pattern''.
 *  The pattern may contain wildcard characters.
 *  * The asterisk * matches zero or more characters.
 *  * The question mark ? matches exactly one character.
 *  @return TRUE, if stri is matched by pattern,
 *          FALSE otherwise.
 *)
const func boolean: wildcardMatch (in string: stri, in string: pattern) is func
  result
    var boolean: doesMatch is FALSE;
  local
    var integer: length is 0;
    var integer: index is 1;
    var string: patternTail is "";
  begin
    if pattern = "" then
      doesMatch := stri = "";
    else
      case pattern[1] of
        when {'*'}:
          if pattern = "*" then
            doesMatch := TRUE;
          else
            length := length(stri);
            patternTail := pattern[2 .. ];
            while index <= length and not doesMatch do
              doesMatch := wildcardMatch(stri[index .. ], patternTail);
              incr(index);
            end while;
          end if;
        when {'?'}:
          if stri <> "" then
            doesMatch := wildcardMatch(stri[2 .. ], pattern[2 .. ]);
          end if;
        otherwise:
          if stri <> "" and stri[1] = pattern[1] then
            doesMatch := wildcardMatch(stri[2 .. ], pattern[2 .. ]);
          end if;
      end case;
    end if;
  end func;


(**
 *  Determine which file paths that match a given ''pattern''.
 *  The pattern may contain wildcard characters.
 *  * The asterisk * matches zero or more characters.
 *  * The question mark ? matches exactly one character.
 *  @param pattern File name pattern (e.g.: *.sd7) or path
 *         followed by a file name pattern (e.g.: prg/*.sd7).
 *  @param caseSensitive TRUE if the match is case sensitive,
 *         FALSE otherwise.
 *  @return array of matching file paths.
 *)
const func array string: findMatchingFiles (in string: pattern,
    in boolean: caseSensitive) is func
  result
    var array string: matchingFiles is 0 times "";
  local
    var integer: slashPos is 0;
    var string: path is "";
    var array string: dirContent is 0 times "";
    var string: filePattern is "";
    var string: fileName is "";
  begin
    slashPos := rpos(pattern, '/');
    block
      if slashPos <> 0 then
        path := pattern[.. pred(slashPos)];
        filePattern := pattern[succ(slashPos) ..];
        dirContent := readDir(path);
        path &:= "/";
      else
        filePattern := pattern;
        dirContent := readDir(".");
      end if;
    exception
      catch FILE_ERROR: noop;
    end block;
    if caseSensitive then
      for fileName range dirContent do
        if wildcardMatch(fileName, filePattern) then
          matchingFiles &:= path & fileName;
        end if;
      end for;
    else
      filePattern := lower(filePattern);
      for fileName range dirContent do
        if wildcardMatch(lower(fileName), filePattern) then
          matchingFiles &:= path & fileName;
        end if;
      end for;
    end if;
  end func;


(**
 *  Determine which file paths that match a given ''pattern''.
 *  The pattern may contain wildcard characters.
 *  * The asterisk * matches zero or more characters.
 *  * The question mark ? matches exactly one character.
 *  @param pattern File name pattern (e.g.: *.sd7) or path
 *         followed by a file name pattern (e.g.: prg/*.sd7).
 *  @return array of matching file paths.
 *)
const func array string: findMatchingFiles (in string: pattern) is
    return findMatchingFiles(pattern, TRUE);