# Copyright 2002 Ben Escoto
#
# This file is part of duplicity.
#
# Duplicity is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 3 of the License, or (at your
# option) any later version.
#
# Duplicity is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with duplicity; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

"""Manage temporary files"""

import tempfile
import log, path, file_naming

import duplicity.tempdir as tempdir

def new_temppath():
	"""Return a new TempPath"""
	filename = tempdir.default().mktemp()
	return TempPath(filename)

class TempPath(path.Path):
	"""Path object used as a temporary file"""
	def delete(self):
		"""Forget and delete"""
		path.Path.delete(self)
		tempdir.default().forget(self.name)

	def open_with_delete(self, mode):
		"""Returns a fileobj.  When that is closed, delete file"""
		fh = FileobjHooked(path.Path.open(self, mode))
		fh.addhook(self.delete)
		return fh

def get_fileobj_duppath(dirpath, filename):
	"""Return a file object open for writing, will write to filename

	Data will be processed and written to a temporary file.  When the
	return fileobject is closed, rename to final position.  filename
	must be a recognizable duplicity data file.
	"""
	td = tempdir.TemporaryDirectory(dirpath.name)
	tdpname = td.mktemp()
	tdp = TempDupPath(tdpname, parseresults = file_naming.parse(filename))
	
	fh = FileobjHooked(tdp.filtered_open("wb"))
	def rename_and_forget():
		tdp.rename(dirpath.append(filename))
		td.forget(tdpname)

	fh.addhook(rename_and_forget)

	return fh

def new_tempduppath(parseresults):
	"""Return a new TempDupPath, using settings from parseresults"""
	filename = tempdir.default().mktemp()
	return TempDupPath(filename, parseresults = parseresults)

class TempDupPath(path.DupPath):
	"""Like TempPath, but build around DupPath"""
	def delete(self):
		"""Forget and delete"""
		path.DupPath.delete(self)
		tempdir.default().forget(self.name)

	def filtered_open_with_delete(self, mode):
		"""Returns a filtered fileobj.  When that is closed, delete file"""
		fh = FileobjHooked(path.DupPath.filtered_open(self, mode))
		fh.addhook(self.delete)
		return fh

	def open_with_delete(self, mode = "rb"):
		"""Returns a fileobj.  When that is closed, delete file"""
		assert mode == "rb" # Why write a file and then close it immediately?
		fh = FileobjHooked(path.DupPath.open(self, mode))
		fh.addhook(self.delete)
		return fh

class FileobjHooked:
	"""Simulate a file, but add hook on close"""
	def __init__(self, fileobj):
		"""Initializer.  fileobj is the file object to simulate"""
		self.fileobj = fileobj
		self.closed = None
		self.hooklist = [] # fill later with thunks to run on close
		# self.second by MDR.  Will be filled by addfilehandle -- poor mans tee
		self.second = None

	def write(self, buf):
		if self.second: self.second.write(buf) # by MDR.  actual tee
		return self.fileobj.write(buf)
	
	def read(self, length = -1): return self.fileobj.read(length)

	def close(self):
		"""Close fileobj, running hooks right afterwards"""
		assert not self.fileobj.close()
		if self.second: assert not self.second.close()
		for hook in self.hooklist: hook()

	def addhook(self, hook):
		"""Add hook (function taking no arguments) to run upon closing"""
		self.hooklist.append(hook)

	def addfilehandle(self, fh): # by MDR
		"""Add a second filehandle for listening to the input
		
		This only works properly for two write handles"""
		assert not self.second
		self.second = fh


syntax highlighted by Code2HTML, v. 0.9.1