diff --git a/base/path.jl b/base/path.jl index b0b6c52b8903f..e295a2a00c42b 100644 --- a/base/path.jl +++ b/base/path.jl @@ -248,16 +248,17 @@ function splitpath(p::String) return out end -joinpath(path::AbstractString)::String = path - if Sys.iswindows() -function joinpath(path::AbstractString, paths::AbstractString...)::String - result_drive, result_path = splitdrive(path) +function joinpath(paths::Union{Tuple, AbstractVector{<:AbstractString}})::String + isempty(paths) && return path_separator + typeassert(paths[1], AbstractString) + result_drive, result_path = splitdrive(paths[1]) - local p_drive, p_path - for p in paths - p_drive, p_path = splitdrive(p) + p_path = "" + for i in firstindex(paths)+1:lastindex(paths) + typeassert(paths[i], AbstractString) + p_drive, p_path = splitdrive(paths[i]) if startswith(p_path, ('\\', '/')) # second path is absolute @@ -293,8 +294,13 @@ end else -function joinpath(path::AbstractString, paths::AbstractString...)::String - for p in paths +function joinpath(paths::Union{Tuple, AbstractVector{<:AbstractString}})::String + isempty(paths) && return path_separator + typeassert(paths[1], AbstractString) + path = paths[1] + for i in firstindex(paths)+1:lastindex(paths) + p = paths[i] + typeassert(p, AbstractString) if isabspath(p) path = p elseif isempty(path) || path[end] == '/' @@ -308,6 +314,28 @@ end end # os-test +""" + joinpath(parts) -> String + +Join collection of path components into a full path. If some argument is an absolute path or +(on Windows) has a drive specification that doesn't match the drive computed for +the join of the preceding paths, then prior components are dropped. + +Note on Windows since there is a current directory for each drive, `joinpath("c:", "foo")` +represents a path relative to the current directory on drive "c:" so this is equal to "c:foo", +not "c:\\foo". Furthermore, `joinpath` treats this as a non-absolute path and ignores the drive +letter casing, hence `joinpath("C:\\A","c:b") = "C:\\A\\b"`. + +# Examples +```jldoctest +julia> joinpath(["/home/myuser", "example.jl"]) +"/home/myuser/example.jl" +julia> joinpath(()) +"$(path_separator)" +``` +""" +joinpath + """ joinpath(parts::AbstractString...) -> String @@ -326,7 +354,8 @@ julia> joinpath("/home/myuser", "example.jl") "/home/myuser/example.jl" ``` """ -joinpath +joinpath(path::AbstractString)::String = path +joinpath(path::AbstractString, paths::AbstractString...)::String = joinpath((path, paths...)) """ normpath(path::AbstractString) -> String diff --git a/test/path.jl b/test/path.jl index e09a46ef9370e..f5302c88152b1 100644 --- a/test/path.jl +++ b/test/path.jl @@ -42,6 +42,11 @@ @test joinpath(S("foo"), S(homedir())) == homedir() @test joinpath(S(abspath("foo")), S(homedir())) == homedir() + @test joinpath(()) == sep + for str in map(S, [sep, "a$(sep)b", "a$(sep)b$(sep)c", "a$(sep)b$(sep)c$(sep)d"]) + @test str == joinpath(splitpath(str)) + end + if Sys.iswindows() @test joinpath(S("foo"),S("bar:baz")) == "bar:baz" @test joinpath(S("C:"),S("foo"),S("D:"),S("bar")) == "D:bar" @@ -58,6 +63,10 @@ @test joinpath(S("\\\\server\\share"),S("a")) == "\\\\server\\share\\a" @test joinpath(S("\\\\server\\share\\"), S("a")) == "\\\\server\\share\\a" + for str in map(S, ["c:\\", "c:\\a", "c:\\a\\b", "c:\\a\\b\\c", "c:\\a\\b\\c\\d"]) + @test str == joinpath(splitpath(str)) + end + elseif Sys.isunix() @test joinpath(S("foo"),S("bar:baz")) == "foo$(sep)bar:baz" @test joinpath(S("C:"),S("foo"),S("D:"),S("bar")) == "C:$(sep)foo$(sep)D:$(sep)bar"