| 1 | {-# OPTIONS_GHC -fglasgow-exts -fallow-overlapping-instances -fallow-undecidable-instances #-} |
|---|
| 2 | module Pugs.Prim.FileTest ( |
|---|
| 3 | isReadable, isWritable, isExecutable, |
|---|
| 4 | exists, isFile, isDirectory, |
|---|
| 5 | fileSize, sizeIsZero, |
|---|
| 6 | fileMTime, fileCTime, fileATime, |
|---|
| 7 | fileTestViaPerl5 |
|---|
| 8 | ) where |
|---|
| 9 | import Pugs.Internals |
|---|
| 10 | import Pugs.Embed |
|---|
| 11 | import Pugs.Types |
|---|
| 12 | import Pugs.AST hiding (isWritable) |
|---|
| 13 | |
|---|
| 14 | -- filetest operators -- |
|---|
| 15 | |
|---|
| 16 | -- Officially, these should return a stat object, which sometimes pretends |
|---|
| 17 | -- to be a boolean, and may(?) return the filename in string context. |
|---|
| 18 | -- DARCS was working on stat, and we should perhaps grab their work: |
|---|
| 19 | -- http://www.abridgegame.org/pipermail/darcs-users/2005-February/005499.html |
|---|
| 20 | -- They currently (2004-04-05) seem to be using: |
|---|
| 21 | -- http://abridgegame.org/cgi-bin/darcs.cgi/darcs/win32/System/Posix.hs |
|---|
| 22 | -- For the moment, these return filename and false or undef. |
|---|
| 23 | -- Known Bugs: multiple stat()s are done, and filename isnt a boolean. |
|---|
| 24 | |
|---|
| 25 | isReadable :: Val -> Eval Val |
|---|
| 26 | isReadable = fileTestIO fileTestIsReadable |
|---|
| 27 | isWritable :: Val -> Eval Val |
|---|
| 28 | isWritable = fileTestIO fileTestIsWritable |
|---|
| 29 | isExecutable :: Val -> Eval Val |
|---|
| 30 | isExecutable = fileTestIO fileTestIsExecutable |
|---|
| 31 | exists :: Val -> Eval Val |
|---|
| 32 | exists = fileTestIO fileTestExists |
|---|
| 33 | isFile :: Val -> Eval Val |
|---|
| 34 | isFile = fileTestIO fileTestIsFile |
|---|
| 35 | isDirectory :: Val -> Eval Val |
|---|
| 36 | isDirectory = fileTestIO fileTestIsDirectory |
|---|
| 37 | fileSize :: Val -> Eval Val |
|---|
| 38 | fileSize = fileTestIO fileTestFileSize |
|---|
| 39 | sizeIsZero :: Val -> Eval Val |
|---|
| 40 | sizeIsZero = fileTestIO fileTestSizeIsZero |
|---|
| 41 | fileMTime :: Val -> Eval Val |
|---|
| 42 | fileMTime = fileTime statFileMTime |
|---|
| 43 | fileCTime :: Val -> Eval Val |
|---|
| 44 | fileCTime = fileTime statFileCTime |
|---|
| 45 | fileATime :: Val -> Eval Val |
|---|
| 46 | fileATime = fileTime statFileATime |
|---|
| 47 | |
|---|
| 48 | fileTestViaPerl5 :: String -> Val -> Eval Val |
|---|
| 49 | fileTestViaPerl5 testOp v = do |
|---|
| 50 | env <- ask |
|---|
| 51 | envSV <- io $ mkEnv env |
|---|
| 52 | argSV <- fromVal v |
|---|
| 53 | subSV <- io $ evalPerl5 ("sub { -" ++ testOp ++ " $_[0] }") envSV (enumCxt cxtItemAny) |
|---|
| 54 | rv <- runInvokePerl5 subSV nullSV [argSV] |
|---|
| 55 | return $ case rv of |
|---|
| 56 | VStr "" -> VBool False |
|---|
| 57 | VNum 1 -> VBool True |
|---|
| 58 | VInt 1 -> VBool True |
|---|
| 59 | _ -> rv |
|---|
| 60 | |
|---|
| 61 | fileTime :: (FilePath -> IO Integer) -> Val -> Eval Val |
|---|
| 62 | fileTime test f = do |
|---|
| 63 | t <- fileTestIO (fileTestDo test) f |
|---|
| 64 | if (t == undef) then return VUndef else do |
|---|
| 65 | t' <- fromVal t :: Eval Integer |
|---|
| 66 | b <- fromVal =<< readVar (cast "$*BASETIME") |
|---|
| 67 | return $ VRat $ (b - (pugsTimeSpec . posixSecondsToUTCTime $ fromIntegral t')) / 86400 |
|---|
| 68 | |
|---|
| 69 | fileTestIO :: (Value n) => (n -> IO Val) -> Val -> Eval Val |
|---|
| 70 | fileTestIO f v = do |
|---|
| 71 | str <- fromVal =<< fromVal' v |
|---|
| 72 | tryIO undef $ f str |
|---|
| 73 | |
|---|
| 74 | valFromBool :: Value a => a -> Bool -> Val |
|---|
| 75 | valFromBool v b = if b then castV v else VBool False |
|---|
| 76 | |
|---|
| 77 | testPerms :: (Permissions -> Bool) -> FilePath -> IO Val |
|---|
| 78 | testPerms t f = do |
|---|
| 79 | p <- getPermissions f |
|---|
| 80 | let b = t p |
|---|
| 81 | return $ valFromBool f b |
|---|
| 82 | |
|---|
| 83 | fileTestIsReadable :: FilePath -> IO Val |
|---|
| 84 | fileTestIsReadable = testPerms readable |
|---|
| 85 | |
|---|
| 86 | fileTestIsWritable :: FilePath -> IO Val |
|---|
| 87 | fileTestIsWritable = testPerms writable |
|---|
| 88 | |
|---|
| 89 | fileTestIsExecutable :: FilePath -> IO Val |
|---|
| 90 | fileTestIsExecutable = testPerms $ liftM2 (||) executable searchable |
|---|
| 91 | |
|---|
| 92 | fileTestExists :: FilePath -> IO Val |
|---|
| 93 | fileTestExists f = doesExist f >>= return . (valFromBool f) |
|---|
| 94 | |
|---|
| 95 | fileTestIsFile :: FilePath -> IO Val |
|---|
| 96 | fileTestIsFile f = doesFileExist f >>= return . (valFromBool f) |
|---|
| 97 | |
|---|
| 98 | fileTestIsDirectory :: FilePath -> IO Val |
|---|
| 99 | fileTestIsDirectory f = doesDirectoryExist f >>= return . (valFromBool f) |
|---|
| 100 | |
|---|
| 101 | fileTestFileSize :: FilePath -> IO Val |
|---|
| 102 | fileTestFileSize f = statFileSize f >>= return . VInt |
|---|
| 103 | |
|---|
| 104 | fileTestSizeIsZero :: FilePath -> IO Val |
|---|
| 105 | fileTestSizeIsZero f = do |
|---|
| 106 | n <- statFileSize f |
|---|
| 107 | return $ if n == 0 then VBool True else VBool False |
|---|
| 108 | |
|---|
| 109 | fileTestDo :: (FilePath -> IO Integer) -> FilePath -> IO Val |
|---|
| 110 | fileTestDo test f = test f >>= return . VInt |
|---|