A file path is a syntactic specification of a file or a directory location in a file system hierarchy. It is made of three parts:
An optional, platform-dependent, volume (e.g. "C:").
An optional root directory separator whose presence distinguishes absolute paths from relative ones (e.g. "/a" versus "a").
A non-empty list of segments delimited by directory separators. Segments are non empty strings except for maybe the last one. The latter syntactically distinguishes directory paths from file paths (e.g. "a/b/" versus "a/b"). But a directory can also be specified by a file path and a syntactic directory path may not be a directory (e.g. a symlink).
The paths 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 (e.g. on "/" or "..").
natural_dir_sep_char is the natural directory separator for the platform. This is '\\' if Sys.win32 and '/' otherwise.
Warning. Do not test for equality against this character to assert directory separators. Use is_dir_sep_char, some platforms support more than one directory separator.
Warning. Do not test for equality against this string to assert directory separators. Use is_dir_sep, some platforms support more than one directory separator.
v s is the string s as a file path. Raises Invalid_argument if s is not a valid path. Use of_string to deal with untrusted input.
Warning. In code only use "/" in string literals as the directory separator even on Windows platforms (don't be upset, the module gives them back to you with backslashes).
null represents a file on the OS that discards all writes and returns end of file on reads. This is NUL if Sys.win32 is true and /dev/null otherwise. See also is_null.
ensure_trailing_dir_sep p is add_segment p "". This ensures that p has a final empty segment and thus a trailing directory separator when converted to a string.
strip_trailing_dir_sep p ensures p has no final empty segment unless p is a root path. When p is not a root path this ensure that p has no trailing directory separator when converted to a string.
Basename and parent directory
Note. The following functions use syntactic semantic properties of paths. Given a path, these properties can be different from the ones your file system attributes to it.
basename p is the last non-empty segment of p or the empty string otherwise. The latter occurs only on root paths and on paths whose last non-empty segment is a relative segment. If strip_exts is true (default to false) the basename's multiple extension, if any, is removed from the segment.
is_prefix prefix p is true iff prefix is a strict prefix of p that respects path segments. More formally iff the following two conditions hold:
not Fpath.(equal (to_dir_path prefix) (to_dir_path p))
Fpath.(String.is_prefix (to_string (to_dir_path prefix) (to_string p))) is true
Warning. By definition is_prefix p p is false. Note also that the prefix relation does not entail directory containement; for example is_prefix (v "..") (v "../..") holds.
Some q otherwise where q is p without the string prefix Fpath.to_dir_path prefix. This means that q is always relative, that it preserves p's syntactic directoryness and that Fpath.(equal (prefix // q) p) holds.
drop_prefixed ps is ps without elements that have a strict prefixes in ps. The list order is preserved. Duplicates are not removed use distinct for this.
relative ~to_dir p is q such that to_dir // q represents the same path as p. Note that q is not necessarily relative: if to_dir is relative and p is absolute p is returned.
Warning. This function is mostly broken at the moment.
Raises Invalid_argument if path to_dir contains "..".
compare p0 p1 is a total order on paths compatible with equal.
File extensions
The file extension (resp. multiple file extensions) 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 a single one.
TODO In the fpath package we correct e.g. has_ext if the given ext has no ext. I think we should either check and raise Invalid_argument or correct.
get_ext ~multi p is the file extension or multiple file extension if multi is true of the basename of p.
The empty string is returned if there is no extension. By definition this operates on the directory name of directory paths, not on the final empty segment.
to_url_path ~escape_space p is the path p as an URL path. This is p with the directory separator replaced by '/' and with the following characters percent encoded: '%', '?', '#', ' ' (if escape_space is true, default), and the US-ASCII control characters.
If Sys.win32 is true and p has a drive a '/' is prepended to the result.
Note. In 2019, the standard definition of URLs is in a sorry state. Assuming p is UTF-8 encoded. It is believed the above function should lead to an URL path component that can be parsed by HTML5's definition of URL parsing.
to_segments p is p's non-empty list of segments. Absolute paths have an empty string added, this allows to recover the path's string with String.concat dir_sep, note however that you may have lost the volume along the way.
sort_by_ext ~multi ps maps elements of ps by their extension as determined by Fpath.get_ext ~multi.
Search paths
A search path is a list of paths separated by a designated separator in which elements are looked up in left to right priority order. A well known search path is PATH in which executable binaries are looked up.
list_of_search_path ~sep s splits s on sep (defaults to search_path_sep) and parses the result with of_string, ignoring empty strings.
This means that sep is not allowed to appear in the file paths, consecutive sep are ignored and the order in the resulting list matches the left-to-right order of paths in s.
If one of_string errors on a path p with e the function errors with the message:
Fmt.str "Illegal path %a in search path: %s" pp p e