
(********************************************************************)
(*                                                                  *)
(*  find7.sd7     Utility to search for files and file contents.    *)
(*  Copyright (C) 2016  Thomas Mertes                               *)
(*                                                                  *)
(*  This program 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 2 of  *)
(*  the License, or (at your option) any later version.             *)
(*                                                                  *)
(*  This program 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 this program; if not, write to the           *)
(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
(*                                                                  *)
(********************************************************************)


$ include "seed7_05.s7i";
  include "osfiles.s7i";
  include "console.s7i";
  include "getf.s7i";


const proc: writeMatchingLines (in string: filePath, in string: stringSearched) is func
  local
    var string: content is "";
    var integer: pos is 0;
    var integer: startPos is 0;
    var integer: endPos is 0;
  begin
    content := getf(filePath);
    pos := pos(content, stringSearched);
    if pos <> 0 then
      writeln(filePath <& ":");
      startPos := rpos(content, '\n', pos);
      incr(startPos);
      endPos := pos(content, '\n', pos);
      if endPos = 0 then
        endPos := length(content);
      else
        decr(endPos);
      end if;
      writeln(content[startPos .. endPos]);
    end if;
  end func;


const proc: searchDir (in string: dirPath,
    in string: partOfFileName, in string: stringSearched) is func
  local
    var array string: dirElements is 0 times "";
    var string: fileName is "";
    var string: filePath is "";
  begin
    block
      dirElements := readDir(dirPath);
    exception
      catch FILE_ERROR:
        writeln(" *** Cannot read directory " <& dirPath);
    end block;
    for fileName range dirElements do
      if dirPath = "/" then
        filePath := "/" & fileName;
      else
        filePath := dirPath & "/" & fileName;
      end if;
      if partOfFileName = "" or
          pos(lower(fileName), partOfFileName) <> 0 then
        if stringSearched = "" then
          writeln(filePath);
        else
          writeMatchingLines(filePath, stringSearched);
        end if;
      end if;
      case fileType(filePath) of
        when {FILE_DIR}:
          if fileTypeSL(filePath) = FILE_SYMLINK and
              startsWith(dirPath, toAbsPath(dirPath, readlink(filePath))) then
            writeln(" *** Symbolic link loop " <& filePath);
          else
            searchDir(filePath, partOfFileName, stringSearched);
          end if;
        when {FILE_SYMLINK}:
          writeln(" *** Cannot follow symbolic link " <& filePath);
      end case;
    end for;
  end func;


const proc: main is func
  local
    var string: dirPath is "";
    var string: partOfFileName is "";
    var string: stringSearched is "";
  begin
    OUT := STD_CONSOLE;
    if length(argv(PROGRAM)) < 2 then
      writeln("Find7 Version 1.0 - Utility to search for files and file contents.");
      writeln("Copyright (C) 2016 Thomas Mertes");
      writeln("This is free software; see the source for copying conditions.  There is NO");
      writeln("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.");
      writeln("Find7 is written in the Seed7 programming language");
      writeln("Homepage: http://seed7.sourceforge.net");
      writeln;
      writeln("usage: find7 directory partOfFileName [stringSearched]");
      writeln;
      writeln("Examples:");
      writeln("  find7 . .sd7");
      writeln("     Search for files with a name that contains .sd7 in the current");
      writeln("     working directory and its subdirectories.");
      writeln("  find7 ../lib .s7i decompose");
      writeln("     Search if .s7i files in the ../lib directory contain the word");
      writeln("     decompose.");
      writeln("  find7 ../doc \"\" \"Seed7 program\"");
      writeln("     Search if any file in the ../doc directory contains the phrase");
      writeln("     Seed7 program.");
    else
      dirPath := convDosPath(argv(PROGRAM)[1]);
      partOfFileName := lower(argv(PROGRAM)[2]);
      if length(argv(PROGRAM)) >= 3 then
        stringSearched := argv(PROGRAM)[3];
      end if;
      searchDir(dirPath, partOfFileName, stringSearched);
    end if;
  end func;
