what4-1.6.2: src/What4/Utils/Environment.hs
------------------------------------------------------------------------
-- |
-- Module : What4.Utils.Environemnt
-- Description : Provides functions for finding an executable, and
-- expanding a path with referenced to environment
-- variables.
-- Copyright : (c) Galois, Inc 2013-2020
-- License : BSD3
-- Maintainer : Joe Hendrix <jhendrix@galois.com>
-- Stability : provisional
--
-- Provides functions for finding an executable, and expanding a path
-- with referenced to environment variables.
------------------------------------------------------------------------
{-# LANGUAGE CPP #-}
module What4.Utils.Environment
( findExecutable
, expandEnvironmentPath
) where
#if !MIN_VERSION_base(4,13,0)
import Control.Monad.Fail( MonadFail )
#endif
import Control.Monad.IO.Class
import Data.Char
import qualified Data.List as List (foldl')
import Data.Map (Map)
import qualified Data.Map as Map
import qualified System.Directory as Sys
import System.Environment
import System.FilePath
-- | Given a mapping of variables to values, this replaces
-- substrings of the form $VAR with the associated value
-- in a string.
expandVars :: MonadFail m => Map String String -> String -> m String
expandVars m = outsideVar id
where -- Parse characters not part of a var.
outsideVar :: MonadFail m => ShowS -> String -> m String
outsideVar res s =
case s of
[] -> return (res [])
'$' : '{' : r -> matchBracketedVar res id r
'$' : c : r | isNumber c -> expandVar res (showChar c) r
'$' : r -> matchVarName res id r
c : r -> outsideVar (res . showChar c) r
-- Return true if this is a character.
isVarChar :: Char -> Bool
isVarChar '_' = True
isVarChar c = isAlphaNum c
matchVarName :: MonadFail m => ShowS -> ShowS -> String -> m String
matchVarName res rnm s =
case s of
[] -> expandVar res rnm s
c:r | isVarChar c -> matchVarName res (rnm . showChar c) r
| otherwise -> expandVar res rnm s
matchBracketedVar res rnm s =
case s of
[] -> fail "Missing '}' to close variable name."
'}':r -> expandVar res rnm r
c :r -> matchBracketedVar res (rnm . showChar c) r
expandVar res rnm r = do
let nm = rnm []
case Map.lookup nm m of
Just v -> outsideVar (res . showString v) r
Nothing -> fail $ "Could not find variable " ++ show nm
++ " in environment."
expandEnvironmentPath :: Map String String
-> String
-> IO String
expandEnvironmentPath base_map path = do
-- Get program name.
prog_name <- getExecutablePath
let prog_path = dropTrailingPathSeparator (dropFileName prog_name)
let init_map = Map.fromList [ ("MSS_BINPATH", prog_path) ]
-- Extend init_map with environment variables.
env <- getEnvironment
let expanded_map = List.foldl' (\m (k,v) -> Map.insert k v m) init_map env
-- Return expanded path.
expandVars (Map.union base_map expanded_map) path
-- | Find an executable from a string.
findExecutable :: (MonadIO m, MonadFail m)
=> FilePath
-- ^ Path to expand
-> m FilePath
findExecutable expanded_path = do
-- Look for variable in expanded_path.
mr <- liftIO $ Sys.findExecutable expanded_path
case mr of
Nothing -> fail $ "Could not find: " ++ expanded_path
Just r -> return r