{-# LINE 1 "libraries\\base\\System\\Environment\\Blank.hsc" #-}
{-# LANGUAGE Safe #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE CApiFFI #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  System.Environment.Blank
-- Copyright   :  (c) Habib Alamin 2017
-- License     :  BSD-style (see the file libraries/base/LICENSE)
--
-- Maintainer  :  libraries@haskell.org
-- Stability   :  provisional
-- Portability :  portable
--
-- A setEnv implementation that allows blank environment variables. Mimics
-- the `System.Posix.Env` module from the @unix@ package, but with support
-- for Windows too.
--
-- The matrix of platforms that:
--
--   * support @putenv("FOO")@ to unset environment variables,
--   * support @putenv("FOO=")@ to unset environment variables or set them
--     to blank values,
--   * support @unsetenv@ to unset environment variables,
--   * support @setenv@ to set environment variables,
--   * etc.
--
-- is very complicated. Some platforms don't support unsetting of environment
-- variables at all.
--
-----------------------------------------------------------------------------

module System.Environment.Blank
    (
      module System.Environment,
      getEnv,
      getEnvDefault,
      setEnv,
      unsetEnv,
  ) where

import Foreign.C

{-# LINE 44 "libraries\\base\\System\\Environment\\Blank.hsc" #-}
import Foreign.Ptr
import GHC.Windows
import Control.Monad

{-# LINE 50 "libraries\\base\\System\\Environment\\Blank.hsc" #-}
import GHC.IO.Exception
import System.IO.Error
import Control.Exception.Base
import Data.Maybe

import System.Environment
    (
      getArgs,
      getProgName,
      getExecutablePath,
      withArgs,
      withProgName,
      getEnvironment
  )

{-# LINE 67 "libraries\\base\\System\\Environment\\Blank.hsc" #-}

-- TODO: include windows_cconv.h when it's merged, instead of duplicating
-- this C macro block.

{-# LINE 71 "libraries\\base\\System\\Environment\\Blank.hsc" #-}

{-# LINE 74 "libraries\\base\\System\\Environment\\Blank.hsc" #-}
#  define WINDOWS_CCONV ccall

{-# LINE 78 "libraries\\base\\System\\Environment\\Blank.hsc" #-}

{-# LINE 79 "libraries\\base\\System\\Environment\\Blank.hsc" #-}



throwInvalidArgument :: String -> IO a
throwInvalidArgument from =
  throwIO (mkIOError InvalidArgument from Nothing Nothing)

-- | Similar to 'System.Environment.lookupEnv'.
getEnv :: String -> IO (Maybe String)

{-# LINE 89 "libraries\\base\\System\\Environment\\Blank.hsc" #-}
getEnv = (<$> getEnvironment) . lookup

{-# LINE 93 "libraries\\base\\System\\Environment\\Blank.hsc" #-}

-- | Get an environment value or a default value.
getEnvDefault ::
  String    {- ^ variable name                    -} ->
  String    {- ^ fallback value                   -} ->
  IO String {- ^ variable value or fallback value -}
getEnvDefault name fallback = fromMaybe fallback <$> getEnv name

-- | Like 'System.Environment.setEnv', but allows blank environment values
-- and mimics the function signature of 'System.Posix.Env.setEnv' from the
-- @unix@ package.
setEnv ::
  String {- ^ variable name  -} ->
  String {- ^ variable value -} ->
  Bool   {- ^ overwrite      -} ->
  IO ()
setEnv key_ value_ overwrite
  | null key       = throwInvalidArgument "setEnv"
  | '=' `elem` key = throwInvalidArgument "setEnv"
  | otherwise      =
    if overwrite
    then setEnv_ key value
    else do
      env_var <- getEnv key
      case env_var of
          Just _  -> return ()
          Nothing -> setEnv_ key value
  where
    key   = takeWhile (/= '\NUL') key_
    value = takeWhile (/= '\NUL') value_

setEnv_ :: String -> String -> IO ()

{-# LINE 126 "libraries\\base\\System\\Environment\\Blank.hsc" #-}
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

{-# LINE 142 "libraries\\base\\System\\Environment\\Blank.hsc" #-}

-- | Like 'System.Environment.unsetEnv', but allows for the removal of
-- blank environment variables. May throw an exception if the underlying
-- platform doesn't support unsetting of environment variables.
unsetEnv :: String -> IO ()

{-# LINE 148 "libraries\\base\\System\\Environment\\Blank.hsc" #-}
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"

eRROR_ENVVAR_NOT_FOUND :: DWORD
eRROR_ENVVAR_NOT_FOUND = 203

foreign import WINDOWS_CCONV unsafe "windows.h GetLastError"
  c_GetLastError:: IO DWORD

{-# LINE 194 "libraries\\base\\System\\Environment\\Blank.hsc" #-}