Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not attempt to find an executable if a script is referenced #216

Merged
merged 3 commits into from
Jan 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions shelly.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ extra-source-files:
test/data/zshrc
test/data/nonascii.txt
test/data/symlinked_dir/hoge_file
test/data/hello.sh
test/testall
README.md
ChangeLog.md
Expand Down
62 changes: 26 additions & 36 deletions src/Shelly.hs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ import Control.Applicative
import Control.Concurrent
import Control.Concurrent.Async (async, wait, Async)
import Control.Exception
import Control.Monad ( when, unless, void, forM, filterM, liftM2 )
import Control.Monad ( when, unless, void, liftM2 )
import Control.Monad.Trans ( MonadIO )
import Control.Monad.Reader (ask)

Expand All @@ -122,22 +122,19 @@ import Data.Maybe
import Data.Semigroup ( (<>) )
#endif
import Data.Sequence ( Seq, (|>) )
import Data.Set ( Set )
import Data.Time.Clock ( getCurrentTime, diffUTCTime )
import Data.Tree ( Tree(..) )
import Data.Typeable

import qualified Data.ByteString as BS
import qualified Data.List as List
import qualified Data.Set as Set
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import qualified Data.Text.Encoding as TE
import qualified Data.Text.Encoding.Error as TE

import System.Directory
( setPermissions, getPermissions, Permissions(..), getTemporaryDirectory, pathIsSymbolicLink
, copyFile, removeFile, doesFileExist, doesDirectoryExist, listDirectory
, copyFile, removeFile, doesFileExist, doesDirectoryExist
, renameFile, renameDirectory, removeDirectoryRecursive, createDirectoryIfMissing
, getCurrentDirectory
)
Expand Down Expand Up @@ -605,14 +602,32 @@ whichEith originalFp = whichFull
whichFull fp = do
(trace . mappend "which " . toTextIgnore) fp >> whichUntraced
where
whichUntraced | isAbsolute fp = checkFile
| dotSlash splitOnDirs = checkFile
| length splitOnDirs > 0 = lookupPath >>= leftPathError
| otherwise = lookupCache >>= leftPathError
whichUntraced | isAbsolute fp = checkFile
| startsWithDot splitOnDirs = checkFile
| otherwise = lookupPath >>= leftPathError

splitOnDirs = splitDirectories fp
dotSlash ("./":_) = True
dotSlash _ = False

-- 'startsWithDot' receives as input the result of 'splitDirectories',
-- which will include the dot (\".\") as its first element only if this
-- is a path of the form \"./foo/bar/baz.sh\". Check for example:
--
-- > import System.FilePath as FP
-- > FP.splitDirectories "./test/data/hello.sh"
-- [".","test","data","hello.sh"]
-- > FP.splitDirectories ".hello.sh"
-- [".hello.sh"]
-- > FP.splitDirectories ".test/hello.sh"
-- [".test","hello.sh"]
-- > FP.splitDirectories ".foo"
-- [".foo"]
--
-- Note that earlier versions of Shelly used
-- \"system-filepath\" which also has a 'splitDirectories'
-- function, but it returns \"./\" as its first argument,
-- so we pattern match on both for backward-compatibility.
startsWithDot (".":_) = True
startsWithDot _ = False

checkFile :: Sh (Either String FilePath)
checkFile = do
Expand All @@ -637,33 +652,8 @@ whichEith originalFp = whichFull
res <- liftIO $ isExecutable fullFp
return $ if res then Just fullFp else Nothing

lookupCache :: Sh (Maybe FilePath)
lookupCache = do
pathExecutables <- cachedPathExecutables
return $ fmap (flip (</>) fp . fst) $
List.find (Set.member fp . snd) pathExecutables


pathDirs = mapM absPath =<< ((map T.unpack . filter (not . T.null) . T.split (== searchPathSeparator)) `fmap` get_env_text "PATH")

cachedPathExecutables :: Sh [(FilePath, Set FilePath)]
cachedPathExecutables = do
mPathExecutables <- gets sPathExecutables
case mPathExecutables of
Just pExecutables -> return pExecutables
Nothing -> do
dirs <- pathDirs
executables <- forM dirs (\dir -> do
files <- (liftIO . listDirectory) dir `catch_sh` (\(_ :: IOError) -> return [])
exes <- fmap (map snd) $ liftIO $ filterM (isExecutable . fst) $
map (\f -> (f, takeFileName f)) files
return $ Set.fromList exes
)
let cachedExecutables = zip dirs executables
modify $ \x -> x { sPathExecutables = Just cachedExecutables }
return $ cachedExecutables


-- | A monadic findMap, taken from MissingM package
findMapM :: Monad m => (a -> m (Maybe b)) -> [a] -> m (Maybe b)
findMapM _ [] = return Nothing
Expand Down
3 changes: 3 additions & 0 deletions test/data/hello.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env bash
andreasabel marked this conversation as resolved.
Show resolved Hide resolved

echo -n "Hello!"
2 changes: 2 additions & 0 deletions test/src/FindSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ findSpec = do
[ "dir"
, "dir/symlinked_dir"
, "dir/symlinked_dir/hoge_file"
, "hello.sh"
, "nonascii.txt"
, "symlinked_dir"
, "symlinked_dir/hoge_file"
Expand All @@ -128,6 +129,7 @@ findSpec = do
sort res @?=
[ "dir"
, "dir/symlinked_dir"
, "hello.sh"
, "nonascii.txt"
, "symlinked_dir"
, "symlinked_dir/hoge_file"
Expand Down
6 changes: 6 additions & 0 deletions test/src/RunSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ runSpec = do
if isWindows
then res @?= "Selbstverst\228ndlich \252berraschend\r\n"
else res @?= "Selbstverst\228ndlich \252berraschend\n"
unless isWindows $ do
it "script at $PWD" $ do
res <- shelly $ do
run_ "chmod" ["+x", "test/data/hello.sh"]
run "./test/data/hello.sh" []
res @?= "Hello!\n"

-- Bash-related commands
describe "bash" $ do
Expand Down