Doc needs help
The second arg should be named BaseName analogous to
file_directory_name(..., -Directory)
Hence:
file_base_name(+Path, -BaseName)
We read:
The behaviour is consistent with the POSIX basename program.
Verified against the utility :
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/basename.html
Interestingly, the function is slightly different :
https://pubs.opengroup.org/onlinepubs/9699919799/functions/basename.html
See also
Watch out
A bit unexpected, a "path without" file is acceptable. This is dubious but is what the basename on the command line and in the C library does
?- file_base_name('/foo/bar/baz/',File).
File = baz.
?- file_base_name('/',File).
File = (/).
In particular, the document for the basename and dirname C library functions (`man 3 basename`) say:
The following list of examples (taken from SUSv2) shows the strings returned by dirname() and basename() for different paths:
path dirname basename
/usr/lib /usr lib
/usr/ / usr
usr . usr
/ / /
. . .
.. . ..
Is it time for a sane_base_name/2 ?
Playing around
Do not reconstitute the path with atomic_list_concat/2, use directory_file_path/3:
necronomicon(Path,DirectoryName,BaseName) :-
file_directory_name(Path, DirectoryName),
file_base_name(Path, BaseName),
format("Decomposed path '~a' into '~a' and '~a'~n",[Path,DirectoryName,BaseName]),
atomic_list_concat([DirectoryName,'/',BaseName],ReconstitutedPath),
directory_file_path(DirectoryName,BaseName,ReconstitutedPath2),
((Path==ReconstitutedPath)
-> format("Match: atomic_list_concat reconstituted path is indeed '~a'~n",[ReconstitutedPath])
; format("MISMATCH: atomic_list_concat reconstituted path is '~a', not '~a'~n",[ReconstitutedPath,Path])),
((Path==ReconstitutedPath2)
-> format("Match: directory_file_path reconstituted path is indeed '~a'~n",[ReconstitutedPath2])
; format("MISMATCH: directory_file_path reconstituted path is '~a', not '~a'~n",[ReconstitutedPath2,Path])).
We find:
?- necronomicon('/',_,_).
Decomposed path '/' into '/' and '/'
MISMATCH: atomic_list_concat reconstituted path is '///', not '/'
Match: directory_file_path reconstituted path is indeed '/'
true.
?- necronomicon('',_,_).
Decomposed path '' into '.' and ''
MISMATCH: atomic_list_concat reconstituted path is './', not ''
Match: directory_file_path reconstituted path is indeed ''
true.
?- necronomicon('.',_,_).
Decomposed path '.' into '.' and '.'
MISMATCH: atomic_list_concat reconstituted path is './.', not '.'
Match: directory_file_path reconstituted path is indeed '.'
true.
?- necronomicon('a/b/',_,_).
Decomposed path 'a/b/' into 'a' and 'b'
MISMATCH: atomic_list_concat reconstituted path is 'a/b', not 'a/b/'
MISMATCH: directory_file_path reconstituted path is 'a/b', not 'a/b/'
true.
?- necronomicon('a/b',_,_).
Decomposed path 'a/b' into 'a' and 'b'
Match: atomic_list_concat reconstituted path is indeed 'a/b'
Match: directory_file_path reconstituted path is indeed 'a/b'
true.
?- necronomicon('/b',_,_).
Decomposed path '/b' into '/' and 'b'
MISMATCH: atomic_list_concat reconstituted path is '//b', not '/b'
Match: directory_file_path reconstituted path is indeed '/b'
true.
?- necronomicon('b',_,_).
Decomposed path 'b' into '.' and 'b'
MISMATCH: atomic_list_concat reconstituted path is './b', not 'b'
Match: directory_file_path reconstituted path is indeed 'b'
true.
Examples
Normal usage:
?- file_base_name("/foo/bar/baz.gz",File).
File = 'baz.gz'.
Trailing slash doesn't faze file_base_name/2
?- file_base_name("/foo/bar/",File).
File = bar.
Weird but documented:
?- file_base_name("/",File).
File = (/).
?- file_base_name("",File).
File = ''.
REALLY weird because neither '/' nor '' can ever be a valid filename. In fact, a filename cannot contain '/'.
Potential replacement
This predicate confused me enough that I have written one to cut up a string by its slashes into a list from where one can pick up the basename, the path, and a terminal slash, if any:
Examples:
A path that is a string:
?- path_text_list("a/b/c",List).
List = [frag("a"),slashes(1),frag("b"),slashes(1),frag("c")].
A path that is an atom:
?- path_text_list('a/b/c',List).
List = [frag(a),slashes(1),frag(b),slashes(1),frag(c)].
With final slash:
?- path_text_list('a/b/c/',List).
List = [frag(a),slashes(1),frag(b),slashes(1),frag(c),slashes(1)].
With starter slash:
?- path_text_list('/a/b/c/',List).
List = [slashes(1),frag(a),slashes(1),frag(b),slashes(1),frag(c),slashes(1)].
With unnecessarily repeated slashes:
?- path_text_list('/a/////b/////c/',List).
List = [slashes(1),frag(a),slashes(5),frag(b),slashes(5),frag(c),slashes(1)].
Works generatively:
?- path_text_list(Text,[slashes(1),frag(foo),slashes(1),frag(bar)]). Text = '/foo/bar'.