# tempfile - manipulates temporary files
# $Id: tempfile.rb 16127 2008-04-21 09:43:44Z knu $
# A class for managing temporary files. This library is written to be
class Tempfile < DelegateClass(File)
# Creates a temporary file of mode 0600 in the temporary directory,
# opens it with mode "w+", and returns a Tempfile object which
# represents the created temporary file. A Tempfile object can be
# treated just like a normal File object.
# The basename parameter is used to determine the name of a
# temporary file. If an Array is given, the first element is used
# as prefix string and the second as suffix string, respectively.
# Otherwise it is treated as prefix string.
# If tmpdir is omitted, the temporary directory is determined by
# Dir::tmpdir provided by 'tmpdir.rb'.
# When $SAFE > 0 and the given tmpdir is tainted, it uses
# /tmp. (Note that ENV values are tainted by default)
def initialize(basename, tmpdir=Dir::tmpdir)
if $SAFE > 0 and tmpdir.tainted?
tmpname = File.join(tmpdir, make_tmpname(basename, n))
end while @@cleanlist.include?(tmpname) or
File.exist?(lock) or File.exist?(tmpname)
retry if failure < MAX_TRY
raise "cannot generate tempfile `%s'" % tmpname
@clean_proc = Tempfile.callback(@data)
ObjectSpace.define_finalizer(self, @clean_proc)
@tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600)
# Now we have all the File/IO methods defined, you must not
# carelessly put bare puts(), etc. after this.
def make_tmpname(basename, n)
prefix, suffix = *basename
prefix, suffix = basename, ''
t = Time.now.strftime("%Y%m%d")
path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}-#{n}#{suffix}"
# Opens or reopens the file with mode "r+".
@tmpfile.close if @tmpfile
@tmpfile = File.open(@tmpname, 'r+')
@tmpfile.close if @tmpfile
# Closes the file. If the optional flag is true, unlinks the file
# If you don't explicitly unlink the temporary file, the removal
# will be delayed until the object is finalized.
def close(unlink_now=false)
# Closes and unlinks the file.
ObjectSpace.undefine_finalizer(self)
# Unlinks the file. On UNIX-like systems, it is often a good idea
# to unlink a temporary file immediately after creating and opening
# it, because it leaves other programs zero chance to access the
# keep this order for thread safeness
File.unlink(@tmpname) if File.exist?(@tmpname)
@@cleanlist.delete(@tmpname)
ObjectSpace.undefine_finalizer(self)
# may not be able to unlink on Windows; just ignore
# Returns the full path name of the temporary file.
# Returns the size of the temporary file. As a side effect, the IO
# buffer is flushed before determining the size.
def callback(data) # :nodoc:
path, tmpfile, cleanlist = *data
print "removing ", path, "..." if $DEBUG
# keep this order for thread safeness
File.unlink(path) if File.exist?(path)
cleanlist.delete(path) if cleanlist
# If no block is given, this is a synonym for new().
# If a block is given, it will be passed tempfile as an argument,
# and the tempfile will automatically be closed when the block
# terminates. In this case, open() returns nil.