Skip to content

Commit 59945aa

Browse files
committed
Merge branch 'windows-strikes-again'
2 parents c27dc31 + 071b671 commit 59945aa

File tree

5 files changed

+268
-54
lines changed

5 files changed

+268
-54
lines changed

.github/workflows/test.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ jobs:
5050
set -eux
5151
cabal update
5252
cabal build --enable-tests --enable-benchmarks
53-
cabal test
53+
cabal test --test-show-details=direct filepath-tests
54+
cabal test --test-show-details=direct --test-options='--quickcheck-tests 50_000' filepath-equivalent-tests
55+
cabal test --test-show-details=direct abstract-filepath
5456
cabal bench
5557
cabal haddock
5658
cabal check

System/FilePath/Internal.hs

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{-# LANGUAGE PatternGuards #-}
22
{-# LANGUAGE TypeApplications #-}
3+
{-# LANGUAGE MultiWayIf #-}
34

45
-- This template expects CPP definitions for:
56
-- MODULE_NAME = Posix | Windows
@@ -602,6 +603,7 @@ isDrive x = not (null x) && null (dropDrive x)
602603
-- > Posix: splitFileName "/" == ("/","")
603604
-- > Windows: splitFileName "c:" == ("c:","")
604605
-- > Windows: splitFileName "\\\\?\\A:\\fred" == ("\\\\?\\A:\\","fred")
606+
-- > Windows: splitFileName "\\\\?\\A:" == ("\\\\?\\A:","")
605607
splitFileName :: FILEPATH -> (STRING, STRING)
606608
splitFileName x = if null path
607609
then (dotSlash, file)
@@ -644,20 +646,43 @@ splitFileName_ fp
644646
-- or UNC location "\\?\UNC\foo", where path separator is a part of the drive name.
645647
-- We can test this by trying dropDrive and falling back to splitDrive.
646648
| isWindows
647-
, Just (s1, _s2, bs') <- uncons2 dirSlash
648-
, isPathSeparator s1
649-
-- If bs' is empty, then s2 as the last character of dirSlash must be a path separator,
650-
-- so we are in the middle of shared drive.
651-
-- Otherwise, since s1 is a path separator, we might be in the middle of UNC path.
652-
, null bs' || maybe False isIncompleteUNC (readDriveUNC dirSlash)
653-
= (fp, mempty)
649+
= case uncons2 dirSlash of
650+
Just (s1, s2, bs')
651+
| isPathSeparator s1
652+
-- If bs' is empty, then s2 as the last character of dirSlash must be a path separator,
653+
-- so we are in the middle of shared drive.
654+
-- Otherwise, since s1 is a path separator, we might be in the middle of UNC path.
655+
, null bs' || maybe False isIncompleteUNC (readDriveUNC dirSlash)
656+
-> (fp, mempty)
657+
-- This handles inputs like "//?/A:" and "//?/A:foo"
658+
| isPathSeparator s1
659+
, isPathSeparator s2
660+
, Just (s3, s4, bs'') <- uncons2 bs'
661+
, s3 == _question
662+
, isPathSeparator s4
663+
, null bs''
664+
, Just (drive, rest) <- readDriveLetter file
665+
-> (dirSlash <> drive, rest)
666+
_ -> (dirSlash, file)
654667
| otherwise
655-
= (dirSlash, file)
668+
= (dirSlash, file)
656669
where
657670
(dirSlash, file) = breakEnd isPathSeparator fp
658-
671+
dropExcessTrailingPathSeparators x
672+
| hasTrailingPathSeparator x
673+
, let x' = dropWhileEnd isPathSeparator x
674+
, otherwise = if | null x' -> singleton (last x)
675+
| otherwise -> addTrailingPathSeparator x'
676+
| otherwise = x
677+
678+
-- an "incomplete" UNC is one without a path (but potentially a drive)
659679
isIncompleteUNC (pref, suff) = null suff && not (hasPenultimateColon pref)
660-
hasPenultimateColon = maybe False (maybe False ((== _colon) . snd) . unsnoc . fst) . unsnoc
680+
681+
-- e.g. @//?/a:/@ or @//?/a://@, but not @//?/a:@
682+
hasPenultimateColon pref
683+
| hasTrailingPathSeparator pref
684+
= maybe False (maybe False ((== _colon) . snd) . unsnoc . fst) . unsnoc . dropExcessTrailingPathSeparators $ pref
685+
| otherwise = False
661686

662687
-- | Set the filename.
663688
--
@@ -671,6 +696,7 @@ replaceFileName x y = a </> y where (a,_) = splitFileName_ x
671696
--
672697
-- > dropFileName "/directory/file.ext" == "/directory/"
673698
-- > dropFileName x == fst (splitFileName x)
699+
-- > isPrefixOf (takeDrive x) (dropFileName x)
674700
dropFileName :: FILEPATH -> FILEPATH
675701
dropFileName = fst . splitFileName
676702

filepath.cabal

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,12 @@ test-suite filepath-equivalent-tests
138138
, base
139139
, bytestring >=0.11.3.0
140140
, filepath
141+
, generic-random
142+
, generic-deriving
141143
, os-string >=2.0.1
142144
, QuickCheck >=2.7 && <2.15
145+
, tasty
146+
, tasty-quickcheck
143147

144148
test-suite abstract-filepath
145149
default-language: Haskell2010

0 commit comments

Comments
 (0)