# -*- coding: utf-8 -*-

# Copyright 2008-2016 Mir Calculate. http://www.calculate-linux.org
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

import sys
import os
import stat
import re
import importlib
from .utils.portage import getInstalledAtom, RepositoryPath, \
    searchProfile, RepositorySubstituting
from .cl_xml import xmlShare
from .utils.system import SystemPath, FUser, emerge_running
from functools import wraps
import random
import string
import time
import glob
import hashlib
import codecs
import uuid
from fnmatch import fnmatch
from math import sqrt
from itertools import *
from collections import OrderedDict
from operator import lt, le, eq, ne, ge, gt
import shutil
import errno
from .utils.common import (_error, _warning, getTupleVersion, getPortageUidGid,
                          isBootstrapDataOnly)
from .utils.text import _u
from .utils.portage import (isPkgInstalled, reVerSplitToPV, EmergeLog,
                           getInstalledAtom,
                           EmergeLogPackageTask, getPkgUses, RepositoryPath)
from .utils.content import PkgContents, checkContents, getCfgFiles, fillContents
from .utils.files import (getModeFile, listDirectory, removeDir, typeFile,
                         scanDirectory, FilesError, dir_sync, find, getProgPath,
                         pathJoin, readFile, readLinesFile, process)
from .utils.mount import Mounts
from .utils.tools import iterate_list, has_any, Locker
from .datavars import DataVarsError, VariableError, CriticalError, SimpleDataVars
from .configparser import (ConfigParser, NoSectionError,
                                        ParsingError)

from .cl_lang import setLocalTranslate, RegexpLocalization

_ = lambda x: x
setLocalTranslate('cl_lib3', sys.modules[__name__])

PORTAGEUID, PORTAGEGID = getPortageUidGid()


class TemplatesError(Exception):
    """
    Error on templates appling
    """


def catch_no_space_left(f):
    def wrapper(*args, **kw):
        try:
            return f(*args, **kw)
        except IOError as e:
            if e.errno == 28:
                raise TemplatesError(_("No space left on device"))
            raise

    return wrapper

def post_unlock_packages(f):
    def wrapper(self, *args, **kw):
        if not kw.get("rerun", True):
            return f(self, *args, **kw)
        else:
            try:
                return f(self, *args, **kw)
            except BaseException as e:
                raise
            finally:
                self.unlock_packages()
    return wrapper

def try_decode_utf8(_bytes):
    try:
        return _bytes.decode("UTF-8"), True
    except UnicodeDecodeError as e:
        return _bytes, False


class DataVarsConfig(SimpleDataVars):
    """
    Получить профиль и emerge config из chroot системы
    """
    def __init__(self, chroot_path='/'):
        from .variables import env
        SimpleDataVars.__init__(
            self,
            env.VariableClMakeProfile(systemRoot=chroot_path),
            env.VariableClEmergeConfig(systemRoot=chroot_path))

class LayeredIni():
    _baseDir = None
    objVar = None

    class IniPath():
        IniName = "ini.env"
        Grp = os.path.join('/var/lib/calculate/calculate-update', IniName)
        System = os.path.join('/var/lib/calculate/', IniName)
        Etc = os.path.join('/etc/calculate', IniName)
        Local = os.path.join('/var/calculate', IniName)
        Remote = os.path.join('/var/calculate/remote', IniName)
        Hardcode = os.path.join(RepositoryPath.CalculateProfiles, IniName)
        Work = System

    def __init__(self):
        # комплексное содержимое ini.env с приоритетом меньше, чем у
        # изменяемого
        self.lowerIni = None
        # комплексное содержимое ini.env с приоритетом выше, чем у
        # изменяемого
        self.upperIni = None

    def is_user(self):
        return self.objVar and self.objVar.Get('cl_action') == "desktop"

    def get_profile_path(self, dv):
        """
        Получить путь до системного профиля
        :param dv:
        :return:
        """
        if not dv:
            return ""
        try:
            make_profile = dv.Get('main.cl_make_profile')
            if os.path.exists(make_profile):
                profiledir = os.path.dirname(make_profile)
                return os.path.join(profiledir, os.readlink(make_profile))
            return ""
        except VariableError:
            return ""

    def get_profiles_inienv(self, dv):
        """
        Получить список ini.env находящихся в профиле с учётом их расположения
        в parent файлах
        :param dv:
        :return:
        """
        if dv:
            profile_path = self.get_profile_path(dv)
            if profile_path:
                repos = RepositorySubstituting(dv, self._baseDir)
                return list(searchProfile(
                    profile_path, self.IniPath.IniName, repository_sub=repos))
        return []

    def read_other_ini(self):
        """
        Прочитать все необходимые файлы env
        :return:
        """
        if not self.lowerIni:
            inifiles = self.get_profiles_inienv(self.objVar)
            inifiles.append(pathJoin(self._baseDir, self.IniPath.Grp))

            try:
                if (self.objVar and
                        (self.objVar.Get('core.ac_backup_restore') == 'on' or
                                 self.objVar.Get(
                                     'core.ac_backup_service') == 'on')):
                    backup_path = self.objVar.Get('cl_backup_ini_env')
                    inifiles.append(backup_path)
            except DataVarsError as e:
                pass
            #print "lower:", inifiles
            self.lowerIni = ConfigParser(strict=False)
            for inifn in inifiles:
                try:
                    self.lowerIni.read(inifn, encoding="utf-8")
                except ParsingError as e:
                    sys.stderr.write("%s\n" % str(e))
                    sys.stderr.flush()
        if not self.upperIni:
            inifiles = [self.IniPath.Etc,
                        self.IniPath.Local,
                        self.IniPath.Remote]
            if self.is_user():
                inifiles = [self.IniPath.Work] + inifiles
            inifiles = [pathJoin(self._baseDir, x) for x in inifiles]
            #print "upper:", inifiles
            self.upperIni = ConfigParser(strict=False)
            for inifn in inifiles:
                try:
                    self.upperIni.read(inifn, encoding="utf-8")
                except ParsingError as e:
                    sys.stderr.write("%s\n" % str(e))
                    sys.stderr.flush()


class SystemIni(LayeredIni):
    _inifile = LayeredIni.IniPath.Work

    @property
    def inifile(self):
        if self.objVar:
            return pathJoin(self.objVar.Get('cl_chroot_path'), self._inifile)
        else:
            return self._inifile

    def is_user(self):
        return False

    def __init__(self, dv=None):
        self.objVar = dv
        if dv:
            self._baseDir = self.objVar.Get('cl_chroot_path')
        else:
            self._baseDir = '/'
        super().__init__()
        self.config = ConfigParser(strict=False)
        try:
            self.config.read(self.inifile, encoding="utf-8")
        except ParsingError as e:
            sys.stderr.write("%s\n" % str(e))
            sys.stderr.flush()
        self.read_other_ini()

    def getVar(self, section, varname):
        value = self.upperIni.get(section, varname, raw=True, fallback=None)
        if value is None:
            value = self.config.get(section, varname, raw=True, fallback=None)
        if value is None:
            value = self.lowerIni.get(section, varname, raw=True, fallback="")
        return value

    def getKeys(self, section):
        skeys = []
        for iniobj in (self.upperIni, self.config, self.lowerIni):
            if iniobj.has_section(section):
                skeys.extend(list(iniobj[section].keys()))
        return list(sorted(list(set(skeys))))

    def delVar(self, section, varname):
        try:
            self.config.remove_option(section, varname)
            for section in (x for x 
                    in self.config.sections() if not self.config[x]):
                self.config.remove_section(section)
            self.__write()
        except NoSectionError:
            pass

    def __write(self):
        comment_block = "\n".join(takewhile(lambda x: x.startswith("#"),
                                            readLinesFile(self.inifile)))
        with open(self.inifile, 'w') as f:
            if comment_block:
                f.write(comment_block)
                f.write('\n\n')
            self.config.write(f)

    def setVar(self, section, var_dict):
        if not self.config.has_section(section):
            self.config.add_section(section)
        for k, v in var_dict.items():
            self.config.set(section, k, v)
        self.__write()


class _shareTemplate():
    """Общие аттрибуты для классов шаблонов"""
    # Метка начала переменной
    varStart = "#-"
    # Метка конца переменной
    varEnd = "-#"
    _deltVarStart = len(varStart)
    _deltVarEnd = len(varEnd)
    objVar = None
    _reVar = re.compile(
        "%s(?:[a-z0-9_]+\.)?[a-zA-Z0-9_-]+%s" % (varStart, varEnd), re.M)

    def applyVarsTemplate(self, textTemplate, nameTemplate):
        """ Заменяет переменные на их значения
        """
        resS = self._reVar.search(textTemplate)
        textTemplateTmp = textTemplate
        while resS:
            mark = textTemplateTmp[resS.start():resS.end()]
            varName = mark[self._deltVarStart:-self._deltVarEnd]
            try:
                t = self.objVar.getInfo(varName).type
                if "list" in t:
                    varValue = self.objVar.serialize(t,
                                                     self.objVar.Get(varName))
                else:
                    varValue = self.objVar.Get(varName)
                if not varValue:
                    varValue = ""
                else:
                    varValue = str(varValue)
            except DataVarsError as e:
                raise TemplatesError(_("error in template %s") % nameTemplate
                                     + "\n" + str(e))
            textTemplateTmp = textTemplateTmp.replace(mark, varValue)
            resS = self._reVar.search(textTemplateTmp)
        return textTemplateTmp

    def getDataUser(self, groupsInfo=False):
        """Получить информацию о пользователе"""
        userName = self.objVar.Get("ur_login")
        if not userName:
            userName = "root"
        import pwd

        try:
            pwdObj = pwd.getpwnam(userName)
            uid = pwdObj.pw_uid
            gid = pwdObj.pw_gid
            homeDir = self.objVar.Get('ur_home_path')
        except Exception:
            raise TemplatesError(_("User %s not found") % str(userName))
        if groupsInfo:
            import grp

            try:
                groupName = grp.getgrgid(gid).gr_name
            except Exception:
                raise TemplatesError(_("Group ID %s not found") % str(gid))
            groupsNames = [x.gr_name for x in grp.getgrall() if userName in x.gr_mem]
            groupsNames = [groupName] + groupsNames
            return uid, gid, homeDir, groupsNames
        return uid, gid, homeDir


class _shareTermsFunction():
    """Общие аттрибуты для классов _terms и templateFunctions"""
    # Символы допустимые в скобках функции шаблона
    _reFunctionArgvInSquareBrackets = (
        "a-zA-Z0-9_:;%@<>=\!\|\{\}\^\$\?\(\)\[\]\-"
        "\n\+\,\*\/\.\'\"~\\\\ ")
    _reFunctionArgvText = "[%s]" % _reFunctionArgvInSquareBrackets
    # регулярное выражение для поиска функции в шаблоне
    _reFunctionText = ("([a-zA-Z0-9\_-]+)\(((?:#-|-#|%s)+|)\)" %
                       _reFunctionArgvText)


class _terms(_error, _shareTermsFunction, _shareTemplate):
    """Вычисление условий применяемых в шаблонах

    """
    # регулярное выражение для поиска функции в шаблоне
    _reFunction = re.compile(_shareTermsFunction._reFunctionText)
    # регулярное выражение для не версии
    _re_not_Version = re.compile("[^0-9\.]")
    # регулярное выражение не номер
    _re_not_Number = re.compile("[^0-9]")
    _suffixDict = {"pre": -2, "p": 0, "alpha": -4, "beta": -3, "rc": -1}
    _lenSuffixDict = len(_suffixDict)
    # Регулярное выражение для названия переменной
    _reRightName = re.compile("^(?:[a-z_\-]+\.)?(?:[a-zA-Z0-9_\-]+)$")
    # Регулярное выражение для сравниваемого значения
    _reDenyValue = re.compile("[^0-9a-zA-Z_/\.,-]")
    # латинские буквы в нижнем регистре
    _letters = list(string.ascii_lowercase)

    # использует из других объектов
    objVar = None

    def _splitVersion(self, strVersion):
        """
        Split version. Version, addition letter, list suffixes with version,
        revision.
        Examples:
          3.0.0_beta2
            ("3.0.0_beta2","",[],"")
          3.0.0_beta2-r1
            ("3.0.0_beta2","",[],"r1")
          3.0.0_beta2a-r1
            ("3.0.0_beta2","a",[],"r1")
          3.0.0_beta2a_rc1-r1
            ("3.0.0_beta2","a",[("rc","1")],"r1")
          3.0.0_beta2a_rc1_p20111212-r1 
            ("3.0.0_beta2","a",[("rc1","1"),("p","20111212")],"r1")
        """
        # get revision from version
        strWorkVersion, spl, rVersion = strVersion.rpartition("-")
        if rVersion == strVersion:
            strWorkVersion = rVersion
            rVersion = ""

        suffixes = []
        # get suffixes from version
        while "_" in strWorkVersion:
            # 2.3_p45 ('2.3','_','p43')
            # 2.3_rc4_p45 ('2.3_rc4','_','p43')
            strWorkVersion, spl, suffix = strWorkVersion.rpartition("_")
            suffSplList = [x for x in self._suffixDict.keys() if suffix.startswith(x)]
            if suffSplList:
                suffSpl = suffSplList[0]
                lenSuffSpl = len(suffSpl)
                suffixVersion = suffix[lenSuffSpl:]
                suffixes.append((suffSpl, suffixVersion))
        letters = ""
        numberVersion = strWorkVersion
        if numberVersion and numberVersion[-1:] in self._letters:
            letters = numberVersion[-1:]
            numberVersion = numberVersion[:-1]
        return numberVersion, letters, suffixes, rVersion

    def _isVersion(self, strVersion):
        """strVersion is not version - True"""
        numberVersion, letters, suffixes, rVersion = \
            self._splitVersion(strVersion)
        if not numberVersion.strip():
            return False
        if self._re_not_Version.search(numberVersion):
            return False
        if letters and letters not in self._letters:
            return False
        for suffix, suffixVersion in suffixes:
            if suffixVersion and self._re_not_Number.search(suffixVersion):
                return False
        if rVersion:
            if rVersion[0] != "r" or len(rVersion) == 1:
                return False
            if self._re_not_Number.search(rVersion[1:]):
                return False
        return True

    def _isIntervalVersion(self, strVersion):
        if "," in strVersion and strVersion.count(',') == 1:
            version1, op, version2 = strVersion.partition(",")
            return self._isVersion(version1) and self._isVersion(version2)
        return False

    def _convertVers(self, verA, verB):
        """Конвертирование номеров версий для корректного сравнения
        """

        def fillZero(elemA, elemB):
            # elemA, elemB = elemA[], elemB[]
            if len(elemA) > len(elemB):
                maxElemB = len(elemB) - 1
                for i in range(len(elemA)):
                    if i > maxElemB:
                        elemB.append("0")
            else:
                maxElemA = len(elemA) - 1
                for i in range(len(elemB)):
                    if i > maxElemA:
                        elemA.append("0")
            for i in range(len(elemB)):
                lenA = len(elemA[i])
                lenB = len(elemB[i])
                if lenA == lenB:
                    pass
                elif lenA > lenB:
                    res = lenA - lenB
                    for z in range(res):
                        elemB[i] = "0" + elemB[i]
                elif lenB > lenA:
                    res = lenB - lenA
                    for z in range(res):
                        elemA[i] = "0" + elemA[i]

        def fillSuffix(elemA, elemB, sA, svA, sB, svB):
            if str(sA) or str(sB):
                svA, svB = [[x] if x else ['0'] for x in (svA, svB)]
                fillZero(svA, svB)
                sA, sB = [x if x else 0 for x in (sA, sB)]
                elemA.append(str(self._lenSuffixDict + sA))
                elemA.extend(svA)
                elemB.append(str(self._lenSuffixDict + sB))
                elemB.extend(svB)

        # Version, letters, suffix, suffixVersion, rVersion
        vA, lA, ssA, rvA = self._splitVersion(verA)
        vB, lB, ssB, rvB = self._splitVersion(verB)
        elemA = vA.split(".")
        elemB = vB.split(".")
        fillZero(elemA, elemB)
        if lA or lB:
            lA, lB = [x if x else '0' for x in (lA, lB)]
            elemA.append(lA)
            elemB.append(lB)

        # dereferencing suffix in suffixes list
        ssA = [(self._suffixDict.get(x[0], 0), x[1]) for x in ssA]
        ssB = [(self._suffixDict.get(x[0], 0), x[1]) for x in ssB]
        for suffix, sufVer in reversed(ssA):
            if ssB:
                sB, svB = ssB.pop()
            else:
                sB, svB = "", ""
            fillSuffix(elemA, elemB, suffix, sufVer, sB, svB)
        while ssB:
            sB, svB = ssB.pop()
            fillSuffix(elemA, elemB, "", "", sB, svB)

        if rvA or rvB:
            rvA, rvB = [[x[1:]] for x in (rvA, rvB)]
            fillZero(rvA, rvB)
            elemA += rvA
            elemB += rvB
        return ".".join(elemA), ".".join(elemB)

    def _checkInterval(self, val, op, interval):
        ver1, ver2 = interval.split(',')

        val1, ver1 = self._convertVers(val, ver1)
        val2, ver2 = self._convertVers(val, ver2)

        comparator = {
            '==': lambda a,b,c,d: a>=b and c <= d,
            '!=': lambda a,b,c,d: a<b or c > d,
            '<=': lambda a,b,c,d: a>b and c <= d,
            '<>': lambda a,b,c,d: a>b and c < d,
            '=>': lambda a,b,c,d: a>=b and c < d
        }
        if op not in comparator:
            raise TemplatesError(_("Wrong interval operator"))

        return comparator[op](val1, ver1, val2, ver2)

    def _equalTerm(self, term, textError, function=None):
        """Вычисление логических выражений для условий

        Для корректной работы в классе который наследует этот класс
        должен быть объявлен аттрибут self.objVar
        (объект для работы с переменными)
        function - функция для  для обработки функций в заголовке блока
        """
        rpl = lambda x: x.replace("@@", " ")
        trm = {"&&": "@@and@@", "||": "@@or@@"}
        dictRuleFunc = OrderedDict((("==", eq), ("!=", ne),
                                    (">=", ge),
                                    ("<=", le),
                                    ("<>", ne), ("=>", ge),
                                    (">", gt), ("<", lt),
                                    ))
        rule = dictRuleFunc.keys()
        listEqual = []
        for k in trm.keys():
            if k in term:
                term = term.replace(k, trm[k])
        trs = term.split("@@")
        listSplitOr = []
        if "or" in trs:
            lst = []
            for t in trs:
                if t != "or":
                    lst.append(t)
                else:
                    listSplitOr.append(lst)
                    lst = []
            if lst:
                listSplitOr.append(lst)
        else:
            listSplitOr = [trs]
        for trsAnd in listSplitOr:
            listEqual = []
            for t in trsAnd:
                def search_rule(t, rule, prefix=""):
                    for sepF in rule:
                        if sepF in t:
                            vals = list(t.partition(sepF)[::2])
                            if vals[0].endswith("\\"):
                                return search_rule(vals[1], rule,
                                                   prefix="%s%s%s" % (
                                                       prefix, vals[0], sepF))
                            return True, sepF, ["%s%s" % (prefix, vals[0]),
                                                vals[1]]
                    return False, None, []

                flagRule, sepF, vals = search_rule(t, rule)
                if flagRule:
                    # проверка на допустимость названия переменной
                    flagFunction = False
                    if not self._reRightName.search(vals[0]):
                        # проверка на допустимость функции
                        flagError = True
                        if callable(function):            
                            searchFunct = self._reFunction.search(vals[0])
                            if searchFunct:
                                flagError = False
                                flagFunction = True
                        if flagError:
                            self.setError(
                                "'%s'" % rpl(term) + " " + _("incorrect"))
                            self.setError(textError)
                            return False
                    # проверка на допустимость значения
                    try:
                        if "#-" in vals[1]:
                            vals[1] = self.applyVarsTemplate(vals[1], "")
                            vals[1] = function(vals[1])
                    except TemplatesError:
                        pass
                    if self._reDenyValue.search(vals[1]):
                        self.setError("'%s'" % rpl(term) + " " + _("incorrect"))
                        self.setError(textError)
                        return False
                    flagIntTypeVar = None
                    if flagFunction and callable(function):
                        valVars = function("#-%s-#" % vals[0])
                        if valVars is False:
                            self.setError(
                                "'%s'" % rpl(term) + " " + _("incorrect"))
                            self.setError(textError)
                            return False
                        if "load" == searchFunct.group(1) and \
                                re.search("\(\s*num\s*,", vals[0]):
                            if valVars:
                                try:
                                    valVars = int(valVars)
                                except ValueError:
                                    self.setError("'%s'" % rpl(term) + " " +
                                                  _("incorrect"))
                                    self.setError(textError)
                                    return False
                                flagIntTypeVar = True
                            else:
                                flagIntTypeVar = False
                        else:
                            if valVars == "" and \
                                    (self._isVersion(vals[1]) or
                                     self._isIntervalVersion(vals[1])):
                                valVars = "0"
                            elif vals[1] == "" and self._isVersion(valVars):
                                vals[1] = "0"
                    else:
                        try:

                            valVars = self.objVar.Get(vals[0])
                            varTable = self.objVar.Get('cl_used_action')
                            varTable.append((vals[0], vals[1]))
                            if not valVars:
                                valVars = ""
                        except DataVarsError as e:
                            raise TemplatesError("{header}\n{body}".format(
                                header=textError, body=str(e)))
                    # Номера версий для ini
                    flagNotIniFunct = True
                    # Два значения не пусты
                    flagNotEmptyVals = not (valVars == "" and vals[1] == "")
                    if flagFunction and flagNotEmptyVals and \
                                    searchFunct.group(1) == "ini":
                        # Проверка значения на версию
                        if self._isVersion(valVars) and \
                                self._isVersion(vals[1]):
                            verFile, verVar = self._convertVers(vals[1],
                                                                valVars)
                            res = dictRuleFunc[sepF](verVar, verFile)
                            if res:
                                listEqual.append(True)
                            else:
                                listEqual.append(False)
                                break
                            flagNotIniFunct = False
                        if self._isVersion(valVars) and \
                                self._isIntervalVersion(vals[1]):
                            res = False
                            try:
                                res = self._checkInterval(
                                    valVars, sepF, vals[1])
                            except TemplatesError:
                                self.setError("'%s'" % rpl(term) + " " + \
                                              _("incorrect"))
                                self.setError(
                                    _("Wrong interval operator"))
                            if res:
                                listEqual.append(True)
                            else:
                                listEqual.append(False)
                                break
                            flagNotIniFunct = False
                    # Cравниваем номера версий
                    if flagNotIniFunct:
                        if flagNotEmptyVals and \
                                ("_ver" in vals[0] or
                                     (flagFunction and searchFunct.group(
                                         1) in
                                         ("pkg", "merge", "mergepkg")) or
                                     (flagFunction and searchFunct.group(
                                         1) == "load" and
                                          re.search("\(\s*ver\s*,",
                                                    vals[0]))):
                            # Проверка значения на версию (или интервал)
                            if (not self._isVersion(vals[1]) and
                                    not self._isIntervalVersion(vals[1])):
                                self.setError("'%s'" % rpl(term) + " " + \
                                              _("incorrect"))
                                self.setError(
                                    _("This value is not a version"))
                                return False
                            # Проверка значения функции на версию
                            if not self._isVersion(valVars):
                                self.setError("'%s'" % rpl(term) + " " + \
                                              _("incorrect"))
                                self.setError(
                                    _("The function value is not a version"))
                                return False
                            if self._isIntervalVersion(vals[1]):
                                res = False
                                try:
                                    res = self._checkInterval(
                                        valVars, sepF, vals[1])
                                except TemplatesError:
                                    self.setError("'%s'" % rpl(term) + " " + \
                                                  _("incorrect"))
                                    self.setError(
                                        _("Wrong interval operator"))
                            else:
                                verFile, verVar = self._convertVers(vals[1],
                                                                    valVars)
                                res = dictRuleFunc[sepF](verVar, verFile)
                            if res:
                                listEqual.append(True)
                            else:
                                listEqual.append(False)
                                break
                        else:
                            if flagIntTypeVar is None:
                                flagIntTypeVar = True
                                try:
                                    valVars = int(valVars)
                                except (TypeError, ValueError):
                                    flagIntTypeVar = False
                            if flagIntTypeVar:
                                if not vals[1].strip():
                                    vals[1] = 0
                                try:
                                    valFile = int(vals[1])
                                    valVar = valVars
                                    res = dictRuleFunc[sepF](valVar, valFile)
                                    if res:
                                        listEqual.append(True)
                                    else:
                                        listEqual.append(False)
                                        break
                                except ValueError:
                                    flagIntTypeVar = False
                            if not flagIntTypeVar:
                                if sepF == "!=" or sepF == "==":
                                    if not vals[1].strip():
                                        vals[1] = ""
                                    valFile = vals[1]
                                    valVar = valVars
                                    res = dictRuleFunc[sepF](valVar, valFile)
                                    if res:
                                        listEqual.append(True)
                                    else:
                                        listEqual.append(False)
                                        break
                                else:
                                    if not flagNotEmptyVals:
                                        listEqual.append(False)
                                        break
                                    else:
                                        self.setError("'%s'" % rpl(term) + " " \
                                                      + _("incorrect"))
                                        self.setError(textError)
                                        return False
                else:
                    if t == "and":
                        if listEqual == [] or False in listEqual:
                            listEqual = [False]
                            break
                        else:
                            listEqual = [True]
                    else:
                        self.setError("'%s'" % rpl(term) + " " + _("incorrect"))
                        self.setError(textError)
                        return False
            if not (listEqual == [] or False in listEqual):
                break
        if listEqual == [] or False in listEqual:
            return False
        return True

    def splitParLine(self, linePar):
        """
        Split params line
        """

        def splitQuote(listPar, quoteSymbol):
            listTerm = [x + quoteSymbol for x in ("=", ">", "<")]
            flagQ = False
            mass = []
            v = ""
            for i in listPar:
                if i.count(quoteSymbol) == 1:
                    if flagQ and i.endswith(quoteSymbol):
                        v = v + " " + i
                        mass.append(v)
                        v = ""
                        flagQ = False
                    elif [x for x in listTerm if x in i]:
                        flagQ = True
                        v = i
                    else:
                        mass.append(i)
                elif flagQ:
                    v = v + " " + i
                else:
                    mass.append(i)
            foundPar = list(set(mass) - set(listPar))
            return not flagQ, [x for x in mass if not x in foundPar], foundPar

        listPar = re.split("\s+", linePar)
        flagFoundQ = "'" in linePar
        flagFoundQQ = '"' in linePar
        if flagFoundQ and flagFoundQQ:
            flagQ, listSplQPar, listFoundQPar = splitQuote(listPar, "'")
            if flagQ:
                flagQQ, listSplQQPar, listFoundQQPar = splitQuote(listSplQPar, '"')
                if flagQQ:
                    listPar = listSplQQPar + listFoundQPar + listFoundQQPar
        elif flagFoundQQ:
            flagQQ, listSplQQPar, listFoundQQPar = splitQuote(listPar, '"')
            if flagQQ:
                listPar = listSplQQPar + listFoundQQPar
        elif flagFoundQ:
            flagQ, listSplQPar, listFoundQPar = splitQuote(listPar, "'")
            if flagQ:
                listPar = listSplQPar + listFoundQPar
        if flagFoundQ:
            listQPar = []
            for par in listPar:
                if par.endswith("'") and par.count("'") > 1:
                    listQPar.append(par[:-1].replace("='", "="))
                else:
                    listQPar.append(par)
            listPar = listQPar
        if flagFoundQQ:
            listQQPar = []
            for par in listPar:
                if par.endswith('"') and par.count('"') > 1:
                    listQQPar.append(par[:-1].replace('="', '='))
                else:
                    listQQPar.append(par)
            listPar = listQQPar
        return listPar


class HParams():
    Format = "format"
    DotAll = "dotall"
    Multiline = "multiline"
    Comment = "comment"
    Append = "append"
    Force = "force"
    DConf = "dconf"
    Convert = "convert"
    Link = "link"
    DirectoryLink = Link
    Mirror = "mirror"
    Symbolic = "symbolic"
    ChangeMode = "chmod"
    ChangeOwner = "chown"
    Name = "name"
    Path = "path"
    Autoupdate = "autoupdate"
    Protected = "protected"
    RunNow = "run"
    RunPost = "exec"
    Merge = "merge"
    PostMerge = "postmerge"
    Module = "module"
    Environ = "env"
    RestartService = "restart"
    StartService = "start"
    StopService = "stop"
    Rebuild = "rebuild"
    Stretch = "stretch"

    ServiceControl = (StopService, StartService, RestartService)

    _Single = (DotAll, Multiline, Force, Mirror, Symbolic, Autoupdate,
               Protected, Stretch)

    class AppendParams():
        Join = "join"
        Before = "before"
        After = "after"
        Replace = "replace"
        Remove = "remove"
        Skip = "skip"
        Patch = "patch"
        Clear = "clear"
        LinkDirCompatible = (Replace, Join)

    class ActionType():
        Merge = "merge"
        Patch = "patch"
        Profile = "profile"

    class ExecuteType():
        Now = "run"
        Post = "exec"

    class Formats():
        Executable = ("diff", "dconf", "ldif", "contents", "sqlite",
                      "backgrounds")
        Meta = ("backgrounds",)
        Modificator = ("sqlite",)

    class OptDir():
        Path = "path"
        Skip = "skip"
        Autoupdate = "autoupdate"

    @classmethod
    def single(cls, it):
        return [x for x in it if x in cls._Single]


class fileHeader(HParams, _terms):
    """Обработка заголовков шаблонов и конфигурационных файлов

    """
    # Допустимые параметры заголовка
    allowParam = (
        HParams.Format, HParams.DotAll, HParams.Multiline, HParams.Comment,
        HParams.Append, HParams.Force, HParams.DConf, HParams.Convert,
        HParams.Link, HParams.Mirror, HParams.Symbolic, HParams.Stretch,
        HParams.ChangeMode, HParams.ChangeOwner,
        HParams.Name, HParams.Path, HParams.Autoupdate,
        HParams.Protected, HParams.RunNow, HParams.RunPost,
        HParams.Merge, HParams.PostMerge, HParams.Module, HParams.Environ,
        HParams.RestartService, HParams.StartService, HParams.StopService,
        HParams.Rebuild
    )

    # Тип шаблона
    fileType = ""
    # Тип вставки шаблона
    typeAppend = ""
    # Возможные типы вставки шаблонов
    _fileAppend = (
        HParams.AppendParams.Join,
        HParams.AppendParams.Before,
        HParams.AppendParams.After,
        HParams.AppendParams.Replace,
        HParams.AppendParams.Remove,
        HParams.AppendParams.Skip,
        HParams.AppendParams.Patch,
        HParams.AppendParams.Clear)

    # Интерпретатор (#!/bin/bash) (#!/usr/bin/python)
    execStr = ""
    # Символ комментария
    comment = False
    # Выражение для поиска строки интерпретатора
    reExecStr = re.compile("^(#!/.+[^#]\s)", re.M)
    # условные операторы
    terms = ('>', '<', '==', '!=', '>=', '<=', '<>', '=>')
    # параметры без значения
    listParNotVal = HParams.single(allowParam)
    # Результат вычисления условия в заголовке
    headerTerm = True

    def __init__(self, templateName, text, comment=None, fileType=False,
                 objVar=False, function=None, templateObj=None):
        self.body = text
        # Объект с переменными
        self.objVar = objVar
        # Параметры описанные в заголовке файла шаблона
        self.params = {}
        # некорректные параметры
        incorrectParams = []
        used_params = []
        # Поиск строки запустка  (#!/bin/bash и.т. д)
        if comment or fileType != "bin":
            reExecRes = self.reExecStr.search(self.body)
            if reExecRes:
                self.execStr = self.body[reExecRes.start():reExecRes.end()]
                self.body = self.body[:reExecRes.start()] + \
                            self.body[reExecRes.end():]
        # Удаление Заголовка Calculate
        if comment:
            titleFirst = "Modified"
            # В случае текста XML
            if isinstance(comment, tuple) and len(comment) == 2:
                reCalcHeader = \
                    re.compile("\s*%s\s+%s.+\s+(.+\n)+%s\s?" \
                               % (comment[0], titleFirst, comment[1]),
                               re.M | re.I)
                reS = reCalcHeader.search(self.body)
                if reS:
                    self.body = self.body[:reS.start()] + self.body[reS.end():]
            else:
                reCalcHeader = re.compile(
                    "\s*%s\-+\s+%s\s+%s.+\s+(%s.+\s+)+%s\-+\s?" \
                    % (comment, comment, titleFirst, comment, comment),
                    re.M | re.I)
                reS = reCalcHeader.search(self.body)
                if reS:
                    self.body = self.body[reS.end():]
        if fileType is not False:
            if fileType == "bin":
                self.params[HParams.Format] = fileType
                self.fileType = self._getType()
                self.typeAppend = self._getAppend()
            else:
                textLines = self.body.splitlines()
                if textLines:
                    textLine = textLines[0]
                    #py3 regex problems
                    rePar = re.compile(
                        "\s*#\s*calculate\s+\\\\?|\s*#\s*calculate\\\\?$", re.I)
                    reP = rePar.search(textLine)
                    if reP:
                        reLns = re.compile(r"\A([^\\\n]*\\\n)+[^\n]*\n*", re.M)
                        reLs = reLns.search(self.body)
                        if reLs:
                            reL = reLs
                            paramLine = self.body[reP.end():reLs.end()]
                            paramLine = paramLine.replace("\\", " ")
                        else:
                            reLn = re.compile("\n")
                            reL = reLn.search(self.body)
                            paramLine = textLine[reP.end():]
                        if reL:
                            self.body = self.body[reL.end():]
                        else:
                            self.body = ""
                        paramList = self.splitParLine(paramLine)
                        if paramList:
                            for i in paramList:
                                for term in self.terms:
                                    if term in i:
                                        if self.headerTerm:
                                            errorMsg = (
                                                _("Incorrect template") +
                                                _(": ") + templateName + "\n" +
                                                _("template header not valid") +
                                                _(": ") + i)
                                            if function:
                                                rezTerm = self._equalTerm(
                                                    i, errorMsg, function)
                                            else:
                                                rezTerm = self._equalTerm(
                                                    i, errorMsg)
                                            if not rezTerm:
                                                self.headerTerm = False
                                        break
                                else:
                                    par = i.split("=")
                                    if len(par) == 1:
                                        if i in self.listParNotVal:
                                            self.params[i] = "True"
                                            used_params.append(i)
                                        else:
                                            if i.strip():
                                                incorrectParams = {i}
                                    elif len(par) == 2:
                                        par[1] = self.applyVarsTemplate(
                                            par[1], "")
                                        par[1] = templateObj.applyFuncTemplate(
                                            par[1], templateName)
                                        self.params[par[0]] = par[1]
                                        used_params.append(par[0])
                                        if par[0] == HParams.Environ:
                                            try:
                                                importlib.import_module(
                                                    "calculate.%s.variables"
                                                    % par[1])
                                            except (ImportError,
                                                    AttributeError):
                                                self.headerTerm = False
                    self.comment = self._getComment()
                self.fileType = self._getType()
                typeAppend = self._getAppend()
                if typeAppend:
                    self.typeAppend = typeAppend
                else:
                    self.headerTerm = False
                    self.setError(
                        _("incorrect header parameter: '%s'")
                        % "%s=%s" % (HParams.Append,
                                     self.params[HParams.Append]))
        if any(x in self.params for x in (HParams.RunPost,
                                          HParams.RunNow)):
            if HParams.RunPost in self.params:
                self.execStr = "#!%s\n" % self.params[HParams.RunPost]
            if HParams.RunNow in self.params:
                self.execStr = "#!%s\n" % self.params[HParams.RunNow]
            if "python" in self.execStr:
                self.execStr += "# -*- coding: utf-8 -*-\n"

        double_params = list(set([x for x in used_params
                                  if used_params.count(x) > 1]))
        if double_params:
            self.headerTerm = False
            self.setError(_("redefine header parameter: '%s'")
                          % " ".join(double_params))

        if not incorrectParams and self.params:
            incorrectParams = set(self.params.keys()) - set(self.allowParam)
        if incorrectParams:
            self.headerTerm = False
            self.setError(_("incorrect header parameter: '%s'") \
                          % " ".join(list(incorrectParams)))

    def _getType(self):
        """Выдать тип файла"""
        return self.params.get(HParams.Format, "raw")

    def _getAppend(self):
        """Выдать тип добавления файла"""
        if HParams.Append in self.params:
            if self.params[HParams.Append] in self._fileAppend:
                return self.params[HParams.Append]
            else:
                return False
        else:
            if self.fileType != "raw" and self.fileType != "bin" and \
                            self.fileType != "":
                if (HParams.Format in self.params and
                            self.params[HParams.Format] in
                            chain(("patch",), HParams.Formats.Executable)):
                    self.params[HParams.Append] = HParams.AppendParams.Patch
                else:
                    self.params[HParams.Append] = HParams.AppendParams.Join
            else:
                self.params[HParams.Append] = HParams.AppendParams.Replace
            return self.params[HParams.Append]

    def _getComment(self):
        """Выдать символ комментария файла"""
        if HParams.Comment in self.params:
            if self.params[HParams.Comment] in ("xml", "XML"):
                return "<!--", "-->"
            else:
                return self.params[HParams.Comment]
        else:
            return False


class dirHeader(HParams, _terms):
    """Обработка заголовков шаблонов директорий

    """
    # Допустимые параметры заголовка
    allowParam = (
        HParams.Append, HParams.ChangeMode,
        HParams.ChangeOwner, HParams.Name,
        HParams.Path, HParams.Autoupdate,
        HParams.Module, HParams.Environ,
        HParams.Merge, HParams.PostMerge,
        HParams.Rebuild,
        HParams.RestartService, HParams.StartService, HParams.StopService,
        HParams.DirectoryLink
    )

    # Тип вставки шаблона
    typeAppend = ""

    # Возможные типы вставки шаблонов
    _fileAppend = (
        HParams.AppendParams.Join,
        HParams.AppendParams.Remove,
        HParams.AppendParams.Skip,
        HParams.AppendParams.Clear,
        HParams.AppendParams.Replace
    )

    # условные операторы
    terms = ('>', '<', '==', '!=', '>=', '<=', '<>', '=>')

    # параметры без значения
    listParNotVal = (HParams.Symbolic, HParams.Force,
                     HParams.Autoupdate)

    # Результат вычисления условия в заголовке
    headerTerm = True

    def __init__(self, templateName, text, objVar=False, function=None,
                 templateObj=None):
        self.body = text
        # Объект с переменными
        self.objVar = objVar
        # Параметры описанные в заголовке файла шаблона
        self.params = {}
        # некорректные параметры
        incorrectParams = set([])
        used_params = []

        textLines = text.splitlines()
        flagErrorBody = False
        if textLines:
            textLine = textLines[0]
            #py3 regex problems
            rePar = re.compile(
                "\s*#\s*calculate\s+\\\\?|\s*#\s*calculate\\\\?$", re.I)
            reP = rePar.search(textLine)
            if reP:
                reLns = re.compile(r"\A([^\\\n]*\\\n)+[^\n]*\n*", re.M)
                reLs = reLns.search(text)
                if reLs:
                    reL = reLs
                    paramLine = text[reP.end():reLs.end()]
                    paramLine = paramLine.replace("\\", " ")
                else:
                    reLn = re.compile("\n")
                    reL = reLn.search(text)
                    paramLine = textLine[reP.end():]
                if reL:
                    self.body = text[reL.end():]
                else:
                    self.body = ""
                if self.body.strip():
                    self.headerTerm = False
                    self.setError(_("incorrect text in the template: '%s'")
                                  % self.body)
                    flagErrorBody = True
                if not flagErrorBody:
                    paramList = self.splitParLine(paramLine)
                    if paramList:
                        for i in paramList:
                            for term in self.terms:
                                if term in i:
                                    if self.headerTerm:
                                        errorMsg = (
                                            _("Incorrect template") +
                                            _(": ") + templateName + "\n" +
                                            _("template header not valid")
                                            + _(": ") + i)
                                        if function:
                                            rezTerm = self._equalTerm(
                                                i, errorMsg, function)
                                        else:
                                            rezTerm = self._equalTerm(
                                                i, errorMsg)
                                        if not rezTerm:
                                            self.headerTerm = False
                                    break
                            else:
                                par = i.split("=")
                                if len(par) == 1:
                                    if i in self.listParNotVal:
                                        self.params[i] = "True"
                                        used_params.append(i)
                                    else:
                                        if i.strip():
                                            incorrectParams = {i}
                                elif len(par) == 2:
                                    # self.params[par[0]] = par[1]
                                    par[1] = self.applyVarsTemplate(
                                        par[1], "")
                                    par[1] = templateObj.applyFuncTemplate(
                                        par[1], templateName)
                                    used_params.append(par[0])
                                    self.params[par[0]] = par[1]
                                    if par[0] == HParams.Environ:
                                        try:
                                            importlib.import_module(
                                                "calculate.%s.variables" %
                                                par[1])
                                        except (ImportError, AttributeError):
                                            self.headerTerm = False
                                        self.objVar.defaultModule = \
                                            self.params[HParams.Environ]
                    typeAppend = self._getAppend()
                    if typeAppend:
                        self.typeAppend = typeAppend
                    else:
                        self.headerTerm = False
                        self.setError(_("incorrect header parameter: '%s'") \
                                      % "%s=%s" % (
                                          HParams.Append,
                                          self.params[HParams.Append]))

        double_params = list(set([x for x in used_params
                                  if used_params.count(x) > 1]))
        if double_params:
            self.headerTerm = False
            self.setError(_("redefine header parameter: '%s'")
                          % " ".join(double_params))

        if not flagErrorBody:
            if not incorrectParams:
                incorrectParams = set(self.params.keys()) - set(self.allowParam)
            if incorrectParams:
                self.headerTerm = False
                self.setError(_("incorrect header parameter: '%s'") \
                              % " ".join(list(incorrectParams)))

    def _getAppend(self):
        """Выдать тип добавления директории"""
        if HParams.Append in self.params:
            if (self.params[HParams.Append] == HParams.AppendParams.Replace and
                        HParams.DirectoryLink not in self.params):
                return False
            if self.params[HParams.Append] in self._fileAppend:
                return self.params[HParams.Append]
            else:
                return False
        else:
            return HParams.AppendParams.Join


class blocText():
    """Разбиваем текст на блоки"""

    def splitTxtToBloc(self, text, openTxtBloc, closeTxtBloc,
                       commentTxtBloc, sepField):
        """Делит текст на блоки (без заголовков)

        openTxtBloc  - регулярное выражение для начала блока
        closeTxtBloc - регулярное выражение для конца блока
        commentTxtBloc  - регулярное выражение - комментарий
        возвращает блоки текста
        """
        blocs = []
        level = 0
        # Нахождение нескольких блоков в строке
        # разделители линий, разделителями могут быть ("","\n")
        sepsLines = []
        # линии
        txtLines = []
        # Исходные строки
        txtLinesSrc = text.splitlines()
        for line in txtLinesSrc:
            lineTmpA = line
            closeBl = False
            txtLinesTmp = []
            commentSpl = commentTxtBloc.split(line)
            textLine = None
            commentLine = None
            if commentSpl[0].strip():
                closeBl = True
                if len(commentSpl) > 1:
                    commentBl = commentTxtBloc.search(line)
                    textLine = commentSpl[0]
                    commentLine = line[commentBl.start(0):]
                    lineTmpA = textLine

            while closeBl:
                closeBl = sepField.search(lineTmpA)
                if closeBl:
                    lineTmpB = lineTmpA[closeBl.end(0):]
                    txtLinesTmp.append(lineTmpA[:closeBl.end(0)])
                    lineTmpA = lineTmpB
            if lineTmpA.strip():
                txtLinesTmp.append(lineTmpA)
            # Если есть значение и комментарий в строке
            if textLine is not None:
                for l in txtLinesTmp:
                    txtLines.append(l)
                    sepsLines.append("")
                if not txtLinesTmp:
                    txtLines.append(textLine)
                    sepsLines.append("")
                txtLines.append(commentLine)
                sepsLines.append("\n")
            # Если есть несколько блоков в строке
            elif len(txtLinesTmp) > 1 and txtLinesTmp[1].strip():
                lenTmpLines = len(txtLinesTmp)
                for l in range(lenTmpLines):
                    txtLines.append(txtLinesTmp[l])
                    if l == lenTmpLines - 1:
                        sepsLines.append("\n")
                    else:
                        sepsLines.append("")
            # Cтрока не преобразована
            else:
                txtLines.append(line)
                sepsLines.append("\n")

        # разбивание на блоки
        z = 0
        bl = ""
        for i in txtLines:
            if commentTxtBloc.split(i)[0].strip() and openTxtBloc.search(i):
                level += len(openTxtBloc.split(i)) - 1
            if commentTxtBloc.split(i)[0].strip() and closeTxtBloc.search(i):
                level -= len(closeTxtBloc.split(i)) - 1
            bl += i + sepsLines[z]
            if level == 0:
                if bl:
                    blocs.append(bl)
                bl = ""
            z += 1
        # cоздание блоков с элементами не входящими в блоки
        realBlocs = []
        z = 0
        bl = ""
        for i in blocs:
            txtLines = i.splitlines()
            if len(txtLines) > 0:
                line = txtLines[0]
            else:
                line = i
            if commentTxtBloc.split(i)[0].strip() and openTxtBloc.search(line):
                if bl:
                    realBlocs.append(bl)
                    bl = ""
                realBlocs.append(i)
            else:
                bl += i
            z += 1
        if bl:
            realBlocs.append(bl)
        if level == 0:
            if text and text[-1] != "\n":
                tmpBlocs = realBlocs.pop()
                tmpBlocs = tmpBlocs[:-1]
                realBlocs.append(tmpBlocs)
            return realBlocs
        else:
            return []

    def findArea(self, text, reTextHeader, reTextArea, numGroupArea=0):
        """ Делит текст на области (с заголовками)

        reTextHeader  - регулярное выражение для заголовка области
        reTextArea - регулярное выражение для всей области
        numGroupArea - номер групы результата поиска по регулярному выражению
        по всей области
        возвращает два списка: первый - заголовки, второй - тела областей без
        заголоков
        """
        # Заголовки областей
        headersArea = []
        # Тексты областей без заголовков
        textBodyArea = []
        r = reTextArea.search(text)
        if not r:
            headersArea.append("")
            textBodyArea.append(text)
            return headersArea, textBodyArea

        txtWr = text
        while r:
            textArea = r.group(numGroupArea)
            txtSpl = txtWr.split(textArea)
            area = txtSpl[0]
            txtWr = txtSpl[1]
            if area:
                headersArea.append("")
                textBodyArea.append(area)
            res = reTextHeader.search(textArea)
            header = textArea[:res.end()]
            body = textArea[res.end():]

            headersArea.append(header)
            textBodyArea.append(body)

            if txtWr:
                r = reTextArea.search(txtWr)
            else:
                r = False
        if txtWr:
            headersArea.append("")
            textBodyArea.append(txtWr)
        return headersArea, textBodyArea

    def findBloc(self, text, captionTxtBloc, bodyTxtBloc):
        """ Делит текст на блоки (с заголовками)

        captionTxtBloc  - регулярное выражение для заголовка блока
        bodyTxtBloc - регулярное выражение для тела блока
        возвращает два списка: первый - заголовки, второй - тела блоков
        """
        # Заголовки блоков
        headersTxt = []
        # Тексты блоков
        blocsTxt = []
        r = captionTxtBloc.search(text)
        if r:
            headersTxt.append(r.group(0))
            txtSpl = text.partition(r.group(0))
            blocTxt = txtSpl[0]
            txtWr = txtSpl[2]
            rb = bodyTxtBloc.search(blocTxt)
            if not blocTxt:
                blocsTxt.append(blocTxt)
            if rb:
                blocsTxt.append(rb.group(0))
            while r:
                r = captionTxtBloc.search(txtWr)
                if r:
                    headersTxt.append(r.group(0))
                    txtSpl = txtWr.partition(r.group(0))
                    blocTxt = txtSpl[0]
                    txtWr = txtSpl[2]
                    rb = bodyTxtBloc.search(blocTxt)
                    if rb:
                        blocsTxt.append(rb.group(0))
                else:
                    blocsTxt.append(txtWr)
        if headersTxt and blocsTxt:
            if len(headersTxt) > len(blocsTxt):
                blocsTxt.insert(0, "")
            elif len(headersTxt) < len(blocsTxt):
                headersTxt.insert(0, "")
            if len(headersTxt) != len(blocsTxt):
                return False
            return headersTxt, blocsTxt
        else:
            return False


class _file(_error):
    """
    Класс для работы с файлами
    """
    configMode = None

    def printWARNING(self, s):
        raise NotImplemented()

    def __init__(self):
        # Имя файла конфигурационного файла
        self.nameFileConfig = ""
        self.nameFileConfigOrig = ""
        # Содержимое конфигурационного файла
        self.textConfig = ""
        # Имя файла шаблона
        self.nameFileTemplate = ""
        # Содержимое шаблона
        self.textTemplate = ""
        # Дескриптор файла шаблона
        self.F_TEMPL = None
        # Дескриптор файла конфигурационного файла
        self.F_CONF = None
        # тип запускаемого шаблона
        self.executeType = None
        # список скриптов на запуск
        self.queueExecute = []

    def saveConfFile(self):
        """Записать конфигурацию"""
        if not self.textConfig:
            self.textConfig = self.textTemplate
        if self.F_CONF:
            try:
                self.F_CONF.truncate(0)
                self.F_CONF.seek(0)
                if isinstance(self.textConfig, str):
                    self.F_CONF.write(self.textConfig.encode("UTF-8"))
                elif isinstance(self.textConfig, bytes):
                    self.F_CONF.write(self.textConfig)
                else:
                    #TODO if after testing this doesn't pop up, replace elif above with else
                    raise Exception(f"self.textConfig is not str or bytes: type == {type(self.textConfig)}")
            except IOError:
                self.setError(_("unable to open the file:")
                              + self.nameFileConfig)
                return False
            self.F_CONF.flush()
            return True
        elif self.executeType == HParams.ExecuteType.Post:
            processor = self.textConfig.partition("\n")[0]
            if processor.startswith("#!"):
                self.queueExecute.append((processor[2:], self.textConfig,
                                          self.nameFileTemplate))
            else:
                self.setError(_("unable to execute '%s'")
                              + self.textConfig)
                return False

    def openTemplFile(self, nameFileTemplate):
        """Открыть файл шаблона"""
        try:
            F_TEMPL = open(nameFileTemplate, "rb")
        except IOError:
            self.setError(_("unable to open the file:")
                          + nameFileTemplate)
            return False
        return F_TEMPL

    def closeTemplFile(self):
        if self.F_TEMPL:
            self.F_TEMPL.close()
            self.F_TEMPL = None

    def __closeOldFile(self):
        if self.F_CONF:
            self.F_CONF.close()
            self.F_CONF = None

    def __openConfFile(self, nameFileConfig):
        """Отктрыть конфигурационный файл"""
        try:
            if os.path.islink(nameFileConfig):
                # если ссылка то удаляем её
                os.unlink(nameFileConfig)
            F_CONF = open(nameFileConfig, "rb+")
        except (IOError, OSError):
            try:
                if os.path.isdir(nameFileConfig):
                    self.printWARNING(_("unable to open the directory as file:")
                                      + nameFileConfig)
                    return False
                F_CONF = open(nameFileConfig, "wb+")
            except (IOError, OSError):
                self.setError(_("unable to open the file:")
                              + nameFileConfig)
                return False
        return F_CONF

    def openFiles(self, nameFileTemplate, nameFileConfig, typeFormat=None,
                  newBuffer=None):
        """Открывает шаблон и конфигурационный файл"""
        self.textConfig = ""
        self.textTemplate = ""
        self.closeFiles()
        self.F_TEMPL = None
        self.F_CONF = None
        self.nameFileConfig = os.path.abspath(nameFileConfig)
        self.nameFileTemplate = os.path.abspath(nameFileTemplate)
        self.F_TEMPL = self.openTemplFile(self.nameFileTemplate)
        copy_stat = not os.path.exists(self.nameFileConfig)
        if (not self.executeType and
                    typeFormat not in HParams.Formats.Executable):
            self.F_CONF = self.__openConfFile(self.nameFileConfig)
        if self.F_TEMPL and self.F_CONF:
            self.textTemplate, ___ = try_decode_utf8(self.F_TEMPL.read())
            self.closeTemplFile()
            if self.configMode == T_NEWCFG:
                origConfigName = re.sub(r'/._cfg\d{4}_([^/]+)$', '/\\1',
                                        self.nameFileConfig)
                if newBuffer is None:
                    self.textConfig = readFile(origConfigName)
                    if copy_stat:
                        self.copy_mod_own(origConfigName, self.nameFileConfig)
                else:
                    self.textConfig = newBuffer
            else:
                self.textConfig, ___ = try_decode_utf8(self.F_CONF.read())

    def copy_mod_own(self, source, target):
        try:
            statdata = os.stat(source)
            statdata_old = os.stat(target)
            if statdata.st_mode != statdata_old.st_mode:
                os.chmod(target, statdata.st_mode)
            if (statdata.st_uid != statdata_old.st_uid or 
                statdata.st_gid != statdata_old.st_gid):
                os.chown(target, statdata.st_uid, statdata.st_gid)
        except OSError:
            pass

    def __del__(self):
        self.closeFiles()

    def closeFiles(self):
        """Закрытие файлов"""
        self.closeTemplFile()
        self.__closeOldFile()


class utfBin():
    """Класс для преобразования в utf-8

    преобразование бинарного или смеси бинарного и utf-8 кода в utf-8 и
    обратное преобразование 
    методы класса encode и decode
    """

    def _retUTF(self, char):
        byte = ord(char)
        if byte <= 127:
            return '_ch_', 1
        elif byte <= 191:
            return '_nb_', 1
        elif byte <= 223:
            return '_fb_', 2
        elif byte <= 239:
            return '_fb_', 3
        elif byte <= 247:
            return '_fb_', 4
        else:
            return '_er_', 1

    def _sumbUtf(self, symbols, lenTail):
        if not symbols:
            return False, 0
        lenSymb = len(symbols)
        if lenSymb >= 4:
            l = 4
        elif lenSymb >= 3:
            l = 3
        elif lenSymb >= 2:
            l = 2
        else:
            if symbols[0] == '_ch_':
                return True, 1
            else:
                return False, 1
        result = False
        i_ = 0
        for i in range(l):
            i_ = i
            if i == 0 and symbols[i] != '_fb_':
                break
            elif i > 0 and symbols[i] != '_nb_':
                break
        if lenTail > 1 and lenTail != i_:
            return False, 1
        if i_ > 0:
            result = True
        return result, i_

    def _intToChar(self, x):
        he = hex(x)[2:]
        return chr(int(he, 16))

    def _hexToChar(self, he):
        return chr(int(he, 16))

    def encode(self, text): 
        """Кодирует смешанный формат в UTF-8"""
        ind = 0
        utf = []
        lenUtf = []
        indErr = []
        i = 0
        for ch in text:
            r, l = self._retUTF(ch)
            utf.append(r)
            lenUtf.append(l)
            i += 1
        while 1:
            if utf[ind] == '_fb_':
                res, l = self._sumbUtf(utf[ind:], lenUtf[ind])
                if res is False:
                    indErr.append(ind)
                if l > 0:
                    ind += l
                    if ind >= len(utf):
                        break
            else:
                if utf[ind] != '_ch_':
                    indErr.append(ind)
                ind += 1
                if ind >= len(utf):
                    break
        if indErr:
            lenIndErr = len(indErr)
            block = []
            blocks = []
            if lenIndErr > 1:
                i = 1
                while 1:
                    if i == 1:
                        block.append(indErr[i - 1])
                    if indErr[i] - indErr[i - 1] == 1:
                        block.append(indErr[i])
                    else:
                        if block:
                            blocks.append(block)
                        block = [indErr[i]]
                    i += 1
                    if i >= lenIndErr:
                        break
            else:
                block.append(indErr[0])
            if block:
                blocks.append(block)
            listErr = []
            for block in blocks:
                string = ""
                last_elem = None
                for elem in block:
                    string += hex(ord(text[elem]))[-2:]
                    last_elem = elem
                if last_elem is not None:
                    listErr.append((block[0], "__hex__?%s?__hex__" % string,
                                    last_elem))
            textOut = text
            deltaInd = 0
            for erEl in listErr:
                startInd = erEl[0] + deltaInd
                endInd = erEl[2] + 1 + deltaInd
                textOut = textOut[:startInd] + erEl[1] + textOut[endInd:]
                deltaInd += len(erEl[1]) - (erEl[2] - erEl[0] + 1)
                # if i == 1:
                # break
                # i += 1
            return textOut

    def decode(self, text):
        """Декодирует UTF-8 в смешанный формат"""
        varStart = "__hex__\?"
        varEnd = "\?__hex__"
        # -1 Это экранирование '?' которое тоже считается
        deltVarStart = len(varStart) - 1
        deltVarEnd = len(varEnd) - 1
        reVar = re.compile("%s[a-f0-9]+%s" % (varStart, varEnd), re.M)
        resS = reVar.search(text)
        textTemplateTmp = text
        while resS:
            mark = textTemplateTmp[resS.start():resS.end()]
            hexString = mark[deltVarStart:-deltVarEnd]
            i = 0
            stringInsert = ""
            hexCode = ""
            for ch in hexString:
                if i >= 1:
                    hexCode += ch
                    stringInsert += self._hexToChar(hexCode)
                    hexCode = ""
                    i = 0
                else:
                    hexCode += ch
                    i += 1
            textTemplateTmp = textTemplateTmp.replace(mark, stringInsert)
            resS = reVar.search(textTemplateTmp)
        return textTemplateTmp


class TemplateFormat(_error):
    """
    Формат шаблон
    """
    def __init__(self, text, parent=None):
        self.text = text
        self.changed_files = []
        self.set_parent(parent)
        self.prepare()

    def prepare(self):
        pass

    def setError(self, error):
        super().setError(error)
        if hasattr(self.parent, "bHasError"):
            self.parent.bHasError = True

    def set_parent(self, parent):
        self.parent = parent

    @property
    def template_name(self):
        return self.parent.nameFileTemplate

    def getIni(self, key, nameFile=""):
        return self.parent.functObj.getIni(key, nameFile)

    def setIni(self, key, value, nameFile=""):
        return self.parent.functObj.setIni(key, value, nameFile)

    @property
    def objVar(self):
        return self.parent.objVar


class FormatFactory():
    """
    Фабрика классов форматов шаблонов
    """
    # Импортированные классы поддерживаемых форматов шаблонов
    importFormats = {}

    newObjProt = {}

    def __init__(self, parent):
        self.parent = parent

    def createNewClass(self, name, bases, attrs=None):
        raise NotImplemented()

    def getClassObj(self, nameClassTemplate):
        """Создает класс шаблона по имени"""
        if nameClassTemplate in self.importFormats:
            classFormat = self.importFormats[nameClassTemplate]
        else:
            try:
                classFormat = getattr(__import__("calculate.lib.format.%s" %
                                                 nameClassTemplate,
                                                 globals(), locals(),
                                                 [nameClassTemplate]),
                                      nameClassTemplate)
            except (ImportError, AttributeError) as e:
                # Создаем объект из self.newObjProt с помощью
                # метаклассов

                if nameClassTemplate in self.newObjProt:
                    # Прототип класса
                    nameProt = self.newObjProt[nameClassTemplate]
                    if nameProt in self.importFormats:
                        classProt = self.importFormats[nameProt]
                    else:
                        try:
                            classProt = getattr(
                                __import__("calculate.lib.format.%s" % nameProt,
                                           globals(), locals(),
                                           [nameProt]),
                                nameProt)
                        except (ImportError, AttributeError) as e:
                            return False
                        self.importFormats[nameProt] = classProt
                    classFormat = self.createNewClass(nameClassTemplate,
                                                      (classProt,))
                else:
                    return False
            self.importFormats[nameClassTemplate] = classFormat
        return classFormat

    def createObject(self, formatTemplate, textTemplate):
        """Создание объекта формата шаблона.

        Объект создается на основании формата шаблона и текста шаблона"""
        classFormat = self.getClassObj(formatTemplate)
        if callable(classFormat):
            obj = classFormat(textTemplate, self.parent)
            return obj
        else:
            return False

class TemplateFunctionError(Exception):
    pass

def template_function(lastall=False):
    """
    Подготовить метод для использования в качестве функции

    lastall: поволяет поделить строку аргументов на указанное число,
             при этом последний аргумент получит все данные, которые
             могут содержать разделитель параметров
    """
    def decor(f):
        @wraps(f)
        def wrapper(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
            def pretty_num(num):
                if num > 1:
                    return _("%d argumens") % num
                else:
                    return _("1 argument")
            funArgv = funArgv.strip()
            # поиск всех служебных переменных
            spec_vars = [x for x in f.__code__.co_varnames[:f.__code__.co_argcount]
                         if x in ("self", "nameTemp", "localVars")]
            varnum = f.__code__.co_argcount - len(spec_vars)
            defnum = len(f.__defaults__) if f.__defaults__ else 0
            # число обязательных параметров
            reqnum = varnum - defnum
            if funArgv:
                terms = [x.strip() for x in funArgv.split(",")]
            else:
                terms = []

            if not varnum and len(terms) != varnum:
                raise self.raiseErrTemplate(_("Function takes no arguments"))
            if len(terms) < reqnum:
                if defnum:
                    raise self.raiseErrTemplate(
                        _("Function takes at least {num}").format(
                        num=pretty_num(reqnum)))
                else:
                    raise self.raiseErrTemplate(
                        _("Function takes exactly {num}").format(
                        num=pretty_num(reqnum)))
            if not lastall:
                if len(terms) > varnum:
                    if defnum:
                        raise self.raiseErrTemplate(
                            _("Function takes at most {num}").format(
                            num=pretty_num(varnum)))
                    else:
                        raise self.raiseErrTemplate(
                            _("Function takes exactly {num}").format(
                            num=pretty_num(varnum)))
            else:
                terms = terms[:varnum-1] + [",".join(terms[varnum-1:])]
            args = [self]
            if "nameTemp" in spec_vars:
                args.append(nameTemp)
            if "localVars" in spec_vars:
                args.append(localVars)
            args.extend(terms)
            try:
                replace = f(*args)
            except TemplateFunctionError as e:
                raise self.raiseErrTemplate(str(e))
            textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                              textTemplateTmp[resS.end():]
            return textTemplateTmp
        return wrapper
    return decor

class templateFunction(_error, _warning, _shareTemplate, _shareTermsFunction,
                       LayeredIni):
    """Класс для функций шаблонов"""
    # Словарь установленных программ {"имя программы":[версии]}
    installProg = {}

    # Cписок просканированных категорий установленных программ
    installCategory = []

    # Флаг сканирования всех установленных программ
    flagAllPkgScan = False

    # Список названий функций шаблона
    namesTemplateFunction = []

    # Словарь {название функции шаблона: функция шаблона, ...}
    templateFunction = {}

    # Регулярное выражение для сложения
    sNum = re.compile("\-[^\-\+]+|[^\-\+]+")

    # Регулярное выражение для умножениея и деления
    sMD = re.compile("[^\-\+\*/]+")

    # директория установленных программ
    _basePkgDir = "/var/db/pkg"
    basePkgDir = _basePkgDir

    # кэш для проверки наличия пакета в портежах
    cachePortdir = {}

    # стек глобальных переменных
    stackGlobalVars = []

    # регулярное выражение для поиска версии
    reFindVer = re.compile("(?<=-)(?:\d+)(?:(?:\.\d+)*)"
                           "(?:[a-z]?)(?:(?:_(?:pre|p|beta|alpha|rc)\d*)*)"
                           "(?:-r\d+)?$")

    reEmptyLoad = re.compile("^\s*$|^\s*;|^\s*#")

    # Имя обрабатываемого шаблона
    nameTemplate = ""

    # Текст функции шаблона
    functText = ""

    # regular for discard sort number and version
    reData = re.compile(r"^(?:\d+-)?(.+?)(?:-(?:|always|\d+|\d(?:\d|\.|pre|_"
                        "|-always|alpha|beta|pre|rc|[a-z][^a-z])*[a-z]?)(?:"
                        "-r\d+)?)?$", re.S)

    currentAction = HParams.ActionType.Merge

    def printSUCCESS(self, s):
        raise NotImplemented()

    def printWARNING(self, s):
        raise NotImplemented()

    def printERROR(self, s):
        raise NotImplemented()

    @classmethod
    def get_pkgname_by_filename(cls, fn):
        fileName = os.path.split(fn)[1]
        if fileName == '.calculate_directory':
            parentDir = os.path.dirname(fn)
            parentDir, pkgName = os.path.split(parentDir)
        else:
            parentDir, pkgName = os.path.split(fn)
        category = os.path.split(parentDir)[1]
        # reg for discard version and sort number
        pkgName = cls.reData.search(pkgName).group(1)
        category = cls.reData.search(category).group(1)
        return "%s/%s" % (category, pkgName)

    currentBelong = ""
    currentBelongSlot = ""
    alreadyInformed = []

    def __init__(self, objVar):
        # Если не определен словарь функций шаблона
        # import services api
        LayeredIni.__init__(self)
        if not self.templateFunction:
            # префикс функций шаблона
            pref = "func"
            # cписок [(название функции, функция), ...]                            
            # удаляем у названия функции префикс и переводим остаток названия
            # в нижний регистр
            dictFunc = [(x[0][len(pref):].lower(), x[1]) for x 
                    in self.__class__.__dict__.items() 
                    if x[0].startswith(pref) and hasattr(x[1], "__call__")]
            # Формируем словарь функций шаблона
            self.templateFunction.update(dictFunc)
            # Формируем список функций шаблона
            for nameFunction in self.templateFunction.keys():
                self.namesTemplateFunction.append(nameFunction)
        # Объект хранения переменных
        self.objVar = objVar
        self._reFunc = re.compile("%s%s%s"
                                  % (self.varStart, self._reFunctionText,
                                     self.varEnd), re.M)
        self._rePrePattern = "%s.{%%d,}?%s" % (self.varStart, self.varEnd)
        self._rePreFuncPattern = "%s.{%%d,}?\)%s" % (self.varStart, self.varEnd)
        # Аттрибуты для функции шаблона ini()
        # Первоначальный словарь переменных для ini()
        self.prevDictIni = {}
        # Текущий словарь переменных для ini()
        self.currDictIni = {}
        # Время модификации конфигурационного файла для ini() 
        self.timeIni = -1
        self.recalculateBaseDir()

        # Словарь времен модификации env файлов
        self.timeConfigsIni = {}
        # Словарь хранения переменых полученных функцией env() из env файлов
        self.valuesVarEnv = {}
        # Словарь хранения опций для функции info()
        self.optionsInfo = {}
        # файл параметров сервисов
        envFile = self.objVar.Get("cl_env_server_path")
        # объект конвертирования из старого remote env файла
        self.convObj = False
        if os.access(envFile, os.R_OK):
            self.convObj = False
        elif os.access("/var/calculate/remote/calculate.env", os.R_OK):
            from .convertenv import convertEnv

            self.convObj = convertEnv()


    def recalculateBaseDir(self):
        """Recalculate basedir and homedir"""
        # Директория другой системы
        self._chrootDir = self.objVar.Get("cl_chroot_path")
        # Изменение директории к базе пакетов
        self.basePkgDir = pathJoin(self._chrootDir, self._basePkgDir)
        self.basePkgDir = os.path.normpath(self.basePkgDir)
        # Базовая директория переноса шаблонов "/mnt/calculate" или "/" и.т.д
        self._baseDir = pathJoin(self._chrootDir,
                                 self.objVar.Get("cl_root_path"))
        self._baseDir = os.path.normpath(self._baseDir)
        self.uid, self.gid, self.homeDir, self.groups = \
            self.getDataUser(groupsInfo=True)
        # Домашняя директория, плюс базовая директория
        self.homeDir = pathJoin(self._baseDir, self.homeDir)
        # path to configuration file for ini() function
        # if action is desktop configuration, then path in user directory
        # else config file place in /etc/calculate
        if self.objVar.Get('cl_action') == "desktop":
            self.pathConfigIni = os.path.join(self.homeDir, ".calculate")
            self.fileConfigIni = os.path.join(self.pathConfigIni,
                                              LayeredIni.IniPath.IniName)
            self.modeConfigIni = 0o640
        else:
            self.fileConfigIni = pathJoin(self._chrootDir,
                                          LayeredIni.IniPath.Work)
            self.pathConfigIni = os.path.dirname(self.fileConfigIni)
            self.modeConfigIni = 0o644

    def equalTerm(self, term, localVars):
        """Метод для вычисления выражения"""
        terms = self.sNum.findall(term)
        if terms:
            strNumers = []
            for n in terms:
                strNum = n.strip()
                if "*" in strNum or "/" in strNum:
                    strNum = self.multAndDiv(strNum, localVars)
                num = 0
                try:
                    num = int(strNum)
                except ValueError:
                    minus = False
                    if strNum[:1] == "-":
                        minus = True
                        strNum = strNum[1:]
                    if strNum in localVars:
                        try:
                            num = int(localVars[strNum])
                        except ValueError:
                            raise self.raiseErrTemplate(
                                _("error: variable %s is not integer") %
                                str(strNum))
                    elif self.objVar.exists(strNum):
                        try:
                            num = int(self.objVar.Get(strNum))
                        except ValueError:
                            raise self.raiseErrTemplate(
                                _("error: variable %s is not integer") %
                                str(strNum))
                    else:
                        raise self.raiseErrTemplate(
                            _("error: local variable %s not defined") %
                            str(strNum))
                    if minus:
                        num = -num
                strNumers.append(num)
            return sum(strNumers)
        raise self.raiseErrTemplate(_("error: template term %s, incorrect data") \
                                    % str(term))

    def multAndDiv(self, term, localVars):
        """Метод для умножения и деления"""
        termTmp = term
        varsLocal = self.sMD.findall(term)
        for var in varsLocal:
            flagVarTxt = True
            try:
                int(var)
            except ValueError:
                flagVarTxt = False
            if flagVarTxt:
                continue
            varReplace = str(self.equalTerm(var, localVars))
            termTmp = termTmp.replace(var, varReplace)
        ret = eval(termTmp)
        return ret

    def getIni(self, key, nameTemp=""):
        class FakeMatch():
            def start(self):
                return 0

            def end(self):
                return 0

        return self.funcIni(key, FakeMatch(), None, "", nameTemp)

    def setIni(self, key, value, nameTemp=""):
        class FakeMatch():
            def start(self):
                return 0

            def end(self):
                return 0

        self.funcIni("%s,%s" % (key, value), FakeMatch(), None, "", nameTemp)

    def funcProfile(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """
        Функция проверят состояние пользовательского профиля:
        configured - профиль настраивался утилитами calculate
        empty - профиль пустой, либо содержит skel, либо сертификат утилит
        custom - профиль настроен и он настраивался не утилитами
        """
        ini_value = self.funcIni("main.update", resS, localVars, "", nameTemp)
        if ini_value:
            replace = "configured"
        else:
            user_dir = self.objVar.Get('ur_home_path')
            if isBootstrapDataOnly(user_dir):
                replace = "empty"
            else:
                replace = "custom"

        textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def check_command(self, command, prefix="/"):
        cmd = getProgPath(command, prefix=prefix)
        if not cmd:
            raise self.raiseErrTemplate(
                _("Command not found '%s'")%command)
        return cmd

    def warning_message(self, message):
        if callable(self.printWARNING):
            self.printWARNING(message)

    @template_function()
    def funcWorld(self, category):
        """
        Функция выполняет eix и возвращает список пакетов
        """
        prefix = self.objVar.Get('cl_chroot_path')
        nfenv = dict(os.environ)
        nfenv["NOFOUND_STATUS"]="0"
        kwargs = {'lang':'C', 'envdict': nfenv}
        if prefix == "/":
            args = [self.check_command("/usr/bin/eix", prefix=prefix)]
        else:
            args = ["/bin/chroot", prefix, self.check_command("/usr/bin/eix", prefix=prefix)]
        args.extend(["-*", "--format", "<bestslotversions:NAMEASLOT>"])
        if "/" in category:
            args.extend(["-e", category])
        else:
            args.extend(["--category", "-e", category])
        p = process(*args, **kwargs)

        if p.success():
            atoms = [x for x in p.read().split() if x.strip()]
            if not atoms:
                self.warning_message(_("No packages in %s category")%category)
            return "\n".join(atoms)
        else:
            raise TemplateFunctionError(_("Failed to execute") + _(": ") +
                                        "eix")

    def funcLs(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """
        Функция получения списка файлов из директории
        """
        globpath, o, pattern = funArgv.partition(',')
        if not pattern:
            pattern = r" "

        pattern = pattern.replace(r"\1", "{0}")
        pattern = re.sub(r"(^|[^\\])\\n", "\\1\n", pattern)
        pattern = pattern.replace(r"\n", "\n")
        pattern = pattern.replace(r"\t", "\t")

        chroot_path = os.path.normpath(self.objVar.Get('cl_chroot_path'))
        globpath = pathJoin(chroot_path, globpath)

        if "*" in globpath:
            files = glob.glob(globpath)
        else:
            files = listDirectory(globpath, fullPath=True)
        if files:
            files = (x for x in files if not os.path.isdir(x))
            if chroot_path != '/':
                l = len(chroot_path)
                files = (x[l:] for x in files)
            if r"{0}" not in pattern:
                replace = pattern.join(files)
            else:
                replace = "".join(pattern.format(x) for x in files)
        else:
            replace = ""
        textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def funcForeach(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """
        Функция получения списка файлов из директории
        """
        varname, o, pattern = funArgv.partition(',')

        values = ""
        try:
            values = self.objVar.Get(varname)
        except DataVarsError as e:
            raise TemplatesError(_("error: variable %s does not exist")
                                 % varname)

        if not pattern:
            pattern = r" "

        if values:
            pattern = pattern.replace(r"\1", "{0}")
            pattern = re.sub(r"(^|[^\\])\\n", "\\1\n", pattern)
            pattern = pattern.replace(r"\n", "\n")
            pattern = pattern.replace(r"\t", "\t")

            if r"{0}" not in pattern:
                replace = pattern.join(values)
            else:
                replace = "".join(pattern.format(x) for x in values)
        else:
            replace = ""
        textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def funcSum(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """Функция шаблона, вычисляет функцию sum()"""
        terms = funArgv.replace(" ", "").split(",")
        # Название локальной переменной
        nameLocVar = terms[0]
        if nameLocVar not in localVars:
            localVars[nameLocVar] = 0
        if len(terms) == 2:
            if terms[1].strip():
                localVars[nameLocVar] = self.equalTerm(terms[1], localVars)
                replace = str(localVars[nameLocVar])
            else:
                replace = ""
            textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                              textTemplateTmp[resS.end():]
        elif len(terms) == 3:
            if terms[1].strip():
                replaceInt = self.equalTerm(terms[1], localVars)
                replace = str(replaceInt)
            else:
                replace = ""
            localVars[nameLocVar] = self.equalTerm(terms[2], localVars)
            textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                              textTemplateTmp[resS.end():]
        else:
            raise self.raiseErrTemplate()
        return textTemplateTmp

    def funcExists(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """Функция шаблона exists(),
        проверяет существование файла, если существует выдает '1'
        если второй параметр root, то проверка осуществляется от корня.
        """
        if funArgv.strip():
            terms = [x.strip() for x in funArgv.split(",")]
            if len(terms) > 2:
                raise self.raiseErrTemplate()
            fileName = terms[0]
            flagNotRootFS = True
            if len(terms) == 2:
                if terms[1] == "root":
                    flagNotRootFS = False
                else:
                    raise self.raiseErrTemplate(
                        _("The second argument of the function is not 'root'"))
            if fileName[0] == "~":
                # Получаем директорию пользователя
                fileName = os.path.join(self.homeDir,
                                        fileName.partition("/")[2], "")[:-1]
            elif fileName[0] != "/":
                raise self.raiseErrTemplate(_("wrong path '%s'") % fileName)
            else:
                if flagNotRootFS:
                    fileName = pathJoin(self._baseDir, fileName)
            replace = ""
            if os.path.exists(fileName):
                check_map = (
                    ('f', stat.S_ISREG),
                    ('d', stat.S_ISDIR),
                    ('l', stat.S_ISLNK),
                    ('b', stat.S_ISBLK),
                    ('c', stat.S_ISCHR),
                    ('p', stat.S_ISFIFO),
                    ('s', stat.S_ISSOCK))
                fmode = os.lstat(fileName)
                for t, func in check_map:
                    if func(fmode.st_mode):
                        replace = t
                        break
                else:
                    replace = "1"
        else:
            replace = ""
        textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def funcLoad(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """Функция шаблона load(),

        если файл существует читает из файла локальную переменную
        если один параметр - выводит значение локальной переменной
        """
        terms = funArgv.split(",")
        if terms:
            lenTerms = len(terms)
            if not terms[0].strip() or \
                    (lenTerms == 2 and not terms[1].strip()) or \
                    (lenTerms == 3 and not terms[2].strip()) or \
                            lenTerms > 3:
                raise self.raiseErrTemplate()
        else:
            raise self.raiseErrTemplate()
        flagNotRootFS = True
        if lenTerms == 3:
            if terms[2] == "root":
                flagNotRootFS = False
            else:
                raise self.raiseErrTemplate(
                    _("The third argument of the function is not 'root'"))
        if lenTerms >= 2:
            if not terms[0] in ["ver", "num", "char", "key", "empty"]:
                raise self.raiseErrTemplate(
                    _("the first argument of the function is neither 'ver'"
                      " or 'num' or 'char' or 'empty'"))
        if lenTerms == 1:
            fileName = terms[0].strip()
        else:
            fileName = terms[1].strip()
        # Если домашняя директория
        if fileName[0] == "~":
            # Получаем директорию пользователя
            fileName = os.path.join(self.homeDir,
                                    fileName.partition("/")[2], "")[:-1]
        elif fileName[0] != "/":
            raise self.raiseErrTemplate(_("wrong path '%s'") % fileName)
        else:
            if flagNotRootFS:
                fileName = pathJoin(self._baseDir, fileName)
        replace = ""
        if os.path.exists(fileName):
            replace = readFile(fileName).strip()
        if replace and lenTerms >= 2 and terms[0] == "empty":
            replace = "\n".join((x for x in replace.split("\n") if not self.reEmptyLoad.search(x)))
        if not replace and lenTerms >= 2 and terms[0] in ["ver", "num"]:
            replace = "0"
        textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def sharePkg(self, pkgs, force=False):
        """
        Update packages from pkgs list
        """
        for pkgname, category, ver, slot in pkgs:
            fullname = "%s/%s" % (category, pkgname)
            if not fullname in self.installProg or \
                            type(self.installProg[fullname]) != dict:
                self.installProg[fullname] = self.installProg[pkgname] = {}
            if force or not slot in self.installProg[fullname]:
                self.installProg[fullname][slot] = ver
        return self.installProg

    # os.path.walk is deprecated in py3
    def getInstallPkgGentoo(self, category=""):
        pkgs = []
        filterFunc = lambda x: "SLOT" == x
        for dirname, dirs, files in os.walk(os.path.join(self.basePkgDir, category)):
            for nameFile in filter(filterFunc, files+dirs):
                absNameFile = os.path.join(dirname, nameFile)
                category, spl, pkgname = dirname.rpartition('/')
                dbpkg, spl, category = category.rpartition('/')
                slot = readFile(absNameFile).strip().partition('/')[0]
                pkgname, spl, rev = pkgname.rpartition("-")
                if rev.startswith('r'):
                    pkgname, spl, ver = pkgname.rpartition("-")
                    ver = "%s-%s" % (ver, rev)
                else:
                    ver = rev
                pkgs.append((pkgname, category, ver, slot))

        return self.sharePkg(pkgs)

    def pkg(self, nameProg, slot=None):
        if len(self.installProg) > 0:
            if type(list(self.installProg.values())[0]) != dict:
                self.installProg.clear()
                self.getInstallPkgGentoo()
        if nameProg in self.installProg:
            versions = self.installProg[nameProg]
            if slot:
                return versions.get(slot, "")
            if len(versions) == 1:
                return list(versions.values())[0]
            else:
                return versions[max(versions.keys(), key=getTupleVersion)]
        else:
            return ""

    def funcPkg(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """Функция шаблона pkg(), выдает номер версии программы"""
        # Название программы
        nameProg = funArgv.replace(" ", "")
        if not nameProg:
            nameProg = self.get_pkgname_by_filename(self.nameTemplate)
        # Замена функции в тексте шаблона
        if "/" in nameProg:
            category, spl, nameProg = nameProg.partition("/")
            nameProg, spl, uses = nameProg.partition('[')
            nameProg, spl, slot = nameProg.partition(":")
            if uses:
                uses = uses.rstrip("]")
            if not category in self.installCategory:
                self.getInstallPkgGentoo(category=category)
                self.installCategory.append(category)
            replace = self.pkg(nameProg, slot=slot or None)
            if replace and uses:
                pkg_use, pkg_iuse = getPkgUses("%s/%s" % (category, nameProg),
                                               replace, prefix=self.objVar.Get(
                        'cl_chroot_path'))
                for use in (x for x in uses.split(',') if x):
                    if (use[0] == "-" and use[1:] in pkg_use or
                                    use[0] != "-" and use not in pkg_use):
                        replace = ""
                        break
        else:
            if not self.flagAllPkgScan:
                self.getInstallPkgGentoo()
                templateFunction.flagAllPkgScan = True
            nameProg, spl, slot = nameProg.partition(":")
            replace = self.pkg(nameProg,
                               slot=slot)
        textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def funcKernel(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """
        Функция kernel(...), выдает значение опции конфига ядра (y,m,)
        """
        terms = funArgv.replace(" ", "").split(",")
        if not terms[0].strip() or len(terms) != 1:
            raise self.raiseErrTemplate()
        kernel_opt = terms[0].upper()
        if kernel_opt.startswith("CONFIG_"):
            raise self.raiseErrTemplate(
                _("the option name should not starts with CONFIG_"))
        kernel_config = self.objVar.Get('install.os_install_kernel_config')
        find_str = "CONFIG_%s" % kernel_opt
        replace = ""
        for line in kernel_config:
            if find_str in line:
                if "%s=" % find_str in line:
                    key, op, value = line.partition("=")
                    replace = value.strip("'\"")
                    break
                elif "%s is not set" % find_str in line:
                    break
        textTemplateTmp = (textTemplateTmp[:resS.start()] + replace +
                           textTemplateTmp[resS.end():])
        return textTemplateTmp

    def funcGrep(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """
        Функция grep (...), выдает значение из файла по регулярному выражению
        """
        fname, op, regpattern = funArgv.replace(" ", "").partition(",")
        regpattern = regpattern.replace("(?\<", "(?<")
        regpattern = self._replace_hex(regpattern)
        if not fname or not regpattern:
            raise self.raiseErrTemplate()
        try:
            reg = re.compile(regpattern)
        except re.error:
            raise self.raiseErrTemplate(_("Wrong regular expression"))
        if fname[0] == "~":
            # Получаем директорию пользователя
            fname = os.path.join(self.homeDir,
                                 fname.partition("/")[2], "")[:-1]
        fname = pathJoin(self.objVar.Get('cl_chroot_path'), fname)
        fileContent = readFile(fname)
        match_data = reg.search(fileContent)
        if match_data:
            md_groups = match_data.groups()
            if md_groups:
                replace = md_groups[0] or ""
            else:
                replace = match_data.group()
        else:
            replace = ""
        return (
            textTemplateTmp[:resS.start()] + replace + textTemplateTmp[
                                                       resS.end():])

    def funcCut(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """
        Функция разбивающая третий аргумент на строки по указанному разеделителю
        и возвращающая указанный блок
        #-cut(2,-,1-2-3-4)-# -> 3
        #-cut(2,,1,2,3,4)-# -> 3
        :param funArgv:
        :param resS:
        :param localVars:
        :param textTemplateTmp:
        :param nameTemp:
        :return:
        """
        if funArgv:
            terms = funArgv.split(",")
        else:
            terms = []
        if len(terms) > 3:
            terms = terms[:2] + [",".join(terms[2:])]
        if len(terms) < 1:
            terms = ["0"]
        if len(terms) < 2:
            terms.append("-")
        if len(terms) < 3:
            terms.append(self.objVar.Get('cl_pass_file'))
        num, delimeter, data = terms
        if not num.isdigit():
            raise self.raiseErrTemplate(
                _("first parameter must be number"))
        delimeter = delimeter or ","
        num = int(num)
        data = data.split(delimeter)
        if num < len(data):
            replace = data[num]
        else:
            replace = ""
        textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def funcRnd(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """Функция шаблона rnd(), выдает строку случайных символов

        первый аргумент:
            'num' - числа,
            'pas' - цифры и буквы
            'uuid' - цифры и строчные буквы a-f
        второй аргумент:
            количество символов
        """
        terms = funArgv.replace(" ", "").split(",")
        gentype = terms[0].strip()
        genlen = None
        uuidmax = 32
        if len(terms) not in (1,2):
            raise self.raiseErrTemplate(
                _("function rnd support one or two arguments only"))
        if len(terms) == 2:
            genlen = terms[1]
            if not genlen.isdigit():
                raise self.raiseErrTemplate(
                    _("the second argument of the function is not a number"))
            genlen = int(terms[1])
        if gentype == 'uuid':
            if not genlen:
                genlen = uuidmax
            if genlen > uuidmax:
                raise self.raiseErrTemplate(
                    _("length of UUID must not be above {maxlen}").format(
                        maxlen=uuidmax))

        if not gentype or not genlen or len(terms) not in (1, 2):
            raise self.raiseErrTemplate()
        fArgvNames = {'num': string.digits,
                      'pas': string.ascii_letters + string.digits,
                      'hex': string.ascii_lowercase[:6] + string.digits,
                      'uuid': string.ascii_lowercase[:6] + string.digits}
        if not gentype in fArgvNames:
            raise self.raiseErrTemplate(
                _("the first argument of the function must "
                  "be 'num', 'pas' or 'uuid'"))
        if gentype == 'uuid':
            offset = [y for x, y in (
                (0, 0), (9, 1), (13, 2), (17, 3), (21, 4)) if genlen >= x][-1]
            replace = str(uuid.uuid4())
            replace = replace[:genlen+offset]
        else:
            choiceStr = fArgvNames[gentype]
            replace = ''.join([random.choice(choiceStr) for i in range(genlen)])

        textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def funcCase(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """Функция шаблона case(), выдает переменную в определенном регистре

        первый аргумент:
            'upper' - верхний регистр,
            'lower' - нижний регистр,
            'capitalize' - первая буква в верхнем регистре
        второй аргумент:
            название переменной
        """
        terms = funArgv.replace(" ", "").split(",")
        if not terms[0].strip() or \
                (len(terms) == 2 and not terms[1].strip()) or len(terms) != 2:
            raise self.raiseErrTemplate()
        fArgvNames = ['upper', 'lower', 'capitalize']
        if not terms[0] in fArgvNames:
            raise self.raiseErrTemplate(_("the first argument of the function"
                                          " is neither 'upper' or 'lower' or"
                                          " 'capitalize'"))
        try:
            strValue = self.objVar.Get(terms[1])
            if not strValue:
                strValue = ""
            else:
                strValue = str(strValue)
        except Exception:
            raise TemplatesError(
                _("error in template %s") % self.nameTemplate + "\n" +
                _("error: variable %s not found") % str(terms[1]))
        replace = ""
        strValue = _u(strValue)
        if terms[0] == 'upper':
            replace = strValue.upper()
        elif terms[0] == 'lower':
            replace = strValue.lower()
        elif terms[0] == 'capitalize':
            replace = strValue.capitalize()
        if replace:
            replace = replace #there used to be encode utf-8 here
        textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def funcIn(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """
        Function in for check value in variable
        """
        terms = funArgv.replace(" ", "").split(",")
        # Название локальной переменной
        nameLocVar = terms[0]
        try:
            value = self.objVar.Get(nameLocVar)
            terms = terms[1:]
            if any(x in terms for x in iterate_list(value)):
                replace = "1"
            else:
                replace = ""
        except Exception:
            raise self.raiseErrTemplate(_("error: variable %s does not exist") \
                                        % str(nameLocVar))
        textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def funcPush(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """локальная функция записывает значение переменной 

        в стек глобальных переменных
        """
        terms = funArgv.replace(" ", "").split(",")
        # Название локальной переменной
        nameLocVar = terms[0]
        value = ""
        if nameLocVar in localVars.keys():
            flagFoundVar = True
            value = localVars[nameLocVar]
        else:
            try:
                value = self.objVar.Get(nameLocVar)
                flagFoundVar = True
            except Exception:
                flagFoundVar = False
        if flagFoundVar:
            # Если переменная существует
            if len(terms) == 1:
                self.stackGlobalVars.append(str(value))
            else:
                raise self.raiseErrTemplate(_("error: variable %s exists") \
                                            % str(nameLocVar))
        else:
            # Если переменная не существует
            if len(terms) == 1:
                raise self.raiseErrTemplate(
                    _("error: variable %s does not exist") \
                    % str(nameLocVar))
            elif len(terms) == 2:
                value = terms[1].strip()
                self.stackGlobalVars.append(str(value))
                localVars[nameLocVar] = value
            else:
                raise self.raiseErrTemplate()
        replace = ""
        textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def funcPop(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """локальная функция получает значение

        из стека глобальных переменных и присваивает локальной переменной 

        """
        terms = funArgv.replace(" ", "").split(",")
        # Название локальной переменной
        nameLocVar = terms[0]
        if len(terms) == 1:
            if self.stackGlobalVars:
                localVars[nameLocVar] = self.stackGlobalVars.pop()
            else:
                raise self.raiseErrTemplate(
                    _("error: global variables stack empty"))
        else:
            raise self.raiseErrTemplate()
        replace = ""
        textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def funcPrint(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """
        Вывод успешного сообщения
        """
        if funArgv:
            funArgv = _(funArgv)
        self.printSUCCESS(funArgv)
        textTemplateTmp = textTemplateTmp[:resS.start()] + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def funcWarning(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """
        Вывод сообщения с предупреждением
        """
        if funArgv:
            funArgv = _(funArgv)
        self.printWARNING(funArgv)
        textTemplateTmp = textTemplateTmp[:resS.start()] + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def funcError(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """
        Вывод сообщения с ошибкой
        """
        if funArgv:
            funArgv = _(funArgv)
        self.printERROR(funArgv)
        textTemplateTmp = textTemplateTmp[:resS.start()] + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def getElogTimestamp(self):
        # Получаем время модификации конфигурационного файла
        curTime = self.getTimeFile(self.fileConfigIni)
        nameLocVar = "update.timestamp"
        if self.timeIni != curTime:
            # читаем переменные из файла
            self.prevDictIni = self.loadVarsIni(self.fileConfigIni)
            self.currDictIni = {}
            self.currDictIni.update(self.prevDictIni)
            self.timeIni = self.getTimeFile(self.fileConfigIni)
        if nameLocVar in self.currDictIni.keys():
            if self.currDictIni[nameLocVar] is None:
                return 0
            else:
                val = self.currDictIni[nameLocVar] #used to be encode utf-8 here
                if val.isdigit():
                    return int(val)
        return 0

    elogFile = '/var/log/emerge.log'

    @classmethod
    def getLastElog(cls):
        # get last timestamp (of ::completed emerge)
        entry = EmergeLog(EmergeLogPackageTask()).get_last_time()
        if entry:
            return entry.partition(":")[0]
        else:
            return "0"

    def funcElog(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """Function for work with emerge.log"""
        funArgv = funArgv.strip()
        rePkg = re.compile(r'\) Merging (?:Binary )?\((\S+)::', re.S)
        replace = ""
        if funArgv:
            lastTimestamp = self.getElogTimestamp()
            for line in reversed(list(readLinesFile(self.elogFile))):
                timestamp, op, info = line.partition(':')
                if timestamp.isdigit() and lastTimestamp and \
                                int(timestamp) < lastTimestamp:
                    break
                match = rePkg.search(info)
                if match and match.group(1).startswith(funArgv):
                    pkgInfo = reVerSplitToPV(match.group(1))
                    if "{CATEGORY}/{PN}".format(**pkgInfo) == funArgv:
                        replace = pkgInfo['PVR']
                        break
        else:
            replace = self.getLastElog()
        textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    @classmethod
    def splash_cmd(cls, splash_type):
        cmd_map = {
            'splashutils': "splash=silent,theme:calculate console=tty1",
            'plymouth': "splash",
        }
        return cmd_map.get(splash_type, "verbose")

    def funcLivemenu(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        def generateSubmenu(data):
            base_dn = self.objVar.Get('builder.cl_builder_flash_path')
            for id, label, iso, vmlinuz_orig, vmlinuz, initrd_orig, initrd, \
                xorg, drivers, splash in data:
                splash = self.splash_cmd(splash)
                yield ("{id};\n{label};\n/boot/{kernel};\n"
                       "root=live iso-scan/filename={iso};\n"
                       "/boot/{initrd};\n"
                       "init=/linuxrc rd.live.squashimg=livecd.squashfs "
                       "{splash} "
                       "nodevfs quiet noresume;\n".format(
                    id=id, label=label, kernel=vmlinuz, initrd=initrd,
                    splash=splash, iso=iso[len(base_dn):]
                ))

        def generateXorg(data):
            for id, label, iso, vmlinuz_orig, vmlinuz, initrd_orig, initrd, \
                xorg, drivers, splash in data:
                if xorg == "on":
                    yield id

        def generateVideo(data):
            for id, label, iso, vmlinuz_orig, vmlinuz, initrd_orig, initrd, \
                xorg, drivers, splash in data:
                if drivers == "on":
                    yield id

        data = [x for x in self.objVar.Get('builder.cl_builder_image_data') if x]
        if funArgv == 'submenu':
            res = "\n".join(generateSubmenu(data))
        elif funArgv == 'xorg':
            res = " ".join(generateXorg(data))
        elif funArgv == 'video':
            res = " ".join(generateVideo(data))
        else:
            res = ""
        textTemplateTmp = textTemplateTmp[:resS.start()] + res + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def loadVarsIni(self, iniFileName):
        """ Читает файл fileName
        создает и заполняет переменные на основе этого файла
        Используеться совместно c funcIni
        """
        localVarsIni = {}
        # получить объект ini файла
        config = ConfigParser(strict=False)
        config.read(iniFileName, encoding="utf-8")
        # получаем все секции из конфигурационного файла
        allsect = config.sections()
        if not allsect:
            return localVarsIni
        # Заполняем переменные для funcIni
        for sect in allsect:
            for name, valueVar in config.items(sect, raw=True):
                nameVar = "%s.%s" % (sect, name)
                localVarsIni[nameVar] = valueVar
        return localVarsIni

    def getTimeFile(self, fileName):
        # Получаем время модификации файла
        nameEnvFile = os.path.split(fileName)[1]
        if nameEnvFile in self.timeConfigsIni:
            return self.timeConfigsIni[nameEnvFile]
        return 0

    def funcWallpaper(self, funArgv, resS, localVars, textTemplateTmp,
                      nameTemp):
        """
        Получить наиболее близкое к заданному разрешение из списка обоев
        """
        terms = funArgv.replace(" ", "").split(",")
        onlyfile = ""
        if len(terms) == 3:
            resol, wpath, onlyfile = terms
            if onlyfile != "file":
                raise self.raiseErrTemplate(
                    _("third parameter may be 'file' only"))
        elif len(terms) == 2:
            resol, wpath = terms
        else:
            raise self.raiseErrTemplate(
                _("function support two or three parameters"))
        if not resol:
            resol = "1024x768"
        _wpath = wpath

        wpath = pathJoin(self._baseDir, wpath)
        if os.path.isdir(wpath):
            re_resol = re.compile("^(\d+)x(\d+)(-\d+(@\d+)?)?$")
            resol = re_resol.match(resol)
            if not resol:
                raise self.raiseErrTemplate(
                    _("the first parameter must be the resolution"))

            re_resol = re.compile(".*?(\d+)x(\d+).*")
            res = [(int(x.group(1)), int(x.group(2)), x.group()) for x in
                [re_resol.search(y) for y in listDirectory(wpath)] if x]           
            width = int(resol.group(1))
            height = int(resol.group(2))
            gep = sqrt(height ** 2 + width ** 2)
            k = float(width) / float(height)
            if res:
                # наиболее подходящее разрешение:
                # минимальная разность между пропорциями (отношение ширины к высоте)
                # минимальная разность между размерами (гепотенуза)
                near_res = min(res,
                               key=lambda x: (abs(x[0] / float(x[1]) - k),
                                              abs(gep - sqrt(
                                                  x[0] ** 2 + x[1] ** 2))))
                replace = near_res[2]
                if not onlyfile:
                    replace = pathJoin(_wpath, replace)
            else:
                replace = ""
        else:
            if os.path.exists(wpath):
                if onlyfile:
                    replace = os.path.basename(_wpath)
                else:
                    replace = _wpath
            else:
                replace = ""
        textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def funcIni(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """локальная функция записывает и считывает значение переменной 

        из ini файла ~./calculate/ini.env
        """
        # Создаем директорию
        if not os.path.exists(self.pathConfigIni):
            os.makedirs(self.pathConfigIni)
            os.chown(self.pathConfigIni, self.uid, self.gid)
        termsRaw = funArgv.split(",")
        flagFirst = True
        terms = []
        for term in termsRaw:
            if flagFirst:
                terms.append(term.replace(" ", ""))
                flagFirst = False
            else:
                val = term.strip()
                # Флаг (не найдены кавычки)
                flagNotFoundQuote = True
                for el in ('"', "'"):
                    if val.startswith(el) and val.endswith(el):
                        terms.append(val[1:-1])
                        flagNotFoundQuote = False
                        break
                if flagNotFoundQuote:
                    if not val:
                        terms.append(None)
                    else:
                        terms.append(val)
        # Название локальной переменной
        if self.objVar.Get('cl_action') in ('image', 'system'):
            oldbasedir = self._baseDir
            old_profile = self.objVar.Get('cl_make_profile')
            old_config = self.objVar.Get('cl_emerge_config')
            try:
                if self.objVar.Get('cl_action') == 'image':
                    self._baseDir = self.objVar.Get('builder.cl_builder_path')
                else:
                    self._baseDir = self.objVar.Get('cl_chroot_path')
                dvc = DataVarsConfig(self._baseDir)
                self.objVar.Set('cl_make_profile',
                                dvc.Get('cl_make_profile'), force=True)
                self.objVar.Set('cl_emerge_config',
                                dvc.Get('cl_emerge_config'), force=True)
                self.read_other_ini()
            finally:
                self._baseDir = oldbasedir
                self.objVar.Set('cl_make_profile', old_profile, force=True)
                self.objVar.Set('cl_emerge_config', old_config, force=True)
        else:
            self.read_other_ini()
        nameLocVar = terms[0]
        namesVar = nameLocVar.split(".")
        if len(namesVar) == 1:
            nameLocVar = "main.%s" % nameLocVar
        elif len(namesVar) > 2:
            raise self.raiseErrTemplate()
        replace = b""
        # Получаем время модификации конфигурационного файла
        curTime = self.getTimeFile(self.fileConfigIni)
        if 1 <= len(terms) <= 3:
            if self.timeIni != curTime:
                # читаем переменные из файла
                self.prevDictIni = self.loadVarsIni(self.fileConfigIni)
                self.currDictIni = {}
                self.currDictIni.update(self.prevDictIni)
                self.timeIni = self.getTimeFile(self.fileConfigIni)
            section, op, varname = nameLocVar.partition(".")
            value = self.upperIni.get(section, varname, raw=True, fallback=None)
            if value is None:
                if nameLocVar in self.currDictIni.keys():
                    if self.currDictIni[nameLocVar] is not None:
                        value = self.currDictIni[nameLocVar]
            if value is None:
                value = self.lowerIni.get(section, varname, raw=True,
                                          fallback="")
        else:
            raise self.raiseErrTemplate()
        if len(terms) == 1:
            replace = value.encode("UTF-8")
        elif len(terms) == 2:
            # Значение локальной переменной
            valueLocVar = terms[1]
            self.currDictIni[nameLocVar] = valueLocVar
        elif len(terms) == 3:
            if not terms[2] in ['url', 'purl', 'unicode', 'hexcode']:
                raise self.raiseErrTemplate(
                    _("the third argument of the function is neither "
                      "'url' or 'purl' or 'unicode'"))
            if terms[1]:
                raise self.raiseErrTemplate()
            if value:
                #awww yesss, legacy stuff
                if terms[2] in ('url', 'purl'):
                    replace = (value.encode("UTF-8").__repr__()[1:-1].replace(
                        '\\x', '%').replace(' ', '%20'))
                    if terms[2] == 'purl':
                        replace = replace.replace('/', '%2f')
                elif terms[2] == 'unicode':
                    replace = value.__repr__()[2:-1]
                elif terms[2] == 'hexcode':
                    replace = value.__repr__()[2:-1].replace('\\u0','\\x')

        replace = replace.decode("UTF-8")
        textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def _replace_hex(self, text):
        """
        Заменить в строке комбинацию \\x00 на символ
        """
        return re.sub(r'\\x([0-9a-fA-F]{2})',
                      lambda x: chr(int(x.group(1), 16)), text)

    def funcReplace(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """локальная функция заменяет в значении переменной old на new

        replace(old, new, name_var_template)

        одинарные и двойные кавычки должны быть обязательно использованы
        в первых двух аргументах old и new
        "test\ntest" - преобразование строки (строка с переводом)
        'test\ntest' - без преобразования (одна строка)
        """

        def getStrArgv(terms):
            """Определяет в двойных или одинарных кавычках параметры

            Результат [(тип, аргумент),...] [("double", arg1).]
            """
            listArgv = []
            for term in terms:
                if term.startswith('"') and term.endswith('"'):
                    replTerms = [(r"\'", "'"), (r'\"', '"'), (r'\n', '\n'),
                                 (r'\r', '\r'),
                                 (r'\t', '\t'), (r"\\", "\\")]
                    textArgv = term[1:-1]
                    for replTerm in replTerms:
                        textArgv = textArgv.replace(*replTerm)
                    textArgv = self._replace_hex(textArgv)
                    listArgv.append(textArgv)
                elif term.startswith("'") and term.endswith("'"):
                    listArgv.append(term[1:-1])
                else:
                    raise self.raiseErrTemplate()
            return listArgv

        terms = [x.strip() for x in funArgv.split(",")]
        if len(terms) != 3:
            raise self.raiseErrTemplate()
        listArgv = getStrArgv(terms[:2])
        old = listArgv[0]
        new = listArgv[1]
        nameVar = terms[2]
        # Получаем значение переменной
        if nameVar in localVars:
            value = str(localVars[nameVar])
        else:
            try:
                value = str(self.objVar.Get(nameVar))
            except Exception:
                raise self.raiseErrTemplate(
                    _("template variable '%s' not found") \
                    % str(nameVar))
        replace = value.replace(old, new)
        textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def funcEnv(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """Функция шаблона env(), выдает значение переменной из env файлов
        """
        terms = funArgv.replace(" ", "").split(",")
        if len(terms) != 1:
            raise self.raiseErrTemplate()
        nameVar = terms[0]
        if nameVar in self.valuesVarEnv:
            replace = self.valuesVarEnv[nameVar]
        else:
            # Получаем значение из env файлов
            value = self.objVar.getIniVar(nameVar)
            if value is False:
                raise self.raiseErrTemplate(self.getError())
            self.valuesVarEnv[nameVar] = value
            replace = value
        textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def funcServer(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """Функция шаблона info(), выдает значение опций сервиса

        из /var/calculate/remote/calculate.env
        """
        terms = funArgv.replace(" ", "").split(",")
        if len(terms) == 0 or len(terms) > 2:
            raise self.raiseErrTemplate()
        nameLocalVar = ""
        if len(terms) == 2:
            if not terms[1]:
                raise self.raiseErrTemplate()
            nameLocalVar = terms[1]
        textLine = terms[0]
        vals = textLine.split(".")
        if len(vals) != 2:
            raise self.raiseErrTemplate()
        if [x for x in vals if not x.strip()]:
            raise self.raiseErrTemplate()
        service, option = vals
        if not service or not option:
            raise self.raiseErrTemplate()
        if not self.optionsInfo:
            # файл /var/calculate/remote/server.env
            envFile = self.objVar.Get("cl_env_server_path")
            # получаем словарь всех информационных переменных
            if self.convObj:
                optInfo = self.convObj.convert()
            else:
                optInfo = self.objVar.getRemoteInfo(envFile)
            if optInfo is False:
                raise self.raiseErrTemplate()
            if optInfo:
                self.optionsInfo = optInfo
        replace = ''
        if service in self.optionsInfo and option in self.optionsInfo[service]:
            value = self.optionsInfo[service][option]
            if nameLocalVar:
                localVars[nameLocalVar] = value
            else:
                replace = value
        elif nameLocalVar:
            localVars[nameLocalVar] = ""
        textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def funcGroups(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """Функция шаблона groups(),
        проверяет нахождение пользователя в группах, если находится выдает '1'
        """
        terms = [x.strip() for x in funArgv.split(",")]
        groupNames = set(terms)
        userGroups = set(self.groups)
        replace = ""
        if groupNames & userGroups:
            replace = "1"
        textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def funcBelong(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        self.printWARNING(_("Function '{funcname}' used by {template} "
                            "is deprecated and will be removed in the future"
                            ).format(funcname="belong", template=nameTemp))
        replace = ""
        return textTemplateTmp[:resS.start()] + replace + \
               textTemplateTmp[resS.end():]

    def funcMergepkg(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """
        Функция объединяющая выполнение merge и pkg
        :param funArgv:
        :param resS:
        :param localVars:
        :param textTemplateTmp:
        :param nameTemp:
        :return:
        """
        term = funArgv.replace(" ", "")
        funcPkg = term
        replace = self.funcMerge(funcPkg, resS, localVars, "", nameTemp)
        if replace == "1":
            replace = self.funcPkg(funcPkg, resS, localVars, "", nameTemp)
        return textTemplateTmp[:resS.start()] + replace + \
               textTemplateTmp[resS.end():]

    def funcMerge(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """
        Belong function use value in first arg and compare it 
        for all values in cl_merge_pkg.
        If cl_merge_pkg empty or first arg <=cl_belogn_pkg
        then "1" else ""
        """

        def uniq_warning(message):
            hashMessage = hashlib.md5(message).digest()
            if not hashMessage in self.alreadyInformed:
                self.printWARNING(message)
                self.alreadyInformed.append(hashMessage)

        def check_skip(pkgName):
            varname_map = {
                HParams.ActionType.Merge: "cl_setup_skip_merge",
                HParams.ActionType.Patch: "cl_setup_skip_patch",
                HParams.ActionType.Profile: "cl_setup_skip_profile",
            }
            skip_data_varname = varname_map.get(self.currentAction)
            skip_data = self.objVar.Get(skip_data_varname)
            pass_location = self.objVar.Get('cl_pass_location')
            if any(skip_data):
                for data in skip_data:
                    if len(data) != 2:
                        uniq_warning(
                            _("Wrong entry '{data}' for {var_name}").format(
                                data=",".join(data),
                                var_name=skip_data_varname))
                    else:
                        pkg, location = data
                        if fnmatch(pkgName, pkg) and (location == "*"
                                                      or location == pass_location):
                            return True
            return False

        term = funArgv.replace(" ", "")
        funcPkg = term
        funcPkg, spl, uses = funcPkg.partition('[')
        funcPkg, spl, slot = funcPkg.partition(":")
        if uses:
            uses = uses.rstrip("]")
        if not funcPkg:
            funcPkg = self.get_pkgname_by_filename(self.nameTemplate)
        self.currentBelong = funcPkg
        self.currentBelongSlot = slot
        if self.objVar.Get('cl_action') == 'patch':
            if funcPkg == "%s/%s" % (
                    self.objVar.Get('core.cl_core_pkg_category'),
                    self.objVar.Get('core.cl_core_pkg_name')):
                spec_slot = self.objVar.Get('core.cl_core_pkg_slot')
                spec_slot = spec_slot.partition('/')[0]
                if not slot or spec_slot == slot:
                    replace = self.objVar.Get('core.cl_core_pkg_version')
                    if uses:
                        from os import environ
                        pkg_use = environ.get("USE", "").split(" ")
                        for use in filter(None, uses.split(',')):
                            if (use[0] == "-" and use[1:] in pkg_use or
                                use[0] != "-" and use not in pkg_use):
                                replace = ""
                    if check_skip(funcPkg):
                        replace = ""
                else:
                    replace = ""
            else:
                replace = ""
        else:
            replace = ""
            pkgs = self.objVar.Get("cl_merge_pkg")
            if pkgs:
                pkgs = [x for x in pkgs if x]
                if slot:
                    for pkg in pkgs:
                        if ":" in pkg:
                            pkg, _, pkgslot = pkg.partition(":")
                            if pkg == funcPkg and pkgslot == slot:
                                replace = "1"
                                break
                        elif pkg == funcPkg:
                            replace = "1"
                            break
                else:
                    if funcPkg in [x.partition(":")[0] for x in pkgs]:
                        replace = "1"
            else:
                replace = "1"
            if replace == "1" and check_skip(funcPkg):
                replace = ""
        textTemplateTmp = textTemplateTmp[:resS.start()] + replace + \
                          textTemplateTmp[resS.end():]
        return textTemplateTmp

    def funcList(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """Функция шаблона list().
        Если первый аргумент является именем локальной или глобальной
        переменной и значение переменной является списком, выдает 
        элемент списка по второму аргументу индексу.
        Первый элемент имеет индекс 0
        """
        terms = funArgv.replace(" ", "").split(",")
        # У функции должно быть два аргумента
        if len(terms) != 2:
            raise self.raiseErrTemplate()
        # Название локальной или глобальной переменной
        nameLocVar = terms[0]
        strIndex = terms[1]
        try:
            try:
                intIndex = int(strIndex)
            except ValueError:
                raise TemplatesError(_("'%s' is not a number") % strIndex)
            if nameLocVar in localVars.keys():
                value = localVars[nameLocVar]
            else:
                try:
                    value = self.objVar.Get(nameLocVar)
                except Exception:
                    raise TemplatesError(_("error: variable %s does not exist")
                                         % str(nameLocVar))
            if not type(value) in (list, tuple):
                # Значение переменной не список или кортеж
                raise TemplatesError(_("value of %s is not a list or a tuple")
                                     % str(nameLocVar))
            if len(value) > intIndex:
                try:
                    replace = str(value[intIndex])
                except IndexError:
                    replace = ""
            else:
                replace = ""
            return textTemplateTmp[:resS.start()] + replace + \
                   textTemplateTmp[resS.end():]
        except TemplatesError as e:
            raise self.raiseErrTemplate(str(e))

    def funcDisk(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """Функция шаблона disk().
        Первый аргумент ищется в значении переменной os_disk_install
        (значение os_install_disk_mount - 
        список точек монтирования при установке)
        второй аргумент используется для поиска в переменной
        os_disk_второй_аргумент (значение os_disk_второй_аргумент - список)
        В os_install_disk_mount ищется первый аргумент, находим его индекс
        результат - элемент cписка из os_disk_второй_аргумент с этим индексом
        """
        terms = funArgv.replace(" ", "").split(",")
        # У функции должно быть два аргумента
        if len(terms) != 2:
            raise self.raiseErrTemplate()
        # Название глобальной переменной
        mountPoint = terms[0]
        lastElementVar = terms[1]
        if not mountPoint or mountPoint[:1] != "/":
            raise self.raiseErrTemplate(_("wrong %s") % lastElementVar)
        nameVar = "install.os_install_disk_mount"
        try:
            try:
                valueVar = self.objVar.Get(nameVar)
            except Exception:
                raise TemplatesError(
                    _("error: variable %s does not exist") % nameVar)
            nameElementVar = "install.os_install_disk_%s" % lastElementVar
            try:
                valueElementVar = self.objVar.Get(nameElementVar)
            except Exception:
                # Если переменная не существует
                nameElementVar = "install.os_disk_%s" % lastElementVar
                try:
                    valueElementVar = self.objVar.Get(nameElementVar)
                except Exception:
                    raise TemplatesError(_("wrong %s") % lastElementVar + "\n" +
                                         _("error: variable %s does not exist")
                                         % nameElementVar)

            for k, v in ((nameVar, valueVar),
                         (nameElementVar, valueElementVar)):
                if not type(v) in (list, tuple):
                    # Значение переменной не список или кортеж
                    raise TemplatesError(
                        _("value of %s is not a list or a tuple") % k)
            if len(valueVar) != len(valueElementVar):
                raise TemplatesError(
                    _("%(name)s does not match %(nameElement)s in size")
                    % {'name': nameVar, 'nameElement': nameElementVar})
            index = None
            for num, mPoint in enumerate(valueVar):
                if mountPoint == mPoint:
                    index = num
                    break
            if index is None:
                for num, mPoint in enumerate(valueVar):
                    if "/" == mPoint:
                        index = num
                        break
            if index is None:
                raise TemplatesError(
                    _("mount point '/' or '/%s' not found "
                      " in the value of variable os_disk_install") % mountPoint)
            replace = valueElementVar[index]
            return textTemplateTmp[:resS.start()] + replace + \
                   textTemplateTmp[resS.end():]
        except TemplatesError as e:
            raise self.raiseErrTemplate(str(e))

    def funcModule(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """Функция шаблона module(), выдает значение аттрибута api.

        аргумент:
            путь_к_атрибуту - путь к аттрибуту

        возможные пути:
            имя_пакета.var.имя_переменной - получаем значение переменной
            имя_пакета.имя_метода_api - выполнение метода, получение результата
            all.имя_метода_api - выполнение метода для всех пакетов с api
        """
        current_version = self.objVar.Get('cl_ver')
        try:
            if funArgv not in self.objVar.importedModules:
                importlib.import_module(
                    "calculate.%s.variables" % funArgv)
            if self.objVar.Get('cl_chroot_path') == "/":
                replace = current_version
            else:
                usrlib = SystemPath(self.objVar.Get('cl_chroot_path')).usrlib
                module_path = os.path.join(
                    usrlib, "python3.9/site-packages/calculate/%s/variables"
                    % funArgv)
                if os.path.exists(module_path):
                    pkg = "sys-apps/calculate-utils:3"
                    chroot_version = self.funcPkg(
                        pkg, re.search(".*", pkg), localVars, pkg, nameTemp)
                    t_chroot_version = getTupleVersion(chroot_version)
                    t_current_version = getTupleVersion(current_version)
                    if t_chroot_version < t_current_version:
                        replace = chroot_version
                    else:
                        replace = current_version
                else:
                    replace = ""
        except (ImportError, AttributeError):
            replace = ""

        return textTemplateTmp[:resS.start()] + replace + \
               textTemplateTmp[resS.end():]

    def raiseErrTemplate(self, message=""):
        """Возвращает ошибки при обработке функций шаблона"""
        if message:
            message = "%s\n" % message
        else:
            message = ""
        return TemplatesError(
            _("error in template %s") % self.nameTemplate + "\n" + \
            _("error, template term '%s'") % str(self.functText) + \
            " " + message)

    def applyFuncTemplate(self, textTemplate, nameTemplate):
        """Применяет функции к тексту шаблона"""
        # Локальные переменные
        localVars = {}
        # Имя обрабатываемого шаблона
        self.nameTemplate = nameTemplate
        # Регулярное выражение для поиска функции в шаблоне
        reFunc = self._reFunc

        textTemplateTmp = textTemplate
        flagIniFunc = False
        writeIniFunc = False

        def funcSearch(s):
            minlen = 1
            resS = re.search(self._rePreFuncPattern % minlen, s, re.M)
            while resS and resS.group().count("#-") != (resS.group().count("-#")
                    - resS.group().count("-#-")):
                minlen = len(resS.group()) - 2
                resS = re.search(self._rePreFuncPattern % minlen, s, re.M)
            if resS:
                funcblock = resS.group()
                resS = reFunc.search(s[:resS.end()])
                if not resS:
                    raise self.raiseErrTemplate(
                        _("wrong function syntax %s") % funcblock)
            return resS

        resS = funcSearch(textTemplateTmp)

        while resS:
            mark = textTemplateTmp[resS.start():resS.end()]
            self.functText = mark[self._deltVarStart:-self._deltVarEnd]
            funcName, spl, funcEnd = self.functText.partition("(")
            if funcName in self.namesTemplateFunction:
                # аргументы функции - '(' аргументы ')'
                funArgv = funcEnd.rpartition(")")[0]
                # вызов функции шаблона
                if "#-" in funArgv and "-#" in funArgv:
                    funArgv = self.applyVarsTemplate(funArgv, nameTemplate)
                    funArgv = self.applyFuncTemplate(funArgv, nameTemplate)
                textTemplateTmp = self.templateFunction[funcName](self, funArgv,
                                                                  resS,
                                                                  localVars,
                                                                  textTemplateTmp,
                                                                  nameTemplate)
                resS = funcSearch(textTemplateTmp)
                if funcName == "ini":
                    if "," in funArgv:
                        writeIniFunc = True
                    flagIniFunc = True
            else:
                raise self.raiseErrTemplate(
                    _("function of templates '%s' not found") \
                    % str(self.functText))
        if flagIniFunc:
            # Очистка файла в случае его ошибочного чтения
            if not self.prevDictIni and os.path.exists(self.fileConfigIni):
                with open(self.fileConfigIni, "r+") as FD:
                    FD.truncate(0)
                    FD.seek(0)
            # Если конф. файл модифицирован шаблоном
            curTime = self.getTimeFile(self.fileConfigIni)
            if curTime != self.timeIni:
                # Считаем переменные из конф. файла
                self.prevDictIni = self.loadVarsIni(self.fileConfigIni)
                self.currDictIni.update(self.prevDictIni)
                self.timeIni = curTime
            # Если словари переменных не совпадают
            if self.prevDictIni != self.currDictIni:
                # Запишем переменные в конфигурационный файл
                # Создание объекта парсера
                config = ConfigParser(strict=False)
                config.read(self.fileConfigIni, encoding="utf-8")
                comment_block = "\n".join(takewhile(lambda x: x.startswith("#"),
                                                    readLinesFile(
                                                        self.fileConfigIni)))
                for k, v in self.currDictIni.items():
                    if "." in k:
                        sect, op, k = k.rpartition('.')
                        if v is None:
                            if config.has_section(sect):
                                config.remove_option(sect, k)
                        else:
                            if not config.has_section(sect):
                                config.add_section(sect)
                            config[sect][k] = v

                for section in (x for x in config.sections() if not config[x]):
                    config.remove_section(section)
                with codecs.open(self.fileConfigIni, 'wb',
                                 'utf-8', 'ignore') as f:
                    if comment_block:
                        f.write(comment_block)
                        f.write('\n\n')
                    config.write(f)
                try:
                    oMode = getModeFile(self.fileConfigIni, mode="mode")
                    if oMode != self.modeConfigIni:
                        os.chmod(self.fileConfigIni, self.modeConfigIni)
                except OSError:
                    pass

                # читаем переменные из файла
                self.prevDictIni = self.loadVarsIni(self.fileConfigIni)
                self.currDictIni.update(self.prevDictIni)
                self.timeConfigsIni[self.fileConfigIni] = float(time.time())
                self.timeIni = self.getTimeFile(self.fileConfigIni)
            # Меняем владельца в случае необходимости
            if writeIniFunc and os.path.exists(self.fileConfigIni):
                uid, gid = getModeFile(self.fileConfigIni, "owner")
                if self.uid not in (uid, PORTAGEUID) or \
                                self.gid not in (gid, PORTAGEGID):
                    try:
                        os.chown(self.fileConfigIni, self.uid, self.gid)
                    except OSError:
                        self.setError(_("error") + " " +
                                      "'chown %d:%d %s'" % (self.uid, self.gid,
                                                            self.fileConfigIni))
        return textTemplateTmp


class ChangedFiles():
    """
    Object which contains modified files and package
    """
    FILE_MODIFIED, FILE_REMOVED, DIR_CREATED, DIR_REMOVED, DIR_EXISTS = 0, 1, 2, 3, 4

    def __init__(self):
        self.data = {}
        self.pkgs = set()

    def _createEntry(self, fn):
        if not fn in self.data:
            self.data[fn] = []

    def addObj(self, filename, action, pkg, slot=""):
        if slot:
            pkgslot = "{}:{}".format(pkg,slot)
        else:
            pkgslot = pkg
        self._createEntry(filename)
        self.data[filename].append((pkgslot, action))
        self.pkgs.add(pkgslot)

    def getPkgs(self):
        return self.pkgs

    def getPkgFiles(self, pkg):
        return [(x[0], x[1][0][1]) for x 
            in [(y[0], [z for z in y[1] if z[0] == pkg]) for y 
                in self.data.items()] if x[1]]

# modes work with configuration file
# T_ORIGIN - work with original config file
# T_CFG - work with last ._cfg file
# T_NEWCFG - new content has difference with (create new ._cfg file)
T_ORIGIN, T_CFG, T_NEWCFG = 0, 1, 2


class Template(_file, _terms, _warning, xmlShare, _shareTemplate):
    """Класс для работы с шаблонами

    На вход 2 параметра: объект хранения переменных, имя сервиса - не
    обязательный параметр

    """
    # Название файла шаблона директории
    templDirNameFile = ".calculate_directory"
    _titleList = ("Modified", "Processed template files" + ":")
    titleEnd_old = "For modify this file, create %(conf_path)s.clt template."
    titleEnd = "To modify this file, create a %(conf_path)s.clt template."
    protectPaths = []
    allContents = {}
    if "CONFIG_PROTECT" in os.environ:
        protectPaths = ["/etc"] + [x for x in os.environ["CONFIG_PROTECT"].split(" ") if x.strip()]
        protectPaths = [os.path.normpath(x) for x in protectPaths]

    @classmethod
    def removeComment(cls, text):
        re_comment = re.compile('(?:<!--|[{symb}]-*)\n'
                                '[{symb}]? {modified} .*\n'
                                '[{symb}]? {processed}\n'
                                '([{symb}]? /.*\n'
                                ')+(?:[{symb}]? (?:{endtitle}|{endtitle_old}).*\n)?'
                                r'(-->|[{symb}]-*)\n'.format(
            modified=cls._titleList[0],
            processed=cls._titleList[1],
            endtitle_old=cls.titleEnd_old % {'conf_path': '.*'},
            endtitle=cls.titleEnd % {'conf_path': '.*'},
            symb='"#'
        ))
        return re_comment.sub('', text)

    def hasError(self):
        return self.getError() or self.bHasError or (
            self.cltObj and self.cltObj.bHasError)

    def __init__(self, objVar, servDir=False, dirsFilter=(), filesFilter=(),
                 cltObj=True, cltFilter=True, printWarning=True,
                 printSUCCESS=lambda x: x, printWARNING=lambda x: x,
                 printERROR=lambda x: x, askConfirm=lambda x: x,
                 userProfile=False, dispatchConf=None,
                 critical=False):
        _file.__init__(self)
        # совместимость с вызовами из модулей предыдущих версий
        self.translator = RegexpLocalization("cl_templates3")
        if userProfile and objVar:
            objVar.Set('cl_protect_use_set', 'off', force=True)

        self.protectPaths = objVar.Get('cl_config_protect')
        self.dispatchConf = dispatchConf
        self.changedFiles = ChangedFiles()
        self.printSUCCESS = printSUCCESS
        self.formatFactory = FormatFactory(self)
        self.printERROR = printERROR
        self.critical = critical
        self.postmergePkgs = []
        self._locked_packages = {}
        if objVar and objVar.Get("cl_merge_pkg"):
            self._locked_packages = {
                x.partition(":")[0]: None
                for x in objVar.Get('cl_merge_pkg')
                if x
            }

        self.postmergeFile = "/var/lib/calculate/-postmerge"
        self.bHasError = False
        if printERROR:
            def newSetError(s):
                self.printERROR(s)
                self.bHasError = True

            self.setError = newSetError
        self.printWARNING = printWARNING
        self.askConfirm = askConfirm
        self.cltObj = None
        self.functObj = None
        self.mounts = None

        # Бесконченое ожидание завершения emerge если выполняется настройка пакетов
        #if objVar:
        #    if (not objVar.Get('cl_ebuild_phase')
        #        and emerge_running()
        #            and objVar.GetBool('install.ac_install_merge')):
        #        self.printWARNING(_("Waiting for emerge to be complete"))
        #        while emerge_running():
        #            time.sleep(1)
        # Предупреждения
        # self.warning = []
        # Печатать ли предупреждения о корневых шаблонах без cl_name==pkg
        self.printWarning = printWarning
        # Необрабатываемые директории
        self.dirsFilter = dirsFilter
        # Необрабатываемые файлы
        self.filesFilter = filesFilter
        _file.__init__(self)
        # Словарь для создания объектов новых классов по образцу
        self.newObjProt = {'proftpd': 'apache'}
        # Заголовок title
        self.__titleHead = "--------------------------------------\
----------------------------------------"
        self._titleBody = ""
        # Условия
        self._reTermBloc = re.compile(
            "#\?(?P<rTerm>(?:[a-z0-9_]+\.)?[a-zA-Z0-9\-_]+)"
            "(?P<func>\(((?:#-|-#|%s)+|)\))?"
            "(?P<lTerm>[><=!&\|]+"
            "(?:#-|-#|[><=!\|&\(\)%s])*)#"
            "\n?(?P<body>.+?\n*?)\n?#(?P=rTerm)#(?P<end>[ ,\t]*\n?)"
            % (self._reFunctionArgvText, self._reFunctionArgvInSquareBrackets),
            re.M | re.S)
        # Объект с переменными
        self.objVar = objVar
        self.recalculateBaseDir()
        # Последняя часть директории шаблона (имя сервиса: samba, mail)
        self._servDir = servDir
        if self._servDir:
            if self._servDir[0] != "/":
                self._servDir = "/" + self._servDir
            if self._servDir[-1] != "/":
                self._servDir += "/"
            self._servDir = os.path.split(self._servDir)[0]
        # Созданные директории
        self.createdDirs = []
        # Примененные файлы
        self.filesApply = []
        # номер обрабатываемого файла
        self.numberProcessTempl = 0
        # имя текущей программы
        _nameProgram = "Calculate Utilities"
        # версия текущей программы
        _versionProgram = self.objVar.Get("cl_ver")
        # имя и версия текущей программы
        self.programVersion = "%s %s" % (_nameProgram, _versionProgram)
        # Словарь директорий с количеством файлов шаблонов
        self.dictTemplates = {}
        # Общее количество шаблонов
        self.allTemplates = 0
        # Объект функций шаблона
        self.functObj = templateFunction(self.objVar)
        self.functObj.printSUCCESS = self.printSUCCESS
        self.functObj.printWARNING = self.printWARNING
        self.functObj.printERROR = self.printERROR
        if self.printERROR:
            self.functObj.setError = self.printERROR
        self.functObj.askConfirm = self.askConfirm
        # Метод применения функций к шаблонам
        self.applyFuncTemplate = self.functObj.applyFuncTemplate
        # Объект для определения типа файла шаблона
        self.typeFileObj = typeFile()
        # Глобальный словарь обработанных шаблонов файлов
        # {путь к конф. файлу:[имена шаблонов] ...}
        self.dictProcessedTemplates = {}
        if cltObj is True:
            # Объект templateClt
            self.cltObj = templateClt(self.objVar, self.postmergePkgs,
                                      printSUCCESS=self.printSUCCESS,
                                      printERROR=self.printERROR,
                                      printWARNING=self.printWARNING,
                                      askConfirm=self.askConfirm,
                                      critical=self.critical)
        elif cltObj:
            # Объект templateClt
            self.cltObj = cltObj
        else:
            # Объект templateClt
            self.cltObj = None
        # Фильтровать ли шаблоны clt по конфигурационным файлам обычных шаблонов
        self.cltFilter = cltFilter
        # autoupdate файлы
        self.autoUpdateFiles = []
        self.autoUpdateDirs = []

        self.protectedFiles = [
                                  pathJoin(self._baseDir, x)
                                  for x in self.objVar.Get('main.cl_env_path')
                                  ] + [self.functObj.fileConfigIni]
        # список выполненных файлов
        self.executedFiles = []

    def recalculateBaseDir(self):
        """Recalculate basedir and homedir"""
        # Базовая директория переноса шаблонов "/mnt/calculate" или "/" и.т.д
        self._baseDir = pathJoin(self.objVar.Get("cl_chroot_path"),
                                 self.objVar.Get("cl_root_path"))
        self._baseDir = os.path.normpath(self._baseDir)
        self.uid, self.gid, self.homeDir = self.getDataUser()
        # Домашняя директория, плюс базовая директория
        self.homeDir = pathJoin(self._baseDir, self.homeDir)
        if self.cltObj:
            self.cltObj.recalculateBaseDir()
        if self.functObj:
            self.functObj.recalculateBaseDir()

    def _addFile(self, filesApl, pkg=None, slot=None):
        """
        Add files to ChangedFiles
        """
        for fn in filesApl:
            if os.path.exists(fn):
                self.changedFiles.addObj(
                    fn, ChangedFiles.FILE_MODIFIED,
                    pkg or self.functObj.currentBelong,
                    slot or self.functObj.currentBelongSlot)
            else:
                self.changedFiles.addObj(
                    fn, ChangedFiles.FILE_REMOVED,
                    pkg or self.functObj.currentBelong,
                    slot or self.functObj.currentBelongSlot)

    def execute_command(self, cmd, lang):
        env = dict(os.environ)
        env['TERM'] = "linux"
        env['EINFO_QUIET'] = "yes"
        return process(cmd, lang=lang, envdict=dict(os.environ))

    def executeTemplate(self, code, execPath):
        """Execute template"""
        p = self.execute_command(execPath, self.objVar.Get('os_locale_locale'))
        if "/bin/bash" in code.partition('\n')[0]:
            p.write("""function translate() {
gettext -d cl_template "$*"
}
""")
        p.write(code)
        p.pipe.stdin.close()
        for line in p.readByLine():
            if line:
                line = self.translator.translate(line)
                self.printSUCCESS(line.strip())
        p.pipe.wait()
        if p.success():
            self.executedFiles.append((code, execPath))
            errdata = p.readerr().rstrip()
            if errdata:
                for line in errdata.split('\n'):
                    if line:
                        line = self.translator.translate(line)
                    self.printWARNING(line.strip())
            return True
        else:
            errdata = p.readerr().rstrip()
            if errdata:
                for line in errdata.split('\n'):
                    if line:
                        line = self.translator.translate(line)
                    self.printERROR(line.strip())
            return False

    def __octToInt(self, strOct):
        """Преобразование восьмеричного в целое (ввод строка, вывод число)"""
        if strOct:
            try:
                # res = string.atoi(strOct, 8)
                res = int(strOct, 8)
            except ValueError:
                self.setError(_("Invalid oct value: ") + str(strOct))
                return False
            return res
        else:
            self.setError(_("Empty oct value"))
            return False

    def getTemplateType(self):
        """выдать тип шаблона (text, bin)"""
        return self.getFileType(self.nameFileTemplate)

    def getFileType(self, fileName):
        """выдать тип файла (text, bin)"""
        isBin = self.typeFileObj.isBinary(fileName)
        if isBin is True:
            typeTemplate = "bin"
        elif isBin is False:
            typeTemplate = "text"
        else:
            self.setError(_("ERROR") + ": getFileType()")
            self.setError(isBin)
            return False
        return typeTemplate

    def createDir(self, dirName, mode=False, uid=False, gid=False):
        """Создает директорию"""
        if os.access(dirName, os.F_OK):
            return True
        else:
            dMode = False
            prevDir, tmpSubdir = os.path.split(dirName)
            createDirs = []
            while not os.access(prevDir, os.F_OK) and prevDir:
                createDirs.append(prevDir)
                prevDir = os.path.split(prevDir)[0]
            try:
                dUid, dGid = getModeFile(prevDir, "owner")
            except OSError:
                self.setError(_("No access to the directory: ") + prevDir)
                return False
            if not mode is False:
                dMode = mode
            if not uid is False:
                dUid = uid
            if not gid is False:
                dGid = gid
            createDirs.reverse()
            for nameDir in createDirs:
                try:
                    if dMode:
                        os.mkdir(nameDir, dMode)
                        os.chmod(nameDir, dMode)
                    else:
                        os.mkdir(nameDir)
                    self.chownConfDir(nameDir, dUid, dGid, None)
                except OSError:
                    self.setError(_("Failed to create the directory: ")
                                  + nameDir)
                    return False
            try:
                if dMode:
                    os.mkdir(dirName, dMode)
                    os.chmod(dirName, dMode)
                else:
                    os.mkdir(dirName)
                self.chownConfDir(dirName, dUid, dGid, None)
                createDirs.append(dirName)
            except OSError:
                self.setError(_("Failed to create the directory: ")
                              + dirName)
                return False
            return createDirs

    reBlock = re.compile(
        "#\?(?P<rTerm>(?:[a-z0-9_]+\.)?[a-zA-Z0-9\-_]+).*?#(?P=rTerm)#"
        "(?:[ ,\t]*\n?)",
        re.S | re.M)

    def applyTermsTemplate(self, textTemplate, nameTemplate):
        """ Применяет условия, к условным блокам текста
        """

        def function(text):
            """Функция обработки функций в заголовке"""
            return self.applyFuncTemplate(text, nameTemplate)

        def searchBlock(s):
            resS = self.reBlock.search(s)
            if resS:
                funcblock = resS.group()
                resS = self._reTermBloc.search(textTemplateTmp[:resS.end()])
                if not resS:
                    raise TemplatesError(
                        "Wrong conditional block: %s" % funcblock)
            return resS

        textTemplateTmp = textTemplate
        resS = searchBlock(textTemplateTmp)
        while resS:
            mark = resS.group(0)
            body = resS.group("body")
            end = resS.group("end")
            notbody = ""
            elseblock = "#!%s#" % resS.group("rTerm")
            if elseblock in body:
                data = re.split("\n?%s\n?" % elseblock, body)
                body = data[0]
                notbody = "".join(data[1:]) + end
            body = body + end
            parent = resS.group("func")
            if not parent:
                parent = ""
            term = resS.group("rTerm") + parent + \
                   resS.group("lTerm")
            if self._equalTerm(term, _("invalid template content: ") + \
                    nameTemplate, function):
                textTemplateTmp = textTemplateTmp.replace(mark, body)
            else:
                textTemplateTmp = textTemplateTmp.replace(mark, notbody)
            resS = searchBlock(textTemplateTmp)
        return textTemplateTmp

    def getNeedTemplate(self, fileTemplate):
        """Применяем правила к названию файла"""
        dirP, fileP = os.path.split(fileTemplate)
        if fileP:
            spFile = fileP.split("?")
            if len(spFile) > 1:
                flagTrue = False
                for term in spFile[1:]:
                    if self._equalTerm(term, _("invalid template name: ") + \
                            fileTemplate):
                        flagTrue = True
                        break
                if flagTrue:
                    return True
                else:
                    return False
            else:
                return True
        else:
            self.setError(_("invalid template name: ") + str(fileTemplate))
            return False

    def getTitle(self, comment, commentList, configPath=""):
        """Выдает заголовок шаблона ( версия и.т.д)"""
        origConfigPath = PkgContents.reCfg.sub("/", configPath)
        if self._baseDir != "/":
            lenBaseDir = len(self._baseDir)
            commentList = [x[lenBaseDir:]
                           if x.startswith(self._baseDir)
                           else x for x in commentList]
        if configPath and self.protectPaths:
            for protectPath in self.protectPaths:
                if self._baseDir != "/":
                    lenBaseDir = len(self._baseDir)
                    if len(configPath) > lenBaseDir and \
                                    configPath[:lenBaseDir] == self._baseDir:
                        configPath = configPath[lenBaseDir:]
                if configPath.startswith(protectPath + "/"):
                    if not any(origConfigPath.endswith(x) for x in
                               ("/calculate.env", "/ini.env", "/custom")):
                        commentList = commentList + \
                                      [self.titleEnd % {
                                          'conf_path': origConfigPath}]
                    break
        if comment:
            commentFirst = comment
            commentInsert = comment
            commentLast = comment
            flagList = False
            # В случае открывающего и закрывающего комментария
            if type(comment) == tuple and len(comment) == 2:
                commentFirst = comment[0]
                commentInsert = ""
                commentLast = comment[1]
                flagList = True
            if flagList:
                self._titleBody = commentFirst + "\n"
            else:
                self._titleBody = commentFirst + self.__titleHead + "\n"
            z = 0
            flagFirst = True
            for com in list(self._titleList) + [""] * (len(commentList)):
                if com:
                    if flagFirst:
                        self._titleBody += commentInsert + " " + com + " " + \
                                           self.programVersion + "\n"
                        flagFirst = False
                    else:
                        self._titleBody += commentInsert + " " + com + "\n"
                else:
                    self._titleBody += commentInsert + " " + \
                                       commentList[z] + "\n"
                    z += 1
            if flagList:
                self._titleBody += commentLast + "\n"
            else:
                self._titleBody += commentLast + self.__titleHead + "\n"
            return self._titleBody
        else:
            return ""

    def changeMergePackage(self, package):
        return True

    def numberAllTemplates(self, number):
        """Количество шаблонов

        Вызов происходит перед наложением шаблонов
        в момент вызова в number находится количество обрабатываемых файлов
        Наследуемая функция
        Используется для отображения прогресса при наложениии шаблонов
        """
        return True

    def numberProcessTemplates(self, number):
        """Номер текущего обрабатываемого шаблона

        Вызов происходит при наложении шаблона
        в момент вызова в number находится номер обрабатываемого шаблона
        Наследуемая функция
        Используется для отображения прогресса при наложениии шаблонов
        """
        return True

    def templateModify(self):
        """
        Files which created by apping templates
        """
        return True

    def fixNameFileConfig(self, origfilename):
        """Support ._cfg0000 files for postinst"""
        # if self.objVar.Get('cl_ebuild_phase') != 'postinst':
        #    return origfilename
        directory, filename = os.path.split(origfilename)
        for i in range(0, 9999):
            if not os.path.exists(os.path.join(directory,
                                               "._cfg%04d_%s" % (i, filename))):
                if i:
                    filename = os.path.join(directory,
                                            "._cfg%04d_%s" % (i - 1, filename))
                    #if not os.path.exists(origfilename):
                    #    return origfilename
                    # origstat = os.stat(origfilename)[stat.ST_CTIME]
                    # newstat = os.stat(filename)[stat.ST_CTIME]
                    self.configMode = T_CFG
                    return filename
                return origfilename
        return origfilename

    def getHeaderText(self, text, binary=False):
        textLines = text.splitlines()
        paramLine = ""
        if textLines:
            textLine = textLines[0]
            reg = "\s*#\s*calculate\s+\\\\?|\s*#\s*calculate\\\\?$" if not binary \
                else b"\s*#\s*calculate\s+\\\\?|\s*#\s*calculate\\\\?$"
            rePar = re.compile(reg, re.I)
            reP = rePar.search(textLine)
            if reP:
                reg = r"\A([^\\\n]*\\\n)+[^\n]*\n*" if not binary \
                    else rb"\A([^\\\n]*\\\n)+[^\n]*\n*"
                reLns = re.compile(reg, re.M)
                reLs = reLns.search(text)
                if reLs:
                    paramLine = text[reP.end():reLs.end()]
                    if binary:
                        paramLine = paramLine.decode("UTF-8")
                    paramLine = paramLine.replace("\\", " ")
                else:
                    paramLine = textLine[reP.end():]
                    if binary:
                        paramLine = paramLine.decode("UTF-8")
        return paramLine

    def getTemplateDirs(self, dirsTemplates):
        """Check template variable cl_name in first directories and files"""
        skipDirs = []
        skipTemplates = []
        debug = False
        for dirsTemplate in dirsTemplates:
            filesAndDirs = [os.path.join(dirsTemplate, x) for x in listDirectory(dirsTemplate)]
            for dirFile in filesAndDirs:
                if os.path.isdir(dirFile):
                    flagDir = True
                    templatePath = os.path.join(dirFile, self.templDirNameFile)
                    if not os.path.isfile(templatePath):
                        skipDirs.append(dirFile)
                        continue
                else:
                    flagDir = False
                    templatePath = dirFile
                if os.path.isfile(templatePath):
                    if self.getFileType(templatePath) == "bin":
                        skipTemplates.append(dirFile)
                    else:
                        with open(templatePath, "r") as f:
                            textTemplate = f.read()
                        if textTemplate:
                            headerLine = self.getHeaderText(textTemplate)
                            if headerLine:
                                if not debug and \
                                        not "cl_name==" in headerLine and \
                                        not "ac_" in headerLine:
                                    if flagDir:
                                        skipDirs.append(dirFile)
                                    else:
                                        skipTemplates.append(dirFile)
                            else:
                                if flagDir:
                                    skipDirs.append(dirFile)
                                else:
                                    skipTemplates.append(dirFile)
                        else:
                            skipTemplates.append(dirFile)
        if skipDirs or skipTemplates:
            # print warning
            self.printWARNING(_("No conditions for checking the value of "
                                "an action variable"))
            skipDirTemplates = []
            for skipDir in skipDirs:
                skipTempl = os.path.join(skipDir, self.templDirNameFile)
                if os.path.isfile(skipTempl):
                    skipDirTemplates.append(skipTempl)
            if skipTemplates or skipDirTemplates:
                self.printWARNING(_("Skipped templates:"))
            for skipTemplate in skipTemplates + skipDirTemplates:
                self.printWARNING(" " * 6 + skipTemplate)
            if skipDirs:
                self.printWARNING(_("Skipped directories:"))
            for skipDir in skipDirs:
                self.printWARNING(" " * 6 + skipDir)
            self.printWARNING("")
            self.printWARNING(_("Headers of directory templates and headers "
                                "of files on the first level should include "
                                "an action variable"))
            self.printWARNING(_("Example:"))
            self.printWARNING("# Calculate ac_install_merge==on")
        return skipDirs + skipTemplates

    def lock_package(self, pkg, fuser=None):
        pkg = pkg.partition(":")[0]
        if pkg not in self._locked_packages:
            category, _, pkg = pkg.partition("/")
            pkglockfile = ("/var/calculate/tmp/portage/"
                "{}/.{}.calculate_lockfile".format(category, pkg))
            ipcfile = ("/var/db/pkg/"
                "{}/.{}*.portage_lockfile".format(category, pkg))
            if os.path.exists(pkglockfile):
                fuser = fuser or FUser()
                if any(fuser.search(ipcfile)):
                    return False
            l = Locker(fn=pkglockfile, timeout=0)
            if l.acquire():
                self._locked_packages[pkg] = l
                return True
            return False
        return True

    def unlock_packages(self):
        for pkg, l in self._locked_packages.items():
            if l:
                l.remove()
        self._locked_packages = {}

    @catch_no_space_left
    @post_unlock_packages
    def applyTemplates(self, progress=True, rerun=True):
        """Применяет шаблоны к конфигурационным файлам"""
        def createDictTemplates(path, prefix, dictTemplates):
            """Создает словарь {"директория":"кол-во шаблонов" ...}

            и считает общее количество шаблонов
            """
            # Количество шаблонов
            self.allTemplates += 1
            dirTemplate = os.path.split(path)[0]
            while True:
                if dirTemplate in dictTemplates.keys():
                    dictTemplates[dirTemplate] += 1
                else:
                    dictTemplates[dirTemplate] = 1
                if dirTemplate == prefix:
                    break
                dirTemplate = os.path.split(dirTemplate)[0]
            return dictTemplates

        self.clearErrors()
        self.clearWarnings()
        if not self.objVar.defined("cl_template_path_use"):
            self.setError(_("undefined variable: ") + "cl_template_path_use")
            return False
        dirsTemplates = [
            os.path.realpath(x) for x in self.objVar.Get("cl_template_path_use")]
        # Созданные директории
        self.createdDirs = []
        # Примененные файлы
        self.filesApply = []
        # Номер применяемого шаблона
        self.numberProcessTempl = 0
        # Словарь директорий с количеством файлов шаблонов
        self.dictTemplates = {}
        # Количество шаблонов
        self.allTemplates = 0
        # Установка по умолчанию аттрибутов для функциии шаблонов ini()
        # Время доступа к конфигурационному файлу функции шаблона ini()
        self.functObj.timeIni = -1
        # Первоначальный словарь переменных для ini()
        self.functObj.prevDictIni = {}
        # Текущий словарь переменных для ini()
        self.functObj.currDictIni = {}
        self.functObj.currentBelong = ""
        self.functObj.currentBelongSlot = ""
        self.functObj.currentAction = HParams.ActionType.Merge
        # Словарь времен модификации env файлов для ini()
        self.functObj.timeConfigsIni = {}
        if self._servDir:
            tmpDirsTemplates = []
            for dirP in dirsTemplates:
                dirTempl = dirP + self._servDir
                if os.access(dirTempl, os.F_OK):
                    # Если директория существует
                    tmpDirsTemplates.append(dirTempl)
            dirsTemplates = tmpDirsTemplates
        scanObj = scanDirectory()
        scanObj.processingFile = lambda x, y: createDictTemplates(x, y,
                                                                  self.dictTemplates)
        # Считаем количество шаблонов
        dirsTemplatesExists = [x for x in dirsTemplates if os.path.exists(x)]
        if not dirsTemplatesExists and not self.cltObj:
            return self.createdDirs, self.filesApply
        
        # check cl_name in first template dirs and files
        skipTemplates = self.getTemplateDirs(dirsTemplatesExists)
        # if not os.environ.get("EBUILD_PHASE","") and progress:
        #    for dirTemplate in dirsTemplatesExists:
        #        scanObj.scanningDirectory(dirTemplate)
        # Считаем количество шаблонов clt
        if self.cltObj:
            # Считаем количество шаблонов clt
            self.cltObj.countsNumberTemplates()
            # не считать количество файлов в объекте self.cltObj
            self.cltObj.checkNumberTemplate = False
            # начальный номер clt шаблона
            self.cltObj.numberProcessTempl = self.allTemplates
            # метод показывающий номер clt шаблона
            self.cltObj.numberProcessTemplates = self.numberProcessTemplates
            # метод показывающий номер clt шаблона
            self.cltObj.templateModify = self.templateModify
            # общее количество шаблонов
            self.allTemplates += self.cltObj.allTemplates
            self.cltObj.allTemplates = self.allTemplates
        self.numberAllTemplates(self.allTemplates)
        # Обрабатываем шаблоны


        locationPath = dict(self.objVar.ZipVars('main.cl_template_path',
                                                'main.cl_template_location'))
        for dirTemplate in dirsTemplatesExists:
            self.objVar.Set('cl_pass_location',
                            locationPath.get(dirTemplate, dirTemplate),
                            force=True)
            if self.scanningTemplates(dirTemplate,
                                      skipTemplates=skipTemplates) is False:
                break
        
        if self.cltObj:
            self.objVar.Set('cl_pass_location', 'clt', True)
            # Созданные директории
            self.cltObj.createdDirs = self.createdDirs
            # Примененные файлы
            self.cltObj.filesApply = self.filesApply
            # Словарь директорий с количеством файлов шаблонов
            self.cltObj.dictTemplates = self.dictTemplates
            # Количество шаблонов
            self.cltObj.allTemplates = self.allTemplates
            # Установка по умолчанию аттрибутов для функциии шаблонов ini()
            # Время доступа к конфигурационному файлу функции шаблона ini()
            self.cltObj.functObj = self.functObj
            self.cltObj.protectedFiles = self.protectedFiles
            # Метод применения функций к шаблонам
            self.cltObj.applyFuncTemplate = self.functObj.applyFuncTemplate
            # Словарь примененных файлов шаблонов
            self.cltObj.dictProcessedTemplates = self.dictProcessedTemplates
            self.cltObj.changedFiles = self.changedFiles
            self.cltObj.autoUpdateFiles = self.autoUpdateFiles
            self.cltObj.autoUpdateDirs = self.autoUpdateDirs
            if self.cltFilter:
                # Шаблоны + .clt которые будут  применены
                self.cltObj.filterApplyTemplates = {}
                if self.objVar.Get('cl_merge_set') == "on":
                    for pkg in self.objVar.Get('cl_merge_pkg'):
                        if not pkg:
                            continue
                        atom = list(sorted(getInstalledAtom(pkg)))
                        if atom:
                            pkgContents = PkgContents("{CATEGORY}/{PF}".format(
                                **atom[-1]))
                            for filename in pkgContents.content.keys():
                                if not filename in self.cltObj.filterApplyTemplates:
                                    self.cltObj.filterApplyTemplates[
                                        filename] = []
                                self.cltObj.filterApplyTemplates[
                                    filename].append(pkg)
                for filename, pkgs in self.changedFiles.data.items():
                    filename = PkgContents.reCfg.sub("/", filename)
                    if not filename in self.cltObj.filterApplyTemplates:
                        self.cltObj.filterApplyTemplates[filename] = []
                    pkgs = [x for x in [z[0] for z in pkgs] 
                        if x not in self.cltObj.filterApplyTemplates[filename]]
                    self.cltObj.filterApplyTemplates[filename].extend(pkgs)
            old_mod = self.objVar.defaultModule
            try:
                self.objVar.defaultModule = "install"
                self.cltObj.applyTemplatesClt()
            finally:
                self.objVar.defaultModule = old_mod
        
        if ((self.objVar.Get('cl_merge_pkg') or
                     self.objVar.Get('cl_action') in (
                         "sync", "domain", "server_setup",
                         "undomain")) and
                self.objVar.Get('cl_merge_pkg_new')):

            skip_pkglist = []
            if self.objVar.Get('cl_ebuild_phase'):
                new_pkglist = []
                for pkgn in self.objVar.Get('cl_merge_pkg_new'):
                    if not pkgn:
                        continue
                    if self.lock_package(pkgn):
                        new_pkglist.append(pkgn)
                    else:
                        skip_pkglist.append(pkgn)

                self.objVar.Set('cl_merge_pkg_new', new_pkglist, force=True)
            else:
                new_pkglist = self.objVar.Get('cl_merge_pkg_new')

            if new_pkglist:
                self.objVar.Set('cl_root_path',
                                self.objVar.Get('cl_root_path_next'), force=True)
                self.recalculateBaseDir()

                self.objVar.Set('cl_merge_pkg_pass', list(
                    set(self.objVar.Get('cl_merge_pkg_pass')) |
                    set(self.objVar.Get('cl_merge_pkg')) |
                    set(skip_pkglist) |
                    set(self.objVar.Get('cl_merge_pkg_new'))), force=True)
                self.objVar.Set('cl_merge_pkg',
                                self.objVar.Get('cl_merge_pkg_new'), force=True)
                self.objVar.Set('cl_merge_pkg_new', [], force=True)
                createdDirs = self.createdDirs
                filesApply = self.filesApply
                self.changeMergePackage(self.objVar.Get('cl_merge_pkg'))
                self.applyTemplates(rerun=False)
                createdDirs.extend(self.createdDirs)
                filesApply.extend(self.filesApply)
                self.filesApply = filesApply
                self.createdDirs = createdDirs
        if rerun:
            if self.cltObj:
                self.queueExecute.extend(self.cltObj.queueExecute)
            for processor, text, nameTemplate in self.queueExecute:
                if not self.executeTemplate(text, processor):
                    self.setError(_("Failed to execute") + _(": ") +
                                  nameTemplate)
                    return False
            self.queueExecute = []
            self.filesApply = list(set(self.filesApply))
            if (self.objVar.Get('cl_root_path') == '/' and
                        self.objVar.Get('cl_chroot_path') == '/'):
                post_script = []
                if any("/etc/env.d" in x for x in self.filesApply):
                    post_script.append(
                        "LANG=C /usr/sbin/env-update --no-ldconfig")
                    post_script.append("source /etc/profile")
                root_type = self.objVar.Get('install.os_install_root_type')
                if (root_type != "livecd" and
                        any("/etc/locale.gen" in x for x in self.filesApply)):
                    post_script.append("/usr/sbin/locale-gen &>/dev/null")
                self.executeTemplate("\n".join(post_script), "/bin/bash")
            if self.objVar.Get('cl_protect_use_set') == 'on':
                self.updateProtectedFiles()
            if self.objVar.Get('cl_verbose_set') == 'on' and self.filesApply:
                self.verboseOutput([x for x in self.filesApply if not x.endswith('/ini.env')]) 
        self.objVar.Set('cl_merge_pkg_pass', [], force=True)
        self.objVar.Set('cl_merge_pkg_new', [], force=True)
        self.objVar.Set('cl_merge_pkg', [], force=True)
        return self.createdDirs, self.filesApply

    def verboseOutput(self, filesApply):
        """
        Verbose output applied templates
        """
        if not filesApply:
            return
        self.printWARNING(_("Calculate Utilities have changed files") + _(":"))
        reGrey = re.compile(r"\._cfg\d{4}_")
        rootPath = self.objVar.Get('cl_root_path')
        for fn in sorted(list(set(filesApply))):
            if rootPath != '/' and self.objVar.Get('cl_action') == 'patch' and \
                    fn.startswith(rootPath):
                fn = fn[len(rootPath) + 1:]
            if reGrey.search(fn):
                self.printSUCCESS("&nbsp;" * 5 + \
                                  "<font color=\"dark gray\">%s</font>" % fn)
            else:
                self.printSUCCESS("&nbsp;" * 5 + fn)

    def updateProtectedFiles(self):
        """
        Update ._cfg0000 files
        """
        if self.objVar.Get('cl_ebuild_phase') == 'compile':
            return
        chrootPath = self.objVar.Get('cl_chroot_path')
        cfgs = getCfgFiles(self.objVar.Get('cl_config_protect'),
                           prefix=chrootPath)
        reverse_cfgs = {y[0][1]: x for x, y in cfgs.items()}
        autoUpdateDict = {}
        get_digest = lambda x: hashlib.md5(readFile(x, binary=True)).hexdigest()
        for pkg in list(set(filter(None, list(self.changedFiles.getPkgs()) +
                self.objVar.Get('cl_merge_pkg')))):
            atom = list(sorted(getInstalledAtom(pkg, prefix=chrootPath)))
            if atom:
                pkgContents = PkgContents("{CATEGORY}/{PF}".format(
                    **atom[-1]), prefix=chrootPath)
                protected = []
                checked_map = {}
                for filename, action in self.changedFiles.getPkgFiles(pkg):
                    origFn = pkgContents.origFileName(filename)
                    if origFn in self.protectedFiles:
                        pkgContents.removeObject(filename)
                        protected.append(origFn)
                        continue
                    if action in (ChangedFiles.FILE_MODIFIED,
                                  ChangedFiles.DIR_CREATED,
                                  ChangedFiles.DIR_EXISTS):
                        orig_filename = reverse_cfgs.get(filename, None)
                        if orig_filename:
                            checked_map[orig_filename] = (
                                get_digest(filename) == get_digest(
                                    orig_filename))
                        # не давать править CONTENTS
                        if (orig_filename is None or
                                not checked_map[orig_filename]):
                            pkgContents.addObject(filename)
                    elif action in (ChangedFiles.FILE_REMOVED,
                                    ChangedFiles.DIR_REMOVED):
                        pkgContents.removeObject(filename)
                files = set([pathJoin(chrootPath, x) for x 
                    in pkgContents.content.keys()] + protected)
                if (self.objVar.Get('cl_dispatch_conf') != 'usenew' and
                            self.objVar.Get('cl_autoupdate_set') != "on"):
                    notUpdate = files - set(self.autoUpdateFiles)
                    files &= set(self.autoUpdateFiles)
                    for filename in list(notUpdate & set(cfgs.keys())):
                        equal = checked_map.get(
                            filename,
                            get_digest(filename) == get_digest(
                                cfgs[filename][0][1]))
                        if equal:
                            files.add(filename)

                for filename in list(files & set(cfgs.keys())):
                    # get ctime from orig filename
                    # if os.path.exists(filename):
                    #    ctime = os.stat(filename).st_ctime
                    # else:
                    #    ctime = 0
                    # if orig filename older that .cfg
                    cfgs[filename].sort(reverse=True)
                    # if ctime < cfgs[filename][0][0]:
                    try:
                        with open(filename, 'w') as f:
                            f.write(readFile(cfgs[filename][0][1]))
                        self.copy_mod_own(cfgs[filename][0][1], filename)
                    except Exception as e:
                        self.printERROR(str(e))
                        self.printWARNING(
                            _("Failed to copy {ffrom} to {fto}").format(
                                ffrom=cfgs[filename][0][1], fto=filename))
                        continue
                    autoUpdateDict[cfgs[filename][0][1]] = filename
                    for mtime, fn in cfgs[filename]:
                        try:
                            if os.path.exists(fn):
                                os.unlink(fn)
                        except OSError:
                            self.printWARNING(_("Failed to remove %s") % fn)
                try:
                    pkgContents.writeContents()
                except IOError:
                    self.printWARNING(_("Failed to modify %s contents") % pkg)
        self.filesApply = [autoUpdateDict.get(x, x) for x in self.filesApply]
        if [x for x in self.filesApply if "._cfg" in x]:
            if self.objVar.Get('cl_ebuild_phase') != '':
                self.printWARNING(_("Some config files need updating. "
                                    "Perform run dispatch-conf."))
            if self.dispatchConf and \
                            self.objVar.Get(
                                'cl_dispatch_conf') == 'dispatch' and \
                            self.objVar.Get('cl_ebuild_phase') == '':
                self.dispatchConf(self.filesApply)

    def scanningTemplates(self, scanDir, prefix=None, flagDir=False,
                          optDir=None, skipTemplates=()):
        """Сканирование и обработка шаблонов в директории scanDir"""
        if optDir is None:
            # ключи: HParams.OptDir.{Path,Skip,Autoupdate}
            optDir = {}
        ret = True
        ##scanDir == '/var/db/repos/calculate/profiles/templates/3.6/2_ac_install_merge/media-sound/pulseaudio/daemon.conf'
        if not prefix:
            prefix = os.path.realpath(scanDir)
        if not flagDir:
            # проверка корневой директории
            retDir = self.processingDirectory(scanDir, scanDir, optDir)
            if retDir is None:
                return None
            elif retDir is False:
                return False
            pathDir, objHead = retDir
            optDir[HParams.OptDir.Path] = pathDir
            if not objHead is True:
                if objHead.typeAppend == HParams.AppendParams.Skip:
                    # Установка опции пропуска директории
                    optDir[HParams.OptDir.Skip] = True
                if (HParams.Autoupdate in objHead.params or
                            self.objVar.Get('cl_autoupdate_set') == 'on'):
                    optDir[HParams.OptDir.Autoupdate] = True
        if flagDir or stat.S_ISDIR(os.lstat(str(scanDir))[stat.ST_MODE]):
            if not os.access(scanDir, os.R_OK | os.X_OK):
                self.printWARNING(_("Failed to read templates directory %s") %
                                  scanDir)
                return False
            for fileOrDir in sorted(listDirectory(scanDir)):
                absPath = os.path.join(scanDir, fileOrDir)
                if skipTemplates and absPath in skipTemplates:
                    continue
                stInfo = os.lstat(str(absPath))
                statInfo = stInfo[stat.ST_MODE]
                prevModule = self.objVar.defaultModule
                prevBelong = self.functObj.currentBelong
                prevBelongSlot = self.functObj.currentBelongSlot
                prevAction = self.functObj.currentAction
                try:
                    if stat.S_ISREG(statInfo):

                        if not self.processingFile(absPath, prefix, optDir):
                            ret = False
                            continue
                    elif stat.S_ISDIR(statInfo):
                        # Обработка директории
                        retDir = self.processingDirectory(absPath, prefix,
                                                          optDir)
                        if retDir is None:
                            continue
                        elif retDir is False:
                            ret = False
                            break
                        # Опции следующей директории
                        optNextDir = {}
                        pathDir, objHead = retDir
                        optNextDir[HParams.OptDir.Path] = pathDir
                        if objHead is not True:
                            if objHead.typeAppend == HParams.AppendParams.Skip:
                                # Установка опции пропуска директории
                                optNextDir[HParams.OptDir.Skip] = True
                            if (HParams.Autoupdate in objHead.params or
                                        self.objVar.Get(
                                            'cl_autoupdate_set') == 'on'):
                                optNextDir[HParams.OptDir.Autoupdate] = True
                        ret = self.scanningTemplates(absPath, prefix, True,
                                                     optNextDir)
                        if objHead is not True:
                            if has_any(objHead.params, HParams.ServiceControl):
                                self.doServiceControl(objHead.params)
                        if ret is False:
                            break
                except TemplatesError as e:
                    self.clearErrors()
                    if self.critical:
                        raise
                    else:
                        self.printWARNING(str(e))
                finally:
                    self.objVar.defaultModule = prevModule
                    self.functObj.currentBelong = prevBelong
                    self.functObj.currentBelongSlot = prevBelongSlot
                    self.functObj.currentAction = prevAction
        return ret

    def processingFile(self, path, prefix, optFile):
        """Обработка в случае шаблона файла"""
        self.numberProcessTempl += 1
        self.numberProcessTemplates(self.numberProcessTempl)
        # Пропуск шаблонов директорий
        if self.templDirNameFile == os.path.split(path)[1]:
            return True
        # Проверка на переменные в названии файла
        if not self.getNeedTemplate(path):
            if self.getError():
                return False
            return True
        if self.getError():
            return False
        nameFileConfig = path.partition(prefix)[2]
        # файл в системе без условий
        nameFileConfig = "/".join((x.split("?")[0] for x in nameFileConfig.split("/")))
                                      
        # Записываем в переменную обрабатываемый файл
        self.objVar.Set("cl_pass_file", os.path.basename(nameFileConfig))
        self.headerParams = None
        #debug these templates:
        #2_ac_install_merge/xfce-base/thunar/uca.xml (ELEMENT goes in before header for some reason (check add comment))
    

        filesApl = self.joinTemplate(path, nameFileConfig, optFile)
        if self.headerParams:
            if has_any(self.headerParams, HParams.ServiceControl):
                self.doServiceControl(self.headerParams)
        if self.getError():
            return False
        if filesApl:
            # Настоящее имя конфигурационного файла
            nameFileConfig = filesApl[0]
            # Пишем время модификации *.env файлов
            if nameFileConfig.endswith(".env"):
                nameEnvFile = os.path.basename(nameFileConfig)
                self.functObj.timeConfigsIni[nameEnvFile] = float(time.time())
            self.filesApply += filesApl
        if filesApl:
            self._addFile(filesApl)
        return True

    def processingDirectory(self, path, prefix, opt):
        """Обработка в случае директории если возвращаем None то пропуск дир."""
        if path.endswith("/.git"):
            return None
        # Файл шаблона директории
        if not os.access(path, os.R_OK | os.X_OK):
            self.printWARNING(_("Failed to read templates directory %s") % path)
            return None
        dirInfoFile = os.path.join(path, self.templDirNameFile)
        newDir = pathJoin(self._baseDir, path.partition(prefix)[2])
        newDir = "/".join((x.split("?")[0]) for x in newDir.split("/"))
        # Применяем шаблон
        pathDir, objHeadDir, createdDirs = \
            self.getApplyHeadDir(newDir, dirInfoFile, opt)
        if createdDirs:
            self.createdDirs += createdDirs
        if os.path.isfile(pathDir):
            self.printWARNING(_("{dirpath} is a file").format(dirpath=pathDir))
            self.printWARNING(
                _("templates in {tempath} are skipped").format(tempath=path))
            return None
        if objHeadDir:
            return pathDir, objHeadDir
        else:
            if self.getError():
                return False
            # Добавление количества файлов в пропущенной директории
            if path in self.dictTemplates.keys():
                self.numberProcessTempl += self.dictTemplates[path]
            return None

    def setRebuildVersion(self, prev_ver):
        if "_rc73" in prev_ver:
            return prev_ver
        ver_nor = prev_ver.partition("-")[0]
        # rc даже после уже существующего гарантирует, что версия будет
        # считаться ниже
        #reRc = re.compile(r"(.*rc)(\d+)(.*)")
        #rc_match = reRc.search(ver_nor)
        #if rc_match:
        #    rc_num = max(0, int(rc_match.group(2)) - 1)
        #    return "%s%d%s" % (rc_match.group(1), rc_num, rc_match.group(3))
        return "%s_rc73" % ver_nor

    def _processRebuild(self, params, templateDirFile):
        """
        Обработка параметра rebuild=
        :param params:
        :param templateDirFile:
        :return:
        """
        if HParams.Rebuild in params:
            rebuild_packages = params[HParams.Rebuild].split(',')
            chroot_path = self.objVar.Get('cl_chroot_path')
            for atom in rebuild_packages:
                for pkg in getInstalledAtom(
                        atom, prefix=chroot_path):
                    ver = pkg["PVR"]
                    new_ver = self.setRebuildVersion(ver)
                    if ver != new_ver:
                        try:
                            fn = pathJoin(chroot_path, "var/db/pkg",
                                          "%s-%s" % (pkg["CATEGORY/PN"],
                                                     pkg["PVR"]))
                            new_fn = pathJoin(chroot_path, "var/db/pkg",
                                              "%s-%s" % (pkg["CATEGORY/PN"],
                                                         new_ver))
                            shutil.copytree(fn, new_fn, symlinks=True)
                            shutil.rmtree(fn)
                        except (OSError, IOError) as e:
                            self.printWARNING(
                                _("Failed to change version of %s") % str(pkg))

    def _processMergePostmerge(self, params, templateDirFile):
        """Обработка параметров merge= , postmerge="""
        if HParams.Merge in params:
            mergePkgs = params[HParams.Merge].split(',')
            if self.objVar.Get('cl_action') == "config":
                self.printWARNING(
                    _("Config action is not support '%s' parameter")
                    % HParams.Merge )
                return
        else:
            mergePkgs = []
        if HParams.PostMerge in params:
            postmergePkgs = params[HParams.PostMerge].split(',')
            if self.objVar.Get('cl_action') == "config":
                self.printWARNING(
                    _("Config action is not support '%s' parameter")
                    % HParams.PostMerge )
                return
        else:
            postmergePkgs = []

        if mergePkgs or postmergePkgs:
            if self.objVar.Get('cl_ebuild_phase') == 'postinst':
                for pkg in postmergePkgs:
                    if pkg not in self.objVar.Get('cl_merge_pkg_pass'):
                        self.objVar.Get('cl_merge_pkg_pass').append(pkg)
                    if pkg not in self.postmergePkgs:
                        try:
                            with open(self.postmergeFile, "a") as f:
                                f.write("%s\n" % pkg)
                            self.postmergePkgs.append(pkg)
                        except OSError:
                            self.printWARNING(
                                _("Failed to reconfigure package %s") % pkg)
            else:
                mergePkgs = mergePkgs + postmergePkgs

            for pkg in mergePkgs:
                curlistset = set(self.objVar.Get('cl_merge_pkg_new') +
                    self.objVar.Get('cl_merge_pkg_pass') +
                    self.objVar.Get('cl_merge_pkg'))
                if ":" not in pkg:
                    curlistset = {x.partition(":")[0] for x in curlistset
                                  if x}
                if pkg not in curlistset:
                    self.objVar.Get('cl_merge_pkg_new').append(pkg)

    def checkVfat(self, fn):
        if self.mounts is None:
            self.mounts = Mounts()
        if self.mounts.getBy(self.mounts.TYPE, where=self.mounts.DIR,
                             _in=fn) in ("vfat", "ntfs-3g", "ntfs"):
            return True
        return False

    def checkOsError(self, e, fn):
        if hasattr(e, 'errno') and e.errno == errno.EPERM:
            if self.checkVfat(fn):
                return True
        if hasattr(e, 'errno') and e.errno == errno.EACCES and \
                        "var/calculate/remote" in fn:
            return True
        return False

    def setUidGidError(self, fn, uid, gid, tfn=None):
        """
        Установить ошибку связанную со сменой UID и GID файла
        """
        import pwd, grp

        try:
            userName = pwd.getpwuid(uid).pw_name
        except (TypeError, KeyError):
            userName = str(uid)
        try:
            groupName = grp.getgrgid(gid).gr_name
        except (TypeError, KeyError):
            groupName = str(gid)
        owner = userName + ":" + groupName
        if tfn:
            self.setError(_("Failed to apply template file %s") % tfn)
        self.setError(_("error") + " " + "'%s %s %s'" % (
            HParams.ChangeOwner, owner, fn))

    def setModeError(self, fn, mode, tfn):
        """
        Установить ошибку связанную со сменой доступа к файлу
        """
        self.setError(_("Failed to apply template file %s") % tfn)
        self.setError(_("error") + " " +
                      "'%s %s %s'" % (
                          HParams.ChangeMode, str(oct(mode)), fn))

    def chownConfDir(self, nameDirConfig, uid, gid, nameFileTemplate):
        """Изменение владельца конфигурационной директории"""
        try:
            os.chown(nameDirConfig, uid, gid)
        except (OSError, Exception) as e:
            if self.checkOsError(e, nameDirConfig):
                return True
            self.setUidGidError(
                nameDirConfig, uid, gid, nameFileTemplate)
            return False
        return True

    def chmodConfDir(self, nameDirConfig, mode, nameFileTemplate):
        """Изменения режима доступа конфигурационного файла"""
        try:
            os.chmod(nameDirConfig, mode)
        except (OSError, Exception) as e:
            if self.checkOsError(e, nameDirConfig):
                return True
            self.setModeError(nameDirConfig, mode, nameFileTemplate)
            return False
        return True

    def getApplyHeadDir(self, newDir, templateDirFile, optDir):
        """Применяет шаблон к директории (права, владелец, и.т. д)"""

        def function(text):
            """Функция обработки функций в заголовке"""
            return self.applyFuncTemplate(text, templateDirFile)

        applyDir = newDir

        # Родительская директория
        if optDir.get(HParams.OptDir.Path):
            path = optDir[HParams.OptDir.Path]
        else:
            if applyDir == self._baseDir:
                path = os.path.dirname(self._baseDir)
            else:
                path = os.path.split(applyDir)[1]
                path = pathJoin(self._baseDir, path)
        if not os.path.exists(templateDirFile):
            if applyDir != self._baseDir:
                applyDir = os.path.join(path, os.path.split(applyDir)[1])
            # Фильтрация шаблонов по названию директории
            realPath = os.path.join("/", applyDir.partition(self._baseDir)[2])
            if realPath in self.dirsFilter:
                return "", False, []
            # Создаем директорию если необходимо
            crDirs = self.createDir(applyDir, False, self.uid, self.gid)
            if not crDirs:
                return "", False, []
            if HParams.OptDir.Autoupdate in optDir:
                self.autoUpdateDirs.append(applyDir)
            if crDirs is True:
                return applyDir, True, []
            else:
                return applyDir, True, crDirs
        try:
            self.objVar.Set("cl_pass_file",
                            os.path.basename(os.path.dirname(templateDirFile)))
            with open(templateDirFile) as FD:
                textTemplate = FD.readline().rstrip()
                buf = textTemplate
                while buf and textTemplate.endswith('\\'):
                    buf = FD.readline()
                    textTemplate = "%s %s" % (textTemplate[:-1], buf.rstrip())
        except IOError:
            self.setError(_("Failed to open the template") + _(": ") +
                          templateDirFile)
            return "", False, []
        headerLine = self.getHeaderText(textTemplate)
        if headerLine:
            envparam = "%s=" % HParams.Environ
            moduleParam = [x for x in headerLine.split() if x.startswith(envparam)]
            if moduleParam:
                self.objVar.defaultModule = moduleParam[0].partition('=')[2]
                try:
                    importlib.import_module(
                        "calculate.%s.variables" % self.objVar.defaultModule)
                except (ImportError, AttributeError):
                    return "", False, []
        # Заменяем переменные на их значения
        # textTemplate = self.applyVarsTemplate(textTemplate, templateDirFile)

        # Заменяем функции на их значения
        # textTemplate = self.applyFuncTemplate(textTemplate, templateDirFile)
        # Обработка заголовка
        objHead = dirHeader(templateDirFile, textTemplate, self.objVar,
                            function, templateObj=self)
        signs = {'ac_install_patch==on': HParams.ActionType.Patch,
                 'ac_desktop_profile==on': HParams.ActionType.Profile}
        for sign in signs:
            if sign in textTemplate:
                self.functObj.currentAction = signs[sign]
                break
        # Директория с профилями не будет применена
        if not objHead.headerTerm:
            if objHead.getError():
                self.setError(_("Incorrect template") + _(": ") +
                              templateDirFile)
            return "", False, []

        # add packeges for reconfigure
        self._processMergePostmerge(objHead.params, templateDirFile)
        self._processRebuild(objHead.params, templateDirFile)
        # Пропускаем директорию
        if objHead.typeAppend == HParams.AppendParams.Skip:
            applyDir = path
            return applyDir, objHead, []

        # Изменяем название родительской директории
        if HParams.Path in objHead.params:
            path = objHead.params[HParams.Path]
            if path and path[0] == "~":
                # Получаем путь с заменой ~ на директорию пользователя
                path = os.path.join(self.homeDir,
                                    path.partition("/")[2], "")[:-1]
            elif not path or path and path[0] != "/":
                self.setError(
                    (_("Wrong value '%s' in the template") % HParams.Path) +
                    _(": ") + templateDirFile)
                return "", False, []
            else:
                path = pathJoin(self._baseDir, path)

        # Изменяем название директории
        if HParams.Name in objHead.params:
            nameDir = objHead.params[HParams.Name]
            if "/" in nameDir or nameDir == ".." or nameDir == ".":
                self.setError(
                    (_("Wrong value '%s' in the template") % HParams.Name) +
                    _(": ") + templateDirFile)
                return "", False, []
            # Новый путь к директории
            applyDir = pathJoin(path, nameDir)
        else:
            applyDir = pathJoin(path, os.path.split(applyDir)[1])

        # Фильтрация шаблонов по названию директории
        realPath = os.path.join("/", applyDir.partition(self._baseDir)[2])
        if realPath in self.dirsFilter:
            return "", False, []
        if HParams.DirectoryLink in objHead.params:
            if objHead.typeAppend not in HParams.AppendParams.LinkDirCompatible:
                self.setError(
                    _("Option '%(opt)s' should be used with %(appends)s only") %
                    {'opt': HParams.DirectoryLink,
                     'appends': ",".join(
                         "%s=%s" % (HParams.Append, x)
                         for x in HParams.AppendParams.LinkDirCompatible)
                     })
                return "", False, []
        # Удаляем директорию
        if objHead.typeAppend == HParams.AppendParams.Remove:
            if os.path.isdir(applyDir):
                self.changedFiles.addObj(applyDir, ChangedFiles.DIR_REMOVED,
                                         self.functObj.currentBelong,
                                         self.functObj.currentBelongSlot)
                # удаляем директорию
                try:
                    removeDir(applyDir)
                except OSError:
                    self.setError(_("Failed to delete the directory: ") + \
                                  applyDir)
            return "", False, []

        # Очищаем директорию
        if objHead.typeAppend == HParams.AppendParams.Clear:
            if os.path.isdir(applyDir):
                for rmPath in os.listdir(applyDir):
                    removePath = pathJoin(applyDir, rmPath)
                    if os.path.isdir(removePath):
                        # удаляем директорию
                        try:
                            removeDir(removePath)
                        except OSError:
                            self.setError(
                                _("Failed to delete the directory: ") + \
                                removePath)
                    else:
                        try:
                            os.unlink(removePath)
                        except OSError:
                            self.setError(
                                _("Failed to delete: ") + removePath)
            return "", False, []

        # Созданные директории
        createdDirs = []
        # chmod - изменяем права
        mode = None
        if HParams.ChangeMode in objHead.params:
            mode = self.__octToInt(objHead.params[HParams.ChangeMode])
            if mode:
                if not os.path.exists(applyDir):
                    crDirs = self.createDir(applyDir, mode, self.uid, self.gid)
                    if not crDirs:
                        return "", False, []
                    if not crDirs is True:
                        createdDirs += crDirs
                else:
                    self.chmodConfDir(applyDir, mode, templateDirFile)
            else:
                self.setError(
                    (_("Wrong value '%s' in the template")
                     % HParams.ChangeMode) + _(": ") + templateDirFile)
                return "", False, []
        # chown - изменяем владельца и группу
        owner_uid, owner_gid = None, None
        if HParams.ChangeOwner in objHead.params:
            owner = objHead.params[HParams.ChangeOwner]
            if owner:
                if ":" in owner:
                    strUid, strGid = owner.split(":")
                    if strUid.isdigit():
                        owner_uid = int(strUid)
                    else:
                        owner_uid = self.getUidFromPasswd(strUid)
                        import pwd

                        try:
                            if owner_uid is None:
                                owner_uid = pwd.getpwnam(strUid).pw_uid
                        except (KeyError, TypeError):
                            self.setError(_("No such user on the system: ")
                                          + strUid)
                            self.setError(
                                (_("Wrong value '%s' in the template")
                                 % HParams.ChangeOwner) + _(": ") + templateDirFile)
                            return "", False, []
                    if strGid.isdigit():
                        owner_gid = int(strGid)
                    else:
                        owner_gid = self.getGidFromGroup(strGid)
                        import grp
                        try:
                            if owner_gid is None:
                                owner_gid = grp.getgrnam(strGid).gr_gid
                        except (KeyError, TypeError):
                            self.setError(_("Group not found on the system: ")
                                          + strGid)
                            self.setError(
                                (_("Wrong value '%s' in the template")
                                 % HParams.ChangeOwner) + _(": ") + templateDirFile)
                            return "", False, []

                    if not os.path.exists(applyDir):
                        crDirs = self.createDir(applyDir, False, owner_uid,
                                                owner_gid)
                        if not crDirs:
                            return "", False, []
                        if not crDirs is True:
                            createdDirs += crDirs
                    else:
                        if not self.chownConfDir(applyDir, owner_uid, owner_gid,
                                                 templateDirFile):
                            return "", False, []
                else:
                    self.setError(
                        (_("Wrong value '%s' in the template")
                         % HParams.ChangeOwner) + _(": ") + templateDirFile)
                    return "", False, []
            else:
                self.setError(
                    (_("Wrong value '%s' in the template")
                     % HParams.ChangeOwner) + _(": ") + templateDirFile)
                return "", False, []
        else:
            # Устанавливаем владельцем директории, пользователя по умолчанию
            # (переменная шаблона ur_login)
            if os.path.exists(applyDir):
                self.changedFiles.addObj(applyDir, ChangedFiles.DIR_EXISTS,
                                         self.functObj.currentBelong,
                                         self.functObj.currentBelongSlot)
                tUid, tGid = getModeFile(applyDir, mode="owner")
                if (self.uid, self.gid) != (tUid, tGid):
                    if not self.chownConfDir(applyDir, self.uid, self.gid,
                                             templateDirFile):
                        return "", False, []
            else:
                self.changedFiles.addObj(applyDir, ChangedFiles.DIR_CREATED,
                                         self.functObj.currentBelong,
                                         self.functObj.currentBelongSlot)
                crDirs = self.createDir(applyDir, False, self.uid, self.gid)
                if not crDirs:
                    return "", False, []
                if crDirs is not True:
                    createdDirs += crDirs
        if HParams.DirectoryLink in objHead.params:
            templateFile = objHead.params[HParams.DirectoryLink]
            templateFile = pathJoin(self._baseDir, templateFile)
            if not os.path.isdir(templateFile):
                self.setError(_("Source path %s is not a directory")
                              % templateFile)
                return "", False, []
            try:
                if objHead.typeAppend == HParams.AppendParams.Replace:
                    for fn in listDirectory(applyDir, fullPath=True):
                        if os.path.isdir(fn):
                            shutil.rmtree(fn)
                        elif os.path.isfile(fn) or os.path.islink(fn):
                            os.unlink(fn)
                for fn in listDirectory(templateFile, fullPath=True):
                    applyFn = os.path.join(applyDir, os.path.basename(fn))
                    if os.path.isfile(fn):
                        shutil.copy2(fn, applyFn)
                    elif os.path.islink(fn):
                        os.symlink(os.readlink(fn), applyFn)
                    elif os.path.isdir(fn):
                        dir_sync(fn, applyFn)
            except (IOError, OSError) as e:
                self.setError(_("Failed to synchronize directory "
                                "{dn}: {error}").format(dn=templateFile,
                                                        error=str(e)))
                return "", False, []
        if not objHead:
            applyDir = ""
        if applyDir:
            if ((HParams.OptDir.Autoupdate in optDir or
                         HParams.Autoupdate in objHead.params) and
                    not self.objVar.Get('cl_merge_pkg_pass')):
                self.autoUpdateDirs.append(applyDir)
        return applyDir, objHead, createdDirs

    def getUidFromPasswd(self, strUid):
        """Get uid by username from chroot passwd file"""
        passwdFile = os.path.join(self._baseDir, 'etc/passwd')
        if os.path.exists(passwdFile):
            with open(passwdFile, 'r') as f:
                mapUid = dict([x for x in [y.split(':')[0:3:2] for y 
                    in f if not y.startswith('#')] 
                    if x and len(x) > 1 and x[0] and x[1]])
                if strUid in mapUid:
                    return int(mapUid[strUid])
        return None

    def getGidFromGroup(self, strGid):
        """Get gid by groupname from chroot group file"""
        groupFile = os.path.join(self._baseDir, 'etc/group')
        if os.path.exists(groupFile):
            with open(groupFile, 'r') as f:
                mapGid = dict([x for x in [y.split(':')[0:3:2] for y 
                    in f if not y.startswith('#')] 
                    if x and len(x) > 1 and x[0] and x[1]])                 
            if strGid in mapGid:
                return int(mapGid[strGid])
        return None

    def checkOnNewConfigName(self, pathFile):
        """
        Check on need update and return pathFile
        """
        # if file in PROTECT_MASK or not in PROTECT
        chrootPath = self.objVar.Get('cl_chroot_path')
        if not [x for x in [pathJoin(chrootPath, y) for y in self.objVar.Get('cl_config_protect')] if pathFile.startswith(x)] or \
                [x for x in [pathJoin(chrootPath, y) for y in self.objVar.Get('cl_config_protect_mask')] if pathFile.startswith(x)]:
            return pathFile
        # if file was already modified by templates
        if pathFile in self.changedFiles.data.keys():
            return pathFile
        # if using already created ._cfg file
        if self.configMode != T_ORIGIN:
            return pathFile
        # not current package file
        pkg = self.functObj.currentBelong
        slot = self.functObj.currentBelongSlot
        if not pkg:
            if not self.allContents:
                fillContents(self.allContents,
                             self.objVar.Get('cl_config_protect'),
                             prefix=self.objVar.Get('cl_chroot_path'))
            origName = pathFile if chrootPath == '/' \
                else pathFile[len(chrootPath):]
            if origName in self.allContents:
                pkg = self.allContents[origName]
            else:
                return pathFile
        if slot:
            pkgslot = "{}:{}".format(pkg,slot)
        else:
            pkgslot = pkg
        atom = list(sorted(getInstalledAtom(pkgslot, prefix=chrootPath)))
        if not atom:
            return pathFile
        if checkContents("{CATEGORY}/{PF}".format(**atom[-1]),
                         pathFile,
                         prefix=chrootPath,
                         reservedFile='/var/lib/calculate/-CONTENTS-{PN}'.format(
                             **atom[-1]) \
                             if self.objVar.Get('cl_ebuild_phase') == 'postinst' \
                             else None):
            return pathFile
        real_filename = os.path.basename(pathFile)
        real_dirname = os.path.dirname(pathFile)
        self.configMode = T_NEWCFG
        return os.path.join(real_dirname, "._cfg0000_%s" % real_filename)

    def chownConfFile(self, nameFileConfig, uid, gid, nameFileTemplate,
                      checkExists=True):
        """Изменение владельца конфигурационного файла"""
        try:
            if checkExists and not os.path.exists(nameFileConfig):
                # Создание файла
                open(nameFileConfig, "w").close()
            os.lchown(nameFileConfig, uid, gid)
        except (OSError, Exception) as e:
            if self.checkOsError(e, nameFileConfig):
                return True
            self.setUidGidError(
                nameFileConfig, uid, gid, nameFileTemplate)
            return False
        return True

    def chmodConfFile(self, nameFileConfig, mode, nameFileTemplate,
                      checkExists=True):
        """Изменения режима доступа конфигурационного файла"""
        try:
            if checkExists and not os.path.exists(nameFileConfig):
                # Создание файла
                open(nameFileConfig, "w").close()
            os.chmod(nameFileConfig, mode)
        except (OSError, Exception) as e:
            if self.checkOsError(e, nameFileConfig):
                return True
            self.setModeError(nameFileConfig, mode, nameFileTemplate)
            return False
        return True

    def getApplyHeadTemplate(self, nameFileTemplate, nameFileConfig,
                             templateFileType, optFile):
        """Применяет заголовок к шаблону (права, владелец, и.т. д)"""

        def function(text):
            """Функция обработки функций в заголовке"""
            return self.applyFuncTemplate(text, nameFileTemplate)

        def preReturn(pathProg):
            """Действия перед выходом из метода"""
            if pathProg:
                os.chdir(pathProg)

        self.closeFiles()
        pathProg = ""
        self.executeType = None
        # Файлы в системе к которым были применены шаблоны
        # В случае бинарного типа файла читаем шаблон
        if templateFileType == "bin":
            self.nameFileTemplate = os.path.abspath(nameFileTemplate)
            self.F_TEMPL = self.openTemplFile(self.nameFileTemplate)
            if not self.F_TEMPL:
                self.setError(_("Failed to open the template") + _(": ") +
                              self.nameFileTemplate)
                return [], False
            self.textTemplate, ___ = try_decode_utf8(self.F_TEMPL.read())
            self.closeTemplFile()
        objHeadNew = fileHeader(nameFileTemplate, self.textTemplate, False,
                                templateFileType, objVar=self.objVar,
                                function=function, templateObj=self)
        # файл шаблона не будет применен
        if not objHeadNew.headerTerm:
            if objHeadNew.getError():
                self.setError(_("Incorrect template") + _(": ") +
                              nameFileTemplate)
            return [], False
        else:
            self.headerParams = objHeadNew.params

        # add packeges for reconfigure
        self._processMergePostmerge(objHeadNew.params, nameFileTemplate)
        self._processRebuild(objHeadNew.params, nameFileTemplate)

        # Родительская директория
        path = optFile[HParams.OptDir.Path]
        # Изменяем название родительской директории
        if HParams.Path in objHeadNew.params:
            path = objHeadNew.params[HParams.Path]
            if path and path[0] == "~":
                # Получаем путь с заменой ~ на директорию пользователя
                path = os.path.join(
                    self.homeDir, path.partition("/")[2], "")[:-1]
            elif not path or path and path[0] != "/":
                self.setError(
                    (_("Wrong value '%s' in the template") % HParams.Path) +
                    _(": ") + nameFileTemplate)
                return [], False
            else:
                path = pathJoin(self._baseDir, path)

        # Путь к оригинальному файлу - pathOldFile
        # Изменяем путь к оригинальному файлу
        if HParams.Name in objHeadNew.params:
            nameFile = objHeadNew.params[HParams.Name]
            if "/" in nameFile or nameFile == ".." or nameFile == ".":
                self.setError(
                    (_("Wrong value '%s' in the template") % HParams.Name) +
                    _(": ") + nameFileTemplate)
                return [], False
            # Новый путь к оригинальному файлу
            pathOldFile = pathJoin(path, nameFile)
        else:
            pathOldFile = pathJoin(path, os.path.split(nameFileConfig)[1])
        pathOrigFile = pathOldFile
        self.nameFileConfigOrig = pathOrigFile
        if self.objVar.Get('cl_protect_use_set') == 'on':
            pathOldFile = self.fixNameFileConfig(pathOldFile)
            pathOldFile = self.checkOnNewConfigName(pathOldFile)
        # буффер для использование в link=
        newBuffer = None
        applyFiles = [pathOldFile]
        # Фильтрация шаблонов по названию файла
        realPath = os.path.join("/", pathOldFile.partition(self._baseDir)[2])
        if realPath in self.filesFilter:
            return [], False
        typeAppendTemplate = objHeadNew.typeAppend

        # Параметр exec
        if (HParams.RunPost in objHeadNew.params or
                    HParams.RunNow in objHeadNew.params):
            if HParams.RunPost in objHeadNew.params:
                paramName = HParams.RunPost
                self.executeType = HParams.ExecuteType.Post
            else:
                paramName = HParams.RunNow
                self.executeType = HParams.ExecuteType.Now
            execPath = objHeadNew.params[paramName]
            if not os.access(execPath, os.X_OK):
                self.setError(
                    _("Wrong value '%s' in the template") % paramName +
                    _(": ") + nameFileTemplate)
                self.setError(_("Failed to execute %s") % execPath)
                return [], False
            if typeAppendTemplate == HParams.AppendParams.Join:
                self.setError(
                    (_("Wrong value '{var}={val}' in template").format
                     (var=HParams.Append, val=HParams.AppendParams.Join)) +
                    _(": ") + nameFileTemplate)
                return [], False

        # Очищаем оригинальный файл
        if typeAppendTemplate == HParams.AppendParams.Clear:
            try:
                with open(pathOldFile, "w") as f:
                    f.truncate(0)
            except IOError:
                self.setError(_("Template error") + _(": ") +
                              nameFileTemplate)
                self.setError(_("Failed to clear the file") + _(": ") +
                              pathOldFile)
            return applyFiles, False
        # Удаляем оригинальный файл
        if typeAppendTemplate == HParams.AppendParams.Remove:
            if HParams.Force in objHeadNew.params:
                pathOldFile = pathOrigFile
                self.configMode = T_ORIGIN
            try:
                if os.path.islink(pathOldFile):
                    # удаляем ссылку
                    try:
                        os.unlink(pathOldFile)
                        return applyFiles, False
                    except OSError:
                        self.setError(_("Template error") + _(": ") +
                                      nameFileTemplate)
                        self.setError(_("Failed to delete the link") + _(": ") +
                                      pathOldFile)
                        return [], False
                if os.path.isfile(pathOldFile) and self.configMode == T_ORIGIN:
                    # удаляем файл
                    try:
                        os.remove(pathOldFile)
                        return applyFiles, False
                    except OSError:
                        self.setError(_("Template error") + _(": ") +
                                      nameFileTemplate)
                        self.setError(_("Failed to delete the file") + _(": ") +
                                      pathOldFile)
                        return [], False
            finally:
                pattern = "%s/._cfg????_%s" % (os.path.dirname(pathOrigFile),
                                               os.path.basename(pathOrigFile))
                for fn in glob.glob(pattern):
                    try:
                        os.unlink(fn)
                    except OSError:
                        pass
                if self.functObj.currentBelong:
                    self.changedFiles.addObj(pathOrigFile,
                                             ChangedFiles.FILE_REMOVED,
                                             self.functObj.currentBelong,
                                             self.functObj.currentBelongSlot)
            return [], False
        # Пропускаем обработку шаблона
        elif typeAppendTemplate == HParams.AppendParams.Skip:
            return [], False

        # Создаем директорию для файла если ее нет
        if not os.path.exists(path):
            if not self.createDir(path):
                return [], False

        # создаём пустой файл если его нет для sqlite
        if objHeadNew.fileType in HParams.Formats.Modificator:
            try:
                if not os.path.exists(pathOrigFile):
                    with open(pathOrigFile, "w") as f:
                        f.truncate(0)
            except IOError:
                self.setError(_("Template error") + _(": ") +
                              nameFileTemplate)
                self.setError(_("Failed to create the file") + _(": ") +
                              pathOrigFile)
        # В случае force
        if (HParams.Force in objHeadNew.params and
            objHeadNew.fileType not in HParams.Formats.Executable):
            if os.path.islink(pathOldFile):
                # удаляем ссылку
                newBuffer = ""
                try:
                    os.unlink(pathOldFile)
                except OSError:
                    self.setError(_("Template error") + _(": ") +
                                  nameFileTemplate)
                    self.setError(_("Failed to delete the link") + _(": ") +
                                  pathOldFile)
                    return [], False
            if os.path.isfile(pathOldFile):
                # удаляем файл
                newBuffer = ""
                try:
                    os.remove(pathOldFile)
                except OSError:
                    self.setError(_("Template error") + _(": ") +
                                  nameFileTemplate)
                    self.setError(_("Failed to delete the file") + _(": ") +
                                  pathOldFile)
                    return [], False

        flagSymlink = False
        # Если есть параметр mirror
        if (HParams.Mirror in objHeadNew.params and
            objHeadNew.fileType not in HParams.Formats.Executable):
            if HParams.Link in objHeadNew.params:
                templateFile = objHeadNew.params[HParams.Link]
                if templateFile and templateFile[0] == "~":
                    # Получаем директорию пользователя
                    templateFile = os.path.join(
                        self.homeDir, templateFile.partition("/")[2], "")[:-1]
                templateFile = pathJoin(self._baseDir, templateFile)
                if (not os.path.exists(templateFile) or
                        not objHeadNew.params[HParams.Link]):
                    if os.path.exists(pathOldFile):
                        try:
                            os.remove(pathOldFile)
                        except OSError:
                            self.setError(_("Template error") + _(": ") +
                                          nameFileTemplate)
                            self.setError(
                                _("Failed to delete the file") + _(": ") +
                                pathOldFile)
                    return [], False
            elif not os.path.exists(pathOldFile):
                return [], False
        # Если есть указатель на файл шаблона (link)
        if (HParams.Link in objHeadNew.params and
                    objHeadNew.fileType not in HParams.Formats.Executable and
                    HParams.Symbolic not in objHeadNew.params):
            templateFile = objHeadNew.params[HParams.Link]
            if templateFile and templateFile[0] == "~":
                # Получаем директорию пользователя
                templateFile = os.path.join(
                    self.homeDir, templateFile.partition("/")[2], "")[:-1]
            templateFile = pathJoin(self._baseDir, templateFile)
            foundTemplateFile = os.path.exists(templateFile)
            buff = None
            fMode, fUid, fGid = None, None, None
            if foundTemplateFile and objHeadNew.params[HParams.Link]:
                try:
                    F_CONF = self.openTemplFile(templateFile)
                    if not F_CONF:
                        raise IOError
                    buff, ___ = try_decode_utf8(F_CONF.read())
                    F_CONF.close()
                    fMode, fUid, fGid = getModeFile(templateFile)
                except (OSError, IOError):
                    self.setError(_("Template error") + _(": ") +
                                  nameFileTemplate)
                    self.setError(_("Failed to open the file") + _(": ") +
                                  templateFile)
                    return [], False
            if os.path.exists(pathOldFile):
                newBuffer = ""
                try:
                    os.remove(pathOldFile)
                except OSError:
                    self.setError(_("Template error") + _(": ") +
                                  nameFileTemplate)
                    self.setError(_("Failed to delete the file") + _(": ") +
                                  pathOldFile)
                    return [], False
            if buff is not None:
                try:
                    mode = "wb+" if isinstance(buff, bytes) else "w+"
                    with open(pathOldFile, mode) as FD:
                        newBuffer = buff
                        FD.write(buff)
                except IOError:
                    self.setError(_("Template error") + _(": ") +
                                  nameFileTemplate)
                    self.setError(_("Failed to create the file") + " '%s'" \
                                  % pathOldFile)
                    return [], False
                oMode = getModeFile(pathOldFile, mode="mode")
                # Если права не совпадают, меняем права
                if fMode != oMode:
                    if not self.chmodConfFile(
                            pathOldFile, fMode, nameFileTemplate,
                            checkExists=False):
                        return [], False

        # Если символическая ссылка
        prevOldFile = None
        if HParams.Symbolic in objHeadNew.params:
            prevOldFile = pathOldFile
            pathOldFile = objHeadNew.params[HParams.Link]
            flagSymlink = True
            if not pathOldFile:
                raise TemplatesError(
                    _("Missed source link in template '%s'")
                    % str(nameFileTemplate))
            if not "/" == pathOldFile[0]:
                pathLink = os.path.split(os.path.abspath(prevOldFile))[0]
                pathProg = os.getcwd()
                try:
                    os.chdir(pathLink)
                except OSError:
                    self.setError(_("Template error") + _(": ") +
                                  nameFileTemplate)
                    self.setError(
                        _("Failed to change the current directory to") + \
                        " " + pathLink)
                    return [], False

        # chmod - изменяем права
        if HParams.ChangeMode in objHeadNew.params:
            mode = self.__octToInt(objHeadNew.params[HParams.ChangeMode])
            if mode:
                if not self.chmodConfFile(pathOldFile, mode, nameFileTemplate):
                    preReturn(pathProg)
                    return [], False
            else:
                self.setError(
                    (_("Wrong value '%s' in the template")
                     % HParams.ChangeMode) + _(": ") + nameFileTemplate)
                preReturn(pathProg)
                return [], False
        # chown - изменяем владельца и группу
        if HParams.ChangeOwner in objHeadNew.params:
            owner = objHeadNew.params[HParams.ChangeOwner]
            if owner:
                if ":" in owner:
                    strUid, strGid = owner.split(":")
                    if strUid.isdigit():
                        uid = int(strUid)
                    else:
                        uid = self.getUidFromPasswd(strUid)
                        import pwd

                        try:
                            if uid is None:
                                uid = pwd.getpwnam(strUid).pw_uid
                        except (KeyError, TypeError):
                            self.setError(_("No such user on the system: ") +
                                          strUid)
                            self.setError((_("Wrong value '%s' in the template")
                                           % HParams.ChangeOwner) + _(": ")
                                          + nameFileTemplate)
                            preReturn(pathProg)
                            return [], False
                    if strGid.isdigit():
                        gid = int(strGid)
                    else:
                        gid = self.getGidFromGroup(strGid)
                        try:
                            if gid is None:
                                import grp

                                gid = grp.getgrnam(strGid).gr_gid
                        except (KeyError, TypeError):
                            self.setError(_("Group not found on the system: ") +
                                          strGid)
                            self.setError((_("Wrong value '%s' in the template")
                                           % HParams.ChangeOwner) + _(": ")
                                          + nameFileTemplate)
                            preReturn(pathProg)
                            return [], False
                    # Изменяем владельца файла
                    if not self.chownConfFile(pathOldFile, uid, gid,
                                              nameFileTemplate):
                        preReturn(pathProg)
                        return [], False
                else:
                    self.setError((_("Wrong value '%s' in the template")
                                   % HParams.ChangeOwner) + _(": ")
                                  + nameFileTemplate)
                    preReturn(pathProg)
                    return [], False
            else:
                self.setError((_("Wrong value '%s' in the template")
                               % HParams.ChangeOwner) + _(": ")
                              + nameFileTemplate)
                preReturn(pathProg)
                return [], False
        if not flagSymlink:
            self.openFiles(nameFileTemplate, pathOldFile, objHeadNew.fileType,
                           newBuffer)
            if self.getError():
                return [], False
        if HParams.ChangeOwner not in objHeadNew.params:
            # Устанавливаем владельцем конфигурационного файла,
            # пользователя по умолчанию (переменная шаблона ur_login)
            if os.path.exists(pathOldFile):
                tUid, tGid = getModeFile(pathOldFile, mode="owner")
                if (self.uid, self.gid) != (tUid, tGid):
                    # Изменяем владельца файла
                    if not self.chownConfFile(
                            pathOldFile, self.uid, self.gid, nameFileTemplate,
                            checkExists=False):
                        preReturn(pathProg)
                        return [], False
        if flagSymlink:
            if os.path.exists(prevOldFile) or os.path.islink(prevOldFile):
                try:
                    if os.path.islink(prevOldFile):
                        # если ссылка то удаляем её
                        os.unlink(prevOldFile)
                    else:
                        # иначе удаляем файл
                        os.remove(prevOldFile)
                except OSError:
                    self.setError(_("Template error") + _(": ") +
                                  nameFileTemplate)
                    self.setError(_("Failed to delete the file") + _(": ") +
                                  prevOldFile)
                    preReturn(pathProg)
                    return [], False
            if not "/" == pathOldFile[0]:
                applyFiles = [
                    prevOldFile]  # ,os.path.join(pathLink,pathOldFile)]
            else:
                applyFiles = [prevOldFile]  # ,pathOldFile]
            try:
                os.symlink(pathOldFile, prevOldFile)
            except OSError:
                self.setError(_("Template error") + _(": ") + nameFileTemplate)
                self.setError(_("Failed to create a symbolic link") + _(": ") +
                              "%s -> %s" % (prevOldFile, pathOldFile))
                preReturn(pathProg)
                return [], False
            if not objHeadNew.body.strip():
                preReturn(pathProg)
                if HParams.Protected in objHeadNew.params:
                    self.protectedFiles += applyFiles
                return applyFiles, False
        else:
            applyFiles = [pathOldFile]
        preReturn(pathProg)
        if HParams.Protected in objHeadNew.params:
            self.protectedFiles += applyFiles
        if ((HParams.OptDir.Autoupdate in optFile
             or HParams.Autoupdate in objHeadNew.params) and
                not self.objVar.Get('cl_merge_pkg_pass')):
            reCfg = re.compile(r"/._cfg\d{4}_", re.S)
            self.autoUpdateFiles += [reCfg.sub('/', x) for x in applyFiles]
        if pathOldFile not in self.dictProcessedTemplates:
            self.dictProcessedTemplates[pathOldFile] = []
        self.dictProcessedTemplates[pathOldFile].append(nameFileTemplate)
        # Если файлы заменяются не нужно их обрабатывать дальше
        if (HParams.AppendParams.Replace == typeAppendTemplate and
                    HParams.Symbolic not in objHeadNew.params and
                    HParams.Link in objHeadNew.params):
            return applyFiles, False
        return applyFiles, objHeadNew

    def doServiceControl(self, params):
        """
        Выполнить действие над сервисом
        :param params: параметры заголовка шаблонов
        :return:
        """
        command_action_map = {
            HParams.RestartService: "restart",
            HParams.StopService: "stop",
            HParams.StartService: "start"
        }
        command_action_messages = {
            HParams.RestartService: _("Service %s has been restarted"),
            HParams.StopService: _("Service %s has been stopped"),
            HParams.StartService: _("Service %s has been started")
        }
        command_action_error = {
            HParams.RestartService: _("Failed to restart %s service"),
            HParams.StopService: _("Failed to stop %s service"),
            HParams.StartService: _("Failed to start %s service")
        }
        for param in HParams.ServiceControl:
            if param in params:
                service_list = (x for x in params[param].split(',') if x)
                command_action = command_action_map[param]
                for service in service_list:
                    try:
                        p = process("/etc/init.d/%s" % service, command_action)
                        if p.success():
                            self.printSUCCESS(
                                command_action_messages[param] % service)
                        else:
                            self.printERROR(
                                command_action_error[param] % service)
                            for line in p.readerr().strip().split('\n'):
                                self.printERROR(line)
                    except FilesError as e:
                        self.printERROR(
                            command_action_error[param] % service)
                        self.printERROR(str(e))

    def createNewClass(self, name, bases, attrs=None):
        """Создает объект нового класса

        createNewClass(self, name, bases, attrs)
        name - имя класса - str,
        bases - cписок наследуемых классов - (tuple),
        attrs - аттрибуты класса - {dict}
        """
        if attrs is None:
            attrs = {}

        class newMethod():
            # Объединяем конфигурации
            def join(self, newObj):
                if newObj.__class__.__name__ == self.__class__.__name__:
                    if hasattr(self, "docObj"):
                        self.docObj.joinDoc(newObj.doc)
                    # Пост обработка
                    if hasattr(self, "postXML"):
                        self.postXML()

        attrsNew = {"configName": name}
        if attrs:
            attrsNew.update(attrs)
        newCl = type(name, bases + (newMethod, object,), attrsNew)
        return newCl

    def fileIsUtf(self, fileName, data=None):
        """Проверяет файл на кодировку UTF-8"""
        if os.path.isfile(fileName):
            if data is None:
                with open(os.path.abspath(fileName), 'rb') as FD:
                    data = FD.read(1) + FD.read()
            try:
                if(not isinstance(data, str)):
                    data.decode("UTF-8")
            except UnicodeDecodeError:
                return False
            return True
        return False

    def joinTemplate(self, nameFileTemplate, nameFileConfig, optFile=None):
        """Объединения шаблона и конф. файла

        join(nameFileTemplate, nameFileConfig, ListOptTitle)
        Объединение шаблона nameFileTemplate и конф. файла nameFileConfig,
        ListOptTitle - список строк которые добавятся в заголовок
        optFile = опции для шаблона
        """
        if optFile is None:
            optFile = {}

        # Выполняем условия для блока текста а так-же заменяем переменные
        self.nameFileTemplate = os.path.abspath(nameFileTemplate)
        self.F_TEMPL = self.openTemplFile(self.nameFileTemplate)
        origTextTemplate, file_is_unicode = try_decode_utf8(self.F_TEMPL.read())
        self.textTemplate = origTextTemplate
        self.configMode = T_ORIGIN
        self.closeTemplFile()
        # Флаг копирования шаблона в конфигурационный файл
        flagCopyTemplate = True
        # Тип шаблона бинарный или текстовый
        if self.textTemplate[:11] == "# Calculate":
            templateFileType = "text"
        else:
            templateFileType = self.getTemplateType()
            if templateFileType == "text" and not file_is_unicode:
                #go home getTemplateType, you're drunk
                templateFileType = "bin"
            elif templateFileType == "bin" and file_is_unicode:
                #probably svg file
                self.textTemplate = self.textTemplate.encode("UTF-8")
        headerLine = self.getHeaderText(self.textTemplate, binary = templateFileType == "bin")
        if headerLine:
            envparam = "%s=" % HParams.Environ
            moduleParam = [x for x in headerLine.split() if x.startswith(envparam)]
            if moduleParam:
                self.objVar.defaultModule = moduleParam[0].partition('=')[2]
                try:
                    importlib.import_module(
                        "calculate.%s.variables" % self.objVar.defaultModule)
                except (ImportError, AttributeError) as e:
                    #DEBUG
                    # print(e)
                    return []

        if not optFile:
            optFile = {"path": os.path.split(nameFileConfig)[0]}
        filesApply, objHeadNew = self.getApplyHeadTemplate(nameFileTemplate,
                                                           nameFileConfig,
                                                           templateFileType,
                                                           optFile)
        if not objHeadNew:
            return filesApply
        # if filesApply and not list(filter(lambda x: "calculate/ini.env" in x,
        #                              filesApply)):
        if filesApply and not any(["calculate/ini.env" in x for x in filesApply]):
            self.templateModify()

        if templateFileType != "bin":
            # Вычисляем условные блоки
            objHeadNew.body = self.applyTermsTemplate(objHeadNew.body,
                                                     nameFileTemplate)
            # Заменяем переменные на их значения
            objHeadNew.body = self.applyVarsTemplate(objHeadNew.body,
                                                     nameFileTemplate)
            flagCopyTemplate = False
            # Вычисляем функции
            objHeadNew.body = self.applyFuncTemplate(objHeadNew.body,
                                                     nameFileTemplate)

        # Настоящее имя конфигурационного файла
        nameFileConfig = filesApply[0]
        # Флаг - кодировка с бинарными примесями у файла шаблона включаем при
        # условии текстового файла и кодировки отличной от UTF-8
        flagNotUtf8New = False
        # Флаг - кодировка с бинарными примесями у оригинального файла
        flagNotUtf8Old = False
        if not flagCopyTemplate:
            # проверяем кодировку шаблона
            if not self.fileIsUtf(nameFileTemplate, data=origTextTemplate):
                flagNotUtf8New = True
        if not (HParams.Link in objHeadNew.params and
                        HParams.Symbolic in objHeadNew.params):
            # проверяем кодировку оригинального файла
            if not self.fileIsUtf(nameFileConfig):
                flagNotUtf8Old = True
        self.textTemplate = objHeadNew.body
        # Список примененных шаблонов
        ListOptTitle = []
        if nameFileConfig in self.dictProcessedTemplates:
            ListOptTitle = self.dictProcessedTemplates[nameFileConfig]
        # Титл конфигурационного файла
        title = ""
        if ListOptTitle:
            title = self.getTitle(objHeadNew.comment, ListOptTitle,
                                  configPath=nameFileConfig)

        objHeadOld = False
        if objHeadNew.comment:
            objHeadOld = fileHeader(nameFileConfig, self.textConfig,
                                    objHeadNew.comment)
        elif (objHeadNew.fileType and
                      objHeadNew.typeAppend in (HParams.AppendParams.Before,
                                                HParams.AppendParams.After)):
            configFileType = self.getFileType(nameFileConfig)
            objHeadOld = fileHeader(nameFileConfig, self.textConfig,
                                    fileType=configFileType)
        # Строка вызова скрипта (#!/bin/bash ...)
        execStr = ""
        if objHeadNew.execStr:
            execStr = objHeadNew.execStr
        elif objHeadOld and objHeadOld.execStr:
            execStr = objHeadOld.execStr
        
        if objHeadNew.fileType != 'patch':
            wrongOpt = [x for x in (HParams.Multiline, HParams.DotAll)
                        if x in objHeadNew.params]
            if wrongOpt:
                self.setError(
                    _("Option %s should be used for format=patch only")
                    % wrongOpt[0])
                return None
        if objHeadNew.fileType != 'dconf':
            wrongOpt = [x for x in (HParams.DConf,)
                        if x in objHeadNew.params]
            if wrongOpt:
                self.setError(
                    _("Option %s should be used for format=dconf only")
                    % wrongOpt[0])
                return None
        if objHeadNew.fileType != 'backgrounds':
            wrongOpt = [x for x in (HParams.Convert, HParams.Stretch)
                        if x in objHeadNew.params]
            if wrongOpt:
                self.setError(
                    _("Option %s should be used for format=backgrounds only")
                    % wrongOpt[0])
                return None
        if objHeadNew.fileType:
            formatTemplate = objHeadNew.fileType
            typeAppendTemplate = objHeadNew.typeAppend
            if formatTemplate in chain(("patch",), HParams.Formats.Executable):
                if typeAppendTemplate != HParams.AppendParams.Patch:
                    self.setError(
                        _("Wrong option '%(param)s=%(type)s' "
                          "in template %(file)s")
                        % {"param": HParams.Append,
                           "type": typeAppendTemplate,
                           "file": nameFileTemplate})
                    return None
                # создаем объект формата шаблона
                objTempl = self.formatFactory.createObject(
                    formatTemplate, self.textTemplate)
                if formatTemplate == 'patch':
                    if HParams.Multiline in objHeadNew.params:
                        objTempl.setMultiline()
                    if HParams.DotAll in objHeadNew.params:
                        objTempl.setDotall()
                if formatTemplate == 'dconf':
                    if HParams.DConf in objHeadNew.params:
                        objTempl.setPath(objHeadNew.params[HParams.DConf])
                    objTempl.setUser(self.objVar.Get('ur_login'))
                if formatTemplate == 'backgrounds':
                    root_path = self.objVar.Get('cl_chroot_path')
                    if root_path != '/':
                        objTempl.setRootPath(root_path)
                    if HParams.Convert in objHeadNew.params:
                        objTempl.setConvert(objHeadNew.params[HParams.Convert])
                    if HParams.Link in objHeadNew.params:
                        objTempl.setSource(objHeadNew.params[HParams.Link])
                    if HParams.Mirror in objHeadNew.params:
                        objTempl.setMirror()
                    if HParams.Stretch in objHeadNew.params:
                        objTempl.setStretch(True)
                    if (HParams.Name in objHeadNew.params and
                            not objHeadNew.params[HParams.Name]):
                        objTempl.setPrefix("")
                if not objTempl:
                    self.setError(
                        _("Wrong header parameter 'format=%s' "
                          "in template")
                        % formatTemplate + " " + nameFileTemplate)
                    return None
                if objHeadOld and objHeadOld.body:
                    self.textConfig = objHeadOld.body
                # обработка конфигурационного файла
                objTempl.printWARNING = self.printWARNING
                self.textTemplate = objTempl.processingFile(
                    self.textConfig, pathJoin(self.objVar.Get('cl_chroot_path'),
                                              self.objVar.Get('cl_root_path')),
                    self.nameFileConfigOrig
                )
                error = objTempl.getError()
                if error:
                    self.printERROR(error.strip())
                    if (formatTemplate in HParams.Formats.Executable and
                                formatTemplate != "diff"):
                        raise TemplatesError(
                            (_("Failed to use %s ") % formatTemplate) +
                            nameFileTemplate)
                    if (self.objVar.Get('cl_ebuild_phase') == 'compile' and
                        self.objVar.Get('cl_template_wrong_patch') == 'break'):
                        raise CriticalError(_("Failed to use patch ") +
                                             nameFileTemplate)
                    raise TemplatesError(_("Failed to use patch ") +
                                         nameFileTemplate)
                elif (formatTemplate == 'diff' and
                              self.objVar.Get('cl_verbose_set') == "on"):
                    #TODO fix transl
                    self.printSUCCESS(_("Appling patch") + " " +
                                      os.path.basename(nameFileTemplate))
                self.textConfig = self.add_comment(
                    execStr, title, self.textTemplate)
                if formatTemplate in HParams.Formats.Executable:
                    return objTempl.changed_files
                else:
                    self.saveConfFile()
                if HParams.RunNow in objHeadNew.params:
                    if not self.executeTemplate(
                            self.textConfig, objHeadNew.params[HParams.RunNow]):
                        self.setError(_("Failed to execute") + _(": ") +
                                      nameFileTemplate)
                        return None
                    return None
                if HParams.RunPost not in objHeadNew.params:
                    return filesApply
                else:
                    return None
            # Создаем объект в случае параметра format в заголовке

            if ((typeAppendTemplate == HParams.AppendParams.Replace or
                         typeAppendTemplate == HParams.AppendParams.Before or
                         typeAppendTemplate == HParams.AppendParams.After) and
                    not (formatTemplate == "bin" or formatTemplate == "raw")):
                # Преобразовываем бинарные файлы
                objTxtCoder = None
                if flagNotUtf8New:
                    objTxtCoder = utfBin()
                    self.textTemplate = objTxtCoder.encode(self.textTemplate)
                # создаем объект формата шаблона
                objTemplNew = self.formatFactory.createObject(
                    formatTemplate, self.textTemplate)
                if not objTemplNew:
                    self.setError(
                        _("Wrong header parameter '{var}={val}' "
                          "in template").format(
                            var=HParams.Format, val=formatTemplate)
                        + " " + nameFileTemplate)
                    return None
                if "xml_" in formatTemplate:
                    if objTemplNew.getError():
                        self.setError(_("Wrong template") + _(": ") +
                                      nameFileTemplate)
                        return None
                    # Имя файла внутри xml xfce конфигурационных файлов
                    nameRootNode = \
                        nameFileConfig.rpartition("/")[2].split(".")[0]
                    objTemplNew.setNameBodyNode(nameRootNode)
                # Объект Документ
                docObj = objTemplNew.docObj
                # Удаление комментариев из документа
                docObj.removeComment(docObj.getNodeBody())
                # Добавление необходимых переводов строк
                docObj.insertBRtoBody(docObj.getNodeBody())
                # Добавление необходимых разделителей между областями
                docObj.insertBeforeSepAreas(docObj.getNodeBody())
                # Пост обработка
                if 'postXML' in dir(objTemplNew):
                    objTemplNew.postXML()
                # Получение текстового файла из XML документа
                self.textTemplate = objTemplNew.getConfig()
                # Если не UTF-8 производим преобразование
                if objTxtCoder:
                    self.textTemplate = objTxtCoder.decode(self.textTemplate)
                # Титл для объединения
                if ListOptTitle:
                    title = self.getTitle(objTemplNew._comment,
                                          ListOptTitle,
                                          configPath=nameFileConfig)
            # Замена
            if typeAppendTemplate == HParams.AppendParams.Replace:
                if "xml_" in formatTemplate:
                    data = self.textTemplate.split("\n")
                    data.insert(1, title)
                    self.textConfig = "\n".join(data)
                else:
                    self.textConfig = self.add_comment(
                        objHeadNew.execStr, title, self.textTemplate)

                self.saveConfFile()
                if HParams.RunNow in objHeadNew.params:
                    if not self.executeTemplate(
                            self.textConfig,
                            objHeadNew.params[HParams.RunNow]):
                        self.setError(_("Failed to execute") + _(": ") + \
                                      nameFileTemplate)
                        return None
                    return None
                if HParams.RunPost not in objHeadNew.params:
                    return filesApply
                else:
                    return None
            # Вверху
            elif typeAppendTemplate == HParams.AppendParams.Before:
                if "xml_" in formatTemplate:
                    self.setError(
                        _("Wrong option '{var}={val}' in template {fn}").format(
                            var=HParams.Append, val=HParams.AppendParams.Before,
                            fn=nameFileTemplate))
                    return None
                if objHeadOld and objHeadOld.body:
                    self.textConfig = objHeadOld.body
                if self.textTemplate and self.textTemplate[-1] == "\n":
                    tmpTemplate = self.textTemplate + self.textConfig
                else:
                    tmpTemplate = self.textTemplate + "\n" + self.textConfig
                self.textConfig = self.add_comment(execStr, title, tmpTemplate)

                self.saveConfFile()
                if HParams.RunNow in objHeadNew.params:
                    if not self.executeTemplate(
                            self.textConfig, objHeadNew.params[HParams.RunNow]):
                        self.setError(_("Failed to execute") + _(": ") +
                                      nameFileTemplate)
                        return None
                    return None
                if HParams.RunPost not in objHeadNew.params:
                    return filesApply
                else:
                    return None
            # Внизу
            elif typeAppendTemplate == HParams.AppendParams.After:
                if "xml_" in formatTemplate:
                    self.setError(
                        _("Wrong option '{var}={val}' in template {fn}").format(
                            var=HParams.Append, val=HParams.AppendParams.After,
                            fn=nameFileTemplate))
                    return None
                if objHeadOld and objHeadOld.body:
                    self.textConfig = objHeadOld.body
                if not self.textTemplate or self.textTemplate[-1] == "\n":
                    tmpTemplate = self.textConfig + self.textTemplate
                else:
                    tmpTemplate = self.textConfig + "\n" + self.textTemplate
                self.textConfig = self.add_comment(execStr, title, tmpTemplate)
                self.saveConfFile()
                if HParams.RunNow in objHeadNew.params:
                    if not self.executeTemplate(
                            self.textConfig, objHeadNew.params[HParams.RunNow]):
                        self.setError(_("Failed to execute") + _(": ") +
                                      nameFileTemplate)
                        return None
                    return None
                if HParams.RunPost not in objHeadNew.params:
                    return filesApply
                else:
                    return None
            # Объединение
            elif typeAppendTemplate == HParams.AppendParams.Join:
                objTxtCoder = None
                if flagNotUtf8New:
                    objTxtCoder = utfBin()
                    self.textTemplate = objTxtCoder.encode(self.textTemplate)
                if formatTemplate == "raw":
                    self.setError(
                        _("Wrong header parameter '{var}={val}' "
                          "in template").format(var=HParams.Append,
                                                    val=typeAppendTemplate) +
                        " " + nameFileTemplate)
                    return None
                # создаем объект формата шаблона
                objTemplNew = self.formatFactory.createObject(
                    formatTemplate, self.textTemplate)
                if not objTemplNew:
                    self.setError(
                        _("Wrong header parameter '{var}={val}' in "
                          "template").format(var=HParams.Format,
                                                 val=formatTemplate) + " "
                        + nameFileTemplate)
                    return None
                if "xml_" in formatTemplate:
                    if objTemplNew.getError():
                        self.setError(_("Wrong template") + _(": ") + \
                                      nameFileTemplate)
                        return None
                    nameRootNode = nameFileConfig.rpartition("/")[2].split(".")[0]
                    objTemplNew.setNameBodyNode(nameRootNode)
                # Титл для объединения
                if ListOptTitle:
                    title = self.getTitle(objTemplNew._comment,
                                          ListOptTitle,
                                          configPath=nameFileConfig)

                # В случае пустого конфигурационного файла
                reNoClean = re.compile("[^\s]", re.M)
                if not self.textConfig or \
                        not reNoClean.search(self.textConfig):
                    self.textConfig = ""

                objHeadOld = fileHeader(nameFileConfig, self.textConfig,
                                        objTemplNew._comment)
                if objHeadOld.body:
                    self.textConfig = objHeadOld.body
                else:
                    self.textConfig = ""
                if flagNotUtf8Old:
                    objTxtCoder = utfBin()
                    self.textConfig = objTxtCoder.encode(self.textConfig)
                # создаем объект формата шаблона для конфигурационного файла
                objTemplOld = self.formatFactory.createObject(
                    formatTemplate, self.textConfig)
                
                if not objTemplOld:
                    self.setError(_("Error in template %s") % nameFileConfig)
                    return None
                if "xml_" in formatTemplate:
                    if objTemplOld.getError():
                        self.setError(_("Wrong template") + _(": ") +
                                      nameFileConfig)
                        return None
                    nameRootNode = nameFileConfig.rpartition("/")[2].split(".")[
                        0]
                    objTemplOld.setNameBodyNode(nameRootNode)
                objTemplOld.join(objTemplNew)

                if "xml_" in formatTemplate:
                    if objTemplOld.getError():
                        self.setError(_("Wrong template") + _(": ") + \
                                      nameFileTemplate)
                        return None
                    data = objTemplOld.getConfig().split("\n")
                    data.insert(1, title)
                    self.textConfig = "\n".join(data)
                else:
                    self.textConfig = self.add_comment(
                        execStr, title, objTemplOld.getConfig())
                # Декодируем если кодировка не UTF-8
                if objTxtCoder:
                    self.textTemplate = objTxtCoder.decode(self.textTemplate)
                    self.textConfig = objTxtCoder.decode(self.textConfig)
                self.saveConfFile()

                if HParams.RunNow in objHeadNew.params:
                    if not self.executeTemplate(
                            self.textConfig, objHeadNew.params[HParams.RunNow]):
                        self.setError(_("Failed to execute") + _(": ") + \
                                      nameFileTemplate)
                        return None
                    return None
                if HParams.RunPost not in objHeadNew.params:
                    return filesApply
                else:
                    return None
            else:
                self.setError(_("Wrong template option (type append)")
                              + _(": ") + typeAppendTemplate)
                return None
        else:
            self.setError(_("Template type not found: ") + nameFileTemplate)
            return None

    def add_comment(self, execStr, comment, body):
        """
        Сформировать выходной файл с учётом строки выполнения, комментария и
        содержимого конфига
        :param execStr:
        :param comment:
        :param body:
        :return:
        """
        if len(execStr) == 0 and len(comment) == 0:
            return body
        if comment.startswith("<!--") and body.startswith('<?xml'):
            header, body = body.partition("\n")[::2]
            body = header + "\n" + comment + body
        else:
            body = comment + body
        if execStr:
            if isinstance(body, bytes):
                execStr = execStr.encode("UTF-8")
            return execStr + body
        return body


class scanDirectoryClt(_error):
    """Класс для cканирования директорий с файлами .clt"""

    # Расширение файла шаблона
    extFileTemplate = ".clt"
    lenExtFileTemplate = len(extFileTemplate)
    filterApplyTemplates = {}
    reHeader = re.compile(r"\s*#\s*calculate\s*", re.I)
    critical = False

    def printWARNING(self, s):
        raise NotImplemented()

    def __init__(self, objVar=None):
        if objVar:
            self.objVar = objVar

    def processingFile(self, path, prefix, optFile=None):
        """Обработка в случае файла"""
        return True

    def hasBelong(self, filename):
        """
        Change belong function
        """
        with open(filename, 'r') as f:
            s = f.readline()
            if self.reHeader.search(s):
                while s:
                    if "belong(" in s or "merge(" in s:
                        return True
                    if not s.strip().endswith('\\'):
                        break
                    s = f.readline()
        return False

    def scanningTemplates(self, scanDir, prefix=None, flagDir=False,
                          objVar=None):
        """Сканирование и обработка шаблонов в директории scanDir (CLT)"""
        if not objVar:
            objVar = self.objVar
        if not prefix:
            prefix = os.path.realpath(scanDir)
        if flagDir or stat.S_ISDIR(os.lstat(str(scanDir))[stat.ST_MODE]):
            for fileOrDir in sorted(listDirectory(scanDir)):
                try:
                    absPath = os.path.join(scanDir, fileOrDir)
                    stInfo = os.lstat(str(absPath))
                    statInfo = stInfo[stat.ST_MODE]
                    if fileOrDir.endswith(self.extFileTemplate) and \
                            stat.S_ISREG(statInfo):
                        if (not self.filterApplyTemplates and
                                    objVar.Get('cl_merge_set') == 'off' or
                                    self.filterApplyTemplates and
                                        absPath[:-self.lenExtFileTemplate] in
                                        self.filterApplyTemplates.keys() or
                                self.hasBelong(absPath)):
                            prevDefault = objVar.defaultModule
                            if not self.processingFile(absPath, prefix):
                                return False
                            objVar.defaultModule = prevDefault
                    elif stat.S_ISDIR(statInfo):
                        if not self.scanningTemplates(absPath, prefix, True):
                            return False
                except TemplatesError as e:
                    self.clearErrors()
                    if self.critical:
                        raise
                    else:
                        self.printWARNING(str(e))
        return True


class templateClt(scanDirectoryClt, Template):
    """Класс для обработки шаблонов c расширением .clt"""

    def __init__(self, objVar, postmergePkgs, **kwargs):
        self.checkNumberTemplate = True
        Template.__init__(self, objVar, cltObj=False, **kwargs)
        self.postmergePkgs = postmergePkgs
        applyPackages = ["calculate-core"]
        self.flagApplyTemplates = False
        if self.objVar.Get("cl_name") in applyPackages:
            self.flagApplyTemplates = True
        # Базовая директория переноса шаблонов "/mnt/calculate" или "/" и.т.д
        self._chrootDir = os.path.normpath(self.objVar.Get("cl_chroot_path"))

    def applyTemplate(self, path):
        """Применение отдельного .clt шаблона"""
        if not self.flagApplyTemplates:
            return True
        return self.processingFile(path, "")

    def processingFile(self, path, prefix, optFile=None):
        """Обработка в случае шаблона файла clt"""
        self.numberProcessTempl += 1
        self.numberProcessTemplates(self.numberProcessTempl)
        # Пропуск шаблонов директорий scanningTemplates
        if self.templDirNameFile == os.path.split(path)[1]:
            return True
        self.functObj.currentBelong = ""
        self.functObj.currentBelongSlot = ""
        # Проверка на переменные в названии файла
        if not self.getNeedTemplate(path):
            if self.getError():
                return False
            return True
        if self.getError():
            return False
        if prefix and prefix[-1] == "/":
            prefix = prefix[:-1]
        if prefix and path.startswith(prefix):
            nameFileConfig = path.partition(prefix)[2]
        else:
            nameFileConfig = path
        nameFileConfig = nameFileConfig[:-self.lenExtFileTemplate]
        origFileName = nameFileConfig
        nameFileConfig = pathJoin(self._baseDir, nameFileConfig)
        # файл в системе без условий
        nameFileConfig = "/".join((x.split("?")[0] for x in nameFileConfig.split("/")))
        # Записываем в переменную обрабатываемый файл
        self.objVar.Set("cl_pass_file", os.path.basename(nameFileConfig))
        self.headerParams = None
        filesApl = self.joinTemplate(path, nameFileConfig)
        if self.headerParams:
            if has_any(self.headerParams, HParams.ServiceControl):
                self.doServiceControl(self.headerParams)
        if self.getError():
            return False
        if filesApl:
            if self.functObj.currentBelong:
                self._addFile(filesApl)
            else:
                if origFileName in self.filterApplyTemplates:
                    for pkg in self.filterApplyTemplates[origFileName]:
                        self._addFile(filesApl, pkg=pkg)
                else:
                    if not self.allContents:
                        fillContents(self.allContents,
                                     self.objVar.Get('cl_config_protect'),
                                     prefix=self.objVar.Get('cl_chroot_path'))
                    for fn in filesApl:
                        fn_orig = PkgContents.reCfg.sub('/', fn)
                        if self.objVar.Get('cl_chroot_path') != '/':
                            fn_orig = \
                                fn_orig[len(self.objVar.Get('cl_chroot_path')):]
                        if fn_orig in self.allContents:
                            self._addFile([fn], pkg=self.allContents[fn_orig])
            # Настоящее имя конфигурационного файла
            nameFileConfig = filesApl[0]
            # Пишем время модификации *.env файлов
            if nameFileConfig.endswith(".env"):
                nameEnvFile = os.path.basename(nameFileConfig)
                self.functObj.timeConfigsIni[nameEnvFile] = float(time.time())
            self.filesApply += filesApl
            return nameFileConfig
        else:
            return True

    def countsNumberTemplates(self, dirsTemplates=()):
        """Считаем количество шаблонов"""

        def createDictTemplates(path, prefix, dictTemplates):
            """Создает словарь {"директория":"кол-во шаблонов" ...}

            и считает общее количество шаблонов
            """
            # Количество шаблонов
            self.allTemplates += 1
            dirTemplate = os.path.split(path)[0]
            while True:
                if dirTemplate in dictTemplates.keys():
                    dictTemplates[dirTemplate] += 1
                else:
                    dictTemplates[dirTemplate] = 1
                if dirTemplate == prefix:
                    break
                dirTemplate = os.path.split(dirTemplate)[0]
            return dictTemplates

        if not dirsTemplates:
            dirsTemplates = list(self.objVar.Get("cl_template_clt_path"))
            dirsTemplates.sort()
        scanObj = scanDirectoryClt(objVar=self.objVar)
        scanObj.processingFile = lambda x, y: createDictTemplates(x, y,
                                                                  self.dictTemplates)
        # Считаем количество шаблонов
        for dirTemplate in dirsTemplates:
            scanObj.scanningTemplates(dirTemplate, "/")

    def applyTemplatesClt(self, cltPath=None):
        """Применяет шаблоны к конфигурационным файлам"""
        if not self.flagApplyTemplates:
            return [], []
        if cltPath is None and \
                not self.objVar.defined("cl_template_clt_path"):
            self.setError(_("undefined variable: ") + "cl_template_clt_path")
            return False
        if cltPath is None:
            dirsTemplates = self.objVar.Get("cl_template_clt_path")
        else:
            dirsTemplates = cltPath
        dirsTemplates = list(dirsTemplates)
        dirsTemplates.sort()
        if self.checkNumberTemplate:
            # Созданные директории
            self.createdDirs = []
            # Примененные файлы
            self.filesApply = []
            # Номер применяемог шаблона
            self.numberProcessTempl = 0
            # Словарь директорий с количеством файлов шаблонов
            self.dictTemplates = {}
            # Количество шаблонов
            self.allTemplates = 0
            # Установка по умолчанию аттрибутов для функциии шаблонов ini()
            # Время доступа к конфигурационному файлу функции шаблона ini()
            self.functObj.timeIni = -1
            # Первоначальный словарь переменных для ini()
            self.functObj.prevDictIni = {}
            # Текущий словарь переменных для ini()
            self.functObj.currDictIni = {}
            # Словарь времен модификации env файлов для ini()
            self.functObj.timeConfigsIni = {}
            # Считаем количество шаблонов
            self.countsNumberTemplates(dirsTemplates=dirsTemplates)
            self.numberAllTemplates(self.allTemplates)
        # default module
        self.defaultModule = "main"
        # Обрабатываем шаблоны
        for dirTemplate in dirsTemplates:
            if self.scanningTemplates(dirTemplate, self._chrootDir) is False:
                break
        return self.createdDirs, self.filesApply


class ProgressTemplate(Template):
    """
    Progress template for wsdl interface
    """

    def __init__(self, setValueCallback, *args, **kwargs):
        Template.__init__(self, *args, **kwargs)
        self.setValueCallback = setValueCallback
        self.value = None
        self.firstValue = True

    def numberAllTemplates(self, number):
        self.maximum = number
        return True

    def numberProcessTemplates(self, number):
        maximum = self.maximum or 1
        value = number * 100 // maximum
        if value != self.value:
            self.setValueCallback(min(100, max(0, value)))
            self.value = value
        return True

    def templateModify(self):
        if self.firstValue and hasattr(self, "onFirstValue"):
            self.onFirstValue()
            self.firstValue = False

