mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
734665d3a9
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@3052 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
133 lines
3 KiB
Ruby
133 lines
3 KiB
Ruby
#
|
|
# $Id$
|
|
#
|
|
# This is a class for managing temporary files.
|
|
#
|
|
# o Tempfile::new("basename") creates a temporary file whose name is
|
|
# "basename.pid.n" and opens with mode "w+".
|
|
# o A Tempfile object can be treated as an IO object.
|
|
# o The temporary directory is determined by ENV['TMPDIR'],
|
|
# ENV['TMP'], and ENV['TEMP'] in the order named, and if none of
|
|
# them is available, it is set to /tmp.
|
|
# o When $SAFE > 0, you should specify a directory via the second argument
|
|
# of Tempfile::new(), or it will end up finding an ENV value tainted and
|
|
# pick /tmp. In case you don't have it, an exception will be raised.
|
|
# o Tempfile#close(true) gets the temporary file removed immediately.
|
|
# o Otherwise, the removal is delayed until the object is finalized.
|
|
# o With Tempfile#open, you can reopen the temporary file.
|
|
# o The file mode for the temporary files is 0600.
|
|
# o This library is (considered to be) thread safe.
|
|
|
|
require 'delegate'
|
|
|
|
class Tempfile < SimpleDelegator
|
|
Max_try = 10
|
|
@@cleanlist = []
|
|
|
|
def Tempfile.callback(data)
|
|
pid = $$
|
|
lambda{
|
|
if pid == $$
|
|
path, tmpfile, cleanlist = *data
|
|
|
|
print "removing ", path, "..." if $DEBUG
|
|
|
|
tmpfile.close if tmpfile
|
|
|
|
# keep this order for thread safeness
|
|
File.unlink(path) if File.exist?(path)
|
|
cleanlist.delete(path) if cleanlist
|
|
|
|
print "done\n" if $DEBUG
|
|
end
|
|
}
|
|
end
|
|
|
|
def initialize(basename, tmpdir=ENV['TMPDIR']||ENV['TMP']||ENV['TEMP']||'/tmp')
|
|
if $SAFE > 0 and tmpdir.tainted?
|
|
tmpdir = '/tmp'
|
|
end
|
|
|
|
lock = nil
|
|
n = failure = 0
|
|
|
|
begin
|
|
Thread.critical = true
|
|
|
|
begin
|
|
tmpname = sprintf('%s/%s%d.%d', tmpdir, basename, $$, n)
|
|
lock = tmpname + '.lock'
|
|
n += 1
|
|
end while @@cleanlist.include?(tmpname) or
|
|
File.exist?(lock) or File.exist?(tmpname)
|
|
|
|
Dir.mkdir(lock)
|
|
rescue
|
|
failure += 1
|
|
retry if failure < Max_try
|
|
raise "cannot generate tempfile `%s'" % tmpname
|
|
ensure
|
|
Thread.critical = false
|
|
end
|
|
|
|
@data = [tmpname]
|
|
@clean_proc = Tempfile.callback(@data)
|
|
ObjectSpace.define_finalizer(self, @clean_proc)
|
|
|
|
@tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600)
|
|
@tmpname = tmpname
|
|
@@cleanlist << @tmpname
|
|
@data[1] = @tmpfile
|
|
@data[2] = @@cleanlist
|
|
|
|
super(@tmpfile)
|
|
|
|
# Now we have all the File/IO methods defined, you must not
|
|
# carelessly put bare puts(), etc. after this.
|
|
|
|
Dir.rmdir(lock)
|
|
end
|
|
|
|
def Tempfile.open(*args)
|
|
Tempfile.new(*args)
|
|
end
|
|
|
|
def open
|
|
@tmpfile.close if @tmpfile
|
|
@tmpfile = File.open(@tmpname, 'r+')
|
|
@data[1] = @tmpfile
|
|
__setobj__(@tmpfile)
|
|
end
|
|
|
|
def close(real=false)
|
|
@tmpfile.close if @tmpfile
|
|
@data[1] = @tmpfile = nil
|
|
if real
|
|
@clean_proc.call
|
|
ObjectSpace.undefine_finalizer(self)
|
|
end
|
|
end
|
|
|
|
def path
|
|
@tmpname
|
|
end
|
|
|
|
def size
|
|
if @tmpfile
|
|
@tmpfile.flush
|
|
@tmpfile.stat.size
|
|
else
|
|
0
|
|
end
|
|
end
|
|
end
|
|
|
|
if __FILE__ == $0
|
|
# $DEBUG = true
|
|
f = Tempfile.new("foo")
|
|
f.print("foo\n")
|
|
f.close
|
|
f.open
|
|
p f.gets # => "foo\n"
|
|
f.close(true)
|
|
end
|