Utility code for moving database files to a (presumably local)
	working location for improved performance and reduced fileserver
	load.

def get_connection_filename(filename, tmp_path = None, replace_file = False, verbose = False):
	"""
	Utility code for moving database files to a (presumably local)
	working location for improved performance and reduced fileserver
	load.
	"""
	def mktmp(path, suffix = ".sqlite", verbose = False):
		with temporary_files_lock:
			# make sure the clean-up signal traps are installed
			install_signal_trap()
			# create the remporary file and replace it's
			# unlink() function
			temporary_file = tempfile.NamedTemporaryFile(suffix = suffix, dir = path if path != "_CONDOR_SCRATCH_DIR" else os.getenv("_CONDOR_SCRATCH_DIR"))
			def new_unlink(self, orig_unlink = temporary_file.unlink):
				# also remove a -journal partner, ignore all errors
				try:
					orig_unlink("%s-journal" % self)
				except:
					pass
				orig_unlink(self)
			temporary_file.unlink = new_unlink
			filename = temporary_file.name
			# hang onto reference to prevent its removal
			temporary_files[filename] = temporary_file
		if verbose:
			print >>sys.stderr, "using '%s' as workspace" % filename
		# mkstemp() ignores umask, creates all files accessible
		# only by owner;  we should respect umask.  note that
		# os.umask() sets it, too, so we have to set it back after
		# we know what it is
		umsk = os.umask(0777)
		os.umask(umsk)
		os.chmod(filename, 0666 & ~umsk)
		return filename

	def truncate(filename, verbose = False):
		if verbose:
			print >>sys.stderr, "'%s' exists, truncating ..." % filename,
		try:
			fd = os.open(filename, os.O_WRONLY | os.O_TRUNC)
		except Exception as e:
			if verbose:
				print >>sys.stderr, "cannot truncate '%s': %s" % (filename, str(e))
			return
		os.close(fd)
		if verbose:
			print >>sys.stderr, "done."

	def cpy(srcname, dstname, verbose = False):
		if verbose:
			print >>sys.stderr, "copying '%s' to '%s' ..." % (srcname, dstname),
		shutil.copy2(srcname, dstname)
		if verbose:
			print >>sys.stderr, "done."
		try:
			# try to preserve permission bits.  according to
			# the documentation, copy() and copy2() are
			# supposed preserve them but don't.  maybe they
			# don't preserve them if the destination file
			# already exists?
			shutil.copystat(srcname, dstname)
		except Exception as e:
			if verbose:
				print >>sys.stderr, "warning: ignoring failure to copy permission bits from '%s' to '%s': %s" % (filename, target, str(e))

	database_exists = os.access(filename, os.F_OK)

	if tmp_path is not None:
		# for suffix, can't use splitext() because it only keeps
		# the last bit, e.g. won't give ".xml.gz" but just ".gz"
		target = mktmp(tmp_path, suffix = ".".join(os.path.split(filename)[-1].split(".")[1:]), verbose = verbose)
		if database_exists:
			if replace_file:
				# truncate database so that if this job
				# fails the user won't think the database
				# file is valid
				truncate(filename, verbose = verbose)
			else:
				# need to copy existing database to work
				# space for modifications
				i = 1
				while True:
					try:
						cpy(filename, target, verbose = verbose)
					except IOError as e:
						import errno
						import time
						if e.errno not in (errno.EPERM, errno.ENOSPC):
							# anything other
							# than out-of-space
							# is a real error
							raise
						if i < 5:
							if verbose:
								print >>sys.stderr, "warning: attempt %d: %s, sleeping and trying again ..." % (i, errno.errorcode[e.errno])
							time.sleep(10)
							i += 1
							continue
						if verbose:
							print >>sys.stderr, "warning: attempt %d: %s: working with original file '%s'" % (i, errno.errorcode[e.errno], filename)
						with temporary_files_lock:
							del temporary_files[target]
						target = filename
					break
	else:
		with temporary_files_lock:
			if filename in temporary_files:
				raise ValueError("file '%s' appears to be in use already as a temporary database file and is to be deleted" % filename)
		target = filename
		if database_exists and replace_file:
			truncate(target, verbose = verbose)

	del mktmp
	del truncate
	del cpy

	return target