# This file is a part of Julia. License is MIT: http://julialang.org/license # get and set current directory function pwd() b = Array(UInt8,1024) len = Csize_t[length(b),] uv_error(:getcwd, ccall(:uv_cwd, Cint, (Ptr{UInt8}, Ptr{Csize_t}), b, len)) bytestring(b[1:len[1]-1]) end function cd(dir::AbstractString) uv_error("chdir $dir", ccall(:uv_chdir, Cint, (Cstring,), dir)) end cd() = cd(homedir()) @unix_only function cd(f::Function, dir::AbstractString) fd = ccall(:open,Int32,(Ptr{UInt8},Int32),".",0) systemerror(:open, fd == -1) try cd(dir) f() finally systemerror(:fchdir, ccall(:fchdir,Int32,(Int32,),fd) != 0) systemerror(:close, ccall(:close,Int32,(Int32,),fd) != 0) end end @windows_only function cd(f::Function, dir::AbstractString) old = pwd() try cd(dir) f() finally cd(old) end end cd(f::Function) = cd(f, homedir()) function mkdir(path::AbstractString, mode::Unsigned=0o777) @unix_only ret = ccall(:mkdir, Int32, (Cstring,UInt32), path, mode) @windows_only ret = ccall(:_wmkdir, Int32, (Cwstring,), path) systemerror(:mkdir, ret != 0) end function mkpath(path::AbstractString, mode::Unsigned=0o777) isdirpath(path) && (path = dirname(path)) dir = dirname(path) (path == dir || isdir(path)) && return mkpath(dir, mode) mkdir(path, mode) end mkdir(path::AbstractString, mode::Signed) = throw(ArgumentError("mode must be an unsigned integer; try 0o$mode")) mkpath(path::AbstractString, mode::Signed) = throw(ArgumentError("mode must be an unsigned integer; try 0o$mode")) function rm(path::AbstractString; recursive::Bool=false) if islink(path) || !isdir(path) @windows_only if !iswritable(path); chmod(path, 0o777); end FS.unlink(path) else if recursive for p in readdir(path) rm(joinpath(path, p), recursive=true) end end @unix_only ret = ccall(:rmdir, Int32, (Cstring,), path) @windows_only ret = ccall(:_wrmdir, Int32, (Cwstring,), path) systemerror(:rmdir, ret != 0) end end # The following use Unix command line facilites function checkfor_mv_cp_cptree(src::AbstractString, dst::AbstractString, txt::AbstractString; remove_destination::Bool=false) if ispath(dst) if remove_destination # Check for issue when: (src == dst) or when one is a link to the other # https://github.com/JuliaLang/julia/pull/11172#issuecomment-100391076 if Base.samefile(src, dst) abs_src = islink(src) ? abspath(readlink(src)) : abspath(src) abs_dst = islink(dst) ? abspath(readlink(dst)) : abspath(dst) throw(ArgumentError(string("'src' and 'dst' refer to the same file/dir.", "This is not supported.\n ", "`src` refers to: $(abs_src)\n ", "`dst` refers to: $(abs_dst)\n"))) end rm(dst; recursive=true) else throw(ArgumentError(string("'$dst' exists. `remove_destination=true` ", "is required to remove '$dst' before $(txt)."))) end end end function cptree(src::AbstractString, dst::AbstractString; remove_destination::Bool=false, follow_symlinks::Bool=false) isdir(src) || throw(ArgumentError("'$src' is not a directory. Use `cp(src, dst)`")) checkfor_mv_cp_cptree(src, dst, "copying"; remove_destination=remove_destination) mkdir(dst) for name in readdir(src) srcname = joinpath(src, name) if !follow_symlinks && islink(srcname) symlink(readlink(srcname), joinpath(dst, name)) elseif isdir(srcname) cptree(srcname, joinpath(dst, name); remove_destination=remove_destination, follow_symlinks=follow_symlinks) else FS.sendfile(srcname, joinpath(dst, name)) end end end function cp(src::AbstractString, dst::AbstractString; remove_destination::Bool=false, follow_symlinks::Bool=false) checkfor_mv_cp_cptree(src, dst, "copying"; remove_destination=remove_destination) if !follow_symlinks && islink(src) symlink(readlink(src), dst) elseif isdir(src) cptree(src, dst; remove_destination=remove_destination, follow_symlinks=follow_symlinks) else FS.sendfile(src, dst) end end function mv(src::AbstractString, dst::AbstractString; remove_destination::Bool=false) checkfor_mv_cp_cptree(src, dst, "moving"; remove_destination=remove_destination) FS.rename(src, dst) end function touch(path::AbstractString) f = FS.open(path,JL_O_WRONLY | JL_O_CREAT, 0o0666) @assert f.handle >= 0 try t = time() futime(f,t,t) finally FS.close(f) end end @unix_only begin # Obtain a temporary filename. function tempname() d = get(ENV, "TMPDIR", C_NULL) # tempnam ignores TMPDIR on darwin p = ccall(:tempnam, Ptr{UInt8}, (Ptr{UInt8},Ptr{UInt8}), d, "julia") systemerror(:tempnam, p == C_NULL) s = bytestring(p) Libc.free(p) return s end # Obtain a temporary directory's path. tempdir() = dirname(tempname()) # Create and return the name of a temporary file along with an IOStream function mktemp(parent=tempdir()) b = joinpath(parent, "tmpXXXXXX") p = ccall(:mkstemp, Int32, (Ptr{UInt8},), b) # modifies b systemerror(:mktemp, p == -1) return (b, fdio(p, true)) end # Create and return the name of a temporary directory function mktempdir(parent=tempdir()) b = joinpath(parent, "tmpXXXXXX") p = ccall(:mkdtemp, Ptr{UInt8}, (Ptr{UInt8},), b) systemerror(:mktempdir, p == C_NULL) return bytestring(p) end end @windows_only begin function tempdir() temppath = Array(UInt16,32767) lentemppath = ccall(:GetTempPathW,stdcall,UInt32,(UInt32,Ptr{UInt16}),length(temppath),temppath) if lentemppath >= length(temppath) || lentemppath == 0 error("GetTempPath failed: $(FormatMessage())") end resize!(temppath,lentemppath+1) return utf8(UTF16String(temppath)) end tempname(uunique::UInt32=UInt32(0)) = tempname(tempdir(), uunique) function tempname(temppath::AbstractString,uunique::UInt32) tname = Array(UInt16,32767) uunique = ccall(:GetTempFileNameW,stdcall,UInt32,(Cwstring,Ptr{UInt16},UInt32,Ptr{UInt16}), temppath,utf16("jul"),uunique,tname) lentname = findfirst(tname,0)-1 if uunique == 0 || lentname <= 0 error("GetTempFileName failed: $(FormatMessage())") end resize!(tname,lentname+1) return utf8(UTF16String(tname)) end function mktemp(parent=tempdir()) filename = tempname(parent, UInt32(0)) return (filename, open(filename,"r+")) end function mktempdir(parent=tempdir()) seed::UInt32 = rand(UInt32) while true if (seed & typemax(UInt16)) == 0 seed += 1 end filename = tempname(parent, seed) ret = ccall(:_wmkdir, Int32, (Ptr{UInt16},), utf16(filename)) if ret == 0 return filename end systemerror(:mktempdir, Libc.errno()!=Libc.EEXIST) seed += 1 end end end function mktemp(fn::Function) (tmp_path, tmp_io) = mktemp() try fn(tmp_path, tmp_io) finally close(tmp_io) rm(tmp_path) end end function mktempdir(fn::Function) tmpdir = mktempdir() try fn(tmpdir) finally rm(tmpdir, recursive=true) end end function readdir(path::AbstractString) # Allocate space for uv_fs_t struct uv_readdir_req = zeros(UInt8, ccall(:jl_sizeof_uv_fs_t, Int32, ())) # defined in sys.c, to call uv_fs_readdir, which sets errno on error. file_count = ccall(:jl_readdir, Int32, (Cstring, Ptr{UInt8}), path, uv_readdir_req) systemerror("unable to read directory $path", file_count < 0) # The list of dir entries is returned as a contiguous sequence of null-terminated # strings, the first of which is pointed to by ptr in uv_readdir_req. # The following lines extracts those strings into dirent entries = ByteString[] offset = 0 for i = 1:file_count entry = bytestring(ccall(:jl_uv_fs_t_ptr_offset, Ptr{UInt8}, (Ptr{UInt8}, Int32), uv_readdir_req, offset)) push!(entries, entry) offset += sizeof(entry) + 1 # offset to the next entry end # Clean up the request string ccall(:jl_uv_fs_req_cleanup, Void, (Ptr{UInt8},), uv_readdir_req) entries end readdir() = readdir(".")