code:
# -*- coding:utf-8 -*-
#
# Main fork Pisi: Copyright (C) 2005 - 2011, Tubitak/UEKAE
#
# Copyright (C) 2016 - 2018, Suleyman POYRAZ (Zaryob)
#
# 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 3 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#

import optparse

import gettext

__trans = gettext.translation('inary', fallback=True)
_ = __trans.gettext

import inary.operations.search as search
import inary.context as ctx
import inary.cli.command as command

class SearchFile(command.Command, metaclass=command.autocommand):
    __doc__ = _("""Search for a file

Usage: search-file <path1> <path2> ... <pathn>

Finds the installed package which contains the specified file.
""")

    def __init__(self, args):
        super(SearchFile, self).__init__(args)

    name = ("search-file", "sf")

    def options(self):
        group = optparse.OptionGroup(self.parser, _("search-file options"))
        group.add_option("-l", "--long", action="store_true",
                         default=False, help=_("Show in long format."))
        group.add_option("-q", "--quiet", action="store_true",
                         default=False, help=_("Show only package name."))
        self.parser.add_option_group(group)

    @staticmethod
    def search_file(path):
        found = search.search_file(path)

        if not found:
            ctx.ui.error(_("Path \"{}\" does not belong to an installed package.").format(path))

        for pkg, files in found:
            for pkg_file in files:
                ctx.ui.info(_("Package \"{0}\" has file \"/{1}\"").format(pkg, pkg_file))

    def run(self):

        self.init(database=True, write=False)

        if not self.args:
            self.help()
            return

        # search among existing files
        for path in self.args:
            if not ctx.config.options.quiet:
                ctx.ui.info(_('Searching for \"{}\"').format(path))
            self.search_file(path)


code:
# -*- coding:utf-8 -*-
#
# Main fork Pisi: Copyright (C) 2005 - 2011, Tubitak/UEKAE
#
# Copyright (C) 2016 - 2018, Suleyman POYRAZ (Zaryob)
#
# 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 3 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#

import optparse

import gettext

__trans = gettext.translation('inary', fallback=True)
_ = __trans.gettext

import inary.db
import inary.cli.command as command
import inary.context as ctx

from inary.operations import repository

class UpdateRepo(command.Command, metaclass=command.autocommand):
    __doc__ = _("""Update repository databases

Usage: update-repo [<repo1> <repo2> ... <repon>]

<repoi>: repository name

Synchronizes the INARY databases with the current repository.
If no repository is given, all repositories are updated.
""")

    def __init__(self, args):
        super(UpdateRepo, self).__init__(args)

    name = ("update-repo", "ur")

    def options(self):

        group = optparse.OptionGroup(self.parser, _("update-repo options"))

        group.add_option("-f", "--force", action="store_true",
                         default=False,
                         help=_("Update database in any case"))

        self.parser.add_option_group(group)

    def run(self):
        self.init(database=True)

        if self.args:
            repos = self.args
        else:
            repos = inary.db.repodb.RepoDB().list_repos(only_active=True)

        repository.update_repos(repos, ctx.get_option('force'))

repo_name: SulinOS/inary

license: mit


code:
# -*- coding:utf-8 -*-
#
# Main fork Pisi: Copyright (C) 2005 - 2011, Tubitak/UEKAE
#
# Copyright (C) 2016 - 2018, Suleyman POYRAZ (Zaryob)
#
# 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 3 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#

import gettext
import optparse

__trans = gettext.translation('inary', fallback=True)
_ = __trans.gettext

import inary.cli.command as command
import inary.context as ctx
import inary.db
from inary.operations import repository, upgrade

class Upgrade(command.PackageOp, metaclass=command.autocommand):
    __doc__ = _("""Upgrade INARY packages

Usage: Upgrade [<package1> <package2> ... <packagen>]

<packagei>: package name

Upgrades the entire system if no package names are given

You may use only package names to specify packages because
the package upgrade operation is defined only with respect
to repositories. If you have specified a package name, it
should exist in the package repositories. If you just want to
reinstall a package from a INARY file, use the install command.

You can also specify components instead of package names, which will be
expanded to package names.
""")

    def __init__(self, args):
        super(Upgrade, self).__init__(args)

    name = ("upgrade", "up")

    def options(self):
        group = optparse.OptionGroup(self.parser, _("upgrade options"))

        super(Upgrade, self).options(group)
        group.add_option("--security-only", action="store_true",
                         default=False, help=_("Security related package upgrades only."))
        group.add_option("-b", "--bypass-update-repo", action="store_true",
                         default=False, help=_("Do not update repositories."))
        group.add_option("--ignore-file-conflicts", action="store_true",
                         default=False, help=_("Ignore file conflicts."))
        group.add_option("--ignore-package-conflicts", action="store_true",
                         default=False, help=_("Ignore package conflicts."))
        group.add_option("-c", "--component", action="append",
                         default=None, help=_("Upgrade component's and recursive components' packages."))
        group.add_option("-r", "--repository", action="store",
                         type="string", default=None, help=_('Name of the to be upgraded packages\' repository.'))
        group.add_option("-f", "--fetch-only", action="store_true",
                         default=False, help=_("Fetch upgrades but do not install."))
        group.add_option("-x", "--exclude", action="append",
                         default=None,
                         help=_("When upgrading system, ignore packages and components whose basenames match pattern."))
        group.add_option("--exclude-from", action="store",
                         default=None,
                         help=_("When upgrading system, ignore packages "
                                "and components whose basenames match "
                                "any pattern contained in file."))
        group.add_option("-s", "--compare-sha1sum", action="store_true",
                         default=False, help=_("Compare sha1sum repo and installed packages."))

        self.parser.add_option_group(group)

    def run(self):

        if self.options.fetch_only:
            self.init(database=True, write=False)
        else:
            self.init()

        if not ctx.get_option('bypass_update_repo'):
            ctx.ui.info(_('Updating repositories.'), color='green')
            repos = inary.db.repodb.RepoDB().list_repos(only_active=True)
            repository.update_repos(repos)
        else:
            ctx.ui.info(_('Will not update repositories.'))

        reposit = ctx.get_option('repository')
        components = ctx.get_option('component')
        packages = []
        if components:
            componentdb = inary.db.componentdb.ComponentDB()
            for name in components:
                if componentdb.has_component(name):
                    if repository:
                        packages.extend(componentdb.get_packages(name, walk=True, repo=reposit))
                    else:
                        packages.extend(componentdb.get_union_packages(name, walk=True))
        packages.extend(self.args)

        upgrade.upgrade(packages, reposit)


code:
# -*- coding: utf-8 -*-
#
# Main fork Pisi: Copyright (C) 2005 - 2011, Tubitak/UEKAE
#
# Copyright (C) 2016 - 2018, Suleyman POYRAZ (Zaryob)
#
# 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 3 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#

"""
INARY Configuration module is used for gathering and providing
regular INARY configurations.
"""

import os
import copy

import gettext

__trans = gettext.translation('inary', fallback=True)
_ = __trans.gettext

import inary.context as ctx
import inary.configfile
import inary.errors
import inary.util as util

class Error(inary.errors.Error):
    pass

class Options(object):
    def __getattr__(self, name):
        if name not in self.__dict__:
            return None
        else:
            return self.__dict__[name]

    def __setattr__(self, name, value):
        self.__dict__[name] = value

class Config(object, metaclass=util.Singleton):
    """Config Singleton"""

    def __init__(self, options=Options()):
        self.set_options(options)
        self.values = inary.configfile.ConfigurationFile("/etc/inary/inary.conf")

        # get the initial environment variables. this is needed for
        # build process.
        self.environ = copy.deepcopy(os.environ)

    def set_options(self, options):
        self.options = options

        # Reset __dest_dir to re-read from options
        self.__dest_dir = None

    def set_option(self, opt, val):
        setattr(self.options, opt, val)

    def get_option(self, opt):
        if self.options:
            if hasattr(self.options, opt):
                return getattr(self.options, opt)
        return None

    # directory accessor functions
    # here is how it goes
    # x_dir: system wide directory for storing info type x
    # pkg_x_dir: per package directory for storing info type x

    def dest_dir(self):
        if self.__dest_dir is None:
            destdir = self.get_option('destdir')
            if destdir:
                self.__dest_dir = os.path.abspath(destdir)
            else:
                self.__dest_dir = self.values.general.destinationdirectory

            if not os.path.exists(self.__dest_dir):
                ctx.ui.warning(_("Destination directory \"{}\" does not exist. Creating it.").format(self.__dest_dir))
                os.makedirs(self.__dest_dir)

        return self.__dest_dir

    def subdir(self, path):
        subdir = util.join_path(self.dest_dir(), path)

        # If the directory does not exist, try to create it.
        try:
            util.ensure_dirs(subdir)
        except OSError:
            pass

        return subdir

    def log_dir(self):
        return self.subdir(self.values.dirs.log_dir)

    def lib_dir(self):
        return self.subdir(self.values.dirs.lib_dir)

    def info_dir(self):
        return self.subdir(self.values.dirs.info_dir)

    def history_dir(self):
        return self.subdir(self.values.dirs.history_dir)

    def lock_dir(self):
        return self.subdir(self.values.dirs.lock_dir)

    def packages_dir(self):
        return self.subdir(self.values.dirs.packages_dir)

    def archives_dir(self):
        retval = self.subdir(self.values.dirs.archives_dir)
        # check write access
        if not os.access(retval, os.W_OK):
            retval = self.tmp_dir()

        return retval

    def cache_root_dir(self):
        return self.subdir(self.values.dirs.cache_root_dir)

    def cached_packages_dir(self):
        return self.subdir(self.values.dirs.cached_packages_dir)

    def compiled_packages_dir(self):
        return self.subdir(self.values.dirs.compiled_packages_dir)

    def debug_packages_dir(self):
        return self.subdir(self.values.dirs.debug_packages_dir)

    def old_paths_cache_dir(self):
        return self.subdir(self.values.dirs.old_paths_cache_dir)

    def index_dir(self):
        return self.subdir(self.values.dirs.index_dir)

    def tmp_dir(self):
        sysdir = self.subdir(self.values.dirs.tmp_dir)
        if 'USER' in os.environ:
            userdir = self.subdir('/tmp/inary-' + os.environ['USER'])
        else:
            userdir = self.subdir('/tmp/inary-root')
        # check write access
        if os.access(sysdir, os.W_OK):
            return sysdir
        else:
            return userdir

# TODO: remove this
config = Config()


code:
# -*- coding: utf-8 -*-
#
# Main fork Pisi: Copyright (C) 2005 - 2011, Tubitak/UEKAE
#
# Copyright (C) 2016 - 2018, Suleyman POYRAZ (Zaryob)
#
# 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 3 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#

# INARY Configuration File module, obviously, is used to read from the
# configuration file. Module also defines default values for
# configuration parameters.
#
# Configuration file is located in /etc/inary/inary.conf by default,
# having an INI like format like below.
#
# [general]
# destinationdirectory = /
# autoclean = False
# bandwidth_limit = 0
#
# [build]
# host = i686-pc-linux-gnu
# generateDebug = False
# enableSandbox = False
# jobs = "-j3"
# CFLAGS= -mtune=generic -march=i686 -O2 -pipe -fomit-frame-pointer -fstack-protector -D_FORTIFY_SOURCE=2
# CXXFLAGS= -mtune=generic -march=i686 -O2 -pipe -fomit-frame-pointer -fstack-protector -D_FORTIFY_SOURCE=2
# LDFLAGS= -Wl,-O1 -Wl,-z,relro -Wl,--hash-style=gnu -Wl,--as-needed -Wl,--sort-common
# buildhelper = None / ccache / icecream
# compressionlevel = 1
# fallback = "ftp://ftp.pardus.org.tr/pub/source/2009"
#
# [directories]
# lib_dir = /var/lib/inary
# info_dir = "/var/lib/inary/info"
# history_dir = /var/lib/inary/history
# archives_dir = /var/cache/inary/archives
# cached_packages_dir = /var/cache/inary/packages
# compiled_packages_dir = "/var/cache/inary/packages"
# index_dir = /var/cache/inary/index
# packages_dir = /var/cache/inary/package
# tmp_dir = /var/inary
# kde_dir = /usr/kde/4
# qt_dir = /usr/qt/4
# kde5_dir =
# qt5_dir =

import configparser
import io
import os
import re

import gettext

__trans = gettext.translation('inary', fallback=True)
_ = __trans.gettext

import inary.errors

class Error(inary.errors.Error):
    pass

class GeneralDefaults:
    """Default values for [general] section"""
    destinationdirectory = "/"
    autoclean = False
    distribution = "Sulin"
    distribution_release = "2019"
    distribution_id = "s19"
    architecture = "x86_64"
    allowrfp = False
    http_proxy = os.getenv("HTTP_PROXY") or None
    https_proxy = os.getenv("HTTPS_PROXY") or None
    ftp_proxy = os.getenv("FTP_PROXY") or None
    package_cache = False
    package_cache_limit = 0
    ssl_verify = True
    bandwidth_limit = 0
    ignore_safety = False
    ignore_delta = False

class BuildDefaults:
    """Default values for [build] section"""
    build_host = "localhost"
    host = "x86_64-linux-gnu"
    jobs = "-j1"
    generateDebug = True
    enableSandbox = False
    cflags = "-mtune=generic -O2 -pipe -fomit-frame-pointer -fstack-protector -D_FORTIFY_SOURCE=2"
    cxxflags = "-mtune=generic -O2 -pipe -fomit-frame-pointer -fstack-protector -D_FORTIFY_SOURCE=2"
    ldflags = "-Wl,-O1 -Wl,-z,relro -Wl,--hash-style=gnu -Wl,--as-needed -Wl,--sort-common"
    makeflags = ""
    allow_docs = True
    allow_pages = True
    allow_dbginfo = True
    allow_static = True
    buildhelper = None
    compressionlevel = 9
    fallback = "http://www.sulin.com.tr/pub"
    ignored_build_types = ""

class DirectoriesDefaults:
    """Default values for [directories] section"""
    lib_dir = "/var/lib/inary"
    log_dir = "/var/log"
    info_dir = "/var/lib/inary/info"
    history_dir = "/var/lib/inary/history"
    archives_dir = "/var/cache/inary/archives"
    cache_root_dir = "/var/cache/inary"
    cached_packages_dir = "/var/cache/inary/packages"
    compiled_packages_dir = "/var/cache/inary/packages"
    debug_packages_dir = "/var/cache/inary/packages-debug"
    old_paths_cache_dir = "/var/cache/inary/old-paths"
    packages_dir = "/var/lib/inary/package"
    lock_dir = "/var/lock/subsys"
    index_dir = "/var/lib/inary/index"
    tmp_dir = "/var/inary"
    kde_dir = "/usr/kde/5"
    qt_dir = "/usr/qt/5"

class ConfigurationSection(object):
    """ConfigurationSection class defines a section in the configuration
	file, using defaults (above) as a fallback."""

    def __init__(self, section, items=None):
        if items is None:
            items = []
        self.items = items

        if section == "general":
            self.defaults = GeneralDefaults
        elif section == "build":
            self.defaults = BuildDefaults
        elif section == "directories":
            self.defaults = DirectoriesDefaults
        else:
            e = _("No section by name '{}'").format(section)
            raise Error(e)

        self.section = section

    def __getattr__(self, attr):

        # first search for attribute in the items provided in the
        # configuration file.
        if self.items:
            for item in self.items:
                if item[0] == attr:
                    # all values are returned as string types by ConfigParser.
                    # evaluate "True" or "False" strings to boolean.
                    if item[1] in ["True", "False", "None"]:
                        return eval(item[1])
                    else:
                        return item[1]

        # then fall back to defaults
        if hasattr(self.defaults, attr):
            return getattr(self.defaults, attr)

        return ""

    # We'll need to access configuration keys by their names as a
    # string. Like; ["default"]...
    def __getitem__(self, key):
        return self.__getattr__(key)

class ConfigurationFile(object):
    """Parse and get configuration values from the configuration file"""

    def __init__(self, filePath):
        self.parser = configparser.ConfigParser()
        self.filePath = filePath

        self.parser.read(self.filePath)

        try:
            generalitems = self.parser.items("general")
        except configparser.NoSectionError:
            generalitems = []
        self.general = ConfigurationSection("general", generalitems)

        try:
            builditems = self.parser.items("build")
        except configparser.NoSectionError:
            builditems = []
        self.build = ConfigurationSection("build", builditems)

        try:
            dirsitems = self.parser.items("directories")
        except configparser.NoSectionError:
            dirsitems = []
        self.dirs = ConfigurationSection("directories", dirsitems)

        try:
            directiveitems = self.parser.items("directives")
        except configparser.NoSectionError:
            self.directives = {}
        else:
            self.directives = {}
            for k, v in directiveitems:
                if v in ("True", "False"):
                    self.directives[k] = eval(v)
                else:
                    self.directives[k] = v

    # get, set and write_config methods added for manipulation of inary.conf file from Scom to solve bug #5668.
    # Current ConfigParser does not keep the comments and white spaces, which we do not want for inary.conf. There
    # are patches floating in the python sourceforge to add this feature. The write_config code is from python
    # sourceforge tracker id: #1410680, modified a little to make it turn into a function.

    def get(self, section, option):
        try:
            return self.parser.get(section, option)
        except configparser.NoOptionError:
            return None

    def set(self, section, option, value):
        self.parser.set(section, option, value)

    def write_config(self, add_missing=True):
        sections = {}
        current = io.StringIO()
        replacement = [current]
        sect = None
        opt = None
        written = []
        optcre = re.compile(
            r'(?P<option>[^:=\s][^:=]*)'
            r'(?P<vi>[:=])'
            r'(?P<padding>\s*)'
            r'(?P<value>.*)$'
        )

        fp = open(self.filePath, "r+")
        # Default to " = " to match write(), but use the most recent
        # separator found if the file has any options.
        padded_vi = " = "
        while True:
            line = fp.readline()
            if not line:
                break
            # Comment or blank line?
            if line.strip() == '' or line[0] in '#;' or \
                    (line.split(None, 1)[0].lower() == 'rem' and
                     line[0] in "rR"):
                current.write(line)
                continue
            # Continuation line?
            if line[0].isspace() and sect is not None and opt:
                if ';' in line:
                    # ';' is a comment delimiter only if it follows
                    # a spacing character
                    pos = line.find(';')
                    if line[pos - 1].isspace():
                        comment = line[pos - 1:]
                        # Get rid of the newline, and put in the comment.
                        current.seek(-1, 1)
                        current.write(comment + "\n")
                continue
            # A section header or option header?
            else:
                # Is it a section header?
                mo = self.parser.SECTCRE.match(line)
                if mo:
                    # Remember the most recent section with this name,
                    # so that any missing options can be added to it.
                    if sect:
                        sections[sect] = current
                    sect = mo.group('header')
                    current = io.StringIO()
                    replacement.append(current)
                    sects = self.parser.sections()
                    sects.append(configparser.DEFAULTSECT)
                    if sect in sects:
                        current.write(line)
                    # So sections can't start with a continuation line:
                    opt = None
                # An option line?
                else:
                    mo = optcre.match(line)
                    if mo:
                        padded_opt, vi, value = mo.group('option', 'vi',
                                                         'value')
                        padded_vi = ''.join(mo.group('vi', 'padding'))
                        comment = ""
                        if vi in ('=', ':') and ';' in value:
                            # ';' is a comment delimiter only if it follows
                            # a spacing character
                            pos = value.find(';')
                            if value[pos - 1].isspace():
                                comment = value[pos - 1:]
                        # Keep track of what we rstrip to preserve formatting
                        opt = padded_opt.rstrip().lower()
                        padded_vi = padded_opt[len(opt):] + padded_vi
                        if self.parser.has_option(sect, opt):
                            value = self.parser.get(sect, opt)
                            # Fix continuations.
                            value = value.replace("\n", "\n\t")
                            current.write("{0}{1}{2}{3}\n".format(opt, padded_vi,
                                                                  value, comment))
                            written.append((sect, opt))
        if sect:
            sections[sect] = current
        if add_missing:
            # Add any new sections.
            sects = self.parser.sections()
            if len(self.parser._defaults) > 0:
                sects.append(configparser.DEFAULTSECT)
            sects.sort()
            for sect in sects:
                if sect == configparser.DEFAULTSECT:
                    opts = list(self.parser._defaults.keys())
                else:
                    # Must use _section here to avoid defaults.
                    opts = list(self.parser._sections[sect].keys())
                opts.sort()
                if sect in sections:
                    output = sections[sect] or current
                else:
                    output = current
                    if len(written) > 0:
                        output.write("\n")
                    output.write("[{}]\n".format(sect))
                    sections[sect] = None
                for opt in opts:
                    if opt != "__name__" and not (sect, opt) in written:
                        value = self.parser.get(sect, opt)
                        # Fix continuations.
                        value = value.replace("\n", "\n\t")
                        output.write("{0}{1}{2}\n".format(opt, padded_vi, value))
                        written.append((sect, opt))
        # Copy across the new file.
        fp.seek(0)
        fp.truncate()
        for sect in replacement:
            if sect is not None:
                fp.write(sect.getvalue())

        fp.close()


code:
# -*- coding: utf-8 -*-
#
# Main fork Pisi: Copyright (C) 2005 - 2011, Tubitak/UEKAE
#
# Copyright (C) 2016 - 2018, Suleyman POYRAZ (Zaryob)
#
# 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 3 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#

"""INARY constants.
If you have a "magic" constant value this is where it should be
defined."""

import gettext

__trans = gettext.translation('inary', fallback=True)
_ = __trans.gettext

class Singleton(type):
    def __init__(cls, name, bases, dict):
        super(Singleton, cls).__init__(name, bases, dict)
        cls.instance = None

    def __call__(cls, *args, **kw):
        if cls.instance is None:
            cls.instance = super(Singleton, cls).__call__(*args, **kw)

        return cls.instance

class _constant:
    """Constant members implementation"""

    class ConstError(TypeError):
        pass

    def __setattr__(self, name, value):
        if name in self.__dict__:
            raise self.ConstError(_("Can't rebind constant: \'{}\'").format(name))
        # Binding an attribute once to a const is available
        self.__dict__[name] = value

    def __delattr__(self, name):
        if name in self.__dict__:
            raise self.ConstError(_("Can't unbind constant: \'{}\'").format(name))
        # we don't have an attribute by this name
        raise NameError(name)

class Constants(metaclass=Singleton):
    """Inary Constants Singleton"""

    __c = _constant()

    def __init__(self):
        # suffix for package names
        self.__c.package_suffix = ".inary"

        # delta suffix for package names
        self.__c.delta_package_suffix = ".delta.inary"

        # suffix for lzma
        self.__c.lzma_suffix = ".lzma"
        # suffix for xz
        self.__c.xz_suffix = ".xz"

        self.__c.partial_suffix = ".part"
        self.__c.temporary_suffix = ".tmp"

        # suffix for auto generated debug packages
        self.__c.debug_name_suffix = "-dbginfo"
        self.__c.debug_file_suffix = ".debug"
        self.__c.debug_file_buildid = ".build-id"

        # suffix for auto generated ar packages
        self.__c.static_name_suffix = "-static"  # an admissible use of constant
        self.__c.ar_file_suffix = ".a"

        # directory suffixes for build
        self.__c.work_dir_suffix = "/work"  # these, too, because we might wanna change 'em
        self.__c.install_dir_suffix = "/install"
        self.__c.debug_dir_suffix = "/debug"
        self.__c.debug_files_suffix = "/usr/lib/debug"
        self.__c.quilt_dir_suffix = "/patches"

        # file/directory names
        # note: these don't seem very well, constants are used
        # when it is easier/more meaningful to write the constant name, or
        # when the constant is bound to change later on.
        # in some places literals are just as good, for instance
        # when constant is the same as string. readability is important...
        self.__c.actions_file = "actions.py"
        self.__c.pspec_file = "pspec.xml"
        self.__c.files_dir = "files"
        self.__c.metadata_dir = "metadata"
        self.__c.translations_file = "translations.xml"
        self.__c.scom_dir = "scom"
        self.__c.files_xml = "files.xml"
        self.__c.metadata_xml = "metadata.xml"
        self.__c.install_tar = "install.tar"
        self.__c.mirrors_conf = "/etc/inary/mirrors.conf"
        self.__c.sandbox_conf = "/etc/inary/sandbox.conf"
        self.__c.blacklist = "/etc/inary/blacklist"
        self.__c.config_pending = "configpending"
        self.__c.needs_restart = "needsrestart"
        self.__c.needs_reboot = "needsreboot"
        self.__c.files_db = "files"
        self.__c.repos = "repos"
        self.__c.devel_package_end = "-devel"
        self.__c.info_package_end = "-pages"
        self.__c.doc_package_end = "-docs?$"
        self.__c.assign_to_system_devel = ["system.base", "system.devel"]
        self.__c.system_devel_component = "system.devel"
        self.__c.devels_component = "programming.devel"
        self.__c.docs_component = "programming.docs"
        self.__c.installed_extra = "installedextra"

        # file/directory permissions
        self.__c.umask = 0o022

        # functions in actions_file
        self.__c.setup_func = "setup"
        self.__c.build_func = "build"
        self.__c.check_func = "check"
        self.__c.install_func = "install"

        # file types
        # FIXME: these seem redundant
        self.__c.doc = "doc"
        self.__c.man = "man"
        self.__c.info = "info"
        self.__c.conf = "config"
        self.__c.header = "header"
        self.__c.library = "library"
        self.__c.executable = "executable"
        self.__c.data = "data"
        self.__c.localedata = "localedata"
        self.__c.colors = {'black': "\033[30m",
                           'red': "\033[31m",
                           'green': "\033[32m",
                           'yellow': "\033[33m",
                           'blue': "\033[34m",
                           'purple': "\033[35m",
                           'cyan': "\033[36m",
                           'white': "\033[37m",
                           'brightblack': "\033[01;30m",
                           'brightred': "\033[01;31m",
                           'brightgreen': "\033[01;32m",
                           'brightyellow': "\033[01;33m",
                           'brightblue': "\033[01;34m",
                           'brightpurple': "\033[01;35m",
                           'brightcyan': "\033[01;36m",
                           'brightwhite': "\033[01;37m",
                           'faintblack': "\033[02;30m",
                           'faintred': "\033[02;31m",
                           'faintgreen': "\033[02;32m",
                           'faintyellow': "\033[02;33m",
                           'faintblue': "\033[02;34m",
                           'faintpurple': "\033[02;35m",
                           'faintcyan': "\033[02;36m",
                           'faintwhite': "\033[02;37m",
                           'italicblack': "\033[03;30m",
                           'italicred': "\033[03;31m",
                           'italicgreen': "\033[03;32m",
                           'italicyellow': "\033[03;33m",
                           'italicblue': "\033[03;34m",
                           'italicpurple': "\033[03;35m",
                           'italiccyan': "\033[03;36m",
                           'italicwhite': "\033[03;37m",
                           'underlineblack': "\033[04;30m",
                           'underlinered': "\033[04;31m",
                           'underlinegreen': "\033[04;32m",
                           'underlineyellow': "\033[04;33m",
                           'underlineblue': "\033[04;34m",
                           'underlinemagenta': "\033[04;35m",
                           'underlinecyan': "\033[04;36m",
                           'underlinewhite': "\033[04;37m",
                           'blinkingblack': "\033[05;30m",
                           'blinkingred': "\033[05;31m",
                           'blinkinggreen': "\033[05;32m",
                           'blinkingyellow': "\033[05;33m",
                           'blinkingblue': "\033[05;34m",
                           'blinkingmagenta': "\033[05;35m",
                           'blinkingcyan': "\033[05;36m",
                           'blinkingwhite': "\033[05;37m",
                           'backgroundblack': "\033[07;30m",
                           'backgroundred': "\033[07;31m",
                           'backgroundgreen': "\033[07;32m",
                           'backgroundyellow': "\033[07;33m",
                           'backgroundblue': "\033[07;34m",
                           'backgroundmagenta': "\033[07;35m",
                           'backgroundcyan': "\033[07;36m",
                           'backgroundwhite': "\033[07;37m",
                           'strikethroughblack': "\033[09;30m",
                           'strikethroughred': "\033[09;31m",
                           'strikethroughgreen': "\033[09;32m",
                           'strikethroughyellow': "\033[09;33m",
                           'strikethroughblue': "\033[09;34m",
                           'strikethroughmagenta': "\033[09;35m",
                           'strikethroughcyan': "\033[09;36m",
                           'strikethroughwhite': "\033[09;37m",
                           'default': "\033[0m"}

    def __getattr__(self, attr):
        return getattr(self.__c, attr)

    def __setattr__(self, attr, value):
        setattr(self.__c, attr, value)

    def __delattr__(self, attr):
        delattr(self.__c, attr)


code:
# -*- coding: utf-8 -*-
#
# Main fork Pisi: Copyright (C) 2005 - 2011, Tubitak/UEKAE
#
# Copyright (C) 2016 - 2018, Suleyman POYRAZ (Zaryob)
#
# 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 3 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#

# global variables here

import signal

import inary.constants
import inary.signalhandler
import inary.ui

const = inary.constants.Constants()
sig = inary.signalhandler.SignalHandler()

config = None

log = None
loghandler = None
# used for bug #10568
locked = False

def set_option(opt, val):
    config.set_option(opt, val)

def get_option(opt):
    return config and config.get_option(opt)

ui = inary.ui.UI()

# stdout, stderr for INARY API
stdout = None
stderr = None

scom = True
scom_updated = False
dbus_sockname = None
dbus_timeout = 60 * 60  # in seconds

# Bug #2879
# FIXME: Maybe we can create a simple rollback mechanism. There are other
# places which need this, too.
# this is needed in build process to clean after if something goes wrong.
build_leftover = None

def disable_keyboard_interrupts():
    sig and sig.disable_signal(signal.SIGINT)

def enable_keyboard_interrupts():
    sig and sig.enable_signal(signal.SIGINT)

def keyboard_interrupt_disabled():
    return sig and sig.signal_disabled(signal.SIGINT)

def keyboard_interrupt_pending():
    return sig and sig.signal_pending(signal.SIGINT)

filesdb = None


code:
# -*- coding: utf-8 -*-
#
# Main fork Pisi: Copyright (C) 2005 - 2011, Tubitak/UEKAE
#
# Copyright (C) 2016 - 2018, Suleyman POYRAZ (Zaryob)
#
# 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 3 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import inary.errors

class Error(inary.errors.Error):
    pass

class Exception(inary.errors.Exception):
    pass


code:
# -*- coding: utf-8 -*-
#
# Main fork Pisi: Copyright (C) 2005 - 2011, Tubitak/UEKAE
#
# Copyright (C) 2016 - 2018, Suleyman POYRAZ (Zaryob)
#
# 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 3 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#

import inary.sxml.autoxml as autoxml
import inary.sxml.xmlfile as xmlfile

class Error(object, metaclass=autoxml.autoxml):
    pass

class Obsolete(metaclass=autoxml.autoxml):
    s_Package = [autoxml.String, autoxml.mandatory]

    def __str__(self):
        return self.package

class Distribution(xmlfile.XmlFile, metaclass=autoxml.autoxml):
    tag = "INARY"

    t_SourceName = [autoxml.String, autoxml.mandatory]  # name of distribution (source)
    t_Description = [autoxml.LocalText, autoxml.mandatory]
    t_Version = [autoxml.String, autoxml.optional]
    t_Type = [autoxml.String, autoxml.mandatory]
    t_Dependencies = [[autoxml.Text], autoxml.optional, "Dependencies/Distribution"]

    t_BinaryName = [autoxml.LocalText, autoxml.optional]  # name of repository (binary distro)
    t_Architecture = [autoxml.Text, autoxml.optional]  # architecture identifier

    t_Obsoletes = [[Obsolete], autoxml.optional, "Obsoletes/Package"]

class Maintainer(xmlfile.XmlFile, metaclass=autoxml.autoxml):
    """representation for component responsibles"""

    t_Name = [autoxml.LocalText, autoxml.mandatory]
    t_Email = [autoxml.String, autoxml.mandatory]

    def __str__(self):
        s = "{0} <{1}>".format(self.name, self.email)
        return s

class Component(xmlfile.XmlFile, metaclass=autoxml.autoxml):
    """representation for component declarations"""

    t_Name = [autoxml.String, autoxml.mandatory]  # fully qualified name

    # component name in other languages, for instance in Turkish
    # LocalName for system.base could be sistem.taban or "Taban Sistem",
    # this could be useful for GUIs

    t_LocalName = [autoxml.LocalText, autoxml.optional]

    # Information about the component
    t_Summary = [autoxml.LocalText, autoxml.optional]
    t_Description = [autoxml.LocalText, autoxml.optional]
    t_Group = [autoxml.String, autoxml.optional]

    # Component responsible
    t_Maintainer = [Maintainer, autoxml.optional]

    # the parts of this component.
    # to be filled by the component database, thus it is optional.
    t_Packages = [[autoxml.String], autoxml.optional, "Parts/Package"]

    t_Sources = [[autoxml.String], autoxml.optional, "Parts/Source"]

class Components(xmlfile.XmlFile, metaclass=autoxml.autoxml):
    """representation for component declarations"""

    tag = "INARY"

    t_Components = [[Component], autoxml.optional, "Components/Component"]

# FIXME: there will be no component.xml only components.xml
class CompatComponent(Component):
    tag = "INARY"

    t_VisibleTo = [autoxml.String, autoxml.optional]


code:
# -*- coding: utf-8 -*-
#
# Main fork Pisi: Copyright (C) 2005 - 2011, Tubitak/UEKAE
#
# Copyright (C) 2016 - 2018, Suleyman POYRAZ (Zaryob)
#
# 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 3 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#

"""Files module provides access to files.xml. files.xml is generated
during the build process of a package and used in installation."""

import inary.sxml.autoxml as autoxml

class FileInfo(metaclass=autoxml.autoxml):
    """File holds the information for a File node/tag in files.xml"""

    t_Path = [autoxml.String, autoxml.mandatory]
    t_Type = [autoxml.String, autoxml.mandatory]
    t_Size = [autoxml.Long, autoxml.optional]
    t_Uid = [autoxml.String, autoxml.optional]
    t_Gid = [autoxml.String, autoxml.optional]
    t_Mode = [autoxml.String, autoxml.optional]
    t_Hash = [autoxml.String, autoxml.optional, "SHA1Sum"]
    t_Permanent = [autoxml.String, autoxml.optional]
    t_Replace = [autoxml.String, autoxml.optional]

    def __str__(self):
        s = "/{0}, type: {1}, size: {2}, sha1sum: {3}".format(self.path, self.type,
                                                              self.size, self.hash)
        return s

class Files(autoxml.xmlfile.XmlFile, metaclass=autoxml.autoxml):
    tag = "Files"

    t_List = [[FileInfo], autoxml.optional, "File"]

    def append(self, fileinfo):
        self.list.append(fileinfo)


code:
# -*- coding: utf-8 -*-
#
# Main fork Pisi: Copyright (C) 2005 - 2011, Tubitak/UEKAE
#
# Copyright (C) 2016 - 2018, Suleyman POYRAZ (Zaryob)
#
# 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 3 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#

import inary.sxml.autoxml as autoxml
import inary.sxml.xmlfile as xmlfile

class Group(xmlfile.XmlFile, metaclass=autoxml.autoxml):
    """representation for group declarations"""

    t_Name = [autoxml.String, autoxml.mandatory]
    t_LocalName = [autoxml.LocalText, autoxml.mandatory]
    t_Icon = [autoxml.String, autoxml.optional]

class Groups(xmlfile.XmlFile, metaclass=autoxml.autoxml):
    """representation for component declarations"""

    tag = "INARY"

    t_Groups = [[Group], autoxml.optional, "Groups/Group"]


code:
# -*- coding: utf-8 -*-
#
# Main fork Pisi: Copyright (C) 2005 - 2011, Tubitak/UEKAE
#
# Copyright (C) 2016 - 2018, Suleyman POYRAZ (Zaryob)
#
# 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 3 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#

import os
import time
import gettext

__trans = gettext.translation('inary', fallback=True)
_ = __trans.gettext

import inary.sxml.autoxml as autoxml
import inary.sxml.xmlfile as xmlfile
import inary.context as ctx

class PackageInfo(metaclass=autoxml.autoxml):
    a_version = [autoxml.String, autoxml.mandatory]
    a_release = [autoxml.String, autoxml.mandatory]

    def __str__(self):
        # FIXME: Do not get these from the config file
        distro_id = ctx.config.values.general.distribution_id
        arch = ctx.config.values.general.architecture

        return "-".join((self.version, self.release, distro_id, arch))

class Repo(metaclass=autoxml.autoxml):
    a_operation = [autoxml.String, autoxml.mandatory]

    t_Name = [autoxml.String, autoxml.mandatory]
    t_Uri = [autoxml.String, autoxml.mandatory]

    def __str__(self):
        # "update", "remove", "add"
        if self.operation == "update":
            return _("{0} repository is updated.").format(self.name)
        elif self.operation == "add":
            pass  # TBD
        elif self.operation == "remove":
            pass  # TBD

class Package(metaclass=autoxml.autoxml):
    a_operation = [autoxml.String, autoxml.mandatory]
    a_type = [autoxml.String, autoxml.optional]

    t_Name = [autoxml.String, autoxml.mandatory]
    t_Before = [PackageInfo, autoxml.optional]
    t_After = [PackageInfo, autoxml.optional]

    def __str__(self):
        # "upgrade", "remove", "install", "reinstall", "downgrade"
        if self.operation == "upgrade":
            if self.type == "delta":
                return _("{0} is upgraded from {1} to {2} with delta.").format(self.name, self.before, self.after)
            else:
                return _("{0} is upgraded from {1} to {2}.").format(self.name, self.before, self.after)
        elif self.operation == "remove":
            return _("{0} {1} is removed.").format(self.name, self.before)
        elif self.operation == "install":
            return _("{0} {1} is installed.").format(self.name, self.after)
        elif self.operation == "reinstall":
            return _("{0} {1} is reinstalled.").format(self.name, self.after)
        elif self.operation == "downgrade":
            return _("{0} is downgraded from {1} to {2}.").format(self.name, self.before, self.after)
        else:
            return ""

class Operation(metaclass=autoxml.autoxml):
    a_type = [autoxml.String, autoxml.mandatory]
    a_date = [autoxml.String, autoxml.mandatory]
    a_time = [autoxml.String, autoxml.mandatory]

    t_Packages = [[Package], autoxml.optional, "Package"]
    t_Repos = [[Repo], autoxml.optional, "Repository"]

    def __str__(self):
        return self.type

class History(xmlfile.XmlFile, metaclass=autoxml.autoxml):
    tag = "INARY"

    t_Operation = [Operation, autoxml.mandatory]

    def create(self, operation):

        if operation not in ["downgrade", "upgrade", "remove", "emerge", "install", "snapshot", "takeback",
                             "repoupdate"]:
            raise Exception(_("Unknown package operation"))

        opno = self._get_latest()
        self.histfile = "{0}_{1}.xml".format(opno, operation)

        year, month, day, hour, minute = time.localtime()[0:5]
        self.operation.type = operation
        self.operation.date = "%s-%02d-%02d" % (year, month, day)
        self.operation.time = "%02d:%02d" % (hour, minute)
        self.operation.no = opno

    def update_repo(self, name, uri, operation=None):
        repo = Repo()
        repo.operation = operation
        repo.name = name
        repo.uri = uri
        self.operation.repos.append(repo)

    # @param otype is currently only used to hold if an upgrade is from "delta"
    def add(self, pkgBefore=None, pkgAfter=None, operation=None, otype=None):

        if operation not in ["upgrade", "remove", "install", "reinstall", "downgrade", "snapshot"]:
            raise Exception(_("Unknown package operation"))

        package = Package()
        package.operation = operation
        package.type = otype
        package.name = (pkgAfter and pkgAfter.name) or (pkgBefore and pkgBefore.name)

        if not pkgBefore:
            package.before = None

        if not pkgAfter:
            package.after = None

        for histInfo, pkgInfo in [(package.before, pkgBefore), (package.after, pkgAfter)]:
            if pkgInfo:
                histInfo.version = str(pkgInfo.version)
                histInfo.release = str(pkgInfo.release)

        self.operation.packages.append(package)

    def update(self):
        self.write(os.path.join("%s/%s", ctx.config.history_dir(), self.histfile))

    @staticmethod
    def _get_latest():

        files = [h for h in os.listdir(ctx.config.history_dir()) if h.endswith(".xml")]
        if not files:
            return "0o01"

        # files.sort(key=lambda x,y:int(x.split("_")[0]) - int(y.split("_")[0]))
        files.sort(key=lambda x: int(x.split("_")[0].replace("0o", "0")))
        no, osxml = files[-1].replace("0o", "0").split("_")
        return "%03d" % (int(no) + 1)


code:
# -*- coding: utf-8 -*-
#
# Main fork Pisi: Copyright (C) 2005 - 2011, Tubitak/UEKAE
#
# Copyright (C) 2016 - 2018, Suleyman POYRAZ (Zaryob)
#
# 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 3 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#

"""INARY source/package index"""

import os
import re
import shutil
import multiprocessing

import gettext

__trans = gettext.translation('inary', fallback=True)
_ = __trans.gettext

import inary.context as ctx
import inary.data.specfile as specfile
import inary.data.metadata as metadata
import inary.errors
import inary.util as util
import inary.package
import inary.sxml.xmlfile as xmlfile
import inary.file
import inary.sxml.autoxml as autoxml
import inary.data.component as component
import inary.data.group as group
import inary.operations.build

class Index(xmlfile.XmlFile, metaclass=autoxml.autoxml):
    tag = "INARY"

    t_Distribution = [component.Distribution, autoxml.optional]
    t_Specs = [[specfile.SpecFile], autoxml.optional, "SpecFile"]
    t_Packages = [[metadata.Package], autoxml.optional, "Package"]
    # t_Metadatas = [ [metadata.MetaData], autoxml.optional, "MetaData"]
    t_Components = [[component.Component], autoxml.optional, "Component"]
    t_Groups = [[group.Group], autoxml.optional, "Group"]

    def read_uri(self, uri, tmpdir, force=False):
        return self.read(uri, tmpDir=tmpdir, sha1sum=not force,
                         compress=inary.file.File.COMPRESSION_TYPE_AUTO,
                         sign=inary.file.File.detached,
                         copylocal=True, nodecode=True)

    # read index for a given repo, force means download even if remote not updated
    def read_uri_of_repo(self, uri, repo=None, force=False):
        """Read PSPEC file"""
        if repo:
            tmpdir = os.path.join(ctx.config.index_dir(), repo)
        else:
            tmpdir = os.path.join(ctx.config.tmp_dir(), 'index')
            util.clean_dir(tmpdir)

        util.ensure_dirs(tmpdir)

        # write uri
        urlfile = open(util.join_path(tmpdir, 'uri'), 'w')
        urlfile.write(uri)  # uri
        urlfile.close()

        self.read_uri(uri, tmpdir, force)

        if not repo:
            repo = self.distribution.name()
            # and what do we do with it? move it to index dir properly
            newtmpdir = os.path.join(ctx.config.index_dir(), repo)
            util.clean_dir(newtmpdir)  # replace newtmpdir
            shutil.move(tmpdir, newtmpdir)

    @staticmethod
    def check_signature(filename, repo):
        tmpdir = os.path.join(ctx.config.index_dir(), repo)
        inary.file.File.check_signature(filename, tmpdir)

    def index(self, repo_uri, skip_sources=False):
        self.repo_dir = repo_uri

        packages = []
        specs = []
        deltas = {}

        ctx.ui.info(_("* Generating index tree...\n"), color="cyan")

        pkgs_sorted = False
        for fn in os.walk(repo_uri).__next__()[2]:
            if fn.endswith(ctx.const.delta_package_suffix) or fn.endswith(ctx.const.package_suffix):
                pkgpath = os.path.join(repo_uri,
                                       util.parse_package_dir_path(fn))
                if not os.path.isdir(pkgpath): os.makedirs(pkgpath)
                ctx.ui.info("{:80.80}\r".format(_(' -> Sorting:  \"{}\"').format(fn)),
                            noln=False if ctx.config.get_option("verbose") else True)
                shutil.copy2(os.path.join(repo_uri, fn), pkgpath)
                os.remove(os.path.join(repo_uri, fn))
                pkgs_sorted = True
        if pkgs_sorted:
            ctx.ui.info("{:80.80}\r".format(util.colorize(_(' * Sorted:  \"{}\"').format(fn), color="green")))

        for root, dirs, files in os.walk(repo_uri):
            # Filter hidden directories
            # TODO: Add --exclude-dirs parameter to CLI and filter according
            # directories here
            dirs[:] = [d for d in dirs if not d.startswith(".")]

            for fn in files:

                if fn.endswith(ctx.const.delta_package_suffix):
                    name, version = util.parse_package_name(fn)
                    deltas.setdefault(name, []).append(os.path.join(root, fn))
                elif fn.endswith(ctx.const.package_suffix):
                    packages.append(os.path.join(root, fn))

                if fn == 'components.xml':
                    self.components.extend(add_components(os.path.join(root, fn)))
                if fn == 'pspec.xml' and not skip_sources:
                    specs.append((os.path.join(root, fn), repo_uri))
                if fn == 'distribution.xml':
                    self.distribution = add_distro(os.path.join(root, fn))
                if fn == 'groups.xml':
                    self.groups.extend(add_groups(os.path.join(root, fn)))

        ctx.ui.info("")

        # Create a process pool, as many processes as the number of CPUs we
        # have
        try:
            obsoletes_list = list(map(str, self.distribution.obsoletes))
        except AttributeError:
            obsoletes_list = []
        if obsoletes_list:
            ctx.ui.info(_(" * Added obsoleted packages: [ {} ]".format(obsoletes_list)), color="blue", noln=False)

        pool = multiprocessing.Pool()

        # Before calling pool.map check if list is empty or not: python#12157
        if specs:
            ctx.ui.info(_(" * Adding source packages: "), color="blue", noln=False)
            try:
                # Add source packages to index using a process pool
                self.specs = pool.map(add_spec, specs)
                ctx.ui.info("\n")
            except:
                # If an exception occurs (like a keyboard interrupt),
                # immediately terminate worker processes and propagate
                # exception. (CLI honors KeyboardInterrupt exception, if you're
                # not using CLI, you must handle KeyboardException yourself)
                pool.terminate()
                pool.join()
                ctx.ui.info("")
                raise

        latest_packages = []

        for pkg in util.filter_latest_packages(packages):
            pkg_name = util.parse_package_name(os.path.basename(pkg))[0]
            if pkg_name.endswith(ctx.const.debug_name_suffix):
                pkg_name = util.remove_suffix(ctx.const.debug_name_suffix,
                                              pkg_name)
            if pkg_name not in obsoletes_list:
                # Currently, multiprocessing.Pool.map method accepts methods
                # with single parameters only. So we have to send our
                # parameters as a tuple to workaround that

                latest_packages.append((pkg, deltas, repo_uri))

        # Before calling pool.map check if list is empty or not: python#12157
        if latest_packages:
            sorted_pkgs = {}
            for pkg in latest_packages:
                key = re.search("\/((lib)?[\d\w])\/", pkg[0])
                key = key.group(1) if key else os.path.dirname(pkg[0])
                try:
                    sorted_pkgs[key].append(pkg)
                except KeyError:
                    sorted_pkgs[key] = [pkg]
            self.packages = []
            ctx.ui.info(_(" * Adding binary packages: "), color="blue", noln=False)
            for key, pkgs in sorted(sorted_pkgs.items()):
                ctx.ui.info("{:80.80}\r".format(_("   -> Adding packages from directory \"{}\"... ".format(key))), noln=True)
                try:
                    # Add binary packages to index using a process pool
                    self.packages.extend(pool.map(add_package, pkgs))
                except:
                    pool.terminate()
                    pool.join()
                    ctx.ui.info("")
                    raise
                ctx.ui.info("{:80.80}\r".format(_("   * Adding packages from directory \"{}\"... done.".format(key))), color="green", noln=False)
            ctx.ui.info(_("* Writing index file."), color="blue")

        pool.close()
        pool.join()

def add_package(params):
    try:
        path, deltas, repo_uri = params

        ctx.ui.info("{:80.80}\r".format(_('   -> Adding package to index: \"{}\"').format( os.path.basename(path))), noln=True)

        package = inary.package.Package(path)
        md = package.get_metadata()
        md.package.packageSize = int(os.path.getsize(path))
        md.package.packageHash = util.sha1_file(path)
        if ctx.config.options and ctx.config.options.absolute_urls:
            md.package.packageURI = os.path.realpath(path)
        else:
            md.package.packageURI = util.removepathprefix(repo_uri, path)

        # check package semantics
        errs = md.errors()
        if md.errors():
            ctx.ui.info("")
            ctx.ui.error(_(' * Package \"{}\": metadata corrupt, skipping...').format(md.package.name))
            ctx.ui.error(str(*errs))
        else:
            # No need to carry these with index (#3965)
            md.package.files = None
            md.package.additionalFiles = None

            if md.package.name in deltas:
                name, version, release, distro_id, arch = \
                    util.split_package_filename(path)

                for delta_path in deltas[md.package.name]:
                    src_release, dst_release, delta_distro_id, delta_arch = \
                        util.split_delta_package_filename(delta_path)[1:]

                    # Add only delta to latest build of the package
                    if dst_release != md.package.release or \
                            (delta_distro_id, delta_arch) != (distro_id, arch):
                        continue

                    delta = metadata.Delta()
                    delta.packageURI = util.removepathprefix(repo_uri, delta_path)
                    delta.packageSize = int(os.path.getsize(delta_path))
                    delta.packageHash = util.sha1_file(delta_path)
                    delta.releaseFrom = src_release

                    md.package.deltaPackages.append(delta)

        return md.package

    except KeyboardInterrupt:
        # Handle KeyboardInterrupt exception to prevent ugly backtrace of all
        # worker processes and propagate the exception to main process.
        #
        # Probably it's better to use just 'raise' here, but multiprocessing
        # module has some bugs about that: (python#8296, python#9205 and
        # python#9207 )
        #
        # For now, worker processes do not propagate exceptions other than
        # Exception (like KeyboardInterrupt), so we have to manually propagate
        # KeyboardInterrupt exception as an Exception.

        raise Exception

def add_groups(path):
    ctx.ui.info(_(' * Adding \"groups.xml\" to index'))
    groups_xml = group.Groups()
    groups_xml.read(path)
    return groups_xml.groups

def add_components(path):
    ctx.ui.info(_(' * Adding \"components.xml\" to index'))
    components_xml = component.Components()
    components_xml.read(path)
    try:
        return components_xml.components
    except:
        ctx.ui.error(_(' * Component in {} is corrupt').format(path))
    ctx.ui.error(str(*errs))

def add_distro(path):
    ctx.ui.info(_(' * Adding \"distribution.xml\" to index'))
    distro = component.Distribution()
    try:
        distro.read(path)
        return distro
    except:
        ctx.ui.error(_(' * Distribution in {} is corrupt').format(path))
    ctx.ui.error(str(*errs))

def add_spec(params):
    try:
        path, repo_uri = params
        # TODO: may use try/except to handle this
        builder = inary.operations.build.Builder(path)
        builder.fetch_component()
        sf = builder.spec
        if ctx.config.options and ctx.config.options.absolute_urls:
            sf.source.sourceURI = os.path.realpath(path)
        else:
            sf.source.sourceURI = util.removepathprefix(repo_uri, path)

        ctx.ui.info("{:80.80}\r".format(_('   -> Adding source to index: \"{}\"').format(path)),
                    noln=False if ctx.config.get_option("verbose") else True)
        return sf

    except KeyboardInterrupt:
        # Multiprocessing hack, see add_package method for explanation
        raise Exception

# INDEXER
def index(dirs=None, output='inary-index.xml',
          skip_sources=False, skip_signing=False,
          compression=0):
    """Accumulate Inary XML files in a directory, and write an index."""
    index = Index()
    index.distribution = None
    if not dirs:
        dirs = ['.']
    for repo_dir in dirs:
        repo_dir = str(repo_dir)
        ctx.ui.info(_('Building index of Inary files under \"{}\" \n').format(repo_dir))
        index.index(repo_dir, skip_sources)

    sign = None if skip_signing else inary.file.File.detached
    index.write(output, sha1sum=True, compress=compression, sign=sign)
    ctx.ui.info(_('* Index file written.'),color="green")


code:
# -*- coding: utf-8 -*-
#
# Main fork Pisi: Copyright (C) 2005 - 2011, Tubitak/UEKAE
#
# Copyright (C) 2016 - 2018, Suleyman POYRAZ (Zaryob)
#
# 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 3 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#

"""
Metadata module provides access to metadata.xml. metadata.xml is
generated during the build process of a package and used in the
installation. Package repository also uses metadata.xml for building
a package index.
"""

import gettext

__trans = gettext.translation('inary', fallback=True)
_ = __trans.gettext

import inary.data.specfile as specfile
import inary.sxml.xmlfile as xmlfile
import inary.sxml.autoxml as autoxml
import inary.util as util

class Delta(metaclass=autoxml.autoxml):
    t_PackageURI = [autoxml.String, autoxml.optional]
    t_PackageSize = [autoxml.Long, autoxml.optional]
    t_PackageHash = [autoxml.String, autoxml.optional, "SHA1Sum"]
    a_buildFrom = [autoxml.String, autoxml.optional]
    a_releaseFrom = [autoxml.String, autoxml.optional]

class Source(metaclass=autoxml.autoxml):
    t_Name = [autoxml.String, autoxml.mandatory]
    t_Homepage = [autoxml.String, autoxml.optional]
    t_Packager = [specfile.Packager, autoxml.mandatory]

class Package(specfile.Package, xmlfile.XmlFile, metaclass=autoxml.autoxml):
    t_Build = [autoxml.Integer, autoxml.optional]
    t_BuildHost = [autoxml.String, autoxml.optional]
    t_Distribution = [autoxml.String, autoxml.mandatory]
    t_DistributionRelease = [autoxml.String, autoxml.mandatory]
    t_Architecture = [autoxml.String, autoxml.mandatory]
    t_InstalledSize = [autoxml.Long, autoxml.mandatory]
    t_PackageSize = [autoxml.Long, autoxml.optional]
    t_PackageHash = [autoxml.String, autoxml.optional, "SHA1Sum"]
    t_InstallTarHash = [autoxml.String, autoxml.optional, "SHA1Sum"]
    t_PackageURI = [autoxml.String, autoxml.optional]
    t_DeltaPackages = [[Delta], autoxml.optional]
    t_PackageFormat = [autoxml.String, autoxml.optional]
    t_Rfp = [autoxml.String, autoxml.optional]

    t_Source = [Source, autoxml.optional]

    def get_delta(self, release):
        for delta in self.deltaPackages:
            if delta.releaseFrom == str(release):
                return delta
        else:
            return None

    def decode_hook(self, node, errs, where):
        self.version = self.history[0].version
        self.release = self.history[0].release

    def __str__(self):
        s = specfile.Package.__str__(self)
        i_size = util.human_readable_size(self.installedSize)
        size = "%.2f %s" % (i_size[0], i_size[1])

        s += _('Distribution: {0}, Dist. Release: {1}\n').format(
            self.distribution, self.distributionRelease)
        s += _('Architecture: {0}, Installed Size: {1}').format(
            self.architecture, size)

        if self.packageSize:
            p_size = util.human_readable_size(self.packageSize)
            size = "%.2f %s" % (p_size[0], p_size[1])
            s += _(', Package Size: {}').format(size)

        s += _(', install.tar.xz sha1sum: {}').format(self.installTarHash)

        return s

class MetaData(xmlfile.XmlFile, metaclass=autoxml.autoxml):
    """Package metadata. Metadata is composed of Specfile and various
    other information. A metadata has two parts, Source and Package."""

    tag = "INARY"

    t_Source = [Source, autoxml.mandatory]
    t_Package = [Package, autoxml.mandatory]

    # t_History = [ [Update], autoxml.mandatory]

    def from_spec(self, src, pkg, history):
        # this just copies fields, it doesn't fix every necessary field
        self.source.name = src.name
        self.source.homepage = src.homepage
        self.source.packager = src.packager
        self.package.source = self.source  # FIXME: I know that replication sucks here, but this is the easiest for now
        self.package.rfp = src.rfp
        self.package.name = pkg.name
        self.package.summary = pkg.summary
        self.package.description = pkg.description
        self.package.icon = pkg.icon
        # merge pkg.isA with src.isA
        pkg.isA.extend(src.isA)
        self.package.isA = pkg.isA
        self.package.partOf = pkg.partOf
        self.package.license = pkg.license
        self.package.packageDependencies = pkg.packageDependencies
        self.package.packageAnyDependencies = pkg.packageAnyDependencies
        self.package.componentDependencies = pkg.componentDependencies
        self.package.files = pkg.files
        self.package.providesScom = pkg.providesScom
        # FIXME: no need to copy full history with comments
        self.package.history = history
        self.package.conflicts = pkg.conflicts
        self.package.replaces = pkg.replaces
        self.package.additionalFiles = pkg.additionalFiles

        # FIXME: right way to do it?
        self.source.version = src.version
        self.source.release = src.release
        self.package.version = src.version
        self.package.release = src.release


code:
# -*- coding: utf-8 -*-
#
# Main fork Pisi: Copyright (C) 2005 - 2011, Tubitak/UEKAE
#
# Copyright (C) 2016 - 2018, Suleyman POYRAZ (Zaryob)
#
# 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 3 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#

"""INARY package relation graph that represents the state of packagedb"""

import inary.context as ctx
import inary.db
import inary.errors
import inary.operations.helper as op_helper

import gettext

__trans = gettext.translation('inary', fallback=True)
_ = __trans.gettext

class CycleException(inary.errors.Exception):
    def __init__(self, cycle):
        self.cycle = cycle

    def __str__(self):
        return _('Encountered cycle {}').format(self.cycle)

class Digraph(object):

    def __init__(self):
        self.__v = set()
        self.__adj = {}
        self.__vdata = {}
        self.__edata = {}

    def vertices(self):
        """return set of vertex descriptors"""
        return self.__v

    def edges(self):
        """return a list of edge descriptors"""
        l = []
        for u in self.__v:
            for v in self.__adj[u]:
                l.append((u, v))
        return l

    def add_vertex(self, u, data=None):
        """add vertex u, optionally with data"""
        if not u in self.__v:
            self.__v.add(u)
            self.__adj[u] = set()
            if data:
                self.__vdata[u] = data
                self.__edata[u] = {}

    def add_edge(self, u, v, edata=None, udata=None, vdata=None):
        """add edge u -> v"""
        if not u in self.__v:
            self.add_vertex(u, udata)
        if not v in self.__v:
            self.add_vertex(v, vdata)
        self.__adj[u].add(v)
        if edata is not None:
            self.__edata[u][v] = edata

    def add_biedge(self, u, v, edata=None):
        self.add_edge(u, v, edata)
        self.add_edge(v, u, edata)

    def set_vertex_data(self, u, data):
        self.__vdata[u] = data

    def vertex_data(self, u):
        return self.__vdata[u]

    def edge_data(self, u, v):
        return self.__edata[u][v]

    def has_vertex(self, u):
        return u in self.__v

    def has_edge(self, u, v):
        if u in self.__v:
            return v in self.__adj[u]
        else:
            return False

    def adj(self, u):
        return self.__adj[u]

    def dfs(self, finish_hook=None):
        self.color = {}
        self.p = {}
        self.d = {}
        self.f = {}
        for u in self.__v:
            self.color[u] = 'w'  # mark white (unexplored)
            self.p[u] = None
        self.time = 0
        for u in self.__v:
            if self.color[u] == 'w':
                self.dfs_visit(u, finish_hook)

    def dfs_visit(self, u, finish_hook):
        self.color[u] = 'g'  # mark green (discovered)
        self.d[u] = self.time = self.time + 1
        for v in self.adj(u):
            if self.color[v] == 'w':  # explore unexplored vertices
                self.p[v] = u
                self.dfs_visit(v, finish_hook)
            elif self.color[v] == 'g':  # cycle detected
                cycle = [u]
                while self.p[u]:
                    u = self.p[u]
                    cycle.append(u)
                    if self.has_edge(cycle[0], u):
                        break
                cycle.reverse()
                raise CycleException(cycle)
        self.color[u] = 'b'  # mark black (completed)
        if finish_hook:
            finish_hook(u)
        self.f[u] = self.time = self.time + 1

    def cycle_free(self):
        try:
            self.dfs()
            return True
        except CycleException:
            return False

    def topological_sort(self):
        list = []
        self.dfs(lambda u: list.append(u))
        list.reverse()
        return inary.util.unique_list(list)

    @staticmethod
    def id_str(u):
        # Graph format only accepts underscores as key values
        # Sanitize the values. This is 2x faster than the old method.
        return u.replace("-", "_").replace("+", "_")

    def write_graphviz(self, f):
        f.write('digraph G {\n')
        for u in self.vertices():
            f.write(self.id_str(u))
            self.write_graphviz_vlabel(f, u)
            f.write(';\n')
        f.write('\n')
        for u in self.vertices():
            for v in self.adj(u):
                f.write(self.id_str(u) + ' -> ' + self.id_str(v))
                self.write_graphviz_elabel(f, u, v)
                f.write(';\n')
        f.write('\n')
        f.write('}\n')

    def write_graphviz_vlabel(self, f, u):
        pass

    def write_graphviz_elabel(self, f, u, v):
        pass

# Cache the results from packagedb queries in a graph

class PGraph(Digraph):

    def __init__(self, packagedb):
        super(PGraph, self).__init__()
        self.packagedb = packagedb

    def add_package(self, pkg):
        pkg1 = self.packagedb.get_package(pkg)
        self.add_vertex(str(pkg), (pkg1.version, pkg1.release))

    def add_plain_dep(self, pkg1name, pkg2name):
        pkg1data = None
        if not pkg1name in self.vertices():
            pkg1 = self.packagedb.get_package(pkg1name)
            pkg1data = (pkg1.version, pkg1.release)
        pkg2data = None
        if not pkg2name in self.vertices():
            pkg2 = self.packagedb.get_package(pkg2name)
            pkg2data = (pkg2.version, pkg2.release)
        self.add_edge(str(pkg1name), str(pkg2name), ('d', None),
                      pkg1data, pkg2data)

    def add_dep(self, pkg, depinfo):
        pkg1data = None
        if not pkg in self.vertices():
            pkg1 = self.packagedb.get_package(pkg)
            pkg1data = (pkg1.version, pkg1.release)
        pkg2data = None
        if not depinfo.package in self.vertices():
            pkg2 = self.packagedb.get_package(depinfo.package)
            pkg2data = (pkg2.version, pkg2.release)
        self.add_edge(str(pkg), str(depinfo.package), ('d', depinfo),
                      pkg1data, pkg2data)

    def write_graphviz_vlabel(self, f, u):
        (v, r) = self.vertex_data(u)
        f.write('[ label = \"' + str(u) + '(' + str(v) + ',' + str(r) + ')\" ]')

# ****** Danger Zone Below! Tressspassers' eyes will explode! ********** #

def package_graph(A, packagedb, ignore_installed=False, reverse=False):
    """Construct a package relations graph.

    Graph will contain all dependencies of packages A, if ignore_installed
    option is True, then only uninstalled deps will be added.

    """

    ctx.ui.debug('A = {}'.format(str(A)))

    # try to construct a inary graph of packages to
    # install / reinstall

    G_f = PGraph(packagedb)  # construct G_f

    # find the "install closure" graph of G_f by package
    # set A using packagedb
    for x in A:
        G_f.add_package(x)
    B = A
    # state = {}
    while len(B) > 0:
        Bp = set()
        for x in B:
            pkg = packagedb.get_package(x)
            # print pkg
            if reverse:
                for name, dep in packagedb.get_rev_deps(x):
                    if ignore_installed:
                        if dep.satisfied_by_installed():
                            continue
                    if not name in G_f.vertices():
                        Bp.add(name)
                    G_f.add_dep(name, dep)
            else:
                for dep in pkg.runtimeDependencies():
                    if ignore_installed:
                        if dep.satisfied_by_installed():
                            continue
                    if not dep.package in G_f.vertices():
                        Bp.add(str(dep.package))
                    G_f.add_dep(x, dep)
        B = Bp
    return G_f

def generate_pending_order(A):
    # returns pending package list in reverse topological order of dependency
    installdb = inary.db.installdb.InstallDB()
    G_f = PGraph(installdb)  # construct G_f
    for x in A:
        G_f.add_package(x)
    B = A
    while len(B) > 0:
        Bp = set()
        for x in B:
            pkg = installdb.get_package(x)
            for dep in pkg.runtimeDependencies():
                if dep.package in G_f.vertices():
                    G_f.add_dep(x, dep)
        B = Bp
    if ctx.get_option('debug'):
        import sys
        G_f.write_graphviz(sys.stdout)
    order = G_f.topological_sort()
    order.reverse()

    componentdb = inary.db.componentdb.ComponentDB()
    # Bug 4211
    if componentdb.has_component('system.base'):
        order = op_helper.reorder_base_packages(order)

    return order


code:
# -*- coding: utf-8 -*-
#
# Main fork Pisi: Copyright (C) 2005 - 2011, Tubitak/UEKAE
#
# Copyright (C) 2016 - 2018, Suleyman POYRAZ (Zaryob)
#
# 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 3 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#

import inary.db
import inary.sxml.autoxml as autoxml
import inary.version

class Relation(metaclass=autoxml.autoxml):
    s_Package = [autoxml.String, autoxml.mandatory]
    a_version = [autoxml.String, autoxml.optional]
    a_versionFrom = [autoxml.String, autoxml.optional]
    a_versionTo = [autoxml.String, autoxml.optional]
    a_release = [autoxml.String, autoxml.optional]
    a_releaseFrom = [autoxml.String, autoxml.optional]
    a_releaseTo = [autoxml.String, autoxml.optional]

    def satisfies_relation(self, version, release):
        if self.version and version != self.version:
            return False
        else:
            v = inary.version.make_version(version)

            if self.versionFrom and \
                    v < inary.version.make_version(self.versionFrom):
                return False

            if self.versionTo and \
                    v > inary.version.make_version(self.versionTo):
                return False

        if self.release and release != self.release:
            return False
        else:
            r = int(release)

            if self.releaseFrom and r < int(self.releaseFrom):
                return False

            if self.releaseTo and r > int(self.releaseTo):
                return False

        return True

def installed_package_satisfies(relation):
    installdb = inary.db.installdb.InstallDB()
    pkg_name = relation.package
    if not installdb.has_package(pkg_name):
        return False
    else:
        pkg = installdb.get_package(pkg_name)
        return relation.satisfies_relation(pkg.version, pkg.release)


code:
# -*- coding: utf-8 -*-
#
# Main fork Pisi: Copyright (C) 2005 - 2011, Tubitak/UEKAE
#
# Copyright (C) 2016 - 2018, Suleyman POYRAZ (Zaryob)
#
# 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 3 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#

import gettext

__trans = gettext.translation('inary', fallback=True)
_ = __trans.gettext

import inary.data.relation as relation

""" Replace relation """

class Replace(relation.Relation):
    def __str__(self):
        s = self.package
        if self.versionFrom:
            s += _(" version >= ") + self.versionFrom
        if self.versionTo:
            s += _(" version <= ") + self.versionTo
        if self.version:
            s += _(" version ") + self.version
        if self.releaseFrom:
            s += _(" release >= ") + self.releaseFrom
        if self.releaseTo:
            s += _(" release <= ") + self.releaseTo
        if self.release:
            s += _(" release ") + self.release
        return s

def installed_package_replaced(repinfo):
    """determine if an installed package in *repository* replaced with
given a package"""
    return relation.installed_package_satisfies(repinfo)


code:
# -*- coding: utf-8 -*-
#
# Main fork Pisi: Copyright (C) 2005 - 2011, Tubitak/UEKAE
#
# Copyright (C) 2016 - 2018, Suleyman POYRAZ (Zaryob)
#
# 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 3 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#

"""
 Specfile module is our handler for PSPEC files. PSPEC (INARY SPEC)
 files are specification files for INARY source packages. This module
 provides read and write routines for PSPEC files.
"""

import gettext

__trans = gettext.translation('inary', fallback=True)
_ = __trans.gettext

# standard python modules
import os.path

# inary modules
import inary.sxml.xmlfile as xmlfile
import inary.sxml.xmlext as xmlext
import inary.sxml.autoxml as autoxml
import inary.context as ctx
import inary.analyzer.dependency
import inary.data.replace
import inary.analyzer.conflict
import inary.data.component as component
import inary.data.group as group
import inary.util as util
import inary.errors
import inary.db

class Error(inary.errors.Error):
    pass

class Packager(metaclass=autoxml.autoxml):
    t_Name = [autoxml.String, autoxml.mandatory]
    t_Email = [autoxml.String, autoxml.mandatory]

    def __str__(self):
        s = "{} <{}>".format(self.name, self.email)
        return s

class AdditionalFile(metaclass=autoxml.autoxml):
    s_Filename = [autoxml.String, autoxml.mandatory]
    a_target = [autoxml.String, autoxml.mandatory]
    a_permission = [autoxml.String, autoxml.optional]
    a_owner = [autoxml.String, autoxml.optional]
    a_group = [autoxml.String, autoxml.optional]

    def __str__(self):
        s = "{0} -> {1} ".format(self.filename, self.target)
        if self.permission:
            s += '({})'.format(self.permission)
        return s

class Type(metaclass=autoxml.autoxml):
    s_type = [autoxml.String, autoxml.mandatory]
    a_package = [autoxml.String, autoxml.optional]

class Action(metaclass=autoxml.autoxml):
    # Valid actions:
    #
    # reverseDependencyUpdate
    # systemRestart
    # serviceRestart

    s_action = [autoxml.String, autoxml.mandatory]
    a_package = [autoxml.String, autoxml.optional]
    a_target = [autoxml.String, autoxml.optional]

    def __str__(self):
        return self.action

class Patch(metaclass=autoxml.autoxml):
    s_Filename = [autoxml.String, autoxml.mandatory]
    a_compressionType = [autoxml.String, autoxml.optional]
    a_level = [autoxml.Integer, autoxml.optional]
    a_reverse = [autoxml.String, autoxml.optional]

    # FIXME: what's the cleanest way to give a default value for reading level?
    # def decode_hook(self, node, errs, where):
    #    if self.level == None:
    #        self.level = 0

    def __str__(self):
        s = self.filename
        if self.compressionType:
            s += ' (' + self.compressionType + ')'
        if self.level:
            s += ' level:' + self.level
        return s

class Update(metaclass=autoxml.autoxml):
    a_release = [autoxml.String, autoxml.mandatory]
    # 'type' attribute is here to keep backward compatibility
    a_type = [autoxml.String, autoxml.optional]
    t_types = [[Type], autoxml.optional, "Type"]
    t_Date = [autoxml.String, autoxml.mandatory]
    t_Version = [autoxml.String, autoxml.mandatory]
    t_Comment = [autoxml.String, autoxml.optional]
    t_Name = [autoxml.String, autoxml.optional]
    t_Email = [autoxml.String, autoxml.optional]
    t_Requires = [[Action], autoxml.optional]

    def __str__(self):
        s = self.date
        s += ", ver=" + self.version
        s += ", rel=" + self.release
        if self.type:
            s += ", type=" + self.type
        return s

class Path(metaclass=autoxml.autoxml):
    s_Path = [autoxml.String, autoxml.mandatory]
    a_fileType = [autoxml.String, autoxml.optional]
    a_permanent = [autoxml.String, autoxml.optional]
    a_replace = [autoxml.String, autoxml.optional]

    def __str__(self):
        s = self.path
        s += ", type=" + self.fileType
        return s

class ScomProvide(metaclass=autoxml.autoxml):
    s_om = [autoxml.String, autoxml.mandatory]
    a_script = [autoxml.String, autoxml.mandatory]
    a_name = [autoxml.String, autoxml.optional]

    def __str__(self):
        # FIXME: descriptive enough?
        s = self.script
        s += ' (' + self.om + '{}'.format(' for {}'.format(self.name) if self.name else '') + ')'
        return s

class Archive(metaclass=autoxml.autoxml):
    s_uri = [autoxml.String, autoxml.mandatory]
    a_type = [autoxml.String, autoxml.optional]
    a_sha1sum = [autoxml.String, autoxml.mandatory]
    a_target = [autoxml.String, autoxml.optional]

    def decode_hook(self, node, errs, where):
        self.name = os.path.basename(str(self.uri))

    def __str__(self):
        s = _('URI: {0}, type: {1}, sha1sum: {2}').format(self.uri, self.type, self.sha1sum)
        return s

class Source(metaclass=autoxml.autoxml):
    t_Name = [autoxml.String, autoxml.mandatory]
    t_Homepage = [autoxml.String, autoxml.optional]
    t_Packager = [Packager, autoxml.mandatory]
    t_Rfp = [autoxml.String, autoxml.optional]
    t_ExcludeArch = [[autoxml.String], autoxml.optional]
    t_License = [[autoxml.String], autoxml.mandatory]
    t_IsA = [[autoxml.String], autoxml.optional]
    t_PartOf = [autoxml.String, autoxml.optional]
    t_Summary = [autoxml.LocalText, autoxml.mandatory]
    t_Description = [autoxml.LocalText, autoxml.mandatory]
    t_Icon = [autoxml.String, autoxml.optional]
    t_Archive = [[Archive], autoxml.mandatory, "Archive"]
    t_AdditionalFiles = [[AdditionalFile], autoxml.optional]
    t_BuildDependencies = [[inary.analyzer.dependency.Dependency], autoxml.optional]
    t_Patches = [[Patch], autoxml.optional]
    t_Version = [autoxml.String, autoxml.optional]
    t_Release = [autoxml.String, autoxml.optional]
    t_SourceURI = [autoxml.String, autoxml.optional]  # used in index

    def buildtimeDependencies(self):
        return self.buildDependencies

class AnyDependency(metaclass=autoxml.autoxml):
    t_Dependencies = [[inary.analyzer.dependency.Dependency], autoxml.optional, "Dependency"]

    def __str__(self):
        return "{{}}".format(_(" or ").join([str(dep) for dep in self.dependencies]))

    def name(self):
        return "{{}}".format(_(" or ").join([dep.package for dep in self.dependencies]))

    def decode_hook(self, node, errs, where):
        self.package = self.dependencies[0].package

    def satisfied_by_dict_repo(self, dict_repo):
        for dependency in self.dependencies:
            if dependency.satisfied_by_dict_repo(dict_repo):
                return True
        return False

    def satisfied_by_any_installed_other_than(self, package):
        for dependency in self.dependencies:
            if dependency.package != package and dependency.satisfied_by_installed():
                return True
        return False

    def satisfied_by_installed(self):
        for dependency in self.dependencies:
            if dependency.satisfied_by_installed():
                return True
        return False

    def satisfied_by_repo(self):
        for dependency in self.dependencies:
            if dependency.satisfied_by_repo():
                return True
        return False

class Package(metaclass=autoxml.autoxml):
    t_Name = [autoxml.String, autoxml.mandatory]
    t_Summary = [autoxml.LocalText, autoxml.optional]
    t_Description = [autoxml.LocalText, autoxml.optional]
    t_IsA = [[autoxml.String], autoxml.optional]
    t_PartOf = [autoxml.String, autoxml.optional]
    t_License = [[autoxml.String], autoxml.optional]
    t_Icon = [autoxml.String, autoxml.optional]
    t_BuildFlags = [[autoxml.String], autoxml.optional, "BuildFlags/Flag"]
    t_BuildType = [autoxml.String, autoxml.optional]
    t_BuildDependencies = [[inary.analyzer.dependency.Dependency], autoxml.optional]
    t_PackageDependencies = [[inary.analyzer.dependency.Dependency], autoxml.optional, "RuntimeDependencies/Dependency"]
    t_PackageAnyDependencies = [[AnyDependency], autoxml.optional, "RuntimeDependencies/AnyDependency"]
    t_ComponentDependencies = [[autoxml.String], autoxml.optional, "RuntimeDependencies/Component"]
    t_Files = [[Path], autoxml.optional]
    t_Conflicts = [[inary.analyzer.conflict.Conflict], autoxml.optional, "Conflicts/Package"]
    t_Replaces = [[inary.data.replace.Replace], autoxml.optional, "Replaces/Package"]
    t_ProvidesScom = [[ScomProvide], autoxml.optional, "Provides/SCOM"]
    t_AdditionalFiles = [[AdditionalFile], autoxml.optional]
    t_History = [[Update], autoxml.optional]

    # FIXME: needed in build process, to distinguish dynamically generated debug packages.
    # find a better way to do this.
    debug_package = False

    def runtimeDependencies(self):
        componentdb = inary.db.componentdb.ComponentDB()
        deps = self.packageDependencies + self.packageAnyDependencies

        # Create Dependency objects for each package coming from
        # a component dependency.
        for component in self.componentDependencies:
            for pkgName in componentdb.get_component(component).packages:
                deps.append(inary.analyzer.dependency.Dependency(package=pkgName))

        return deps

    def pkg_dir(self):
        packageDir = self.name + '-' \
                     + self.version + '-' \
                     + self.release

        return util.join_path(ctx.config.packages_dir(), packageDir)

    def satisfies_runtime_dependencies(self):
        for dep in self.runtimeDependencies():
            if not dep.satisfied_by_installed():
                ctx.ui.error(_('\"{0}\" dependency of package \"{1}\" is not satisfied.').format(dep, self.name))
                return False
        return True

    def installable(self):
        """calculate if pkg is installable currently"""
        return self.satisfies_runtime_dependencies()

    def get_update_types(self, old_release):
        """Returns update types for the releases greater than old_release.

        @type  old_release: string
        @param old_release: The release of the installed package.

        @rtype:  set of strings
        @return: Update types.
        """

        types = set()

        for update in self.history:
            if update.release == old_release:
                break

            if update.type:
                types.add(update.type)

            for type_ in update.types:
                if type_.package and type_.package != self.name:
                    continue

                types.add(type_.type)

        return types

    def has_update_type(self, type_name, old_release):
        """Checks whether the package has the given update type.

        @type  type_name:   string
        @param type_name:   Name of the update type.
        @type  old_release: string
        @param old_release: The release of the installed package.

        @rtype:  bool
        @return: True if the type exists, else False.
        """

        for update in self.history:
            if update.release == old_release:
                break

            if update.type == type_name:
                return True

            for type_ in update.types:
                if type_.package and type_.package != self.name:
                    continue

                if type_.type == type_name:
                    return True

        return False

    def get_update_actions(self, old_release=None):
        """Returns update actions for the releases greater than old_release.

        @type  old_release: string
        @param old_release: The release of the installed package.

        @rtype:  dict
        @return: A set of affected packages for each action.
        """

        if old_release is None:
            installdb = inary.db.installdb.InstallDB()
            if not installdb.has_package(self.name):
                return {}

            version, old_release, build = installdb.get_version(self.name)

        actions = {}

        for update in self.history:
            if update.release == old_release:
                break

            for action in update.requires:
                if action.package and action.package != self.name:
                    continue

                target = action.target or self.name
                actions.setdefault(action.action, set()).add(target)

        return actions

    def __str__(self):
        s = _('Name: {0}, version: {1}, release: {2}\n').format(
            self.name, self.version, self.release)
        s += _('Summary: {}\n').format(str(self.summary))
        s += _('Description: {}\n').format(str(self.description))
        s += _('Licenses: {}\n').format(", ".join(self.license))
        s += _('Component: {}\n').format(str(self.partOf))
        s += _('Provides: ')
        for x in self.providesScom:
            s += x.om + ' '
        s += '\n'
        s += _('Dependencies: ')
        for x in self.componentDependencies:
            s += x + ' '
        for x in self.packageDependencies:
            s += x.name() + ' '
        for x in self.packageAnyDependencies:
            s += x.name() + ' '
        return s + '\n'

class SpecFile(xmlfile.XmlFile, metaclass=autoxml.autoxml):
    tag = "INARY"

    t_Source = [Source, autoxml.mandatory]
    t_Packages = [[Package], autoxml.mandatory, "Package"]
    t_History = [[Update], autoxml.mandatory]
    t_Components = [[component.Component], autoxml.optional, "Component"]
    t_Groups = [[group.Group], autoxml.optional, "Group"]

    def decode_hook(self, node, errs, where):
        current_version = self.history[0].version
        current_release = self.history[0].release

        for package in self.packages:
            deps = package.packageDependencies[:]
            deps += sum([x.dependencies for x
                         in package.packageAnyDependencies], [])
            for dep in deps:
                for attr_name, attr_value in list(dep.__dict__.items()):
                    if attr_value != "current":
                        continue

                    if attr_name.startswith("version"):
                        dep.__dict__[attr_name] = current_version

                    elif attr_name.startswith("release"):
                        dep.__dict__[attr_name] = current_release

    def getSourceVersion(self):
        return self.history[0].version

    def getSourceRelease(self):
        return self.history[0].release

    @staticmethod
    def _set_i18n(tag, inst):
        try:
            for summary in xmlext.getTagByName(tag, "Summary"):
                inst.summary[xmlext.getNodeAttribute(summary, "xml:lang")] = xmlext.getNodeText(summary)
            for desc in xmlext.getTagByName(tag, "Description"):
                inst.description[xmlext.getNodeAttribute(desc, "xml:lang")] = xmlext.getNodeText(desc)
        except AttributeError as e:
            raise Error(_("translations.xml file is badly formed.: {}").format(e))

    def read_translations(self, path):
        if not os.path.exists(path):
            return

        doc = xmlext.parse(path)

        if xmlext.getNodeText(xmlext.getNode(doc, "Source"), "Name") == self.source.name:
            # Set source package translations
            self._set_i18n(xmlext.getNode(doc, "Source"), self.source)

        for pak in xmlext.getTagByName(doc, "Package"):
            for inst in self.packages:
                if inst.name == xmlext.getNodeText(pak, "Name"):
                    self._set_i18n(pak, inst)
                    break

    def __str__(self):
        s = _('Name: {0}, version: {1}, release: {2}\n').format(
            self.source.name, self.history[0].version, self.history[0].release)
        s += _('Summary: {}\n').format(str(self.source.summary))
        s += _('Description: {}\n').format(str(self.source.description))
        s += _('Licenses: {}\n').format(", ".join(self.source.license))
        s += _('Component: {}\n').format(str(self.source.partOf))
        s += _('Build Dependencies: ')
        for x in self.source.buildDependencies:
            s += x.package + ' '
        return s


code:
# -*- coding: utf-8 -*-
#
# Main fork Pisi: Copyright (C) 2005 - 2011, Tubitak/UEKAE
#
# Copyright (C) 2016 - 2018, Suleyman POYRAZ (Zaryob)
#
# 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 3 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#

import gettext

__trans = gettext.translation('inary', fallback=True)
_ = __trans.gettext

from . import *

def invalidate_caches():
    # Invalidates inary caches in use and forces to re-fill caches from disk when needed
    for db in [packagedb.PackageDB(), sourcedb.SourceDB(), componentdb.ComponentDB(),
               installdb.InstallDB(), historydb.HistoryDB(), groupdb.GroupDB(), repodb.RepoDB()]:
        db.invalidate()

def flush_caches():
    # Invalidate and flush caches to re-generate them when needed
    for db in [packagedb.PackageDB(), sourcedb.SourceDB(),
               componentdb.ComponentDB(), groupdb.GroupDB()]:
        db.invalidate()
        db.cache_flush()

def update_caches():
    # Updates ondisk caches
    for db in [packagedb.PackageDB(), sourcedb.SourceDB(), componentdb.ComponentDB(),
               installdb.InstallDB(), groupdb.GroupDB()]:
        if db.is_initialized():
            db.cache_save()

def regenerate_caches():
    flush_caches()
    # Force cache regeneration
    for db in [packagedb.PackageDB(), sourcedb.SourceDB(),
               componentdb.ComponentDB(), groupdb.GroupDB()]:
        db.cache_regenerate()