Skip to content

Commit

Permalink
Manually implement tempname on non-windows (#38879)
Browse files Browse the repository at this point in the history
Co-authored-by: Steven G. Johnson <[email protected]>

Co-authored-by: Elliot Saba <[email protected]>

Co-authored-by: Steven G. Johnson <[email protected]>
  • Loading branch information
vchuravy and stevengj authored Dec 28, 2021
1 parent c790938 commit 693b447
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 46 deletions.
82 changes: 41 additions & 41 deletions base/file.jl
Original file line number Diff line number Diff line change
Expand Up @@ -554,15 +554,52 @@ end

const temp_prefix = "jl_"

if Sys.iswindows()
# Use `Libc.rand()` to generate random strings
function _rand_filename(len = 10)
slug = Base.StringVector(len)
chars = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
for i = 1:len
slug[i] = chars[(Libc.rand() % length(chars)) + 1]
end
return String(slug)
end


# Obtain a temporary filename.
function tempname(parent::AbstractString=tempdir(); max_tries::Int = 100, cleanup::Bool=true)
isdir(parent) || throw(ArgumentError("$(repr(parent)) is not a directory"))

prefix = joinpath(parent, temp_prefix)
filename = nothing
for i in 1:max_tries
filename = string(prefix, _rand_filename())
if ispath(filename)
filename = nothing
else
break
end
end

if filename === nothing
error("tempname: max_tries exhausted")
end

function _win_tempname(temppath::AbstractString, uunique::UInt32)
cleanup && temp_cleanup_later(filename)
return filename
end

if Sys.iswindows()
# While this isn't a true analog of `mkstemp`, it _does_ create an
# empty file for us, ensuring that other simultaneous calls to
# `_win_mkstemp()` won't collide, so it's a better name for the
# function than `tempname()`.
function _win_mkstemp(temppath::AbstractString)
tempp = cwstring(temppath)
temppfx = cwstring(temp_prefix)
tname = Vector{UInt16}(undef, 32767)
uunique = ccall(:GetTempFileNameW, stdcall, UInt32,
(Ptr{UInt16}, Ptr{UInt16}, UInt32, Ptr{UInt16}),
tempp, temppfx, uunique, tname)
tempp, temppfx, UInt32(0), tname)
windowserror("GetTempFileName", uunique == 0)
lentname = something(findfirst(iszero, tname))
@assert lentname > 0
Expand All @@ -571,49 +608,13 @@ function _win_tempname(temppath::AbstractString, uunique::UInt32)
end

function mktemp(parent::AbstractString=tempdir(); cleanup::Bool=true)
filename = _win_tempname(parent, UInt32(0))
filename = _win_mkstemp(parent)
cleanup && temp_cleanup_later(filename)
return (filename, Base.open(filename, "r+"))
end

# generate a random string from random bytes
function _rand_string()
nchars = 10
A = Vector{UInt8}(undef, nchars)
windowserror("SystemFunction036 (RtlGenRandom)", 0 == ccall(
(:SystemFunction036, :Advapi32), stdcall, UInt8, (Ptr{Cvoid}, UInt32),
A, sizeof(A)))

slug = Base.StringVector(10)
chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
for i = 1:nchars
slug[i] = chars[(A[i] % length(chars)) + 1]
end
return name = String(slug)
end

function tempname(parent::AbstractString=tempdir(); cleanup::Bool=true)
isdir(parent) || throw(ArgumentError("$(repr(parent)) is not a directory"))
name = _rand_string()
filename = joinpath(parent, temp_prefix * name)
@assert !ispath(filename)
cleanup && temp_cleanup_later(filename)
return filename
end

else # !windows

# Obtain a temporary filename.
function tempname(parent::AbstractString=tempdir(); cleanup::Bool=true)
isdir(parent) || throw(ArgumentError("$(repr(parent)) is not a directory"))
p = ccall(:tempnam, Cstring, (Cstring, Cstring), parent, temp_prefix)
systemerror(:tempnam, p == C_NULL)
s = unsafe_string(p)
Libc.free(p)
cleanup && temp_cleanup_later(s)
return s
end

# Create and return the name of a temporary file along with an IOStream
function mktemp(parent::AbstractString=tempdir(); cleanup::Bool=true)
b = joinpath(parent, temp_prefix * "XXXXXX")
Expand All @@ -623,7 +624,6 @@ function mktemp(parent::AbstractString=tempdir(); cleanup::Bool=true)
return (b, fdio(p, true))
end


end # os-test


Expand Down
18 changes: 13 additions & 5 deletions test/file.jl
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,23 @@ using Random
end

@testset "tempname with parent" begin
withenv("TMPDIR" => nothing) do
t = tempname()
@test dirname(t) == tempdir()
mktempdir() do d
t = tempname()
@test dirname(t) == tempdir()
mktempdir() do d
t = tempname(d)
@test dirname(t) == d
end
@test_throws ArgumentError tempname(randstring())

# 38873: check that `TMPDIR` being set does not
# override the parent argument to `tempname`.
mktempdir() do d
withenv("TMPDIR"=>tempdir()) do
t = tempname(d)
@test dirname(t) == d
end
@test_throws ArgumentError tempname(randstring())
end
@test_throws ArgumentError tempname(randstring())
end

child_eval(code::String) = eval(Meta.parse(readchomp(`$(Base.julia_cmd()) -E $code`)))
Expand Down

0 comments on commit 693b447

Please sign in to comment.