{-# LANGUAGE CPP, NoImplicitPrelude #-} {-# LANGUAGE ForeignFunctionInterface #-} -- | Miscellaneous information about the system environment. module System.Environment.Compat ( getArgs , getProgName , getEnv , lookupEnv , setEnv , unsetEnv , withArgs , withProgName , getEnvironment ) where import System.Environment #if !(MIN_VERSION_base(4,7,0)) import Prelude.Compat # ifdef mingw32_HOST_OS import Control.Monad import Foreign.C import Foreign.Safe import GHC.Windows # else import qualified System.Posix.Env as Posix # endif # ifdef mingw32_HOST_OS # if defined(i386_HOST_ARCH) # define WINDOWS_CCONV stdcall # elif defined(x86_64_HOST_ARCH) # define WINDOWS_CCONV ccall # else # error Unknown mingw32 arch # endif foreign import WINDOWS_CCONV unsafe "windows.h GetLastError" c_GetLastError:: IO DWORD eRROR_ENVVAR_NOT_FOUND :: DWORD eRROR_ENVVAR_NOT_FOUND = 203 # endif # if !(MIN_VERSION_base(4,6,0)) -- | Return the value of the environment variable @var@, or @Nothing@ if -- there is no such value. -- -- For POSIX users, this is equivalent to 'System.Posix.Env.getEnv'. lookupEnv :: String -> IO (Maybe String) lookupEnv k = lookup k `fmap` getEnvironment # endif -- | @setEnv name value@ sets the specified environment variable to @value@. -- -- On Windows setting an environment variable to the /empty string/ removes -- that environment variable from the environment. For the sake of -- compatibility we adopt that behavior. In particular -- -- @ -- setEnv name \"\" -- @ -- -- has the same effect as -- -- @ -- `unsetEnv` name -- @ -- -- If you don't care about Windows support and want to set an environment -- variable to the empty string use @System.Posix.Env.setEnv@ from the @unix@ -- package instead. -- -- Throws `Control.Exception.IOException` if @name@ is the empty string or -- contains an equals sign. -- -- Note that setting Unicode values may not work correctly on versions of GHC -- prior to 7.2. setEnv :: String -> String -> IO () setEnv key value_ | null value = unsetEnv key | otherwise = setEnv_ key value where -- NOTE: Anything that follows NUL is ignored on both POSIX and Windows. -- We still strip it manually so that the null check above succeds if a -- value starts with NUL, and `unsetEnv` is called. This is important for -- two reasons. -- -- * On POSIX setting an environment variable to the empty string does not -- remove it. -- -- * On Windows setting an environment variable to the empty string -- removes that environment variable. A subsequent call to -- GetEnvironmentVariable will then return 0, but the calling thread's -- last-error code will not be updated, and hence a call to GetLastError -- may not return ERROR_ENVVAR_NOT_FOUND. The failed lookup will then -- result in a random error instead of the expected -- `isDoesNotExistError` (this is at least true for Windows XP, SP 3). -- Explicitly calling `unsetEnv` prevents this. value = takeWhile (/= '\NUL') value_ setEnv_ :: String -> String -> IO () # ifdef mingw32_HOST_OS setEnv_ key value = withCWString key $ \k -> withCWString value $ \v -> do success <- c_SetEnvironmentVariable k v unless success (throwGetLastError "setEnv") foreign import WINDOWS_CCONV unsafe "windows.h SetEnvironmentVariableW" c_SetEnvironmentVariable :: LPTSTR -> LPTSTR -> IO Bool # else setEnv_ k v = Posix.setEnv k v True # endif -- | @unsetEnv name@ removes the specified environment variable from the -- environment of the current process. -- -- Throws `Control.Exception.IOException` if @name@ is the empty string or -- contains an equals sign. unsetEnv :: String -> IO () # ifdef mingw32_HOST_OS unsetEnv key = withCWString key $ \k -> do success <- c_SetEnvironmentVariable k nullPtr unless success $ do -- We consider unsetting an environment variable that does not exist not as -- an error, hence we ignore eRROR_ENVVAR_NOT_FOUND. err <- c_GetLastError unless (err == eRROR_ENVVAR_NOT_FOUND) $ do throwGetLastError "unsetEnv" # else unsetEnv = Posix.unsetEnv # endif #endif