{-# LANGUAGE CPP #-}
module CanonicalizePath where
#include "util.inl"
import System.Directory.Internal
import System.OsPath ((</>), dropFileName, dropTrailingPathSeparator,
                      normalise, takeFileName)
import TestUtils

main :: TestEnv -> IO ()
main _t = do
  dot <- canonicalizePath ""
  dot2 <- canonicalizePath "."
  dot3 <- canonicalizePath "./"
  dot4 <- canonicalizePath "./."
  T(expectEq) () dot (dropTrailingPathSeparator dot)
  T(expectEq) () dot dot2
  T(expectEq) () dot dot3
  T(expectEq) () dot dot4

  writeFile "bar" ""
  bar <- canonicalizePath "bar"
  bar2 <- canonicalizePath "bar/"
  bar3 <- canonicalizePath "bar/."
  bar4 <- canonicalizePath "bar/./"
  bar5 <- canonicalizePath "./bar"
  bar6 <- canonicalizePath "./bar/"
  bar7 <- canonicalizePath "./bar/."
  T(expectEq) () bar (normalise (dot </> "bar"))
  T(expectEq) () bar bar2
  T(expectEq) () bar bar3
  T(expectEq) () bar bar4
  T(expectEq) () bar bar5
  T(expectEq) () bar bar6
  T(expectEq) () bar bar7

  createDirectory "foo"
  foo <- canonicalizePath "foo"
  foo2 <- canonicalizePath "foo/"
  foo3 <- canonicalizePath "foo/."
  foo4 <- canonicalizePath "foo/./"
  foo5 <- canonicalizePath "./foo"
  foo6 <- canonicalizePath "./foo/"
  T(expectEq) () foo (normalise (dot </> "foo"))
  T(expectEq) () foo foo2
  T(expectEq) () foo foo3
  T(expectEq) () foo foo4
  T(expectEq) () foo foo5
  T(expectEq) () foo foo6

  -- should not fail for non-existent paths
  fooNon <- canonicalizePath "foo/non-existent"
  fooNon2 <- canonicalizePath "foo/non-existent/"
  fooNon3 <- canonicalizePath "foo/non-existent/."
  fooNon4 <- canonicalizePath "foo/non-existent/./"
  fooNon5 <- canonicalizePath "./foo/non-existent"
  fooNon6 <- canonicalizePath "./foo/non-existent/"
  fooNon7 <- canonicalizePath "./foo/./non-existent"
  fooNon8 <- canonicalizePath "./foo/./non-existent/"
  T(expectEq) () fooNon (normalise (foo </> "non-existent"))
  T(expectEq) () fooNon fooNon2
  T(expectEq) () fooNon fooNon3
  T(expectEq) () fooNon fooNon4
  T(expectEq) () fooNon fooNon5
  T(expectEq) () fooNon fooNon6
  T(expectEq) () fooNon fooNon7
  T(expectEq) () fooNon fooNon8

  -- make sure ".." gets expanded properly by 'toExtendedLengthPath'
  -- (turns out this test won't detect the problem because GetFullPathName
  -- would expand them for us if we don't, but leaving it here anyway)
  T(expectEq) () foo =<< canonicalizePath (foo </> ".." </> "foo")

  supportsSymbolicLinks <- supportsSymlinks
  when supportsSymbolicLinks $ do

    let barQux = dot </> "bar" </> "qux"

    -- note: this also checks that "../bar" gets normalized to "..\\bar"
    --       since Windows does not like "/" in symbolic links targets
    createFileLink "../bar" "foo/bar"
    T(expectEq) () bar =<< canonicalizePath "foo/bar"
    T(expectEq) () barQux =<< canonicalizePath "foo/bar/qux"

    createDirectoryLink "foo" "lfoo"
    T(expectEq) () foo =<< canonicalizePath "lfoo"
    T(expectEq) () foo =<< canonicalizePath "lfoo/"
    T(expectEq) () bar =<< canonicalizePath "lfoo/bar"
    T(expectEq) () barQux =<< canonicalizePath "lfoo/bar/qux"

    -- create a haphazard chain of links
    createDirectoryLink "./../foo/../foo/." "./foo/./somelink3"
    createDirectoryLink ".././foo/somelink3" "foo/somelink2"
    createDirectoryLink "./foo/somelink2" "somelink"
    T(expectEq) () foo =<< canonicalizePath "somelink"

    -- regression test for #64
    createFileLink "../foo/non-existent" "foo/qux"
    removeDirectoryLink "foo/somelink3" -- break the chain made earlier
    qux <- canonicalizePath "foo/qux"
    T(expectEq) () qux =<< canonicalizePath "foo/non-existent"
    T(expectEq) () (foo </> "somelink3") =<< canonicalizePath "somelink"

    -- make sure it can handle loops
    createFileLink "loop1" "loop2"
    createFileLink "loop2" "loop1"
    loop1 <- canonicalizePath "loop1"
    loop2 <- canonicalizePath "loop2"
    T(expectEq) () loop1 (normalise (dot </> "loop1"))
    T(expectEq) () loop2 (normalise (dot </> "loop2"))

    -- make sure ".." gets expanded properly by 'toExtendedLengthPath'
    createDirectoryLink (foo </> ".." </> "foo") "foolink"
    _ <- listDirectory "foolink" -- make sure directory is accessible
    T(expectEq) () foo =<< canonicalizePath "foolink"

  caseInsensitive <-
    (False <$ createDirectory "FOO")
      `catch` \ e ->
        if isAlreadyExistsError e
        then pure True
        else throwIO e

  -- if platform is case-insensitive, we expect case to be canonicalized too
  when caseInsensitive $ do
    foo7 <- canonicalizePath "FOO"
    foo8 <- canonicalizePath "FOO/"
    T(expectEq) () foo foo7
    T(expectEq) () foo foo8

    fooNon9 <- canonicalizePath "FOO/non-existent"
    fooNon10 <- canonicalizePath "fOo/non-existent/"
    fooNon11 <- canonicalizePath "foO/non-existent/."
    fooNon12 <- canonicalizePath "FoO/non-existent/./"
    fooNon13 <- canonicalizePath "./fOO/non-existent"
    fooNon14 <- canonicalizePath "./FOo/non-existent/"
    cfooNon15 <- canonicalizePath "./FOO/./NON-EXISTENT"
    cfooNon16 <- canonicalizePath "./FOO/./NON-EXISTENT/"
    T(expectEq) () fooNon fooNon9
    T(expectEq) () fooNon fooNon10
    T(expectEq) () fooNon fooNon11
    T(expectEq) () fooNon fooNon12
    T(expectEq) () fooNon fooNon13
    T(expectEq) () fooNon fooNon14
    T(expectEq) () fooNon (dropFileName cfooNon15 <>
                           (os (toLower <$> so (takeFileName cfooNon15))))
    T(expectEq) () fooNon (dropFileName cfooNon16 <>
                           (os (toLower <$> so (takeFileName cfooNon16))))
    T(expectNe) () fooNon cfooNon15
    T(expectNe) () fooNon cfooNon16

    setCurrentDirectory "foo"
    foo9 <- canonicalizePath "../FOO"
    foo10 <- canonicalizePath "../FOO/"
    T(expectEq) () foo foo9
    T(expectEq) () foo foo10

    -- Make sure long file names can be canonicalized too
    -- (i.e. GetLongPathName by itself won't work)
    createDirectory "verylongdirectoryname"
    vldn <- canonicalizePath "verylongdirectoryname"
    vldn2 <- canonicalizePath "VERYLONGDIRECTORYNAME"
    T(expectEq) () vldn vldn2
