#coding:utf-8
import os
import re
import sys
import stat
import time
import shutil
import string
import xml.sax
import datetime

class File:
    
    def __init__(self, file, timestamp=None, size=None):
        self.setFile(file, timestamp, size)
        self.imortal = 0
    
    def __cmp__(self, file):
        if self.getBaseName() != file.getBaseName() or \
         self.timestamp == file.timestamp:
            rc = 0
        elif self.timestamp > file.timestamp:
            rc = 1
        else:
            rc = -1

        if rc and self.imortal + file.imortal == 1:
            if self.imortal:
                rc = 1
            else:
                rc = -1
        
        return rc

    def setFile(self, file, timestamp=None, size=None):
        self.pathname   = os.path.dirname(file)
        self.filename   = os.path.basename(file)

        if None not in (timestamp, size):
            self.timestamp = timestamp
            self.size      = size
        elif not os.path.islink(file):
            self.timestamp = os.path.getmtime(file)
            self.size      = os.path.getsize(file)
        else:
            orig = os.readlink(file)
            if os.path.isfile(orig):
                self.timestamp = os.path.getmtime(orig)
                self.size      = os.path.getsize(file)
            else:
                self.timestamp = 0
                self.size      = 0

    def explode(self):

        file = self.getFullName()
        if os.path.islink(file):
            orig = os.readlink(file)
            if os.path.isfile(orig):
                os.unlink(file)
                shutil.copy2(orig, file)
        
                tm = os.path.getmtime(orig)
                os.utime(file, (tm, tm))
        
    def setImortal(self):
        self.imortal = 1
    
    def getTimestamp(self):
        return self.timestamp

    def getSize(self):
        return float(self.size) / (1024*1024)
    
    def getUniqueName(self):
        return self.filename

    def getBaseName(self):
        return self.filename
    
    def getName(self):
        return self.filename

    def getShortName(self):
        return os.path.splitext(self.filename)[0]

    def getPath(self):
        return self.pathname

    def getFullName(self):
        return str(os.path.join(self.pathname, self.filename))

    def getFileMode(self):
        return self.getMode(self.getFullName())

    def getMode(self, path):
        dirs = os.stat(path)
        mode = oct(dirs.st_mode)[-3:] # 目录权限0o750 -3:750
        return (mode, dirs.st_uid, dirs.st_gid)

    def symlink(self, target, base=''):
        if os.path.isfile(target) or os.path.islink(target):
            os.unlink(target)
        os.symlink(self.getFullName(), target)

    def chmod(self, mode):
        # python2 : 0664
        # python3 : 0o664
        if os.path.exists(self.getFullName()):
            os.chmod(self.getFullName(), mode)

    def dump(self):
        print ('%s(%s)' % (self.filename, self.pathname))

class Tree:

    def __init__(self, root):
        self.root = root
        self.tree = {}
        self.build('')

    def getRoot(self):
        return self.root

    def getDirs(self):
        return list(self.tree.keys())

    def getSize(self):
        len = 0
        for key in self.tree.keys():
            for file in self.tree[key]:
                len = len + file.getSize()
        return float(len)

    def getFiles(self, path=''):

        try:
            list = self.tree[path]
        except KeyError:
            list = []
        return list

    def setFiles(self, path, files):
        self.tree[path] = files
    
    def build(self, dir):
        path = os.path.join(self.root, dir)
        if not os.path.isdir(path):
            return

        try:
            files = os.listdir(path)
            # 移除python缓存文件夹
            if '__pycache__' in files: files.remove('__pycache__')
        except:
            files = []

        v = []
        for f in files:
            filepath = os.path.join(path, f)
            if os.path.isdir(filepath) and not \
                    os.path.islink(filepath):
                self.build(os.path.join(dir, f))
            else:
                if re.match('.*\.rpm$', f) != None:
                    v.append(RPMFile(filepath))
                elif re.match('roll-.*\.iso$', f) != None:
                    v.append(RollFile(filepath))
                else:
                    v.append(File(filepath))
        self.tree[dir] = v

    def apply(self, func, root=None):
        for key in self.tree.keys():
            for e in self.tree[key]:
                func(key, e, root)


class RPMBaseFile(File):

    def __init__(self, file, timestamp=None, size=None, ext=1):
        File.__init__(self, file, timestamp, size)
        self.list   = []

        s = self.filename    # name-ver-rpmver.arch.rpm
        for x in range(0, ext):
            i = s.rfind(".")
            s = self.filename[:i]
    
        i = s.rfind(".")
        self.list.append(s[i+1:])   # get architecture string
        s = self.filename[:i]

        i = s.rfind("-")    # get RPM version string
        self.release = s[i+1:]
        self.list.append(self.versionList(s[i+1:]))
        s = self.filename[:i]

        i = s.rfind("-")    # get software version string
        self.version = s[i+1:]
        self.list.append(self.versionList(s[i+1:]))
        self.list.append(self.filename[:i]) # get package name
        self.list.reverse()     # we built the list backwards

    def versionList(self, s):
        list = []
        for e in re.split('\.+|_+', s):
            l       = []
            num     = ''
            alpha   = ''
            for c in e:
                if c in string.digits:
                    num = num + c
                    if alpha:
                        l.append(alpha)
                        alpha = ''
                else:
                    alpha = alpha + c
                    if num:
                        l.append(num)
                        num = ''
            if alpha:
                l.append(alpha)
            if num:
                l.append(num)
            list.append(l)
        return list

    def getBaseName(self):
        return self.list[0]

    def getUniqueName(self):
        return '%s-%s' % (self.list[0], self.list[3])

class RPMFile(RPMBaseFile):

    def __init__(self, file, timestamp=None, size=None):
        RPMBaseFile.__init__(self, file, timestamp, size)
    
    def __cmp__(self, file):
        if self.getPackageArch() != file.getPackageArch():
            rc = 0
        else:
            if abs(int(self.timestamp) - int(file.timestamp)) < 120 :
                # print "CMP %s:%s" % (self.getFullName(), file.getFullName())
                f1=os.popen("rpm -qp --qf '%%{BUILDTIME}' %s" % self.getFullName())
                self.timestamp=float(f1.readline())
                f1.close()
                f2=os.popen("rpm -qp --qf '%%{BUILDTIME}' %s" % file.getFullName())
                file.timestamp=float(f2.readline())
                f2.close()

            rc = File.__cmp__(self, file)
        return rc

    def getPackageName(self):
        return self.getBaseName()

    def getPackageVersion(self):
        return self.list[1]

    def getPackageRelease(self):
        return self.list[2]

    def getPackageVersionString(self):
        return self.version

    def getPackageReleaseString(self):
        return self.release

    def getPackageArch(self):
        return self.list[3]
        
class RollInfoFile(File,
    xml.sax.handler.ContentHandler, xml.sax.handler.DTDHandler,
    xml.sax.handler.EntityResolver, xml.sax.handler.ErrorHandler):

    def __init__(self, file):
        File.__init__(self, file)
        
        self.attrs = {}
        parser = xml.sax.make_parser()
        parser.setContentHandler(self)
        fin = open(file, 'r')
        parser.parse(fin)
        fin.close()
        
    def startElement(self, name, attrs):
        self.attrs[str(name)] = {}
        for (attrName, attrVal) in attrs.items():
            self.attrs[str(name)][str(attrName)] = str(attrVal)
    
    def getXML(self):

        xml = []
        xml.append('<roll name="%s" interface="%s">' %
            (self.getRollName(), self.getRollInterface()))
        for tag in self.attrs.keys():
            if tag == 'roll':
                continue
            attrs = ''
            for key,val in self.attrs[tag].items():
                attrs += ' %s="%s"' % (key, val)
            xml.append('\t<%s%s/>' % (tag, attrs))
        xml.append('</roll>')
        
        return string.join(xml, '\n')
        
    def getRollName(self):
        return self.attrs['roll']['name']
        
    def getRollInterface(self):
        return self.attrs['roll']['interface']
        
    def getRollVersion(self):
        return self.attrs['info']['version']
    
    def getRollRelease(self):
        return self.attrs['info']['release']
        
    def setRollOS(self, os):
        self.attrs['info']['os'] = os
        
    def getRollOS(self):
        try:
            return self.attrs['info']['os']
        except KeyError:
            return 'linux'

    def setRollArch(self, arch):
        self.attrs['info']['arch'] = arch

    def getRollArch(self):
        return self.attrs['info']['arch']
        
    def getISOMaxSize(self):
        return float(self.attrs['iso']['maxsize'])

    def setISOMaxSize(self, size):
        self.attrs['iso']['maxsize'] = size
        
    def getISOFlags(self):
        return self.attrs['iso']['mkisofs']
        
    def getRollRolls(self):
        return self.attrs['rpm']['rolls']
        
    def isBootable(self):
        return int(self.attrs['iso']['bootable'])
        
    def hasRolls(self):
        if self.attrs['rpm']['rolls'] != '0':
            return 1
        else:
            return 0
        
    def hasRPMS(self):
        return int(self.attrs['rpm']['bin'])
        
    def hasSRPMS(self):
        return int(self.attrs['rpm']['src'])

    def getDepsName(self):
        return self.attrs['deps']['name']

    def getDepsHost(self):
        return self.attrs['deps']['host']

class RPMbuild:
    def __init__(self, root):
        self.root       = root
        self.tree       = {}
        self.fulldirs   = {}
        self.build('')

    def getRoot(self):
        return self.root

    def getRootName(self):
        return self.root.split('/')[-1]

    def getRootMode(self):
        return self.getMode(self.root)

    def getPathMode(self, path):
        return self.getMode(path)
    
    def getMode(self, path):
        dirs = os.stat(path)
        mode = oct(dirs.st_mode)[-3:] # 目录权限0o750 -3:750
        return (mode, dirs.st_uid, dirs.st_gid)

    def getDirs(self):
        return list(self.tree.keys())

    def getDirMode(self, path):
        return self.fulldirs[path]

    def getFiles(self, path=''):
        try:
            list = self.tree[path]
        except KeyError:
            list = []
        return list

    def build(self, dir):

        path = os.path.join(self.root, dir)
        if not os.path.isdir(path):
            return

        try:
            files = os.listdir(path)
            # 移除python缓存文件夹
            #if '__pycache__' in files: files.remove('__pycache__')
        except:
            raise
            files = []

        v = []
        for f in files:
            filepath = os.path.join(path, f)
            if os.path.isdir(filepath) and not os.path.islink(filepath):
                self.build(os.path.join(dir, f))
            else:
                v.append(File(filepath))

        dirs = os.stat(path)
        mode = oct(dirs[stat.ST_MODE])[-3:] # 目录权限0o750 -3:750
        uid = dirs.st_uid #目录的uid
        gid = dirs.st_gid #目录的gid

        # 格式: (dirname, owner, uid, gid)
        self.fulldirs[dir] = (mode, uid, gid)
        self.tree[dir] = v










