# -*- 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 calculate.lib.utils.portage import getInstalledAtom, RepositoryPath, \
    searchProfile, RepositorySubstituting
from calculate.lib.cl_xml import xmlShare
from functools import wraps
import types
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

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
from datavars import DataVarsError, VariableError, SimpleDataVars
from calculate.lib.configparser import (ConfigParser, NoSectionError,
                                        ParsingError)

from calculate.lib.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


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

class LayeredIni(object):
    _baseDir = None
    objVar = None

    class IniPath(object):
        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(SystemIni, self).__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 delVar(self, section, varname):
        try:
            self.config.remove_option(section, varname)
            for section in filter(lambda x: not self.config[x],
                                  self.config.sections()):
                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, 'wb') 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(object):
    """Общие аттрибуты для классов шаблонов"""
    # Метка начала переменной
    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, 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 = map(lambda x: x.gr_name,
                              filter(lambda x: userName in x.gr_mem,
                                     grp.getgrall()))
            groupsNames = [groupName] + groupsNames
            return uid, gid, homeDir, groupsNames
        return uid, gid, homeDir


class _shareTermsFunction(object):
    """Общие аттрибуты для классов _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 = filter(lambda x: suffix.startswith(x),
                                 self._suffixDict.keys())
            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 = map(lambda x: [x] if x else ['0'], (svA, svB))
                fillZero(svA, svB)
                sA, sB = map(lambda x: x if x else 0, (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 = map(lambda x: x if x else '0', (lA, lB))
            elemA.append(lA)
            elemB.append(lB)

        # dereferencing suffix in suffixes list
        ssA = map(lambda x: (self._suffixDict.get(x[0], 0), x[1]), ssA)
        ssB = map(lambda x: (self._suffixDict.get(x[0], 0), x[1]), 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 = map(lambda x: [x[1:]], (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, 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 = map(lambda x: x + quoteSymbol, ("=", ">", "<"))
            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 filter(lambda x: x in i, listTerm):
                        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, filter(lambda x: not x in foundPar,
                                     mass), 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(object):
    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(object):
        Join = "join"
        Before = "before"
        After = "after"
        Replace = "replace"
        Remove = "remove"
        Skip = "skip"
        Patch = "patch"
        Clear = "clear"
        LinkDirCompatible = (Replace, Join)

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

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

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

    class OptDir(object):
        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]
                    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]
            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(object):
    """Разбиваем текст на блоки"""

    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)
                self.F_CONF.write(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, "r")
        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, "r+")
        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, "w+")
            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 = 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 = 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(object):
    """Класс для преобразования в 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:]
        ret = None
        exec ("ret = '\\x%s'" % he)
        return ret

    def _hexToChar(self, he):
        ret = None
        exec ("ret = '\\x%s'" % he)
        return ret

    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(TemplateFormat, self).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(object):
    """
    Фабрика классов форматов шаблонов
    """
    # Импортированные классы поддерживаемых форматов шаблонов
    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):
                # Создаем объект из 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):
                            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.func_code.co_varnames[:f.func_code.co_argcount]
                         if x in ("self", "nameTemp", "localVars")]
            varnum = f.func_code.co_argcount - len(spec_vars)
            defnum = len(f.func_defaults) if f.func_defaults else 0
            # число обязательных параметров
            reqnum = varnum - defnum
            if funArgv:
                terms = map(lambda x: x.strip(), 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 = filter(lambda x: x[0].startswith(pref) and \
                                        hasattr(x[1], "__call__"),
                              self.__class__.__dict__.items())
            # удаляем у названия функции префикс и переводим остаток названия
            # в нижний регистр
            dictFunc = map(lambda x: (x[0][len(pref):].lower(), x[1]), dictFunc)
            # Формируем словарь функций шаблона
            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 = 0640
        else:
            self.fileConfigIni = pathJoin(self._chrootDir,
                                          LayeredIni.IniPath.Work)
            self.pathConfigIni = os.path.dirname(self.fileConfigIni)
            self.modeConfigIni = 0644

    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 localVars.has_key(strNum):
                        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(object):
            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(object):
            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')
        if prefix == "/":
            p = process(self.check_command("/usr/bin/eix", prefix=prefix),
                        "-*", "--format", "<bestslotversions:NAMEASLOT>",
                        "--category", "-e", category,
                        lang="C", envdict=dict(os.environ))
        else:
            p = process("/bin/chroot", prefix, self.check_command("/usr/bin/eix", prefix=prefix),
                        "-*", "--format", "<bestslotversions:NAMEASLOT>",
                        "--category", "-e", category,
                        lang="C", envdict=dict(os.environ))
        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, 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 not localVars.has_key(nameLocVar):
            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 = map(lambda x: x.strip(), 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(filter(lambda x: not self.reEmptyLoad.search(x),
                                       replace.split("\n")))
        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

    def getInstallPkgGentoo(self, category=""):
        pkgs = []
        filterFunc = lambda x: "SLOT" == x

        def getFilesDir(pkgs, dirname, names):
            for nameFile in filter(filterFunc, names):
                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 True

        os.path.walk(os.path.join(self.basePkgDir, category),
                     getFilesDir, pkgs)
        return self.sharePkg(pkgs)

    def pkg(self, nameProg, slot=None):
        if len(self.installProg) > 0:
            if type(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 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 filter(None, uses.split(',')):
                    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 xrange(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.encode("UTF-8")
        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].encode("UTF-8")
                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 = filter(None, self.objVar.Get('builder.cl_builder_image_data'))
        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 = map(lambda x: (int(x.group(1)), int(x.group(2)), x.group()),
                      filter(None,
                             map(re_resol.search,
                                 listDirectory(wpath))))
            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('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 = ""
        # Получаем время модификации конфигурационного файла
        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:
                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')
        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 = map(lambda x: x.strip(), 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 filter(lambda x: not x.strip(), vals):
            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 = map(lambda x: x.strip(), 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:
            pkgs = self.objVar.Get("cl_merge_pkg")
            replace = ""
            if pkgs:
                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:
                module_path = os.path.join(
                    self.objVar.Get('cl_chroot_path'),
                    "usr/lib/python2.7/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 filter(lambda x: not config[x],
                                      config.sections()):
                    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):
    """
    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 map(lambda x: (x[0], x[1][0][1]),
                   filter(lambda x: x[1],
                          map(lambda x: (
                              x[0], filter(lambda x: x[0] == pkg, x[1])),
                              self.data.items())))

# 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 = "For modify this file, create %(conf_path)s.clt template."
    protectPaths = []
    allContents = {}
    if "CONFIG_PROTECT" in os.environ:
        protectPaths = ["/etc"] + filter(lambda x: x.strip(),
                                         os.environ["CONFIG_PROTECT"].split(
                                             " "))
        protectPaths = map(lambda x: os.path.normpath(x), protectPaths)

    @classmethod
    def removeComment(cls, text):
        re_comment = re.compile('(?:<!--|[{symb}]-*)\n'
                                '[{symb}]? {modified} .*\n'
                                '[{symb}]? {processed}\n'
                                '([{symb}]? /.*\n'
                                ')+(?:[{symb}]? {endtitle}.*\n)?'
                                r'(-->|[{symb}]-*)\n'.format(
            modified=cls._titleList[0],
            processed=cls._titleList[1],
            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):
        # совместимость с вызовами из модулей предыдущих версий
        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.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
        # Предупреждения
        # 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))
            if p.readerr():
                for line in p.readerr().split('\n'):
                    if line:
                        line = self.translator.translate(line)
                    self.printWARNING(line.strip())
            return True
        else:
            if p.readerr():
                for line in p.readerr().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)
            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) == types.TupleType 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):
        textLines = text.splitlines()
        paramLine = ""
        if textLines:
            textLine = textLines[0]
            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:
                    paramLine = text[reP.end():reLs.end()]
                    paramLine = paramLine.replace("\\", " ")
                else:
                    paramLine = textLine[reP.end():]
        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 = map(lambda x: os.path.join(dirsTemplate, x),
                               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) 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

    @catch_no_space_left
    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 = 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 = filter(lambda x: os.path.exists(x), dirsTemplates)
        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 = filter(
                        lambda x: x not in
                                  self.cltObj.filterApplyTemplates[filename],
                        map(lambda x: x[0], pkgs))
                    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')):
            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(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(
                    filter(lambda x: not x.endswith('/ini.env'),
                           self.filesApply))
        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)).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(map(lambda x: pathJoin(chrootPath, x),
                                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 = map(lambda x: autoUpdateDict.get(x, x),
                              self.filesApply)
        if filter(lambda x: "._cfg" in x, self.filesApply):
            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
        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(map(lambda x: x.split("?")[0],
                                      nameFileConfig.split("/")))
        # Записываем в переменную обрабатываемый файл
        self.objVar.Set("cl_pass_file", os.path.basename(nameFileConfig))
        self.headerParams = None
        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 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(map(lambda x: x.split("?")[0], 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("(.*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(',')
        else:
            mergePkgs = []
        if HParams.PostMerge in params:
            postmergePkgs = params[HParams.PostMerge].split(',')
        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:
                if not pkg in self.objVar.Get('cl_merge_pkg_new') and \
                        not pkg in self.objVar.Get('cl_merge_pkg_pass') and \
                        not pkg in self.objVar.Get('cl_merge_pkg'):
                    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 == os.errno.EPERM:
            if self.checkVfat(fn):
                return True
        if hasattr(e, 'errno') and e.errno == os.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 = filter(lambda x: x.startswith(envparam),
                                 headerLine.split())
            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(":")
                    import pwd

                    try:
                        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, []
                    try:
                        import grp

                        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(
                    filter(lambda x: x and len(x) > 1 and x[0] and x[1],
                           map(lambda x: x.split(':')[0:3:2],
                               filter(lambda x: not x.startswith('#'),
                                      f))))
                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(
                    filter(lambda x: x and len(x) > 1 and x[0] and x[1],
                           map(lambda x: x.split(':')[0:3:2],
                               filter(lambda x: not x.startswith('#'),
                                      f))))
            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 filter(pathFile.startswith,
                      map(lambda x: pathJoin(chrootPath, x),
                          self.objVar.Get('cl_config_protect'))) or \
                filter(pathFile.startswith,
                       map(lambda x: pathJoin(chrootPath, x),
                           self.objVar.Get('cl_config_protect_mask'))):
            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 = 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 = 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:
                    with open(pathOldFile, "w+") 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 += map(lambda x: reCfg.sub('/', x), 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 = filter(None, params[param].split(','))
                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(object):
            # Объединяем конфигурации
            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), 'r') as FD:
                    data = FD.read(1) + FD.read()
            try:
                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 = 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()
        headerLine = self.getHeaderText(self.textTemplate)
        if headerLine:
            envparam = "%s=" % HParams.Environ
            moduleParam = filter(lambda x: x.startswith(envparam),
                                 headerLine.split())
            if moduleParam:
                self.objVar.defaultModule = moduleParam[0].partition('=')[2]
                try:
                    importlib.import_module(
                        "calculate.%s.variables" % self.objVar.defaultModule)
                except (ImportError, AttributeError):
                    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 filter(lambda x: "calculate/ini.env" in x,
                                     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)
            title = title.encode("UTF-8")

        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)
                    raise TemplatesError(_("Failed to use patch ") +
                                         nameFileTemplate)
                elif (formatTemplate == 'diff' and
                              self.objVar.Get('cl_verbose_set') == "on"):
                    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(_("Wrong template") + _(": ") +
                                      nameFileTemplate)
                        self.setError(_("Failed to execute") + _(": ") +
                                      self.nameFileConfig)
                        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().encode("UTF-8")
                # Если не UTF-8 производим преобразование
                if objTxtCoder:
                    self.textTemplate = objTxtCoder.decode(self.textTemplate)
                # Титл для объединения
                if ListOptTitle:
                    title = self.getTitle(objTemplNew._comment,
                                          ListOptTitle,
                                          configPath=nameFileConfig)
                    title = title.encode("UTF-8")
            # Замена
            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(_("Wrong template") + _(": ") + \
                                      nameFileTemplate)
                        self.setError(_("Failed to execute") + _(": ") + \
                                      self.nameFileConfig)
                        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(_("Wrong template") + _(": ") +
                                      nameFileTemplate)
                        self.setError(_("Failed to execute") + _(": ") +
                                      self.nameFileConfig)
                        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(_("Wrong template") + _(": ") +
                                      nameFileTemplate)
                        self.setError(_("Failed to execute") + _(": ") +
                                      self.nameFileConfig)
                        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)
                    title = title.encode("UTF-8")

                # В случае пустого конфигурационного файла
                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().encode("UTF-8").split("\n")
                    data.insert(1, title)
                    self.textConfig = "\n".join(data)
                else:
                    self.textConfig = self.add_comment(
                        execStr, title, objTemplOld.getConfig().encode("UTF-8"))
                # Декодируем если кодировка не 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(_("Wrong template") + _(": ") + \
                                      nameFileTemplate)
                        self.setError(_("Failed to execute") + _(": ") + \
                                      self.nameFileConfig)
                        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 comment.startswith("<!--") and body.startswith('<?xml'):
            header, body = body.partition("\n")[::2]
            body = header + "\n" + comment + body
        else:
            body = comment + body
        if execStr:
            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(map(lambda x: x.split("?")[0],
                                      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 = 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.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

