Module Fpath
File system paths, file extensions, path sets and maps.
A (file system) path specifies a file or a directory in a file system hierarchy. A path has three parts:
- An optional, platform-dependent, volume.
- An optional root directory separator
dir_sep
whose presence distinguishes absolute paths ("/a"
) from relative ones ("a"
) - A non-empty list of
dir_sep
separated segments. Segments are non empty strings except for maybe the last one. The latter distinguishes directory paths ("a/b/"
) from file paths ("a/b"
).
The path segments "."
and ".."
are relative path segments that respectively denote the current and parent directory. The basename of a path is its last non-empty segment if it is not a relative path segment or the empty string otherwise.
Consult a few important tips.
Note. Fpath
processes paths without accessing the file system.
%%VERSION%% - homepage
Separators and segments
val dir_sep : string
dir_sep
is the platform dependent natural directory separator. This is"/"
on POSIX and"\\"
on Windows.
val is_seg : string -> bool
is_seg s
istrue
iffs
does not containdir_sep
or'/'
or a0x00
byte.
Paths
val v : string -> t
v s
is the strings
as a path.- raises Invalid_argument
if
s
is not a valid path. Useof_string
to deal with untrusted input.
val add_seg : t -> string -> t
add_seg p seg
adds segmentseg
to the segments ofp
ifp
's last segment is non-empty or replaces the last empty segment withseg
. Examples.- raises Invalid_argument
if
is_seg
seg
isfalse
.
val append : t -> t -> t
append p q
appendsq
top
as follows:
val split_volume : t -> string * t
split_volume p
is the pair(vol, q)
wherevol
is the platform dependent volume ofp
or the empty string if there is none andq
the pathp
without its volume, that is its optional rootdir_sep
and segments.On POSIX if
vol
is non-empty then it can only be"/"
(e.g. inv "//a/b"
). On Windowsvol
may be one of the following prefixes parsed before an absolute rootdir_sep
, except in the first case where a relative path can follow:$(drive): \\$(server)\$(share) \\?\$(drive): \\?\$(server)\$(share) \\?\UNC\$(server)\$(share) \\.\$(device)
The following invariant holds:
equal p (v @@ vol ^ (to_string q))
val segs : t -> string list
segs p
isp
's non-empty list of segments. Absolute paths have an initial empty string added, this allows to recover the path's string withString
.concat~sep:dir_sep
. Examples.The following invariant holds:
equal p (v @@ (fst @@ split_volume p) ^ (String.concat ~sep:dir_sep (segs p)))
File and directory paths
Note. The following functions use syntactic semantic properties of paths. Given a path, these properties can be different from the one your file system attributes to it.
val is_dir_path : t -> bool
is_dir_path p
istrue
iffp
represents a directory. This means thatp
's last segment is either empty (""
) or relative. The property is invariant with respect to normalization. Examples.
val is_file_path : t -> bool
is_file_path p
istrue
iffp
represents a file. This is the negation ofis_dir_path
. This means thatp
's last segment is neither empty (""
) nor relative. The property is invariant with respect to normalization. Examples.
val to_dir_path : t -> t
to_dir_path p
isadd_seg
p ""
. It ensure that the result represents a directory and, if converted to a string, that it ends with adir_sep
. Examples.
val filename : t -> string
filename p
is the file name ofp
. This is the last segment ofp
ifp
is a file path and the empty string otherwise. The result is invariant with respect to normalization. See alsobasename
. Examples.
Base and parent paths
val split_base : t -> t * t
split_base p
splitsp
into a directoryd
and a relative base pathb
such that:b
is a relative path that contains the segments ofp
that start at the last non-empty segment. This means thatb
has a single non-empty segment, and preserves directoryness ofp
. Ifp
is a root path there are no such segments andb
is"./"
.d
is a directory such thatd // b
represents the same path asp
. They may however differ syntactically when converted to a string.
Note. Normalizing
p
before using the function ensures thatb
is a relative segment iffp
cannot be named (like in"."
,"../../"
,"/"
, etc.).
val basename : t -> string
basename p
isp
's last non-empty segment if non-relative or the empty string otherwise. The latter occurs only on root paths and on paths whose last non-empty segment is a relative segment. See alsofilename
andbase
. Examples.Note. Normalizing
p
before using the function ensures the empty string is only returned iffp
cannot be named (like in"."
,"../../"
,"/"
, etc.)
val parent : t -> t
parent p
is a directory path that containsp
. Ifp
is a root path this isp
itself. Examples.Warning.
parent p // base p
may not representp
, usesplit_base
for this.
Normalization
val rem_empty_seg : t -> t
rem_empty_seg p
removes an existing last empty segment ofp
ifp
is not a root path. This ensure that ifp
is converted to a string it will not have a trailingdir_sep
unlessp
is a root path. Note that this may affectp
's directoryness. Examples.
val normalize : t -> t
normalize p
is a path that represents the same path asp
, directoryness included, and that has the following properties:- If
p
is absolute the resulting path has no"."
and".."
segments. - If
p
is relative the resulting path is either"./"
or it has no"."
segments and".."
segments may only appear as initial segments. - If
p
is a directory it always end with an empty segment; this means it doesn't end with"."
or".."
.
Warning. Like file and directory path functions this function does not consult the file system and is purely based on the syntactic semantic of paths which can be different from the one of your concrete file system attributes. For example in presence of symbolic links the resulting path may not point to the same entity. Use the normalization functions of your OS system library to ensure correct behaviour with respect to a concrete file system.
- If
Prefixes
Warning. The syntactic prefix relation between paths does not, in general, entail directory containement. The following examples show this:
is_prefix (v "..") (v "../..") = true
is_prefix (v "..") (v ".") = false
However, on normalized, absolute paths, the prefix relation does entail directory containement. See also is_rooted
.
val is_prefix : t -> t -> bool
is_prefix prefix p
istrue
ifprefix
is a prefix ofp
. This checks that:prefix
has the same optional volume asp
.prefix
has the same optional root directory separator asp
.- The list of segments of
prefix
is a prefix of those ofp
, ignoring the last empty segment ofprefix
if the number of non-empty segments ofp
is strictly larger than those ofprefix
. This means thatis_prefix (v "a/") (v "a/b")
istrue
butis_prefix (v "a/") (v "a")
isfalse
val find_prefix : t -> t -> t option
find_prefix p p'
isSome prefix
if there existsprefix
such thatprefix
is the longest path withis_prefix prefix p && is_prefix prefix p' = true
andNone
otherwise. Note that if bothp
andp'
are absolute and have the same volume then a prefix always exists: the root path of their volume. Examples.
val rem_prefix : t -> t -> t option
rem_prefix prefix p
is:None
ifprefix
is not a prefix ofp
or ifprefix
andp
are equal.Some q
otherwise whereq
isp
without the prefixprefix
and preservesp
's directoryness. This means thatq
is a always relative and that the pathprefix // q
andp
represent the same paths. They may however differ syntactically when converted to a string.
Roots and relativization
val relativize : root:t -> t -> t option
relativize ~root p
is:Some q
if there exists a relative pathq
such thatroot // q
andp
represent the same paths, directoryness included. They may however differ syntactically when converted to a string. Note thatq
is normalized.None
otherwise.
val is_rooted : root:t -> t -> bool
is_rooted root p
istrue
iff the pathp
is the directoryroot
or contained inroot
and thatp
can be relativized w.r.t.root
(the normalized relative path will have no parent directory segments). Examples.
Predicates and comparison
val is_rel : t -> bool
is_rel p
istrue
iffp
is a relative path, i.e. the root directory separator is missing inp
.
val is_abs : t -> bool
is_abs p
istrue
iffp
is an absolute path, i.e. the root directory separator is present inp
.
val is_root : t -> bool
is_root p
istrue
iffp
is a root directory, i.e.p
has the root directory separator and a single, empty, segment. Examples.Warning. By definition this is a syntactic test. For example it will return
false
on"/a/.."
or"/.."
. Normalizing the path before testing avoids this problem.
val is_current_dir : ?prefix:bool -> t -> bool
is_current_dir p
is true iffp
is the current relative directory, i.e. either"."
or"./"
. Ifprefix
istrue
(defaults tofalse
) simply checks thatp
is relative and its first segment is"."
.Warning. By definition this is a syntactic test. For example it will return
false
on"./a/.."
or"./."
. Normalizing the path before testing avoids this problem.
val is_parent_dir : ?prefix:bool -> t -> bool
is_parent_dir p
istrue
iffp
is the relative parent directory, i.e. either".."
or"../"
. Ifprefix
istrue
(defaults tofalse
), simply checks thatp
is relative and its first segment is".."
.Warning. By definition this is a syntactic test. For example it will return
false
on"./a/../.."
or"./.."
. Normalizing the path before testing avoids this problem.
val is_dotfile : t -> bool
is_dotfile p
istrue
iffp
's basename is non empty and starts with a'.'
.Warning. By definition this is a syntactic test. For example it will return
false
on".ssh/."
. Normalizing the path before testing avoids this problem.
val equal : t -> t -> bool
equal p p'
istrue
ifp
andp'
have the same volume are both relative or absolute and have the same segments.Warning. By definition this is a syntactic test. For example
equal (v "./") (v "a/..")
isfalse
. Normalizing the paths before testing avoids this problem.
Conversions and pretty printing
val to_string : t -> string
to_string p
is the pathp
as a string. The result can be safely converted back withv
.
val of_string : string -> (t, [ `Msg of string ]) Stdlib.result
of_string s
is the strings
as a path. The following transformations are performed on the string:- On Windows any
'/'
occurence is converted to'\\'
before any processing occurs. - Non-initial empty segments are suppressed;
"a//b"
becomes"a/b"
,"//a////b//"
becomes"//a/b/"
, etc. - On Windows empty absolute UNC paths are completed to their root. For example
"\\\\server\\share"
becomes"\\\\server\\share\\"
, but incomplete UNC volumes like"\\\\a"
returnError
.
Error (`Msg (strf "%S: invalid path" s))
is returned ifs
or the path following the volume is empty (""
), except on Windows UNC paths, see above.s
has null byte ('\x00'
).- On Windows,
s
is an invalid UNC path (e.g."\\\\"
or"\\\\a"
)
- On Windows any
val dump : Stdlib.Format.formatter -> t -> unit
dump ppf p
prints pathp
onppf
usingString
.dump.
File extensions
The file extension (resp. multiple file extension) of a path segment is the suffix that starts at the last (resp. first) occurence of a '.'
that is preceeded by at least one non '.'
character. If there is no such occurence in the segment, the extension is empty. With these definitions, "."
, ".."
, "..."
and dot files like ".ocamlinit"
or "..ocamlinit"
have no extension, but ".emacs.d"
and "..emacs.d"
do have one.
Warning. The following functions act on paths whose basename is non empty and do nothing otherwise. Normalizing p
before using the functions ensures that the functions do nothing iff p
cannot be named, see basename
.
val get_ext : ?multi:bool -> t -> ext
get_ext p
isp
's basename file extension or the empty string if there is no extension. Ifmulti
istrue
(defaults tofalse
), returns the multiple file extension. Examples.
val has_ext : ext -> t -> bool
has_ext e p
istrue
iffget_ext p = e || get_ext ~multi:true p = e
. Ife
doesn't start with a'.'
one is prefixed before making the test. Examples.
val mem_ext : ext list -> t -> bool
mem_ext exts p
isList.mem (get_ext p) exts || List.mem (get_ext ~multi:true p) exts
.
val exists_ext : ?multi:bool -> t -> bool
exists_ext ~multi p
istrue
iffp
's basename file extension is not empty. Ifmulti
istrue
(default tofalse
) returnstrue
iffp
has more than one extension. Examples.
val add_ext : ext -> t -> t
add_ext ext p
isp
with the stringext
concatenated top
's basename, if non empty. Ifext
doesn't start with a'.'
one is prefixed to it before concatenation except ifext
is""
. Examples.- raises Invalid_argument
if
is_seg
ext
isfalse
.
val rem_ext : ?multi:bool -> t -> t
rem_ext p
isp
with the extension ofp
's basename removed. Ifmulti
istrue
(default tofalse
), the multiple file extension is removed. Examples.
Path sets and maps
module Set : sig ... end
Path sets.
module Map : sig ... end
Path maps.
Tips
- The documentation sometimes talks about the last non-empty segment of a path. This usually means that we don't care whether the path is a file path (e.g.
"a"
) or a directory path (e.g."a/"
). - Windows accepts both
'\\'
and'/'
as directory separator. HoweverFpath
on Windows converts'/'
to'\\'
on the fly. Therefore you should either use'/'
for defining constant paths you inject withv
or better, construct them directly with(/)
.to_string
then converts paths to strings using the platform's specific directory separatordir_sep
. - Avoid platform specific volumes or hard-coding file hierarchy conventions in your constants.
- Do not assume there is a single root path and that it is
"/"
. On Windows each volume can have a root path. Useis_root
on normalized paths to detect roots. - Do not use
to_string
to construct URIs,to_string
usesdir_sep
to separate segments, on Windows this is'\\'
which is not what URIs expect. Access path segments directly with Separators and segments; note that you will need to percent encode these.
Examples
equal (add_seg (v "/a") "b") (v "/a/b")
equal (add_seg (v "/a/") "b") (v "/a/b")
equal (add_seg (v "/a/b") "") (v "/a/b/")
equal (add_seg (v "/a/b/") "") (v "/a/b/")
equal (add_seg (v "/") "") (v "/")
equal (add_seg (v "/") "a") (v "/a")
equal (add_seg (v ".") "") (v "./")
equal (add_seg (v ".") "a") (v "./a")
equal (add_seg (v "..") "") (v "../")
equal (add_seg (v "..") "a") (v "../a")
equal (append (v "/a/b/") (v "e/f")) (v "/a/b/e/f")
equal (append (v "/a/b") (v "e/f")) (v "/a/b/e/f")
equal (append (v "/a/b/") (v "/e/f")) (v "/e/f")
equal (append (v "a/b/") (v "e/f")) (v "a/b/e/f")
equal (append (v "a/b") (v "C:e")) (v "C:e")
(Windows)
segs (v "/a/b/") = [""; "a"; "b"; ""]
segs (v "/a/b") = [""; "a"; "b"]
segs (v "a/b/") = ["a"; "b"; ""]
segs (v "a/b") = ["a"; "b"]
segs (v "a") = ["a"]
segs (v "/") = [""; ""]
segs (v "\\\\.\\dev\\") = ["";""]
(Windows)segs (v "\\\\server\\share\\a") = ["";"a"]
(Windows)segs (v "C:a") = ["a"]
(Windows)segs (v "C:\\a") = ["";"a"]
(Windows)
is_dir_path (v ".") = true
is_dir_path (v "..") = true
is_dir_path (v "../") = true
is_dir_path (v "/") = true
is_dir_path (v "/a/b/") = true
is_dir_path (v "/a/b") = false
is_dir_path (v "a/") = true
is_dir_path (v "a") = false
is_dir_path (v "a/.") = true
is_dir_path (v "a/..") = true
is_dir_path (v "a/..b") = false
is_dir_path (v "C:\\") = true
(Windows)is_dir_path (v "C:a") = false
(Windows)
is_file_path (v ".") = false
is_file_path (v "..") = false
is_file_path (v "../") = false
is_file_path (v "/") = false
is_file_path (v "/a/b/") = false
is_file_path (v "/a/b") = true
is_file_path (v "a/") = false
is_file_path (v "a") = true
is_file_path (v "a/.") = false
is_file_path (v "a/..") = false
is_file_path (v "a/..b") = true
is_file_path (v "C:\\") = false
(Windows)is_file_path (v "C:a") = true
(Windows)
equal (to_dir_path @@ v ".") (v "./")
equal (to_dir_path @@ v "..") (v "../")
equal (to_dir_path @@ v "../") (v "../")
equal (to_dir_path @@ v "/") (v "/")
equal (to_dir_path @@ v "/a/b/") (v "/a/b/")
equal (to_dir_path @@ v "/a/b") (v "/a/b/")
equal (to_dir_path @@ v "a/") (v "a/")
equal (to_dir_path @@ v "a") (v "a/")
equal (to_dir_path @@ v "a/.") (v "a/./")
equal (to_dir_path @@ v "a/..") (v "a/../")
equal (to_dir_path @@ v "a/..b") (v "a/..b/")
equal (to_dir_path @@ v "\\\\server\\share\\") (v "\\\\server\\share\\")
(Windows)equal (to_dir_path @@ v "C:a") (v "C:a\\")
(Windows)equal (to_dir_path @@ v "C:\\") (v "C:\\")
(Windows)
filename (v ".") = ""
filename (v "./") = ""
filename (v "..") = ""
filename (v "../") = ""
filename (v "../..") = ""
filename (v "/") = ""
filename (v "/a/b/") = ""
filename (v "/a/b") = "b"
filename (v "a/") = ""
filename (v "a") = "a"
filename (v "a/.") = ""
filename (v "a/..") = ""
filename (v "a/..b") = "..b"
filename (v "C:\\") = ""
(Windows)filename (v "C:a") = "a"
(Windows)
(split_base @@ v ".") = (v "./"), (v ".")
(split_base @@ v "./") = (v "./"), (v "./")
(split_base @@ v "..") = (v "./"), (v "..")
(split_base @@ v "../") = (v "./"), (v "../")
(split_base @@ v "../../") = (v "../"), (v "../")
(split_base @@ v ".././") = (v "../"), (v "./")
(split_base @@ v "../../../") = (v "../../"), (v "../")
(split_base @@ v "/") = (v "/"), (v "./")
(split_base @@ v "/a/b/") = (v "/a/"), (v "b/")
(split_base @@ v "/a/b") = (v "/a/"), (v "b")
(split_base @@ v "a/") = (v "./"), (v "a/")
(split_base @@ v "a") = (v "./"), (v "a")
(split_base @@ v "a/b") = (v "a/"), (v "b")
(split_base @@ v "a/b/") = (v "a/b/"), (v "b/")
(split_base @@ v "a/.") = (v "a/"), (v ".")
(split_base @@ v "a/..") = (v "a/"), (v "..")
(split_base @@ v "a/../..") = (v "a/../"), (v "..")
(split_base @@ v "a/..b") = (v "a/"), (v "..b")
(split_base @@ v "./a") = (v "./"), (v "a")
(split_base @@ v "./a/") = (v "./"), (v "a/")
(split_base @@ v "../a") = (v "../"), (v "a")
(split_base @@ v "../a/") = (v "../"), (v "a/")
basename (v ".") = ""
basename (v "..") = ""
basename (v "../") = ""
basename (v "../../") = ""
basename (v "/") = ""
basename (v "/a/b/") = "b"
basename (v "/a/b") = "b"
basename (v "a/") = "a"
basename (v "a") = "a"
basename (v "a/.") = ""
basename (v "a/./") = ""
basename (v "a/..") = ""
basename (v "a/..b") = "..b"
basename (v "./a") = "a"
basename (v "../a") = "a"
basename (v "C:\\") = ""
(Windows)basename (v "C:a") = "a"
(Windows)
equal (parent @@ v ".") (v "./../")
equal (parent @@ v "..") (v "../../")
equal (parent @@ v "../") (v "../../")
equal (parent @@ v "../../") (v "../../../")
equal (parent @@ v "/") (v "/")
equal (parent @@ v "/a/b/") (v "/a/")
equal (parent @@ v "/a/b") (v "/a/")
equal (parent @@ v "a/") (v "./")
equal (parent @@ v "a") (v "./")
equal (parent @@ v "a/.") (v "a/./../")
equal (parent @@ v "a/./") (v "a/./../")
equal (parent @@ v "a/..") (v "a/../../")
equal (parent @@ v "a/../") (v "a/../../")
equal (parent @@ v "a/..b") (v "a/")
equal (parent @@ v "./a") (v "./")
equal (parent @@ v "../a") (v "../")
equal (parent @@ v "../../a") (v "../../")
equal (parent @@ v "\\\\server\\share\\") (v "\\\\server\\share\\")
(Windows)equal (parent @@ v "C:\\") (v "C:\\")
(Windows)equal (parent @@ v "C:a") (v "C:.\\")
(Windows)
equal (rem_empty_seg @@ v ".") (v ".")
equal (rem_empty_seg @@ v "..") (v "..")
equal (rem_empty_seg @@ v "../") (v "..")
equal (rem_empty_seg @@ v "../../") (v "../..")
equal (rem_empty_seg @@ v "/") (v "/")
equal (rem_empty_seg @@ v "/a/b/") (v "/a/b")
equal (rem_empty_seg @@ v "/a/b") (v "/a/b")
equal (rem_empty_seg @@ v "a/") (v "a")
equal (rem_empty_seg @@ v "a") (v "a")
equal (rem_empty_seg @@ v "a/.") (v "a/.")
equal (rem_empty_seg @@ v "a/./") (v "a/.")
equal (rem_empty_seg @@ v "a/..") (v "a/..")
equal (rem_empty_seg @@ v "a/../") (v "a/..")
equal (rem_empty_seg @@ v "a/..b") (v "a/..b")
equal (rem_empty_seg @@ v "./a") (v "./a")
equal (rem_empty_seg @@ v "../a") (v "../a")
equal (rem_empty_seg @@ v "../../a") (v "../../a")
equal (rem_empty_seg @@ v "\\\\server\\share\\") (v "\\\\server\\share\\")
(Windows)equal (rem_empty_seg @@ v "C:\\") (v "C:\\")
(Windows)equal (rem_empty_seg @@ v "C:a\\") (v "C:a")
(Windows)
equal (normalize @@ v ".") (v "./")
equal (normalize @@ v "..") (v "../")
equal (normalize @@ v "../") (v "../")
equal (normalize @@ v "../../") (v "../../")
equal (normalize @@ v "/") (v "/")
equal (normalize @@ v "/a/b/") (v "/a/b/")
equal (normalize @@ v "/a/b") (v "/a/b")
equal (normalize @@ v "a/") (v "a/")
equal (normalize @@ v "a") (v "a")
equal (normalize @@ v "a/.") (v "a/")
equal (normalize @@ v "a/./") (v "a/")
equal (normalize @@ v "a/..") (v "./")
equal (normalize @@ v "a/../") (v "./")
equal (normalize @@ v "a/..b") (v "a/..b")
equal (normalize @@ v "./a") (v "a")
equal (normalize @@ v "../a") (v "../a")
equal (normalize @@ v "../../a") (v "../../a")
equal (normalize @@ v "./a/..") (v "./")
equal (normalize @@ v "/a/b/./..") (v "/a/")
equal (normalize @@ v "/../..") (v "/")
equal (normalize @@ v "/a/../..") (v "/")
equal (normalize @@ v "./../..") (v "../../")
equal (normalize @@ v "../../a/") (v "../../a/")
equal (normalize @@ v "/a/b/c/./../../g") (v "/a/g")
equal (normalize @@ v "/a/b/c/./../../g/") (v "/a/g/")
equal (normalize @@ v "\\\\?\\UNC\\server\\share\\..") (v "\\\\?\\UNC\\server\\share\\")
(Windows)equal (normalize @@ v "\\\\server\\share\\") (v "\\\\server\\share\\")
(Windows)equal (normalize @@ v "C:\\") (v "C:\\")
(Windows)equal (normalize @@ v "C:a\\") (v "C:a\\")
(Windows)
is_prefix (v "/a/b") (v "/a/b") = true
is_prefix (v "/a/b") (v "/a/bc") = false
is_prefix (v "/a/b") (v "/a/b/") = true
is_prefix (v "a/b/") (v "a/b") = false
is_prefix (v "a/b/") (v "a/b/") = true
is_prefix (v "a/b/") (v "a/b/c") = true
is_prefix (v ".") (v "./") = true
is_prefix (v "..") (v ".") = false
is_prefix (v "C:a") (v "a") = false
(Windows)
find_prefix (v "a/b/c") (v "a/b/d")
isSome (v "a/b/")
find_prefix (v "a/b/c") (v "a/b/cd")
isSome (v "a/b/")
find_prefix (v "a/b") (v "a/b")
isSome (v "a/b")
find_prefix (v "a/b") (v "a/b/")
isSome (v "a/b")
find_prefix (v "a/b") (v "e/f")
isNone
find_prefix (v "/a/b") (v "/e/f")
isSome (v "/")
find_prefix (v "/a/b") (v "e/f")
isNone
find_prefix (v "C:\\a") (v "\\a")
isNone
(Windows)
rem_prefix (v "a/b/") (v "a/b")
isNone
rem_prefix (v "a/b/") (v "a/b/")
isNone
rem_prefix (v "a/b") (v "a/b")
isNone
rem_prefix (v "a/b") (v "a/b/")
isSome "./"
rem_prefix (v "a/b") (v "a/b/c")
isSome (v "c")
rem_prefix (v "a/b/") (v "a/b/c")
isSome (v "c")
rem_prefix (v "a/b") (v "a/b/c/")
isSome (v "c/")
rem_prefix (v "a/b/") (v "a/b/c/")
isSome (v "c/")
rem_prefix (v "C:\\a") (v "C:\\a\\b")
isSome (v "b")
(Windows)
relativize ~root:(v "/a/b") (v "c")
isNone
relativize ~root:(v "/a/b") (v "/c")
isSome (v "../../c")
relativize ~root:(v "/a/b") (v "/c/")
isSome (v "../../c/")
relativize ~root:(v "/a/b") (v "/c")
isSome (v "../../c")
relativize ~root:(v "/a/b") (v "/c/")
isSome (v "../../c/")
relativize ~root:(v "/a/b") (v "/a/b/c")
isSome (v "c")
relativize ~root:(v "/a/b") (v "/a/b/c/")
isSome (v "c/")
relativize ~root:(v "/a/b") (v "/a/b")
isNone
relativize ~root:(v "/a/b") (v "/a/b/")
isSome (v ".")
relativize ~root:(v "a/b") (v "/c")
isNone
.relativize ~root:(v "a/b") (v "c")
isSome (v "../../c")
relativize ~root:(v "a/b") (v "c/")
isSome (v "../../c/")
relativize ~root:(v "a/b") (v "a/b/c")
isSome (v "c")
relativize ~root:(v "a/b") (v "a/b")
isSome (v ".")
relativize ~root:(v "a/b") (v "a/b/")
isSome (v ".")
relativize ~root:(v "../") (v "./")
isNone
relativize ~root:(v "../a") (v "b")
isNone
relativize ~root:(v "../a") (v "../b/c")
isSome (v "../b/c")
relativize ~root:(v "../../a") (v "../b")
isNone
relativize ~root:(v "../a") (v "../../b")
is(Some "../../b")
is_rooted ~root:(v "a/b") (v "a/b") = false
is_rooted ~root:(v "a/b") (v "a/b/") = true
is_rooted ~root:(v "a/b/") (v "a/b") = false
is_rooted ~root:(v "a/b/") (v "a/b/") = true
is_rooted ~root:(v "./") (v "a") = true
is_rooted ~root:(v "./") (v "a/") = true
is_rooted ~root:(v "./") (v "a/../") = true
is_rooted ~root:(v "./") (v "..") = false
is_rooted ~root:(v "../") (v "./") = false
is_rooted ~root:(v "../") (v "a") = false
is_rooted ~root:(v "../") (v "../") = true
is_rooted ~root:(v "../") (v "../a") = true
is_rooted ~root:(v "../a") (v "./") = false
is_rooted ~root:(v "/a") (v "/a/..") = true
is_rooted ~root:(v "/a") (v "/a/../") = true
is_rooted ~root:(v "/a") (v "/..") = true
is_root (v "/") = true
is_root (v "/a") = false
is_root (v "/a/..") = false
is_root (v "//") = true
(POSIX)is_root (v "\\\\.\\dev\\") = true
(Windows)is_root (v "\\\\.\\dev\\a") = false
(Windows)is_root (v "\\\\server\\share\\") = true
(Windows)is_root (v "\\\\server\\share\\a") = false
(Windows)is_root (v "C:\\") = true
(Windows)is_root (v "C:a") = false
(Windows)is_root (v "C:\\a") = false
(Windows)
get_ext (v "/") = ""
get_ext (v "a/b") = ""
get_ext (v "a/b.mli/..") = ""
get_ext (v "a/b.mli/...") = ""
get_ext (v "a/b.") = "."
get_ext (v "a/b.mli") = ".mli"
get_ext ~multi:true (v "a/b.mli") = ".mli"
get_ext (v "a/b.mli/") = ".mli"
get_ext (v "a/.ocamlinit") = ""
get_ext (v "a/.emacs.d") = ".d"
get_ext (v "a/.emacs.d/") = ".d"
get_ext ~multi:true (v "a/.emacs.d") = ".d"
get_ext (v "a.tar.gz") = ".gz"
get_ext ~multi:true (v "a.tar.gz") = ".tar.gz"
has_ext "mli" (v "a/b.mli") = true
has_ext ".mli" (v "a/b.mli") = true
has_ext ".mli" (v "a/b.mli/") = true
has_ext ".mli" (v "a/bmli") = false
has_ext "mli" (v "a/bmli") = false
has_ext ".tar.gz" (v "a/f.tar.gz") = true
has_ext "tar.gz" (v "a/f.tar.gz") = true
has_ext ".gz" (v "a/f.tar.gz") = true
has_ext ".tar" (v "a/f.tar.gz") = false
has_ext ".cache" (v "a/.cache") = false
has_ext "" (v "a/b") = false
has_ext "" (v "a/b.") = true
has_ext "." (v "a/b.") = true
exists_ext (v "a/f") = false
exists_ext (v "a/f.") = true
exists_ext (v "a/f.gz") = true
exists_ext ~multi:true (v "a/f.gz") = false
exists_ext (v "a/f.tar.gz") = true
exists_ext ~multi:true (v "a/f.tar.gz") = true
exists_ext (v "a/f.tar.gz/") = true
exists_ext (v ".emacs.d") = true
exists_ext (v ".emacs.d/") = true
exists_ext (v ".ocamlinit") = false
equal (add_ext "mli" (v "a/b")) (v "a/b.mli")
equal (add_ext ".mli" (v "a/b")) (v "a/b.mli")
equal (add_ext ".mli" (v "a/b/")) (v "a/b.mli/")
equal (add_ext ".mli" (v "/")) (v "/")
equal (add_ext ".mli" (v "a/b/..")) (v "a/b/..")
equal (add_ext "." (v "a/b")) (v "a/b.")
equal (add_ext "" (v "a/b")) (v "a/b")
equal (add_ext "tar.gz" (v "a/f")) (v "a/f.tar.gz")
equal (add_ext ".tar.gz" (v "a/f")) (v "a/f.tar.gz")
equal (add_ext "gz" (v "a/f.tar") ) (v "a/f.tar.gz")
equal (add_ext ".gz" (v "a/f.tar") ) (v "a/f.tar.gz")
equal (rem_ext @@ v "/") (v "/")
equal (rem_ext @@ v "/a/b") (v "/a/b")
equal (rem_ext @@ v "/a/b.mli") (v "/a/b")
equal (rem_ext @@ v "/a/b.mli/") (v "/a/b/")
equal (rem_ext @@ v "/a/b.mli/..") (v "/a/b.mli/..")
equal (rem_ext @@ v "/a/b.mli/.") (v "/a/b.mli/.")
equal (rem_ext @@ v "a/.ocamlinit") (v "a/.ocamlinit")
equal (rem_ext @@ v "a/.emacs.d") (v "a/.emacs")
equal (rem_ext @@ v "a/.emacs.d/") (v "a/.emacs/")
equal (rem_ext @@ v "f.tar.gz") (v "f.tar")
equal (rem_ext ~multi:true @@ v "f.tar.gz") (v "f")
equal (rem_ext ~multi:true @@ v "f.tar.gz/") (v "f/")