------------------------------------------------------------------------ -- | -- 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 -- 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 Data.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 = 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