@@ -602,6 +602,7 @@ isDrive x = not (null x) && null (dropDrive x)
602
602
-- > Posix: splitFileName "/" == ("/","")
603
603
-- > Windows: splitFileName "c:" == ("c:","")
604
604
-- > Windows: splitFileName "\\\\?\\A:\\fred" == ("\\\\?\\A:\\","fred")
605
+ -- > Windows: splitFileName "\\\\?\\A:" == ("\\\\?\\A:","")
605
606
splitFileName :: FILEPATH -> (STRING , STRING )
606
607
splitFileName x = if null path
607
608
then (dotSlash, file)
@@ -644,20 +645,43 @@ splitFileName_ fp
644
645
-- or UNC location "\\?\UNC\foo", where path separator is a part of the drive name.
645
646
-- We can test this by trying dropDrive and falling back to splitDrive.
646
647
| 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 )
648
+ = case uncons2 dirSlash of
649
+ Just (s1, s2, bs')
650
+ | isPathSeparator s1
651
+ -- If bs' is empty, then s2 as the last character of dirSlash must be a path separator,
652
+ -- so we are in the middle of shared drive.
653
+ -- Otherwise, since s1 is a path separator, we might be in the middle of UNC path.
654
+ , null bs' || maybe False isIncompleteUNC (readDriveUNC dirSlash)
655
+ -> (fp, mempty )
656
+ -- This handles inputs like "//?/A:" and "//?/A:foo"
657
+ | isPathSeparator s1
658
+ , isPathSeparator s2
659
+ , Just (s3, s4, bs'') <- uncons2 bs'
660
+ , s3 == _question
661
+ , isPathSeparator s4
662
+ , null bs''
663
+ , Just (drive, rest) <- readDriveLetter file
664
+ -> (dirSlash <> drive, rest)
665
+ _ -> (dirSlash, file)
654
666
| otherwise
655
- = (dirSlash, file)
667
+ = (dirSlash, file)
656
668
where
657
669
(dirSlash, file) = breakEnd isPathSeparator fp
658
-
670
+ dropExcessTrailingPathSeparators x
671
+ | hasTrailingPathSeparator x
672
+ , let x' = dropWhileEnd isPathSeparator x
673
+ , otherwise = if | null x' -> singleton (last x)
674
+ | otherwise -> addTrailingPathSeparator x'
675
+ | otherwise = x
676
+
677
+ -- an "incomplete" UNC is one without a path (but potentially a drive)
659
678
isIncompleteUNC (pref, suff) = null suff && not (hasPenultimateColon pref)
660
- hasPenultimateColon = maybe False (maybe False ((== _colon) . snd ) . unsnoc . fst ) . unsnoc
679
+
680
+ -- e.g. @//?/a:/@ or @//?/a://@, but not @//?/a:@
681
+ hasPenultimateColon pref
682
+ | hasTrailingPathSeparator pref
683
+ = maybe False (maybe False ((== _colon) . snd ) . unsnoc . fst ) . unsnoc . dropExcessTrailingPathSeparators $ pref
684
+ | otherwise = False
661
685
662
686
-- | Set the filename.
663
687
--
0 commit comments