mcm-0.6.4.10: mcmtags.hs
-- MCM - Machine Configuration Manager; manages the contents of files and directories
-- Copyright (c) 2013-2016 Anthony Doggett <mcm@interfaces.org.uk>
--
-- Licence:
-- 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 3 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, see <http://www.gnu.org/licenses/>.
-- Parses all .mcm files below the current directory
-- (If directories given as arguments, uses those instead)
-- Writes the results to "tags" file (in current directory)
-- Prints warnings for any files that fail to parse
module Main (main)
where
import Parser (mcmParse)
import ParserTypes (Define(..), Section(..), MCMFile(..), DefName(..))
import Paths_mcm (version)
import Control.Monad (filterM, unless)
import Data.Char (isAsciiUpper)
import Data.List (isSuffixOf, intercalate, sort, foldl')
import Data.Version (showVersion)
import qualified Data.Map as Map
import qualified Data.Text.Lazy as T
import qualified Data.Text.Lazy.IO as TextIO
import System.Console.GetOpt
import System.Directory (getDirectoryContents)
import System.Environment (getArgs)
import System.Exit (exitSuccess)
import System.FilePath (joinPath)
import System.Posix.Files (getFileStatus, isDirectory)
usage :: String
usage = unlines ["Usage: mcmtags [DIR..]"
,"Parse all *.mcm files below the current directory,"
,"or if directories given as arguments, below those directories."
,"The resulting tags are written to ./tags."
,"Print warnings for any files that fail to parse."
]
toWarn :: FilePath -> String
toWarn f = "WARNING: failed to parse " ++ f
tryParse :: FilePath -> IO (Maybe MCMFile)
tryParse f = do
ss <- TextIO.readFile f
case mcmParse ss of
Right a -> return $ Just a
Left _ -> return Nothing
makeTag :: FilePath -> String -> String -> String
makeTag f n ex_cmd = n ++ "\t" ++ f ++ "\t" ++ ex_cmd
toTags :: (FilePath, MCMFile) -> [String]
toTags (f, MCMFile pp (Section _ _ ds)) = ppTag:map dsTag (Map.elems ds)
where
ppTag = makeTag f (lastBitOfPath (show pp)) "/^MCM/"
dsTag d = makeTag f (strDefName d) ("/^define " ++ strDefName d ++ "(/")
lastBitOfPath :: String -> String
lastBitOfPath = reverse . takeWhile (/= '.') . reverse
strDefName = T.unpack . fromDefName . defName
isDirectory' :: FilePath -> IO Bool
isDirectory' f = do
s <- getFileStatus f
return $ isDirectory s
findMcm :: FilePath -> IO [FilePath]
findMcm p = do
allfiles <- getDirectoryContents p
let fs = [f | f <- allfiles, f `notElem` [".", ".."]]
ms = [joinPath [p,f] | f <- fs, ".mcm" `isSuffixOf` f]
ds = [joinPath [p,f] | f <- fs, isAsciiUpper (head f), '.' `notElem` f]
dirs <- filterM isDirectory' ds
children <- mapM findMcm dirs
return $ ms ++ concat children
splitParsed :: [(FilePath, Maybe MCMFile)] -> ([FilePath], [(FilePath, MCMFile)])
splitParsed [] = ([], [])
splitParsed ((fp, m):xs) =
let (as, bs) = splitParsed xs
in case m of
Nothing -> (fp:as, bs)
Just f -> (as, (fp, f):bs)
main :: IO ()
main = do
args <- getArgs
let (actions, nonOpts, msgs) = getOpt Permute options args
unless (null msgs) $ error $ concat msgs ++ usageInfo usage options
_ <- foldl' (>>=) (return defaultOptions) actions
let toRecurse = case nonOpts of
[] -> ["."]
_ -> args
toParse <- mapM findMcm toRecurse
let toParse' = concat toParse
parsed <- mapM tryParse toParse'
let fparsed = zip toParse' parsed
let (bad, good) = splitParsed fparsed
tags = sort $ concatMap toTags good
mapM_ (putStrLn . toWarn) bad
writeFile "tags" $ intercalate "\n" tags
return ()
data Options = Options {}
defaultOptions :: Options
defaultOptions = Options {}
options :: [OptDescr (Options -> IO Options)]
options = [Option "V" ["version"] (NoArg displayVersion) "show version and exit"
,Option "h" ["help"] (NoArg justHelp) "show this help and exit"
]
displayVersion :: Options -> IO Options
displayVersion _ = do
putStrLn $ "mcmtags " ++ showVersion version
exitSuccess
justHelp :: Options -> IO Options
justHelp _ = do
putStrLn $ usageInfo usage options
exitSuccess