Skip to content

Commit

Permalink
Fix joinpath on Windows and improve joinpath on linux
Browse files Browse the repository at this point in the history
  • Loading branch information
musm committed Oct 10, 2019
1 parent bf8c324 commit a68ae00
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 24 deletions.
2 changes: 1 addition & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Julia includes code from the following projects, which have their own licenses:
- [MUSL](https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT) (for getopt implementation on Windows) [MIT]
- [MINGW](https://sourceforge.net/p/mingw/mingw-org-wsl/ci/legacy/tree/mingwrt/mingwex/dirname.c) (for dirname implementation on Windows) [MIT]
- [NetBSD](https://www.netbsd.org/about/redistribution.html) (for setjmp, longjmp, and strptime implementations on Windows) [BSD-3]
- [Python](https://docs.python.org/2/license.html) (for strtod implementation on Windows) [BSD-3, effectively]
- [Python](https://docs.python.org/2/license.html) (for strtod and joinpath implementation on Windows) [BSD-3, effectively]

The following components included in Julia `Base` have their own separate licenses:

Expand Down
86 changes: 63 additions & 23 deletions base/path.jl
Original file line number Diff line number Diff line change
Expand Up @@ -198,14 +198,6 @@ function splitext(path::String)
a*m.captures[1], String(m.captures[2])
end

function pathsep(paths::AbstractString...)
for path in paths
m = match(path_separator_re, String(path))
m !== nothing && return m.match[1:1]
end
return path_separator
end

"""
splitpath(path::AbstractString) -> Vector{String}
Expand Down Expand Up @@ -246,8 +238,69 @@ function splitpath(p::String)
return out
end

joinpath(path::AbstractString) = String(path)

if Sys.iswindows()

function joinpath(path::AbstractString, paths::AbstractString...)
result_drive, result_path = splitdrive(path)

local p_drive, p_path
for p in paths
p_drive, p_path = splitdrive(p)

if startswith(p_path, ('\\', '/'))
# second path is absolute
if !isempty(p_drive) || !isempty(result_drive)
result_drive = p_drive
end
result_path = p_path
continue
elseif !isempty(p_drive) && p_drive != result_drive
if lowercase(p_drive) != lowercase(result_drive)
# different drives, ignore the first path entirely
result_drive = p_drive
result_path = p_path
continue
end
result_drive = p_drive # same drive in different case
end

# second path is relative to the first
if !isempty(result_path) && result_path[end] ('\\', '/')
result_path *= "\\"
end

result_path = result_path * p_path
end

# add separator between UNC and non-absolute path
if !isempty(p_path) && result_path[1] ('\\', '/') && !isempty(result_drive) && result_drive[end] != ':'
return result_drive * "\\" * result_path
end

return result_drive * result_path
end

else

function joinpath(path::AbstractString, paths::AbstractString...)
for p in paths
if isabspath(p)
path = p
elseif isempty(path) || path[end] == '/'
path *= p
else
path *= "/" * p
end
end
return path
end

end # os-test

"""
joinpath(parts...) -> AbstractString
joinpath(parts::AbstractString...) -> String
Join 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
Expand All @@ -259,20 +312,7 @@ julia> joinpath("/home/myuser", "example.jl")
"/home/myuser/example.jl"
```
"""
joinpath(a::AbstractString, b::AbstractString, c::AbstractString...) = joinpath(joinpath(a,b), c...)
joinpath(a::AbstractString) = a

function joinpath(a::String, b::String)
isabspath(b) && return b
A, a = splitdrive(a)
B, b = splitdrive(b)
!isempty(B) && A != B && return string(B,b)
C = isempty(B) ? A : B
isempty(a) ? string(C,b) :
occursin(path_separator_re, a[end:end]) ? string(C,a,b) :
string(C,a,pathsep(a,b),b)
end
joinpath(a::AbstractString, b::AbstractString) = joinpath(String(a), String(b))
joinpath

"""
normpath(path::AbstractString) -> AbstractString
Expand Down
18 changes: 18 additions & 0 deletions test/path.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,33 @@
@test isdirpath(S(".."))
end
@testset "joinpath" begin
@test joinpath(S("")) == ""
@test joinpath(S("foo")) == "foo"
@test joinpath(S("foo"), S("bar")) == "foo$(sep)bar"
@test joinpath(S("foo"), S("bar"), S("baz")) == "foo$(sep)bar$(sep)baz"
@test joinpath(S("foo"), S(""), S("baz")) == "foo$(sep)baz"
@test joinpath(S("foo"), S(""), S("")) == "foo$(sep)"
@test joinpath(S("foo"), S(""), S(""), S("bar")) == "foo$(sep)bar"

@test joinpath(S("foo"), S(homedir())) == homedir()
@test joinpath(S(abspath("foo")), S(homedir())) == homedir()

if Sys.iswindows()
@test joinpath(S("foo"),S("bar:baz")) == "bar:baz"
@test joinpath(S("C:"),S("foo"),S("D:"),S("bar")) == "D:bar"
@test joinpath(S("C:"),S("foo"),S("D:bar"),S("baz")) == "D:bar$(sep)baz"

# relative folders and case-insensitive drive letters
@test joinpath(S("C:\\a\\b"), S("c:c\\e")) == "c:\\a\\b\\c\\e"

# UNC paths
@test joinpath(S("\\\\server"), S("share")) == "\\\\server\\share"
@test joinpath(S("\\\\server"), S("share"), S("a")) == "\\\\server\\share\\a"
@test joinpath(S("\\\\server\\"), S("share"), S("a")) == "\\\\server\\share\\a"
@test joinpath(S("\\\\server"), S("share"), S("a"), S("b")) == "\\\\server\\share\\a\\b"
@test joinpath(S("\\\\server\\share"),S("a")) == "\\\\server\\share\\a"
@test joinpath(S("\\\\server\\share\\"), S("a")) == "\\\\server\\share\\a"

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 a68ae00

Please sign in to comment.