#!/usr/bin/python
#
#  SPI version 1.0-beta1                              											
#                                  											 	
# The Slackware Package Inspector   											
# written by Nicola La Gloria (c) 2004, 2005      									
#																				
# This program 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 2					
# of the License, or (at your option) any later version.							
#																				
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 

import getopt
import ConfigParser
import os
import sys
import bsddb
import re
import ftplib
import urllib
from bz2 import *
from textwrap import *
from shutil import copy
from time import *
class spinet:
    def __init__(self):
	"""class for networking options"""
    def connectionInit(self,connection):
        connectiondict=dict()
        l=[]
        if connection=="slackware":     
           # try:
        # return connection parameter which are version_name, ftp mirror, path ecc...
                mirrorsite=cfg.get('slackware_ftp','mirror')
                connectiondict['mirrorsite'] = mirrorsite
                versionpath=cfg.get('slackware_ftp','version_path')
                connectiondict['versionpath'] = versionpath
                section_list=cfg.options('slackware_sections')
                connectiondict['version']=cfg.get('slackware_ftp','version')
                connectiondict['packagesource']=cfg.get('slackware_ftp','packagesource')
                for section in section_list:
                    l.append(cfg.get('slackware_sections',section))
                connectiondict['sections'] = tuple(l)                
            #except ConfigParser.NoOptionError:
            #    print errmsg.noOption
            #    sys.exit(2)
            
        if connection=="thirdparty":
            #try:
                mirrorsite=cfg.get('thirdparty_ftp','mirror')
                connectiondict['mirrorsite'] = mirrorsite
                versionpath=cfg.get('thirdparty_ftp','version_path')
                connectiondict['versionpath'] = versionpath
                section_list=cfg.options('thirdparty_sections')
                connectiondict['version']=cfg.get('thirdparty_ftp','version')
                connectiondict['packagesource']=cfg.get('thirdparty_ftp','packagesource')
                for section in section_list:
                    l.append(cfg.get('thirdparty_sections',section))
                connectiondict['sections'] = tuple(l)       
            #except ConfigParser.NoOptionError:
            #    print errmsg.noOption
            #    sys.exit(2)
        return connectiondict
        
    def connect(self,connectiondict):       
        buildparams=dict()
        buildparams['version']=(connectiondict['version'])
        buildparams['packagesource']=(connectiondict['packagesource'])
        sys.stdout.write("=======%% Trying to connect to "+connectiondict['mirrorsite']+"......")
        try:
            session=ftplib.FTP(connectiondict['mirrorsite'],"anonymous","spi@slackit.org")         
        except ftplib.all_errors:
            sys.stdout.write("unable to connect, socket error\n")
            sys.exit(2) 
        sys.stdout.write("Connected\n")
        try:
            session.cwd(connectiondict['versionpath'])
            #print connectiondict['versionpath']      #DEBUG
        except:
            sys.stdout.write("fail to open remote directory")
            sys.exit(2)       
        sys.stdout.write("=======%% Slackware version: "+connectiondict['version']+"\n")
        # here starts cycle for every section         
        for section in connectiondict['sections']:           
            #print section                              #DEBUG
            sys.stdout.write("=======%% Trying to get files for section '"+section+"' from "+connectiondict['mirrorsite']+"......")
            try:
                #session.cwd(version+'/'+name)
                session.cwd(section)
            except:
                session.cwd('./')       
            path=os.path.join(spihome,'.packages',section.upper()+'_PACKAGES.TXT')
            dest=self.write(path)       
            #dest=open(spihome+"/.packages/"+section.upper()+"_PACKAGES.TXT","w+")    
            try:                                     
               session.retrbinary("RETR PACKAGES.TXT",dest.write)
            except:
                print errmgs.failToGet
                sys.exit(2)       
            path=os.path.join(spihome,'.manifests',section.upper()+'_MANIFEST.bz2')
            dest=self.write(path)
            #dest=open(spihome+"/.manifests/"+section.upper()+"_MANIFEST.bz2","w+") # put destination file in spihome this is for debugging!!!
            try:
                session.retrbinary("RETR MANIFEST.bz2",dest.write)
            except:
                sys.stdout.write(errmgs.failToGet+"\n")
                sys.exit(2)                      
            sys.stdout.write("DONE!\n")
            session.cwd('..')   # came back to root
        session.close()
        return buildparams
        
    def write (self,path):
        dest=open(path,"w+")
        return dest
class package:
    """This class represents a PACKAGES.TXT's entry"""
    def __init__(self, name, loc, csize, usize, desc):
        self.name = name
        self.loc = loc
        self.csize = csize
        self.usize = usize
        self.desc = desc
    
    def __str__(self):
        return self.name
    
    def __repr__(self):
        return "\nname = " + self.name + \
        	"\nloc = " + self.loc + \
            "\ncsize = " + self.csize + \
        	"\nusize = " + self.usize \
            + "\ndesc = " +self.desc         


class slackwareParse:
    def __init__(self):
        """ parse manifest & PACKGES istnce.desc"""

    def build_packages_dict(self,package_file,buildparams):   
		# Open PACKAGES.txt or die. 
        # buildparams is a list [version,pkgsource,section]       
        try:
           f = open(spihome+"/.packages/"+package_file) # get from current directory
           file_iterator=iter(f)
        except:
           print 'error'        
        # Initializing parsing helpers.
        d = dict()
        pkg = None
        name = ""
        namelen = 0
        loc = "" 
        csize = ""
        usize = ""
        desc = ""
        indesc = 0
        for line in file_iterator:
            # Skip empty lines.
            if line == '\n': 
                continue
            # Splits the input line.
            fields = line.split()
            # Try to append this line (opportuned modified) to the package
    		# description. If this line don't starts with PACKAGE *and* we
        	# aren't inside a package description we simply skip this line.
            if fields[0] != 'PACKAGE':
                if indesc == 1:
                    descfields = line.split(" ", 1)
                    if len(descfields) < 2:
                        continue
                    if descfields[0] != "\n":
        	# Appending this line to the package's description
        	# with 'name: ' prefix removed
                        desc += " " + descfields[1]     
                    else:
                        desc += "\n"
                continue
            if fields[1] == 'NAME:':
                if name != "":
        	# Adding this package to the dictionary.
                    d[name] = package(name, loc, csize, usize, desc)
                    desc=""  # Clening attribute value
                name = fields[2][:-4]
    		# We compute here the name lenght's so we avoid to
            # recalculating it on each iteration (see above).
                namelen = len(name) + 2
                indesc = 0
                continue
    		# Saving the location.
            if fields[1] == 'LOCATION:':
                if buildparams.has_key('location'):
                    loc=buildparams['location']
                else:
                    loc = fields[2][2:]
                continue
            # This starts a the package's description while setting a flag.
            
            if fields[1] == 'DESCRIPTION:':
                indesc = 1
                continue
            # Skip this line if there aren't enough fields (3)
            if len(fields) <= 2:
                continue
            # Parsing the compressed size of the package.
            if fields[2] == '(compressed):':
                csize = fields[3] + fields[4]
                continue
            # Parsing the uncompressed size of the package.
            if fields[2] == '(uncompressed):':
                usize = fields[3] + fields[4]
                continue
    		# Never reached.
            continue
        f.close()
        d[name] = package(name, loc, csize, usize, desc) # write le last element!!!
        # write database
        # The berkeley db structure of filedb is the cuple
        # (pkgname,"LOCATION|NAME|COMP_SIZE|UNCOMP_SIZE|DESCRIPTION|SECTION|VERSION|PKGSOURCE")
        # buildparams indexe [0]= version [1]=pksgource [2]=section
        for elem in d.keys():
            # HERE!!..check if a key exist and process a new version string
            slackwareversion=comm.checkVersion(elem,buildparams['version'])  
                
            # make value before storing
            db_value=d.get(elem).loc+"||"+d.get(elem).name+"||"+d.get(elem).csize+"||"+d.get(elem).usize+"||" \
            +d.get(elem).desc+"||"+buildparams['section']+"||"+slackwareversion+"||"+buildparams['packagesource'] 
            try:
                # for avoid to write over a record from a official slackware rep when we build from an ext-rep we check!
                if list(dbcnx.setLocation(2,elem))[1].split('||')[7] != "official-slackware-repository":
                    dbcnx.writeInfodb(elem,(db_value))           
            # write NOT installed for every package, we'll establish if a pkg is INSTALLED or NOT
            # when local buil is performed
                    dbcnx.writeStatedb(elem,"NOT INSTALLED")
            except:
                dbcnx.writeInfodb(elem,(db_value))
                dbcnx.writeStatedb(elem,"NOT INSTALLED")
    
    def build_files_dict(self,manifest_file,section):    
        #print manifest_file                     #DEBUG
        def getpkgname(line):
            l=line.split("/")
            num=len(l)                          # there's no last() method!!!!
            return l[num-1]                     # return the last element of the list (<pkgname>.tgz)
        try:
            f = BZ2File(spihome+'/.manifests/'+manifest_file) # put the rigth path
        except:
            print 'An error occurred during unpacking a manifest file'
            sys.exit(2)    
        d = dict()
        l = []                                  # give initial slash for paths
        pkg = ""
        for line in iter(f):
            if line.find('++=', 0, 3) == 0 or line == '\n':
                continue
            if line.find('||', 0, 2) == 0:
                s = line.split()
                if len(s) < 2:
                    continue
                if l != []:
                   # some packages into pasture section have tar.gz extension so we don't consider them but spi notices the user
                   if pkg.split('.')[-1]== "tgz":
                       l.append('||'+section)           # append section of package to the list
                       pkg = pkg [:-4]
                       d[pkg] = l
                   else:
                       print "Skip file (not a package)" + pkg
                       log.write("Skip file (not a package)" + pkg+"\n")
                   l = []
                pkg = getpkgname(s[2]) 
                continue

            file = line.split()[5]
            if file != './':
                l.append("/"+file+"\n")       
        f.close()
        return d       
    
    def build(self,buildparams):
        for t in os.listdir(spihome+'/.packages'):
            # i don't know the process order!!
            section=t.split('_')[0]
            buildparams['section']=(section.lower()) # better is a dictionary
            #print section.lower()
            self.build_packages_dict(t,buildparams)
            del buildparams['section']
    
        for t in os.listdir(spihome+'/.manifests'):
            section=t.split('_')[0]
            p=self.build_files_dict(t,section.lower())
            print "Processing...." 
            #print p
            for elem in p.keys():
                #print p.get(elem)
                if dbcnx.infodbHasAkey(elem)==1:
                    if dbcnx.filedbHasAkey(elem)==1 and list(dbcnx.setLocation(2,elem))[1].split('||')[7] == "official-slackware-repository":
                        continue
                    else:
                        dbcnx.writeFiledb(elem,("".join(p.get(elem))))      # join list element as a unique string
                        dbcnx.writeStatedb(elem,"NOT INSTALLED")
                else:
                    print "Skipping package "+elem+ ": No package info available"
                    continue
            p=dict()

class spidbconnector:   
    def __init__(self,filedb,infodb,statedb,dbdir):
        """db connection class"""
        self.filedb=filedb
        self.infodb=infodb
        self.statedb=statedb
        self.dbdir=dbdir    

    def openConnection(self,rw_flag):
        if rw_flag=='w':
            DBFLAG=bsddb.db.DB_CREATE
        else:
            DBFLAG=bsddb.db.DB_RDONLY
        self.filedb=bsddb.db.DB()
        self.infodb=bsddb.db.DB()
        self.statedb=bsddb.db.DB()
        try:
            self.filedb.open(self.dbdir+'/'+'files.spi',bsddb.db.DB_BTREE,DBFLAG,0644)
            self.infodb.open(self.dbdir+'/'+'info.spi',bsddb.db.DB_BTREE,DBFLAG,0644) 
            self.statedb.open(self.dbdir+'/'+'state.spi',bsddb.db.DB_BTREE,DBFLAG,0644)     
        except bsddb._db.DBAccessError:
            print errmsg.priv
            sys.exit(2)
        return 

    def closeConnection(self):
        print ""
        print "Synching db"
        self.filedb.sync()
        self.statedb.sync()
        self.infodb.sync()
        print "Closing db"
        self.filedb.close()
        self.statedb.close()
        self.infodb.close()
        return
    
    def infodbHasAkey(self,key):
        if (self.infodb.has_key(key)==1):
            return 1
        return 0    
             
    def filedbHasAkey(self,key):
        if (self.filedb.has_key(key)==1):
            return 1
        return 0    
        
    def giveKeys(self,db_pointer):
        if (db_pointer==1):
            return self.filedb.keys()
        if (db_pointer==2):
            return self.infodb.keys()
        if (db_pointer==3):
            return self.statedb.keys()
            
    def setLocation(self,db_pointer,key):
        if (db_pointer==1):
            cursor=self.filedb.cursor()
            desc=cursor.set(key)
            return desc
        if (db_pointer==2):
            cursor=self.infodb.cursor()
            desc=cursor.set(key)
            return desc
        if (db_pointer==3):
            cursor=self.statedb.cursor()
            desc=cursor.set(key)
            return desc
    
    def writeFiledb(self,key,desc): 
        self.filedb[key]=desc
        return      
    
    def writeInfodb(self,key,desc):
        self.infodb[key]=desc
        return
    def writeStatedb(self,key,desc):
        self.statedb[key]=desc
        return 
    def removeDB(self):
        try:
            sys.stdout.write("Try to remove old database......")
            os.remove(spihome+'/info.spi')
            os.remove(spihome+'/files.spi')
            os.remove(spihome+'/state.spi')
            sys.stdout.write("REMOVED\n")
        except:
            sys.stdout.write("NOTHING DONE!\n")
            print "Fisrt building instead"
        return       
class processOpt:
        def __init__(self):
            """class for dispatch options"""
            return
        def check(self,UPDATE,BUILD,PKGINFO,SEARCH,EXACT_SEARCH,INSENSITIVE,NETOPT,CDROM):
            if (bool(UPDATE) and bool(BUILD)):
                print errmsg.incOpt
                return 1
            if ((bool(UPDATE) or bool(BUILD)) and bool(PKGINFO)):
                print errmsg.incOpt
                return 1
            if (bool(UPDATE) or bool(BUILD)) and (bool(SEARCH) or bool(EXACT_SEARCH)):
                print errmsg.incOpt
                return 1
            if (bool(SEARCH) and bool(EXACT_SEARCH)):
                print errmsg.incOpt
                return 1
            if (bool(SEARCH) and bool(NETOPT)):
                print errmsg.incOpt
                return 1
            if (bool(EXACT_SEARCH) and bool(NETOPT)):
                print errmsg.incOpt
                return 1
            if (bool(PKGINFO) and bool(NETOPT)):
                print errmsg.incOpt
                return 1
            if (bool(NETOPT) or bool(CDROM)):
                    if (bool(UPDATE)==0 and bool(BUILD)==0):
                        print errmsg.runbuild   
                        sys.exit(2)
        def process (self,argdict,UPDATE,BUILD,PKGINFO,SEARCH,EXACT_SEARCH,INSENSITIVE,NETOPT,CDROM,LIST,FILES,EXTERNAL):        
            netlist=[]
            if (bool(LIST)):
                    comm.getPackageList(argdict["section"],argdict["version"])         
            if (bool(FILES)):
                    comm.getFilesList(argdict["packagename"])       
            if (bool(SEARCH)):
                self.dbexist()
                match_type=0
                dbsearch().search(match_type,INSENSITIVE,argdict["searchstring"])
            if (bool(EXACT_SEARCH)):    
                self.dbexist()
                match_type=1
                dbsearch().search(match_type,INSENSITIVE,argdict["searchstring"])
            if (bool(PKGINFO)):
                self.dbexist()
                if (bool(INSENSITIVE)):
                    argdict["searchstring"]=argdict["searchstring"].upper()
                dbsearch().pkginfo(INSENSITIVE,argdict["packagestring"])
            if (bool(BUILD) or bool(UPDATE)):
                comm.clean()
                comm.createEnv()
                if (bool(BUILD)): 
                     dbcnx.removeDB()
                dbcnx.openConnection('w')
                if (bool(UPDATE)): 
                    print "Updating..."            
                if (bool(NETOPT)):                  
                     #### for debug we get net files from filesystem
                     if argdict["netargument"]== "all":
                         netlist=["slackware","thirdparty"]
                     else:
                         netlist.append(argdict["netargument"])
                     for item in netlist:                      
                             connectiondict=spinet().connectionInit(item)                        
                             buildparams=spinet().connect(connectiondict)                          
                             slackwareParse().build(buildparams)
                             comm.clean()
                if (bool(CDROM)):
                    buildparams=comm.installcdInit()
                    slackwareParse().build(buildparams)
                    comm.clean()                 
                if (bool(EXTERNAL)):
                    buildparams=comm.extRepositoryInit(argdict)
                    slackwareParse().build(buildparams)
                    comm.clean()       
                comm.updateFromLocalInst() 
                dbcnx.closeConnection()        
            return
           
        def dbexist(self):
            if(os.path.exists(dbcnx.dbdir+'/files.spi')==0):
                print errmsg.nodbpresent
                sys.exit(2)
            else:
                return     
class errMesg:
    def __init__(self):
        """error msgs"""
        err="SPI Error: "
        warn="SPI Warning: "
        self.incOpt=err+"inconsistent options"
        self.nodbpresent=err+"no db present use spi -b for build db"
        self.priv=err+"permission denied"    
        self.generic=err+"ops!"
        self.invArgument=err+"invalid argument"
        self.noMatch="No Match"
        self.badConf=err+"something wrong in conf file"
        self.runbuild=err+"you must use -n or -c option with -b or -u option"
        self.nocache=err+"/var/cache/spi/ directory not present"
        self.badnet=err+"bad network argument"
        self.cderror=err+"can't read data from cd"
        self.missingarg=err+"missing argument"
        self.dupSect=err+"duplicate section in config file"
        self.noOption=err+"option not found in config file, please check!" 
        self.noSection=err+"section not found in config file, please check!" 
        self.noDir=warn+"missing some temporary files... doesn't matter I create for you"
        self.failToGetErr=err+"fail getting file"
        self.stateDBWarn=warn+"state db non alligned"
        self.copyerror=err+"can't copy manifest and packages files (exist?)"
        self.badType=err+"bad configuration, check config file..."
        self.NoExtSect=err+"no section specified"
        return 
    #########################################
    ####    CLASS for UPDATE db from     ####
    ####   	Local INSTALLATION           ####    
    ####                                 ####
    #########################################        
    
    
class localbuild:
   def  __init__(self):
        """build from local db, this class has parsefile() method which create dictionary for
           InfoDB and FileDB"""
   def parsefile(self,buildparams):
        inst_pkg_path=cfg.get('installed_pkg_dir','directory')
        # create pkg list from filesystem
        inst_pkg_list=os.listdir(inst_pkg_path)
        #inst_pkg_list.append("end")
        d = dict() # dict for pkg info
        m = dict() # dict for files info
        infile = 0
        files = ""
        pkg = None
        name = ""
        loc = "" 
        csize = ""
        usize = ""
        desc = ""
        indesc = 0
        counter=0 
        # The algorithm is quite different from slackparParse() method, because files describing installed
        # packages have a different structure, so the algorithm has been little modified.
        number=len(inst_pkg_list)
        #print inst_pkg_list
        for element in inst_pkg_list: 
            counter = counter + 1          
            a=(counter*100)/number
            sys.stderr.write("\r\t\t\t\t"+str(a)+"%")
            f = open(inst_pkg_path+"/"+element) # get from current directory         
            tmplist=[]
            for elem in iter(f):
                tmplist.append(elem)
            
            for line in tmplist:
                # Skip empty lines.
                if line == '\n': 
                    continue
                # Splits the input line.
                fields = line.split()
                # Try to append this line (opportuned modified) to the package
                # description. If this line don't starts with PACKAGE *and* we
                # aren't inside a package description we simply skip this line.
                if fields[0] == 'FILE' and fields[1]=="LIST:":
                    hasKey=dbcnx.filedbHasAkey(element)
                    if hasKey==0:
                        infile=1
                        continue
                    else:
                       break
                if infile==1:
                    if fields[0]== "./":
                   # we can exclude slackdesc and /install
                        continue
                    if line==tmplist[-1]:
                        # write values in dict and exit
                        files=files+'||'+buildparams['section']          # and write section as 'local'
                        m[element]= files
                        d[name] = package(name, loc, csize, usize, desc) # WRITE HERE
                        #print d[name]
                        files = ""
                        infile = 0
                        break
                    files += "/"+fields[0]+"\n"
                if (fields[0] != 'PACKAGE' ):
                    if fields[0] == 'COMPRESSED':
                        csize = fields[3] + fields[4]
                        continue
                    if fields[0] == 'UNCOMPRESSED':
                        usize = fields[3] + fields[4]
                        continue    
                    if indesc == 1:
                        descfields = line.split(" ", 1)
                        if len(descfields) < 2:
                            continue
                        if descfields[0] != "\n":
                # Appending this line to the package's description
                # with 'name: ' prefix removed
                            desc += " " + descfields[1]     
                        else:
                            desc += "\n"
                    continue
                  
                if fields[1] == 'NAME:':
                    if name != "":
                # Adding this package to the dictionary.
                        #d[name] = package(name, loc, csize, usize, desc)  MOVED!!!
                        #print d[name] # NOTICE: it's the previous 'name'!!!
                        desc=""  # Clening attribute value
                    name = fields[2]
                    #print name
                # We compute here the name lenght's here so we avoid to
                # recalculating it on each iteration (see above).
                #    namelen = len(name) + 2
                    indesc = 0
                    ## if it's the last pkg write
                    
                    continue
                # Saving the location.
                if fields[1] == 'LOCATION:':
                    loc = fields[2]
                    continue
                # This starts a the package's description while setting a flag.
                if fields[1] == 'DESCRIPTION:':
                    indesc = 1
                    continue
                # Skip this line if there aren't enough fields (3)
                if len(fields) < 2:
                    continue
                
                # Never reached.
                continue                
                f.close()
        # write files-dictionary into FilesDB
        for elem in m.keys():
            dbcnx.writeFiledb(elem,m.get(elem))
        # write dictionary into InfoDB              
        for elem in d.keys(): 
            checkey=dbcnx.infodbHasAkey(elem) # if a key already exists is from network, so keep existing!
            if (checkey == 0):
                # make value string for db
                #location=inst_pkg_path+"/"+d.get(elem).name    # WE TAKE THE NAME AS LOCATION AS IN THE PKG ORIGINAL DESC
                location=''
                db_value=location+"||"+d.get(elem).name+"||"+d.get(elem).csize+"||"+d.get(elem).usize+"||"+d.get(elem).desc+"||"+\
                buildparams['section']+"||"+buildparams['version']+"||"+buildparams['packagesource']
                dbcnx.writeInfodb(elem,(db_value))      
        
        for elem in inst_pkg_list:
            dbcnx.writeStatedb(elem,"INSTALLED")   
        
        localpkglist=[]
        for k in dbcnx.giveKeys(2):
            valueslist=list(dbcnx.setLocation(2,k))[1].split('||')
            if valueslist[7]== "local-installation":   # is a local packages? 
                localpkglist.append(k)
        
        for k in localpkglist:          
            if (inst_pkg_list.count(k)) !=0:
                continue
            else:
                print
                print "SPI: Removing "+k 
                dbcnx.filedb.delete(k)
                dbcnx.infodb.delete(k) 
                dbcnx.statedb.delete(k)   
        return
    
    ###################################################
    ####     SEARCHING CLASS					   ####
    ####										   ####
    ###################################################
class dbsearch:
    def __init__(self):
        return
    def search(self,match_type,upper,arg1):
        found=0
        dbcnx.openConnection('r')
        print "Searching...."
        if bool(upper):
            arg1=arg1.upper()
        if (match_type==1):
            match_rule=".+/"+arg1+"\n"
        else:
            match_rule=".+"+arg1+".*"+"\n"
    
        for pkg in dbcnx.giveKeys(1):
            result_list=[]
            files=list(dbcnx.setLocation(1,pkg))[1].split('||')[0]            
            section=list(dbcnx.setLocation(1,pkg))[1].split('||')[1]
            if (bool(upper)):
                files=files.upper()
            try:
                result_list=(re.compile(r""+match_rule).findall(files))
            except:
                print errmsg.invArgument
                sys.exit(2)
            if (len(result_list) > 0):             
                found=1
                try:
                    #if dbcnx.filedbHasAkey(pkg)==1:
                    state=list(dbcnx.setLocation(3,pkg))[1]
                except:
                    print errmsg.stateDBWarn
                comm.outputFiles(result_list,pkg,section,state,upper)      
        if (found==0):
            print errmsg.noMatch  
        return

    def pkginfo(self,upper,arg1): 
        found=0
        print "Searching...."
        dbcnx.openConnection('r')
        for pkg in dbcnx.giveKeys(2):
            if (bool(upper)):
                tmp_pkg=pkg.upper()
            else:
                tmp_pkg=pkg
            try:
                result=re.search((arg1),tmp_pkg)
            except:
                print errmsg.invArgument
                sys.exit(2)
            if (result):
                found=1
                mylocation_tuple=dbcnx.setLocation(2,pkg)
                mylocation_list=list(mylocation_tuple)
                dbvalueList=mylocation_list[1].split('||')
                try:
                    mystate_tuple=dbcnx.setLocation(3,pkg)
                    mystate_list=list(mystate_tuple)
                    state=mystate_list[1]            
                except:
                    state="NOT INSTALLED"             
                comm.outputInfo(dbvalueList,pkg,state)
        
        if (found==0):
            print errmsg.noMatch    

class common:
    def __init__(self):
        """common resources"""
        self.usage  ="Spi , Slackware Package Inspector by Nicola La Gloria, Kynetics (c) 2004,2005. \n"
        self.usage +="-v, --version                                      print spi version\n"
        self.usage +="-h, --help                                         print this help\n"
        self.usage +="-i, --insensitive                                  search case insensitive\n"
        self.usage +="-s, --search <string>                              match the specified string\n"
        self.usage +="-e, --search-exac <string>                         match exactly the specified string\n"
        self.usage +="-u, --update                                       update database\n"
        self.usage +="-b, --build                                        build database\n"
        self.usage +="-p, --pkg-info <string>                            match pkg string or substring and return package info\n"
        self.usage +="-n, --net <slackware|thirdparty|all>               download packages data for spi database from a mirror\n"
        self.usage +="-l, --list <section> <version>                     list packages of specific section\n"
        self.usage +="-m, --files string                                 list files in a package\n"
        self.usage +="-x, --external <path> <section> <[version]>        data source is a local repository\n"
        self.usage +="-c, --cdrom                                        use slackware install CD as package data source for spi database\n"
        self.usage +="-k, --checkversion                                 ask if a new spi version is available\n\n"
        self.usage +="Report bugs at weird@slackit.org , http://spi.slackit.org\n"
        return
    def checkspiversion(self,spiversion,userhome):
        spiurl="http://spi.slackit.org/spiversion.txt"
        filename=os.path.join(userhome,'.spi','spiversion.txt')
        urllib.urlretrieve(spiurl,filename)
        urllib.urlcleanup()
        try:
           f = open(filename) # get from current directory
           version=f.readline().split()[0]
        except:
           print 'error reading file version'        
        if version != spiversion:
            print "There's a new version of spi available, download it!"
        else:
            print "No upgrade available"  
        return
    
    def getPackageList(self,section,version):
        dbcnx.openConnection('r')
        packageslist=list(dbcnx.infodb.keys())
        for i in packageslist:
            valueslist=list(dbcnx.setLocation(2,i))[1].split('||')
            if len(valueslist)>1:
                if version=="":
                   if (valueslist[5]==section):
                       print i+".tgz"+"    "+dbcnx.setLocation(3,i)[1]
                   else:
                        continue 
                elif (valueslist[5]==section and (valueslist[6].split('-')[1]==version or valueslist[6]==version)):
                    print i+".tgz"+"    "+dbcnx.setLocation(3,i)[1]
                else:
                    continue          
        return
    
    def getFilesList(self,package):
        print ""
        if package.split('.')[-1]=='tgz':
            package=package[:-4]
        dbcnx.openConnection('r')
        pkglist=list(dbcnx.filedb.keys())
        for i in pkglist:
            result=re.search((package),i)
            if (result):
                print "PACKAGE: "+i
                valueslist=list(dbcnx.setLocation(1,i))[1].split('||')
                print valueslist[0]
                state=dbcnx.setLocation(3,i)[1]
                print "STATUS: "+state+"\n"
            else:
                continue          
        return
    
    def checkVersion(self,key,slackwareversion):
        slash="/"
        versions=slackwareversion.split('/')
        if dbcnx.infodbHasAkey(key)==1:
            desc=dbcnx.setLocation(2,key)
            # line splitting, TODO a class for line splitting (used also in infosearch)
            mylocation_tuple=dbcnx.setLocation(2,key)
            mylocation_list=list(mylocation_tuple)
            dbvalueList=mylocation_list[1].split('||')
            versionlist=dbvalueList[6].split('/')
            if (versionlist.count("")!=0 or slackwareversion==''):  # don't put the slash if version field is empty (ex. if localbuild is performed first)
                slash=""
            for e in versionlist:
                for i in versions:
                    if e == i:
                       continue
                    else:
                        slackwareversion=e+slash+slackwareversion
        return slackwareversion
    
    def createEnv(self):             
        if os.path.exists(spihome+'/.manifests') == 0:
           os.mkdir(spihome+'/.manifests',0755)
        if os.path.exists(spihome+'/.packages') == 0:
           os.mkdir(spihome+'/.packages',0755)
        return
        
    def updateFromLocalInst(self):
        sys.stderr.write("Processing installed packages...")
        # for localbuild we don't know the version of slackware the pkg correspond to.
        buildparams=dict() 
        buildparams['version']=''
        buildparams['section']='local'
        buildparams['packagesource']='local-installation'
        lb=localbuild()
        lb.parsefile(buildparams)  
        return   
    
    def installcdInit(self):
        # this method return build parameters 
        print "build from cdrom"
        device=cfg.get('slackware_install_cd','cdrom')
        path=cfg.get('slackware_install_cd','version_path')
        version=cfg.get('slackware_install_cd','version')
        section=cfg.get('slackware_install_cd','section')
        packagesource=cfg.get('slackware_install_cd','packagesource')
        try:
            copy(device+'/'+path+'/MANIFEST.bz2',spihome+'/.manifests/'+section.upper()+'_MANIFEST.bz2')
            copy(device+'/'+path+'/PACKAGES.TXT',spihome+'/.packages/'+section.upper()+'_PACKAGES.TXT')
        except:
            print errmsg.cderror
            sys.exit(2)
        buildparams=dict()
        buildparams['version']=version
        buildparams['section']=section
        buildparams['packagesource']=packagesource
        return buildparams
    
    def extRepositoryInit(self,argdict):
        packagesource='local-repository'
        buildparams=dict()
        
        if argdict.has_key('extversion'):
            buildparams['version']=argdict['extversion']
        else:
            buildparams['version']=''
        if argdict.has_key('extsection')==0:
            print errmsg.NoExtSect
            sys.exit(2)
        buildparams['packagesource']=packagesource
        buildparams['location']=argdict['extpath']
        location=argdict["extpath"]
        try:           
            copy(os.path.join(argdict["extpath"],'MANIFEST.bz2'),os.path.join(spihome,'.manifests',argdict["extsection"].upper()+'_MANIFEST.bz2'))
            copy(os.path.join(argdict["extpath"],'PACKAGES.TXT'),os.path.join(spihome,'.packages',argdict["extsection"].upper()+'_PACKAGES.TXT'))           
        except:
            print errmsg.copyerror
            sys.exit(2)
        
        return buildparams
        
    
    def write_log(self,logstr,flag):
        if flag==1:
            Now=localtime(time())
            mytime=strftime("\n%b %d %I:%M:%S",Now)
            mytime=mytime+': '
        else:
            mytime=''
        log.write(mytime+logstr)    
        return

    def clean(self):
        #print "Removing files..."       
        try:
            for f in os.listdir(spihome+'/.manifests/'):
                os.remove(spihome+'/.manifests/'+f)
            for f in os.listdir(spihome+'/.packages/'):
                os.remove(spihome+'/.packages/'+f)
        except:
            print errmsg.noDir
        return
    
    # The berkeley db structure of filedb is the cuple
    # (pkgname,"LOCATION|NAME|COMP_SIZE|UNCOMP_SIZE|DESCRIPTION|SECTION|VERSION|PKGSOURCE")
    
    def outputInfo(self,dbvalueList,pkg,state):
        location="PACKAGE LOCATION: "+dbvalueList[0]+"\n"     
        name="PACKAGE NAME: "+dbvalueList[1]+"\n"
        csize="PACKAGE COMPRESSED SIZE: "+dbvalueList[2]+"\n"
        usize="PACKAGE UNCOMPRESSED SIZE: "+dbvalueList[3]+"\n"
        desc="PACKAGE DESCRIPTION:\n"+dbvalueList[4]+"\n"
        section="PACKAGE SECTION: "+dbvalueList[5]+"\n"
        version="SLACKWARE VERSION: "+dbvalueList[6]+"\n"
        pkgsource="PACKAGE SOURCE: "+dbvalueList[7]+"\n"
        
        state="STATUS: "+state+"\n"
        result=name+section+version+location+csize+usize+pkgsource+desc+state
        self.write_log("\n",1)
        self.write_log(result+"\n",0)
        print result    
        return
        
    def outputFiles(self,result_list,pkg,section,state,upper):
        print ""
        logstr="Match found in package: "+pkg+"\n"+"SECTION: "+section.lower()+"\n"+"STATUS: "+state
        self.write_log(logstr+"\n",1)
        print logstr
        for a in (result_list):
            if bool(upper):
                a=a.lower()
            sys.stdout.write(a)
            self.write_log(a,0)
        return
    
    def checkConfig(self):
        netlist=['slackware','thirdparty']
        try:
            cfg.get('cache','directory')
            for item in netlist:                      
                connectiondict=spinet().connectionInit(item)
                connectiondict=dict()
            cfg.get('slackware_install_cd','cdrom')
            cfg.get('slackware_install_cd','version_path')
            cfg.get('slackware_install_cd','version')
            cfg.get('slackware_install_cd','section')
            cfg.get('slackware_install_cd','packagesource')    
            cfg.get('installed_pkg_dir','directory')
            cfg.get('thirdparty_sections','section')
        except ConfigParser.NoOptionError:
            print errmsg.noOption
            sys.exit(2)
        except ConfigParser.NoSectionError:
            print errmsg.noSection
            sys.exit(2)
        return
        
    def usage(self):        
        return
    def checkOption(self,args,flag):
        if (len(args)>1 or flag !=1):
            print comm.usage
            sys.exit(2)
        return

def main():    
    global spihome 
    global cnx
    global dbcnx
    global errmsg
    global cfg
    global log
    global comm
    
    
    #### boolean constant
    UPDATE=0
    INSENSITIVE=0
    NETOPT=0
    USAGE=0
    SEARCH=0
    PKGINFO=0
    BUILD=0
    EXACT_SEARCH=0
    CDROM=0 
    LIST=0
    FILES=0
    EXTERNAL=0
    PATH=0
    
    filedb=""
    infodb=""
    statedb="" 
    argdict=dict()
    MYHOME=os.environ['HOME']
    spiversion="spi-1.0_rc4"
    spi_version_string="SPI Slackware Package Inspector Version: "+spiversion
    # class istance
    errmsg=errMesg()
    cnx=spinet()
    comm=common()    
    options=processOpt()
    
    cfg=ConfigParser.ConfigParser()    
    
    if os.path.exists("/etc/spi/spi.conf") == 0:
        cfg.read('./spi.conf')
    else:
        cfg.read('/etc/spi/spi.conf')
    comm.checkConfig()
    spihome=cfg.get('cache','directory')
    dbcnx=spidbconnector(filedb,infodb,statedb,spihome) 
    
    if(os.path.isdir(spihome)==0):
                print errmsg.nocache
                sys.exit(2)   
    if(os.path.isdir(MYHOME+'/.spi')==0):
        os.makedirs(MYHOME+'/.spi')    
    log=open(MYHOME+'/.spi/spi.log','w+')    
    try:
        opts, args = getopt.getopt(sys.argv[1:], "vkhe:s:p:iubn:cl:m:x:", ["version","checkversion","help","search-exact=","search=","pkg-info=","insensitive",\
          "update","build","net=","cdrom","list=","files=","external="])
    except getopt.GetoptError:
        # print help information and exit:
            print comm.usage
            sys.exit(2)
    if (len(opts)==0):
        print comm.usage
        sys.exit(2)    
    if (len(args)>3):
        print errmsg.invArgument
        print comm.usage
        sys.exit(2)
   
    for opt,arg  in opts:
        if opt in ("-v", "--version"):          
            print spi_version_string
            sys.exit(2)         
        if opt in ("-h", "--help"):          
            print comm.usage
            sys.exit(2)           
        if opt in ("-s","--search"):        
            SEARCH=1
            argdict["searchstring"]=arg
        if opt in ("-e","--search-exact"):        
            EXACT_SEARCH=1
            argdict['searchstring']=arg
        if opt in ("-u","--update"):
            UPDATE=1  
        if opt in ("-b","--build"):
            BUILD=1
        if opt in ("-p","--pkg-info"):
            PKGINFO=1
            argdict["packagestring"]=arg
        if opt in ("-i","--insensitive"):
            INSENSITIVE=1
        if opt in ("-c","--cdrom"):
            CDROM=1
        if opt in ("-n","--net"):
            NETOPT=1
            if (arg != "slackware" and arg != "thirdparty" and arg != "all"):
                print errmsg.badnet
                sys.exit(2)
            argdict["netargument"]=arg
        if opt in ("-l","--list"):
            LIST=1
            try:
                argdict["version"]=args[0]
            except:
                argdict["version"]=""
            argdict["section"]=arg
        if opt in ("-m","--files"):
            FILES=1
            argdict["packagename"]=arg
        if opt in ("-x","--external"):
            EXTERNAL=1
            if (len(args)<1):
            #if (arg !='' or len(args)>0):
                print comm.usage
                sys.exit(2)
            argdict["extpath"]=arg
            argdict["extsection"]=args[0] 
            if len(args)>1:
                argdict["extversion"]=args[1].lower()
            else:          
                argdict["extversion"]=''
        if opt in ("-k","--checkversion"):
            if (arg !='' or len(args)>0):
                print comm.usage
                sys.exit(2)
            else:
                comm.checkspiversion(spiversion,MYHOME)
                
    badopt=options.check(UPDATE,BUILD,PKGINFO,SEARCH,EXACT_SEARCH,INSENSITIVE,NETOPT,CDROM)
       
    if (badopt!=1):      
        options.process(argdict,UPDATE,BUILD,PKGINFO,SEARCH,EXACT_SEARCH,INSENSITIVE,NETOPT,CDROM,LIST,FILES,EXTERNAL)
    log.close()      
             
if (__name__=="__main__"):

    main()   
        
