#!/usr/bin/env python

# easibox = the main Easibox executable
#
#############################################################
# Copyright (c) 2001-2004 Philip Hunt. 
# You may use this software under the terms of the GNU
# General Public License (GPL). See file COPYING for details.
#############################################################

# Last altered: 12-Jan-2004
# History:
# 14-Oct-2001 PH: created

import os.path
import glob
import sys
import getopt
import commands

import utility
from easibox_const import *

debug = 0 # debugging this module?
verbose = 0

archDirBase = "."
keepDirectories = 1

fileList = [ ]
postRunList = [ ]
execfileGlobals = { }

#---------------------------------------------------------------------
# stuff useful for debugging

def outFiles(fl):
   """ returns string containing a list of files. For debugging. """
   r = ""
   for f in fl:
      r = r + "   " + f + "\n"
   return r   

def printVariables(glob):
   """ print all the variables found in (glob). Don't print functions.
       For debugging """
   print "Variables:"
   keyList = glob.keys()
   keyList.sort()
   for varName in keyList:
      value = glob[varName]
      if varName != "__builtins__":
         if not callable(value):
            print "   %s = %s" % (varName, repr(value))


#---------------------------------------------------------------------
# commands and functions that can go in .easiboxrc files
   
def include(s):
   global fileList
   if debug or verbose: print "[include '%s']" % s
   filesToAdd = glob.glob(s)
   #print "Adding:\n" + outFiles(filesToAdd)
   for fa in filesToAdd:
      if fa not in fileList:
         if not utility.isDir(fa):
            if debug: print "Appending file %s" % fa
            fileList.append(fa)
   #print "New File List:\n" + outFiles(fileList)
   
def exclude(s):
   global fileList
   if debug or verbose: print "[exclude '%s']" % s
   filesToRemove = glob.glob(s)
   #print "Removing:\n" + outFiles(filesToRemove)
   for fr in filesToRemove:
      if fr in fileList:
         fileList.remove(fr)
   #print "New File List:\n" + outFiles(fileList)
   
def getBoxFilenames():
   """ Return a list of the full pathnames of all the box files
   that are expected to be produced.
   @ return [list of string]
   """   
   boxFormats = execfileGlobals['boxFormats']
   packageName = execfileGlobals['packageName']
   packageVer = execfileGlobals['packageVer']
   archiveDestination = execfileGlobals['archiveDestination']
   result = []
   for bf in boxFormats:
      fn = "%s-%s.%s" % (packageName, packageVer, bf)
      pn = os.path.join(archiveDestination, fn)
      result.append(pn)
   ##/for   
   return result
     
def prerun(s):
   if debug or verbose: print "[prerun '%s']" % s
   os.system(s)
   
def postrun(s):
   global postRunList
   if debug or verbose: print "[postrun '%s']" % s
   postRunList.append(s)
   
def dummy_include(s):
   if debug: print "[dummy include '%s']" %s   

def dummy_exclude(s):
   if debug: print "[dummy exclude '%s']" %s   

def dummy_prerun(s):
   if debug: print "[dummy prerun '%s']" %s   

def dummy_postrun(s):
   if debug: print "[dummy postrun '%s']" %s   

#---------------------------------------------------------------------

"""*** 
Execute an operating system command, as if at the command line.

If the command returns a status other than 0, this function raises an 
IOError exception. The behaviour can be prevented by setting
careAboutErrors=0 when calling the function, in which case no
exception is raised.
***"""

def system(cmd, careAboutErrors =1):
   if debug or verbose:
      print "CMD { %s }" % cmd
   status,output = commands.getstatusoutput(cmd)
   if status != 0:
      errorMsg = """Command {%s} returned value %d; output was:
*****(begin)      
%s
*****(end)""" % (cmd,status,output)
      if debug or verbose: print errorMsg
      if careAboutErrors: raise IOError, errorMsg
   return output   

#---------------------------------------------------------------------

def makeArchiveDir(aDirBase, aName, aVer):
   if debug: print "makeArchiveDir(aDirBase=[%s],aName=[%s],aVer=[%s])" %(
         aDirBase, aName, aVer)
   aNameVer = aName + "-" + aVer
   aDir = os.path.join(aDirBase, aNameVer)
   
   # if the archive directory already exists, remove it.
   if utility.dirExists(aDir):
      cmd = "rm -r %s" % aDir
      system(cmd)
   # create the archive directory   
   utility.forceDir(aDir)   
   return aDir, aNameVer
   
def copyArchiveFiles(fList, aDir):
   if debug:
      print "copyArchiveFiles() fList=%s aDir=%s" % (fList,aDir)
   for f in fList:
      destFile = os.path.join(aDir, f)
   
      # force destination directory to exist, before copying
      utility.forceDir(os.path.dirname(destFile))
   
      cmd = "cp -p %s %s" % (f, destFile)
      system(cmd, careAboutErrors=0)

def makeBoxFiles(aDirBase, aNameVer):
   global boxFormats, BOX_FORMAT_INTERP
   # start of command
   cmd0 = "cd %s; " % aDirBase   
   for ext in boxFormats:
      if debug: print "makeArchiveFile(), ext='%s'" % ext
      vars = {'ad': aNameVer, 'afn': aNameVer+"."+ext}
      template = BOX_FORMAT_INTERP[ext]
      if debug: print "template='%s'" % template      
      cmd1 = "rm -f %(afn)s; " % vars
      cmd2 = template % vars
      cmd = cmd0 + cmd1 + cmd2
      fullBoxFileName = os.path.join(aDirBase, vars["afn"])
      print "easibox: creating file %s" % fullBoxFileName
      system(cmd)
   #//for ext            

def makeArchive():
   global archName, archVer, archDirBase, fileList
   aDir, aNameVer = makeArchiveDir(archDirBase, archName, archVer)
   copyArchiveFiles(fileList, aDir)
   makeBoxFiles(archDirBase, aNameVer)
   
def execPostRun():
   """ execute the postrun instructions """
   global postRunList
   for cmd in postRunList:
      if verbose: print "easibox: postrun: %s" % cmd 
      system(cmd)  

def getPackageFromCD():
   """ get the name of the current package from the name of the
       curent directory we are in. """
   return os.path.basename(os.getcwd())
   
def execEasiboxFile(filename, warnIfMissing =0):
   """ execute a file for easibox """
   #global boxFormats
   global execfileGlobals
   if utility.fileExists(filename):
      if verbose: print "easibox: running '%s'..." % filename
      execfile(filename, execfileGlobals)
      #execfile(filename)
   else:
      if warnIfMissing: print "easibox: missing file '%s'." \
         % os.path.abspath(filename)
         

def makeArchives(givenName, givenVer): 
   global archName, archVer, execfileGlobals 
   global boxFormats, archDirBase, keepDirectories
   if debug: print "makeArchives(%s, %s)" %(givenName, givenVer)  
   if givenName == None: 
      archName = getPackageFromCD()
   else:
      archName = givenName 
   archVer = givenVer      
   
   # initialise variables for archiving:
   execfileGlobals['boxFormats'] = DEFAULT_BOX_FORMATS
   execfileGlobals['archiveDestination'] = DEFAULT_BOX_DESTINATION
   execfileGlobals['keepDirectories'] = DEFAULT_KEEP_DIRECTORIES
   execfileGlobals['packageName'] = archName
   execfileGlobals['packageVer'] = archVer
   execfileGlobals['userName'] = os.environ['USER']
   
   execfileGlobals['include'] = include
   execfileGlobals['exclude'] = exclude
   execfileGlobals['getBoxFilenames'] = getBoxFilenames
   execfileGlobals['prerun'] = prerun
   execfileGlobals['postrun'] = postrun
       
   execEasiboxFile("/etc/easiboxrc")
   execEasiboxFile(os.path.expanduser("~/.easiboxrc"))
   execEasiboxFile(".easiboxrc", 1)
   
   boxFormats = execfileGlobals['boxFormats']
   archDirBase = execfileGlobals['archiveDestination']
   keepDirectories = execfileGlobals['keepDirectories']
   
   if verbose:
      # print variables altered in easibox files:
      printVariables(execfileGlobals)
         
   makeArchive()  
   execPostRun()
 
#---------------------------------------------------------------------
# print list information

def easiboxList(givenName):
   global archName
   if givenName == None: 
      archName = getPackageFromCD()
   else:
      archName = givenName 
   archVer = "0.1"   
      
   # read easibox files,  to find out what directory we are archiving to:
   execfileGlobals['boxFormats'] = DEFAULT_BOX_FORMATS
   execfileGlobals['archiveDestination'] = DEFAULT_BOX_DESTINATION
   execfileGlobals['keepDirectories'] = DEFAULT_KEEP_DIRECTORIES
   execfileGlobals['packageName'] = archName
   execfileGlobals['packageVer'] = archVer
   execfileGlobals['userName'] = os.environ['USER']
   
   execfileGlobals['include'] = dummy_include
   execfileGlobals['exclude'] = dummy_exclude
   execfileGlobals['getBoxFilenames'] = getBoxFilenames
   execfileGlobals['prerun'] = dummy_prerun
   execfileGlobals['postrun'] = dummy_postrun
       
   execEasiboxFile("/etc/easiboxrc")
   execEasiboxFile(os.path.expanduser("~/.easiboxrc"))
   execEasiboxFile(".easiboxrc", 1)
   
   # we are archiving to this directory
   archDirBase = execfileGlobals['archiveDestination']
   
   allBoxExtensions = BOX_FORMAT_INTERP.keys()
   lsFileList = ""
   for ext in allBoxExtensions:
      lsFileList += " %s-*.%s" % (archName, ext)
   
   cmd = "cd %s; ls -lvdF %s-*" % (archDirBase, archName)
   if debug or verbose:
      print "CMD { %s }" % cmd
   status,output = commands.getstatusoutput(cmd)
   if status == 0:
      print "Archiving to %s; stored files are:" % archDirBase
      print output
   else:
      print "Archiving to %s; no stored files for %s"\
          % (archDirBase, archName)
      #print "### %s ###" % output   

#---------------------------------------------------------------------
# print usage and other information

def usage():
   versionAndCopyright()
   print """
Easibox is a program to facilitate the creation of archive files such 
as .tar files. You may use Easibox under the terms of the GNU General 
Public License; see file COPYING for details.  

Usage:
   easibox [--pack <package>] --ver <ver>
      These cause Easibox to create box files for the target package. 
      (Short forms are -p, -v).
      
   easibox [--pack <package>] --list
      Lists all boxed versions of the target package. (short form for
      --list is -l).
      
   easibox --usage
      Prints information on how to use Easibox.
   easibox --version
      Prints the version of Easibox being used.      
   easibox --info
      Prints information about how Easibox is currently configured.
      
   --verbose
      Flag that causes easibox to say what it's doing (short form is -V).   
""",

def versionInfo():
   print "easibox-%s" % EASIBOX_VERSION
   
   
def versionAndCopyright():
   print "easibox-%s. Copyright (c) 1999-2004 Philip Hunt." % EASIBOX_VERSION


def easiboxInfo():
   print "easibox-%s can make these archive types:" % EASIBOX_VERSION
   s = ""
   for bf in BOX_FORMAT_INTERP.keys():
      s += bf + ", "
   s = s[:-2]
   print "   %s" % s    


#---------------------------------------------------------------------

 
def main():
   global verbose

   try:
      opts,args = getopt.getopt(sys.argv[1:], "v:p:Vl",
         ["ver=", "pack=", "verbose", "version", "info", "usage", "list"])
   except:
      # print help info, and exit
      usage()
      sys.exit(1)
      
   archName = None 
   archVer = None
   gotVer = 0 # user included -v or --ver options
        
   for opt, oa in opts:
      if opt == "--verbose" or opt == "-V":
         verbose = 1
         #print "--VERBOSE--"
      if opt == "-p" or opt == "--pack":
         archName = oa   
      if opt == "-v" or opt == "--ver":
         archVer = oa
         gotVer = 1      
      if opt == "--usage":
         usage()
         sys.exit()         
      if opt == "--version":
         versionInfo()
         sys.exit()
      if opt == "--info":
         easiboxInfo()
         sys.exit()
      if opt == "--list" or opt == "-l":
         easiboxList(archName)
         sys.exit()   
   #//for
   
   if gotVer:
      versionAndCopyright()
      makeArchives(archName, archVer)
   else:
      # no valid options specified, so print usage information
      usage()
      sys.exit()


# run main:
if __name__ == "__main__": main()

#end
