From 25955bc845f3680632b6230148f1a39d6a5c1e88 Mon Sep 17 00:00:00 2001 From: sof Date: Thu, 10 Jul 2003 19:25:59 +0000 Subject: [PATCH] [project @ 2003-07-10 19:25:58 by sof] For System.Directory.renameFile on Win32 platforms, implement the Haskell98 semantics of replacing the target file if it already exists (i.e., file/directory renaming is now done by dirUtils.c:__hscore_renameFile().) Document that System.Directory.renameDirectory will fail on Win32 boxes should the target directory exist. Merge to STABLE, I suppose. --- System/Directory.hs | 3 ++ System/Posix/Internals.hs | 2 +- cbits/dirUtils.c | 70 +++++++++++++++++++++++++++++++++++++++++++++ include/dirUtils.h | 1 + 4 files changed, 75 insertions(+), 1 deletion(-) diff --git a/System/Directory.hs b/System/Directory.hs index d37a364..17672c5 100644 --- a/System/Directory.hs +++ b/System/Directory.hs @@ -292,6 +292,9 @@ renaming directories in all situations (e.g. renaming to an existing directory, or across different physical devices), but the constraints must be documented. +On Win32 platforms, @renameDirectory@ fails if the /new/ directory already +exists. + The operation may fail with: * 'HardwareFault' diff --git a/System/Posix/Internals.hs b/System/Posix/Internals.hs index 47362ed..575ec66 100644 --- a/System/Posix/Internals.hs +++ b/System/Posix/Internals.hs @@ -354,7 +354,7 @@ foreign import ccall unsafe "HsBase.h read" foreign import ccall unsafe "HsBase.h readdir" c_readdir :: Ptr CDir -> IO (Ptr CDirent) -foreign import ccall unsafe "HsBase.h rename" +foreign import ccall unsafe "dirUtils.h __hscore_renameFile" c_rename :: CString -> CString -> IO CInt foreign import ccall unsafe "HsBase.h rewinddir" diff --git a/cbits/dirUtils.c b/cbits/dirUtils.c index 8d94a45..316a689 100644 --- a/cbits/dirUtils.c +++ b/cbits/dirUtils.c @@ -65,3 +65,73 @@ __hscore_readdir( HsAddr dirPtr, HsAddr pDirEnt ) } #endif } + +/* + * Function: __hscore_renameFile() + * + * Provide Haskell98's semantics for renaming files and directories. + * It mirrors that of POSIX.1's behaviour for rename() by overwriting + * the target if it exists (the MS CRT implementation of rename() returns + * an error + * + */ +HsInt +__hscore_renameFile( HsAddr src, + HsAddr dest) +{ +#if defined(_MSC_VER) || defined(_WIN32) + static int forNT = -1; + DWORD rc; + + /* ToDo: propagate error codes back */ + if (MoveFileA(src, dest)) { + return 0; + } else { + rc = GetLastError(); + } + + /* Failed...it could be because the target already existed. */ + if ( !GetFileAttributes(dest) ) { + /* No, it's not there - just fail. */ + errno = 0; + return (-1); + } + + if (forNT == -1) { + OSVERSIONINFO ovi; + ovi.dwOSVersionInfoSize = sizeof(ovi); + if ( !GetVersionEx(&ovi) ) { + errno = 0; + return (-1); + } + forNT = ((ovi.dwPlatformId & VER_PLATFORM_WIN32_NT) != 0); + } + + if (forNT) { + /* Easy, go for MoveFileEx() */ + if ( MoveFileExA(src, dest, MOVEFILE_REPLACE_EXISTING) ) { + return 0; + } else { + errno = 0; + return (-1); + } + } + + /* No MoveFileEx() for Win9x, try deleting the target. */ + /* Similarly, if the MoveFile*() ops didn't work out under NT */ + if (DeleteFileA(dest)) { + if (MoveFileA(src,dest)) { + return 0; + } else { + errno = 0; + return (-1); + } + } else { + errno = 0; + return (-1); + } +#else + return rename(src,dest); +#endif +} + diff --git a/include/dirUtils.h b/include/dirUtils.h index fb91ba1..ce11b08 100644 --- a/include/dirUtils.h +++ b/include/dirUtils.h @@ -7,5 +7,6 @@ #define __DIRUTILS_H__ extern HsInt __hscore_readdir(HsAddr dirPtr, HsAddr pDirEnt); +extern HsInt __hscore_renameFile(HsAddr src, HsAddr dest); #endif /* __DIRUTILS_H__ */ -- 1.7.10.4