-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix W.splitFileName "/\\?/a:" #220
Changes from all commits
399df96
c394803
67a45a4
0530486
74713b9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
{-# LANGUAGE PatternGuards #-} | ||
{-# LANGUAGE TypeApplications #-} | ||
{-# LANGUAGE MultiWayIf #-} | ||
|
||
-- This template expects CPP definitions for: | ||
-- MODULE_NAME = Posix | Windows | ||
|
@@ -602,6 +603,7 @@ isDrive x = not (null x) && null (dropDrive x) | |
-- > Posix: splitFileName "/" == ("/","") | ||
-- > Windows: splitFileName "c:" == ("c:","") | ||
-- > Windows: splitFileName "\\\\?\\A:\\fred" == ("\\\\?\\A:\\","fred") | ||
-- > Windows: splitFileName "\\\\?\\A:" == ("\\\\?\\A:","") | ||
splitFileName :: FILEPATH -> (STRING, STRING) | ||
splitFileName x = if null path | ||
then (dotSlash, file) | ||
|
@@ -644,20 +646,46 @@ splitFileName_ fp | |
-- or UNC location "\\?\UNC\foo", where path separator is a part of the drive name. | ||
-- We can test this by trying dropDrive and falling back to splitDrive. | ||
| isWindows | ||
, Just (s1, _s2, bs') <- uncons2 dirSlash | ||
, isPathSeparator s1 | ||
-- If bs' is empty, then s2 as the last character of dirSlash must be a path separator, | ||
-- so we are in the middle of shared drive. | ||
-- Otherwise, since s1 is a path separator, we might be in the middle of UNC path. | ||
, null bs' || maybe False isIncompleteUNC (readDriveUNC dirSlash) | ||
= (fp, mempty) | ||
= case uncons2 dirSlash of | ||
Just (s1, s2, bs') | ||
| isPathSeparator s1 | ||
-- If bs' is empty, then s2 as the last character of dirSlash must be a path separator, | ||
-- so we are in the middle of shared drive. | ||
-- Otherwise, since s1 is a path separator, we might be in the middle of UNC path. | ||
, null bs' || maybe False isIncompleteUNC (readDriveUNC dirSlash) | ||
-> (fp, mempty) | ||
-- This handles inputs like "//?/A:" and "//?/A:foo" | ||
| isPathSeparator s1 | ||
, isPathSeparator s2 | ||
, Just (s3, s4, bs'') <- uncons2 bs' | ||
, s3 == _question | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that this is in fact a bug (although the old filepath behaves the same way):
There are three UNC namespaces:
I'm still unsure whether fixing those bugs will cause more good than harm, but those are bugs. |
||
, isPathSeparator s4 | ||
, null bs'' | ||
, Just (drive, rest) <- readDriveLetter file | ||
-> (dirSlash <> drive, rest) | ||
_ -> (dirSlash, file) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this line is redundant? I'tll fall through to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately it does not fall through, the line is required. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oops, I misread the code. |
||
| otherwise | ||
= (dirSlash, file) | ||
= (dirSlash, file) | ||
where | ||
(dirSlash, file) = breakEnd isPathSeparator fp | ||
|
||
-- an adjustant variant of 'dropTrailingPathSeparator' that normalises trailing path separators | ||
-- on windows | ||
dropTrailingPathSeparator' x = | ||
if hasTrailingPathSeparator x | ||
then let x' = dropWhileEnd isPathSeparator x | ||
in if | null x' -> singleton (last x) | ||
| isDrive x -> addTrailingPathSeparator x' | ||
| otherwise -> x' | ||
else x | ||
|
||
-- an "incomplete" UNC is one without a path (but potentially a drive) | ||
isIncompleteUNC (pref, suff) = null suff && not (hasPenultimateColon pref) | ||
hasPenultimateColon = maybe False (maybe False ((== _colon) . snd) . unsnoc . fst) . unsnoc | ||
|
||
-- e.g. @//?/a:/@ or @//?/a://@, but not @//?/a:@ | ||
hasPenultimateColon pref | ||
| hasTrailingPathSeparator pref | ||
= maybe False (maybe False ((== _colon) . snd) . unsnoc . fst) . unsnoc . dropTrailingPathSeparator' $ pref | ||
| otherwise = False | ||
|
||
-- | Set the filename. | ||
-- | ||
|
@@ -671,6 +699,7 @@ replaceFileName x y = a </> y where (a,_) = splitFileName_ x | |
-- | ||
-- > dropFileName "/directory/file.ext" == "/directory/" | ||
-- > dropFileName x == fst (splitFileName x) | ||
-- > isPrefixOf (takeDrive x) (dropFileName x) | ||
dropFileName :: FILEPATH -> FILEPATH | ||
dropFileName = fst . splitFileName | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trivia:
//?/A:foo
is actually invalid in some sense... it's hard to interpret it. UNC paths are absolute, butA:foo
on its own is a relative directory (relative to current working dir on driveA
). The windows API probably won't care, but no such file can ever exist, I think.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, but
filepath-1.4.2.2
does not mind to process//?/A:foo
even if it's complete nonsense.