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.

*v0.7.1 - homepage*

`val dir_sep : string`

`dir_sep`

is the platform dependent natural directory separator. This is
`"/"`

on POSIX and `"\\"`

on Windows.

`val is_rel_seg : string -> bool`

`is_rel_seg s`

is true iff `s`

is a relative segment, that is
`"."`

or `".."`

.

`val v : string -> t`

`v s`

is the string `s`

as a path.

- Raises Invalid_argument: if
`s`

is not a valid path. Use of_string to deal with untrusted input.

`add_seg p seg`

adds segment `seg`

to the segments of `p`

if
`p`

's last segment is non-empty or replaces the last empty
segment with `seg`

. Examples.

- Raises Invalid_argument: if is_seg
`seg`

is`false`

.

`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:

```
$(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`

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)))`

**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`

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.

`val is_file_path : t -> bool`

`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.

`val filename : t -> string`

`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.

Examples.

**Note.**Normalizing`p`

before using the function
ensures that `b`

is a relative segment iff `p`

cannot
be named (like in `"."`

, `"../../"`

, `"/"`

, etc.).

`val basename : t -> 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.**Normalizing`p`

before using the function
ensures the empty string is only returned iff `p`

cannot be
named (like in `"."`

, `"../../"`

, `"/"`

, etc.)

`parent p`

is a directory path that contains `p`

.
If `p`

is a root path this is `p`

itself.
Examples.

**Warning.**`parent p // base p`

may not represent `p`

, use
split_base for this.

`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.

`normalize p`

is a path that represents the same path as `p`

,
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`".."`

.

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.

**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.

`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`

Examples.

`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.

`rem_prefix prefix p`

is:

`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.

Examples.

`relativize ~root p`

is:

`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.`None`

otherwise.

Examples.

`is_rooted root p`

is `true`

iff the path `p`

is the
*directory*`root`

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.

`val is_rel : t -> bool`

`is_rel p`

is `true`

iff `p`

is a relative path, i.e. the root
directory separator is missing in `p`

.

`val is_abs : t -> bool`

`is_abs p`

is `true`

iff `p`

is an absolute path, i.e. the root
directory separator is present in `p`

.

`val is_root : t -> bool`

`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.

`val is_current_dir : ?prefix:bool -> t -> bool`

`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.

`val is_parent_dir : ?prefix:bool -> t -> bool`

`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.

`val is_dotfile : t -> bool`

`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.

`val to_string : t -> string`

`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. `Result.Error`

is returned if

`s`

or the path following the volume is empty (`""`

), except on Windows UNC paths, see below.`s`

has null byte (`'\x00'`

).- On Windows,
`s`

is an invalid UNC path (e.g.`"\\\\"`

or`"\\\\a"`

)

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`

.

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.

`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.

`has_ext e p`

is `true`

iff `get_ext p = e || get_ext ~multi:true p = e`

.
If `e`

doesn't start with a `'.'`

one is prefixed before making
the test. Examples.

`val exists_ext : ?multi:bool -> t -> bool`

`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.

`rem_ext p`

is `p`

with the extension of `p`

's
basename removed. If `multi`

is `true`

(default to
`false`

), the multiple file extension is
removed. Examples.

`split_ext ?multi p`

is `(rem_ext ?multi p, get_ext ?multi p)`

. If this is
`(q, ext)`

the following invariant holds:

`equal p (add_ext q ext)`