Skip to content

Commit

Permalink
Merge pull request #14 from davidanthoff/windows-support
Browse files Browse the repository at this point in the history
Windows support
  • Loading branch information
rofinn authored Feb 19, 2018
2 parents 26d23de + 923149f commit a480e3c
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 55 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ os:
- linux
- osx
julia:
- 0.5
- 0.6
- nightly
notifications:
Expand Down
2 changes: 1 addition & 1 deletion REQUIRE
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
julia 0.5
julia 0.6
Compat 0.17.0
7 changes: 4 additions & 3 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
environment:
matrix:
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.5/julia-0.5-latest-win32.exe"
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.5/julia-0.5-latest-win64.exe"
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.6/julia-0.6-latest-win32.exe"
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.6/julia-0.6-latest-win64.exe"

Expand Down Expand Up @@ -31,4 +29,7 @@ build_script:
- C:\projects\julia\bin\julia -e "Pkg.clone(pwd(), \"FilePaths\"); versioninfo(); Pkg.build(\"FilePaths\")"

test_script:
- C:\projects\julia\bin\julia --check-bounds=yes -e "Pkg.test(\"FilePaths\")"
- C:\projects\julia\bin\julia --check-bounds=yes -e "Pkg.test(\"FilePaths\", coverage=true)"

after_test:
- C:\projects\julia\bin\julia -e "cd(Pkg.dir(\"FilePaths\")); Pkg.add(\"Coverage\"); using Coverage; Codecov.submit(Codecov.process_folder())"
3 changes: 3 additions & 0 deletions src/FilePaths.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ export
Status,

# Methods
anchor,
cwd,
drive,
home,
parts,
root,
Expand Down Expand Up @@ -66,6 +68,7 @@ Base.next(p::AbstractPath, i::Int) = next(String(p), i)
Base.String(path::AbstractPath) = error("`String not implemented")
parts(path::AbstractPath) = error("`parts` not implemented.")
root(path::AbstractPath) = error("`root` not implemented.")
drive(path::AbstractPath) = error("`drive` not implemented.")

Base.convert(::Type{AbstractPath}, x::AbstractString) = Path(x)

Expand Down
4 changes: 2 additions & 2 deletions src/libc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ function User(name::String)
User(ps)
end

function User(uid::UInt64)
function User(uid::UInt)
ps = @static if is_unix()
ccall((:getpwuid, "libc"), Ptr{Cpasswd}, (UInt64,), uid)
else
Expand Down Expand Up @@ -114,7 +114,7 @@ function Group(name::String)
Group(ps)
end

function Group(gid::UInt64)
function Group(gid::UInt)
gr = @static if is_unix()
ccall((:getgrgid, "libc"), Ptr{Cgroup}, (UInt64,), gid)
else
Expand Down
23 changes: 15 additions & 8 deletions src/path.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@ Responsible for creating the appropriate platform specific path
"""
Path() = @static is_unix() ? PosixPath() : WindowsPath()
Path(path::AbstractPath) = path
Path(pieces::Tuple) = @static is_unix() ? PosixPath(pieces) : WindowsPath(pieces, "")
Path(pieces::Tuple) = @static is_unix() ? PosixPath(pieces) : WindowsPath(pieces)
Path(str::AbstractString) = @static is_unix() ? PosixPath(str) : WindowsPath(str)

Base.show(io::IO, path::AbstractPath) = print(io, "p\"$(join(parts(path), '/'))\"")

"""
@p_str -> Path
Expand All @@ -27,6 +25,8 @@ end
cwd() = Path(pwd())
home() = Path(homedir())

anchor(path::AbstractPath) = drive(path) * root(path)

#=
Path Modifiers
===============================================
Expand Down Expand Up @@ -144,7 +144,7 @@ function extension(path::AbstractPath)
end

"""
extension(path::AbstractPath) -> AbstractString
extensions(path::AbstractPath) -> AbstractString
Extracts all extensions from a filename if there any, otherwise it returns an empty string.
Expand Down Expand Up @@ -250,14 +250,21 @@ function relative{T<:AbstractPath}(path::T, start::T=T("."))
p = parts(abs(path))
s = parts(abs(start))

p == s && return curdir
p == s && return T(curdir)

i = 0
while i < min(length(p), length(s))
i += 1
if p[i] != s[i]
i -= 1
break
@static if is_windows()
if lowercase(p[i]) != lowercase(s[i])
i -= 1
break
end
else
if p[i] != s[i]
i -= 1
break
end
end
end

Expand Down
2 changes: 2 additions & 0 deletions src/posix.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ end
Base.String(path::PosixPath) = joinpath(parts(path)...)
parts(path::PosixPath) = path.parts

Base.show(io::IO, path::PosixPath) = print(io, "p\"$(join(parts(path), '/'))\"")

function isabs(path::PosixPath)
if parts(path)[1] == POSIX_PATH_SEPARATOR
return true
Expand Down
74 changes: 52 additions & 22 deletions src/windows.jl
Original file line number Diff line number Diff line change
@@ -1,46 +1,76 @@
immutable WindowsPath <: AbstractPath
parts::Tuple
parts::Tuple{Vararg{String}}
drive::String
root::String
end

WindowsPath() = WindowsPath(tuple(), "")
WindowsPath() = WindowsPath(tuple(), "", "")

WindowsPath(parts::Tuple) = WindowsPath(parts, "", "")

function WindowsPath(str::AbstractString)
if isempty(str)
return WindowsPath(tuple("."), "")
return WindowsPath(tuple("."), "", "")
end

drive, path = splitdir(str)
tokenized = split(path, WIN_PATH_SEPARATOR)

if isempty(tokenized[1])
tokenized[1] = WIN_PATH_SEPARATOR
if startswith(str, "\\\\?\\")
error("The \\\\?\\ prefix is currently not supported.")
end

return WindowsPath(tuple(map(String, tokenized)...), drive)
end
str = replace(str, POSIX_PATH_SEPARATOR, WIN_PATH_SEPARATOR)

if startswith(str, "\\\\")
error("UNC paths are currently not supported.")
elseif startswith(str, "\\")
tokenized = split(str, WIN_PATH_SEPARATOR)

return WindowsPath(tuple(WIN_PATH_SEPARATOR, String.(tokenized[2:end])...), "", WIN_PATH_SEPARATOR)
elseif contains(str, ":")
l_drive, l_path = splitdrive(str)

tokenized = split(l_path, WIN_PATH_SEPARATOR)

l_root = isempty(tokenized[1]) ? WIN_PATH_SEPARATOR : ""

if isempty(tokenized[1])
tokenized = tokenized[2:end]
end

if !isempty(l_drive) || !isempty(l_root)
tokenized = tuple(string(l_drive, l_root), tokenized...)
end

return WindowsPath(tuple(String.(tokenized)...), l_drive, l_root)
else
tokenized = split(str, WIN_PATH_SEPARATOR)

# The following should be implemented in the concrete types
==(a::WindowsPath, b::WindowsPath) = parts(a) == parts(b) && drive(a) == drive(b)
return WindowsPath(tuple(String.(tokenized)...), "", "")
end
end

function ==(a::WindowsPath, b::WindowsPath)
return lowercase.(parts(a)) == lowercase.(parts(b)) &&
lowercase(drive(a)) == lowercase(drive(b)) &&
lowercase(root(a)) == lowercase(root(b))
end
Base.String(path::WindowsPath) = joinpath(parts(path)...)
parts(path::WindowsPath) = path.parts
drive(path::WindowsPath) = path.drive
root(path::WindowsPath) = path.root

function isabs(path::WindowsPath)
if parts(path[1]) == WIN_PATH_SEPARATOR && !isempty(drive(path))
return true
function Base.show(io::IO, path::WindowsPath)
print(io, "p\"")
if isabs(path)
print(io, replace(anchor(path), "\\", "/"))
print(io, join(parts(path)[2:end], "/"))
else
return false
print(io, join(parts(path), "/"))
end
print(io, "\"")
end

function root(path::WindowsPath)
if parts(path)[1] == WIN_PATH_SEPARATOR
return WIN_PATH_SEPARATOR
else
return ""
end
function isabs(path::WindowsPath)
return !isempty(drive(path)) || !isempty(root(path))
end

expanduser(path::WindowsPath) = path
48 changes: 30 additions & 18 deletions test/path.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

cd(abs(parent(Path(@__FILE__)))) do
@testset "Simple Path Usage" begin
reg = "../src/FilePaths.jl"
reg = is_windows() ? "..\\src\\FilePaths.jl" : "../src/FilePaths.jl"
@test ispath(reg)

p = Path(reg)
Expand Down Expand Up @@ -30,7 +30,17 @@ cd(abs(parent(Path(@__FILE__)))) do
@test !isabs(p)
@test String(norm(p"../src/../src/FilePaths.jl")) == normpath("../src/../src/FilePaths.jl")
@test String(abs(p)) == abspath(String(p))
@test String(relative(p, home())) == relpath(String(p), homedir())
# This works around an issue with Base.relpath: that function does not take
# into account the paths on Windows should be compared case insensitive.
homedir_patched = homedir()
if is_windows()
conv_f = isupper(abspath(String(p))[1]) ? uppercase : lowercase
homedir_patched = conv_f(homedir_patched[1]) * homedir_patched[2:end]
end
@test String(relative(p, home())) == relpath(String(p), homedir_patched)

@test isa(relative(Path(".")), AbstractPath)
@test relative(Path(".")) == Path(".")

s = stat(p)
lstat(p)
Expand Down Expand Up @@ -106,22 +116,24 @@ mktmpdir() do d
@test_throws ErrorException chown(p"newfile", "nobody", "nogroup"; recursive=true)
end

chmod(p"newfile", user=(READ+WRITE+EXEC), group=(READ+EXEC), other=READ)
@test String(mode(p"newfile")) == "-rwxr-xr--"
@test isexecutable(p"newfile")
@test iswritable(p"newfile")
@test isreadable(p"newfile")

chmod(p"newfile", "-x")
@test !isexecutable(p"newfile")

@test String(mode(p"newfile")) == "-rw-r--r--"
chmod(p"newfile", "+x")
write(p"newfile", "foobar")
@test read(p"newfile") == "foobar"
chmod(p"newfile", "u=rwx")

chmod(new_path, mode(p"newfile"); recursive=true)
@static if is_unix()
chmod(p"newfile", user=(READ+WRITE+EXEC), group=(READ+EXEC), other=READ)
@test String(mode(p"newfile")) == "-rwxr-xr--"
@test isexecutable(p"newfile")
@test iswritable(p"newfile")
@test isreadable(p"newfile")

chmod(p"newfile", "-x")
@test !isexecutable(p"newfile")

@test String(mode(p"newfile")) == "-rw-r--r--"
chmod(p"newfile", "+x")
write(p"newfile", "foobar")
@test read(p"newfile") == "foobar"
chmod(p"newfile", "u=rwx")

chmod(new_path, mode(p"newfile"); recursive=true)
end
end
end
end

0 comments on commit a480e3c

Please sign in to comment.