\begin{code} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE CPP #-} #if __GLASGOW_HASKELL__ >= 800 {-# OPTIONS_GHC -fno-warn-redundant-constraints #-} #endif module Text.RE.Tools.Find ( -- * Find -- $tutorial FindMethods(..) , findMatches_ , findMatches_' -- * IsRegex , IsRegex(..) , SearchReplace(..) , searchReplaceAll , searchReplaceFirst -- * Replace , module Text.RE.Replace ) where import qualified Data.List as L import Prelude.Compat import Text.RE.Replace import Text.RE.Tools.IsRegex \end{code} \begin{code} -- | as we don't want the @directory@ and FilePath dependencies -- we will abstract the three calls we need into this record type data FindMethods s = FindMethods { doesDirectoryExistDM :: s -> IO Bool -- ^ doesDirectoryExist from -- System.Directory , listDirectoryDM :: s -> IO [s] -- ^ either getDirectoryContents -- or listDirectory from -- System.Directory , combineDM :: s -> s -> s -- ^ from System.FilePath } \end{code} \begin{code} -- | recursively list all files whose filename matches given RE, -- sorting the list into ascending order; if the argument path has a -- trailing '/' then it will be removed findMatches_ :: IsRegex re s => FindMethods s -> re -> s -> IO [s] findMatches_ fm = findMatches_' fm L.sort matched -- | recursively list all files whose filename matches given RE, -- using the given function to determine which matches to accept findMatches_' :: IsRegex re s => FindMethods s -- ^ the directory and filepath methods -> ([s]->[s]) -- ^ result post-processing function -> (Match s->Bool) -- ^ filtering function -> re -- ^ re to be matched against the leaf filename -> s -- ^ root directory of the search -> IO [s] findMatches_' fm srt tst re fp = srt <$> find_ fm tst re (packR "") fp find_ :: IsRegex re s => FindMethods s -> (Match s->Bool) -> re -> s -> s -> IO [s] find_ fm@FindMethods{..} tst re fn fp = do is_dir <- doesDirectoryExistDM fp case is_dir of True -> do fns <- filter ordinary <$> listDirectoryDM fp concat <$> mapM (uncurry $ find_ fm tst re) [ (fn_,abs_path fn_) | fn_<-fns ] False -> return [ fp | lengthR fp /= 0 && tst (matchOnce re fn) ] where abs_path fn_ = fp `combineDM` fn_ ordinary fn_ = not $ fn_ `elem` [packR ".",packR ".."] \end{code} \begin{code} -- $tutorial -- The Find toolkit traverses directory trees invoking actions for each -- file that matches a RE. -- -- See the Regex Tools tutorial at http://re-tutorial-tools.regex.uk \end{code}