content: {-# LANGUAGE CPP #-}
  
  module General.FileLock(usingLockFile) where
  
  import Control.Exception.Extra
  import System.FilePath
  import General.Extra
  import General.Cleanup
  #ifdef mingw32_HOST_OS
  import Control.Monad
  import Data.Bits
  import Data.Word
  import Foreign.Ptr
  import Foreign.C.String
  #else
  import System.IO
  import System.Posix.IO
  #endif
  
  #ifdef mingw32_HOST_OS
  
  #ifdef x86_64_HOST_ARCH
  #define CALLCONV ccall
  #else
  #define CALLCONV stdcall
  #endif
  
  foreign import CALLCONV unsafe "Windows.h CreateFileW" c_CreateFileW :: CWString -> Word32 -> Word32 -> Ptr () -> Word32 -> Word32 -> Ptr () -> IO (Ptr ())
  foreign import CALLCONV unsafe "Windows.h CloseHandle" c_CloseHandle :: Ptr () -> IO Bool
  foreign import CALLCONV unsafe "Windows.h GetLastError" c_GetLastError :: IO Word32
  
  c_GENERIC_WRITE = 0x40000000 :: Word32
  c_GENERIC_READ  = 0x80000000 :: Word32
  c_FILE_SHARE_NONE = 0 :: Word32
  c_OPEN_ALWAYS = 4 :: Word32
  c_FILE_ATTRIBUTE_NORMAL = 0x80 :: Word32
  c_INVALID_HANDLE_VALUE = intPtrToPtr (-1)
  c_ERROR_SHARING_VIOLATION = 32
  #endif
  
  usingLockFile :: Cleanup -> FilePath -> IO ()
  
  #ifdef mingw32_HOST_OS
  
  usingLockFile b file = do
      createDirectoryRecursive $ takeDirectory file
      let open = withCWString file $ \cfile ->
              c_CreateFileW cfile (c_GENERIC_READ .|. c_GENERIC_WRITE) c_FILE_SHARE_NONE nullPtr c_OPEN_ALWAYS c_FILE_ATTRIBUTE_NORMAL nullPtr
      h <- allocate b open (void . c_CloseHandle)
      when (h == c_INVALID_HANDLE_VALUE) $ do
          err <- c_GetLastError
          errorIO $ "Shake failed to acquire a file lock on " ++ file ++ "\n" ++
                      (if err == c_ERROR_SHARING_VIOLATION
                          then "ERROR_SHARING_VIOLATION - Shake is probably already running."
                          else "Code " ++ show err ++ ", unknown reason for failure.")
  
  #else
  
  usingLockFile cleanup file = do
      createDirectoryRecursive $ takeDirectory file
      tryIO $ writeFile file ""
  
      fd <- allocate cleanup (openSimpleFd file ReadWrite) closeFd
      let lock = (WriteLock, AbsoluteSeek, 0, 0)
      setLock fd lock `catchIO` \e -> do
          res <- getLock fd lock
          errorIO $ "Shake failed to acquire a file lock on " ++ file ++ "\n" ++
                      (case res of
                          Nothing -> ""
                          Just (pid, _) -> "Shake process ID " ++ show pid ++ " is using this lock.\n") ++
                      show e
  
  #ifndef MIN_VERSION_unix
  #define MIN_VERSION_unix(a,b,c) 0
  #endif
  #if MIN_VERSION_unix(2,8,0)
  openSimpleFd file mode = openFd file mode defaultFileFlags
  #else
  openSimpleFd file mode = openFd file mode Nothing defaultFileFlags
  #endif
  
  #endif
  