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 gettext

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

import inary.context as ctx
import inary.package
import inary.util as util

def create_delta_packages_from_obj(old_packages, new_package_obj, specdir):
    new_pkg_info = new_package_obj.metadata.package
    new_pkg_files = new_package_obj.files

    new_pkg_path = new_package_obj.tmp_dir

    new_pkg_name = os.path.basename(new_package_obj.filepath)
    name, new_version, new_release, new_distro_id, new_arch = \
        util.split_package_filename(new_pkg_name)

    cwd = os.getcwd()
    out_dir = ctx.get_option("output_dir")
    target_format = ctx.get_option("package_format")
    delta_packages = []

    for old_package in old_packages:
        old_pkg = inary.package.Package(old_package)
        old_pkg_info = old_pkg.metadata.package

        if old_pkg_info.name != new_pkg_info.name:
            ctx.ui.warning(
                _("The file \"{0}\" belongs to a different package other than '{1}'. Skipping it...").format(old_package,
                                                                                new_pkg_info.name))
            continue

        if old_pkg_info.release == new_pkg_info.release:
            ctx.ui.warning(
                _("Package \"{}\" has the same release number with the new package. Skipping it...").format(old_package))
            continue

        delta_name = "-".join((old_pkg_info.name,
                               old_pkg_info.release,
                               new_pkg_info.release,
                               new_distro_id,
                               new_arch)) + ctx.const.delta_package_suffix

        ctx.ui.info(_("Creating delta package: \"{}\"...").format(delta_name))

        if out_dir:
            delta_name = util.join_path(out_dir, delta_name)

        old_pkg_files = old_pkg.get_files()

        files_delta = find_delta(old_pkg_files, new_pkg_files)

        if len(files_delta) == len(new_pkg_files.list):
            ctx.ui.warning(_(
                "All files in the package \"{}\" are different from the files in the new package. Skipping it...").format(
                old_package))
            continue

        delta_pkg = inary.package.Package(delta_name, "w", format=target_format)

        # add conf scripts
        os.chdir(specdir)
        for pscom in new_pkg_info.providesScom:
            fname = util.join_path(ctx.const.scom_dir, pscom.script)
            delta_pkg.add_to_package(fname)

        # add xmls and files
        os.chdir(new_pkg_path)

        delta_pkg.add_metadata_xml(ctx.const.metadata_xml)
        delta_pkg.add_files_xml(ctx.const.files_xml)

        # only metadata information may change in a package,
        # so no install archive added to delta package
        if files_delta:
            # Sort the files in-place according to their path for an ordered
            # tarfile layout which dramatically improves the compression
            # performance of lzma. This improvement is stolen from build.py
            # (commit r23485).
            files_delta.sort(key=lambda x: x.path)

            for finfo in files_delta:
                orgname = util.join_path("install", finfo.path)
                if new_pkg_info.debug_package:
                    orgname = util.join_path("debug", finfo.path)
                delta_pkg.add_to_install(orgname, finfo.path)

        os.chdir(cwd)

        delta_pkg.close()
        delta_packages.append(delta_name)

    # Return delta package names
    return delta_packages

def create_delta_packages(old_packages, new_package):
    if new_package in old_packages:
        ctx.ui.warning(_("New package \"{}\" exists in the list of old packages. Skipping it...").format(new_package))
        while new_package in old_packages:
            old_packages.remove(new_package)

    new_pkg_name = os.path.splitext(os.path.basename(new_package))[0]
    new_pkg_path = util.join_path(ctx.config.tmp_dir(), new_pkg_name)

    new_pkg = inary.package.Package(new_package, tmp_dir=new_pkg_path)
    new_pkg.read()

    # Unpack new package to temp
    new_pkg.extract_inary_files(new_pkg_path)
    new_pkg.extract_dir("scom", new_pkg_path)

    install_dir = util.join_path(new_pkg_path, "install")
    util.clean_dir(install_dir)
    os.mkdir(install_dir)
    new_pkg.extract_install(install_dir)

    delta_packages = create_delta_packages_from_obj(old_packages,
                                                    new_pkg,
                                                    new_pkg_path)

    # Remove temp dir
    util.clean_dir(new_pkg_path)

    # Return delta package names
    return delta_packages

def create_delta_package(old_package, new_package):
    packages = create_delta_packages([old_package], new_package)
    return packages or None

#  Hash not equals                      (these are the deltas)
#  Hash equal but path different ones   (these are the relocations)
#  Hash and also path equal ones        (do nothing)

def find_delta(old_files, new_files):
    hashto_files = {}
    for f in new_files.list:
        hashto_files.setdefault(f.hash, []).append(f)

    new_hashes = set([f.hash for f in new_files.list])
    old_hashes = set([f.hash for f in old_files.list])
    hashes_delta = list(new_hashes - old_hashes)

    # Add to-be-replaced config files to delta package regardless of its state
    hashes_delta.extend([f.hash for f in new_files.list if f.type == 'config' and f.replace])

    deltas = []
    for h in hashes_delta:
        deltas.extend(hashto_files[h])

    # Directory hashes are None. There was a bug with PolicyKit that
    # should have an empty directory.
    if None in hashto_files:
        deltas.extend(hashto_files[None])

    return deltas

def find_relocations(oldfiles, newfiles):
    files_new = {}
    for f in newfiles.list:
        files_new.setdefault(f.hash, []).append(f)

    files_old = {}
    for f in oldfiles.list:
        files_old.setdefault(f.hash, []).append(f)

    relocations = []
    for h in list(files_new.keys()):
        if h and h in files_old:
            old_paths = [x.path for x in files_old[h]]
            for i in range(len(files_new[h])):
                if files_new[h][i].path not in old_paths:
                    relocations.append((files_old[h][0], files_new[h][i]))

    return relocations

def find_permission_changes(oldfiles, newfiles):
    files_new = {}
    for f in newfiles.list:
        files_new.setdefault(f.hash, []).append(f)

    for old_file in oldfiles.list:
        files = files_new.get(old_file.hash, [])

        for _file in files:
            if old_file.mode == _file.mode:
                continue

            path = os.path.join(ctx.config.dest_dir(), _file.path)
            if os.path.exists(path):
                yield path, int(_file.mode, 8)


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 sys

import gettext

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

import inary.operations as operations
import inary.context as ctx
import inary.util as util
import inary.atomicoperations as atomicoperations
import inary.ui as ui
import inary.db
import inary.data

@util.locked
def emerge(A):
    """
    Builds and installs the given packages from source
    @param A: list of package names -> list_of_strings
    """
    inary.db.historydb.HistoryDB().create_history("emerge")

    # A was a list, remove duplicates and expand components
    A = [str(x) for x in A]
    A_0 = A = inary.operations.helper.expand_src_components(set(A))
    ctx.ui.debug('A = {}'.format(str(A)))

    if len(A) == 0:
        ctx.ui.info(_('No packages to emerge.'))
        return

    # A |= upgrade_base(A)

    # FIXME: Errr... order_build changes type conditionally and this
    # is not good. - baris
    if not ctx.config.get_option('ignore_dependency'):
        G_f, order_inst, order_build = plan_emerge(A)
    else:
        G_f = None
        order_inst = []
        order_build = A

    if order_inst:
        ctx.ui.info(_("""The following list of packages will be installed
from repository in the respective order to satisfy dependencies:
""") + util.strlist(order_inst))
    ctx.ui.info(_("""The following list of packages will be built and
installed in the respective order to satisfy dependencies:
""") + util.strlist(order_build))

    if ctx.get_option('dry_run'):
        return

    if len(order_inst) + len(order_build) > len(A_0):
        if not ctx.ui.confirm(_('There are extra packages due to dependencies. Would you like to continue?')):
            return False

    ctx.ui.notify(ui.packagestogo, order=order_inst)

    for x in order_inst:
        atomicoperations.install_single_name(x)

    # ctx.ui.notify(ui.packagestogo, order = order_build)

    for x in order_build:
        package_names = operations.build.build(x).new_packages
        inary.operations.install.install_pkg_files(package_names, reinstall=True)  # handle inter-package deps here
        # reset counts between builds
        ctx.ui.errors = ctx.ui.warnings = 0

    # FIXME: take a look at the fixme above :(, we have to be sure
    # that order_build is a known type...
    U = set(order_build)
    U.update(order_inst)

def plan_emerge(A):
    sourcedb = inary.db.sourcedb.SourceDB()

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

    G_f = inary.data.pgraph.Digraph()

    def get_spec(name):
        if sourcedb.has_spec(name):
            return sourcedb.get_spec(name)
        else:
            raise Exception(_('Cannot find source package: \"{}\"').format(name))

    def get_src(name):
        return get_spec(name).source

    def add_src(src):
        if not str(src.name) in G_f.vertices():
            G_f.add_vertex(str(src.name), (src.version, src.release))

    def pkgtosrc(pkg):
        return sourcedb.pkgtosrc(pkg)

    # setup first
    # specfiles = [ sourcedb.get_source(x)[1] for x in A ]
    # pkgtosrc = {}
    B = A

    install_list = set()

    while len(B) > 0:
        Bp = set()
        for x in B:
            sf = get_spec(x)
            src = sf.source
            add_src(src)

            # add dependencies

            def process_dep(dep):
                if not dep.satisfied_by_installed():
                    if dep.satisfied_by_repo():
                        install_list.add(dep.package)
                        return
                    srcdep = pkgtosrc(dep.package)
                    if not srcdep in G_f.vertices():
                        Bp.add(srcdep)
                        add_src(get_src(srcdep))
                    if not src.name == srcdep:  # firefox - firefox-devel thing
                        G_f.add_edge(src.name, srcdep)

            for builddep in src.buildDependencies:
                process_dep(builddep)

            for pkg in sf.packages:
                for rtdep in pkg.packageDependencies:
                    process_dep(rtdep)
        B = Bp

    if ctx.config.get_option('debug'):
        G_f.write_graphviz(sys.stdout)
    order_build = G_f.topological_sort()
    order_build.reverse()

    G_f2, order_inst = inary.operations.install.plan_install_pkg_names(install_list)

    return G_f, order_inst, order_build

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 os

import gettext

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

import inary.context as ctx
import inary.util as util
import inary.ui as ui
import inary.analyzer.conflict
import inary.db

def reorder_base_packages(order):
    componentdb = inary.db.componentdb.ComponentDB()

    """system.base packages must be first in order"""
    systembase = componentdb.get_union_component('system.base').packages

    systembase_order = []
    nonbase_order = []
    for pkg in order:
        if pkg in systembase:
            systembase_order.append(pkg)
        else:
            nonbase_order.append(pkg)

    return systembase_order + nonbase_order

def check_conflicts(order, packagedb):
    """check if upgrading to the latest versions will cause havoc
    done in a simple minded way without regard for dependencies of
    conflicts, etc."""

    (C, D, pkg_conflicts) = inary.analyzer.conflict.calculate_conflicts(order, packagedb)

    if D:
        raise Exception(_("Selected packages \"[{}]\" are in conflict with each other.").format(util.strlist(list(D))))

    if pkg_conflicts:
        conflicts = ""
        for pkg in list(pkg_conflicts.keys()):
            conflicts += _(" - [\"{0}\" conflicts with: \"{1}\"]\n").format(pkg, util.strlist(pkg_conflicts[pkg]))

        ctx.ui.info(_("The following packages have conflicts:\n{}").format(conflicts))

        if not ctx.ui.confirm(_('Remove the following conflicting packages?')):
            raise Exception(_("Conflicting packages should be removed to continue."))

    return list(C)

def expand_src_components(A):
    componentdb = inary.db.componentdb.ComponentDB()
    Ap = set()
    for x in A:
        if componentdb.has_component(x):
            Ap = Ap.union(componentdb.get_union_component(x).sources)
        else:
            Ap.add(x)
    return Ap

def calculate_free_space_needed(order):
    total_needed = 0
    installdb = inary.db.installdb.InstallDB()
    packagedb = inary.db.packagedb.PackageDB()

    for pkg in [packagedb.get_package(name) for name in order]:
        if installdb.has_package(pkg.name):
            (version, release, build, distro, distro_release) = installdb.get_version_and_distro_release(pkg.name)
            # inary distro upgrade should not use delta support
            if distro == pkg.distribution and distro_release == pkg.distributionRelease:
                delta = pkg.get_delta(release)

            ignore_delta = ctx.config.values.general.ignore_delta

            installed_release_size = installdb.get_package(pkg.name).installedSize

            if delta and not ignore_delta:
                pkg_size = delta.installedSize
            else:
                pkg_size = pkg.installedSize - installed_release_size

            total_needed += pkg_size

        else:
            total_needed += int(packagedb.get_package(pkg.name).installedSize)

    needed, symbol = util.human_readable_size(total_needed)

    if total_needed < 0:
          ctx.ui.info(_("After this operation, {:.2f} {} space will be freed.").format(abs(int(needed)), symbol), color='cyan')
    else:
          ctx.ui.info(_("After this operation, {:.2f} {} space will be used.").format(abs(int(needed)), symbol), color='cyan')

    return total_needed

def calculate_download_sizes(order):
    total_size = cached_size = 0

    installdb = inary.db.installdb.InstallDB()
    packagedb = inary.db.packagedb.PackageDB()

    try:
        cached_packages_dir = ctx.config.cached_packages_dir()
    except OSError:
        # happens when cached_packages_dir tried to be created by an unpriviledged user
        cached_packages_dir = None

    for pkg in [packagedb.get_package(name) for name in order]:

        delta = None
        if installdb.has_package(pkg.name):
            (version, release, build, distro, distro_release) = installdb.get_version_and_distro_release(pkg.name)
            # inary distro upgrade should not use delta support
            if distro == pkg.distribution and distro_release == pkg.distributionRelease:
                delta = pkg.get_delta(release)

        ignore_delta = ctx.config.values.general.ignore_delta

        if delta and not ignore_delta:
            fn = os.path.basename(delta.packageURI)
            pkg_hash = delta.packageHash
            pkg_size = delta.packageSize
        else:
            fn = os.path.basename(pkg.packageURI)
            pkg_hash = pkg.packageHash
            pkg_size = pkg.packageSize

        if cached_packages_dir:
            path = util.join_path(cached_packages_dir, fn)
            # check the file and sha1sum to be sure it _is_ the cached package
            if os.path.exists(path) and util.sha1_file(path) == pkg_hash:
                cached_size += pkg_size
            elif os.path.exists("{}.part".format(path)):
                cached_size += os.stat("{}.part".format(path)).st_size

        total_size += pkg_size

    ctx.ui.notify(ui.cached, logging=False, total=total_size, cached=cached_size)
    return total_size, cached_size

def get_package_requirements(packages):
    """
    Returns a dict with two keys - systemRestart, serviceRestart - with package lists as their values
    @param packages: list of package names -> list_of_strings

    >>> lu = inary.api.list_upgrades()

    >>> requirements = inary.api.get_package_requirements(lu)

    >>> print requirements
    >>> { "systemRestart":["kernel", "module-alsa-driver"], "serviceRestart":["mysql-server", "memcached", "postfix"] }

    """

    actions = ("systemRestart", "serviceRestart")
    requirements = dict((action, []) for action in actions)

    installdb = inary.db.installdb.InstallDB()
    packagedb = inary.db.packagedb.PackageDB()

    for i_pkg in packages:
        try:
            pkg = packagedb.get_package(i_pkg)
        except Exception:  # FIXME: Should catch RepoItemNotFound exception
            pass

        version, release, build = installdb.get_version(i_pkg)
        pkg_actions = pkg.get_update_actions(release)

        for action_name in pkg_actions:
            if action_name in actions:
                requirements[action_name].append(pkg.name)

    return requirements


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 gettext

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

import inary.errors
import inary.context as ctx
import inary.util as util
import inary.db
import inary.fetcher
import inary.operations as operations

class PackageNotFound(inary.errors.Error):
    pass

def __pkg_already_installed(name, pkginfo):
    installdb = inary.db.installdb.InstallDB()
    if not installdb.has_package(name):
        return False

    ver, rel = str(pkginfo).split("-")[:2]
    return (ver, rel) == installdb.get_version(name)[:-1]

def __listactions(actions):
    beinstalled = []
    beremoved = []
    configs = []

    installdb = inary.db.installdb.InstallDB()
    for pkg in actions:
        action, pkginfo, operation = actions[pkg]
        if action == "install":
            if __pkg_already_installed(pkg, pkginfo):
                continue
            beinstalled.append("{0}-{1}".format(pkg, pkginfo))
            configs.append((pkg, operation))
        else:
            if installdb.has_package(pkg):
                beremoved.append("{}".format(pkg))

    return beinstalled, beremoved, configs

def __getpackageurl_binman(package):
    packagedb = inary.db.packagedb.PackageDB()
    repodb = inary.db.repodb.RepoDB()
    pkg, ver = inary.util.parse_package_name(package)

    reponame = None
    try:
        reponame = packagedb.which_repo(pkg)
    except Exception:
        # Maybe this package is obsoluted from repository
        for repo in repodb.get_binary_repos():
            if pkg in packagedb.get_obsoletes(repo):
                reponame = repo

    if not reponame:
        raise PackageNotFound

    package_ = packagedb.get_package(pkg)
    repourl = repodb.get_repo_url(reponame)
    base_package = os.path.dirname(package_.packageURI)
    repo_base = os.path.dirname(repourl)
    possible_url = os.path.join(repo_base, base_package, package)
    ctx.ui.info(_("Package \"{0}\" found in repository \"{1}\".").format(pkg, reponame))

    # return _possible_ url for this package
    return possible_url

def __getpackageurl(package):
    packagedb = inary.db.packagedb.PackageDB()
    repodb = inary.db.repodb.RepoDB()
    pkg, ver = util.parse_package_name(package)

    reponame = None
    try:
        reponame = packagedb.which_repo(pkg)
    except Exception:
        # Maybe this package is obsoluted from repository
        for repo in repodb.get_binary_repos():
            if pkg in packagedb.get_obsoletes(repo):
                reponame = repo

    if not reponame:
        raise PackageNotFound

    repourl = repodb.get_repo_url(reponame)
    ctx.ui.info(_("Package \"{0}\" found in repository \"{1}\".").format(pkg, reponame))

    # return _possible_ url for this package
    return os.path.join(os.path.dirname(repourl),
                        util.parse_package_dir_path(package),
                        package)

def fetch_remote_file(package, errors):
    try:
        uri = inary.file.File.make_uri(__getpackageurl_binman(package))
    except PackageNotFound:
        errors.append(package)
        ctx.ui.info(_("\"{}\" could not be found.").format(package), color="red")
        return False

    dest = ctx.config.cached_packages_dir()
    filepath = os.path.join(dest, uri.filename())
    if not os.path.exists(filepath):
        failed = True
        try:
            inary.fetcher.fetch_url(uri, dest, ctx.ui.Progress)
        except inary.fetcher.FetchError:
            errors.append(package)
            ctx.ui.info(_("\"{}\" could not be found.").format(package), color="red")
            failed = True
        if failed:
            try:
                new_uri = inary.file.File.make_uri(__getpackageurl(package))
                inary.fetcher.fetch_url(new_uri, dest, ctx.ui.Progress)
            except:
                errors.append(package)
                ctx.ui.info(_("\"{}\" could not be found.").format(package), "red")
                return False

    else:
        ctx.ui.info(_('\"{}\" [cached]').format(uri.filename()))
    return True

def get_snapshot_actions(operation):
    actions = {}
    snapshot_pkgs = set()
    installdb = inary.db.installdb.InstallDB()

    for pkg in operation.packages:
        snapshot_pkgs.add(pkg.name)
        actions[pkg.name] = ("install", pkg.before, operation.no)

    for pkg in set(installdb.list_installed()) - snapshot_pkgs:
        actions[pkg] = ("remove", None, None)

    return actions

def get_takeback_actions(operation):
    actions = {}
    historydb = inary.db.historydb.HistoryDB()

    for operation in historydb.get_till_operation(operation):
        if operation.type == "snapshot":
            pass

        for pkg in operation.packages:
            if pkg.operation in ["upgrade", "downgrade", "remove"]:
                actions[pkg.name] = ("install", pkg.before, operation.no)
            if pkg.operation == "install":
                actions[pkg.name] = ("remove", None, operation.no)

    return actions

def plan_takeback(operation):
    historydb = inary.db.historydb.HistoryDB()
    op = historydb.get_operation(operation)
    if op.type == "snapshot":
        actions = get_snapshot_actions(op)
    else:
        actions = get_takeback_actions(operation)

    return __listactions(actions)

@util.locked
def takeback(operation):
    """
    Takes back the system to a previous state. Uses inary history to find out which packages were
    installed at the time _after_ the given operation that the system is requested to be taken back.
    @param operation: number of the operation that the system will be taken back -> integer
    """
    historydb = inary.db.historydb.HistoryDB()
    historydb.create_history("takeback")
    beinstalled, beremoved, configs = plan_takeback(operation)
    if not beinstalled and not beremoved:
        ctx.ui.info(_("There is no packages to taking back (installing or removing)."))

    if beinstalled:
        ctx.ui.info(_("Following packages will be installed:\n") + util.strlist(beinstalled))

    if beremoved:
        ctx.ui.info(_("Following packages will be removed:\n") + util.strlist(beremoved))

    if (beremoved or beinstalled) and not ctx.ui.confirm(_('Would you like to continue?')):
        return

    errors = []
    paths = []
    for pkg in beinstalled:
        ctx.ui.info(_("Downloading {} / {} => [{}]").format(beinstalled.index(pkg) + 1, len(beinstalled),pkg), color="yellow")
        pkg += ctx.const.package_suffix
        if fetch_remote_file(pkg, errors):
            paths.append(os.path.join(ctx.config.cached_packages_dir(), pkg))

    if errors:
        ctx.ui.info(_("\nFollowing packages could not be found in repositories and are not cached:\n") +
                    util.strlist(errors))
        if not ctx.ui.confirm(_('Would you like to continue?')):
            return

    if beremoved:
        operations.remove.remove(beremoved, True, True)

    if paths:
        operations.install.install_pkg_files(paths, True)

    for pkg, operation in configs:
        historydb.load_config(operation, pkg)

def get_takeback_plan(operation):
    """
    Calculates and returns the plan of the takeback operation that contains information of which
    packages are going to be removed and which packages are going to be installed
    @param operation: number of the operation that the system will be taken back -> integer
    """

    beinstalled, beremoved, configs = plan_takeback(operation)
    return beinstalled, beremoved

@util.locked
def snapshot():
    """
    Takes snapshot of the system packages. The snapshot is only a record of which packages are currently
    installed. The record is kept by inary history mechanism as it works automatically on install, remove
    and upgrade operations.
    """

    installdb = inary.db.installdb.InstallDB()
    historydb = inary.db.historydb.HistoryDB()
    historydb.create_history("snapshot")

    li = installdb.list_installed()
    progress = ctx.ui.Progress(len(li))

    processed = 0
    for name in installdb.list_installed():
        package = installdb.get_package(name)
        historydb.add_package(pkgBefore=package, operation="snapshot")
        # Save changed config files of the package in snapshot
        for f in installdb.get_files(name).list:
            if f.type == "config" and util.config_changed(f):
                fpath = util.join_path(ctx.config.dest_dir(), f.path)
                historydb.save_config(name, fpath)

        processed += 1
        ctx.ui.display_progress(operation="snapshot",
                                percent=progress.update(processed),
                                info=_("Taking snapshot of the system."))

    historydb.update_history()


code:
# -*- coding: utf-8 -*-
#
# Copyright (C) 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 inary.context as ctx
import inary.data
import inary.db
import inary.errors
import inary.package

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

def info(package, installed=False):
    if package.endswith(ctx.const.package_suffix):
        return info_file(package)
    else:
        metadata, files, repo = info_name(package, installed)
        return metadata, files

def info_file(package_fn):
    if not os.path.exists(package_fn):
        raise inary.errors.Error(_('File \"{}\" not found.').format(package_fn))

    package = inary.package.Package(package_fn)
    package.read()
    return package.metadata, package.files

def info_name(package_name, useinstalldb=False):
    """Fetch package information for the given package."""

    installdb = inary.db.installdb.InstallDB()
    packagedb = inary.db.packagedb.PackageDB()
    if useinstalldb:
        package = installdb.get_package(package_name)
        repo = None
    else:
        package, repo = packagedb.get_package_repo(package_name)

    metadata = inary.data.metadata.MetaData()
    metadata.package = package
    # FIXME: get it from sourcedb if available
    metadata.source = None
    # TODO: fetch the files from server if possible (wow, you maniac -- future exa)
    if useinstalldb and installdb.has_package(package.name):
        try:
            files = installdb.get_files(package.name)
        except inary.errors.Error as e:
            ctx.ui.warning(e)
            files = None
    else:
        files = None
    return metadata, files, repo


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 sys
import zipfile

import gettext

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

import inary.package
import inary.context as ctx
import inary.util as util
import inary.atomicoperations as atomicoperations
import inary.operations as operations
import inary.data.pgraph as pgraph
import inary.ui as ui
import inary.db
import inary.libraries.sort as sort

def install_pkg_names(A, reinstall=False, extra=False):
    """This is the real thing. It installs packages from
    the repository, trying to perform a minimum number of
    installs"""

    installdb = inary.db.installdb.InstallDB()
    packagedb = inary.db.packagedb.PackageDB()
    # A was a list, remove duplicates
    A_0 = A = set(A)

    # filter packages that are already installed
    if not reinstall:
        Ap = set([x for x in A if not installdb.has_package(x)])
        d = A - Ap
        if len(d) > 0:
            ctx.ui.warning(_("The following package(s) are already installed "
                             "and are not going to be installed again:"))
            ctx.ui.info(util.format_by_columns(sorted(d)))
            A = Ap

    if len(A) == 0:
        ctx.ui.info(_('No packages to install.'))
        return True

    A |= operations.upgrade.upgrade_base(A)

    if not ctx.config.get_option('ignore_dependency'):
        ctx.ui.info(_('Checking dependencies for install...'),color="brightpurple")
        G_f, order = plan_install_pkg_names(A)
    else:
        G_f = None
        order = list(A)

    componentdb = inary.db.componentdb.ComponentDB()

    # Bug 4211
    if componentdb.has_component('system.base'):
        order = operations.helper.reorder_base_packages(order)

    if len(order) > 1:
        ctx.ui.info(_("Following packages will be installed:"), color="brightblue")
        ctx.ui.info(util.format_by_columns(sorted(order)))

    operations.helper.calculate_download_sizes(order)
    operations.helper.calculate_free_space_needed(order)

    if ctx.get_option('dry_run'):
        return True

    extra_packages = set(order) - A_0
    if extra_packages:
        if not ctx.ui.confirm(_('There are extra packages due to dependencies. Would you like to continue?')):
            raise Exception(_('External dependencies not satisfied.'))

    ctx.ui.notify(ui.packagestogo, order=order)

    conflicts = []
    if not ctx.get_option('ignore_package_conflicts'):
        conflicts = operations.helper.check_conflicts(order, packagedb)

    paths = []
    extra_paths = {}
    sorted_order=sort.sort_auto(order)
    for x in sorted_order:
        ctx.ui.info(_("Downloading {} / {} => [{}]").format(sorted_order.index(x) + 1, len(sorted_order),x), color="yellow")
        install_op = atomicoperations.Install.from_name(x)
        paths.append(install_op.package_fname)

        if x in extra_packages or (extra and x in A):
            extra_paths[install_op.package_fname] = x
        elif reinstall and x in installdb.installed_extra:
            installdb.installed_extra.remove(x)
            with open(os.path.join(ctx.config.info_dir(), ctx.const.installed_extra), "w") as ie_file:
                ie_file.write("\n".join(installdb.installed_extra) + ("\n" if installdb.installed_extra else ""))

    # fetch to be installed packages but do not install them.
    if ctx.get_option('fetch_only'):
        return

    if conflicts:
        operations.remove.remove_conflicting_packages(conflicts)

    for path in order:
        if installdb.has_package(path):
            remove_op = atomicoperations.Remove(path)
            remove_op.run_preremove()
    for path in paths:
        install_op = atomicoperations.Install(path)
        install_op.preInstall()

    for path in paths:
        install_op = atomicoperations.Install(path)
        basename=path.split("/")[-1]
        ctx.ui.info(_("Installing {} / {} => [{}]") .format(paths.index(path) + 1, len(paths),basename), color="yellow")
        install_op.install(False)
        try:
            with open(os.path.join(ctx.config.info_dir(), ctx.const.installed_extra), "a") as ie_file:
                ie_file.write("{}\n".format(extra_paths[path]))
            installdb.installed_extra.append(extra_paths[path])
        except KeyError:
            pass

    for path in paths:
        install_op = atomicoperations.Install(path)
        install_op.postInstall()

    return True

def install_pkg_files(package_URIs, reinstall=False):
    """install a number of inary package files"""

    installdb = inary.db.installdb.InstallDB()
    ctx.ui.debug('A = {}'.format(str(package_URIs)))

    for x in package_URIs:
        if not x.endswith(ctx.const.package_suffix):
            raise Exception(_('Mixing file names and package names not supported yet.'))

    # filter packages that are already installed
    tobe_installed, already_installed = [], set()
    if not reinstall:
        for x in package_URIs:
            if not x.endswith(ctx.const.delta_package_suffix) and x.endswith(ctx.const.package_suffix):
                pkg_name, pkg_version = util.parse_package_name(os.path.basename(x))
                if installdb.has_package(pkg_name):
                    already_installed.add(pkg_name)
                else:
                    tobe_installed.append(x)
        if already_installed:
            ctx.ui.warning(_("The following package(s) are already installed "
                             "and are not going to be installed again:"))
            ctx.ui.info(util.format_by_columns(sorted(already_installed)))
        package_URIs = tobe_installed

    if ctx.config.get_option('ignore_dependency'):
        # simple code path then
        for x in package_URIs:
            atomicoperations.install_single_file(x, reinstall)
        return True

    # read the package information into memory first
    # regardless of which distribution they come from
    d_t = {}
    dfn = {}
    for x in package_URIs:
        try:
            package = inary.package.Package(x)
            package.read()
        except zipfile.BadZipfile:
            # YALI needed to get which file is broken
            raise zipfile.BadZipfile(x)
        name = str(package.metadata.package.name)
        d_t[name] = package.metadata.package
        dfn[name] = x

    # check packages' DistributionReleases and Architecture
    if not ctx.get_option('ignore_check'):
        for x in list(d_t.keys()):
            pkg = d_t[x]
            if pkg.distributionRelease != ctx.config.values.general.distribution_release:
                raise Exception(_('Package \"{0}\" is not compatible with your distribution release \'{1}\' \'{2}\'.').format(
                    x, ctx.config.values.general.distribution,
                    ctx.config.values.general.distribution_release))
            if pkg.architecture != ctx.config.values.general.architecture:
                raise Exception(
                    _('Package \"{0}\" (\'{1}\') is not compatible with your \'{2}\' architecture.').format(x, pkg.architecture,
                                                                                ctx.config.values.general.architecture))

    def satisfiesDep(dep):
        # is dependency satisfied among available packages
        # or packages to be installed?
        return dep.satisfied_by_installed() or dep.satisfied_by_dict_repo(d_t)

    # for this case, we have to determine the dependencies
    # that aren't already satisfied and try to install them
    # from the repository
    dep_unsatis = []
    for name in list(d_t.keys()):
        pkg = d_t[name]
        deps = pkg.runtimeDependencies()
        for dep in deps:
            if not satisfiesDep(dep) and dep.package not in [x.package for x in dep_unsatis]:
                dep_unsatis.append(dep)

    # now determine if these unsatisfied dependencies could
    # be satisfied by installing packages from the repo
    for dep in dep_unsatis:
        if not dep.satisfied_by_repo():
            raise Exception(_('External dependencies not satisfied: \"{}\"').format(dep))

    # if so, then invoke install_pkg_names
    extra_packages = [x.package for x in dep_unsatis]
    if extra_packages:
        ctx.ui.warning(_("The following packages will be installed "
                         "in order to satisfy dependencies:"))
        ctx.ui.info(util.format_by_columns(sorted(extra_packages)))
        if not ctx.ui.confirm(_('Would you like to continue?')):
            raise Exception(_('External dependencies not satisfied.'))
        install_pkg_names(extra_packages, reinstall=True, extra=True)

    class PackageDB:
        @staticmethod
        def get_package(key, repo=None):
            return d_t[str(key)]

    packagedb = PackageDB()

    A = list(d_t.keys())

    if len(A) == 0:
        ctx.ui.info(_('No packages to install.'))
        return

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

    G_f = pgraph.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
    while len(B) > 0:
        Bp = set()
        for x in B:
            pkg = packagedb.get_package(x)
            for dep in pkg.runtimeDependencies():
                if dep.satisfied_by_dict_repo(d_t):
                    if not dep.package in G_f.vertices():
                        Bp.add(str(dep.package))
                    G_f.add_dep(x, dep)
        B = Bp
    if ctx.config.get_option('debug'):
        G_f.write_graphviz(sys.stdout)
    order = G_f.topological_sort()
    if not ctx.get_option('ignore_package_conflicts'):
        conflicts = operations.helper.check_conflicts(order, packagedb)
        if conflicts:
            operations.remove.remove_conflicting_packages(conflicts)
    order.reverse()
    ctx.ui.info(_('Installation order: ') + util.strlist(order))

    if ctx.get_option('dry_run'):
        return True

    ctx.ui.notify(ui.packagestogo, order=order)

    for x in order:
        atomicoperations.install_single_file(dfn[x], reinstall)

    return True

def plan_install_pkg_names(A):
    # try to construct a inary graph of packages to
    # install / reinstall
    packagedb = inary.db.packagedb.PackageDB()

    G_f = pgraph.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

    while len(B) > 0:
        Bp = set()
        for x in B:
            pkg = packagedb.get_package(x)
            for dep in pkg.runtimeDependencies():
                ctx.ui.info(_(' -> checking {}').format(str(dep)), verbose=True)
                # we don't deal with already *satisfied* dependencies
                if not dep.satisfied_by_installed():
                    if not dep.satisfied_by_repo(packagedb=packagedb):
                        raise Exception(_('\"{0}\" dependency of package \"{1}\" is not satisfied.').format(dep, pkg.name))
                    if not dep.package in G_f.vertices():
                        Bp.add(str(dep.package))
                    G_f.add_dep(x, dep)
            if ctx.config.values.general.allow_docs:
                dep = x + ctx.const.doc_package_end
                if packagedb.has_package(dep):
                    Bp.add(dep)
                    G_f.add_package(dep)
            if ctx.config.values.general.allow_pages:
                dep = x + ctx.const.info_package_end
                if packagedb.has_package(dep):
                    Bp.add(dep)
                    G_f.add_package(dep)
            if ctx.config.values.general.allow_dbginfo:
                dep = x + ctx.const.debug_name_suffix
                if packagedb.has_package(dep):
                    Bp.add(dep)
                    G_f.add_package(dep)
            if ctx.config.values.general.allow_static:
                dep = x + ctx.const.static_name_suffix
                if packagedb.has_package(dep):
                    Bp.add(dep)
                    G_f.add_package(dep)

        B = Bp
    if ctx.config.get_option('debug'):
        G_f.write_graphviz(sys.stdout)
    order = G_f.topological_sort()
    order.reverse()
    return G_f, order

def get_install_order(packages):
    # LOOK: This function is important
    """
    Return a list of packages in the installation order with extra needed
    dependencies -> list_of_strings
    @param packages: list of package names -> list_of_strings
    """
    i_graph, order = plan_install_pkg_names(packages)
    return order

@util.locked
def install(packages, reinstall=False, ignore_file_conflicts=False, ignore_package_conflicts=False):
    """
    Returns True if no errors occured during the operation
    @param packages: list of package names -> list_of_strings
    @param reinstall: reinstalls already installed packages else ignores
    @param ignore_file_conflicts: Ignores file conflicts during the installation and continues to install
    packages.
    @param ignore_package_conflicts: Ignores package conflicts during the installation and continues to
    install packages.
    """

    inary.db.historydb.HistoryDB().create_history("install")

    if not ctx.get_option('ignore_file_conflicts'):
        ctx.set_option('ignore_file_conflicts', ignore_file_conflicts)

    if not ctx.get_option('ignore_package_conflicts'):
        ctx.set_option('ignore_package_conflicts', ignore_package_conflicts)

    # Install inary package files or inary packages from a repository
    if packages and packages[0].endswith(ctx.const.package_suffix):
        return install_pkg_files(packages, reinstall)
    else:
        return install_pkg_names(packages, reinstall)


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 sys

import gettext

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

import inary.atomicoperations as atomicoperations
import inary.context as ctx
import inary.data.pgraph as pgraph
import inary.db
import inary.errors
import inary.util as util
import inary.ui as ui

@util.locked
def remove(A, ignore_dep=False, ignore_safety=False):
    """
    Removes the given packages from the system
    @param A: list of package names -> list_of_strings
    @param ignore_dep: removes packages without looking into theirs reverse deps if True
    @param ignore_safety: system.base packages can also be removed if True
    """
    inary.db.historydb.HistoryDB().create_history("remove")
    componentdb = inary.db.componentdb.ComponentDB()
    installdb = inary.db.installdb.InstallDB()

    A = [str(x) for x in A]

    # filter packages that are not installed
    A_0 = A = set(A)

    if not ctx.get_option('ignore_safety') and not ctx.config.values.general.ignore_safety and not ignore_safety:
        if componentdb.has_component('system.base'):
            systembase = set(componentdb.get_union_component('system.base').packages)
            refused = A.intersection(systembase)
            if refused:
                raise inary.errors.Error(_("Safety switch prevents the removal of "
                                           "following packages:\n") +
                                         util.format_by_columns(sorted(refused)))
                A = A - systembase
        else:
            ctx.ui.warning(_("Safety switch: The component system.base cannot be found."))

    Ap = []
    for x in A:
        if installdb.has_package(x):
            Ap.append(x)
        else:
            ctx.ui.info(_('Package \"{}\" does not exist. Cannot remove.').format(x))
    A = set(Ap)

    if len(A) == 0:
        ctx.ui.info(_('No packages to remove.'))
        return False

    if not ctx.config.get_option('ignore_dependency') and not ignore_dep:
        G_f, order = plan_remove(A)
    else:
        G_f = None
        order = util.unique_list(A)

    ctx.ui.info(_("""The following list of packages will be removed in the respective order to satisfy dependencies:"""), color='green')
    ctx.ui.info(util.strlist(order))

    removal_size = 0
    for pkg in [installdb.get_package(name) for name in order]:
        removal_size += pkg.installedSize

    removal_size, symbol  = util.human_readable_size(removal_size)
    ctx.ui.info(_('After this operation, {:.2f} {} space will be freed.').format(removal_size, symbol), color='cyan')
    del removal_size, symbol

    if len(order) > len(A_0):
        if not ctx.ui.confirm(_('Would you like to continue?')):
            ctx.ui.warning(_('Package removal declined.'))
            return False

    if ctx.get_option('dry_run'):
        return

    ctx.ui.notify(ui.packagestogo, order=order)

    for x in order:
        if installdb.has_package(x):
            atomicoperations.remove_single(x)
            if x in installdb.installed_extra:
                installdb.installed_extra.remove(x)
                with open(os.path.join(ctx.config.info_dir(), ctx.const.installed_extra), "w") as ie_file:
                    ie_file.write("\n".join(installdb.installed_extra) + ("\n" if installdb.installed_extra else ""))

        else:
            ctx.ui.info(_('Package \"{}\" is not installed. Cannot remove.').format(x))

def plan_remove(A):
    # try to construct a inary graph of packages to
    # install / reinstall

    installdb = inary.db.installdb.InstallDB()
    packagedb = inary.db.packagedb.PackageDB()

    G_f = pgraph.PGraph(installdb)  # 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
    while len(B) > 0:
        Bp = set()
        for x in B:
            rev_deps = installdb.get_rev_deps(x)
            for (rev_dep, depinfo) in rev_deps:
                # we don't deal with uninstalled rev deps
                # and unsatisfied dependencies (this is important, too)
                # satisfied_by_any_installed_other_than is for AnyDependency
                if installdb.has_package(rev_dep) and \
                        depinfo.satisfied_by_installed() and not \
                        depinfo.satisfied_by_any_installed_other_than(x):
                    if not rev_dep in G_f.vertices():
                        Bp.add(rev_dep)
                        G_f.add_plain_dep(rev_dep, x)

                    #IDEA: Optimize
                    if ctx.config.values.general.allow_docs:
                        doc_package = x + ctx.const.doc_package_end
                        if packagedb.has_package(doc_package):
                            Bp.add(doc_package)

                    if ctx.config.values.general.allow_pages:
                        info_package = x + ctx.const.info_package_end
                        if packagedb.has_package(info_package):
                            Bp.add(info_package)

                    if ctx.config.values.general.allow_dbginfo:
                        dbg_package = x + ctx.const.debug_name_suffix
                        if packagedb.has_package(dbg_package):
                            Bp.add(dbg_package)

                    if ctx.config.values.general.allow_static:
                        static_package = x + ctx.const.static_name_suffix
                        if packagedb.has_package(static_package):
                            Bp.add(static_package)

        B = Bp
    if ctx.config.get_option('debug'):
        G_f.write_graphviz(sys.stdout)
    order = G_f.topological_sort()
    return G_f, order

def remove_conflicting_packages(conflicts):
    if remove(conflicts, ignore_dep=True, ignore_safety=True):
        raise Exception(_("Conflicts remain."))

def remove_obsoleted_packages():
    installdb = inary.db.installdb.InstallDB()
    packagedb = inary.db.packagedb.PackageDB()
    obsoletes = list(filter(installdb.has_package, packagedb.get_obsoletes()))
    if obsoletes:
        if remove(obsoletes, ignore_dep=True, ignore_safety=True):
            raise Exception(_("Obsoleted packages remaining."))

def remove_replaced_packages(replaced):
    if remove(replaced, ignore_dep=True, ignore_safety=True):
        raise Exception(_("Replaced package remains."))

def get_remove_order(packages):
    """
    Return a list of packages in the remove order -> list_of_strings
    @param packages: list of package names -> list_of_strings
    """
    i_graph, order = plan_remove(packages)
    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 gettext

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

import inary.context as ctx
import inary.db
import inary.data
import inary.errors
import inary.file
import inary.ui
import inary.uri
import inary.util as util

@util.locked
def add_repo(name, indexuri, at=None):
    import re
    if not re.match("^[0-9{}\-\\_\\.\s]*$".format(str(util.letters())), name):
        raise inary.errors.Error(_('Not a valid repository name.'))
    repodb = inary.db.repodb.RepoDB()
    if repodb.has_repo(name):
        raise inary.errors.Error(_('Repository \"{}\" already present.').format(name))
    elif repodb.has_repo_url(indexuri, only_active=False):
        repo = repodb.get_repo_by_url(indexuri)
        raise inary.errors.Error(_('Repository \"{}\" already present with name \"{}\".').format(name, repo))
    else:
        repo = inary.db.repodb.Repo(inary.uri.URI(indexuri))
        repodb.add_repo(name, repo, at=at)
        ctx.ui.info(_('Flushing database caches...'), verbose=True)
        inary.db.flush_caches()
        ctx.ui.info(_('Repository \"{}\" added to system.').format(name))

@util.locked
def remove_repo(name):
    repodb = inary.db.repodb.RepoDB()
    if repodb.has_repo(name):
        repodb.remove_repo(name)
        ctx.ui.info(_('Flushing database caches...'), verbose=True)
        inary.db.flush_caches()
        ctx.ui.info(_('Repository \"{}\" removed from system.').format(name))
    else:
        raise inary.errors.Error(_('Repository \"{}\" does not exist. Cannot remove.').format(name))

@util.locked
def set_repo_activity(name, active):
    """
    Changes the activity status of a  repository. Inactive repositories will have no effect on
    upgrades and installs.
    @param name: name of the repository
    @param active: the new repository status
    """
    repodb = inary.db.repodb.RepoDB()
    if active:
        repodb.activate_repo(name)
    else:
        repodb.deactivate_repo(name)
    ctx.ui.info(_('Regenerating database caches...'), verbose=True)
    inary.db.regenerate_caches()

@util.locked
def update_repos(repos, force=False):
    inary.db.historydb.HistoryDB().create_history("repoupdate")
    updated = False
    try:
        for repo in repos:
            updated |= __update_repo(repo, force)
    finally:
        if updated:
            ctx.ui.info(_('Regenerating database caches...'), verbose=True)
            inary.db.regenerate_caches()

@util.locked
def update_repo(repo, force=False):
    inary.db.historydb.HistoryDB().create_history("repoupdate")
    updated = __update_repo(repo, force)
    if updated:
        ctx.ui.info(_('Regenerating database caches...'), verbose=True)
        inary.db.regenerate_caches()

def __update_repo(repo, force=False):
    ctx.ui.status(_('Updating package repository: \"{}\"').format(repo), push_screen=False)
    ctx.ui.notify(inary.ui.updatingrepo, name=repo)
    repodb = inary.db.repodb.RepoDB()
    index = inary.data.index.Index()
    if repodb.has_repo(repo):
        repouri = repodb.get_repo(repo).indexuri.get_uri()
        try:
            index.read_uri_of_repo(repouri, repo)
        except inary.file.AlreadyHaveException:
            ctx.ui.info(_('\"{}\" repository information is up-to-date.').format(repo))
            if force:
                ctx.ui.info(_('Updating database at any rate as requested.'))
                index.read_uri_of_repo(repouri, repo, force=force)
            else:
                return False

        inary.db.historydb.HistoryDB().update_repo(repo, repouri, "update")

        repodb.check_distribution(repo)

        try:
            index.check_signature(repouri, repo)
        except inary.file.InvalidSignature as e:
            ctx.ui.info(_("Repository \"{}\" is deactivated because of GPG Signature fail.").format(repo),
                          color='brightyellow')
            set_repo_activity(repo, False)
            ctx.ui.warning(e)

        ctx.ui.info(_('Package database updated.'))
    else:
        raise inary.errors.Error(_('No repository named \"{}\" found.').format(repo))

    return True


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.context as ctx
import inary.db

def search_package(terms, lang=None, repo=None):
    """
    Return a list of packages that contains all the given terms either in its name, summary or
    description -> list_of_strings
    @param terms: a list of terms used to search package -> list_of_strings
    @param lang: language of the summary and description
    @param repo: Repository of the packages. If repo is None than returns a list of all the packages
    in all the repositories that meets the search
    """
    packagedb = inary.db.packagedb.PackageDB()
    return packagedb.search_package(terms, lang, repo)

def search_installed(terms, lang=None):
    """
    Return a list of components that contains all the given terms either in its name, summary or
    description -> list_of_strings
    @param terms: a list of terms used to search components -> list_of_strings
    @param lang: language of the summary and description
    """
    installdb = inary.db.installdb.InstallDB()
    return installdb.search_package(terms, lang)

def search_source(terms, lang=None, repo=None):
    """
    Return a list of source packages that contains all the given terms either in its name, summary or
    description -> list_of_strings
    @param terms: a list of terms used to search source package -> list_of_strings
    @param lang: language of the summary and description
    @param repo: Repository of the source packages. If repo is None than returns a list of all the source
    packages in all the repositories that meets the search
    """
    sourcedb = inary.db.sourcedb.SourceDB()
    return sourcedb.search_spec(terms, lang, repo)

def search_component(terms, lang=None, repo=None):
    """
    Return a list of components that contains all the given terms either in its name, summary or
    description -> list_of_strings
    @param terms: a list of terms used to search components -> list_of_strings
    @param lang: language of the summary and description
    @param repo: Repository of the components. If repo is None than returns a list of all the components
    in all the repositories that meets the search
    """
    componentdb = inary.db.componentdb.ComponentDB()
    return componentdb.search_component(terms, lang, repo)

def search_file(term):
    """
    Returns a tuple of package and matched files list that matches the files of the installed
    packages -> list_of_tuples
    @param term: used to search file -> list_of_strings

    >>> import inary.operations
    >>> files = inary.operations.search.search_file("kvm-")

    >>> print files

    >>> [("kvm", (["lib/modules/2.6.18.8-86/extra/kvm-amd.ko","lib/modules/2.6.18.8-86/extra/kvm-intel.ko"])),]
    """
    if term.startswith("/"):
        term = term[1:]
    return ctx.filesdb.search_file(term)


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 sys

import gettext

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

import inary
import inary.ui as ui
import inary.context as ctx
import inary.data.pgraph as pgraph
import inary.atomicoperations as atomicoperations
import inary.operations as operations
import inary.util as util
import inary.db
import inary.blacklist

def check_update_actions(packages):
    installdb = inary.db.installdb.InstallDB()
    packagedb = inary.db.packagedb.PackageDB()

    actions = {}

    for package in packages:
        if not installdb.has_package(package):
            continue

        pkg = packagedb.get_package(package)
        version, release, build = installdb.get_version(package)
        pkg_actions = pkg.get_update_actions(release)

        for action_name, action_targets in list(pkg_actions.items()):
            item = actions.setdefault(action_name, [])
            for action_target in action_targets:
                item.append((package, action_target))

    has_actions = False

    if "serviceRestart" in actions:
        has_actions = True
        ctx.ui.warning(_("You must restart the following service(s) manually "
                         "for the updated software to take effect:"))
        for package, target in actions["serviceRestart"]:
            ctx.ui.info("    - {}".format(target))

    if "systemRestart" in actions:
        has_actions = True
        ctx.ui.warning(_("You must restart your system for the updates "
                         "in the following package(s) to take effect:"))
        for package, target in actions["systemRestart"]:
            ctx.ui.info("    - {}".format(package))

    return has_actions

def find_upgrades(packages, replaces):
    packagedb = inary.db.packagedb.PackageDB()
    installdb = inary.db.installdb.InstallDB()

    debug = ctx.config.get_option("debug")
    security_only = ctx.get_option('security_only')
    comparesha1sum = ctx.get_option('compare_sha1sum')

    Ap = []
    ds = []
    for i_pkg in packages:

        if i_pkg in list(replaces.keys()):
            # Replaced packages will be forced for upgrade, cause replaced packages are marked as obsoleted also. So we
            # pass them.
            continue

        if i_pkg.endswith(ctx.const.package_suffix):
            ctx.ui.info(_("Warning: package *name* ends with '.inary'"), verbose=True)

        if not installdb.has_package(i_pkg):
            ctx.ui.info(_('Package \"{}\" is not installed.').format(i_pkg), True)
            continue

        if not packagedb.has_package(i_pkg):
            ctx.ui.info(_('Package \"{}\" is not available in repositories.').format(i_pkg), True)
            continue

        pkg = packagedb.get_package(i_pkg)
        hash = installdb.get_install_tar_hash(i_pkg)
        (version, release, build, distro, distro_release) = installdb.get_version_and_distro_release(i_pkg)

        if security_only and not pkg.has_update_type("security", release):
            continue

        if pkg.distribution == distro and \
                inary.version.make_version(pkg.distributionRelease) > inary.version.make_version(distro_release):
            Ap.append(i_pkg)

        else:
            if int(release) < int(pkg.release):
                Ap.append(i_pkg)
            elif comparesha1sum and \
                    int(release) == int(pkg.release) and \
                    not pkg.installTarHash == hash:
                Ap.append(i_pkg)
                ds.append(i_pkg)
            else:
                ctx.ui.info(_('Package \"{0.name}\" is already at the latest release {0.release}.').format(
                    pkg), True)

    if debug and ds:
        ctx.ui.info(_('The following packages have different sha1sum:'))
        ctx.ui.info(util.format_by_columns(sorted(ds)))

    return Ap

@util.locked
def upgrade(A=None, repo=None):
    """Re-installs packages from the repository, trying to perform
    a minimum or maximum number of upgrades according to options.

    Upgrades the given packages, if no package given upgrades all the packages
    @param A: list of package names -> list_of_strings
    @param repo: name of the repository that only the packages from that repo going to be upgraded
    """
    if A is None:
        A = []
    inary.db.historydb.HistoryDB().create_history("upgrade")

    packagedb = inary.db.packagedb.PackageDB()
    installdb = inary.db.installdb.InstallDB()
    replaces = packagedb.get_replaces()
    if not A:
        # if A is empty, then upgrade all packages
        A = installdb.list_installed()

    if repo:
        repo_packages = set(packagedb.list_packages(repo))
        A = set(A).intersection(repo_packages)

    A_0 = A = set(A)
    Ap = find_upgrades(A, replaces)
    A = set(Ap)

    # Force upgrading of installed but replaced packages or else they will be removed (they are obsoleted also).
    # This is not wanted for a replaced driver package (eg. nvidia-X).
    A |= set(inary.util.flatten_list(list(replaces.values())))

    A |= upgrade_base(A)

    A = inary.blacklist.exclude_from(A, ctx.const.blacklist)

    if ctx.get_option('exclude_from'):
        A = inary.blacklist.exclude_from(A, ctx.get_option('exclude_from'))

    if ctx.get_option('exclude'):
        A = inary.blacklist.exclude(A, ctx.get_option('exclude'))

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

    if len(A) == 0:
        ctx.ui.info(_('No packages to upgrade.'))
        return True

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

    if not ctx.config.get_option('ignore_dependency'):
        G_f, order = plan_upgrade(A, replaces=replaces)
    else:
        G_f = None
        order = list(A)

    componentdb = inary.db.componentdb.ComponentDB()

    # Bug 4211
    if componentdb.has_component('system.base'):
        order = operations.helper.reorder_base_packages(order)

    ctx.ui.info(_('The following packages will be upgraded:'), color="green")
    ctx.ui.info(util.format_by_columns(sorted(order)))

    operations.helper.calculate_download_sizes(order)
    operations.helper.calculate_free_space_needed(order)

    needs_confirm = check_update_actions(order)

    # NOTE: replaces.values() was already flattened above, it can be reused
    if set(order) - A_0 - set(inary.util.flatten_list(list(replaces.values()))):
        ctx.ui.warning(_("There are extra packages due to dependencies."))
        needs_confirm = True

    if ctx.get_option('dry_run'):
        return

    if needs_confirm and \
            not ctx.ui.confirm(_("Would you like to continue?")):
        raise Exception(_('External dependencies not satisfied.'))

    ctx.ui.notify(ui.packagestogo, order=order)

    conflicts = []
    if not ctx.get_option('ignore_package_conflicts'):
        conflicts = operations.helper.check_conflicts(order, packagedb)

    paths = []
    for x in order:
        ctx.ui.info(_("Downloading {} / {} => [{}]").format(order.index(x) + 1, len(order),x), color="yellow")
        install_op = atomicoperations.Install.from_name(x)
        paths.append(install_op.package_fname)

    # fetch to be upgraded packages but do not install them.
    if ctx.get_option('fetch_only'):
        return

    if conflicts:
        operations.remove.remove_conflicting_packages(conflicts)

    operations.remove.remove_obsoleted_packages()

    for path in order:
        if installdb.has_package(path):
            remove_op = atomicoperations.Remove(path)
            remove_op.run_preremove()
    for path in paths:
        install_op = atomicoperations.Install(path)
        install_op.preInstall()

    for path in paths:
        install_op = atomicoperations.Install(path)
        basename=path.split("/")[-1]
        ctx.ui.info(_("Installing {} / {} => [{}]") .format(paths.index(path) + 1, len(paths),basename), color="yellow")
        install_op.install(False)
        try:
            with open(os.path.join(ctx.config.info_dir(), ctx.const.installed_extra), "a") as ie_file:
                ie_file.write("{}\n".format(extra_paths[path]))
            installdb.installed_extra.append(extra_paths[path])
        except KeyError:
            pass

    for path in paths:
        install_op = atomicoperations.Install(path)
        install_op.postInstall()

def plan_upgrade(A, force_replaced=True, replaces=None):
    # FIXME: remove force_replaced
    # try to construct a inary graph of packages to
    # install / reinstall

    packagedb = inary.db.packagedb.PackageDB()

    G_f = pgraph.PGraph(packagedb)  # construct G_f

    A = set(A)

    # Force upgrading of installed but replaced packages or else they will be removed (they are obsoleted also).
    # This is not wanted for a replaced driver package (eg. nvidia-X).
    #
    # FIXME: this is also not nice. this would not be needed if replaced packages are not written as obsoleted also.
    # But if they are not written obsoleted "inary index" indexes them
    if force_replaced:
        if replaces is None:
            replaces = packagedb.get_replaces()
        A |= set(inary.util.flatten_list(list(replaces.values())))

    # find the "install closure" graph of G_f by package
    # set A using packagedb
    for x in A:
        G_f.add_package(x)

    installdb = inary.db.installdb.InstallDB()

    def add_runtime_deps(pkg, Bp):
        for dep in pkg.runtimeDependencies():
            # add packages that can be upgraded
            if installdb.has_package(dep.package) and dep.satisfied_by_installed():
                continue

            if dep.satisfied_by_repo():
                if not dep.package in G_f.vertices():
                    Bp.add(str(dep.package))

                # Always add the dependency info although the dependant
                # package is already a member of this graph. Upgrade order
                # might change if the dependency info differs from the
                # previous ones.
                G_f.add_dep(pkg.name, dep)
            else:
                ctx.ui.error(_('Dependency \"{0}\" of \"{1}\" cannot be satisfied.').format(dep, pkg.name))
                raise Exception(_("Upgrade is not possible."))

    def add_resolvable_conflicts(pkg, Bp):
        """Try to resolve conflicts by upgrading

        If a package B conflicts with an old version of package A and
        does not conflict with the new version of A, add A to the upgrade list.
        """
        for conflict in pkg.conflicts:
            if conflict.package in G_f.vertices():
                # Conflicting package is already in the upgrade list.
                continue

            if not inary.analyzer.conflict.installed_package_conflicts(conflict):
                # Conflicting package is not installed.
                # No need to deal with it.
                continue

            if not packagedb.has_package(conflict.package):
                # Conflicting package is not available in repo.
                # Installed package will be removed.
                continue

            new_pkg = packagedb.get_package(conflict.package)
            if conflict.satisfies_relation(new_pkg.version, new_pkg.release):
                # Package still conflicts with the repo package.
                # Installed package will be removed.
                continue

            # Upgrading the package will resolve conflict.
            # Add it to the upgrade list.
            Bp.add(conflict.package)
            G_f.add_package(conflict.package)

    def add_broken_revdeps(pkg, Bp):
        # Search reverse dependencies to see if anything
        # should be upgraded
        rev_deps = installdb.get_rev_deps(pkg.name)
        for rev_dep, depinfo in rev_deps:
            # add only installed but unsatisfied reverse dependencies
            if rev_dep in G_f.vertices() or depinfo.satisfied_by_repo():
                continue

            if is_upgradable(rev_dep):
                Bp.add(rev_dep)
                G_f.add_plain_dep(rev_dep, pkg.name)

    def add_needed_revdeps(pkg, Bp):
        # Search for reverse dependency update needs of to be upgraded packages
        # check only the installed ones.
        version, release, build = installdb.get_version(pkg.name)
        actions = pkg.get_update_actions(release)

        packages = actions.get("reverseDependencyUpdate")
        if packages:
            for target_package in packages:
                for name, dep in installdb.get_rev_deps(target_package):
                    if name in G_f.vertices() or not is_upgradable(name):
                        continue

                    Bp.add(name)
                    G_f.add_plain_dep(name, target_package)

    while A:
        Bp = set()

        for x in A:
            pkg = packagedb.get_package(x)

            add_runtime_deps(pkg, Bp)
            add_resolvable_conflicts(pkg, Bp)

            if installdb.has_package(x):
                add_broken_revdeps(pkg, Bp)
                add_needed_revdeps(pkg, Bp)

        A = Bp

    if ctx.config.get_option('debug'):
        G_f.write_graphviz(sys.stdout)

    order = G_f.topological_sort()
    order.reverse()
    return G_f, order

def upgrade_base(A=None):
    if A is None:
        A = set()
    installdb = inary.db.installdb.InstallDB()
    componentdb = inary.db.componentdb.ComponentDB()
    if not ctx.config.values.general.ignore_safety and not ctx.get_option('ignore_safety'):
        if componentdb.has_component('system.base'):
            systembase = set(componentdb.get_union_component('system.base').packages)
            extra_installs = [x for x in systembase - set(A) if not installdb.has_package(x)]
            extra_installs = inary.blacklist.exclude_from(extra_installs, ctx.const.blacklist)
            if extra_installs:
                ctx.ui.warning(_("Safety switch forces the installation of "
                                 "following packages:"))
                ctx.ui.info(util.format_by_columns(sorted(extra_installs)))

            # Will delete G_F and extra_upgrades
            G_f, install_order = operations.install.plan_install_pkg_names(extra_installs)
            extra_upgrades = [x for x in systembase - set(install_order) if is_upgradable(x)]
            upgrade_order = []

            extra_upgrades = inary.blacklist.exclude_from(extra_upgrades, ctx.const.blacklist)

            if ctx.get_option('exclude_from'):
                extra_upgrades = inary.blacklist.exclude_from(extra_upgrades, ctx.get_option('exclude_from'))

            if ctx.get_option('exclude'):
                extra_upgrades = inary.blacklist.exclude(extra_upgrades, ctx.get_option('exclude'))

            if extra_upgrades:
                ctx.ui.warning(_("Safety switch forces the upgrade of "
                                 "following packages:"))
                ctx.ui.info(util.format_by_columns(sorted(extra_upgrades)))
                G_f, upgrade_order = plan_upgrade(extra_upgrades, force_replaced=False)

            # no-need-for-upgrade-order patch
            # extra_upgrades = filter(lambda x: is_upgradable(x, ignore_build), systembase - set(extra_installs))
            # return set(extra_installs + extra_upgrades)

            # return packages that must be added to any installation
            return set(install_order + upgrade_order)
        else:
            ctx.ui.warning(_('Safety switch: The component system.base cannot be found.'))
    return set()

def is_upgradable(name):
    installdb = inary.db.installdb.InstallDB()

    if not installdb.has_package(name):
        return False

    (i_version, i_release, i_build, i_distro, i_distro_release) = installdb.get_version_and_distro_release(name)

    packagedb = inary.db.packagedb.PackageDB()

    try:
        version, release, build, distro, distro_release = packagedb.get_version_and_distro_release(name,
                                                                                packagedb.which_repo(
                                                                                name))
    except KeyboardInterrupt:
        raise
    except Exception:  # FIXME: what exception could we catch here, replace with that.
        return False

    if distro == i_distro and \
            inary.version.make_version(distro_release) > inary.version.make_version(i_distro_release):
        return True

    return int(i_release) < int(release)

def get_upgrade_order(packages):
    """
    Return a list of packages in the upgrade order with extra needed
    dependencies -> list_of_strings
    @param packages: list of package names -> list_of_strings
    """
    upgrade_order = inary.operations.upgrade.plan_upgrade
    i_graph, order = upgrade_order(packages)
    return order

def get_base_upgrade_order(packages):
    """
    Return a list of packages of the system.base component that needs to be upgraded
    or installed in install order -> list_of_strings
    All the packages of the system.base component must be installed on the system
    @param packages: list of package names -> list_of_strings
    """
    upgrade_order = inary.operations.upgrade.upgrade_base
    order = upgrade_order(packages)
    return list(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.
#

"""package abstraction methods to add/remove files, extract control files"""

import os.path

import gettext

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

import inary.errors
import inary.context as ctx
import inary.archive as archive
import inary.uri
import inary.ui
import inary.data.metadata
import inary.file
import inary.data.files
import inary.util as util
from . import fetcher

class Error(inary.errors.Error):
    pass

class Package:
    """INARY Package Class provides access to a inary package (.inary
    file)."""

    formats = ("1.0", "1.1", "1.2")
    default_format = "1.2"
    timestamp = None

    @staticmethod
    def archive_name_and_format(package_format):
        if package_format == "1.2":
            archive_format = "tarxz"
            archive_suffix = ctx.const.xz_suffix
        elif package_format == "1.1":
            archive_format = "tarlzma"
            archive_suffix = ctx.const.lzma_suffix
        else:
            # "1.0" format does not have an archive
            return None, None

        archive_name = ctx.const.install_tar + archive_suffix
        return archive_name, archive_format

    def __init__(self, packagefn, mode='r', format=None, tmp_dir=None,pkgname='',no_fetch=False):
        self.filepath = packagefn
        url = inary.uri.URI(packagefn)

        if ("://" in self.filepath) and not no_fetch:
            self.fetch_remote_file(url)

        try:
            self.impl = archive.ArchiveZip(self.filepath, 'zip', mode)
        except IOError as e:
            raise Error(_("Cannot open package file: \"{}\"").format(e))

        self.install_archive = None

        if mode == "r":
            self.metadata = self.get_metadata()
            format = self.metadata.package.packageFormat

            # Many of the old packages do not contain format information
            # because of a bug in old Inary versions. This is a workaround
            # to guess their package format.
            if format is None:
                archive_name = ctx.const.install_tar + ctx.const.lzma_suffix
                if self.impl.has_file(archive_name):
                    format = "1.1"
                else:
                    format = "1.0"

        self.format = format or Package.default_format

        if self.format not in Package.formats:
            raise Error(_("Unsupported package format: {}").format(format))

        self.tmp_dir = tmp_dir or ctx.config.tmp_dir()

    def fetch_remote_file(self, url,pkgname=''):
        dest = ctx.config.cached_packages_dir()
        self.filepath = os.path.join(dest, url.filename())
        if not os.path.exists(self.filepath):
            try:
                inary.file.File.download(url, dest,pkgname)
            except fetcher.FetchError:
                # Bug 3465
                if ctx.get_option('reinstall'):
                    raise Error(_(
                        "There was a problem while fetching \"{}\".\nThe package may have been upgraded. Please try to upgrade the package.").format(
                        url))
                raise
        else:
            if ctx.config.get_option('debug'):
                ctx.ui.info(_('{} [cached]').format(url.filename()))

    def add_to_package(self, fn, an=None):
        """Add a file or directory to package"""
        self.impl.add_to_archive(fn, an)

    def add_to_install(self, name, arcname=None):
        """Add the file 'name' to the install archive"""

        if arcname is None:
            arcname = name

        if self.format == "1.0":
            arcname = util.join_path("install", arcname)
            self.add_to_package(name, arcname)
            return

        if self.install_archive is None:
            archive_name, archive_format = \
                self.archive_name_and_format(self.format)
            self.install_archive_path = util.join_path(self.tmp_dir,
                                                       archive_name)
            ctx.build_leftover = self.install_archive_path
            self.install_archive = archive.ArchiveTar(
                self.install_archive_path,
                archive_format)

        self.install_archive.add_to_archive(name, arcname)

    def add_metadata_xml(self, path):
        self.metadata = inary.data.metadata.MetaData()
        self.metadata.read(path)

        self.add_to_package(path, ctx.const.metadata_xml)

    def add_files_xml(self, path):
        self.files = inary.data.files.Files()
        self.files.read(path)

        self.add_to_package(path, ctx.const.files_xml)

    def close(self):
        """Close the package archive"""
        if self.install_archive:
            self.install_archive.close()
            arcpath = self.install_archive_path
            arcname = os.path.basename(arcpath)
            if self.timestamp is not None:
                tstamp = self.timestamp
                os.utime(arcpath, (tstamp, tstamp))
            self.add_to_package(arcpath, arcname)

        self.impl.close()

        if self.install_archive:
            os.unlink(self.install_archive_path)
            ctx.build_leftover = None

    def get_install_archive(self):
        archive_name, archive_format = \
            self.archive_name_and_format(self.format)

        if archive_name is None or not self.impl.has_file(archive_name):
            return

        archive_file = self.impl.open(archive_name)
        tar = archive.ArchiveTar(fileobj=archive_file,
                                 arch_type=archive_format,
                                 no_same_permissions=False,
                                 no_same_owner=False)

        return tar

    def extract(self, outdir):
        """Extract entire package contents to directory"""
        self.extract_dir('', outdir)  # means package root

    def extract_files(self, paths, outdir):
        """Extract paths to outdir"""
        self.impl.unpack_files(paths, outdir)

    def extract_file(self, path, outdir):
        """Extract file with path to outdir"""
        self.extract_files([path], outdir)

    def extract_file_synced(self, path, outdir):
        """Extract file with path to outdir"""
        data = self.impl.read_file(path)
        fpath = util.join_path(outdir, path)
        util.ensure_dirs(os.path.dirname(fpath))

        with open(fpath, "wb") as f:
            f.write(data.encode("utf-8"))
            f.flush()
            os.fsync(f.fileno())

    def extract_dir(self, dir, outdir):
        """Extract directory recursively, this function
        copies the directory archiveroot/dir to outdir"""
        self.impl.unpack_dir(dir, outdir)

    def extract_install(self, outdir):
        def callback(tarinfo, extracted):
            if not extracted:
                # Installing packages (especially shared libraries) is a
                # bit tricky. You should also change the inode if you
                # change the file, cause the file is opened allready and
                # accessed. Removing and creating the file will also
                # change the inode and will do the trick (in fact, old
                # file will be deleted only when its closed).
                #
                # Also, tar.extract() doesn't write on symlinks... Not any
                # more :).
                if os.path.isfile(tarinfo.name) or os.path.islink(tarinfo.name):
                    try:
                        os.unlink(tarinfo.name)
                    except OSError as e:
                        ctx.ui.warning(e)

            else:
                # Added for package-manager
                if tarinfo.name.endswith(".desktop"):
                    ctx.ui.notify(inary.ui.desktopfile, logging=False, desktopfile=tarinfo.name)

        tar = self.get_install_archive()

        if tar:
            tar.unpack_dir(outdir, callback=callback)
        else:
            self.extract_dir_flat('install', outdir)

    def extract_dir_flat(self, dir, outdir):
        """Extract directory recursively, this function
        unpacks the *contents* of directory archiveroot/dir inside outdir
        this is the function used by the installer"""
        self.impl.unpack_dir_flat(dir, outdir)

    def extract_to(self, outdir, clean_dir=False):
        """Extracts contents of the archive to outdir. Before extracting if clean_dir
        is set, outdir is deleted with its contents"""
        self.impl.unpack(outdir, clean_dir)

    def extract_inary_files(self, outdir):
        """Extract INARY control files: metadata.xml, files.xml,
        action scripts, etc."""
        self.extract_files([ctx.const.metadata_xml, ctx.const.files_xml], outdir)
        self.extract_dir('config', outdir)

    def get_metadata(self):
        """reads metadata.xml from the INARY package and returns MetaData object"""
        md = inary.data.metadata.MetaData()
        md.parse(self.impl.read_file(ctx.const.metadata_xml))
        return md

    def get_files(self):
        """reads files.xml from the INARY package and returns Files object"""
        files = inary.data.files.Files()
        files.parse(self.impl.read_file(ctx.const.files_xml))
        return files

    def read(self):
        self.files = self.get_files()
        self.metadata = self.get_metadata()

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

        return os.path.join(ctx.config.packages_dir(), packageDir)

    def scom_dir(self):
        return os.path.join(self.pkg_dir(), ctx.const.scom_dir)

    @staticmethod
    def is_cached(packagefn):
        url = inary.uri.URI(packagefn)
        filepath = packagefn
        if url.is_remote_file():
            filepath = os.path.join(ctx.config.cached_packages_dir(), url.filename())
            return os.path.exists(filepath) and filepath
        else:
            return filepath


code:
# -*- coding: utf-8 -*-
#
#Copyright (C) 2019, Ali Rıza KESKİN (sulincix)
#
# 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.
#

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

import inary.context as ctx
import inary.db
import inary.util as util
import os
import sys

def post_install(package_name, provided_scripts,
             scriptpath, metapath, filepath,
             fromVersion, fromRelease, toVersion, toRelease):
    """Do package's post install operations"""
    pkg_path = metapath.replace("metadata.xml","scom")
    if(os.path.isfile(pkg_path+"/package.py")):
        i=os.system('cd {} ; python3 -c \'import package\nif(hasattr(package,"postInstall")):\n package.postInstall("{}",{},"{}",{})\''.format((pkg_path),fromVersion, fromRelease, toVersion, toRelease))
        if(i!=0):
            raise SystemExit
    elif(os.path.isfile(pkg_path+"/package.sh")):
        i=os.system('source {}/package.sh ; postInstall "{}" {} "{}" {}'.format((pkg_path),fromVersion, fromRelease, toVersion, toRelease))
        if(i!=0):
            raise SystemExit
    else:
        return 0

def pre_install(package_name, provided_scripts,
             scriptpath, metapath, filepath,
             fromVersion, fromRelease, toVersion, toRelease):
    """Do package's pre install operations"""
    pkg_path = metapath.replace("metadata.xml","scom")
    if(os.path.isfile(pkg_path+"/package.py")):
        i=os.system('cd {} ; python3 -c \'import package\nif(hasattr(package,"preInstall")):\n  package.preInstall("{}",{},"{}",{})\''.format((pkg_path),fromVersion, fromRelease, toVersion, toRelease))
        if(i!=0):
            raise SystemExit
    elif(os.path.isfile(pkg_path+"/package.sh")):
        i=os.system('source {}/package.sh ; preInstall "{}" "{}" "{}" "{}"'.format((pkg_path),fromVersion, fromRelease, toVersion, toRelease))
        if(i!=0):
            raise SystemExit
    else:
        return 0

def pre_remove(package_name, metapath, filepath,provided_scripts=None):
    """Do package's post removal operations"""
    pkg_path = metapath.replace("metadata.xml","scom")
    if(os.path.isfile(pkg_path+"/package.py")):
        i=os.system('cd {} ; python3 -c \'import package\nif(hasattr(package,"preRemove")):\n package.preRemove()\''.format(pkg_path))
        if(i!=0):
            raise SystemExit
    elif(os.path.isfile(pkg_path+"/package.sh")):
        i=os.system('source {}/package.sh ; preRemove'.format((pkg_path)))
        if(i!=0):
            raise SystemExit
    else:
        return 0


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 signal

exception = {
    signal.SIGINT: KeyboardInterrupt
}

class Signal:
    def __init__(self, sig):
        self.signal = sig
        self.oldhandler = signal.getsignal(sig)
        self.pending = False

class SignalHandler:

    def __init__(self):
        self.signals = {}

    def signal_handler(self, sig, frame):
        signal.signal(sig, signal.SIG_IGN)
        self.signals[sig].pending = True

    def disable_signal(self, sig):
        if sig not in list(self.signals.keys()):
            self.signals[sig] = Signal(sig)
            signal.signal(sig, self.signal_handler)

    def enable_signal(self, sig):
        if sig in list(self.signals.keys()):
            if self.signals[sig].oldhandler:
                oldhandler = self.signals[sig].oldhandler
            else:
                oldhandler = signal.SIG_DFL
            pending = self.signals[sig].pending
            del self.signals[sig]
            signal.signal(sig, oldhandler)
            if pending:
                raise exception[sig]

    def signal_disabled(self, sig):
        return sig in list(self.signals.keys())

    def signal_pending(self, sig):
        return self.signal_disabled(sig) and self.signals[sig].pending


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.errors

class Error(inary.errors.Error):
    pass