1999-08-13 01:45:20 -04:00
|
|
|
#
|
2002-12-19 15:00:19 -05:00
|
|
|
# tempfile - manipulates temporary files
|
1999-08-13 01:45:20 -04:00
|
|
|
#
|
2002-12-19 15:00:19 -05:00
|
|
|
# $Id$
|
2002-11-18 02:26:44 -05:00
|
|
|
#
|
1999-08-13 01:45:20 -04:00
|
|
|
|
|
|
|
require 'delegate'
|
2003-07-21 11:34:18 -04:00
|
|
|
require 'tmpdir'
|
2006-12-31 10:02:22 -05:00
|
|
|
require 'thread'
|
1999-08-13 01:45:20 -04:00
|
|
|
|
2002-12-19 15:00:19 -05:00
|
|
|
# A class for managing temporary files. This library is written to be
|
|
|
|
# thread safe.
|
2004-03-29 02:54:38 -05:00
|
|
|
class Tempfile < DelegateClass(File)
|
2002-12-19 14:01:59 -05:00
|
|
|
MAX_TRY = 10
|
2002-11-17 13:01:17 -05:00
|
|
|
@@cleanlist = []
|
2006-12-31 10:02:22 -05:00
|
|
|
@@lock = Mutex.new
|
1999-08-13 01:45:20 -04:00
|
|
|
|
2007-09-11 04:28:29 -04:00
|
|
|
# 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.
|
2002-12-19 15:00:19 -05:00
|
|
|
#
|
|
|
|
# If tmpdir is omitted, the temporary directory is determined by
|
2003-07-23 12:37:35 -04:00
|
|
|
# Dir::tmpdir provided by 'tmpdir.rb'.
|
2003-07-21 11:34:18 -04:00
|
|
|
# When $SAFE > 0 and the given tmpdir is tainted, it uses
|
|
|
|
# /tmp. (Note that ENV values are tainted by default)
|
2008-10-18 06:32:26 -04:00
|
|
|
def initialize(basename, *rest)
|
|
|
|
# I wish keyword argument settled soon.
|
|
|
|
if opts = Hash.try_convert(rest[-1])
|
|
|
|
rest.pop
|
|
|
|
end
|
|
|
|
tmpdir = rest[0] || Dir::tmpdir
|
2000-12-08 02:10:38 -05:00
|
|
|
if $SAFE > 0 and tmpdir.tainted?
|
|
|
|
tmpdir = '/tmp'
|
|
|
|
end
|
2002-11-17 13:01:17 -05:00
|
|
|
|
2006-12-31 10:02:22 -05:00
|
|
|
lock = tmpname = nil
|
2002-11-17 13:01:17 -05:00
|
|
|
n = failure = 0
|
2006-12-31 10:02:22 -05:00
|
|
|
@@lock.synchronize {
|
2001-05-06 11:06:00 -04:00
|
|
|
begin
|
2006-12-31 10:02:22 -05:00
|
|
|
begin
|
|
|
|
tmpname = File.join(tmpdir, make_tmpname(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
|
|
|
|
end
|
|
|
|
}
|
1999-08-13 01:45:20 -04:00
|
|
|
|
2002-11-17 13:01:17 -05:00
|
|
|
@data = [tmpname]
|
|
|
|
@clean_proc = Tempfile.callback(@data)
|
|
|
|
ObjectSpace.define_finalizer(self, @clean_proc)
|
1999-08-13 01:45:20 -04:00
|
|
|
|
2008-10-18 06:32:26 -04:00
|
|
|
if opts.nil?
|
|
|
|
opts = []
|
|
|
|
else
|
|
|
|
opts = [opts]
|
|
|
|
end
|
|
|
|
@tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600, *opts)
|
2001-05-06 11:06:00 -04:00
|
|
|
@tmpname = tmpname
|
2002-11-17 13:01:17 -05:00
|
|
|
@@cleanlist << @tmpname
|
|
|
|
@data[1] = @tmpfile
|
|
|
|
@data[2] = @@cleanlist
|
|
|
|
|
2001-05-06 11:06:00 -04:00
|
|
|
super(@tmpfile)
|
2002-11-17 13:01:17 -05:00
|
|
|
|
|
|
|
# Now we have all the File/IO methods defined, you must not
|
|
|
|
# carelessly put bare puts(), etc. after this.
|
|
|
|
|
2001-05-06 11:06:00 -04:00
|
|
|
Dir.rmdir(lock)
|
1999-08-13 01:45:20 -04:00
|
|
|
end
|
|
|
|
|
2004-07-07 11:18:00 -04:00
|
|
|
def make_tmpname(basename, n)
|
2007-09-11 04:28:29 -04:00
|
|
|
case basename
|
|
|
|
when Array
|
|
|
|
prefix, suffix = *basename
|
|
|
|
else
|
|
|
|
prefix, suffix = basename, ''
|
|
|
|
end
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-09-11 04:28:29 -04:00
|
|
|
t = Time.now.strftime("%Y%m%d")
|
|
|
|
path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}-#{n}#{suffix}"
|
2004-07-07 11:18:00 -04:00
|
|
|
end
|
|
|
|
private :make_tmpname
|
|
|
|
|
2002-12-19 15:00:19 -05:00
|
|
|
# Opens or reopens the file with mode "r+".
|
1999-08-13 01:45:20 -04:00
|
|
|
def open
|
|
|
|
@tmpfile.close if @tmpfile
|
|
|
|
@tmpfile = File.open(@tmpname, 'r+')
|
2002-11-17 13:01:17 -05:00
|
|
|
@data[1] = @tmpfile
|
1999-08-13 01:45:20 -04:00
|
|
|
__setobj__(@tmpfile)
|
|
|
|
end
|
|
|
|
|
2002-12-19 15:00:19 -05:00
|
|
|
def _close # :nodoc:
|
1999-08-13 01:45:20 -04:00
|
|
|
@tmpfile.close if @tmpfile
|
2004-03-23 20:08:20 -05:00
|
|
|
@tmpfile = nil
|
|
|
|
@data[1] = nil if @data
|
2004-03-23 14:14:16 -05:00
|
|
|
end
|
2002-12-19 14:01:59 -05:00
|
|
|
protected :_close
|
|
|
|
|
2004-03-23 20:08:20 -05:00
|
|
|
#Closes the file. If the optional flag is true, unlinks the file
|
2002-12-19 15:00:19 -05:00
|
|
|
# after closing.
|
|
|
|
#
|
|
|
|
# If you don't explicitly unlink the temporary file, the removal
|
|
|
|
# will be delayed until the object is finalized.
|
2002-12-23 09:48:14 -05:00
|
|
|
def close(unlink_now=false)
|
|
|
|
if unlink_now
|
2002-12-19 14:01:59 -05:00
|
|
|
close!
|
|
|
|
else
|
|
|
|
_close
|
1999-08-13 01:45:20 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2002-12-19 15:00:19 -05:00
|
|
|
# Closes and unlinks the file.
|
2002-12-19 14:01:59 -05:00
|
|
|
def close!
|
|
|
|
_close
|
|
|
|
@clean_proc.call
|
|
|
|
ObjectSpace.undefine_finalizer(self)
|
2004-03-24 04:27:15 -05:00
|
|
|
@data = @tmpname = nil
|
2002-12-19 14:01:59 -05:00
|
|
|
end
|
|
|
|
|
2002-12-19 15:00:19 -05:00
|
|
|
# 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
|
|
|
|
# file.
|
2002-12-19 14:01:59 -05:00
|
|
|
def unlink
|
|
|
|
# keep this order for thread safeness
|
2004-05-07 04:44:24 -04:00
|
|
|
begin
|
2009-05-19 20:35:30 -04:00
|
|
|
if File.exist?(@tmpname)
|
|
|
|
closed? or close
|
|
|
|
File.unlink(@tmpname)
|
|
|
|
end
|
2004-05-07 04:44:24 -04:00
|
|
|
@@cleanlist.delete(@tmpname)
|
2004-10-19 06:25:23 -04:00
|
|
|
@data = @tmpname = nil
|
|
|
|
ObjectSpace.undefine_finalizer(self)
|
2005-05-10 21:46:31 -04:00
|
|
|
rescue Errno::EACCES
|
2004-05-07 04:44:24 -04:00
|
|
|
# may not be able to unlink on Windows; just ignore
|
|
|
|
end
|
2002-12-19 14:01:59 -05:00
|
|
|
end
|
|
|
|
alias delete unlink
|
|
|
|
|
2002-12-19 15:00:19 -05:00
|
|
|
# Returns the full path name of the temporary file.
|
1999-08-13 01:45:20 -04:00
|
|
|
def path
|
|
|
|
@tmpname
|
|
|
|
end
|
2002-06-04 03:34:19 -04:00
|
|
|
|
2002-12-19 15:00:19 -05:00
|
|
|
# Returns the size of the temporary file. As a side effect, the IO
|
|
|
|
# buffer is flushed before determining the size.
|
2002-06-04 03:34:19 -04:00
|
|
|
def size
|
|
|
|
if @tmpfile
|
|
|
|
@tmpfile.flush
|
|
|
|
@tmpfile.stat.size
|
|
|
|
else
|
|
|
|
0
|
|
|
|
end
|
|
|
|
end
|
2002-12-19 15:00:19 -05:00
|
|
|
alias length size
|
|
|
|
|
|
|
|
class << self
|
|
|
|
def callback(data) # :nodoc:
|
|
|
|
pid = $$
|
2004-11-15 11:45:03 -05:00
|
|
|
Proc.new {
|
2009-03-05 22:56:38 -05:00
|
|
|
if pid == $$
|
2002-12-19 15:00:19 -05:00
|
|
|
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
|
|
|
|
|
2003-01-20 07:27:53 -05:00
|
|
|
# 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.
|
2002-12-19 15:00:19 -05:00
|
|
|
def open(*args)
|
2003-01-20 07:27:53 -05:00
|
|
|
tempfile = new(*args)
|
|
|
|
|
|
|
|
if block_given?
|
|
|
|
begin
|
|
|
|
yield(tempfile)
|
|
|
|
ensure
|
|
|
|
tempfile.close
|
|
|
|
end
|
|
|
|
else
|
|
|
|
tempfile
|
|
|
|
end
|
2002-12-19 15:00:19 -05:00
|
|
|
end
|
|
|
|
end
|
1999-08-13 01:45:20 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
if __FILE__ == $0
|
|
|
|
# $DEBUG = true
|
|
|
|
f = Tempfile.new("foo")
|
|
|
|
f.print("foo\n")
|
|
|
|
f.close
|
|
|
|
f.open
|
|
|
|
p f.gets # => "foo\n"
|
2002-12-19 14:01:59 -05:00
|
|
|
f.close!
|
1999-08-13 01:45:20 -04:00
|
|
|
end
|