Skip to content

Commit 693b447

Browse files
vchuravystevengj
andauthored
Manually implement tempname on non-windows (#38879)
Co-authored-by: Steven G. Johnson <stevenj@alum.mit.edu> Co-authored-by: Elliot Saba <staticfloat@gmail.com> Co-authored-by: Steven G. Johnson <stevenj@alum.mit.edu>
1 parent c790938 commit 693b447

File tree

2 files changed

+54
-46
lines changed

2 files changed

+54
-46
lines changed

base/file.jl

Lines changed: 41 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -554,15 +554,52 @@ end
554554

555555
const temp_prefix = "jl_"
556556

557-
if Sys.iswindows()
557+
# Use `Libc.rand()` to generate random strings
558+
function _rand_filename(len = 10)
559+
slug = Base.StringVector(len)
560+
chars = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
561+
for i = 1:len
562+
slug[i] = chars[(Libc.rand() % length(chars)) + 1]
563+
end
564+
return String(slug)
565+
end
566+
567+
568+
# Obtain a temporary filename.
569+
function tempname(parent::AbstractString=tempdir(); max_tries::Int = 100, cleanup::Bool=true)
570+
isdir(parent) || throw(ArgumentError("$(repr(parent)) is not a directory"))
571+
572+
prefix = joinpath(parent, temp_prefix)
573+
filename = nothing
574+
for i in 1:max_tries
575+
filename = string(prefix, _rand_filename())
576+
if ispath(filename)
577+
filename = nothing
578+
else
579+
break
580+
end
581+
end
582+
583+
if filename === nothing
584+
error("tempname: max_tries exhausted")
585+
end
558586

559-
function _win_tempname(temppath::AbstractString, uunique::UInt32)
587+
cleanup && temp_cleanup_later(filename)
588+
return filename
589+
end
590+
591+
if Sys.iswindows()
592+
# While this isn't a true analog of `mkstemp`, it _does_ create an
593+
# empty file for us, ensuring that other simultaneous calls to
594+
# `_win_mkstemp()` won't collide, so it's a better name for the
595+
# function than `tempname()`.
596+
function _win_mkstemp(temppath::AbstractString)
560597
tempp = cwstring(temppath)
561598
temppfx = cwstring(temp_prefix)
562599
tname = Vector{UInt16}(undef, 32767)
563600
uunique = ccall(:GetTempFileNameW, stdcall, UInt32,
564601
(Ptr{UInt16}, Ptr{UInt16}, UInt32, Ptr{UInt16}),
565-
tempp, temppfx, uunique, tname)
602+
tempp, temppfx, UInt32(0), tname)
566603
windowserror("GetTempFileName", uunique == 0)
567604
lentname = something(findfirst(iszero, tname))
568605
@assert lentname > 0
@@ -571,49 +608,13 @@ function _win_tempname(temppath::AbstractString, uunique::UInt32)
571608
end
572609

573610
function mktemp(parent::AbstractString=tempdir(); cleanup::Bool=true)
574-
filename = _win_tempname(parent, UInt32(0))
611+
filename = _win_mkstemp(parent)
575612
cleanup && temp_cleanup_later(filename)
576613
return (filename, Base.open(filename, "r+"))
577614
end
578615

579-
# generate a random string from random bytes
580-
function _rand_string()
581-
nchars = 10
582-
A = Vector{UInt8}(undef, nchars)
583-
windowserror("SystemFunction036 (RtlGenRandom)", 0 == ccall(
584-
(:SystemFunction036, :Advapi32), stdcall, UInt8, (Ptr{Cvoid}, UInt32),
585-
A, sizeof(A)))
586-
587-
slug = Base.StringVector(10)
588-
chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
589-
for i = 1:nchars
590-
slug[i] = chars[(A[i] % length(chars)) + 1]
591-
end
592-
return name = String(slug)
593-
end
594-
595-
function tempname(parent::AbstractString=tempdir(); cleanup::Bool=true)
596-
isdir(parent) || throw(ArgumentError("$(repr(parent)) is not a directory"))
597-
name = _rand_string()
598-
filename = joinpath(parent, temp_prefix * name)
599-
@assert !ispath(filename)
600-
cleanup && temp_cleanup_later(filename)
601-
return filename
602-
end
603-
604616
else # !windows
605617

606-
# Obtain a temporary filename.
607-
function tempname(parent::AbstractString=tempdir(); cleanup::Bool=true)
608-
isdir(parent) || throw(ArgumentError("$(repr(parent)) is not a directory"))
609-
p = ccall(:tempnam, Cstring, (Cstring, Cstring), parent, temp_prefix)
610-
systemerror(:tempnam, p == C_NULL)
611-
s = unsafe_string(p)
612-
Libc.free(p)
613-
cleanup && temp_cleanup_later(s)
614-
return s
615-
end
616-
617618
# Create and return the name of a temporary file along with an IOStream
618619
function mktemp(parent::AbstractString=tempdir(); cleanup::Bool=true)
619620
b = joinpath(parent, temp_prefix * "XXXXXX")
@@ -623,7 +624,6 @@ function mktemp(parent::AbstractString=tempdir(); cleanup::Bool=true)
623624
return (b, fdio(p, true))
624625
end
625626

626-
627627
end # os-test
628628

629629

test/file.jl

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,23 @@ using Random
106106
end
107107

108108
@testset "tempname with parent" begin
109-
withenv("TMPDIR" => nothing) do
110-
t = tempname()
111-
@test dirname(t) == tempdir()
112-
mktempdir() do d
109+
t = tempname()
110+
@test dirname(t) == tempdir()
111+
mktempdir() do d
112+
t = tempname(d)
113+
@test dirname(t) == d
114+
end
115+
@test_throws ArgumentError tempname(randstring())
116+
117+
# 38873: check that `TMPDIR` being set does not
118+
# override the parent argument to `tempname`.
119+
mktempdir() do d
120+
withenv("TMPDIR"=>tempdir()) do
113121
t = tempname(d)
114122
@test dirname(t) == d
115123
end
116-
@test_throws ArgumentError tempname(randstring())
117124
end
125+
@test_throws ArgumentError tempname(randstring())
118126
end
119127

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

0 commit comments

Comments
 (0)