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.
split_volume p is the pair (vol, q) where vol is the platform dependent volume of p or the empty string if there is none and q the path p without its volume, that is its optional root dir_sep and segments.
On POSIX if vol is non-empty then it can only be "/" (e.g. in v "//a/b"). On Windows vol may be one of the following prefixes parsed before an absolute root dir_sep, except in the first case where a relative path can follow:
segs p is p's non-empty list of segments. Absolute paths have an initial empty string added, this allows to recover the path's string with String.concat ~sep:dir_sep. Examples.
The following invariant holds:
equal p (v @@ (fst @@ split_volume p) ^ (String.concat ~sep:dir_sep
(segs p)))
is_dir_path p is true iff p represents a directory. This means that p's last segment is either empty ("") or relative. The property is invariant with respect to normalization. Examples.
is_file_path p is true iff p represents a file. This is the negation of is_dir_path. This means that p's last segment is neither empty ("") nor relative. The property is invariant with respect to normalization. Examples.
filename p is the file name of p. This is the last segment of p if p is a file path and the empty string otherwise. The result is invariant with respect to normalization. See also basename. Examples.
split_base p splits p into a directory d and a relative base path b such that:
b is a relative path that contains the segments of p that start at the last non-empty segment. This means that b has a single non-empty segment, and preserves directoryness of p. If p is a root path there are no such segments and b is "./".
d is a directory such that d // b represents the same path as p. They may however differ syntactically when converted to a string.
basename p is p'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 also filename and base. Examples.
Note.Normalizingp before using the function ensures the empty string is only returned iff p cannot be named (like in ".", "../../", "/", etc.)
rem_empty_seg p removes an existing last empty segment of p if p is not a root path. This ensure that if p is converted to a string it will not have a trailing dir_sep unless p is a root path. Note that this may affect p's directoryness. Examples.
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.
is_prefix prefix p is true if prefix is a prefix of p. This checks that:
prefix has the same optional volume as p.
prefix has the same optional root directory separator as p.
The list of segments of prefix is a prefix of those of p, ignoring the last empty segment of prefix if the number of non-empty segments of p is strictly larger than those of prefix. This means that is_prefix (v "a/") (v "a/b") is true but is_prefix (v "a/") (v "a") is false
find_prefix p p' is Some prefix if there exists prefix such that prefix is the longest path with is_prefix prefix p &&
is_prefix prefix p' = true and None otherwise. Note that if both p and p' are absolute and have the same volume then a prefix always exists: the root path of their volume. Examples.
None if prefix is not a prefix of p or if prefix and p are equal.
Some q otherwise where q is p without the prefix prefix and preserves p's directoryness. This means that q is a always relative and that the path prefix // q and p represent the same paths. They may however differ syntactically when converted to a string.
Some q if there exists a relative path q such that root // q and p represent the same paths, directoryness included. They may however differ syntactically when converted to a string. Note that q is normalized.
is_rooted root p is true iff the path p is the directoryroot or contained in root and that p can be relativized w.r.t. root (the normalized relative path will have no parent directory segments). Examples.
is_root p is true iff p 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.
is_current_dir p is true iff p is the current relative directory, i.e. either "." or "./". If prefix is true (defaults to false) simply checks that p 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.
is_parent_dir p is true iff p is the relative parent directory, i.e. either ".." or "../". If prefix is true (defaults to false), simply checks that p 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.
is_dotfile p is true iff p'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.
equal p p' is true if p and p' 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/..") is false. Normalizing the paths before testing avoids this problem.
to_string p is the path p as a string. The result can be safely converted back with v.
val of_string : string ->(t, [ `Msg of string ])Result.result
of_string s is the string s 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" return Result.Error.
Result.Error (`Msg (strf "%S: invalid path" s)) is returned if
s 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")
get_ext p is p's basename file extension or the empty string if there is no extension. If multi is true (defaults to false), returns the multiple file extension. Examples.
exists_ext ~multi p is true iff p's basename file extension is not empty. If multi is true (default to false) returns true iff p has more than one extension. Examples.
add_ext ext p is p with the string ext concatenated to p's basename, if non empty. If ext doesn't start with a '.' one is prefixed to it before concatenation except if ext is "". Examples.