aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/Environment.hs
blob: 5a3978e38bfac6f92668a1ff199ca6c986bf952e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
{-
 - Copyright 2015 Egor Tensin <Egor.Tensin@gmail.com>
 - This file is licensed under the terms of the MIT License.
 - See LICENSE.txt for details.
-}

module Environment
    ( queryFromRegistry
    , saveToRegistry
    , saveToRegistryWithPrompt
    , wipeFromRegistry
    , wipeFromRegistryWithPrompt
    , getEnv
    , splitPaths
    , joinPaths
    , RegistryBasedEnvironment(..)
    ) where

import           Control.Monad      (liftM, when)
import           Data.List          (intercalate)
import           Data.List.Split    (splitOn)
import           Data.Maybe         (fromMaybe)
import qualified System.Environment (lookupEnv)
import           System.IO.Error    (catchIOError, isDoesNotExistError)

import qualified Graphics.Win32.Window as WinAPI
import qualified System.Win32.Types    as WinAPI

import qualified Registry
import qualified Utils (promptToContinue)

data RegistryBasedEnvironment
    = CurrentUserEnvironment
    | AllUsersEnvironment
    deriving (Eq, Show)

subKeyPath :: RegistryBasedEnvironment -> Registry.KeyPath
subKeyPath CurrentUserEnvironment =
    Registry.keyPathFromString "Environment"
subKeyPath AllUsersEnvironment =
    Registry.keyPathFromString "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"

rootKey :: RegistryBasedEnvironment -> Registry.RootKey
rootKey CurrentUserEnvironment = Registry.CurrentUser
rootKey AllUsersEnvironment    = Registry.LocalMachine

openRootKey :: RegistryBasedEnvironment -> Registry.KeyHandle
openRootKey = Registry.openRootKey . rootKey

openRegistryKey :: RegistryBasedEnvironment -> IO Registry.KeyHandle
openRegistryKey env = Registry.openSubKey (openRootKey env) (subKeyPath env)

registryKeyPath :: RegistryBasedEnvironment -> Registry.KeyPath
registryKeyPath env = Registry.keyPathJoin [Registry.rootKeyPath $ rootKey env, subKeyPath env]

notifyEnvUpdate :: IO ()
notifyEnvUpdate =
    WinAPI.withTString "Environment" $ \lparamPtr -> do
        let wparam = 0
        let lparam = fromIntegral $ WinAPI.castPtrToUINTPtr lparamPtr
        _ <- WinAPI.sendMessage allWindows messageCode wparam lparam
        return ()
  where
    wM_SETTINGCHANGE = 0x1A
    messageCode = wM_SETTINGCHANGE

    allWindows = WinAPI.castUINTPtrToPtr 0xffff

saveToRegistry :: RegistryBasedEnvironment -> Registry.ValueName -> Registry.ValueData -> IO ()
saveToRegistry env name value = do
    keyHandle <- openRegistryKey env
    Registry.setString keyHandle name value
    notifyEnvUpdate

saveToRegistryWithPrompt :: RegistryBasedEnvironment -> Registry.ValueName -> Registry.ValueData -> IO ()
saveToRegistryWithPrompt env name value = do
    putStrLn $ "Saving variable '" ++ name ++ "' to '" ++ registryKeyPath env ++ "'..."
    oldValue <- queryFromRegistry env name
    putStrLn $ "\tOld value: " ++ oldValue
    putStrLn $ "\tNew value: " ++ value
    agreed <- Utils.promptToContinue
    when agreed $ saveToRegistry env name value

queryFromRegistry :: RegistryBasedEnvironment -> Registry.ValueName -> IO Registry.ValueData
queryFromRegistry env name = do
    keyHandle <- openRegistryKey env
    catchIOError (Registry.getString keyHandle name) emptyIfDoesNotExist
  where
    emptyIfDoesNotExist e = if isDoesNotExistError e then return "" else ioError e

wipeFromRegistry :: RegistryBasedEnvironment -> Registry.ValueName -> IO ()
wipeFromRegistry env name = do
    keyHandle <- openRegistryKey env
    catchIOError (Registry.delValue keyHandle name) ignoreIfDoesNotExist
    notifyEnvUpdate
  where
    ignoreIfDoesNotExist e = if isDoesNotExistError e then return () else ioError e

wipeFromRegistryWithPrompt :: RegistryBasedEnvironment -> Registry.ValueName -> IO ()
wipeFromRegistryWithPrompt env name = do
    putStrLn $ "Deleting variable '" ++ name ++ "' from '" ++ registryKeyPath env ++ "'..."
    agreed <- Utils.promptToContinue
    when agreed $ wipeFromRegistry env name

getEnv :: String -> IO String
getEnv = liftM (fromMaybe "") . System.Environment.lookupEnv

pathSep :: String
pathSep = ";"

splitPaths :: String -> [String]
splitPaths = filter (not . null) . splitOn pathSep

joinPaths :: [String] -> String
joinPaths = intercalate pathSep . filter (not . null)