packages feed

anagrep-0.1.0.1: src/anagrep.hs

{-# LANGUAGE RecordWildCards #-}

import qualified Data.ByteString.Lazy.Char8 as BSLC
import           Data.CaseInsensitive (FoldCase, foldCase)
import qualified System.Console.GetOpt as Opt
import           System.Environment (getArgs, getProgName)
import           System.Exit (exitFailure)
import           System.IO (hPutStrLn, stderr)

import Text.Regex.Anagram

data Opts = Opts
  { optText, optCI :: Bool }

defOpts :: Opts
defOpts = Opts True False

options :: [Opt.OptDescr (Opts -> Opts)]
options =
  [ Opt.Option "b" []
      (Opt.NoArg (\o -> o{ optText = False }))
      "treat input as raw byte sequence (uses locale encoding by default)"
  , Opt.Option "i" []
      (Opt.NoArg (\o -> o{ optCI = True }))
      "ignore case distinctions in patterns and data"
  ]

main :: IO ()
main = do
  prog <- getProgName
  args <- getArgs
  (Opts{..}, pat, files) <- case Opt.getOpt Opt.Permute options args of
    (o, r:f, []) -> case makeAnagrex r of
      Right p -> return (foldl (flip ($)) defOpts o, p, f)
      Left e -> do
        hPutStrLn stderr e
        exitFailure
    (_, _, e) -> do
      mapM_ (hPutStrLn stderr) e
      hPutStrLn stderr $ Opt.usageInfo ("Usage: " ++ prog ++ " REGEXP [FILE]...\n\
\Print lines in each FILE (or stdin) for which some permutation (anagram)\n\
\matches the given REGEXP.  REGEXP is a restricted regular expression that can\n\
\contain the following patterns:\n\
\  Character matches\n\
\    x         single literal character\n\
\    [aein-z]  character set (any listed character)\n\
\    [^a-mou]  negated character set (any character not listed)\n\
\    .         any single character\n\
\    \\x        escape single literal character (no special meanings)\n\
\  Repeat modifiers - may only be applied to characters (above)\n\
\    {N,M}     repeat character N-M times\n\
\    {N,}      repeat character at least N times\n\
\    {N}       equivalent to {N,N}\n\
\    ?         equivalent to {0,1}\n\
\    *         equivalent to {0,}\n\
\    +         equivalent to {1,}\n\
\  Combination\n\
\    XY        concatenation matches pattern X and Y in either order\n\
\    X|Y       alternation matches pattern X or Y\n\
\    (X)       grouping (only useful for alternation - note that successive\n\
\              grouped alternations involve a cross-product expansion and may\n\
\              be slow)\n\
\Other regular expression features are not currently supported.  Matching is\n\
\always done on entire lines (like grep -x).\n\
\\n\
\Example: " ++ prog ++ " 'pq[aeiou]{4,}' /usr/share/dict/words\n\
\  > opaque\n\
\\n\
\Flags:") options
      exitFailure
  let ci :: FoldCase a => a -> a
      ci
        | optCI = foldCase
        | otherwise = id
      grex = ci pat
      test = testAnagrex grex . ci
      proc f
        | optText = mapM_ putStrLn . filter test . lines =<< maybe getContents readFile f
        | otherwise = mapM_ BSLC.putStrLn . filter (test . BSLC.unpack) . BSLC.lines =<< maybe BSLC.getContents BSLC.readFile f
  case files of
    [] -> proc Nothing
    _ -> mapM_ (proc . Just) files