#!/usr/bin/python
import calendar,math,md5,optparse,os,re,signal,struct,time,zlib,zipfile,shutil,sys
#----------------------------------------------------------
# Misc function (QLIB)
#----------------------------------------------------------
def popenlread(c):
	(r,w)=os.pipe()
	pid=os.fork()
	if pid==0:
		os.close(r)
		os.dup2(w,1)
		os.close(w)
		os.execvp(c[0], c)
	else:
		os.close(w)
		f=os.fdopen(r)
		res=f.read()
		f.close()
		os.waitpid(pid,0)
		return res
def popenlwrite(c,s):
	(r,w)=os.pipe()
	pid=os.fork()
	if pid==0:
		os.close(w)
		os.dup2(r,0)
		os.close(r)
		os.execvp(c[0], c)
	else:
		os.close(r)
		f=os.fdopen(w,"w")
		f.write(s)
		f.close()
		rc=os.waitpid(pid,0)

#----------------------------------------------------------
# VFS zip
#----------------------------------------------------------
class VZipFile:
	def __init__(self,zip,path,size,mtime):
		self._zip=zip
		self._path=path
		self.fullname=os.path.join(zip,path)
		self.name=os.path.basename(path)
		self.size=size
		self.mtime=mtime
	def read(self):
		try:
			fb=zipfile.ZipFile(self._zip).read(self._path)
			return fb
		except zipfile.BadZipfile,e:
			print "ZipError: Skipping ",self._zip,e
		except RuntimeError,e:
			print "ZipError: Skipping ",self._zip,e
		return ""
	def unlink(self):
		c= ["7za","d",self._zip,self._path]
		os.spawnvp(os.P_WAIT, c[0], c)
		if os.path.getsize(self._zip)==22:
			os.unlink(self._zip)
class VZipDir:
	def __init__(self,path):
		self._path=path
		self.name=path
	def add(self,vfile,name):
#		print "Add to zip:",self._path,name,vfile.fullname
		zd=os.path.dirname(self._path)
		if not os.path.isdir(zd):
			os.makedirs(zd)
		mode='w'
		if os.path.isfile(self._path):
			mode='a'
		zf=zipfile.ZipFile(self._path,mode,zipfile.ZIP_DEFLATED)
		zi=zipfile.ZipInfo()
		zi.filename=name
		zi.date_time=time.gmtime(vfile.mtime)[:6]
		zi.compress_type=zipfile.ZIP_DEFLATED
		zf.writestr(zi,vfile.read())
	def listrec(self):
		l=[]
		try:
			zf=zipfile.ZipFile(self._path)
			for i in zf.infolist():
				tl=list(i.date_time)
				tl.extend([-1,-1,-1])
				if tl[1]==0:
					tl[1]=1
				if tl[2]==0:
					tl[2]=1
				mtime=calendar.timegm(tuple(tl))
				l.append(VZipFile(self._path,i.filename,i.file_size,mtime))
			zf.close()
		except IOError,e:
			print "ZipError: Skipping ",self._path,e
		except zipfile.BadZipfile,e:
			print "ZipError: Skipping ",self._path,e
		except RuntimeError,e:
			print "ZipError: Skipping ",self._path,e
		return l

#----------------------------------------------------------
# VFS 7z
#----------------------------------------------------------
class V7ZipFile(VZipFile):
	def read(self):
		c=['7za','e','-so',self._zip,self._path]
		r=popenlread(c)
		if len(r)==0:
			print "7Zip: warning empty file ",self._zip,self._path
		return r
	def unlink(self):
		c= ["7za","d",self._zip,self._path]
		print " ",c
		os.spawnvp(os.P_WAIT, c[0], c)
		if os.path.getsize(self._zip)==32:
			os.unlink(self._zip)
class V7ZipDir:
	def __init__(self,zip):
		self._zip=zip
		self.name=zip
		self.regexp=re.compile(r'([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2}) .....\s+(\d+) [0-9 ]{12}  ([^\n]+)\n',re.S|re.M)
	def add(self,vfile,name):
		zd=os.path.dirname(self._zip)
		if not os.path.isdir(zd):
			os.makedirs(zd)
		c=['7za','u','-si'+name,self._zip]
#		date_time=time.gmtime(vfile.mtime)[:6]
		popenlwrite(c,vfile.read())
	def listrec(self):
		c=['7za','l',self._zip]
		r=popenlread(c)
		l=re.findall(self.regexp,r)
		r=[]
		for (dy,dm,dd,th,tm,ts,s,n) in l:
			mtime=calendar.timegm((int(dy),int(dm),int(dd),int(th),int(tm),int(ts),-1,-1,-1))
			r.append(V7ZipFile(self._zip,n,int(s),mtime))
		return r
	def list(self,p):
		c=['7za','l',self._zip]
		r=popenlread(c)
		l=re.findall(self.regexp,r)
		r=[]
		for (dy,dm,dd,th,tm,ts,s,n) in l:
			if n.startswith(p):
				r.append(n[len(p):])
		return r
	def vfile(self,p):
		return V7ZipFile(self._zip,p,0,0)

#----------------------------------------------------------
# VFS filesystem
#----------------------------------------------------------
class VFile:
	def __init__(self,path):
		self._path=path
		self.fullname=path
		self.name=os.path.basename(path)
		self.size=os.path.getsize(path)
		self.mtime=os.path.getmtime(path)
	def read(self):
		return file(self._path).read()
	def unlink(self):
		return os.unlink(self._path)
class VDir:
	def __init__(self,path):
		self._path=path
		self.name=path
	def add(self,vfile,name):
		if not os.path.isdir(self._path):
			os.makedirs(self._path)
		dest=os.path.join(self._path,name)
		if isinstance(vfile,VFile):
			os.rename(vfile._path,dest)
		else:
			f=file(os.path.join(self._path,name),"wb")
			f.write(vfile.read())
			f.close()
		os.utime(dest,(vfile.mtime,vfile.mtime))
	def listrec_walk(self,l,dirname,names):
		for fn in [os.path.join(dirname,i) for i in names]:
			if os.path.isfile(fn):
				if fn.lower().endswith(".zip"):
					l.extend(VZipDir(fn).listrec())
				elif fn.lower().endswith(".7z"):
					l.extend(V7ZipDir(fn).listrec())
				else:
					l.append(VFile(fn))
	def listrec(self):
		l=[]
		if os.path.isdir(self._path):
			os.path.walk(self._path,self.listrec_walk,l)
		return l
	def vpath(self,p):
		head=p.split('/')
		tail=[]
		while 1:
			headp='/'.join(head)
			tailp='/'.join(tail)
			if os.path.isfile(headp) and (headp.lower().endswith(".zip") or headp.lower().endswith(".7z")):
				return V7ZipDir(headp),tailp
			if headp=='':
				return (None,tailp)
			tail[0:0]=[head.pop()]
	def list(self,p='.'):
		vd,tailp=self.vpath(p)
		if vd:
			l=vd.list(tailp)
		else:
			l=[]
			try:
				l=os.listdir(os.path.join(self._path,p))
			except:
				pass
		return l
	def vfile(self,p):
		vd,tailp=self.vpath(p)
		if vd:
			return vd.vfile(tailp)
		else:
			fullp=os.path.join(self._path,p)
			if os.path.isfile(fullp):
				return VFile(fullp)


#
