text: import collections
  import json
  import os
  import re
  import tarfile
  
  import anchore_engine.utils
  from anchore_engine.subsys import logger
  
  from . import utils
  
  binary_package_el = {
      "name": None,
      "version": None,
      "location": None,
      "type": "binary",
      "files": [],
      "license": "N/A",
      "origin": "N/A",
      "metadata": json.dumps({}),
  }
  
  
  # this is a direct port of the binary analyzer module to here... this should be refactored
  def catalog_image(allpkgfiles, unpackdir):
      squashtar = os.path.join(unpackdir, "squashed.tar")
  
      # get a listing of all files from either a previous run or the squashtar
      allfiles = {}
      if os.path.exists(unpackdir + "/anchore_allfiles.json"):
          with open(unpackdir + "/anchore_allfiles.json", "r") as FH:
              allfiles = json.loads(FH.read())
      else:
          fmap, allfiles = utils.get_files_from_squashtar(
              os.path.join(unpackdir, "squashed.tar")
          )
          with open(unpackdir + "/anchore_allfiles.json", "w") as OFH:
              OFH.write(json.dumps(allfiles))
  
      # set up ordered dictionary structure for the runtimes and evidence types
      evidence = collections.OrderedDict()
      for runtime in ["python", "go", "busybox"]:
          evidence[runtime] = collections.OrderedDict()
          for etype in ["binary", "devel"]:
              evidence[runtime][etype] = []
  
      # perform a per file routine to evaluate files for gathering binary package version evidence
      with tarfile.open(
          os.path.join(unpackdir, "squashed.tar"), mode="r", format=tarfile.PAX_FORMAT
      ) as tfl:
          alltnames = tfl.getnames()
          alltfiles = {}
          for name in alltnames:
              alltfiles[name] = True
  
          memberhash = utils.get_memberhash(tfl)
          for member in list(memberhash.values()):
              try:
                  _get_python_evidence(tfl, member, memberhash, evidence)
              except Exception as err:
                  logger.exception(
                      "Unexpected exception evaluating file='{}' for python runtime evidence".format(
                          member.name
                      )
                  )
  
              try:
                  _get_golang_evidence(tfl, member, memberhash, evidence)
              except Exception as err:
                  logger.exception(
                      "Unexpected exception evaluating file='{}' for python golang evidence".format(
                          member.name
                      )
                  )
  
              try:
                  _get_busybox_evidence(tfl, member, memberhash, evidence)
              except Exception as err:
                  logger.exception(
                      "Unexpected exception evaluating file='{}' for python busybox evidence".format(
                          member.name
                      )
                  )
  
      # write evidence back to a results data structure
      resultlist = {}
      version_found_map = {}
      for runtime in evidence.keys():
          for e in evidence[runtime].keys():
              for t in evidence[runtime][e]:
  
                  version = t.get("version")
                  location = t.get("location")
  
                  if location in allpkgfiles:
                      logger.info(
                          "Skipping evidence {}: file is owned by OS package".format(
                              repr(location)
                          )
                      )
                  else:
                      key = "{}-{}".format(runtime, version)
                      if key not in version_found_map:
                          result = {}
                          result.update(binary_package_el)
                          result.update(t)
                          result["metadata"] = json.dumps({"evidence_type": e})
                          resultlist[location] = result
                          version_found_map[key] = True
  
      # process hints file ...
      # note: upstream of this has already wiped out the hints file contents if the service
      # doesn't have hints processing enabled.
      hints = utils.get_hintsfile(unpackdir, squashtar)
      for pkg in hints.get("packages", []):
          pkg_type = pkg.get("type", "").lower()
          if pkg_type == "binary":
              pkg_key, el = utils._hints_to_binary(pkg)
              resultlist[pkg_key] = el
  
      # transform output into analyzer-module/service "raw" analyzer json document
      nested_dict = lambda: collections.defaultdict(nested_dict)
      findings = nested_dict()
  
      findings["package_list"]["pkgs.binary"]["base"] = resultlist
  
      return utils.defaultdict_to_dict(findings)
  
  
  def _get_python_evidence(tfl, member, memberhash, evidence):
      global binary_package_el
  
      fullpath = "/{}".format(member.name)
      filename = os.path.basename(fullpath)
  
      el = {}
      el.update(binary_package_el)
  
      patt_bin = re.match(r"^python([0-9]+\.[0-9]+)$", filename)
      patt_lib = re.match(r"^libpython([0-9]+\.[0-9]+).so.*$", filename)
      if (patt_bin or patt_lib) and member.isreg():
          f_vers = ""
          if patt_bin:
              f_vers = patt_bin.group(1)
          elif patt_lib:
              f_vers = patt_lib.group(1)
          with tfl.extractfile(member) as FH:
              for line in FH.readlines():
                  subline = line
                  try:
                      the_re = r".*{}\.([0-9]+[-_a-zA-Z0-9]*).*".format(f_vers)
                      patt = re.match(anchore_engine.utils.ensure_bytes(the_re), subline)
                      if patt and f_vers:
                          b_vers = "{}.{}".format(
                              f_vers, anchore_engine.utils.ensure_str(patt.group(1))
                          )
                          if b_vers.startswith(f_vers):
                              el["name"] = "python"
                              el["version"] = b_vers
                              el["location"] = fullpath
                              evidence["python"]["binary"].append(el)
                              break
                  except Exception as err:
                      raise err
  
      elif filename == "patchlevel.h" and member.isreg():
          with tfl.extractfile(member) as FH:
              for line in FH.readlines():
                  line = line.strip()
                  patt = re.match(
                      b'.*#define +PY_VERSION +r"*([0-9\.\-_a-zA-Z]+)"*', line
                  )
                  if patt:
                      h_vers = anchore_engine.utils.ensure_str(patt.group(1))
                      el["name"] = "python"
                      el["version"] = h_vers
                      el["location"] = fullpath
                      evidence["python"]["devel"].append(el)
                      break
  
  
  def _get_golang_evidence(tfl, member, memberhash, evidence):
      global binary_package_el
  
      fullpath = "/{}".format(member.name)
      filename = os.path.basename(fullpath)
  
      el = {}
      el.update(binary_package_el)
  
      if filename in ["go"] and member.isreg():
          with tfl.extractfile(member) as FH:
              for line in FH.readlines():
                  subline = line
                  try:
                      the_re = r".*go([0-9]+\.[0-9]+(\.[0-9]+|beta[0-9]+|alpha[0-9]+|rc[0-9]+)*).*"
                      patt = re.match(anchore_engine.utils.ensure_bytes(the_re), subline)
                      if patt:
                          vers = anchore_engine.utils.ensure_str(patt.group(1))
                          el["name"] = "go"
                          el["version"] = vers
                          el["location"] = fullpath
                          evidence["go"]["binary"].append(el)
                          break
                  except Exception as err:
                      raise err
      elif filename == "VERSION" and member.isreg():
          with tfl.extractfile(member) as FH:
              for line in FH.readlines():
                  line = line.strip()
                  patt = re.match(
                      b".*go([0-9]+\.[0-9]+(\.[0-9]+|beta[0-9]+|alpha[0-9]+|rc[0-9]+)*).*",
                      line,
                  )
                  if patt:
                      vers = anchore_engine.utils.ensure_str(patt.group(1))
                      final_loc = fullpath
                      if memberhash.get(
                          os.path.join(os.path.dirname(member.name), "bin", "go"), None
                      ):
                          final_loc = os.path.join(
                              "/", os.path.dirname(member.name), "bin", "go"
                          )
  
                      el["name"] = "go"
                      el["version"] = vers
                      el["location"] = final_loc
                      evidence["go"]["devel"].append(el)
                      break
  
  
  def _get_busybox_evidence(tfl, member, memberhash, evidence):
      global binary_package_el
  
      fullpath = "/{}".format(member.name)
      filename = os.path.basename(fullpath)
  
      if filename == "busybox" and (member.isreg() or member.islnk()):
          # Perform any specific checks using prior metadata
          with tfl.extractfile(member) as FH:
              for line in FH.readlines():
                  subline = line
                  try:
                      patt = re.match(
                          anchore_engine.utils.ensure_bytes(
                              r".*BusyBox\s+v([0-9]+\.[0-9]+\.[0-9]+).*"
                          ),
                          subline,
                      )
                      if patt:
                          version = anchore_engine.utils.ensure_str(patt.group(1))
                          el = {}
                          el.update(binary_package_el)
  
                          el["name"] = "busybox"
                          el["version"] = version
                          el["location"] = fullpath
  
                          evidence["busybox"]["binary"].append(el)
                          break
                  except Exception as err:
                      raise err
  