Skip to content

Commit d8c2edf

Browse files
authored
Merge pull request #254 from haskell/avoid-init-and-last
Avoid partial functions Data.List.init and Data.List.last
2 parents a31bf53 + 1962387 commit d8c2edf

File tree

1 file changed

+26
-21
lines changed

1 file changed

+26
-21
lines changed

System/FilePath/Internal.hs

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ import qualified Data.List as L
122122
#ifndef OS_PATH
123123
import Data.String (fromString)
124124
import System.Environment(getEnv)
125-
import Prelude (String, map, FilePath, Eq, IO, id, last, init, reverse, dropWhile, null, break, take, all, elem, any, span)
125+
import Prelude (String, map, FilePath, Eq, IO, id, reverse, dropWhile, null, break, take, all, elem, any, span)
126126
import Data.Char(toLower, toUpper, isAsciiLower, isAsciiUpper)
127127
import Data.List(stripPrefix, isSuffixOf, uncons, dropWhileEnd)
128128
#define CHAR Char
@@ -299,14 +299,15 @@ getSearchPath = fmap splitSearchPath (getEnv "PATH")
299299
-- Instead we speculatively split on the extension separator first, then check
300300
-- whether results are well-formed.
301301
splitExtension :: FILEPATH -> (STRING, STRING)
302-
splitExtension x
302+
splitExtension x = case unsnoc nameDot of
303303
-- Imagine x = "no-dots", then nameDot = ""
304-
| null nameDot = (x, mempty)
305-
-- Imagine x = "\\shared.with.dots\no-dots"
306-
| isWindows && null (dropDrive nameDot) = (x, mempty)
307-
-- Imagine x = "dir.with.dots/no-dots"
308-
| any isPathSeparator ext = (x, mempty)
309-
| otherwise = (init nameDot, extSeparator `cons` ext)
304+
Nothing -> (x, mempty)
305+
Just (initNameDot, _)
306+
-- Imagine x = "\\shared.with.dots\no-dots"
307+
| isWindows && null (dropDrive nameDot) -> (x, mempty)
308+
-- Imagine x = "dir.with.dots/no-dots"
309+
| any isPathSeparator ext -> (x, mempty)
310+
| otherwise -> (initNameDot, extSeparator `cons` ext)
310311
where
311312
(nameDot, ext) = breakEnd isExtSeparator x
312313

@@ -668,9 +669,9 @@ splitFileName_ fp
668669
where
669670
(dirSlash, file) = breakEnd isPathSeparator fp
670671
dropExcessTrailingPathSeparators x
671-
| hasTrailingPathSeparator x
672+
| Just lastX <- getTrailingPathSeparator x
672673
, let x' = dropWhileEnd isPathSeparator x
673-
, otherwise = if | null x' -> singleton (last x)
674+
, otherwise = if | null x' -> singleton lastX
674675
| otherwise -> addTrailingPathSeparator x'
675676
| otherwise = x
676677

@@ -742,10 +743,13 @@ replaceBaseName pth nam = combineAlways a (nam <.> ext)
742743
-- > hasTrailingPathSeparator "test" == False
743744
-- > hasTrailingPathSeparator "test/" == True
744745
hasTrailingPathSeparator :: FILEPATH -> Bool
745-
hasTrailingPathSeparator x
746-
| null x = False
747-
| otherwise = isPathSeparator $ last x
746+
hasTrailingPathSeparator = isJust . getTrailingPathSeparator
748747

748+
getTrailingPathSeparator :: FILEPATH -> Maybe CHAR
749+
getTrailingPathSeparator x = case unsnoc x of
750+
Just (_, lastX)
751+
| isPathSeparator lastX -> Just lastX
752+
_ -> Nothing
749753

750754
hasLeadingPathSeparator :: FILEPATH -> Bool
751755
hasLeadingPathSeparator = maybe False (isPathSeparator . fst) . uncons
@@ -767,11 +771,12 @@ addTrailingPathSeparator x = if hasTrailingPathSeparator x then x else x <> sing
767771
-- > Windows: dropTrailingPathSeparator "\\" == "\\"
768772
-- > Posix: not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x
769773
dropTrailingPathSeparator :: FILEPATH -> FILEPATH
770-
dropTrailingPathSeparator x =
771-
if hasTrailingPathSeparator x && not (isDrive x)
772-
then let x' = dropWhileEnd isPathSeparator x
773-
in if null x' then singleton (last x) else x'
774-
else x
774+
dropTrailingPathSeparator x = case getTrailingPathSeparator x of
775+
Just lastX
776+
| not (isDrive x)
777+
-> let x' = dropWhileEnd isPathSeparator x
778+
in if null x' then singleton lastX else x'
779+
_ -> x
775780

776781

777782
-- | Get the directory name, move up one level.
@@ -1044,9 +1049,9 @@ normalise filepath =
10441049
&& not (hasTrailingPathSeparator result)
10451050
&& not (isRelativeDrive drv)
10461051

1047-
isDirPath xs = hasTrailingPathSeparator xs
1048-
|| not (null xs) && last xs == _period
1049-
&& hasTrailingPathSeparator (init xs)
1052+
isDirPath xs = hasTrailingPathSeparator xs || case unsnoc xs of
1053+
Nothing -> False
1054+
Just (initXs, lastXs) -> lastXs == _period && hasTrailingPathSeparator initXs
10501055

10511056
f = joinPath . dropDots . propSep . splitDirectories
10521057

0 commit comments

Comments
 (0)