aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/bin
diff options
context:
space:
mode:
Diffstat (limited to 'bin')
-rw-r--r--bin/AddPath.hs83
-rw-r--r--bin/ListPaths.hs84
-rw-r--r--bin/Prompt.hs50
-rw-r--r--bin/PromptMessage.hs39
-rw-r--r--bin/RemovePath.hs85
-rw-r--r--bin/SetEnv.hs72
-rw-r--r--bin/UnsetEnv.hs66
7 files changed, 479 insertions, 0 deletions
diff --git a/bin/AddPath.hs b/bin/AddPath.hs
new file mode 100644
index 0000000..73d86d0
--- /dev/null
+++ b/bin/AddPath.hs
@@ -0,0 +1,83 @@
+-- |
+-- Copyright : (c) 2015 Egor Tensin <Egor.Tensin@gmail.com>
+-- License : MIT
+-- Maintainer : Egor.Tensin@gmail.com
+-- Stability : experimental
+-- Portability : Windows-only
+
+module Main (main) where
+
+import Control.Monad (void, when)
+import Control.Monad.Trans.Except (catchE, runExceptT, throwE)
+import Data.List (union)
+import System.IO.Error (ioError, isDoesNotExistError)
+
+import Options.Applicative
+
+import qualified WindowsEnv
+
+import Prompt
+import PromptMessage
+
+data Options = Options
+ { optName :: WindowsEnv.VarName
+ , optYes :: Bool
+ , optGlobal :: Bool
+ , optPaths :: [WindowsEnv.VarValue]
+ } deriving (Eq, Show)
+
+optionParser :: Parser Options
+optionParser = Options
+ <$> optNameDesc
+ <*> optYesDesc
+ <*> optGlobalDesc
+ <*> optPathsDesc
+ where
+ optNameDesc = strOption
+ $ long "name" <> short 'n'
+ <> metavar "NAME" <> value "PATH"
+ <> help "Variable name ('PATH' by default)"
+ optYesDesc = switch
+ $ long "yes" <> short 'y'
+ <> help "Skip confirmation prompt"
+ optGlobalDesc = switch
+ $ long "global" <> short 'g'
+ <> help "Add for all users"
+ optPathsDesc = many $ argument str
+ $ metavar "PATH"
+ <> help "Directories to add"
+
+main :: IO ()
+main = execParser parser >>= addPath
+ where
+ parser = info (helper <*> optionParser) $
+ fullDesc <> progDesc "Add directories to your PATH"
+
+addPath :: Options -> IO ()
+addPath options = runExceptT doAddPath >>= either ioError return
+ where
+ varName = optName options
+ pathsToAdd = optPaths options
+
+ forAllUsers = optGlobal options
+ profile
+ | forAllUsers = WindowsEnv.AllUsers
+ | otherwise = WindowsEnv.CurrentUser
+
+ skipPrompt = optYes options
+
+ emptyIfMissing e
+ | isDoesNotExistError e = return ""
+ | otherwise = throwE e
+
+ doAddPath = do
+ oldValue <- WindowsEnv.query profile varName `catchE` emptyIfMissing
+ let oldPaths = WindowsEnv.pathSplit oldValue
+ let newPaths = oldPaths `union` pathsToAdd
+ when (length oldPaths /= length newPaths) $ do
+ let newValue = WindowsEnv.pathJoin newPaths
+ let promptAnd = if skipPrompt
+ then withoutPrompt
+ else withPrompt $ oldNewMessage profile varName oldValue newValue
+ let engrave = WindowsEnv.engrave profile varName newValue
+ void $ promptAnd engrave
diff --git a/bin/ListPaths.hs b/bin/ListPaths.hs
new file mode 100644
index 0000000..666423f
--- /dev/null
+++ b/bin/ListPaths.hs
@@ -0,0 +1,84 @@
+-- |
+-- Copyright : (c) 2015 Egor Tensin <Egor.Tensin@gmail.com>
+-- License : MIT
+-- Maintainer : Egor.Tensin@gmail.com
+-- Stability : experimental
+-- Portability : Windows-only
+
+module Main (main) where
+
+import Control.Monad (filterM)
+import Control.Monad.Trans.Class (lift)
+import Control.Monad.Trans.Except (runExceptT)
+import Data.Maybe (fromMaybe)
+import System.Directory (doesDirectoryExist)
+import System.Environment (lookupEnv)
+import System.IO.Error (ioError)
+
+import Options.Applicative
+
+import qualified WindowsEnv
+
+data WhichPaths = All | ExistingOnly | MissingOnly
+ deriving (Eq, Show)
+
+shouldListPath :: WhichPaths -> WindowsEnv.VarValue -> IO Bool
+shouldListPath All = return . const True
+shouldListPath ExistingOnly = doesDirectoryExist
+shouldListPath MissingOnly = fmap not . doesDirectoryExist
+
+data Source = Environment | Registry WindowsEnv.Profile
+ deriving (Eq, Show)
+
+data Options = Options
+ { optName :: WindowsEnv.VarName
+ , optWhichPaths :: WhichPaths
+ , optSource :: Source
+ } deriving (Eq, Show)
+
+optionParser :: Parser Options
+optionParser = Options
+ <$> optNameDesc
+ <*> optWhichPathsDesc
+ <*> optSourceDesc
+ where
+ optNameDesc = strOption
+ $ long "name" <> short 'n'
+ <> metavar "NAME" <> value "PATH"
+ <> help "Variable name ('PATH' by default)"
+ optWhichPathsDesc = pure All
+ <|> flag' ExistingOnly (long "existing" <> short 'e'
+ <> help "List existing paths only")
+ <|> flag' MissingOnly (long "missing" <> short 'm'
+ <> help "List missing paths only")
+ optSourceDesc = pure Environment
+ <|> flag' (Registry WindowsEnv.CurrentUser) (long "user" <> short 'u'
+ <> help "List current user's paths only")
+ <|> flag' (Registry WindowsEnv.AllUsers) (long "global" <> short 'g'
+ <> help "List global (all users') paths only")
+
+main :: IO ()
+main = execParser parser >>= listPaths
+ where
+ parser = info (helper <*> optionParser) $
+ fullDesc <> progDesc "List directories in your PATH"
+
+listPaths :: Options -> IO ()
+listPaths options = runExceptT doListPaths >>= either ioError return
+ where
+ varName = optName options
+ whichPaths = optWhichPaths options
+ source = optSource options
+
+ query = queryFrom source
+
+ queryFrom Environment = lift $ fromMaybe "" <$> lookupEnv varName
+ queryFrom (Registry profile) = WindowsEnv.query profile varName
+
+ filterPaths = filterM $ shouldListPath whichPaths
+
+ doListPaths = do
+ paths <- WindowsEnv.pathSplit <$> query
+ lift $ do
+ pathsToPrint <- filterPaths paths
+ mapM_ putStrLn pathsToPrint
diff --git a/bin/Prompt.hs b/bin/Prompt.hs
new file mode 100644
index 0000000..404c582
--- /dev/null
+++ b/bin/Prompt.hs
@@ -0,0 +1,50 @@
+-- |
+-- Copyright : (c) 2015 Egor Tensin <Egor.Tensin@gmail.com>
+-- License : MIT
+-- Maintainer : Egor.Tensin@gmail.com
+-- Stability : experimental
+-- Portability : portable
+
+module Prompt
+ ( withPrompt
+ , withoutPrompt
+ ) where
+
+import Control.Monad (void, when)
+import Control.Monad.Trans.Class (lift)
+import Control.Monad.Trans.Except (ExceptT)
+import Data.Char (toLower)
+import System.IO (hFlush, stdout)
+
+prompt :: String -> IO String
+prompt msg = do
+ putStr msg
+ hFlush stdout
+ getLine
+
+promptYesNo :: String -> IO Bool
+promptYesNo msg = do
+ response <- map toLower <$> prompt msg
+ if response `elem` yeses
+ then return True
+ else if response `elem` noes
+ then return False
+ else promptToContinue
+ where
+ yeses = ["y", "yes"]
+ noes = ["n", "no"]
+
+promptToContinue :: IO Bool
+promptToContinue = promptYesNo "Continue? (y/n) "
+
+withPrompt :: String -> ExceptT IOError IO a -> ExceptT IOError IO Bool
+withPrompt msg m = do
+ lift $ do
+ putStr msg
+ hFlush stdout
+ agreed <- lift promptToContinue
+ when agreed $ void m
+ return agreed
+
+withoutPrompt :: ExceptT IOError IO a -> ExceptT IOError IO Bool
+withoutPrompt m = m >> return True
diff --git a/bin/PromptMessage.hs b/bin/PromptMessage.hs
new file mode 100644
index 0000000..a3ff776
--- /dev/null
+++ b/bin/PromptMessage.hs
@@ -0,0 +1,39 @@
+-- |
+-- Copyright : (c) 2016 Egor Tensin <Egor.Tensin@gmail.com>
+-- License : MIT
+-- Maintainer : Egor.Tensin@gmail.com
+-- Stability : experimental
+-- Portability : Windows-only
+
+module PromptMessage
+ ( oldNewMessage
+ , newMessage
+ , wipeMessage
+ ) where
+
+import Text.Printf (printf)
+
+import qualified WindowsEnv
+
+oldNewMessage :: WindowsEnv.Profile -> WindowsEnv.VarName -> WindowsEnv.VarValue -> WindowsEnv.VarValue -> String
+oldNewMessage profile name oldValue newValue =
+ descrMsg ++ oldValueMsg ++ newValueMsg
+ where
+ profileKey = WindowsEnv.profileKeyPath profile
+ descrMsg = printf "Saving variable '%s' to '%s'...\n" name (show profileKey)
+ oldValueMsg = printf "\tOld value: %s\n" oldValue
+ newValueMsg = printf "\tNew value: %s\n" newValue
+
+newMessage :: WindowsEnv.Profile -> WindowsEnv.VarName -> WindowsEnv.VarValue -> String
+newMessage profile name newValue =
+ descrMsg ++ newValueMsg
+ where
+ profileKey = WindowsEnv.profileKeyPath profile
+ descrMsg = printf "Saving variable '%s' to '%s'...\n" name (show profileKey)
+ newValueMsg = printf "\tNew value: %s\n" newValue
+
+wipeMessage :: WindowsEnv.Profile -> WindowsEnv.VarName -> String
+wipeMessage profile name =
+ printf "Deleting variable '%s' from '%s'...\n" name (show profileKey)
+ where
+ profileKey = WindowsEnv.profileKeyPath profile
diff --git a/bin/RemovePath.hs b/bin/RemovePath.hs
new file mode 100644
index 0000000..7d0c104
--- /dev/null
+++ b/bin/RemovePath.hs
@@ -0,0 +1,85 @@
+-- |
+-- Copyright : (c) 2015 Egor Tensin <Egor.Tensin@gmail.com>
+-- License : MIT
+-- Maintainer : Egor.Tensin@gmail.com
+-- Stability : experimental
+-- Portability : Windows-only
+
+module Main (main) where
+
+import Control.Monad (void, when)
+import Control.Monad.Trans.Except (catchE, runExceptT, throwE)
+import Data.List ((\\))
+import System.IO.Error (ioError, isDoesNotExistError)
+
+import Options.Applicative
+
+import qualified WindowsEnv
+
+import Prompt
+import PromptMessage
+
+data Options = Options
+ { optName :: WindowsEnv.VarName
+ , optYes :: Bool
+ , optGlobal :: Bool
+ , optPaths :: [WindowsEnv.VarValue]
+ } deriving (Eq, Show)
+
+optionParser :: Parser Options
+optionParser = Options
+ <$> optNameDesc
+ <*> optYesDesc
+ <*> optGlobalDesc
+ <*> optPathsDesc
+ where
+ optNameDesc = strOption
+ $ long "name" <> short 'n'
+ <> metavar "NAME" <> value "PATH"
+ <> help "Variable name ('PATH' by default)"
+ optYesDesc = switch
+ $ long "yes" <> short 'y'
+ <> help "Skip confirmation prompt"
+ optGlobalDesc = switch
+ $ long "global" <> short 'g'
+ <> help "Remove for all users"
+ optPathsDesc = many $ argument str
+ $ metavar "PATH"
+ <> help "Directories to remove"
+
+main :: IO ()
+main = execParser parser >>= removePath
+ where
+ parser = info (helper <*> optionParser) $
+ fullDesc <> progDesc "Remove directories from your PATH"
+
+removePath :: Options -> IO ()
+removePath options = runExceptT doRemovePath >>= either ioError return
+ where
+ varName = optName options
+ pathsToRemove = optPaths options
+
+ forAllUsers = optGlobal options
+
+ skipPrompt = optYes options
+
+ emptyIfMissing e
+ | isDoesNotExistError e = return ""
+ | otherwise = throwE e
+
+ doRemovePath = do
+ removePathFrom WindowsEnv.CurrentUser
+ when forAllUsers $
+ removePathFrom WindowsEnv.AllUsers
+
+ removePathFrom profile = do
+ oldValue <- WindowsEnv.query profile varName `catchE` emptyIfMissing
+ let oldPaths = WindowsEnv.pathSplit oldValue
+ let newPaths = oldPaths \\ pathsToRemove
+ when (length oldPaths /= length newPaths) $ do
+ let newValue = WindowsEnv.pathJoin newPaths
+ let promptAnd = if skipPrompt
+ then withoutPrompt
+ else withPrompt $ oldNewMessage profile varName oldValue newValue
+ let engrave = WindowsEnv.engrave profile varName newValue
+ void $ promptAnd engrave
diff --git a/bin/SetEnv.hs b/bin/SetEnv.hs
new file mode 100644
index 0000000..87d7812
--- /dev/null
+++ b/bin/SetEnv.hs
@@ -0,0 +1,72 @@
+-- |
+-- Copyright : (c) 2015 Egor Tensin <Egor.Tensin@gmail.com>
+-- License : MIT
+-- Maintainer : Egor.Tensin@gmail.com
+-- Stability : experimental
+-- Portability : Windows-only
+
+module Main (main) where
+
+import Control.Monad (void)
+import Control.Monad.Trans.Except (runExceptT)
+import System.IO.Error (ioError)
+
+import Options.Applicative
+
+import qualified WindowsEnv
+
+import Prompt
+import PromptMessage
+
+data Options = Options
+ { optYes :: Bool
+ , optGlobal :: Bool
+ , optName :: WindowsEnv.VarName
+ , optValue :: WindowsEnv.VarValue
+ } deriving (Eq, Show)
+
+optionParser :: Parser Options
+optionParser = Options
+ <$> optYesDesc
+ <*> optGlobalDesc
+ <*> optNameDesc
+ <*> optValueDesc
+ where
+ optYesDesc = switch
+ $ long "yes" <> short 'y'
+ <> help "Skip confirmation prompt"
+ optGlobalDesc = switch
+ $ long "global" <> short 'g'
+ <> help "Set for all users"
+ optNameDesc = argument str
+ $ metavar "NAME"
+ <> help "Variable name"
+ optValueDesc = argument str
+ $ metavar "VALUE"
+ <> help "Variable value"
+
+main :: IO ()
+main = execParser parser >>= setEnv
+ where
+ parser = info (helper <*> optionParser) $
+ fullDesc <> progDesc "Define environment variables"
+
+setEnv :: Options -> IO ()
+setEnv options = runExceptT doSetEnv >>= either ioError return
+ where
+ varName = optName options
+ varValue = optValue options
+
+ forAllUsers = optGlobal options
+ profile
+ | forAllUsers = WindowsEnv.AllUsers
+ | otherwise = WindowsEnv.CurrentUser
+
+ skipPrompt = optYes options
+ promptAnd
+ | skipPrompt = withoutPrompt
+ | otherwise = withPrompt $ newMessage profile varName varValue
+
+ engrave = WindowsEnv.engraveForce profile varName varValue
+
+ doSetEnv = void $ promptAnd engrave
diff --git a/bin/UnsetEnv.hs b/bin/UnsetEnv.hs
new file mode 100644
index 0000000..f60503b
--- /dev/null
+++ b/bin/UnsetEnv.hs
@@ -0,0 +1,66 @@
+-- |
+-- Copyright : (c) 2015 Egor Tensin <Egor.Tensin@gmail.com>
+-- License : MIT
+-- Maintainer : Egor.Tensin@gmail.com
+-- Stability : experimental
+-- Portability : Windows-only
+
+module Main (main) where
+
+import Control.Monad (void)
+import Control.Monad.Trans.Except (runExceptT)
+import System.IO.Error (ioError)
+
+import Options.Applicative
+
+import qualified WindowsEnv
+
+import Prompt
+import PromptMessage
+
+data Options = Options
+ { optYes :: Bool
+ , optGlobal :: Bool
+ , optName :: WindowsEnv.VarName
+ } deriving (Eq, Show)
+
+optionParser :: Parser Options
+optionParser = Options
+ <$> optYesDesc
+ <*> optGlobalDesc
+ <*> optNameDesc
+ where
+ optYesDesc = switch
+ $ long "yes" <> short 'y'
+ <> help "Skip confirmation prompt"
+ optGlobalDesc = switch
+ $ long "global" <> short 'g'
+ <> help "Unset for all users"
+ optNameDesc = argument str
+ $ metavar "NAME"
+ <> help "Variable name"
+
+main :: IO ()
+main = execParser parser >>= unsetEnv
+ where
+ parser = info (helper <*> optionParser) $
+ fullDesc <> progDesc "Delete environment variables"
+
+unsetEnv :: Options -> IO ()
+unsetEnv options = runExceptT doUnsetEnv >>= either ioError return
+ where
+ varName = optName options
+
+ forAllUsers = optGlobal options
+ profile
+ | forAllUsers = WindowsEnv.AllUsers
+ | otherwise = WindowsEnv.CurrentUser
+
+ skipPrompt = optYes options
+ promptAnd
+ | skipPrompt = withoutPrompt
+ | otherwise = withPrompt $ wipeMessage profile varName
+
+ wipe = WindowsEnv.wipe profile varName
+
+ doUnsetEnv = void $ promptAnd wipe