#!/bin/sh
#
# Copyright (C) 2012-2018  Etersoft
# Copyright (C) 2012-2018  Vitaly Lipatov <lav@etersoft.ru>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#

PROGDIR=$(dirname "$0")
PROGNAME=$(basename "$0")
[ "$PROGDIR" = "." ] && PROGDIR=$(pwd)
if [ "$0" = "/dev/stdin" ] || [ "$0" = "sh" ] ; then
    PROGDIR=""
    PROGNAME=""
fi

# will replaced to /usr/share/eepm during install
SHAREDIR=$PROGDIR

load_helper()
{
    local CMD="$SHAREDIR/$1"
    # do not use fatal() here, it can be initial state
    [ -r "$CMD" ] || { echo "FATAL: Have no $CMD helper file" ; exit 1; }
    # shellcheck disable=SC1090
    . $CMD
}



# File bin/epm-sh-functions:


inputisatty()
{
	# check stdin
	tty -s 2>/dev/null
}

isatty()
{
	# check stdout
	test -t 1
}

isatty2()
{
	# check stderr
	test -t 2
}

check_tty()
{
	isatty2 || return

	# Set a sane TERM required for tput
	[ -n "$TERM" ] || TERM=dumb
	export TERM

	# egrep from busybox may not --color
	# egrep from MacOS print help to stderr
	if grep -E --help 2>&1 | grep -q -- "--color" ; then
		export EGREPCOLOR="--color"
	fi

	which tput >/dev/null 2>/dev/null || return
	# FreeBSD does not support tput -S
	echo | tput -S >/dev/null 2>/dev/null || return
	[ -z "$USETTY" ] || return
	export USETTY=1
}

: ${BLACK:=0} ${RED:=1} ${GREEN:=2} ${YELLOW:=3} ${BLUE:=4} ${MAGENTA:=5} ${CYAN:=6} ${WHITE:=7}

set_boldcolor()
{
	[ "$USETTY" = "1" ] || return
	{
		echo bold
		echo setaf $1
	} |tput -S
}

restore_color()
{
	[ "$USETTY" = "1" ] || return
	{
		echo op; # set Original color Pair.
		echo sgr0; # turn off all special graphics mode (bold in our case).
	} |tput -S
}

echover()
{
    [ -z "$verbose" ] && return
    echo "$*" >&2
}

echon()
{
	# default /bin/sh on MacOS does not recognize -n
	/bin/echo -n "$*"
}


set_target_pkg_env()
{
	[ -n "$DISTRNAME" ] || fatal "Missing DISTRNAME in set_target_pkg_env."
	PKGFORMAT=$($DISTRVENDOR -p "$DISTRNAME")
	PKGVENDOR=$($DISTRVENDOR -s "$DISTRNAME")
	RPMVENDOR=$($DISTRVENDOR -n "$DISTRNAME")
}

showcmd()
{
	if [ -z "$quiet" ] ; then
		set_boldcolor $GREEN
		local PROMTSIG="\$"
		[ "$EFFUID" = 0 ] && PROMTSIG="#"
		echo " $PROMTSIG $*"
		restore_color
	fi >&2
}

docmd()
{
	showcmd "$*$EXTRA_SHOWDOCMD"
	$@
}

docmd_foreach()
{
	local cmd pkg
	cmd="$1"
	#showcmd "$@"
	shift
	for pkg in "$@" ; do
		docmd "$cmd" $pkg
	done
}

sudocmd()
{
	[ -n "$SUDO" ] && showcmd "$SUDO $*" || showcmd "$*"
	$SUDO $@
}

sudocmd_foreach()
{
	local cmd pkg
	cmd="$1"
	#showcmd "$@"
	shift
	for pkg in "$@" ; do
		sudocmd "$cmd" $pkg || return
	done
}

if ! which realpath 2>/dev/null >/dev/null ; then
realpath()
{
	readlink -f "$@"
}
fi

get_firstarg()
{
	echon "$1"
}

get_lastarg()
{
	local lastarg
	eval "lastarg=\${$#}"
	echon "$lastarg"
}

isnumber()
{
	echo "$*" | filter_strip_spaces | grep -q "^[0-9]\+$"
}

filter_strip_spaces()
{
        # possible use just
        #xargs echo
        sed -e "s| \+| |g" | \
                sed -e "s|^ ||" | sed -e "s| \$||"
}

strip_spaces()
{
        echo "$*" | filter_strip_spaces
}

subst_option()
{
	eval "[ -n \"\$$1\" ]" && echo "$2" || echo "$3"
}

store_output()
{
    # use make_temp_file from etersoft-build-utils
    RC_STDOUT=$(mktemp)
    local CMDSTATUS=$RC_STDOUT.pipestatus
    echo 1 >$CMDSTATUS
    #RC_STDERR=$(mktemp)
    ( $@ 2>&1 ; echo $? >$CMDSTATUS ) | tee $RC_STDOUT
    return "$(cat $CMDSTATUS)"
    # bashism
    # http://tldp.org/LDP/abs/html/bashver3.html#PIPEFAILREF
    #return $PIPESTATUS
}

showcmd_store_output()
{
    showcmd "$@"
    store_output "$@"
}

clean_store_output()
{
    rm -f $RC_STDOUT $RC_STDOUT.pipestatus
}

epm()
{
	[ -n "$PROGNAME" ] || fatal "Can't use epm call from the piped script"
	$PROGDIR/$PROGNAME $@
}

fatal()
{
	if [ -z "$TEXTDOMAIN" ] ; then
		echo "Error: $*" >&2
	fi
	exit 1
}

warning()
{
	if [ -z "$TEXTDOMAIN" ] ; then
		echo "Warning: $*" >&2
	fi
}

info()
{
	[ -n "$quiet" ] && return

	# print message to stderr if stderr forwarded to (a file)
	if isatty2 ; then
		isatty || return 0
		echo "$*"
	else
		echo "$*" >&2
	fi
}

set_sudo()
{
	SUDO=""
	# skip SUDO if disabled
	[ -n "$EPMNOSUDO" ] && return
	if [ "$DISTRNAME" = "Cygwin" ] || [ "$DISTRNAME" = "Windows" ] ; then
		# skip sudo using on Windows
		return
	fi

	EFFUID=$(id -u)

	# do not need sudo
	[ $EFFUID = "0" ] && return

	# use sudo if possible
	if which sudo >/dev/null 2>/dev/null ; then
		SUDO="sudo --"
		# check for < 1.7 version which do not support -- (and --help possible too)
		sudo -h 2>/dev/null | grep -q "  --" || SUDO="sudo"
		return
	fi

	SUDO="fatal 'Can't find sudo. Please install sudo or run epm under root.'"
}

withtimeout()
{
	local TO=$(which timeout 2>/dev/null || which gtimeout 2>/dev/null)
	if [ -x "$TO" ] ; then
		$TO "$@"
		return
	fi
	# fallback: drop time arg and run without timeout
	shift
	"$@"
}

set_eatmydata()
{
	# skip if disabled
	[ -n "$EPMNOEATMYDATA" ] && return
	# use if possible
	which eatmydata >/dev/null 2>/dev/null || return
	[ -n "$SUDO" ] && SUDO="$SUDO eatmydata" || SUDO="eatmydata"
	[ -n "$verbose" ] && info "Uwaga! eatmydata is installed, we will use it for disable all sync operations."
	return 0
}

__get_package_for_command()
{
	case "$1" in
		equery|revdep-rebuild)
			echo 'gentoolkit'
			;;
		update-kernel|remove-old-kernels)
			echo 'update-kernel'
			;;
	esac
}

confirm() {
    local response
    # call with a prompt string or use a default
    read -r -p "${1:-Are you sure? [y/N]} " response
    case $response in
        [yY][eE][sS]|[yY])
            true
            ;;
        *)
            false
            ;;
    esac
}

assure_root()
{
	[ "$EFFUID" = 0 ] || fatal "run me only under root"
}

regexp_subst()
{
	local expression="$1"
	shift
	sed -i -r -e "$expression" "$@"
}

assure_exists()
{
	local package="$2"
	local textpackage=
	[ -n "$package" ] || package="$(__get_package_for_command "$1")"
	[ -n "$3" ] && textpackage=" >= $3"
	__epm_assure "$1" $package $3 || fatal "Can't assure in '$1' command from $package$textpackage package"
}

disabled_eget()
{
	# use internal eget only if exists
	if [ -s $SHAREDIR/tools_eget ] ; then
		$SHAREDIR/tools_eget "$@"
		return
	fi

	assure_exists eget
	# run external command, not the function
	EGET=$(which eget) || fatal "Missed command eget from installed package eget"
	$EGET "$@"
}

disabled_estrlist()
{
	if [ -s $SHAREDIR/tools_estrlist ] ; then
		$SHAREDIR/tools_estrlist "$@"
		return
	fi
	fatal "missed tools_estrlist"
}

estrlist()
{
	internal_tools_estrlist "$@"
}

eget()
{
	assure_exists wget
	internal_tools_eget "$@"
}

get_package_type()
{
	local i
	case $1 in
		*.deb)
			echo "deb"
			return
			;;
		*.rpm)
			echo "rpm"
			return
			;;
		*.txz)
			echo "txz"
			return
			;;
		*.tbz)
			echo "tbz"
			return
			;;
		*.exe)
			echo "exe"
			return
			;;
		*.msi)
			echo "msi"
			return
			;;
		*)
			#fatal "Don't know type of $1"
			# return package name for info
			echo "$1"
			return 1
			;;
	esac
}


get_help()
{
    if [ "$0" = "/dev/stdin" ] || [ "$0" = "sh" ] ; then
        return
    fi

    grep -v -- "^#" $0 | grep -- "# $1" | while read -r n ; do
        opt=$(echo $n | sed -e "s|) # $1:.*||g")
        desc=$(echo $n | sed -e "s|.*) # $1:||g")
        printf "    %-20s %s\n" $opt "$desc"
    done
}


set_pm_type()
{
	local CMD

	# Fill for use: PMTYPE, DISTRNAME, DISTRVERSION, PKGFORMAT, PKGVENDOR, RPMVENDOR
	DISTRVENDOR=internal_distr_info
	[ -n "$DISTRNAME" ] || DISTRNAME=$($DISTRVENDOR -d) || fatal "Can't get distro name."
	[ -n "$DISTRVERSION" ] || DISTRVERSION=$($DISTRVENDOR -v)
	set_target_pkg_env

if [ -n "$FORCEPM" ] ; then
	PMTYPE=$FORCEPM
	return
fi

case $DISTRNAME in
	ALTLinux)
		CMD="apt-rpm"
		#which ds-install 2>/dev/null >/dev/null && CMD=deepsolver-rpm
		;;
	PCLinux)
		CMD="apt-rpm"
		;;
	Ubuntu|Debian|Mint|AstraLinux|Elbrus)
		CMD="apt-dpkg"
		#which aptitude 2>/dev/null >/dev/null && CMD=aptitude-dpkg
		which snappy 2>/dev/null >/dev/null && CMD=snappy
		;;
	Mandriva|ROSA)
		CMD="urpm-rpm"
		;;
	FreeBSD|NetBSD|OpenBSD|Solaris)
		CMD="pkgsrc"
		which pkg 2>/dev/null >/dev/null && CMD=pkgng
		;;
	Gentoo)
		CMD="emerge"
		;;
	ArchLinux)
		CMD="pacman"
		;;
	Fedora|LinuxXP|ASPLinux|CentOS|RHEL|Scientific|GosLinux)
		CMD="yum-rpm"
		which dnf 2>/dev/null >/dev/null && test -d /var/lib/dnf/yumdb && CMD=dnf-rpm
		;;
	Slackware)
		CMD="slackpkg"
		;;
	SUSE|SLED|SLES|Tumbleweed)
		CMD="zypper-rpm"
		;;
	ForesightLinux|rPathLinux)
		CMD="conary"
		;;
	Windows)
		CMD="chocolatey"
		;;
	MacOS)
		CMD="homebrew"
		;;
	OpenWRT)
		CMD="ipkg"
		;;
	GNU/Linux/Guix)
		CMD="guix"
		;;
	Android)
		CMD="android"
		;;
	Cygwin)
		CMD="aptcyg"
		;;
	alpine)
		CMD="apk"
		;;
	TinyCoreLinux)
		CMD="tce"
		;;
	VoidLinux)
		CMD="xbps"
		;;
	*)
		fatal "Have no suitable DISTRNAME $DISTRNAME"
		;;
esac
PMTYPE=$CMD
}


is_active_systemd()
{
	local a
	SYSTEMCTL=/bin/systemctl
	SYSTEMD_CGROUP_DIR=/sys/fs/cgroup/systemd
	[ -x "$SYSTEMCTL" ] || return
	[ -d "$SYSTEMD_CGROUP_DIR" ] || return
	a='' mountpoint -q "$SYSTEMD_CGROUP_DIR" || return
	readlink /sbin/init | grep -q 'systemd' || return
	# some hack
	# shellcheck disable=SC2009
	ps ax | grep '[s]ystemd' | grep -q -v 'systemd-udev'
}

assure_distr()
{
	local TEXT="this option"
	[ -n "$2" ] && TEXT="$2"
	[ "$DISTRNAME" = "$1" ] || fatal "$TEXT supported only for $1 distro"
}

# File bin/epm-addrepo:


__epm_addrepo_altlinux()
{
	local repo="$@"
	case "$1" in
		etersoft)
			info "add Etersoft's addon repo"
			epm install --skip-installed apt-conf-etersoft-common apt-conf-etersoft-hold
			# TODO: ignore only error code 22 (skipped) || fatal
			local branch="$DISTRVERSION/branch"
			[ "$DISTRVERSION" = "Sisyphus" ] && branch="$DISTRVERSION"
			# FIXME
			[ -n "$DISTRVERSION" ] || fatal "Empty DISTRVERSION"
			# TODO: func?
			local arch=$(uname -m)
			[ "$arch" = "i686" ] && arch="i586"
			# TODO: use apt-repo add ?
			echo "" | sudocmd tee -a /etc/apt/sources.list
			echo "# added with eepm addrepo etersoft" | sudocmd tee -a /etc/apt/sources.list
			echo "rpm [etersoft] http://download.etersoft.ru/pub/Etersoft LINUX@Etersoft/$branch/$arch addon" | sudocmd tee -a /etc/apt/sources.list
			if [ "$arch" = "x86_64" ] ; then
				echo "rpm [etersoft] http://download.etersoft.ru/pub/Etersoft LINUX@Etersoft/$branch/$arch-i586 addon" | sudocmd tee -a /etc/apt/sources.list
			fi
			echo "rpm [etersoft] http://download.etersoft.ru/pub/Etersoft LINUX@Etersoft/$branch/noarch addon" | sudocmd tee -a /etc/apt/sources.list
			repo="$DISTRVERSION"
			return 0
			;;
		autoimports)
			[ -n "$DISTRVERSION" ] || fatal "Empty DISTRVERSION"
			repo="$repo.$(echo "$DISTRVERSION" | tr "[:upper:]" "[:lower:]")"
			;;
		archive)
			datestr="$2"
			echo "$datestr" | grep -Eq "^20[0-2][0-9]/[01][0-9]/[0-3][0-9]$" || fatal "use follow date format: 2017/12/31"
			# TODO: func?
			local arch=$(uname -m)
			[ "$arch" = "i686" ] && arch="i586"
			echo "" | sudocmd tee -a /etc/apt/sources.list
			local distrversion="$(echo "$DISTRVERSION" | tr "[:upper:]" "[:lower:]")"
			local rpmsign='[alt]'
			[ "$distrversion" != "sisyphus" ] && rpmsign="[$distrversion]"
			echo "rpm $rpmsign http://ftp.altlinux.org/pub/distributions archive/$distrversion/date/$datestr/$arch classic" | sudocmd tee -a /etc/apt/sources.list
			if [ "$arch" = "x86_64" ] ; then
				echo "rpm $rpmsign http://ftp.altlinux.org/pub/distributions archive/$distrversion/date/$datestr/$arch-i586 classic" | sudocmd tee -a /etc/apt/sources.list
			fi
			echo "rpm $rpmsign http://ftp.altlinux.org/pub/distributions archive/$distrversion/date/$datestr/noarch classic" | sudocmd tee -a /etc/apt/sources.list
			return 0
			;;
	esac

	assure_exists apt-repo

	if tasknumber "$repo" >/dev/null ; then
		sudocmd apt-repo add $(tasknumber "$repo")
		return
	fi

	if [ -z "$repo" ] ; then
		info "Add branch repo. Use follow params:"
		sudocmd apt-repo add branch
		echo "etersoft (for LINUX@Etersoft repo)"
		echo "archive 2018/02/09 (for archive from that date)"
		return
	fi

	sudocmd apt-repo add "$repo"

}

epm_addrepo()
{
local repo="$(eval echo "$quoted_args")"

case $DISTRNAME in
	ALTLinux)
		__epm_addrepo_altlinux $repo
		return
		;;
esac

case $PMTYPE in
	apt-dpkg)
		assure_exists apt-add-repository software-properties-common
		if echo "$repo" | grep -q "https://" ; then
			assure_exists apt-transport-https
		fi
		apt-add-repository "$repo"
		info "Check file /etc/apt/sources.list if needed"
		;;
	aptitude-dpkg)
		info "You need manually add repo to /etc/apt/sources.list (TODO)"
		;;
	yum-rpm)
		assure_exists yum-utils
		sudocmd yum-config-manager --add-repo "$repo"
		;;
	urpm-rpm)
		sudocmd urpmi.addmedia "$repo"
		;;
	zypper-rpm)
		sudocmd zypper ar "$repo"
		;;
	emerge)
		sudocmd layman -a "$repo"
		;;
	pacman)
		info "You need manually add repo to /etc/pacman.conf"
		# Only for alone packages:
		#sudocmd repo-add $pkg_filenames
		;;
	npackd)
		sudocmd npackdcl add-repo --url="$repo"
		;;
	slackpkg)
		info "You need manually add repo to /etc/slackpkg/mirrors"
		;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
esac

}

# File bin/epm-assure:

__check_command_in_path()
{
    PATH=$PATH:/sbin:/usr/sbin which "$1" 2>/dev/null
}


rhas()
{
	echo "$1" | grep -E -q -- "$2"
}

is_dirpath()
{
    [ "$1" = "." ] && return $?
    rhas "$1" "/"
}

__epm_need_update()
{
    local PACKAGE="$1"
    local PACKAGEVERSION="$2"

    [ -n "$PACKAGEVERSION" ] || return 0

    is_installed "$PACKAGE" || return 0

    # epm print version for package N
    local INSTALLEDVERSION=$(query_package_field "version" "$PACKAGE")
    # if needed >= installed, return 0
    [ "$(compare_version "$PACKAGEVERSION" "$INSTALLEDVERSION")" -gt 0 ] && return 0

    return 1
}

__epm_assure_checking()
{
    local CMD="$1"
    local PACKAGE="$2"
    local PACKAGEVERSION="$3"

    [ -n "$PACKAGEVERSION" ] && return 1

    if is_dirpath "$CMD" ; then
        if [ -e "$CMD" ] ; then
            if [ -n "$verbose" ] ; then
                info "File or directory $CMD is already exists."
                epm qf "$CMD"
            fi
            return 0
        fi

        [ -n "$PACKAGE" ] || fatal "You need run with package name param when use with absolute path"
        return 1
    fi

    if __check_command_in_path "$CMD" >/dev/null ; then
        if [ -n "$verbose" ] ; then
            local compath="$(__check_command_in_path "$1")"
            info "Command $CMD is exists: $compath"
            epm qf "$compath"
        fi
        return 0
    fi

    return 1
}



__epm_assure()
{
    local CMD="$1"
    local PACKAGE="$2"
    local PACKAGEVERSION="$3"
    [ -n "$PACKAGE" ] || PACKAGE="$1"

    __epm_assure_checking $CMD $PACKAGE $PACKAGEVERSION && return 0

    info "Installing appropriate package for $CMD command..."
    __epm_need_update $PACKAGE $PACKAGEVERSION || return 0

    docmd epm --auto install $PACKAGE || return

    [ -n "$PACKAGEVERSION" ] || return 0
    # check if we couldn't update and still need update
    __epm_need_update $PACKAGE $PACKAGEVERSION && return 1
    return 0
}


epm_assure()
{
    [ -n "$pkg_filenames" ] || fatal "Assure: Missing params. Check $0 --help for info."

    # use helper func for extract separate params
    # shellcheck disable=SC2046
    __epm_assure $(eval echo $quoted_args)
}

# File bin/epm-audit:

epm_audit()
{

[ -z "$pkg_filenames" ] || fatal "No arguments are allowed here"

case $PMTYPE in
	pkgng)
		sudocmd pkg audit -F
		;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
esac

}

# File bin/epm-autoorphans:

__epm_orphan_altrpm()
{
	docmd "apt-cache list-extras"
}

epm_autoorphans()
{

[ -z "$pkg_filenames" ] || fatal "No arguments are allowed here"


case $PMTYPE in
	apt-rpm)
		# ALT Linux only
		assure_exists /etc/buildreqs/files/ignore.d/apt-scripts apt-scripts
		if [ -z "$dryrun" ] ; then
			echo "We will try remove all installed packages which are missed in repositories"
			warning "Use with caution!"
		fi
		local PKGLIST=$(__epm_orphan_altrpm \
			| sed -e "s/\.32bit//g" \
			| grep -v -- "^eepm$" \
			| grep -v -- "^kernel")

		if [ -n "$quiet" ] ; then
			echo "$PKGLIST"
		else
			docmd epm remove $dryrun $PKGLIST
		fi
		;;
	apt-dpkg|aptitude-dpkg)
		assure_exists deborphan
		showcmd deborphan
		a='' deborphan | docmd epm remove $dryrun
		;;
	#aura)
	#	sudocmd aura -Oj
	#	;;
	yum-rpm)
		docmd epm upgrade
		assure_exists package-cleanup yum-utils
		showcmd package-cleanup --orphans
		local PKGLIST=$(package-cleanup -q --orphans | grep -v "^eepm-")
		docmd epm remove $dryrun $PKGLIST
		;;
	dnf-rpm)
		# TODO: dnf list extras
		docmd epm upgrade
		assure_exists package-cleanup dnf-utils
		showcmd package-cleanup --orphans
		local PKGLIST=$(package-cleanup -q --orphans | grep -v "^eepm-")
		docmd epm remove $dryrun $PKGLIST
		;;
	urpm-rpm)
		if [ -n "$dryrun" ] ; then
			fatal "--dry-run is not supported yet"
		else
			showcmd urpme --report-orphans
			sudocmd urpme --auto-orphans
		fi
		;;
	#emerge)
	#	sudocmd emerge --depclean
	#	assure_exists revdep-rebuild
	#	sudocmd revdep-rebuild
	#	;;
	pacman)
		if [ -n "$dryrun" ] ; then
			info "Autoorphans packages list:"
			sudocmd pacman -Qdtq
		else
			sudocmd pacman -Qdtq | sudocmd pacman -Rs -
		fi
		;;
	slackpkg)
		# clean-system removes non official packages
		sudocmd slackpkg clean-system
		;;
	#guix)
	#	sudocmd guix gc
	#	;;
	#pkgng)
	#	sudocmd pkg autoremove
	#	;;
	zypper-rpm)
		# https://www.linux.org.ru/forum/desktop/11931830
		assure_exists zypper zypper 1.9.2
		# For zypper < 1.9.2: zypper se -si | grep 'System Packages'
		sudocmd zypper packages --orphaned
		# FIXME: x86_64/i586 are duplicated
		local PKGLIST=$(zypper packages --orphaned | tail -n +5 | cut -d \| -f 3 | sort -u)
		docmd epm remove $dryrun --clean-deps $PKGLIST
		;;
	xbps)
		if [ -n "$dryrun" ] ; then
			fatal "--dry-run is not supported yet"
		else
			sudocmd xbps-remove -o
		fi
		;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
esac

}

# File bin/epm-autoremove:


__epm_print_excluded()
{
	local pkgs="$1"
	local fullpkgs="$2"
	local excluded
	excluded="$(estrlist exclude "$pkgs" "$fullpkgs")"
	if [ -n "$excluded" ] ; then
		echo "Skipped manually installed:"
		estrlist union $excluded
	fi
}

__epm_autoremove_altrpm_pp()
{
	local pkgs fullpkgs

	info "Removing unused python/perl modules..."
	#[ -n "$force" ] || info "You can run with --force for more deep removing"
	local force=force

	local libexclude="$1"

	local flag=

	[ -n "$force" ] || libexclude=$libexclude'[^-]*$'

	showcmd "apt-cache list-nodeps | grep -E -- \"$libexclude\""
	fullpkgs=$(apt-cache list-nodeps | grep -E -- "$libexclude" )
	pkgs=$(skip_manually_installed $fullpkgs)

	if [ -n "$dryrun" ] ; then
		info "Packages for autoremoving:"
		echo "$pkgs"
		__epm_print_excluded "$pkgs" "$fullpkgs"
		return 0
	fi

	__epm_print_excluded "$pkgs" "$fullpkgs"

	[ -n "$pkgs" ] && sudocmd rpm -v -e $pkgs && flag=1

	if [ -n "$flag" ] ; then
		info ""
		info "call again for next cycle until all modules will be removed"
		__epm_autoremove_altrpm_pp "$libexclude"
	fi

	return 0
}

__epm_autoremove_altrpm_lib()
{
	local pkgs fullpkgs

	local nodevel="$1"

	info
	if [ "$nodevel" = "nodevel" ] ; then
		info "Removing all non -devel/-debuginfo libs packages not need by anything..."
		local develrule='-(devel|devel-static)$'
	else
		info "Removing all non -debuginfo libs packages (-devel too) not need by anything..."
		local develrule='-(NONONO)$'
	fi
	#[ -n "$force" ] || info "You can run with --force for more deep removing"
	local force=force

	local flag=
	local libgrep='^(lib|i586-lib|bzlib|zlib)'
	[ -n "$force" ] || libexclude=$libgrep'[^-]*$'

	# https://www.altlinux.org/APT_в_ALT_Linux/Советы_по_использованию#apt-cache_list-nodeps
	showcmd "apt-cache list-nodeps | grep -E -- \"$libgrep\""
	fullpkgs=$(apt-cache list-nodeps | grep -E -- "$libgrep" \
		| sed -e "s/[-\.]32bit$//g" \
		| grep -E -v -- "$develrule" \
		| grep -E -v -- "-(debuginfo)$" \
		| grep -E -v -- "-(util|utils|tool|tools|plugin|daemon|help)$" \
		| grep -E -v -- "^(libsystemd|libreoffice|libnss|libvirt-client|libvirt-daemon|libsasl2-plugin|eepm)" )
	pkgs=$(skip_manually_installed $fullpkgs)

	if [ -n "$dryrun" ] ; then
		info "Packages for autoremoving:"
		echo "$pkgs"
		__epm_print_excluded "$pkgs" "$fullpkgs"
		return 0
	fi

	__epm_print_excluded "$pkgs" "$fullpkgs"

	# commented, with hi probability user install i586- manually
	# workaround against missed i586- handling in apt-cache list-nodeps
	if epmqp i586-lib >/dev/null ; then
		info "You can try removing all i586- with follow command"
		showcmd rpm -v -e $(epmqp i586-lib)
	fi

	[ -n "$pkgs" ] && sudocmd rpm -v -e $pkgs && flag=1

	if [ -n "$flag" ] ; then
		info ""
		info "call again for next cycle until all libs will be removed"
		__epm_autoremove_altrpm_lib $nodevel
	fi

	return 0
}


__epm_autoremove_altrpm()
{
	local i
	assure_exists /etc/buildreqs/files/ignore.d/apt-scripts apt-scripts

	if [ -z "$pkg_names" ] ; then
		__epm_autoremove_altrpm_pp '^(python-module-|python3-module-|python-modules-|python3-modules|perl-)'
		__epm_autoremove_altrpm_lib nodevel
		return 0
	fi

	for i in $pkg_names ; do
		case $i in
		libs)
			__epm_autoremove_altrpm_lib nodevel
			;;
		python)
			__epm_autoremove_altrpm_pp '^(python-module-|python3-module-|python-modules-|python3-modules)'
			;;
		perl)
			__epm_autoremove_altrpm_pp '^(perl-)'
			;;
		libs-devel)
			__epm_autoremove_altrpm_lib
			;;
		*)
			fatal "autoremove: unsupported '$i'. Use libs, python, perl, libs-devel."
			;;
		esac
	done

	return 0
}

epm_autoremove()
{

case $DISTRNAME in
	ALTLinux)
		__epm_autoremove_altrpm

		[ -n "$dryrun" ] && return

		docmd epm remove-old-kernels
		
		if which nvidia-clean-driver 2>/dev/null ; then
			sudocmd nvidia-clean-driver
		fi

		return
		;;
	*)
		;;
esac

[ -z "$pkg_filenames" ] || fatal "No arguments are allowed here"

case $PMTYPE in
	apt-dpkg|aptitude-dpkg)
		sudocmd apt-get autoremove $dryrun
		;;
	aura)
		if [ -n "$dryrun" ] ; then
			fatal "--dry-run is not supported yet"
		fi
		sudocmd aura -Oj
		;;
	yum-rpm)
		# cleanup orphanes?
		while true ; do
			# shellcheck disable=SC2046
			docmd package-cleanup --leaves $(subst_option non_interactive --assumeyes)
			# FIXME: package-cleanup have to use stderr for errors
			local PKGLIST=$(package-cleanup -q --leaves | grep -v "^eepm-")
			[ -n "$PKGLIST" ] || break
			showcmd epm remove $PKGLIST
		done
		;;
	dnf-rpm)
		if [ -n "$dryrun" ] ; then
			fatal "--dry-run is not supported yet"
		fi
		sudocmd dnf autoremove
		;;
	# see autoorhans
	#urpm-rpm)
	#	sudocmd urpme --auto-orphans
	#	;;
	emerge)
		if [ -n "$dryrun" ] ; then
			fatal "--dry-run is not supported yet"
		fi
		sudocmd emerge --depclean
		assure_exists revdep-rebuild
		sudocmd revdep-rebuild
		;;
	# see autoorhans
	#pacman)
	#	sudocmd pacman -Qdtq | sudocmd pacman -Rs -
	#	;;
	slackpkg)
		# clean-system removes non official packages
		#sudocmd slackpkg clean-system
		;;
	guix)
		sudocmd guix gc
		;;
	pkgng)
		sudocmd pkg autoremove
		;;
	zypper-rpm)
		# https://www.linux.org.ru/forum/desktop/11931830
		assure_exists zypper zypper 1.9.3
		sudocmd zypper packages --unneeded
		# FIXME: x86_64/i586 are duplicated
		local PKGLIST=$(zypper packages --unneeded | tail -n +5 | cut -d \| -f 3 | sort -u)
		showcmd epm remove --clean-deps $PKGLIST
		;;
	xbps)
		if [ -n "$dryrun" ] ; then
			fatal "--dry-run is not supported yet"
		fi
		sudocmd xbps-remove -O
		;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
esac

}

# File bin/epm-changelog:


__epm_changelog_apt()
{
	local i
	for i in $@ ; do
		docmd apt-cache show $i | grep -A 1000 "^Changelog:"
	done
}

__epm_changelog_files()
{
	[ -z "$*" ] && return

	# TODO: detect every file
	case $(get_package_type $1) in
		rpm)
			assure_exists rpm
			docmd_foreach "rpm -q -p --changelog" $@ | less
			;;
		*)
			fatal "Have no suitable command for $1"
			;;
	esac
}

__epm_changelog_local_names()
{
	[ -z "$*" ] && return

	case $PMTYPE in
		apt-rpm|yum-rpm|dnf-rpm|urpm-rpm|zypper-rpm)
			docmd_foreach "rpm -q --changelog" $@ | less
			;;
		apt-dpkg|aptitude-dpkg)
			docmd zcat /usr/share/doc/$1/changelog.Debian.gz | less
			;;
		emerge)
			assure_exists equery
			docmd equery changes -f $1 | less
			;;
		pacman)
			docmd pacman -Qc $1 | less
			;;
		*)
			fatal "Have no suitable command for $PMTYPE"
			;;
	esac
}

__epm_changelog_unlocal_names()
{
	[ -z "$*" ] && return

	case $PMTYPE in
		apt-rpm)
			__epm_changelog_apt $@ | less
			;;
		#apt-dpkg)
		#	# FIXME: only first pkg
		#	docmd zcat /usr/share/doc/$1/changelog.Debian.gz | less
		#	;;
		#yum-rpm)
		#	sudocmd yum clean all
		#	;;
		urpm-rpm)
			docmd urpmq --changelog $@ | less
			;;
		#zypper-rpm)
		#	sudocmd zypper clean
		#	;;
		emerge)
			assure_exists equery
			docmd equery changes -f $1 | less
			;;
		*)
			fatal "Have no suitable command for $PMTYPE. Try install the package firstly."
			;;
	esac

}


epm_changelog()
{
	[ -n "$pkg_filenames" ] || fatal "Changelog: Missing package(s) name"

	__epm_changelog_files $pkg_files

	local pkg
	for pkg in $pkg_names ; do
		if is_installed $pkg ; then
			__epm_changelog_local_names $pkg
		else
			__epm_changelog_unlocal_names $pkg
		fi
	done
}

# File bin/epm-check:

epm_check()
{
case $PMTYPE in
	apt-rpm|apt-dpkg)
		#sudocmd apt-get check || exit
		#sudocmd apt-get update || exit
		sudocmd apt-get -f install
		;;
	apt-dpkg)
		#sudocmd apt-get update || exit
		#sudocmd apt-get check || exit
		sudocmd apt-get -f install || return
		sudocmd apt-get autoremove
		;;
	aptitude-dpkg)
		sudocmd aptitude -f install || return
		#sudocmd apt-get autoremove
		;;
	yum-rpm)
		docmd yum check
		docmd package-cleanup --problems

		#docmd package-cleanup --dupes
		sudocmd package-cleanup --cleandupes

		docmd rpm -Va --nofiles --nodigest
		;;
	dnf-rpm)
		sudocmd dnf check
		;;
	emerge)
		sudocmd revdep-rebuild
		;;
	#urpm-rpm)
	#	sudocmd urpme --auto-orphans
	#	;;
	zypper-rpm)
		sudocmd zypper verify
		;;
	conary)
		sudocmd conary verify
		;;
	pkgng)
		sudocmd pkg check -d -a
		;;
	homebrew)
		docmd brew doctor
		;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
esac

}

# File bin/epm-checkpkg:

__rpm_allows_nosignature()
{
    a= rpm --help | grep -q -- "--nosignature"
}

check_pkg_integrity()
{
	local PKG="$1"
	local RET
	local NOSIGNATURE

	case $(get_package_type $PKG) in
	rpm)
		assure_exists rpm
		__rpm_allows_nosignature && NOSIGNATURE="--nosignature" || NOSIGNATURE="--nogpg"
		docmd rpm --checksig $NOSIGNATURE $PKG
		;;
	deb)
		assure_exists dpkg
		# FIXME: debsums -ca package ?
		docmd dpkg --contents $PKG >/dev/null && echo "Package $PKG is correct."
		;;
	exe)
		file $PKG | grep -q "executable for MS Windows"
		;;
	msi)
		# TODO: add to patool via cabextract
		assure_exists cabextract
		#file $PKG | grep -q "Microsoft Office Document"
		docmd cabextract -t $PKG
		;;
	ebuild)
		true
		;;
	*)
		assure_exists erc
		docmd erc test "$PKG" && return
		;;
	esac
}

__epm_check_installed_pkg()
{
case $PMTYPE in
	*-rpm)
		docmd rpm -V $@
		;;
	*-dpkg)
		assure_exists debsums
		docmd debsums $@
		;;
	emerge)
		assure_exists equery
		docmd equery check $@
		;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
esac

}


epm_checkpkg()
{
	if [ -n "$pkg_names" ] ; then
		# TODO: если есть / или расширение, это отсутствующий файл
		info "Suggest $pkg_names are name(s) of installed packages"
		__epm_check_installed_pkg $pkg_names
		return
	fi

	# if possible, it will put pkg_urls into pkg_files or pkg_names
	if [ -n "$pkg_urls" ] ; then
		__handle_pkg_urls_to_checking
	fi

	[ -n "$pkg_files" ] || fatal "Checkpkg: missing file or package name(s)"

	local RETVAL=0

	local pkg
	for pkg in $pkg_files ; do
		check_pkg_integrity $pkg || RETVAL=1
	done

	# TODO: reinvent
	[ -n "$to_remove_pkg_files" ] && rm -fv $to_remove_pkg_files
	[ -n "$to_remove_pkg_files" ] && rmdir -v $(dirname $to_remove_pkg_files | head -n1) 2>/dev/null

	#fatal "Broken package $pkg"
	return $RETVAL
}

# File bin/epm-checksystem:


epm_checksystem_ALTLinux()
{
	local TDIR=$(mktemp -d)
	assure_exists time
	touch $TDIR/added
	for ft in $(ls /usr/lib/rpm/*.filetrigger | sort) ; do
		echo "Try run $ft ..."
		echo $TDIR/added $TDIR/removed | a='' time $ft
	done
	rm -f $TDIR/added fatal
	rmdir $TDIR || fatal
	echo "Count lines:"
	wc -l /var/lib/rpm/files-awaiting-filetriggers
}


epm_checksystem()
{

[ $EFFUID = "0" ] && fatal "Do not use checksystem under root"

case $DISTRNAME in
	ALTLinux)
		epm_checksystem_$DISTRNAME
		;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
esac

}

if [ "$1" = "--debug" ] ; then
	shift
	SUDO=sudo
	DISTRNAME=ALTLinux
	epm_checksystem
fi

# File bin/epm-check_updated_repo:

__is_repo_info_downloaded()
{
    case $PMTYPE in
        apt-*)
            if [ -r /var/cache/apt ] ; then
                $SUDO test -r /var/cache/apt/pkgcache.bin || return
            fi
            ;;
        *)
            ;;
    esac
    return 0
}

__is_repo_info_uptodate()
{
    case $PMTYPE in
        apt-*)
            # apt-deb do not update lock file date
            #if $SUDO test -r /var/lib/apt/lists ; then
                local LOCKFILE=/var/lib/apt/lists
                $SUDO test -r $LOCKFILE || return
                # if repo older than 1 day, return false
                # find print string if file is obsoleted
                test -z "$(find $LOCKFILE -maxdepth 0 -mtime +1)" || return
            #fi
            ;;
        *)
            ;;
    esac
    return 0
}

update_repo_if_needed()
{
    # check if we need skip update checking
    if [ "$1" = "soft" ] && [ -n "$SUDO" ] ; then
        # if sudo requires a password, skip autoupdate
        sudo -n true 2>/dev/null || { info "sudo requires a password, skip repo status checking" ; return 0 ; }
    fi

    cd / || fatal
    if ! __is_repo_info_downloaded || ! __is_repo_info_uptodate ; then
        pkg_filenames='' epm_update
    fi
    cd - >/dev/null || fatal

}

save_installed_packages()
{
	[ -d /var/lib/rpm ] || return 0
	estrlist list "$@" | $SUDO tee /var/lib/rpm/EPM-installed >/dev/null
}

check_manually_installed()
{
	[ -r /var/lib/rpm/EPM-installed ] || return 1
	grep -q -- "^$1\$" /var/lib/rpm/EPM-installed
}

skip_manually_installed()
{
	local i
	for i in "$@" ; do
		check_manually_installed "$i" && continue
		echo "$i"
	done
}

# File bin/epm-clean:

__remove_alt_apt_cache_file()
{
	sudocmd rm -vf /var/cache/apt/*.bin
	sudocmd rm -vf /var/cache/apt/partial/*
	sudocmd rm -vf /var/lib/apt/lists/*pkglist*
	sudocmd rm -vf /var/lib/apt/lists/*release*
}

__remove_deb_apt_cache_file()
{
	sudocmd rm -vf /var/cache/apt/*.bin
	sudocmd rm -vf /var/cache/apt/archives/partial/*
	sudocmd rm -vf /var/lib/apt/lists/*Packages*
	sudocmd rm -vf /var/lib/apt/lists/*Release*
	sudocmd rm -vf /var/lib/apt/lists/*Translation*
}

epm_clean()
{

[ -z "$pkg_filenames" ] || fatal "No arguments are allowed here"


case $PMTYPE in
	apt-rpm)
		sudocmd apt-get clean
		[ -n "$force" ] && __remove_alt_apt_cache_file
		;;
	apt-dpkg)
		sudocmd apt-get clean
		[ -n "$force" ] && __remove_deb_apt_cache_file
		;;
	aptitude-dpkg)
		sudocmd aptitude clean
		[ -n "$force" ] && __remove_deb_apt_cache_file
		;;
	yum-rpm)
		sudocmd yum clean all
		#sudocmd yum makecache
		;;
	dnf-rpm)
		sudocmd dnf clean all
		;;
	urpm-rpm)
		sudocmd urpmi --clean
		;;
	pacman)
		sudocmd pacman -Sc --noconfirm
		;;
	zypper-rpm)
		sudocmd zypper clean
		;;
	nix)
		sudocmd nix-collect-garbage
		;;
	slackpkg)
		;;
	pkgng)
		sudocmd pkg clean -a
		;;
	xbps)
		sudocmd xbps-remove -O
		;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
esac
	info "It is recommend to run 'epm autoremove' also"

}

# File bin/epm-conflicts:


epm_conflicts_files()
{
	[ -n "$pkg_files" ] || return

	case $(get_package_type $pkg_files) in
		rpm)
			assure_exists rpm
			docmd "rpm -q --conflicts -p" $pkg_files
			;;
		#deb)
		#	a= docmd dpkg -I $pkg_files | grep "^ *Depends:" | sed "s|^ *Depends:||g"
		#	;;
		*)
			fatal "Have no suitable command for $PMTYPE"
			;;
	esac
}

epm_conflicts_names()
{
	local CMD
	[ -n "$pkg_names" ] || return

case $PMTYPE in
	apt-rpm)
		# FIXME: need fix for a few names case
		# FIXME: too low level of requires name (libSOME.so)
		if is_installed $pkg_names ; then
			CMD="rpm -q --conflicts"
		else
			EXTRA_SHOWDOCMD=' | grep "Conflicts:"'
			docmd apt-cache show $pkg_names | grep "Conflicts:"
			return
		fi

		;;
	urpm-rpm|zypper-rpm)
		# FIXME: use hi level commands
		CMD="rpm -q --conflicts"
		;;
	#yum-rpm)
	#	CMD="yum deplist"
	#	;;
	#pacman)
	#	CMD="pactree"
	#	;;
	apt-dpkg)
		# FIXME: need fix for a few names case
		if is_installed $pkg_names ; then
			showcmd dpkg -s $pkg_names
			a='' dpkg -s $pkg_names | grep "^Conflicts:" | sed "s|^Conflicts:||g"
			return
		else
			EXTRA_SHOWDOCMD=' | grep "Conflicts:"'
			docmd apt-cache show $pkg_names | grep "Conflicts:"
			return
		fi
		;;
	# TODO: why-not show who conflicts with us
	#aptitude-dpkg)
	#	docmd aptitude why-not $pkg_names
	#	;;

	#emerge)
	#	assure_exists equery
	#	CMD="equery depgraph"
	#	;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
esac


docmd $CMD $pkg_names

}

epm_conflicts()
{
	[ -n "$pkg_filenames" ] || fatal "Conflicts: Missing package(s) name"
	epm_conflicts_files
	epm_conflicts_names
}

# File bin/epm-downgrade:


__epm_add_alt_apt_downgrade_preferences()
{
	[ -r /etc/apt/preferences ] && fatal "/etc/apt/preferences already exists"
	cat <<EOF | $SUDO tee /etc/apt/preferences
Package: *
Pin: release c=classic
Pin-Priority: 1001

Package: *
Pin: release c=addon
Pin-Priority: 1101
EOF
}

__epm_add_deb_apt_downgrade_preferences()
{
	[ -r /etc/apt/preferences ] && fatal "/etc/apt/preferences already exists"
	info "Running with /etc/apt/preferences:"
	cat <<EOF | $SUDO tee /etc/apt/preferences
Package: *
Pin: release a=stable
Pin-Priority: 1001

Package: *
Pin: release a=testing
Pin-Priority: 900

Package: *
Pin: release a=unstable
Pin-Priority: 800
EOF
}

__epm_remove_apt_downgrade_preferences()
{
	sudocmd rm -f /etc/apt/preferences
}

epm_downgrade()
{
	local CMD

	# it is useful for first time running
	update_repo_if_needed

	info "Running command for downgrade packages"

	case $PMTYPE in
	apt-rpm)
		__epm_add_alt_apt_downgrade_preferences || return
		if [ -n "$pkg_filenames" ] ; then
			sudocmd apt-get install $pkg_filenames
		else
			sudocmd apt-get dist-upgrade
		fi
		__epm_remove_apt_downgrade_preferences
		;;
	apt-dpkg)
		__epm_add_deb_apt_downgrade_preferences || return
		if [ -n "$pkg_filenames" ] ; then
			sudocmd apt-get install $pkg_filenames
		else
			sudocmd apt-get dist-upgrade
		fi
		__epm_remove_apt_downgrade_preferences
		;;
	yum-rpm)
		# can do update repobase automagically
		if [ -n "$pkg_filenames" ] ; then
			sudocmd yum downgrade $pkg_filenames
		else
			sudocmd yum distro-sync
		fi
		;;
	dnf-rpm)
		if [ -n "$pkg_filenames" ] ; then
			sudocmd dnf downgrade $pkg_filenames
		else
			sudocmd dnf distro-sync
		fi
		;;
	urpm-rpm)
		assure_exists urpm-reposync urpm-tools
		sudocmd urpm-reposync -v
		;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
	esac
}

# File bin/epm-download:

alt_base_dist_url="http://ftp.basealt.ru/pub/distributions"

__use_url_install()
{
	case $DISTRNAME in
		"ALTLinux")
			# do not support https yet
			echo "$pkg_urls" | grep -q "https://" && return 1
			# force download if wildcard is used
			echo "$pkg_urls" | grep -q "[?*]" && return 1
			pkg_names="$pkg_names $pkg_urls"
			return 0
			;;
	esac

	case $PMTYPE in
		#apt-rpm)
		#	pkg_names="$pkg_names $pkg_urls"
		#	;;
		#deepsolver-rpm)
		#	pkg_names="$pkg_names $pkg_urls"
		#	;;
		#urpm-rpm)
		#	pkg_names="$pkg_names $pkg_urls"
		#	;;
		pacman)
			pkg_names="$pkg_names $pkg_urls"
			;;
		yum-rpm|dnf-rpm)
			pkg_names="$pkg_names $pkg_urls"
			;;
		#zypper-rpm)
		#	pkg_names="$pkg_names $pkg_urls"
		#	;;
		*)
			return 1
			;;
	esac
	return 0
}

__download_pkg_urls()
{
	local url
	[ -z "$pkg_urls" ] && return
	for url in $pkg_urls ; do
		local tmppkg=$(mktemp -d) || fatal "failed mktemp -d"
		cd $tmppkg || fatal
		if docmd eget "$url" ; then
			local i
			for i in $(basename $url) ; do
				[ -s "$tmppkg/$i" ] || continue
				pkg_files="$pkg_files $tmppkg/$i"
				to_remove_pkg_files="$to_remove_pkg_files $tmppkg/$i"
			done
		else
			warning "Failed to download $url, ignoring"
		fi
		cd - >/dev/null
	done
	# reconstruct
	pkg_filenames=$(strip_spaces "$pkg_files $pkg_names")
}

__handle_pkg_urls_to_install()
{
	#[ -n "$pkg_urls" ] || return

	# TODO: do it correctly
	to_remove_pkg_files=
	# FIXME: check type of pkg_urls separately?
	if [ "$(get_package_type "$pkg_urls")" != $PKGFORMAT ] || ! __use_url_install ; then
		# use workaround with eget: download and put in pkg_files
		__download_pkg_urls
	fi

	pkg_urls=
}

__handle_pkg_urls_to_checking()
{
	#[ -n "$pkg_urls" ] || return

	# TODO: do it correctly
	to_remove_pkg_files=
	
	# use workaround with eget: download and put in pkg_files
	__download_pkg_urls

	pkg_urls=
}


__epm_get_altpkg_url()
{
	info "TODO: https://packages.altlinux.org/api/branches"
	local arch=$(paoapi packages/$1 | get_pao_var arch)
	# FIXME: arch can be list
	[ "$arch" = "noarch" ] || arch=$(arch)
	# HACK: filename can be list
	local filename=$(paoapi packages/$1 | get_pao_var filename | grep $arch)
	[ -n "$filename" ] || fatal "Can't get filename"
	# fixme: get from /branches
	local dv=$DISTRNAME/$DISTRVERSION/branch
	[ "$DISTRVERSION" = "Sisyphus" ] && dv=$DISTRNAME/$DISTRVERSION
	echo "$alt_base_dist_url/$dv/$arch/RPMS.classic/$filename"
}

__epm_print_url_alt()
{
	local url="$1"
	echo "$url"
	echo "$url" | sed -e "s|$alt_base_dist_url/$DISTRNAME|http://mirror.yandex.ru/altlinux|g"
	echo "$url" | sed -e "s|$alt_base_dist_url/$DISTRNAME|http://download.etersoft.ru/pub/ALTLinux|g"
}

__epm_print_url_alt_check()
{
	local pkg=$1
	shift
	local tm=$(mktemp)
	epm assure curl
	quiet=1
	local buildtime=$(paoapi packages/$pkg | get_pao_var buildtime)
	echo
	echo "Latest release: $(paoapi packages/$pkg | get_pao_var sourcepackage) $buildtime"
	__epm_print_url_alt "$1" | while read url ; do
		a='' curl -s --head $url >$tm || { echo "$url: missed" ; continue ; }
		local http=$(cat $tm | grep "^HTTP" | sed -e "s|\r||g")
		local lastdate=$(cat $tm | grep "^Last-Modified:" | sed -e "s|\r||g")
		local size=$(cat $tm | grep "^Content-Length:" | sed -e "s|^Content-Length: ||g"  | sed -e "s|\r||g")
		echo "$url ($http $lastdate) Size: $size"
	done
	rm -f $tm
}

__epm_download_alt()
{
	local pkg
	if [ "$1" = "--check" ] ; then
		local checkflag="$1"
		shift
	fi
	for pkg in "$@" ; do
		local url=$(__epm_get_altpkg_url $pkg)
		[ -n "$url" ] || warning "Can't get URL for $pkg"
		if [ -n "$checkflag" ] ; then
			__epm_print_url_alt_check "$pkg" "$url"
		else
			docmd eget $url || return
		fi
	done
}

epm_download()
{
	local CMD

	case $DISTRNAME in
		ALTLinux)
			__epm_download_alt $pkg_filenames
			return
			;;
	esac

	case $PMTYPE in
	dnf-rpm)
		sudocmd dnf download $pkg_filenames
		;;
	aptcyg)
		sudocmd apt-cyg download $pkg_filenames
		;;
	yum-rpm)
		# TODO: check yum install --downloadonly --downloaddir=/tmp <package-name>
		assure_exists yumdownloader yum-utils
		sudocmd yumdownloader $pkg_filenames
		;;
	dnf-rpm)
		sudocmd dnf download $pkg_filenames
		;;
	urpm-rpm)
		sudocmd urpmi --no-install $URPMOPTIONS $@
		;;
	tce)
		sudocmd tce-load -w $pkg_filenames
		;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
	esac
}

# File bin/epm-epm_install:



__epm_korinf_site() {
    local archprefix=""
    [ "$($DISTRVENDOR -a)" = "x86_64" ] && archprefix="x86_64/"
    local aftername="-"
    [ "$($DISTRVENDOR -p)" = "deb" ] && aftername="_"
    echo "http://updates.etersoft.ru/pub/Korinf/$archprefix$($DISTRVENDOR -e)"
}

__epm_korinf_list() {
    [ "$1" = "--list" ] && shift
    local MASK="$1"
    showcmd eget --list "$(__epm_korinf_site)/$MASK*.$($DISTRVENDOR -p)"
    eget --list "$(__epm_korinf_site)/$MASK*.$($DISTRVENDOR -p)" | sort
}


__epm_korinf_install(){
    local PACKAGE="$1"
    # FIXME: some way to get latest package
    local fn="$(__epm_korinf_list $PACKAGE$aftername | tail -n1)"
    [ -n "$fn" ] || fatal "Can't find package file for $PACKAGE"
    local download_link=$(__epm_korinf_site)/$fn
    #info "Install $download_link ..."
    pkg_names='' pkg_files='' pkg_urls="$download_link" epm_install
}

epm_epm_install(){
    local i
    local pkglist="$pkg_filenames"
    # install epm by default
    [ -n "$pkglist" ] || pkglist="eepm"

    case "$pkglist" in
        --list*)
            __epm_korinf_list $pkglist
            return
            ;;
    esac

    for i in $pkglist ; do
        __epm_korinf_install $i
    done
}

# File bin/epm-filelist:


__alt_local_content_filelist()
{

    local CI="$(get_local_alt_contents_index)"
    [ -n "$CI" ] || fatal "Have no local contents index"

    # TODO: safe way to use less
    #local OUTCMD="less"
    #[ -n "$USETTY" ] || OUTCMD="cat"
    OUTCMD="cat"

    {
        [ -n "$USETTY" ] && info "Search in $CI for $1..."
        __local_ercat $CI | grep -h -- ".*$1$" | sed -e "s|\(.*\)\t\(.*\)|\1|g"
    } | $OUTCMD
}

__deb_local_content_filelist()
{
    showcmd "apt-file list $1 | grep '^$1: ' | sed -e 's|$1: ||g'"
    a='' apt-file list "$1" | grep "^$1: " | sed -e "s|$1: ||g"
}


__epm_filelist_remote()
{
	[ -z "$*" ] && return

	case $PMTYPE in
		apt-rpm)
			# TODO: use RESTful interface to prometeus? See ALT bug #29496
			docmd_foreach __alt_local_content_filelist "$@"
			;;
		apt-dpkg)
			assure_exists apt-file || return
			if sudo -n true 2>/dev/null ; then
				sudocmd apt-file update
			else
				info "sudo requires a password, skip apt-file update"
			fi
			docmd_foreach __deb_local_content_filelist "$@"
			;;
		yum-rpm)
			assure_exists yum-utils || return
			docmd repoquery -q -l "$@"
			;;
		dnf-rpm)
			assure_exists dnf-plugins-core || return
			docmd dnf repoquery -l "$@"
			;;
		*)
			fatal "Query filelist for non installed packages is not implemented yet."
			;;
	esac
}

__epm_filelist_file()
{
	local CMD

	[ -z "$*" ] && return

	# TODO: allow a new packages
	case $(get_package_type $1) in
		rpm)
			assure_exists rpm
			CMD="rpm -qlp"
			;;
		deb)
			assure_exists dpkg
			CMD="dpkg --contents"
			;;
		*)
			fatal "Have no suitable query command for $PMTYPE"
			;;
	esac

	docmd $CMD $@ | less
}

__epm_filelist_name()
{
	local CMD

	[ -z "$*" ] && return

	warmup_lowbase

	case $PMTYPE in
		*-rpm)
			CMD="rpm -ql"
			;;
		*-dpkg)
			CMD="dpkg -L"
			;;
		android)
			CMD="pm list packages -f"
			;;
		conary)
			CMD="conary query --ls"
			;;
		pacman)
			docmd pacman -Ql $@ | sed -e "s|.* ||g" | less
			return
			;;
		emerge)
			assure_exists equery
			CMD="equery files"
			;;
		homebrew)
			CMD="brew list"
			;;
		pkgng)
			CMD="pkg info -l"
			;;
		xbps)
			CMD="xbps-query -f"
			;;
		aptcyg)
			docmd apt-cyg listfiles $@ | sed -e "s|^|/|g"
			return
			;;
		slackpkg)
			is_installed $@ || fatal "Query filelist for non installed packages is not implemented yet"
			docmd awk 'BEGIN{desk=1}{if(/^FILE LIST:$/){desk=0} else if (desk==0) {print}}' /var/log/packages/${pkg_filenames}* | less
			return
			;;
		*)
			fatal "Have no suitable query command for $PMTYPE"
			;;
	esac

	# TODO: add less
	docmd $CMD $@ && return
	# TODO: may be we need check is installed before prev. line?
	is_installed $@ || __epm_filelist_remote $@
}


epm_filelist()
{
	[ -n "$pkg_filenames" ] || fatal "Filelist: missing package(s) name"


	__epm_filelist_file $pkg_files || return
	# shellcheck disable=SC2046
	__epm_filelist_name $(print_name $pkg_names) || return

}

# File bin/epm-info:


__epm_info_rpm_low()
{
	if [ -n "$pkg_files" ] ; then
		docmd rpm -qip $pkg_files
	fi
	[ -z "$pkg_names" ] && return
	is_installed $pkg_names && docmd rpm -qi $pkg_names && return
}

__epm_info_by_pkgtype()
{
	[ -n "$pkg_files" ] || return 1

	case $(get_package_type $pkg_files) in
		rpm)
			__epm_info_rpm_low && return
			;;
		deb)
			docmd dpkg -I $pkg_files
			;;
		*)
			return 1
			;;
	esac
}

__epm_info_by_pmtype()
{
case $PMTYPE in
	apt-rpm)
		__epm_info_rpm_low && return
		docmd apt-cache show $pkg_names
		;;
	apt-dpkg)
		if [ -n "$pkg_files" ] ; then
			docmd dpkg -I $pkg_files
		fi
		[ -z "$pkg_names" ] && return
		is_installed $pkg_names && docmd dpkg -p $pkg_names && return
		docmd apt-cache show $pkg_names
		;;
	aptitude-dpkg)
		if [ -n "$pkg_files" ] ; then
			docmd dpkg -I $pkg_files
		fi
		[ -z "$pkg_names" ] && return
		docmd aptitude show $pkg_names
		;;
	yum-rpm)
		__epm_info_rpm_low && return
		docmd yum info $pkg_names
		;;
	urpmi-rpm)
		__epm_info_rpm_low && return
		docmd urpmq -i $pkg_names
		;;
	dnf-rpm)
		__epm_info_rpm_low && return
		docmd dnf info $pkg_names
		;;
	zypper-rpm)
		__epm_info_rpm_low && return
		docmd zypper info $pkg_names
		;;
	pacman)
		is_installed $pkg_names && docmd pacman -Qi $pkg_names && return
		docmd pacman -Si $pkg_names
		;;
	aura)
		is_installed $pkg_names && docmd pacman -Qi $pkg_names && return
		docmd aura -Ai $pkg_names
		;;
	npackd)
		# FIXME: --version=
		docmd npackdcl info --package=$pkg_names
		;;
	conary)
		is_installed $pkg_names && docmd conary query $pkg_names --info && return
		docmd conary repquery $pkg_names --info
		;;
	emerge)
		assure_exists equery
		docmd equery meta $pkg_names
		docmd equery which $pkg_names
		docmd equery uses $pkg_names
		docmd equery size $pkg_names
		;;
	slackpkg)
		docmd /usr/sbin/slackpkg info $pkg_names
		;;
	ipkg)
		docmd ipkg info $pkg_names
		;;
	pkgng)
		docmd pkg info $pkg_names
		;;
	xbps)
		docmd xbps-query --show $pkg_names
		;;
	homebrew)
		docmd brew info $pkg_names
		;;
	aptcyg)
		docmd apt-cyg show $pkg_names
		;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
esac
}


epm_info()
{

if [ -n "$pkg_urls" ] ; then
    __handle_pkg_urls_to_checking
fi

[ -n "$pkg_filenames" ] || fatal "Info: missing package(s) name"

__epm_info_by_pkgtype || __epm_info_by_pmtype

local RETVAL=$?

[ -n "$to_remove_pkg_files" ] && rm -fv $to_remove_pkg_files
[ -n "$to_remove_pkg_files" ] && rmdir -v $(dirname $to_remove_pkg_files | head -n1) 2>/dev/null

return $RETVAL
}

# File bin/epm-install:


__fast_hack_for_filter_out_installed_rpm()
{
	LANG=C LC_ALL=C xargs -n1 rpm -q 2>&1 | grep 'is not installed' |
		sed -e 's|^.*package \(.*\) is not installed.*|\1|g'
}

filter_out_installed_packages()
{
	[ -z "$skip_installed" ] && cat && return

	case $PMTYPE in
		yum-rpm|dnf-rpm)
			if [ "$($DISTRVENDOR -a)" = "x86_64" ] ; then
				# shellcheck disable=SC2013
				for i in $(cat) ; do
					is_installed "$(__print_with_arch_suffix $i .x86_64)" && continue
					is_installed "$(__print_with_arch_suffix $i .noarch)" && continue
					echo $i
				done
			else
				__fast_hack_for_filter_out_installed_rpm
			fi
			;;
		*-rpm)
			__fast_hack_for_filter_out_installed_rpm
			;;
		# dpkg -l lists some non ii status (un, etc)
		#"deb")
		#	LANG=C LC_ALL=C xargs -n1 dpkg -l 2>&1 | grep -i 'no packages found matching' |
		#		sed -e 's|\.\+$||g' -e 's|^.*[Nn]o packages found matching \(.*\)|\1|g'
		#	;;
		*)
			# shellcheck disable=SC2013
			for i in $(cat) ; do
				is_installed $i || echo $i
			done
			;;
	esac | sed -e "s|rpm-build-altlinux-compat[^ ]*||g" | filter_strip_spaces
}

__use_zypper_no_gpg_checks()
{
    a='' zypper install --help 2>&1 | grep -q -- "--no-gpg-checks" && echo "--no-gpg-checks"
}

__separate_sudocmd_foreach()
{
    local cmd_re=$1
    local cmd_in=$2
    shift 2
    separate_installed $@
    if [ -n "$pkg_noninstalled" ] ; then
        sudocmd_foreach "$cmd_re" $pkg_noninstalled || return
    fi
    if [ -n "$pkg_installed" ] ; then
        sudocmd_foreach "$cmd_in" $pkg_installed || return
    fi
    return 0
}

__separate_sudocmd()
{
    local cmd_re=$1
    local cmd_in=$2
    shift 2
    separate_installed $@
    if [ -n "$pkg_noninstalled" ] ; then
        sudocmd "$cmd_re" $pkg_noninstalled || return
    fi
    if [ -n "$pkg_installed" ] ; then
        sudocmd "$cmd_in" $pkg_installed || return
    fi
    return 0
}

epm_install_names()
{
	[ -z "$1" ] && return

	warmup_hibase

	if [ -n "$non_interactive" ] ; then
		epm_ni_install_names "$@"
		return
	fi

	case $PMTYPE in
		apt-rpm|apt-dpkg)
			APTOPTIONS="$APTOPTIONS $(subst_option verbose "-o Debug::pkgMarkInstall=1 -o Debug::pkgProblemResolver=1")"
			sudocmd apt-get $APTOPTIONS $noremove install $@ && save_installed_packages $@
			return ;;
		aptitude-dpkg)
			sudocmd aptitude install $@
			return ;;
		deepsolver-rpm)
			sudocmd ds-install $@
			return ;;
		urpm-rpm)
			sudocmd urpmi $URPMOPTIONS $@
			return ;;
		pkgsrc)
			sudocmd pkg_add -r $@
			return ;;
		pkgng)
			sudocmd pkg install $@
			return ;;
		emerge)
			sudocmd emerge -uD $@
			return ;;
		pacman)
			sudocmd pacman -S $force $nodeps $@
			return ;;
		aura)
			sudocmd aura -A $force $nodeps $@
			return ;;
		yum-rpm)
			sudocmd yum $YUMOPTIONS install $(echo "$*" | exp_with_arch_suffix)
			return ;;
		dnf-rpm)
			sudocmd dnf install $(echo "$*" | exp_with_arch_suffix)
			return ;;
		snappy)
			sudocmd snappy install $@
			return ;;
		zypper-rpm)
			sudocmd zypper install $ZYPPEROPTIONS $@
			return ;;
		mpkg)
			sudocmd mpkg install $@
			return ;;
		conary)
			sudocmd conary update $@
			return ;;
		npackd)
			# FIXME: correct arg
			__separate_sudocmd_foreach "npackdcl add --package=" "npackdcl update --package=" $@
			return ;;
		slackpkg)
			__separate_sudocmd_foreach "/usr/sbin/slackpkg install" "/usr/sbin/slackpkg upgrade" $@
			return ;;
		homebrew)
			# FIXME: sudo and quote
			SUDO='' __separate_sudocmd "brew install" "brew upgrade" "$@"
			return ;;
		ipkg)
			[ -n "$force" ] && force=-force-depends
			sudocmd ipkg $force install $@
			return ;;
		nix)
			__separate_sudocmd "nix-env --install" "nix-env --upgrade" "$@"
			return ;;
		apk)
			sudocmd apk add $@
			return ;;
		tce)
			sudocmd tce-load -wi $@
			return ;;
		guix)
			__separate_sudocmd "guix package -i" "guix package -i" $@
			return ;;
		android)
			fatal "We still have no idea how to use package repository, ever if it is F-Droid."
			return ;;
		aptcyg)
			sudocmd apt-cyg install $@
			return ;;
		xbps)
			sudocmd xbps-install $@
			return ;;
		*)
			fatal "Have no suitable install command for $PMTYPE"
			;;
	esac
}

epm_ni_install_names()
{
	[ -z "$1" ] && return

	case $PMTYPE in
		apt-rpm|apt-dpkg)
			export DEBIAN_FRONTEND=noninteractive
			sudocmd apt-get -y $noremove --force-yes -o APT::Install::VirtualVersion=true -o APT::Install::Virtual=true -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" $APTOPTIONS install $@
			return ;;
		aptitude-dpkg)
			sudocmd aptitude -y install $@
			return ;;
		yum-rpm)
			sudocmd yum -y $YUMOPTIONS install $(echo "$*" | exp_with_arch_suffix)
			return ;;
		dnf-rpm)
			sudocmd dnf -y $YUMOPTIONS install $(echo "$*" | exp_with_arch_suffix)
			return ;;
		urpm-rpm)
			sudocmd urpmi --auto $URPMOPTIONS $@
			return ;;
		zypper-rpm)
			# FIXME: returns true ever no package found, need check for "no found", "Nothing to do."
			yes | sudocmd zypper --non-interactive $ZYPPEROPTIONS install $@
			return ;;
		pkgsrc)
			sudocmd pkg_add -r $@
			return ;;
		pkgng)
			sudocmd pkg install -y $@
			return ;;
		emerge)
			sudocmd emerge -uD $@
			return ;;
		pacman)
			sudocmd pacman -S --noconfirm $force $nodeps $@
			return ;;
		aura)
			sudocmd aura -A $force $nodeps $@
			return ;;
		npackd)
			#  npackdcl update --package=<package> (remove old and install new)
			sudocmd npackdcl add --package="$*"
			return ;;
		chocolatey)
			docmd chocolatey install $@
			return ;;
		ipkg)
			sudocmd ipkg -force-defaults install $@
			return ;;
		nix)
			sudocmd nix-env --install $@
			return ;;
		apk)
			sudocmd apk add $@
			return ;;
		tce)
			sudocmd tce-load -wi $@
			return ;;
		xbps)
			sudocmd xbps-install -y $@
			return ;;
		homebrew)
			# FIXME: sudo and quote
			SUDO='' __separate_sudocmd "brew install" "brew upgrade" $@
			return ;;
		#android)
		#	sudocmd pm install $@
		#	return ;;
		slackpkg)
			# FIXME: broken status when use batch and default answer
			__separate_sudocmd_foreach "/usr/sbin/slackpkg -batch=on -default_answer=yes install" "/usr/sbin/slackpkg -batch=on -default_answer=yes upgrade" $@
			return ;;
		*)
			fatal "Have no suitable appropriate install command for $PMTYPE"
			;;
	esac
}

__epm_check_if_rpm_already_installed()
{
	# Not: we can make optimize if just check version?
	LANG=C $SUDO rpm -Uvh $force $nodeps $@ 2>&1 | grep -q "is already installed"
}

__handle_direct_install()
{
    case "$DISTRNAME" in
        "ALTLinux")
            local pkg url
            for pkg in $pkg_names ; do
                url=$(__epm_get_altpkg_url $pkg)
                [ -n "$url" ] || continue
                # TODO: use estrlist
                pkg_urls="$pkg_urls $url"
            done
            # FIXME: need remove
            pkg_names=""
            ;;
    esac
}

epm_install_files()
{
    [ -z "$1" ] && return

    # TODO: check read permissions
    # sudo test -r FILE
    # do not fallback to install_names if we have no permissions

    case $PMTYPE in
        apt-rpm)
            # TODO: replace with name changed function
            __epm_check_if_try_install_deb $@ && return

            # do not using low-level for install by file path (FIXME: reasons?)
            if ! is_dirpath "$@" || [ "$(get_package_type "$@")" = "rpm" ] ; then
                sudocmd rpm -Uvh $force $nodeps $@ && save_installed_packages $@ && return
                local RES=$?
                # TODO: check rpm result code and convert it to compatible format if possible
                __epm_check_if_rpm_already_installed $@ && return

            # if run with --nodeps, do not fallback on hi level
            [ -n "$nodeps" ] && return $RES
            fi

            # use install_names
            ;;

        apt-dpkg|aptitude-dpkg)
            # the new version of the conf. file is installed with a .dpkg-dist suffix
            if [ -n "$non_interactive" ] ; then
                DPKGOPTIONS="--force-confdef --force-confold"
            fi

            __epm_check_if_try_install_rpm $@ && return

            # FIXME: return false in case no install and in case install with broken deps
            sudocmd dpkg $DPKGOPTIONS -i $@
            local RES=$?
            # if run with --nodeps, do not fallback on hi level

            [ -n "$nodeps" ] && return $RES
            # fall to apt-get -f install for fix deps
            # can't use APTOPTIONS with empty install args
            epm_install_names -f

            # repeat install for get correct status
            sudocmd dpkg $DPKGOPTIONS -i $@
            return
            ;;

        yum-rpm|dnf-rpm)
            __epm_check_if_try_install_deb $@ && return

            sudocmd rpm -Uvh $force $nodeps $@ && return
            # if run with --nodeps, do not fallback on hi level

            __epm_check_if_rpm_already_installed $@ && return

            [ -n "$nodeps" ] && return
            YUMOPTIONS=--nogpgcheck
            # use install_names
            ;;

        zypper-rpm)
            __epm_check_if_try_install_deb $@ && return
            sudocmd rpm -Uvh $force $nodeps $@ && return
            local RES=$?

            __epm_check_if_rpm_already_installed $@ && return

            # if run with --nodeps, do not fallback on hi level

            [ -n "$nodeps" ] && return $RES
            ZYPPEROPTIONS=$(__use_zypper_no_gpg_checks)
            # use install_names
            ;;

        urpm-rpm)
            __epm_check_if_try_install_deb $@ && return
            sudocmd rpm -Uvh $force $nodeps $@ && return
            local RES=$?

            __epm_check_if_rpm_already_installed $@ && return

            # if run with --nodeps, do not fallback on hi level
            [ -n "$nodeps" ] && return $RES

            URPMOPTIONS=--no-verify-rpm
            # use install_names
            ;;
        pkgsrc)
            sudocmd pkg_add $@
            return ;;
        pkgng)
            local PKGTYPE="$(get_package_type $@)"
            case "$PKGTYPE" in
                tbz)
                    sudocmd pkg_add $@
                    ;;
                *)
                    sudocmd pkg add $@
                    ;;
            esac
            return ;;
        android)
            sudocmd pm install $@
            return ;;
        emerge)
            sudocmd epm_install_emerge $@
            return ;;
        pacman)
            sudocmd pacman -U --noconfirm $force $nodeps $@ && return
            local RES=$?

            [ -n "$nodeps" ] && return $RES
            sudocmd pacman -U $force $@
            return ;;
        slackpkg)
            # FIXME: check for full package name
            # FIXME: broken status when use batch and default answer
            __separate_sudocmd_foreach "/sbin/installpkg" "/sbin/upgradepkg" $@
            return ;;
    esac

    # other systems can install file package via ordinary command
    epm_install_names "$@"
}

epm_print_install_command()
{
    # print out low level command by default (wait --low-level for control it)
    #[ -z "$1" ] && return
    [ -z "$1" ] && [ -n "$pkg_names" ] && return
    case $PMTYPE in
        apt-rpm|yum-rpm|urpm-rpm|zypper-rpm|dnf-rpm)
            echo "rpm -Uvh --force $nodeps $*"
            ;;
        apt-dpkg|aptitude-dpkg)
            echo "dpkg -i $*"
            ;;
        pkgsrc)
            echo "pkg_add $*"
            ;;
        pkgng)
            echo "pkg add $*"
            ;;
        emerge)
            # need be placed in /usr/portage/packages/somewhere
            echo "emerge --usepkg $*"
            ;;
        pacman)
            echo "pacman -U --noconfirm --force $nodeps $*"
            ;;
        slackpkg)
            echo "/sbin/installpkg $*"
            ;;
        npackd)
            echo "npackdcl add --package=$*"
            ;;
        ipkg)
            echo "ipkg install $*"
            ;;
        android)
            echo "pm install $*"
            ;;
        aptcyg)
            echo "apt-cyg install $*"
            ;;
        tce)
            echo "tce-load -wi $*"
            ;;
        xbps)
            echo "xbps-install -y $*"
            ;;
        homebrew)
            # FIXME: sudo and quote
            echo "brew install $*"
            ;;

        *)
            fatal "Have no suitable appropriate install command for $PMTYPE"
            ;;
    esac
}

epm_print_install_names_command()
{
	# check for pkg_files to support print out command without pkg names in args
	#[ -z "$1" ] && [ -n "$pkg_files" ] && return
	[ -z "$1" ] && return
	case $PMTYPE in
		apt-rpm|apt-dpkg)
			echo "apt-get -y --force-yes -o APT::Install::VirtualVersion=true -o APT::Install::Virtual=true $APTOPTIONS install $*"
			return ;;
		aptitude-dpkg)
			echo "aptitude -y install $*"
			return ;;
		yum-rpm)
			echo "yum -y $YUMOPTIONS install $*"
			return ;;
		dnf-rpm)
			echo "dnf -y $YUMOPTIONS install $*"
			return ;;
		urpm-rpm)
			echo "urpmi --auto $URPMOPTIONS $*"
			return ;;
		zypper-rpm)
			echo "zypper --non-interactive $ZYPPEROPTIONS install $*"
			return ;;
		pacman)
			echo "pacman -S --noconfirm $force $*"
			return ;;
		chocolatey)
			echo "chocolatey install $*"
			return ;;
		nix)
			echo "nix-env --install $*"
			return ;;
		*)
			fatal "Have no suitable appropriate install command for $PMTYPE"
			;;
	esac
}


epm_install()
{
    if tasknumber "$pkg_names" >/dev/null ; then
        assure_distr ALTLinux "install with task number"
        assure_exists apt-repo
        sudocmd apt-repo test $(tasknumber "$pkg_names")
        return
    fi

    if [ -n "$show_command_only" ] ; then
        epm_print_install_command $pkg_files
        epm_print_install_names_command $pkg_names
        return
    fi

    if [ -n "$direct" ] && [ -z "$repack" ] ; then
        __handle_direct_install
    fi

    # if possible, it will put pkg_urls into pkg_files and reconstruct pkg_filenames
    if [ -n "$pkg_urls" ] ; then
        __handle_pkg_urls_to_install
    fi

    [ -z "$pkg_files$pkg_names" ] && info "Skip empty install list" && return 22

    # to be filter happy
    warmup_lowbase

    local names="$(echo $pkg_names | filter_out_installed_packages)"
    #local names="$(echo $pkg_names | exp_with_arch_suffix | filter_out_installed_packages)"
    local files="$(echo $pkg_files | filter_out_installed_packages)"

    # can be empty only after skip installed
    if [ -z "$files$names" ] ; then
        # TODO: assert $skip_installed
        [ -n "$verbose" ] && info "Skip empty install list"
        # FIXME: see to_remove below
        return 22
    fi

    if [ -z "$files" ] && [ -z "$direct" ] ; then
        # it is useful for first time running
        update_repo_if_needed
    fi

    # FIXME: see to_remove below
    epm_install_names $names || return

    # repack binary files
    if [ -n "$repack" ] ; then
        # FIXME: see to_remove below
        __epm_repack_rpm $files || fatal
        files="$repacked_rpms"
    fi

    epm_install_files $files
    local RETVAL=$?

    # TODO: move it to exit handler
    if [ -z "$DEBUG" ] ; then
    # TODO: reinvent
    [ -n "$to_remove_pkg_files" ] && rm -fv $to_remove_pkg_files
    [ -n "$to_remove_pkg_files" ] && rmdir -v $(dirname $to_remove_pkg_files | head -n1) 2>/dev/null
    fi

    return $RETVAL
}

# File bin/epm-Install:


epm_Install()
{
    # copied from epm_install
    local names="$(echo $pkg_names | filter_out_installed_packages)"
    local files="$(echo $pkg_files | filter_out_installed_packages)"

    [ -z "$files$names" ] && info "Install: Skip empty install list." && return 22

	# do update only if really need install something
	case $PMTYPE in
		yum-rpm)
			;;
		*)
			pkg_filenames='' epm_update || return
			;;
	esac

    epm_install_names $names || return
    epm_install_files $files

}

# File bin/epm-install-emerge:



__emerge_install_ebuild()
{
	local EBUILD="$1"
	[ -s "$EBUILD" ] || fatal ".ebuild file '$EBUILD' is missing"

	# load ebuild and get vars
	. $(pwd)/$EBUILD
	[ -n "$SRC_URI" ] || fatal "Can't load SRC_URI from $EBUILD"

	# try to detect tarballs
	local TARBALLS=
	local BASEDIR=$(dirname $EBUILD)
	for i in $SRC_URI ; do
		[ -s "$BASEDIR/$(basename $i)" ] || continue
		TARBALLS="$TARBALLS $BASEDIR/$(basename $i)"
	done

	local PORTAGENAME=epm
	local LP=/usr/local/portage/$PORTAGENAME
	docmd mkdir -p $LP/
	MAKECONF=/etc/portage/make.conf
	[ -r "$MAKECONF" ] || MAKECONF=/etc/make.conf
	if ! grep -v "^#" $MAKECONF | grep -q $LP ; then
		echo "PORTDIR_OVERLAY=\"$LP \${PORTDIR_OVERLAY}\"" >>$MAKECONF
		# Overlay name
		mkdir -p $LP/profiles/
		echo "$PORTAGENAME" > $LP/profiles/repo_name
	fi

	# copy tarballs
	local DDIR=/usr/portage/distfiles
	# FIXME: use independent dir
	[ -d /var/calculate/remote/distfiles ] && DDIR=/var/calculate/remote/distfiles
	docmd cp -f $TARBALLS $DDIR/ || return

	# copy ebuild
	docmd cp -f $EBUILD $LP/ || return
	cd $LP
	docmd ebuild $(basename $EBUILD) digest
	cd -
	# FIXME: more correcty get name
	local PKGNAME=$(echo $EBUILD | sed -e "s|-[0-9].*||g")
	docmd emerge -av $PKGNAME || return
}

__emerge_install_tbz2()
{
	local TGDIR=/usr/portage/packages/app-arch
	mkdir -p $TGDIR
	cp $i $TGDIR || return
	docmd emerge --usepkg $TGDIR/$(basename $i) || return
}

epm_install_emerge()
{
	local EBUILD=
	#local TARBALLS=
	local i

	# search ebuild in the args
	for i in $* ; do
		if echo $i | grep -q ebuild ; then
			__emerge_install_ebuild $i || return
		elif echo $i | grep -q "\.tbz2$" ; then
			__emerge_install_tbz2 $i || return
	#	else
	#		TARBALLS="$TARBALLS $i"
		fi
	done
}

# File bin/epm-kernel_update:


epm_kernel_update()
{
	warmup_bases

	info "Updating system kernel to the latest version..."

	case $DISTRNAME in
	ALTLinux)
		if ! __epm_query_package kernel-image >/dev/null ; then
			info "No installed kernel packages, skipping update"
			return
		fi
		assure_exists update-kernel update-kernel 0.9.9
		update_repo_if_needed
		sudocmd update-kernel $pkg_filenames || return
		docmd epm remove-old-kernels $pkg_filenames || fatal
		return ;;
	esac

	case $PMTYPE in
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
	esac
}

# File bin/epm-optimize:

__repack_rpm_base()
{
	assure_exists db_dump || fatal
	assure_exists db_load || fatal
	cd /var/lib/rpm || fatal
	mv Packages Packages.BACKUP || fatal
	# mask dependencies with a=
	a='' db_dump Packages.BACKUP | a='' db_load Packages || fatal
	rm Packages.BACKUP
}

epm_optimize()
{

[ -z "$pkg_filenames" ] || fatal "No arguments are allowed here"

case $PMTYPE in
	*-rpm)
		#__repack_rpm_base
		#rm -f /var/lib/rpm/__db*
		rpm --rebuilddb
		;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
esac

}

# File bin/epm-packages:


__epm_packages_sort()
{
case $PMTYPE in
	apt-rpm|yum-rpm|urpm-rpm|zypper-rpm|dnf-rpm)
		# FIXME: space with quotes problems, use point instead
		warmup_rpmbase
		docmd rpm -qa --queryformat "%{size}@%{name}-%{version}-%{release}\n" $pkg_filenames | sed -e "s|@| |g" | sort -n -k1
		;;
	apt-dpkg)
		warmup_dpkgbase
		docmd dpkg-query -W --showformat="\${Installed-Size}@\${Package}-\${Version}:\${Architecture}\n" $pkg_filenames | sed -e "s|@| |g" | sort -n -k1
		;;
	*)
		fatal "Sorted package list function is not implemented for $PMTYPE"
		;;
esac
}

__aptcyg_print_full()
{
	#showcmd apt-cyg show
	local VERSION=$(apt-cyg show "$1" | grep -m1 "^version: " | sed -e "s|^version: ||g")
	echo "$1-$VERSION"
}

__fo_pfn()
{
	grep -v "^$" | grep -- "$pkg_filenames"
}

epm_packages()
{
	local CMD
	[ -n "$sort" ] && __epm_packages_sort && return

case $PMTYPE in
	apt-rpm)
		warmup_rpmbase
		# FIXME: strong equal
		CMD="rpm -qa $pkg_filenames"
		[ -n "$short" ] && CMD="rpm -qa --queryformat %{name}\n $pkg_filenames"
		docmd $CMD
		return ;;
	*-dpkg)
		warmup_dpkgbase
		# FIXME: strong equal
		#CMD="dpkg -l $pkg_filenames"
		CMD="dpkg-query -W --showformat=\${db:Status-Abbrev}\${Package}-\${Version}:\${Architecture}\n $pkg_filenames"
		# TODO: ${Architecture}
		[ -n "$short" ] && CMD="dpkg-query -W --showformat=\${db:Status-Abbrev}\${Package}\n $pkg_filenames"
		showcmd $CMD
		$CMD | grep "^i" | sed -e "s|.* ||g" | __fo_pfn
		return ;;
	snappy)
		CMD="snappy info"
		;;
	yum-rpm|urpm-rpm|zypper-rpm|dnf-rpm)
		warmup_rpmbase
		# FIXME: strong equal
		CMD="rpm -qa $pkg_filenames"
		[ -n "$short" ] && CMD="rpm -qa --queryformat %{name}\n $pkg_filenames"
		docmd $CMD
		return ;;
	emerge)
		CMD="qlist -I -C"
		# print with colors for console output
		isatty && CMD="qlist -I"
		;;
	pkgsrc)
		CMD="pkg_info"
		showcmd $CMD
		$CMD | sed -e "s| .*||g" | __fo_pfn
		return ;;
	pkgng)
		if [ -n "$pkg_filenames" ] ; then
			CMD="pkg info -E $pkg_filenames"
		else
			CMD="pkg info"
		fi
		showcmd $CMD
		if [ -n "$short" ] ; then
		    $CMD | sed -e "s| .*||g" | sed -e "s|-[0-9].*||g" | __fo_pfn
		else
		    $CMD | sed -e "s| .*||g" | __fo_pfn
		fi
		return ;;
	pacman)
		CMD="pacman -Qs $pkg_filenames"
		showcmd $CMD
		if [ -n "$short" ] ; then
			$CMD | sed -e "s| .*||g" -e "s|.*/||g" | __fo_pfn
			return
		fi
		;;
	npackd)
		CMD="npackdcl list --status=installed"
		# TODO: use search if pkg_filenames is not empty
		;;
	conary)
		CMD="conary query"
		;;
	chocolatey)
		CMD="chocolatey list"
		;;
	slackpkg)
		CMD="ls -1 /var/log/packages/"
		if [ -n "$short" ] ; then
			# FIXME: does not work for libjpeg-v8a
			# TODO: remove last 3 elements (if arch is second from the last?)
			# FIXME this hack
			docmd ls -1 /var/log/packages/ | sed -e "s|-[0-9].*||g" | sed -e "s|libjpeg-v8a.*|libjpeg|g" | __fo_pfn
			return
		fi
		;;
	homebrew)
		docmd brew list | xargs -n1 echo
		;;
	ipkg)
		CMD="ipkg list"
		;;
	apk)
		CMD="apk info"
		;;
	tce)
		CMD="ls -1 /usr/local/tce.installed"
		;;
	guix)
		CMD="guix package -I"
		;;
	xbps)
		CMD="xbps-query -l"
		showcmd $CMD
		if [ -n "$short" ] ; then
			$CMD | sed -e "s|^ii ||g" -e "s| .*||g" -e "s|\(.*\)-.*|\1|g" | __fo_pfn
		else
			$CMD | sed -e "s|^ii ||g" -e "s| .*||g" | __fo_pfn
		fi
		return 0
		;;
	android)
		CMD="pm list packages"
		showcmd $CMD
		$CMD | sed -e "s|^package:||g" | __fo_pfn
		return
		;;
	aptcyg)
		CMD="apt-cyg list $pkg_filenames"
		if [ -z "$short" ] ; then
			showcmd $CMD
			# TODO: fix this slow way
			for i in $($CMD) ; do
				__aptcyg_print_full $i
			done
			return
		fi
		;;
	*)
		fatal "Have no suitable query command for $PMTYPE"
		;;
esac

docmd $CMD | __fo_pfn

}

# File bin/epm-policy:


epm_policy()
{

[ -n "$pkg_names" ] || fatal "Info: missing package(s) name"

warmup_bases

pkg_names=$(__epm_get_hilevel_name $pkg_names)

case $PMTYPE in
	apt-rpm)
		docmd apt-cache policy $pkg_names
		;;
	apt-dpkg)
		docmd apt-cache policy $pkg_names
		;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
esac

}

# File bin/epm-print:


query_package_field()
{
	local FORMAT="%{$1}\n"
	shift
	local INSTALLED="-p"
	# if not file, drop -p for get from rpm base
	[ -e "$1" ] || INSTALLED=""
	rpmquery $INSTALLED --queryformat "$FORMAT" "$@"
}


print_binpkgfilelist()
{
	local PKGDIR=$1
	local PKGNAME=$(basename $2)
	find "$PKGDIR" ! -name '*\.src\.rpm' -name '*\.rpm' -execdir \
		rpmquery -p --qf='%{sourcerpm}\t%{name}-%{version}-%{release}.%{arch}.rpm\n' "{}" \; \
		| grep "^$PKGNAME[[:space:]].*" \
		| cut -f2 \
		| xargs -n1 -I "{}" echo -n "$PKGDIR/{} "
}

PKGNAMEMASK="\(.*\)-\([0-9].*\)-\(.*[0-9].*\)"

print_name()
{
    echo "$@" | xargs -n1 echo | sed -e "s|$PKGNAMEMASK|\1|g"
}

print_version()
{
    echo "$1" | xargs -n1 echo | sed -e "s|$PKGNAMEMASK|\2|g"
}

print_release()
{
    echo "$1" | xargs -n1 echo | sed -e "s|$PKGNAMEMASK|\3|g"
}

print_pkgname()
{
    local i
    for i in $@ ; do
        # TODO: deb and other, arch string
        echo "$(basename "$i") " | sed -e "s|\.[a-z_0-9]*\.rpm||g" -e "s|\(.*\)_\(.*\)_[a-z_0-9]*\.deb|\1-\2|g"
    done
}

print_srcname()
{
    print_name "$(print_srcpkgname "$@")"
}

print_specname()
{
    # CHECKME: it is possible to have two or more specs in one package?
    rpm -qlp "$@" | grep "\.spec\$"
}

print_srcpkgname()
{
    query_package_field sourcerpm "$@"
}

compare_version()
{
    which rpmevrcmp 2>/dev/null >/dev/null || fatal "rpmevrcmp exists in ALT Linux only"
    rpmevrcmp "$@"
}

__epm_print()
{
    local WHAT="$1"
    shift
    local FNFLAG=
    local PKFLAG=
    [ "$1" = "from" ] && shift
    [ "$1" = "for" ] && shift
    [ "$1" = "in" ] && shift
    if [ "$1" = "filename" ] ; then
        FNFLAG="$1"
        shift
    fi

    if [ "$1" = "package" ] ; then
        PKFLAG="$1"
        shift
    fi

    case "$WHAT" in
        "")
            fatal "Use epm print help for get help."
            ;;
        "-h"|"--help"|"help")
cat <<EOF
  Examples:
    epm print name [from filename|for package] NN        print only name of package name or package file
    epm print version [from filename|for package] NN     print only version of package name or package file
    epm print release [from filename|for package] NN     print only release of package name or package file
    epm print field FF for package NN        print field of the package
    epm print pkgname from filename NN       print package name for the package file
    epm print srcname from filename NN       print source name for the package file
    epm print srcpkgname from [filename|package] NN    print source package name for the binary package file
    epm print specname from filename NN      print spec filename for the source package file
    epm print binpkgfilelist in DIR for NN   list binary package(s) filename(s) from DIR for the source package file
    epm print compare [package] version N1 N2          compare (package) versions and print -1, 0, 1
EOF
            ;;
        "name")
            [ -n "$1" ] || fatal "Arg is missed"
            if [ -n "$FNFLAG" ] ; then
                print_name "$(print_pkgname "$@")"
            elif [ -n "$PKFLAG" ] ; then
                query_package_field "name" "$@"
            else
                print_name "$@"
            fi
            ;;
        "version")
            [ -n "$1" ] || fatal "Arg is missed"
            if [ -n "$FNFLAG" ] ; then
                print_version "$(print_pkgname "$@")"
            elif [ -n "$PKFLAG" ] ; then
                query_package_field "version" "$@"
            else
                print_version "$@"
            fi
            ;;
        "release")
            [ -n "$1" ] || fatal "Arg is missed"
            if [ -n "$FNFLAG" ] ; then
                print_release "$(print_pkgname "$@")"
            elif [ -n "$PKFLAG" ] ; then
                query_package_field "release" "$@"
            else
                print_release "$@"
            fi
            ;;
        "field")
            [ -n "$1" ] || fatal "Arg is missed"
            local FIELD="$1"
            shift
            [ "$1" = "for" ] && shift
            query_package_field "$FIELD" "$@"
            ;;
        "pkgname")
            [ -n "$FNFLAG" ] || fatal "print $WHAT works only for filename(s)"
            [ -n "$1" ] || fatal "Arg is missed"
            # TODO: drop_pkg_extensions
            print_pkgname "$@"
            ;;
        "srcname")
            [ -n "$FNFLAG" ] || fatal "print $WHAT works only for filename(s)"
            [ -n "$1" ] || fatal "Arg is missed"
            print_srcname "$@"
            ;;
        "srcpkgname")
            [ -n "$FNFLAG" ] || [ -n "$PKFLAG" ] || fatal "print $WHAT works only for filename(s)"
            [ -n "$1" ] || fatal "Arg is missed"
            print_srcpkgname "$@"
            ;;
        "specname")
            [ -n "$FNFLAG" ] || [ -n "$PKFLAG" ] || fatal "print $WHAT works only for filename(s)"
            [ -n "$1" ] || fatal "Arg is missed"
            print_specname "$@"
            ;;
        "binpkgfilelist")
            # TODO: rpm only
            # TODO: replace get_binpkg_list
            local DIR="$1"
            shift
            [ "$1" = "for" ] && shift
            [ -n "$DIR" ] || fatal "DIR arg is missed"
            [ -n "$1" ] || fatal "source package filename is missed"
            print_binpkgfilelist "$DIR" "$1"
            ;;
        "compare")
            [ "$1" = "version" ] && shift
            [ -n "$1" ] || fatal "Arg is missed"
            #if [ -n "$PKFLAG" ] ; then
            #    query_package_field "name" "$@"
            #else
                 compare_version "$1" "$2"
            #fi
            ;;
        *)
            fatal "Unknown command $ epm print $WHAT. Use epm print help for get help."
            ;;
    esac
}


epm_print()
{

    [ -n "$pkg_filenames" ] || fatal "Missed args. Use epm print help for get help."
    # Note! do not quote args below (see eterbug #11863)
    # shellcheck disable=SC2046
    __epm_print $(eval echo $quoted_args)
}


# File bin/epm-programs:


epm_programs()
{
	case $DISTRNAME in
		FreeBSD|NetBSD|OpenBSD|Solaris)
			local DESKTOPDIR=/usr/local/share/applications
			;;
		*)
			local DESKTOPDIR=/usr/share/applications
			;;
	esac

	[ -d "$DESKTOPDIR" ] || fatal "There is no $DESKTOPDIR dir on the system."
	#find /usr/share/applications -type f -name "*.desktop" | while read f; do pkg_files="$f" quiet=1 short=1 epm_query_file ; done | sort -u
	showcmd "find $DESKTOPDIR -type f -print0 -name "*.desktop" | xargs -0 $0 -qf --quiet --short | sort -u"
	find $DESKTOPDIR -type f -print0 -name "*.desktop" | \
		xargs -0 $0 -qf --quiet --short | sort -u
}

# File bin/epm-provides:


epm_provides_files()
{
	local pkg_files="$*"
	[ -n "$pkg_files" ] || return

	local PKGTYPE="$(get_package_type $pkg_files)"

	case $PKGTYPE in
		rpm)
			assure_exists rpm
			docmd rpm -q --provides -p $pkg_files
			;;
		deb)
			assure_exists dpkg
			# FIXME: will we provide ourself?
			docmd dpkg -I $pkg_files | grep "^ *Provides:" | sed "s|^ *Provides:||g"
			;;
		*)
			fatal "Have no suitable command for $PMTYPE"
			;;
	esac
}


epm_provides_names()
{
	local pkg_names="$*"
	local CMD
	[ -n "$pkg_names" ] || return

case $PMTYPE in
	apt-rpm)
		# FIXME: need fix for a few names case
		# TODO: separate this function to two section
		if is_installed $pkg_names ; then
			CMD="rpm -q --provides"
		else
			EXTRA_SHOWDOCMD=' | grep "Provides:"'
			docmd apt-cache show $pkg_names | grep "Provides:"
			return
		fi
		;;
	urpm-rpm|zypper-rpm|yum-rpm|dnf-rpm)
		if is_installed $pkg_names ; then
			CMD="rpm -q --provides"
		else
			fatal "FIXME: use hi level commands"
		fi
		;;
	emerge)
		assure_exists equery
		CMD="equery files"
		;;
	pkgng)
		CMD="pkg info -b"
		;;
	apt-dpkg)
		# FIXME: need fix for a few names case
		if is_installed $pkg_names ; then
			info "Please inform the author how to get provides from dpkg"
		fi
		#	CMD="rpm -q --provides"
		#else
			EXTRA_SHOWDOCMD=' | grep "Provides:"'
			docmd apt-cache show $pkg_names | grep "Provides:"
			return
		#fi
		;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
esac

docmd $CMD $pkg_names

}

epm_provides()
{
	[ -n "$pkg_filenames" ] || fatal "Provides: missing package(s) name"

	epm_provides_files $pkg_files
	# shellcheck disable=SC2046
	epm_provides_names $(print_name $pkg_names)
}

# File bin/epm-query:


__print_with_arch_suffix()
{
	local pkg="$1"
	local suffix="$2"
	# do not change if some suffix already exists
	echo "$pkg" | grep -q "(x86-32)$" && echo "$pkg" | sed -e "s|(x86-32)$|.i686|" && return 1
	echo "$pkg" | grep "\.x86_64$" && return 1
	echo "$pkg" | grep "\.noarch$" && return 1
	echo "$pkg" | grep "\.i[56]86$" && return 1
	echo "$pkg$suffix"
}

exp_with_arch_suffix()
{
	local suffix

	[ "$($DISTRVENDOR -a)" = "x86_64" ] || { cat ; return ; }
	# TODO: it is ok for ALT rpm to remove with this suffix
	# TODO: separate install and remove?
	case $PMTYPE in
		yum-rpm|dnf-rpm)
			suffix=".x86_64"
			;;
		*)
			cat
			return
			;;
	esac

	# TODO: use estrlist or some function to do it
	local pkg
	for pkg in $(cat) ; do
		local p
		# check only packages without arch
		p="$(__print_with_arch_suffix "$pkg" .i686)" || { echo "$pkg" ; continue ; }
		# add arch suffix only if arch package already installed (otherwise we don't know package arch)
		is_installed "$p" || { echo "$pkg" ; continue ; }
		echo "$pkg.x86_64"
	done
}


_get_grep_exp()
{
	local def="^$1$"
	[ "$PMTYPE" != "emerge" ] && echo "$def" && return
	# Gentoo hack: support for short package form
	echo "$1" | grep -q "/" && echo "$def" && return
	echo "/$1$"
}

_shortquery_via_packages_list()
{
	local res=1
	local grepexp
	local firstpkg=$1
	shift

	grepexp=$(_get_grep_exp $firstpkg)

	# TODO: we miss status due grep
	# Note: double call due stderr redirect
	# Note: we use short=1 here due grep by ^name$
	# separate first line for print out command
	short=1 pkg_filenames=$firstpkg epm_packages | grep -- "$grepexp" && res=0 || res=1

	local pkg
	for pkg in "$@" ; do
		grepexp=$(_get_grep_exp $pkg)
		short=1 pkg_filenames=$pkg epm_packages 2>/dev/null | grep -- "$grepexp" || res=1
	done

	# TODO: print in query (for user): 'warning: package $pkg is not installed'
	return $res
}

_query_via_packages_list()
{
	local res=1
	local grepexp
	local firstpkg=$1
	shift

	grepexp=$(_get_grep_exp $firstpkg)

	# TODO: we miss status due grep
	# TODO: grep correctly
	# Note: double call due stderr redirect
	# Note: we use short=1 here due grep by ^name$
	# separate first line for print out command
	short=1 pkg_filenames=$firstpkg epm_packages | grep -q -- "$grepexp" && quiet=1 pkg_filenames=$firstpkg epm_packages && res=0 || res=1

	local pkg
	for pkg in "$@" ; do
		grepexp=$(_get_grep_exp $pkg)
		short=1 pkg_filenames=$pkg epm_packages 2>/dev/null | grep -q -- "$grepexp" && quiet=1 pkg_filenames=$pkg epm_packages || res=1
	done

	return $res
}

__epm_get_hilevel_nameform()
{
	[ -n "$*" ] || return

	case $PMTYPE in
		apt-rpm)
			# use # as delimeter for apt
			local pkg
			pkg=$(rpm -q --queryformat "%{NAME}#%{SERIAL}:%{VERSION}-%{RELEASE}\n" $1)
			echo $pkg | grep -q "(none)" && pkg=$(rpm -q --queryformat "%{NAME}#%{VERSION}-%{RELEASE}\n" $1)
			# HACK: can use only for multiple install packages like kernel
			echo $pkg | grep -q kernel || return 1
			echo $pkg
			return
			;;
		yum-rpm|dnf-rpm)
			# just use strict version with Epoch and Serial
			local pkg
			pkg=$(rpm -q --queryformat "%{EPOCH}:%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n" $1)
			echo $pkg | grep -q "(none)" && pkg=$(rpm -q --queryformat "%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n" $1)
			echo $pkg
			return
			;;
		*)
			return 1
			;;
	esac
}

__epm_get_hilevel_name()
{
	local i
	for i in $@ ; do
		local pkg
		# get short form in pkg
		# FIXME: where we use it? continue or pkg=$i?
		quiet=1 pkg=$(__epm_query_shortname "$i") || pkg="$i" #continue # drop not installed packages
		# if already short form, skipped
		[ "$pkg" = "$i" ] && echo "$i" && continue
		# try get long form or use short form
		__epm_get_hilevel_nameform "$i" || echo $pkg
	done
}

__epm_query_file()
{
	local CMD

	[ -z "$*" ] && return

	case $PMTYPE in
		*-rpm)
			CMD="rpm -qp"
			[ -n "$short" ] && CMD="rpm -qp --queryformat %{name}\n"
			;;
		*-dpkg)
			CMD="dpkg-deb --show --showformat=\${Package}-\${Version}\n"
			[ -n "$short" ] && CMD="dpkg-query --show --showformat=\${Package}\n"
			;;
		*)
			fatal "Do not know command for query file package"
			;;
	esac

	docmd $CMD $@
}

__epm_query_dpkg_check()
{
	local i
	for i in $@ ; do
		a='' dpkg -s $i >/dev/null 2>/dev/null || return
	done
	return 0
}

__epm_query_name()
{
	local CMD

	[ -z "$*" ] && return

	case $PMTYPE in
		*-rpm)
			CMD="rpm -q"
			;;
		*-dpkg)
			#docmd dpkg -l $@ | grep "^ii"
			#CMD="dpkg-query -W --showformat=\${Package}-\${Version}\n"
			docmd dpkg-query -W "--showformat=\${Package}-\${Version}\n" $@ || return
			__epm_query_dpkg_check $@ || return
			return
			;;
		npackd)
			docmd "npackdcl path --package=$1"
			return
			;;
		conary)
			CMD="conary query"
			;;
		homebrew)
			docmd brew info "$1" >/dev/null 2>/dev/null && echo "$1" && return
			return 1
			;;
		pacman)
			docmd pacman -Q $@
			return
			;;
		# TODO: need to print name if exists
		#pkgng)
		#	CMD="pkg info -e"
		#	;;
		# Note: slackpkg info pkgname
		*)
			# default slow workaround
			_query_via_packages_list $@
			return
			;;
	esac

	docmd $CMD $@
}

__epm_query_shortname()
{
	local CMD

	[ -z "$*" ] && return

	case $PMTYPE in
		*-rpm)
			CMD="rpm -q --queryformat %{name}\n"
			;;
		*-dpkg)
			#CMD="dpkg-query -W --showformat=\${Package}\n"
			docmd dpkg-query -W "--showformat=\${Package}\n" $@ || return
			__epm_query_dpkg_check $@ || return
			return
			;;
		npackd)
			docmd "npackdcl path --package=$1"
			return
			;;
		conary)
			CMD="conary query"
			;;
		homebrew)
			docmd brew info "$1" >/dev/null 2>/dev/null && echo "$1" && return
			return 1
			;;
		# TODO: check status
		#pacman)
		#	docmd pacman -Q $@ | sed -e "s| .*||g"
		#	return
		#	;;

		# TODO: need to print name if exists
		#pkgng)
		#	CMD="pkg info -e"
		#	;;
		# Note: slackpkg info pkgname
		*)
			# default slow workaround
			_shortquery_via_packages_list $@
			return
			;;
	esac

	docmd $CMD $@
}

is_installed()
{
	__epm_query_shortname "$@" >/dev/null 2>/dev/null
	# broken way to recursive call here (overhead!)
	#epm installed $@ >/dev/null 2>/dev/null
}

separate_installed()
{
	pkg_installed=
	pkg_noninstalled=
	for i in "$@" ; do
		is_installed $i && pkg_installed="$pkg_installed $i" || pkg_noninstalled="$pkg_noninstalled $i"
	done
}

epm_query()
{
	[ -n "$pkg_filenames" ] || fatal "Query: missing package(s) name"

	__epm_query_file $pkg_files || return

	if [ -n "$short" ] ; then
		# shellcheck disable=SC2046
		__epm_query_shortname $(print_name $pkg_names) || return
	else
		# shellcheck disable=SC2046
		__epm_query_name $(print_name $pkg_names) || return
	fi
}

# File bin/epm-query_file:


__abs_filename()
{
	if echo "$1" | grep -q "/" ; then
		echo "$1"
		return
	fi
	if [ -e "$1" ] ; then
		echo "$(pwd)/$1"
		return
	fi
	echo "$1"
}

__do_query_real_file()
{
	local TOFILE
	
	# get canonical path
	if [ -e "$1" ] ; then
		TOFILE="$(__abs_filename "$1")"
	else
		TOFILE=$(which "$1" 2>/dev/null || echo "$1")
		if [ "$TOFILE" != "$1" ] ; then
			info "Note: $1 is placed as $TOFILE"
		fi
	fi
	
	# get value of symbolic link
	if [ -L "$TOFILE" ] ; then
		local LINKTO
		__do_query "$TOFILE"
		LINKTO=$(readlink -f "$TOFILE")
		info "Note: $TOFILE is link to $LINKTO"
		__do_query_real_file "$LINKTO"
		return
	fi

	FULLFILEPATH="$TOFILE"
}

dpkg_print_name_version()
{
	local ver i
	for i in "$@" ; do
		ver=$(dpkg -s $i 2>/dev/null | grep "Version:" | sed -e "s|Version: ||g")
		if [ -z "$ver" ] ; then
			echo "$i"
		else
			echo "$i-$ver"
		fi
	done
}


__do_query()
{
    local CMD
    case $PMTYPE in
        apt-rpm)
            CMD="rpm -qf"
            ;;
        *-dpkg)
            showcmd dpkg -S "$1"
            dpkg_print_name_version "$(dpkg -S $1 | grep -v "^diversion by" | sed -e "s|:.*||")"
            return ;;
        yum-rpm|dnf-rpm|urpm-rpm)
            CMD="rpm -qf"
            ;;
        zypper-rpm)
            CMD="rpm -qf"
            ;;
        emerge)
            assure_exists equery
            CMD="equery belongs"
            ;;
        pacman)
            CMD="pacman -Qo"
            ;;
        pkgng)
            CMD="pkg which"
            ;;
        conary)
            CMD="conary query --path"
            ;;
        slackpkg)
            # note: need remove leading slash for grep
            docmd grep -R -- "$(echo $@ | sed -e 's|^/\+||g')" /var/log/packages | sed -e "s|/var/log/packages/||g"
            return
            ;;
        ipkg)
            CMD="ipkg files"
            ;;
        xbps)
            # FIXME: maybe it is search file?
            CMD="xbps-query -o"
            ;;
        aptcyg)
            #CMD="apt-cyg packageof"
            # is not implemented locally
            return 1
            ;;
        *)
            fatal "Have no suitable query command for $PMTYPE"
            ;;
    esac

    docmd $CMD $@
}


__do_short_query()
{
    local CMD
    case $PMTYPE in
        *-rpm)
            CMD="rpm -qf --queryformat %{NAME}\n"
            ;;
        NOapt-dpkg)
            showcmd dpkg -S "$1"
            dpkg_print_name_version "$(dpkg -S $1 | sed -e "s|:.*||" | grep -v "^diversion by")"
            return ;;
        NOemerge)
            assure_exists equery
            CMD="equery belongs"
            ;;
        NOpacman)
            CMD="pacman -Qo"
            ;;
        NOslackpkg)
            # note: need remove leading slash for grep
            docmd grep -R "$(echo $@ | sed -e 's|^/\+||g')" /var/log/packages | sed -e "s|/var/log/packages/||g"
            return
            ;;
        *)
            fatal "Have no suitable query command for $PMTYPE"
            ;;
    esac

    docmd $CMD $@
}


epm_query_file()
{
    # И где это используется?
    # in short mode print handle only real names and do short output
    # TODO: move to separate command?
    # FIXME: it is possible use query
    if [ -n "$short" ] ; then
        [ -n "$pkg_files$pkg_dirs" ] || fatal "Run query without file names (needed path to files)"
        __do_short_query $pkg_files $pkg_dirs
         return
    fi

    # file can exists or not
    [ -n "$pkg_filenames" ] || fatal "Run query without file names"


    #load_helper epm-search_file

    for pkg in $pkg_filenames ; do
        __do_query_real_file "$pkg"
        __do_query "$FULLFILEPATH" || info "Try epm sf for search file in all packages in repository" #|| pkg_filenames="$FULLFILEPATH" epm_search_file
    done

}

# File bin/epm-query_package:


__epm_query_package()
{
	pkg_filenames="$*" quoted_args="$*" quiet=1 epm_query_package
}

epm_query_package()
{
	[ -n "$pkg_filenames" ] || fatal "Please, use search with some argument or run epmqa for get all packages."
	# FIXME: do it better
	local MGS
	MGS=$(eval __epm_search_make_grep $quoted_args)
	EXTRA_SHOWDOCMD=$MGS
	# Note: get all packages list and do grep
	eval "pkg_filenames='' epm_packages \"$(eval get_firstarg $quoted_args)\" $MGS"
}

# File bin/epm-reinstall:


epm_reinstall_names()
{
	[ -n "$1" ] || return

	case $PMTYPE in
		apt-rpm|apt-dpkg)
			local APTOPTIONS="$(subst_option non_interactive -y)"
			sudocmd apt-get --reinstall $APTOPTIONS install $@
			return ;;
		aptitude-dpkg)
			sudocmd aptitude reinstall $@
			return ;;
		yum-rpm)
			sudocmd yum reinstall $@
			return ;;
		dnf-rpm)
			sudocmd dnf reinstall $@
			return ;;
		pkgng)
			sudocmd pkg install -f $@
			return ;;
		slackpkg)
			sudocmd_foreach "/usr/sbin/slackpkg reinstall" $@
			return ;;
	esac

	# fallback to generic install
	epm_install_names $@
}

epm_reinstall_files()
{
    [ -z "$1" ] && return

    case $PMTYPE in
        apt-rpm)
            sudocmd rpm -Uvh --force $@ && return
            sudocmd apt-get --reinstall install $@
            return ;;
        apt-dpkg|aptitude-dpkg)
            sudocmd dpkg -i $@
            return ;;
        slackpkg)
            sudocmd_foreach "/sbin/installpkg" $@
            return ;;
    esac

    # other systems can install file package via ordinary command
    epm_reinstall_names $@
}


epm_reinstall()
{
    [ -n "$pkg_filenames" ] || fatal "Reinstall: missing package(s) name."

    warmup_lowbase

    # get package name for hi level package management command (with version if supported and if possible)
    pkg_names=$(__epm_get_hilevel_name $pkg_names)

    warmup_hibase

    epm_reinstall_names $pkg_names
    epm_reinstall_files $pkg_files
}


# File bin/epm-release_upgrade:


confirm_info()
{
	info "$*"
	if [ -z "$non_interactive" ] ; then
		confirm "Are you sure? [y/N]" || fatal "Exiting"
	fi

}

__replace_text_in_alt_repo()
{
	local i
	for i in /etc/apt/sources.list /etc/apt/sources.list.d/*.list ; do
		[ -s "$i" ] || continue
		regexp_subst "$1" "$i"
	done
}

__wcount()
{
	echo "$*" | wc -w
}

__detect_alt_release_by_repo()
{
	local BRD=$(cat /etc/apt/sources.list /etc/apt/sources.list.d/*.list \
		| grep -v "^#" \
		| grep "[tp][5-9]/branch/" \
		| sed -e "s|.*\([tp][5-9]\)/branch.*|\1|g" \
		| sort -u )
	if [ "$(__wcount $BRD)" = "1" ] ; then
		echo "$BRD"
		return
	fi

	local BRD=$(cat /etc/apt/sources.list /etc/apt/sources.list.d/*.list \
		| grep -v "^#" \
		| grep "Sisyphus/" \
		| sed -e "s|.*\(Sisyphus\).*|\1|g" \
		| sort -u )
	if [ "$(__wcount $BRD)" = "1" ] ; then
		echo "$BRD"
		return
	fi

	return 1
}

__replace_alt_version_in_repo()
{
	local i
	assure_exists apt-repo
	#echo "Upgrading $DISTRNAME from $1 to $2 ..."
	docmd apt-repo list | sed -e "s|\($1\)|{\1}->{$2}|g" | grep -E --color -- "$1"
	# ask and replace only we will have changes
	if a='' apt-repo list | grep -E -q -- "$1" ; then
		__replace_text_in_alt_repo "/^ *#/! s!$1!$2!g"
	fi
	docmd apt-repo list
}

__alt_repofix()
{
	showcmd epm repofix
	quiet=1 pkg_filenames='' epm_repofix >/dev/null
	__replace_text_in_alt_repo "/^ *#/! s!\[[tpc][6-9]\]![updates]!g"
}

__get_conflict_release_pkg()
{
	epmqf --quiet --short /etc/fedora-release | head -n1
}

get_fix_release_pkg()
{
	local TOINSTALL=''

	local FORCE=''
	if [ "$1" == "--force" ] ; then
		FORCE="$1"
		shift
	fi

	local TO="$1"

	if [ "$TO" = "Sisyphus" ] ; then
		TO="sisyphus"
		echo "apt-conf-$TO"
	else
		echo "apt-conf-branch"
	fi

	if [ "$FORCE" == "--force" ] ; then
		# assure we have set needed release
		TOINSTALL="altlinux-release-$TO"
	else
		# just assure we have /etc/altlinux-release and switched from sisyphus
		if [ ! -s /etc/altlinux-release ] || epmqf /etc/altlinux-release | grep -q sisyphus ; then
			TOINSTALL="altlinux-release-$TO"
		fi
	fi

	# workaround against obsoleted altlinux-release-sisyphus package from 2008 year
	[ "$TOINSTALL" = "altlinux-release-sisyphus" ] && TOINSTALL="branding-alt-sisyphus-release"

	if [ -n "$TOINSTALL" ] ; then
		echo "$TOINSTALL"

		# workaround against
		#    file /etc/fedora-release from install of altlinux-release-p8-20160414-alt1 conflicts with file from package branding-simply-linux-release-8.2.0-alt1
		# problem
		if __get_conflict_release_pkg | grep -q -v "^altlinux-release" && [ "$TOINSTALL" != "$(__get_conflict_release_pkg)" ] ; then
			echo $(__get_conflict_release_pkg)-
		fi
	fi
}

__update_to_the_distro()
{
	local TO="$1"
	__alt_repofix
	case "$TO" in
		p7)
			docmd epm update || fatal
			docmd epm install rpm apt "$(get_fix_release_pkg --force "$TO")" || fatal "Check an error and run epm release-upgrade again"
			__alt_repofix
			__replace_text_in_alt_repo "/^ *#/! s!\[updates\]![$TO]!g"
			docmd epm update || fatal
			docmd epm upgrade || fatal "Check an error and run epm release-upgrade again"
			;;
		p8)
			docmd epm update || fatal
			docmd epm install rpm apt "$(get_fix_release_pkg --force "$TO")" || fatal "Check an error and run epm release-upgrade again"
			__alt_repofix
			__replace_text_in_alt_repo "/^ *#/! s!\[updates\]![$TO]!g"
			docmd epm update || fatal
			# sure we have systemd if systemd is running
			if is_installed systemd && is_active_systemd systemd ; then
				docmd epm install systemd || fatal
			fi
			docmd epm upgrade || fatal "Check an error and run epm release-upgrade again"
			;;
		Sisyphus)
			docmd epm update || fatal
			docmd epm install librpm7 librpm rpm apt "$(get_fix_release_pkg --force "$TO")" || fatal "Check an error and run again"
			#docmd apt-get upgrade || fatal "Check an error and run epm release-upgrade or just epm upgrade again"
			docmd epm upgrade || fatal "Check an error and run epm release-upgrade or just epm upgrade again"
			;;
		*)
	esac
}


__update_alt_to_next_distro()
{
	local TO="$2"
	local FROM="$1"
	[ -n "$TO" ] || TO="$FROM"
	info
 	case "$*" in
		"p6"|"p6 p7"|"t6 p7"|"c6 c7")
			TO="p7"
			confirm_info "Upgrade $DISTRNAME from $FROM to $TO ..."
			docmd epm install rpm apt "$(get_fix_release_pkg "$FROM")" || fatal
			__replace_alt_version_in_repo "$FROM/branch/" "$TO/branch/"
			__update_to_the_distro "$TO"
			docmd epm update-kernel
			info "Done."
			info "Run epm release-upgrade again for update to p8"
			;;
		"p7"|"p7 p8"|"t7 p8"|"c7 c8"|"p8 p8")
			TO="p8"
			confirm_info "Upgrade $DISTRNAME from $FROM to $TO ..."
			docmd epm install rpm apt "$(get_fix_release_pkg "$FROM")" || fatal
			__replace_alt_version_in_repo $FROM/branch/ $TO/branch/
			__update_to_the_distro $TO
			docmd epm update-kernel || fatal
			info "Done."
			;;
		"Sisyphus p8")
			TO="p8"
			confirm_info "Downgrade $DISTRNAME from $FROM to $TO ..."
			docmd epm install "$(get_fix_release_pkg "$FROM")" || fatal
			__replace_alt_version_in_repo "$FROM/" "$TO/branch/"
			__replace_text_in_alt_repo "/^ *#/! s!\[alt\]![$TO]!g"
			__update_to_the_distro $TO
			docmd epm downgrade || fatal
			info "Done."
			;;
		"p8 Sisyphus"|"Sisyphus Sisyphus")
			TO="Sisyphus"
			confirm_info "Upgrade $DISTRNAME from $FROM to $TO ..."
			docmd epm install rpm apt "$(get_fix_release_pkg "$FROM")" || fatal
			docmd epm upgrade || fatal
			__replace_alt_version_in_repo "$FROM/branch/" "$TO/"
			__alt_repofix
			__replace_text_in_alt_repo "/^ *#/! s!\[updates\]![alt]!g"
			__update_to_the_distro $TO
			docmd epm update-kernel || fatal
			info "Done."
			;;
		*)
			if [ "$FROM" = "$TO" ] ; then
				info "It seems your system is already updated to newest $DISTRNAME $TO"
			else
				warning "Have no idea how to update from $DISTRNAME $FROM to $DISTRNAME $TO."
			fi
			info "Try run f.i. # epm release-upgrade p8 or # epm release-upgrade Sisyphus"
			info "Also possible you need install altlinux-release-p? package for correct distro version detecting"
			return 1
	esac
}

epm_release_upgrade()
{
	assure_root
	info "Starting upgrade whole system to the next release"
	info "Check also http://wiki.etersoft.ru/Admin/UpdateLinux"

	# TODO: it is possible eatmydata does not do his work
	export EPMNOEATMYDATA=1

	case $DISTRNAME in
	ALTLinux)
		docmd epm update || fatal

		# try to detect current release by repo
		if [ "$DISTRVERSION" = "Sisyphus" ] || [ -z "$DISTRVERSION" ] ; then
			local dv
			dv="$(__detect_alt_release_by_repo)"
			if [ -n "$dv" ] && [ "$dv" != "$DISTRVERSION" ] ; then
				DISTRVERSION="$dv"
				info "Detected running $DISTRNAME $DISTRVERSION (according to using repos)"
			fi
		fi

		__alt_repofix

		# check forced target
		if [ -n "$pkg_filenames" ] ; then
			[ "$(__wcount $pkg_filenames)" = "1" ] || fatal "Too many args: $pkg_filenames"
		fi

		# TODO: ask before upgrade
		__update_alt_to_next_distro $DISTRVERSION $pkg_filenames
		return
		;;
	*)
		;;
	esac

	case $PMTYPE in
	apt-rpm)
		#docmd epm update
		info "Have no idea how to upgrade $DISTRNAME"
		;;
	*-dpkg)
		assure_exists do-release-upgrade update-manager-core
		sudocmd do-release-upgrade
		;;
	yum-rpm)
		docmd epm install rpm yum
		sudocmd yum clean all
		# TODO
		showcmd rpm -Uvh http://mirror.yandex.ru/fedora/linux/releases/16/Fedora/x86_64/os/Packages/fedora-release-16-1.noarch.rpm
		docmd epm Upgrade
		;;
	dnf-rpm)
		info "Check https://fedoraproject.org/wiki/DNF_system_upgrade for an additional info"
		docmd epm install dnf
		sudocmd dnf --refresh upgrade
		sudocmd dnf clean all
		assure_exists dnf-plugin-system-upgrade
		sudocmd dnf upgrade --refresh
		local RELEASEVER="$pkg_filenames"
		[ -n "$RELEASEVER" ] || RELEASEVER=$(($DISTRVERSION + 1))
		#[ -n "$RELEASEVER" ] || fatal "Run me with new version"
		confirm_info "Upgrade to $DISTRNAME/$RELEASEVER"
		sudocmd dnf system-upgrade download --refresh --releasever=$RELEASEVER
		# TODO: from docs:
		# dnf system-upgrade reboot
		# FIXME: download all packages again
		sudocmd dnf distro-sync --releasever=$RELEASEVER
		info "Run epm autoorphans to remove orphaned packages"
		;;
	urpm-rpm)
		sudocmd urpmi.removemedia -av
		# TODO
		showcmd urpmi.addmedia --distrib http://mirror.yandex.ru/mandriva/devel/2010.2/i586/
		sudocmd urpmi --auto-update --replacefiles
		;;
	zypper-rpm)
		docmd epm repolist
		# TODO
		# sudocmd zypper rr <номер_репозитория>
		showcmd rr N
		showcmd epm ar http://mirror.yandex.ru/opensuse/distribution/11.1/repo/oss 11.1oss
		showcmd zypper ref
		docmd epm update
		docmd epm install rpm zypper
		docmd epm upgrade
		;;
	pacman)
		epm Upgrade
		;;
	conary)
		epm Upgrade
		;;
	emerge)
		epm Upgrade
		;;
	guix)
		sudocmd guix pull --verbose
		;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
	esac

}

# File bin/epm-remove:


epm_remove_low()
{
	[ -z "$1" ] && return

	warmup_lowbase

	case $PMTYPE in
		apt-rpm|yum-rpm|zypper-rpm|urpm-rpm|dnf-rpm)
			sudocmd rpm -ev $nodeps $@
			return ;;
		apt-dpkg|aptitude-dpkg)
			# shellcheck disable=SC2046
			sudocmd dpkg -P $(subst_option nodeps --force-all) $(print_name "$@")
			return ;;
		pkgsrc)
			sudocmd pkg_delete -r $@
			return ;;
		pkgng)
			sudocmd pkg delete -R $@
			return ;;
		emerge)
			sudocmd emerge --unmerge $@
			return ;;
		pacman)
			sudocmd pacman -R $@
			return ;;
		slackpkg)
			sudocmd /sbin/removepkg $@
			return ;;
	esac
	return 1
}

epm_remove_names()
{
	[ -z "$1" ] && return

	warmup_bases

	case $PMTYPE in
		apt-dpkg)
			sudocmd apt-get remove --purge $APTOPTIONS $@
			return ;;
		aptitude-dpkg)
			sudocmd aptitude purge $@
			return ;;
		apt-rpm)
			sudocmd apt-get remove $APTOPTIONS $@
			return ;;
		deepsolver-rpm)
			sudocmd ds-remove $@
			return ;;
		urpm-rpm)
			sudocmd urpme $@
			return ;;
		pkgsrc) # without dependencies
			sudocmd pkg_delete $@
			return ;;
		pkgng)
			sudocmd pkg delete -R $@
			return ;;
		emerge)
			#sudocmd emerge --unmerge $@
			sudocmd emerge -aC $@
			return ;;
		pacman)
			sudocmd pacman -Rc $@
			return ;;
		yum-rpm)
			sudocmd yum remove $@
			return ;;
		dnf-rpm)
			sudocmd dnf remove $@
			return ;;
		snappy)
			sudocmd snappy uninstall $@
			return ;;
		zypper-rpm)
			sudocmd zypper remove --clean-deps $@
			return ;;
		mpkg)
			sudocmd mpkg remove $@
			return ;;
		conary)
			sudocmd conary erase $@
			return ;;
		npackd)
			sudocmd npackdcl remove --package=$1
			return ;;
		nix)
			sudocmd nix-env --uninstall $@
			return ;;
		apk)
			sudocmd apk del $@
			return ;;
		guix)
			sudocmd guix package -r $@
			return ;;
		android)
			sudocmd pm uninstall $@
			return ;;
		chocolatey)
			sudocmd chocolatey uninstall $@
			return ;;
		slackpkg)
			sudocmd /usr/sbin/slackpkg remove $@
			return ;;
		homebrew)
			docmd brew remove $@
			return ;;
		aptcyg)
			sudocmd apt-cyg remove $@
			return ;;
		xbps)
			sudocmd xbps remove -R $@
			return ;;
		ipkg)
			# shellcheck disable=SC2046
			sudocmd ipkg $(subst_option force -force-depends) remove $@
			return ;;
		*)
			fatal "Have no suitable command for $PMTYPE"
			;;
	esac
}

epm_remove_nonint()
{
	warmup_bases

	case $PMTYPE in
		apt-dpkg)
			sudocmd apt-get -y --force-yes remove --purge $@
			return ;;
		aptitude-dpkg)
			sudocmd aptitude -y purge $@
			return ;;
		apt-rpm)
			sudocmd apt-get -y --force-yes remove $@
			return ;;
		urpm-rpm)
			sudocmd urpme --auto $@
			return ;;
		pacman)
			sudocmd pacman -Rc --noconfirm $@
			return ;;
		yum-rpm)
			sudocmd yum -y remove $@
			return ;;
		dnf-rpm)
			sudocmd dnf remove --assumeyes $@
			return ;;
		zypper-rpm)
			sudocmd zypper --non-interactive remove --clean-deps $@
			return ;;
		slackpkg)
			sudocmd /usr/sbin/slackpkg -batch=on -default_answer=yes remove $@
			return ;;
		pkgng)
			sudocmd pkg delete -y -R $@
			return ;;
		ipkg)
			sudocmd ipkg -force-defaults remove $@
			return ;;
		xbps)
			sudocmd xbps remove -y $@
			return ;;
	esac
	return 5
}

epm_print_remove_command()
{
	case $PMTYPE in
		apt-rpm|yum-rpm|zypper-rpm|urpm-rpm|dnf-rpm)
			echo "rpm -ev $nodeps $*"
			;;
		apt-dpkg|aptitude-dpkg)
			echo "dpkg -P $*"
			;;
		pkgsrc)
			echo "pkg_delete -r $*"
			;;
		pkgng)
			echo "pkg delete -R $*"
			;;
		pacman)
			echo "pacman -R $*"
			;;
		emerge)
			echo "emerge --unmerge $*"
			;;
		slackpkg)
			echo "/sbin/removepkg $*"
			;;
		ipkg)
			echo "ipkg remove $*"
			;;
		aptcyg)
			echo "apt-cyg remove $*"
			;;
		xbps)
			echo "xbps remove -y $*"
			;;
		*)
			fatal "Have no suitable appropriate remove command for $PMTYPE"
			;;
	esac
}


epm_remove()
{
	if [ -n "$show_command_only" ] ; then
		epm_print_remove_command $pkg_filenames
		return
	fi

	local tn=$(tasknumber "$pkg_names")
	if [ -n "$tn" ] ; then
		assure_distr ALTLinux "remove with task number"
		assure_exists apt-repo
		pkg_names=$(showcmd apt-repo list $tn)
		#docmd epm remove $dryrun
		return
	fi

	# TODO: fix pkg_names override
	# get full package name(s) from the package file(s)
	[ -n "$pkg_files" ] && pkg_names="$pkg_names $(epm query $pkg_files)"
	pkg_files=''

	[ -n "$pkg_names" ] || fatal "Remove: missing package(s) name."
	# remove according current arch (if x86_64) by default
	pkg_names="$(echo $pkg_names | exp_with_arch_suffix)"

	if [ -n "$dryrun" ] ; then
		info "Packages for removing:"
		echo "$pkg_names"
		case $PMTYPE in
			apt-rpm)
				nodeps="--test"
				APTOPTIONS="--simulate"
				;;
			*)
				return
				;;
		esac
	fi

	epm_remove_low $pkg_names && return
	local STATUS=$?
	# TODO: check if we need continue with hi level

	if [ -n "$direct" ] ; then
		return $STATUS
	fi

	# get package name for hi level package management command (with version if supported and if possible)
	pkg_names=$(__epm_get_hilevel_name $pkg_names)

	if [ -n "$non_interactive" ] ; then
		epm_remove_nonint $pkg_names
		local RET=$?
		# if not separate command, use usual command
		[ "$RET" = "5" ] || return $RET
	fi

	epm_remove_names $pkg_names
}


# File bin/epm-remove_old_kernels:


epm_remove_old_kernels()
{

	warmup_bases

	case $DISTRNAME in
	ALTLinux)
		if ! __epm_query_package kernel-image >/dev/null ; then
			info "No installed kernel packages, skipping cleaning"
			return
		fi
		assure_exists update-kernel update-kernel 0.9.9
		sudocmd remove-old-kernels $pkg_filenames
		return ;;
	Ubuntu)
		if ! __epm_query_package linux-image >/dev/null ; then
			info "No installed kernel packages, skipping cleaning"
			return
		fi
		info "Note: it is enough to use eepm autoremove for old kernel removing..."
		info "Check also http://ubuntuhandbook.org/index.php/2016/05/remove-old-kernels-ubuntu-16-04/"
		# http://www.opennet.ru/tips/2980_ubuntu_apt_clean_kernel_packet.shtml
		case $DISTRVERSION in
		10.04|12.04|14.04|15.04|15.10)
			assure_exists purge-old-kernels bikeshed
			;;
		*)
			# since Ubuntu 16.04
			assure_exists purge-old-kernels byobu
			;;
		esac
		sudocmd purge-old-kernels $pkg_filenames
		return ;;
	Gentoo)
		sudocmd emerge -P gentoo-sources
		return ;;
	VoidLinux)
		sudocmd vkpurge rm all
		return ;;
	esac

	case $PMTYPE in
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
	esac
}

# File bin/epm-removerepo:


epm_removerepo()
{
local repo="$(eval echo $quoted_args)"

case $DISTRNAME in
	ALTLinux)
		case "$repo" in
			autoimports)
				info "remove autoimports repo"
				[ -n "$DISTRVERSION" ] || fatal "Empty DISTRVERSION"
				repo="$repo.$(echo "$DISTRVERSION" | tr "[:upper:]" "[:lower:]")"
				;;
			archive)
				info "remove archive repos"
				assure_exists apt-repo
				epm repolist | grep "archive/" | while read repo ; do
					sudocmd apt-repo rm "$repo"
				done
				return 0
				;;
			tasks)
				info "remove task repos"
				assure_exists apt-repo
				epm repolist | grep "/repo/" | while read repo ; do
					sudocmd apt-repo rm "$repo"
				done
				return 0
				;;
			*)
				if tasknumber "$repo" >/dev/null ; then
					repo="$(epm repolist | grep "repo/$(tasknumber "$repo")" | line)"
					# "
				fi
				;;
		esac

		[ -n "$repo" ] || fatal "No such repo or task. Use epm remove repo [autoimports|archive|TASK]"
		assure_exists apt-repo
		sudocmd apt-repo rm "$repo"
		return
		;;
esac;

case $PMTYPE in
	apt-dpkg)
		assure_exists apt-add-repository software-properties-common
		apt-add-repository --remove "$repo"
		info "Check file /etc/apt/sources.list if needed"
		;;
	aptitude-dpkg)
		info "You need remove repo from /etc/apt/sources.list"
		;;
	yum-rpm)
		assure_exists yum-utils
		sudocmd yum-config-manager --disable "$repo"
		;;
	urpm-rpm)
		sudocmd urpmi.removemedia "$repo"
		;;
	zypper-rpm)
		sudocmd zypper removerepo "$repo"
		;;
	emerge)
		sudocmd layman "-d$repo"
		;;
	pacman)
		info "You need remove repo from /etc/pacman.conf"
		;;
	npackd)
		sudocmd npackdcl remove-repo --url="$repo"
		;;
	slackpkg)
		info "You need remove repo from /etc/slackpkg/mirrors"
		;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
esac

}

# File bin/epm-repack:


__epm_split_by_pkg_type()
{
	local type="$1"
	shift

	split_replaced_pkgs=''

	for pkg in "$@" ; do
		[ "$(get_package_type "$pkg")" = "$type" ] || return 1
		[ -e "$pkg" ] || fatal "Can't read $pkg"
		split_replaced_pkgs="$split_target_pkgs $(realpath "$pkg")"
	done

	[ -n "$split_replaced_pkgs" ]
}

__epm_repack_deb_to_rpm()
{
	local pkg

	assure_exists alien
	assure_exists dpkg
	# TODO: Для установки требует: /usr/share/debconf/confmodule но пакет не может быть установлен
	# assure_exists debconf

	repacked_rpms=''

	local TDIR=$(mktemp -d)
	cd $TDIR || fatal
	for pkg in "$@" ; do
		# TODO: fakeroot for non ALT?
		showcmd_store_output alien -r -k $scripts "$pkg" || fatal
		local RPMCONVERTED=$(grep "rpm generated" $RC_STDOUT | sed -e "s| generated||g")
		repacked_rpms="$repacked_rpms $(realpath $RPMCONVERTED)"
		to_remove_pkg_files="$to_remove_pkg_files $(realpath $RPMCONVERTED)"
		clean_store_output
	done

	cd - >/dev/null
	return 0
}

__epm_check_if_try_install_deb()
{
	__epm_split_by_pkg_type deb "$@" || return 1
	__epm_repack_deb_to_rpm "$@"

	# TODO: move to install
	docmd epm install $force $nodeps $repacked_rpms

	return 0
}

__epm_repack_rpm_to_deb()
{
	local pkg

	assure_exists alien
	assure_exists fakeroot
	assure_exists rpm

	repacked_debs=''

	local TDIR=$(mktemp -d)
	cd $TDIR || fatal

	for pkg in $rpmpkgs ; do
		showcmd_store_output fakeroot alien -d -k $scripts "$pkg"
		local DEBCONVERTED=$(grep "deb generated" $RC_STDOUT | sed -e "s| generated||g")
		repacked_debs="$repacked_rpms $(realpath $DEBCONVERTED)"
		to_remove_pkg_files="$to_remove_pkg_files $(realpath $DEBCONVERTED)"
		clean_store_output
	done

	cd - >/dev/null
	return 0
}


__epm_check_if_try_install_rpm()
{
	__epm_split_by_pkg_type rpm "$@" || return 1
	__epm_repack_rpm_to_deb "$@"

	# TODO: move to install
	docmd epm install $force $nodeps $repacked_debs

	return 0
}


__fix_spec()
{
    local buildroot="$1"
    local spec="$2"
    local i
    for i in $(grep '^"/' $spec | sed -e 's|^"\(.*\)"$|\1|') ; do
        #' hack for highlight
        # add %dir to dir in list
        if [ -d "$buildroot$i" ] ; then
            subst 's|^\("'$i'"\)$|%dir \1|' $spec
        fi
    done
    subst "s|^Release: |Release: alt1.repacked.with.epm.|" $spec
    subst "s|^\((Converted from a rpm package.*\)|(Repacked from binary rpm with epm $EPMVERSION)\n\1|" $spec
    #" hack for highlight
}

__apply_fix_code()
{
    local repackcode="/etc/eepm/repack.d/$1.sh"
    [ -x "$repackcode" ] || return
    shift
    docmd $repackcode "$1" "$2" || warning "There was errors with $repackcode script"
}

__create_rpmmacros()
{
    cat <<EOF >$HOME/.rpmmacros
%_topdir	$HOME/RPM
%_tmppath	$TMPDIR

%packager	EPM <support@etersoft.ru>
%_gpg_name	support@etersoft.ru
EOF
}

__epm_repack_rpm()
{
    assure_distr ALTLinux "install --repack"

    assure_exists fakeroot || fatal
    assure_exists alien || fatal
    assure_exists rpmbuild rpm-build || fatal

    local pkg
    export HOME=$(mktemp -d)
    local tmpbuilddir=$HOME/repack
    mkdir $tmpbuilddir
    __create_rpmmacros

    local abspkg
    repacked_rpms=''
    for pkg in $* ; do
        abspkg=$(realpath $pkg)
        info "Repacking $abspkg to local rpm format ..."
        cd $tmpbuilddir || fatal
        docmd fakeroot alien --generate --to-rpm $verbose $scripts $abspkg || fatal

        local subdir="$(echo *)"
        [ -d "$subdir" ] || fatal "can't find subdir"

        # detect spec and move to prev dir
        local spec="$(echo $tmpbuilddir/$subdir/*.spec)"
        [ -s "$spec" ] || fatal "can't find spec"
        mv $spec $tmpbuilddir || fatal
        spec="$tmpbuilddir/$(basename "$spec")"
        __fix_spec $tmpbuilddir/$subdir $spec
        local pkgname="$(grep "^Name: " $spec | sed -e "s|Name: ||g" | head -n1)"
        __apply_fix_code $pkgname $tmpbuilddir/$subdir $spec
        showcmd fakeroot rpmbuild --buildroot $tmpbuilddir/$subdir --define='_allow_root_build 1' -bb $spec
        if [ -n "$verbose" ] ; then
            a='' fakeroot rpmbuild --buildroot $tmpbuilddir/$subdir  --define='_allow_root_build 1' -bb $spec || fatal
        else
            a='' fakeroot rpmbuild --buildroot $tmpbuilddir/$subdir  --define='_allow_root_build 1' -bb $spec >/dev/null || fatal
        fi
        local repacked_rpm="$(realpath $tmpbuilddir/../*.rpm)"
        if [ -s "$repacked_rpm" ] ; then
            repacked_rpms="$repacked_rpms $repacked_rpm"
            to_remove_pkg_files="$to_remove_pkg_files $repacked_rpm"
        else
            warning "Can't find converted rpm for source binary $pkg package"
        fi
        cd - >/dev/null
        rm -rf $tmpbuilddir/$subdir/
        #rm -rf $tmpbuilddir/../*.rpm
        rm -rf $spec
    done
    rmdir $tmpbuilddir
    #rmdir $tmpbuilddir/..
    true
}

epm_repack()
{
    # if possible, it will put pkg_urls into pkg_files and reconstruct pkg_filenames
    if [ -n "$pkg_urls" ] ; then
        __handle_pkg_urls_to_install
    fi

    [ -z "$pkg_files" ] && info "Skip empty repack list" && return 22

    # TODO: если у нас rpm, а пакет - deb и наоборот
    case $PKGFORMAT in
        rpm)
            if __epm_split_by_pkg_type deb $pkg_files ; then
                __epm_repack_deb_to_rpm $split_replaced_pkgs
                cp -v $repacked_rpms .
                pkg_files="$(estrlist exclude $split_replaced_pkgs $pkg_files)"
            fi

            if [ -n "$pkg_files" ] ; then
                __epm_repack_rpm $pkg_files || fatal
                cp -v $repacked_rpms .
            fi
            ;;
        deb)
            if __epm_split_by_pkg_type rpm $pkg_files ; then
                __epm_repack_rpm_to_deb $split_replaced_pkgs
                cp -v $repacked_debs .
                pkg_files="$(estrlist exclude $split_replaced_pkgs $pkg_files)"
            fi
            ;;
        *)
            fatal "$PKGFORMAT is not supported for repack yet"
            ;;
    esac


    # TODO: move it to exit handler
    if [ -z "$DEBUG" ] ; then
    # TODO: reinvent
    [ -n "$to_remove_pkg_files" ] && rm -fv $to_remove_pkg_files
    [ -n "$to_remove_pkg_files" ] && rmdir -v $(dirname $to_remove_pkg_files | head -n1) 2>/dev/null
    fi

}

# File bin/epm-repofix:


__repofix_check_vendor()
{
	local i
	for i in /etc/apt/vendors.list.d/*.list; do
		[ -e "$i" ] || continue
		grep -q "^simple-key \"$1\"" $i && return
	done
	return 1
}

__try_fix_apt_source_list()
{
	local list="$1"
	local br="$2"
	local path="$3"
	if grep -q -e "^[^#].*$path" $list ; then
		if __repofix_check_vendor $br ; then
			regexp_subst "/$path/s/^rpm[[:space:]]*([fhr])/rpm [$br] \1/" $list
		else
			warning "Skip set $br vendor key (it misssed) for $list"
			regexp_subst "/$path/s/^rpm[[:space:]]*\[$br\][[:space:]]*([fhr])/rpm \1/" $list
		fi
	fi
}

__fix_apt_sources_list()
{
	# for beauty spaces
	local SUBST_ALT_RULE='s!^(.*)[/ ](ALTLinux|LINUX\@Etersoft)[/ ]*(Sisyphus|p8[/ ]branch|p7[/ ]branch|t7[/ ]branch|c7[/ ]branch|p6[/ ]branch|t6[/ ]branch)[/ ](x86_64|i586|x86_64-i586|noarch) !\1 \2/\3/\4 !gi'
	local i
	assure_root
	for i in "$@" ; do
		[ -s "$i" ] || continue
		#perl -i.bak -pe "$SUBST_ALT_RULE" $i
		# TODO: only for uncommented strings
		#sed -i -r -e "$SUBST_ALT_RULE" $i
		regexp_subst "/^ *#/! $SUBST_ALT_RULE" $i

		# Sisyphus uses 'alt' vendor key
		__try_fix_apt_source_list $i alt "ALTLinux\/Sisyphus"
		__try_fix_apt_source_list $i etersoft "Etersoft\/Sisyphus"

		# skip branch replacement for ALT Linux Sisyphus
		[ "$DISTRVERSION" = "Sisyphus" ] && continue

		# add signs for branches
		__try_fix_apt_source_list $i $DISTRVERSION "ALTLinux\/$DISTRVERSION\/branch"
		__try_fix_apt_source_list $i etersoft "Etersoft\/$DISTRVERSION\/branch"
	done
}

epm_repofix()
{

[ -z "$pkg_filenames" ] || fatal "No arguments are allowed here"

case $PMTYPE in
	apt-rpm)
		assure_exists apt-repo
		[ -n "$quiet" ] || docmd apt-repo list
		__fix_apt_sources_list /etc/apt/sources.list
		__fix_apt_sources_list /etc/apt/sources.list.d/*.list
		docmd apt-repo list
		# FIXME: what the best place?
		# rebuild rpm database
		#sudocmd rm -fv /var/lib/rpm/__db*
		#sudocmd rpm --rebuilddb
		;;
	yum-rpm|dnf-rpm)
		# FIXME: what the best place?
		#sudocmd rm -fv /var/lib/rpm/__db*
		#sudocmd rpm --rebuilddb
		;;
	xbps)
		sudocmd xbps-pkgdb -a
		;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
esac

}

# File bin/epm-repolist:

print_apt_sources_list()
{
    local i
    for i in $@ ; do
        test -r "$i" || continue
        #echo
        #echo "$i:"
        grep -v -- "^#" $i
    done | grep -v -- "^ *\$"
}

epm_repolist()
{
case $PMTYPE in
	apt-rpm)
		assure_exists apt-repo
		docmd apt-repo list
		;;
	deepsolver-rpm)
		docmd ds-conf
		;;
	apt-dpkg|aptitude-dpkg)
		showcmd cat /etc/apt/sources.list /etc/apt/sources.list.d/*.list
		print_apt_sources_list /etc/apt/sources.list /etc/apt/sources.list.d/*.list
		;;
	yum-rpm)
		docmd yum repolist -v
		;;
	dnf-rpm)
		docmd dnf repolist -v
		;;
	urpm-rpm)
		docmd urpmq --list-url
		;;
	zypper-rpm)
		docmd zypper sl -d
		;;
	emerge)
		docmd eselect profile list
		docmd layman -L
		;;
	xbps)
		docmd xbps-query -L
		;;
	pacman)
		docmd grep -v -- "^#\|^$" /etc/pacman.conf
		;;
	slackpkg)
		docmd grep -v -- "^#\|^$" /etc/slackpkg/mirrors
		;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
esac

}

# File bin/epm-requires:


epm_requires_files()
{
	local pkg_files="$*"
	[ -n "$pkg_files" ] || return

	local PKGTYPE="$(get_package_type $pkg_files)"

	case "$PKGTYPE" in
		rpm)
			assure_exists rpm
			docmd rpm -q --requires -p $pkg_files
			;;
		deb)
			assure_exists dpkg
			a='' docmd dpkg -I $pkg_files | grep "^ *Depends:" | sed "s|^ *Depends:||g"
			;;
		*)
			fatal "Have no suitable command for $PKGTYPE"
			;;
	esac
}

epm_requires_names()
{
	local pkg_names="$*"
	local CMD
	[ -n "$pkg_names" ] || return

case $PMTYPE in
	apt-rpm)
		# FIXME: need fix for a few names case
		# FIXME: too low level of requires name (libSOME.so)
		if is_installed $pkg_names ; then
			CMD="rpm -q --requires"
		else
			#EXTRA_SHOWDOCMD=' | grep "Depends:"'
			#docmd apt-cache show $pkg_names | grep "Depends:"
			#return
			CMD="apt-cache depends"
		fi

		;;
	#zypper-rpm)
	#	# FIXME: use hi level commands
	#	CMD="rpm -q --requires"
	#	;;
	urpm-rpm)
		CMD="urpmq --requires"
		;;
	yum-rpm)
		if is_installed $pkg_names ; then
			CMD="rpm -q --requires"
		else
			CMD="yum deplist"
		fi
		;;
	dnf-rpm)
		if is_installed $pkg_names ; then
			CMD="rpm -q --requires"
		else
			CMD="dnf repoquery --requires"
		fi
		;;
	pacman)
		CMD="pactree"
		;;
	apt-dpkg|aptitude-dpkg)
		# FIXME: need fix for a few names case
		if is_installed $pkg_names ; then
			showcmd dpkg -s $pkg_names
			a='' dpkg -s $pkg_names | grep "^Depends:" | sed "s|^Depends:||g"
			return
		else
			CMD="apt-cache depends"
		fi
		;;
	emerge)
		assure_exists equery
		CMD="equery depgraph"
		;;
	pkgng)
		#CMD="pkg rquery '%dn-%dv'"
		CMD="pkg info -d"
		;;
	xbps)
		CMD="xbps-query -x"
		;;
	aptcyg)
		#CMD="apt-cyg depends"
		# print show version
		docmd apt-cyg show $pkg_names | grep "^requires: " | sed "s|^requires: ||g"
		return
		;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
esac


docmd $CMD $pkg_names

}

epm_requires()
{
	[ -n "$pkg_filenames" ] || fatal "Requires: missing package(s) name"
	epm_requires_files $pkg_files
	# shellcheck disable=SC2046
	epm_requires_names $(print_name $pkg_names)
}

# File bin/epm-search:


__epm_search_output()
{
local CMD
local string="$1"
case $PMTYPE in
	apt-rpm|apt-dpkg)
		CMD="apt-cache search --"
		;;
	aptitude-dpkg)
		CMD="aptitude search --"
		;;
	deepsolver-rpm)
		CMD="ds-require --"
		;;
	urpm-rpm)
		# urpmq does not support --
		CMD="urpmq -y"
		;;
	pkgsrc)
		CMD="pkg_info -x --"
		;;
	pkgng)
		CMD="pkg search -i --"
		;;
	emerge)
		CMD="emerge --search --"
		;;
	pacman)
		CMD="pacman -Ss --"
		;;
	aura)
		CMD="aura -As --"
		;;
	yum-rpm)
		CMD="yum search --"
		;;
	dnf-rpm)
		CMD="dnf search --"
		;;
	zypper-rpm)
		CMD="zypper search --"
		;;
	mpkg)
		CMD="mpkg search"
		;;
	apk)
		CMD="apk search"
		;;
	tce)
		CMD="tce-ab"
		;;
	conary)
		CMD="conary repquery"
		;;
	npackd)
		docmd npackdcl search --query="$string" --status=all
		return
		;;
	chocolatey)
		CMD="chocolatey list"
		;;
	slackpkg)
		# FIXME
		echo "Note: case sensitive search"
		if [ -n "$verbose" ] ; then
			CMD="/usr/sbin/slackpkg search"
		else
			LANG=C docmd /usr/sbin/slackpkg search $string | grep " - " | sed -e 's|.* - ||g'
			return
		fi
		;;
	homebrew)
		CMD="brew search"
		;;
	guix)
		CMD="guix package -A"
		;;
	android)
		CMD="pm list packages"
		;;
	aptcyg)
		CMD="apt-cyg searchall"
		;;
	xbps)
		CMD="xbps-query -s"
		;;
	*)
		fatal "Have no suitable search command for $PMTYPE"
		;;
esac

LANG=C docmd $CMD $string
}

__epm_search_make_grep()
{
	local i
	[ -z "$*" ] && return

	local list=
	local listN=
	for i in $@ ; do
		case "$i" in
			~*)
				# will clean from ~ later (and have the bug here with empty arg if run with one ~ only)
				listN="$listN $i"
				;;
			*)
				list="$list $i"
				;;
		esac
	done

	#list=$(strip_spaces $list | sed -e "s/ /|/g")
	listN=$(strip_spaces $listN | sed -e "s/ /|/g" | sed -e "s/~//g")

	if [ -n "$short" ] ; then
		echon " | sed -e \"s| .*||g\""
	fi

	[ -n "$listN" ] && echon " | egrep -i -v -- \"$listN\""

	# FIXME: The World has not idea how to do grep both string
	# http://stackoverflow.com/questions/10110051/grep-with-two-strings-logical-and-in-regex?rq=1

	# Need only if we have more than one word (with one word we will grep for colorify)
	if [ "$(echo "$list" | wc -w)" -gt 1 ] ; then
		for i in $list ; do
			# FIXME -n on MacOS?
			echon " | egrep -i -- \"$i\""
		done
	fi

	# FIXME: move from it
	#isatty || return

	# TODO: sorts word by length from large to short

	local COLO=""
	# rule for colorife
	for i in $list $listN; do
		[ -n "$COLO" ] && COLO="$COLO|"
		COLO="$COLO$i"
	done

	# TODO: use some colorifer instead grep (check grep adove too)
	if [ -n "$list" ] ; then
		echon " | egrep -i $EGREPCOLOR -- \"($COLO)\""
	fi
}

__epm_search_internal()
{
	[ -n "$pkg_filenames" ] || fatal "Search: missing search argument(s)"

	# it is useful for first time running
	update_repo_if_needed soft

	warmup_bases

	__epm_search_output $(get_firstarg $pkg_filenames) | grep "$pkg_filenames"
}


epm_search()
{
	[ -n "$pkg_filenames" ] || fatal "Search: missing search argument(s)"

	# it is useful for first time running
	update_repo_if_needed soft

	warmup_bases

	# FIXME: do it better
	local MGS
	MGS=$(eval __epm_search_make_grep $quoted_args)
	EXTRA_SHOWDOCMD="$MGS"
	eval "__epm_search_output \"$(eval get_firstarg $quoted_args)\" $MGS"
}

# File bin/epm-search_file:


__alt_search_file_output()
{
    # grep only on left part (filename), then revert order and grep with color
    __local_ercat $1 | grep -h -- ".*$2.*[[:space:]]" | sed -e "s|\(.*\)\t\(.*\)|\2: \1|g" $3
}

__alt_local_content_search()
{

    info "Locate contents index file(s) ..."
    local CI="$(get_local_alt_contents_index)"
    # TODO use something like
    [ -n "$CI" ] || fatal "Have no local contents index"

    info "Searching in"
    echo "$CI"
    echo "for $1... "

    # FIXME: do it better
    local MGS
    MGS=$(eval __epm_search_make_grep $quoted_args)
    showcmd "$ cat contents_index $MGS"
    eval "__alt_search_file_output \"$CI\" \"$(eval get_firstarg $quoted_args)\" $MGS"
}

epm_search_file()
{
	local CMD
	[ -n "$pkg_filenames" ] || fatal "Search file: missing file name(s)"

case $PMTYPE in
	apt-rpm)
		__alt_local_content_search $pkg_filenames
		return ;;
	apt-dpkg|aptitude-dpkg)
		assure_exists apt-file
		sudocmd apt-file update
		docmd apt-file search $pkg_filenames
		return ;;
	yum-rpm)
		# TODO
		info "Search by full packages list is not implemented yet"
		CMD="yum provides"
		;;
	dnf-rpm)
		# TODO
		info "Search by full packages list is not implemented yet"
		CMD="dnf provides"
		;;
	urpm-rpm)
		CMD="urpmf"
		;;
	zypper-rpm)
		CMD="zypper wp vi"
		;;
	pacman)
		CMD="pacman -Qo"
		;;
	slackpkg)
		CMD="/usr/sbin/slackpkg file-search"
		;;
	ipkg)
		CMD="ipkg search"
		;;
	xbps)
		CMD="xbps-query -Ro"
		;;
	aptcyg)
		docmd apt-cyg searchall "$(echo " $pkg_filenames" | sed -e "s| /| |g")"
		return
		;;
	*)
		fatal "Have no suitable search file command for $PMTYPE"
		;;
esac

docmd $CMD $pkg_filenames

}

# File bin/epm-sh-altlinux:


get_local_alt_mirror_path()
{
    local DN1=$(dirname "$1")
    local DN2=$(dirname $DN1)
    local DN3=$(dirname $DN2)

    local BN0=$(basename "$1") # arch
    local BN1=$(basename $DN1) # branch/Sisyphus
    local BN2=$(basename $DN2) # p8/ALTLinux
    local BN3=$(basename $DN3) # ALTLinux/

    [ "$BN1" = "branch" ] && echo "/tmp/eepm/$BN3/$BN2/$BN1/$BN0" || echo "/tmp/eepm/$BN2/$BN1/$BN0"
}

__local_ercat()
{
   local i
   for i in "$@" ; do
       case "$i" in
           *.xz)
               a='' xzcat $i
               ;;
           *.lz4)
               a='' lz4cat $i
               ;;
           *.failed)
               # just ignore
               ;;
           *)
               cat $i
               ;;
       esac
   done
}

compress_file_inplace()
{
    local OFILE="$1"
    if epm assure lz4 </dev/null ; then
        #docmd lz4 --rm "$OFILE" "$OFILE.lz4" || return
        # due old lz4
        docmd lz4 -f "$OFILE" "$OFILE.lz4" || return
        rm -fv "$OFILE"
    else
        epm assure xz </dev/null || return
        docmd xz -f "$OFILE" || return
    fi
    return 0
}

download_alt_contents_index()
{
    local URL="$1"
    local TD="$2"
    local OFILE="$TD/$(basename "$URL")"

    local DONE=$(echo $OFILE*)
    # TODO: check if too old
    if [ -r "$DONE" ] ; then
        return
    fi

    mkdir -p "$TD"

    if echo "$URL" | grep -q "^file:/" ; then
        URL=$(echo "$URL" | sed -e "s|^file:||")
        [ -s "$URL" ] || { touch $OFILE.failed ; return 1; }
        ln -sf "$URL" "$OFILE" || { touch $OFILE.failed ; return 1; }
    else
        docmd eget -O "$OFILE" "$URL" || { rm -fv $OFILE ; touch $OFILE.failed ; return 1; }
    fi

    rm -f $OFILE.failed
    compress_file_inplace "$OFILE"
}

get_local_alt_contents_index()
{

    local LOCALPATH

    epm_repolist | grep -E "rpm.*(ftp://|http://|https://|file:/)" | sed -e "s@^rpm.*\(ftp://\|http://\|https://\|file:\)@\1@g" | while read -r URL ARCH other ; do
        LOCALPATH=$(get_local_alt_mirror_path "$URL/$ARCH")
        download_alt_contents_index $URL/$ARCH/base/contents_index $LOCALPATH >&2 || continue
        echo "$LOCALPATH/contents_index*"
    done

}

tasknumber()
{
    local num="$(echo "$*" | sed -e "s| *#*||g")"
    isnumber "$num" && echo "$num"
}


# File bin/epm-sh-warmup:

is_warmup_allowed()
{
    local MEM
    MEM="$($DISTRVENDOR -m)"
    # disable warm if have no enough memory
    [ "$MEM" -le 1024 ] && return 1
    return 0
}

__warmup_files()
{
    local D="$1"
    shift
    #showcmd "$*"
    [ -n "$D" ] && info "Warming up $D ..."
    # TODO: use progress, calc files size before
    docmd cat $* >/dev/null 2>/dev/null
}

warmup_rpmbase()
{
    is_warmup_allowed || return
    __warmup_files "rpm" "/var/lib/rpm/*"
}

warmup_dpkgbase()
{
    is_warmup_allowed || { warning "Skipping warmup bases due low memory size" ; return ; }
    __warmup_files "dpkg" "/var/lib/dpkg/*"
}

warmup_lowbase()
{
    case $PKGFORMAT in
        "rpm")
            warmup_rpmbase "$@"
            ;;
        "dpkg")
            warmup_dpkgbase "$@"
            ;;
        *)
            ;;
    esac
}

warmup_aptbase()
{
    is_warmup_allowed || return
    __warmup_files "apt" "/var/lib/apt/lists/* /var/cache/apt/*.bin"
}

warmup_hibase()
{
    case $PMTYPE in
        "apt-rpm"|"apt-dpkg")
            warmup_aptbase "$@"
            ;;
        *)
            ;;
    esac
}

warmup_bases()
{
    DISquiet=1 warmup_lowbase
    DISquiet=1 warmup_hibase
}

# File bin/epm-simulate:


__use_zypper_dry_run()
{
    a='' zypper install --help 2>&1 | grep -q -- "--dry-run" && echo "--dry-run"
}

__use_yum_assumeno()
{
    a='' yum --help 2>&1 | grep -q -- "--assumeno"
}


__check_yum_result()
{
    grep "^No package" $1 && return 1
    grep "^Complete!" $1 && return 0
    grep "Exiting on user [Cc]ommand" $1 && return 0
    # dnf issue
    grep "^Operation aborted." $1 && return 0
    # return default result by default
    return $2
}

__check_pacman_result()
{
    grep "^error: target not found:" $1 && return 1
    grep "^Total Installed Size:" $1 && return 0
    grep "^Total Download Size:" $1 && return 0
    # return default result by default
    return $2
}


_epm_do_simulate()
{
    local CMD
    local RES=0
    local filenames="$*"

    case $PMTYPE in
    	apt-rpm|apt-dpkg)
    		CMD="apt-get --simulate install"
    		;;
    	aptitude-dpkg)
    		CMD="aptitude -s install"
    		;;
    	yum-rpm)
    		if __use_yum_assumeno ; then
    			LC_ALL=C store_output sudocmd yum --assumeno install $filenames
    			__check_yum_result $RC_STDOUT $?
    		else
    			LC_ALL=C store_output sudocmd yum install $filenames <<EOF
n
EOF
    			__check_yum_result $RC_STDOUT $?
    		fi
    		RES=$?
    		clean_store_output
    		return $RES ;;
    	dnf-rpm)
    		LC_ALL=C store_output sudocmd dnf --assumeno install $filenames
    		__check_yum_result $RC_STDOUT $?
    		RES=$?
    		clean_store_output
    		return $RES ;;
    	urpm-rpm)
    		CMD="urpmi --test --auto"
    		;;
    	zypper-rpm)
    		if ! __use_zypper_dry_run >/dev/null ; then
    			fatal "zypper is too old: does not support --dry-run"
    		fi
    		CMD="zypper --non-interactive install --dry-run"
    		;;
    	emerge)
    		local res=0
    		for pkg in $filenames ; do
			is_installed $pkg && continue
			docmd emerge --pretend $pkg && continue
			pkg=1
			break
    		done
    		return $res ;;
    	pacman)
    		LC_ALL=C store_output sudocmd pacman -v -S $filenames <<EOF
no
EOF
    		__check_pacman_result $RC_STDOUT $?
    		RES=$?
    		clean_store_output
    		return $RES ;;
    	slackpkg)
    		#docmd /usr/sbin/slackpkg -batch=on -default_answer=yes download
    		# just try search every package
    		# FIXME: epm_search have to return false status code if the package does not found
    		local pkg res
    		res=0
    		for pkg in $filenames ; do
    			# FIXME: -[0-0] does not work in search!
    			# FIXME: we need strict search here (not find gst-plugins-base if search for gst-plugins
    			# TODO: use short?
    			# use verbose for get package status
    			#pkg_filenames="$pkg-[0-9]" verbose=--verbose __epm_search_internal | egrep "(installed|upgrade)" && continue
    			#pkg_filenames="$pkg" verbose=--verbose __epm_search_internal | egrep "(installed|upgrade)" && continue
    			pkg_filenames="$pkg" __epm_search_internal | grep -q "^$pkg-[0-9]" && continue
    			res=1
    			info "Package '$pkg' does not found in repository."
    		done
    		return $res ;;
    	*)
    		fatal "Have no suitable simulate command for $PMTYPE"
    		;;
    esac

    sudocmd $CMD $filenames
}

epm_simulate()
{
    [ -z "$pkg_filenames" ] && info "Simulate: Skip empty list" && return 22

    local filenames="$(echo $pkg_filenames | filter_out_installed_packages)"

    [ -z "$filenames" ] && info "Simulate: All packages are already installed" && return 0

    _epm_do_simulate $filenames
    local RES=$?
    if [ -z "$quiet" ] ; then
        if [ "$RES" = 0 ] ; then
            info "Simulate result: $filenames package(s) CAN BE installed"
        else
            info "Simulate result: There are PROBLEMS with install some package(s)"
        fi
    fi
    return $RES
}


# File bin/epm-site:


PAOURL="https://packages.altlinux.org"

paoapi()
{
	# http://petstore.swagger.io/?url=http://packages.altlinux.org/api/docs
	epm assure curl || return 1
	showcmd curl "$PAOURL/api/$1"
	a='' curl -s --header "Accept: application/json" "$PAOURL/api/$1"
}

get_pao_var()
{
	local FIELD="$1"
	#grep '"$FIELD"' | sed -e 's|.*"$FIELD":"||g' | sed -e 's|".*||g'
	internal_tools_json -b | grep -E "\[.*\"$FIELD\"\]" | sed -e 's|.*[[:space:]]"\(.*\)"|\1|g'
	return 0
}


run_command_if_exists()
{
	local CMD="$1"
	shift
	if which "$CMD" 2>/dev/null >/dev/null ; then
		docmd "$CMD" "$@"
		return 0
	fi
	return 1
}

open_browser()
{
	local i
	for i in xdg-open firefox chromium links ; do
		run_command_if_exists $i "$@" && return
	done
}

__query_package_hl_url()
{
	case $DISTRNAME in
		ALTLinux)
			paoapi srpms/$1 | get_pao_var url
			;;
	esac
	return 1
}

query_package_url()
{
	local URL

	case $PMTYPE in
		*-rpm)
			# TODO: for binary packages?
			query_package_field URL "$1" || __query_package_hl_url "$1"
			#LANG=C epm info "$1"
			return
			;;
	esac
	fatal "rpm based distro supported only. TODO: Realize via web service?"
}

get_locale()
{
	local loc
	loc=$(a='' natspec --locale 2>/dev/null)
	[ -n "$loc" ] || loc=$LANG
	echo $loc
}

get_pao_url()
{
	local loc
	loc=$(get_locale | cut -c1-2)
	case $loc in
		en|ru|uk|br)
			loc=$loc
			;;
		*)
			loc=en
	esac
	echo "$PAOURL/$loc/Sisyphus/srpms"
}

query_altlinux_url()
{
	local URL
	case $PMTYPE in
		*-rpm)
			local srpm=$(print_srcname "$1")
			[ -n "$srpm" ] || fatal "Can't get source name for $1"
			echo "$(get_pao_url)/$srpm"
			return
			;;
	esac
	fatal "rpm based distro supported only. TODO: Realize via web service?"
}

epm_site()
{

[ -n "$pkg_filenames" ] || fatal "Info: missing package(s) name"

local PAO=""
for f in $pkg_names $pkg_files ; do
	[ "$f" = "-p" ] && PAO="$f" && continue
	if [ -n "$PAO" ] ; then
		pkg_url=$(query_altlinux_url $f)
	else
		pkg_url=$(query_package_url $f)
	fi
	[ -n "$pkg_url" ] && open_browser "$pkg_url" && continue
	warning "Can't get URL for $f package"
done



}

# File bin/epm-update:



epm_update()
{
	[ -z "$pkg_filenames" ] || fatal "No arguments are allowed here"
	info "Running command for update remote package repository database"

warmup_hibase

case $PMTYPE in
	apt-rpm)
		sudocmd apt-get update || return
		#sudocmd apt-get -f install || exit
		;;
	apt-dpkg)
		sudocmd apt-get update || return
		#sudocmd apt-get -f install || exit
		#sudocmd apt-get autoremove
		;;
	#snappy)
	#	sudocmd snappy
	#	;;
	aptitude-dpkg)
		sudocmd aptitude update || return
		;;
	yum-rpm)
		info "update command is stubbed for yum"
		# yum makecache
		#sudocmd yum check-update
		;;
	dnf-rpm)
		info "update command is stubbed for dnf"
		# dnf makecache
		#sudocmd dnf check-update
		;;
	urpm-rpm)
		sudocmd urpmi.update -a
		;;
	pacman)
		sudocmd pacman -S -y
		;;
	aura)
		sudocmd aura -A -y
		;;
	zypper-rpm)
		sudocmd zypper refresh
		;;
	emerge)
		sudocmd emerge --sync
		;;
	slackpkg)
		sudocmd /usr/sbin/slackpkg -batch=on update
		;;
	deepsolver-rpm)
		sudocmd ds-update
		;;
	npackd)
		sudocmd packdcl detect # get packages from MSI database
		;;
	homebrew)
		docmd brew update
		;;
	ipkg)
		sudocmd ipkg update
		;;
	apk)
		sudocmd apk update
		;;
	pkgsrc)
		# portsnap extract for the first time?
		sudocmd portsnap fetch update
		;;
	aptcyg)
		sudocmd apt-cyg update
		;;
	xbps)
		sudocmd xbps-install -S
		;;
	*)
		fatal "Have no suitable update command for $PMTYPE"
		;;
esac

}

# File bin/epm-upgrade:


epm_upgrade()
{
	local CMD

	#[ -z "$pkg_filenames" ] || fatal "No arguments are allowed here"

	# it is useful for first time running
	update_repo_if_needed

	warmup_bases
	info "Running command for upgrade packages"

	case $PMTYPE in
	apt-rpm|apt-dpkg)
		local APTOPTIONS="$(subst_option non_interactive -y) $(subst_option verbose "-o Debug::pkgMarkInstall=1 -o Debug::pkgProblemResolver=1")"
		# Функцию добавления параметра при условии
		CMD="apt-get $APTOPTIONS dist-upgrade $noremove"
		;;
	aptitude-dpkg)
		CMD="aptitude dist-upgrade"
		;;
	yum-rpm)
		local OPTIONS="$(subst_option non_interactive -y)"
		# can do update repobase automagically
		CMD="yum $OPTIONS update $pkg_filenames"
		;;
	dnf-rpm)
		local OPTIONS="$(subst_option non_interactive -y)"
		CMD="dnf $OPTIONS distro-sync $pkg_filenames"
		;;
	snappy)
		CMD="snappy update"
		;;
	urpm-rpm)
		# or --auto-select --replace-files
		CMD="urpmi --update --auto-select $pkg_filenames"
		;;
	zypper-rpm)
		CMD="zypper dist-upgrade"
		;;
	pacman)
		CMD="pacman -S -u $force"
		;;
	aura)
		CMD="aura -A -u"
		;;
	emerge)
		CMD="emerge -NuDa world"
		;;
	conary)
		CMD="conary updateall"
		;;
	pkgsrc)
		CMD="freebsd-update fetch install"
		;;
	pkgng)
		CMD="pkg upgrade"
		;;
	chocolatey)
		CMD="chocolatey update all"
		;;
	homebrew)
		#CMD="brew upgrade"
		docmd "brew upgrade $(brew outdated)"
		return
		;;
	ipkg)
		CMD="ipkg upgrade"
		;;
	slackpkg)
		CMD="/usr/sbin/slackpkg upgrade-all"
		;;
	guix)
		CMD="guix package -u"
		;;
	aptcyg)
		# shellcheck disable=SC2046
		docmd_foreach "epm install" $(short=1 epm packages)
		return
		;;
	xbps)
		CMD="xbps-install -Su"
		;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
	esac

	sudocmd $CMD $pkg_filenames
}

# File bin/epm-Upgrade:


epm_Upgrade()
{
	case $PMTYPE in
		yum-rpm)
			;;
		*)
			pkg_filenames='' epm_update || return
			;;
	esac

	epm_upgrade
}

# File bin/epm-whatdepends:



epm_whatdepends()
{
	local CMD
	[ -n "$pkg_files" ] && fatal "whatdepends do not handle files"
	[ -n "$pkg_names" ] || fatal "whatdepends: missing package(s) name"
	local pkg=$(print_name $pkg_names)

case $PMTYPE in
	apt-rpm)
		CMD="apt-cache whatdepends"
		;;
	apt-dpkg|aptitude-dpkg)
		CMD="apt-cache rdepends"
		;;
	aptitude-dpkg)
		CMD="aptitude why"
		;;
	yum-rpm)
		CMD="repoquery --whatrequires"
		;;
	urpm-rpm)
		CMD="urpmq --whatrequires"
		;;
	dnf-rpm)
		# check command: dnf repoquery --whatrequires
		CMD="repoquery --whatrequires"
		;;
	emerge)
		assure_exists equery
		CMD="equery depends -a"
		;;
	pkgng)
		CMD="pkg info -r"
		;;
	aptcyg)
		CMD="apt-cyg rdepends"
		;;
	xbps)
		CMD="xbps-query -X"
		;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
esac

docmd $CMD $pkg

}

# File bin/epm-whatprovides:


epm_whatprovides()
{
	local CMD
	[ -n "$pkg_files" ] && fatal "whatprovides does not handle files"
	[ -n "$pkg_names" ] || fatal "whatprovides: missing package(s) name"
	local pkg=$(print_name $pkg_names)

case $PMTYPE in
	conary)
		CMD="conary repquery --what-provides"
		;;
	apt-rpm|apt-dpkg|aptitude-dpkg)
		LANG=C docmd apt-get install --print-uris $pkg | grep "^Selecting" | cut -f2 -d" "
		return
		;;
	yum-rpm)
		CMD="yum whatprovides"
		;;
	urpm-rpm)
		CMD="urpmq --whatprovides"
		;;
	dnf-rpm)
		CMD="yum provides"
		;;
	zypper-rpm)
		CMD="zypper what-provides"
		;;
	*)
		fatal "Have no suitable command for $PMTYPE"
		;;
esac

docmd $CMD $pkg

}

################# incorporate bin/distr_info #################
internal_distr_info()
{
# Author: Vitaly Lipatov <lav@etersoft.ru>
# 2007, 2009, 2010, 2012, 2016 (c) Etersoft
# 2007-2016 Public domain

# Detect the distro and version
# Welcome to send updates!

# You can set ROOTDIR to root system dir
#ROOTDIR=

# Check for DISTRO specific file in /etc
distro()
{
	#[ -n "$ROOTDIR" ] || return
	# fill global DISTROFILE
	DISTROFILE="$ROOTDIR/etc/$1"
	[ -f "$DISTROFILE" ]
}

# Has a distro file the specified word?
has()
{
	[ -n "$DISTROFILE" ] || exit 1
	grep "$*" "$DISTROFILE" >/dev/null 2>&1
}

firstupper()
{
	echo "$*" | sed 's/.*/\u&/'
}

# Translate DISTRIB_ID to vendor name (like %_vendor does)
rpmvendor()
{
	[ "$DISTRIB_ID" = "ALTLinux" ] && echo "alt" && return
	[ "$DISTRIB_ID" = "AstraLinux" ] && echo "astra" && return
	[ "$DISTRIB_ID" = "LinuxXP" ] && echo "lxp" && return
	[ "$DISTRIB_ID" = "TinyCoreLinux" ] && echo "tcl" && return
	[ "$DISTRIB_ID" = "VoidLinux" ] && echo "void" && return
	echo "$DISTRIB_ID" | tr "[:upper:]" "[:lower:]"
}

# Translate DISTRIB_ID name to package manner (like in the package release name)
pkgvendor()
{
	[ "$DISTRIB_ID" = "Mandriva" ] && echo "mdv" && return
	rpmvendor
}

# Print pkgtype (need DISTRIB_ID var)
pkgtype()
{
# TODO: try use generic names
    case $(pkgvendor) in
		freebsd) echo "tbz" ;;
		sunos) echo "pkg.gz" ;;
		slackware|mopslinux) echo "tgz" ;;
		archlinux|manjaro) echo "pkg.tar.xz" ;;
		gentoo) echo "tbz2" ;;
		windows) echo "exe" ;;
		android) echo "apk" ;;
		alpine) echo "apk" ;;
		tinycorelinux) echo "tcz" ;;
		voidlinux) echo "xbps" ;;
		cygwin) echo "tar.xz" ;;
		debian|ubuntu|mint|runtu|mcst|astra) echo "deb" ;;
		alt|asplinux|suse|mandriva|rosa|mandrake|pclinux|sled|sles)
			echo "rpm" ;;
		fedora|redhat|scientific|centos|rhel|goslinux)
			echo "rpm" ;;
		*)  echo "rpm" ;;
	esac
}

get_var()
{
	# get first variable and print it out, drop quotes if exists
	grep -i "^$1 *=" | head -n 1 | sed -e "s/^[^=]*[ \t]*=[ \t]*//" | sed -e "s/^[\'\"]\(.*\)[\'\"]/\1/"
}

# 2010.1 -> 2010
get_major_version()
{
	echo "$1" | sed -e "s/\..*//g"
}

# Default values
DISTRIB_ID="Generic"
DISTRIB_RELEASE=""
DISTRIB_CODENAME=""

# Default with LSB
if distro lsb-release ; then
	DISTRIB_ID=$(cat $DISTROFILE | get_var DISTRIB_ID)
	DISTRIB_RELEASE=$(cat $DISTROFILE | get_var DISTRIB_RELEASE)
	DISTRIB_CODENAME=$(cat $DISTROFILE | get_var DISTRIB_CODENAME)
fi

# ALT Linux based
if distro altlinux-release ; then
	DISTRIB_ID="ALTLinux"
	if has Sisyphus ; then DISTRIB_RELEASE="Sisyphus"
	elif has "ALT Linux 7." ; then DISTRIB_RELEASE="p7"
	elif has "ALT Linux t7." ; then DISTRIB_RELEASE="t7"
	elif has "ALT Linux 8." ; then DISTRIB_RELEASE="p8"
	elif has "ALT .*8.[0-9]" ; then DISTRIB_RELEASE="p8"
	elif has "Simply Linux 6." ; then DISTRIB_RELEASE="p6"
	elif has "Simply Linux 7." ; then DISTRIB_RELEASE="p7"
	elif has "Simply Linux 8." ; then DISTRIB_RELEASE="p8"
	elif has "ALT Linux 6." ; then DISTRIB_RELEASE="p6"
	elif has "ALT Linux p8"  ; then DISTRIB_RELEASE="p8"
	elif has "ALT Linux p7"  ; then DISTRIB_RELEASE="p7"
	elif has "ALT Linux p6"  ; then DISTRIB_RELEASE="p6"
	elif has "ALT Linux p5"  ; then DISTRIB_RELEASE="p5"
	elif has "ALT Linux 5.1" ; then DISTRIB_RELEASE="5.1"
	elif has "ALT Linux 5.0" ; then DISTRIB_RELEASE="5.0"
	elif has "ALT Linux 4.1" ; then DISTRIB_RELEASE="4.1"
	elif has "ALT Linux 4.0" ; then DISTRIB_RELEASE="4.0"
	elif has Walnut          ; then DISTRIB_RELEASE="4.0"
	elif has Hypericum       ; then DISTRIB_RELEASE="p8"
	elif has "starter kit"   ; then DISTRIB_RELEASE="p8"
	elif has 20070810 ; then DISTRIB_RELEASE="4.0"
	elif has Ajuga    ; then DISTRIB_RELEASE="4.0"
	elif has 20050723 ; then DISTRIB_RELEASE="3.0"
	elif has Citron   ; then DISTRIB_RELEASE="2.4"
	fi

elif distro gentoo-release ; then
	DISTRIB_ID="Gentoo"
	MAKEPROFILE=$(readlink $ROOTDIR/etc/portage/make.profile 2>/dev/null) || MAKEPROFILE=$(readlink $ROOTDIR/etc/make.profile)
	DISTRIB_RELEASE=$(basename $MAKEPROFILE)
	echo $DISTRIB_RELEASE | grep -q "[0-9]" || DISTRIB_RELEASE=$(basename "$(dirname $MAKEPROFILE)")

# Slackware based
elif distro mopslinux-version ; then
	DISTRIB_ID="MOPSLinux"
	if   has 4.0 ; then DISTRIB_RELEASE="4.0"
	elif has 5.0 ; then DISTRIB_RELEASE="5.0"
	elif has 5.1 ; then DISTRIB_RELEASE="5.1"
	elif has 6.0 ; then DISTRIB_RELEASE="6.0"
	elif has 6.1 ; then DISTRIB_RELEASE="6.1"
	fi
elif distro slackware-version ; then
	DISTRIB_ID="Slackware"
	DISTRIB_RELEASE="$(grep -Eo '[0-9]+\.[0-9]+' $DISTROFILE)"

elif distro os-release && which apk 2>/dev/null >/dev/null ; then
	# shellcheck disable=SC1090
	. $ROOTDIR/etc/os-release
	DISTRIB_ID="$(firstupper "$ID")"
	DISTRIB_RELEASE="$VERSION_ID"

elif distro os-release && which tce-ab 2>/dev/null >/dev/null ; then
	# shellcheck disable=SC1090
	. $ROOTDIR/etc/os-release
	DISTRIB_ID="TinyCoreLinux"
	DISTRIB_RELEASE="$VERSION_ID"

elif distro os-release && which xbps-query 2>/dev/null >/dev/null ; then
	# shellcheck disable=SC1090
	. $ROOTDIR/etc/os-release
	DISTRIB_ID="VoidLinux"
	DISTRIB_RELEASE="Live"

elif distro arch-release ; then
	DISTRIB_ID="ArchLinux"
	DISTRIB_RELEASE="2010"
	if grep 2011 -q $ROOTDIR/etc/pacman.d/mirrorlist ; then
		DISTRIB_RELEASE="2011"
	fi

# Elbrus
elif distro mcst_version ; then
	DISTRIB_ID="MCST"
	DISTRIB_RELEASE=$(cat "$DISTROFILE" | grep "release" | sed -e "s|.*release \([0-9]*\).*|\1|g")

elif distro astra_version ; then
	#DISTRIB_ID=`cat $DISTROFILE | get_var DISTRIB_ID`
	DISTRIB_ID="AstraLinux"
	#DISTRIB_RELEASE=$(cat "$DISTROFILE" | head -n1 | sed -e "s|.* \([a-z]*\).*|\1|g")
	DISTRIB_RELEASE=$DISTRIB_CODENAME

# for Ubuntu use standard LSB info
elif [ "$DISTRIB_ID" = "Ubuntu" ] && [ -n "$DISTRIB_RELEASE" ]; then
	# use LSB version
	true

# Debian based
elif distro debian_version ; then
	DISTRIB_ID="Debian"
	DISTRIB_RELEASE=$(cat $DISTROFILE | sed -e "s/\..*//g")


# Mandriva based
elif distro pclinuxos-release ; then
	DISTRIB_ID="PCLinux"
	if   has "2007" ; then DISTRIB_RELEASE="2007"
	elif has "2008" ; then DISTRIB_RELEASE="2008"
	elif has "2010" ; then DISTRIB_RELEASE="2010"
	fi

elif distro mandriva-release || distro mandrake-release ; then
	DISTRIB_ID="Mandriva"
	if   has 2005 ; then DISTRIB_RELEASE="2005"
	elif has 2006 ; then DISTRIB_RELEASE="2006"
	elif has 2007 ; then DISTRIB_RELEASE="2007"
	elif has 2008 ; then DISTRIB_RELEASE="2008"
	elif has 2009.0 ; then DISTRIB_RELEASE="2009.0"
	elif has 2009.1 ; then DISTRIB_RELEASE="2009.1"
	else
		# use /etc/lsb-release info by default
		if has ROSA ; then
			DISTRIB_ID="ROSA"
		fi
	fi

# Fedora based
elif distro linux-xp-release || distro lxp-release; then
	DISTRIB_ID="LinuxXP"
	if has "Attack of the Clones" ; then DISTRIB_RELEASE="2006"
	elif has "2007" ; then DISTRIB_RELEASE="2007"
	elif has "2008" ; then DISTRIB_RELEASE="2008"
	elif has "2009" ; then DISTRIB_RELEASE="2009"
	fi

elif distro asplinux-release ; then
	DISTRIB_ID="ASPLinux"
	if   has Karelia ; then DISTRIB_RELEASE="10"
	elif has Seliger ; then DISTRIB_RELEASE="11"
	elif has "11.1" ; then DISTRIB_RELEASE="11.1"
	elif has Ladoga ; then DISTRIB_RELEASE="11.2"
	elif has "11.2" ; then DISTRIB_RELEASE="11.2"
	elif has "12" ; then DISTRIB_RELEASE="12"
	elif has "13" ; then DISTRIB_RELEASE="13"
	elif has "14" ; then DISTRIB_RELEASE="14"
	elif has "15" ; then DISTRIB_RELEASE="15"
	fi

elif distro MCBC-release ; then
	DISTRIB_ID="MCBC"
	if   has 3.0 ; then DISTRIB_RELEASE="3.0"
	elif has 3.1 ; then DISTRIB_RELEASE="3.1"
	fi

elif distro fedora-release ; then
	DISTRIB_ID="Fedora"
	DISTRIB_RELEASE=$(cat "$DISTROFILE" | grep "release" | sed -e "s|.*release \([0-9]*\).*|\1|g")

elif distro redhat-release ; then
	# FIXME if need
	# actually in the original RHEL: Red Hat Enterprise Linux .. release N
	DISTRIB_ID="RHEL"
	if has CentOS ; then
		DISTRIB_ID="CentOS"
	elif has Scientific ; then
		DISTRIB_ID="Scientific"
	elif has GosLinux ; then
		DISTRIB_ID="GosLinux"
	fi
	if has Beryllium ; then
		DISTRIB_ID="Scientific"
		DISTRIB_RELEASE="4.1"
	elif has Shrike ; then
		DISTRIB_ID="RedHat"
		DISTRIB_RELEASE="9"
	elif has Taroon ; then 	DISTRIB_RELEASE="3"
	elif has "release 4" ; then DISTRIB_RELEASE="4"
	elif has "release 5" ; then DISTRIB_RELEASE="5"
	elif has "release 6" ; then DISTRIB_RELEASE="6"
	elif has "release 7" ; then DISTRIB_RELEASE="7"
	fi

# SUSE based
elif distro SuSe-release || distro SuSE-release ; then
	DISTRIB_ID="SUSE"
	DISTRIB_RELEASE=$(cat "$DISTROFILE" | grep "VERSION" | sed -e "s|^VERSION = ||g")
	if   has "SUSE Linux Enterprise Desktop" ; then
		DISTRIB_ID="SLED"
	elif has "SUSE Linux Enterprise Server" ; then
		DISTRIB_ID="SLES"
	fi

# https://www.freedesktop.org/software/systemd/man/os-release.html
elif distro os-release ; then
	# shellcheck disable=SC1090
	. $ROOTDIR/etc/os-release
	DISTRIB_ID="$(firstupper "$ID")"
	DISTRIB_RELEASE="$VERSION_ID"
	[ -n "$DISTRIB_RELEASE" ] || DISTRIB_RELEASE="CUR"

# fixme: can we detect by some file?
elif [ "$(uname)" = "FreeBSD" ] ; then
	DISTRIB_ID="FreeBSD"
	UNAME=$(uname -r)
	DISTRIB_RELEASE=$(echo "$UNAME" | grep RELEASE | sed -e "s|\([0-9]\.[0-9]\)-RELEASE|\1|g")

# fixme: can we detect by some file?
elif [ "$(uname)" = "SunOS" ] ; then
	DISTRIB_ID="SunOS"
	DISTRIB_RELEASE=$(uname -r)

# fixme: can we detect by some file?
elif [ "$(uname -s 2>/dev/null)" = "Darwin" ] ; then
	DISTRIB_ID="MacOS"
	DISTRIB_RELEASE=$(uname -r)

# fixme: move to up
elif [ "$(uname)" = "Linux" ] && which guix 2>/dev/null >/dev/null ; then
	DISTRIB_ID="GNU/Linux/Guix"
	DISTRIB_RELEASE=$(uname -r)

# fixme: move to up
elif [ "$(uname)" = "Linux" ] && [ -x $ROOTDIR/system/bin/getprop ] ; then
	DISTRIB_ID="Android"
	DISTRIB_RELEASE=$(getprop | awk -F": " '/build.version.release/ { print $2 }' | tr -d '[]')

elif [ "$(uname -o 2>/dev/null)" = "Cygwin" ] ; then
        DISTRIB_ID="Cygwin"
        DISTRIB_RELEASE="all"

# try use standart LSB info by default
elif distro lsb-release && [ -n "$DISTRIB_RELEASE" ]; then
	# use LSB

	# fix distro name
	case "$DISTRIB_ID" in
		"openSUSE Tumbleweed")
			DISTRIB_ID="Tumbleweed"
			;;
	esac
fi

get_base_os_name()
{
local DIST_OS
# Resolve the os
DIST_OS=`uname -s | tr [:upper:] [:lower:] | tr -d " \t\r\n"`
case "$DIST_OS" in
    'sunos')
        DIST_OS="solaris"
        ;;
    'hp-ux' | 'hp-ux64')
        DIST_OS="hpux"
        ;;
    'darwin' | 'oarwin')
        DIST_OS="macosx"
        ;;
    'unix_sv')
        DIST_OS="unixware"
        ;;
    'freebsd' | 'openbsd' | 'netbsd')
        DIST_OS="freebsd"
        ;;
esac
echo "$DIST_OS"
}

get_uname_m()
{
    uname -m | tr [:upper:] [:lower:] | tr -d " \t\r\n"
}

get_arch()
{
local DIST_ARCH
# Resolve the architecture
DIST_ARCH="$(get_uname_m)"
case "$DIST_ARCH" in
    'ia32' | 'i386' | 'i486' | 'i586' | 'i686')
        DIST_ARCH="x86"
        ;;
    'amd64' | 'x86_64')
        DIST_ARCH="x86_64"
        ;;
    'ia64' | 'ia-64')
        DIST_ARCH="ia64"
        ;;
    'ip27' | 'mips')
        DIST_ARCH="mips"
        ;;
    'powermacintosh' | 'power' | 'powerpc' | 'power_pc' | 'ppc64')
        DIST_ARCH="ppc"
        ;;
    'pa_risc' | 'pa-risc')
        DIST_ARCH="parisc"
        ;;
    'sun4u' | 'sparcv9')
        DIST_ARCH="sparc"
        ;;
    '9000/800')
        DIST_ARCH="parisc"
        ;;
    armv*)
        if [ -z "$(readelf -A /proc/self/exe | grep Tag_ABI_VFP_args)" ] ; then
            DIST_ARCH="armel"
        else
            DIST_ARCH="armhf"
        fi
        ;;
esac
echo "$DIST_ARCH"
}

get_bit_size()
{
local DIST_BIT
# Check if we are running on 64bit platform, seems like a workaround for now...
DIST_BIT="$(get_uname_m)"
case "$DIST_BIT" in
    'amd64' | 'ia64' | 'x86_64' | 'ppc64')
        DIST_BIT="64"
        ;;
#    'pa_risc' | 'pa-risc') # Are some of these 64bit? Least not all...
#       BIT="64"
#        ;;
    'sun4u' | 'sparcv9') # Are all sparcs 64?
        DIST_BIT="64"
        ;;
#    '9000/800')
#       DIST_BIT="64"
#        ;;
    *) # In any other case default to 32
        DIST_BIT="32"
        ;;
esac
echo "$DIST_BIT"
}

# TODO: check before calc
get_memory_size() {
    local detected=0
    local DIST_OS="$(get_base_os_name)"
    case "$DIST_OS" in
        macosx)
            detected=$((`sysctl hw.memsize | sed s/"hw.memsize: "//`/1024/1024))
            ;;
        freebsd)
            detected=$((`sysctl hw.physmem | sed s/"hw.physmem: "//`/1024/1024))
            ;;
        linux)
            [ -r /proc/meminfo ] && detected=$((`cat /proc/meminfo | grep MemTotal | awk '{print $2}'`/1024))
            ;;
    esac

    # Exit codes only support values between 0 and 255. So use stdout.
    echo $detected
}


case $1 in
	-p)
		# override DISTRIB_ID
		test -n "$2" && DISTRIB_ID="$2"
		pkgtype
		exit 0
		;;
	-h)
		echo "distr_vendor - system name and version detection"
		echo "Usage: distr_vendor [options] [args]"
		echo "-p [SystemName] - print type of packaging system"
		echo "-d - print distro name"
		echo "-a - print hardware architecture"
		echo "-b - print size of arch bit (32/64)"
		echo "-m - print system memory size (in MB)"
		echo "-o - print base os name"
		echo "-v - print version of distro"
		echo "-e - print full name of distro with version (by default)"
		echo "-s [SystemName] - print name of distro for build system (like in the package release name)"
		echo "-n [SystemName] - print vendor name (as _vendor macros in rpm)"
		echo "-V - print the version of $0"
		echo "-h - this help"
		exit 0
		;;
	-d)
		echo $DISTRIB_ID
		;;
	-a)
		get_arch
		;;
	-b)
		get_bit_size
		;;
	-m)
		get_memory_size
		;;
	-o)
		get_base_os_name
		;;
	-v)
		echo $DISTRIB_RELEASE
		;;
	-s)
		# override DISTRIB_ID
		test -n "$2" && DISTRIB_ID="$2"
		pkgvendor
		exit 0
		;;
	-n)
		# override DISTRIB_ID
		test -n "$2" && DISTRIB_ID="$2"
		rpmvendor
		exit 0
		;;
	-V)
		echo "20171010"
		exit 0
		;;
	*)
		# if run without args, just printout Name/Version of the current system
		[ -n "$DISTRIB_RELEASE" ] && echo $DISTRIB_ID/$DISTRIB_RELEASE || echo $DISTRIB_ID
		;;
esac

}
################# end of incorporated bin/distr_info #################


################# incorporate bin/tools_eget #################
internal_tools_eget()
{
# eget - simply shell on wget for loading directories over http (wget does not support wildcard for http)
# Example use:
# eget http://ftp.altlinux.ru/pub/security/ssl/*
#
# Copyright (C) 2014-2014, 2016  Etersoft
# Copyright (C) 2014 Daniil Mikhailov <danil@etersoft.ru>
# Copyright (C) 2016-2017 Vitaly Lipatov <lav@etersoft.ru>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#

WGET="wget"

# TODO: passthrou all wget options
if [ "$1" = "-q" ] ; then
    WGET="wget -q"
    shift
fi

if [ "$1" = "--list" ] ; then
    LISTONLY="$1"
    shift
fi

fatal()
{
    echo "$*" >&2
    exit 1
}

# check man glob
filter_glob()
{
	# translate glob to regexp
	grep "^$(echo "$1" | sed -e "s|\*|.*|g" -e "s|\?|.|g")$"
}


# download to this file
WGET_OPTION_TARGET=
if [ "$1" = "-O" ] ; then
    TARGETFILE="$2"
    WGET_OPTION_TARGET="-O $2"
    shift 2
fi

# TODO:
# -P support

if [ -z "$1" ] ; then
    echo "eget - wget wrapper" >&2
    fatal "Run with URL, like http://somesite.ru/dir/*.log"
fi

if [ "$1" = "-h" ] || [ "$1" = "--help" ] ; then
    echo "eget - wget wrapper, with support"
    echo "Usage: eget [-O target file] [--list] http://somesite.ru/dir/na*.log"
    echo "Options:"
    echo "    --list - print files frm url with mask"
    echo
    wget --help
    return
fi

# do not support /
if echo "$1" | grep -q "/$" ; then
    fatal "Use http://example.com/e/* to download all files in dir"
fi

# If ftp protocol, just download
if echo "$1" | grep -q "^ftp://" ; then
    [ -n "$LISTONLY" ] && fatal "Error: list files for ftp:// do not supported yet"
    $WGET $WGET_OPTION_TARGET "$1"
    return
fi

# drop mask part
URL="$(dirname "$1")/"

if echo "$URL" | grep -q "[*?]" ; then
    fatal "Error: there are globbing symbols (*?) in $URL"
fi

# mask allowed only in last part of path
MASK=$(basename "$1")

# If have no wildcard symbol like asterisk, just download
if echo "$MASK" | grep -qv "[*?]" ; then
    $WGET $WGET_OPTION_TARGET "$1"
    return
fi

get_urls()
{
    $WGET -O- $URL | \
        grep -o -E 'href="([^\*/"#]+)"' | cut -d'"' -f2
}

if [ -n "$LISTONLY" ] ; then
    WGET="$WGET -q"
    for fn in $(get_urls | filter_glob "$MASK") ; do
        echo "$(basename "$fn")"
    done
    return
fi

ERROR=0
for fn in $(get_urls | filter_glob "$MASK") ; do
    $WGET "$URL/$(basename "$fn")" || ERROR=1
done
exit $ERROR

}
################# end of incorporated bin/tools_eget #################


################# incorporate bin/tools_estrlist #################
internal_tools_estrlist()
{
#!/bin/bash
# 2009-2010, 2012, 2017 Etersoft www.etersoft.ru
# Author: Vitaly Lipatov <lav@etersoft.ru>
# Public domain

# TODO: rewrite with shell commands, perl or C
# Python - http://www.linuxtopia.org/online_books/programming_books/python_programming/python_ch16s03.html
# Shell  - http://linux.byexamples.com/archives/127/uniq-and-basic-set-theory/
#        - http://maiaco.com/articles/shellSetOperations.php
# Perl   - http://docstore.mik.ua/orelly/perl/cookbook/ch04_09.htm
#        - http://blogs.perl.org/users/polettix/2012/03/sets-operations.html
# http://rosettacode.org/wiki/Symmetric_difference
# TODO: add unit tests
# http://ru.wikipedia.org/wiki/Операции_над_множествами

# Base set operations:
# * union
#   "1 2 3" "3 4 5" -> "1 2 3 4 5"
# * intersection
#   "1 2 3" "3 4 5" -> "3"
# * relative complement (substracted, difference) ( A ? B – members in A but not in B )
# http://en.wikipedia.org/wiki/Complement_%28set_theory%29
#   "1 3" "1 2 3 4" -> "2 4"
# * symmetric difference (симметричная разность) ( A ^ B – members in A or B but not both )
# http://en.wikipedia.org/wiki/Symmetric_difference
#   "1 2 3" "3 4 5" -> "1 2 4 5"

filter_strip_spaces()
{
        # possible use just
        #xargs echo
        sed -e "s| \+| |g" -e "s|^ ||" -e "s| \$||"
}

strip_spaces()
{
        echo "$*" | filter_strip_spaces
}

isempty()
{
        [ "$(strip_spaces "$*")" = "" ]
}

list()
{
        local i
        for i in $@ ; do
                echo "$i"
        done
}

count()
{
         list $@ | wc -l
}

union()
{
         strip_spaces $(list $@ | sort -u)
}

uniq()
{
        union $@
}

has()
{
	local wd="$1"
	shift
	echo "$*" | grep -q -- "$wd"
}

# Note: used egrep! write '[0-9]+(first|two)', not '[0-9]\+...'
match()
{
	local wd="$1"
	shift
	echo "$*" | egrep -q -- "$wd"
}


# remove_from_list "1." "11 12 21 22" -> "21 22"
reg_remove()
{
        local i
        local RES=
        for i in $2 ; do
                echo "$i" | grep -q "$1" || RES="$RES $i"
        done
        strip_spaces "$RES"
}

# remove_from_list "1." "11 12 21 22" -> "21 22"
reg_wordremove()
{
        local i
        local RES=
        for i in $2 ; do
                echo "$i" | grep -q -w "$1" || RES="$RES $i"
        done
        strip_spaces "$RES"
}

# Args: LIST1 LIST2
# do_exclude_list print LIST2 list exclude fields contains also in LIST1
# Example: exclude "1 3" "1 2 3 4" -> "2 4"
exclude()
{
        local i
        local RES=
        for i in $2 ; do
                echo "$1" | grep -q -w "$i" || RES="$RES $i"
        done
        strip_spaces "$RES"
}

# regexclude_list "22 1." "11 12 21 22" -> "21"
reg_exclude()
{
        local i
        local RES="$2"
        for i in $1 ; do
                RES=$(reg_remove "$i" "$RES")
        done
        strip_spaces "$RES"
}

# regexclude_list "22 1." "11 12 21 22" -> "21"
reg_wordexclude()
{
        local i
        local RES="$2"
        for i in $1 ; do
                RES=$(reg_wordremove "$i" "$RES")
        done
        strip_spaces "$RES"
}

# FIXME:
# reg_include "1." "11 12 21 22" -> "11 12"
reg_include()
{
        local i
        local RES=
        for i in $2 ; do
                echo "$i" | grep -q -w "$1" && RES="$RES $i"
        done
        strip_spaces "$RES"
}

example()
{
        local CMD="$1"
        local ARG1="$2"
        shift 2
        echo "\$ $0 $CMD \"$ARG1\" \"$@\""
        $0 $CMD "$ARG1" "$@"
}

example_res()
{
	example "$@" && echo TRUE || echo FALSE
}

help()
{
        echo "estrlist developed for string list operations. See also cut, join, paste..."
        echo "Usage: $0 <command> [args]"
        echo "Commands:"
        echo "  strip_spaces [args]               - remove spaces between words"
        echo "  filter_strip_spaces               - remove spaces from words from standart input"
        echo "  reg_remove  <PATTERN> [word list] - remove words containing a match to the given PATTERN (grep notation)"
        echo "  reg_wordremove  <PATTERN> [word list] - remove words containing a match to the given PATTERN (grep -w notation)"
        echo "  exclude <list1> <list2>           - print list2 words exclude list1 items"
        echo "  reg_exclude <PATTERN> [word list] - print only words do not matched with PATTERN"
        echo "  reg_wordexclude <PATTERN> [word list] - print only words do not matched with PATTERN"
        echo "  has <PATTERN> string              - check the string for a match to the regular expression given in PATTERN (grep notation)"
        echo "  match <PATTERN> string            - check the string for a match to the regular expression given in PATTERN (egrep notation)"
        echo "  isempty [string]                  - true if string has no any symbols (only zero or more spaces)"
        echo "  union [word list]                 - sort and remove duplicates"
        echo "  uniq [word list]                  - alias for union"
        echo "  list [word list]                  - just list words line by line"
        echo "  count [word list]                 - print word count"
        echo
        echo "Examples:"
        example reg_remove "1." "11 12 21 22"
        example reg_wordremove "1." "11 12 21 22"
        example exclude "1 3" "1 2 3 4"
        example reg_exclude "22 1." "11 12 21 22"
        example reg_wordexclude "wo.* er" "work were more else"
        example union "1 2 2 3 3"
        example count "1 2 3 4 10"
        example_res isempty "  "
        #example_res isempty " 1 "
        example_res has ex "exactly"
        example_res has exo "exactly"
        example_res match "M[0-9]+" "M250"
        example_res match "M[0-9]+" "MI"
}

COMMAND="$1"
if [ -z "$COMMAND" ] ; then
        echo "Run with --help for get command description."
        exit 1
fi

if [ "$COMMAND" = "-h" ] || [ "$COMMAND" = "--help" ] ; then
        COMMAND="help"
fi

shift

# FIXME: do to call function directly, use case instead?
if [ "$1" = "-" ] ; then
    shift
    "$COMMAND" "$(cat) $@"
elif [ "$2" = "-" ] ; then
    "$COMMAND" "$1" "$(cat)"
else
    "$COMMAND" "$@"
fi
}
################# end of incorporated bin/tools_estrlist #################


################# incorporate bin/tools_json #################
internal_tools_json()
{

# License: MIT or Apache
# Homepage: http://github.com/dominictarr/JSON.sh

throw() {
  echo "$*" >&2
  exit 1
}

BRIEF=0
LEAFONLY=0
PRUNE=0
NO_HEAD=0
NORMALIZE_SOLIDUS=0

usage() {
  echo
  echo "Usage: JSON.sh [-b] [-l] [-p] [-s] [-h]"
  echo
  echo "-p - Prune empty. Exclude fields with empty values."
  echo "-l - Leaf only. Only show leaf nodes, which stops data duplication."
  echo "-b - Brief. Combines 'Leaf only' and 'Prune empty' options."
  echo "-n - No-head. Do not show nodes that have no path (lines that start with [])."
  echo "-s - Remove escaping of the solidus symbol (straight slash)."
  echo "-h - This help text."
  echo
}

parse_options() {
  set -- "$@"
  local ARGN=$#
  while [ "$ARGN" -ne 0 ]
  do
    case $1 in
      -h) usage
          exit 0
      ;;
      -b) BRIEF=1
          LEAFONLY=1
          PRUNE=1
      ;;
      -l) LEAFONLY=1
      ;;
      -p) PRUNE=1
      ;;
      -n) NO_HEAD=1
      ;;
      -s) NORMALIZE_SOLIDUS=1
      ;;
      ?*) echo "ERROR: Unknown option."
          usage
          exit 0
      ;;
    esac
    shift 1
    ARGN=$((ARGN-1))
  done
}

awk_egrep () {
  local pattern_string=$1

  gawk '{
    while ($0) {
      start=match($0, pattern);
      token=substr($0, start, RLENGTH);
      print token;
      $0=substr($0, start+RLENGTH);
    }
  }' pattern="$pattern_string"
}

tokenize () {
  local GREP
  local ESCAPE
  local CHAR

  if echo "test string" | egrep -ao --color=never "test" >/dev/null 2>&1
  then
    GREP='egrep -ao --color=never'
  else
    GREP='egrep -ao'
  fi

  if echo "test string" | egrep -o "test" >/dev/null 2>&1
  then
    ESCAPE='(\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})'
    CHAR='[^[:cntrl:]"\\]'
  else
    GREP=awk_egrep
    ESCAPE='(\\\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})'
    CHAR='[^[:cntrl:]"\\\\]'
  fi

  local STRING="\"$CHAR*($ESCAPE$CHAR*)*\""
  local NUMBER='-?(0|[1-9][0-9]*)([.][0-9]*)?([eE][+-]?[0-9]*)?'
  local KEYWORD='null|false|true'
  local SPACE='[[:space:]]+'

  # Force zsh to expand $A into multiple words
  local is_wordsplit_disabled=$(unsetopt 2>/dev/null | grep -c '^shwordsplit$')
  if [ $is_wordsplit_disabled != 0 ]; then setopt shwordsplit; fi
  $GREP "$STRING|$NUMBER|$KEYWORD|$SPACE|." | egrep -v "^$SPACE$"
  if [ $is_wordsplit_disabled != 0 ]; then unsetopt shwordsplit; fi
}

parse_array () {
  local index=0
  local ary=''
  read -r token
  case "$token" in
    ']') ;;
    *)
      while :
      do
        parse_value "$1" "$index"
        index=$((index+1))
        ary="$ary""$value" 
        read -r token
        case "$token" in
          ']') break ;;
          ',') ary="$ary," ;;
          *) throw "EXPECTED , or ] GOT ${token:-EOF}" ;;
        esac
        read -r token
      done
      ;;
  esac
  [ "$BRIEF" -eq 0 ] && value=$(printf '[%s]' "$ary") || value=
  :
}

parse_object () {
  local key
  local obj=''
  read -r token
  case "$token" in
    '}') ;;
    *)
      while :
      do
        case "$token" in
          '"'*'"') key=$token ;;
          *) throw "EXPECTED string GOT ${token:-EOF}" ;;
        esac
        read -r token
        case "$token" in
          ':') ;;
          *) throw "EXPECTED : GOT ${token:-EOF}" ;;
        esac
        read -r token
        parse_value "$1" "$key"
        obj="$obj$key:$value"        
        read -r token
        case "$token" in
          '}') break ;;
          ',') obj="$obj," ;;
          *) throw "EXPECTED , or } GOT ${token:-EOF}" ;;
        esac
        read -r token
      done
    ;;
  esac
  [ "$BRIEF" -eq 0 ] && value=$(printf '{%s}' "$obj") || value=
  :
}

parse_value () {
  local jpath="${1:+$1,}$2" isleaf=0 isempty=0 print=0
  case "$token" in
    '{') parse_object "$jpath" ;;
    '[') parse_array  "$jpath" ;;
    # At this point, the only valid single-character tokens are digits.
    ''|[!0-9]) throw "EXPECTED value GOT ${token:-EOF}" ;;
    *) value=$token
       # if asked, replace solidus ("\/") in json strings with normalized value: "/"
       [ "$NORMALIZE_SOLIDUS" -eq 1 ] && value=$(echo "$value" | sed 's#\\/#/#g')
       isleaf=1
       [ "$value" = '""' ] && isempty=1
       ;;
  esac
  [ "$value" = '' ] && return
  [ "$NO_HEAD" -eq 1 ] && [ -z "$jpath" ] && return

  [ "$LEAFONLY" -eq 0 ] && [ "$PRUNE" -eq 0 ] && print=1
  [ "$LEAFONLY" -eq 1 ] && [ "$isleaf" -eq 1 ] && [ $PRUNE -eq 0 ] && print=1
  [ "$LEAFONLY" -eq 0 ] && [ "$PRUNE" -eq 1 ] && [ "$isempty" -eq 0 ] && print=1
  [ "$LEAFONLY" -eq 1 ] && [ "$isleaf" -eq 1 ] && \
    [ $PRUNE -eq 1 ] && [ $isempty -eq 0 ] && print=1
  [ "$print" -eq 1 ] && printf "[%s]\t%s\n" "$jpath" "$value"
  :
}

parse () {
  read -r token
  parse_value
  read -r token
  case "$token" in
    '') ;;
    *) throw "EXPECTED EOF GOT $token" ;;
  esac
}

if ([ "$0" = "$BASH_SOURCE" ] || ! [ -n "$BASH_SOURCE" ]);
then
  parse_options "$@"
  tokenize | parse
fi

# vi: expandtab sw=2 ts=2
}
################# end of incorporated bin/tools_json #################


#PATH=$PATH:/sbin:/usr/sbin

set_pm_type

set_sudo

check_tty

#############################

phelp()
{
	echo "$Descr
$Usage
 Commands:
$(get_help HELPCMD)

 Options:
$(get_help HELPOPT)
"
}

print_version()
{
        echo "EPM package manager version 2.5.0"
        echo "Running on $($DISTRVENDOR) ('$PMTYPE' package manager uses '$PKGFORMAT' package format)"
        echo "Copyright (c) Etersoft 2012-2018"
        echo "This program may be freely redistributed under the terms of the GNU AGPLv3."
}


Usage="Usage: epm [options] <command> [package name(s), package files]..."
Descr="epm - EPM package manager"

EPMVERSION=2.5.0
verbose=
quiet=
nodeps=
noremove=
dryrun=
force=
repack=
scripts=
short=
direct=
sort=
non_interactive=
skip_installed=
show_command_only=
epm_cmd=
pkg_files=
pkg_dirs=
pkg_names=
pkg_urls=
quoted_args=

# load system wide config
[ -f /etc/eepm/eepm.conf ] && . /etc/eepm/eepm.conf


case $PROGNAME in
    epmi)
        epm_cmd=install
        ;;
    epmI)
        epm_cmd=Install
        ;;
    epme)
        epm_cmd=remove
        ;;
    epmcl)
        epm_cmd=changelog
        ;;
    epms)
        epm_cmd=search
        ;;
    epmsf)
        epm_cmd=search_file
        ;;
    epmq)
        epm_cmd=query
        ;;
    epmqi)
        epm_cmd=info
        ;;
    epmqf)
        epm_cmd=query_file
        ;;
    epmqa)
        epm_cmd=packages
        ;;
    epmqp)
        epm_cmd=query_package
        ;;
    epmql)
        epm_cmd=filelist
        ;;
    epmrl)
        epm_cmd=repolist
        ;;
    epmu)
        epm_cmd=update
        ;;
    epm|upm|eepm)
        ;;
    epm.sh)
        ;;
    *)
        # epm by default
        # fatal "Unknown command: $progname"
        ;;
esac

# was called with alias name
[ -n "$epm_cmd" ] && PROGNAME="epm"

check_command()
{
    # do not override command
    [ -z "$epm_cmd" ] || return

# Base commands
    case $1 in
    -i|install|add|i)         # HELPCMD: install package(s) from remote repositories or from local file
        epm_cmd=install
        ;;
    -e|-P|rm|del|remove|delete|uninstall|erase|e)  # HELPCMD: remove (delete) package(s) from the database and the system
        epm_cmd=remove
        ;;
    -s|search|s)                # HELPCMD: search in remote package repositories
        epm_cmd=search
        ;;
    -qp|qp|query_package)     # HELPCMD: search in the list of installed packages
        epm_cmd=query_package
        ;;
    -qf|qf|-S|which|belongs)     # HELPCMD: query package(s) owning file
        epm_cmd=query_file
        ;;

# Useful commands
    reinstall)                # HELPCMD: reinstall package(s) from remote repositories or from local file
        epm_cmd=reinstall
        ;;
    Install)                  # HELPCMD: perform update package repo info and install package(s) via install command
        epm_cmd=Install
        ;;
    -q|q|installed|query)     # HELPCMD: check presence of package(s) and print this name (also --short is supported)
        epm_cmd=query
        ;;
    -sf|sf|filesearch)        # HELPCMD: search in which package a file is included
        epm_cmd=search_file
        ;;
    -ql|ql|filelist)          # HELPCMD: print package file list
        epm_cmd=filelist
        ;;
    check|fix|verify)         # HELPCMD: check local package base integrity and fix it
        epm_cmd=check
        ;;
    -cl|cl|changelog)         # HELPCMD: show changelog for package
        epm_cmd=changelog
        ;;
    -qi|qi|info|show)         # HELPCMD: print package detail info
        epm_cmd=info
        ;;
    requires|deplist|req)     # HELPCMD: print package requires
        epm_cmd=requires
        ;;
    provides|prov)            # HELPCMD: print package provides
        epm_cmd=provides
        ;;
    whatdepends|wd)           # HELPCMD: print packages dependences on that
        epm_cmd=whatdepends
        ;;
    whatprovides)             # HELPCMD: print packages provides that target
        epm_cmd=whatprovides
        ;;
    conflicts)                # HELPCMD: print package conflicts
        epm_cmd=conflicts
        ;;
    -qa|qa|-l|list|packages)  # HELPCMD: print list of installed package(s)
        epm_cmd=packages
        ;;
    programs)                 # HELPCMD: print list of installed GUI program(s) (they have .desktop files)
        epm_cmd=programs
        ;;
    assure)                   # HELPCMD: <command> [package]: install package if command does not exist
        epm_cmd=assure
        ;;
    policy)                   # HELPCMD: print detailed information about the priority selection of package
        epm_cmd=policy
        ;;

# Repository control
    update)                   # HELPCMD: update remote package repository databases
        epm_cmd=update
        ;;
    addrepo|ar)               # HELPCMD: add package repo (etersoft, autoimports, archive 2017/12/31); run with param to get list
        epm_cmd=addrepo
        ;;
    repolist|sl|rl|listrepo|repo)  # HELPCMD: print repo list
        epm_cmd=repolist
        ;;
    repofix)                  # HELPCMD: fix paths in sources lists (ALT Linux only)
        epm_cmd=repofix
        ;;
    removerepo|rr)            # HELPCMD: remove package repo
        epm_cmd=removerepo
        ;;
    release-upgrade|upgrade-release)          # HELPCMD: update whole system to the release in arg (default: next (latest) release)
        epm_cmd=release_upgrade
        ;;
    kernel-update|kernel-upgrade|update-kernel|upgrade-kernel)      # HELPCMD: update system kernel to the last repo version
        epm_cmd=kernel_update
        ;;
    remove-old-kernels|remove-old-kernel)      # HELPCMD: remove old system kernels (exclude current or last two kernels)
        epm_cmd=remove_old_kernels
        ;;

# Other commands
    clean)                    # HELPCMD: clean local package cache
        epm_cmd=clean
        ;;
    autoremove|package-cleanup)   # HELPCMD: auto remove unneeded package(s) Supports args for ALT: [libs|python|perl|libs-devel]
        epm_cmd=autoremove
        ;;
    autoorphans|--orphans)    # HELPCMD: remove all packages not from the repository
        epm_cmd=autoorphans
        ;;
    upgrade|dist-upgrade)     # HELPCMD: performs upgrades of package software distributions
        epm_cmd=upgrade
        ;;
    Upgrade)                  # HELPCMD: force update package base, then run upgrade
        epm_cmd=Upgrade
        ;;
    downgrade)                # HELPCMD: downgrade [all] packages to the repo state
        epm_cmd=downgrade
        ;;
    download)                 # HELPCMD: download package(s) file to the current dir
        epm_cmd=download
        ;;
# TODO: replace with install --simulate
    simulate)                 # HELPCMD: simulate install with check requires
        epm_cmd=simulate
        ;;
    audit)                    # HELPCMD: audits installed packages against known vulnerabilities
        epm_cmd=audit
        ;;
    #checksystem)              # HELPCMD: check system for known errors (package management related)
    #    epm_cmd=checksystem
    #    ;;
    site|url)                 # HELPCMD: open package's site in a browser (use -p for open packages.altlinux.org site)
        epm_cmd=site
        ;;
    ei|ik|epminstall|epm-install|selfinstall) # HELPCMD: install package(s) from Korinf (eepm by default)
        epm_cmd=epm_install
        ;;
    print)                    # HELPCMD: print various info, run epm print help for details
        epm_cmd=print
        ;;
    repack)                   # HELPCMD: repack rpm to local compatibility
        epm_cmd=repack
        ;;
    -V|checkpkg|integrity)    # HELPCMD: check package file integrity (checksum)
        epm_cmd=checkpkg
        ;;
    -h|--help|help)           # HELPOPT: print this help
        help=1
        phelp
        exit 0
        ;;
    *)
        return 1
        ;;
    esac
    return 0
}

check_option()
{
    case $1 in
    -v|--version)         # HELPOPT: print version
        print_version
        exit 0
        ;;
    --verbose)            # HELPOPT: verbose mode
        verbose=1
        ;;
    --skip-installed)     # HELPOPT: skip already installed packages during install
        skip_installed=1
        ;;
    --show-command-only)  # HELPOPT: show command only, do not any action (supports install and remove ONLY)
        show_command_only=1
        ;;
    --quiet|--silent)     # HELPOPT: quiet mode (do not print commands before exec)
        quiet=1
        ;;
    --nodeps)             # HELPOPT: skip dependency check (during install/simulate and so on)
        nodeps="--nodeps"
        ;;
    --force)              # HELPOPT: force install/remove package (f.i., override)
        force="--force"
        ;;
    --noremove|--no-remove)  # HELPOPT: exit if any packages are to be removed during upgrade
        noremove="--no-remove"
        ;;
    --dry-run|--simulate|--just-print|-recon--no-act) # HELPOPT: print only (autoremove/autoorphans/remove only)
        dryrun="--dry-run"
        ;;
    --short)              # HELPOPT: short output (just 'package' instead 'package-version-release')
        short="--short"
        ;;
    --direct)              # HELPOPT: direct install package file from ftp (not via hilevel repository manager)
        direct="--direct"
        ;;
    --repack)              # HELPOPT: repack rpm package(s) before install
        repack="--repack"
        ;;
    --scripts)              # HELPOPT: include scripts in repacked rpm package(s) (see --repack or repacking when foreign package is installed)
        scripts="--scripts"
        ;;
    --sort)               # HELPOPT: sort output, f.i. --sort=size (supported only for packages command)
        # TODO: how to read arg?
        sort="$1"
        ;;
    --auto|--non-interactive)  # HELPOPT: non interactive mode
        non_interactive=1
        ;;
    *)
        return 1
        ;;
    esac
    return 0
}

check_filenames()
{
    local opt
    for opt in "$@" ; do
        # files can be with full path or have extension via .
        if [ -f "$opt" ] && echo "$opt" | grep -q "[/\.]" ; then
            pkg_files="$pkg_files $opt"
        elif [ -d "$opt" ] ; then
            pkg_dirs="$pkg_dirs $opt"
        elif echo "$opt" | grep -q "://" ; then
            pkg_urls="$pkg_urls $opt"
        else
            pkg_names="$pkg_names $opt"
        fi
        quoted_args="$quoted_args \"$opt\""
    done
}

FLAGENDOPTS=
for opt in "$@" ; do
    [ "$opt" = "--" ] && FLAGENDOPTS=1 && continue
    if [ -z "$FLAGENDOPTS" ] ; then
        check_command $opt && continue
        check_option $opt && continue
    fi
    # Note: will parse all params separately (no package names with spaces!)
    check_filenames $opt
done

# if input is not console and run script from file, get pkgs from stdin too
if ! inputisatty && [ -n "$PROGDIR" ] ; then
    for opt in $(withtimeout 1 cat) ; do
        # FIXME: do not work
        # workaround against # yes | epme
        [ "$opt" = "y" ] && break;
        [ "$opt" = "yes" ] && break;
        check_filenames $opt
    done
fi

pkg_files=$(strip_spaces "$pkg_files")
pkg_dirs=$(strip_spaces "$pkg_dirs")
# in common case dirs equals to names only suddenly
pkg_names=$(strip_spaces "$pkg_names $pkg_dirs")
pkg_urls=$(strip_spaces "$pkg_urls")

pkg_filenames=$(strip_spaces "$pkg_files $pkg_names")

# Just debug
#echover "command: $epm_cmd"
#echover "pkg_files=$pkg_files"
#echover "pkg_names=$pkg_names"

# Just printout help if run without args
if [ -z "$epm_cmd" ] ; then
    print_version
    echo
    fatstr="Unknown command in $* arg(s)"
    [ -n "$*" ] || fatstr="That program needs be running with some command"
    fatal "$fatstr. Run $ $PROGNAME --help to get help."
fi

# Use eatmydata for write specific operations
case $epm_cmd in
    update|upgrade|Upgrade|install|reinstall|Install|remove|autoremove|kernel_update|release_upgrade|check)
        set_eatmydata
        ;;
esac

# Run helper for command
epm_$epm_cmd
# return last error code (from subroutine)