clean-home (empty) → 0.0.1
raw patch · 14 files changed
+518/−0 lines, 14 filesdep +HSHdep +IfElsedep +basesetup-changed
Dependencies added: HSH, IfElse, base, cmdargs, containers, directory
Files
- LICENSE +30/−0
- Setup.hs +2/−0
- clean-home.cabal +79/−0
- data/PkgList +46/−0
- data/PrgList +47/−0
- src/Args.hs +70/−0
- src/Control/Exception/Extra.hs +13/−0
- src/Env.hs +59/−0
- src/Main.hs +65/−0
- src/SearchCommand/Apt.hs +18/−0
- src/SearchCommand/UserSet.hs +54/−0
- src/SearchCommand/Which.hs +9/−0
- src/SearchCommands.hs +17/−0
- src/System/Err.hs +9/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c)2012, Ivan Perez - Keera Studios++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Ivan Perez nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ clean-home.cabal view
@@ -0,0 +1,79 @@+-- clean-home.cabal auto-generated by cabal init. For additional+-- options, see+-- http://www.haskell.org/cabal/release/cabal-latest/doc/users-guide/authors.html#pkg-descr.+-- The name of the package.+Name: clean-home++-- The package version. See the Haskell package versioning policy+-- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for+-- standards guiding when and how versions should be incremented.+Version: 0.0.1++-- A short (one-line) description of the package.+Synopsis: Keep your home dir clean by finding old conf files+Description: clean-home searches for config files in your $HOME+ that no installed package/program will need.++-- URL for the project homepage or repository.+Homepage: https://github.com/ivanperez-keera/clean-home++-- The license under which the package is released.+License: BSD3++-- The file containing the license text.+License-file: LICENSE++-- The package author(s).+Author: Ivan Perez++-- An email address to which users can send suggestions, bug reports,+-- and patches.+Maintainer: ivan.perez@keera.es++-- A copyright notice.+-- Copyright: ++Category: System++Build-type: Simple++Data-files: data/PkgList+ , data/PrgList++-- Extra files to be distributed with the package, such as examples or+-- a README.+-- Extra-source-files: ++-- Constraint on the version of Cabal needed to build this package.+Cabal-version: >=1.6+++Executable clean-home+ Main-is: Main.hs+ hs-source-dirs: src/+ ghc-options: -Wall+ + -- Packages needed in order to build this package.+ Build-depends: base >= 4 && <= 5+ , cmdargs >= 0.9+ , HSH >= 2.0+ , directory+ , containers >= 0.4+ , IfElse+ + -- Modules not exported by this package.+ Other-modules: SearchCommand.Apt+ , SearchCommand.Which+ , SearchCommand.UserSet+ , SearchCommands+ , Args+ , Env+ , Control.Exception.Extra+ , System.Err+ + -- Extra tools (e.g. alex, hsc2hs, ...) needed to build the source.+ -- Build-tools: + +source-repository head+ type: git+ location: http://github.com/ivanperez-keera/clean-home
+ data/PkgList view
@@ -0,0 +1,46 @@+[ (".cabal", [ "cabal", "cabal-install" ])+, (".emacs", [ "emacs", "emacs21", "emacs22", "emacs23" ]) +, (".emacs.d", [ "emacs", "emacs21", "emacs22", "emacs23"])+, (".mozilla", [ "firefox", "iceweasel" ])+, (".lftp", [ "lftp" ])+, (".mission-control", [ "mission-control" ])+, (".viminfo", [ "vim", "vim-gnome" ])+, (".vim", [ "vim", "vim-gnome" ])+, (".vimrc", [ "vim", "vim-gnome" ])+, (".wine", [ "wine", "wine1.2", "wine1.3" ])+, (".eclipse", [ "eclipse" ])+, (".kde", [ "kde" ])+, (".libreoffice", [ "libreoffice" ])+, (".openoffice.org", [ "openoffice" ])+, (".VirtualBox", [ "virtualbox-ose" ])+, (".ssh", [ "openssh-client" ])+, (".xmind", [ "xmind" ])+, (".gSTM", [ "gstm" ])+, (".bashrc", [ "bash" ])+, (".bash_logout", [ "bash" ])+, (".bash_profile", [ "bash" ])+, (".bash_history", [ "bash" ])+, (".gimp-2.6", [ "gimp" ])+, (".hgrc", [ "mercurial" ])+, (".gconfd", [ "gconf2" ])+, (".subversion", [ "subversion" ])+, (".lyx", [ "lyx" ])+, (".thunderbird", [ "thunderbird" ])+, (".xine", [ "xine-ui" ])+, (".lesshst", [ "less" ])+, (".gitconfig", [ "git" ])+, (".Skype", [ "skype" ])+, (".gem", [ "rubygems" ])+, (".mplayer", [ "mplayer" ])+, (".aptitude", [ "aptitude" ])+, (".sqlite_history", [ "sqlite3" ])+, (".shotwell", [ "shotwell" ])+, (".ghc", [ "ghc" ])+, (".gvfs", [ "gvfs" ])+, (".mysql_history", [ "mysql-client" ])+, (".audacity-data", [ "audacity" ])+, (".xournal", [ "xournal" ])+, (".qtstalker", [ "qtstalker" ])+, (".icedteaplugin", [ "icedtea-plugin" ])+, (".gnupg", [ "gnupg" ])+]
+ data/PrgList view
@@ -0,0 +1,47 @@+[ (".cabal", [ "cabal", "cabal-install" ])+, (".emacs", [ "emacs", "emacs21", "emacs22", "emacs23" ]) +, (".emacs.d", [ "emacs", "emacs21", "emacs22", "emacs23"])+, (".lftp", [ "lftp" ])+, (".mission-control", [ "mission-control" ])+, (".mozilla", [ "firefox", "iceweasel" ])+, (".viminfo", [ "vim", "gvim" ])+, (".diamondcard-sms", [ "keera-diamondcard-sms-trayicon"])+, (".vim", [ "vim", "gvim" ])+, (".vimrc", [ "vim", "gvim" ])+, (".wine", [ "wine" ])+, (".eclipse", [ "eclipse" ])+, (".kde", [ "kde" ])+, (".libreoffice", [ "libreoffice" ])+, (".openoffice.org", [ "openoffice" ])+, (".VirtualBox", [ "virtualbox-ose" ])+, (".ssh", [ "openssh-client" ])+, (".xmind", [ "xmind" ])+, (".gSTM", [ "gstm" ])+, (".bashrc", [ "bash" ])+, (".bash_logout", [ "bash" ])+, (".bash_profile", [ "bash" ])+, (".bash_history", [ "bash" ])+, (".gimp-2.6", [ "gimp" ])+, (".hgrc", [ "mercurial" ])+, (".gconfd", [ "gconf2" ])+, (".subversion", [ "subversion" ])+, (".lyx", [ "lyx" ])+, (".thunderbird", [ "thunderbird" ])+, (".xine", [ "xine" ])+, (".lesshst", [ "less" ])+, (".gitconfig", [ "git" ])+, (".Skype", [ "skype" ])+, (".gem", [ "rubygems" ])+, (".mplayer", [ "mplayer" ])+, (".aptitude", [ "aptitude" ])+, (".sqlite_history", [ "sqlite3" ])+, (".shotwell", [ "shotwell" ])+, (".ghc", [ "ghc" ])+, (".gvfs", [ "gvfs" ])+, (".mysql_history", [ "mysql-client" ])+, (".audacity-data", [ "audacity" ])+, (".xournal", [ "xournal" ])+, (".qtstalker", [ "qtstalker" ])+, (".icedteaplugin", [ "icedtea-plugin" ])+, (".gnupg", [ "gnupg" ])+]
+ src/Args.hs view
@@ -0,0 +1,70 @@+{-# LANGUAGE DeriveDataTypeable #-}+module Args where++import Data.Data+import System.Console.CmdArgs++-- This is the CLI app definition : what we get from the user+data Args = Args {+ showNotOrphan :: Bool+ , byPackage :: Bool+ , byProgram :: Bool+ , pkgSearchProg :: String+ , pkgSearchArgs :: [String]+ , pkgSearchExitCode :: Bool+ + -- To be added+ -- home :: String -- Use this dir instead of the current+ -- -- user's home dir+ -- , verbose :: Bool -- Show everything you do+ -- , ignore :: [String] -- Ignore these filepaths+ -- , remove :: Bool -- Give users the option to remove orphan fps+ }+ deriving (Show, Data, Typeable)++-- This is the cmdArgs-based CLI interface definition+chArgs :: Args+chArgs = Args+ { showNotOrphan = False+ &= explicit+ &= name "show-not-orphan"+ &= help "Whether dirs with an owning program show be shown" + + , byPackage = enum [ True &= explicit &= name "by-package"+ &= help "Search by package name (enabled by default)"+ , False &= explicit &= name "not-by-package"+ &= help "Do not search by package name"+ ]+ -- &= explicit+ -- &= name "by-package"+ -- &= help "Search the responsible program by package name"++ , byProgram = enum [ True &= explicit &= name "by-program"+ &= help "Search by program name (enabled by default)"+ , False &= explicit &= name "not-by-program"+ &= help "Do not search by program name"+ ]++ , pkgSearchProg = def+ &= explicit+ &= name "with-pkg-search"+ &= help "Program used to search for packages"+ &= typ "PROGRAM"++ , pkgSearchArgs = def+ &= explicit+ &= name "with-pkg-search-opt"+ &= help "Arguments passed to pkg-search program"+ &= typ "ARG"++ , pkgSearchExitCode = False+ &= explicit+ &= name "with-pkg-search-exit-code"+ &= help "Use program exit code to determine package presence"+ + }+ &= summary "clean-home 0.0.1"+ &= details [ "(c) 2012 Ivan Perez - Keera Studios"+ , "Find more about clean-home at http://keera.es"+ , "and http://github.com/ivanperez-keera/clean-home"+ ]
+ src/Control/Exception/Extra.hs view
@@ -0,0 +1,13 @@+module Control.Exception.Extra where++import qualified Control.Exception as E++-- | Returns a given computation ignoring an exception+anyway :: a -> E.SomeException -> a+anyway f _ = f++-- | Tries to execute all the IO computations+-- until one succeeds+trySeq :: [IO ()] -> IO ()+trySeq [] = return ()+trySeq (x:xs) = E.handle (anyway (trySeq xs)) x
+ src/Env.hs view
@@ -0,0 +1,59 @@+module Env where++import Control.Monad+import Control.Exception as E+import Control.Exception.Extra++import Args+import System.Err+import Paths_clean_home++data Env = Env+ { appArgs :: Args+ , pkgList :: PkgList+ , prgList :: PrgList+ }++type PrgList = [ (String, [ String ]) ]+type PkgList = [ (String, [ String ]) ]++type ConfigSearchCmd = Env -> FilePath -> String -> IO (Either String Bool)++-- | Reads and returns the program list contained in+-- the file PrgList.+readPrgList :: Args -> IO (Maybe PrgList)+readPrgList appArgs+ | byProgram appArgs = readListFP "data/PrgList" "program list"+ | otherwise = return $ Just []++-- | Reads and returns the package list contained in+-- the file PrgList.+readPkgList :: Args -> IO (Maybe PkgList)+readPkgList appArgs+ | byPackage appArgs = readListFP "data/PkgList" "package list"+ | otherwise = return $ Just []++readListFP :: Read a => FilePath -> String -> IO (Maybe [a])+readListFP fn n = do+ fp <- getDataFileName fn+ list <- E.handle (anyway (return [])) $+ liftM reads $ readFile fp+ case list of+ [] -> do putErrLn $ "Cannot read or parse " ++ n+ return Nothing+ ((x,_):_) -> return (Just x)++-- FIXME: MaybeT?+buildEnv :: Args -> IO (Maybe Env)+buildEnv appArgs + | not (pkg || prg)+ = do putErrLn "You must enable --by-package or --by-program"+ return Nothing+ | otherwise + = do pkgList <- readPkgList appArgs+ prgList <- readPrgList appArgs+ case (pkgList, prgList) of+ (Just pkgs, Just prgs) -> return $ Just $ Env appArgs pkgs prgs+ _ -> return Nothing+ where pkg = byPackage appArgs+ prg = byProgram appArgs
+ src/Main.hs view
@@ -0,0 +1,65 @@+-- | This program checks every file/dir in $HOME against+-- a list of know programs/packages to determine whether+-- we have any program installed that may need it.++import Control.Monad.IfElse+import Data.List+import qualified Data.Map as M+import System.Directory+import System.Console.CmdArgs++-- Local imports+import Args+import Env+import SearchCommands+import System.Err++main :: IO ()+main = do+ -- Process args and build an env+ env <- buildEnv =<< cmdArgs chArgs+ awhen env $ \env' ->+ -- Get config files and check them+ mapM_ (checkFile env') =<< getHomeConfFiles env'++-- | Gets the list of filepaths that may be config paths+getHomeConfFiles :: Env -> IO [FilePath]+getHomeConfFiles _env = do+ homeDir <- getHomeDirectory + names <- getDirectoryContents homeDir+ return $ filter isConfigPath $ sort names+ where isConfigPath :: String -> Bool+ isConfigPath x = x `notElem` [".", ".."] && "." `isPrefixOf` x++-- Checks a file using all the know programs and packages+-- that may be responsible for it+checkFile :: Env -> FilePath -> IO ()+checkFile env fp = checkFile' env fp allChks+ where allChks = prgChks' ++ pkgChks'+ pkgChks' = if byPackage (appArgs env) then pkgChks else []+ prgChks' = if byProgram (appArgs env) then prgChks else []+ pkgChks = map (checkPackage env fp) pkgs+ prgChks = map (checkProgram env fp) prgs+ pkgs = packageSearch env fp+ prgs = programSearch env fp++-- | Prints a message indicating that the file is orphan (when it is)+-- and shows error messages when appropriate+checkFile' :: Env -> FilePath -> [IO (Either String Bool)] -> IO ()+checkFile' _env fp [] = putErrLn $ "Warning: Orphan dir: " ++ fp+checkFile' env fp (x:xs) = do+ res <- x+ case res of+ (Left s) -> putErrLn s+ (Right False) -> checkFile' env fp xs+ (Right True) -> return ()++-- | Search config cp and return associated packages+packageSearch :: Env -> String -> [ String ]+packageSearch env k = M.findWithDefault [] k (M.fromList pkgs)+ where pkgs = pkgList env++-- | Search config cp and return associated programs+programSearch :: Env -> String -> [ String ]+programSearch env k = M.findWithDefault [] k (M.fromList prgs)+ where prgs = prgList env
+ src/SearchCommand/Apt.hs view
@@ -0,0 +1,18 @@+module SearchCommand.Apt where++import Data.List++-- Local imports+import Env+import SearchCommand.UserSet++aptSearch :: ConfigSearchCmd+aptSearch env fp pkg = userSetSearchCustom+ (Left "apt-get exited with error code")+ (\results -> not (null results) && any ((pkg ++ " -") `isPrefixOf`) results)+ cmd env fp pkg+ where cmd = ("apt-cache", [ "search"+ , "--installed"+ , "--names-only"+ , "^" ++ pkg ++ "$"]+ )
+ src/SearchCommand/UserSet.hs view
@@ -0,0 +1,54 @@+module SearchCommand.UserSet+ ( userSetSearch+ , userSetSearchExitCode+ , userSetSearchOutput+ , userSetSearchCustom+ , userSetSearchCombined+ )+ where++-- External imports+import Control.Monad+import Control.Exception as E+import Control.Exception.Extra+import HSH++-- Local imports+import Args+import Env+import System.Err++userSetSearch :: ConfigSearchCmd+userSetSearch env fp pkg+ | pkgSearchExitCode (appArgs env) = userSetSearchExitCode cmd env fp pkg+ | otherwise = userSetSearchOutput cmd env fp pkg+ where cmd = (pkgSearchProg (appArgs env),+ pkgSearchArgs (appArgs env) ++ [ pkg ]+ )++type CMD = (String, [String])++userSetSearchExitCode :: CMD -> ConfigSearchCmd+userSetSearchExitCode = userSetSearchCustom (Right False) (const True)++userSetSearchOutput :: CMD -> ConfigSearchCmd+userSetSearchOutput cmd env fp pkg =+ userSetSearchCustom (Left (pkg ++ " exited with error code"))+ (not.null)+ cmd env fp pkg++userSetSearchCombined :: CMD -> ConfigSearchCmd+userSetSearchCombined = userSetSearchCustom (Right False) (not.null)+ +userSetSearchCustom :: Either String Bool+ -> ([String] -> Bool)+ -> CMD+ -> ConfigSearchCmd+userSetSearchCustom exceptRet test cmd env fp pkg =+ E.handle (anyway (return exceptRet)) $ do+ results <- run cmd :: IO [String]+ let notOrphan = test results+ when (notOrphan && showNotOrphan (appArgs env)) $+ putErrLn $ pkg ++ " might be responsible for " ++ fp+ return $ Right notOrphan+
+ src/SearchCommand/Which.hs view
@@ -0,0 +1,9 @@+module SearchCommand.Which where++-- Local imports+import Env+import SearchCommand.UserSet++whichSearch :: ConfigSearchCmd+whichSearch env fp prg = userSetSearchCombined cmd env fp prg+ where cmd = ("which", [ prg ])
+ src/SearchCommands.hs view
@@ -0,0 +1,17 @@+module SearchCommands where++-- Local imports+import Args+import Env+import SearchCommand.Apt+import SearchCommand.Which+import SearchCommand.UserSet++-- FIXME: The problem is here+checkPackage :: ConfigSearchCmd+checkPackage env fp pkg+ | null (pkgSearchProg (appArgs env)) = aptSearch env fp pkg+ | otherwise = userSetSearch env fp pkg++checkProgram :: ConfigSearchCmd+checkProgram = whichSearch
+ src/System/Err.hs view
@@ -0,0 +1,9 @@+module System.Err where++import System.IO++putErr :: String -> IO()+putErr = hPutStr stderr++putErrLn :: String -> IO()+putErrLn = hPutStrLn stderr