#!/usr/bin/env python

#    -----------------------------------------------------------------
#
#    Flagpoll: A tool to extract flags from installed applications
#    for compiling, settting variables, etc.
#
#    Original Authors:
#       Daniel E. Shipton <dshipton@gmail.com>
#
#    Flagpoll is Copyright (C) 2006-2007 by Daniel E. Shipton
#
#    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:
#    Free SoftwareFoundation, Inc.
#    51 Franklin Street, Fifth Floor, 
#    Boston, MA  02110-1301  USA
#
#    -----------------------------------------------------------------


import sys, os, glob, os.path, copy, platform
pj = os.path.join


####################################################################
# SNAGGED FROM PYTHON 2.4 for versions under 2.4
####################################################################
import re as _re
class _multimap:
    """Helper class for combining multiple mappings.

    Used by .{safe_,}substitute() to combine the mapping and keyword
    arguments.
    """
    def __init__(self, primary, secondary):
        self._primary = primary
        self._secondary = secondary

    def __getitem__(self, key):
        try:
            return self._primary[key]
        except KeyError:
            return self._secondary[key]

class _TemplateMetaclass(type):
    pattern = r"""
    %(delim)s(?:
      (?P<escaped>%(delim)s) |   # Escape sequence of two delimiters
      (?P<named>%(id)s)      |   # delimiter and a Python identifier
      {(?P<braced>%(id)s)}   |   # delimiter and a braced identifier
      (?P<invalid>)              # Other ill-formed delimiter exprs
    )
    """

    def __init__(cls, name, bases, dct):
        super(_TemplateMetaclass, cls).__init__(name, bases, dct)
        if 'pattern' in dct:
            pattern = cls.pattern
        else:
            pattern = _TemplateMetaclass.pattern % {
                'delim' : _re.escape(cls.delimiter),
                'id'    : cls.idpattern,
                }
        cls.pattern = _re.compile(pattern, _re.IGNORECASE | _re.VERBOSE)

class Template:
    """A string class for supporting $-substitutions."""
    __metaclass__ = _TemplateMetaclass

    delimiter = '$'
    idpattern = r'[_a-z][_a-z0-9]*'

    def __init__(self, template):
        self.template = template

    # Search for $$, $identifier, ${identifier}, and any bare $'s

    def _invalid(self, mo):
        i = mo.start('invalid')
        lines = self.template[:i].splitlines(True)
        if not lines:
            colno = 1
            lineno = 1
        else:
            colno = i - len(''.join(lines[:-1]))
            lineno = len(lines)
        raise ValueError('Invalid placeholder in string: line %d, col %d' %
                         (lineno, colno))

    def substitute(self, *args, **kws):
        if len(args) > 1:
            raise TypeError('Too many positional arguments')
        if not args:
            mapping = kws
        elif kws:
            mapping = _multimap(kws, args[0])
        else:
            mapping = args[0]
        # Helper function for .sub()
        def convert(mo):
            # Check the most common path first.
            named = mo.group('named') or mo.group('braced')
            if named is not None:
                val = mapping[named]
                # We use this idiom instead of str() because the latter will
                # fail if val is a Unicode containing non-ASCII characters.
                return '%s' % val
            if mo.group('escaped') is not None:
                return self.delimiter
            if mo.group('invalid') is not None:
                self._invalid(mo)
            raise ValueError('Unrecognized named group in pattern',
                             self.pattern)
        return self.pattern.sub(convert, self.template)

    def safe_substitute(self, *args, **kws):
        if len(args) > 1:
            raise TypeError('Too many positional arguments')
        if not args:
            mapping = kws
        elif kws:
            mapping = _multimap(kws, args[0])
        else:
            mapping = args[0]
        # Helper function for .sub()
        def convert(mo):
            named = mo.group('named')
            if named is not None:
                try:
                    # We use this idiom instead of str() because the latter
                    # will fail if val is a Unicode containing non-ASCII
                    return '%s' % mapping[named]
                except KeyError:
                    return self.delimiter + named
            braced = mo.group('braced')
            if braced is not None:
                try:
                    return '%s' % mapping[braced]
                except KeyError:
                    return self.delimiter + '{' + braced + '}'
            if mo.group('escaped') is not None:
                return self.delimiter
            if mo.group('invalid') is not None:
                return self.delimiter
            raise ValueError('Unrecognized named group in pattern',
                             self.pattern)
        return self.pattern.sub(convert, self.template)
####################################################################
# End Snagging
####################################################################

def tokenize(val, split_char):
   val = val.strip()
   results = []
   while len(val) > 0:
      sn = val.find(split_char)
      qn = val.find('"')
      #print " -> val: %s Len: %s" % (val, len(val))

      # If we find a quote first, then find the space after the next quote.
      if qn < sn:
         #print " -> Found a quote first:", qn
         # Find next quote.
         qn2 = val.find('"', qn+1)
         #print " -> Found second quote:", qn2
         sn = val.find(split_char, qn2+1)
         #print " -> Found next space:", sn

      if sn < 0:
         results.append(val[:])
         val = ""
      else:
         results.append(val[:sn])
         val = val[sn:]
         val = val.strip()

   return results

class Utils:
   # Holds collection of small utility functions

   EXTRA_FLAGPOLL_SEARCH_PATHS = ""
   EXTRA_FLAGPOLL_FILES = ""
   path_list_cache = None                         # Cache of found path lists.  (only calcuate once)
   KEEP_DUPS = False
   FLAGPOLL_LIST_OF_POSSIBLE_ERRORS = []

   def addPossibleError(pos_err):
      Utils.FLAGPOLL_LIST_OF_POSSIBLE_ERRORS.append(pos_err)
   addPossibleError = staticmethod(addPossibleError)

   def getPossibleErrors():
      return Utils.stripDupInList(Utils.FLAGPOLL_LIST_OF_POSSIBLE_ERRORS)
   getPossibleErrors = staticmethod(getPossibleErrors)

   def getFlagpollVersion():
      FLAGPOLL_MAJOR_VERSION = 0
      FLAGPOLL_MINOR_VERSION = 9
      FLAGPOLL_PATCH_VERSION = 4
      return ( FLAGPOLL_MAJOR_VERSION, FLAGPOLL_MINOR_VERSION, FLAGPOLL_PATCH_VERSION )
   getFlagpollVersion = staticmethod(getFlagpollVersion)

   def getPathList():
      if None != Utils.path_list_cache:
         return Utils.path_list_cache

      #TODO: expand LD_LIBRARY_PATH to 64/32/etc???
      pkg_cfg_dir = []
      flg_cfg_dir = []
      ld_path = []

      if os.environ.has_key("PKG_CONFIG_PATH"):
         pkg_cfg_dir = os.environ["PKG_CONFIG_PATH"].split(os.pathsep)

      if os.environ.has_key("FLAGPOLL_PATH"):
         flg_cfg_dir = os.environ["FLAGPOLL_PATH"].split(os.pathsep)

      ld_dirs = ['LD_LIBRARY_PATH', 'DYLD_LIBRARY_PATH', 'PATH']
      for d in ld_dirs:
         if os.environ.has_key(d):
            cur_path = os.environ[d].split(os.pathsep)
            ld_path_pkg = [pj(p,'pkgconfig') for p in cur_path]
            ld_path_flg = [pj(p,'flagpoll') for p in cur_path]
            ld_path.extend(ld_path_pkg)
            ld_path.extend(ld_path_flg)

      extra_paths = Utils.EXTRA_FLAGPOLL_SEARCH_PATHS.split(os.pathsep)
      path_list = extra_paths
      path_list.extend(flg_cfg_dir)
      path_list.extend(pkg_cfg_dir)
      path_list.extend(ld_path)
      default_path_list = [pj('/','usr','lib64'), pj('/','usr','lib32'), pj('/','usr','lib'), pj('/','usr','share')]
      default_path_pkg = [pj(p,'pkgconfig') for p in default_path_list]
      default_path_flg = [pj(p,'flagpoll') for p in default_path_list]
      path_list.extend(default_path_pkg)
      path_list.extend(default_path_flg)

      flagDBG().out(flagDBG.VERBOSE, "Utils.getPathList",
                    "Potential path list: " + str(path_list))
      clean_path_list = []
      for p in path_list:
         if os.path.exists(p) and os.path.isdir(p) and not p in clean_path_list:
            clean_path_list.append(p)
      path_list = clean_path_list
      flagDBG().out(flagDBG.INFO, "Utils.getPathList",
                    "Using path list: " + str(path_list))
      Utils.path_list_cache = path_list
      return path_list
   getPathList = staticmethod(getPathList)

   def getFileList():
      flist = Utils.EXTRA_FLAGPOLL_FILES.split(":")
      filelist = []
      for f in flist:
         if os.path.isfile(f):
            filelist.append(f)
      return filelist
   getFileList = staticmethod(getFileList)

   def setKeepDups(keep_dups):
      Utils.KEEP_DUPS = keep_dups
   setKeepDups = staticmethod(setKeepDups)

   def stripDupInList(gen_list):
      new_list = []
      list_len = len(gen_list)
      i = 0
      while i < list_len:
         item = gen_list[i].strip()
         if Utils.KEEP_DUPS:
            if len(item) > 0:
               new_list.append(item)
            continue
         if item in ('-framework', '-arch', '-isysroot'):
            item = '%s %s' % (item, gen_list[i + 1])
            i += 1
         if item not in new_list:
            if len(item) > 0:
               new_list.append(item)
         i += 1
      return new_list
   stripDupInList = staticmethod(stripDupInList)

   def stripDupLinkerFlags(flag_list):
      # List is constructed as such ("-L /path", "-L/sfad", "-fpge", "-l pg", "-lpg")
      # We do slightly dumb stripping though
      lib_list = []
      dir_list = []
      list_len = len(flag_list)
      i = 0
      vc_linker_flag_re = _re.compile(r'^[/-]libpath:', _re.I)
      other_linker_flag_re = _re.compile(r'^-[LRF]')
      while i < list_len:
         flg = flag_list[i].strip()
         if len(flg) > 0:
            if (other_linker_flag_re.match(flg) is not None\
                  or vc_linker_flag_re.match(flg) is not None):
               dir_list.append(flg)
            else:
               if flg in ("-framework", "-arch", "-isysroot"):
                  flg = "%s %s" % (flg, flag_list[i + 1])
                  i += 1
               lib_list.append(flg)
         i += 1
      new_list = dir_list + lib_list
      return Utils.stripDupInList(new_list)
   stripDupLinkerFlags = staticmethod(stripDupLinkerFlags)

   def stripDupIncludeFlags(flag_list):
      # List is constructed as such ("-I/inc", "-fno-static-blah")
      # We do slightly dumb stripping though
      inc_list = []
      extra_list = []
      list_len = len(flag_list)
      i = 0
      while i < list_len:
         flg = flag_list[i].strip()
         if len(flg) > 0:
            if flg.startswith("-I") or flg.startswith("/I"):
               inc_list.append(flg)
            elif flg in ("-arch", "-isysroot"):
               extra_list.append("%s %s" % (flg, flag_list[i + 1]))
               i += 1
            else:
               extra_list.append(flg)
         i += 1
      extra_list.sort()
      new_list = inc_list + extra_list
      return Utils.stripDupInList(new_list)
   stripDupIncludeFlags = staticmethod(stripDupIncludeFlags)

   def cflagsOnlyDirIncludeFlags(flag_list):
      # List is constructed as such ("-I/inc", "-fno-static-blah")
      # We do slightly dumb stripping though
      inc_list = []
      for flg in flag_list:
         flg = flg.strip()
         if len(flg) > 0:
            if flg.startswith("-I") or flg.startswith("/I"):
               inc_list.append(flg)
      return Utils.stripDupInList(inc_list)
   cflagsOnlyDirIncludeFlags = staticmethod(cflagsOnlyDirIncludeFlags)

   def cflagsOnlyOtherIncludeFlags(flag_list):
      # List is constructed as such ("-I/inc", "-fno-static-blah")
      # We do slightly dumb stripping though
      extra_list = []
      for flg in flag_list:
         flg = flg.strip()
         if len(flg) > 0:
            if not flg.startswith("-I") and not flg.startswith("/I"):
               extra_list.append(flg)
      return Utils.stripDupInList(extra_list)
   cflagsOnlyOtherIncludeFlags = staticmethod(cflagsOnlyOtherIncludeFlags)

   def libsOnlyLinkerFlags(flag_list):
     # List is constructed as such ("-L /path", "-L/sfad", "-fpge", "-l pg", "-lpg")
      # We do slightly dumb stripping though
      lib_list = []
      for flg in flag_list:
         flg = flg.strip()
         if len(flg) > 0:
            if flg.startswith("-l") or flg.endswith(".lib"):
               lib_list.append(flg)
      return Utils.stripDupInList(lib_list)
   libsOnlyLinkerFlags = staticmethod(libsOnlyLinkerFlags)

   def libsOnlyOtherLinkerFlags(flag_list):
     # List is constructed as such ("-L /path", "-L/sfad", "-fpge", "-l pg", "-lpg")
      # We do slightly dumb stripping though
      other_list = []
      for flg in flag_list:
         flg = flg.strip()
         if len(flg) > 0:
            upper_flg = flg.upper()
            if not flg.startswith("-l") \
                  and not flg.startswith("-L")\
                  and not flg.startswith("-R")\
                  and not upper_flg.startswith("/LIBPATH")\
                  and not upper_flg.startswith("-LIBPATH")\
                  and not flg.endswith(".lib"):
               other_list.append(flg)
      return Utils.stripDupInList(other_list)
   libsOnlyOtherLinkerFlags = staticmethod(libsOnlyOtherLinkerFlags) 

   def libDirsOnlyLinkerFlags(flag_list):
     # List is constructed as such ("-L /path", "-L/sfad", "-fpge", "-l pg", "-lpg")
      # We do slightly dumb stripping though
      dir_list = []
      vc_linker_flag_re = _re.compile(r'^[/-]libpath:', _re.I)
      other_linker_flag_re = _re.compile(r'^-[LRF]')
      for flg in flag_list:
         flg = flg.strip()
         if len(flg) > 0:
            if (other_linker_flag_re.match(flg) is not None or
                vc_linker_flag_re.match(flg) is not None):
               dir_list.append(flg)
      return Utils.stripDupInList(dir_list)
   libDirsOnlyLinkerFlags = staticmethod(libDirsOnlyLinkerFlags) 

   def printList(gen_list):
      list_string = ""
      for item in gen_list:
         if len(item) > 0:
            list_string += str(item)
            list_string += " "
      print list_string.strip()
   printList = staticmethod(printList)

   def parsePcFile(filename):
      lines = open(filename).readlines()
      fvars = {}
      fvars["FlagpollFilename"] = filename
      lokals = {}
      lokals["fp_file_cwd"] = str(filename[:- ((len(os.path.basename(filename) ) + 1))])
      lines.append("prefix: ${prefix}")
      append_next = False
      append_dict = {}
      append_key = ""
      for line in lines:
       line = line.strip()
       if not line:
         continue
       if append_next:
         tmp = append_dict[append_key]
         tmp = tmp[:-1] + " " + line
         append_dict[append_key] = tmp
         if tmp[-1:] == "\\":
            append_next = True
         else:
            append_next = False
         continue
       if line.startswith("#"):
         continue
       eq_pos = line.find('=')
       col_pos = line.find(':')
       if eq_pos < col_pos and eq_pos != -1 or col_pos == -1 and eq_pos != -1: # local variable
         name, val = line.split('=', 1)
         name = name.strip()
         val = val.strip()
         if '$' in val:
           try:
             val = Template(val).safe_substitute(lokals)
           except ValueError:
            flagDBG().out(flagDBG.WARN, "PkgInfo.parse", "%s has an invalid metadata file" % filename)
         lokals[name] = val
         if val[-1:] == "\\":
            append_next = True
            append_dict = lokals
            append_key = name
         continue
       if col_pos < eq_pos and col_pos != -1 or eq_pos == -1 and col_pos != -1: # exported variable
         name, val = line.split(':', 1)
         name = name.strip()
         val = val.strip()
         if '$' in val:
           try:
             val = Template(val).safe_substitute(lokals)
           except ValueError:
            flagDBG().out(flagDBG.ERROR, "PkgInfo.parse", "%s has an invalid metadata file" % filename)
         fvars[name] = val
         if val[-1:] == "\\":
            append_next = True
            append_dict = fvars
            append_key = name
      return fvars
   parsePcFile = staticmethod(parsePcFile)


class flagDBG(object):
#      Logging class is really easy to use
#      Levels:
#      0 - VERBOSE
#      1 - INFO
#      2 - WARN
#      3 - ERROR

   VERBOSE = 0
   INFO    = 1
   WARN    = 2
   ERROR   = 3

   __initialized = False

   def __new__(type):
      if not '_the_instance' in type.__dict__:
         type._the_instance = object.__new__(type)
      return type._the_instance

   def __init__(self):
      if flagDBG.__initialized:
         return
      flagDBG.__initialized = True
      self.mLevel = self.WARN
      self.mLevelList = ["VERBOSE", "INFO", "WARN", "ERROR"]

   def setLevel(self, level):
      if level <= 3:
         self.mLevel = level

   def out(self, level, obj, message):
      if level == self.ERROR:
         if self.mLevel == self.VERBOSE:
            print self.mLevelList[level] + ":" + str(obj) + ": " + str(message)
         else:
            sys.stderr.write(self.mLevelList[level] + ": " + str(message) + "\n")
         errs = Utils.getPossibleErrors()
         if len(errs) > 0:
            sys.stderr.write("Possible culprits:\n")
            for pos_err in errs:
               sys.stderr.write( "   " + str(pos_err) + "\n" )
         sys.exit(1)
      if level >= self.mLevel: 
         print self.mLevelList[level] + ":" + str(obj) + ": " + str(message)


class DepResolutionSystem(object):
   """ You add PkgAgents with Filters into system and call resolve()
       you can check for succes by depsSatisfied() and get the list
       of packages that work back by calling getPackages()
   """

   __initialized = False

   def __new__(type):
      if not '_the_instance' in type.__dict__:
         type._the_instance = object.__new__(type)
      return type._the_instance

   def __init__(self):
      if DepResolutionSystem.__initialized:
         return
      DepResolutionSystem.__initialized = True
      self.mResolveAgents = []
      self.mAgentDict = {}
      self.mFilters = []
      self.mResolveAgentFilters = []
      self.mInvalidPackageList = []
      self.mSatisfied = False
      self.mAgentChangeList = [] # list of those responsible for adding Filters
                                 # to agents in higher in the chain than them
                                 # these are the first agents to ask that they pick
                                 # the next best package of them
      self.mAgentsVisitedList = [] # list of Agents that have been visited
      self.mResolvedPackageList = [] # list that is generated when deps are satified
      self.mCheckPrivateRequires = False
      self.mMadeDependList = False

   def getFilters(self):
      return self.mFilters

   def addFilter(self, filter):
      self.mFilters.append(filter)

   def addResolveAgentFilter(self, filter):
      self.mResolveAgentFilters.append(filter)

   def makeRequireFilter(self, requires):
      arch_list = []
      arch_list.append("no_arch")
      arch_list.append("")
      if platform.system() == "Linux":
         if requires == "64":
            arch_list.append("x86_64")
            arch_list.append("x64")
         if requires == "32":
            arch_list.append("i386")
            arch_list.append("i486")
            arch_list.append("i586")
            arch_list.append("i686")
      if platform.system() == "Darwin":
         if requires == "64":
            arch_list.append("ppc64")
         if requires == "32":
            arch_list.append("ppc")
      if platform.system().startswith("IRIX"):
         arch_list.append(requires)

      new_filter = Filter("Arch", lambda x,v=arch_list: x in v)
      self.addFilter(new_filter)


   def createAgent(self, name):
      if self.checkAgentExists(name):
         return self.getAgent(name)
      else:
         agent = PkgAgent(name)
         flagDBG().out(flagDBG.INFO, "DepResSys.createAgent", "Adding %s" % name)
         self.addNewAgent(agent)
         return agent

   def createResolveAgent(self, name):
      if self.checkAgentExists(name):
         return self.getAgent(name)
      else:
         agent = PkgAgent(name)
         flagDBG().out(flagDBG.INFO, "DepResSys.createResolveAgent", "Adding %s" % name)
         self.addResolveAgent(agent)
         return agent

   def setPrivateRequires( self, req ):
      self.mCheckPrivateRequires = req

   def checkPrivateRequires(self):
      return self.mCheckPrivateRequires

   def checkAgentExists(self, name):
      return self.mAgentDict.has_key(name)

   def getAgent(self, name):
      if self.mAgentDict.has_key(name):
         return self.mAgentDict[name]
      else:
         flagDBG().out(flagDBG.ERROR, "DepResSys.getAgent", "Agent %s does not exist" % name)

   def addNewAgent(self, agent):
      if not self.checkAgentExists(agent):
         self.mAgentDict[agent.getName()] = agent

   def addResolveAgent(self, agent):
      if agent not in self.mResolveAgents:
         self.mResolveAgents.append(agent)
         for filt in self.mResolveAgentFilters:
            agent.addFilter(filt)

   def isSatisfied(self):
      return self.mSatisfied

   def updateResolvedPackages(self):
      pkg_list = []
      agent_list = []
      for agent in self.mResolveAgents:
         list_of_pkgs = agent.getCurrentPackageList(agent_list)
         pkg_list.extend(list_of_pkgs)
      self.mResolvedPackageList = pkg_list

   def getPackages(self):
      flagDBG().out(flagDBG.INFO, "DepResSys.getPackages",
                    "Generating list of valid packages ")
                    #str([pkg.getCurrentPackage.getName() for pkg in self.mResolvedPackageList]))
      # If this comes back empty then there isn't a valid set of packages to use
      self.updateResolvedPackages()
      return self.mResolvedPackageList

   def checkFiltersChanged(self):
      list_to_use = []
      return True in [pkg.filtersChanged(list_to_use) for pkg in self.mResolveAgents] 

   def resolveHelper(self):
      self.resolveAgentsChanged = True
      while self.resolveAgentsChanged:
         for agent in self.mResolveAgents:
            flagDBG().out(flagDBG.INFO, "DepResSys.resolveHelper",
                          "Updating " + agent.getName())
            agent.update(self.mAgentsVisitedList, self.mAgentChangeList)
         self.resolveAgentsChanged = self.checkFiltersChanged()

   # Ask mResolveAgents if they are done(they ask sub people) unless they are
   # really above you in the walk
   # If there were changes...run update on mResolveAgents again
   # at the end ask for pkglist..if it comes back empty then we don't
   # have a usable configuration for those packages
   def resolveDeps(self):
      self.resolveHelper()
      if not self.mMadeDependList:
         for agent in self.mResolveAgents:
            agent.makeDependList()
         self.mMadeDependList = True

      self.updateResolvedPackages()
      # Check if the first round through we found something
      # Otherwise we need to start removing packages that aren't viable
      # during the loop
      self.mSatisfied = (len(self.mResolvedPackageList) != 0)

      # remove top of packages that added Filters.
      # then move on to resolving again
      # remove more if neccesary
      agentChangeNumber = 0
      while not self.mResolvedPackageList:
         if(self.mAgentChangeList[agentChangeNumber]):
            if self.mAgentChangeList[agentChangeNumber] in self.mInvalidPackageList:
               agentChangeNumber += 1
            else:
               if(self.mAgentChangeList[agentChangeNumber].getViablePackages()):
                  flagDBG().out(flagDBG.INFO, "DepResSys.resolveDeps",
                                "Removing bad pkg from " +
                                self.mAgentChangeList[agentChangeNumber].getName())
                  self.mInvalidPackageList.append(self.mAgentChangeList[agentChangeNumber].removeCurrentPackage())
               else:
                  flagDBG().out(flagDBG.INFO, "DepResSys.resolveDeps",
                                "No combinations.. Resetting " +
                                self.mAgentChangeList[agentChangeNumber].getName())
                  self.mAgentChangeList[agentChangeNumber].reset()
                  agentChangeNumber += 1
         self.resolveHelper() 
         self.updateResolvedPackages()
         self.mSatisfied = (len(self.mResolvedPackageList) != 0)      
      return

class PkgAgent:
   """ Agent that keeps track of the versions for a package given some filters
       and the addition of Filters
   """

   #   Makes a PkgAgent that finds its current package with the version reqs
   def __init__(self, name):
      if DepResolutionSystem().checkAgentExists(name):
         flagDBG().out(flagDBG.ERROR, "PkgAgent", "Package Agent %s already exists" % self.mName)

      self.mName = name
      flagDBG().out(flagDBG.INFO, "PkgAgent", "Creating:" + str(self.mName))

      if not PkgDB().exists(name):
         flagDBG().out(flagDBG.ERROR, "PkgAgent", "No info for package: %s" % self.mName)

      #XXX Doesn't keep filters added for a redo...
      DepResolutionSystem().addNewAgent(self)
      self.mFilterList = []
      self.mFilterList.extend(DepResolutionSystem().getFilters())
      self.mBasePackageList = PkgDB().getPkgInfos(name)
      self.mBasePackageList.sort(lambda lhs, rhs: cmp(rhs.getVariable('Version'),
                                            lhs.getVariable('Version')))
      for filt in self.mFilterList:
         self.mBasePackageList = filt.filter(self.mBasePackageList)

      if len(self.mBasePackageList) == 0:
         flagDBG().out(flagDBG.ERROR, "PkgAgent", "No viable packages for: %s" % self.mName)

      self.mViablePackageList = copy.deepcopy(self.mBasePackageList)
      if self.mViablePackageList:
         self.mCurrentPackage = self.mViablePackageList[0]
      else:
         self.mCurrentPackage = []
      self.mFiltersChanged = True
      self.mCheckDepends = True
      self.mAgentDependList = []


   def getName(self):
      return self.mName

   def setCheckDepends(self, tf):
      self.mCheckDepends = tf

   #Filter("Version", lambda x: x == "4.5")
   def makeDependList(self):
      if not self.mCheckDepends:
         self.mAgentDependList = []
         return 

      dep_list = []
      if self.mCurrentPackage:
         req_string = self.mCurrentPackage.getVariable("Requires")
         if DepResolutionSystem().checkPrivateRequires():
            req_string = req_string + " " + self.mCurrentPackage.getVariable("Requires.private")
         req_string = req_string.strip()
         if req_string == "":
            return
         req_string = req_string.replace(',', ' ')
         req_string = req_string.replace('(', ' ')
         req_string = req_string.replace(')', ' ')
         space_req_string_list = req_string.split(' ')
         req_string_list = []
         for entry in space_req_string_list:
            if len(entry) > 0:
               req_string_list.append(entry)
         i = 0
         flagDBG().out(flagDBG.INFO, "PkgAgent.makeDependList",
                       self.mCurrentPackage.getName() + " requires: " + str(req_string_list))
         while len(req_string_list) > i:
            if PkgDB().exists(req_string_list[i]):
               new_filter = []
               new_agent = DepResolutionSystem().createAgent(req_string_list[i])
               if len(req_string_list) > i+1:
                  if req_string_list[i+1] == "=":
                     ver_to_filt = req_string_list[i+2].strip()
                     new_filter = Filter("Version", lambda x,v=ver_to_filt: x == v)
                  elif req_string_list[i+1] == "<=":
                     ver_to_filt = req_string_list[i+2].strip()
                     new_filter = Filter("Version", lambda x,v=ver_to_filt: x <= v)
                  elif req_string_list[i+1] == ">=":
                     ver_to_filt = req_string_list[i+2].strip()
                     new_filter = Filter("Version", lambda x,v=ver_to_filt: x >= v)
                  elif req_string_list[i+1] == ">":
                     ver_to_filt = req_string_list[i+2].strip()
                     new_filter = Filter("Version", lambda x,v=ver_to_filt: x > v)
                  elif req_string_list[i+1] == "<":
                     ver_to_filt = req_string_list[i+2].strip()
                     new_filter = Filter("Version", lambda x,v=ver_to_filt: x < v)
                  else:
                     i += 1
               else:
                  i += 1
               dep_list.append(new_agent)
               if new_filter:
                  i+=3
                  new_agent.addFilter(new_filter)
            else:
               flagDBG().out(flagDBG.ERROR, "PkgAgent.makeDependList",
                             "Package %s depends on %s but does not seem to be installed." % (self.mName, str(req_string_list[i])))

         flagDBG().out(flagDBG.INFO, "PkgAgent.makeDependList", 
                       str(self.mName) + " : List is:" + str([pkg.getName() for pkg in dep_list]))
      self.mAgentDependList = dep_list

   def filtersChanged(self, packageList):
      tf_list = [self.mFiltersChanged]
      if self.mName not in packageList:
         packageList.append(self.mName)
         for pkg in self.mAgentDependList:
            tf_list.append(pkg.filtersChanged(packageList))
      return True in tf_list

   def getCurrentPackageList(self, packageList):
      pkgs = []
      if self.mName not in packageList:
         flagDBG().out(flagDBG.INFO, "PkgAgent.getCurrentPackageList",
                       "Package: %s" % self.mName)
         pkgs.append(self.mCurrentPackage)
         packageList.append(self.mName)
         # Make depend list if we are supposed to
         self.updateFilters()
         self.makeDependList()

         for pkg in self.mAgentDependList:
            pkgs.extend(pkg.getCurrentPackageList(packageList))
      return pkgs

   # current pkginfo for me
   def getCurrentPkgInfo(self):
      return self.mCurrentPackage

   # Someone else usually places these on me
   # I keep track of those separately
   def addFilter(self, filter):
      flagDBG().out(flagDBG.INFO, "PkgAgent.addFilter",
                    "Filter %s" % self.mName + " on %s." % filter.getVarName())
      self.mFiltersChanged = True
      self.mFilterList.append(filter)

   def removeCurrentPackage(self):
      flagDBG().out(flagDBG.INFO, "PkgAgent.removeCurrentPackage",
                    "Removing current package of %s" % self.mName)
      if self.mViablePackageList:
         ret_val = self.mViablePackageList[0] in self.mBasePackageList
         del self.mViablePackageList[0]
         if self.mViablePackageList:
            self.mCurrentPackage = self.mViablePackageList[0]
         return ret_val

   def update(self, agentVisitedList, agentChangeList):
      flagDBG().out(flagDBG.INFO, "PkgAgent.update",
                    "%s" % self.mName)
      if self.mName in agentVisitedList:
         return

      agentVisitedList.append(self.mName)

      if self.mFiltersChanged:
         self.mFiltersChanged = False
         self.updateFilters()

      # TODO: checkFilters and add them
      # if a pkg is in visitedList then add yourself to agentChangeList

      for pkg in self.mAgentDependList:
         pkg.update(agentVisitedList, agentChangeList)
      return

   def updateFilters(self):
      for filt in self.mFilterList:
         self.mViablePackageList = filt.filter(self.mViablePackageList)
      flagDBG().out(flagDBG.INFO, "PkgAgent.updateFilters",
                     "%s has " % self.mName + str(len(self.mViablePackageList)) + " left")
      if len(self.mViablePackageList) > 0:
         self.mCurrentPackage = self.mViablePackageList[0]
      else:
         for f in self.mFilterList:
            Utils.addPossibleError(str(self.mName) + " -> " + str(f.getVarName()))
         self.mCurrentPackage = []


   def reset(self):
      flagDBG().out(flagDBG.INFO, "PkgAgent.reset", "Resetting package: %s" % self.mName)
      self.mViablePackageList = self.mBasePackageList
      if self.mViablePackageList:
         self.mCurrentPacakge = self.mViablePackageList[0]
      else:
         self.mCurrentPackage = []
      self.mFilterList = DepResolutionSystem().getFilters()
      self.mAgentDependList = []
      self.mFiltersChanged = True
      return

class Filter:
   """ A single Filter that knows how to filter the list it recieves
       Will be inheireted....?..?
   """

   def __init__(self, variableName, testCallable):
      self.mVarName = variableName
      self.mTestCallable = testCallable

   def getVarName(self):
      return self.mVarName

   def filter(self, pkg_info_list):
      ret_pkg_list = []
      # Filter first
      for pkg in pkg_info_list:
         var = pkg.getVariable(self.mVarName)
         if self.mTestCallable(var):
            flagDBG().out(flagDBG.INFO, "Filter", "Passed: %s:" % pkg.getName() +
                     " %s is " % self.mVarName + str(var))
            ret_pkg_list.append(pkg)
         else:
            flagDBG().out(flagDBG.INFO, "Filter", "Failed: %s:" % pkg.getName() +
                     " %s is " % self.mVarName + str(var))

      # Now sort
      ret_pkg_list.sort(lambda lhs, rhs: cmp(rhs.getVariable(self.mVarName),
                                            lhs.getVariable(self.mVarName)))

      return ret_pkg_list

#requires: qt == 4.5

#Filter("Version", lambda x: x <= "4.5")
#Filter("Version", lambda x: x <= "4.5")

class PkgDB(object):
   """ Holds all the neccesary information to evaluate itself when needed.
       Is in charge of holding a list of PkgInfo's that contain info
       about the package.
   """

   __initialized = False

   def __init__(self):
      if self.__initialized:
         return
      self.__initialized = True
      self.mPkgInfos = {}           # {pkg_name: List of package infos}
      self.populatePkgInfoDBPcFiles()
      self.populatePkgInfoDBFpcFiles()

   def __new__(type):
      if not '_the_instance' in type.__dict__:
         type._the_instance = object.__new__(type)
      return type._the_instance

   def printAllPackages(self):
      list_of_package_names = []
      for pkg in self.mPkgInfos:
         list_of_package_names.append(pkg)
      for name in list_of_package_names:
         print name
      sys.exit(0)

   def getVariables(self, name, variable_list):
      flagDBG().out(flagDBG.INFO, "PkgDB.getVariable", 
                    "Finding " + str(variable_list) + " in " + str(name))
      ret_list = []
      for var in variable_list:
         if self.mPkgInfos.has_key(name):
            ret_list.extend(self.mPkgInfos[name][0].getVariable(var))
         else:
            flagDBG().out(flagDBG.ERROR, "PkgDB.getVariable", "Package %s not found." % name)
      return ret_list

   def checkPackage(self, name):
      flagDBG().out(flagDBG.INFO, "PkgDB.checkPackage", 
                    "Finding " + str(name))
      if self.mPkgInfos.has_key(name):
         return True
      else:
         flagDBG().out(flagDBG.ERROR, "PkgDB.CheckPackage", "Package %s not found." % name)

      return False

   def getVariablesAndDeps(self, pkg_list, variable_list):
      flagDBG().out(flagDBG.INFO, "PkgDB.getVariablesAndDeps", 
                    "Finding " + str(variable_list) + " in " + str(pkg_list))

      if DepResolutionSystem().checkPrivateRequires():
         temp_var_list = []
         for var in variable_list:
            temp_var_list.append(var.join(".private"))
         variable_list = variable_list + temp_var_list

      for name in pkg_list:
         if self.mPkgInfos.has_key(name):
            agent = DepResolutionSystem().createAgent(name)
            DepResolutionSystem().addResolveAgent(agent)
         else:
            flagDBG().out(flagDBG.ERROR, "PkgDB.getVariablesAndDeps", "Package %s not found." % name)

      DepResolutionSystem().resolveDeps()
      pkgs = DepResolutionSystem().getPackages()
      var_list = []
      for pkg in pkgs:
         if pkg:
            for var in variable_list:
               var_list.extend(tokenize(pkg.getVariable(var), pkg.getSplitChar()))

      return var_list

   def getPkgInfos(self, name):
      if self.mPkgInfos.has_key(name):
         return self.mPkgInfos[name]
      else:
         flagDBG().out(flagDBG.ERROR, "PkgDB.getPkgInfos", "Package %s not found." % name)

   def exists(self, name):
      return self.mPkgInfos.has_key(name)

   def getInfo(self, name):
      if self.mPkgInfos.has_key(name):
         return [pkg.getInfo() for pkg in self.mPkgInfos[name]]
      else:
         flagDBG().out(flagDBG.ERROR, "PkgDB.getInfo", "Package %s not found." % name)

   def buildPcFileDict(self):
      """ Builds up a dictionary of {name: list of files for name} """
      pc_dict = {}
      for p in Utils.getPathList():
         glob_list = glob.glob(os.path.join(p, "*.pc")) # List of .pc files in that directory
         flagDBG().out(flagDBG.VERBOSE, "PkgDB.buildPcFileDict",
                       "Process these pc files: %s" % str(glob_list))
         for g in glob_list: # Get key name and add file to value list in dictionary
            key = os.path.basename(g)[:-3]   # Strip .pc off the filename...rstrip no worky
            pc_dict.setdefault(key, []).append(g)
      for f in Utils.getFileList():
         if f.endswith(".pc"):
            key = os.path.basename(f)[:-3]   # Strip .pc off the filename...rstrip no worky
            pc_dict.setdefault(key, []).append(f)
      return pc_dict # { "key", [ "list", "of", "corresponding", "pc", "files"] }

   def populatePkgInfoDBPcFiles(self):
      dict_to_pop_from = self.buildPcFileDict()
      for (pkg,files) in dict_to_pop_from.iteritems():
         for f in files:
            self.mPkgInfos.setdefault(pkg,[]).append(PkgInfo(pkg, f, "pc"))

   def buildFpcFileList(self):
      """ Builds up a dictionary of {name: list of files for name} """
      file_list = []
      for p in Utils.getPathList():
         glob_list = glob.glob(os.path.join(p, "*.fpc")) # List of .fpc files in that directory
         flagDBG().out(flagDBG.VERBOSE, "PkgDB.buildPcFileDict",
                       "Process these fpc files: %s" % str(glob_list))
         for g in glob_list:
            file_list.append(g)
      for f in Utils.getFileList():
         if f.endswith(".fpc"):
            file_list.append(f)
      return file_list # [ "list", "of", "fpc", "files"] 

   def populatePkgInfoDBFpcFiles(self):
      list_to_add = self.buildFpcFileList()
      for filename in list_to_add:
         var_dict = Utils.parsePcFile(filename)
         if not var_dict.has_key("Provides"):
            flagDBG().out(flagDBG.WARN, "PkgDB.populate", "%s missing Provides" % str(filename))
            continue
         if not var_dict.has_key("Arch"):
            flagDBG().out(flagDBG.WARN, "PkgDB.populate", "%s missing Arch" % str(filename))
            continue
         provides_string = var_dict["Provides"]
         provides_string = provides_string.replace(',', ' ')
         provides_string = provides_string.replace('(', ' ')
         provides_string = provides_string.replace(')', ' ')
         for key in provides_string.split(" "):
            if len(key) > 0:
               self.mPkgInfos.setdefault(key, []).append(PkgInfo(key, filename, "fpc", var_dict))

class PkgInfo:
   """ Holds the information for a package file on the system. These however
       are evaluated when the need arises.
   """

   def __init__(self, name, file, type, varDict={}):
      self.mName = name
      self.mFile = file
      self.mIsEvaluated = False
      self.mType = type
      self.mVariableDict = varDict
      if self.mType == "fpc":
         self.mIsEvaluated = True

   def getVariable(self, variable):
      self.evaluate()
      return self.mVariableDict.get(variable,"")      

   def getName(self):
      return self.mName

   def evaluate(self):
      if not self.mIsEvaluated:
         flagDBG().out(flagDBG.INFO, "PkgInfo.evaluate", "Evaluating %s" % self.mName)
         self.mVariableDict= Utils.parsePcFile(self.mFile)
         self.mIsEvaluated = True

   def getInfo(self):
      self.evaluate()
      return self.mVariableDict

   def getSplitChar(self):
      self.evaluate()
      split_char = self.mVariableDict.get("SplitCharacter", ' ')
      if split_char == "":
         split_char = ' '
      return split_char

class OptionsEvaluator:

   def __init__(self):
      if len(sys.argv) < 2:
         self.printHelp()

   # This is only the args....(first arg is stripped)
   def evaluateArgs(self, args):

      # Catch any args that need to be tended to immediately

      # Process args in the order that they were recieved.  Some options are not
      # shown below because they are sub-options of an option below (--strip-dups)

      for option in args:
         if option.startswith("--verbose-debug"):
            flagDBG().setLevel(flagDBG.VERBOSE)
         if option.startswith("--debug"):
            flagDBG().setLevel(flagDBG.INFO)
         elif option.startswith("--from-file"):
            val_start = option.find('=') + 1
            file = option[val_start:]
            Utils.EXTRA_FLAGPOLL_FILES = Utils.EXTRA_FLAGPOLL_FILES + ":" + file
         elif option.startswith("--help"):
            self.printHelp()
            sys.exit(0)
         elif option.startswith("--version"):
            print "%s.%s.%s" % Utils.getFlagpollVersion()
            sys.exit(0)
         elif option.startswith("--list-all"):
            PkgDB().printAllPackages()
            continue


      listedAgentNames = []
      agent = None
      num_args = len(args)
      curr_arg = 0
      output_options = [] # (option, name/"")
      important_options = [] # ones that need special attention
      while curr_arg < num_args:

         if args[curr_arg].startswith("--atleast-version"):
            val_start = args[curr_arg].find('=') + 1
            atleast_version = args[curr_arg][val_start:]
            atleast_filter = Filter("Version", lambda x,v=atleast_version: x >= v)
            curr_arg = curr_arg + 1
            if agent is not None:
               agent.addFilter(atleast_filter)
            else:
               flagDBG().out(flagDBG.ERROR, "OptionsEvaluator", "No package specified for option:  --at-least-version")
            continue

         if args[curr_arg].startswith("--cflags-only-I"):
            output_options.append(("cflags-only-I", ""))
            curr_arg = curr_arg + 1
            continue

         if args[curr_arg].startswith("--cflags-only-other"):
            output_options.append(("cflags-only-other", ""))
            curr_arg = curr_arg + 1
            continue

         #This could be the catch all for the above ones to and then filter based on what is asked...
         #For clarity of course...
         if args[curr_arg].startswith("--cflags"):
            output_options.append(("cflags", ""))
            curr_arg = curr_arg + 1
            continue

         if args[curr_arg].startswith("--extra-paths"):
            val_start = args[curr_arg].find('=') + 1
            paths = args[curr_arg][val_start:]
            Utils.EXTRA_FLAGPOLL_SEARCH_PATHS = Utils.EXTRA_FLAGPOLL_SEARCH_PATHS + ":" + paths
            curr_arg = curr_arg + 1
            continue

         if args[curr_arg].startswith("--exact-version"):
            val_start = args[curr_arg].find('=') + 1
            exact_version = args[curr_arg][val_start:]
            exact_filter = Filter("Version", lambda x,v=exact_version: x == v)
            curr_arg = curr_arg + 1
            if agent is not None:
               agent.addFilter(exact_filter)
            else:
               flagDBG().out(flagDBG.ERROR, "OptionsEvaluator", "No package specified for option:  --exact-version")
            continue

         if args[curr_arg].startswith("--exists"):
            important_options.append("exists")
            curr_arg = curr_arg + 1
            continue

         if args[curr_arg].startswith("--from-file"):
            val_start = args[curr_arg].find('=') + 1
            file = args[curr_arg][val_start:]
            file_filter = Filter("FlagpollFilename", lambda x,f=file: x == f)
            curr_arg = curr_arg + 1
            if agent is not None:
               agent.addFilter(file_filter)
            else:
               flagDBG().out(flagDBG.ERROR, "OptionsEvaluator", "No package specified for option:  --from-file")
            continue

         if args[curr_arg].startswith("--info"):
            important_options.append("info")
            curr_arg = curr_arg + 1
            continue

         if args[curr_arg].startswith("--print-pkg-options"):
            important_options.append("print-pkg-options")
            curr_arg = curr_arg + 1
            continue

         if args[curr_arg].startswith("--keep-dups"):
            Utils.setKeepDups(True)
            curr_arg = curr_arg + 1
            continue

         if args[curr_arg].startswith("--libs-only-l"):
            output_options.append(("libs-only-l", ""))
            curr_arg = curr_arg + 1
            continue

         if args[curr_arg].startswith("--libs-only-L"):
            output_options.append(("libs-only-L", ""))
            curr_arg = curr_arg + 1
            continue

         if args[curr_arg].startswith("--libs-only-other"):
            output_options.append(("libs-only-other", ""))
            curr_arg = curr_arg + 1
            continue

         #This could be the catch all for the above ones to and then filter based on what is asked...
         if args[curr_arg].startswith("--libs"):
            output_options.append(("libs", ""))
            curr_arg = curr_arg + 1
            continue

         if args[curr_arg].startswith("--require-all"):
            rlen = len("--require-all")
            req_string = args[curr_arg][rlen:]
            req_string = req_string.replace('-','_')
            curr_arg = curr_arg + 1
            if req_string.find("<=") != -1:
               op_start = req_string.find("<=")
               req_var = req_string[:op_start]
               req_val = req_string[op_start + 2:]
               new_filter = Filter(req_var, lambda x,v=req_val: x <= v)
               DepResolutionSystem().addFilter(new_filter)
               continue
            if req_string.find(">=") != -1:
               op_start = req_string.find(">=")
               req_var = req_string[:op_start]
               req_val = req_string[op_start + 2:]
               new_filter = Filter(req_var, lambda x,v=req_val: x >= v)
               DepResolutionSystem().addFilter(new_filter)
               continue
            if req_string.find(">") != -1:
               op_start = req_string.find(">")
               req_var = req_string[:op_start]
               req_val = req_string[op_start + 1:]
               new_filter = Filter(req_var, lambda x,v=req_val: x > v)
               DepResolutionSystem().addFilter(new_filter)
               continue
            if req_string.find("<") != -1:
               op_start = req_string.find("<")
               req_var = req_string[:op_start]
               req_val = req_string[op_start + 1:]
               new_filter = Filter(req_var, lambda x,v=req_val: x < v)
               DepResolutionSystem().addFilter(new_filter)
               continue
            if req_string.find("=") != -1:
               op_start = req_string.find("=")
               req_var = req_string[:op_start]
               req_val = req_string[op_start + 1:]
               new_filter = Filter(req_var, lambda x,v=req_val: x == v)
               DepResolutionSystem().addFilter(new_filter)
               continue
            if req_string.find("=") == -1:
               new_filter = Filter(req_string, lambda x: len(x) > 0)
               DepResolutionSystem().addFilter(new_filter)
               continue

            continue

         if args[curr_arg].startswith("--require-"):
            rlen = len("--require-")
            req_string = args[curr_arg][rlen:]
            req_string = req_string.replace('-','_')
            curr_arg = curr_arg + 1
            if agent is None:
               flagDBG().out(flagDBG.ERROR, "OptionsEvaluator", "No package specified for option:  --require")
            if req_string.find("<=") != -1:
               op_start = req_string.find("<=")
               req_var = req_string[:op_start]
               req_val = req_string[op_start + 2:]
               new_filter = Filter(req_var, lambda x,v=req_val: x <= v)
               agent.addFilter(new_filter)
               continue
            if req_string.find(">=") != -1:
               op_start = req_string.find(">=")
               req_var = req_string[:op_start]
               req_val = req_string[op_start + 2:]
               new_filter = Filter(req_var, lambda x,v=req_val: x >= v)
               agent.addFilter(new_filter)
               continue
            if req_string.find(">") != -1:
               op_start = req_string.find(">")
               req_var = req_string[:op_start]
               req_val = req_string[op_start + 1:]
               new_filter = Filter(req_var, lambda x,v=req_val: x > v)
               agent.addFilter(new_filter)
               continue
            if req_string.find("<") != -1:
               op_start = req_string.find("<")
               req_var = req_string[:op_start]
               req_val = req_string[op_start + 1:]
               new_filter = Filter(req_var, lambda x,v=req_val: x < v)
               agent.addFilter(new_filter)
               continue
            if req_string.find("=") != -1:
               op_start = req_string.find("=")
               req_var = req_string[:op_start]
               req_val = req_string[op_start + 1:]
               new_filter = Filter(req_var, lambda x,v=req_val: x == v)
               agent.addFilter(new_filter)
               continue
            if req_string.find("=") == -1:
               new_filter = Filter(req_string, lambda x: len(x) > 0)
               agent.addFilter(new_filter)
               continue

            continue

         if args[curr_arg].startswith("--no-deps"):
            if agent is not None:
               agent.setCheckDepends(False)
            else:
               flagDBG().out(flagDBG.ERROR, "OptionsEvaluator", "No package specified for option:  --no-deps")
            curr_arg = curr_arg + 1
            continue

         if args[curr_arg].startswith("--require="):
            val_start = args[curr_arg].find('=') + 1
            req_var = args[curr_arg][val_start:]
            curr_arg = curr_arg + 1
            DepResolutionSystem().makeRequireFilter(req_var)
            continue

         if args[curr_arg].startswith("--modversion"):
            if agent is not None:
               output_options.append(("Version", agent.getName()))
            else:
               flagDBG().out(flagDBG.ERROR, "OptionsEvaluator", "No package specified for option:  --modversion")
            curr_arg = curr_arg + 1
            continue

         if args[curr_arg].startswith("--max-release"):
            val_start = args[curr_arg].find('=') + 1
            max_release = args[curr_arg][val_start:]
            max_filter = Filter("Version", lambda x,v=max_release: x.startswith(v))
            curr_arg = curr_arg + 1
            if agent is not None:
               agent.addFilter(max_filter)
            else:
               flagDBG().out(flagDBG.ERROR, "OptionsEvaluator", "No package specified for option:  --max-release")
            continue

         if args[curr_arg].startswith("--static"):
            DepResolutionSystem().setPrivateRequires(True)
            curr_arg = curr_arg + 1
            continue

         if args[curr_arg].startswith("--variable"):
            val_start = args[curr_arg].find('=') + 1
            fetch_var = args[curr_arg][val_start:]
            if agent is not None:
               output_options.append((fetch_var, agent.getName()))
            else:
               flagDBG().out(flagDBG.ERROR, "OptionsEvaluator", "No package specified for option:  --variable")
            curr_arg = curr_arg + 1
            continue

         if args[curr_arg].startswith("--get-all"):
            rlen = len("--get-all-")
            get_string = args[curr_arg][rlen:]
            get_string = get_string.replace('-','_')
            output_options.append((get_string, ""))
            curr_arg = curr_arg + 1
            continue

         if args[curr_arg].startswith("--get"):
            rlen = len("--get-")
            get_string = args[curr_arg][rlen:]
            get_string = get_string.replace('-','_')
            if agent is not None:
               output_options.append((get_string, agent.getName()))
            else:
               flagDBG().out(flagDBG.ERROR, "OptionsEvaluator", "No package specified for option:  --get")
            curr_arg = curr_arg + 1
            continue

         if args[curr_arg].startswith("--concat"):
            important_options.append("concat")
            curr_arg = curr_arg + 1
            continue

         if not args[curr_arg].startswith("--"):
            name = args[curr_arg]
            curr_arg = curr_arg + 1
            if PkgDB().checkPackage(name):
               agent = DepResolutionSystem().createResolveAgent(name)
               listedAgentNames.append(name)
            else:
               flagDBG().out(flagDBG.ERROR, "OptionsEvaluator", "Invalid Package %s" % name)
               sys.exit(1)
            continue

         if args[curr_arg] != "--debug" and args[curr_arg] != "--verbose-debug":
            flagDBG().out(flagDBG.ERROR, "OptionsEvaluator", "Invalid Option: %s" % args[curr_arg])

         curr_arg = curr_arg + 1

      if len(listedAgentNames) == 0:
         flagDBG().out(flagDBG.ERROR, "OptionsEvaluator",
                        "Must specify at least one package")

      DepResolutionSystem().resolveDeps()
      pkgs = DepResolutionSystem().getPackages()
      for p in pkgs:
         if not p:
            for option in important_options:
               if option == "exists":
                  print "no"
                  sys.exit(1)
            flagDBG().out(flagDBG.ERROR, "OptionsEvaluator",
                           "No valid configurations found")

      if len(pkgs) == 0:
         for option in important_options:
            if option == "exists":
               print "no"
               sys.exit(1)
         flagDBG().out(flagDBG.ERROR, "OptionsEvaluator",
                        "No valid configurations found")
         sys.exit(1)

      concat = False
      for option in important_options:
         if option == "exists":
            print "yes"
            return sys.exit(0) # already checked for none above
         if option == "concat":
            concat = True
         if option == "info":
            pkg_info_list = []
            for pkg in pkgs:
               if pkg.getName() in listedAgentNames:
                  print pkg.getName()
                  pkg_info = pkg.getInfo()
                  for key in pkg_info.keys():
                     print " %s : %s" % (key, pkg_info[key])
         if option == "print-pkg-options":
            print ""
            print "All options below would start with (--get-/--get-all-/--require-/--require-all-)"
            print "NOTE: this list does not include the standard variables that are exported."
            print ""
            for pkg in pkgs:
               if pkg.getName() not in listedAgentNames:
                  continue
               print "Package: " + pkg.getName()
               for key in pkg.getInfo().keys():
                  if key not in [ "Name", "Description", "URL", "Version", 
                                 "Requires", "Requires.private", "Conflicts", 
                                 "Libs", "Libs.private", "Cflags", "Provides",
                                 "Arch", "FlagpollFilename", "prefix" ]:
                     print "   " +  key.replace('_','-')

      #print output_options
      #output_options = []
      #output_single = []
      #Process options here..............
      concat_list = []
      if concat:
         print_option = lambda x: concat_list.extend(x)
      else:
         print_option = lambda x: Utils.printList(x)

      for option in output_options:
         if option[0] == "cflags":
            print_option(Utils.stripDupIncludeFlags(self.getVarFromPkgs("Cflags", pkgs, option[1])))
         elif option[0] == "cflags-only-I":
            print_option(Utils.cflagsOnlyDirIncludeFlags(self.getVarFromPkgs("Cflags", pkgs, option[1])))
         elif option[0] == "cflags-only-other":
            print_option(Utils.cflagsOnlyOtherIncludeFlags(self.getVarFromPkgs("Cflags", pkgs, option[1])))
         elif option[0] == "libs-only-l":
            print_option(Utils.libsOnlyLinkerFlags(self.getVarFromPkgs("Libs", pkgs, option[1])))
         elif option[0] == "libs-only-L":
            print_option(Utils.libDirsOnlyLinkerFlags(self.getVarFromPkgs("Libs", pkgs, option[1])))
         elif option[0] == "libs-only-other":
            print_option(Utils.libsOnlyOtherLinkerFlags(self.getVarFromPkgs("Libs", pkgs, option[1])))
         elif option[0] == "libs":
            print_option(Utils.stripDupLinkerFlags(self.getVarFromPkgs("Libs", pkgs, option[1])))
         else:
            print_option(Utils.stripDupInList(self.getVarFromPkgs(option[0], pkgs, option[1])))

      if concat:
         Utils.printList(Utils.stripDupInList(concat_list))


   def getVarFromPkgs(self, var, pkgs, pkg_name=""):
      var_list = []
      ext_vars = []
      if var == "Libs":
         if DepResolutionSystem().checkPrivateRequires:
            ext_vars.append("Libs.private")

      if pkg_name == "":
         for pkg in pkgs:
            if pkg:
               var_list.extend(tokenize(pkg.getVariable(var), pkg.getSplitChar()))
               for extra_var in ext_vars:
                  var_list.extend(tokenize(pkg.getVariable(extra_var), pkg.getSplitChar()))
      else:
         for pkg in pkgs:
            if pkg:
               if pkg.getName() == pkg_name:
                  var_list.extend(tokenize(pkg.getVariable(var), pkg.getSplitChar()))
                  for extra_var in ext_vars:
                     var_list.extend(tokenize(pkg.getVariable(extra_var), pkg.getSplitChar()))
      return var_list

   def printHelp(self):
      print "usage: flagpoll pkg options [pkg options] [generic options]"
      print " "
      print "generic options:"
      print "  --help                show this help message and exit"
      print "  --version             output version of flagpoll"
      print "  --list-all            list all known packages"
      print "  --debug               show debug information"
      print "  --verbose-debug       show verbose debug information"
      print "  --extra-paths=EXTRA_PATHS"
      print "                        extra paths for flagpoll to search for meta-data files"
      print "  --concat              concatenate all output and output as one line"
      print "  --keep-dups           do not strip out all duplicate info from options"
      print "  --info                show information for packages"
      print "  --print-pkg-options   show pkg specific options"
      print " "
      print "options:"
      print "  --modversion          output version for package"
      print "  --require=REQUIRE     adds additional requirements for packages ex. 32/64"
      print "  --libs                output all linker flags"
      print "  --static              output linker flags for static linking"
      print "  --libs-only-l         output -l flags"
      print "  --libs-only-other     output other libs (e.g. -pthread)"
      print "  --libs-only-L         output -L flags"
      print "  --cflags              output all pre-processor and compiler flags"
      print "  --cflags-only-I       output -I flags"
      print "  --cflags-only-other   output cflags not covered by the cflags-only-I option"
      print "  --exists              return 0 if the module(s) exist"
      print "  --from-file=FILE      use the specified FILE for the package"
      #print "  --print-provides      print which packages the package provides"
      #print "  --print-requires      print which packages the package requires"
      print "  --no-deps                do not lookup dependencies for the package"
      print "  --atleast-version=ATLEAST_VERSION"
      print "                        return 0 if the module is at least version"
      print "                        ATLEAST_VERSION"
      print "  --exact-version=EXACT_VERSION"
      print "                        return 0 if the module is exactly version"
      print "                        EXACT_VERSION"
      print "  --max-release=MAX_RELEASE"
      print "                        return 0 if the module has a release that has a"
      print "                        version of MAX_RELEASE and will return the max"
      print "  --variable=VARIABLE   get the value of a variable"
      print " "
      print "dynamic options(replace VAR with a variable you want to get/filter on)"
      print "                NOTE: replace underscores with dashes in VAR"
      print "                NOTE: quotes are needed around <,>,= combinations"
      print "  --get-VAR             get VAR from package"
      print "  --get-all-VAR         get VAR from package and its deps"
      print "  --require-VAR[<,>,=VAL]"
      print "                        require that a var is defined for a package"
      print "                        or optionally use equality operators against VAL"
      print "  --require-all-VAR[<,>,=VAL]"
      print "                        require that a var is defined for all packages"
      print "                        or optionally use equality operators against VAL"
      print ""
      print "extending search path"
      print "  FLAGPOLL_PATH         environment variable that can be set to"
      print "                        extend the search path for .fpc/.pc files"
      print ""
      print ""
      print "Send bug reports and/or feedback to flagpoll-users@vrsource.org"
      sys.exit(0)

def main():
   # GO!
   # Initialize singletons and the start evaluating
   my_dbg = flagDBG()
   my_dep_system = DepResolutionSystem()
   opt_evaluator = OptionsEvaluator()
   opt_evaluator.evaluateArgs(sys.argv[1:])
   sys.exit(0)

if __name__ == "__main__":
   main()
