|
|
#-*- coding: utf-8 -*-
|
|
|
|
|
|
# Copyright 2008-2010 Mir 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.
|
|
|
|
|
|
import sys
|
|
|
import os
|
|
|
import stat
|
|
|
import re
|
|
|
import xml.dom.minidom
|
|
|
from xml import xpath
|
|
|
import subprocess
|
|
|
import types
|
|
|
import random
|
|
|
import string
|
|
|
from cl_utils import _error, scan, _toUNICODE
|
|
|
from cl_overriding import exit
|
|
|
import cl_lang
|
|
|
|
|
|
tr = cl_lang.lang()
|
|
|
tr.setLocalDomain('cl_lib')
|
|
|
tr.setLanguage(sys.modules[__name__])
|
|
|
|
|
|
class _terms(_error):
|
|
|
"""Вычисление условий применяемых в шаблонах
|
|
|
|
|
|
"""
|
|
|
def _convertVers(self, verA, verB):
|
|
|
"""Конвертирование номеров версий для корректного сравнения
|
|
|
"""
|
|
|
elemA = verA.split(".")
|
|
|
elemB = verB.split(".")
|
|
|
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]
|
|
|
return (".".join(elemA), ".".join(elemB))
|
|
|
|
|
|
def executeListEqual(self, listEqual):
|
|
|
"""Вычисляет список выражений
|
|
|
|
|
|
пример списка:
|
|
|
listEqual = [False, True, ' or ', True , True]
|
|
|
(если нет or между логическими выражениями то между ними and)
|
|
|
результат True
|
|
|
"""
|
|
|
lenOr = listEqual.count(" or ")
|
|
|
for i in xrange(lenOr):
|
|
|
ind = listEqual.index(' or ')
|
|
|
if False in listEqual[:ind]:
|
|
|
listEqual = listEqual[ind+1:]
|
|
|
continue
|
|
|
else:
|
|
|
return True
|
|
|
if False in listEqual:
|
|
|
return False
|
|
|
else:
|
|
|
return True
|
|
|
|
|
|
|
|
|
def _equalTerm(self, term, textError, function=False):
|
|
|
"""Вычисление логических выражений для условий
|
|
|
|
|
|
Для корректной работы в классе который наследует этот класс
|
|
|
должен быть объявлен аттрибут self.objVar
|
|
|
(объект для работы с переменными)
|
|
|
function - функция для для обработки функций в заголовке блока
|
|
|
"""
|
|
|
trm = {"&&":" and ","||":" or "}
|
|
|
rule = ["==", "!=", ">=", "<=", ">", "<"]
|
|
|
listEqual = []
|
|
|
for k in trm.keys():
|
|
|
if k in term:
|
|
|
term = term.replace(k,trm[k])
|
|
|
trs = term.split(" ")
|
|
|
for t in trs:
|
|
|
flagRule = False
|
|
|
for sepF in rule:
|
|
|
if sepF in t:
|
|
|
flagRule = True
|
|
|
vals = t.split(sepF)
|
|
|
break
|
|
|
|
|
|
if not flagRule:
|
|
|
flagLog = False
|
|
|
for k in trm.values():
|
|
|
if k.strip() == t:
|
|
|
flagLog = True
|
|
|
break
|
|
|
if not flagLog:
|
|
|
self.setError("'%s'"%term + " " + _("incorrect"))
|
|
|
self.setError (textError)
|
|
|
return False
|
|
|
elif k == " or ":
|
|
|
listEqual.append(k)
|
|
|
else:
|
|
|
#проверка на допустимость названия переменной
|
|
|
reDenyName = re.compile("[^a-zA-Z0-9\_\-]")
|
|
|
flagFunction = False
|
|
|
if reDenyName.search(vals[0]):
|
|
|
#проверка на допустимость функции
|
|
|
flagError = True
|
|
|
if function:
|
|
|
reFunction = re.compile(\
|
|
|
"([a-zA-Z0-9\_\-]+)\([a-zA-Z0-9_\-\+\,\*\/\.]+\)")
|
|
|
searchFunct = reFunction.search(vals[0])
|
|
|
if searchFunct:
|
|
|
flagError = False
|
|
|
flagFunction = True
|
|
|
if flagError:
|
|
|
self.setError("'%s'"%term + " " + _("incorrect"))
|
|
|
self.setError(textError)
|
|
|
return False
|
|
|
#проверка на допустимость значения
|
|
|
reDenyValue = re.compile("[^0-9a-zA-Z_\.-]")
|
|
|
if reDenyValue.search(vals[1]):
|
|
|
self.setError("'%s'"%term + " " + _("incorrect"))
|
|
|
self.setError(textError)
|
|
|
return False
|
|
|
flagIntTypeVar = None
|
|
|
if flagFunction:
|
|
|
valVars = function("#-%s-#"%vals[0])
|
|
|
if valVars == "":
|
|
|
flagFunction = False
|
|
|
if valVars == False:
|
|
|
self.setError("'%s'"%term + " " + _("incorrect"))
|
|
|
self.setError(textError)
|
|
|
return False
|
|
|
if "load" == searchFunct.group(1):
|
|
|
if re.search("\(\s*num\s*,",vals[0]):
|
|
|
if valVars:
|
|
|
try:
|
|
|
valVars = int(valVars)
|
|
|
except:
|
|
|
self.setError("'%s'"%term + " " + \
|
|
|
_("incorrect"))
|
|
|
self.setError (textError)
|
|
|
return False
|
|
|
flagIntTypeVar = True
|
|
|
else:
|
|
|
flagIntTypeVar = False
|
|
|
else:
|
|
|
try:
|
|
|
valVars = self.objVar.Get(vals[0])
|
|
|
except self.objVar.DataVarsError, e:
|
|
|
print textError
|
|
|
print e
|
|
|
exit(1)
|
|
|
# Cравниваем номера версий
|
|
|
if "_ver" in vals[0] or \
|
|
|
(flagFunction and "pkg" == searchFunct.group(1)) or\
|
|
|
(flagFunction and "load" == searchFunct.group(1) and\
|
|
|
re.search("\(\s*ver\s*,",vals[0])):
|
|
|
verFile, verVar = self._convertVers(vals[1],valVars)
|
|
|
exec("res=("+"'"+verVar+"'"+sepF+"'"+verFile+"'"+")")
|
|
|
if res:
|
|
|
listEqual.append(True)
|
|
|
else:
|
|
|
listEqual.append(False)
|
|
|
else:
|
|
|
if flagIntTypeVar == 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])
|
|
|
except:
|
|
|
self.setError("'%s'"%term + " " + _("incorrect"))
|
|
|
self.setError (textError)
|
|
|
return False
|
|
|
valVar = valVars
|
|
|
exec("res=(%d%s%d)"%(valVar,sepF,valFile))
|
|
|
if res:
|
|
|
listEqual.append(True)
|
|
|
else:
|
|
|
listEqual.append(False)
|
|
|
else:
|
|
|
if sepF == "!=" or sepF == "==":
|
|
|
if not vals[1].strip():
|
|
|
vals[1] = ""
|
|
|
valFile = vals[1]
|
|
|
valVar = valVars
|
|
|
exec("res=("+'"""'+valVar+'"""'+sepF+"'"+valFile+\
|
|
|
"'"+")")
|
|
|
if res:
|
|
|
listEqual.append(True)
|
|
|
else:
|
|
|
listEqual.append(False)
|
|
|
else:
|
|
|
if valVars == "":
|
|
|
listEqual.append(False)
|
|
|
else:
|
|
|
self.setError("'%s'"%term + " "\
|
|
|
+ _("incorrect"))
|
|
|
self.setError (textError)
|
|
|
return False
|
|
|
#exec("res=(%s)"%("".join(listEqual)))
|
|
|
res = self.executeListEqual(listEqual)
|
|
|
return res
|
|
|
|
|
|
|
|
|
class shareHeader:
|
|
|
"""Общие методы для обработки заголовков"""
|
|
|
reSplParHeader = re.compile("\s+",re.I)
|
|
|
reHeader=re.compile(r"\A\s*#\s*calculate(\s+)?\\?([^\\\n]*\\\n)+[^\\\n]*\n?\
|
|
|
|\s*#\s*calculate\s+([^\\\n]*\n?)",re.I|re.M)
|
|
|
|
|
|
def getHeader(self, text):
|
|
|
"""Получаем результат поиска и заголовок файла"""
|
|
|
sHeader = self.reHeader.search(text)
|
|
|
if sHeader:
|
|
|
return (sHeader, sHeader.group())
|
|
|
else:
|
|
|
return (False, "")
|
|
|
|
|
|
def getParamsHeader(self, textHeader):
|
|
|
"""Получаем параметры заголовка в виде списка"""
|
|
|
listParams = self.reSplParHeader.split(textHeader.replace("\\"," "))
|
|
|
if listParams[0] == "#":
|
|
|
return filter(lambda x: x, listParams[2:])
|
|
|
else:
|
|
|
return filter(lambda x: x, listParams[1:])
|
|
|
|
|
|
class fileHeader(shareHeader, _terms):
|
|
|
"""Обработка заголовков шаблонов и конфигурационных файлов
|
|
|
|
|
|
"""
|
|
|
# Допустимые параметры заголовка
|
|
|
allowParam = ("format", "comment", "append", "force", "link", "mirror",
|
|
|
"symbolic", "chmod", "chown", "path", "name")
|
|
|
|
|
|
# параметры без значения
|
|
|
listParNotVal = ("symbolic", "force", "mirror")
|
|
|
|
|
|
# Возможные типы вставки шаблонов
|
|
|
_fileAppend = ("join", "before", "after", "replace", "remove", "skip")
|
|
|
|
|
|
|
|
|
# условные операторы
|
|
|
terms = ('>', '<', '==', '!=', '>=', '<=')
|
|
|
|
|
|
# параметры без значения
|
|
|
#listParNotVal = ("symbolic", "force", "mirror")
|
|
|
# Форматы файлов для которых метод объединения replace если он не задан
|
|
|
#replaceFormats = ("raw","bin")
|
|
|
|
|
|
def delHeaderConfFile(self, text, comment):
|
|
|
"""Удаляет заголовок в тексте конфигурационного файла"""
|
|
|
if comment and text:
|
|
|
# Удаление Заголовка Calculate в конфигурационном файле
|
|
|
# В случае текста XML
|
|
|
if type(comment) == types.TupleType and len(comment) == 2:
|
|
|
_titleList = (_("Modified"), _("File of a template"))
|
|
|
reCalcHeader =\
|
|
|
re.compile(u"\s*%s\s+%s.+Calculate.+\s+%s.+\s+%s\s?"%(\
|
|
|
comment[0],
|
|
|
_titleList[0].decode("UTF-8"),
|
|
|
_titleList[1].decode("UTF-8"),
|
|
|
comment[1],
|
|
|
),
|
|
|
re.M|re.I|re.U)
|
|
|
textUnicode = text.decode("UTF-8")
|
|
|
reS = reCalcHeader.search(textUnicode)
|
|
|
if reS:
|
|
|
textBody = textUnicode[:reS.start()]+textUnicode[reS.end():]
|
|
|
if textBody:
|
|
|
return textBody.encode("UTF-8")
|
|
|
else:
|
|
|
# В остальных случаях
|
|
|
reCalcHeader =\
|
|
|
re.compile("\s*%s\-+\s+%s.+Calculate.+\s+%s.+\s+%s\-+\s?"%(\
|
|
|
comment,
|
|
|
comment,
|
|
|
comment,
|
|
|
comment,
|
|
|
),
|
|
|
re.M|re.I)
|
|
|
reS = reCalcHeader.search(text)
|
|
|
if reS:
|
|
|
return text[reS.end():]
|
|
|
return text
|
|
|
|
|
|
def getPropertyTemplate(self, textHeader, foundHeader, fileType, objVar,
|
|
|
function, fileName):
|
|
|
"""Получаем свойства шаблона из текста заголовка шаблона"""
|
|
|
# Объект с переменными
|
|
|
self.objVar=objVar
|
|
|
# Параметры файла шаблона
|
|
|
params = {}
|
|
|
# Будет ли шаблон применен
|
|
|
headerTerm = True
|
|
|
# Бинарный шаблон
|
|
|
if fileType=="bin":
|
|
|
params["format"] = fileType
|
|
|
params["_position"] = 0
|
|
|
params["append"] = "replace"
|
|
|
params["_apply"] = headerTerm
|
|
|
# текстовый шаблон с заголовком
|
|
|
elif foundHeader:
|
|
|
# некорректные параметры
|
|
|
incorrectParams = set([])
|
|
|
# Получаем список параметров шаблона
|
|
|
paramList = self.getParamsHeader(textHeader)
|
|
|
if paramList:
|
|
|
errTerm = _("header template '%s' not valid")%fileName
|
|
|
for i in paramList:
|
|
|
foundTerm = False
|
|
|
for term in self.terms:
|
|
|
if term in i:
|
|
|
foundTerm = True
|
|
|
rezTerm = self._equalTerm(i, "%s: %s"%(errTerm,i),
|
|
|
function)
|
|
|
if not rezTerm:
|
|
|
headerTerm = False
|
|
|
break
|
|
|
if not foundTerm:
|
|
|
par = i.split("=")
|
|
|
if len(par) == 1:
|
|
|
ret = self.checkParams(i, None, params)
|
|
|
if not ret:
|
|
|
incorrectParams = set([i])
|
|
|
break
|
|
|
elif len(par) == 2:
|
|
|
ret = self.checkParams(par[0], par[1], params)
|
|
|
if not ret:
|
|
|
incorrectParams = set([i])
|
|
|
break
|
|
|
if not "format" in params:
|
|
|
# format - raw
|
|
|
params["format"] = "raw"
|
|
|
if not "append" in params:
|
|
|
params["append"] = "replace"
|
|
|
else:
|
|
|
if not "append" in params:
|
|
|
# в зависимости от формата - join или replace
|
|
|
formatTemplate = params["format"]
|
|
|
if formatTemplate in ("raw", "bin", ""):
|
|
|
params["append"] = "replace"
|
|
|
else:
|
|
|
params["append"] = "join"
|
|
|
if incorrectParams:
|
|
|
headerTerm = False
|
|
|
self.setError(_("incorrect header parameters - '%s'")\
|
|
|
%" ".join(list(incorrectParams)))
|
|
|
params["_position"] = foundHeader.end()
|
|
|
params["_apply"] = headerTerm
|
|
|
# текстовый шаблон без заголовка
|
|
|
else:
|
|
|
params["format"] = "raw"
|
|
|
params["_position"] = 0
|
|
|
params["append"] = "replace"
|
|
|
params["_apply"] = headerTerm
|
|
|
return params
|
|
|
|
|
|
def checkParams(self, name, value, dictPar):
|
|
|
"""Проверка параметра заголовка, при успехе запись в словарь dictPar"""
|
|
|
# Проверка на допустимые параметры заголовка
|
|
|
if not name in self.allowParam:
|
|
|
return False
|
|
|
if name in self.listParNotVal and not value is None:
|
|
|
return False
|
|
|
if name == "append":
|
|
|
if not value in self._fileAppend:
|
|
|
return False
|
|
|
dictPar[name] = value
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
class dirHeader(shareHeader, _terms):
|
|
|
"""Обработка заголовков шаблонов директорий
|
|
|
|
|
|
"""
|
|
|
# Допустимые параметры заголовка
|
|
|
allowParam = ("append", "chmod", "chown", "path", "name")
|
|
|
# Возможные типы вставки шаблонов
|
|
|
_fileAppend = ("join", "remove", "skip")
|
|
|
# условные операторы
|
|
|
terms = ('>', '<', '==', '!=', '>=', '<=')
|
|
|
|
|
|
|
|
|
def getPropertyTemplate(self, text, objVar, function, fileName):
|
|
|
"""Получаем свойства шаблона из текста шаблона"""
|
|
|
# Объект с переменными
|
|
|
self.objVar=objVar
|
|
|
# Параметры описанные в заголовке файла шаблона
|
|
|
params = {}
|
|
|
# Некорректные параметры
|
|
|
incorrectParams = set([])
|
|
|
# Будет ли шаблон применен
|
|
|
headerTerm = True
|
|
|
foundHeader, textHeader = self.getHeader(text)
|
|
|
if foundHeader:
|
|
|
paramList = self.getParamsHeader(textHeader)
|
|
|
if paramList:
|
|
|
errTerm = _("header template '%s' not valid")%fileName
|
|
|
for i in paramList:
|
|
|
foundTerm = False
|
|
|
for term in self.terms:
|
|
|
if term in i:
|
|
|
foundTerm = True
|
|
|
rezTerm = self._equalTerm(i, "%s: %s"%(errTerm,i),
|
|
|
function)
|
|
|
if not rezTerm:
|
|
|
headerTerm = False
|
|
|
break
|
|
|
if not foundTerm:
|
|
|
par = i.split("=")
|
|
|
if len(par) == 1:
|
|
|
ret = self.checkParams(i, None, params)
|
|
|
if not ret:
|
|
|
incorrectParams = set([i])
|
|
|
break
|
|
|
elif len(par) == 2:
|
|
|
ret = self.checkParams(par[0], par[1], params)
|
|
|
if not ret:
|
|
|
incorrectParams = set([i])
|
|
|
break
|
|
|
if not "append" in params:
|
|
|
# По умолчанию join
|
|
|
params["append"] = "join"
|
|
|
if incorrectParams:
|
|
|
headerTerm = False
|
|
|
self.setError(_("incorrect header parameters - '%s'")\
|
|
|
%" ".join(list(incorrectParams)))
|
|
|
params["_apply"] = headerTerm
|
|
|
# текстовый шаблон без заголовка
|
|
|
else:
|
|
|
headerTerm = False
|
|
|
self.setError(_("Can not found header in template"))
|
|
|
params["_apply"] = headerTerm
|
|
|
return params
|
|
|
|
|
|
def checkParams(self, name, value, dictPar):
|
|
|
"""Проверка параметра заголовка, при успехе запись в словарь dictPar"""
|
|
|
# Проверка на допустимые параметры заголовка
|
|
|
if not name in self.allowParam:
|
|
|
return False
|
|
|
if name == "append":
|
|
|
if not value in self._fileAppend:
|
|
|
return False
|
|
|
if value is None:
|
|
|
return False
|
|
|
dictPar[name] = value
|
|
|
return True
|
|
|
|
|
|
|
|
|
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):
|
|
|
"""Выдает конфигурационный файл"""
|
|
|
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 "".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 = xml.dom.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
|
|
|
#print parentNode.toprettyxml()
|
|
|
if parentNode:
|
|
|
fieldsVal = xpath.Evaluate(\
|
|
|
"child::field[attribute::type='seplist'][child::name='%s'] "\
|
|
|
%(nameField), parentNode)
|
|
|
#print nameField
|
|
|
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)
|
|
|
#else:
|
|
|
#self.setActionField(node, "append")
|
|
|
#baseBody.appendChild(node)
|
|
|
for node in notSepListField:
|
|
|
if self.getTypeField(node) == "seplist":
|
|
|
#if removeNodesDict.has_key(self.getNameField(node)):
|
|
|
#print removeNodesDict[self.getNameField(node)]
|
|
|
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):
|
|
|
"""По имени области выводит названия и значения всех переменных
|
|
|
|
|
|
поиск ведется только в 1-х потомках области xmlArea
|
|
|
на выход словарь переменных {имя:значение}
|
|
|
"""
|
|
|
namesAreaComare = xpath.Evaluate(\
|
|
|
"child::area/child::caption[child::name='%s']" %(nameArea),
|
|
|
xmlArea)
|
|
|
if not namesAreaComare:
|
|
|
return False
|
|
|
fields = xpath.Evaluate("child::field/child::name",
|
|
|
namesAreaComare[0].parentNode)
|
|
|
dictVar = {}
|
|
|
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
|
|
|
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])
|
|
|
return fieldValue[0].firstChild.nodeValue
|
|
|
|
|
|
|
|
|
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.split(r.group(0))
|
|
|
blocTxt = txtSpl[0]
|
|
|
txtWr = txtSpl[1]
|
|
|
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.split(r.group(0))
|
|
|
blocTxt = txtSpl[0]
|
|
|
txtWr = txtSpl[1]
|
|
|
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, scan):
|
|
|
"""Класс для работы с файлами
|
|
|
|
|
|
"""
|
|
|
|
|
|
def __init__(self):
|
|
|
# Имя файла старого шаблона
|
|
|
self.nameFileOld = ""
|
|
|
# Старый шаблон
|
|
|
self.oldTemplate = ""
|
|
|
# Имя файла нового шаблона
|
|
|
self.nameFileNew = ""
|
|
|
# Новый шаблон
|
|
|
self.newTemplate = ""
|
|
|
# Дескриптор файла нового шаблона
|
|
|
self.FN = False
|
|
|
# Дескриптор файла старого шаблона
|
|
|
self.FO = False
|
|
|
|
|
|
def getFileType(self, fileName=""):
|
|
|
"""выдать тип файла (text, bin)
|
|
|
"""
|
|
|
if fileName:
|
|
|
nameFile = fileName
|
|
|
elif self.nameFileNew:
|
|
|
nameFile = self.nameFileNew
|
|
|
else:
|
|
|
return ""
|
|
|
sp = subprocess.Popen("file '%s'"%nameFile, stdin=subprocess.PIPE,
|
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
|
|
close_fds=True, shell=True)
|
|
|
fout, fin, ferr = (sp.stdout, sp.stdin, sp.stderr)
|
|
|
fin.close()
|
|
|
textLine = fout.readline()
|
|
|
fout.readlines()
|
|
|
fout.close()
|
|
|
retText = ""
|
|
|
if textLine:
|
|
|
listTextLine = textLine.split(":")
|
|
|
if len(listTextLine) == 2:
|
|
|
textFormats = ["text", "XML"]
|
|
|
retText = "bin"
|
|
|
fileTypeString = listTextLine[1]
|
|
|
for textFormat in textFormats:
|
|
|
if textFormat in fileTypeString:
|
|
|
retText = "text"
|
|
|
break
|
|
|
ferr.close()
|
|
|
return retText
|
|
|
|
|
|
def absFileName(self, nameFile):
|
|
|
"""Вычисление пути к файлу"""
|
|
|
pathList = nameFile.split("/")
|
|
|
chortNameFile = pathList.pop()
|
|
|
absPath = os.path.abspath("/".join(pathList))
|
|
|
File = absPath + "/" + chortNameFile
|
|
|
return File
|
|
|
|
|
|
def saveOldFile(self):
|
|
|
"""Записать конфигурацию"""
|
|
|
if self.FO:
|
|
|
self.FO.truncate(0)
|
|
|
self.FO.seek(0)
|
|
|
if not self.oldTemplate:
|
|
|
self.oldTemplate = self.newTemplate
|
|
|
try:
|
|
|
self.FO.write(self.oldTemplate)
|
|
|
except:
|
|
|
self.setError (_("not open file:" ) + self.nameFileOld)
|
|
|
return False
|
|
|
return True
|
|
|
|
|
|
def getModeFile(self, nameFile):
|
|
|
"""Выдает информацию о файле
|
|
|
|
|
|
права файла, владелец, группа файла (777,test, group)
|
|
|
"""
|
|
|
fd = os.open(nameFile, os.O_RDONLY)
|
|
|
fst = os.fstat(fd)
|
|
|
uid = fst.st_uid
|
|
|
gid = fst.st_gid
|
|
|
mode = stat.S_IMODE(fst.st_mode)
|
|
|
os.close(fd)
|
|
|
return (mode,uid,gid)
|
|
|
|
|
|
def openNewFile(self, nameFileNew):
|
|
|
"""Открыть файл шаблона"""
|
|
|
try:
|
|
|
FN = open (nameFileNew, "r")
|
|
|
except:
|
|
|
self.setError (_("not open file:" ) + nameFileNew)
|
|
|
return False
|
|
|
return FN
|
|
|
|
|
|
def closeNewFile(self):
|
|
|
if self.FN:
|
|
|
self.FN.close()
|
|
|
self.FN = False
|
|
|
|
|
|
def __closeOldFile(self):
|
|
|
if self.FO:
|
|
|
self.FO.close()
|
|
|
self.FO = False
|
|
|
|
|
|
def __openOldFile(self, nameFileOld, funcCreateFile, removeLink):
|
|
|
"""Открыть конфигурационный файл"""
|
|
|
FO = False
|
|
|
try:
|
|
|
if os.path.islink(nameFileOld) and removeLink:
|
|
|
# если ссылка то удаляем её
|
|
|
os.unlink(nameFileOld)
|
|
|
FO = open (nameFileOld, "r+")
|
|
|
except:
|
|
|
#try:
|
|
|
# Создаем новый файл
|
|
|
funcCreateFile.__call__(nameFileOld)
|
|
|
FO = open(nameFileOld, "r+")
|
|
|
#except:
|
|
|
#self.setError (_("not open file:" ) + nameFileOld)
|
|
|
#return False
|
|
|
return FO
|
|
|
|
|
|
|
|
|
def openFiles(self, nameFileNew, nameFileOld, funcCreateFile, seekFileNew,
|
|
|
removeLink):
|
|
|
"""Открывает шаблон и конфигурационный файл"""
|
|
|
self.oldTemplate = ""
|
|
|
self.newTemplate = ""
|
|
|
self.closeFiles()
|
|
|
self.nameFileOld = self.absFileName(nameFileOld)
|
|
|
self.nameFileNew = self.absFileName(nameFileNew)
|
|
|
self.FN = self.openNewFile(self.nameFileNew)
|
|
|
self.FO = self.__openOldFile(self.nameFileOld,funcCreateFile,removeLink)
|
|
|
if not (self.FN and self.FO):
|
|
|
return False
|
|
|
self.FN.seek(seekFileNew)
|
|
|
self.newTemplate = self.FN.read()
|
|
|
self.oldTemplate = self.FO.read()
|
|
|
return True
|
|
|
|
|
|
def __del__(self):
|
|
|
self.closeFiles()
|
|
|
|
|
|
def closeFiles(self):
|
|
|
"""Закрытие файлов"""
|
|
|
self.closeNewFile()
|
|
|
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
|
|
|
i += 1
|
|
|
if lenTail>1 and lenTail != i:
|
|
|
return (False,1)
|
|
|
if i > 0:
|
|
|
result = True
|
|
|
return (result, i)
|
|
|
|
|
|
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:(ind+lenUtf[ind])],lenUtf[ind])
|
|
|
if res == 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)
|
|
|
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 += chr(int(hexCode, 16))
|
|
|
hexCode = ""
|
|
|
i = 0
|
|
|
else:
|
|
|
hexCode += ch
|
|
|
i += 1
|
|
|
textTemplateTmp = textTemplateTmp.replace(mark, stringInsert)
|
|
|
resS = reVar.search(textTemplateTmp)
|
|
|
return textTemplateTmp
|
|
|
|
|
|
class template(_file, _terms, xmlShare):
|
|
|
"""Класс для работы с шаблонами
|
|
|
|
|
|
На вход 2 параметра: объект хранения переменных, имя сервиса - не
|
|
|
обязательный параметр
|
|
|
|
|
|
"""
|
|
|
# Импортированные классы поддерживаемых форматов шаблонов
|
|
|
importFormats = {}
|
|
|
# Имена установленных программ
|
|
|
installProg = []
|
|
|
# Версии установленных программ
|
|
|
installProgVersions = []
|
|
|
# кеш вызванных значений программа, номер версии
|
|
|
cacheInstallProg = {}
|
|
|
# Название файла шаблона директории
|
|
|
templDirNameFile = ".calculate_directory"
|
|
|
|
|
|
def __init__(self, objVar, dirsFilter=[], filesFilter=[]):
|
|
|
# Необрабатываемые директории
|
|
|
self.dirsFilter = dirsFilter
|
|
|
# Необрабатываемые файлы
|
|
|
self.filesFilter = filesFilter
|
|
|
_file.__init__(self)
|
|
|
# Словарь для создания объектов новых классов по образцу
|
|
|
# (proftpd создается на основе apache)
|
|
|
self.newObjProt = {'proftpd':'apache'}
|
|
|
# Заголовок title
|
|
|
self.__titleHead = "--------------------------------------\
|
|
|
----------------------------------------"
|
|
|
self._titleBody = ""
|
|
|
self._titleList = (_("Modified"), _("File of a template"))
|
|
|
|
|
|
# Метки
|
|
|
varStart = "#-"
|
|
|
varEnd = "-#"
|
|
|
self._reVar = re.compile(("%s[a-zA-Z0-9_-]+%s")%(varStart,varEnd),re.M)
|
|
|
self._reFunc = re.compile(("%s[a-zA-Z0-9_\-\+\(\)\, \*\/\.]+%s")\
|
|
|
%(varStart,varEnd),re.M)
|
|
|
self._deltVarStart = len(varStart)
|
|
|
self._deltVarEnd = len(varEnd)
|
|
|
# Условия
|
|
|
self._reTermBloc = re.compile("#\?(?P<rTerm>[a-zA-Z0-9\-_]+)\
|
|
|
(?P<func>\([a-zA-Z0-9_\-\+\,\*\/\.]+\))?\
|
|
|
(?P<lTerm>[\>\<\=\!\&\|]+\
|
|
|
[a-zA-Z0-9\>\<\=\!\|\&\-\+\*\/_\.\,\(\)]*)#\
|
|
|
\n*(?P<body>.+?)\n*#(?P=rTerm)#(?P<end>[ ,\t]*\n?)",re.M|re.S)
|
|
|
# Объект с переменными
|
|
|
self.objVar = objVar
|
|
|
# Базовая директория переноса шаблонов "/mnt/calculate" или "/" и.т.д
|
|
|
baseDir = self.objVar.Get("cl_root_path")
|
|
|
#self._baseDir = os.path.split(baseDir)[0]
|
|
|
self._baseDir = baseDir
|
|
|
if self._baseDir == "/":
|
|
|
self._baseDir = ""
|
|
|
|
|
|
def __octToInt(self, strOct):
|
|
|
"""Преобразование восьмеричного в целое (ввод строка, вывод число)"""
|
|
|
if strOct:
|
|
|
z = 0
|
|
|
i = 0
|
|
|
lst = list(strOct)
|
|
|
lst.reverse()
|
|
|
for ch in lst:
|
|
|
try:
|
|
|
v = int(ch)
|
|
|
except:
|
|
|
self.setError(_("Not valid oct value: ") + str(strOct))
|
|
|
return False
|
|
|
if v > 7:
|
|
|
self.setError (_("Not valid oct value: ") + str(strOct))
|
|
|
return False
|
|
|
z = z + v*8**i
|
|
|
i = i +1
|
|
|
return z
|
|
|
else:
|
|
|
self.setError (_("Empty oct value"))
|
|
|
return False
|
|
|
|
|
|
def getFormatObj(self, formatTemplate, textTemplate):
|
|
|
"""Создание объекта формата шаблона.
|
|
|
|
|
|
Объект создается на основании формата шаблона и текста шаблона"""
|
|
|
if formatTemplate in self.importFormats:
|
|
|
classFormat = self.importFormats[formatTemplate]
|
|
|
else:
|
|
|
try:
|
|
|
classFormat = getattr(__import__("format.%s"%formatTemplate,
|
|
|
globals(), locals(),
|
|
|
[formatTemplate]),
|
|
|
formatTemplate)
|
|
|
except (ImportError, AttributeError):
|
|
|
#Создаем объект из self.newObjProt с помощью
|
|
|
# метаклассов
|
|
|
if formatTemplate in self.newObjProt:
|
|
|
# Прототип класса
|
|
|
nameProt = self.newObjProt[formatTemplate]
|
|
|
try:
|
|
|
classProt = getattr(__import__("format.%s"%nameProt,
|
|
|
globals(), locals(),
|
|
|
[nameProt]),
|
|
|
nameProt)
|
|
|
except (ImportError, AttributeError):
|
|
|
return False
|
|
|
newCl = self.createNewClass(formatTemplate, (classProt,))
|
|
|
self.importFormats[formatTemplate] = newCl
|
|
|
return newCl(textTemplate)
|
|
|
else:
|
|
|
return False
|
|
|
self.importFormats[formatTemplate] = classFormat
|
|
|
return classFormat(textTemplate)
|
|
|
|
|
|
def removeDir(self, rmDir):
|
|
|
"""Рекурсивное удаление директории
|
|
|
|
|
|
входной параметр директория
|
|
|
"""
|
|
|
if not os.path.exists(rmDir):
|
|
|
self.printERROR(_("Not found remove dir %s") %rmDir)
|
|
|
return False
|
|
|
fileObj = _file()
|
|
|
# Сканируем директорию
|
|
|
scanObjs = fileObj.scanDirs([rmDir])
|
|
|
for socketRm in scanObjs[0].sockets:
|
|
|
# Удаляем сокеты
|
|
|
if os.path.exists(socketRm):
|
|
|
os.remove(socketRm)
|
|
|
for linkRm in scanObjs[0].links:
|
|
|
# Удаляем ссылки
|
|
|
os.unlink(linkRm[1])
|
|
|
for fileRm in scanObjs[0].files:
|
|
|
# Удаляем файлы
|
|
|
os.remove(fileRm)
|
|
|
scanObjs[0].dirs.sort(lambda x, y: cmp(len(y), len(x)))
|
|
|
for dirRm in scanObjs[0].dirs:
|
|
|
# Удаляем директории
|
|
|
os.rmdir(dirRm)
|
|
|
if rmDir:
|
|
|
os.rmdir(rmDir)
|
|
|
return True
|
|
|
|
|
|
|
|
|
def createConfFile(self, pathTemplate, path=False):
|
|
|
"""Создает конфигурационный файл если его нет"""
|
|
|
# Создаем директорию если ее нет
|
|
|
if not path:
|
|
|
path = os.path.split(pathTemplate)[0]
|
|
|
if not os.path.exists(path):
|
|
|
createDirs = self.createDir(path)
|
|
|
if not createDirs:
|
|
|
self.setError (_("Can not create file:" ) + pathTemplate)
|
|
|
return False
|
|
|
# Создаем файл если его нет
|
|
|
if not os.path.exists(pathTemplate):
|
|
|
try:
|
|
|
dMode, uid, gid = self.getModeFile(path)
|
|
|
#mode = dMode & ~0111
|
|
|
# Создаем файл
|
|
|
open(pathTemplate,"w").close()
|
|
|
#os.chmod(pathTemplate, mode)
|
|
|
os.chown(pathTemplate, uid, gid)
|
|
|
except:
|
|
|
self.setError (_("Can not create file:" ) + pathTemplate)
|
|
|
return False
|
|
|
return True
|
|
|
|
|
|
def createDir(self, dirName, mode=False, uid=False, gid=False):
|
|
|
"""Создает директорию"""
|
|
|
if os.access(dirName, os.F_OK):
|
|
|
return [dirName]
|
|
|
else:
|
|
|
dMode = False
|
|
|
prevDir, tmpSubdir = os.path.split(dirName)
|
|
|
createDirs = []
|
|
|
while not os.access(prevDir, os.F_OK):
|
|
|
createDirs.append(prevDir)
|
|
|
prevDir = os.path.split(prevDir)[0]
|
|
|
try:
|
|
|
tmpMode,dUid,dGid = self.getModeFile(prevDir)
|
|
|
except OSError:
|
|
|
self.setError (_("Not access dir: " ) + 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)
|
|
|
else:
|
|
|
os.mkdir(nameDir)
|
|
|
os.chown(nameDir, dUid, dGid)
|
|
|
except:
|
|
|
self.setError (_("Can not create dir: " ) + nameDir)
|
|
|
return False
|
|
|
try:
|
|
|
if dMode:
|
|
|
os.mkdir(dirName, dMode)
|
|
|
else:
|
|
|
os.mkdir(dirName)
|
|
|
os.chown(dirName, dUid, dGid)
|
|
|
createDirs.append(dirName)
|
|
|
except:
|
|
|
self.setError (_("Can not create dir: " ) + 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 = str(self.objVar.Get(varName))
|
|
|
except self.objVar.DataVarsError, e:
|
|
|
print _("error in template %s")%nameTemplate
|
|
|
print e
|
|
|
exit(1)
|
|
|
textTemplateTmp = textTemplateTmp.replace(mark, varValue)
|
|
|
resS = self._reVar.search(textTemplateTmp)
|
|
|
return textTemplateTmp
|
|
|
|
|
|
|
|
|
def applyFuncTemplate(self, textTemplate, nameTemplate):
|
|
|
""" Применяет функции к тексту шаблона
|
|
|
"""
|
|
|
def equalTerm(term, sNum, sMD, localVars):
|
|
|
"""Локальная функция для вычисления выражения"""
|
|
|
terms = sNum.findall(term)
|
|
|
if terms:
|
|
|
strNumers = []
|
|
|
for n in terms:
|
|
|
strNum = n.strip()
|
|
|
if "*" in strNum or "/" in strNum:
|
|
|
strNum = multAndDiv(strNum,sNum,sMD,localVars)
|
|
|
try:
|
|
|
num = int(strNum)
|
|
|
except:
|
|
|
minus = False
|
|
|
if strNum[:1] == "-":
|
|
|
minus = True
|
|
|
strNum = strNum[1:]
|
|
|
if localVars.has_key(strNum):
|
|
|
num = localVars[strNum]
|
|
|
elif self.objVar.exists(strNum):
|
|
|
try:
|
|
|
num = int(self.objVar.Get(strNum))
|
|
|
except:
|
|
|
print _("error in template %s")%nameTemplate
|
|
|
print _("error var %s not int")%str(strNum)
|
|
|
exit(1)
|
|
|
else:
|
|
|
print _("error in template %s")%nameTemplate
|
|
|
print _("error local var %s not defined")\
|
|
|
%str(strNum)
|
|
|
exit(1)
|
|
|
if minus:
|
|
|
num =-num
|
|
|
strNumers.append(num)
|
|
|
return sum(strNumers)
|
|
|
print _("error in template %s")%nameTemplate
|
|
|
print _("error template term %s, incorrect data")%str(term)
|
|
|
exit(1)
|
|
|
|
|
|
def multAndDiv(term,sNum,sMD,localVars):
|
|
|
"""локальная функция для умножения и деления"""
|
|
|
termTmp = term
|
|
|
varsLocal = sMD.findall(term)
|
|
|
for var in varsLocal:
|
|
|
flagVarTxt = True
|
|
|
try:
|
|
|
int(var)
|
|
|
except:
|
|
|
flagVarTxt = False
|
|
|
if flagVarTxt:
|
|
|
continue
|
|
|
varReplace = str(equalTerm(var,sNum,sMD,localVars))
|
|
|
termTmp = termTmp.replace(var,varReplace)
|
|
|
ret = eval(termTmp)
|
|
|
return ret
|
|
|
|
|
|
def funcSum(funTxt,resS,localVars,textTemplateTmp):
|
|
|
"""локальная функция вычисляет первую функцию sum() в шаблоне"""
|
|
|
terms = funTxt[4:-1].replace(" ","").split(",")
|
|
|
# Название локальной переменной
|
|
|
nameLocVar = terms[0]
|
|
|
if not localVars.has_key(nameLocVar):
|
|
|
localVars[nameLocVar] = 0
|
|
|
if len(terms) == 2:
|
|
|
if terms[1].strip():
|
|
|
localVars[nameLocVar] = equalTerm(terms[1],sNum,sMD,
|
|
|
localVars)
|
|
|
replace = str(localVars[nameLocVar])
|
|
|
else:
|
|
|
replace = ""
|
|
|
textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\
|
|
|
textTemplateTmp[resS.end():]
|
|
|
elif len(terms) == 3:
|
|
|
if terms[1].strip():
|
|
|
replaceInt = equalTerm(terms[1],sNum,sMD,localVars)
|
|
|
replace = str(replaceInt)
|
|
|
else:
|
|
|
replace = ""
|
|
|
localVars[nameLocVar] = equalTerm(terms[2],
|
|
|
sNum,sMD,localVars)
|
|
|
textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\
|
|
|
textTemplateTmp[resS.end():]
|
|
|
else:
|
|
|
print _("error in template %s")%nameTemplate
|
|
|
print _("error template term %s")%str(funTxt)
|
|
|
exit(1)
|
|
|
return textTemplateTmp
|
|
|
|
|
|
def funcLoad(funTxt,resS,textTemplateTmp):
|
|
|
"""если файл существует читает из файла локальную переменную
|
|
|
|
|
|
если один параметр - выводит значение локальной переменной
|
|
|
"""
|
|
|
terms = funTxt[5:-1].replace(" ","").split(",")
|
|
|
if not terms[0].strip() or\
|
|
|
(len(terms)==2 and not terms[1].strip()) or\
|
|
|
len(terms)>2:
|
|
|
print _("error in template %s")%nameTemplate
|
|
|
print _("error template term %s")%str(funTxt)
|
|
|
exit(1)
|
|
|
if len(terms) == 2:
|
|
|
if not terms[0] in ["ver","num","char","key"]:
|
|
|
print _("error in template %s")%nameTemplate
|
|
|
print _("error template term %s")%str(funTxt)
|
|
|
print _("first argument function is not 'ver' or 'num' or\
|
|
|
'char'")
|
|
|
exit(1)
|
|
|
if len(terms) == 1:
|
|
|
fileName = terms[0].strip()
|
|
|
if fileName[0] != "/":
|
|
|
print _("error in template %s")%nameTemplate
|
|
|
print _("error template term %s")%str(funTxt)
|
|
|
print _("incorrect path %s")%fileName
|
|
|
exit(1)
|
|
|
else:
|
|
|
fileName = terms[1].strip()
|
|
|
if fileName[0] != "/":
|
|
|
print _("error in template %s")%nameTemplate
|
|
|
print _("error template term %s")%str(funTxt)
|
|
|
print _("incorrect path %s")%fileName
|
|
|
exit(1)
|
|
|
replace = ""
|
|
|
if os.path.exists(fileName):
|
|
|
FD = open(fileName)
|
|
|
replace = FD.read().strip()
|
|
|
FD.close
|
|
|
if not replace and len(terms) == 2 and terms[0] in ["ver","num"]:
|
|
|
replace = "0"
|
|
|
textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\
|
|
|
textTemplateTmp[resS.end():]
|
|
|
return textTemplateTmp
|
|
|
|
|
|
def getInstallPkgGentoo(names = [], versions = []):
|
|
|
"""Выдает два списка, инсталлированные программы и номера версий"""
|
|
|
baseDir = "/var/db/pkg"
|
|
|
pkgs = []
|
|
|
reVer = re.compile("(?<=\-)\d+\.?\d*\.?\d*")
|
|
|
def getFilesDir(pkgs, dirname, names):
|
|
|
for nameFile in names:
|
|
|
absNameFile = os.path.join(dirname,nameFile)
|
|
|
if os.path.isdir(absNameFile):
|
|
|
tail = absNameFile.split(baseDir)
|
|
|
if len(tail)==2:
|
|
|
tail = tail[1].split('/')
|
|
|
if len(tail)==3 and tail[1]!='virtual':
|
|
|
pkgs.append(tail[2])
|
|
|
return True
|
|
|
os.path.walk(baseDir,getFilesDir, pkgs)
|
|
|
pkgs.sort()
|
|
|
for pkg in pkgs:
|
|
|
findVer = reVer.search(pkg)
|
|
|
if findVer:
|
|
|
ver = findVer.group()
|
|
|
versions.append(ver)
|
|
|
names.append(pkg.split(ver)[0][:-1])
|
|
|
#return pkgs
|
|
|
return names, versions
|
|
|
|
|
|
def pkg(nameProg, names, versions):
|
|
|
"""Выдает установленные версии по имени программы"""
|
|
|
# Значение версии из кеша
|
|
|
if nameProg in self.cacheInstallProg:
|
|
|
return self.cacheInstallProg[nameProg]
|
|
|
i = 0
|
|
|
vers = []
|
|
|
for name in names:
|
|
|
if nameProg == name:
|
|
|
while nameProg == names[i]:
|
|
|
vers.append(versions[i])
|
|
|
i += 1
|
|
|
break
|
|
|
i += 1
|
|
|
if vers:
|
|
|
version = vers[-1]
|
|
|
# Запись значения версии в кеш
|
|
|
self.cacheInstallProg[nameProg] = version
|
|
|
return version
|
|
|
else:
|
|
|
return ""
|
|
|
|
|
|
def funcPkg(funTxt,resS,textTemplateTmp):
|
|
|
"""локальная функция выдает номер версии программы"""
|
|
|
terms = funTxt[4:-1].replace(" ","")
|
|
|
# Название программы
|
|
|
nameProg = terms
|
|
|
if not self.installProg:
|
|
|
# Получение всех названий и версий установленных программ
|
|
|
self.installProg,self.installProgVersions =\
|
|
|
getInstallPkgGentoo(names=self.installProg,
|
|
|
versions=self.installProgVersions)
|
|
|
replace = pkg(nameProg,self.installProg,self.installProgVersions)
|
|
|
textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\
|
|
|
textTemplateTmp[resS.end():]
|
|
|
return textTemplateTmp
|
|
|
|
|
|
def funcRnd(funTxt,resS,textTemplateTmp):
|
|
|
"""локальная функция выдает строку случайных символов
|
|
|
|
|
|
первый аргумент:
|
|
|
'num' - числа,
|
|
|
'pas' - цифры и буквы
|
|
|
второй аргумент:
|
|
|
количество символов
|
|
|
"""
|
|
|
terms = funTxt[4:-1].replace(" ","").split(",")
|
|
|
if not terms[0].strip() or\
|
|
|
(len(terms)==2 and not terms[1].strip()) or\
|
|
|
len(terms)!=2:
|
|
|
print _("error in template %s")%nameTemplate
|
|
|
print _("error template term %s")%str(funTxt)
|
|
|
exit(1)
|
|
|
fArgvNames = ['num','pas']
|
|
|
if not terms[0] in fArgvNames:
|
|
|
print _("error in template %s")%nameTemplate
|
|
|
print _("error template term %s")%str(funTxt)
|
|
|
print _("first argument function is not 'num' or 'pas'")
|
|
|
exit(1)
|
|
|
try:
|
|
|
lenStr = int(terms[1])
|
|
|
except:
|
|
|
print _("error in template %s")%nameTemplate
|
|
|
print _("error template term %s")%str(funTxt)
|
|
|
print _("two argument function is not number")
|
|
|
exit(1)
|
|
|
if terms[0] == fArgvNames[0]:
|
|
|
replace=''.join([random.choice(string.digits)\
|
|
|
for i in xrange(lenStr)])
|
|
|
elif terms[0] == fArgvNames[1]:
|
|
|
replace=''.join([random.choice(string.ascii_letters + \
|
|
|
string.digits) for i in xrange(lenStr)])
|
|
|
else:
|
|
|
print _("error in template %s")%nameTemplate
|
|
|
print _("error template term %s")%str(funTxt)
|
|
|
exit(1)
|
|
|
textTemplateTmp = textTemplateTmp[:resS.start()] + replace +\
|
|
|
textTemplateTmp[resS.end():]
|
|
|
return textTemplateTmp
|
|
|
|
|
|
def funcCase(funTxt,resS,textTemplateTmp):
|
|
|
"""локальная функция выдает переменную в определенном регистре
|
|
|
|
|
|
первый аргумент:
|
|
|
'upper' - верхний регистр,
|
|
|
'lower' - нижний регистр,
|
|
|
'capitalize' - первая буква в верхнем регистре
|
|
|
второй аргумент:
|
|
|
название переменной
|
|
|
"""
|
|
|
terms = funTxt[5:-1].replace(" ","").split(",")
|
|
|
if not terms[0].strip() or\
|
|
|
(len(terms)==2 and not terms[1].strip()) or len(terms)!=2:
|
|
|
print _("error in template %s")%nameTemplate
|
|
|
print _("error template term %s")%str(funTxt)
|
|
|
exit(1)
|
|
|
fArgvNames = ['upper','lower','capitalize']
|
|
|
if not terms[0] in fArgvNames:
|
|
|
print _("error in template %s")%nameTemplate
|
|
|
print _("error template term %s")%str(funTxt)
|
|
|
print _("first argument function is not 'upper' or 'lower' or\
|
|
|
'capitalize'")
|
|
|
exit(1)
|
|
|
try:
|
|
|
strValue = str(self.objVar.Get(terms[1]))
|
|
|
except:
|
|
|
print _("error in template %s")%nameTemplate
|
|
|
print _("error var %s not found")%str(terms[1])
|
|
|
exit(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
|
|
|
|
|
|
# Локальные переменные
|
|
|
localVars = {}
|
|
|
# Регулярное выражние для сложения
|
|
|
sNum = re.compile("\-[^\-\+]+|[^\-\+]+")
|
|
|
# Регулярное выражение для умножениея и деления
|
|
|
sMD = re.compile("[^\-\+\*\/]+")
|
|
|
resS = self._reFunc.search(textTemplate)
|
|
|
textTemplateTmp = textTemplate
|
|
|
while resS:
|
|
|
mark = textTemplateTmp[resS.start():resS.end()]
|
|
|
funTxt = mark[self._deltVarStart:-self._deltVarEnd]
|
|
|
# Функция sum
|
|
|
if funTxt[:4] == "sum(":
|
|
|
textTemplateTmp = funcSum(funTxt,resS,localVars,textTemplateTmp)
|
|
|
resS = self._reFunc.search(textTemplateTmp)
|
|
|
# Функция load
|
|
|
elif funTxt[:5] == "load(":
|
|
|
textTemplateTmp = funcLoad(funTxt,resS,textTemplateTmp)
|
|
|
resS = self._reFunc.search(textTemplateTmp)
|
|
|
elif funTxt[:4] == "pkg(":
|
|
|
textTemplateTmp = funcPkg(funTxt,resS,textTemplateTmp)
|
|
|
resS = self._reFunc.search(textTemplateTmp)
|
|
|
elif funTxt[:4] == "rnd(":
|
|
|
textTemplateTmp = funcRnd(funTxt,resS,textTemplateTmp)
|
|
|
resS = self._reFunc.search(textTemplateTmp)
|
|
|
elif funTxt[:5] == "case(":
|
|
|
textTemplateTmp = funcCase(funTxt,resS,textTemplateTmp)
|
|
|
resS = self._reFunc.search(textTemplateTmp)
|
|
|
else:
|
|
|
resS = False
|
|
|
return textTemplateTmp
|
|
|
|
|
|
def applyTermsTemplate(self,textTemplate,nameTemplate,nameSystemFile=False):
|
|
|
""" Применяет условия, к условным блокам текста
|
|
|
"""
|
|
|
textTerm = ""
|
|
|
resS = self._reTermBloc.search(textTemplate)
|
|
|
textTemplateTmp = textTemplate
|
|
|
def function(text):
|
|
|
"""Функция обработки функций в заголовке"""
|
|
|
return self.applyFuncTemplate(text, nameTemplate)
|
|
|
if nameSystemFile:
|
|
|
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, _("content template not valid: ")+\
|
|
|
nameTemplate, function):
|
|
|
textTemplateTmp = textTemplateTmp.replace(mark, body+end)
|
|
|
else:
|
|
|
textTemplateTmp = textTemplateTmp.replace(mark, "")
|
|
|
resS = self._reTermBloc.search(textTemplateTmp)
|
|
|
else:
|
|
|
while resS:
|
|
|
mark = resS.group(0)
|
|
|
body = resS.group("body")
|
|
|
end = resS.group("end")
|
|
|
term = resS.group("rTerm") + resS.group("lTerm")
|
|
|
if self._equalTerm(term, _("content template not valid: ")+\
|
|
|
nameTemplate):
|
|
|
textTemplateTmp = textTemplateTmp.replace(mark, body+end)
|
|
|
else:
|
|
|
textTemplateTmp = textTemplateTmp.replace(mark, "")
|
|
|
resS = self._reTermBloc.search(textTemplateTmp)
|
|
|
return textTemplateTmp
|
|
|
|
|
|
def getTitle(self, comment, commentList):
|
|
|
"""Выдает заголовок шаблона ( версия и.т.д)"""
|
|
|
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
|
|
|
lenCommentList = len(commentList) - 1
|
|
|
for com in self._titleList:
|
|
|
if lenCommentList < z:
|
|
|
self._titleBody += commentInsert + " " + com + "\n"
|
|
|
else:
|
|
|
self._titleBody += commentInsert + " " + com +\
|
|
|
" " + commentList[z] + "\n"
|
|
|
z += 1
|
|
|
if flagList:
|
|
|
self._titleBody += commentLast +"\n"
|
|
|
else:
|
|
|
self._titleBody += commentLast + self.__titleHead + "\n"
|
|
|
return self._titleBody
|
|
|
else:
|
|
|
return ""
|
|
|
|
|
|
def numberAllTemplates(self, number):
|
|
|
"""Количество шаблонов
|
|
|
|
|
|
Вызов происходит перед наложением шаблонов
|
|
|
в момент вызова в number находится количество обрабатываемых файлов
|
|
|
Наследуемая функция
|
|
|
Используется для отображения прогресса при наложениии шаблонов
|
|
|
"""
|
|
|
return True
|
|
|
|
|
|
def numberProcessTemplates(self, number):
|
|
|
"""Номер текущего обрабатываемого шаблона
|
|
|
|
|
|
Вызов происходит при наложении шаблона
|
|
|
в момент вызова в number находится номер обрабатываемого шаблона
|
|
|
Наследуемая функция
|
|
|
Используется для отображения прогресса при наложениии шаблонов
|
|
|
"""
|
|
|
return True
|
|
|
|
|
|
def __clearInErrorDirObj(self, dirObj):
|
|
|
"""Очищает объект директории при ошибке"""
|
|
|
# директории [(путь к директории, свойства директории)...]
|
|
|
dirObj.dirs = []
|
|
|
# файлы [(путь к файлу, свойства файла)...]
|
|
|
dirObj.files = []
|
|
|
# Ошибка
|
|
|
dirObj.flagError = True
|
|
|
return dirObj
|
|
|
|
|
|
def __scanDir(self, templatesDir, dirObj, dirProperties=()):
|
|
|
"""Измененное cканирование одной директории"""
|
|
|
# сканирование файлов и директорий (следующие сканирования)
|
|
|
if dirProperties:
|
|
|
# Выход при ошибке
|
|
|
if dirObj.flagError:
|
|
|
return dirObj
|
|
|
dirName, prop = dirProperties
|
|
|
# В случае удаления не смотрим внутренние директории
|
|
|
if "append" in prop and prop["append"] == "remove":
|
|
|
return dirObj
|
|
|
filesOrDirs = os.listdir(dirName)
|
|
|
# Сортируем файлы и директории
|
|
|
filesOrDirs.sort()
|
|
|
# Добавляем директорию и ее свойства
|
|
|
dirObj.dirs.append((dirName, prop))
|
|
|
if self.templDirNameFile in filesOrDirs:
|
|
|
# Удаляем файл описания директории из списка файлов
|
|
|
filesOrDirs.remove(self.templDirNameFile)
|
|
|
for fileOrDir in filesOrDirs:
|
|
|
# Выход при ошибке
|
|
|
if dirObj.flagError:
|
|
|
return dirObj
|
|
|
absPath = os.path.join(dirName,fileOrDir)
|
|
|
statInfo = os.stat(absPath)[stat.ST_MODE]
|
|
|
if stat.S_ISREG(statInfo):
|
|
|
# Свойства файла
|
|
|
propF, applyFile=self.__isApplyHeadTemplate(dirObj.fHeadObj,
|
|
|
absPath)
|
|
|
if self.getError():
|
|
|
self.setError(_("Incorrect template: " ) + absPath)
|
|
|
return self.__clearInErrorDirObj(dirObj)
|
|
|
if not applyFile:
|
|
|
continue
|
|
|
# Обработка skip
|
|
|
if "append" in propF and propF["append"] == "skip":
|
|
|
continue
|
|
|
if not "path" in propF:
|
|
|
propF["path"] = prop["_real_path"]
|
|
|
if not "name" in propF:
|
|
|
propF["name"] = os.path.split(absPath)[1]
|
|
|
propF["_real_path"] = os.path.join(propF["path"],
|
|
|
propF["name"])
|
|
|
dirObj.files.append((absPath, propF))
|
|
|
elif stat.S_ISDIR(statInfo):
|
|
|
# Информация о директории
|
|
|
pDir = {}
|
|
|
# Файл информации о директории
|
|
|
dirInfoFile = os.path.join(absPath,
|
|
|
self.templDirNameFile)
|
|
|
if os.path.exists(dirInfoFile) and\
|
|
|
stat.S_ISREG(os.stat(dirInfoFile)[stat.ST_MODE]):
|
|
|
# Настройки директории и применение
|
|
|
pDir, applyDir = self.__isApplyHeadDir(dirObj.dHeadObj,
|
|
|
dirInfoFile)
|
|
|
if self.getError():
|
|
|
self.setError(_("Incorrect template: " ) +\
|
|
|
dirInfoFile)
|
|
|
return self.__clearInErrorDirObj(dirObj)
|
|
|
if not applyDir:
|
|
|
continue
|
|
|
if not "path" in pDir:
|
|
|
pDir["path"] = prop["_real_path"]
|
|
|
if not "name" in pDir:
|
|
|
pDir["name"] = os.path.split(absPath)[1]
|
|
|
# Обработка skip
|
|
|
if "append" in pDir and pDir["append"] == "skip":
|
|
|
pDir["_real_path"] = pDir["path"]
|
|
|
else:
|
|
|
pDir["_real_path"] = os.path.join(pDir["path"],
|
|
|
pDir["name"])
|
|
|
self.__scanDir(False, dirObj, (absPath, pDir))
|
|
|
return dirObj
|
|
|
# Сканирование для получения настроек директории (первое)
|
|
|
else:
|
|
|
if templatesDir and stat.S_ISDIR(os.stat(templatesDir)[stat.ST_MODE]):
|
|
|
# настройки директории
|
|
|
pDir = {}
|
|
|
# Файл информации о директории
|
|
|
dirInfoFile = os.path.join(templatesDir, self.templDirNameFile)
|
|
|
if os.path.exists(dirInfoFile) and\
|
|
|
stat.S_ISREG(os.stat(dirInfoFile)[stat.ST_MODE]):
|
|
|
# Настройки директории и применение
|
|
|
pDir,applyDir = self.__isApplyHeadDir(dirObj.dHeadObj,
|
|
|
dirInfoFile)
|
|
|
if self.getError():
|
|
|
self.setError(_("Incorrect template: " ) +\
|
|
|
dirInfoFile)
|
|
|
return self.__clearInErrorDirObj(dirObj)
|
|
|
if not applyDir:
|
|
|
return dirObj
|
|
|
if not "path" in pDir:
|
|
|
pDir["path"] = "/"
|
|
|
if not "name" in pDir:
|
|
|
pDir["name"] = os.path.split(templatesDir)[1]
|
|
|
# Обработка skip
|
|
|
if "append" in pDir and pDir["append"] == "skip":
|
|
|
pDir["_real_path"] = pDir["path"]
|
|
|
else:
|
|
|
pDir["_real_path"] = os.path.join(pDir["path"],
|
|
|
pDir["name"])
|
|
|
self.__scanDir(False, dirObj, (templatesDir, pDir))
|
|
|
return dirObj
|
|
|
|
|
|
|
|
|
def scanDirs(self, templatesDirs, objVar):
|
|
|
"""Измененное cканирование директорий на вход список
|
|
|
|
|
|
Выход список объктов _dir
|
|
|
"""
|
|
|
dirs = []
|
|
|
# Объект заголовка файла
|
|
|
fHeadObj = fileHeader()
|
|
|
# Объект заголовка директории
|
|
|
dHeadObj = dirHeader()
|
|
|
# Объект переменных
|
|
|
for templateDir in templatesDirs:
|
|
|
dirP = scan._dir()
|
|
|
dirP.baseDir = templateDir
|
|
|
# Ошибка при сканировании
|
|
|
dirP.flagError = False
|
|
|
# Объект заголовка файла
|
|
|
dirP.fHeadObj = fHeadObj
|
|
|
# Объект заголовка директории
|
|
|
dirP.dHeadObj = dHeadObj
|
|
|
# Объект переменных
|
|
|
dirP.objVar = objVar
|
|
|
try:
|
|
|
self.__scanDir(templateDir, dirP)
|
|
|
except OSError, e:
|
|
|
print e.strerror, e.filename
|
|
|
self.__clearInErrorDirObj(dirP)
|
|
|
return [dirP]
|
|
|
if dirP.flagError:
|
|
|
return [dirP]
|
|
|
dirs.append(dirP)
|
|
|
return dirs
|
|
|
|
|
|
def applyTemplates(self):
|
|
|
"""Применяет шаблоны к конфигурационным файлам"""
|
|
|
if not self.objVar.defined("cl_template_path"):
|
|
|
self.setError (_("not defined Var: ") + "cl_template_path")
|
|
|
return False
|
|
|
dirsTemplates = self.objVar.Get("cl_template_path")
|
|
|
dirsTemplates.sort()
|
|
|
dirObjs = self.scanDirs(dirsTemplates,self.objVar)
|
|
|
#файлы к которым были применены шаблоны
|
|
|
filesApply = []
|
|
|
#созданные директории
|
|
|
createdDirs = []
|
|
|
# Получаем общее количество шаблонов (нужно для прогресбара)
|
|
|
allApplyFiles = self.scanTemplates(dirObjs)
|
|
|
if allApplyFiles == False:
|
|
|
return False
|
|
|
numberAllTemplates = len(allApplyFiles)
|
|
|
# Вызываем пустой метод с параметром общее количество шаблонов
|
|
|
self.numberAllTemplates(numberAllTemplates)
|
|
|
# номер обрабатываемого файла
|
|
|
numberProcessTemplates = 0
|
|
|
# имя текущей программы
|
|
|
_nameProgram = self.objVar.Get("cl_name").capitalize()
|
|
|
# версия текущей программы
|
|
|
_versionProgram = self.objVar.Get("cl_ver")
|
|
|
# имя и версия текущей программы
|
|
|
programVersion = "%s %s"%(_nameProgram, _versionProgram)
|
|
|
# Объект обработки заголовков файлов шаблонов
|
|
|
fileHeadObj = fileHeader()
|
|
|
# Проверка на ошибки
|
|
|
for dirObj in dirObjs:
|
|
|
if dirObj.flagError:
|
|
|
return False
|
|
|
for dirObj in dirObjs:
|
|
|
# сортируем файлы по названию
|
|
|
if dirObj.files:
|
|
|
dirObj.files.sort()
|
|
|
# сортируем директории по названию
|
|
|
if dirObj.dirs:
|
|
|
dirObj.dirs.sort()
|
|
|
blockDirs = []
|
|
|
propDir = {}
|
|
|
for dirTemplate, pDirs in dirObj.dirs:
|
|
|
# Получаем реальный путь директории
|
|
|
pathDir = pDirs["_real_path"]
|
|
|
# Фильтрация шаблонов по названию директории
|
|
|
if pathDir in self.dirsFilter:
|
|
|
blockDirs.append(dirTemplate)
|
|
|
continue
|
|
|
dirInfoFile = os.path.join(dirTemplate, self.templDirNameFile)
|
|
|
pathDir, propDir, crDirs = self.__getApplyHeadDir(pDirs)
|
|
|
if not propDir and self.getError():
|
|
|
self.setError(_("Error in apply template: " ) +\
|
|
|
dirInfoFile)
|
|
|
return False
|
|
|
if crDirs:
|
|
|
createdDirs += crDirs
|
|
|
for fileTemplate, propFile in dirObj.files:
|
|
|
findBlock = False
|
|
|
pathFile = propFile["_real_path"]
|
|
|
for blDir in blockDirs:
|
|
|
st,mid,end = pathFile.partition(blDir)
|
|
|
if (not st) and mid and end:
|
|
|
findBlock = True
|
|
|
break
|
|
|
if findBlock:
|
|
|
continue
|
|
|
numberProcessTemplates += 1
|
|
|
self.numberProcessTemplates(numberProcessTemplates)
|
|
|
# Фильтрация шаблонов по названию файла
|
|
|
if pathFile in self.filesFilter:
|
|
|
continue
|
|
|
titleBaseDir = os.path.split(dirObj.baseDir)[0]
|
|
|
titlePath = fileTemplate.partition(titleBaseDir)[2]
|
|
|
templTitle = '"' + titlePath[1:] + '"'
|
|
|
# Записываем в переменную обрабатываемый файл
|
|
|
self.objVar.Set("cl_pass_file",pathFile)
|
|
|
filesApl = self.join(fileTemplate, propFile,
|
|
|
(programVersion,templTitle), fileHeadObj)
|
|
|
if filesApl:
|
|
|
filesApply += filesApl
|
|
|
else:
|
|
|
if self.getError():
|
|
|
#print self.getError()
|
|
|
return False
|
|
|
self.closeFiles()
|
|
|
return (createdDirs, filesApply)
|
|
|
|
|
|
def scanTemplates(self, dirObjs=False):
|
|
|
"""Сканирует директории шаблонов - выводит список файлов"""
|
|
|
if not self.objVar.defined("cl_template_path"):
|
|
|
self.setError (_("not defined Var: ") + "cl_template_path")
|
|
|
return False
|
|
|
if not dirObjs:
|
|
|
dirsTemplates = self.objVar.Get("cl_template_path")
|
|
|
dirObjs = self.scanDirs(dirsTemplates, self.objVar)
|
|
|
#файлы к которым были применены шаблоны
|
|
|
filesApply = []
|
|
|
# Проверка на ошибки
|
|
|
for dirObj in dirObjs:
|
|
|
if dirObj.flagError:
|
|
|
return False
|
|
|
for dirObj in dirObjs:
|
|
|
# сортируем файлы по названию
|
|
|
if dirObj.files:
|
|
|
dirObj.files.sort()
|
|
|
# сортируем директории по названию
|
|
|
if dirObj.dirs:
|
|
|
dirObj.dirs.sort()
|
|
|
blockDirs = []
|
|
|
for dirTemplate, pDirs in dirObj.dirs:
|
|
|
pathDir = pDirs["_real_path"]
|
|
|
# Фильтрация шаблонов по названию директории
|
|
|
if pathDir in self.dirsFilter:
|
|
|
blockDirs.append(dirTemplate)
|
|
|
continue
|
|
|
for fileTemplate, propFile in dirObj.files:
|
|
|
findBlock = False
|
|
|
pathFile = propFile["_real_path"]
|
|
|
# Фильтрация файлов по названию директории
|
|
|
for blDir in blockDirs:
|
|
|
st,mid,end = pathFile.partition(blDir)
|
|
|
if (not st) and mid and end:
|
|
|
findBlock = True
|
|
|
break
|
|
|
if findBlock:
|
|
|
continue
|
|
|
# Фильтрация шаблонов по названию файла
|
|
|
if pathFile in self.filesFilter:
|
|
|
continue
|
|
|
filesApply.append(pathFile)
|
|
|
return filesApply
|
|
|
|
|
|
|
|
|
def __isApplyHeadDir(self, headerObj, templateDirFile):
|
|
|
"""Будет ли применен шаблон корневой директории
|
|
|
|
|
|
Возвращает:
|
|
|
(Настройки директории, и будет ли она применена (True, False))
|
|
|
"""
|
|
|
# Настройки для директории
|
|
|
dPrefs = {}
|
|
|
|
|
|
def function(text):
|
|
|
"""Функция обработки функций в заголовке"""
|
|
|
return self.applyFuncTemplate(text, templateDirFile)
|
|
|
|
|
|
if not os.path.exists(templateDirFile):
|
|
|
return (dPrefs, True)
|
|
|
try:
|
|
|
FD = open(templateDirFile)
|
|
|
textTemplate = FD.read()
|
|
|
FD.close()
|
|
|
except:
|
|
|
self.setError(_("Error open template: " ) + templateDirFile)
|
|
|
return (dPrefs, False)
|
|
|
# Заменяем переменные на их значения
|
|
|
textTemplate = self.applyVarsTemplate(textTemplate, templateDirFile)
|
|
|
# Обработка заголовка
|
|
|
propDir = headerObj.getPropertyTemplate(textTemplate,self.objVar,
|
|
|
function, templateDirFile)
|
|
|
if not propDir["_apply"]:
|
|
|
if headerObj.getError():
|
|
|
self.setError(_("Incorrect template: " ) + templateDirFile)
|
|
|
return (dPrefs, False)
|
|
|
# Записываем настройки
|
|
|
dPrefs.update(propDir)
|
|
|
# Тип добавления
|
|
|
if dPrefs['append'] == "remove":
|
|
|
# Удаление конфигурационного файла
|
|
|
return (dPrefs, False)
|
|
|
# chmod - изменяем права
|
|
|
if "chmod" in dPrefs:
|
|
|
mode = self.__octToInt(dPrefs["chmod"])
|
|
|
if mode:
|
|
|
dPrefs["chmod"] = mode
|
|
|
else:
|
|
|
self.setError (_("False value 'chmod' in template: " ) +\
|
|
|
templateDirFile)
|
|
|
return (dPrefs, False)
|
|
|
# chown - изменяем владельца и группу
|
|
|
if "chown" in dPrefs:
|
|
|
owner = dPrefs["chown"]
|
|
|
uid = False
|
|
|
gid = False
|
|
|
if ":" in owner:
|
|
|
strUid, strGid = owner.split(":")
|
|
|
import pwd
|
|
|
try:
|
|
|
uid = pwd.getpwnam(strUid)[2]
|
|
|
except:
|
|
|
self.setError (_("Not user in this system: ") + strUid)
|
|
|
self.setError (_("False value 'chown' in template: " )+\
|
|
|
templateDirFile)
|
|
|
return (dPrefs, False)
|
|
|
try:
|
|
|
import grp
|
|
|
gid = grp.getgrnam(strGid)[2]
|
|
|
except:
|
|
|
self.setError (_("Not group in this system: ")+strGid)
|
|
|
self.setError (_("False value 'chown' in template: " )+\
|
|
|
templateDirFile)
|
|
|
return (dPrefs, False)
|
|
|
dPrefs["chown"] = [uid, gid]
|
|
|
else:
|
|
|
self.setError (_("False value 'chown' in template: " ) +\
|
|
|
templateDirFile)
|
|
|
return (dPrefs, False)
|
|
|
# Проверяем path
|
|
|
if "path" in dPrefs and\
|
|
|
(not dPrefs["path"] or not dPrefs["path"][0] == "/"):
|
|
|
self.setError (_("False value 'path' in template: " )+\
|
|
|
templateDirFile)
|
|
|
return (dPrefs, False)
|
|
|
# Проверяем name
|
|
|
if "name" in dPrefs and\
|
|
|
(dPrefs["name"] and dPrefs["name"][0] == "/"):
|
|
|
self.setError (_("False value 'name' in template: " )+\
|
|
|
templateDirFile)
|
|
|
return (dPrefs, False)
|
|
|
return (dPrefs, True)
|
|
|
|
|
|
def __getApplyHeadDir(self, dictPropDir):
|
|
|
"""Применяет шаблон к директории (права, владелец, и.т. д)"""
|
|
|
newDirMv = dictPropDir["_real_path"]
|
|
|
applyDir = newDirMv
|
|
|
# Созданные директории
|
|
|
createDirs = []
|
|
|
|
|
|
if "append" in dictPropDir:
|
|
|
if dictPropDir["append"]=="skip":
|
|
|
return (applyDir, dictPropDir, createDirs)
|
|
|
# Удаляем директорию
|
|
|
elif dictPropDir["append"]=="remove":
|
|
|
if os.path.isdir(newDirMv):
|
|
|
# удаляем директорию
|
|
|
try:
|
|
|
self.removeDir(newDirMv)
|
|
|
except:
|
|
|
self.setError(_("Can not delete dir: " ) +\
|
|
|
newDirMv)
|
|
|
return (applyDir, False, createDirs)
|
|
|
|
|
|
# Флаг проверки существования директории
|
|
|
flagFoundDir = os.path.exists(newDirMv)
|
|
|
mode = False
|
|
|
uid = False
|
|
|
gid = False
|
|
|
# chmod - изменяем права
|
|
|
if "chmod" in dictPropDir:
|
|
|
mode = dictPropDir['chmod']
|
|
|
if flagFoundDir:
|
|
|
os.chmod(newDirMv, mode)
|
|
|
# chown - изменяем владельца и группу
|
|
|
if "chown" in dictPropDir:
|
|
|
uid, gid = dictPropDir['chown']
|
|
|
if flagFoundDir:
|
|
|
os.chown(newDirMv, uid, gid)
|
|
|
if not flagFoundDir:
|
|
|
createDirs = self.createDir(newDirMv, mode, uid, gid)
|
|
|
if not createDirs:
|
|
|
return (applyDir, False, createDirs)
|
|
|
return (applyDir, dictPropDir, createDirs)
|
|
|
|
|
|
|
|
|
def __isApplyHeadTemplate(self, headerObj, templateName):
|
|
|
"""Будет ли применен файл шаблона
|
|
|
|
|
|
Возвращает:
|
|
|
(Настройки файла, и будет ли он применен (True, False))
|
|
|
"""
|
|
|
# Настройки для файла
|
|
|
fPrefs = {}
|
|
|
def function(text):
|
|
|
"""Функция обработки функций в заголовке"""
|
|
|
return self.applyFuncTemplate(text, templateName)
|
|
|
|
|
|
if not os.path.exists(templateName):
|
|
|
return (fPrefs, True)
|
|
|
try:
|
|
|
FD = open(templateName)
|
|
|
textTemplate = FD.read()
|
|
|
FD.close()
|
|
|
except:
|
|
|
self.setError(_("Error open template: " ) + templateName)
|
|
|
return (fPrefs, False)
|
|
|
# Бинарный или текстовый файл шаблона
|
|
|
templateType = self.getFileType(templateName)
|
|
|
foundHeader = False
|
|
|
textHeader = ""
|
|
|
if templateType != "bin":
|
|
|
# Получаем заголовок файла шаблона
|
|
|
foundHeader, textHeader = headerObj.getHeader(textTemplate)
|
|
|
# Заменяем переменные на их значения
|
|
|
textHeader = self.applyVarsTemplate(textHeader, templateName)
|
|
|
propFile = headerObj.getPropertyTemplate(textHeader, foundHeader,
|
|
|
templateType, self.objVar,
|
|
|
function,templateName)
|
|
|
if not propFile["_apply"]:
|
|
|
if headerObj.getError():
|
|
|
self.setError(_("Incorrect template: " ) + templateName)
|
|
|
return (fPrefs, False)
|
|
|
if propFile["append"] == "remove":
|
|
|
# Удаление конфигурационного файла
|
|
|
return (propFile, False)
|
|
|
# Записываем настройки
|
|
|
fPrefs.update(propFile)
|
|
|
# chmod - изменяем права
|
|
|
if "chmod" in fPrefs:
|
|
|
mode = self.__octToInt(fPrefs["chmod"])
|
|
|
if mode:
|
|
|
fPrefs["chmod"] = mode
|
|
|
else:
|
|
|
self.setError (_("False value 'chmod' in template: " ) +\
|
|
|
templateName)
|
|
|
return (fPrefs, False)
|
|
|
# chown - изменяем владельца и группу
|
|
|
if "chown" in fPrefs:
|
|
|
owner = fPrefs["chown"]
|
|
|
uid = False
|
|
|
gid = False
|
|
|
if ":" in owner:
|
|
|
strUid, strGid = owner.split(":")
|
|
|
import pwd
|
|
|
try:
|
|
|
uid = pwd.getpwnam(strUid)[2]
|
|
|
except:
|
|
|
self.setError (_("Not user in this system: ") + strUid)
|
|
|
self.setError (_("False value 'chown' in template: " )+\
|
|
|
templateName)
|
|
|
return (fPrefs, False)
|
|
|
try:
|
|
|
import grp
|
|
|
gid = grp.getgrnam(strGid)[2]
|
|
|
except:
|
|
|
self.setError (_("Not group in this system: ")+strGid)
|
|
|
self.setError (_("False value 'chown' in template: " )+\
|
|
|
templateName)
|
|
|
return (fPrefs, False)
|
|
|
fPrefs["chown"] = [uid, gid]
|
|
|
else:
|
|
|
self.setError (_("False value 'chown' in template: " ) +\
|
|
|
templateName)
|
|
|
return (fPrefs, False)
|
|
|
# Проверяем path
|
|
|
if "path" in fPrefs and\
|
|
|
(not fPrefs["path"] or not fPrefs["path"][0] == "/"):
|
|
|
self.setError (_("False value 'path' in template: " )+\
|
|
|
templateDirFile)
|
|
|
return (fPrefs, False)
|
|
|
# Проверяем name
|
|
|
if "name" in fPrefs and\
|
|
|
(not fPrefs["name"] or fPrefs["name"][0] == "/"):
|
|
|
self.setError (_("False value 'name' in template: " )+\
|
|
|
templateDirFile)
|
|
|
return (fPrefs, False)
|
|
|
return (fPrefs, True)
|
|
|
|
|
|
def __getApplyHeadTemplate(self, newFile, dictPropFile):
|
|
|
"""Применяет заголовок к шаблону (права, владелец, и.т. д)"""
|
|
|
# Конфигурационный файл в системе
|
|
|
pathOldFile = dictPropFile["_real_path"]
|
|
|
# Директория в которой находится шаблон
|
|
|
newDir = dictPropFile["path"]
|
|
|
# Файлы в системе к которым были применены шаблоны
|
|
|
applyFiles = [pathOldFile]
|
|
|
pathProg = ""
|
|
|
# В случае force
|
|
|
if "force" in dictPropFile:
|
|
|
if os.path.islink(pathOldFile):
|
|
|
# удаляем ссылку
|
|
|
try:
|
|
|
os.unlink(pathOldFile)
|
|
|
except:
|
|
|
self.setError(_("Can not delete link: " ) +\
|
|
|
pathOldFile)
|
|
|
return (applyFiles, False)
|
|
|
if os.path.isfile(pathOldFile):
|
|
|
# удаляем файл
|
|
|
try:
|
|
|
os.remove(pathOldFile)
|
|
|
except:
|
|
|
self.setError(_("Can not delete file: " ) +\
|
|
|
pathOldFile)
|
|
|
return (applyFiles, False)
|
|
|
|
|
|
# Удаляем оригинальный файл
|
|
|
if dictPropFile["append"] == "remove":
|
|
|
if os.path.islink(pathOldFile):
|
|
|
# удаляем ссылку
|
|
|
try:
|
|
|
os.unlink(pathOldFile)
|
|
|
except:
|
|
|
self.setError(_("Can not delete link: " ) +\
|
|
|
pathOldFile)
|
|
|
if os.path.isfile(pathOldFile):
|
|
|
# удаляем файл
|
|
|
try:
|
|
|
os.remove(pathOldFile)
|
|
|
except:
|
|
|
self.setError(_("Can not delete file: " ) +\
|
|
|
pathOldFile)
|
|
|
return (applyFiles, False)
|
|
|
|
|
|
flagSymlink = False
|
|
|
flagForce = False
|
|
|
# Если есть параметр mirror
|
|
|
if "mirror" in dictPropFile:
|
|
|
if "link" in dictPropFile:
|
|
|
templateFile = dictPropFile['link']
|
|
|
if not os.path.exists(templateFile):
|
|
|
if os.path.exists(pathOldFile):
|
|
|
os.remove(pathOldFile)
|
|
|
return (applyFiles, False)
|
|
|
elif not os.path.exists(pathOldFile):
|
|
|
return (applyFiles, False)
|
|
|
|
|
|
# Если есть указатель на файл шаблона (link)
|
|
|
if "link" in dictPropFile and\
|
|
|
not "symbolic" in dictPropFile:
|
|
|
templateFile = dictPropFile['link']
|
|
|
foundTemplateFile = os.path.exists(templateFile)
|
|
|
if foundTemplateFile:
|
|
|
FO = self.openNewFile(templateFile)
|
|
|
buff = FO.read()
|
|
|
FO.close()
|
|
|
if os.path.exists(pathOldFile):
|
|
|
os.remove(pathOldFile)
|
|
|
if foundTemplateFile:
|
|
|
if not self.createConfFile(pathOldFile, newDir):
|
|
|
return (applyFiles, False)
|
|
|
FON = open (pathOldFile, "r+")
|
|
|
FON.write(buff)
|
|
|
FON.close()
|
|
|
|
|
|
# Если символическая ссылка
|
|
|
if "symbolic" in dictPropFile:
|
|
|
prevOldFile = pathOldFile
|
|
|
if "link" in dictPropFile:
|
|
|
pathOldFile = dictPropFile['link']
|
|
|
flagSymlink = True
|
|
|
if not "/" == pathOldFile[0]:
|
|
|
pathLink = os.path.split(os.path.abspath(prevOldFile))[0]
|
|
|
pathProg = os.getcwd()
|
|
|
os.chdir(pathLink)
|
|
|
|
|
|
# Флаг - создан конфигурационный файл
|
|
|
flagCreateFile = False
|
|
|
# chmod - изменяем права
|
|
|
if "chmod" in dictPropFile:
|
|
|
mode = dictPropFile['chmod']
|
|
|
if not os.path.exists(pathOldFile):
|
|
|
if not self.createConfFile(pathOldFile, newDir):
|
|
|
return (applyFiles, False)
|
|
|
flagCreateFile = True
|
|
|
os.chmod(pathOldFile, mode)
|
|
|
|
|
|
# chown - изменяем владельца и группу
|
|
|
if "chown" in dictPropFile:
|
|
|
uid, gid = dictPropFile['chown']
|
|
|
if not flagCreateFile and not os.path.exists(pathOldFile):
|
|
|
if not self.createConfFile(pathOldFile, newDir):
|
|
|
return (applyFiles, False)
|
|
|
os.chown(pathOldFile, uid, gid)
|
|
|
|
|
|
if flagSymlink:
|
|
|
if os.path.exists(prevOldFile) or os.path.islink(prevOldFile):
|
|
|
if os.path.islink(prevOldFile):
|
|
|
# если ссылка то удаляем её
|
|
|
os.unlink(prevOldFile)
|
|
|
else:
|
|
|
# иначе удаляем файл
|
|
|
os.remove(prevOldFile)
|
|
|
if not "/" == pathOldFile[0]:
|
|
|
os.symlink(pathOldFile, prevOldFile)
|
|
|
applyFiles = [prevOldFile,os.path.join(pathLink,pathOldFile)]
|
|
|
else:
|
|
|
os.symlink(pathOldFile, prevOldFile)
|
|
|
applyFiles = [prevOldFile,pathOldFile]
|
|
|
removeLink = not flagSymlink
|
|
|
# Создаем конфигурационный файл если он отсутствует
|
|
|
# открываем файл шаблона и конфигурационный файл
|
|
|
# указатель начала файла шаблона указыввает на текст после заголовка
|
|
|
# файла
|
|
|
if not self.openFiles(newFile, pathOldFile, self.createConfFile,
|
|
|
dictPropFile["_position"], removeLink):
|
|
|
return (applyFiles, False)
|
|
|
if pathProg:
|
|
|
os.chdir(pathProg)
|
|
|
# Если файлы заменяются не нужно их обрабатывать дальше
|
|
|
if "replace" in dictPropFile and\
|
|
|
not "symbolic" in dictPropFile and\
|
|
|
"link" in dictPropFile:
|
|
|
return (applyFiles, False)
|
|
|
return (applyFiles, dictPropFile)
|
|
|
|
|
|
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 textIsUtf8(self, text):
|
|
|
"""Проверяет текст на кодировку UTF-8"""
|
|
|
try:
|
|
|
text.decode("UTF-8")
|
|
|
except:
|
|
|
return False
|
|
|
return True
|
|
|
|
|
|
def setTemplateRules(self, templateProp, textTemplate, templFile, confFile):
|
|
|
"""Устанавливаем значения переменных, условий, функций для текста"""
|
|
|
# Вычисляем условные блоки
|
|
|
textTemplate = self.applyTermsTemplate(textTemplate,
|
|
|
templFile, confFile)
|
|
|
# Заменяем переменные на их значения
|
|
|
textTemplate = self.applyVarsTemplate(textTemplate,
|
|
|
templFile)
|
|
|
# Вычисляем функции
|
|
|
textTemplate = self.applyFuncTemplate(textTemplate, templFile)
|
|
|
return textTemplate
|
|
|
|
|
|
def join(self, newFile, propFile, ListOptTitle, fileHeadObj):
|
|
|
"""Объединения шаблона и конф. файла
|
|
|
|
|
|
join(newFile, oldFile, ListOptTitle)
|
|
|
Объединение шаблона newFile и конф. файла oldFile,
|
|
|
propFile - словарь свойств конфигурационного файла
|
|
|
ListOptTitle - список строк которые добавятся в заголовок
|
|
|
"""
|
|
|
# Применяем свойства к конфигурационному файлу
|
|
|
# Открываем файл шаблона и конфигурационный файл
|
|
|
|
|
|
# В случае type=print (печатаем содержимое шаблона)
|
|
|
flagPrintTemplate = False
|
|
|
filesApply, templateProp = self.__getApplyHeadTemplate(newFile,propFile)
|
|
|
if not templateProp:
|
|
|
if self.getError():
|
|
|
return []
|
|
|
return filesApply
|
|
|
# Путь к конфигурационному файлу
|
|
|
oldFile = templateProp["_real_path"]
|
|
|
# Формат шаблона
|
|
|
formatTemplate = templateProp["format"]
|
|
|
# Тип объединение шаблона
|
|
|
appendTemplate = templateProp["append"]
|
|
|
if formatTemplate != "bin":
|
|
|
# Применение условий, переменных, функций и переменных заголовка
|
|
|
self.newTemplate = self.setTemplateRules(templateProp,
|
|
|
self.newTemplate, newFile,
|
|
|
oldFile)
|
|
|
else:
|
|
|
# Копируем содержимое шаблона в содержимое конфигурационного файла
|
|
|
self.oldTemplate = self.newTemplate
|
|
|
# Если файл шаблона имеет поддерживаемый формат
|
|
|
if not formatTemplate in ["raw", "bin"]:
|
|
|
# Флаг- кодировка с бинарными примесями у файла шаблона включаем при
|
|
|
# условии текстового файла и кодировки отличной от UTF-8
|
|
|
flagNotUtf8New = False
|
|
|
# Флаг - кодировка с бинарными примесями у оригинального файла
|
|
|
flagNotUtf8Old = False
|
|
|
# проверяем кодировку шаблона
|
|
|
if not self.textIsUtf8(self.newTemplate):
|
|
|
flagNotUtf8New = True
|
|
|
if not ("link" in templateProp and "symbolic" in templateProp):
|
|
|
# проверяем кодировку оригинального файла
|
|
|
if not self.textIsUtf8(self.oldTemplate):
|
|
|
flagNotUtf8Old = True
|
|
|
# Титл конфигурационного файла
|
|
|
title = ""
|
|
|
if ListOptTitle and "comment" in templateProp:
|
|
|
title = self.getTitle(templateProp["comment"],
|
|
|
ListOptTitle)
|
|
|
title = title.encode("UTF-8")
|
|
|
# Удаляем заголовок Calculate из конфигурационного файла
|
|
|
if "comment" in templateProp:
|
|
|
self.oldTemplate = fileHeadObj.delHeaderConfFile(self.oldTemplate,
|
|
|
templateProp["comment"])
|
|
|
# Создаем объект в случае параметра format в заголовке
|
|
|
if not formatTemplate in ["raw", "bin"] and\
|
|
|
appendTemplate in ["replace", "before", "after"]:
|
|
|
# Преобразовываем бинарные файлы
|
|
|
if flagNotUtf8New:
|
|
|
objTxtCoder = utfBin()
|
|
|
self.newTemplate = objTxtCoder.encode(self.newTemplate)
|
|
|
# создаем объект формата шаблона
|
|
|
objTemplNew = self.getFormatObj(formatTemplate, self.newTemplate)
|
|
|
if not objTemplNew:
|
|
|
self.setError (\
|
|
|
_("Incorrect header parmeter format=%s in template")\
|
|
|
%formatTemplate + " " + newFile)
|
|
|
return False
|
|
|
|
|
|
if "xml_" in formatTemplate:
|
|
|
if objTemplNew.getError():
|
|
|
self.setError (_("False template: " ) + newFile)
|
|
|
return False
|
|
|
nameRootNode = oldFile.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.newTemplate = objTemplNew.getConfig().encode("UTF-8")
|
|
|
# Если не UTF-8 производим преобразование
|
|
|
if flagNotUtf8New:
|
|
|
self.newTemplate = objTxtCoder.decode(self.newTemplate)
|
|
|
# Титл для объединения
|
|
|
if ListOptTitle:
|
|
|
title = self.getTitle(objTemplNew._comment,
|
|
|
ListOptTitle)
|
|
|
title = title.encode("UTF-8")
|
|
|
# Замена
|
|
|
if appendTemplate == "replace":
|
|
|
if "xml_" in formatTemplate:
|
|
|
data = self.newTemplate.split("\n")
|
|
|
data.insert(1,title)
|
|
|
self.oldTemplate = "\n".join(data)
|
|
|
else:
|
|
|
self.oldTemplate = title + self.newTemplate
|
|
|
self.saveOldFile()
|
|
|
return filesApply
|
|
|
# Впереди
|
|
|
elif appendTemplate == "before":
|
|
|
if "xml_" in formatTemplate:
|
|
|
self.setError (\
|
|
|
_("False option append=before in template %s") %newFile)
|
|
|
return False
|
|
|
if self.newTemplate:
|
|
|
if self.newTemplate[-1] == "\n":
|
|
|
tmpTemplate = self.newTemplate + self.oldTemplate
|
|
|
else:
|
|
|
tmpTemplate = self.newTemplate + "\n" + self.oldTemplate
|
|
|
else:
|
|
|
tmpTemplate = self.oldTemplate
|
|
|
self.oldTemplate = title + tmpTemplate
|
|
|
self.saveOldFile()
|
|
|
return filesApply
|
|
|
# Cзади
|
|
|
elif appendTemplate == "after":
|
|
|
if "xml_" in formatTemplate:
|
|
|
self.setError (\
|
|
|
_("False option append=after in template %s") %newFile)
|
|
|
return False
|
|
|
if self.newTemplate:
|
|
|
if self.newTemplate[-1] == "\n":
|
|
|
tmpTemplate = self.oldTemplate + self.newTemplate
|
|
|
else:
|
|
|
tmpTemplate = self.oldTemplate + "\n" + self.newTemplate
|
|
|
else:
|
|
|
tmpTemplate = self.oldTemplate
|
|
|
self.oldTemplate = title + tmpTemplate
|
|
|
self.saveOldFile()
|
|
|
return filesApply
|
|
|
# Объединение
|
|
|
elif appendTemplate == "join":
|
|
|
if flagNotUtf8New:
|
|
|
objTxtCoder = utfBin()
|
|
|
self.newTemplate = objTxtCoder.encode(self.newTemplate)
|
|
|
# создаем объект формата шаблона
|
|
|
objTemplNew = self.getFormatObj(formatTemplate, self.newTemplate)
|
|
|
if not objTemplNew:
|
|
|
self.setError (\
|
|
|
_("Incorrect header parmeter format=%s in template")\
|
|
|
%formatTemplate + " " + newFile)
|
|
|
return False
|
|
|
if "xml_" in formatTemplate:
|
|
|
if objTemplNew.getError():
|
|
|
self.setError (_("False template: " ) + newFile)
|
|
|
return False
|
|
|
nameRootNode = oldFile.rpartition("/")[2].split(".")[0]
|
|
|
objTemplNew.setNameBodyNode(nameRootNode)
|
|
|
# Титл для объединения
|
|
|
if ListOptTitle:
|
|
|
title = self.getTitle(objTemplNew._comment,
|
|
|
ListOptTitle)
|
|
|
title = title.encode("UTF-8")
|
|
|
# В случае пустого конфигурационного файла
|
|
|
reNoClean = re.compile("[^\s]",re.M)
|
|
|
if not self.oldTemplate or\
|
|
|
not reNoClean.search(self.oldTemplate):
|
|
|
self.oldTemplate = ""
|
|
|
# Удаляем заголовок Calculate из конфигурационного файла
|
|
|
self.oldTemplate = fileHeadObj.delHeaderConfFile(self.oldTemplate,
|
|
|
objTemplNew._comment)
|
|
|
if flagNotUtf8Old:
|
|
|
objTxtCoder = utfBin()
|
|
|
self.oldTemplate = objTxtCoder.encode(self.oldTemplate)
|
|
|
# создаем объект формата шаблона для конфигурационного файла
|
|
|
objTemplOld = self.getFormatObj(formatTemplate, self.oldTemplate)
|
|
|
if not objTemplOld:
|
|
|
self.setError (_("Error in template %s") %oldFile)
|
|
|
return False
|
|
|
if "xml_" in formatTemplate:
|
|
|
if objTemplOld.getError():
|
|
|
self.setError (_("False template: " ) + oldFile)
|
|
|
return False
|
|
|
nameRootNode = oldFile.rpartition("/")[2].split(".")[0]
|
|
|
objTemplOld.setNameBodyNode(nameRootNode)
|
|
|
|
|
|
#print "#%s#" %(objTemplOld.docObj.body.toprettyxml())
|
|
|
#print "#%s#" %(objTemplNew.docObj.body.toprettyxml())
|
|
|
objTemplOld.join(objTemplNew)
|
|
|
if "xml_" in formatTemplate:
|
|
|
if objTemplOld.getError():
|
|
|
self.setError (_("False template: " ) + newFile)
|
|
|
return False
|
|
|
data = \
|
|
|
objTemplOld.getConfig().encode("UTF-8").split("\n")
|
|
|
data.insert(1,title)
|
|
|
self.oldTemplate = "\n".join(data)
|
|
|
else:
|
|
|
self.oldTemplate = title +\
|
|
|
objTemplOld.getConfig().encode("UTF-8")
|
|
|
# Декодируем если кодировка не UTF-8
|
|
|
if flagNotUtf8New or flagNotUtf8Old:
|
|
|
self.newTemplate = objTxtCoder.decode(self.newTemplate)
|
|
|
self.oldTemplate = objTxtCoder.decode(self.oldTemplate)
|
|
|
self.saveOldFile()
|
|
|
return filesApply
|
|
|
else:
|
|
|
self.setError (_("False (type append) template: " )+appendTemplate)
|
|
|
return False
|
|
|
|
|
|
class iniParser(_error):
|
|
|
"""Класс для работы с ini файлами
|
|
|
|
|
|
"""
|
|
|
def __init__(self, iniFile):
|
|
|
# Класс samba
|
|
|
self.samba = getattr(__import__("format.samba",
|
|
|
globals(), locals(),
|
|
|
["samba"]), "samba")
|
|
|
# название ini файла
|
|
|
self.iniFile = iniFile
|
|
|
# права создаваемого ini-файла
|
|
|
self.mode = 0640
|
|
|
# Cоответствует ли формат файла нужному
|
|
|
self.checkIni = None
|
|
|
|
|
|
def setMode(self, mode):
|
|
|
"""установка прав создаваемого ini-файла"""
|
|
|
self.mode = mode
|
|
|
|
|
|
def openIniFile(self):
|
|
|
if not os.access(self.iniFile, os.R_OK):
|
|
|
return ""
|
|
|
FD = open(self.iniFile, "r")
|
|
|
textIni = FD.read()
|
|
|
FD.close()
|
|
|
return textIni
|
|
|
|
|
|
def writeIniFile(self, txtConfig):
|
|
|
if not os.path.exists(self.iniFile):
|
|
|
fd = os.open(self.iniFile, os.O_CREAT)
|
|
|
os.close(fd)
|
|
|
os.chmod(self.iniFile, self.mode)
|
|
|
if not os.path.exists(self.iniFile):
|
|
|
self.setError(_("Unable to create file") + ": " + self.iniFile)
|
|
|
return False
|
|
|
FD = open(self.iniFile, "r+")
|
|
|
FD.truncate(0)
|
|
|
FD.seek(0)
|
|
|
FD.write(txtConfig)
|
|
|
FD.close()
|
|
|
|
|
|
def setVar(self, strHeader, dictVar):
|
|
|
"""Заменяет или добавляет область и переменные
|
|
|
|
|
|
Добавляет область в ini-файл или объединяет с существующей
|
|
|
strHeader - имя области
|
|
|
dictVar - словарь переменных
|
|
|
"""
|
|
|
textIni = self.openIniFile()
|
|
|
if not self.checkIniFile(textIni):
|
|
|
return False
|
|
|
# создаем объект типа samba и записываем в него содержимое ini-файла
|
|
|
objIni = self.samba(textIni)
|
|
|
# создаем текст в формате samba из строки заголовка и
|
|
|
# словаря переменных области
|
|
|
txtConfig = objIni.createTxtConfig(strHeader, dictVar)
|
|
|
# создаем объект типа samba и записываем в него текст
|
|
|
objIniAdd = self.samba(txtConfig)
|
|
|
# объединяем объекты для получения результирующего текста
|
|
|
objIni.join(objIniAdd)
|
|
|
# получаем текст
|
|
|
txtConfig = objIni.getConfig().encode("UTF-8")
|
|
|
# записываем его в ini файл
|
|
|
self.writeIniFile(txtConfig)
|
|
|
return True
|
|
|
|
|
|
def isEmptyFile(self, textIni):
|
|
|
"""Является ли файл пустым"""
|
|
|
if not textIni.strip():
|
|
|
return True
|
|
|
else:
|
|
|
return False
|
|
|
|
|
|
def checkIniFile(self, textIni):
|
|
|
"""Проверка на правильность формата файла"""
|
|
|
if self.checkIni == None:
|
|
|
# Ошибка
|
|
|
if textIni == False:
|
|
|
self.checkIni = False
|
|
|
return False
|
|
|
self.checkIni = True
|
|
|
# В файле есть данные
|
|
|
if not self.isEmptyFile(textIni):
|
|
|
objIni = self.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 файла"""
|
|
|
delStrHeader = "!%s" %(strHeader)
|
|
|
dictVar = {"del":"del"}
|
|
|
res = self.setVar(delStrHeader, dictVar)
|
|
|
return res
|
|
|
|
|
|
def getVar(self, strHeader, nameVar):
|
|
|
"""Получаем значение переменной из ini-файла"""
|
|
|
textIni = self.openIniFile()
|
|
|
if not self.checkIniFile(textIni):
|
|
|
return False
|
|
|
# создаем объект типа samba и записываем в него содержимое ini-файла
|
|
|
objIni = self.samba(textIni)
|
|
|
# получаем ноду body
|
|
|
xmlBody = objIni.docObj.getNodeBody()
|
|
|
# находим в области переменную
|
|
|
res = objIni.docObj.getAreaFieldValues(strHeader, nameVar, xmlBody)
|
|
|
if res == False:
|
|
|
return ""
|
|
|
else:
|
|
|
return res
|
|
|
|
|
|
def getAreaVars(self,strHeader):
|
|
|
"""Получаем все переменнные области из ini-файла"""
|
|
|
textIni = self.openIniFile()
|
|
|
if not self.checkIniFile(textIni):
|
|
|
return False
|
|
|
# создаем объект типа samba и записываем в него содержимое ini-файла
|
|
|
objIni = self.samba(textIni)
|
|
|
# получаем ноду body
|
|
|
xmlBody = objIni.docObj.getNodeBody()
|
|
|
# если находим область то выдаем словарем все переменные иначе False
|
|
|
res = objIni.docObj.getAreaFields(strHeader, xmlBody)
|
|
|
if res == False:
|
|
|
return {}
|
|
|
else:
|
|
|
return res
|
|
|
|
|
|
def getAllSectionNames(self):
|
|
|
"""Получаем все имена секций определенных в ini файле"""
|
|
|
textIni = self.openIniFile()
|
|
|
if not self.checkIniFile(textIni):
|
|
|
return False
|
|
|
# создаем объект типа samba и записываем в него содержимое ini-файла
|
|
|
objIni = self.samba(textIni)
|
|
|
# получаем ноду body
|
|
|
xmlBody = objIni.docObj.getNodeBody()
|
|
|
xmlNodes = objIni.docObj.getFieldsArea(xmlBody)
|
|
|
# Имена секций ini файла
|
|
|
namesSection = []
|
|
|
for xmlNode in xmlNodes:
|
|
|
if xmlNode.tagName == "area":
|
|
|
nSect = objIni.docObj.getNameArea(xmlNode)
|
|
|
if nSect:
|
|
|
namesSection.append(nSect)
|
|
|
return namesSection
|