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

# Copyright 2008-2013 Calculate Ltd. 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.
from contextlib import contextmanager

import sys
import os
import stat
import re
import errno
import xml.dom.minidom as minidom
import xml
import importlib
if hasattr(xml,"use_pyxml"):
    xml.use_pyxml()
from xml import xpath
import subprocess
import types
import random
import string
import time
import glob
import hashlib
import fcntl
from fnmatch import fnmatch
from math import sqrt
from itertools import *
#                    <   <=  ==  !=  >=  >
from operator import lt, le, eq, ne, ge, gt

from utils.common import _error, _warning,getTupleVersion,getPortageUidGid
from utils.text import  _toUNICODE, convertStrListDict
from utils.portage import (isPkgInstalled,reVerSplitToPV,EmergeLog,
                           EmergeLogPackageTask, getPkgUses)
from utils.content import PkgContents,checkContents,getCfgFiles,fillContents
from utils.files import (getModeFile, listDirectory,removeDir, typeFile,
                        scanDirectory,timeout,
                        pathJoin,readFile,readLinesFile,process,STDOUT)
from datavars import DataVarsError

from calculate.lib.cl_lang import setLocalTranslate, RegexpLocalization

setLocalTranslate('cl_lib3',sys.modules[__name__])

PORTAGEUID,PORTAGEGID = getPortageUidGid()

class TemplatesInterrupt(Exception):
    """
    Interrupt templates appling
    """
    EXIT,ABORT=1,2

    def __init__(self,*args):
        self.message = args[0]
        self.args = args
    
    def __str__(self):
        return str(self.message)

    def isExit(self): return self.isInterrupt() and self.args[1] == self.EXIT

    def isAbort(self): return self.isInterrupt() and self.args[1] == self.ABORT

    def status(self): return self.args[1] if self.isInterrupt() else 0

    def isInterrupt(self): return len(self.args)>1

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

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


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

    """
    # регулярное выражение для поиска функции в шаблоне
    _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)

    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 _notVersion(self, strVersion):
        """strVersion is not version - True"""
        numberVersion, letters, suffixes, rVersion =\
            self._splitVersion(strVersion)
        if not numberVersion.strip():
            return True
        if self._re_not_Version.search(numberVersion):
            return True
        if letters and not letters in self._letters:
            return True
        for suffix,suffixVersion in suffixes:
            if suffixVersion and self._re_not_Number.search(suffixVersion):
                return True
        if rVersion:
            if rVersion[0] != "r" or len(rVersion) == 1:
                return True
            if self._re_not_Number.search(rVersion[1:]):
                return True
        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 _equalTerm(self, term, textError, function=False):
        """Вычисление логических выражений для условий

        Для корректной работы в классе который наследует этот класс
        должен быть объявлен аттрибут self.objVar
        (объект для работы с переменными)
        function - функция для  для обработки функций в заголовке блока
        """
        rpl = lambda x: x.replace("@@"," ")
        trm = {"&&":"@@and@@","||":"@@or@@"}
        dictRuleFunc = {"==":eq, "!=":ne, ">=":ge, "<=":le, ">":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:
                flagRule = False
                for sepF in rule:
                    if sepF in t:
                        flagRule = True
                        vals = t.split(sepF)
                        break
                if flagRule:
                    #проверка на допустимость названия переменной
                    flagFunction = False
                    if not self._reRightName.search(vals[0]):
                        #проверка на допустимость функции
                        flagError = True
                        if 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
                    #проверка на допустимость значения
                    if self._reDenyValue.search(vals[1]):
                        self.setError("'%s'"%rpl(term) + " " + _("incorrect"))
                        self.setError(textError)
                        return False
                    flagIntTypeVar = None
                    if flagFunction:
                        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:
                                    self.setError("'%s'"%rpl(term) + " " + \
                                    _("incorrect"))
                                    self.setError(textError)
                                    return False
                                flagIntTypeVar = True
                            else:
                                flagIntTypeVar = False
                        else:
                            if valVars == "" and\
                                not self._notVersion(vals[1]):
                                    valVars = "0"
                            elif vals[1] == "" and\
                                not self._notVersion(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 not self._notVersion(valVars) and\
                            not self._notVersion(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
                    # Cравниваем номера версий
                    if flagNotIniFunct:
                        if flagNotEmptyVals and\
                            ("_ver" in vals[0] or\
                            (flagFunction and searchFunct.group(1) in \
                                ("pkg","merge")) or\
                            (flagFunction and searchFunct.group(1)=="load" and\
                            re.search("\(\s*ver\s*,",vals[0]))):
                            # Проверка значения на версию
                            if self._notVersion(vals[1]):
                                self.setError("'%s'"%rpl(term)+" "+\
                                              _("incorrect"))
                                self.setError(
                                    _("This value is not a version"))
                                return False
                            # Проверка значения функции на версию
                            if self._notVersion(valVars):
                                self.setError("'%s'"%rpl(term)+" "+\
                                              _("incorrect"))
                                self.setError(\
                                    _("The function value is not a version"))
                                return False
                            verFile, verVar = self._convertVers(vals[1],valVars)
                            res = dictRuleFunc[sepF](verVar,verFile)
                            if res:
                                listEqual.append(True)
                            else:
                                listEqual.append(False)
                                break
                            flagNotIniFunct = False
                        else:
                            if flagIntTypeVar is None:
                                flagIntTypeVar = True
                                try:
                                    valVars = int(valVars)
                                except:
                                    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:
                                    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 fileHeader(_terms):
    """Обработка заголовков шаблонов и конфигурационных файлов

    """
    # Допустимые параметры заголовка
    allowParam = ["format","dotall","multiline",
                  "comment", "append", "force",
                  "link", "mirror", "symbolic", "chmod", "chown", "name",
                  "path", "autoupdate","protected", "run","exec","merge",
                  "module","env","postmerge"]

    # Тип шаблона
    fileType = ""
    # Тип вставки шаблона
    typeAppend = ""
    # Возможные типы вставки шаблонов
    _fileAppend = "join", "before", "after", "replace", "remove", "skip",\
                  "patch", "clear"
    # Интерпретатор (#!/bin/bash) (#!/usr/bin/python)
    execStr = ""
    # Символ комментария
    comment = False
    # Выражение для поиска строки интерпретатора
    reExecStr = re.compile("^(#!.+\s)",re.M)
    # условные операторы
    terms = ('>', '<', '==', '!=', '>=', '<=')
    # параметры без значения
    listParNotVal = ("multiline", "dotall", "symbolic", "force", "mirror",
                     "autoupdate", "protected")
    # Результат вычисления условия в заголовке
    headerTerm = True

    def __init__(self, templateName, text, comment=False, fileType=False, 
                 objVar=False, function=False, templateObj=None):
        self.body = text
        # Объект с переменными
        self.objVar=objVar
        # Параметры описанные в заголовке файла шаблона
        self.params = {}
        # некорректные параметры
        incorrectParams = []
        # Поиск строки запустка  (#!/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 type(comment) == types.TupleType 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 != False:
            if fileType=="bin":
                self.params["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:
                        reL = False
                        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 = ""
                        paramLine = templateObj.applyFuncTemplate(paramLine,templateName)
                        paramList = self.splitParLine(paramLine)
                        if paramList:
                            for i in paramList:
                                foundTerm = False
                                for term in self.terms:
                                    if term in i:
                                        foundTerm = True
                                        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
                                if not foundTerm:
                                    par = i.split("=")
                                    if len(par) == 1:
                                        if i in self.listParNotVal:
                                            self.params[i] = "True"
                                        else:
                                            if i.strip():
                                                incorrectParams = set([i])
                                    elif len(par) == 2:
                                        self.params[par[0]] = par[1]
                                        if par[0] == "env":
                                            try:
                                                varModule = \
                                                    importlib.import_module(
                                                    "calculate.%s.variables"
                                                    %par[1])
                                            except (ImportError,
                                                    AttributeError),e:
                                                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'")\
                                    %"append=%s"%self.params["append"])
        if any(x in self.params for x in ('exec','run')):
            if 'exec' in self.params:
                self.execStr = "#!%s\n"%self.params['exec']
            if 'run' in self.params:
                self.execStr = "#!%s\n"%self.params['run']
            if "python" in self.execStr:
                self.execStr += "# -*- coding: utf-8 -*-\n"

        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):
        """Выдать тип файла"""
        if "format" in self.params:
            return self.params["format"]
        else:
            return "raw"

    def _getAppend(self):
        """Выдать тип добавления файла"""
        if self.params.has_key("append"):
            if self.params["append"] in self._fileAppend:
                return self.params["append"]
            else:
                return False
        else:
            if self.fileType != "raw" and self.fileType != "bin" and\
                self.fileType != "":
                if "format" in self.params and self.params["format"] in \
                    ("patch","diff"):
                    self.params["append"] = "patch"
                    return "patch"
                self.params["append"] = "join"
                return "join"
            self.params["append"] = "replace"
            return "replace"

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


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

    """
    # Допустимые параметры заголовка
    allowParam = ["append", "chmod", "chown", "name", "path", "autoupdate",
                  "module","env","merge","postmerge"]

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

    # Возможные типы вставки шаблонов
    _fileAppend = "join", "remove", "skip", "clear"

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

    # параметры без значения
    listParNotVal = ("symbolic", "force", "autoupdate")

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

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

        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:
                reL = False
                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:
                            foundTerm = False
                            for term in self.terms:
                                if term in i:
                                    foundTerm = True
                                    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
                            if not foundTerm:
                                par = i.split("=")
                                if len(par) == 1:
                                    if i in self.listParNotVal:
                                        self.params[i] = "True"
                                    else:
                                        if i.strip():
                                            incorrectParams = set([i])
                                elif len(par) == 2:
                                    self.params[par[0]] = par[1]
                                    if par[0] == "env":
                                        try:
                                            varModule = \
                                                importlib.import_module(
                                                "calculate.%s.variables"%par[1])
                                        except (ImportError,AttributeError),e:
                                            self.headerTerm = False
                                        self.objVar.defaultModule = \
                                            self.params['env']
                    typeAppend = self._getAppend()
                    if typeAppend:
                        self.typeAppend = typeAppend
                    else:
                        self.headerTerm = False
                        self.setError(_("incorrect header parameter: '%s'")\
                                      %"append=%s"%self.params["append"])
        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 self.params.has_key("append"):
            if self.params["append"] in self._fileAppend:
                return self.params["append"]
            else:
                return False
        else:
            return "join"

class objShare:
    """Общий клас для объектов, наследуем

    """

    def createFieldTerm(self, name, value, quote, docObj):
        """Создание поля переменная - значение

        при создании поля проверяется первый символ названия переменной
        и добавляется тег action
        "!" - <action>drop</action> удаляет
        "+" - <action>join</action> добавляет
        "-" - <action>replace</action> заменяет
        """
        fieldAction = False
        if name:
            if name[0] == "!" or name[0] == "-" or name[0] == "+":
                qnt = self.removeSymbolTerm(quote)
                fieldXML = docObj.createField("var",[qnt],
                                              name[1:], [value],
                                              False, False)
                if name[0] == "!":
                    fieldAction = "drop"
                elif name[0] == "+":
                    fieldXML.setAttribute("type", "seplist")
                    fieldAction = "join"
            else:
                fieldXML = docObj.createField("var",
                                              [quote.replace("\n","")],
                                              name, [value],
                                              False, False)
        else:
            fieldXML = docObj.createField("var",
                                          [quote.replace("\n","")],
                                          name, [value],
                                          False, False)
        if fieldAction:
            docObj.setActionField(fieldXML, fieldAction)
        return fieldXML

    def removeSymbolTerm(self, text):
        """Удаляет первый символ названия переменной в строке

        Если первый встречающийся символ с начала строки
        '+', '-', '!' то он из этой строки будет удален,
        если перед этим символом были пробельные символы,
        то они будут сохранены, так-же если в строке есть символ
        перевода строки он будет удален.
        """
        reTerm = re.compile("^[ \t]*(\!|\+|\-)")
        textNS = text.replace("\n","")
        res = reTerm.search(textNS)
        if res:
            textNS = textNS[res.start():res.end()-1] + textNS[res.end():]
        return textNS

    def getConfig(self,joinChar=""):
        """Выдает конфигурационный файл"""
        listConfigTxt = []
        childNodes = self.docObj.getNodeBody().childNodes
        for node in childNodes:
            if node.nodeType == node.ELEMENT_NODE:
                if node.tagName == "field":
                    listConfigTxt.append(self.docObj.getQuoteField(node))
                elif node.tagName == "area":
                    self.docObj.xmlToText([node], listConfigTxt)
        return joinChar.join(listConfigTxt)


    def splitToFields(self, txtBloc):
        """Разбиваем текст на поля (объекты) которые имеют следующие аттрибуты

        self.name  - если переменная то имя переменной
        self.value  - если у переменной есть значение то значение переменной
        self.comment - если комментарий то текст комментария
        self.br - если перевод строки то текст перед переводом из пробелов

        Результат список объектов полей
        """
        finBloc = "\n"
        if txtBloc[-1] != "\n":
            finBloc = ""

        linesBlocTmp = txtBloc.splitlines()
        linesBloc = []
        brBloc = []
        z = 0
        lenLines = len(linesBlocTmp)
        for i in linesBlocTmp:

            if self.reComment.split(i)[0]:
                findCooment = self.reComment.search(i)
                comment = False
                par = i
                if findCooment:
                    par = i[:findCooment.start()]
                    comment = i[findCooment.start():]
                fields = self.reSepFields.split(par)
                lenFields = len(fields)

                if lenFields>1:
                    for fi in range(lenFields-1):
                        linesBloc.append(fields[fi]+ self.sepFields)
                        if fi == lenFields-2:
                            if comment:
                                brBloc.append("")
                            else:
                                if (lenLines-1)== z:
                                    brBloc.append(finBloc)
                                else:
                                    brBloc.append("\n")
                        else:
                            brBloc.append("")
                    if comment:
                        linesBloc.append(comment)
                        if (lenLines-1)== z:
                            brBloc.append(finBloc)
                        else:
                            brBloc.append("\n")
                else:
                    linesBloc.append(i)
                    if (lenLines-1)== z:
                        brBloc.append(finBloc)
                    else:
                        brBloc.append("\n")
            else:
                linesBloc.append(i)
                if (lenLines-1)== z:
                    brBloc.append(finBloc)
                else:
                    brBloc.append("\n")
            z +=1
        fields = self.setDataField(linesBloc, brBloc)
        return fields

class xmlShare:
    """Общий класс для объектов XML, наследуем

    """
    def _createElement(self, doc, tagName, text="", attributes={}):
        """Создание нового XML элемента"""
        element = doc.createElement(tagName)
        if text:
            txtNode = doc.createTextNode(_toUNICODE(text))
            element.appendChild(txtNode)
        for attr in attributes.keys():
            attribute = doc.createAttribute(attr)
            attribute.nodeValue = attributes[attr]
            element.setAttributeNode(attribute)
        return element


class xmlNode(xmlShare):
    """Класс для создания нод без аттрибутов

    """
    def __init__(self):
        self.node = False


    def createNode(self, doc, tagName, text=""):
        """Создает XML элемент без аттрибутов"""
        self.node=self._createElement(doc, tagName, text)
        return self.node

    def getNode(self):
        return self.node


class xmlCaption:
    """Класс XML заголовок

    """
    def __init__(self):
        #Заголовок области XML нода
        self.caption = False


    def createCaption(self, doc, name, quotes, action=False):
        """Создание заголовка области"""
        tmpNode = xmlNode()
        self.caption = tmpNode.createNode(doc, "caption")
        nameNode = tmpNode.createNode(doc, "name",name)
        self.caption.appendChild(nameNode)
        if action:
           actNode = tmpNode.createNode(doc, "action", action)
           self.caption.appendChild(actNode)
        for q in quotes:
            quoteNode = tmpNode.createNode(doc, "quote", q)
            self.caption.appendChild(quoteNode)
        return self.caption

    def getCaption(self):
        """Выдает XML ноду заголовка области"""
        return self.caption

class xmlField(xmlShare):
    """Класс для работы с XML полем

    """
    def __init__(self):
        # XML нода поле
        self.field = False


    def createField(self, doc, typeField, quotes, name="",
                    values=[],action=False):
        """Cоздание XML ноды поле"""
        self.field = self._createElement(doc, "field", "", {"type":typeField})
        if name:
            nameNode = self._createElement(doc, "name", name)
            self.field.appendChild(nameNode)
        for v in values:
            valueNode = self._createElement(doc, "value", v)
            self.field.appendChild(valueNode)
        if action:
           actNode = self._createElement(doc, "action", action)
           self.field.appendChild(actNode)
        for q in quotes:
            quoteNode = self._createElement(doc, "quote", q)
            self.field.appendChild(quoteNode)
        return self.field



class xmlFields:
    """Класс, в котором находится список ХМL нод field

    """
    def __init__(self):
        self.fields = []

    def appendField(self, field):
        """Добавить XML ноду field"""
        self.fields.append(field)
        return self.fields

    def getFields(self):
        """Выдать список XML нод"""
        return self.fields


class xmlArea:
    """Класс для работы с XML областью

    """
    def __init__(self):
        # Область
        self.area = False

    def createArea(self, doc, xmlCaption, xmlFields):
        """Создание XML области"""
        tmpNode = xmlNode()
        self.area = tmpNode.createNode(doc, "area")
        if xmlCaption and xmlCaption.getCaption():
            self.area.appendChild(xmlCaption.getCaption())
        if xmlFields:
            fields = xmlFields.getFields()
            for field in fields:
                self.area.appendChild(field)
        return self.area

class xmlDoc:
    """Класс для работы с XML документом

    """
    def __init__(self):
        # документ
        self.doc = False
        # главная нода
        self.root = False
        # тело документа
        self.body = False
        # Заголовок области - временный (в реальности один объект заголовок)
        self.tmpCaption = False
        # Поля - временные (в реальности один объект поля)
        self.tmpFields = False
        # Разделитель областей - по умолчанию перевод строки "\n"
        self.sepAreas  = False
        # Разделитель разделенных списков - по умолчанию перевод строки "\n"
        #self.sepSplitFields  = False


    def createDoc(self, typeDoc, version):
        """Создание нового документа новый документ"""
        docTxt = '<?xml version="1.0" encoding="UTF-8"?><cxmlconf><head>'
        docTxt += '<ver>%s</ver>'% version
        docTxt += '<format>%s</format>' % typeDoc
        docTxt += '</head><body></body></cxmlconf>'
        self.doc = minidom.parseString(docTxt)
        self.root = self.doc.documentElement
        self.body = xpath.Evaluate('child::body',self.root)[0]
        # установка разделителя областей
        self.sepAreas  = self.createField("br",[],"",[],False,False)
        # установка разделителя областей разделенных списков
        #self.sepSplitFields  = self.createField("br",[],"",[],False,False)
        return self.doc

    def addField(self, field):
        """Добавляет поле во временный список

        Из этого списка в дальнейшем формируется XML область
        """
        if not self.tmpFields:
            self.tmpFields = xmlFields()
        self.tmpFields.appendField(field)

    def createCaption(self, name, quotes, action=False):
        """Cоздает заголовок области

        Помещает заголовок в временный артибут
        Используется при создании области
        """
        self.tmpCaption = xmlCaption()
        return self.tmpCaption.createCaption(self.doc, name, quotes, action)

    def createField(self, typeField, quotes=[], name="",
                    values=[] ,action=False,addTmpField=True):
        """Cоздает поле

        Если установлена переменнная addTmpField
        добавляет поле во временный список
        """
        fieldObj = xmlField()
        field = fieldObj.createField(self.doc, typeField, quotes, name,
                                    values, action)
        if addTmpField:
            self.addField(field)
        return field

    def clearTmpFields(self):
        """Очищает временный список"""
        self.tmpFields = False

    def createArea(self):
        """Cоздает область

        Область создается на основании временного атрибута и временного списка
        """
        areaObj = xmlArea()
        area = areaObj.createArea(self.doc, self.tmpCaption, self.tmpFields)
        self.clearTmpCaptionAndFields()
        return area

    def clearTmpCaptionAndFields(self):
        """Очищает временный аттрибут и временный список"""
        self.tmpCaption = False
        self.tmpFields = False

    def getNodeRoot(self):
        """Выдает корневую ноду"""
        return self.root

    def getNodeBody(self):
        """Выдает ноду body"""
        return self.body

    def setActionField(self, xmlField, actionTxt):
        """Устанавливает свойство action для XML поля"""
        xmlActions = xpath.Evaluate('child::action',xmlField)
        if xmlActions and xmlActions[0].firstChild:
            xmlActions[0].firstChild.nodeValue = actionTxt
        else:
            nodeObj = xmlNode()
            newNode = nodeObj.createNode(self.doc, "action", actionTxt)
            xmlField.appendChild(newNode)


    def setActionArea(self, xmlArea, actionTxt):
        """Устанавливает свойство action для XML области"""
        xmlActions = xpath.Evaluate('child::caption/action',xmlArea)
        xmlCaptions = xpath.Evaluate('child::caption',xmlArea)
        if xmlActions and xmlActions[0].firstChild:
            xmlActions[0].firstChild.nodeValue = actionTxt
        else:
            if xmlCaptions:
                nodeObj = xmlNode()
                newNode = nodeObj.createNode(self.doc, "action", actionTxt)
                xmlCaptions[0].appendChild(newNode)

    def joinField(self, xmlArea, xmlNewField):
        """Объединяет XML ноду область и XML ноду поле"""
        newNameField = self.getNameField(xmlNewField)
        if not newNameField or not newNameField.strip():
            return False
        fieldsOldComp = xpath.Evaluate("child::field[child::name='%s']"\
                                    %(newNameField), xmlArea)
        # Если поле не найдено добавляем его
        typeNewField = self.getTypeField(xmlNewField)
        if not fieldsOldComp and typeNewField  != "seplist":
            if self.getActionField(xmlNewField) != "drop":
                self.setActionField(xmlNewField, "append")
                xmlArea.appendChild(xmlNewField)
            return True
        newFieldsAction = self.getActionField(xmlNewField)
        newValues = self.getFieldValues(xmlNewField)
        flagCompare = True

        for nodeFieldOld in fieldsOldComp:
            if newFieldsAction == "drop":
                if nodeFieldOld.nextSibling and\
                    self.getTypeField(nodeFieldOld.nextSibling) == "br":
                        xmlArea.removeChild(nodeFieldOld.nextSibling)
                elif nodeFieldOld.previousSibling and\
                    self.getTypeField(nodeFieldOld.previousSibling) == "br":
                        xmlArea.removeChild(nodeFieldOld.previousSibling)
                xmlArea.removeChild(nodeFieldOld)
                continue
            oldValues = self.getFieldValues(nodeFieldOld)
            # Сравнение значений переменной шаблона и файла
            if set(newValues) != set(oldValues):
                flagCompare = False
        if self.getActionField(xmlNewField) == "drop":
            return True
        appSplLst = []
        insSplLst = []
        if typeNewField  == "seplist":
            if fieldsOldComp:
                xmlOldField = fieldsOldComp[-1]
            else:
                xmlOldField = False
            seplistNewXML = self.getSepListToField(xmlNewField)
            if seplistNewXML:
                for nodeSeplist in seplistNewXML:
                    if self.getActionField(nodeSeplist) != "drop":
                        if newFieldsAction == "join":
                            flagCompareSeplist = False
                            newValues = self.getFieldValues(nodeSeplist)
                            for nodeFieldOld in fieldsOldComp:
                                oldValues = self.getFieldValues(nodeFieldOld)
                                for newValue in newValues:
                                    if newValue in oldValues:
                                        flagCompareSeplist = True
                                        break
                            if not flagCompareSeplist:
                                nextNode = xmlOldField.nextSibling
                                newInsNode = nodeSeplist.cloneNode(True)
                                self.setActionField(newInsNode,"append")

                                if nextNode:
                                    appSplLst.append((newInsNode,
                                                        nextNode,
                                                        "insert"))
                                else:
                                    appSplLst.append((newInsNode,
                                                        False,
                                                        "append"))
                        else:
                            newInsNode = nodeSeplist.cloneNode(True)
                            if self.getActionField(newInsNode) == "join":
                                self.setActionField(newInsNode,"append")
                            if xmlOldField:
                                insSplLst.append((newInsNode,
                                                  xmlOldField,
                                                  "insert"))
                            else:
                                insSplLst.append((newInsNode,
                                                  False,
                                                  "append"))

                            #xmlArea.insertBefore(\
                                #nodeSeplist.cloneNode(True),
                                #xmlOldField)

                        parentNode = nodeSeplist.parentNode
                        parentNode.removeChild(nodeSeplist)

                insNodesRepl = []
                for newNode, nxtNode, app in insSplLst:
                    flagCompareSeplist = False
                    newValues = self.getFieldValues(newNode)
                    for nodeRepl, nxtNode, app in insNodesRepl:
                        oldValues = self.getFieldValues(nodeRepl)
                        for newValue in newValues:
                            if newValue in oldValues:
                                flagCompareSeplist = True
                                break
                    if not flagCompareSeplist:
                        if xmlOldField:
                            insNodesRepl.append((newNode, nxtNode, app))

                for newNode, nxtNode, app in insNodesRepl:
                    if app == "insert":
                        xmlArea.insertBefore(newNode,nxtNode)
                    elif app == "append":
                        xmlArea.appendChild(newNode)
                if xmlOldField:
                    parentNode = xmlOldField.parentNode
                    if parentNode and newFieldsAction != "join":
                        parentNode.removeChild(xmlOldField)

        for newNode, nxtNode, app in appSplLst:
            if app == "insert":
                xmlArea.insertBefore(newNode,nxtNode)
            elif app == "append":
                xmlArea.appendChild(newNode)

        if not flagCompare and typeNewField  != "seplist":
            # Устанавливаем action=replace
            self.setActionField(xmlNewField, "replace")
            # Если параметры поля не сходятся заменяем поле
            xmlArea.replaceChild(xmlNewField.cloneNode(True),
                                             fieldsOldComp[-1])

        if newFieldsAction == "join":
            fieldsOldRemove = []
        else:
            fieldsOldRemove = fieldsOldComp[:-1]

        for nodeFieldOld in fieldsOldRemove:
            actionOldNode = self.getActionField(nodeFieldOld)
            if actionOldNode == "insert" or actionOldNode == "append":
                pass
            else:
                if nodeFieldOld.nextSibling and\
                    self.getTypeField(nodeFieldOld.nextSibling) == "br":
                        xmlArea.removeChild(nodeFieldOld.nextSibling)
                xmlArea.removeChild(nodeFieldOld)
        return True


    def getSepListToField(self, xmlField):
        """Выдает элементы распределенного массива

        Область предок поля, в этой области ищутся
        элементы распределенного массива
        """
        nameField = self.getNameField(xmlField)
        if not nameField:
            return []
        parentNode = xmlField.parentNode
        if parentNode:
            fieldsVal = xpath.Evaluate(\
            "child::field[attribute::type='seplist'][child::name='%s'] "\
            %(nameField), parentNode)
            return fieldsVal
        else:
            return []

    def removeComment(self, xmlArea):
        """Удаляет комментарии в XML области"""
        fieldNodes = xpath.Evaluate('descendant::field',xmlArea)
        for fieldNode in fieldNodes:
            if fieldNode.hasAttribute("type"):
                if fieldNode.getAttribute("type") == "comment" or\
                    fieldNode.getAttribute("type") == "br":
                    parentNode = fieldNode.parentNode
                    parentNode.removeChild(fieldNode)
                else:
                    if self.getActionField(fieldNode) == "drop":
                        pass
                    elif self.getActionField(fieldNode) == "join":
                        pass
                    else:
                        self.setActionField(fieldNode,"append")


    def joinBody(self, baseBody, newBody):
        """Объединяет две области Body"""
        newFields = xpath.Evaluate('child::field',newBody)
        xmlNewAreas = xpath.Evaluate('child::area',newBody)
        for xmlNewArea in xmlNewAreas:
            self.joinArea(baseBody,xmlNewArea)
        joinNewFields = xpath.Evaluate("child::field[child::action='join']"
                                       ,newBody)
        self.addNewFielsOldArea(newFields, joinNewFields, baseBody)


    def getRemoveNodeSepList(self, removeNodesDict, baseNode, xmNewlField):
        """Находит элементы разделенного списка

        Параметры:
        removeNodesDict - Cловарь удаляемых полей разделенного списка
                          формируется программой
        baseNode        - Нода в которой идет поиск
        xmNewlField     - Нода field которая проверяется на принадлежность
                          к разделенному списку
        """
        flagNewNodeSeplist = False
        if self.getTypeField(xmNewlField) == "seplist":
            flagNewNodeSeplist = True
        nameNewField = self.getNameField(xmNewlField)
        if nameNewField:
            if removeNodesDict.has_key(nameNewField):
                return removeNodesDict[nameNewField]
            else:
                oldFields = xpath.Evaluate('child::field', baseNode)
                removeNodes = []
                lenOldFields = len(oldFields)
                for i in range(lenOldFields):
                    oldNode = oldFields[i]
                    flagSep = self.getTypeField(oldNode) == "seplist"
                    if flagNewNodeSeplist:
                        flagSep = True
                    if  flagSep and\
                        nameNewField == self.getNameField(oldNode):
                        removeNodes.append(oldNode)
                        if i+1<lenOldFields:
                            nextNode = oldFields[i+1]
                            if self.getTypeField(nextNode) == "br":
                                removeNodes.append(nextNode)
                removeNodesDict[nameNewField] = removeNodes
                return removeNodes


    def addNewFielsOldArea(self, newFields, joinNewFields, xmlOldArea):
        """Добавляет новые XML поля в область шаблона"""
        removeNodesDict = {}
        notRemoveNodesDict = {}
        for notRemNode in joinNewFields:
            nameField = self.getNameField(notRemNode)
            if not notRemoveNodesDict.has_key(nameField):
                notRemoveNodesDict[nameField] = []
                notRemoveNodesDict[nameField].append(notRemNode)
            else:
                notRemoveNodesDict[nameField].append(notRemNode)
        notSepListField = []
        sepListField = []
        for nField in newFields:
            if self.getRemoveNodeSepList(removeNodesDict, xmlOldArea,
                                         nField):
                sepListField.append(nField)
            else:
                if self.getNameField(nField):
                    notSepListField.append(nField)
        for name in notRemoveNodesDict.keys():
            if removeNodesDict.has_key(name):
                removeNodesDict[name] = []
        for removeNodes in  removeNodesDict.values():
            if removeNodes:
                if self.getTypeField(removeNodes[-1]) == "seplist":
                    removeNodes = removeNodes[:-1]
                else:
                    removeNodes = removeNodes[:-2]

            for removeNode in removeNodes:
                xmlOldArea.removeChild(removeNode)
        for node in sepListField:
            node.setAttribute("type", "seplist")
            if not (self.getActionField(node) == "join" or\
            self.getActionField(node) == "drop"):
                self.setActionField(node,"insert")
            self.joinField(xmlOldArea, node)
        for node in notSepListField:
            if self.getTypeField(node) == "seplist":
                self.setActionField(node, "append")
                xmlOldArea.appendChild(node)
            else:
                self.joinField(xmlOldArea, node)

    def insertBeforeSepAreas(self, xmlArea):
        """Добавляет разделитель областей перед каждой областью"""
        if not self.sepAreas:
            return False
        areaNodes = xpath.Evaluate('descendant::area',xmlArea)
        for areaNode in areaNodes:
            prevNode = areaNode.previousSibling
            if prevNode:
                parentNode = areaNode.parentNode
                parentNode.insertBefore(self.sepAreas.cloneNode(True),
                                        areaNode)
        return True

    def getAreaFields(self, nameArea, xmlArea, allVars=False):
        """По имени области выводит названия и значения всех переменных

        поиск ведется только в 1-х потомках области xmlArea
        на выход словарь переменных {имя:значение}
        """
        namesAreaCompareAll = xpath.Evaluate(\
            "child::area/child::caption[child::name='%s']" %(nameArea),
                    xmlArea)
        if not namesAreaCompareAll:
            return False
        dictVar = {}
        for namesAreaCompare in namesAreaCompareAll:
            fields = xpath.Evaluate("child::field/child::name",
                                     namesAreaCompare.parentNode)
            for fieldName in fields:
                nodeField = fieldName.parentNode
                fieldValue = xpath.Evaluate("child::value",nodeField)
                name = fieldName.firstChild.nodeValue
                value = ""
                if fieldValue and fieldValue[0].firstChild:
                    value = fieldValue[0].firstChild.nodeValue
                dictVar[name] = value
            if not allVars:
                break
        return dictVar

    def getAreaFieldValues(self, nameArea, nameField, xmlArea):
        """По имени области и имени переменной выводит значениe переменной

        поиск ведется только в 1-х потомках области xmlArea
        """
        namesAreaComare = xpath.Evaluate(\
            "child::area/child::caption[child::name='%s']" %(nameArea),
                    xmlArea)
        fieldsVal = False
        for areaComp in namesAreaComare:
            fieldsVal = xpath.Evaluate(\
            "child::field[child::name='%s'] "\
            %(nameField), areaComp.parentNode)
            if fieldsVal:
                break
        if not fieldsVal:
            return False
        fieldValue = xpath.Evaluate("child::value",
             fieldsVal[0])
        if not fieldValue:
            return False
        if fieldValue[0].firstChild:
            return fieldValue[0].firstChild.nodeValue
        else:
            return ""

    def getAllAreas(self):
        """Выдает все области"""
        return xpath.Evaluate('descendant::area', self.body)

    def getArea(self, nameArea, xmlArea):
        """По имени области находит области (первый потомок xmlArea)"""
        namesAreaComare = xpath.Evaluate(\
            "child::area/child::caption[child::name='%s']" %(nameArea),
                    xmlArea)
        return map(lambda x: x.parentNode, namesAreaComare)


    def joinArea(self, baseNode, xmlNewArea):
        """Объединяет область c областью Body (xmlNewArea c baseNode)"""
        def appendArea(baseNode, xmlNewArea):
            fieldsRemove = xpath.Evaluate(\
            "descendant::field[child::action='drop']", xmlNewArea)
            for rmNode in fieldsRemove:
                parentNode = rmNode.parentNode
                parentNode.removeChild(rmNode)
            captionAreasRemove = xpath.Evaluate(\
                    "descendant::area/child::caption[child::action='drop']",
                    xmlNewArea)
            for rmNodeCapt in captionAreasRemove:
                rmNode = rmNodeCapt.parentNode
                parentNode = rmNode.parentNode
                parentNode.removeChild(rmNode)
            self.setActionArea(xmlNewArea, "append")
            # Добавляем разделитель областей во вложенные области
            areaNodes = xpath.Evaluate('descendant::area',xmlNewArea)
            for areaNode in areaNodes:
                self.setActionArea(areaNode,"append")
                parentNode = areaNode.parentNode
                parentNode.insertBefore(self.sepAreas.cloneNode(True),
                                        areaNode)
            baseNode.appendChild(xmlNewArea)
            # Добавляем разделитель областей
            baseNode.insertBefore(self.sepAreas.cloneNode(True), xmlNewArea)

        nodesNames = xpath.Evaluate('child::area/caption/name',baseNode)
        nodesNewArea = xpath.Evaluate('child::caption/name',xmlNewArea)
        if not nodesNames:
            # Добавляем область
            if nodesNewArea:
                newAreaAction = self.getActionArea(xmlNewArea)
                if not (newAreaAction == "drop" or newAreaAction == "replace"):
                    appendArea(baseNode, xmlNewArea)
            return True
        if not nodesNames or not nodesNewArea:
            return False
        nameArea = ""
        if nodesNewArea[0].firstChild:
            nameArea = nodesNewArea[0].firstChild.nodeValue.strip()
        flagFindArea = False
        baseNodes = []
        for oName in nodesNames:
            newAreaAction = self.getActionArea(xmlNewArea)
            oArea = oName.parentNode.parentNode
            oNameTxt = ""
            if oName.firstChild:
                oNameTxt = oName.firstChild.nodeValue
            if nameArea == oNameTxt:
                flagFindArea = True
                # При использовании удаления
                if newAreaAction == "drop":
                    prevNode = oName.parentNode.parentNode.previousSibling
                    removePrevNodes = []
                    while (prevNode) and self.getTypeField(prevNode) == "br":
                        removePrevNodes.append(prevNode)
                        prevNode = prevNode.previousSibling
                    for removeNode in removePrevNodes:
                        baseNode.removeChild(removeNode)
                    baseNode.removeChild(oName.parentNode.parentNode)
                    continue
                elif newAreaAction == "replace":
                    oldAreaNode = oName.parentNode.parentNode
                    newAreaCaption = xpath.Evaluate('child::caption',
                                                    xmlNewArea)[0]
                    oldAreaCaption = xpath.Evaluate('child::caption',
                                                    oldAreaNode)[0]
                    if newAreaCaption and oldAreaCaption:
                        xmlNewArea.replaceChild(oldAreaCaption,newAreaCaption)
                        self.setActionArea(xmlNewArea,"replace")
                    baseNode.replaceChild(xmlNewArea,
                                          oldAreaNode)
                    continue
                baseNodes.append(oName.parentNode.parentNode)
                newFields = xpath.Evaluate('child::field',xmlNewArea)

                joinNewFields = xpath.Evaluate(\
                    "child::field[child::action='join']"
                                       ,xmlNewArea)
                self.addNewFielsOldArea(newFields, joinNewFields, oArea)


        if not flagFindArea:
            # Добавляем область
            if not (newAreaAction == "drop" or\
                    newAreaAction == "replace"):
                appendArea(baseNode, xmlNewArea)
        else:
            tmpXmlNewAreas = xpath.Evaluate('child::area',xmlNewArea)
            for na in tmpXmlNewAreas:
                for bn in baseNodes:
                    self.joinArea(bn, na)
        return True

    def joinDoc(self, xmlNewDoc):
        """Объединяет два документа"""
        newRootNode = xmlNewDoc.documentElement
        newBodyNode = xpath.Evaluate('child::body',newRootNode)[0]
        newImportBodyNode = self.doc.importNode(newBodyNode, True)
        # Перед объединение области с документом
        # удаляем комментарии
        self.removeComment(newImportBodyNode)
        self.joinBody(self.body, newImportBodyNode)
        # расставляем BR
        self.insertBRtoBody(self.body)

    def getQuoteField(self, xmlField):
        """Выдает текст из поля"""
        xmlQuotes = xpath.Evaluate('child::quote',xmlField)
        br = ""
        if xmlField.hasAttribute("type") and\
            xmlField.getAttribute("type") == "br":
            br = "\n"
        if xmlQuotes:
            field = xmlQuotes[0]
            if field.firstChild:
                return field.firstChild.nodeValue + br
        return "" + br

    def getFieldsArea(self, xmlArea):
        """Выдает потомков XML области"""
        xmlFields = []
        childNodes = xmlArea.childNodes
        for node in childNodes:
            if node.nodeType == node.ELEMENT_NODE:
                if node.tagName == "area" or node.tagName == "field":
                    xmlFields.append(node)
        return xmlFields

    def getTypeField(self, xmlField):
        """Выдает тип поля"""
        if xmlField.hasAttribute("type"):
            return xmlField.getAttribute("type")
        else:
            return False

    def getNameField(self, xmlField):
        """Выдает имя поля"""
        xmlNameFields = xpath.Evaluate('child::name', xmlField)
        if xmlNameFields and xmlNameFields[0].firstChild:
            return xmlNameFields[0].firstChild.nodeValue
        else:
            return False

    def getNameArea(self, xmlArea):
        """Выдает имя области"""
        xmlNameAreas = xpath.Evaluate('child::caption/name', xmlArea)
        if xmlNameAreas and xmlNameAreas[0].firstChild:
            return xmlNameAreas[0].firstChild.nodeValue
        else:
            return False

    def xmlToText(self, xmlAreas, text):
        """Преобразует список XML областей в текст"""
        def getQuotesArea(xmlArea):
            quotes = []
            xmlQuotes = xpath.Evaluate('child::caption/quote',xmlArea)
            for node in xmlQuotes:
                if node.firstChild:
                    quotes.append(node.firstChild.nodeValue)
            if len(quotes) == 0:
                quotes.append("")
                quotes.append("")
            elif len(quotes) == 1:
                quotes.append("")
            return quotes

        for i in xmlAreas:
            if i.tagName == "area":
                quotesI = getQuotesArea(i)
                startAreaI = quotesI[0]
                endAreaI = quotesI[1]
                text.append(startAreaI)
                xmlFieldsI = self.getFieldsArea(i)
                for f in xmlFieldsI:
                    if f.tagName == "area":
                        quotesF = getQuotesArea(f)
                        startAreaF = quotesF[0]
                        endAreaF = quotesF[1]
                        text.append(startAreaF)
                        xmlFieldsF = self.getFieldsArea(f)
                        self.xmlToText(xmlFieldsF, text)
                        text.append(endAreaF)
                    else:
                        fieldF = self.getQuoteField(f)
                        text.append(fieldF)
                text.append(endAreaI)
            else:
                fieldI = self.getQuoteField(i)
                text.append(fieldI)

    def getActionField(self, xmlField):
        """Выдает свойство action XML поля"""
        xmlActions = xpath.Evaluate('child::action',xmlField)
        if xmlActions and xmlActions[0].firstChild:
            return xmlActions[0].firstChild.nodeValue
        else:
            return False

    def getFieldValues(self, xmlField):
        """Выдает значения XML поля в виде массива"""
        vals = []
        xmlValues = xpath.Evaluate('child::value',xmlField)
        if xmlValues:
            for node in xmlValues:
                if node.firstChild:
                    vals.append(node.firstChild.nodeValue)
        return vals

    def getActionArea(self, xmlArea):
        """Выдает свойство action XML области"""
        xmlActions = xpath.Evaluate('child::caption/action',xmlArea)
        if xmlActions and xmlActions[0].firstChild:
            return xmlActions[0].firstChild.nodeValue
        else:
            return False

    def delActionNodeArea(self, xmlArea):
        """Удаляет свойство action XML области"""
        xmlActions = xpath.Evaluate('child::caption/action',xmlArea)
        if xmlActions and xmlActions[0].firstChild:
            parentNode = xmlActions[0].parentNode
            parentNode.removeChild(xmlActions[0])
            return True
        else:
            return False

    def delActionNodeField(self, xmlField):
        """Удаляет свойство action XML поля"""
        xmlActions = xpath.Evaluate('child::action',xmlField)
        if xmlActions and xmlActions[0].firstChild:
            parentNode = xmlActions[0].parentNode
            parentNode.removeChild(xmlActions[0])
            return True
        else:
            return False

    # Создает распределенные списки
    def postParserListSeplist(self, xmlArea):
        """Создает распределенные списки из элементов области"""
        # Потомки
        childNodes = self.getFieldsArea(xmlArea)
        # содержит списки нод полей с одинаковыми именами в одной области
        fieldsSeplist = {}
        for node in childNodes:
            if node.tagName == "area":
                self.postParserListSeplist(node)
            else:
                fieldName = False
                xmlFieldNameNodes = xpath.Evaluate('child::name',node)
                if xmlFieldNameNodes and xmlFieldNameNodes[0].firstChild:
                    fieldName = xmlFieldNameNodes[0].firstChild.nodeValue
                if fieldName:
                    if  fieldsSeplist.has_key(fieldName):
                        fieldsSeplist[fieldName].append(node)
                    else:
                        fieldsSeplist[fieldName] = []
                        fieldsSeplist[fieldName].append(node)
        for listNodes in fieldsSeplist.values():
            if len(listNodes) > 1:
                for node in listNodes:
                    node.setAttribute("type", "seplist")

    def insertBRtoBody(self, xmlArea):
        """Добавляет необходимые переводы строк
        """
        # Потомки
        childNodes = self.getFieldsArea(xmlArea)
        # нода BR
        fieldXMLBr = self.createField("br",[],"",[],False, False)
        # разделитель поля
        fieldSplit = False
        # Предыдущая нода
        lastNode = False
        # Cледующая нода
        nextNode = False
        lenChildNodes = len(childNodes)
        for i in range(lenChildNodes):
            node = childNodes[i]
            lastTmpNode = node
            # Нода area
            if node.tagName == "area":
                if self.getActionArea(node) == "append" or\
                    self.getActionArea(node) == "join":
                    self.delActionNodeArea(node)
                    if lastNode and lastNode.hasAttribute("type") and\
                        lastNode.getAttribute("type") == "br" or\
                        lastNode and lastNode.hasAttribute("type") and\
                        lastNode.getAttribute("type") == "comment":
                        indNext = i + 1
                        if indNext == lenChildNodes:
                            xmlArea.appendChild(fieldXMLBr.cloneNode(True))
                        else:
                            nextNode = childNodes[indNext]
                            lastTmpNode = xmlArea.insertBefore(\
                                                    fieldXMLBr.cloneNode(True),
                                                    nextNode)
                    else:
                        xmlArea.insertBefore(fieldXMLBr.cloneNode(True),
                                             node)
                self.insertBRtoBody(node)
            # Нода field
            else:
                if self.getActionField(node) == "append" or\
                self.getActionField(node) == "join":
                    self.delActionNodeField(node)
                    if lastNode and lastNode.hasAttribute("type") and\
                        lastNode.getAttribute("type") == "br" or\
                        lastNode and lastNode.hasAttribute("type") and\
                        lastNode.getAttribute("type") == "comment":
                        indNext = i + 1
                        if indNext == lenChildNodes:
                            xmlArea.appendChild(fieldXMLBr.cloneNode(True))
                        else:
                            nextNode = childNodes[indNext]
                            lastTmpNode = xmlArea.insertBefore(\
                                                    fieldXMLBr.cloneNode(True),
                                                    nextNode)
                    else:
                        xmlArea.insertBefore(fieldXMLBr.cloneNode(True),
                                             node)
            lastNode = lastTmpNode



    def postParserList(self):
        """Находит подходящие XML области и делаем из них поля-массивы"""
        xmlAreas = xpath.Evaluate('descendant::area', self.body)
        for xmlArea in xmlAreas:
            flagListXml = True
            fieldValues = []
            xmlFields = xpath.Evaluate('child::field',xmlArea)
            if not xmlFields:
                flagListXml = False
            lenXmlFields = len(xmlFields)
            lenBrArea = 0
            for xmlField in xmlFields:
                xmlNames = xpath.Evaluate('child::name',xmlField)
                xmlVals = xpath.Evaluate('child::value',xmlField)
                if xmlField.hasAttribute("type") and\
                    xmlField.getAttribute("type") == "br":
                    lenBrArea += 1
                    continue
                if not xmlNames and not xmlVals:
                    flagListXml = False
                    break
                if xmlNames and xmlNames[0].firstChild and\
                    xmlNames[0].firstChild.nodeValue:
                    flagListXml = False
                    break
                if not (xmlVals and xmlVals[0].firstChild and\
                xmlVals[0].firstChild.nodeValue):
                    flagListXml = False
                    break
                else:
                   fieldValues.append(xmlVals[0].firstChild.nodeValue)

            if lenXmlFields == lenBrArea:
                flagListXml = False
            if flagListXml:
                nameNode = xpath.Evaluate('child::caption/name',xmlArea)[0]
                fieldName = ""
                if nameNode.firstChild:
                    fieldName = nameNode.firstChild.nodeValue
                listArea = []
                self.xmlToText([xmlArea],listArea)
                fieldQuote = "".join(listArea)
                fieldXMLBr = False
                if fieldQuote and fieldQuote[-1] == "\n":
                    fieldQuote = fieldQuote[:-1]
                    fieldXMLBr = self.createField("br",[],"",[],False, False)
                fieldXML = self.createField("list",
                                        [fieldQuote],
                                        fieldName, fieldValues,
                                        False, False)
                areaAction = self.getActionArea(xmlArea)
                if areaAction:
                    self.setActionField(fieldXML, areaAction)
                parentNode = xmlArea.parentNode
                parentNode.insertBefore(fieldXML,xmlArea)
                if fieldXMLBr:
                    parentNode.insertBefore(fieldXMLBr,xmlArea)
                parentNode.removeChild(xmlArea)


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

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

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

            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 flagCommentLine:
                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)
            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):
    """Класс для работы с файлами

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

    def saveConfFile(self):
        """Записать конфигурацию"""
        if not self.textConfig:
            self.textConfig = self.textTemplate
        if self.F_CONF:
            self.F_CONF.truncate(0)
            self.F_CONF.seek(0)
            try:
                self.F_CONF.write(self.textConfig)
            except:
                self.setError(_("unable to open the file:")
                            + self.nameFileConfig)
                return False
            self.F_CONF.flush()
            return True
        elif self.executeType == "exec":
            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):
        """Открыть файл шаблона"""
        F_TEMPL = False
        try:
            F_TEMPL = open(nameFileTemplate, "r")
        except:
            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 = False

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

    def __openConfFile(self, nameFileConfig):
        """Отктрыть конфигурационный файл"""
        F_CONF = False
        try:
            if os.path.islink(nameFileConfig):
                # если ссылка то удаляем её
                os.unlink(nameFileConfig)
            F_CONF = open (nameFileConfig, "r+")
        except:
            try:
                if os.path.isdir(nameFileConfig):
                    self.printWARNING(_("unable to open the directory as file:")
                            + nameFileConfig)
                    return False
                F_CONF = open(nameFileConfig, "w+")
            except:
                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 = False
        self.F_CONF = False
        self.nameFileConfig = os.path.abspath(nameFileConfig)
        self.nameFileTemplate = os.path.abspath(nameFileTemplate)
        self.F_TEMPL = self.openTemplFile(self.nameFileTemplate)
        if not self.executeType and typeFormat != "diff":
            self.F_CONF = self.__openConfFile(self.nameFileConfig)
        if self.F_TEMPL and self.F_CONF:
            self.textTemplate = self.F_TEMPL.read()
            if self.configMode == T_NEWCFG:
                origConfigName = re.sub(r'/._cfg\d{4}_([^/]+)$','/\\1',
                            self.nameFileConfig)
                if newBuffer is None:
                    self.textConfig = readFile(origConfigName)
                else:
                    self.textConfig = newBuffer
            else:
                self.textConfig = self.F_CONF.read()

    def __del__(self):
        self.closeFiles()

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


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

    преобразование бинарного или смеси бинарного и utf-8 кода в utf-8 и
    обратное преобразование 
    методы класса encode и decode
    """
    def _retUTF(self, char):
        byte = ord(char)
        if byte<=127:
            return ('_ch_',1)
        elif byte<=191:
            return ('_nb_',1)
        elif byte<=223:
            return ('_fb_',2)
        elif byte<=239:
            return ('_fb_',3)
        elif byte<=247:
            return ('_fb_',4)
        else:
            return ('_er_',1)

    def _sumbUtf(self, symbols, lenTail):
        if not symbols:
            return (False,0)
        lenSymb = len(symbols)
        if lenSymb >= 4:
            l = 4
        elif lenSymb >= 3:
            l = 3
        elif lenSymb >= 2:
            l = 2
        else:
            if symbols[0] == '_ch_':
                return (True,1)
            else:
                return (False,1)
        result = False
        for i in range(l):
            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:]
        exec("ret = '\\x%s'" %he)
        return ret

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

    def encode(self, text):
        """Кодирует смешанный формат в UTF-8"""
        ind = 0
        l = 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 = []
                        block.append(indErr[i])
                    i +=1
                    if i >= lenIndErr:
                        break
            else:
                block.append(indErr[0])
            if block:
                blocks.append(block)
            listErr = []
            for block in blocks:
                string = ""
                for elem in block:
                    string += hex(ord(text[elem]))[-2:]
                listErr.append((block[0],"__hex__?%s?__hex__" %string,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(object):
    """Методы получения классов и объектов форматов шаблонов"""
    # Импортированные классы поддерживаемых форматов шаблонов
    importFormats = {}

    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 getFormatObj(self, formatTemplate, textTemplate):
        """Создание объекта формата шаблона.

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

class _shareTemplate:
    """Общие аттрибуты для классов шаблонов"""
    # Метка начала переменной
    varStart = "#-"
    # Метка конца переменной
    varEnd = "-#"
    _deltVarStart = len(varStart)
    _deltVarEnd = len(varEnd)

    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:
            raise TemplatesError(_("User %s not found")%str(userName))
        if groupsInfo:
            import grp
            try:
                groupName = grp.getgrgid(gid).gr_name
            except:
                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 templateFunction(_error, _warning, _shareTemplate, _shareTermsFunction):
    """Класс для функций шаблонов"""
    # Словарь установленных программ {"имя программы":[версии]}
    installProg = {}

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

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

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

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

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

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

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

    # кэш для проверки наличия пакета в портежах
    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)

    currentBelong = ""
    alreadyInformed = []

    def __init__(self, objVar):
        # Если не определен словарь функций шаблона
        # import services api
        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)
        # Аттрибуты для функции шаблона 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")
        if self._chrootDir != '/':
            # Изменение директории к базе пакетов
            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.modeConfigIni = 0640
        else:
            self.pathConfigIni = pathJoin(self._chrootDir,'/etc/calculate')
            self.modeConfigIni = 0644
        self.fileConfigIni = os.path.join(self.pathConfigIni,"ini.env")

    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)
                try:
                    num = int(strNum)
                except:
                    minus = False
                    if strNum[:1] == "-":
                        minus = True
                        strNum = strNum[1:]
                    if localVars.has_key(strNum):
                        try:
                            num = int(localVars[strNum])
                        except:
                            self.raiseErrTemplate(
                                _("error: variable %s is not integer")%
                                    str(strNum))
                    elif self.objVar.exists(strNum):
                        try:
                            num = int(self.objVar.Get(strNum))
                        except:
                            self.raiseErrTemplate(
                                _("error: variable %s is not integer")%
                                    str(strNum))
                    else:
                        self.raiseErrTemplate(
                                _("error: local variable %s not defined")%
                                    str(strNum))
                    if minus:
                        num = -num
                strNumers.append(num)
            return sum(strNumers)
        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:
                flagVarTxt = False
            if flagVarTxt:
                continue
            varReplace = str(self.equalTerm(var, localVars))
            termTmp = termTmp.replace(var,varReplace)
        ret = eval(termTmp)
        return ret

    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:
            self.raiseErrTemplate()
        return textTemplateTmp

    def funcExists(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """Функция шаблона exists(),
        проверяет существование файла, если существует выдает '1'
        если второй параметр root, то проверка осуществляется от корня.
        """
        terms = map(lambda x: x.strip(), funArgv.split(","))
        if len(terms) > 2:
            self.raiseErrTemplate()
        fileName = terms[0]
        flagNotRootFS = True
        if len(terms) == 2:
            if terms[1] == "root":
                flagNotRootFS = False
            else:
                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] != "/":
            self.raiseErrTemplate(_("wrong path '%s'")%fileName)
        else:
            if flagNotRootFS:
                fileName = pathJoin(self._baseDir, fileName)
        replace = ""
        if os.path.exists(fileName):
            replace = "1"
        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:
                self.raiseErrTemplate()
        else:
            self.raiseErrTemplate()
        flagNotRootFS = True
        if lenTerms == 3:
            if terms[2] =="root":
                flagNotRootFS = False
            else:
                self.raiseErrTemplate(
                    _("The third argument of the function is not 'root'"))
        if lenTerms >= 2:
            if not terms[0] in ["ver","num","char","key","empty"]:
                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] != "/":
            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 = "SLOT".__eq__
        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()
                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:
            fileName = os.path.split(self.nameTemplate)[1]
            if fileName == '.calculate_directory':
                parentDir = os.path.dirname(self.nameTemplate)
                parentDir, pkgName = os.path.split(parentDir)
            else:
                parentDir, pkgName = os.path.split(self.nameTemplate)
            category = os.path.split(parentDir)[1]
            # reg for discard version and sort number
            pkgName = self.reData.search(pkgName).group(1)
            category = self.reData.search(category).group(1)
            nameProg = "%s/%s"%(category,pkgName)
        # Замена функции в тексте шаблона
        replace = ""
        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)
                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 funcRnd(self, funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """Функция шаблона rnd(), выдает строку случайных символов

        первый аргумент:
            'num' - числа,
            'pas' - цифры и буквы
            'uuid' - цифры и строчные буквы a-f
        второй аргумент:
            количество символов
        """
        terms = funArgv.replace(" ","").split(",")
        if  not terms[0].strip() or\
            (len(terms)==2 and not terms[1].strip()) or\
            len(terms)!=2:
            self.raiseErrTemplate()
        fArgvNames = {'num':string.digits,
                      'pas':string.ascii_letters + string.digits,
                      'uuid':string.ascii_lowercase[:6] + string.digits}
        if not terms[0] in fArgvNames:
            self.raiseErrTemplate(
                _("the first argument of the function must "
                  "be 'num', 'pas' or 'uuid'"))
        try:
            lenStr = int(terms[1])
        except:
            self.raiseErrTemplate(
                _("the second argument of the function is not a number"))
        choiceStr = fArgvNames[terms[0]]
        replace = ''.join([random.choice(choiceStr) for i in xrange(lenStr)])
        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:
            self.raiseErrTemplate()
        fArgvNames = ['upper','lower','capitalize']
        if not terms[0] in fArgvNames:
            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:
            raise TemplatesError(
                _("error in template %s")%self.nameTemplate + "\n" +
                _("error: variable %s not found")%str(terms[1]))
        replace = ""
        strValue = _toUNICODE(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]
        flagFoundVar = False
        try:
            value = self.objVar.Get(nameLocVar)
            flagFoundVar = True
        except:
            pass
        if flagFoundVar:
            if type(value) in (list, tuple):
                terms = terms[1:]
                if any(x in terms for x in value):
                    replace = "1"
                else:
                    replace = ""
            else:
                if value in terms[1:]:
                    replace = "1"
                else:
                    replace = ""
        else:
            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]
        flagFoundVar = False
        if nameLocVar in localVars.keys():
                flagFoundVar = True
                value = localVars[nameLocVar]
        else:
            try:
                value =  self.objVar.Get(nameLocVar)
                flagFoundVar = True
            except:
                pass
        if flagFoundVar:
            # Если переменная существует
            if len(terms) == 1:
                self.stackGlobalVars.append(str(value))
            else:
                self.raiseErrTemplate(_("error: variable %s exists")\
                                         %str(nameLocVar))
        else:
            # Если переменная не существует
            if len(terms) == 1:
                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:
                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:
                self.raiseErrTemplate(
                    _("error: global variables stack empty"))
        else:
            self.raiseErrTemplate()
        replace = ""
        textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\
                textTemplateTmp[resS.end():]
        return textTemplateTmp

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

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

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

    def funcExit(self,funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """
        Good exit
        """
        # TODO: need remove
        return textTemplateTmp
        self.printSUCCESS(_(funArgv))
        raise TemplatesInterrupt(_("Execution of templates was stopped"),
                                 TemplatesInterrupt.EXIT)

    def funcBreak(self,funArgv, resS, localVars, textTemplateTmp, nameTemp):
        """
        Bad exit
        """
        # TODO: need remove
        return textTemplateTmp
        self.printERROR(_(funArgv))
        raise TemplatesInterrupt(_("Execution of templates was "
                                   "interrupted with an error"),
                                 TemplatesInterrupt.ABORT)

    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 ""

    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()
            skip = True
            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

    def funcConfirm(self,funArgv, resS, localVars, textTemplateTmp, nameTemp):
        # TODO: need remove
        return textTemplateTmp
        res = self.askConfirm(_(funArgv))
        textTemplateTmp = textTemplateTmp[:resS.start()] + res + \
                textTemplateTmp[resS.end():]
        return textTemplateTmp

    def loadVarsIni(self, iniFileName):
        """ Читает файл fileName
        создает и заполняет переменные на основе этого файла
        Используеться совместно c funcIni
        """
        localVarsIni = {}
        # получить объект ini файла
        config = iniParser(iniFileName)
        # получаем все секции из конфигурационного файла
        allsect = config.getAllSectionNames()
        if not allsect:
            if self.getError():
                # Очистка ошибки
                _error.error = []
            return localVarsIni
        # Заполняем переменные для funcIni
        for sect in allsect:
            sectVars = config.getAreaVars(sect)
            for name in sectVars.keys():
                nameVar = "%s.%s"%(sect,name) 
                valueVar = sectVars[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(",")
        if len(terms) != 2:
            self.raiseErrTemplate(_("funtion takes only two parameters"))
        resol, wpath = terms

        re_resol = re.compile("^(\d+)x(\d+)$")
        resol = re_resol.match(resol)
        if not resol:
            self.raiseErrTemplate(
                _("the first parameter must be the resolution"))

        re_resol = re.compile(".*?(\d+)x(\d+).*")
        wpath = pathJoin(self._baseDir, wpath)
        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]
        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)
        # Название локальной переменной
        nameLocVar = terms[0]
        namesVar = nameLocVar.split(".")
        if len(namesVar) == 1:
            nameLocVar = "main.%s"%nameLocVar
        elif len(namesVar)>2:
            self.raiseErrTemplate()
        replace = ""
        # Получаем время модификации конфигурационного файла
        curTime = self.getTimeFile(self.fileConfigIni)
        if len(terms) == 1:
            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:
                    replace = ""
                else:
                    replace = self.currDictIni[nameLocVar].encode("UTF-8")
        elif len(terms) == 2:
            if self.timeIni != curTime:
                # читаем переменные из файла
                self.prevDictIni = self.loadVarsIni(self.fileConfigIni)
                self.currDictIni= {}
                self.currDictIni.update(self.prevDictIni)
                self.timeIni = self.getTimeFile(self.fileConfigIni)
            # Значение локальной переменной
            valueLocVar = terms[1]
            self.currDictIni[nameLocVar] = valueLocVar
        elif len(terms) == 3:
            if not terms[2] in ['url','purl','unicode']:
                self.raiseErrTemplate(
                    _("the third argument of the function is neither "
                      "'url' or 'purl' or 'unicode'"))
            if terms[1]:
                self.raiseErrTemplate()
            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():
                unicodeValue = self.currDictIni[nameLocVar]
                if unicodeValue is None:
                    unicodeValue = ""
                if terms[2] in ('url', 'purl'):
                    replace = unicodeValue.encode("UTF-8").\
                              __repr__()[1:-1].replace('\\x','%').\
                              replace(' ','%20')
                    if terms[2] == 'purl':
                        replace = replace.replace('/','%2f')
                elif terms[2] == 'unicode':
                    replace = unicodeValue.__repr__()[2:-1]
        else:
            self.raiseErrTemplate()
        textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\
                textTemplateTmp[resS.end():]
        return (textTemplateTmp)

    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 = re.sub(r'\\x([0-9a-fA-F]{2})',
                        lambda x:chr(int(x.group(1),16)),textArgv)
                    listArgv.append(textArgv)
                elif term.startswith("'") and term.endswith("'"):
                    listArgv.append(term[1:-1])
                else:
                    self.raiseErrTemplate()
            return listArgv

        terms = map(lambda x: x.strip(), funArgv.split(","))
        if len(terms) != 3:
            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:
                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:
            self.raiseErrTemplate()
        nameVar = terms[0]
        replace = ''
        if nameVar in self.valuesVarEnv:
            replace = self.valuesVarEnv[nameVar]
        else:
            # Получаем значение из env файлов
            value = self.objVar.getIniVar(nameVar)
            if value is False:
                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:
            self.raiseErrTemplate()
        nameLocalVar = ""
        if len(terms)==2:
            if not terms[1]:
                self.raiseErrTemplate()
            nameLocalVar = terms[1]
        textLine = terms[0]
        vals = textLine.split(".")
        if len(vals)!= 2:
            self.raiseErrTemplate()
        if filter(lambda x: not x.strip(), vals):
            self.raiseErrTemplate()
        service, option = vals
        if not service or not option:
            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:
                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 checkCorrectPkgName(self,package):
        """
        Check pkg name (in /var/db/pkg,/usr/portage,/var/lib/layman/calculate
        and other overlays)
        """
        category,spl,pkgname = package.partition('/')
        pkgname, spl, slot = pkgname.partition(":")
        # check installed package in system
        if not category in self.installCategory:
            self.getInstallPkgGentoo(category=category)
            self.installCategory.append(category)
        if self.pkg("%s/%s"%(category,pkgname), slot=slot or None):
            return True
        # check package in portage and calculate,overlay
        def queuePortdir():
            yield "/usr/portage"
            calculate = "/var/lib/layman/calculate"
            yield calculate
            for portdir in filter(lambda x:x!=calculate,
                           self.objVar.Get('cl_portdir_overlay')):
                yield portdir

        for portdir in queuePortdir():
            if not portdir in self.cachePortdir:
                lPortdir = len(portdir)+1
                self.cachePortdir[portdir] = list(set(
                    map(lambda x:x[lPortdir:].rpartition('/')[0],
                    glob.glob("%s/*/*/*.ebuild"%portdir))))
            if package in self.cachePortdir[portdir]:
                return True
            else:
                if portdir == "/usr/portage" and \
                    len(self.cachePortdir[portdir]) < 30:
                    #raise TemplatesError(_("Ebuild files are not found in portage"))
                    return True
        if not "portage/update" in self.cachePortdir:
            updateFiles = sorted(listDirectory('/usr/portage/profiles/updates'),
                          key=lambda x:tuple(reversed(x.split('Q-'))),reverse=True)
            if updateFiles:
                self.cachePortdir["portage/update"] = \
                    map(lambda x:x.split(' ')[1],
                    filter(lambda x:x.startswith('move '),
                    readLinesFile(os.path.join('/usr/portage/profiles/updates',
                                            updateFiles[0]))))
        if "portage/update" in self.cachePortdir and \
            package in self.cachePortdir["portage/update"]:
            self.printWARNING(_("Package {frompkg} was renamed").format(frompkg=package))
            return True
        return False

    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):
            skip_data_varname = 'cl_setup_skip_%s'%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
        terms = funArgv.replace(" ","").split(",")
        if len(terms) != 1:
            self.raiseErrTemplate()
        funcPkg = terms[0]
        if not funcPkg:
            fileName = os.path.split(self.nameTemplate)[1]
            if fileName == '.calculate_directory':
                parentDir = os.path.dirname(self.nameTemplate)
                parentDir, pkgName = os.path.split(parentDir)
            else:
                parentDir, pkgName = os.path.split(self.nameTemplate)
            category = os.path.split(parentDir)[1]
            # reg for discard version and sort number
            pkgName = self.reData.search(pkgName).group(1)
            category = self.reData.search(category).group(1)
            funcPkg = "%s/%s"%(category,pkgName)
        try:
            if not self.checkCorrectPkgName(funcPkg):
                uniq_warning(_("Package {pkgName} specified in the merge() into "
                    "{templName} template is not found").format(
                   pkgName=funcPkg,templName=self.nameTemplate))
                return textTemplateTmp[:resS.start()] + \
                        textTemplateTmp[resS.end():]
        except TemplatesError as e:
            self.printWARNING(str(e))
        self.currentBelong = funcPkg
        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')):
                replace = self.objVar.Get('core.cl_core_pkg_version')
                if check_skip(funcPkg):
                    replace = ""
            else:
                replace = ""
        else:
            pkg = self.objVar.Get("cl_merge_pkg")
            replace = ""
            if pkg:
                if funcPkg in pkg:
                    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:
            self.raiseErrTemplate()
        # Название локальной или глобальной переменной
        nameLocVar = terms[0]
        strIndex = terms[1]
        try:
            intIndex = int(strIndex)
        except:
            self.raiseErrTemplate(_("'%s' is not a number")%strIndex)
        flagFoundVar = False
        if nameLocVar in localVars.keys():
                flagFoundVar = True
                value = localVars[nameLocVar]
        else:
            try:
                value =  self.objVar.Get(nameLocVar)
                flagFoundVar = True
            except:
                pass
        if not flagFoundVar:
            # Если переменная не существует
            self.raiseErrTemplate(_("error: variable %s does not exist")\
                                         %str(nameLocVar))
        if not type(value) in (list,tuple):
            # Значение переменной не список или кортеж
            self.raiseErrTemplate(_("value of %s is not a list or a tuple")\
                                         %str(nameLocVar))
        try:
            if len(value) > intIndex:
                replace = str(value[intIndex])
            else:
                replace = ""
        except:
            self.raiseErrTemplate(_("wrong %s")%strIndex)
        textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\
                textTemplateTmp[resS.end():]
        return textTemplateTmp

    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:
            self.raiseErrTemplate()
        # Название глобальной переменной
        mountPoint = terms[0]
        lastElementVar = terms[1]
        if not mountPoint or mountPoint[:1] !="/":
            self.raiseErrTemplate(_("wrong %s")%lastElementVar)
        nameVar = "install.os_install_disk_mount"
        try:
            valueVar =  self.objVar.Get(nameVar)
        except:
            # Если переменная не существует
            self.raiseErrTemplate(
                _("error: variable %s does not exist")%nameVar)
        nameElementVar = "install.os_install_disk_%s"%lastElementVar
        try:
            valueElementVar =  self.objVar.Get(nameElementVar)
        except:
            # Если переменная не существует
            nameElementVar = "install.os_disk_%s"%lastElementVar
            try:
                valueElementVar =  self.objVar.Get(nameElementVar)
            except:
                self.raiseErrTemplate(_("wrong %s")%lastElementVar+"\n"+
                                      _("error: variable %s does not exist")\
                                             %nameElementVar)
        if not type(valueVar) in (list,tuple):
            # Значение переменной не список или кортеж
            self.raiseErrTemplate(_("value of %s is not a list or a tuple")\
                                         %nameVar)
        if not type(valueElementVar) in (list,tuple):
            # Значение переменной не список или кортеж
            self.raiseErrTemplate(_("value of %s is not a list or a tuple")\
                                         %nameElementVar)
        if len(valueVar) != len(valueElementVar):
            self.raiseErrTemplate(
                _("%(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:
            self.raiseErrTemplate(_("mount point '/' or '/%s' not found "
                 " in the value of variable os_disk_install")
                 %mountPoint)
        replace = valueElementVar[index]
        textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\
                textTemplateTmp[resS.end():]
        return textTemplateTmp

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

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

        возможные пути:
            имя_пакета.var.имя_переменной - получаем значение переменной
            имя_пакета.имя_метода_api - выполнение метода, получение результата
            all.имя_метода_api - выполнение метода для всех пакетов с api
        """
        return textTemplateTmp

    def raiseErrTemplate(self,message=""):
        """Возвращает ошибки при обработке функций шаблона"""
        if message:
            message = "%s\n"%message
        else:
            message = ""
        raise 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
        resS = reFunc.search(textTemplate)
        textTemplateTmp = textTemplate
        flagIniFunc = False
        writeIniFunc = False
        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]
                # вызов функции шаблона
                textTemplateTmp = self.templateFunction[funcName](self, funArgv,
                                                                resS, localVars,
                                                                textTemplateTmp,
                                                                nameTemplate)
                resS = reFunc.search(textTemplateTmp)
                if funcName == "ini":
                    if "," in funArgv:
                        writeIniFunc = True
                    flagIniFunc = True
            else:
                self.raiseErrTemplate(
                    _("function of templates '%s' not found")\
                    %str(self.functText))
        if flagIniFunc:
            # Очистка файла в случае его ошибочного чтения
            if not self.prevDictIni and os.path.exists(self.fileConfigIni):
                FD = open(self.fileConfigIni, "r+")
                FD.truncate(0)
                FD.seek(0)
                FD.close()
            # Если конф. файл модифицирован шаблоном
            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 = iniParser(self.fileConfigIni)
                # set specified mode for ini file
                config.setMode(self.modeConfigIni)
                # секции будущего конфигурационного файла
                sects = list(set(map(lambda x: x.split(".")[0],\
                                     self.currDictIni.keys())))
                # запись переменных в файл
                for sect in sects:
                    dictVar = {}
                    for varName in self.currDictIni.keys():
                        if varName.startswith("%s."%sect):
                            nameVar = varName.rpartition(".")[2]
                            valueVar = self.currDictIni[varName]
                            if valueVar is None:
                                config.delVar(sect,nameVar)
                            else:
                                dictVar[nameVar] = valueVar
                    if dictVar:
                        # Запись переменных в секцию
                        config.setVar(sect, dictVar)
                # читаем переменные из файла
                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 not self.uid in (uid,PORTAGEUID) or \
                    not self.gid in (gid,PORTAGEGID):
                    try:
                        os.chown(self.fileConfigIni, self.uid, self.gid)
                    except OSError as e:
                        self.setError(_("error") + " " +\
                                      "'chown %d:%d %s'"%(self.uid, self.gid,
                                                          self.fileConfigIni))
        return textTemplateTmp

class ChangedFiles:
    """
    Object which contains modified files and package
    """
    FILE_MODIFIED,FILE_REMOVED,DIR_CREATED,DIR_REMOVED,DIR_EXISTS = 0,1,2,3,4
    
    def __init__(self):
        self.data = {}
        self.pkgs = set()

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

    def addObj(self,filename,action,pkg):
        self._createEntry(filename)
        self.data[filename].append((pkg,action))
        self.pkgs.add(pkg)

    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,templateFormat,_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):
        comment_symb = '#"'
        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

    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):
        # совместимость с вызовами из модулей предыдущих версий
        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.printERROR = printERROR
        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.stop = 0
        self.cltObj = None
        self.functObj = 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._reVar = re.compile(("%s(?:[a-z0-9_]+\.)?[a-zA-Z0-9_-]+%s")%(self.varStart,
                                                         self.varEnd),re.M)
        # Условия
        self._reTermBloc = re.compile(
"#\?(?P<rTerm>(?:[a-z0-9_]+\.)?[a-zA-Z0-9\-_]+)\
(?P<func>\((%s+|)\))?\
(?P<lTerm>[\>\<\=\!\&\|]+\
[\>\<\=\!\|\&\(\)%s]*)#\
\n*(?P<body>.+?)\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 = self.objVar.Get("cl_name").capitalize()
        # версия текущей программы
        _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)
        elif cltObj:
            # Объект templateClt
            self.cltObj = cltObj
        else:
            # Объект templateClt
            self.cltObj = False
        # Фильтровать ли шаблоны clt по конфигурационным файлам обычных шаблонов
        self.cltFilter = cltFilter
        # autoupdate файлы
        self.autoUpdateFiles = []
        self.autoUpdateDirs = []

        self.protectedFiles = 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):
        """
        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)
            else:
                self.changedFiles.addObj(fn,ChangedFiles.FILE_REMOVED,
                                         pkg or self.functObj.currentBelong)
    
    def executeTemplate(self, code, execPath):
        """Execute template"""
        p = process(execPath,lang=self.objVar.Get('os_locale_locale'),
                    envdict=dict(os.environ))
        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)
        typeTemplate = "bin"
        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)
                    os.chown(nameDir, dUid, dGid)
                except:
                    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)
                os.chown(dirName, dUid, dGid)
                createDirs.append(dirName)
            except:
                self.setError(_("Failed to create the directory: ")
                   + dirName)
                return False  
            return createDirs

    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]
            varValue = ""
            try:
                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 applyTermsTemplate(self,textTemplate,nameTemplate):
        """ Применяет условия, к условным блокам текста
        """
        def function(text):
            """Функция обработки функций в заголовке"""
            return self.applyFuncTemplate(text, nameTemplate)

        textTerm = ""
        resS = self._reTermBloc.search(textTemplate)
        textTemplateTmp = textTemplate
        while resS:
            mark = resS.group(0)
            body = resS.group("body")
            end = resS.group("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+end)
            else:
                textTemplateTmp = textTemplateTmp.replace(mark, "")
            resS = self._reTermBloc.search(textTemplateTmp)
        return textTemplateTmp

    def getNeedTemplate(self, fileTemplate):
        """Применяем правила к названию файла"""
        dirP,fileP = os.path.split(fileTemplate)
        if fileP:
            spFile = fileP.split("?")
            realFileName = spFile[0]
            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=""):
        """Выдает заголовок шаблона ( версия и.т.д)"""
        if configPath and self.protectPaths:
            flagFoundPath = False
            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 + "/"):
                    flagFoundPath = True
                    break
            if flagFoundPath:
                origConfigPath = PkgContents.reCfg.sub("/",configPath)
                commentList = commentList +\
                              [self.titleEnd%{'conf_path':origConfigPath}]
        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)
        i = 0
        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:
                reL = False
                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():]
        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:
                        textTemplate = open(templatePath).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
            from cl_print import color_print
            self.printWARNING(_("No conditions for checking the value of "
                                "an action variable"))
            skipDirTemplates = []
            for skipDir in skipDirs:
                skipTempl = os.path.join(skipDir,self.templDirNameFile)
                if os.path.isfile(skipTempl):
                    skipDirTemplates.append(skipTempl)
            if skipTemplates or skipDirTemplates:
                self.printWARNING(_("Skipped templates:"))
            for skipTemplate in skipTemplates + skipDirTemplates:
                self.printWARNING(" "*6 + skipTemplate)
            if skipDirs:
                self.printWARNING(_("Skipped directories:"))
            for skipDir in skipDirs:
                self.printWARNING(" "*6 + skipDir)
            self.printWARNING("")
            self.printWARNING(_("Headers of directory templates and headers "
                         "of files on the first level should include "
                         "an action variable"))
            self.printWARNING(_("Example:"))
            self.printWARNING("# Calculate ac_install_merge==on")
        return skipDirs + skipTemplates

    def 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.currentAction = "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 and not self.stop:
            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
                        category = isPkgInstalled(pkg)
                        if category:
                            pkgContents = PkgContents("{CATEGORY}/{PF}".format(
                                        **category[0]))
                            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:not x in \
                                  self.cltObj.filterApplyTemplates[filename],
                           map(lambda x:x[0],pkgs))
                    self.cltObj.filterApplyTemplates[filename].extend(pkgs)
            self.cltObj.applyTemplates()
            self.stop = self.stop or self.cltObj.stop
        if not self.stop and ((self.objVar.Get('cl_merge_pkg') or \
            self.objVar.Get('cl_action') in ("sync","domain","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 and self.stop != TemplatesInterrupt.ABORT:
            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_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)
        autoUpdateDict = {}
        for pkg in list(set(filter(None,list(self.changedFiles.getPkgs())+
                            self.objVar.Get('cl_merge_pkg')))):
            category = isPkgInstalled(pkg,prefix=chrootPath)
            if category:
                pkgContents = PkgContents("{CATEGORY}/{PF}".format(
                           **category[0]),prefix=chrootPath)
                for filename,action in self.changedFiles.getPkgFiles(pkg):
                    if filename in self.protectedFiles:
                        pkgContents.removeObject(filename)
                        continue
                    if action in (ChangedFiles.FILE_MODIFIED,
                                  ChangedFiles.DIR_CREATED,
                                  ChangedFiles.DIR_EXISTS):
                        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()))
                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())):
                        if hashlib.md5(readFile(filename)).hexdigest() == \
                           hashlib.md5(readFile(
                            cfgs[filename][0][1])).hexdigest():
                            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:
                        open(filename,'w').write(
                            readFile(cfgs[filename][0][1]))
                    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:
                            os.unlink(fn)
                        except Exception as e:
                            self.printWARNING(_("Failed to remove %s")%fn)
                try:
                    pkgContents.writeContents()
                except IOError as e:
                    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={}, skipTemplates=[]):
        """Сканирование и обработка шаблонов в директории scanDir"""
        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["path"] = pathDir
            if not objHead is True:
                if objHead.typeAppend == "skip":
                    # Установка опции пропуска директории
                    optDir["skip"] = True
                if ("autoupdate" in objHead.params or
                    self.objVar.Get('cl_autoupdate_set') == 'on'):
                    optDir["autoupdate"] = True
        if flagDir or stat.S_ISDIR(os.lstat(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(absPath)
                statInfo = stInfo[stat.ST_MODE]
                prevModule = self.objVar.defaultModule
                prevBelong = self.functObj.currentBelong
                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["path"] = pathDir
                        if not objHead is True:
                            if objHead.typeAppend == "skip":
                                # Установка опции пропуска директории
                                optNextDir["skip"] = True
                            if ("autoupdate" in objHead.params or
                                self.objVar.Get('cl_autoupdate_set') == 'on'):
                                optNextDir["autoupdate"] = True
                        ret = self.scanningTemplates(absPath, prefix, True,
                                                     optNextDir)
                        if ret is False:
                            break
                except TemplatesInterrupt as e:
                    if e.isInterrupt():
                        self.stop = e.status()
                        return False
                    else:
                        self.clearErrors()
                        self.printWARNING(str(e))
                finally:
                    self.objVar.defaultModule = prevModule
                    self.functObj.currentBelong = prevBelong
                    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",nameFileConfig)
        filesApl = self.joinTemplate(path, nameFileConfig, optFile)
        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 _processMergePostmerge(self,params, templateDirFile):
        """Обработка параметров merge= , postmerge="""
        if "merge" in params:
            mergePkgs = params['merge'].split(',')
        else:
            mergePkgs = []
        if "postmerge" in params:
            postmergePkgs = params['postmerge'].split(',')
        else:
            postmergePkgs = []

        if mergePkgs or postmergePkgs:
            for wrongPkg in (x for x in mergePkgs + postmergePkgs \
                             if not self.functObj.checkCorrectPkgName(x)):
                self.printWARNING(
                    _("Wrong package '%s' for 'merge' in the template")%
                              wrongPkg + _(": ") + templateDirFile)
            if self.objVar.Get('cl_ebuild_phase') == 'postinst':
                for pkg in postmergePkgs:
                    if not pkg in self.objVar.Get('cl_merge_pkg_pass'):
                        self.objVar.Get('cl_merge_pkg_pass').append(pkg)
                    if not pkg in self.postmergePkgs:
                        try:
                            with open(self.postmergeFile,"a") as f:
                                f.write("%s\n"%pkg)
                            self.postmergePkgs.append(pkg)
                        except:
                            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 getApplyHeadDir(self, newDir, templateDirFile, optDir):
        """Применяет шаблон к директории (права, владелец, и.т. д)"""

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

        def chownConfDir(nameDirConfig, uid, gid, nameFileTemplate):
            """Изменение владельца конфигурационной директории"""
            try:
                os.chown(nameDirConfig, uid, gid)
            except Exception as e:
                if hasattr(e,'errno') and e.errno == 13 and \
                    "var/calculate/remote" in nameDirConfig:
                    return True
                import pwd, grp
                try:
                    userName = pwd.getpwuid(uid).pw_name
                except:
                    userName = str(uid)
                try:
                    groupName = grp.getgrgid(gid).gr_name
                except:
                    groupName = str(gid)
                owner = userName + ":" + groupName
                self.setError(_("Failed to apply template file %s")\
                                %nameFileTemplate)
                self.setError(_("error") + " " +\
                              "'chown %s %s'"%(owner, nameDirConfig))
                return False
            return True

        applyDir = newDir

        # Родительская директория
        if optDir.get("path"):
            path = 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 "autoupdate" in optDir:
                self.autoUpdateDirs.append(applyDir)
            if crDirs is True:
                return (applyDir, True, [])
            else:
                return (applyDir, True, crDirs)
        try:
            FD = open(templateDirFile)
            textTemplate = FD.readline().rstrip()
            buf = textTemplate
            while buf and textTemplate.endswith('\\'):
                buf = FD.readline()
                textTemplate = "%s %s"%(textTemplate[:-1],buf.rstrip())
            FD.close()
        except:
            self.setError(_("Failed to open the template") + _(": ") +
                               templateDirFile)
            return ("", False, [])

        headerLine = self.getHeaderText(textTemplate)
        if headerLine:
            moduleParam = filter(lambda x:x.startswith('env='),
                          headerLine.split())
            if moduleParam:
                self.objVar.defaultModule = moduleParam[0].partition('=')[2]
                try:
                    varModule = importlib.import_module(
                        "calculate.%s.variables"%self.objVar.defaultModule)
                except (ImportError, AttributeError),e:
                    return ("", False, [])
        # Заменяем переменные на их значения
        textTemplate = self.applyVarsTemplate(textTemplate, templateDirFile)

        # Заменяем функции на их значения
        textTemplate = self.applyFuncTemplate(textTemplate, templateDirFile)
        # Обработка заголовка
        objHead = dirHeader(templateDirFile,textTemplate,self.objVar,function)
        signs = {'ac_install_patch==on':'patch',
                 'ac_desktop_profile==on':'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)
        # Пропускаем директорию
        if objHead.typeAppend == "skip":
            applyDir = path
            return (applyDir, objHead, [])

        # Изменяем название родительской директории
        if "path" in  objHead.params:
            path = objHead.params['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 'path' in the template") +
                              _(": ") + templateDirFile)
                return ("", False, [])
            else:
                path = pathJoin(self._baseDir, path)

        # Изменяем название директории
        if  "name" in  objHead.params:
            nameDir = objHead.params['name']
            if "/" in nameDir or nameDir == ".." or nameDir == ".":
                self.setError(_("Wrong value 'name' in the template") +
                              _(": ") + 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 objHead.typeAppend == "remove":
            if os.path.isdir(applyDir):
                self.changedFiles.addObj(applyDir,ChangedFiles.DIR_REMOVED,
                                         self.functObj.currentBelong)
                # удаляем директорию
                try:
                    removeDir(applyDir)
                except:
                     self.setError(_("Failed to delete the directory: ") +\
                                   applyDir)
            return ("", False, [])

        # Очищаем директорию
        if objHead.typeAppend == "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:
                            self.setError(
                                _("Failed to delete the directory: ") +\
                                          removePath)
                    else:
                        try:
                            os.unlink(removePath)
                        except:
                            self.setError(
                                _("Failed to delete: ") + removePath)
            return ("", False, [])

        # Созданные директории
        createdDirs = []
        # chmod - изменяем права
        if "chmod" in objHead.params:
            mode = self.__octToInt(objHead.params['chmod'])
            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:
                    try:
                        os.chmod(applyDir, mode)
                    except:
                        self.setError(_("Failed to change the mode for "
                            "the directory: ") +applyDir)
            else:
                self.setError(_("Wrong value 'chmod' in the template") +
                              _(": ") + templateDirFile)
                return ("", False, [])
        # chown - изменяем владельца и группу
        if "chown" in objHead.params:
            owner = objHead.params['chown']
            if owner:
                if  ":" in owner:
                    strUid, strGid = owner.split(":")
                    import pwd
                    uid = self.getUidFromPasswd(strUid)
                    try:
                        uid = pwd.getpwnam(strUid).pw_uid
                    except:
                        self.setError(_("No such user on the system: ")
                                + strUid)
                        self.setError(_("Wrong value 'chown' in the template")+
                                      _(": ") + templateDirFile)
                        return ("", False, [])
                    gid = self.getGidFromGroup(strGid)
                    try:
                        import grp
                        gid = grp.getgrnam(strGid).gr_gid
                    except:
                        self.setError(_("Group not found on the system: ")
                                        +strGid)
                        self.setError(
                            _("Wrong value 'chown' in the template") +
                            _(": ") + templateDirFile)
                        return ("", False, [])

                    if not os.path.exists(applyDir):
                        crDirs = self.createDir(applyDir, False, uid, gid)
                        if not crDirs:
                            return ("", False, [])
                        if not crDirs is True:
                            createdDirs += crDirs
                    else:
                        if not chownConfDir(applyDir, uid, gid,
                                            templateDirFile):
                            return ("", False, [])
                else:
                    self.setError(_("Wrong value 'chown' in the template") +
                                  _(": ") + templateDirFile)
                    return ("", False, [])
            else:
                self.setError(_("Wrong value 'chown' in the template") +
                              _(": ") + templateDirFile)
                return ("", False, [])
        else:
            # Устанавливаем владельцем директории, пользователя по умолчанию
            # (переменная шаблона ur_login)
            if os.path.exists(applyDir):
                self.changedFiles.addObj(applyDir,ChangedFiles.DIR_EXISTS,
                                         self.functObj.currentBelong)
                tUid, tGid = getModeFile(applyDir, mode="owner")
                if (self.uid, self.gid) != (tUid, tGid):
                    if not chownConfDir(applyDir, self.uid, self.gid,
                                        templateDirFile):
                        return ("", False, [])
            else:
                self.changedFiles.addObj(applyDir,ChangedFiles.DIR_CREATED,
                                         self.functObj.currentBelong)
                crDirs = self.createDir(applyDir, False, self.uid, self.gid)
                if not crDirs:
                    return ("", False, [])
                if not crDirs is True:
                    createdDirs += crDirs
        if not objHead:
            applyDir = ""
        if applyDir:
            if ("autoupdate" in optDir or "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):
            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('#'),
                     open(passwdFile,'r')))))
            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):
            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('#'),
                     open(groupFile,'r')))))
            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
        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
        pkg = isPkgInstalled(pkg,sortByVersion=True,prefix=chrootPath)
        if not pkg:
            return pathFile
        if checkContents("{CATEGORY}/{PF}".format(**pkg[-1]),
               pathFile,
               prefix=chrootPath,
               reservedFile='/var/lib/calculate/-CONTENTS-{PN}'.format(**pkg[-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 getApplyHeadTemplate(self, nameFileTemplate, nameFileConfig,
                               templateFileType, optFile):
        """Применяет заголовок к шаблону (права, владелец, и.т. д)"""
        def function(text):
            """Функция обработки функций в заголовке"""
            return self.applyFuncTemplate(text,nameFileTemplate)

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

        def chownConfFile(nameFileConfig, uid, gid, nameFileTemplate,
                          checkExists=True):
            """Изменение владельца конфигурационного файла"""
            try:
                if checkExists and not os.path.exists(nameFileConfig):
                    # Создание файла
                    FD  = open(nameFileConfig, "w")
                    FD.close()
                os.lchown(nameFileConfig, uid, gid)
            except Exception as e:
                if hasattr(e,'errno') and e.errno == 13 and \
                    "var/calculate/remote" in nameFileConfig:
                    return True
                import pwd, grp
                try:
                    userName = pwd.getpwuid(uid).pw_name
                except:
                    userName = str(uid)
                try:
                    groupName = grp.getgrgid(gid).gr_name
                except:
                    groupName = str(gid)
                owner = userName + ":" + groupName
                self.setError(_("Failed to apply template file %s")\
                                %nameFileTemplate)
                self.setError(_("error") + " " +\
                              "'chown %s %s'"%(owner, nameFileConfig))
                return False
            return True

        def chmodConfFile(nameFileConfig, mode, nameFileTemplate,
                          checkExists=True):
            """Изменения режима доступа конфигурационного файла"""
            try:
                if checkExists and not os.path.exists(pathOldFile):
                    # Создание файла
                    FD  = open(nameFileConfig, "w")
                    FD.close()
                os.chmod(nameFileConfig, mode)
            except Exception as e:
                if hasattr(e,'errno') and e.errno == 13 and \
                    "var/calculate/remote" in nameFileConfig:
                    return True
                self.setError(_("Failed to apply template file %s")\
                                %nameFileTemplate)
                self.setError(\
                    _("error") + " " +\
                    "'chmod %s %s'"%(str(oct(mode)), nameFileConfig))
                return False
            return True

        self.closeFiles()
        pathProg = ""
        self.executeType = None
        # Файлы в системе к которым были применены шаблоны
        applyFiles = [nameFileConfig]
        # В случае бинарного типа файла читаем шаблон
        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)

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

        # Родительская директория
        path = optFile["path"]
        # Изменяем название родительской директории
        if "path" in  objHeadNew.params:
            path = objHeadNew.params['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 'path' in the template") +
                              _(": ") + nameFileTemplate)
                return ([], False)
            else:
                path = pathJoin(self._baseDir, path)

        # Путь к оригинальному файлу - pathOldFile
        # Изменяем путь к оригинальному файлу
        if objHeadNew.params.has_key("name"):
            nameFile = objHeadNew.params['name']
            if "/" in nameFile or nameFile == ".." or nameFile == ".":
                self.setError(_("Wrong value 'name' in the template") +
                              _(": ") + nameFileTemplate)
                return ([], False)
            # Новый путь к оригинальному файлу
            pathOldFile = pathJoin(path,nameFile)
        else:
            pathOldFile = pathJoin(path,os.path.split(nameFileConfig)[1])
        pathOrigFile = pathOldFile
        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 "exec" in  objHeadNew.params or "run" in objHeadNew.params:
            if "exec" in objHeadNew.params:
                paramName = "exec"
            else:
                paramName = "run"
            self.executeType = paramName
            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 == "join":
                self.setError(_("Wrong value 'append=join' in the template") +
                              _(": ") + nameFileTemplate)
                return ([], False)

        # Очищаем оригинальный файл
        if typeAppendTemplate == "clear":
            try:
                open(pathOldFile, "w").truncate(0)
                newBuffer = ""
            except:
                self.setError(_("Template error") + _(": ") +
                              nameFileTemplate)
                self.setError(_("Failed to clear the file") + _(": ") +
                              pathOldFile)
            return (applyFiles, False)
        # Удаляем оригинальный файл
        if typeAppendTemplate == "remove":
            if objHeadNew.params.has_key("force"):
                pathOldFile = pathOrigFile
                self.configMode = T_ORIGIN
            if os.path.islink(pathOldFile):
                # удаляем ссылку
                try:
                    os.unlink(pathOldFile)
                    return (applyFiles, False)
                except:
                    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:
                    self.setError(_("Template error") + _(": ") +
                                  nameFileTemplate)
                    self.setError(_("Failed to delete the file") + _(": ") +
                                  pathOldFile)
                    return ([], False)
            return ([], False)
        # Пропускаем обработку шаблона
        elif typeAppendTemplate == "skip":
             return ([], False)

        # Создаем директорию для файла если ее нет
        if not os.path.exists(path):
            if not self.createDir(path):
                return ([], False)
        # В случае force
        if objHeadNew.params.has_key("force"):
            if os.path.islink(pathOldFile):
                # удаляем ссылку
                try:
                    newBuffer = ""
                    os.unlink(pathOldFile)
                except:
                    self.setError(_("Template error") + _(": ") +
                                  nameFileTemplate)
                    self.setError(_("Failed to delete the link") + _(": ") +
                                  pathOldFile)
                    return ([], False)
            if os.path.isfile(pathOldFile):
                # удаляем файл
                try:
                    newBuffer = ""
                    os.remove(pathOldFile)
                except:
                    self.setError(_("Template error") + _(": ") +
                                  nameFileTemplate)
                    self.setError(_("Failed to delete the file") + _(": ") +
                                  pathOldFile)
                    return ([], False)

        flagSymlink = False
        flagForce = False
        # Если есть параметр mirror
        if objHeadNew.params.has_key("mirror"):
            if objHeadNew.params.has_key("link"):
                templateFile = objHeadNew.params['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):
                    if os.path.exists(pathOldFile):
                        try:
                            newBuffer = ""
                            os.remove(pathOldFile)
                        except:
                            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 objHeadNew.params.has_key("link") and\
            not objHeadNew.params.has_key("symbolic"):
            templateFile = objHeadNew.params['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)
            if foundTemplateFile:
                try:
                    F_CONF = self.openTemplFile(templateFile)
                    buff = F_CONF.read()
                    F_CONF.close()
                    fMode, fUid, fGid = getModeFile(templateFile)
                except:
                    self.setError(_("Template error") + _(": ") +
                                  nameFileTemplate)
                    self.setError(_("Failed to open the file") + _(": ") +
                                  templateFile)
                    return ([], False)
            if os.path.exists(pathOldFile):
                try:
                    newBuffer = ""
                    os.remove(pathOldFile)
                except:
                    self.setError(_("Template error") + _(": ") +
                                  nameFileTemplate)
                    self.setError(_("Failed to delete the file") + _(": ") +
                                  pathOldFile)
                    return ([], False)
            if foundTemplateFile:
                try:
                    FD  = open(pathOldFile, "w+")
                    newBuffer = buff
                    FD.write(buff)
                    FD.close()
                except:
                    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 chmodConfFile(pathOldFile, fMode, nameFileTemplate,
                                         checkExists=False):
                        return ([], False)

        # Если символическая ссылка
        if objHeadNew.params.has_key("symbolic"):
            prevOldFile = pathOldFile
            pathOldFile = objHeadNew.params['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:
                    self.setError(_("Template error") + _(": ") +
                                  nameFileTemplate)
                    self.setError(
                        _("Failed to change the current directory to")+\
                                    " " + pathLink)
                    return ([], False)

        # chmod - изменяем права
        if objHeadNew.params.has_key("chmod"):
            mode = self.__octToInt(objHeadNew.params['chmod'])
            if mode:
                if not chmodConfFile(pathOldFile, mode, nameFileTemplate):
                    preReturn(pathProg)
                    return ([], False)
            else:
                self.setError(_("Wrong value 'chmod' in the template") +
                              _(": ") + nameFileTemplate)
                preReturn(pathProg)
                return ([], False)
        # chown - изменяем владельца и группу
        if objHeadNew.params.has_key("chown"):
            owner = objHeadNew.params['chown']
            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:
                            self.setError(_("No such user on the system: ") +
                                          strUid)
                            self.setError(
                                _("Wrong value 'chown' in the template") +
                                _(": ") + 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:
                            self.setError(_("Group not found on the system: ") +
                                          strGid)
                            self.setError(
                                _("Wrong value 'chown' in the template") +
                                _(": ") + nameFileTemplate)
                            preReturn(pathProg)
                            return ([], False)
                    # Изменяем владельца файла
                    if not chownConfFile(pathOldFile,uid,gid,nameFileTemplate):
                        preReturn(pathProg)
                        return ([], False)
                else:
                    self.setError(_("Wrong value 'chown' in the template") +
                                  _(": ") + nameFileTemplate)
                    preReturn(pathProg)
                    return ([], False)
            else:
                self.setError(_("Wrong value 'chown' in the template") +
                              _(": ") + nameFileTemplate)
                preReturn(pathProg)
                return ([], False)
        if not flagSymlink:
            self.openFiles(nameFileTemplate, pathOldFile,objHeadNew.fileType,
                            newBuffer)
            if self.getError():
                return ([], False)
        if not objHeadNew.params.has_key("chown"):
            # Устанавливаем владельцем конфигурационного файла,
            # пользователя по умолчанию (переменная шаблона ur_login)
            if os.path.exists(pathOldFile):
                tUid, tGid = getModeFile(pathOldFile, mode="owner")
                if (self.uid, self.gid) != (tUid, tGid):
                    # Изменяем владельца файла
                    if not 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:
                    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:
                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 "protected" in objHeadNew.params:
                    self.protectedFiles += applyFiles
                return (applyFiles, False)
        else:
            applyFiles = [pathOldFile]
        if pathProg:
            os.chdir(pathProg)
        # Если файлы заменяются не нужно их обрабатывать дальше
        if typeAppendTemplate == "replace" and\
            not objHeadNew.params.has_key("symbolic") and\
            objHeadNew.params.has_key("link"):
                preReturn(pathProg)
                return (applyFiles, False)
        if not pathOldFile in self.dictProcessedTemplates:
            self.dictProcessedTemplates[pathOldFile] = []
        self.dictProcessedTemplates[pathOldFile].append(nameFileTemplate)
        preReturn(pathProg)
        if ( "autoupdate" in optFile or "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 "protected" in objHeadNew.params:
            self.protectedFiles += applyFiles
        return (applyFiles, objHeadNew)

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

        createNewClass(self, name, bases, attrs)
        name - имя класса - str,
        bases - cписок наследуемых классов - (tuple),
        attrs - аттрибуты класса - {dict}
        """
        class newMethod:
            #Объединяем конфигурации
            def join(self, newObj):
                if newObj.__class__.__name__ == self.__class__.__name__:
                    self.docObj.joinDoc(newObj.doc)
                    # Пост обработка
                    if 'postXML' in dir(self):
                        self.postXML()
        attrsNew = {}
        attrsNew["configName"] = name
        if attrs:
            for key in attrs.keys():
               attrsNew[key] = attrs[key]
        newCl = type(name, bases + (newMethod, object,), attrsNew)
        return newCl

    def fileIsUtf(self, fileName):
        """Проверяет файл на кодировку UTF-8"""
        if os.path.isfile(fileName):
            FD = open(os.path.abspath(fileName),'r')
            newTemplate = FD.read(1)+FD.read()
            FD.close()
            try:
                newTemplate.decode("UTF-8")
            except:
                return False
            return True
        return False

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

        join(nameFileTemplate, nameFileConfig, ListOptTitle)
        Объединение шаблона nameFileTemplate и конф. файла nameFileConfig,
        ListOptTitle - список строк которые добавятся в заголовок
        optFile = опции для шаблона
        """
        # Выполняем условия для блока текста а так-же заменяем переменные
        self.nameFileTemplate = os.path.abspath(nameFileTemplate)
        self.F_TEMPL = self.openTemplFile(self.nameFileTemplate)
        self.textTemplate = self.F_TEMPL.read()
        self.configMode = T_ORIGIN
        self.closeTemplFile()
        # Флаг копирования шаблона в конфигурационный файл
        flagCopyTemplate = True
        # Тип шаблона бинарный или текстовый
        templateFileType = self.getTemplateType()
        headerLine = self.getHeaderText(self.textTemplate)
        if headerLine:
            moduleParam = filter(lambda x:x.startswith('env='),
                          headerLine.split())
            if moduleParam:
                self.objVar.defaultModule = moduleParam[0].partition('=')[2]
                try:
                    varModule = importlib.import_module(
                        "calculate.%s.variables"%self.objVar.defaultModule)
                except (ImportError, AttributeError),e:
                    return []

        if templateFileType != "bin":
            # Заменяем переменные на их значения
            self.textTemplate = self.applyVarsTemplate(self.textTemplate,
                                                    nameFileTemplate)
            flagCopyTemplate = False
        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.applyFuncTemplate(objHeadNew.body,
                                                       nameFileTemplate)
        # Настоящее имя конфигурационного файла
        nameFileConfig = filesApply[0]
        # Флаг - кодировка с бинарными примесями у файла шаблона включаем при
        # условии текстового файла и кодировки отличной от UTF-8
        flagNotUtf8New = False
         # Флаг - кодировка с бинарными примесями у оригинального файла
        flagNotUtf8Old = False 
        if not flagCopyTemplate:
            # проверяем кодировку шаблона
            if not self.fileIsUtf(nameFileTemplate):
                flagNotUtf8New = True
        if not (objHeadNew.params.has_key("link") and\
            objHeadNew.params.has_key("symbolic")):
            # проверяем кодировку оригинального файла
            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 ("before", "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 ("multiline","dotall")
                        if objHeadNew.params.has_key(x)]
            if wrongOpt:
                self.setError(\
                    _("Option %s should be used for format=patch only")\
                      %wrongOpt[0])
                return False
        if objHeadNew.fileType:
            formatTemplate = objHeadNew.fileType
            typeAppendTemplate = objHeadNew.typeAppend
            if formatTemplate in ("patch","diff"):
                if typeAppendTemplate != "patch":
                    self.setError(\
                        _("Wrong option append=%(type)s in template %(file)s")\
                          %{"type":typeAppendTemplate,"file":nameFileTemplate})
                    return False
                # создаем объект формата шаблона
                objTempl = self.getFormatObj(formatTemplate, self.textTemplate)
                if formatTemplate == 'patch':
                    if objHeadNew.params.has_key("multiline"):
                        objTempl.setMultiline()
                    if  objHeadNew.params.has_key("dotall"):
                        objTempl.setDotall()
                if not objTempl:
                    self.setError(\
                        _("Incorrect header parameter format=%s "
                          "in the template")\
                          %formatTemplate + " " + nameFileTemplate)
                    return False
                if objHeadOld and objHeadOld.body:
                    self.textConfig = objHeadOld.body
                # обработка конфигурационного файла
                self.textTemplate = objTempl.processingFile(self.textConfig,
                                    pathJoin(self.objVar.Get('cl_chroot_path'),
                                        self.objVar.Get('cl_root_path')))
                if objTempl.getError():
                    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))
                if execStr:
                    self.textConfig = execStr + title + self.textTemplate
                else:
                    self.textConfig = title + self.textTemplate
                if formatTemplate in ("diff",):
                    return objTempl.patchFiles
                else:
                    self.saveConfFile()
                if  'run' in objHeadNew.params:
                    if not self.executeTemplate(self.textConfig,
                                                objHeadNew.params['run']):
                        self.setError(_("Wrong template") + _(": ") +\
                                      nameFileTemplate)
                        self.setError(_("Failed to execute") + _(": ") +\
                                      self.nameFileConfig)
                        return False
                    return False
                return filesApply if not 'exec' in objHeadNew.params else False
            # Создаем объект в случае параметра format в заголовке
            if (typeAppendTemplate == "replace" or\
                typeAppendTemplate == "before" or\
                typeAppendTemplate == "after") and\
                not (formatTemplate == "bin" or\
                formatTemplate == "raw"):
                # Преобразовываем бинарные файлы
                if flagNotUtf8New:
                    objTxtCoder = utfBin()
                    self.textTemplate = objTxtCoder.encode(self.textTemplate)
                # создаем объект формата шаблона
                objTemplNew = self.getFormatObj(formatTemplate,
                                                self.textTemplate)
                if not objTemplNew:
                    self.setError(\
                        _("Incorrect header parameter format=%s "
                          "in the template")\
                          %formatTemplate + " " + nameFileTemplate)
                    return False
                if "xml_" in formatTemplate:
                    if objTemplNew.getError():
                        self.setError(_("Wrong template") + _(": ") +\
                                      nameFileTemplate)
                        return False
                    # Имя файла внутри 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 flagNotUtf8New:
                    self.textTemplate = objTxtCoder.decode(self.textTemplate)
                # Титл для объединения
                if ListOptTitle:
                    title = self.getTitle(objTemplNew._comment,
                                          ListOptTitle,
                                          configPath=nameFileConfig)
                    title = title.encode("UTF-8")
            # Замена
            if typeAppendTemplate == "replace":
                if "xml_" in formatTemplate:
                    data = self.textTemplate.split("\n")
                    data.insert(1,title)
                    self.textConfig = "\n".join(data)
                else:
                    if objHeadNew.execStr:
                        self.textConfig = objHeadNew.execStr+title+\
                                          self.textTemplate
                    else:
                        self.textConfig = title + self.textTemplate
                
                self.saveConfFile()
                if  'run' in objHeadNew.params:
                    if not self.executeTemplate(self.textConfig,
                                                objHeadNew.params['run']):
                        self.setError(_("Wrong template") + _(": ") +\
                                      nameFileTemplate)
                        self.setError(_("Failed to execute") + _(": ") +\
                                      self.nameFileConfig)
                        return False
                    return False
                return filesApply if not 'exec' in objHeadNew.params else False
            # Вверху
            elif typeAppendTemplate == "before":
                if "xml_" in formatTemplate:
                    self.setError(\
                        _("Wrong option append=before in template %s")\
                        %nameFileTemplate)
                    return False
                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
                if execStr:
                    self.textConfig = execStr + title + tmpTemplate
                else:
                    self.textConfig = title + tmpTemplate

                self.saveConfFile()
                if  'run' in objHeadNew.params:
                    if not self.executeTemplate(self.textConfig,
                                                objHeadNew.params['run']):
                        self.setError(_("Wrong template") + _(": ") +\
                                      nameFileTemplate)
                        self.setError(_("Failed to execute") + _(": ") +\
                                      self.nameFileConfig)
                        return False
                    return False
                return filesApply if not 'exec' in objHeadNew.params else False
            # Внизу
            elif typeAppendTemplate == "after":
                if "xml_" in formatTemplate:
                    self.setError(\
                        _("Wrong option append=after in template %s")\
                        %nameFileTemplate)
                    return False
                if objHeadOld and objHeadOld.body:
                    self.textConfig = objHeadOld.body
                if self.textTemplate[-1] == "\n":
                    tmpTemplate = self.textConfig + self.textTemplate
                else:
                    tmpTemplate = self.textConfig + "\n" + self.textTemplate
                if execStr:
                    self.textConfig = execStr + title + tmpTemplate
                else:
                    self.textConfig = title + tmpTemplate
                self.saveConfFile()
                if  'run' in objHeadNew.params:
                    if not self.executeTemplate(self.textConfig,
                                                objHeadNew.params['run']):
                        self.setError(_("Wrong template") + _(": ") +\
                                      nameFileTemplate)
                        self.setError(_("Failed to execute") + _(": ") +\
                                      self.nameFileConfig)
                        return False
                    return False
                return filesApply if not 'exec' in objHeadNew.params else False
            # Объединение
            elif typeAppendTemplate == "join":
                if flagNotUtf8New:
                    objTxtCoder = utfBin()
                    self.textTemplate = objTxtCoder.encode(self.textTemplate)
                if formatTemplate =="raw":
                    self.setError(\
                        _("Incorrect header parameter append=%s "
                          "in the template")\
                          %typeAppendTemplate + " " + nameFileTemplate)
                    return False
                # создаем объект формата шаблона
                objTemplNew = self.getFormatObj(formatTemplate,
                                                self.textTemplate)
                if not objTemplNew:
                    self.setError(\
                        _("Incorrect header parameter format=%s in "
                          "the template")\
                          %formatTemplate + " " + nameFileTemplate)
                    return False
                if "xml_" in formatTemplate:
                    if objTemplNew.getError():
                        self.setError(_("Wrong template") + _(": ") +\
                                        nameFileTemplate)
                        return False
                    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.getFormatObj(formatTemplate,
                                                self.textConfig)
                if not objTemplOld:
                    self.setError(_("Error in template %s") %nameFileConfig)
                    return False
                if "xml_" in formatTemplate:
                    if objTemplOld.getError():
                        self.setError(_("Wrong template") + _(": ") +\
                                        nameFileConfig)
                        return False
                    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 False
                    data = objTemplOld.getConfig().encode("UTF-8").split("\n")
                    data.insert(1,title)
                    self.textConfig = "\n".join(data)
                else:
                    if execStr:
                        self.textConfig = execStr + title +\
                            objTemplOld.getConfig().encode("UTF-8")
                    else:
                        self.textConfig = title +\
                                        objTemplOld.getConfig().encode("UTF-8")
                # Декодируем если кодировка не UTF-8
                if flagNotUtf8New or flagNotUtf8Old:
                    self.textTemplate = objTxtCoder.decode(self.textTemplate)
                    self.textConfig = objTxtCoder.decode(self.textConfig)
                self.saveConfFile()
                if  'run' in objHeadNew.params:
                    if not self.executeTemplate(self.textConfig,
                                                objHeadNew.params['run']):
                        self.setError(_("Wrong template") + _(": ") +\
                                      nameFileTemplate)
                        self.setError(_("Failed to execute") + _(": ") +\
                                      self.nameFileConfig)
                        return False
                    return False
                return filesApply if not 'exec' in objHeadNew.params else False
            else:
                self.setError(_("Wrong template option (type append)")
                            + _(": ") + typeAppendTemplate)
                return False
        else:
            self.setError(_("Template type not found: ") + nameFileTemplate)
            return False
        return filesApply

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

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

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

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

    def hasBelong(self,filename):
        """
        Change belong function
        """
        f = open(filename,'r')
        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"""
        ret = True
        if not objVar:
            objVar = self.objVar
        if not prefix:
            prefix = os.path.realpath(scanDir)
        if flagDir or stat.S_ISDIR(os.lstat(scanDir)[stat.ST_MODE]):
            for fileOrDir in sorted(listDirectory(scanDir)):
                try:
                    absPath = os.path.join(scanDir,fileOrDir)
                    stInfo = os.lstat(absPath)
                    statInfo = stInfo[stat.ST_MODE]
                    if fileOrDir.endswith(self.extFileTemplate) and\
                        stat.S_ISREG(statInfo):
                        if not self.filterApplyTemplates and \
                            self.objVar.Get('cl_merge_set') == 'off' or \
                            self.filterApplyTemplates and \
                            absPath[:-self.lenExtFileTemplate] in\
                            self.filterApplyTemplates.keys() or self.hasBelong(absPath):
                            prevDefault = self.objVar.defaultModule
                            if not self.processingFile(absPath, prefix):
                                return False
                            self.objVar.defaultModule = prevDefault
                    elif stat.S_ISDIR(statInfo):
                        if not self.scanningTemplates(absPath, prefix, True):
                            return False
                except TemplatesInterrupt as e:
                    if e.isInterrupt():
                        self.stop = e.status()
                        return False
                    else:
                        self.clearErrors()
                        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):
        """Обработка в случае шаблона файла"""
        self.numberProcessTempl += 1
        self.numberProcessTemplates(self.numberProcessTempl)
        # Пропуск шаблонов директорийscanningTemplates
        if self.templDirNameFile == os.path.split(path)[1]:
            return True
        self.functObj.currentBelong = ""
        # Проверка на переменные в названии файла
        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",nameFileConfig)
        filesApl = self.joinTemplate(path, nameFileConfig)
        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 applyTemplates(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 iniParser(_error, templateFormat):
    """Класс для работы с ini файлами

    """
    def __init__(self, iniFile=None,text=None):
        # название ini файла
        self.iniFile = iniFile or ""
        # права создаваемого ini-файла
        self.mode = 0640
        # Cоответствует ли формат файла нужному
        self.checkIni = None
        self.FD = None
        self.readOnly = False
        self.text = text
        self.locked = False

    def joinText(self, iniObj, xmlNewDoc):
        """Объединяет два документа"""
        newRootNode = xmlNewDoc.documentElement
        newBodyNode = xpath.Evaluate('child::body',newRootNode)[0]
        newImportBodyNode = iniObj.doc.importNode(newBodyNode, True)
        iniObj.docObj.joinBody(iniObj.docObj.body, newImportBodyNode)
        #iniObj.docObj.insertBRtoBody(iniObj.docObj.body)

    def setMode(self, mode):
        """установка прав создаваемого ini-файла"""
        self.mode = mode

    def lockfile(self,fd,fn,timeout=5):
        """
        Блокировка файла с таймаутом
        """
        if self.locked:
            return True
        for i in range(0,timeout):
            try:
                fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
                return True
            except IOError,e:
                if e.errno != errno.EAGAIN:
                    raise e
            time.sleep(1)
        else:
            raise TemplatesError(_("Lock timeout of %s")%fn)

    @property
    def iniFile_lock(self):
        return self.iniFile+".lock~"

    def wait_global_lock(self, timeout=10):
        lockfn = self.iniFile_lock
        for i in range(0,timeout):
            if self.locked or not os.path.exists(lockfn):
                break
            time.sleep(1)

    @contextmanager
    def lock(self):
        lockfn = self.iniFile_lock
        lockf = open(lockfn, "w+")
        self.lockfile(lockf.fileno(), lockfn)
        self.locked = True
        try:
            yield
        finally:
            self.locked = False
            os.unlink(lockfn)

    def _open(self, mode):
        self.wait_global_lock()
        return open(self.iniFile, mode)

    def openIniFile(self):
        if not self.text is None:
            return self.text
        if not os.access(self.iniFile, os.R_OK):
            return ""
        self.FD = self._open("r")
        self.lockfile(self.FD.fileno(),self.iniFile)
        textIni = self.FD.read()
        return textIni

    def openRWIniFile(self):
        if not os.access(self.iniFile, os.R_OK):
            return ""
        try:
            self.FD = self._open("r+")
        except (IOError, OSError), e:
            self.FD = self._open("r")
            self.readOnly = True
        self.lockfile(self.FD.fileno(), self.iniFile)
        textIni = self.FD.read()
        return textIni

    def writeIniFile(self, txtConfig):
        if self.readOnly:
            self.setError(_("Failed to write to file")
                    + _(": ") + self.iniFile)
            return False
        if not os.path.exists(self.iniFile):
            try:
                # Создание файла
                self.FD  = self._open("w+")
                self.lockfile(self.FD.fileno(), self.iniFile)
                os.chmod(self.iniFile, self.mode)
            except:
                self.setError(_("Failed to create the file") +
                            _(": ") + self.iniFile)
                return False
        if not self.FD:
            self.setError(_("Failed to write to file")
                    + _(": ") + self.iniFile)
            return False
        self.FD.truncate(0)
        self.FD.seek(0)
        self.FD.write(txtConfig)
        self.FD.close()
        self.FD = None
        return True

    def setVar(self, strHeader, dictVar):
        """Заменяет или добавляет область и переменные

        Добавляет область в ini-файл или объединяет с существующей
        strHeader - имя области
        dictVar - словарь переменных
        """
        textIni = self.openRWIniFile()
        nameFomat = self.checkIniFile(textIni)
        if not nameFomat:
            return False
        if type(strHeader) in (tuple, list):
            # формат plasma
            classObj = self.getClassObj("plasma")
        else:
            if nameFomat == "plasma":
                self.setError(_("Trying to write a variable of 'samba' "
                                "format to file %s ('plasma' format)")\
                                %self.iniFile)
                return False
            # формат samba
            classObj = self.getClassObj("samba")
        # создаем объект
        # и записываем в него содержимое ini-файла
        objIni = classObj(textIni)
        # создаем текст из строки заголовка и
        # словаря переменных области
        txtConfig = objIni.createTxtConfig(strHeader, dictVar)
        # создаем объект и записываем в него текст
        objIniAdd = classObj(txtConfig)
        # объединяем объекты для получения результирующего текста
        objIni.join(objIniAdd)
        # получаем текст
        txtConfig = objIni.getConfig().encode("UTF-8")
        # записываем его в ini файл
        if not self.writeIniFile(txtConfig):
            return False
        return True

    def isEmptyFile(self, textIni):
        """Если файл пустой или содержит только комментарии - False

        иначе - True
        """
        if textIni.strip():
            if filter(lambda x: x.strip(),
                      map(lambda x:x[0].split(";")[0], 
                          map(lambda x: x.split("#"),
                              textIni.splitlines()))):
                return False
            else:
                return True
        else:
            return True

    def checkIniFile(self, textIni):
        """Проверка на правильность формата файла"""
        if self.checkIni is None:
            # Ошибка
            if textIni == False:
                self.checkIni = False
                return False
            self.checkIni = "samba"
            # В файле есть данные
            if not self.isEmptyFile(textIni):
                try:
                    objIni = self.getClassObj("plasma")(textIni)
                except:
                    self.setError(_("Incorrect file format") + _(": ") + \
                                    self.iniFile)
                    self.checkIni = False
                    return self.checkIni
                allAreas = objIni.docObj.getAllAreas()
                for xmlArea in allAreas:
                    parentNode = xmlArea.parentNode
                    if parentNode and parentNode.tagName == "area":
                        self.checkIni = "plasma"
                        break
                if self.checkIni == "samba":
                    objIni = self.getClassObj("samba")(textIni)
                    xmlBody = objIni.docObj.getNodeBody()
                    if not xmlBody.firstChild:
                        self.checkIni = False
        return self.checkIni

    def delVar(self, strHeader, nameVar):
        """Удаляем переменную из ini файла"""
        delNameVar = "!%s" %(nameVar)
        dictVar = {delNameVar:"del"}
        res = self.setVar(strHeader, dictVar)
        return res

    def delArea(self, strHeader):
        """Удаляем область из ini файла"""
        if type(strHeader) in (tuple, list):
            # Формат plasma
            delStrHeader = list(strHeader[:])
            delStrHeader[-1] = "!%s"%delStrHeader[-1]
        else:
            # Формат samba
            delStrHeader = "!%s" %(strHeader)
        dictVar = {"del":"del"}
        res = self.setVar(delStrHeader, dictVar)
        return res

    def getVar(self, strHeader, nameVar, checkExistVar=False):
        """Получаем значение переменной из ini-файла"""
        textIni = self.openIniFile()
        if self.FD:
            self.FD.close()
            self.FD = None
        nameFomat = self.checkIniFile(textIni)
        if not nameFomat:
            return False
        formatPlasma = False
        if type(strHeader) in (tuple, list):
            # формат plasma
            classObj = self.getClassObj("plasma")
            formatPlasma = True
        else:
            if nameFomat == "plasma":
                self.setError(_("Trying to fetch a variable of 'samba' "
                                "format from file %s ('plasma' format)")\
                                %self.iniFile)
                return False
            # формат samba
            classObj = self.getClassObj("samba")
        # создаем объект и записываем в него содержимое ini-файла
        objIni = classObj(textIni)
        # получаем ноду body
        xmlBody = objIni.docObj.getNodeBody()
        flagFound, xmlBody = self.getLastNode(objIni, xmlBody, strHeader,
                                              formatPlasma)
        if flagFound and xmlBody:
            if formatPlasma:
                strHeader = strHeader[-1]
            # находим в области переменную
            res = objIni.docObj.getAreaFieldValues(strHeader, nameVar, xmlBody)
        else:
            res = False
        if checkExistVar:
            if res is False:
                return False, ""
            else:
                return True, res
        else:
            if res is False:
                return ""
            else:
                return res


    def getLastNode(self, objIni, xmlBody, strHeader, formatPlasma):
        """Ищет область в XML в которой область с переменными"""
        flagFound = True
        if not strHeader:
            flagFound = False
            return flagFound,xmlBody
        lenStrHeader = len(strHeader)
        if formatPlasma and lenStrHeader>0:
            xmlAreas = [xmlBody]
            for i in xrange(lenStrHeader-1):
                flagFound = False
                for xmlArea in xmlAreas:
                    xmlAreas = objIni.docObj.getArea(strHeader[i], xmlArea)
                    if xmlAreas:
                        flagFound = True
                        break
            if xmlAreas:
                xmlBody = xmlAreas[0]
        return flagFound,xmlBody

    def getAreaVars(self, strHeader):
        """Получаем все переменнные области из ini-файла"""
        textIni = self.openIniFile()
        if self.FD:
            self.FD.close()
            self.FD = None
        nameFomat = self.checkIniFile(textIni)
        if not nameFomat:
            return False
        formatPlasma = False
        if type(strHeader) in (tuple, list):
            # формат plasma
            classObj = self.getClassObj("plasma")
            formatPlasma = True
        else:
            if nameFomat == "plasma":
                self.setError(_("Trying to fetch a variable of 'samba' "
                                "format from file %s ('plasma' format)")\
                                %self.iniFile)
                return False
            # формат samba
            classObj = self.getClassObj("samba")
        # создаем объект типа samba и записываем в него содержимое ini-файла
        objIni = classObj(textIni)
        # получаем ноду body
        xmlBody = objIni.docObj.getNodeBody()
        flagFound, xmlBody = self.getLastNode(objIni, xmlBody, strHeader,
                                              formatPlasma)
        if flagFound and xmlBody:
            if formatPlasma:
                strHeader = strHeader[-1]
            # если находим область то выдаем словарем все переменные иначе False
            res = objIni.docObj.getAreaFields(strHeader, xmlBody, allVars=True)
        else:
            res = False
        if res is False:
            return {}
        else:
            return res

    def getAllSectionNames(self):
        """Получаем все имена секций определенных в ini файле

        Если формат ini файла plasma то имя секции -
        имена нескольких секций через запятую
        """
        textIni = self.openIniFile()
        if self.FD:
            self.FD.close()
            self.FD = None
        nameFomat = self.checkIniFile(textIni)
        if not nameFomat:
            return False
        if nameFomat == "samba":
            # создаем объект типа samba и записываем в него содержимое ini-файла
            objIni = self.getClassObj("samba")(textIni)
        elif nameFomat == "plasma":
            # создаем объект типа plasma и записываем в него содержимое
            # ini-файла
            objIni = self.getClassObj("plasma")(textIni)
        else:
            return []
        xmlNodes = objIni.docObj.getAllAreas()
        # Имена секций ini файла
        namesSection = []
        if nameFomat == "plasma":
            for xmlNode in xmlNodes:
                nSect = objIni.docObj.getNameArea(xmlNode)
                if nSect:
                    namesSect = [nSect]
                    parentNode =  xmlNode.parentNode
                    while parentNode != objIni.docObj.body:
                        nameSect = objIni.docObj.getNameArea(parentNode)
                        if nameSect:
                            namesSect.append(nameSect)
                            parentNode =  parentNode.parentNode
                        else:
                            return []
                    namesSection.append(",".join(reversed(namesSect)))
        elif nameFomat == "samba":
            # получаем ноду body
            for xmlNode in xmlNodes:
                nSect = objIni.docObj.getNameArea(xmlNode)
                if nSect:
                    namesSection.append(nSect)
        return namesSection

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


class SystemIni(iniParser):
    inifile = '/etc/calculate/ini.env'
    def __init__(self):
        super(SystemIni, self).__init__(self.inifile)
