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 CString ( packStringIO, allocChars,
85 import Directory ( removeDirectory, -- re-use its code
92 %************************************************************
94 \subsection[DirStream]{POSIX Directory streams}
96 %************************************************************
98 Accessing directories is done in POSIX via @DIR@ streams, with
99 operations for opening, closing, reading and rewinding the current
100 pointer in a directory.
102 {\bf Note:} The standard interface @Directory@ provides the
103 operation @getDirectoryContents@ which returns the directory contents of a
104 specified file path, which supplants some of the raw @DirStream@ operations
109 data DirStream = DirStream# Addr#
110 instance CCallable DirStream
111 instance CReturnable DirStream
113 openDirStream :: FilePath -> IO DirStream
115 packStringIO name >>= \dir ->
116 _ccall_ opendir dir >>= \dirp@(A# dirp#) ->
118 then return (DirStream# dirp#)
119 else syserr "openDirStream"
121 readDirStream :: DirStream -> IO String
122 readDirStream dirp = do
124 dirent <- _ccall_ readdir dirp
125 if dirent /= nullAddr
127 str <- _casm_ ``%r = ((struct dirent *)%0)->d_name;'' dirent
131 errno <- getErrorCode
133 then fail (IOError Nothing EOF "readDirStream" "EOF")
134 else syserr "readDirStream"
136 rewindDirStream :: DirStream -> IO ()
137 rewindDirStream dirp = do
138 _ccall_ rewinddir dirp
141 closeDirStream :: DirStream -> IO ()
142 closeDirStream dirp = do
143 rc <- _ccall_ closedir dirp
146 else syserr "closeDirStream"
149 Renamings of functionality provided via Directory interface,
150 kept around for b.wards compatibility and for having more POSIXy
153 getWorkingDirectory :: IO FilePath
154 getWorkingDirectory = getCurrentDirectory
156 changeWorkingDirectory :: FilePath -> IO ()
157 changeWorkingDirectory name = setCurrentDirectory name
160 %************************************************************
162 \subsection[FileMode]{POSIX File modes}
164 %************************************************************
166 The abstract type @FileMode@ and constants and operators for manipulating the
167 file modes defined by POSIX.
171 data FileMode = FileMode# Word#
172 instance CCallable FileMode
173 instance CReturnable FileMode
175 nullFileMode :: FileMode
176 nullFileMode = FileMode# (case ``0'' of { W# x -> x})
178 ownerReadMode :: FileMode
179 ownerReadMode = FileMode# (case ``S_IRUSR'' of { W# x -> x})
181 ownerWriteMode :: FileMode
182 ownerWriteMode = FileMode# (case ``S_IWUSR'' of { W# x -> x})
184 ownerExecuteMode :: FileMode
185 ownerExecuteMode = FileMode# (case ``S_IXUSR'' of { W# x -> x})
187 groupReadMode :: FileMode
188 groupReadMode = FileMode# (case ``S_IRGRP'' of { W# x -> x})
190 groupWriteMode :: FileMode
191 groupWriteMode = FileMode# (case ``S_IWGRP'' of { W# x -> x})
193 groupExecuteMode :: FileMode
194 groupExecuteMode = FileMode# (case ``S_IXGRP'' of { W# x -> x})
196 otherReadMode :: FileMode
197 otherReadMode = FileMode# (case ``S_IROTH'' of { W# x -> x})
199 otherWriteMode :: FileMode
200 otherWriteMode = FileMode# (case ``S_IWOTH'' of { W# x -> x})
202 otherExecuteMode :: FileMode
203 otherExecuteMode = FileMode# (case ``S_IXOTH'' of { W# x -> x})
205 setUserIDMode :: FileMode
206 setUserIDMode = FileMode# (case ``S_ISUID'' of { W# x -> x})
208 setGroupIDMode :: FileMode
209 setGroupIDMode = FileMode# (case ``S_ISGID'' of { W# x -> x})
211 stdFileMode :: FileMode
212 stdFileMode = FileMode# (case ``(S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)'' of { W# x -> x})
214 ownerModes :: FileMode
215 ownerModes = FileMode# (case ``S_IRWXU'' of { W# x -> x})
217 groupModes :: FileMode
218 groupModes = FileMode# (case ``S_IRWXG'' of { W# x -> x})
220 otherModes :: FileMode
221 otherModes = FileMode# (case ``S_IRWXO'' of { W# x -> x})
223 accessModes :: FileMode
224 accessModes = FileMode# (case ``(S_IRWXU|S_IRWXG|S_IRWXO)'' of { W# x -> x})
226 unionFileModes :: FileMode -> FileMode -> FileMode
227 unionFileModes (FileMode# m1#) (FileMode# m2#) = FileMode# (m1# `or#` m2#)
229 intersectFileModes :: FileMode -> FileMode -> FileMode
230 intersectFileModes (FileMode# m1#) (FileMode# m2#) = FileMode# (m1# `and#` m2#)
234 %************************************************************
236 \subsection[FileDescriptor]{POSIX File descriptors}
238 %************************************************************
240 File descriptors (formerly @Channel@s) are the lowest level
241 handles to file objects.
244 stdInput, stdOutput, stdError :: Fd
246 stdOutput = intToFd 1
249 data OpenMode = ReadOnly | WriteOnly | ReadWrite
260 defaultFileFlags :: OpenFileFlags
272 -> Maybe FileMode -- Just x => O_CREAT, Nothing => must exist
275 openFd name how maybe_mode (OpenFileFlags append exclusive noctty nonBlock truncate) =
276 packStringIO name >>= \file ->
277 _ccall_ open file flags mode_w >>= \fd@(I# fd#) ->
279 then return (FD# fd#)
282 mode_w = case maybe_mode of { Nothing -> ``0'' ; Just x -> x }
283 flags = W# (creat# `or#` flags# `or#` how#)
285 or (W# x#) (W# y#) = W# (x# `or#` y#)
288 (if append then ``O_APPEND'' else zero) `or`
289 (if exclusive then ``O_EXCL'' else zero) `or`
290 (if noctty then ``O_NOCTTY'' else zero) `or`
291 (if nonBlock then ``O_NONBLOCK'' else zero) `or`
292 (if truncate then ``O_TRUNC'' else zero)
294 zero = W# (int2Word# 0#)
297 case (case maybe_mode of {
299 Just _ -> ``O_CREAT'' }) of {
304 (case how of { ReadOnly -> ``O_RDONLY'';
305 WriteOnly -> ``O_WRONLY'';
306 ReadWrite -> ``O_RDWR''}) of {
309 createFile :: FilePath -> FileMode -> IO Fd
310 createFile name mode =
311 packStringIO name >>= \file ->
312 _ccall_ creat file mode >>= \fd@(I# fd#) ->
314 then return (FD# fd#)
315 else syserr "createFile"
317 setFileCreationMask :: FileMode -> IO FileMode
318 setFileCreationMask mask = _ccall_ umask mask
320 createLink :: FilePath -> FilePath -> IO ()
321 createLink name1 name2 = do
322 path1 <- packStringIO name1
323 path2 <- packStringIO name2
324 rc <- _ccall_ link path1 path2
327 else syserr "createLink"
329 createDirectory :: FilePath -> FileMode -> IO ()
330 createDirectory name mode = do -- NB: diff signature from LibDirectory one!
331 dir <- packStringIO name
332 rc <- _ccall_ mkdir dir mode
335 else syserr "createDirectory"
337 createNamedPipe :: FilePath -> FileMode -> IO ()
338 createNamedPipe name mode = do
339 pipe <- packStringIO name
340 rc <-_ccall_ mkfifo pipe mode
343 else syserr "createNamedPipe"
345 removeLink :: FilePath -> IO ()
347 path <- packStringIO name
348 rc <-_ccall_ unlink path
351 else syserr "removeLink"
353 rename :: FilePath -> FilePath -> IO ()
354 rename name1 name2 = do
355 path1 <- packStringIO name1
356 path2 <- packStringIO name2
357 rc <- _ccall_ rename path1 path2
362 type FileStatus = ByteArray Int
366 fileMode :: FileStatus -> FileMode
367 fileMode stat = unsafePerformIO $
368 _casm_ ``%r = ((struct stat *)%0)->st_mode;'' stat
370 fileID :: FileStatus -> FileID
371 fileID stat = unsafePerformIO $
372 _casm_ ``%r = ((struct stat *)%0)->st_ino;'' stat
374 deviceID :: FileStatus -> DeviceID
375 deviceID stat = unsafePerformIO $
376 _casm_ ``%r = ((struct stat *)%0)->st_dev;'' stat
378 linkCount :: FileStatus -> LinkCount
379 linkCount stat = unsafePerformIO $
380 _casm_ ``%r = ((struct stat *)%0)->st_nlink;'' stat
382 fileOwner :: FileStatus -> UserID
383 fileOwner stat = unsafePerformIO $
384 _casm_ ``%r = ((struct stat *)%0)->st_uid;'' stat
386 fileGroup :: FileStatus -> GroupID
387 fileGroup stat = unsafePerformIO $
388 _casm_ ``%r = ((struct stat *)%0)->st_gid;'' stat
390 fileSize :: FileStatus -> FileOffset
391 fileSize stat = unsafePerformIO $
392 _casm_ ``%r = ((struct stat *)%0)->st_size;'' stat
394 accessTime :: FileStatus -> EpochTime
395 accessTime stat = unsafePerformIO $
396 _casm_ ``%r = ((struct stat *)%0)->st_atime;'' stat
398 modificationTime :: FileStatus -> EpochTime
399 modificationTime stat = unsafePerformIO $
400 _casm_ ``%r = ((struct stat *)%0)->st_mtime;'' stat
402 statusChangeTime :: FileStatus -> EpochTime
403 statusChangeTime stat = unsafePerformIO $
404 _casm_ ``%r = ((struct stat *)%0)->st_ctime;'' stat
406 isDirectory :: FileStatus -> Bool
407 isDirectory stat = unsafePerformIO $
408 _casm_ ``%r = S_ISDIR(((struct stat *)%0)->st_mode);'' stat >>= \ rc ->
411 isCharacterDevice :: FileStatus -> Bool
412 isCharacterDevice stat = unsafePerformIO $
413 _casm_ ``%r = S_ISCHR(((struct stat *)%0)->st_mode);'' stat >>= \ rc ->
416 isBlockDevice :: FileStatus -> Bool
417 isBlockDevice stat = unsafePerformIO $
418 _casm_ ``%r = S_ISBLK(((struct stat *)%0)->st_mode);'' stat >>= \ rc ->
421 isRegularFile :: FileStatus -> Bool
422 isRegularFile stat = unsafePerformIO $
423 _casm_ ``%r = S_ISREG(((struct stat *)%0)->st_mode);'' stat >>= \ rc ->
426 isNamedPipe :: FileStatus -> Bool
427 isNamedPipe stat = unsafePerformIO $
428 _casm_ ``%r = S_ISFIFO(((struct stat *)%0)->st_mode);'' stat >>= \ rc ->
431 getFileStatus :: FilePath -> IO FileStatus
432 getFileStatus name = do
433 path <- packStringIO name
434 bytes <- allocChars ``sizeof(struct stat)''
435 rc <- _casm_ ``%r = stat(%0,(struct stat *)%1);'' path bytes
440 else syserr "getFileStatus"
442 getFdStatus :: Fd -> IO FileStatus
444 bytes <- allocChars ``sizeof(struct stat)''
445 rc <- _casm_ ``%r = fstat(%0,(struct stat *)%1);'' fd bytes
450 else syserr "getFdStatus"
452 fileAccess :: FilePath -> Bool -> Bool -> Bool -> IO Bool
453 fileAccess name read write exec = do
454 path <- packStringIO name
455 rc <- _ccall_ access path flags
458 flags = I# (word2Int# (read# `or#` write# `or#` exec#))
459 read# = case (if read then ``R_OK'' else ``0'') of { W# x -> x }
460 write# = case (if write then ``W_OK'' else ``0'') of { W# x -> x }
461 exec# = case (if exec then ``X_OK'' else ``0'') of { W# x -> x }
463 fileExist :: FilePath -> IO Bool
465 path <- packStringIO name
466 rc <- _ccall_ access path (``F_OK''::Int)
469 setFileMode :: FilePath -> FileMode -> IO ()
470 setFileMode name mode = do
471 path <- packStringIO name
472 rc <- _ccall_ chmod path mode
475 else syserr "setFileMode"
477 setOwnerAndGroup :: FilePath -> UserID -> GroupID -> IO ()
478 setOwnerAndGroup name uid gid = do
479 path <- packStringIO name
480 rc <- _ccall_ chown path uid gid
483 else syserr "setOwnerAndGroup"
485 setFileTimes :: FilePath -> EpochTime -> EpochTime -> IO ()
486 setFileTimes name atime mtime = do
487 path <- packStringIO name
488 rc <- _casm_ ``do {struct utimbuf ub; ub.actime = (time_t) %0;
489 ub.modtime = (time_t) %1;
490 %r = utime(%2, &ub);} while(0);'' atime mtime path
493 else syserr "setFileTimes"
495 {- Set access and modification time to current time -}
496 touchFile :: FilePath -> IO ()
498 path <- packStringIO name
499 rc <- _ccall_ utime path nullAddr
502 else syserr "touchFile"
504 data PathVar = LinkLimit {- _PC_LINK_MAX -}
505 | InputLineLimit {- _PC_MAX_CANON -}
506 | InputQueueLimit {- _PC_MAX_INPUT -}
507 | FileNameLimit {- _PC_NAME_MAX -}
508 | PathNameLimit {- _PC_PATH_MAX -}
509 | PipeBufferLimit {- _PC_PIPE_BUF -}
510 | SetOwnerAndGroupIsRestricted {- _PC_CHOWN_RESTRICTED -}
511 | FileNamesAreNotTruncated {- _PC_NO_TRUNC -}
513 getPathVar :: PathVar -> FilePath -> IO Limit
516 LinkLimit -> pathconf ``_PC_LINK_MAX''
517 InputLineLimit -> pathconf ``_PC_MAX_CANON''
518 InputQueueLimit -> pathconf ``_PC_MAX_INPUT''
519 FileNameLimit -> pathconf ``_PC_NAME_MAX''
520 PathNameLimit -> pathconf ``_PC_PATH_MAX''
521 PipeBufferLimit -> pathconf ``_PC_PIPE_BUF''
522 SetOwnerAndGroupIsRestricted -> pathconf ``_PC_CHOWN_RESTRICTED''
523 FileNamesAreNotTruncated -> pathconf ``_PC_NO_TRUNC'') name
525 pathconf :: Int -> FilePath -> IO Limit
527 path <- packStringIO name
528 rc <- _ccall_ pathconf path n
532 errno <- getErrorCode
533 if errno == invalidArgument
534 then fail (IOError Nothing NoSuchThing "getPathVar" "no such path limit or option")
535 else syserr "PosixFiles.getPathVar"
538 getFileVar :: PathVar -> Fd -> IO Limit
541 LinkLimit -> fpathconf (``_PC_LINK_MAX''::Int)
542 InputLineLimit -> fpathconf (``_PC_MAX_CANON''::Int)
543 InputQueueLimit -> fpathconf ``_PC_MAX_INPUT''
544 FileNameLimit -> fpathconf ``_PC_NAME_MAX''
545 PathNameLimit -> fpathconf ``_PC_PATH_MAX''
546 PipeBufferLimit -> fpathconf ``_PC_PIPE_BUF''
547 SetOwnerAndGroupIsRestricted -> fpathconf ``_PC_CHOWN_RESTRICTED''
548 FileNamesAreNotTruncated -> fpathconf ``_PC_NO_TRUNC'') fd
550 fpathconf :: Int -> Fd -> IO Limit
552 rc <- _ccall_ fpathconf fd n
556 errno <- getErrorCode
557 if errno == invalidArgument
558 then fail (IOError Nothing NoSuchThing "getFileVar" "no such path limit or option")
559 else syserr "getFileVar"