<commit_before>require "fpm/package"
require "backports"
require "fileutils"
require "find"

class FPM::Package::Pacman < FPM::Package

  DIGEST_ALGORITHMS = [
    "md5",
    "sha1",
    "sha224",
    "sha256",
    "sha384",
    "sha512"
  ] unless defined?(DIGEST_ALGORITHMS)

  option "--user", "USER", "Set the user to USER in the %files section. Overrides the user when used with use-file-permissions setting."

  option "--group", "GROUP", "Set the group to GROUP in the %files section. Overrides the group when used with use-file-permissions setting."

  option "--digest", DIGEST_ALGORITHMS.join("|"),
    "Select a digest algorithm. The algorithm 'sha256' is recommended.",
    :default => "sha256" do |value|
    if !DIGEST_ALGORITHMS.include?(value.downcase)
      raise "Unknown digest algorithm '#{value}'. Valid options " \
        "include: #{DIGEST_ALGORITHM_MAP.keys.join(", ")}"
    end
    value.downcase
  end

  option "--changelog", "FILEPATH", "Add changelog from FILEPATH contents" do |file|
    File.read(File.expand_path(file))
  end

  option "--sign", :flag, "Pass --sign to makepkg"

  option "--check-script", "FILE",
    "A script to be sourced when running the check() verification" do |val|
    File.expand_path(val) # Get the full path to the script
  end # --check-script

  private

  # This method is invoked on a package when it has been convertxed to a new
  # package format. The purpose of this method is to do any extra conversion
  # steps, like translating dependency conditions, etc.
  #def converted_from(origin)
    # nothing to do by default. Subclasses may implement this.
    # See the RPM package class for an example.
  #end # def converted_from

  def parse_info(info)
    info_map = {}
    last_match_key = nil
    info.each_line do |line|
      match = /^([[:alpha:]]+(?: [[:alpha:]]+)*) +: +(.*)$/.match(line)
      if not match.nil? and match[2] !~ /^[[:space:]]*[Nn]one[[:space:]]*$/
        info_map[match[1]] = match[2]
        last_match_key = match[1]
      else
        info_map[last_match_key] = info_map[last_match_key] + " " + line.chomp()
      end
    end
    return info_map
  end

  def extract_info(pacman_pkg_path)
    info = safesystemout("/usr/bin/pacman", "-Qpi", pacman_pkg_path)
    fields = parse_info(info)

    self.name = fields["Name"]

    # Parse 'epoch:version-iteration' in the version string
    version_re = /^(?:([0-9]+):)?(.+?)(?:-(.*))?$/
    m = version_re.match(fields["Version"])
    if !m
      raise "Unsupported version string '#{fields["Version"]}'"
    end

    self.epoch, self.version, self.iteration = m.captures

    if fields.has_key?("Packager")
      self.maintainer = fields["Packager"]
    end

    # `vendor' tag simply isn't supported by arch
    self.vendor = nil

    if fields.has_key?("URL")
      self.url = fields["URL"]
    end

    # Groups could include more than one.
    # Speaking of just taking the first entry of the field:
    # A crude thing to do, but I suppose it's better than nothing.
    # -- <NAME>, 3/24/2015
    if fields.has_key?("Groups")
      groups = fields["Groups"].split()
      if groups.length > 0
        self.category = groups[0]
      end
    end

    # Licenses could include more than one.
    # Speaking of just taking the first entry of the field:
    # A crude thing to do, but I suppose it's better than nothing.
    # -- <NAME>, 3/24/2015
    if fields.has_key?("Licenses")
      licenses = fields["Licenses"].split()
      if licenses.length > 0
        self.license = licenses[0]
      end
    end

    self.architecture = fields["Architecture"]

    if fields.has_key?("Depends On")
      self.dependencies = fields["Depends On"].split()
    end

    if fields.has_key?("Provides")
      provides = fields["Provides"]
      self.provides = fields["Provides"].split()
    end

    # TODO: scripts, config_files, directories, attributes (optional for).

    if fields.has_key?("Conflicts With")
      conflicts = fields["Conflicts With"].split()
      if conflics != "None"
        self.conflicts = conflicts
      end
    end

    if fields.has_key?("Replaces")
      replaces = fields["Replaces"].split()
      if replaces != "None"
        self.replaces = replaces
      end
    end

    if fields.has_key?("Description")
      self.description = fields["Description"]
    end
  end
  # Add a new source to this package.
  # The exact behavior depends on the kind of package being managed.
  #
  # For instance:
  #
  # * for FPM::Package::Dir, << expects a path to a directory or files.
  # * for FPM::Package::RPM, << expects a path to an rpm.
  #
  # The idea is that you can keep pumping in new things to a package
  # for later conversion or output.
  #
  # Implementations are expected to put files relevant to the 'input' in the
  # staging_path
  def input(pacman_pkg_path)
    extract_info(pacman_pkg_path)
    extract_files(pacman_pkg_path)
#Optional Deps  : bash-completion: for tab completion
#Conflicts With : None
#Replaces       : None
#Installed Size :   6.18 MiB
#Packager       : Bart
#Build Date     : Tue Dec 30 22:08:56 2014
#Install Date   : Sat Mar 14 04:28:54 2015
#Install Reason : Explicitly installed
#Install Script : Yes
#Validated By   : Signature

  end # def input

  # Output this package to the given path.
  def output(path)
    raise NotImplementedError.new("#{self.class.name} does not yet support " \
                                  "creating #{self.type} packages")
  end # def output

end # class FPM::Package::Pacman<commit_msg>Overhaul pacman's input() to use .PKGINFO. Works now<commit_after># -*- coding: utf-8 -*-
require "fpm/package"
require "backports"
require "fileutils"
require "find"

class FPM::Package::Pacman < FPM::Package

  DIGEST_ALGORITHMS = [
    "md5",
    "sha1",
    "sha224",
    "sha256",
    "sha384",
    "sha512"
  ] unless defined?(DIGEST_ALGORITHMS)

  option "--user", "USER", "Set the user to USER in the %files section. Overrides the user when used with use-file-permissions setting."

  option "--group", "GROUP", "Set the group to GROUP in the %files section. Overrides the group when used with use-file-permissions setting."

  option "--digest", DIGEST_ALGORITHMS.join("|"),
    "Select a digest algorithm. The algorithm 'sha256' is recommended.",
    :default => "sha256" do |value|
    if !DIGEST_ALGORITHMS.include?(value.downcase)
      raise "Unknown digest algorithm '#{value}'. Valid options " \
        "include: #{DIGEST_ALGORITHM_MAP.keys.join(", ")}"
    end
    value.downcase
  end

  option "--changelog", "FILEPATH", "Add changelog from FILEPATH contents" do |file|
    File.read(File.expand_path(file))
  end

  option "--sign", :flag, "Pass --sign to makepkg"

  option "--check-script", "FILE",
    "A script to be sourced when running the check() verification" do |val|
    File.expand_path(val) # Get the full path to the script
  end # --check-script



  # This method is invoked on a package when it has been convertxed to a new
  # package format. The purpose of this method is to do any extra conversion
  # steps, like translating dependency conditions, etc.
  #def converted_from(origin)
    # nothing to do by default. Subclasses may implement this.
    # See the RPM package class for an example.
  #end # def converted_from


  # Add a new source to this package.
  # The exact behavior depends on the kind of package being managed.
  #
  # For instance:
  #
  # * for FPM::Package::Dir, << expects a path to a directory or files.
  # * for FPM::Package::RPM, << expects a path to an rpm.
  #
  # The idea is that you can keep pumping in new things to a package
  # for later conversion or output.
  #
  # Implementations are expected to put files relevant to the 'input' in the
  # staging_path
  def input(pacman_pkg_path)
    control = {}
    # Unpack the control tarball
    safesystem("tar", "-C", staging_path, "-xf", pacman_pkg_path)
    pkginfo = File.join(staging_path, ".PKGINFO")
    mtree = File.join(staging_path, ".MTREE")
    install = File.join(staging_path, ".INSTALL")

    control_contents = File.read(pkginfo)
    FileUtils.rm(pkginfo)
    FileUtils.rm(mtree)
    FileUtils.rm(install)
    # TODO: install script
    # This is difficult. We have to extract the contents of
    # bash functions.
    #install_parts = { "global": [] }
    #install_contents = File.read(install).split("\n")
    #install_contents.each do |install_line|
    #  m = /^\s*(\w+)\s*\(\s*\)\s*{\s*$/.match(install_line)
    #  if m.nil?
    #    install_parts["global"].push(install_line)
    #  else
    #    install_parts[m[1]]
    #  end
    #end
    #parse_install = lambda do |field|
    #pre_install — The script is run right before files are
    #extracted. One argument is passed: new package version.
    #post_install — The script is run right after files are
    #extracted. One argument is passed: new package version.
    #pre_upgrade — The script is run right before files are
    #extracted. Two arguments are passed in the following order: new
    #package version, old package version.  post_upgrade — The script
    #is run right after files are extracted. Two arguments are passed
    #in the following order: new package version, old package version.
    #pre_remove — The script is run right before files are
    #removed. One argument is passed: old package version.
    #post_remove — The script is run right after files are
    #removed. One argument is passed: old package version.

    control_lines = control_contents.split("\n")
    control_lines.each do |line|
      key, val = line.split(/ += +/, 2)
      if control.has_key? key
        control[key].push(val)
      else
        control[key] = [val]
      end
    end

    self.name = control["pkgname"][0]

    # Parse 'epoch:version-iteration' in the version string
    version_re = /^(?:([0-9]+):)?(.+?)(?:-(.*))?$/
    m = version_re.match(control["pkgver"][0])
    if !m
      raise "Unsupported version string '#{control["pkgver"][0]}'"
    end
    self.epoch, self.version, self.iteration = m.captures

    self.maintainer = control["packager"][0]

    # Arch has no notion of vendor, so...
    #self.vendor =

    self.url = control["url"][0]
    # Groups could include more than one.
    # Speaking of just taking the first entry of the field:
    # A crude thing to do, but I suppose it's better than nothing.
    # -- <NAME>, 3/24/2015
    self.category = control["group"][0] || self.category

    # Licenses could include more than one.
    # Speaking of just taking the first entry of the field:
    # A crude thing to do, but I suppose it's better than nothing.
    # -- <NAME>, 3/24/2015
    self.license = control["license"][0] || self.license

    self.architecture = ["arch"][0]

    self.dependencies = control["depend"] || self.dependencies

    self.provides = control["provides"] || self.provides

    self.conflicts = control["conflict"] || self.conflict

    self.replaces = control["replaces"] || self.replaces

    self.description = control["pkgdesc"][0]

    self.config_files = control["backup"].map{|file| "/" + file}

    self.dependencies = control["depend"] || self.dependencies

    self.attributes["size"] = control["size"][0]
    self.attributes["builddate"] = control["builddate"][0]
    self.attributes["optdepend"] = control["optdepend"] || nil
    # There are other available attributes, but I didn't include them because:
    # - makedepend: deps needed to make the arch package. But it's already
    #   made. It just needs to be converted at this point
    # - checkdepend: See above
    # - makepkgopt: See above

    # TODO: scripts, directories.

  end # def input
  
  private
  # Output this package to the given path.
  def output(path)
    raise NotImplementedError.new("#{self.class.name} does not yet support " \
                                  "creating #{self.type} packages")
  end # def output

end # class FPM::Package::Pacman
{'fraction_non_alphanumeric': 0.12157290181940136, 'fraction_numerical': 0.01014504904837763, 'mean_word_length': 3.0338180588434223, 'pattern_counts': {'":': 1, '<': 17, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4521213', 'n_tokens_mistral': 3863, 'n_tokens_neox': 3654, 'n_words': 1220}