2 % (c) The GRASP/AQUA Project, Glasgow University, 1995-1996
4 \section[PosixFiles]{Haskell 1.3 POSIX File and Directory Operations}
11 openDirStream, closeDirStream,
12 readDirStream, rewindDirStream,
14 -- set/get process' working directory.
15 getWorkingDirectory, changeWorkingDirectory,
17 -- File modes/permissions
20 ownerReadMode, ownerWriteMode, ownerExecuteMode, ownerModes,
21 groupReadMode, groupWriteMode, groupExecuteMode, groupModes,
22 otherReadMode, otherWriteMode, otherExecuteMode, otherModes,
23 setUserIDMode, setGroupIDMode,
24 stdFileMode, accessModes,
26 unionFileModes, intersectFileModes,
28 -- File operations on descriptors
29 stdInput, stdOutput, stdError,
31 OpenFileFlags(..), defaultFileFlags,
34 -- other file&directory operations
36 createLink, removeLink,
37 createDirectory, removeDirectory,
43 getFileStatus, getFdStatus,
54 accessTime, modificationTime, statusChangeTime,
55 isDirectory, isCharacterDevice,
56 isBlockDevice, isRegularFile,
59 setOwnerAndGroup, -- chown (might be restricted)
60 setFileTimes, -- set access and modification time
61 touchFile, -- set access and modification time to current time.
63 -- run-time limit & POSIX feature testing
74 import IOExts (unsafePerformIO)
75 import PackedString (psToByteArrayST)
83 import Directory ( removeDirectory, -- re-use its code
90 %************************************************************
92 \subsection[DirStream]{POSIX Directory streams}
94 %************************************************************
96 Accessing directories is done in POSIX via @DIR@ streams, with
97 operations for opening, closing, reading and rewinding the current
98 pointer in a directory.
100 {\bf Note:} The standard interface @Directory@ provides the
101 operation @getDirectoryContents@ which returns the directory contents of a
102 specified file path, which supplants some of the raw @DirStream@ operations
107 data DirStream = DirStream# Addr#
108 instance CCallable DirStream
109 instance CReturnable DirStream
111 openDirStream :: FilePath -> IO DirStream
113 psToByteArrayIO name >>= \dir ->
114 _ccall_ opendir dir >>= \dirp@(A# dirp#) ->
115 if dirp /= (``NULL''::Addr)
116 then return (DirStream# dirp#)
117 else syserr "openDirStream"
119 readDirStream :: DirStream -> IO String
120 readDirStream dirp = do
122 dirent <- _ccall_ readdir dirp
123 if dirent /= (``NULL''::Addr)
125 str <- _casm_ ``%r = ((struct dirent *)%0)->d_name;'' dirent
129 errno <- getErrorCode
131 then fail (IOError Nothing EOF "readDirStream" "EOF")
132 else syserr "readDirStream"
134 rewindDirStream :: DirStream -> IO ()
135 rewindDirStream dirp = do
136 _ccall_ rewinddir dirp
139 closeDirStream :: DirStream -> IO ()
140 closeDirStream dirp = do
141 rc <- _ccall_ closedir dirp
144 else syserr "closeDirStream"
147 Renamings of functionality provided via Directory interface,
148 kept around for b.wards compatibility and for having more POSIXy
151 getWorkingDirectory :: IO FilePath
152 getWorkingDirectory = getCurrentDirectory
154 changeWorkingDirectory :: FilePath -> IO ()
155 changeWorkingDirectory name = setCurrentDirectory name
158 %************************************************************
160 \subsection[FileMode]{POSIX File modes}
162 %************************************************************
164 The abstract type @FileMode@ and constants and operators for manipulating the
165 file modes defined by POSIX.
169 data FileMode = FileMode# Word#
170 instance CCallable FileMode
171 instance CReturnable FileMode
173 nullFileMode :: FileMode
174 nullFileMode = FileMode# (case ``0'' of { W# x -> x})
176 ownerReadMode :: FileMode
177 ownerReadMode = FileMode# (case ``S_IRUSR'' of { W# x -> x})
179 ownerWriteMode :: FileMode
180 ownerWriteMode = FileMode# (case ``S_IWUSR'' of { W# x -> x})
182 ownerExecuteMode :: FileMode
183 ownerExecuteMode = FileMode# (case ``S_IXUSR'' of { W# x -> x})
185 groupReadMode :: FileMode
186 groupReadMode = FileMode# (case ``S_IRGRP'' of { W# x -> x})
188 groupWriteMode :: FileMode
189 groupWriteMode = FileMode# (case ``S_IWGRP'' of { W# x -> x})
191 groupExecuteMode :: FileMode
192 groupExecuteMode = FileMode# (case ``S_IXGRP'' of { W# x -> x})
194 otherReadMode :: FileMode
195 otherReadMode = FileMode# (case ``S_IROTH'' of { W# x -> x})
197 otherWriteMode :: FileMode
198 otherWriteMode = FileMode# (case ``S_IWOTH'' of { W# x -> x})
200 otherExecuteMode :: FileMode
201 otherExecuteMode = FileMode# (case ``S_IXOTH'' of { W# x -> x})
203 setUserIDMode :: FileMode
204 setUserIDMode = FileMode# (case ``S_ISUID'' of { W# x -> x})
206 setGroupIDMode :: FileMode
207 setGroupIDMode = FileMode# (case ``S_ISGID'' of { W# x -> x})
209 stdFileMode :: FileMode
210 stdFileMode = FileMode# (case ``(S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)'' of { W# x -> x})
212 ownerModes :: FileMode
213 ownerModes = FileMode# (case ``S_IRWXU'' of { W# x -> x})
215 groupModes :: FileMode
216 groupModes = FileMode# (case ``S_IRWXG'' of { W# x -> x})
218 otherModes :: FileMode
219 otherModes = FileMode# (case ``S_IRWXO'' of { W# x -> x})
221 accessModes :: FileMode
222 accessModes = FileMode# (case ``(S_IRWXU|S_IRWXG|S_IRWXO)'' of { W# x -> x})
224 unionFileModes :: FileMode -> FileMode -> FileMode
225 unionFileModes (FileMode# m1#) (FileMode# m2#) = FileMode# (m1# `or#` m2#)
227 intersectFileModes :: FileMode -> FileMode -> FileMode
228 intersectFileModes (FileMode# m1#) (FileMode# m2#) = FileMode# (m1# `and#` m2#)
232 %************************************************************
234 \subsection[FileDescriptor]{POSIX File descriptors}
236 %************************************************************
238 File descriptors (formerly @Channel@s) are the lowest level
239 handles to file objects.
242 stdInput, stdOutput, stdError :: Fd
244 stdOutput = intToFd 1
247 data OpenMode = ReadOnly | WriteOnly | ReadWrite
258 defaultFileFlags :: OpenFileFlags
270 -> Maybe FileMode -- Just x => O_CREAT, Nothing => must exist
273 openFd name how maybe_mode (OpenFileFlags append exclusive noctty nonBlock truncate) =
274 psToByteArrayIO name >>= \file ->
275 _ccall_ open file flags mode_w >>= \fd@(I# fd#) ->
277 then return (FD# fd#)
280 mode_w = case maybe_mode of { Nothing -> ``0'' ; Just x -> x }
281 flags = W# (creat# `or#` flags# `or#` how#)
283 or (W# x#) (W# y#) = W# (x# `or#` y#)
286 (if append then ``O_APPEND'' else zero) `or`
287 (if exclusive then ``O_EXCL'' else zero) `or`
288 (if noctty then ``O_NOCTTY'' else zero) `or`
289 (if nonBlock then ``O_NONBLOCK'' else zero) `or`
290 (if truncate then ``O_TRUNC'' else zero)
292 zero = W# (int2Word# 0#)
295 case (case maybe_mode of {
297 Just _ -> ``O_CREAT'' }) of {
302 (case how of { ReadOnly -> ``O_RDONLY'';
303 WriteOnly -> ``O_WRONLY'';
304 ReadWrite -> ``O_RDWR''}) of {
307 createFile :: FilePath -> FileMode -> IO Fd
308 createFile name mode =
309 psToByteArrayIO name >>= \file ->
310 _ccall_ creat file mode >>= \fd@(I# fd#) ->
312 then return (FD# fd#)
313 else syserr "createFile"
315 setFileCreationMask :: FileMode -> IO FileMode
316 setFileCreationMask mask = _ccall_ umask mask
318 createLink :: FilePath -> FilePath -> IO ()
319 createLink name1 name2 = do
320 path1 <- psToByteArrayIO name1
321 path2 <- psToByteArrayIO name2
322 rc <- _ccall_ link path1 path2
325 else syserr "createLink"
327 createDirectory :: FilePath -> FileMode -> IO ()
328 createDirectory name mode = do -- NB: diff signature from LibDirectory one!
329 dir <- psToByteArrayIO name
330 rc <- _ccall_ mkdir dir mode
333 else syserr "createDirectory"
335 createNamedPipe :: FilePath -> FileMode -> IO ()
336 createNamedPipe name mode = do
337 pipe <- psToByteArrayIO name
338 rc <-_ccall_ mkfifo pipe mode
341 else syserr "createNamedPipe"
343 removeLink :: FilePath -> IO ()
345 path <- psToByteArrayIO name
346 rc <-_ccall_ unlink path
349 else syserr "removeLink"
351 rename :: FilePath -> FilePath -> IO ()
352 rename name1 name2 = do
353 path1 <- psToByteArrayIO name1
354 path2 <- psToByteArrayIO name2
355 rc <- _ccall_ rename path1 path2
360 type FileStatus = ByteArray ()
364 fileMode :: FileStatus -> FileMode
365 fileMode stat = unsafePerformIO $
366 _casm_ ``%r = ((struct stat *)%0)->st_mode;'' stat
368 fileID :: FileStatus -> FileID
369 fileID stat = unsafePerformIO $
370 _casm_ ``%r = ((struct stat *)%0)->st_ino;'' stat
372 deviceID :: FileStatus -> DeviceID
373 deviceID stat = unsafePerformIO $
374 _casm_ ``%r = ((struct stat *)%0)->st_dev;'' stat
376 linkCount :: FileStatus -> LinkCount
377 linkCount stat = unsafePerformIO $
378 _casm_ ``%r = ((struct stat *)%0)->st_nlink;'' stat
380 fileOwner :: FileStatus -> UserID
381 fileOwner stat = unsafePerformIO $
382 _casm_ ``%r = ((struct stat *)%0)->st_uid;'' stat
384 fileGroup :: FileStatus -> GroupID
385 fileGroup stat = unsafePerformIO $
386 _casm_ ``%r = ((struct stat *)%0)->st_gid;'' stat
388 fileSize :: FileStatus -> FileOffset
389 fileSize stat = unsafePerformIO $
390 _casm_ ``%r = ((struct stat *)%0)->st_size;'' stat
392 accessTime :: FileStatus -> EpochTime
393 accessTime stat = unsafePerformIO $
394 _casm_ ``%r = ((struct stat *)%0)->st_atime;'' stat
396 modificationTime :: FileStatus -> EpochTime
397 modificationTime stat = unsafePerformIO $
398 _casm_ ``%r = ((struct stat *)%0)->st_mtime;'' stat
400 statusChangeTime :: FileStatus -> EpochTime
401 statusChangeTime stat = unsafePerformIO $
402 _casm_ ``%r = ((struct stat *)%0)->st_ctime;'' stat
404 isDirectory :: FileStatus -> Bool
405 isDirectory stat = unsafePerformIO $
406 _casm_ ``%r = S_ISDIR(((struct stat *)%0)->st_mode);'' stat >>= \ rc ->
409 isCharacterDevice :: FileStatus -> Bool
410 isCharacterDevice stat = unsafePerformIO $
411 _casm_ ``%r = S_ISCHR(((struct stat *)%0)->st_mode);'' stat >>= \ rc ->
414 isBlockDevice :: FileStatus -> Bool
415 isBlockDevice stat = unsafePerformIO $
416 _casm_ ``%r = S_ISBLK(((struct stat *)%0)->st_mode);'' stat >>= \ rc ->
419 isRegularFile :: FileStatus -> Bool
420 isRegularFile stat = unsafePerformIO $
421 _casm_ ``%r = S_ISREG(((struct stat *)%0)->st_mode);'' stat >>= \ rc ->
424 isNamedPipe :: FileStatus -> Bool
425 isNamedPipe stat = unsafePerformIO $
426 _casm_ ``%r = S_ISFIFO(((struct stat *)%0)->st_mode);'' stat >>= \ rc ->
429 getFileStatus :: FilePath -> IO FileStatus
430 getFileStatus name = do
431 path <- psToByteArrayIO name
432 bytes <- allocChars ``sizeof(struct stat)''
433 rc <- _casm_ ``%r = stat(%0,(struct stat *)%1);'' path bytes
438 else syserr "getFileStatus"
440 getFdStatus :: Fd -> IO FileStatus
442 bytes <- allocChars ``sizeof(struct stat)''
443 rc <- _casm_ ``%r = fstat(%0,(struct stat *)%1);'' fd bytes
448 else syserr "getFdStatus"
450 fileAccess :: FilePath -> Bool -> Bool -> Bool -> IO Bool
451 fileAccess name read write exec = do
452 path <- psToByteArrayIO name
453 rc <- _ccall_ access path flags
456 flags = I# (word2Int# (read# `or#` write# `or#` exec#))
457 read# = case (if read then ``R_OK'' else ``0'') of { W# x -> x }
458 write# = case (if write then ``W_OK'' else ``0'') of { W# x -> x }
459 exec# = case (if exec then ``X_OK'' else ``0'') of { W# x -> x }
461 fileExist :: FilePath -> IO Bool
463 path <- psToByteArrayIO name
464 rc <- _ccall_ access path (``F_OK''::Int)
467 setFileMode :: FilePath -> FileMode -> IO ()
468 setFileMode name mode = do
469 path <- psToByteArrayIO name
470 rc <- _ccall_ chmod path mode
473 else syserr "setFileMode"
475 setOwnerAndGroup :: FilePath -> UserID -> GroupID -> IO ()
476 setOwnerAndGroup name uid gid = do
477 path <- psToByteArrayIO name
478 rc <- _ccall_ chown path uid gid
481 else syserr "setOwnerAndGroup"
483 setFileTimes :: FilePath -> EpochTime -> EpochTime -> IO ()
484 setFileTimes name atime mtime = do
485 path <- psToByteArrayIO name
486 rc <- _casm_ ``do {struct utimbuf ub; ub.actime = (time_t) %0;
487 ub.modtime = (time_t) %1;
488 %r = utime(%2, &ub);} while(0);'' atime mtime path
491 else syserr "setFileTimes"
493 {- Set access and modification time to current time -}
494 touchFile :: FilePath -> IO ()
496 path <- psToByteArrayIO name
497 rc <- _ccall_ utime path (``NULL''::Addr)
500 else syserr "touchFile"
502 data PathVar = LinkLimit {- _PC_LINK_MAX -}
503 | InputLineLimit {- _PC_MAX_CANON -}
504 | InputQueueLimit {- _PC_MAX_INPUT -}
505 | FileNameLimit {- _PC_NAME_MAX -}
506 | PathNameLimit {- _PC_PATH_MAX -}
507 | PipeBufferLimit {- _PC_PIPE_BUF -}
508 | SetOwnerAndGroupIsRestricted {- _PC_CHOWN_RESTRICTED -}
509 | FileNamesAreNotTruncated {- _PC_NO_TRUNC -}
511 getPathVar :: PathVar -> FilePath -> IO Limit
514 LinkLimit -> pathconf ``_PC_LINK_MAX''
515 InputLineLimit -> pathconf ``_PC_MAX_CANON''
516 InputQueueLimit -> pathconf ``_PC_MAX_INPUT''
517 FileNameLimit -> pathconf ``_PC_NAME_MAX''
518 PathNameLimit -> pathconf ``_PC_PATH_MAX''
519 PipeBufferLimit -> pathconf ``_PC_PIPE_BUF''
520 SetOwnerAndGroupIsRestricted -> pathconf ``_PC_CHOWN_RESTRICTED''
521 FileNamesAreNotTruncated -> pathconf ``_PC_NO_TRUNC'') name
523 pathconf :: Int -> FilePath -> IO Limit
525 path <- psToByteArrayIO name
526 rc <- _ccall_ pathconf path n
530 errno <- getErrorCode
531 if errno == invalidArgument
532 then fail (IOError Nothing NoSuchThing "getPathVar" "no such path limit or option")
533 else syserr "PosixFiles.getPathVar"
536 getFileVar :: PathVar -> Fd -> IO Limit
539 LinkLimit -> fpathconf (``_PC_LINK_MAX''::Int)
540 InputLineLimit -> fpathconf (``_PC_MAX_CANON''::Int)
541 InputQueueLimit -> fpathconf ``_PC_MAX_INPUT''
542 FileNameLimit -> fpathconf ``_PC_NAME_MAX''
543 PathNameLimit -> fpathconf ``_PC_PATH_MAX''
544 PipeBufferLimit -> fpathconf ``_PC_PIPE_BUF''
545 SetOwnerAndGroupIsRestricted -> fpathconf ``_PC_CHOWN_RESTRICTED''
546 FileNamesAreNotTruncated -> fpathconf ``_PC_NO_TRUNC'') fd
548 fpathconf :: Int -> Fd -> IO Limit
550 rc <- _ccall_ fpathconf fd n
554 errno <- getErrorCode
555 if errno == invalidArgument
556 then fail (IOError Nothing NoSuchThing "getFileVar" "no such path limit or option")
557 else syserr "getFileVar"