File path names

Author(s): Jose F. Morales.

A pathname is an symbolic identifier that locates a file in a filesystem (e.g., foo/bar/baz.txt). This module provides predicates to manipulate pathnames, encoded as atoms. No file system access is required for pathname manipulation.

Documentation on exports

REGTYPEpathname/1

Usage:pathname(X)

X is a pathname (encoded as an atom)

    Usage:path_is_absolute(Path)

    Path is an absolute pathname

    • Call and exit should be compatible with:
      (pathnames:pathname/1)Path is a pathname (encoded as an atom)
    • The following properties should hold at call time:
      (term_typing:nonvar/1)Path is currently a term which is not a free variable.

    Usage:path_is_relative(Path)

    Path is a relative pathname

    • Call and exit should be compatible with:
      (pathnames:pathname/1)Path is a pathname (encoded as an atom)
    • The following properties should hold at call time:
      (term_typing:nonvar/1)Path is currently a term which is not a free variable.

    Usage:path_is_basename(Path)

    Path is a basename (empty directory part) (equivalent to path_split(Path, ”, _)

    • Call and exit should be compatible with:
      (pathnames:pathname/1)Path is a pathname (encoded as an atom)
    • The following properties should hold at call time:
      (term_typing:nonvar/1)Path is currently a term which is not a free variable.

    Usage:path_is_root(Path)

    Path is a root directory (normalized into / or //) (equivalent to + Path = ”, path_split(Path, Path, ”)

    • Call and exit should be compatible with:
      (pathnames:pathname/1)Path is a pathname (encoded as an atom)
    • The following properties should hold at call time:
      (term_typing:nonvar/1)Path is currently a term which is not a free variable.

    PREDICATEpath_concat/3

    Usage:path_concat(PathA,PathB,Path)

    Concatenate pathnames PathA and PathB in a new path Path, adding a / separator if needed. If PathB is , then Path is a / ended path. If PathB is an absolute pathname, Path is PathB. No pathname normalization is performed in any case.

    PREDICATEpath_split/3
    path_split(Path,Dir,Base)

    Given a pathname Path, Base is the last component of the path and Dir is the rest of the path. The following rules must hold:

    • All trailing slashes from Dir are removed (except if it is a root directory containing one or more slashes, e.g., '/', '//').
    • If Path ends in a slash ('.../', Base is empty ().
    • If Path is empty, both Dir and Base are empty.
    • Concatenating Dir and Base results in pathname that is equivalent (modulo normalization) to Path. That is, for all A, B, C:
              path_split(A,B,C),
              path_concat(B,C,D),
              path_norm(A,An), path_norm(D,Dn), An = Dn.
            

    Usage:path_split(Path,Dir,Base)

    Split Path into the directory part Dir and the basename part Base.

    PREDICATEpath_norm/2
    path_norm(Path,NormPath)

    NormPath is the normalized pathname version of Path. Normalization removes redundant separators (// into /), and collapses references to the parent (..) and current (.) level. The parent of a root path (/ or //) is itself. Empty paths () or relative paths where all parent references are collapsed are normalized as '.'.

    For compatibility, leading double-slashes are preserved in normalization (the POSIX.2 standard states that "a pathname that begins with two successive slashes may be interpreted in an implementation-defined manner, although more than two leading slashes shall be treated as a single slash."). Some systems (like Linux) ignore double-slashes while others (like Cygwin for //hostname/path SMB network drives) do not.

    Note that path_norm/2 does not access the filesystem, which may affect the semantics when symbolic links are used (and no path normalization is involved). E.g., the following query obtain different values for S1 and S2:

    ?- P = '/usr/donotexists/..',
       path_norm(P, N),
       process_call(path(test), ['-e', P], [status(S1)]),
       process_call(path(test), ['-e', N], [status(S2)]).
    
    N = '/usr',
    P = '/usr/donotexists/..',
    S1 = 1,
    S2 = 0 ? 
    
    ?- use_module(library(process)).
    ?- copy_file('/usr', 'u', [symlink]),
       P = 'u/../u',
       path_norm(P, N),
       process_call(path(test), ['-e', P], [status(S1)]),
       process_call(path(test), ['-e', N], [status(S2)]),
       delete_file('u').
    
    N = u,
    P = 'u/../u',
    S1 = 1,
    S2 = 0 ? 
    
    yes
    

    Usage:path_norm(Path,NormPath)

    NormPath is the normalized pathname version of Path.

    path_splitext(Path,NoExt,Ext)

    The extension Ext is the shortest suffix that begins with '.' of Path. The rest of the pathname is NoExt, which cannot be empty (). The extension is if the pathname has no extension.

    In this example, all the following goals succeed:

    path_splitext('a/foo.', 'a/foo', '.')
    path_splitext('a/foo.c', 'a/foo', '.c')
    path_splitext('a/foo.c.d', 'a/foo.c', '.d')
    path_splitext('a/.foo.', 'a/.foo', '.')
    path_splitext('a/.foo.c', 'a/.foo', '.c')
    path_splitext('a/.foo.c.d', 'a/.foo.c', '.d')
    

    Usage 1:path_splitext(Path,NoExt,Ext)

    Split Path into its extension Ext and the rest of the pathname NoExt.

    Usage 2:path_splitext(Path,NoExt,Ext)

    Compose Path by concatenating the extension Ext to NoExt pathname.

    Usage:path_basename(Path,Base)

    Base is the basename corresponding to the Path (equivalent to path_split(Path,_,Base)).

    Usage:path_dirname(Path,Dir)

    Dir is the directory part corresponding to the Path (equivalent to path_split(Path,Dir,_)).

    Usage:path_relocate(FromDir,ToDir,FromPath,ToPath)

    Replace FromDir prefix by DestDir in FromDir to generate ToDir

    Usage:path_get_relative(BaseDir,Path,RelPath)

    Obtain path RelPath such that path_concat(BaseDir, RelPath, Path) RelPath will not contain any trailing '/'

    Usage:path_split_list(Path,Bases)

    Split Path into its components Bases, calling path_split/3 recursively.

    Usage:path_concat_list(Bases,Path)

    Concatenate all components in Bases a new path Path, calling path_concat/3 recursively.

    Documentation on imports

    This module has the following direct dependencies:

    Known bugs and planned improvements

    • Some system predicates do unexpected path normalization (which also changes the semantics when symbolic links are used). Document at least.
    • path_norm/2 and expand_file_name in os_utils.c should be equivalent (disabling expansion of home and env vars)
    • Windows paths (drive letters) are not supported (see core/bugs/Pending/expand_file_trailing_slash/test.pl for some test cases)
    • This library offers no reversible versions of many of its predicates (e.g., path_concat/3) due to nondeterminism of reverse normalization. E.g., path_concat(a,B,'a/b'),B='b///' would fail unless all unnormalized paths are enumerated by the first call.

      Reversible path operations can be implemented by decomposing paths in list of names (using path_split/2 recursively), manipulating the lists, and composing back the paths (using path_concat/2 recursively).