filepath-1.5.4.0: Library for manipulating FilePaths in a cross platform way.

Safe Haskell | None
Language | Haskell2010

System.OsPath.Windows

Contents

- Types
- Filepath construction
- Filepath deconstruction
- Word construction
- Word deconstruction
- Separator predicates
- $PATH methods
- Extension functions
- Filename/directory functions
- Drive functions
- Trailing slash functions
- File name manipulations

Synopsis

- data WindowsString
- data WindowsChar
- type WindowsPath = WindowsString
- encodeUtf :: MonadThrow m => String -> m WindowsString
- unsafeEncodeUtf :: HasCallStack => String -> WindowsString
- encodeWith :: TextEncoding -> String -> Either EncodingException WindowsString
- encodeFS :: String -> IO WindowsPath
- pstr :: QuasiQuoter
- pack :: [WindowsChar] -> WindowsString
- decodeUtf :: MonadThrow m => WindowsString -> m String
- decodeWith :: TextEncoding -> WindowsString -> Either EncodingException String
- decodeFS :: WindowsPath -> IO String
- unpack :: WindowsString -> [WindowsChar]
- unsafeFromChar :: Char -> WindowsChar
- toChar :: WindowsChar -> Char
- pathSeparator :: WindowsChar
- pathSeparators :: [WindowsChar]
- isPathSeparator :: WindowsChar -> Bool
- searchPathSeparator :: WindowsChar
- isSearchPathSeparator :: WindowsChar -> Bool
- extSeparator :: WindowsChar
- isExtSeparator :: WindowsChar -> Bool
- splitSearchPath :: WindowsString -> [WindowsPath]
- splitExtension :: WindowsPath -> (WindowsPath, WindowsString)
- takeExtension :: WindowsPath -> WindowsString
- replaceExtension :: WindowsPath -> WindowsString -> WindowsPath
- (-<.>) :: WindowsPath -> WindowsString -> WindowsPath
- dropExtension :: WindowsPath -> WindowsPath
- addExtension :: WindowsPath -> WindowsString -> WindowsPath
- hasExtension :: WindowsPath -> Bool
- (<.>) :: WindowsPath -> WindowsString -> WindowsPath
- splitExtensions :: WindowsPath -> (WindowsPath, WindowsString)
- dropExtensions :: WindowsPath -> WindowsPath
- takeExtensions :: WindowsPath -> WindowsString
- replaceExtensions :: WindowsPath -> WindowsString -> WindowsPath
- isExtensionOf :: WindowsString -> WindowsPath -> Bool
- stripExtension :: WindowsString -> WindowsPath -> Maybe WindowsPath
- splitFileName :: WindowsPath -> (WindowsPath, WindowsPath)
- takeFileName :: WindowsPath -> WindowsPath
- replaceFileName :: WindowsPath -> WindowsString -> WindowsPath
- dropFileName :: WindowsPath -> WindowsPath
- takeBaseName :: WindowsPath -> WindowsPath
- replaceBaseName :: WindowsPath -> WindowsString -> WindowsPath
- takeDirectory :: WindowsPath -> WindowsPath
- replaceDirectory :: WindowsPath -> WindowsPath -> WindowsPath
- combine :: WindowsPath -> WindowsPath -> WindowsPath
- (</>) :: WindowsPath -> WindowsPath -> WindowsPath
- splitPath :: WindowsPath -> [WindowsPath]
- joinPath :: [WindowsPath] -> WindowsPath
- splitDirectories :: WindowsPath -> [WindowsPath]
- splitDrive :: WindowsPath -> (WindowsPath, WindowsPath)
- joinDrive :: WindowsPath -> WindowsPath -> WindowsPath
- takeDrive :: WindowsPath -> WindowsPath
- hasDrive :: WindowsPath -> Bool
- dropDrive :: WindowsPath -> WindowsPath
- isDrive :: WindowsPath -> Bool
- hasTrailingPathSeparator :: WindowsPath -> Bool
- addTrailingPathSeparator :: WindowsPath -> WindowsPath
- dropTrailingPathSeparator :: WindowsPath -> WindowsPath
- normalise :: WindowsPath -> WindowsPath
- equalFilePath :: WindowsPath -> WindowsPath -> Bool
- makeRelative :: WindowsPath -> WindowsPath -> WindowsPath
- isRelative :: WindowsPath -> Bool
- isAbsolute :: WindowsPath -> Bool
- isValid :: WindowsPath -> Bool
- makeValid :: WindowsPath -> WindowsPath

# Types

data WindowsString Source #

Commonly used windows string as wide character bytes.

#### Instances

Instances details

NFData WindowsString |
Instance details Defined in System.OsString.Internal.Types Methods rnf :: WindowsString -> () Source #
Monoid WindowsString |
Instance details Defined in System.OsString.Internal.Types Methods mempty :: WindowsString # mappend :: WindowsString -> WindowsString -> WindowsString # mconcat :: [WindowsString] -> WindowsString #
Semigroup WindowsString |
Instance details Defined in System.OsString.Internal.Types Methods (<>) :: WindowsString -> WindowsString -> WindowsString # sconcat :: NonEmpty WindowsString -> WindowsString # stimes :: Integral b => b -> WindowsString -> WindowsString #
Generic WindowsString |

Instance details

Defined in System.OsString.Internal.Types

Associated Types

type Rep WindowsString |
Instance details Defined in System.OsString.Internal.Types type Rep WindowsString = D1 ('MetaData "WindowsString" "System.OsString.Internal.Types" "os-string-2.0.7-bb8c" 'True) (C1 ('MetaCons "WindowsString" 'PrefixI 'True) (S1 ('MetaSel ('Just "getWindowsString") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 ShortByteString)))

Methods

from :: WindowsString -> Rep WindowsString x #

to :: Rep WindowsString x -> WindowsString #

Show WindowsString | Decodes as UCS-2.
Instance details Defined in System.OsString.Internal.Types Methods showsPrec :: Int -> WindowsString -> ShowS # show :: WindowsString -> String # showList :: [WindowsString] -> ShowS #
Eq WindowsString |
Instance details Defined in System.OsString.Internal.Types Methods (==) :: WindowsString -> WindowsString -> Bool # (/=) :: WindowsString -> WindowsString -> Bool #
Ord WindowsString |

Instance details

Defined in System.OsString.Internal.Types

Methods

compare :: WindowsString -> WindowsString -> Ordering #

(<) :: WindowsString -> WindowsString -> Bool #

(<=) :: WindowsString -> WindowsString -> Bool #

(>) :: WindowsString -> WindowsString -> Bool #

(>=) :: WindowsString -> WindowsString -> Bool #

max :: WindowsString -> WindowsString -> WindowsString #

min :: WindowsString -> WindowsString -> WindowsString #

Lift WindowsString |
Instance details Defined in System.OsString.Internal.Types Methods lift :: Quote m => WindowsString -> m Exp Source # liftTyped :: forall (m :: Type -> Type). Quote m => WindowsString -> Code m WindowsString Source #
type Rep WindowsString |
Instance details Defined in System.OsString.Internal.Types type Rep WindowsString = D1 ('MetaData "WindowsString" "System.OsString.Internal.Types" "os-string-2.0.7-bb8c" 'True) (C1 ('MetaCons "WindowsString" 'PrefixI 'True) (S1 ('MetaSel ('Just "getWindowsString") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 ShortByteString)))

data WindowsChar Source #

#### Instances

Instances details

NFData WindowsChar |
Instance details Defined in System.OsString.Internal.Types Methods rnf :: WindowsChar -> () Source #
Generic WindowsChar |

Instance details

Defined in System.OsString.Internal.Types

Associated Types

type Rep WindowsChar |
Instance details Defined in System.OsString.Internal.Types type Rep WindowsChar = D1 ('MetaData "WindowsChar" "System.OsString.Internal.Types" "os-string-2.0.7-bb8c" 'True) (C1 ('MetaCons "WindowsChar" 'PrefixI 'True) (S1 ('MetaSel ('Just "getWindowsChar") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Word16)))

Methods

from :: WindowsChar -> Rep WindowsChar x #

to :: Rep WindowsChar x -> WindowsChar #

Show WindowsChar |
Instance details Defined in System.OsString.Internal.Types Methods showsPrec :: Int -> WindowsChar -> ShowS # show :: WindowsChar -> String # showList :: [WindowsChar] -> ShowS #
Eq WindowsChar |
Instance details Defined in System.OsString.Internal.Types Methods (==) :: WindowsChar -> WindowsChar -> Bool # (/=) :: WindowsChar -> WindowsChar -> Bool #
Ord WindowsChar |
Instance details Defined in System.OsString.Internal.Types Methods compare :: WindowsChar -> WindowsChar -> Ordering # (<) :: WindowsChar -> WindowsChar -> Bool # (<=) :: WindowsChar -> WindowsChar -> Bool # (>) :: WindowsChar -> WindowsChar -> Bool # (>=) :: WindowsChar -> WindowsChar -> Bool # max :: WindowsChar -> WindowsChar -> WindowsChar # min :: WindowsChar -> WindowsChar -> WindowsChar #
type Rep WindowsChar |
Instance details Defined in System.OsString.Internal.Types type Rep WindowsChar = D1 ('MetaData "WindowsChar" "System.OsString.Internal.Types" "os-string-2.0.7-bb8c" 'True) (C1 ('MetaCons "WindowsChar" 'PrefixI 'True) (S1 ('MetaSel ('Just "getWindowsChar") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Word16)))

type WindowsPath = WindowsString Source #

Filepaths are `wchar_t*` data on windows as passed to syscalls.

# Filepath construction

encodeUtf :: MonadThrow m => String -> m WindowsString Source #

Partial unicode friendly encoding.

This encodes as UTF16-LE (strictly), which is a pretty good guess.

Throws an `EncodingException` if encoding fails. If the input does not contain surrogate chars, you can use `unsafeEncodeUtf`.

unsafeEncodeUtf :: HasCallStack => String -> WindowsString Source #

Unsafe unicode friendly encoding.

Like `encodeUtf`, except it crashes when the input contains surrogate chars. For sanitized input, this can be useful.

encodeWith Source #

Arguments

:: TextEncoding | text encoding (wide char)
-> String |
-> Either EncodingException WindowsString |

Encode a `String` with the specified encoding.

Note: We expect a "wide char" encoding (e.g. UCS-2 or UTF-16). Anything that works with `Word16` boundaries. Picking an incompatible encoding may crash filepath operations.

encodeFS :: String -> IO WindowsPath Source #

pstr :: QuasiQuoter Source #

QuasiQuote a `WindowsPath`. This accepts Unicode characters and encodes as UTF-16LE. Runs `isValid` on the input.

pack :: [WindowsChar] -> WindowsString Source #

Pack a list of platform words to a platform string.

Note that using this in conjunction with `unsafeFromChar` to convert from `[Char]` to platform string is probably not what you want, because it will truncate unicode code points.

# Filepath deconstruction

decodeUtf :: MonadThrow m => WindowsString -> m String Source #

Partial unicode friendly decoding.

This decodes as UTF16-LE (strictly), which is a pretty good.

Throws a `EncodingException` if decoding fails.

decodeWith :: TextEncoding -> WindowsString -> Either EncodingException String Source #

Decode a `WindowsString` with the specified encoding.

The String is forced into memory to catch all exceptions.

decodeFS :: WindowsPath -> IO String Source #

unpack :: WindowsString -> [WindowsChar] Source #

Unpack a platform string to a list of platform words.

# Word construction

unsafeFromChar :: Char -> WindowsChar Source #

Truncates to 2 octets.

# Word deconstruction

toChar :: WindowsChar -> Char Source #

Converts back to a unicode codepoint (total).

# Separator predicates

pathSeparator :: WindowsChar Source #

The character that separates directories. In the case where more than one character is possible, `pathSeparator` is the 'ideal' one.

    pathSeparator == '\\'S

pathSeparators :: [WindowsChar] Source #

The list of all possible separators.

    pathSeparators == ['\\', '/']
    pathSeparator `elem` pathSeparators

isPathSeparator :: WindowsChar -> Bool Source #

Rather than using `(== `pathSeparator`)`, use this. Test if something is a path separator.

    isPathSeparator a == (a `elem` pathSeparators)

searchPathSeparator :: WindowsChar Source #

The character that is used to separate the entries in the $PATH environment variable.

    searchPathSeparator == ';'

isSearchPathSeparator :: WindowsChar -> Bool Source #

Is the character a file separator?

    isSearchPathSeparator a == (a == searchPathSeparator)

extSeparator :: WindowsChar Source #

File extension character

    extSeparator == '.'

isExtSeparator :: WindowsChar -> Bool Source #

Is the character an extension character?

    isExtSeparator a == (a == extSeparator)

# $PATH methods

splitSearchPath :: WindowsString -> [WindowsPath] Source #

Take a string, split it on the `searchPathSeparator` character.

Blank items are ignored and path elements are stripped of quotes.

    splitSearchPath "File1;File2;File3"  == ["File1","File2","File3"]
    splitSearchPath "File1;;File2;File3" == ["File1","File2","File3"]
    splitSearchPath "File1;\"File2\";File3" == ["File1","File2","File3"]

# Extension functions

splitExtension :: WindowsPath -> (WindowsPath, WindowsString) Source #

Split on the extension. `addExtension` is the inverse.

    splitExtension "/directory/path.ext" == ("/directory/path",".ext")
    uncurry (<>) (splitExtension x) == x
    Valid x => uncurry addExtension (splitExtension x) == x
    splitExtension "file.txt" == ("file",".txt")
    splitExtension "file" == ("file","")
    splitExtension "file/file.txt" == ("file/file",".txt")
    splitExtension "file.txt/boris" == ("file.txt/boris","")
    splitExtension "file.txt/boris.ext" == ("file.txt/boris",".ext")
    splitExtension "file/path.txt.bob.fred" == ("file/path.txt.bob",".fred")
    splitExtension "file/path.txt/" == ("file/path.txt/","")

takeExtension :: WindowsPath -> WindowsString Source #

Get the extension of a file, returns `""` for no extension, `.ext` otherwise.

    takeExtension "/directory/path.ext" == ".ext"
    takeExtension x == snd (splitExtension x)
    Valid x => takeExtension (addExtension x "ext") == ".ext"
    Valid x => takeExtension (replaceExtension x "ext") == ".ext"

replaceExtension :: WindowsPath -> WindowsString -> WindowsPath Source #

Set the extension of a file, overwriting one if already present, equivalent to `-<.>`.

    replaceExtension "/directory/path.txt" "ext" == "/directory/path.ext"
    replaceExtension "/directory/path.txt" ".ext" == "/directory/path.ext"
    replaceExtension "file.txt" ".bob" == "file.bob"
    replaceExtension "file.txt" "bob" == "file.bob"
    replaceExtension "file" ".bob" == "file.bob"
    replaceExtension "file.txt" "" == "file"
    replaceExtension "file.fred.bob" "txt" == "file.fred.txt"
    replaceExtension x y == addExtension (dropExtension x) y

(-<.>) :: WindowsPath -> WindowsString -> WindowsPath Source #

Remove the current extension and add another, equivalent to `replaceExtension`.

    "/directory/path.txt" -<.> "ext" == "/directory/path.ext"
    "/directory/path.txt" -<.> ".ext" == "/directory/path.ext"
    "foo.o" -<.> "c" == "foo.c"

dropExtension :: WindowsPath -> WindowsPath Source #

Remove last extension, and the "." preceding it.

    dropExtension "/directory/path.ext" == "/directory/path"
    dropExtension x == fst (splitExtension x)

addExtension :: WindowsPath -> WindowsString -> WindowsPath Source #

Add an extension, even if there is already one there, equivalent to `<.>`.

    addExtension "/directory/path" "ext" == "/directory/path.ext"
    addExtension "file.txt" "bib" == "file.txt.bib"
    addExtension "file." ".bib" == "file..bib"
    addExtension "file" ".bib" == "file.bib"
    addExtension "/" "x" == "/.x"
    addExtension x "" == x
    Valid x => takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
    Windows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt"

Add an extension, even if there is already one there, equivalent to `<.>`.

    addExtension "/directory/path" "ext" == "/directory/path.ext"
    addExtension "file.txt" "bib" == "file.txt.bib"
    addExtension "file." ".bib" == "file..bib"
    addExtension "file" ".bib" == "file.bib"
    addExtension "/" "x" == "/.x"
    addExtension x "" == x
    Valid x => takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
    addExtension "\\\\share" ".txt" == "\\\\share\\.txt"

hasExtension :: WindowsPath -> Bool Source #

Does the given filename have an extension?

    hasExtension "/directory/path.ext" == True
    hasExtension "/directory/path" == False
    null (takeExtension x) == not (hasExtension x)

(<.>) :: WindowsPath -> WindowsString -> WindowsPath Source #

Add an extension, even if there is already one there, equivalent to `addExtension`.

    "/directory/path" <.> "ext" == "/directory/path.ext"
    "/directory/path" <.> ".ext" == "/directory/path.ext"

splitExtensions :: WindowsPath -> (WindowsPath, WindowsString) Source #

Split on all extensions.

    splitExtensions "/directory/path.ext" == ("/directory/path",".ext")
    splitExtensions "file.tar.gz" == ("file",".tar.gz")
    uncurry (<>) (splitExtensions x) == x
    Valid x => uncurry addExtension (splitExtensions x) == x
    splitExtensions "file.tar.gz" == ("file",".tar.gz")

dropExtensions :: WindowsPath -> WindowsPath Source #

Drop all extensions.

    dropExtensions "/directory/path.ext" == "/directory/path"
    dropExtensions "file.tar.gz" == "file"
    not $ hasExtension $ dropExtensions x
    not $ any isExtSeparator $ takeFileName $ dropExtensions x

takeExtensions :: WindowsPath -> WindowsString Source #

Get all extensions.

    takeExtensions "/directory/path.ext" == ".ext"
    takeExtensions "file.tar.gz" == ".tar.gz"

replaceExtensions :: WindowsPath -> WindowsString -> WindowsPath Source #

Replace all extensions of a file with a new extension. Note that `replaceExtension` and `addExtension` both work for adding multiple extensions, so only required when you need to drop all extensions first.

    replaceExtensions "file.fred.bob" "txt" == "file.txt"
    replaceExtensions "file.fred.bob" "tar.gz" == "file.tar.gz"

isExtensionOf :: WindowsString -> WindowsPath -> Bool Source #

Does the given filename have the specified extension?

    "png" `isExtensionOf` "/directory/file.png" == True
    ".png" `isExtensionOf` "/directory/file.png" == True
    ".tar.gz" `isExtensionOf` "bar/foo.tar.gz" == True
    "ar.gz" `isExtensionOf` "bar/foo.tar.gz" == False
    "png" `isExtensionOf` "/directory/file.png.jpg" == False
    "csv/table.csv" `isExtensionOf` "/data/csv/table.csv" == False

stripExtension :: WindowsString -> WindowsPath -> Maybe WindowsPath Source #

Drop the given extension from a filepath, and the `"."` preceding it. Returns `Nothing` if the filepath does not have the given extension, or `Just` and the part before the extension if it does.

This function can be more predictable than `dropExtensions`, especially if the filename might itself contain `.` characters.

    stripExtension "hs.o" "foo.x.hs.o" == Just "foo.x"
    stripExtension "hi.o" "foo.x.hs.o" == Nothing
    dropExtension x == fromJust (stripExtension (takeExtension x) x)
    dropExtensions x == fromJust (stripExtension (takeExtensions x) x)
    stripExtension ".c.d" "a.b.c.d"  == Just "a.b"
    stripExtension ".c.d" "a.b..c.d" == Just "a.b."
    stripExtension "baz"  "foo.bar"  == Nothing
    stripExtension "bar"  "foobar"   == Nothing
    stripExtension ""     x          == Just x

# Filename/directory functions

splitFileName :: WindowsPath -> (WindowsPath, WindowsPath) Source #

Split a filename into directory and file. `</>` is the inverse. The first component will often end with a trailing slash.

    splitFileName "/directory/file.ext" == ("/directory/","file.ext")
    Valid x => uncurry (</>) (splitFileName x) == x || fst (splitFileName x) == "./"
    Valid x => isValid (fst (splitFileName x))
    splitFileName "file/bob.txt" == ("file/", "bob.txt")
    splitFileName "file/" == ("file/", "")
    splitFileName "bob" == ("./", "bob")
    splitFileName "c:" == ("c:","")

takeFileName :: WindowsPath -> WindowsPath Source #

Get the file name.

    takeFileName "/directory/file.ext" == "file.ext"
    takeFileName "test/" == ""
    takeFileName x `isSuffixOf` x
    takeFileName x == snd (splitFileName x)
    Valid x => takeFileName (replaceFileName x "fred") == "fred"
    Valid x => takeFileName (x </> "fred") == "fred"
    Valid x => isRelative (takeFileName x)

replaceFileName :: WindowsPath -> WindowsString -> WindowsPath Source #

Set the filename.

    replaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext"
    Valid x => replaceFileName x (takeFileName x) == x

dropFileName :: WindowsPath -> WindowsPath Source #

Drop the filename. Unlike `takeDirectory`, this function will leave a trailing path separator on the directory.

    dropFileName "/directory/file.ext" == "/directory/"
    dropFileName x == fst (splitFileName x)

takeBaseName :: WindowsPath -> WindowsPath Source #

Get the base name, without an extension or path.

    takeBaseName "/directory/file.ext" == "file"
    takeBaseName "file/test.txt" == "test"
    takeBaseName "dave.ext" == "dave"
    takeBaseName "" == ""
    takeBaseName "test" == "test"
    takeBaseName (addTrailingPathSeparator x) == ""
    takeBaseName "file/file.tar.gz" == "file.tar"

replaceBaseName :: WindowsPath -> WindowsString -> WindowsPath Source #

Set the base name.

    replaceBaseName "/directory/other.ext" "file" == "/directory/file.ext"
    replaceBaseName "file/test.txt" "bob" == "file/bob.txt"
    replaceBaseName "fred" "bill" == "bill"
    replaceBaseName "/dave/fred/bob.gz.tar" "new" == "/dave/fred/new.tar"
    Valid x => replaceBaseName x (takeBaseName x) == x

takeDirectory :: WindowsPath -> WindowsPath Source #

Get the directory name, move up one level.

    takeDirectory "/directory/other.ext" == "/directory"
    takeDirectory x `isPrefixOf` x || takeDirectory x == "."
    takeDirectory "foo" == "."
    takeDirectory "/" == "/"
    takeDirectory "/foo" == "/"
    takeDirectory "/foo/bar/baz" == "/foo/bar"
    takeDirectory "/foo/bar/baz/" == "/foo/bar/baz"
    takeDirectory "foo/bar/baz" == "foo/bar"
    takeDirectory "foo\\bar" == "foo"
    takeDirectory "foo\\bar\\\\" == "foo\\bar"
    takeDirectory "C:\\" == "C:\\"

replaceDirectory :: WindowsPath -> WindowsPath -> WindowsPath Source #

Set the directory, keeping the filename the same.

    replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext"
    Valid x => replaceDirectory x (takeDirectory x) `equalFilePath` x

combine :: WindowsPath -> WindowsPath -> WindowsPath Source #

An alias for `</>`.

(</>) :: WindowsPath -> WindowsPath -> WindowsPath Source #

Combine two paths with a path separator. If the second path starts with a path separator or a drive letter, then it returns the second. The intention is that `readFile (dir `</>` file)` will access the same file as `setCurrentDirectory dir; readFile file`.

    "/directory" </> "file.ext" == "/directory\\file.ext"
    "directory" </> "/file.ext" == "/file.ext"
    Valid x => (takeDirectory x </> takeFileName x) `equalFilePath` x

Combined:

    "C:\\foo" </> "bar" == "C:\\foo\\bar"
    "home" </> "bob" == "home\\bob"

Not combined:

    "home" </> "C:\\bob" == "C:\\bob"

Not combined (tricky):

If a filepath starts with a single slash, it is relative to the root of the current drive. In [1], this is (confusingly) referred to as an absolute path. The current behavior of `</>` is to never combine these forms.

    "home" </> "/bob" == "/bob"
    "home" </> "\\bob" == "\\bob"
    "C:\\home" </> "\\bob" == "\\bob"

From [1]: "If a file name begins with only a disk designator but not the backslash after the colon, it is interpreted as a relative path to the current directory on the drive with the specified letter." The current behavior of `</>` is to never combine these forms.

    "D:\\foo" </> "C:bar" == "C:bar"
    "C:\\foo" </> "C:bar" == "C:bar"

splitPath :: WindowsPath -> [WindowsPath] Source #

Split a path by the directory separator.

    splitPath "/directory/file.ext" == ["/","directory/","file.ext"]
    concat (splitPath x) == x
    splitPath "test//item/" == ["test//","item/"]
    splitPath "test/item/file" == ["test/","item/","file"]
    splitPath "" == []
    splitPath "c:\\test\\path" == ["c:\\","test\\","path"]

joinPath :: [WindowsPath] -> WindowsPath Source #

Join path elements back together.

    joinPath z == foldr (</>) "" z
    joinPath ["/","directory/","file.ext"] == "/directory/file.ext"
    Valid x => joinPath (splitPath x) == x
    joinPath [] == ""

splitDirectories :: WindowsPath -> [WindowsPath] Source #

Just as `splitPath`, but don't add the trailing slashes to each element.

    splitDirectories "/directory/file.ext" == ["/","directory","file.ext"]
    splitDirectories "test/file" == ["test","file"]
    splitDirectories "/test/file" == ["/","test","file"]
    splitDirectories "C:\\test\\file" == ["C:\\", "test", "file"]
    Valid x => joinPath (splitDirectories x) `equalFilePath` x
    splitDirectories "" == []
    splitDirectories "C:\\test\\\\\\file" == ["C:\\", "test", "file"]
    splitDirectories "/test///file" == ["/","test","file"]

# Drive functions

splitDrive :: WindowsPath -> (WindowsPath, WindowsPath) Source #

Split a path into a drive and a path.

    uncurry (<>) (splitDrive x) == x
    splitDrive "file" == ("","file")
    splitDrive "c:/file" == ("c:/","file")
    splitDrive "c:\\file" == ("c:\\","file")
    splitDrive "\\\\shared\\test" == ("\\\\shared\\","test")
    splitDrive "\\\\shared" == ("\\\\shared","")
    splitDrive "\\\\?\\UNC\\shared\\file" == ("\\\\?\\UNC\\shared\\","file")
    splitDrive "\\\\?\\UNCshared\\file" == ("\\\\?\\","UNCshared\\file")
    splitDrive "\\\\?\\d:\\file" == ("\\\\?\\d:\\","file")
    splitDrive "/d" == ("","/d")

joinDrive :: WindowsPath -> WindowsPath -> WindowsPath Source #

Join a drive and the rest of the path.

    Valid x => uncurry joinDrive (splitDrive x) == x
    Windows: joinDrive "C:" "foo" == "C:foo"
    Windows: joinDrive "C:\\" "bar" == "C:\\bar"
    Windows: joinDrive "\\\\share" "foo" == "\\\\share\\foo"
    Windows: joinDrive "/:" "foo" == "/:\\foo"

Join a drive and the rest of the path.

    Valid x => uncurry joinDrive (splitDrive x) == x
    joinDrive "C:" "foo" == "C:foo"
    joinDrive "C:\\" "bar" == "C:\\bar"
    joinDrive "\\\\share" "foo" == "\\\\share\\foo"
    joinDrive "/:" "foo" == "/:\\foo"

takeDrive :: WindowsPath -> WindowsPath Source #

Get the drive from a filepath.

    takeDrive x == fst (splitDrive x)

hasDrive :: WindowsPath -> Bool Source #

Does a path have a drive.

    not (hasDrive x) == null (takeDrive x)
    hasDrive "C:\\foo" == True
    hasDrive "C:foo" == True
    hasDrive "foo" == False
    hasDrive "" == False

dropDrive :: WindowsPath -> WindowsPath Source #

Delete the drive, if it exists.

    dropDrive x == snd (splitDrive x)

isDrive :: WindowsPath -> Bool Source #

Is an element a drive

    isDrive "C:\\" == True
    isDrive "C:\\foo" == False
    isDrive "" == False

# Trailing slash functions

hasTrailingPathSeparator :: WindowsPath -> Bool Source #

Is an item either a directory or the last character a path separator?

    hasTrailingPathSeparator "test" == False
    hasTrailingPathSeparator "test/" == True

addTrailingPathSeparator :: WindowsPath -> WindowsPath Source #

Add a trailing file path separator if one is not already present.

    hasTrailingPathSeparator (addTrailingPathSeparator x)
    hasTrailingPathSeparator x ==> addTrailingPathSeparator x == x

dropTrailingPathSeparator :: WindowsPath -> WindowsPath Source #

Remove any trailing path separators

    dropTrailingPathSeparator "file/test/" == "file/test"
    dropTrailingPathSeparator "/" == "/"
    dropTrailingPathSeparator "\\" == "\\"

# File name manipulations

normalise :: WindowsPath -> WindowsPath Source #

Normalise a file

- // outside of the drive can be made blank
- / -> `pathSeparator`
- ./ -> ""

Does not remove `".."`, because of symlinks.

    normalise "c:\\file/bob\\" == "C:\\file\\bob\\"
    normalise "c:\\" == "C:\\"
    normalise "C:.\\" == "C:"
    normalise "\\\\server\\test" == "\\\\server\\test"
    normalise "//server/test" == "\\\\server\\test"
    normalise "c:/file" == "C:\\file"
    normalise "/file" == "\\file"
    normalise "\\" == "\\"
    normalise "/./" == "\\"
    normalise "." == "."

equalFilePath :: WindowsPath -> WindowsPath -> Bool Source #

Equality of two filepaths. If you call `System.Directory.canonicalizePath` first this has a much better chance of working. Note that this doesn't follow symlinks or DOSNAM~1s.

Similar to `normalise`, this does not expand `".."`, because of symlinks.

    x == y ==> equalFilePath x y
    normalise x == normalise y ==> equalFilePath x y
    equalFilePath "foo" "foo/"
    not (equalFilePath "/a/../c" "/c")
    not (equalFilePath "foo" "/foo")
    equalFilePath "foo" "FOO"
    not (equalFilePath "C:" "C:/")

makeRelative :: WindowsPath -> WindowsPath -> WindowsPath Source #

Contract a filename, based on a relative path. Note that the resulting path will never introduce `..` paths, as the presence of symlinks means `../b` may not reach `a/b` if it starts from `a/c`. For a worked example see this blog post.

The corresponding `makeAbsolute` function can be found in `System.Directory`.

    makeRelative "/directory" "/directory/file.ext" == "file.ext"
    Valid x => makeRelative (takeDirectory x) x `equalFilePath` takeFileName x
    makeRelative x x == "."
    Valid x y => equalFilePath x y || (isRelative x && makeRelative y x == x) || equalFilePath (y </> makeRelative y x) x
    makeRelative "C:\\Home" "c:\\home\\bob" == "bob"
    makeRelative "C:\\Home" "c:/home/bob" == "bob"
    makeRelative "C:\\Home" "D:\\Home\\Bob" == "D:\\Home\\Bob"
    makeRelative "C:\\Home" "C:Home\\Bob" == "C:Home\\Bob"
    makeRelative "/Home" "/home/bob" == "bob"
    makeRelative "/" "//" == "//"

isRelative :: WindowsPath -> Bool Source #

Is a path relative, or is it fixed to the root?

    isRelative "path\\test" == True
    isRelative "c:\\test" == False
    isRelative "c:test" == True
    isRelative "c:\\" == False
    isRelative "c:/" == False
    isRelative "c:" == True
    isRelative "\\\\foo" == False
    isRelative "\\\\?\\foo" == False
    isRelative "\\\\?\\UNC\\foo" == False
    isRelative "/foo" == True
    isRelative "\\foo" == True

According to [1]:

- "A UNC name of any format [is never relative]."
- "You cannot use the "\?" prefix with a relative path."

isAbsolute :: WindowsPath -> Bool Source #

    not . isRelative

    isAbsolute x == not (isRelative x)

isValid :: WindowsPath -> Bool Source #

Is a filepath valid, i.e. could you create a file like it? This function checks for invalid names, and invalid characters, but does not check if length limits are exceeded, as these are typically filesystem dependent.

    isValid "" == False
    isValid "\0" == False
    isValid "c:\\test" == True
    isValid "c:\\test:of_test" == False
    isValid "test*" == False
    isValid "c:\\test\\nul" == False
    isValid "c:\\test\\prn.txt" == False
    isValid "c:\\nul\\file" == False
    isValid "\\\\" == False
    isValid "\\\\\\foo" == False
    isValid "\\\\?\\D:file" == False
    isValid "foo\tbar" == False
    isValid "nul .txt" == False
    isValid " nul.txt" == True

makeValid :: WindowsPath -> WindowsPath Source #

Take a filepath and make it valid; does not change already valid filepaths.

    isValid (makeValid x)
    isValid x ==> makeValid x == x
    makeValid "" == "_"
    makeValid "file\0name" == "file_name"
    makeValid "c:\\already\\/valid" == "c:\\already\\/valid"
    makeValid "c:\\test:of_test" == "c:\\test_of_test"
    makeValid "test*" == "test_"
    makeValid "c:\\test\\nul" == "c:\\test\\nul_"
    makeValid "c:\\test\\prn.txt" == "c:\\test\\prn_.txt"
    makeValid "c:\\test/prn.txt" == "c:\\test/prn_.txt"
    makeValid "c:\\nul\\file" == "c:\\nul_\\file"
    makeValid "\\\\\\foo" == "\\\\drive"
    makeValid "\\\\?\\D:file" == "\\\\?\\D:\\file"
    makeValid "nul .txt" == "nul _.txt"