Skip to content

Commit

Permalink
Fix #31368: joinpath works on collections of paths
Browse files Browse the repository at this point in the history
  • Loading branch information
belamenso committed Nov 2, 2020
1 parent d33e7e0 commit ca8ab7d
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 10 deletions.
49 changes: 39 additions & 10 deletions base/path.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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] == '/'
Expand All @@ -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
Expand All @@ -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
Expand Down
9 changes: 9 additions & 0 deletions test/path.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down

0 comments on commit ca8ab7d

Please sign in to comment.