Skip to content

Commit b2793e1

Browse files
mmhathasufell
authored andcommitted
Added fallback behavior to 'readDirStreamWithType'
* Changed the assignment of values for missing DT_* constants. Since the 'd_type' field is of type 'unsigned char' we assign a negative number for each missing DT_* constant so they are distinguishable but do not collide with the values from the libc implementation. * Added a new 'DirStreamWithPath' that contains the path of the directory the directory stream belongs to. * 'readDirStreamWithType' falls back to a 'stat' if the 'd_type' is unknown or undetermined. * Added some tests for 'readDirStreamWithType'.
1 parent 5525915 commit b2793e1

File tree

6 files changed

+155
-24
lines changed

6 files changed

+155
-24
lines changed

System/Posix/Directory.hsc

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ module System.Posix.Directory (
2828
createDirectory, removeDirectory,
2929

3030
-- * Reading directories
31-
DirStream,
31+
DirStream, DirStreamWithPath,
32+
fromDirStreamWithPath,
3233
DirType( UnknownType
3334
, NamedPipeType
3435
, CharacterDeviceType
@@ -43,6 +44,7 @@ module System.Posix.Directory (
4344
isRegularFileType, isDirectoryType, isSymbolicLinkType, isSocketType,
4445
isWhiteoutType,
4546
openDirStream,
47+
openDirStreamWithPath,
4648
readDirStream,
4749
readDirStreamMaybe,
4850
readDirStreamWithType,
@@ -63,12 +65,14 @@ module System.Posix.Directory (
6365
) where
6466

6567
import Data.Maybe
68+
import System.FilePath ((</>))
6669
import System.Posix.Error
6770
import System.Posix.Types
6871
import Foreign
6972
import Foreign.C
7073

7174
import System.Posix.Directory.Common
75+
import System.Posix.Files
7276
import System.Posix.Internals (withFilePath, peekFilePath)
7377

7478
-- | @createDirectory dir mode@ calls @mkdir@ to
@@ -92,6 +96,11 @@ openDirStream name =
9296
dirp <- throwErrnoPathIfNullRetry "openDirStream" name $ c_opendir s
9397
return (DirStream dirp)
9498

99+
-- | A version of 'openDirStream' where the path of the directory is stored in
100+
-- the returned 'DirStreamWithPath'.
101+
openDirStreamWithPath :: FilePath -> IO (DirStreamWithPath FilePath)
102+
openDirStreamWithPath name = toDirStreamWithPath name <$> openDirStream name
103+
95104
foreign import capi unsafe "HsUnix.h opendir"
96105
c_opendir :: CString -> IO (Ptr CDir)
97106

@@ -124,12 +133,15 @@ readDirStreamMaybe = readDirStreamWith
124133
--
125134
-- __Note__: The returned 'DirType' has some limitations; Please see its
126135
-- documentation.
127-
readDirStreamWithType :: DirStream -> IO (Maybe (FilePath, DirType))
128-
readDirStreamWithType = readDirStreamWith
129-
(\(DirEnt dEnt) -> (,)
130-
<$> (d_name dEnt >>= peekFilePath)
131-
<*> (DirType <$> d_type dEnt)
136+
readDirStreamWithType :: DirStreamWithPath FilePath -> IO (Maybe (FilePath, DirType))
137+
readDirStreamWithType (DirStreamWithPath (base, ptr)) = readDirStreamWith
138+
(\(DirEnt dEnt) -> do
139+
name <- d_name dEnt >>= peekFilePath
140+
let getStat = getFileStatus (base </> name)
141+
dtype <- d_type dEnt >>= getRealDirType getStat . DirType
142+
return (name, dtype)
132143
)
144+
(DirStream ptr)
133145

134146
foreign import ccall unsafe "__hscore_d_name"
135147
d_name :: Ptr CDirent -> IO CString

System/Posix/Directory/ByteString.hsc

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{-# LANGUAGE CApiFFI #-}
22
{-# LANGUAGE NondecreasingIndentation #-}
3+
{-# LANGUAGE OverloadedStrings #-}
34
{-# LANGUAGE Safe #-}
45

56
-----------------------------------------------------------------------------
@@ -28,7 +29,8 @@ module System.Posix.Directory.ByteString (
2829
createDirectory, removeDirectory,
2930

3031
-- * Reading directories
31-
DirStream,
32+
DirStream, DirStreamWithPath,
33+
fromDirStreamWithPath,
3234
DirType( UnknownType
3335
, NamedPipeType
3436
, CharacterDeviceType
@@ -43,6 +45,7 @@ module System.Posix.Directory.ByteString (
4345
isRegularFileType, isDirectoryType, isSymbolicLinkType, isSocketType,
4446
isWhiteoutType,
4547
openDirStream,
48+
openDirStreamWithPath,
4649
readDirStream,
4750
readDirStreamMaybe,
4851
readDirStreamWithType,
@@ -70,6 +73,7 @@ import Foreign.C
7073
import Data.ByteString.Char8 as BC
7174

7275
import System.Posix.Directory.Common
76+
import System.Posix.Files.ByteString
7377
import System.Posix.ByteString.FilePath
7478

7579
-- | @createDirectory dir mode@ calls @mkdir@ to
@@ -93,6 +97,11 @@ openDirStream name =
9397
dirp <- throwErrnoPathIfNullRetry "openDirStream" name $ c_opendir s
9498
return (DirStream dirp)
9599

100+
-- | A version of 'openDirStream' where the path of the directory is stored in
101+
-- the returned 'DirStreamWithPath'.
102+
openDirStreamWithPath :: RawFilePath -> IO (DirStreamWithPath RawFilePath)
103+
openDirStreamWithPath name = toDirStreamWithPath name <$> openDirStream name
104+
96105
foreign import capi unsafe "HsUnix.h opendir"
97106
c_opendir :: CString -> IO (Ptr CDir)
98107

@@ -125,12 +134,15 @@ readDirStreamMaybe = readDirStreamWith
125134
--
126135
-- __Note__: The returned 'DirType' has some limitations; Please see its
127136
-- documentation.
128-
readDirStreamWithType :: DirStream -> IO (Maybe (RawFilePath, DirType))
129-
readDirStreamWithType = readDirStreamWith
130-
(\(DirEnt dEnt) -> (,)
131-
<$> (d_name dEnt >>= peekFilePath)
132-
<*> (DirType <$> d_type dEnt)
137+
readDirStreamWithType :: DirStreamWithPath RawFilePath -> IO (Maybe (RawFilePath, DirType))
138+
readDirStreamWithType (DirStreamWithPath (base, ptr)) = readDirStreamWith
139+
(\(DirEnt dEnt) -> do
140+
name <- d_name dEnt >>= peekFilePath
141+
let getStat = getFileStatus (base <> "/" <> name)
142+
dtype <- d_type dEnt >>= getRealDirType getStat . DirType
143+
return (name, dtype)
133144
)
145+
(DirStream ptr)
134146

135147
foreign import ccall unsafe "__hscore_d_name"
136148
d_name :: Ptr CDirent -> IO CString

System/Posix/Directory/Common.hsc

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{-# LANGUAGE CPP, Safe, CApiFFI, PatternSynonyms #-}
1+
{-# LANGUAGE CPP, Safe, CApiFFI, MultiWayIf, PatternSynonyms #-}
22

33
-----------------------------------------------------------------------------
44
-- |
@@ -19,7 +19,9 @@
1919
##include "HsUnixConfig.h"
2020

2121
module System.Posix.Directory.Common (
22-
DirStream(..), DirEnt(..), CDir, CDirent, DirStreamOffset(..),
22+
DirStream(..), DirStreamWithPath(..),
23+
fromDirStreamWithPath, toDirStreamWithPath,
24+
DirEnt(..), CDir, CDirent, DirStreamOffset(..),
2325
DirType( DirType
2426
, UnknownType
2527
, NamedPipeType
@@ -34,6 +36,7 @@ module System.Posix.Directory.Common (
3436
isUnknownType, isBlockDeviceType, isCharacterDeviceType, isNamedPipeType,
3537
isRegularFileType, isDirectoryType, isSymbolicLinkType, isSocketType,
3638
isWhiteoutType,
39+
getRealDirType,
3740
unsafeOpenDirStreamFd,
3841
readDirStreamWith,
3942
readDirStreamWithPtr,
@@ -59,8 +62,26 @@ import System.IO.Error ( ioeSetLocation )
5962
import GHC.IO.Exception ( unsupportedOperation )
6063
#endif
6164

65+
import System.Posix.Files.Common
66+
6267
newtype DirStream = DirStream (Ptr CDir)
6368

69+
newtype DirStreamWithPath a = DirStreamWithPath (a, Ptr CDir)
70+
71+
-- | Convert a 'DirStreamWithPath' to a 'DirStream'.
72+
-- Note that the underlying pointer is shared by both values, hence any
73+
-- modification to the resulting 'DirStream' will also modify the original
74+
-- 'DirStreamWithPath'.
75+
fromDirStreamWithPath :: DirStreamWithPath a -> DirStream
76+
fromDirStreamWithPath (DirStreamWithPath (_, ptr)) = DirStream ptr
77+
78+
-- | Construct a 'DirStreamWithPath' from a 'DirStream'.
79+
-- Note that the underlying pointer is shared by both values, hence any
80+
-- modification to the pointer of the resulting 'DirStreamWithPath' will also
81+
-- modify the original 'DirStream'.
82+
toDirStreamWithPath :: a -> DirStream -> DirStreamWithPath a
83+
toDirStreamWithPath path (DirStream ptr) = DirStreamWithPath (path, ptr)
84+
6485
newtype DirEnt = DirEnt (Ptr CDirent)
6586

6687
-- We provide a hand-written instance here since GeneralizedNewtypeDeriving and
@@ -97,7 +118,7 @@ data {-# CTYPE "struct dirent" #-} CDirent
97118
-- case none of those patterns will match and the application must handle that
98119
-- case accordingly.
99120
newtype DirType = DirType CChar
100-
deriving Eq
121+
deriving (Eq, Ord, Show)
101122

102123
-- | The 'DirType' refers to an entry of unknown type.
103124
pattern UnknownType :: DirType
@@ -164,6 +185,26 @@ isSymbolicLinkType dtype = dtype == SymbolicLinkType
164185
isSocketType dtype = dtype == SocketType
165186
isWhiteoutType dtype = dtype == WhiteoutType
166187

188+
getRealDirType :: IO FileStatus -> DirType -> IO DirType
189+
getRealDirType _ BlockDeviceType = return BlockDeviceType
190+
getRealDirType _ CharacterDeviceType = return CharacterDeviceType
191+
getRealDirType _ NamedPipeType = return NamedPipeType
192+
getRealDirType _ RegularFileType = return RegularFileType
193+
getRealDirType _ DirectoryType = return DirectoryType
194+
getRealDirType _ SymbolicLinkType = return SymbolicLinkType
195+
getRealDirType _ SocketType = return SocketType
196+
getRealDirType _ WhiteoutType = return WhiteoutType
197+
getRealDirType getFileStatus _ = do
198+
stat <- getFileStatus
199+
return $ if | isBlockDevice stat -> BlockDeviceType
200+
| isCharacterDevice stat -> CharacterDeviceType
201+
| isNamedPipe stat -> NamedPipeType
202+
| isRegularFile stat -> RegularFileType
203+
| isDirectory stat -> DirectoryType
204+
| isSymbolicLink stat -> SymbolicLinkType
205+
| isSocket stat -> SocketType
206+
| otherwise -> UnknownType
207+
167208
-- | Call @fdopendir@ to obtain a directory stream for @fd@. @fd@ must not be
168209
-- otherwise used after this.
169210
--

System/Posix/Directory/PosixPath.hsc

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ module System.Posix.Directory.PosixPath (
2727
createDirectory, removeDirectory,
2828

2929
-- * Reading directories
30-
Common.DirStream,
30+
Common.DirStream, Common.DirStreamWithPath,
31+
Common.fromDirStreamWithPath,
3132
Common.DirType( UnknownType
3233
, NamedPipeType
3334
, CharacterDeviceType
@@ -42,6 +43,7 @@ module System.Posix.Directory.PosixPath (
4243
Common.isNamedPipeType, Common.isRegularFileType, Common.isDirectoryType,
4344
Common.isSymbolicLinkType, Common.isSocketType, Common.isWhiteoutType,
4445
openDirStream,
46+
openDirStreamWithPath,
4547
readDirStream,
4648
readDirStreamMaybe,
4749
readDirStreamWithType,
@@ -66,8 +68,9 @@ import System.Posix.Types
6668
import Foreign
6769
import Foreign.C
6870

69-
import System.OsPath.Types
71+
import System.OsPath.Posix
7072
import qualified System.Posix.Directory.Common as Common
73+
import System.Posix.Files.PosixString
7174
import System.Posix.PosixPath.FilePath
7275

7376
-- | @createDirectory dir mode@ calls @mkdir@ to
@@ -91,6 +94,11 @@ openDirStream name =
9194
dirp <- throwErrnoPathIfNullRetry "openDirStream" name $ c_opendir s
9295
return (Common.DirStream dirp)
9396

97+
-- | A version of 'openDirStream' where the path of the directory is stored in
98+
-- the returned 'DirStreamWithPath'.
99+
openDirStreamWithPath :: PosixPath -> IO (Common.DirStreamWithPath PosixPath)
100+
openDirStreamWithPath name = Common.toDirStreamWithPath name <$> openDirStream name
101+
94102
foreign import capi unsafe "HsUnix.h opendir"
95103
c_opendir :: CString -> IO (Ptr Common.CDir)
96104

@@ -123,12 +131,15 @@ readDirStreamMaybe = Common.readDirStreamWith
123131
--
124132
-- __Note__: The returned 'DirType' has some limitations; Please see its
125133
-- documentation.
126-
readDirStreamWithType :: Common.DirStream -> IO (Maybe (PosixPath, Common.DirType))
127-
readDirStreamWithType = Common.readDirStreamWith
128-
(\(Common.DirEnt dEnt) -> (,)
129-
<$> (d_name dEnt >>= peekFilePath)
130-
<*> (Common.DirType <$> d_type dEnt)
134+
readDirStreamWithType :: Common.DirStreamWithPath PosixPath -> IO (Maybe (PosixPath, Common.DirType))
135+
readDirStreamWithType (Common.DirStreamWithPath (base, ptr))= Common.readDirStreamWith
136+
(\(Common.DirEnt dEnt) -> do
137+
name <- d_name dEnt >>= peekFilePath
138+
let getStat = getFileStatus (base </> name)
139+
dtype <- d_type dEnt >>= Common.getRealDirType getStat . Common.DirType
140+
return (name, dtype)
131141
)
142+
(Common.DirStream ptr)
132143

133144
foreign import ccall unsafe "__hscore_d_name"
134145
d_name :: Ptr Common.CDirent -> IO CString

configure.ac

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,42 @@ AC_CHECK_HEADERS([bsd/libutil.h libutil.h pty.h utmp.h])
2828
AC_CHECK_HEADERS([termios.h time.h unistd.h utime.h])
2929

3030
AC_STRUCT_DIRENT_D_TYPE
31-
FP_CHECK_CONSTS([DT_UNKNOWN DT_FIFO DT_CHR DT_DIR DT_BLK DT_REG DT_LNK DT_SOCK DT_WHT], [
31+
FP_CHECK_CONST([DT_UNKNOWN], [
3232
#if HAVE_STRUCT_DIRENT_D_TYPE
3333
#include <dirent.h>
34-
#endif])
34+
#endif], [-1])
35+
FP_CHECK_CONST([DT_FIFO], [
36+
#if HAVE_STRUCT_DIRENT_D_TYPE
37+
#include <dirent.h>
38+
#endif], [-2])
39+
FP_CHECK_CONST([DT_CHR], [
40+
#if HAVE_STRUCT_DIRENT_D_TYPE
41+
#include <dirent.h>
42+
#endif], [-3])
43+
FP_CHECK_CONST([DT_DIR], [
44+
#if HAVE_STRUCT_DIRENT_D_TYPE
45+
#include <dirent.h>
46+
#endif], [-4])
47+
FP_CHECK_CONST([DT_BLK], [
48+
#if HAVE_STRUCT_DIRENT_D_TYPE
49+
#include <dirent.h>
50+
#endif], [-5])
51+
FP_CHECK_CONST([DT_REG], [
52+
#if HAVE_STRUCT_DIRENT_D_TYPE
53+
#include <dirent.h>
54+
#endif], [-6])
55+
FP_CHECK_CONST([DT_LNK], [
56+
#if HAVE_STRUCT_DIRENT_D_TYPE
57+
#include <dirent.h>
58+
#endif], [-7])
59+
FP_CHECK_CONST([DT_SOCK], [
60+
#if HAVE_STRUCT_DIRENT_D_TYPE
61+
#include <dirent.h>
62+
#endif], [-8])
63+
FP_CHECK_CONST([DT_WHT], [
64+
#if HAVE_STRUCT_DIRENT_D_TYPE
65+
#include <dirent.h>
66+
#endif], [-9])
3567

3668
AC_CHECK_FUNCS([getgrgid_r getgrnam_r getpwnam_r getpwuid_r getpwnam getpwuid])
3769
AC_CHECK_FUNCS([getpwent getgrent])

tests/ReadDirStream.hs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
module ReadDirStream
22
( emptyDirStream
33
, nonEmptyDirStream
4+
, dirStreamWithTypes
45
) where
56

7+
import qualified Data.List
68
import System.Posix.Files
79
import System.Posix.Directory
810
import System.Posix.IO
@@ -33,6 +35,18 @@ nonEmptyDirStream = do
3335
cleanup
3436
entries @?= ["file"]
3537

38+
dirStreamWithTypes :: IO ()
39+
dirStreamWithTypes = do
40+
cleanup
41+
createDirectory dir ownerModes
42+
createDirectory (dir ++ "/somedir") ownerModes
43+
_ <- createFile (dir ++ "/somefile") ownerReadMode
44+
dir_p <- openDirStreamWithPath dir
45+
entries <- readDirStreamEntriesWithTypes dir_p
46+
closeDirStream (fromDirStreamWithPath dir_p)
47+
cleanup
48+
Data.List.sort entries @?= [("somedir", DirectoryType), ("somefile", RegularFileType)]
49+
3650
readDirStreamEntries :: DirStream -> IO [FilePath]
3751
readDirStreamEntries dir_p = do
3852
ment <- readDirStreamMaybe dir_p
@@ -42,6 +56,15 @@ readDirStreamEntries dir_p = do
4256
Just ".." -> readDirStreamEntries dir_p
4357
Just ent -> (ent :) <$> readDirStreamEntries dir_p
4458

59+
readDirStreamEntriesWithTypes :: DirStreamWithPath FilePath -> IO [(FilePath, DirType)]
60+
readDirStreamEntriesWithTypes dir_p = do
61+
ment <- readDirStreamWithType dir_p
62+
case ment of
63+
Nothing -> return []
64+
Just (".", _) -> readDirStreamEntriesWithTypes dir_p
65+
Just ("..", _) -> readDirStreamEntriesWithTypes dir_p
66+
Just ent -> (ent :) <$> readDirStreamEntriesWithTypes dir_p
67+
4568
cleanup :: IO ()
4669
cleanup = do
4770
ignoreIOExceptions $ removeLink $ dir ++ "/file"

0 commit comments

Comments
 (0)