{-# LANGUAGE CPP, ScopedTypeVariables, TypeSynonymInstances #-}{- arch-tag: HVFS main file
Copyright (c) 2004-2011 John Goerzen <jgoerzen@complete.org>
All rights reserved.
For license and copyright information, see the file LICENSE
-}{- |
Module : System.IO.HVFS
Copyright : Copyright (C) 2004-2011 John Goerzen
License : BSD3
Maintainer : John Goerzen <jgoerzen@complete.org>
Stability : provisional
Portability: portable
Haskell Virtual FS -- generic support for real or virtual filesystem in Haskell
Copyright (c) 2004-2005 John Goerzen, jgoerzen\@complete.org
The idea of this module is to provide virtualization of filesystem calls.
In addition to the \"real\" system filesystem, you can also provide access
to other, virtual, filesystems using the same set of calls. Examples of
such virtual filesystems might include a remote FTP server, WebDAV server,
a local Hashtable, a ConfigParser object, or any other data structure
you can represent as a tree of named nodes containing strings.
Each 'HVFS' function takes a 'HVFS' \"handle\" ('HVFS' instance) as its
first parameter. If you wish to operate on the standard system filesystem,
you can just use 'SystemFS'.
The "MissingH.HVFS.IO.InstanceHelpers" module contains some code to help
you make your own HVFS instances.
The 'HVFSOpenable' class works together with the "System.IO.HVIO" module
to provide a complete virtual filesystem and I\/O model that allows you
to open up virtual filesystem files and act upon them in a manner similar
to standard Handles.
-}moduleSystem.IO.HVFS(-- * Implementation Classes \/ TypesHVFS(..),HVFSStat(..),HVFSOpenable(..),HVFSOpenEncap(..),HVFSStatEncap(..),withStat,withOpen,SystemFS(..),-- * Re-exported types from other modulesFilePath,DeviceID,FileID,FileMode,LinkCount,UserID,GroupID,FileOffset,EpochTime,IOMode)whereimportqualifiedControl.Exception(catch,IOException)importSystem.IO.HVIOimportSystem.Time.UtilsimportSystem.IOimportSystem.IO.ErrorimportSystem.IO.PlafCompatimportSystem.Posix.TypesimportSystem.TimeimportSystem.Directory#if MIN_VERSION_directory(1,2,0)importData.Time.Clock.POSIX(utcTimeToPOSIXSeconds)#endif{- | Encapsulate a 'HVFSStat' result. This is required due to Haskell
typing restrictions. You can get at it with:
> case encap of
> HVFSStatEncap x -> -- now use x
-}dataHVFSStatEncap=foralla.HVFSStata=>HVFSStatEncapa{- | Convenience function for working with stat -- takes a stat result
and a function that uses it, and returns the result.
Here is an example from the HVFS source:
> vGetModificationTime fs fp =
> do s <- vGetFileStatus fs fp
> return $ epochToClockTime (withStat s vModificationTime)
See 'System.Time.Utils.epochToClockTime' for more information.
-}withStat::forallb.HVFSStatEncap->(foralla.HVFSStata=>a->b)->bwithStatsf=casesofHVFSStatEncapx->fx{- | Similar to 'HVFSStatEncap', but for 'vOpen' result.
-}dataHVFSOpenEncap=foralla.HVIOa=>HVFSOpenEncapa{- | Similar to 'withStat', but for the 'vOpen' result. -}withOpen::forallb.HVFSOpenEncap->(foralla.HVIOa=>a->b)->bwithOpensf=casesofHVFSOpenEncapx->fx{- | Evaluating types of files and information about them.
This corresponds to the System.Posix.Types.FileStatus type, and indeed,
that is one instance of this class.
Inplementators must, at minimum, implement 'vIsDirectory' and
'vIsRegularFile'.
Default implementations of everything else are provided, returning
reasonable values.
A default implementation of this is not currently present on Windows.
-}class(Showa)=>HVFSStatawherevDeviceID::a->DeviceIDvFileID::a->FileID{- | Refers to file permissions, NOT the st_mode field from stat(2) -}vFileMode::a->FileModevLinkCount::a->LinkCountvFileOwner::a->UserIDvFileGroup::a->GroupIDvSpecialDeviceID::a->DeviceIDvFileSize::a->FileOffsetvAccessTime::a->EpochTimevModificationTime::a->EpochTimevStatusChangeTime::a->EpochTimevIsBlockDevice::a->BoolvIsCharacterDevice::a->BoolvIsNamedPipe::a->BoolvIsRegularFile::a->BoolvIsDirectory::a->BoolvIsSymbolicLink::a->BoolvIsSocket::a->BoolvDeviceID_=0vFileID_=0vFileModex=ifvIsDirectoryxthen0x755else0o0644vLinkCount_=1vFileOwner_=0vFileGroup_=0vSpecialDeviceID_=0vFileSize_=0vAccessTime_=0vModificationTime_=0vStatusChangeTime_=0vIsBlockDevice_=FalsevIsCharacterDevice_=FalsevIsNamedPipe_=FalsevIsSymbolicLink_=FalsevIsSocket_=False{- | The main HVFS class.
Default implementations of these functions are provided:
* 'vGetModificationTime' -- implemented in terms of 'vGetFileStatus'
* 'vRaiseError'
* 'vDoesFileExist' -- implemented in terms of 'vGetFileStatus'
* 'vDoesDirectoryExist' -- implemented in terms of 'vGetFileStatus'
* 'vDoesExist' -- implemented in terms of 'vGetSymbolicLinkStatus'
* 'vGetSymbolicLinkStatus' -- set to call 'vGetFileStatus'.
Default implementations of all other functions
will generate an isIllegalOperation error, since they are assumed to be
un-implemented.
You should always provide at least a 'vGetFileStatus' call, and almost
certainly several of the others.
Most of these functions correspond to functions in System.Directory or
System.Posix.Files. Please see detailed documentation on them there.
-}class(Showa)=>HVFSawherevGetCurrentDirectory::a->IOFilePathvSetCurrentDirectory::a->FilePath->IO()vGetDirectoryContents::a->FilePath->IO[FilePath]vDoesFileExist::a->FilePath->IOBoolvDoesDirectoryExist::a->FilePath->IOBool{- | True if the file exists, regardless of what type it is.
This is even True if the given path is a broken symlink. -}vDoesExist::a->FilePath->IOBoolvCreateDirectory::a->FilePath->IO()vRemoveDirectory::a->FilePath->IO()vRenameDirectory::a->FilePath->FilePath->IO()vRemoveFile::a->FilePath->IO()vRenameFile::a->FilePath->FilePath->IO()vGetFileStatus::a->FilePath->IOHVFSStatEncapvGetSymbolicLinkStatus::a->FilePath->IOHVFSStatEncapvGetModificationTime::a->FilePath->IOClockTime{- | Raise an error relating to actions on this class. -}vRaiseError::a->IOErrorType->String->MaybeFilePath->IOcvCreateSymbolicLink::a->FilePath->FilePath->IO()vReadSymbolicLink::a->FilePath->IOFilePathvCreateLink::a->FilePath->FilePath->IO()vGetModificationTimefsfp=dos<-vGetFileStatusfsfpreturn$epochToClockTime(withStatsvModificationTime)vRaiseError_etdescmfp=ioError$mkIOErroretdescNothingmfpvGetCurrentDirectoryfs=ehfs"vGetCurrentDirectory"vSetCurrentDirectoryfs_=ehfs"vSetCurrentDirectory"vGetDirectoryContentsfs_=ehfs"vGetDirectoryContents"vDoesFileExistfsfp=Control.Exception.catch(dos<-vGetFileStatusfsfpreturn$withStatsvIsRegularFile)(\(_::Control.Exception.IOException)->returnFalse)vDoesDirectoryExistfsfp=Control.Exception.catch(dos<-vGetFileStatusfsfpreturn$withStatsvIsDirectory)(\(_::Control.Exception.IOException)->returnFalse)vDoesExistfsfp=Control.Exception.catch(dos<-vGetSymbolicLinkStatusfsfpreturnTrue)(\(_::Control.Exception.IOException)->returnFalse)vCreateDirectoryfs_=ehfs"vCreateDirectory"vRemoveDirectoryfs_=ehfs"vRemoveDirectory"vRemoveFilefs_=ehfs"vRemoveFile"vRenameFilefs__=ehfs"vRenameFile"vRenameDirectoryfs__=ehfs"vRenameDirectory"vCreateSymbolicLinkfs__=ehfs"vCreateSymbolicLink"vReadSymbolicLinkfs_=ehfs"vReadSymbolicLink"vCreateLinkfs__=ehfs"vCreateLink"vGetSymbolicLinkStatus=vGetFileStatus-- | Error handler helpereh::HVFSa=>a->String->IOcehfsdesc=vRaiseErrorfsillegalOperationErrorType(desc++" is not implemented in this HVFS class")Nothing{- | Types that can open a HVIO object should be instances of this class.
You need only implement 'vOpen'. -}classHVFSa=>HVFSOpenableawherevOpen::a->FilePath->IOMode->IOHVFSOpenEncapvReadFile::a->FilePath->IOStringvWriteFile::a->FilePath->String->IO()vOpenBinaryFile::a->FilePath->IOMode->IOHVFSOpenEncapvReadFilehfp=dooe<-vOpenhfpReadModewithOpenoe(\fh->vGetContentsfh)vWriteFilehfps=dooe<-vOpenhfpWriteModewithOpenoe(\fh->dovPutStrfhsvClosefh)-- | Open a file in binary mode.vOpenBinaryFile=vOpeninstanceShowFileStatuswhereshow_="<FileStatus>"------------------------------------------------------------------------ Standard implementations----------------------------------------------------------------------instanceHVFSStatFileStatuswherevDeviceID=deviceIDvFileID=fileIDvFileMode=fileModevLinkCount=linkCountvFileOwner=fileOwnervFileGroup=fileGroupvSpecialDeviceID=specialDeviceIDvFileSize=fileSizevAccessTime=accessTimevModificationTime=modificationTimevStatusChangeTime=statusChangeTimevIsBlockDevice=isBlockDevicevIsCharacterDevice=isCharacterDevicevIsNamedPipe=isNamedPipevIsRegularFile=isRegularFilevIsDirectory=isDirectoryvIsSymbolicLink=isSymbolicLinkvIsSocket=isSocketdataSystemFS=SystemFSderiving(Eq,Show)instanceHVFSSystemFSwherevGetCurrentDirectory_=getCurrentDirectoryvSetCurrentDirectory_=setCurrentDirectoryvGetDirectoryContents_=getDirectoryContentsvDoesFileExist_=doesFileExistvDoesDirectoryExist_=doesDirectoryExistvCreateDirectory_=createDirectoryvRemoveDirectory_=removeDirectoryvRenameDirectory_=renameDirectoryvRemoveFile_=removeFilevRenameFile_=renameFilevGetFileStatus_fp=getFileStatusfp>>=return.HVFSStatEncap#if !(defined(mingw32_HOST_OS) || defined(mingw32_TARGET_OS) || defined(__MINGW32__))vGetSymbolicLinkStatus_fp=getSymbolicLinkStatusfp>>=return.HVFSStatEncap#else-- No symlinks on Windows; just get the file status directlyvGetSymbolicLinkStatus=vGetFileStatus#endif#if MIN_VERSION_directory(1,2,0)vGetModificationTime_p=getModificationTimep>>=(\modUTCTime->return$TOD((toEnum.fromEnum.utcTimeToPOSIXSeconds)modUTCTime)0)#elsevGetModificationTime_=getModificationTime#endif#if !(defined(mingw32_HOST_OS) || defined(mingw32_TARGET_OS) || defined(__MINGW32__))vCreateSymbolicLink_=createSymbolicLinkvReadSymbolicLink_=readSymbolicLinkvCreateLink_=createLink#elsevCreateSymbolicLink___=fail"Symbolic link creation not supported by Windows"vReadSymbolicLink__=fail"Symbolic link reading not supported by Widnows"vCreateLink___=fail"Hard link creation not supported by Windows"#endifinstanceHVFSOpenableSystemFSwherevOpen_fpiomode=openFilefpiomode>>=return.HVFSOpenEncapvOpenBinaryFile_fpiomode=openBinaryFilefpiomode>>=return.HVFSOpenEncap