diff --git a/build/lib/calculate-lib/pym/__init__.py b/build/lib/calculate-lib/pym/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/calculate-lib/pym/cl_data.py b/build/lib/calculate-lib/pym/cl_data.py new file mode 100644 index 0000000..926c224 --- /dev/null +++ b/build/lib/calculate-lib/pym/cl_data.py @@ -0,0 +1,624 @@ +#-*- 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 os +import sys +import cl_utils +from cl_lang import lang +from cl_template import iniParser +from cl_string import columnWrite + +# Перевод модуля на другой язык +tr = lang() +tr.setLocalDomain('cl_lib') +tr.setLanguage(sys.modules[__name__]) + +class var: + '''Объект "Переменная окружения"''' + # название сервиса которому принадлежит переменная + #(Global, Builder, Client, Server итд) + service = None + # значение переменной + value = "" + # режим записи (атрибут mode) + mode = "r" + # переменная для внутреннего использования (official) + official = False + # количество вызовов метода заполнения + countFill = 0 + # объект в котором создан этот объект + parentObj = None + # запускать или нет метод заполнения + fillStart = True + # dynamic = True то переменная динамическая при повтороном запуске + # запускается метод заполнения + # метод заполнения не запускается только если fillStart = False + # (осторожно возможно зацикливание программы если методы заполнения + # переменных используют методы друг друга) + dynamic = False + + def __init__(self, parentObj): + # словарь зависимых переменных {имя:значение} + self.dependValues = {} + # тип переменной (атрибут type) + self.type = ('default') + # список допустимых значений переменных (select) + self.select = () + # объект который создал этот объект + self.parentObj = parentObj + + def is_update(self): + #Нужно ли перезапускать метод заполнения (если зависимые переменные + #обновились то нужно) + upd = False + for depVarName in self.dependValues.keys(): + value = self.parentObj.__getattribute__(depVarName).Get() + if self.dependValues[depVarName] != value: + self.dependValues[depVarName] =\ + self.parentObj.__getattribute__(depVarName).value + upd = True + break + return upd + + def Get(self): + """Получение значения переменной""" + if not self.fillStart: + return self.value + if self.dynamic: + self.value = self.Fill() + return self.value + if not self.value: + if self.countFill>0: + return self.value + self.countFill += 1 + self.value = self.Fill() + if self.dependValues and self.is_update(): + self.countFill += 1 + self.value = self.Fill() + return self.value + + def Set(self, value): + """Запись значения переменной""" + self.value = value + return self.value + + def Fill(self): + """Заполнение переменной в далнейшем заменяем методом заполнения""" + return self.value + + +class DataVars(object): + class DataVarsError(Exception): + """Класс ошибок""" + pass + # добавляем пути к модулям если они не добавлены + if not os.path.abspath(\ + '/usr/lib/calculate/calculate-server/pym') in sys.path: + sys.path.insert(0,os.path.abspath(\ + '/usr/lib/calculate/calculate-server/pym')) + if not os.path.abspath(\ + '/usr/lib/calculate/calculate-lib/pym') in sys.path: + sys.path.insert(0,os.path.abspath(\ + '/usr/lib/calculate/calculate-lib/pym')) + if not os.path.abspath(\ + '/usr/lib/calculate/calculate-builder/pym') in sys.path: + sys.path.insert(0,os.path.abspath(\ + '/usr/lib/calculate/calculate-builder/pym')) + # Импортируемые модули - (раздел: модуль переменных, модуль заполнения + #переменных) + __modlist={'Global':('cl_vars','cl_fill'), + 'Server':('cl_vars_server','cl_fill_server'), + 'Builder':('cl_vars_builder','cl_fill_builder'), + 'Client':('cl_vars_client','cl_fill_client'), + } + def __init__(self): + #self.t1 = fillVars() + #self.t1.Get = self.Get + #self.t1.Set = self.Set + # Для нахождения зависимостей переменных + self.__levelNumber = 0 + self.__LevelsVar = [] + # Для хранения импортированных модулей и объектов + #[(cекция,импортированный модуль переменных, объект заполнения),] + self._importList = [] + self._importData("Global") + + def _importData(self, section): + """Импортирует модули с переменными и модули с функциями заполнения + + section секция раздела (Global, Server, Client итд) + создает необходимые структуры данных + """ + if not section in self.__modlist.keys(): + raise self.DataVarsError(_("Unsupported section %s")%section) + modVar = self.__modlist[section][0] + modFill = self.__modlist[section][1] + # Импортируем класс описания переменных и класс заполнения + try: + exec ("import %s" % (modVar)) + except ImportError, e: + err1 = _("Error in import module %s")%modVar + err2 = _("error") + ": " +str(e) + raise self.DataVarsError("%s\n%s"%(err1,err2)) + flagFindFillModule = True + try: + exec ("import %s" % (modFill)) + except ImportError, e: + if "No module named" in str(e): + flagFindFillModule = False + else: + err1 = _("Error in import module %s")%modFill + err2 = _("error") + ": " +str(e) + raise self.DataVarsError("%s\n%s"%(err1,err2)) + if flagFindFillModule: + # Создаем объект с методами заполнения переменных + exec("fillObj = %s.fillVars()" %modFill) + # Подключаем методы получения и записи переменных + fillObj.Get = self.Get + fillObj.Set = self.Set + else: + fillObj = False + # Заполняем self._importList + exec("self._importList.insert(0,(section,%s,fillObj))"%(modVar)) + + def __findVarData(self, nameVar): + """Находит данные для создания объекта переменная в модулях и + + объектах + """ + # Ищем переменную в модуле + dataVar = False + e = False + for section, moduleVar, fillobj in self._importList: + try: + exec("dataVar=moduleVar.Data.%s"%nameVar) + except AttributeError, e: + pass + if dataVar: + break + if dataVar == False: + err1 = _("Not found variable %s")%nameVar + err2 = "" + if e: + err2 = _("error") + ": " +str(e) + raise self.DataVarsError("%s\n%s"%(err1,err2)) + dataVar['service'] = section + # Ищем метод в объекте методов заполнения + nameMethod = "get_" + nameVar + flagFindMetod = False + for section, moduleVar, fillobj in self._importList: + if fillobj: + if nameMethod in dir(fillobj): + flagFindMetod = True + method = fillobj.__getattribute__(nameMethod) + break + if flagFindMetod: + return (dataVar,method) + else: + return (dataVar,False) + + def __setAttributesVar(self, var ,nameVar, dict): + """Установка аттрибутов для созданного объекта var + + название аттрибута и его значение берется из словаря dict + """ + dict['type'] = nameVar.split('_') + if not set(dict.keys()) <= set(dir(var)): + raise self.DataVarsError(\ + _("error initalize variable %s, incorrect data")%nameVar) + for nameAttr in dict.keys(): + setattr(var,nameAttr, dict[nameAttr]) + return True + + def __Get(self, nameVar): + ret = "" + self.__LevelsVar.append((self.__levelNumber, nameVar)) + self.__levelNumber += 1 + #nameMethod = "get_" + nameVar + if self.__dict__.has_key(nameVar): + ret = self.__getattribute__(nameVar).Get() + elif self.__findVarData(nameVar): + dictVar, methodFill =self.__findVarData(nameVar) + varobj = var(self) + # Устанавливаем аттрибуты + self.__setAttributesVar(varobj, nameVar, dictVar) + if methodFill: + varobj.Fill = methodFill + self.__setattr__(nameVar, varobj) + ret = self.__getattribute__(nameVar).Get() + self.__levelNumber -= 1 + if self.__levelNumber == 0 and\ + self.__getattribute__(nameVar).fillStart and\ + len(self.__LevelsVar)>1: + links = self.__getLinks(self.__LevelsVar) + for name in links.keys(): + for nameLink in links[name].keys(): + val = self.__getattribute__(nameLink).Get() + self.__getattribute__(name).dependValues[nameLink] = val + if self.__levelNumber == 0: + self.__LevelsVar = [] + return ret + + def Get(self, nameVar): + return self.__Get(nameVar) + + + def __Set(self, nameVar, value, force=False): + nameMethod = "get_" +nameVar + if not self.__dict__.has_key(nameVar) and self.__findVarData(nameVar): + dictVar, methodFill =self.__findVarData(nameVar) + varobj = var(self) + # Устанавливаем аттрибуты + self.__setAttributesVar(varobj, nameVar, dictVar) + if methodFill: + varobj.Fill = methodFill + self.__setattr__(nameVar, varobj) + if self.__dict__.has_key(nameVar): + if not force and "r" in getattr(self, nameVar).mode: + print _("Attempt to rewrite a variable for reading:%s")\ + %nameVar + return False + self.__getattribute__(nameVar).fillStart = False + return self.__getattribute__(nameVar).Set(value) + + def Set(self, nameVar, value, force=False): + return self.__Set(nameVar, value, force) + + def __frame(self, lVar): + """получить список областей зависимости переменных""" + data = [] + if not lVar: + return data + firstLevel = lVar[0][0] + for level, name in lVar[1:]: + if level> firstLevel: + data.append((level, name)) + else: + break + return data + + def __getLinks(self, lVar): + """Получить список переменных и от каких переменных они зависят + + на вход список [(уровень рекурсии, название переменной),] + """ + links = {} + frames = {} + levelLinks = {} + lVarFr = lVar + for level, name in lVar: + fr = self.__frame(lVarFr) + if not frames.has_key(name): + frames[name] = fr + levelLinks[name] = level+1 + lVarFr = lVarFr[1:] + for name in frames.keys(): + level = levelLinks[name] + fr = frames[name] + links[name] = {} + for lv, nm in fr: + if level == lv: + links[name][nm] = "" + return links + + def __getPathCalculateIni(self): + """Получить пути до ini файлов""" + return self.Get('cl_env_path') + + def __getSection(self, vname): + """секция для записи в ini файл переменной + + vname - имя переменной + """ + if self.__dict__.has_key(vname): + if self.__dict__[vname].service == 'Global': + return 'calculate' + else: + return self.__dict__[vname].service.lower() + + def __writeVarValue(self, vname, val, location, header): + '''Записать значение в calculate.ini + + Параметры: + vname имя переменной + val значение переменной + location расположение ini файла ('default', 'local', 'remote') + header раздел ini файла ('client', 'server', 'calculate') + + Возвращаемые значение: + True запись успешна + False запись не удалсь + ''' + # получаем все пути до ini файлов + calculate_ini = self.__getPathCalculateIni() + # получаем полный путь до файла ini + if location == 'default': + name_calculate_ini = calculate_ini[2] + elif location == 'local': + name_calculate_ini = calculate_ini[1] + elif location == 'remote': + name_calculate_ini = calculate_ini[0] + else: + return False + # извлекаем из полного имени файла путь + onlydir = os.path.split(name_calculate_ini)[0] + try: + # проверяем чтобы путь до ини файла существовал + if not os.path.exists(onlydir): + # создаем его если отсутствует + os.makedirs(onlydir) + except OSError (nerr,msg): + print nerr, msg + return False + config = iniParser(name_calculate_ini) + # Получаем секцию конфигурационного файла + if not header: + header = self.__getSection(vname) + return config.setVar(header,{vname: cl_utils.convertStrListDict(val)}) + + def __deleteVarValue(self, vname, location, header): + '''Удалить переменную в calculate.ini + + Параметры: + vname имя переменной + location расположение ini файла ('default', 'local', 'remote') + + Возвращаемые значение: + True удалено успешна + False удаление не удалсь + ''' + # получаем все пути до ini файлов + calculate_ini = self.__getPathCalculateIni() + # получаем полный путь до файла ini + if location == 'default': + name_calculate_ini = calculate_ini[2] + elif location == 'local': + name_calculate_ini = calculate_ini[1] + elif location == 'remote': + name_calculate_ini = calculate_ini[0] + else: + return False + # извлекаем из полного имени файла путь + onlydir = os.path.split(name_calculate_ini)[0] + # проверяем чтобы путь до ини файла существовал + if not os.path.exists(onlydir): + return False + config = iniParser(name_calculate_ini) + # Получаем секцию конфигурационного файла + if not header: + header = self.__getSection(vname) + # Удаляем переменную + retDelVar = config.delVar(header, vname) + retDelArea = True + if not config.getAreaVars(header): + retDelArea = config.delArea(header) + if retDelArea and retDelVar: + return True + else: + return False + + def Write(self, vname, val, force=False, location='default',header=False): + '''Установить и записать значение переменной в ini файл + + Параметры: + vname имя переменной + val значение переменной + force "принудительный режим" + location расположение ini файла ('default', 'local', 'remote') + header раздел ini файла ('client', 'server', 'calculate') + ''' + if self.__Set(vname, val, force)!= False: + if not val.strip(): + self.__deleteVarValue(vname, location, header) + self.__writeVarValue(vname, val, location, header) + return True + return False + + def Delete(self, vname, location='default', header=False): + '''Удалить переменную в calculate.ini + + Параметры: + vname имя переменной + location расположение ini файла ('default', 'local', 'remote') + + Возвращаемые значение: + True удалено успешна + False удаление не удалсь + ''' + return self.__deleteVarValue(vname, location, header) + + def __getActiveSections(self): + """активные секции в ini файле""" + act_section = [] + for service,t,t in self._importList: + if service == "Global": + act_section.append('calculate') + else: + act_section.append(service.lower()) + return act_section + + def flIniFile(self): + '''Заместить значение переменных значениями из ини файлов + + Возвращаемые значения: + cловарь импортированных переменных - переменные считаны + False - файл не был обнаружен + ''' + #Cловарь переменных из ini файлов + importVars = {} + calculate_ini = self.__getPathCalculateIni() + # активные секции (секции из которых будут использованы переменные) + act_section = self.__getActiveSections() + set_act_section = set(act_section) + i = 0 + locations = ['remote','local','default'] + for name_calculate_ini in calculate_ini: + # проверить сущестование ini файла + if os.path.exists(name_calculate_ini): + # получить объект настроенный на ini + config = iniParser(name_calculate_ini) + # получаем все секции из конфигурационного файла + allsect = config.getAllSectionNames() + if not allsect: + continue + # находим встречающиеся у обоих секции + act_sect = tuple(set(allsect)& set_act_section) + # словарь переменных для ini - файла + importFileVars = {} + # получаем все переменные из всех секций + for section in act_sect: + allvars = config.getAreaVars(section) + if allvars == False: + return False + # словарь переменных для ini - файла + importFileVars = {} + # принудительно переписать все переменные окружения + # полученные из ini + for (k,v) in allvars.items(): + k = k.encode("UTF-8") + value = cl_utils.convertStrListDict(v.encode("UTF-8")) + self.Set(k, value, True) + importFileVars[k] = value + if i < 3: + importVars[locations[i]] = importFileVars + i += 1 + return importVars + + def flServer(self, **args): + '''Заполнить конфигурацию переменных, для ldap''' + # заполнить переменные окружения алгоритмом по умолнанию + self._importData("Server") + + def flClient(self, **args): + '''Заполнить конфигурацию переменных, для клиента''' + # заполнить переменные окружения алгоритмом по умолнанию + self._importData("Client") + + def flBuilder(self, **args): + '''Заполнить конфигурацию переменных, для билдера''' + self.Set('setup_pass','builder',True) + # заполнить переменные окружения алгоритмом по умолнанию + self._importData("Builder") + + def flInstall(self, **args): + '''Заполнить конфигурацию переменных для инсталятора''' + self.Set('setup_pass','install',True) + + #def defined(self, vname): + #if vname: + #if self.__dict__.has_key(vname): + #return True + #return False + def defined(self, vname): + return True + + + + def exists(self, nameVar): + """ Определяет существует ли переменная с таким имененм + """ + if self.__dict__.has_key(nameVar): + return True + foundVar = False + # Ищем переменную в импортируемых модулях + for section, moduleVar, fillobj in self._importList: + if moduleVar.Data.__dict__.has_key(nameVar): + foundVar = True + break + return foundVar + + def getVars(self, type_names=None): + ret = {} + for section, moduleVar, fillobj in self._importList: + dataVar=moduleVar.Data + dictVars = dir(dataVar) + for nameVar in dictVars: + if not "__" in nameVar: + if not (getattr(dataVar,nameVar).has_key("official") and\ + getattr(dataVar,nameVar)['official']): + self.Get(nameVar) + if type_names: + #type_names.sort() + varType =list(getattr(dataVar,nameVar)['type']) + #varType.sort() + #print type_names + #print varType + #print + if not set(type_names)<=set(varType): + continue + ret[nameVar] = getattr(self,nameVar) + return ret + + #распечатать список переменных с значениями + def printVars(self,type_names=None): + var=None + var=self.getVars(type_names) + mlen_name=0; + mlen_type=0; + mlen_mode=0; + for i,j in var.items(): + if len(i)>mlen_name: + mlen_name=len(i) + #if len(str(j.type))>mlen_type: + #mlen_type=len(str(j.type)) + vtype=str(type(var[i].value)).split(" ")[1][1] + if not '[' in var[i].mode: + if vtype in ['d','l']: + mode="[%s%s]"%(var[i].mode.lower(),vtype) + else: + mode="[%s]"%(var[i].mode.lower()) + var[i].mode=mode + if len(mode)>mlen_mode: + mlen_mode=len(mode) + plist=var.keys() + plist.sort() + br = cl_utils.fillstr("-",mlen_name) + " " +\ + cl_utils.fillstr("-",mlen_mode) + " " + cl_utils.fillstr("-",10) + #cl_utils.fillstr("-",mlen_type) + " " +\ + + print "The list of variables:" + print "var name".center(mlen_name),\ + "Mode","Value" + #"Type".center(mlen_type),\ + + + print br + for i in plist: + #if var[i].value is None: + #continue + p_val=var[i].value + if var[i].official: + continue + columnWrite( i, mlen_name, var[i].mode.lower(), + mlen_mode, + #str(var[i].type), + #mlen_type, + p_val) + print br + +class glob_attr: + """Глобальные аттрибуты для методов заполнения переменных""" + + def _runos(self,cmd, ret_first=None, env={}): + """Вернуть результат выполнения команды ОС""" + if not env: + envDict = {} + env.update(os.environ.items() + [("PATH",cl_utils.getpathenv())] +\ + env.items()) + retCode, programOut = cl_utils.runOsCommand(cmd, None, ret_first, env) + if not retCode: + return programOut + return False diff --git a/build/lib/calculate-lib/pym/cl_fill.py b/build/lib/calculate-lib/pym/cl_fill.py new file mode 100644 index 0000000..4d3b598 --- /dev/null +++ b/build/lib/calculate-lib/pym/cl_fill.py @@ -0,0 +1,346 @@ +#-*- 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 re +import os +import types +from cl_overriding import exit +import cl_utils +import cl_data + +class fillVars(object, cl_data.glob_attr): + + def get_os_net_domain(self): + ''' Определим домен''' + domain=self._runos("hostname -d 2>&1") + if not domain: + print _("Error:") + " " +_("Not found domain name") + print _("Command 'hostname -d' returns an empty value") + exit(1) + elif re.search("^hostname: ",domain): + return "local" + else: + return domain + + def get_os_linux_shortname(self): + '''Получить переменную короткого названия системы''' + path = '/etc/calculate/calculate.ini' + if os.path.exists(path): + FD = open(path) + data = FD.readlines() + FD.close() + shortNameList = filter(lambda y:y, + map(lambda x:\ + len(x.split("="))==2 and\ + x.split("=")[0]=="calculate" and\ + x.split("=")[1].strip(), data)) + if shortNameList: + return shortNameList[0] + gentooFile = "/etc/gentoo-release" + shortName = "Linux" + if os.path.exists(gentooFile): + shortName = "Gentoo" + return shortName + + def get_os_linux_name(self): + """полное название системы""" + linuxShortName = self.Get("os_linux_shortname") + if linuxShortName: + dictLinuxName = {"CLD":"Calculate Linux Desktop", + "CLDX":"Calculate Linux Desktop", + "CDS":"Calculate Directory Server", + "Gentoo":"Gentoo"} + if linuxShortName in dictLinuxName.keys(): + return dictLinuxName[linuxShortName] + else: + return "Linux" + else: + return "Linux" + + def get_os_linux_subname(self): + """постфикс к названию системы""" + linuxShortName = self.Get("os_linux_shortname") + if linuxShortName: + dictLinuxSubName = {"CLD":"KDE", "CLDX":"XFCE"} + if linuxShortName in dictLinuxSubName.keys(): + return dictLinuxSubName[linuxShortName] + else: + return "" + else: + return "" + + def get_os_linux_ver(self): + '''Получить версию системы''' + path = '/etc/calculate/calculate.ini' + if os.path.exists(path): + FD = open(path) + data = FD.readlines() + FD.close() + shortNameList = filter(lambda y:y, + map(lambda x:\ + len(x.split("="))==2 and\ + x.split("=")[0]=="linuxver" and\ + x.split("=")[1].strip(), data)) + if shortNameList: + return shortNameList[0] + gentooFile = "/etc/gentoo-release" + systemVersion = "" + flagGentoo = False + if os.path.exists(gentooFile): + gentooLink = "/etc/make.template" + if os.path.islink(gentooLink): + systemVersion = os.readlink(gentooLink).rpartition("/")[2] + flagGentoo = True + if not flagGentoo: + kernelVersion=self._runos("uname -r") + if kernelVersion: + systemVersion = kernelVersion.partition("-")[0] + return systemVersion + + def get_os_net_hostname(self): + '''Считать имя компьютера net_host''' + hostname=self._runos("hostname -s 2>&1") + if not hostname: + return "" + if re.search("^hostname: ",hostname): + hostname=self._runos("hostname 2>&1") + if not hostname: + return "" + if re.search("^hostname: ",hostname): + return self.Get('os_linux_shortname') + else: + if hostname=='livecd': + return self.Get('os_linux_shortname') + return hostname + + # все ip + def get_os_net_ip(self): + """все ip компьютера, разделитель запятая""" + IPs = [] + netInterfaces=cl_utils.getdirlist("/sys/class/net/") + for i in netInterfaces: + res = self._runos("/sbin/ifconfig %s"%i) + if not res: + break + for line in res: + searchIP = re.search('addr:([0-9\.]+).+Bcast:', line) + if searchIP: + # ip адрес + ip = searchIP.groups()[0] + IPs.append(ip) + return ",".join(IPs) + + # Разрешенные сети (в данном случае все сети) + def get_os_net_allow(self): + """Разрешенные сети разделитель запятая""" + + def getNet(ip, mask): + """По ip и маске получаем сеть""" + octetsMult = (0x1, 0x100, 0x10000, 0x1000000) + octetsIp = map(lambda x: int(x), ip.split(".")) + octetsMask = map(lambda x: int(x), mask.split(".")) + ipNumb = 0 + for i in octetsMult: + ipNumb += octetsIp.pop()*i + maskNumb = 0 + for i in octetsMult: + maskNumb += octetsMask.pop()*i + startIpNumber = maskNumb&ipNumb + x = startIpNumber + nMask = lambda y: len(filter(lambda x: y >> x &1 ,range(32))) + return "%s.%s.%s.%s/%s"\ + %(x>>24, x>>16&255, x>>8&255, x&255, nMask(maskNumb)) + + networks=[] + netInterfaces=cl_utils.getdirlist("/sys/class/net/") + flagError = False + for i in netInterfaces: + res = self._runos("/sbin/ifconfig %s"%i) + if not res: + flagError = True + break + for j in res: + s_ip=re.search('addr:([0-9\.]+).+Bcast:.+Mask:([0-9\.]+)' ,j) + if s_ip: + ip, mask = s_ip.groups() + networks.append(getNet(ip, mask)) + if flagError: + return "" + return ",".join(networks) + + def get_os_locale_locale(self): + """локаль (прим: ru_RU.UTF-8)""" + if os.environ.has_key("LANG"): + return os.environ["LANG"] + else: + return "en_US.UTF-8" + + def get_os_locale_lang(self): + """язык (прим: ru_RU)""" + locale = self.Get("os_locale_locale") + if locale: + return locale.split(".")[0] + return "" + + def get_os_locale_language(self): + """язык (прим: ru)""" + lang = self.Get("os_locale_lang") + if lang: + return lang.split("_")[0] + return "" + + def get_os_locale_xkb(self): + """раскладка клавиатуры для X""" + path = '/etc/conf.d/keymaps' + mapDict={"by":"us,by", + "be-latin1":"be,us", + "br-abnt2":"br,us", + "cf":"ca,us", + "dk-latin1":"dk,us", + "fr-latin9":"fr,us", + "de-latin1":"de,us", + "is-latin1":"is,us", + "it":"it,us", + "no-latin1":"no,us", + "pl":"pl,us", + "-u ru4":"us,ru(winkeys)", + "es euro2":"es,us", + "sv-latin1":"se,us", + "ua-utf":"us,ua(winkeys)", + "uk":"gb,us", + "us":"us"} + if os.path.exists(path): + FD = open(path) + data = FD.readlines() + FD.close() + shortNameList = filter(lambda y:y, + map(lambda x:\ + len(x.split("="))==2 and\ + x.split("=")[0]=="KEYMAP" and\ + x.split("=")[1].replace('"',"").strip(),\ + data)) + if shortNameList: + if shortNameList[0] in mapDict.keys(): + return mapDict[shortNameList[0]] + lang = self.Get("os_locale_lang") + # Языки: + # Португальский - pt_BR + # Французский - fr_FR + # Немецкий - de_DE + # Итальянский - it_IT + # Польский - pl_PL + # Русский - ru_RU + # Испанский - es_ES + # Украинский - uk_UA + # Английский - en_US + xkbDict = {'pt_BR':'br,us', + 'fr_FR':'fr,us', + 'de_DE':'de,us', + 'it_IT':'it,us', + 'pl_PL':'pl,us', + 'ru_RU':'us,ru(winkeys)', + 'es_ES':'es,us', + 'uk_UA':'us,ua(winkeys)', + 'en_US':'us'} + if lang: + if xkbDict.has_key(lang): + return xkbDict[lang] + return "" + + def get_os_locale_xkbname(self): + """названия используемых раскладок клавиатуры для X""" + localeXkb = self.Get("os_locale_xkb") + if localeXkb: + return localeXkb.split("(")[0] + return "" + + def get_os_arch_machine(self): + """архитектура процессора""" + march = self._runos("uname -m") + if not march: + return "" + return march + + def get_os_root_dev(self): + """корневой раздел файловой системы""" + for record in open('/proc/cmdline','rb').readlines(): + re_res=re.search('^root=(\/dev\/[a-z]+[0-9]).*',record.strip()) + if re_res: + return re_res.group(1) + else: + mountLunes = self._runos("mount") + if not mountLunes: + return "" + if type(mountLunes) == types.ListType: + root_dev = mountLunes[0].split("on / type")[0].strip() + if root_dev: + return root_dev + return "" + + def get_os_root_type(self): + """тип носителя (ram, hdd, livecd)""" + mountLunes = self._runos("mount") + if not mountLunes: + return "" + rootType = "hdd" + if type(mountLunes) == types.ListType: + flagCD = False + for line in mountLunes: + if "/dev/loop0 on / type" in line: + rootType = "ram" + break + elif "/dev/loop0 on /newroot/mnt/livecd type" in line: + rootType = "ram" + flagCD = True + break + if rootType == "ram": + if os.path.exists("/mnt/livecd") or flagCD: + rootType = "livecd" + return rootType + rootDev = self.Get("os_root_dev") + if rootType != "ram" and rootDev: + slpRootDev = rootDev.split("/dev/") + if len(slpRootDev) == 2: + rDev = slpRootDev[1] + devLines = self._runos("ls -la /dev/disk/by-id/", None, + {"LANG":"C"}) + if not devLines: + return "" + if type(devLines) == types.ListType: + for line in devLines: + if rDev in line and "usb-" in line: + rootType = "usb-hdd" + break + if rootType == "ram": + rootType = "hdd" + return rootType + else: + return "" + + def get_hr_virtual(self): + """Название виртуальной машины (virtualbox, vmware, qemu)""" + pciLines = self._runos("/usr/sbin/lspci") + if not pciLines: + return False + virtSysDict = {'VirtualBox':'virtualbox', + 'VMware':'vmware', + 'Qumranet':'qemu'} + virtName = '' + for vName in virtSysDict.keys(): + if filter(lambda x: vName in x, pciLines): + virtName = virtSysDict[vName] + break + return virtName diff --git a/build/lib/calculate-lib/pym/cl_help.py b/build/lib/calculate-lib/pym/cl_help.py new file mode 100644 index 0000000..20ca654 --- /dev/null +++ b/build/lib/calculate-lib/pym/cl_help.py @@ -0,0 +1,375 @@ +#-*- 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 getopt +import sys +from cl_string import prettyColumnStr + +pcs = prettyColumnStr + +class opt: + def __init__(self,shortOpt,longOpt = []): + """ Длинные и короткие опции командной строки допустимые в программе + a - короткая опция + >program -a + + a: - короткая опциия со значением + >program -a 10 + + a:: - короткая опциия у которой может быть или не быть значение + >program -a + >program -a 15 + + "ha:" - значение параметра shortOpt + две опции h - без значения, a - со значением + + help - длинная опция без значения + test= - длинная опция со значением + + ["help","test="] - значение парамера longOpt + >program -a + две опции help - без значения, test - со значением + """ + self.shortOpt = shortOpt + self.longOpt = longOpt + self.sysArgv = sys.argv[1:] + + def getopt(self): + try: + opts, args = getopt.getopt(self.sysArgv,self.shortOpt,self.longOpt) + except getopt.GetoptError: + self.handlerErrOpt() + sys.exit(1) + for option, value in opts: + if len(option) == 2: + option = option[1:] + else: + option = option[2:] + self.handlerOpt(option,value) + for param in args: + self.handlerParam(param) + + def handlerErrOpt(self): + # Обработчик в случае неправильных параметров + pass + + def handlerOpt(self,option,value): + # Обработчик (параметр значение) + pass + + def handlerParam(self,param): + # Обработчик хвостов (значение) + pass + +class cl_help: + """Объект для работы со справкой, и обработкой параметров. + + Конструктор __init__ должен определить следующие переменные: + + self.chapter список разделов справки, каждый элементы состоит из + имени раздела, флаг видимый/скрытый, кол-во переводов строк + после названия раздела, количество строк после раздела, + тип раздела + Пример: [("Copyright",False,0,2),"options"] + self.relService словарь связей сервисов и действующих опций + ключ - название сервиса, значение - список отображаемых + разделов отмеченных как "options" + Пример: {"samba":[_("Common options"), + _("Service Samba options")]} + self.relOptions словарь связей длинных опций помощи и выводимых разделов + помощи с опциями + ключ - параметр справки, значение список отображаемых + разделов справки + Пример: {"help-ldap":[_("Common options"), + _("Service LDAP options)]} + self.progName словарь имена используемых программ и их номера для + доступа к переменным + Пример: {'cl-groupadd':0, 'cl-groupdel':1} + self.data список данных для справки, каждый элемент словарь: + progAccess: список номеров программ отображающих + Пример: {'progAccess':(0,), + 'shortOption':"g", + 'longOption':"gid", + 'optVal':"GID", + 'helpChapter':_("Options"), + 'help':_("use GID for the new group") + }, + после заполнения параметров необходимо выполнить + self._cl_help__setParamHelp() для заполнения справки + + """ + def __init__(self, cmdName): + # ширина консоли взята за 80 + # -1 чтобы компенсировать расстрояние между колонками + self.consolewidth = 79 + self.column_width = 32 + self.cmdName = cmdName + #короткие опции командной строки + self.shortOpt = [] + #длинные опции командной строки + self.longOpt = [] + # массив разделов (заполняется в __setParamHelp) + self.chapterBloc = [] + #optEnd = "" + #if "user" in self.cmdName and not "mod" in self.cmdName: + #optEnd = _("user") + #elif "group" in self.cmdName and not "mod" in self.cmdName: + #optEnd = _("group") + #self.__setParamHelp() + + def getChapterNumber(self,NameChapter): + """Получить номер раздела по имени""" + num = 0 + for i in self.chapter: + if i[0] == NameChapter: + return num + num += 1 + return False + + def __setParamHelp(self): + """Внутренняя функция формирования справки по данным + + Перебирает все элементы списка data, проверяет их на доступность + данной программы, разбирает опции на среди data и формирует + для по ним справку. + """ + # сформировать нужное количество блоков раздела + self.chapterBloc = [""]*len(self.chapter) + # + sp = {} + i = 0 + # перебираем все элементы справки собираем элементы опции + # так же формируем разделы не опции + for par in self.data: + # перебираем только те опции, которые принадлежат команде + if self.access(par): + # есть короткая (возможно есть и длинная) + if par.has_key("shortOption"): + sp[par["shortOption"]+":"+par["helpChapter"]] = i + # есть только длинная опция + elif par.has_key("longOption"): + sp[par["longOption"]+":"+par["helpChapter"]] = i + # формирование разделов не опций + else: + helpTxt = par['help'] + numChapter = self.getChapterNumber(par['helpChapter']) + self.addChapterHelp(numChapter,helpTxt) + i += 1 + # перебираем все "собранные" опции + # опции перебираются по порядку в списке date + # для сортировки по ключам следует применить код: + # for index in sorted(sp.keys()): + # par = self.data[sp[index]] + for index in sorted(sp.values()): + par = self.data[index] + numChapter = self.getChapterNumber(par['helpChapter']) + # если есть и короткая и длинная + if "shortOption" in par and "longOption" in par: + paraminfo = "-%s, --%s "%(par["shortOption"],par["longOption"]) + # если есть только короткая + elif "shortOption" in par: + paraminfo = "-%s "%par["shortOption"] + # если только длинная + else: + paraminfo = "--%s "%par["longOption"] + # если указан параметр для опции + if "optVal" in par: + optVal = par["optVal"] + else: + optVal = "" + + # вывод вида: " [-o, ][--option] [PARAM]" "helpstring" + helpTxt = pcs(" "+paraminfo+optVal, self.column_width, \ + par['help'], self.consolewidth-self.column_width) + # добавить строку в нужный раздел + self.addChapterHelp(numChapter,helpTxt) + + def getHelp(self, optionsChapters=False): + """Выдать справку. + + Выдает справку в случае если указан optionsChapters, то фильтрует по + типу разделов. + + Параметры: + optionsChapters Flase или список опциональных разделов для + отображения + + Возвращаемые параметры: + Строка со справкой. + """ + # Выдать справку + help = "" + # перебираем все элементы справочных блоков + iterChapterBloc = iter(self.chapterBloc) + # перебираем все разделы по параметрам + for (nameChapter, visibleChapter, beforeStrChapter, \ + afterStrChapter, typeChapter) in self.chapter: + # получаем следующий блок (т.о. textChapterBloc соответ, chapter) + textChapterBloc = iterChapterBloc.next() + # если тип раздела опциональный + if optionsChapters and typeChapter=="options": + # проверяем нужно ли его отображать + if not (nameChapter in optionsChapters): + continue + bef = "\n"*beforeStrChapter + aft = "\n"*afterStrChapter + # если блок не пустой и раздел отображаемый + if len(textChapterBloc) > 0: + if visibleChapter: + help += nameChapter + ": " + bef + help += textChapterBloc + aft + help = help.rstrip()+"\n" + return help + + def addChapterHelp(self, numChapter, helpTxt): + """Добавить в раздел помощи numChapteк тектстовую строку helpTxt + + Параметры: + numChapter номер раздела в который нужно добавить данные справки + helpTxt строка, содержащая данные + """ + self.chapterBloc[numChapter] += helpTxt + return True + + def addData(self,dataHash): + # На будущее (добавляет опции) + self.data.append(dataHash) + return True + + def handleCheckAccess(self,dataHash): + """Замещаемый дополнительный обработчик проверки + доступности опции. + + Входные параметры: + dataHash элементы списка данных справки (self.data) + """ + return True + + def access(self,dataHash): + """Доступна ли опция вызывающей программе + + Параметры: + dataHash словарь элемент типа self.data + + Возвращаемые параметры: + True/False доступна/недоступна + """ + # доступна ли опция вызывающей программе + # опция без progAccess доступна + numProg = self.progName[self.cmdName] + if 'progAccess' in dataHash: + if numProg in dataHash['progAccess']: + # вызов дополнительной проверки доступа к опции + return self.handleCheckAccess(dataHash) + else: + return False + else: + # вызов дополнительной проверки доступа к опции + return self.handleCheckAccess(dataHash) + + def getTypeChapter(self, nameChapter): + """Получить тип раздела по его имени + + Параметры: + nameChapter название раздела + + Возвращаемые параметры: + строка тип раздела + Flase(Boolean) такой раздел отсутствует + """ + # фильтруем список по имени раздела, помещаем в список тип раздела + filtered = [typeChapter for name, na, na, na, typeChapter \ + in self.chapter if name == nameChapter] + # если среди фильтрованных есть хоть один элемент + if len(filtered) > 0: + # возвращаем - он запрашиваемый + return filtered[0] + else: + # такой раздел отсутствует + return False + + def clearAllOpt(self): + """Очистить все опции, полученные посредством getAllOpt""" + if len(self.shortOpt) > 0: + self.shortOpt = [] + if len(self.longOpt) > 0: + self.longOpt = [] + return True + + def getAllOpt(self,typeOpt="all", optionsChapters=False): + """Получить все доступные опции + + Параметры: + typeOpt 'short'/'long'/'all', вернуть короткие или длинные + опции или все (возвращаются кортежем) + optionsChapters фильтр для опций по типам разделов (список,кортеж) + + Возвращаемые параметры: + строка коротки или список строк длинных опций ('hb:c:wg:G:k:ms:u:') + """ + # Выдать все действующие опции + if typeOpt=="short" or typeOpt=="all": + if len(self.shortOpt) == 0: + for par in self.data: + if optionsChapters and\ + self.getTypeChapter(par['helpChapter'])=="options": + if not (par['helpChapter'] in optionsChapters): + continue + if par.has_key("shortOption") and self.access(par): + if par.has_key("optVal"): + self.shortOpt.append(par["shortOption"]+':') + else: + self.shortOpt.append(par["shortOption"]) + if typeOpt=="long" or typeOpt=="all": + if len(self.longOpt) == 0: + for par in self.data: + if optionsChapters and\ + self.getTypeChapter(par['helpChapter'])=="options": + #print par["longOption"] + if not (par['helpChapter'] in optionsChapters): + continue + if par.has_key("longOption") and self.access(par): + if par.has_key("optVal"): + self.longOpt.append(par["longOption"]+'=') + else: + self.longOpt.append(par["longOption"]) + if typeOpt=="short": + return "".join(self.shortOpt) + elif typeOpt=="long": + return self.longOpt + elif typeOpt=="all": + return ("".join(self.shortOpt),self.longOpt) + + def getShortOpt(self,option): + """Из любой опции получить короткую опцию. + + Фильтрация также происходит и по названию команды. + + Параметры: + option запрашиваемая опция + + Возвращаемые параметры: + короткая опция, если же для длинной опции нет короткой, возвращается + пустая строка. + """ + # Из любой опции получаем короткую опцию + for par in self.data: + if par.has_key("shortOption") and self.access(par): + if (par.has_key("longOption") and\ + par["longOption"] == option) or \ + par["shortOption"] == option: + return par["shortOption"] + return "" diff --git a/build/lib/calculate-lib/pym/cl_lang.py b/build/lib/calculate-lib/pym/cl_lang.py new file mode 100644 index 0000000..eb0cf69 --- /dev/null +++ b/build/lib/calculate-lib/pym/cl_lang.py @@ -0,0 +1,168 @@ +#-*- 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 os, gettext +from cl_overriding import __findFileMO + +class GlobalParam(type): + """ Метакласс для глобальных параметров + """ + def __init__(cls, *args): + cls.GP = [] + cls.GP.append("") + +gettext.find = __findFileMO + +class lang: + """Класс многоязыковой поддержки lang для перевода сообщений программ на +другие языки. + +Типичное использование: + import sys + import lang + + # язык сообщений английский + tr = lang.lang(en) + # язык определяется системой + #tr = lang.lang() + + #Установка домена переводаldap + # в последующем можно не использовать - задается глобально + tr.setGlobalDomain('calc') + # задается локально для одного файла + #tr.setLocalDomain('calc') + + # Установка метода перевода для текущего модуля + tr.setLanguage(sys.modules[__name__]) + +Где: + tr -- объект перевода + 'en' -- язык выводимых сообщений + sys.modules[__name__] -- модуль сообщения которого переводятся + calc - домен перевода - имя файла перевода без расширения + +Если файл перевода не найден то сообщения не переводятся +Если в модуле сообщения которого переводим, экспортируются другие модули то +они тоже переводятся. + +По умолчанию директория в которой находятся переводы: 'lang/i18h' относительно +исполняемого файла названия файлов перевода совпадают с названиями модулей +если не определен методом setDomainTranslate() - домен перевода. + """ + __metaclass__ = GlobalParam + def __init__(self,l=''): + self.nameDomain = self.GP[0] + #self.nameDomain = '' + """ Название файла перевода (Домен) если используется 1 файл перевода + """ + self.__catalog = os.path.abspath('/usr/share/calculate/i18n') + """ Путь к каталогу переводов (в этом каталоге + ru_RU/LC_MESSAGES в котором файл перевода) + """ + env = os.environ + if l == "" and env.has_key('LANG'): + l = env['LANG'].split('.')[0].split("_")[0] + """ Определение языка """ + self.__modnames = {} + """ Словарь переведенных модулей + ключ --- имя модуля + значение --- был ли модуль переведен (1 или 0) + """ + self.__l = l + """Язык перевода для всех модулей""" + + def __translate(self,message): + """Метод translate возвращает полученное значение без +изменений""" + return message + + def setLanguage(self,module): + """ Установка языка перевода для модуля module. + + параметр --- экспортируемый модуль python + если в этом модуле экспортируются другие модули + то язык устанавливается и для них + Метод запускается после экспорта модуля который будем переводить + """ + t = vars(module) + for i in dir(module): + q = str(t[i]) + if 'module' in q and not '__' in i and not '/usr/lib' in q\ + and not 'built-in' in q : + mod = vars(module)[i] + self.__setLang(mod) + return self.__setLang(module) + + def __setLang(self,module): + """ Установка языка перевода для модуля module. + + В случае нахождения файла перевода возвращает истину. + Во всех случаях устанавливает метод перевода для модуля. + Если нет файла перевода метод перевода возвращает то же + значение что получает + """ + if module.__name__ in self.__modnames.keys(): + return True + + if self.nameDomain == '': + if module.__name__ == "__main__": + nameDomain = module.__file__.split('.')[0] + else: + nameDomain = module.__name__ + else: + nameDomain = self.nameDomain + + if self.__l == 'en': + module._ = self.__translate + ret = 1 + else: + la = [] + la.append(self.__l) + if gettext.find(nameDomain,self.__catalog,la): + """Если найден словарь то инициализируем переводчик""" + transl = gettext.translation(nameDomain\ + ,self.__catalog,la) + + #module._ = transl.ugettext + module._ = transl.gettext + ret = 1 + else: + module._ = self.__translate + ret = 0 + self.__modnames[module.__name__] = ret + return ret + + def getTranslatorByName(self,namemodule): + """ Метод который по имени модуля определяет, был ли модуль с этим + именем переведен + """ + if self.__modnames.has_key(namemodule): + return self.__modnames[namemodule] + return 0 + + def setGlobalDomain(self, nameDomain): + """ Метод для установки домена перевода (глобально для всех файлов) + """ + self.GP[0] = nameDomain + self.nameDomain = self.GP[0] + return True + + def setLocalDomain(self, nameDomain): + """ Метод для установки домена перевода (локально для одного файла) + """ + self.nameDomain = nameDomain + return True + diff --git a/build/lib/calculate-lib/pym/cl_ldap.py b/build/lib/calculate-lib/pym/cl_ldap.py new file mode 100644 index 0000000..bea5fae --- /dev/null +++ b/build/lib/calculate-lib/pym/cl_ldap.py @@ -0,0 +1,58 @@ +#-*- 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 ldap +from cl_utils import _error + +class ldapFun(_error): + '''Объект для работы с LDAP сервером + + подключение к серверу и поиск данных + ''' + def __init__(self, dnUser, password, host="localhost"): + self.conLdap = False + # Получаем соединение с LDAP + try: + self.conLdap = self.__ldapConnect(dnUser, password, host) + except ldap.LDAPError, e: + self.setError(e[0]['desc']) + + def __ldapConnect(self, dnUser, password, host): + """Соединение с LDAP сервером""" + conLdap = ldap.initialize('ldap://%s'%host) + conLdap.simple_bind_s(dnUser, password) + return conLdap + + def ldapSearch(self,baseDN, searchScope, searchFilter, retrieveAttributes): + try: + ldap_result_id = self.conLdap.search(baseDN, searchScope, + searchFilter, + retrieveAttributes) + result_set = [] + while 1: + result_type, result_data = self.conLdap.result(ldap_result_id, + 0) + if (result_data == []): + break + else: + if result_type == ldap.RES_SEARCH_ENTRY: + result_set.append(result_data) + except ldap.NO_SUCH_OBJECT: + return [] + except: + return False + return result_set + diff --git a/build/lib/calculate-lib/pym/cl_log.py b/build/lib/calculate-lib/pym/cl_log.py new file mode 100644 index 0000000..ab65d0e --- /dev/null +++ b/build/lib/calculate-lib/pym/cl_log.py @@ -0,0 +1,60 @@ +#-*- 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 os +import time + +class log(): + """Класс для записи в лог""" + # Директория хранения логов + logDir = "/var/log/calculate" + def __init__(self,fileName, addDataTime=True): + self.logFile = os.path.join(self.logDir,fileName) + if not os.path.exists(self.logDir): + os.makedirs(self.logDir) + self.addDataTime = addDataTime + + def addLog(self, textLog): + """Добавляет текст в лог файл""" + if not os.path.exists(self.logFile): + try: + fd = os.open(self.logFile, os.O_CREAT,0600) + os.close(fd) + except: + print "Error creating file %s"%self.logFile + return False + textWrite = textLog + if not textLog[-1:] == "\n" : + textWrite = "%s\n"%textLog + if self.addDataTime: + textWrite = "%s %s"%(time.strftime("%d/%m/%Y %H:%M:%S",\ + time.localtime()),textWrite) + try: + FD = open (self.logFile, "a") + FD.write(textWrite) + FD.close() + except: + print "Error writing to file %s"%self.logFile + return False + return True + + def writeError(self, textLog): + """Добавляет сообщение об ошибке в log""" + return self.addLog("ERROR: %s" %textLog) + + def writeSuccess(self, textLog): + """Добавляет сообщение об успехе в log""" + return self.addLog("SUCCESS: %s" %textLog) diff --git a/build/lib/calculate-lib/pym/cl_overriding.py b/build/lib/calculate-lib/pym/cl_overriding.py new file mode 100644 index 0000000..0c6ca8e --- /dev/null +++ b/build/lib/calculate-lib/pym/cl_overriding.py @@ -0,0 +1,58 @@ +#-*- 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 os,sys,gettext + +def __findFileMO(domain, localedir=None, languages=None, all=0): + """Модифицированный метод, ищет файл перевода + + замена gettext.find""" + if localedir is None: + localedir = _default_localedir + if languages is None: + languages = [] + for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'): + val = os.environ.get(envar) + if val: + languages = val.split(':') + break + if 'C' not in languages: + languages.append('C') + # now normalize and expand the languages + nelangs = [] + for lang in languages: + for nelang in gettext._expand_lang(lang): + if nelang not in nelangs: + nelangs.append(nelang) + # select a language + if all: + result = [] + else: + result = None + for lang in nelangs: + if lang == 'C': + break + mofile = os.path.join(localedir, '%s_%s.mo' % (domain,lang)) + if os.path.exists(mofile): + if all: + result.append(mofile) + else: + return mofile + return result + +def exit(codeExit): + """Метод выхода из программы""" + sys.exit(codeExit) \ No newline at end of file diff --git a/build/lib/calculate-lib/pym/cl_print.py b/build/lib/calculate-lib/pym/cl_print.py new file mode 100644 index 0000000..1298357 --- /dev/null +++ b/build/lib/calculate-lib/pym/cl_print.py @@ -0,0 +1,217 @@ +#-*- 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, struct, termios, fcntl +from cl_utils import _toUNICODE + +class color_print(object): + + def getconsolewidth(self): + """Получить ширину текущей консоли""" + s = struct.pack("HHHH", 0, 0, 0, 0) + fd_stdout = sys.stdout.fileno() + try: + x = fcntl.ioctl(fd_stdout, termios.TIOCGWINSZ, s) + except IOError: + # если ошибка то ширина 80 символов + return 80 + #(rows, cols, x pixels, y pixels) + return struct.unpack("HHHH", x)[1] + + def printRight(self, offsetLeft, offsetRight): + """Добавляет необходимое количество пробелов: + + количество пробелов = (ширина консоли - offsetLeft - offsetRight) + """ + cols = self.getconsolewidth() + for i in range(cols - offsetLeft - offsetRight): + sys.stdout.write(" ") + + def colorPrint(self,attr,fg,bg,string): + """Раскрашивает выводимое сообщение + + Параметры: + attr - это атрибут + fg - цвет символа + bg - цвет фона + + в случае если параметр равен "" то он не изменяется + + attr может принимать следующие значения: + 0 сбросить все атрибуты (вернуться в нормальный режим) + 1 яркий (обычно включает толстый шрифт) + 2 тусклый + 3 подчёркнутый + 5 мигающий + 7 реверсный + 8 невидимый + + fg может принимать следующие значения: + 30 чёрный + 31 красный + 32 зелёный + 33 жёлтый + 34 синий + 35 фиолетовый + 36 голубой + 37 белый + + bg может принимать следующие значения: + 40 чёрный + 41 красный + 42 зелёный + 43 жёлтый + 44 синий + 45 фиолетовый + 46 голубой + 47 белый + """ + lst = [] + if attr: + lst.append(attr) + if fg: + lst.append(fg) + if bg: + lst.append(bg) + sys.stdout.write("\033[%sm%s\033[0m" %(";".join(lst),string)) + + def redBrightPrint(self, string): + """Печатает яркое красное сообщение""" + self.colorPrint("1","31","",string) + + def greenBrightPrint(self, string): + """Печатает яркое зеленое сообщение""" + self.colorPrint("1","32","",string) + + def yellowBrightPrint(self, string): + """Печатает яркое желтое сообщение""" + self.colorPrint("1","33","",string) + + def blueBrightPrint(self, string): + """Печатает яркое cинее сообщение""" + self.colorPrint("1","34","",string) + + def lenString(self, string): + """Получаем длинну строки""" + stringUnicode = _toUNICODE(string) + lenString = len(stringUnicode) + return lenString + + + def defaultPrint(self, string): + sys.stdout.write(string) + sys.stdout.flush() + + def printLine(self, argL, argR, offsetL=0, printBR=True): + """Печатает справа и слева консоли цветные сообщения""" + #Допустимые цвета + colorDict = {\ + # цвет по умолчанию + '':self.defaultPrint, + # ярко зеленый + 'greenBr':self.greenBrightPrint, + # ярко голубой + 'blueBr':self.blueBrightPrint, + # ярко красный + 'redBr':self.redBrightPrint, + # ярко желтый + 'yellowBr':self.yellowBrightPrint, + } + # cмещение от левого края консоли + #offsetL = 0 + for color,leftString in argL: + offsetL += self.lenString(leftString) + if colorDict.has_key(color): + # печатаем и считаем смещение + colorDict[color](leftString) + else: + colorDict[''](leftString) + # cмещение от правого края консоли + offsetR = 0 + for color,rightString in argR: + offsetR += self.lenString(rightString) + # Добавляем пробелы + if offsetR: + self.printRight(offsetL, offsetR) + for color,rightString in argR: + if colorDict.has_key(color): + # печатаем и считаем смещение + colorDict[color](rightString) + else: + colorDict[''](rightString) + if printBR: + print "" + + def printNotOK(self, string, offsetL=0, printBR=True): + """Вывод на печать в случае сбоя""" + self.printLine((('greenBr',' * '), + ('',string), + ), + (('blueBr','['), + ('redBr',' !! '), + ('blueBr',']'), + ), offsetL, printBR) + + def printOnlyNotOK(self, string, offsetL=0, printBR=True): + """Вывод на печать в случае сбоя""" + self.printLine((('', string),), + (('blueBr','['), + ('redBr',' !! '), + ('blueBr',']'), + ), offsetL, printBR) + + def printOK(self, string, offsetL=0, printBR=True): + """Вывод на печать в случае успеха""" + self.printLine((('greenBr',' * '), + ('',string), + ), + (('blueBr','['), + ('greenBr',' ok '), + ('blueBr',']'), + ), offsetL, printBR) + + def printOnlyOK(self, string, offsetL=0, printBR=True): + """Вывод на печать в случае успеха""" + self.printLine((('',string),), + (('blueBr','['), + ('greenBr',' ok '), + ('blueBr',']'), + ), offsetL, printBR) + + def printWARNING(self, string, offsetL=0, printBR=True): + """Вывод на печать предупреждения""" + self.printLine((('yellowBr',' * '), + ('',string), + ), + (('',''), + ), offsetL, printBR) + + def printERROR(self, string, offsetL=0, printBR=True): + """Вывод на печать предупреждения""" + self.printLine((('redBr',' * '), + ('',string), + ), + (('',''), + ), offsetL, printBR) + + def printSUCCESS(self, string, offsetL=0, printBR=True): + """Вывод на печать в случае успеха без [ok] справа""" + self.printLine((('greenBr',' * '), + ('',string), + ), + (('',''), + ), offsetL, printBR) diff --git a/build/lib/calculate-lib/pym/cl_string.py b/build/lib/calculate-lib/pym/cl_string.py new file mode 100644 index 0000000..757cbfb --- /dev/null +++ b/build/lib/calculate-lib/pym/cl_string.py @@ -0,0 +1,254 @@ +#-*- 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. + +from re import search, compile, S +from cl_utils import _toUNICODE + +def prettyColumnStr(*cols): + '''Функция преобразования строк в текстовые колонки. Если указанный текст + не помещается в колонку, то строка переносится на следующую этой же колонки + перенос текста идет по словам, и текст выравнивается по ширине колонки за + счет дополнительных пробелов между словами. Если в строке используется + перенос строки, то текст переносится не просто на следующую строку, а также + на следующую строку колонки, причем если используется \r текст выравнива- + ется по ширине, а если \n, то просто перевод строки. + + Параметры: + cols множестово пар: текст, ширина колонки, причем, если у последней + колонки не указывать ширину, то она будет выведена вся. + + Возвращаемые параметры: + строка, которую можно использовать для вывода на экран + + Пример: columnWrite( "Some text", 10, "Next column", 20 ) + ''' + # шаблон поиска переводов строк + wherenr = compile( '[\n\r]', S ) + retstr = "" + # перевести кортеж в список, т.к. изменяется + cols = list(cols) + # перевести текст в юникод, заодно перевести числа в строку + noconvert = False + space = u' ' + nospace = u'' + for i in xrange(0,len(cols),2): + cols[i] = _toUNICODE(cols[i]) + # флаг "есть еще текст для вывода" + repeat = True + while repeat: + # сбросить итератор на первый элемент + q = 0 + repeat = False + # пока не закончили перебирать параметры (перебираем по парам) + while q < len(cols): + # если это последний параметр, и для него не указана ширина + if q == len(cols)-1: + # выводим его полностью не смотря на ширину окна + retstr += cols[q] + " " + cols[q] = '' + else: + # вывести часть строки не больше указанной ширины колонки + partstr = cols[q][:cols[q+1]] + # искать перевод строки с полученной части + brfind = wherenr.search(partstr) + # если это не последняя колонка + if q + 2 < len(cols): + # добавить разделитель между колонками + cellspacing = space + else: + # разделитель не нужен + cellspacing = nospace + + # если перевод строки найден, то + if brfind != None: + # для текущего вывода в колонку + # берем часть строки до перевода + partstr = partstr[:brfind.start()] + # остальная часть идет в остаток (без перевода) + cols[q] = cols[q][brfind.start()+1:] +# # если используется перевод каретки +# if brfind.group() == '\r': +# # то выравниваем по ширине колонки +# partstr = partstr.ljust(cols[q+1], ' ') +# else: +# # добавить отступы чтобы закончить колонку + partstr = partstr.ljust(cols[q+1], ' ') + # если взята часть строки + elif len(partstr) == cols[q+1] and partstr != cols[q]: + # если взята часть строки (разрыв в слове) + if cols[q][cols[q+1]] != ' ': + # ищем ближайший пробел справа + spacepos = partstr.rfind(' ') + # если пробел найти не удалось + if spacepos == -1: + # то на вывод идет часть строки равной ширине + cols[q] = cols[q][cols[q+1]:] + # если пробел найден + else: + # обрезаем строку до найденного пробела + partstr = partstr[:spacepos] + cols[q] = cols[q][spacepos+1:] + # если взята часть строки (разрыв на пробеле) + else: + # ислючить переносной пробел + cols[q] = cols[q][cols[q+1]+1:] + # выровнить текст по ширине колонки + partstr = partstr.ljust(cols[q+1], ' ') + #partstr = justify(partstr, cols[q+1]) + # остатки строки + else: + # добавить отступы чтобы закончить колонку + partstr = partstr.ljust(cols[q+1], ' ') + cols[q] = '' + + retstr+= partstr + cellspacing + + # остальную часть строки оставить на следующую итерацию + # если от строки что то осаталось + if len(cols[q]) > 0: + # отметить запуск еще одной итерации по параметрам + repeat = True + # следующая пара + q += 2 + # колонки отображены + retstr += "\n" + return retstr.encode('utf8') + +def columnStr(*cols): + '''Вывод данных по колонкам, причем, если данные не вмещаются в указнаную + колонку, то они переносятся на следующую строку в нужную колонку. В строку. + + Параметры: + cols множестово пар: текст, ширина колонки, причем, если у последней + колонки не указывать ширину, то она будет выведена вся. + + Возвращаемые параметры: + строка, которую можно использовать для вывода на экран + + Пример: columnWrite( "Some text", 10, "Next column", 20 ) + ''' + retstr = "" + # перевести кортеж в список, т.к. изменяется + cols = list(cols) + # перевести текст в юникод, заодно перевести числа в строку + for i in xrange(0,len(cols),2): + cols[i] = (str(cols[i])).decode('utf8') + + # флаг "есть еще текст для вывода" + repeat = True + while repeat: + # сбросить итератор на первый элемент + q = 0 + repeat = False + # пока не закончили перебирать параметры (перебираем по парам) + while q < len(cols): + # если это последний параметр, и для него не указана ширина + if q == len(cols)-1: + # выводим его полностью не смотря на ширину окна + retstr += cols[q] + " " + cols[q] = '' + else: + # вывести часть строки не больше указанной ширины колонки + retstr+=(cols[q][:cols[q+1]].ljust(cols[q+1])).encode('utf8') \ + + " " + # остальную часть строки оставить на следующую итерацию + cols[q] = cols[q][cols[q+1]:] + # если от строки что то осаталось + if len(cols[q]) > 0: + # отметить запуск еще одной итерации по параметрам + repeat = True + # следующая пара + q += 2 + # колонки отображены + retstr += "\n" + return retstr + +def columnWrite(*cols): + '''Вывод данных по колонкам, причем, если данные не вмещаются в указнаную + колонку, то они переносятся на следующую строку в нужную колонку. + + Параметры: + cols множестово пар: текст, ширина колонки, причем, если у последней + колонки не указывать ширину, то она будет выведена вся. + + Пример: columnWrite( "Some text", 10, "Next column", 20 ) + ''' + # перевести кортеж в список, т.к. изменяется + cols = list(cols) + # перевести текст в юникод, заодно перевести числа в строку + for i in xrange(0,len(cols),2): + cols[i] = (str(cols[i])).decode('utf8') + + # флаг "есть еще текст для вывода" + repeat = True + while repeat: + # сбросить итератор на первый элемент + q = 0 + repeat = False + # пока не закончили перебирать параметры (перебираем по парам) + while q < len(cols): + # если это последний параметр, и для него не указана ширина + if q == len(cols)-1: + # выводим его полностью не смотря на ширину окна + print cols[q].encode('utf8'), + cols[q] = '' + else: + # вывести часть строки не больше указанной ширины колонки + print (cols[q][:cols[q+1]].ljust(cols[q+1])).encode('utf8'), + # остальную часть строки оставить на следующую итерацию + cols[q] = cols[q][cols[q+1]:] + # если от строки что то осаталось + if len(cols[q]) > 0: + # отметить запуск еще одной итерации по параметрам + repeat = True + # следующая пара + q += 2 + # колонки отображены + print + +def justify(s,width): + '''Выровнить текст по ширине + + Параметры: + s выводимая строка + width ширина на которую надо выровнить строку + + Возвращаямые параметры: + Выровненная строка + ''' + # если подана строка без пробелов - прекратить обработку + if s.find(' ') == -1: + return s + pos = 0 + # переводим в юникод для правильного вычисления длины + try: + s = s.decode( 'utf-8' ) + # пропуск если это не utf-8 + except UnicodeEncodeError: + pass + # пока длина строки меньше указанной + while len(s) < width: + # находим очередной пробел + pos = s.find( ' ', pos ) + # если не найден искать сначала + if pos == -1: + pos = s.find(' ') + # вставить в позицию еще один пробел + s = s[:pos] +' ' +s[pos:] + # оставить удвоенный пробел + pos += 3 + # вернуть строку в utf8 если она пришла в utf8 + return s.encode('utf-8') diff --git a/build/lib/calculate-lib/pym/cl_template.py b/build/lib/calculate-lib/pym/cl_template.py new file mode 100644 index 0000000..68f0c0f --- /dev/null +++ b/build/lib/calculate-lib/pym/cl_template.py @@ -0,0 +1,3774 @@ +#-*- 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 + "!" - drop удаляет + "+" - join добавляет + "-" - replace заменяет + """ + 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 = '' + docTxt += '%s'% version + docTxt += '%s' % typeDoc + docTxt += '' + 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 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)= 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[a-zA-Z0-9\-_]+)\ +(?P\([a-zA-Z0-9_\-\+\,\*\/\.]+\))?\ +(?P[\>\<\=\!\&\|]+\ +[a-zA-Z0-9\>\<\=\!\|\&\-\+\*\/_\.\,\(\)]*)#\ +\n*(?P.+?)\n*#(?P=rTerm)#(?P[ ,\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 diff --git a/build/lib/calculate-lib/pym/cl_utils.py b/build/lib/calculate-lib/pym/cl_utils.py new file mode 100644 index 0000000..5a29f48 --- /dev/null +++ b/build/lib/calculate-lib/pym/cl_utils.py @@ -0,0 +1,214 @@ +#-*- 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 subprocess +import string +from random import choice +import os +import types +import stat + + +def _toUNICODE(val): + """перевод текста в юникод""" + if type(val) == types.UnicodeType: + return val + else: + return str(val).decode('UTF-8') + +def runOsCommand(cmd, inStr=None, ret_first=None, env_dict=None): + """Выполняет внешнюю программу + + Параметры: + cmd внешняя программа + inStr данные передаваемые программе на страндартный вход. + ret_first вернуть только первую строку + env_dict словарь переменных окружения + Возвращаемые параметры: + строка/строки которую выведет внешняя программа + Возвращает код возврата, stdout+stderr + """ + pipe = subprocess.Popen(cmd, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env_dict, + close_fds=True, + shell=True) + fout, fin, ferr = (pipe.stdout, pipe.stdin, pipe.stderr) + # если есть данные на вход, передать их + if inStr: + fin.write(inStr) + fin.close() + # Код возврата + retcode = pipe.wait() + res = fout.readlines() + fout.close() + res += ferr.readlines() + ferr.close() + if res: + if len(res) == 1 or ret_first: + return retcode, res[0].strip() + else: + return retcode, res + return retcode, None + +def getpathenv(): + """вернуть пути для запуска программ""" + bindir=['/sbin','/bin','/usr/sbin','/usr/bin'] + env=os.environ + if env and env.has_key('PATH'): + lpath=env['PATH'].split(":") + npath=[] + for dirname in bindir: + if os.path.exists(dirname) and dirname not in lpath: + npath.append(dirname) + lpath=npath+lpath + return ":".join(lpath) + +def genpassword(passlen=9): + '''Вернуть случайный пароль указанной длины + + Параметры: + passlen длина пароля который нужно сгенерировать + + Возвращаемые параметры: + Сгенерированный пароль указанной длины + ''' + res=''.join([choice(string.ascii_letters+string.digits)\ + for i in xrange(passlen)]) + return res + +def fillstr(char, width): + '''Заполнить строку указанным числом символов. Псеводоним символ*кол-во''' + return str(char) * width + + +def list2str(list): + '''Функция переводит список в строку''' + return '['+','.join(list)+']' + +def str2list(s): + '''Функция переводит строку в список''' + return s[1:-1].split(',') + +def dict2str(dict): + '''Функция перводит словарь в строку''' + return '{'+','.join(["%s:%s" % (str(k),str(v)) \ + for (k,v) in dict.items()])+'}' #: + +def str2dict(s): + '''Функция переводит строку в словарь''' + dict = {} + for i in s[1:-1].split(','): + k,v = i.split(':') + dict[k] = v + return dict + +def convertStrListDict(val): + '''Функция определеяется что на входе (строка, список, словарь) + и переводит их в строку и обратно''' + # если подан список + if type(val) == types.ListType: + return list2str(val) + # если подан словарь + elif type(val) == types.DictType: + return dict2str(val) + # если подана строка + else: + # если поданная строка содержит словарь + if ':' in val and '{' in val: + return str2dict(val) + # если поданная строка содержит список + elif ',' in val and '[' in val: + return str2list(val) + # если это просто строка + else: + return val + +def getdirlist(s_path): + """Получить список директорий по указаному пути""" + return filter(lambda x: os.path.isdir(x), os.listdir(s_path)) + +class _error: + # Здесь ошибки, если они есть + error = [] + def getError(self): + """Выдать ошибки""" + if not self.error: + return False + error = "" + for e in self.error: + error += e + "\n" + return error + + def setError(self, error): + """Установка ошибки""" + if not error in self.error: + self.error.append(error) + return True + +class scan: + """Класс для сканирования директорий""" + class _dir: + """Класс для хранения директорий""" + def __init__(self): + # Базовая директория + self.baseDir = False + # Все директории в базовой включая вложенные + self.dirs = [] + # Все файлы внутри базовой директории + self.files = [] + # Все ссылки внутри базовой директории + self.links = [] + # Все сокеты внутри базовой директории + self.sockets = [] + + + def __scanDir(self, scanDir, dirObj, flagDir=False): + """Сканирование одной директории""" + if flagDir or stat.S_ISDIR(os.stat(scanDir)[stat.ST_MODE]): + for fileOrDir in os.listdir(scanDir): + absPath = os.path.join(scanDir,fileOrDir) + statInfo = os.stat(absPath)[stat.ST_MODE] + if stat.S_ISDIR(statInfo): + dirObj.dirs.append(absPath) + self.__scanDir(absPath, dirObj, True) + elif stat.S_ISLNK(statInfo): + dest = absPath + src = os.readlink(absPath) + dirObj.links.append((src,dest)) + elif stat.S_ISREG(statInfo): + dirObj.files.append(absPath) + elif stat.S_ISSOCK(statInfo): + dirObj.sockets.append(absPath) + return dirObj + + def scanDirs(self, scanDirs): + """Сканирование директорий на вход список + + Выход список объктов _dirProf + """ + dirs = [] + for scanDir in scanDirs: + dirP = scan._dir() + try: + self.__scanDir(scanDir, dirP) + except OSError, e: + print e.strerror, e.filename + return [] + dirP.baseDir = scanDir + dirs.append(dirP) + return dirs \ No newline at end of file diff --git a/build/lib/calculate-lib/pym/cl_vars.py b/build/lib/calculate-lib/pym/cl_vars.py new file mode 100644 index 0000000..4097326 --- /dev/null +++ b/build/lib/calculate-lib/pym/cl_vars.py @@ -0,0 +1,89 @@ +#-*- 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. + + +#Допустимые ключи значений +# mode - режим переменной r-не переназначается из командной строки, +# w-переназначается из командной строки +# type - тип переменной состоит из двух элементов(что это и для чего +# это) +# value - значение переменной + +class Data: + + # имя компьютера + os_net_hostname = {'mode':"w"} + # разрешенные сети + os_net_allow ={} + # ip на всех интерфейсах + os_net_ip ={} + + #короткое название системы (CLD) + os_linux_shortname={} + + #домен + os_net_domain = {'mode':"w"} + + # Пути к ini файлам + cl_env_path = {'value':['/var/calculate/remote/calculate.env', + '/var/calculate/calculate.env', + '/etc/calculate/calculate.env']} + + # локаль (прим: ru_RU.UTF-8) + os_locale_locale = {} + # язык (прим: ru_RU) + os_locale_lang = {} + # язык (прим: ru) + os_locale_language = {} + + # раскладка клавиатуры для X + os_locale_xkb = {} + + # названия используемых раскладок клавиатуры для X + os_locale_xkbname = {} + + # архитектура компьютера (i686,x86_64) + os_arch_machine = {} + + #проход при наложении шаблонов 1,2,3,4,5 и.т д + cl_pass_step = {'mode':"w"} + + # обрабатываемый файл шаблона + cl_pass_file = {'mode':"w"} + + # корневой раздел файловой системы + os_root_dev = {} + + # тип носителя (ram, hdd, usb-hdd, livecd) + os_root_type = {} + + # полное название системы + os_linux_name = {} + + # постфикс к названию системы + os_linux_subname = {} + + # название виртуальной машины (virtualbox, vmware, qemu) + hr_virtual = {} + + # версия системы + os_linux_ver = {} + + # Тип шаблона + cl_pass_type = {'mode':"w"} + + # Действие программы + cl_pass_run = {'mode':"w"} diff --git a/build/lib/calculate-lib/pym/format/__init__.py b/build/lib/calculate-lib/pym/format/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/calculate-lib/pym/format/apache.py b/build/lib/calculate-lib/pym/format/apache.py new file mode 100644 index 0000000..0b1ffc2 --- /dev/null +++ b/build/lib/calculate-lib/pym/format/apache.py @@ -0,0 +1,218 @@ +#-*- 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 re +from xml import xpath +from cl_template import blocText, xmlDoc +from format.bind import bind + +class apache(bind): + """Класс для обработки конфигурационного файла типа apache + + """ + _comment = "#" + configName = "apache" + configVersion = "0.1" + __headerArea = "[^\<\> \t]+[ \t]+[^\<\> \t]+" + __openArea = "[ \t]*\<%s\>"%(__headerArea) + __closeArea = "[ \t]*\<\/[^\<\>]+\>" + sepFields = "\n" + reOpen = re.compile(__openArea) + reClose = re.compile(__closeArea) + reCloseArea = re.compile(__closeArea + "\s*\Z") + reComment = re.compile("[ \t]*%s"%(_comment)) + reSepFields = re.compile(sepFields) + reSeparator = re.compile("[ \t]+") + reHeader = re.compile(__headerArea) + + def __init__(self,text): + self.text = text + self.blocTextObj = blocText() + # Объект документ + self.docObj = self.textToXML() + # Создаем поля-массивы + self.docObj.postParserList() + # Создаем поля разделенные массивы + self.docObj.postParserListSeplist(self.docObj.body) + # XML документ + self.doc = self.docObj.doc + + def postXML(self): + """Последующая постобработка XML""" + # Для добавления перевода строки перед закрывающим тегом + # конфигурационного файла + xmlFields = xpath.Evaluate("child::fields", self.docObj.body) + if not (xmlFields and\ + self.docObj.getTypeField(xmlFields[-1]) == "br"): + self.docObj.body.appendChild(self.docObj.createField("br", + [],"",[], + False,False)) + xmlAreas = xpath.Evaluate("child::area", self.docObj.body) + for xmlArea in xmlAreas: + xmlFields = xpath.Evaluate("child::field", xmlArea) + if not (xmlFields and\ + self.docObj.getTypeField(xmlFields[-1]) == "br"): + xmlArea.appendChild(self.docObj.createField("br", + [],"",[], + False,False)) + + def join(self, apacheObj): + """Объединяем конфигурации""" + if isinstance(apacheObj, apache): + #print self.docObj.doc.toprettyxml() + self.docObj.joinDoc(apacheObj.doc) + self.postXML() + + # Делим область на составные части + def findOpenClose(self, text, reOpen, reClose, reComment, reHeader): + """Делит область на составные части + + начальный текстовый блок, + открывающий блок, + блок-тело, + закрывающий блок + """ + firstBloc = "" + startBloc = "" + bodyBloc = "" + endBloc = "" + textLines = text.splitlines() + findOpen = False + if textLines: + findOpen = reOpen.search(textLines[0]) + openBl = reOpen.search(text) + if findOpen and reComment.split(text)[0].strip(): + blocA = text[openBl.end():] + firstBloc = "" + startBloc = text[openBl.start():openBl.end()] + headBl = reHeader.search(startBloc) + if headBl: + firstBloc = headBl.group(0) + closeBl = reClose.search(blocA) + endBloc = blocA[closeBl.start():closeBl.end()] + bodyBloc = blocA[:closeBl.start()] + return (firstBloc, startBloc, bodyBloc, endBloc) + else: + return (firstBloc, startBloc, text, endBloc) + + # Делим текст на области включая вложенные (areas массив областей) + def splitToAllArea(self, text, areas, reOpen, reClose, reCloseArea, + reComment, reSepFields, reHeader): + """Делит текст на области включая вложенные + + возвращает список объектов областей (переменная areas) + """ + class area: + def __init__(self): + self.header = False + self.start = False + self.fields = [] + self.end = False + + blocs = self.blocTextObj.splitTxtToBloc(text,reOpen,reClose, + reComment,reSepFields) + for i in blocs: + areaA = area() + first,start,body,end = self.findOpenClose(i, reOpen, reCloseArea, + reComment, reHeader) + areaA.header = first.replace(" ","").replace("\t","") + areaA.start = start + areaA.end = end + + if areaA.end: + blocsA = self.blocTextObj.splitTxtToBloc(body,reOpen,reClose, + reComment,reSepFields) + if blocsA and blocsA[0] == body: + areaA.fields.append(body) + areas.append(areaA) + else: + for ar in blocsA: + self.splitToAllArea(ar, areaA.fields, reOpen, + reClose, + reCloseArea, reComment, + reSepFields, reHeader) + areas.append(areaA) + else: + areaA.fields.append(body) + areas.append(areaA) + + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + #print "#"+brBloc[z]+"#" + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len (nameValue) == 1: + field.name = "" + field.value = textLine.replace(self.sepFields,"") + field.br = textLine + fields.append(field) + field = fieldData() + + if len(nameValue) == 3: + valueList = nameValue[2:] + nameValue =["".join(nameValue[:2])," ".join(valueList)] + + if len(nameValue) > 3: + valueList = nameValue[1:] + nameValue =[nameValue[0]," ".join(valueList).replace(\ + self.sepFields,"")] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + + def textToXML(self): + """Преобразуем тект в XML""" + areas = [] + self.splitToAllArea(self.text, areas, self.reOpen, self.reClose, + self.reCloseArea,self.reComment,self.reSepFields, + self.reHeader) + docObj = xmlDoc() + + # Создание объекта документ c пустым разделителем между полями + docObj.createDoc(self.configName, self.configVersion) + if not areas: + return docObj + self.createXML(areas, docObj.getNodeBody(), docObj) + return docObj \ No newline at end of file diff --git a/build/lib/calculate-lib/pym/format/bind.py b/build/lib/calculate-lib/pym/format/bind.py new file mode 100644 index 0000000..7028196 --- /dev/null +++ b/build/lib/calculate-lib/pym/format/bind.py @@ -0,0 +1,315 @@ +#-*- 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 re +from cl_template import objShare, blocText, xmlDoc + +class bind(objShare): + """Класс для обработки конфигурационного файла типа bind + + """ + _comment = "//" + configName = "bind" + configVersion = "0.1" + __openArea = "{" + __closeArea = "[ \t]*\}[ \t]*;[ \t]*" + sepFields = ";" + reOpen = re.compile(__openArea) + reClose = re.compile(__closeArea) + reCloseArea = re.compile(__closeArea + "\s*\Z") + reComment = re.compile("[ \t]+%s|^%s|(?<=;)%s"%(_comment,_comment,_comment)) + reSepFields = re.compile(sepFields) + reSeparator = re.compile("[ \t]+") + + def __init__(self,text): + self.text = text + self.blocTextObj = blocText() + # Объект документ + self.docObj = self.textToXML() + # Создаем поля-массивы + self.docObj.postParserList() + # XML документ + self.doc = self.docObj.doc + + # Делим область на составные части + def findOpenClose(self, text, reOpen, reClose, reComment): + """Делит область на составные части + + начальный текстовый блок, + открывающий блок, + блок-тело, + закрывающий блок + """ + firstBloc = "" + startBloc = "" + bodyBloc = "" + endBloc = "" + textLines = text.splitlines() + findOpen = False + if textLines: + findOpen = reOpen.search(textLines[0]) + openBl = reOpen.search(text) + if findOpen and reComment.split(text)[0].strip(): + blocA = text[openBl.end():] + firstBloc = text[:openBl.start()] + startBloc = text[openBl.start():openBl.end()] + closeBl = reClose.search(blocA) + endBloc = blocA[closeBl.start():closeBl.end()] + bodyBloc = blocA[:closeBl.start()] + return (firstBloc, startBloc, bodyBloc, endBloc) + else: + return (firstBloc, startBloc, text, endBloc) + + + # Делим текст на области включая вложенные (areas массив областей) + def splitToAllArea(self, text, areas, reOpen, reClose, reCloseArea, + reComment, reSepFields): + """Делит текст на области включая вложенные + + возвращает список объектов областей (переменная areas) + """ + class area: + def __init__(self): + self.header = False + self.start = False + self.fields = [] + self.end = False + + blocs = self.blocTextObj.splitTxtToBloc(text,reOpen,reClose, + reComment,reSepFields) + for i in blocs: + areaA = area() + first,start,body,end = self.findOpenClose(i, reOpen, reCloseArea, + reComment) + areaA.header = first.replace(" ","").replace("\t","") + areaA.start = first + start + areaA.end = end + + if areaA.end: + blocsA = self.blocTextObj.splitTxtToBloc(body,reOpen,reClose, + reComment,reSepFields) + if blocsA and blocsA[0] == body: + areaA.fields.append(body) + areas.append(areaA) + else: + for ar in blocsA: + self.splitToAllArea(ar, areaA.fields, reOpen, + reClose, + reCloseArea, reComment, + reSepFields) + areas.append(areaA) + else: + areaA.fields.append(body) + areas.append(areaA) + return areas + + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len (nameValue) == 1: + field.name = "" + field.value = textLine.replace(self.sepFields,"") + field.br = textLine + fields.append(field) + field = fieldData() + + if len(nameValue) > 2: + valueList = nameValue[1:] + nameValue =[nameValue[0]," ".join(valueList).replace(\ + self.sepFields,"")] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + def createCaptionTerm(self, header, start, end, docObj): + """Создание пустой области с заголовком + + при создании области проверяется первый символ заголовка + и добавляется тег action + "!" - drop + "-" - replace + """ + areaAction = False + if header: + if header[0] == "!": + docObj.createCaption(header[1:], [start, + end.replace("\n","")]) + areaAction = "drop" + elif header[0] == "-": + docObj.createCaption(header[1:], [start, + end.replace("\n","")]) + areaAction = "replace" + else: + docObj.createCaption(header, [start, + end.replace("\n","")]) + else: + docObj.createCaption(header, [start, + end.replace("\n","")]) + + areaXML = docObj.createArea() + if areaAction: + docObj.setActionArea(areaXML, areaAction) + return areaXML + + def createXML(self, areas, rootNode, docObj): + """Создаем из массивов областей XML""" + for i in areas: + if str(i.__class__.__name__) == "area": + if i.header and i.start: + areaXML = self.createCaptionTerm(i.header, i.start, + i.end.replace("\n",""), + docObj) + else: + areaXML = rootNode + for f in i.fields: + if str(f.__class__.__name__) == "area": + if f.header and f.start: + areaXMLChild = self.createCaptionTerm(f.header, + f.start, + f.end.replace("\n",""), + docObj) + + self.createXML(f.fields, areaXMLChild, docObj) + + areaXML.appendChild(areaXMLChild) + else: + self.createXML(f.fields, areaXML, docObj) + if "\n" in f.end: + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + areaXML.appendChild(fieldXMLBr) + else: + if not f: + continue + fields = self.splitToFields(f) + for field in fields: + if field.name != False: + fieldXML = self.createFieldTerm(field.name, + field.value, + field.br, docObj) + areaXML.appendChild(fieldXML) + if field.br[-1] == "\n": + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + areaXML.appendChild(fieldXMLBr) + elif field.comment != False: + fieldXML = docObj.createField("comment", + [field.comment], + "", [], + False, False) + areaXML.appendChild(fieldXML) + elif field.br != False: + brText = field.br.replace("\n","") + if brText: + fieldXML = docObj.createField('br', + [brText], + "", [], + False, False) + else: + fieldXML = docObj.createField('br', + [], + "", [], + False, False) + areaXML.appendChild(fieldXML) + + if i.header and i.start: + rootNode.appendChild(areaXML) + if "\n" in i.end: + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + rootNode.appendChild(fieldXMLBr) + + else: + fields = self.splitToFields(i) + for field in fields: + if field.name != False: + fieldXML = self.createFieldTerm(field.name, + field.value, + field.br, docObj) + rootNode.appendChild(fieldXML) + if field.br[-1] == "\n": + fieldXMLBr = docObj.createField("br",[],"", [], + False, False) + rootNode.appendChild(fieldXMLBr) + elif field.comment != False: + fieldXML = docObj.createField("comment", + [field.comment], + "", [], + False, False) + rootNode.appendChild(fieldXML) + elif field.br != False: + brText = field.br.replace("\n","") + if brText: + fieldXML = docObj.createField('br', [brText],"",[], + False, False) + else: + fieldXML = docObj.createField('br', [], "", [], + False, False) + rootNode.appendChild(fieldXML) + #rootNode.appendChild(areaXML) + + def textToXML(self): + """Преобразуем текст в XML""" + areas = [] + if self.text.strip(): + self.splitToAllArea(self.text, areas, self.reOpen, self.reClose, + self.reCloseArea,self.reComment,self.reSepFields) + docObj = xmlDoc() + # Создание объекта документ c пустым разделителем между полями + docObj.createDoc(self.configName, self.configVersion) + if not areas: + return docObj + self.createXML(areas, docObj.getNodeBody(), docObj) + return docObj + + + def join(self, bindObj): + """Объединяем конфигурации""" + if isinstance(bindObj, bind): + self.docObj.joinDoc(bindObj.doc) diff --git a/build/lib/calculate-lib/pym/format/compiz.py b/build/lib/calculate-lib/pym/format/compiz.py new file mode 100644 index 0000000..40ae487 --- /dev/null +++ b/build/lib/calculate-lib/pym/format/compiz.py @@ -0,0 +1,41 @@ +#-*- 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 re +from format.samba import samba + +class compiz(samba): + """Класс для обработки конфигурационного файла типа compiz + + """ + _comment = "#" + configName = "compiz" + configVersion = "0.1" + reHeader = re.compile("^[\t ]*\[[^\[\]]+\].*\n",re.M) + reBody = re.compile(".+",re.M|re.S) + reComment = re.compile("\s*%s.*"%(_comment)) + reSeparator = re.compile("\s*=\s*") + sepFields = "\n" + reSepFields = re.compile(sepFields) + + def __init__(self,text): + samba.__init__(self,text) + + def join(self, compizObj): + """Объединяем конфигурации""" + if isinstance(compizObj, compiz): + self.docObj.joinDoc(compizObj.doc) + self.postXML() diff --git a/build/lib/calculate-lib/pym/format/dhcp.py b/build/lib/calculate-lib/pym/format/dhcp.py new file mode 100644 index 0000000..4f47666 --- /dev/null +++ b/build/lib/calculate-lib/pym/format/dhcp.py @@ -0,0 +1,100 @@ +#-*- 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 re +from format.bind import bind + +class dhcp(bind): + """Класс для обработки конфигурационного файла типа dhcp + + """ + _comment = "#" + configName = "dhcp" + configVersion = "0.1" + __openArea = "{" + __closeArea = "[ \t]*\}[ \t]*" + sepFields = ";" + reOpen = re.compile(__openArea) + reClose = re.compile(__closeArea) + reCloseArea = re.compile(__closeArea + "\s*\Z") + reComment = re.compile("^[ \t]*%s"%(_comment)) + reSepFields = re.compile(sepFields) + reSeparator = re.compile("[ \t]+") + + def __init__(self,text): + bind.__init__(self,text) + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len (nameValue) == 1: + field.name = textLine.replace(self.sepFields,"").strip() + field.value = "" + field.br = textLine + fields.append(field) + field = fieldData() + + if len(nameValue) > 2: + nameCheck = nameValue[0] + if nameValue[0][:1] in ["+","-","!"]: + nameCheck = nameValue[0][1:] + if nameCheck == "option" or nameCheck == "hardware" or\ + nameCheck == "set": + valueList = nameValue[2:] + nameValue =[nameValue[0]+nameValue[1], + " ".join(valueList).replace(\ + self.sepFields,"")] + else: + valueList = nameValue[1:] + nameValue =[nameValue[0]," ".join(valueList).replace(\ + self.sepFields,"")] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + def join(self, dhcpObj): + """Объединяем конфигурации""" + if isinstance(dhcpObj, dhcp): + self.docObj.joinDoc(dhcpObj.doc) diff --git a/build/lib/calculate-lib/pym/format/dovecot.py b/build/lib/calculate-lib/pym/format/dovecot.py new file mode 100644 index 0000000..5130771 --- /dev/null +++ b/build/lib/calculate-lib/pym/format/dovecot.py @@ -0,0 +1,65 @@ +#-*- 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 re +from xml import xpath +from format.bind import bind + +class dovecot(bind): + """Класс для обработки конфигурационного файла типа dovecot + + """ + _comment = "#" + configName = "dovecot" + configVersion = "0.1" + __openArea = "{" + __closeArea = "[ \t]*\}[ \t]*" + sepFields = "\n" + reOpen = re.compile(__openArea) + reClose = re.compile(__closeArea) + reCloseArea = re.compile(__closeArea + "\s*\Z") + reComment = re.compile("[ \t]*%s" %(_comment)) + reSepFields = re.compile(sepFields) + # разделитель названия и значения переменной + reSeparator = re.compile("\s*=\s*") + + def __init__(self, text): + bind.__init__(self,text) + + def postXML(self, xmlArea=False): + """Последующая постобработка XML""" + # Добавляем перевод строки если его нет в конец области + if not xmlArea: + xmlArea = self.docObj.body + xmlFields = xpath.Evaluate("child::field", xmlArea) + if xmlFields and not (\ + self.docObj.getTypeField(xmlFields[-1]) == "br" or\ + self.docObj.getTypeField(xmlFields[-1]) == "comment"): + xmlArea.appendChild(self.docObj.createField("br", + [],"",[], + False,False)) + xmlAreas = xpath.Evaluate("child::area", xmlArea) + for area in xmlAreas: + self.postXML(area) + + def join(self, dovecotObj): + """Объединяем конфигурации""" + if isinstance(dovecotObj, dovecot): + #print self.docObj.doc.toprettyxml() + self.docObj.joinDoc(dovecotObj.doc) + # Для добавления перевода строки перед закрывающим тегом + # конфигурационного файла + self.postXML() \ No newline at end of file diff --git a/build/lib/calculate-lib/pym/format/kde.py b/build/lib/calculate-lib/pym/format/kde.py new file mode 100644 index 0000000..5c9b3fd --- /dev/null +++ b/build/lib/calculate-lib/pym/format/kde.py @@ -0,0 +1,160 @@ +#-*- 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 re +from xml import xpath +from cl_template import xmlDoc +from format.samba import samba + +class kde(samba): + """Класс для обработки конфигурационного файла типа kde + + """ + _comment = "#" + configName = "kde" + configVersion = "0.1" + reHeader = re.compile("^[\t ]*\[[^\[\]]+\].*\n?",re.M) + reBody = re.compile(".+",re.M|re.S) + reComment = re.compile("^\s*%s.*"%(_comment)) + reSeparator = re.compile("=") + sepFields = "\n" + reSepFields = re.compile(sepFields) + + def __init__(self,text): + samba.__init__(self,text) + + + def _textToXML(self): + """Преобразует текст в XML""" + blTmp = self.blocTextObj.findBloc(self.text,self.reHeader,self.reBody) + blocs = self.getFullAreas(blTmp) + headers = [] + startHeaders = [] + finHeaders = [] + docObj = xmlDoc() + docObj.createDoc(self.configName, self.configVersion) + rootNode = docObj.getNodeBody() + # Если пустой текст то создаем пустой документ + if not blocs: + return docObj + + for h in blocs[0]: + reH = re.compile("]\s*$") + #listfinH = h.split("]") + listfinH = reH.split(h) + finH = listfinH[0] + if "[" in finH: + startHeaders.append(finH + "]") + else: + startHeaders.append(finH) + if len(listfinH) == 2: + finHeaders.append(listfinH[1]) + else: + finHeaders.append("") + head=finH.replace("][",".").replace("[","").replace("]","").strip() + headers.append(head) + bodys = blocs[1] + + z = 0 + for h in headers: + if not bodys[z]: + z += 1 + continue + areaAction = False + if h: + if h[0] == "!": + docObj.createCaption(h[1:], [startHeaders[z],""]) + areaAction = "drop" + elif h[0] == "-": + docObj.createCaption(h[1:], [startHeaders[z],""]) + areaAction = "replace" + else: + docObj.createCaption(h, [startHeaders[z],""]) + else: + docObj.createCaption(h, [startHeaders[z],""]) + + if "\n" in blocs[0][z]: + if self.reComment.search(finHeaders[z]): + docObj.createField('comment', [finHeaders[z]]) + elif not finHeaders[z].strip() and\ + finHeaders[z].replace("\n",""): + docObj.createField('br', + [finHeaders[z].replace("\n","")]) + else: + docObj.createField('br') + fields = self._splitToFields(bodys[z]) + for f in fields: + if f.name != False and f.value!=False and f.br!=False: + # Обработка условий для samba + if f.name[0] == "!" or f.name[0] == "-" or\ + f.name[0] == "+": + qns = self.removeSymbolTerm(f.br) + xmlField = docObj.createField("var", + [qns], + f.name[1:], [f.value]) + if f.name[0] == "!": + # Удаляемое в дальнейшем поле + docObj.setActionField(xmlField, "drop") + else: + docObj.createField("var",[f.br.replace("\n","")], + f.name, [f.value]) + docObj.createField('br') + elif f.comment != False: + docObj.createField('comment', [f.comment]) + elif f.br != False: + docObj.createField('br', [f.br.replace("\n","")]) + if h.strip(): + area = docObj.createArea() + if areaAction: + docObj.setActionArea(area, areaAction) + rootNode.appendChild(area) + else: + fieldsNodes = docObj.tmpFields.getFields() + for fieldNode in fieldsNodes: + rootNode.appendChild(fieldNode) + docObj.clearTmpFields() + z += 1 + #print docObj.doc.toprettyxml() + return docObj + + def postXML(self): + """Последующая постобработка XML""" + # Для добавления перевода строки между областями если его нет + #print self.docObj.body.toprettyxml() + xmlAreas = xpath.Evaluate("child::area", self.docObj.body) + for xmlArea in xmlAreas: + if xmlArea.previousSibling and\ + self.docObj.getTypeField(xmlArea.previousSibling) == "br": + continue + firstArea = False + xmlFields = xpath.Evaluate("child::field", xmlArea) + if not (xmlFields and\ + (self.docObj.getTypeField(xmlFields[-1]) == "br" or\ + self.docObj.getTypeField(xmlFields[-1]) == "comment")): + if xmlArea.nextSibling: + parentNode = xmlArea.parentNode + nextNode = xmlArea.nextSibling + parentNode.insertBefore(self.docObj.createField("br", + [],"",[], + False,False), + nextNode) + + def join(self, kdeObj): + """Объединяем конфигурации""" + if isinstance(kdeObj, kde): + self.docObj.joinDoc(kdeObj.doc) + self.postXML() + diff --git a/build/lib/calculate-lib/pym/format/ldap.py b/build/lib/calculate-lib/pym/format/ldap.py new file mode 100644 index 0000000..5df868c --- /dev/null +++ b/build/lib/calculate-lib/pym/format/ldap.py @@ -0,0 +1,183 @@ +#-*- 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 re +from cl_template import blocText, xmlDoc +from format.samba import samba + +class ldap(samba): + """Класс для обработки конфигурационного файла типа ldap + + """ + _comment = "#" + configName = "ldap" + configVersion = "0.1" + # Регулярное выражение для заголовка области + reHeader = re.compile("^[\t ]*(access|syncrepl)[^\n]+\n?") + # Регулярное выражения для области + reArea = re.compile("([\t ]*(access|syncrepl)[^\n]+\ +\n([\t ]+[^\n]+\n?)+)",re.M|re.S) + reComment = re.compile("\s*%s.*"%(_comment)) + # разделитель между переменной и значением переменной + reSeparator = re.compile("\s+|\s*=\s*") + # разделитель полей + sepFields = "\n" + # регулярное выражение для разделителя полей + reSepFields = re.compile(sepFields) + + def __init__(self,text): + self.text = text + self.blocTextObj = blocText() + self._splitToFields = self.splitToFields + # Объект документ + self.docObj = self._textToXML() + # Создаем поля-массивы + self.docObj.postParserList() + # Создаем поля разделенные массивы + self.docObj.postParserListSeplist(self.docObj.body) + # XML документ + self.doc = self.docObj.doc + + def join(self, ldapObj): + """Объединяем конфигурации""" + if isinstance(ldapObj, ldap): + self.docObj.joinDoc(ldapObj.doc) + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len(nameValue) > 2: + valueList = nameValue[2:] + nameValue =[nameValue[0]+nameValue[1]," ".join(valueList)] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + def _textToXML(self): + """Преобразует текст в XML""" + blTmp = self.blocTextObj.findArea(self.text,self.reHeader,self.reArea) + blocs = self.getFullAreas(blTmp) + headers = [] + startHeaders = [] + finHeaders = [] + docObj = xmlDoc() + docObj.createDoc(self.configName, self.configVersion) + rootNode = docObj.getNodeBody() + # Если пустой текст то создаем пустой документ + if not blocs: + return docObj + + for h in blocs[0]: + headers.append(h.rstrip()) + bodys = blocs[1] + + z = 0 + for h in headers: + if not bodys[z]: + z += 1 + continue + areaAction = False + if h: + if h[0] == "!": + header = self.removeSymbolTerm(h.strip()) + headerQuote = self.removeSymbolTerm(h) + docObj.createCaption(header,[headerQuote,""]) + areaAction = "drop" + elif h[0] == "-": + header = self.removeSymbolTerm(h.strip()) + headerQuote = self.removeSymbolTerm(h) + docObj.createCaption(header,[headerQuote,""]) + areaAction = "replace" + else: + docObj.createCaption(h.strip(), [h.rstrip(),""]) + else: + docObj.createCaption(h.strip(), [h.rstrip(),""]) + + if "\n" in blocs[0][z]: + resHead = self.reComment.search(h) + if resHead: + docObj.createField('comment', + blocs[0][z][resHead.start():]) + else: + docObj.createField('br') + + fields = self._splitToFields(bodys[z]) + for f in fields: + if f.name != False and f.value!=False and f.br!=False: + # Обработка условий для samba + if f.name[0] == "!" or f.name[0] == "-" or\ + f.name[0] == "+": + qns = self.removeSymbolTerm(f.br) + xmlField = docObj.createField("var", + [qns], + f.name[1:], [f.value]) + if f.name[0] == "!": + # Удаляемое в дальнейшем поле + docObj.setActionField(xmlField, "drop") + elif f.name[0] == "+": + # Добавляем уникальное поле + xmlField.setAttribute("type", "seplist") + docObj.setActionField(xmlField, "join") + else: + docObj.createField("var",[f.br.replace("\n","")], + f.name, [f.value]) + docObj.createField('br') + elif f.comment != False: + docObj.createField('comment', [f.comment]) + elif f.br != False: + docObj.createField('br', [f.br.replace("\n","")]) + if h.strip(): + area = docObj.createArea() + if areaAction: + docObj.setActionArea(area, areaAction) + rootNode.appendChild(area) + else: + fieldsNodes = docObj.tmpFields.getFields() + for fieldNode in fieldsNodes: + rootNode.appendChild(fieldNode) + docObj.clearTmpFields() + z += 1 + #print docObj.doc.toprettyxml() + return docObj diff --git a/build/lib/calculate-lib/pym/format/plasma.py b/build/lib/calculate-lib/pym/format/plasma.py new file mode 100644 index 0000000..d5e9500 --- /dev/null +++ b/build/lib/calculate-lib/pym/format/plasma.py @@ -0,0 +1,597 @@ +#-*- 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 re +import types +import copy +from xml import xpath +from cl_template import xmlDoc +from format.samba import samba + +class xmlDocPlasma: + """Класс для замены метода joinArea в xmlDoc для plasma""" + # заменяемый метод для xmlDoc + 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"): + 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) + + # Заменяем QUOTE + oldAreaNode = oName.parentNode.parentNode + oldAreaQuote = xpath.Evaluate('child::caption/quote', + oldAreaNode)[0] + if oldAreaQuote and\ + not oldAreaQuote.firstChild: + newAreaQuote = xpath.Evaluate('child::caption/quote', + xmlNewArea)[0] + oldAreaCaption = xpath.Evaluate('child::caption', + oldAreaNode)[0] + if newAreaQuote and oldAreaCaption: + oldAreaCaption.replaceChild(newAreaQuote, oldAreaQuote) + + 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"): + appendArea(baseNode, xmlNewArea) + else: + tmpXmlNewAreas = xpath.Evaluate('child::area',xmlNewArea) + for na in tmpXmlNewAreas: + for bn in baseNodes: + self.joinArea(bn, na) + return True + +class plasma(samba): + """Класс для обработки конфигурационного файла типа kde + + """ + _comment = "#" + configName = "plasma" + configVersion = "0.1" + reHeader = re.compile("^[\t ]*\[[^\[\]]+\].*\n?",re.M) + reBody = re.compile(".+",re.M|re.S) + reComment = re.compile("^\s*%s.*"%(_comment)) + reSeparator = re.compile("=") + sepFields = "\n" + reSepFields = re.compile(sepFields) + + def __init__(self,text): + samba.__init__(self,text) + + # Делим текст на области включая вложенные (areas массив областей) + def splitToAllArea(self, text, areas): + """Делит текст на области включая вложенные + + возвращает список объектов областей (переменная areas) + """ + class area: + def __init__(self): + self.header = False + self.start = False + self.fields = [] + self.end = False + + + def findPathArea(listPath, areaF): + """Ищет путь в области + + areaF - объект area + listPath - cписок названий областей + """ + ret = False + if not listPath: + return ret + flagList = False + if type(areaF) == types.ListType: + fields = areaF + flagList = True + else: + fields = areaF.fields + if areaF.header == listPath[0]: + ret = areaF + else: + return ret + for i in fields: + if str(i.__class__.__name__) == "area": + add = False + if not flagList: + add = listPath.pop(0) + if not listPath: + break + ret = False + if i.header == listPath[0]: + ret = findPathArea(listPath, i) + break + else: + if add: + listPath.insert(0,add) + if ret == areaF and len(listPath)>1: + ret = False + return ret + + + blTmp = self.blocTextObj.findBloc(self.text,self.reHeader,self.reBody) + blocs = self.getFullAreas(blTmp) + reH = re.compile("\[([^\[\]]+)\]") + # Список имен блоков + namesBlockList = [] + # Временные поля + fieldsTmp = [] + # Добавляем заголовки + z = 0 + for h in blocs[0]: + if not h: + if blocs[1][z] == "": + fieldsTmp.append("") + #fieldsTmp.append("\n") + else: + fieldsTmp.append(blocs[1][z]) + #print '"' + blocs[1][z] + '"' + z += 1 + continue + #print '"' + blocs[1][z] + '"' + z += 1 + slpNamesBlock = reH.split(h) + # Отступ слева для заголовка + indentionLeft = slpNamesBlock[0] + namesBlock = filter(lambda x: x.strip(), slpNamesBlock) + #namesBlock = map(lambda x: self.removeSymbolTerm(x), namesBlock) + findArea = findPathArea(copy.copy(namesBlock), areas) + namesBlockList.append(namesBlock) + if findArea: + if len(namesBlock) > 1: + namesBlockView = map(lambda x: self.removeSymbolTerm(x), + namesBlock) + else: + namesBlockView = namesBlock + findArea.start = indentionLeft + "[" + \ + "][".join(namesBlockView) + "]" + else: + i = 0 + lenNamesBlock = len(namesBlock) + namesBlockTmp = [] + for nameB in namesBlock: + namesBlockTmp.append(nameB) + findArea = findPathArea(copy.copy(namesBlockTmp), areas) + i += 1 + if not findArea: + areaNew = area() + areaNew.header = nameB + if lenNamesBlock == i: + if len(namesBlock) > 1: + namesBlockView = map(\ + lambda x: self.removeSymbolTerm(x), + namesBlock) + else: + namesBlockView = namesBlock + areaNew.start = indentionLeft + "[" + \ + "][".join(namesBlockView) + "]" + else: + areaNew.start = "" + areaNew.end = "" + if i == 1: + if lenNamesBlock == i: + areas += fieldsTmp + areas.append(areaNew) + findAreaPrev = areas[-1] + else: + if lenNamesBlock == i: + findAreaPrev.fields += fieldsTmp + findAreaPrev.fields.append(areaNew) + findAreaPrev = findAreaPrev.fields[-1] + else: + findAreaPrev = findArea + fieldsTmp = [] + i = 0 + delt = 0 + # Добавляем тела + for body in blocs[1]: + #print "#" + body + "#" + #print + if not blocs[0][i]: + i += 1 + delt +=1 + continue + ## В случае последнего комментария не добавляем перевод строки + #if self.reComment.search(body.splitlines()[-1]): + body = "\n" + body + + namesBlock = namesBlockList[i-delt] + findArea = findPathArea(copy.copy(namesBlock), areas) + if findArea: + #if findArea.fields: + #if type(findArea.fields[0]) == types.StringType: + #findArea.fields.pop(0) + findArea.fields.insert(0, body) + i += 1 + + #eee = 1 + #def prAreas(ar, eee): + #for a in ar: + #if type(a) == types.StringType: + #print 'field', a + #else: + #print "--------------------" + #print "HEADER =", a.header + #print "START =", a.start + #print "FIELDS =", a.fields + #print "LEVEL", eee + #if type(a) != types.StringType: + #if a.fields: + #eee += 1 + #prAreas(a.fields, eee) + + #prAreas(areas, eee) + return areas + + def createCaptionTerm(self, header, start, end, docObj): + """Создание пустой области с заголовком + + при создании области проверяется первый символ заголовка + и добавляется тег action + "!" - drop + "-" - replace + """ + areaAction = False + if header: + if header[0] == "!": + docObj.createCaption(header[1:], [start, + end.replace("\n","")]) + areaAction = "drop" + elif header[0] == "-": + docObj.createCaption(header[1:], [start, + end.replace("\n","")]) + areaAction = "replace" + else: + docObj.createCaption(header, [start, + end.replace("\n","")]) + else: + docObj.createCaption(header, [start, + end.replace("\n","")]) + + areaXML = docObj.createArea() + if areaAction: + docObj.setActionArea(areaXML, areaAction) + return areaXML + + def createXML(self, areas, rootNode, docObj): + """Создаем из массивов областей XML""" + for i in areas: + if str(i.__class__.__name__) == "area": + if i.header: + areaXML = self.createCaptionTerm(i.header, i.start, + i.end.replace("\n",""), + docObj) + for f in i.fields: + if str(f.__class__.__name__) == "area": + if f.header: + areaXMLChild = self.createCaptionTerm(f.header, + f.start, + f.end.replace("\n",""), + docObj) + + self.createXML(f.fields, areaXMLChild, docObj) + + areaXML.appendChild(areaXMLChild) + else: + self.createXML(f.fields, areaXML, docObj) + if "\n" in f.end: + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + areaXML.appendChild(fieldXMLBr) + else: + if not f: + continue + fields = self.splitToFields(f) + for field in fields: + if field.name != False: + fieldXML = self.createFieldTerm(field.name, + field.value, + field.br, docObj) + areaXML.appendChild(fieldXML) + if field.br[-1] == "\n": + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + areaXML.appendChild(fieldXMLBr) + elif field.comment != False: + fieldXML = docObj.createField("comment", + [field.comment], + "", [], + False, False) + areaXML.appendChild(fieldXML) + elif field.br != False: + brText = field.br.replace("\n","") + if brText: + fieldXML = docObj.createField('br', + [brText], + "", [], + False, False) + else: + fieldXML = docObj.createField('br', + [], + "", [], + False, False) + if areaXML: + areaXML.appendChild(fieldXML) + + if i.header: + rootNode.appendChild(areaXML) + if "\n" in i.end: + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + rootNode.appendChild(fieldXMLBr) + + else: + if not i: + continue + fields = self.splitToFields(i) + for field in fields: + if field.name != False: + fieldXML = self.createFieldTerm(field.name, + field.value, + field.br, docObj) + rootNode.appendChild(fieldXML) + if field.br[-1] == "\n": + fieldXMLBr = docObj.createField("br",[],"", [], + False, False) + rootNode.appendChild(fieldXMLBr) + elif field.comment != False: + fieldXML = docObj.createField("comment", + [field.comment], + "", [], + False, False) + rootNode.appendChild(fieldXML) + elif field.br != False: + brText = field.br.replace("\n","") + if brText: + fieldXML = docObj.createField('br', [brText],"",[], + False, False) + else: + fieldXML = docObj.createField('br', [], "", [], + False, False) + rootNode.appendChild(fieldXML) + #rootNode.appendChild(areaXML) + + def _textToXML(self): + """Преобразуем текст в XML""" + areas = [] + if self.text.strip(): + self.splitToAllArea(self.text, areas) + #docObj = xmlDoc() + # Создаем новый класс xmlDoc с измененным методом joinArea + newClass = type("newXmlDocPlalma",(xmlDocPlasma,xmlDoc,object),{}) + # Создаем экземпляр нового класса + docObj = newClass() + # Создание объекта документ c пустым разделителем между полями + docObj.createDoc(self.configName, self.configVersion) + if not areas: + return docObj + self.createXML(areas, docObj.getNodeBody(), docObj) + return docObj + + + def postXML(self): + """Последующая постобработка XML""" + # Для добавления перевода строки между областями если его нет + #print self.docObj.body.toprettyxml() + 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 + + xmlAreas = xpath.Evaluate("descendant::area", self.docObj.body) + #print "-------------------------------------------------------" + #print xmlAreas + #if xmlAreas: + #prXmlArea = xmlAreas[0] + for xmlArea in xmlAreas: + # Перед пустой областью и после нее удаляем переводы строк + if getQuotesArea(xmlArea) == ["",""]: + #areaTXT = xpath.Evaluate("child::caption/name", xmlArea)[0] + #print "CL_AREA", areaTXT.firstChild + if xmlArea.previousSibling and\ + self.docObj.getTypeField(xmlArea.previousSibling) == "br": + parentNode = xmlArea.previousSibling.parentNode + if xmlArea.previousSibling.previousSibling and\ + self.docObj.getTypeField(xmlArea.previousSibling.previousSibling) == "br": + parentNode.removeChild(\ + xmlArea.previousSibling.previousSibling) + parentNode.removeChild(xmlArea.previousSibling) + if xmlArea.nextSibling and\ + self.docObj.getTypeField(xmlArea.nextSibling) == "br": + parentNode = xmlArea.nextSibling.parentNode + if xmlArea.nextSibling.nextSibling and\ + self.docObj.getTypeField(xmlArea.nextSibling.nextSibling) == "br": + parentNode.removeChild(xmlArea.nextSibling.nextSibling) + parentNode.removeChild(xmlArea.nextSibling) + continue + + # Собираем поля в кучку + xmlChildAreas = xpath.Evaluate("child::area", xmlArea) + if xmlChildAreas: + childNodes = self.docObj.getFieldsArea(xmlArea) + firstChildArea = xmlChildAreas[0] + if firstChildArea.previousSibling and\ + self.docObj.getTypeField(firstChildArea.previousSibling)=="br": + if firstChildArea.previousSibling.previousSibling: + if self.docObj.getTypeField(\ + firstChildArea.previousSibling.previousSibling)=="br": + firstChildArea = firstChildArea.previousSibling + flagFoundArea = False + it = 0 + lenChild = len(childNodes) + for node in childNodes: + it += 1 + if node.tagName == "area": + flagFoundArea = True + continue + if flagFoundArea and node.tagName == "field": + if self.docObj.getTypeField(node) == "var": + xmlArea.insertBefore(node, firstChildArea) + if it < lenChild: + if self.docObj.getTypeField(childNodes[it])==\ + "br": + xmlArea.insertBefore(childNodes[it], + firstChildArea) + + # Добавляем BR если его нет первым полем + xmlFields = xpath.Evaluate("child::field", xmlArea) + if not (xmlFields and\ + (self.docObj.getTypeField(xmlFields[0]) == "br" or\ + self.docObj.getTypeField(xmlFields[0]) == "comment")): + if xmlFields: + xmlArea.insertBefore(self.docObj.createField("br", + [],"",[], + False,False), + xmlFields[0]) + # Если последним полем BR, удаляем его + if xmlFields and self.docObj.getTypeField(xmlFields[-1]) == "br": + #print "DEL_BR", xmlFields[-1].nextSibling + #and\ + if not xmlFields[-1].nextSibling: + xmlArea.removeChild(xmlFields[-1]) + + # Если предыдущим полем не (BR или комментарий) - добавляем BR + if xmlArea.previousSibling and\ + not (self.docObj.getTypeField(xmlArea.previousSibling) == "br" or\ + self.docObj.getTypeField(xmlArea.previousSibling) == "comment"): + parentNode = xmlArea.parentNode + parentNode.insertBefore(self.docObj.createField("br", + [],"",[], + False,False), + xmlArea) + # Если есть предыдущее поле, и поле предыдущеее предыдущему + # не равно BR или комментарий то добавляем BR + if xmlArea.previousSibling: + prPrSibling = xmlArea.previousSibling.previousSibling + if prPrSibling and\ + not (self.docObj.getTypeField(prPrSibling) == "br" or\ + self.docObj.getTypeField(prPrSibling) == "comment"): + parentNode = xmlArea.parentNode + parentNode.insertBefore(self.docObj.createField("br", + [],"",[], + False,False), + xmlArea) + # Если после есть BR а за ним ничего нет, удаляем BR + if xmlArea.nextSibling and\ + self.docObj.getTypeField(xmlArea.nextSibling) == "br": + if not xmlArea.nextSibling.nextSibling: + parentNode = xmlArea.nextSibling.parentNode + parentNode.removeChild(xmlArea.nextSibling) + + #xmlName = xpath.Evaluate("child::caption/name", xmlArea)[0] + #print "------------------------------------" + #print "Name =", xmlName.firstChild + #if xmlArea.previousSibling: + #print "PR_TYPE =", self.docObj.getTypeField(xmlArea.previousSibling) + + + def join(self, kdeObj): + """Объединяем конфигурации""" + if isinstance(kdeObj, plasma): + self.docObj.joinDoc(kdeObj.doc) + self.postXML() + + diff --git a/build/lib/calculate-lib/pym/format/postfix.py b/build/lib/calculate-lib/pym/format/postfix.py new file mode 100644 index 0000000..4dc2a34 --- /dev/null +++ b/build/lib/calculate-lib/pym/format/postfix.py @@ -0,0 +1,117 @@ +#-*- 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 re +from cl_template import xmlDoc +from format.apache import apache + +class postfix(apache): + """Класс для обработки конфигурационного файла типа postfix + + """ + _comment = "#" + configName = "postfix" + configVersion = "0.1" + sepFields = "\n" + reComment = re.compile("[ \t]*%s"%(_comment)) + reSepFields = re.compile(sepFields) + # разделитель названия и значения переменной + reSeparator = re.compile("\s*=\s*") + def __init__(self,text): + self.text = text + # Объект документ + self.docObj = self.textToXML() + # Создаем поля разделенные массивы + self.docObj.postParserListSeplist(self.docObj.body) + # XML документ + self.doc = self.docObj.doc + + def join(self, postfixObj): + """Объединяем конфигурации""" + if isinstance(postfixObj, postfix): + self.docObj.joinDoc(postfixObj.doc) + + def textToXML(self): + """Преобразуем текст в XML""" + class area: + def __init__(self): + self.header = False + self.start = False + self.fields = [] + self.end = False + areas = [] + oneArea = area() + oneArea.header = "" + oneArea.start = "" + oneArea.fields = [self.text] + oneArea.end = "" + areas.append(oneArea) + docObj = xmlDoc() + # Создание объекта документ c пустым разделителем между полями + docObj.createDoc(self.configName, self.configVersion) + if not areas: + return docObj + self.createXML(areas, docObj.getNodeBody(), docObj) + return docObj + + + def setDataField(self, txtLines, endtxtLines): + """Cоздаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + #print "#"+brBloc[z]+"#" + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len (nameValue) == 1: + field.name = "" + field.value = textLine.replace(self.sepFields,"") + field.br = textLine + fields.append(field) + field = fieldData() + + if len(nameValue) > 2: + valueList = nameValue[1:] + nameValue =[nameValue[0],"=".join(valueList).replace(\ + self.sepFields,"")] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields diff --git a/build/lib/calculate-lib/pym/format/procmail.py b/build/lib/calculate-lib/pym/format/procmail.py new file mode 100644 index 0000000..50fa05f --- /dev/null +++ b/build/lib/calculate-lib/pym/format/procmail.py @@ -0,0 +1,115 @@ +#-*- 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 re +from cl_template import objShare, xmlDoc + +class procmail(objShare): + """Класс для обработки конфигурационного файла типа procmail + + """ + _comment = "#" + configName = "procmail" + configVersion = "0.1" + sepFields = "\n" + reComment = re.compile("[ \t]*%s" %(_comment)) + reSepFields = re.compile(sepFields) + # разделитель названия и значения переменной + reSeparator = re.compile("=") + def __init__(self, text): + self.text = text + self.docObj = self.textToXML() + self.doc = self.docObj.doc + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + def textToXML(self): + docObj = xmlDoc() + docObj.createDoc(self.configName, self.configVersion) + if self.text: + nodeBody = docObj.getNodeBody() + fields = self.splitToFields(self.text) + for field in fields: + if field.name != False: + fieldXML = self.createFieldTerm(field.name, + field.value, + field.br, docObj) + nodeBody.appendChild(fieldXML) + if field.br[-1] == "\n": + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + nodeBody.appendChild(fieldXMLBr) + elif field.comment != False: + fieldXML = docObj.createField("comment", + [field.comment], + "", [], + False, False) + nodeBody.appendChild(fieldXML) + elif field.br != False: + brText = field.br.replace("\n","") + if brText: + fieldXML = docObj.createField('br', + [brText], + "", [], + False, False) + else: + fieldXML = docObj.createField('br', + [], + "", [], + False, False) + nodeBody.appendChild(fieldXML) + return docObj + + def join(self, procmailObj): + """Объединяем конфигурации""" + if isinstance(procmailObj, procmail): + #print self.docObj.doc.toprettyxml() + self.docObj.joinDoc(procmailObj.doc) diff --git a/build/lib/calculate-lib/pym/format/samba.py b/build/lib/calculate-lib/pym/format/samba.py new file mode 100644 index 0000000..981cc52 --- /dev/null +++ b/build/lib/calculate-lib/pym/format/samba.py @@ -0,0 +1,261 @@ +#-*- 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 re +from xml import xpath +from cl_template import objShare, blocText, xmlDoc + +class samba(objShare): + """Класс для обработки конфигурационного файла типа samba + + """ + _comment = "#" + configName = "samba" + configVersion = "0.1" + reHeader = re.compile("^[\t ]*\[[^\[\]]+\].*\n",re.M) + reBody = re.compile(".+",re.M|re.S) + reComment = re.compile("\s*%s.*|\s*;.*"%(_comment)) + reSeparator = re.compile("\s*=\s*") + sepFields = "\n" + reSepFields = re.compile(sepFields) + + def __init__(self,text): + self.text = text + self.blocTextObj = blocText() + self._splitToFields = self.splitToFields + # Объект документ + self.docObj = self._textToXML() + # XML документ + self.doc = self.docObj.doc + + def postXML(self): + """Последующая постобработка XML""" + # Для добавления перевода строки между областями если его нет + #print self.docObj.body.toprettyxml() + xmlAreas = xpath.Evaluate("child::area", self.docObj.body) + for xmlArea in xmlAreas: + if xmlArea.previousSibling and\ + self.docObj.getTypeField(xmlArea.previousSibling) == "br": + continue + firstArea = False + xmlFields = xpath.Evaluate("child::field", xmlArea) + if not (xmlFields and\ + (self.docObj.getTypeField(xmlFields[-1]) == "br" or\ + self.docObj.getTypeField(xmlFields[-1]) == "comment")): + if xmlArea.nextSibling: + parentNode = xmlArea.parentNode + nextNode = xmlArea.nextSibling + parentNode.insertBefore(self.docObj.createField("br", + [],"",[], + False,False), + nextNode) + + + def join(self, sambaObj): + """Объединяем конфигурации""" + if isinstance(sambaObj, samba): + self.docObj.joinDoc(sambaObj.doc) + self.postXML() + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len(nameValue) > 2: + valueList = nameValue[1:] + nameValue =[nameValue[0],"=".join(valueList)] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + + def splitCleanBloc(self, txtBloc): + """Делим блок на две части (переменные, пустые строки в конце)""" + txtLines = txtBloc.split("\n") + firstBloc = [] + nextBloc = [] + txtLines.reverse() + z = 0 + for txtLine in txtLines: + if not txtLine.strip(): + nextBloc.append(txtLine) + else: + break + z += 1 + txtLines.reverse() + firstBloc = txtLines[:-z] + nextBloc.reverse() + if nextBloc: + firstBloc.append("") + if nextBloc and "\n".join(nextBloc): + return ("\n".join(firstBloc), "\n".join(nextBloc)) + else: + return False + + def getFullAreas(self, blocs): + """Делит текст на области, (Заголовок, тело) + + Возвращает два списка: заголовки, тела + """ + headsAreas = [] + bodyAreas = [] + if not blocs: + return [] + lenBlocs = len(blocs[0]) + for i in range(lenBlocs): + txtBloc = blocs[1][i] + clean = self.splitCleanBloc(txtBloc) + if clean: + headsAreas.append(blocs[0][i]) + bodyAreas.append(clean[0]) + headsAreas.append("") + bodyAreas.append(clean[1]) + else: + headsAreas.append(blocs[0][i]) + bodyAreas.append(blocs[1][i]) + return (headsAreas, bodyAreas) + + def createTxtConfig(self, strHeader, dictVar): + """Cоздает область с заголовком + + создает текст конфигурационного файла в формате samba из + заголовка (строка) и словаря переменных + """ + if not strHeader: + return "" + outTxt = "[" + strHeader + "]\n" + for key in dictVar.keys(): + outTxt += "%s = %s\n" %(key,dictVar[key]) + return outTxt + + def _textToXML(self): + """Преобразует текст в XML""" + blTmp = self.blocTextObj.findBloc(self.text,self.reHeader,self.reBody) + blocs = self.getFullAreas(blTmp) + headers = [] + startHeaders = [] + finHeaders = [] + docObj = xmlDoc() + docObj.createDoc(self.configName, self.configVersion) + rootNode = docObj.getNodeBody() + # Если пустой текст то создаем пустой документ + if not blocs: + return docObj + + for h in blocs[0]: + listfinH = h.split("]") + finH = listfinH[0] + if "[" in finH: + startHeaders.append(finH + "]") + else: + startHeaders.append(finH) + if len(listfinH) == 2: + finHeaders.append(listfinH[1]) + else: + finHeaders.append("") + headers.append(finH.replace("[","").replace("]","").strip()) + bodys = blocs[1] + + z = 0 + for h in headers: + if not bodys[z]: + z += 1 + continue + areaAction = False + if h: + if h[0] == "!": + docObj.createCaption(h[1:], [startHeaders[z],""]) + areaAction = "drop" + elif h[0] == "-": + docObj.createCaption(h[1:], [startHeaders[z],""]) + areaAction = "replace" + else: + docObj.createCaption(h, [startHeaders[z],""]) + else: + docObj.createCaption(h, [startHeaders[z],""]) + + if "\n" in blocs[0][z]: + if self.reComment.search(finHeaders[z]): + docObj.createField('comment', [finHeaders[z]]) + elif not finHeaders[z].strip() and\ + finHeaders[z].replace("\n",""): + docObj.createField('br', + [finHeaders[z].replace("\n","")]) + else: + docObj.createField('br') + fields = self._splitToFields(bodys[z]) + for f in fields: + if f.name != False and f.value!=False and f.br!=False: + # Обработка условий для samba + if f.name[0] == "!" or f.name[0] == "-" or\ + f.name[0] == "+": + qns = self.removeSymbolTerm(f.br) + xmlField = docObj.createField("var", + [qns], + f.name[1:], [f.value]) + if f.name[0] == "!": + # Удаляемое в дальнейшем поле + docObj.setActionField(xmlField, "drop") + else: + docObj.createField("var",[f.br.replace("\n","")], + f.name, [f.value]) + docObj.createField('br') + elif f.comment != False: + docObj.createField('comment', [f.comment]) + elif f.br != False: + docObj.createField('br', [f.br.replace("\n","")]) + if h.strip(): + area = docObj.createArea() + if areaAction: + docObj.setActionArea(area, areaAction) + rootNode.appendChild(area) + else: + fieldsNodes = docObj.tmpFields.getFields() + for fieldNode in fieldsNodes: + rootNode.appendChild(fieldNode) + docObj.clearTmpFields() + z += 1 + #print docObj.doc.toprettyxml() + return docObj + diff --git a/build/lib/calculate-lib/pym/format/squid.py b/build/lib/calculate-lib/pym/format/squid.py new file mode 100644 index 0000000..fba8153 --- /dev/null +++ b/build/lib/calculate-lib/pym/format/squid.py @@ -0,0 +1,86 @@ +#-*- 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 re +from format.procmail import procmail + +class squid(procmail): + """Класс для обработки конфигурационного файла типа squid + + """ + configName = "squid" + configVersion = "0.1" + # разделитель названия и значения переменной + reSeparator = re.compile(" ") + + def __init__(self, text): + procmail.__init__(self, text) + # Создаем поля-массивы + self.docObj.postParserList() + # Создаем поля разделенные массивы + self.docObj.postParserListSeplist(self.docObj.body) + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + z += 1 + findComment = self.reComment.search(textLine) + flagVariable = True + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + flagVariable = False + elif findComment: + if textLine[:findComment.start()].strip(): + field.comment = textLine[findComment.start():] + textLine = textLine[:findComment.start()] + else: + field.comment = textLine + flagVariable = False + fields.append(field) + field = fieldData() + if flagVariable: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len(nameValue) > 2: + valueList = nameValue[1:] + nameValue =[nameValue[0]," ".join(valueList)] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + def join(self, squidObj): + """Объединяем конфигурации""" + if isinstance(squidObj, squid): + #print squidObj.getConfig() + self.docObj.joinDoc(squidObj.doc) diff --git a/build/lib/calculate-lib/pym/format/xml_gconf.py b/build/lib/calculate-lib/pym/format/xml_gconf.py new file mode 100644 index 0000000..84894bd --- /dev/null +++ b/build/lib/calculate-lib/pym/format/xml_gconf.py @@ -0,0 +1,262 @@ +#-*- 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 time +from xml import xpath +import xml.dom.minidom +from format.xml_xfce import xml_xfce +# Перевод cообщений модуля +from cl_lang import lang +tr = lang() +tr.setLocalDomain('cl_lib') +tr.setLanguage(sys.modules[__name__]) + + +class xml_gconf(xml_xfce): + """Класс для объединения gconf-xml файлов""" + # root нода + rootNode = False + # body нода + bodyNode = False + # Документ + doc = False + # Текст шаблона + text = "" + # Текущее время в секундах + currentTime = "" + # Комментарий + _comment = ("") + # поддерживаемые аттрибуты тега entry. Пример + supportEntryTypes = ("int", "bool", "float", "string", "list", "pair") + + def __init__(self, text): + self.text = text + # Создаем пустой объект + self.docObj = type("_empty_class", (object,), {})() + # Названия аттрибутов для пустого объекта + emptyMethods = ["getNodeBody","removeComment","insertBRtoBody", + "insertBeforeSepAreas"] + # Добавляем необходимые аттрибуты пустому объекту + for method in emptyMethods: + setattr(self.docObj, method, self.emptyMethod) + # Пустой метод (не нужно имя файла для корневой ноды) + setattr(self, "setNameBodyNode", self.emptyMethod) + # Создаем XML документ + self.doc = self.textToXML() + + def getCurrentTime(self): + """Получение текущего времени в секундах""" + return str(int(time.time())) + + def textToXML(self): + """Создание из текста XML документа + Храним xml в своем формате + """ + if not self.text.strip(): + self.text = '''''' + try: + self.doc = xml.dom.minidom.parseString(self.text) + except: + self.setError(_("Can not text template is XML")) + return False + self.rootNode = self.doc.documentElement + self.bodyNode = self.rootNode + return self.doc + + def cmpListsNodesEntry(self, listXmlA, listXmlB): + """Сравнение содержимого двух списков XML нод""" + getTextsNodes = lambda y: map(lambda x:\ + x.toxml().replace(" ","").replace("\t","").replace("\n",""), + map(lambda x: x.removeAttribute("mtime") or x, + map(lambda x: x.cloneNode(True), + filter(lambda x: x.nodeType==x.ELEMENT_NODE, y)))) + if set(getTextsNodes(listXmlA))==set(getTextsNodes(listXmlB)): + return True + return False + + def _join(self, xmlNewNode, xmlOldNode, flagRootNode=True, levelNumber=0): + """Объединение корневой ноды шаблона и корневой ноды файла""" + if levelNumber>1: + return True + xmlNode = xmlNewNode + childNodes = xmlNode.childNodes + nextOldNode = xmlOldNode + flagError = False + if xmlNode.nodeType == xmlNode.ELEMENT_NODE: + n = xmlNode + tagName = n.tagName + nAction = u'' + nName = u'' + nType = u'' + nValue = u'' + attrName = '' + attrType = '' + if flagRootNode: + if not tagName == "gconf": + self.setError(_("The text is not a valid gconf-XML format \ +(not found '...')")) + return False + else: + if not tagName == "entry": + self.setError(_("The text is not a valid gconf-XML format \ +(found '<%s>..'")%(tagName,tagName)) + return False + if not n.hasAttribute("name"): + self.setError(_('Not found arrtibute "name" in tag entry')) + return False + if not n.hasAttribute("type"): + self.setError(_('Not found arrtibute "type" in tag entry')) + return False + nName = n.getAttribute("name") + attrName = u"attribute::name='%s'"%nName + nType = n.getAttribute("type") + # Проверка правильности аттрибута type + if not nType in self.supportEntryTypes: + self.setError(\ + _('Incorrect arrtibute "type" - ')%nType) + return False + attrType = u"attribute::type='%s'"%nType + if n.hasAttribute("value"): + nValue = n.getAttribute("value") + if n.hasAttribute("action"): + nAction = n.getAttribute("action") + if not nAction in ("join","replace","drop"): + textError = _('''In the text, XML template, look \ +for a reserved attribute 'action' with the incorrect value.\n\ +Valid values attribute 'action': \ +(action="join", action="replace", action="drop")''') + self.setError(textError) + return False + if xmlOldNode.parentNode: + findStr = u"child::%s"%tagName + strAttr = [attrName, attrType] + findAttr = filter(lambda x: x, strAttr) + findAttrStr = '' + if findAttr: + strAttr = u' and '.join(findAttr) + findAttrStr = "[%s]"%strAttr + findPath = u"child::%s%s"%(tagName,findAttrStr) + # Рабочая нода + if flagRootNode: + workNode = xmlOldNode.parentNode + else: + workNode = xmlOldNode + oldNodes = xpath.Evaluate(findPath, workNode) + # Новая нода список + flagArray = False + if nType == "list" or nType == "pair": + flagArray = True + flagDrop = False + flagJoin = True + flagReplace = False + if nType=="string" or nAction=="replace": + flagJoin = False + flagReplace = True + elif nAction == "drop": + flagJoin = False + flagDrop = True + if flagRootNode: + textError = _('Incorrect action="drop" in root node') + self.setError(textError) + return False + if oldNodes: + if len(oldNodes)>1: + textError = _("The uncertainty in this template are \ +the same nodes at one level") + self.setError(textError) + return False + nextOldNode = oldNodes[0] + # Замещаем ноду в случае массива + if flagArray and not flagDrop: + replaceXmlNode = xmlNode.cloneNode(True) + # Сравнение содержимого нод + if not self.cmpListsNodesEntry([replaceXmlNode], + [nextOldNode]): + replaceXmlNode.setAttribute("mtime", + self.currentTime) + if nAction: + replaceXmlNode.removeAttribute("action") + workNode.replaceChild(replaceXmlNode, + nextOldNode) + flagJoin = False + flagReplace = False + childNodes = False + # Объединение нод + if flagJoin: + if nextOldNode.hasAttribute("value"): + oValue = nextOldNode.getAttribute("value") + if nValue != oValue: + nextOldNode.setAttribute("mtime", + self.currentTime) + nextOldNode.setAttribute("value",nValue) + # Замещение ноды + elif flagReplace: + replaceXmlNode = xmlNode.cloneNode(True) + # Сравнение содержимого нод + if not self.cmpListsNodesEntry([replaceXmlNode], + [nextOldNode]): + replaceXmlNode.setAttribute("mtime", + self.currentTime) + if not\ + self._removeDropNodesAndAttrAction(\ + replaceXmlNode): + return False + workNode.replaceChild(replaceXmlNode, + nextOldNode) + childNodes = False + # Удаление ноды + elif flagDrop: + workNode.removeChild(nextOldNode) + childNodes = False + else: + # Добавление ноды + childNodes = False + if not flagDrop: + appendXmlNode = xmlNode.cloneNode(True) + appendXmlNode.setAttribute("mtime", self.currentTime) + if not\ + self._removeDropNodesAndAttrAction(appendXmlNode): + return False + workNode.appendChild(appendXmlNode) + if childNodes: + for node in childNodes: + levelNumber +=1 + if not self._join(node, nextOldNode, False, levelNumber): + flagError = True + break + levelNumber -= 1 + if flagError: + return False + return True + + def join(self, xml_gconfObj): + """Объединяем конфигурации""" + # Получаем текущее время + self.currentTime = self.getCurrentTime() + if isinstance(xml_gconfObj, xml_gconf): + try: + self.joinDoc(xml_gconfObj.doc) + except: + self.setError(_("Can not join template")) + return False + return True + + def getConfig(self): + """Получение текстового файла из XML документа""" + data = self.doc.toprettyxml().split("\n") + data = filter(lambda x: x.strip(), data) + return "\n".join(data).replace("\t"," ") diff --git a/build/lib/calculate-lib/pym/format/xml_xfce.py b/build/lib/calculate-lib/pym/format/xml_xfce.py new file mode 100644 index 0000000..9acc947 --- /dev/null +++ b/build/lib/calculate-lib/pym/format/xml_xfce.py @@ -0,0 +1,268 @@ +#-*- 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 +from xml import xpath +import xml.dom.minidom +from cl_utils import _error +# Перевод cообщений модуля +from cl_lang import lang +tr = lang() +tr.setLocalDomain('cl_lib') +tr.setLanguage(sys.modules[__name__]) + +class xml_xfce(_error): + """Класс для объединения xfce-xml файлов""" + # root нода + rootNode = False + # body нода + bodyNode = False + # Документ + doc = False + # Текст шаблона + text = "" + # Комментарий + _comment = ("") + + def __init__(self, text): + self.text = text + # Создаем пустой объект + self.docObj = type("_empty_class", (object,), {})() + # Названия аттрибутов для пустого объекта + emptyMethods = ["getNodeBody","removeComment","insertBRtoBody", + "insertBeforeSepAreas"] + # Добавляем необходимые аттрибуты пустому объекту + for method in emptyMethods: + setattr(self.docObj, method, self.emptyMethod) + # Создаем XML документ + self.doc = self.textToXML() + + def emptyMethod(self, *arg , **argv): + """Пустой метод""" + return True + + def setNameBodyNode(self, name): + """Устанавливает название для корневой ноды документа""" + if not self.bodyNode: + return False + self.bodyNode.setAttribute("name", name) + return True + + def textToXML(self): + """Создание из текста XML документа + Храним xml в своем формате + """ + if not self.text.strip(): + self.text = ''' + +''' + try: + self.doc = xml.dom.minidom.parseString(self.text) + except: + self.setError(_("Can not text template is XML")) + return False + self.rootNode = self.doc.documentElement + self.bodyNode = self.rootNode + return self.doc + + def join(self, xml_xfceObj): + """Объединяем конфигурации""" + if isinstance(xml_xfceObj, xml_xfce): + try: + self.joinDoc(xml_xfceObj.doc) + except: + self.setError(_("Can not join template")) + return False + return True + + def _removeDropNodesAndAttrAction(self, xmlNode): + """Удаляет ноды с аттрибутом action='drop' + + Также удаляет аттрибут action у всех нод + """ + flagError = False + childNodes = xmlNode.childNodes + if xmlNode.nodeType ==xmlNode.ELEMENT_NODE: + if xmlNode.hasAttribute("action"): + nAction = xmlNode.getAttribute("action") + if not nAction in ("join","replace","drop"): + textError = _('''In the text, XML template, look \ +for a reserved attribute 'action' with the incorrect value.\n\ +Valid values attribute 'action': \ +(action="join", action="replace", action="drop")''') + self.setError(textError) + return False + xmlNode.removeAttribute("action") + if nAction == "drop": + parentNode = xmlNode.parentNode + if parentNode: + parentNode.removeChild(xmlNode) + if childNodes: + for node in childNodes: + if not self._removeDropNodesAndAttrAction(node): + flagError = True + break + if flagError: + return False + return True + + def postXML(self): + """Последующая постобработка XML""" + # Удаляем теги action и удаляемые ноды + self._removeDropNodesAndAttrAction(self.bodyNode) + + def _join(self, xmlNewNode, xmlOldNode, flagRootNode=True): + """Объединение корневой ноды шаблона и корневой ноды файла""" + xmlNode = xmlNewNode + childNodes = xmlNode.childNodes + nextOldNode = xmlOldNode + flagError = False + if xmlNode.nodeType ==xmlNode.ELEMENT_NODE: + n = xmlNode + path = u'' + nName = u'' + nType = u'' + nValue = u'' + nAction = u'' + attrName = '' + attrType = '' + path = n.tagName + if n.hasAttribute("name"): + nName = n.getAttribute("name") + attrName = u"attribute::name='%s'"%nName + if n.hasAttribute("type"): + nType = n.getAttribute("type") + attrType = u"attribute::type='%s'"%nType + if n.hasAttribute("value"): + nValue = n.getAttribute("value") + if n.hasAttribute("action"): + nAction = n.getAttribute("action") + if not nAction in ("join","replace","drop"): + textError = _('''In the text, XML template, look \ +for a reserved attribute 'action' with the incorrect value.\n\ +Valid values attribute 'action': \ +(action="join", action="replace", action="drop")''') + self.setError(textError) + return False + if xmlOldNode.parentNode: + findStr = u"child::%s"%path + strAttr = [attrName, attrType] + findAttr = filter(lambda x: x, strAttr) + findAttrStr = '' + if findAttr: + strAttr = u' and '.join(findAttr) + findAttrStr = "[%s]"%strAttr + findPath = u"child::%s%s"%(path,findAttrStr) + # Рабочая нода + if flagRootNode: + workNode = xmlOldNode.parentNode + else: + workNode = xmlOldNode + oldNodes = xpath.Evaluate(findPath, workNode) + #print findPath + #print workNode + #print "----------------------------" + # Новая нода список + flagArray = False + if nType == "array": + flagArray = True + flagDrop = False + flagJoin = True + flagReplace = False + if nAction == "replace": + flagJoin = False + flagReplace = True + elif nAction == "drop": + flagJoin = False + flagDrop = True + if flagRootNode: + textError = _('Incorrect action="drop" in root node') + self.setError(textError) + return False + if oldNodes: + if len(oldNodes)>1: + textError = _("The uncertainty in this template are \ +the same nodes at one level") + self.setError(textError) + return False + nextOldNode = oldNodes[0] + # Замещаем ноду в случае массива + if flagArray and not flagDrop: + replaceXmlNode = xmlNode.cloneNode(True) + if nAction: + replaceXmlNode.removeAttribute("action") + workNode.replaceChild(replaceXmlNode, + nextOldNode) + flagJoin = False + flagReplace = False + childNodes = False + # Объединение нод + if flagJoin: + if nextOldNode.hasAttribute("value"): + oValue = nextOldNode.getAttribute("value") + if nValue != oValue: + nextOldNode.setAttribute("value",nValue) + # Замещение ноды + elif flagReplace: + replaceXmlNode = xmlNode.cloneNode(True) + if not\ + self._removeDropNodesAndAttrAction(replaceXmlNode): + return False + workNode.replaceChild(replaceXmlNode, + nextOldNode) + childNodes = False + # Удаление ноды + elif flagDrop: + workNode.removeChild(nextOldNode) + childNodes = False + else: + # Добавление ноды + childNodes = False + if not flagDrop: + appendXmlNode = xmlNode.cloneNode(True) + if not\ + self._removeDropNodesAndAttrAction(appendXmlNode): + return False + workNode.appendChild(appendXmlNode) + if childNodes: + for node in childNodes: + if not self._join(node, nextOldNode, False): + flagError = True + break + if flagError: + return False + return True + + def joinDoc(self, doc): + """Объединение документа шаблона и документа файла""" + if not self.doc: + self.setError(_("Can not text file is XML")) + return False + if not doc: + self.setError(_("Can not text template is XML")) + return False + # Импортируем корневую ноду нового документа в текущий документ + #newImportBodyNode = self.doc.importNode(doc.documentElement, True) + # Объединение корневой ноды шаблона и корневой ноды файла + if not self._join(doc.documentElement, self.bodyNode): + return False + return True + + def getConfig(self): + """Получение текстового файла из XML документа""" + data = self.doc.toprettyxml(encoding='UTF-8').split("\n") + data = filter(lambda x: x.strip(), data) + return "\n".join(data).replace("\t"," ").decode("UTF-8") diff --git a/build/lib/calculate-lib/pym/format/xml_xfcepanel.py b/build/lib/calculate-lib/pym/format/xml_xfcepanel.py new file mode 100644 index 0000000..4e9eb06 --- /dev/null +++ b/build/lib/calculate-lib/pym/format/xml_xfcepanel.py @@ -0,0 +1,199 @@ +#-*- 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 +from xml import xpath +import xml.dom.minidom +from format.xml_xfce import xml_xfce + +# Перевод cообщений модуля +from cl_lang import lang +tr = lang() +tr.setLocalDomain('cl_lib') +tr.setLanguage(sys.modules[__name__]) + +class xml_xfcepanel(xml_xfce): + """Класс для объединения xfce-panel файлов""" + def __init__(self, text): + xml_xfce.__init__(self, text) + self.panelNumbers = {} + + def textToXML(self): + """Создание из текста XML документа + Храним xml в своем формате + """ + if not self.text.strip(): + self.text = ''' + + +''' + try: + self.doc = xml.dom.minidom.parseString(self.text) + except: + self.setError(_("Can not text profile is XML")) + return False + self.rootNode = self.doc.documentElement + self.bodyNode = self.rootNode + return self.doc + + def setNameBodyNode(self, name): + """Пустой метод""" + return True + + def _join(self, xmlNewNode, xmlOldNode, flagRootNode=True, levelNumber=0): + """Объединение корневой ноды профиля и корневой ноды файла""" + xmlNode = xmlNewNode + childNodes = xmlNode.childNodes + nextOldNode = xmlOldNode + flagError = False + if xmlNode.nodeType ==xmlNode.ELEMENT_NODE: + n = xmlNode + path = u'' + nName = u'' + flagArray = False + nValue = u'' + nAction = u'' + attrName = '' + attrType = '' + path = n.tagName + if path == "items": + flagArray = True + if not flagArray: + if n.hasAttribute("name"): + nName = n.getAttribute("name") + attrName = u"attribute::name='%s'"%nName + if n.hasAttribute("value"): + nValue = n.getAttribute("value") + if n.hasAttribute("action"): + nAction = n.getAttribute("action") + if not nAction in ("join","replace","drop"): + textError = _('''In the text, XML profile, look \ +for a reserved attribute 'action' with the incorrect value.\n\ +Valid values attribute 'action': \ +(action="join", action="replace", action="drop")''') + self.setError(textError) + return False + if xmlOldNode.parentNode: + findStr = u"child::%s"%path + findAttrStr = "" + if attrName: + findAttrStr = "[%s]"%attrName + findPath = u"child::%s%s"%(path,findAttrStr) + # Рабочая нода + if flagRootNode: + workNode = xmlOldNode.parentNode + else: + workNode = xmlOldNode + oldNodes = xpath.Evaluate(findPath, workNode) + flagDrop = False + flagJoin = True + flagReplace = False + flagAppend = False + if nAction == "replace": + flagJoin = False + flagReplace = True + elif nAction == "drop": + flagJoin = False + flagDrop = True + if flagRootNode: + textError = _('Incorrect action="drop" in root node') + self.setError(textError) + return False + if path == "panel": + flagJoin = False + if levelNumber in self.panelNumbers.keys(): + self.panelNumbers[levelNumber] += 1 + else: + self.panelNumbers[levelNumber] = 0 + if oldNodes: + if len(oldNodes)>1 and path != "panel": + textError = _("The uncertainty in this profile are \ +the same nodes at one level") + self.setError(textError) + return False + if path == "panel": + if len(oldNodes)<=self.panelNumbers[levelNumber]: + nextOldNode = oldNodes[-1] + # Добавляем ноду + if not flagDrop: + flagAppend = True + flagReplace = False + childNodes = False + else: + nextOldNode=oldNodes[self.panelNumbers[levelNumber]] + else: + nextOldNode = oldNodes[0] + # Замещаем ноду в случае массива + if flagArray and not flagDrop: + replaceXmlNode = xmlNode.cloneNode(True) + if nAction: + replaceXmlNode.removeAttribute("action") + workNode.replaceChild(replaceXmlNode, + nextOldNode) + flagJoin = False + flagReplace = False + childNodes = False + # Объединение нод + if flagJoin: + if nextOldNode.hasAttribute("value"): + oValue = nextOldNode.getAttribute("value") + if nValue != oValue: + nextOldNode.setAttribute("value",nValue) + # Замещение ноды + elif flagReplace: + replaceXmlNode = xmlNode.cloneNode(True) + if not\ + self._removeDropNodesAndAttrAction(replaceXmlNode): + return False + workNode.replaceChild(replaceXmlNode, + nextOldNode) + childNodes = False + # Удаление ноды + elif flagDrop: + workNode.removeChild(nextOldNode) + childNodes = False + else: + flagAppend = True + flagDrop = False + if flagAppend and not flagDrop: + # Добавление ноды + childNodes = False + if not flagDrop: + appendXmlNode = xmlNode.cloneNode(True) + if not\ + self._removeDropNodesAndAttrAction(appendXmlNode): + return False + workNode.appendChild(appendXmlNode) + if childNodes: + for node in childNodes: + levelNumber +=1 + if not self._join(node, nextOldNode, False, levelNumber): + flagError = True + break + levelNumber -= 1 + if flagError: + return False + return True + + def join(self, xml_xfceObj): + """Объединяем конфигурации""" + if isinstance(xml_xfceObj, xml_xfcepanel): + try: + self.joinDoc(xml_xfceObj.doc) + except: + self.setError(_("Can not join profile")) + return False + return True diff --git a/pym/cl_data.py b/pym/cl_data.py new file mode 100644 index 0000000..926c224 --- /dev/null +++ b/pym/cl_data.py @@ -0,0 +1,624 @@ +#-*- 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 os +import sys +import cl_utils +from cl_lang import lang +from cl_template import iniParser +from cl_string import columnWrite + +# Перевод модуля на другой язык +tr = lang() +tr.setLocalDomain('cl_lib') +tr.setLanguage(sys.modules[__name__]) + +class var: + '''Объект "Переменная окружения"''' + # название сервиса которому принадлежит переменная + #(Global, Builder, Client, Server итд) + service = None + # значение переменной + value = "" + # режим записи (атрибут mode) + mode = "r" + # переменная для внутреннего использования (official) + official = False + # количество вызовов метода заполнения + countFill = 0 + # объект в котором создан этот объект + parentObj = None + # запускать или нет метод заполнения + fillStart = True + # dynamic = True то переменная динамическая при повтороном запуске + # запускается метод заполнения + # метод заполнения не запускается только если fillStart = False + # (осторожно возможно зацикливание программы если методы заполнения + # переменных используют методы друг друга) + dynamic = False + + def __init__(self, parentObj): + # словарь зависимых переменных {имя:значение} + self.dependValues = {} + # тип переменной (атрибут type) + self.type = ('default') + # список допустимых значений переменных (select) + self.select = () + # объект который создал этот объект + self.parentObj = parentObj + + def is_update(self): + #Нужно ли перезапускать метод заполнения (если зависимые переменные + #обновились то нужно) + upd = False + for depVarName in self.dependValues.keys(): + value = self.parentObj.__getattribute__(depVarName).Get() + if self.dependValues[depVarName] != value: + self.dependValues[depVarName] =\ + self.parentObj.__getattribute__(depVarName).value + upd = True + break + return upd + + def Get(self): + """Получение значения переменной""" + if not self.fillStart: + return self.value + if self.dynamic: + self.value = self.Fill() + return self.value + if not self.value: + if self.countFill>0: + return self.value + self.countFill += 1 + self.value = self.Fill() + if self.dependValues and self.is_update(): + self.countFill += 1 + self.value = self.Fill() + return self.value + + def Set(self, value): + """Запись значения переменной""" + self.value = value + return self.value + + def Fill(self): + """Заполнение переменной в далнейшем заменяем методом заполнения""" + return self.value + + +class DataVars(object): + class DataVarsError(Exception): + """Класс ошибок""" + pass + # добавляем пути к модулям если они не добавлены + if not os.path.abspath(\ + '/usr/lib/calculate/calculate-server/pym') in sys.path: + sys.path.insert(0,os.path.abspath(\ + '/usr/lib/calculate/calculate-server/pym')) + if not os.path.abspath(\ + '/usr/lib/calculate/calculate-lib/pym') in sys.path: + sys.path.insert(0,os.path.abspath(\ + '/usr/lib/calculate/calculate-lib/pym')) + if not os.path.abspath(\ + '/usr/lib/calculate/calculate-builder/pym') in sys.path: + sys.path.insert(0,os.path.abspath(\ + '/usr/lib/calculate/calculate-builder/pym')) + # Импортируемые модули - (раздел: модуль переменных, модуль заполнения + #переменных) + __modlist={'Global':('cl_vars','cl_fill'), + 'Server':('cl_vars_server','cl_fill_server'), + 'Builder':('cl_vars_builder','cl_fill_builder'), + 'Client':('cl_vars_client','cl_fill_client'), + } + def __init__(self): + #self.t1 = fillVars() + #self.t1.Get = self.Get + #self.t1.Set = self.Set + # Для нахождения зависимостей переменных + self.__levelNumber = 0 + self.__LevelsVar = [] + # Для хранения импортированных модулей и объектов + #[(cекция,импортированный модуль переменных, объект заполнения),] + self._importList = [] + self._importData("Global") + + def _importData(self, section): + """Импортирует модули с переменными и модули с функциями заполнения + + section секция раздела (Global, Server, Client итд) + создает необходимые структуры данных + """ + if not section in self.__modlist.keys(): + raise self.DataVarsError(_("Unsupported section %s")%section) + modVar = self.__modlist[section][0] + modFill = self.__modlist[section][1] + # Импортируем класс описания переменных и класс заполнения + try: + exec ("import %s" % (modVar)) + except ImportError, e: + err1 = _("Error in import module %s")%modVar + err2 = _("error") + ": " +str(e) + raise self.DataVarsError("%s\n%s"%(err1,err2)) + flagFindFillModule = True + try: + exec ("import %s" % (modFill)) + except ImportError, e: + if "No module named" in str(e): + flagFindFillModule = False + else: + err1 = _("Error in import module %s")%modFill + err2 = _("error") + ": " +str(e) + raise self.DataVarsError("%s\n%s"%(err1,err2)) + if flagFindFillModule: + # Создаем объект с методами заполнения переменных + exec("fillObj = %s.fillVars()" %modFill) + # Подключаем методы получения и записи переменных + fillObj.Get = self.Get + fillObj.Set = self.Set + else: + fillObj = False + # Заполняем self._importList + exec("self._importList.insert(0,(section,%s,fillObj))"%(modVar)) + + def __findVarData(self, nameVar): + """Находит данные для создания объекта переменная в модулях и + + объектах + """ + # Ищем переменную в модуле + dataVar = False + e = False + for section, moduleVar, fillobj in self._importList: + try: + exec("dataVar=moduleVar.Data.%s"%nameVar) + except AttributeError, e: + pass + if dataVar: + break + if dataVar == False: + err1 = _("Not found variable %s")%nameVar + err2 = "" + if e: + err2 = _("error") + ": " +str(e) + raise self.DataVarsError("%s\n%s"%(err1,err2)) + dataVar['service'] = section + # Ищем метод в объекте методов заполнения + nameMethod = "get_" + nameVar + flagFindMetod = False + for section, moduleVar, fillobj in self._importList: + if fillobj: + if nameMethod in dir(fillobj): + flagFindMetod = True + method = fillobj.__getattribute__(nameMethod) + break + if flagFindMetod: + return (dataVar,method) + else: + return (dataVar,False) + + def __setAttributesVar(self, var ,nameVar, dict): + """Установка аттрибутов для созданного объекта var + + название аттрибута и его значение берется из словаря dict + """ + dict['type'] = nameVar.split('_') + if not set(dict.keys()) <= set(dir(var)): + raise self.DataVarsError(\ + _("error initalize variable %s, incorrect data")%nameVar) + for nameAttr in dict.keys(): + setattr(var,nameAttr, dict[nameAttr]) + return True + + def __Get(self, nameVar): + ret = "" + self.__LevelsVar.append((self.__levelNumber, nameVar)) + self.__levelNumber += 1 + #nameMethod = "get_" + nameVar + if self.__dict__.has_key(nameVar): + ret = self.__getattribute__(nameVar).Get() + elif self.__findVarData(nameVar): + dictVar, methodFill =self.__findVarData(nameVar) + varobj = var(self) + # Устанавливаем аттрибуты + self.__setAttributesVar(varobj, nameVar, dictVar) + if methodFill: + varobj.Fill = methodFill + self.__setattr__(nameVar, varobj) + ret = self.__getattribute__(nameVar).Get() + self.__levelNumber -= 1 + if self.__levelNumber == 0 and\ + self.__getattribute__(nameVar).fillStart and\ + len(self.__LevelsVar)>1: + links = self.__getLinks(self.__LevelsVar) + for name in links.keys(): + for nameLink in links[name].keys(): + val = self.__getattribute__(nameLink).Get() + self.__getattribute__(name).dependValues[nameLink] = val + if self.__levelNumber == 0: + self.__LevelsVar = [] + return ret + + def Get(self, nameVar): + return self.__Get(nameVar) + + + def __Set(self, nameVar, value, force=False): + nameMethod = "get_" +nameVar + if not self.__dict__.has_key(nameVar) and self.__findVarData(nameVar): + dictVar, methodFill =self.__findVarData(nameVar) + varobj = var(self) + # Устанавливаем аттрибуты + self.__setAttributesVar(varobj, nameVar, dictVar) + if methodFill: + varobj.Fill = methodFill + self.__setattr__(nameVar, varobj) + if self.__dict__.has_key(nameVar): + if not force and "r" in getattr(self, nameVar).mode: + print _("Attempt to rewrite a variable for reading:%s")\ + %nameVar + return False + self.__getattribute__(nameVar).fillStart = False + return self.__getattribute__(nameVar).Set(value) + + def Set(self, nameVar, value, force=False): + return self.__Set(nameVar, value, force) + + def __frame(self, lVar): + """получить список областей зависимости переменных""" + data = [] + if not lVar: + return data + firstLevel = lVar[0][0] + for level, name in lVar[1:]: + if level> firstLevel: + data.append((level, name)) + else: + break + return data + + def __getLinks(self, lVar): + """Получить список переменных и от каких переменных они зависят + + на вход список [(уровень рекурсии, название переменной),] + """ + links = {} + frames = {} + levelLinks = {} + lVarFr = lVar + for level, name in lVar: + fr = self.__frame(lVarFr) + if not frames.has_key(name): + frames[name] = fr + levelLinks[name] = level+1 + lVarFr = lVarFr[1:] + for name in frames.keys(): + level = levelLinks[name] + fr = frames[name] + links[name] = {} + for lv, nm in fr: + if level == lv: + links[name][nm] = "" + return links + + def __getPathCalculateIni(self): + """Получить пути до ini файлов""" + return self.Get('cl_env_path') + + def __getSection(self, vname): + """секция для записи в ini файл переменной + + vname - имя переменной + """ + if self.__dict__.has_key(vname): + if self.__dict__[vname].service == 'Global': + return 'calculate' + else: + return self.__dict__[vname].service.lower() + + def __writeVarValue(self, vname, val, location, header): + '''Записать значение в calculate.ini + + Параметры: + vname имя переменной + val значение переменной + location расположение ini файла ('default', 'local', 'remote') + header раздел ini файла ('client', 'server', 'calculate') + + Возвращаемые значение: + True запись успешна + False запись не удалсь + ''' + # получаем все пути до ini файлов + calculate_ini = self.__getPathCalculateIni() + # получаем полный путь до файла ini + if location == 'default': + name_calculate_ini = calculate_ini[2] + elif location == 'local': + name_calculate_ini = calculate_ini[1] + elif location == 'remote': + name_calculate_ini = calculate_ini[0] + else: + return False + # извлекаем из полного имени файла путь + onlydir = os.path.split(name_calculate_ini)[0] + try: + # проверяем чтобы путь до ини файла существовал + if not os.path.exists(onlydir): + # создаем его если отсутствует + os.makedirs(onlydir) + except OSError (nerr,msg): + print nerr, msg + return False + config = iniParser(name_calculate_ini) + # Получаем секцию конфигурационного файла + if not header: + header = self.__getSection(vname) + return config.setVar(header,{vname: cl_utils.convertStrListDict(val)}) + + def __deleteVarValue(self, vname, location, header): + '''Удалить переменную в calculate.ini + + Параметры: + vname имя переменной + location расположение ini файла ('default', 'local', 'remote') + + Возвращаемые значение: + True удалено успешна + False удаление не удалсь + ''' + # получаем все пути до ini файлов + calculate_ini = self.__getPathCalculateIni() + # получаем полный путь до файла ini + if location == 'default': + name_calculate_ini = calculate_ini[2] + elif location == 'local': + name_calculate_ini = calculate_ini[1] + elif location == 'remote': + name_calculate_ini = calculate_ini[0] + else: + return False + # извлекаем из полного имени файла путь + onlydir = os.path.split(name_calculate_ini)[0] + # проверяем чтобы путь до ини файла существовал + if not os.path.exists(onlydir): + return False + config = iniParser(name_calculate_ini) + # Получаем секцию конфигурационного файла + if not header: + header = self.__getSection(vname) + # Удаляем переменную + retDelVar = config.delVar(header, vname) + retDelArea = True + if not config.getAreaVars(header): + retDelArea = config.delArea(header) + if retDelArea and retDelVar: + return True + else: + return False + + def Write(self, vname, val, force=False, location='default',header=False): + '''Установить и записать значение переменной в ini файл + + Параметры: + vname имя переменной + val значение переменной + force "принудительный режим" + location расположение ini файла ('default', 'local', 'remote') + header раздел ini файла ('client', 'server', 'calculate') + ''' + if self.__Set(vname, val, force)!= False: + if not val.strip(): + self.__deleteVarValue(vname, location, header) + self.__writeVarValue(vname, val, location, header) + return True + return False + + def Delete(self, vname, location='default', header=False): + '''Удалить переменную в calculate.ini + + Параметры: + vname имя переменной + location расположение ini файла ('default', 'local', 'remote') + + Возвращаемые значение: + True удалено успешна + False удаление не удалсь + ''' + return self.__deleteVarValue(vname, location, header) + + def __getActiveSections(self): + """активные секции в ini файле""" + act_section = [] + for service,t,t in self._importList: + if service == "Global": + act_section.append('calculate') + else: + act_section.append(service.lower()) + return act_section + + def flIniFile(self): + '''Заместить значение переменных значениями из ини файлов + + Возвращаемые значения: + cловарь импортированных переменных - переменные считаны + False - файл не был обнаружен + ''' + #Cловарь переменных из ini файлов + importVars = {} + calculate_ini = self.__getPathCalculateIni() + # активные секции (секции из которых будут использованы переменные) + act_section = self.__getActiveSections() + set_act_section = set(act_section) + i = 0 + locations = ['remote','local','default'] + for name_calculate_ini in calculate_ini: + # проверить сущестование ini файла + if os.path.exists(name_calculate_ini): + # получить объект настроенный на ini + config = iniParser(name_calculate_ini) + # получаем все секции из конфигурационного файла + allsect = config.getAllSectionNames() + if not allsect: + continue + # находим встречающиеся у обоих секции + act_sect = tuple(set(allsect)& set_act_section) + # словарь переменных для ini - файла + importFileVars = {} + # получаем все переменные из всех секций + for section in act_sect: + allvars = config.getAreaVars(section) + if allvars == False: + return False + # словарь переменных для ini - файла + importFileVars = {} + # принудительно переписать все переменные окружения + # полученные из ini + for (k,v) in allvars.items(): + k = k.encode("UTF-8") + value = cl_utils.convertStrListDict(v.encode("UTF-8")) + self.Set(k, value, True) + importFileVars[k] = value + if i < 3: + importVars[locations[i]] = importFileVars + i += 1 + return importVars + + def flServer(self, **args): + '''Заполнить конфигурацию переменных, для ldap''' + # заполнить переменные окружения алгоритмом по умолнанию + self._importData("Server") + + def flClient(self, **args): + '''Заполнить конфигурацию переменных, для клиента''' + # заполнить переменные окружения алгоритмом по умолнанию + self._importData("Client") + + def flBuilder(self, **args): + '''Заполнить конфигурацию переменных, для билдера''' + self.Set('setup_pass','builder',True) + # заполнить переменные окружения алгоритмом по умолнанию + self._importData("Builder") + + def flInstall(self, **args): + '''Заполнить конфигурацию переменных для инсталятора''' + self.Set('setup_pass','install',True) + + #def defined(self, vname): + #if vname: + #if self.__dict__.has_key(vname): + #return True + #return False + def defined(self, vname): + return True + + + + def exists(self, nameVar): + """ Определяет существует ли переменная с таким имененм + """ + if self.__dict__.has_key(nameVar): + return True + foundVar = False + # Ищем переменную в импортируемых модулях + for section, moduleVar, fillobj in self._importList: + if moduleVar.Data.__dict__.has_key(nameVar): + foundVar = True + break + return foundVar + + def getVars(self, type_names=None): + ret = {} + for section, moduleVar, fillobj in self._importList: + dataVar=moduleVar.Data + dictVars = dir(dataVar) + for nameVar in dictVars: + if not "__" in nameVar: + if not (getattr(dataVar,nameVar).has_key("official") and\ + getattr(dataVar,nameVar)['official']): + self.Get(nameVar) + if type_names: + #type_names.sort() + varType =list(getattr(dataVar,nameVar)['type']) + #varType.sort() + #print type_names + #print varType + #print + if not set(type_names)<=set(varType): + continue + ret[nameVar] = getattr(self,nameVar) + return ret + + #распечатать список переменных с значениями + def printVars(self,type_names=None): + var=None + var=self.getVars(type_names) + mlen_name=0; + mlen_type=0; + mlen_mode=0; + for i,j in var.items(): + if len(i)>mlen_name: + mlen_name=len(i) + #if len(str(j.type))>mlen_type: + #mlen_type=len(str(j.type)) + vtype=str(type(var[i].value)).split(" ")[1][1] + if not '[' in var[i].mode: + if vtype in ['d','l']: + mode="[%s%s]"%(var[i].mode.lower(),vtype) + else: + mode="[%s]"%(var[i].mode.lower()) + var[i].mode=mode + if len(mode)>mlen_mode: + mlen_mode=len(mode) + plist=var.keys() + plist.sort() + br = cl_utils.fillstr("-",mlen_name) + " " +\ + cl_utils.fillstr("-",mlen_mode) + " " + cl_utils.fillstr("-",10) + #cl_utils.fillstr("-",mlen_type) + " " +\ + + print "The list of variables:" + print "var name".center(mlen_name),\ + "Mode","Value" + #"Type".center(mlen_type),\ + + + print br + for i in plist: + #if var[i].value is None: + #continue + p_val=var[i].value + if var[i].official: + continue + columnWrite( i, mlen_name, var[i].mode.lower(), + mlen_mode, + #str(var[i].type), + #mlen_type, + p_val) + print br + +class glob_attr: + """Глобальные аттрибуты для методов заполнения переменных""" + + def _runos(self,cmd, ret_first=None, env={}): + """Вернуть результат выполнения команды ОС""" + if not env: + envDict = {} + env.update(os.environ.items() + [("PATH",cl_utils.getpathenv())] +\ + env.items()) + retCode, programOut = cl_utils.runOsCommand(cmd, None, ret_first, env) + if not retCode: + return programOut + return False diff --git a/pym/cl_fill.py b/pym/cl_fill.py index 59beef4..4d3b598 100644 --- a/pym/cl_fill.py +++ b/pym/cl_fill.py @@ -1,6 +1,6 @@ #-*- coding: utf-8 -*- -#Copyright 2008 Calculate Pack, http://www.calculate-linux.org +# 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. @@ -17,10 +17,11 @@ import re import os import types +from cl_overriding import exit import cl_utils -import cl_base +import cl_data -class fillVars(object, cl_base.glob_attr): +class fillVars(object, cl_data.glob_attr): def get_os_net_domain(self): ''' Определим домен''' @@ -28,7 +29,7 @@ class fillVars(object, cl_base.glob_attr): if not domain: print _("Error:") + " " +_("Not found domain name") print _("Command 'hostname -d' returns an empty value") - cl_base.exit(1) + exit(1) elif re.search("^hostname: ",domain): return "local" else: @@ -99,7 +100,7 @@ class fillVars(object, cl_base.glob_attr): systemVersion = "" flagGentoo = False if os.path.exists(gentooFile): - gentooLink = "/etc/make.profile" + gentooLink = "/etc/make.template" if os.path.islink(gentooLink): systemVersion = os.readlink(gentooLink).rpartition("/")[2] flagGentoo = True diff --git a/pym/cl_help.py b/pym/cl_help.py new file mode 100644 index 0000000..20ca654 --- /dev/null +++ b/pym/cl_help.py @@ -0,0 +1,375 @@ +#-*- 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 getopt +import sys +from cl_string import prettyColumnStr + +pcs = prettyColumnStr + +class opt: + def __init__(self,shortOpt,longOpt = []): + """ Длинные и короткие опции командной строки допустимые в программе + a - короткая опция + >program -a + + a: - короткая опциия со значением + >program -a 10 + + a:: - короткая опциия у которой может быть или не быть значение + >program -a + >program -a 15 + + "ha:" - значение параметра shortOpt + две опции h - без значения, a - со значением + + help - длинная опция без значения + test= - длинная опция со значением + + ["help","test="] - значение парамера longOpt + >program -a + две опции help - без значения, test - со значением + """ + self.shortOpt = shortOpt + self.longOpt = longOpt + self.sysArgv = sys.argv[1:] + + def getopt(self): + try: + opts, args = getopt.getopt(self.sysArgv,self.shortOpt,self.longOpt) + except getopt.GetoptError: + self.handlerErrOpt() + sys.exit(1) + for option, value in opts: + if len(option) == 2: + option = option[1:] + else: + option = option[2:] + self.handlerOpt(option,value) + for param in args: + self.handlerParam(param) + + def handlerErrOpt(self): + # Обработчик в случае неправильных параметров + pass + + def handlerOpt(self,option,value): + # Обработчик (параметр значение) + pass + + def handlerParam(self,param): + # Обработчик хвостов (значение) + pass + +class cl_help: + """Объект для работы со справкой, и обработкой параметров. + + Конструктор __init__ должен определить следующие переменные: + + self.chapter список разделов справки, каждый элементы состоит из + имени раздела, флаг видимый/скрытый, кол-во переводов строк + после названия раздела, количество строк после раздела, + тип раздела + Пример: [("Copyright",False,0,2),"options"] + self.relService словарь связей сервисов и действующих опций + ключ - название сервиса, значение - список отображаемых + разделов отмеченных как "options" + Пример: {"samba":[_("Common options"), + _("Service Samba options")]} + self.relOptions словарь связей длинных опций помощи и выводимых разделов + помощи с опциями + ключ - параметр справки, значение список отображаемых + разделов справки + Пример: {"help-ldap":[_("Common options"), + _("Service LDAP options)]} + self.progName словарь имена используемых программ и их номера для + доступа к переменным + Пример: {'cl-groupadd':0, 'cl-groupdel':1} + self.data список данных для справки, каждый элемент словарь: + progAccess: список номеров программ отображающих + Пример: {'progAccess':(0,), + 'shortOption':"g", + 'longOption':"gid", + 'optVal':"GID", + 'helpChapter':_("Options"), + 'help':_("use GID for the new group") + }, + после заполнения параметров необходимо выполнить + self._cl_help__setParamHelp() для заполнения справки + + """ + def __init__(self, cmdName): + # ширина консоли взята за 80 + # -1 чтобы компенсировать расстрояние между колонками + self.consolewidth = 79 + self.column_width = 32 + self.cmdName = cmdName + #короткие опции командной строки + self.shortOpt = [] + #длинные опции командной строки + self.longOpt = [] + # массив разделов (заполняется в __setParamHelp) + self.chapterBloc = [] + #optEnd = "" + #if "user" in self.cmdName and not "mod" in self.cmdName: + #optEnd = _("user") + #elif "group" in self.cmdName and not "mod" in self.cmdName: + #optEnd = _("group") + #self.__setParamHelp() + + def getChapterNumber(self,NameChapter): + """Получить номер раздела по имени""" + num = 0 + for i in self.chapter: + if i[0] == NameChapter: + return num + num += 1 + return False + + def __setParamHelp(self): + """Внутренняя функция формирования справки по данным + + Перебирает все элементы списка data, проверяет их на доступность + данной программы, разбирает опции на среди data и формирует + для по ним справку. + """ + # сформировать нужное количество блоков раздела + self.chapterBloc = [""]*len(self.chapter) + # + sp = {} + i = 0 + # перебираем все элементы справки собираем элементы опции + # так же формируем разделы не опции + for par in self.data: + # перебираем только те опции, которые принадлежат команде + if self.access(par): + # есть короткая (возможно есть и длинная) + if par.has_key("shortOption"): + sp[par["shortOption"]+":"+par["helpChapter"]] = i + # есть только длинная опция + elif par.has_key("longOption"): + sp[par["longOption"]+":"+par["helpChapter"]] = i + # формирование разделов не опций + else: + helpTxt = par['help'] + numChapter = self.getChapterNumber(par['helpChapter']) + self.addChapterHelp(numChapter,helpTxt) + i += 1 + # перебираем все "собранные" опции + # опции перебираются по порядку в списке date + # для сортировки по ключам следует применить код: + # for index in sorted(sp.keys()): + # par = self.data[sp[index]] + for index in sorted(sp.values()): + par = self.data[index] + numChapter = self.getChapterNumber(par['helpChapter']) + # если есть и короткая и длинная + if "shortOption" in par and "longOption" in par: + paraminfo = "-%s, --%s "%(par["shortOption"],par["longOption"]) + # если есть только короткая + elif "shortOption" in par: + paraminfo = "-%s "%par["shortOption"] + # если только длинная + else: + paraminfo = "--%s "%par["longOption"] + # если указан параметр для опции + if "optVal" in par: + optVal = par["optVal"] + else: + optVal = "" + + # вывод вида: " [-o, ][--option] [PARAM]" "helpstring" + helpTxt = pcs(" "+paraminfo+optVal, self.column_width, \ + par['help'], self.consolewidth-self.column_width) + # добавить строку в нужный раздел + self.addChapterHelp(numChapter,helpTxt) + + def getHelp(self, optionsChapters=False): + """Выдать справку. + + Выдает справку в случае если указан optionsChapters, то фильтрует по + типу разделов. + + Параметры: + optionsChapters Flase или список опциональных разделов для + отображения + + Возвращаемые параметры: + Строка со справкой. + """ + # Выдать справку + help = "" + # перебираем все элементы справочных блоков + iterChapterBloc = iter(self.chapterBloc) + # перебираем все разделы по параметрам + for (nameChapter, visibleChapter, beforeStrChapter, \ + afterStrChapter, typeChapter) in self.chapter: + # получаем следующий блок (т.о. textChapterBloc соответ, chapter) + textChapterBloc = iterChapterBloc.next() + # если тип раздела опциональный + if optionsChapters and typeChapter=="options": + # проверяем нужно ли его отображать + if not (nameChapter in optionsChapters): + continue + bef = "\n"*beforeStrChapter + aft = "\n"*afterStrChapter + # если блок не пустой и раздел отображаемый + if len(textChapterBloc) > 0: + if visibleChapter: + help += nameChapter + ": " + bef + help += textChapterBloc + aft + help = help.rstrip()+"\n" + return help + + def addChapterHelp(self, numChapter, helpTxt): + """Добавить в раздел помощи numChapteк тектстовую строку helpTxt + + Параметры: + numChapter номер раздела в который нужно добавить данные справки + helpTxt строка, содержащая данные + """ + self.chapterBloc[numChapter] += helpTxt + return True + + def addData(self,dataHash): + # На будущее (добавляет опции) + self.data.append(dataHash) + return True + + def handleCheckAccess(self,dataHash): + """Замещаемый дополнительный обработчик проверки + доступности опции. + + Входные параметры: + dataHash элементы списка данных справки (self.data) + """ + return True + + def access(self,dataHash): + """Доступна ли опция вызывающей программе + + Параметры: + dataHash словарь элемент типа self.data + + Возвращаемые параметры: + True/False доступна/недоступна + """ + # доступна ли опция вызывающей программе + # опция без progAccess доступна + numProg = self.progName[self.cmdName] + if 'progAccess' in dataHash: + if numProg in dataHash['progAccess']: + # вызов дополнительной проверки доступа к опции + return self.handleCheckAccess(dataHash) + else: + return False + else: + # вызов дополнительной проверки доступа к опции + return self.handleCheckAccess(dataHash) + + def getTypeChapter(self, nameChapter): + """Получить тип раздела по его имени + + Параметры: + nameChapter название раздела + + Возвращаемые параметры: + строка тип раздела + Flase(Boolean) такой раздел отсутствует + """ + # фильтруем список по имени раздела, помещаем в список тип раздела + filtered = [typeChapter for name, na, na, na, typeChapter \ + in self.chapter if name == nameChapter] + # если среди фильтрованных есть хоть один элемент + if len(filtered) > 0: + # возвращаем - он запрашиваемый + return filtered[0] + else: + # такой раздел отсутствует + return False + + def clearAllOpt(self): + """Очистить все опции, полученные посредством getAllOpt""" + if len(self.shortOpt) > 0: + self.shortOpt = [] + if len(self.longOpt) > 0: + self.longOpt = [] + return True + + def getAllOpt(self,typeOpt="all", optionsChapters=False): + """Получить все доступные опции + + Параметры: + typeOpt 'short'/'long'/'all', вернуть короткие или длинные + опции или все (возвращаются кортежем) + optionsChapters фильтр для опций по типам разделов (список,кортеж) + + Возвращаемые параметры: + строка коротки или список строк длинных опций ('hb:c:wg:G:k:ms:u:') + """ + # Выдать все действующие опции + if typeOpt=="short" or typeOpt=="all": + if len(self.shortOpt) == 0: + for par in self.data: + if optionsChapters and\ + self.getTypeChapter(par['helpChapter'])=="options": + if not (par['helpChapter'] in optionsChapters): + continue + if par.has_key("shortOption") and self.access(par): + if par.has_key("optVal"): + self.shortOpt.append(par["shortOption"]+':') + else: + self.shortOpt.append(par["shortOption"]) + if typeOpt=="long" or typeOpt=="all": + if len(self.longOpt) == 0: + for par in self.data: + if optionsChapters and\ + self.getTypeChapter(par['helpChapter'])=="options": + #print par["longOption"] + if not (par['helpChapter'] in optionsChapters): + continue + if par.has_key("longOption") and self.access(par): + if par.has_key("optVal"): + self.longOpt.append(par["longOption"]+'=') + else: + self.longOpt.append(par["longOption"]) + if typeOpt=="short": + return "".join(self.shortOpt) + elif typeOpt=="long": + return self.longOpt + elif typeOpt=="all": + return ("".join(self.shortOpt),self.longOpt) + + def getShortOpt(self,option): + """Из любой опции получить короткую опцию. + + Фильтрация также происходит и по названию команды. + + Параметры: + option запрашиваемая опция + + Возвращаемые параметры: + короткая опция, если же для длинной опции нет короткой, возвращается + пустая строка. + """ + # Из любой опции получаем короткую опцию + for par in self.data: + if par.has_key("shortOption") and self.access(par): + if (par.has_key("longOption") and\ + par["longOption"] == option) or \ + par["shortOption"] == option: + return par["shortOption"] + return "" diff --git a/pym/cl_lang.py b/pym/cl_lang.py new file mode 100644 index 0000000..eb0cf69 --- /dev/null +++ b/pym/cl_lang.py @@ -0,0 +1,168 @@ +#-*- 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 os, gettext +from cl_overriding import __findFileMO + +class GlobalParam(type): + """ Метакласс для глобальных параметров + """ + def __init__(cls, *args): + cls.GP = [] + cls.GP.append("") + +gettext.find = __findFileMO + +class lang: + """Класс многоязыковой поддержки lang для перевода сообщений программ на +другие языки. + +Типичное использование: + import sys + import lang + + # язык сообщений английский + tr = lang.lang(en) + # язык определяется системой + #tr = lang.lang() + + #Установка домена переводаldap + # в последующем можно не использовать - задается глобально + tr.setGlobalDomain('calc') + # задается локально для одного файла + #tr.setLocalDomain('calc') + + # Установка метода перевода для текущего модуля + tr.setLanguage(sys.modules[__name__]) + +Где: + tr -- объект перевода + 'en' -- язык выводимых сообщений + sys.modules[__name__] -- модуль сообщения которого переводятся + calc - домен перевода - имя файла перевода без расширения + +Если файл перевода не найден то сообщения не переводятся +Если в модуле сообщения которого переводим, экспортируются другие модули то +они тоже переводятся. + +По умолчанию директория в которой находятся переводы: 'lang/i18h' относительно +исполняемого файла названия файлов перевода совпадают с названиями модулей +если не определен методом setDomainTranslate() - домен перевода. + """ + __metaclass__ = GlobalParam + def __init__(self,l=''): + self.nameDomain = self.GP[0] + #self.nameDomain = '' + """ Название файла перевода (Домен) если используется 1 файл перевода + """ + self.__catalog = os.path.abspath('/usr/share/calculate/i18n') + """ Путь к каталогу переводов (в этом каталоге + ru_RU/LC_MESSAGES в котором файл перевода) + """ + env = os.environ + if l == "" and env.has_key('LANG'): + l = env['LANG'].split('.')[0].split("_")[0] + """ Определение языка """ + self.__modnames = {} + """ Словарь переведенных модулей + ключ --- имя модуля + значение --- был ли модуль переведен (1 или 0) + """ + self.__l = l + """Язык перевода для всех модулей""" + + def __translate(self,message): + """Метод translate возвращает полученное значение без +изменений""" + return message + + def setLanguage(self,module): + """ Установка языка перевода для модуля module. + + параметр --- экспортируемый модуль python + если в этом модуле экспортируются другие модули + то язык устанавливается и для них + Метод запускается после экспорта модуля который будем переводить + """ + t = vars(module) + for i in dir(module): + q = str(t[i]) + if 'module' in q and not '__' in i and not '/usr/lib' in q\ + and not 'built-in' in q : + mod = vars(module)[i] + self.__setLang(mod) + return self.__setLang(module) + + def __setLang(self,module): + """ Установка языка перевода для модуля module. + + В случае нахождения файла перевода возвращает истину. + Во всех случаях устанавливает метод перевода для модуля. + Если нет файла перевода метод перевода возвращает то же + значение что получает + """ + if module.__name__ in self.__modnames.keys(): + return True + + if self.nameDomain == '': + if module.__name__ == "__main__": + nameDomain = module.__file__.split('.')[0] + else: + nameDomain = module.__name__ + else: + nameDomain = self.nameDomain + + if self.__l == 'en': + module._ = self.__translate + ret = 1 + else: + la = [] + la.append(self.__l) + if gettext.find(nameDomain,self.__catalog,la): + """Если найден словарь то инициализируем переводчик""" + transl = gettext.translation(nameDomain\ + ,self.__catalog,la) + + #module._ = transl.ugettext + module._ = transl.gettext + ret = 1 + else: + module._ = self.__translate + ret = 0 + self.__modnames[module.__name__] = ret + return ret + + def getTranslatorByName(self,namemodule): + """ Метод который по имени модуля определяет, был ли модуль с этим + именем переведен + """ + if self.__modnames.has_key(namemodule): + return self.__modnames[namemodule] + return 0 + + def setGlobalDomain(self, nameDomain): + """ Метод для установки домена перевода (глобально для всех файлов) + """ + self.GP[0] = nameDomain + self.nameDomain = self.GP[0] + return True + + def setLocalDomain(self, nameDomain): + """ Метод для установки домена перевода (локально для одного файла) + """ + self.nameDomain = nameDomain + return True + diff --git a/pym/cl_ldap.py b/pym/cl_ldap.py new file mode 100644 index 0000000..bea5fae --- /dev/null +++ b/pym/cl_ldap.py @@ -0,0 +1,58 @@ +#-*- 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 ldap +from cl_utils import _error + +class ldapFun(_error): + '''Объект для работы с LDAP сервером + + подключение к серверу и поиск данных + ''' + def __init__(self, dnUser, password, host="localhost"): + self.conLdap = False + # Получаем соединение с LDAP + try: + self.conLdap = self.__ldapConnect(dnUser, password, host) + except ldap.LDAPError, e: + self.setError(e[0]['desc']) + + def __ldapConnect(self, dnUser, password, host): + """Соединение с LDAP сервером""" + conLdap = ldap.initialize('ldap://%s'%host) + conLdap.simple_bind_s(dnUser, password) + return conLdap + + def ldapSearch(self,baseDN, searchScope, searchFilter, retrieveAttributes): + try: + ldap_result_id = self.conLdap.search(baseDN, searchScope, + searchFilter, + retrieveAttributes) + result_set = [] + while 1: + result_type, result_data = self.conLdap.result(ldap_result_id, + 0) + if (result_data == []): + break + else: + if result_type == ldap.RES_SEARCH_ENTRY: + result_set.append(result_data) + except ldap.NO_SUCH_OBJECT: + return [] + except: + return False + return result_set + diff --git a/pym/cl_log.py b/pym/cl_log.py index 5c23348..ab65d0e 100644 --- a/pym/cl_log.py +++ b/pym/cl_log.py @@ -1,6 +1,6 @@ #-*- coding: utf-8 -*- -#Copyright 2008 Calculate Pack, http://www.calculate-linux.org +# 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. @@ -13,6 +13,7 @@ # 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 os import time diff --git a/pym/cl_overriding.py b/pym/cl_overriding.py new file mode 100644 index 0000000..0c6ca8e --- /dev/null +++ b/pym/cl_overriding.py @@ -0,0 +1,58 @@ +#-*- 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 os,sys,gettext + +def __findFileMO(domain, localedir=None, languages=None, all=0): + """Модифицированный метод, ищет файл перевода + + замена gettext.find""" + if localedir is None: + localedir = _default_localedir + if languages is None: + languages = [] + for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'): + val = os.environ.get(envar) + if val: + languages = val.split(':') + break + if 'C' not in languages: + languages.append('C') + # now normalize and expand the languages + nelangs = [] + for lang in languages: + for nelang in gettext._expand_lang(lang): + if nelang not in nelangs: + nelangs.append(nelang) + # select a language + if all: + result = [] + else: + result = None + for lang in nelangs: + if lang == 'C': + break + mofile = os.path.join(localedir, '%s_%s.mo' % (domain,lang)) + if os.path.exists(mofile): + if all: + result.append(mofile) + else: + return mofile + return result + +def exit(codeExit): + """Метод выхода из программы""" + sys.exit(codeExit) \ No newline at end of file diff --git a/pym/cl_print.py b/pym/cl_print.py new file mode 100644 index 0000000..1298357 --- /dev/null +++ b/pym/cl_print.py @@ -0,0 +1,217 @@ +#-*- 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, struct, termios, fcntl +from cl_utils import _toUNICODE + +class color_print(object): + + def getconsolewidth(self): + """Получить ширину текущей консоли""" + s = struct.pack("HHHH", 0, 0, 0, 0) + fd_stdout = sys.stdout.fileno() + try: + x = fcntl.ioctl(fd_stdout, termios.TIOCGWINSZ, s) + except IOError: + # если ошибка то ширина 80 символов + return 80 + #(rows, cols, x pixels, y pixels) + return struct.unpack("HHHH", x)[1] + + def printRight(self, offsetLeft, offsetRight): + """Добавляет необходимое количество пробелов: + + количество пробелов = (ширина консоли - offsetLeft - offsetRight) + """ + cols = self.getconsolewidth() + for i in range(cols - offsetLeft - offsetRight): + sys.stdout.write(" ") + + def colorPrint(self,attr,fg,bg,string): + """Раскрашивает выводимое сообщение + + Параметры: + attr - это атрибут + fg - цвет символа + bg - цвет фона + + в случае если параметр равен "" то он не изменяется + + attr может принимать следующие значения: + 0 сбросить все атрибуты (вернуться в нормальный режим) + 1 яркий (обычно включает толстый шрифт) + 2 тусклый + 3 подчёркнутый + 5 мигающий + 7 реверсный + 8 невидимый + + fg может принимать следующие значения: + 30 чёрный + 31 красный + 32 зелёный + 33 жёлтый + 34 синий + 35 фиолетовый + 36 голубой + 37 белый + + bg может принимать следующие значения: + 40 чёрный + 41 красный + 42 зелёный + 43 жёлтый + 44 синий + 45 фиолетовый + 46 голубой + 47 белый + """ + lst = [] + if attr: + lst.append(attr) + if fg: + lst.append(fg) + if bg: + lst.append(bg) + sys.stdout.write("\033[%sm%s\033[0m" %(";".join(lst),string)) + + def redBrightPrint(self, string): + """Печатает яркое красное сообщение""" + self.colorPrint("1","31","",string) + + def greenBrightPrint(self, string): + """Печатает яркое зеленое сообщение""" + self.colorPrint("1","32","",string) + + def yellowBrightPrint(self, string): + """Печатает яркое желтое сообщение""" + self.colorPrint("1","33","",string) + + def blueBrightPrint(self, string): + """Печатает яркое cинее сообщение""" + self.colorPrint("1","34","",string) + + def lenString(self, string): + """Получаем длинну строки""" + stringUnicode = _toUNICODE(string) + lenString = len(stringUnicode) + return lenString + + + def defaultPrint(self, string): + sys.stdout.write(string) + sys.stdout.flush() + + def printLine(self, argL, argR, offsetL=0, printBR=True): + """Печатает справа и слева консоли цветные сообщения""" + #Допустимые цвета + colorDict = {\ + # цвет по умолчанию + '':self.defaultPrint, + # ярко зеленый + 'greenBr':self.greenBrightPrint, + # ярко голубой + 'blueBr':self.blueBrightPrint, + # ярко красный + 'redBr':self.redBrightPrint, + # ярко желтый + 'yellowBr':self.yellowBrightPrint, + } + # cмещение от левого края консоли + #offsetL = 0 + for color,leftString in argL: + offsetL += self.lenString(leftString) + if colorDict.has_key(color): + # печатаем и считаем смещение + colorDict[color](leftString) + else: + colorDict[''](leftString) + # cмещение от правого края консоли + offsetR = 0 + for color,rightString in argR: + offsetR += self.lenString(rightString) + # Добавляем пробелы + if offsetR: + self.printRight(offsetL, offsetR) + for color,rightString in argR: + if colorDict.has_key(color): + # печатаем и считаем смещение + colorDict[color](rightString) + else: + colorDict[''](rightString) + if printBR: + print "" + + def printNotOK(self, string, offsetL=0, printBR=True): + """Вывод на печать в случае сбоя""" + self.printLine((('greenBr',' * '), + ('',string), + ), + (('blueBr','['), + ('redBr',' !! '), + ('blueBr',']'), + ), offsetL, printBR) + + def printOnlyNotOK(self, string, offsetL=0, printBR=True): + """Вывод на печать в случае сбоя""" + self.printLine((('', string),), + (('blueBr','['), + ('redBr',' !! '), + ('blueBr',']'), + ), offsetL, printBR) + + def printOK(self, string, offsetL=0, printBR=True): + """Вывод на печать в случае успеха""" + self.printLine((('greenBr',' * '), + ('',string), + ), + (('blueBr','['), + ('greenBr',' ok '), + ('blueBr',']'), + ), offsetL, printBR) + + def printOnlyOK(self, string, offsetL=0, printBR=True): + """Вывод на печать в случае успеха""" + self.printLine((('',string),), + (('blueBr','['), + ('greenBr',' ok '), + ('blueBr',']'), + ), offsetL, printBR) + + def printWARNING(self, string, offsetL=0, printBR=True): + """Вывод на печать предупреждения""" + self.printLine((('yellowBr',' * '), + ('',string), + ), + (('',''), + ), offsetL, printBR) + + def printERROR(self, string, offsetL=0, printBR=True): + """Вывод на печать предупреждения""" + self.printLine((('redBr',' * '), + ('',string), + ), + (('',''), + ), offsetL, printBR) + + def printSUCCESS(self, string, offsetL=0, printBR=True): + """Вывод на печать в случае успеха без [ok] справа""" + self.printLine((('greenBr',' * '), + ('',string), + ), + (('',''), + ), offsetL, printBR) diff --git a/pym/cl_string.py b/pym/cl_string.py new file mode 100644 index 0000000..757cbfb --- /dev/null +++ b/pym/cl_string.py @@ -0,0 +1,254 @@ +#-*- 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. + +from re import search, compile, S +from cl_utils import _toUNICODE + +def prettyColumnStr(*cols): + '''Функция преобразования строк в текстовые колонки. Если указанный текст + не помещается в колонку, то строка переносится на следующую этой же колонки + перенос текста идет по словам, и текст выравнивается по ширине колонки за + счет дополнительных пробелов между словами. Если в строке используется + перенос строки, то текст переносится не просто на следующую строку, а также + на следующую строку колонки, причем если используется \r текст выравнива- + ется по ширине, а если \n, то просто перевод строки. + + Параметры: + cols множестово пар: текст, ширина колонки, причем, если у последней + колонки не указывать ширину, то она будет выведена вся. + + Возвращаемые параметры: + строка, которую можно использовать для вывода на экран + + Пример: columnWrite( "Some text", 10, "Next column", 20 ) + ''' + # шаблон поиска переводов строк + wherenr = compile( '[\n\r]', S ) + retstr = "" + # перевести кортеж в список, т.к. изменяется + cols = list(cols) + # перевести текст в юникод, заодно перевести числа в строку + noconvert = False + space = u' ' + nospace = u'' + for i in xrange(0,len(cols),2): + cols[i] = _toUNICODE(cols[i]) + # флаг "есть еще текст для вывода" + repeat = True + while repeat: + # сбросить итератор на первый элемент + q = 0 + repeat = False + # пока не закончили перебирать параметры (перебираем по парам) + while q < len(cols): + # если это последний параметр, и для него не указана ширина + if q == len(cols)-1: + # выводим его полностью не смотря на ширину окна + retstr += cols[q] + " " + cols[q] = '' + else: + # вывести часть строки не больше указанной ширины колонки + partstr = cols[q][:cols[q+1]] + # искать перевод строки с полученной части + brfind = wherenr.search(partstr) + # если это не последняя колонка + if q + 2 < len(cols): + # добавить разделитель между колонками + cellspacing = space + else: + # разделитель не нужен + cellspacing = nospace + + # если перевод строки найден, то + if brfind != None: + # для текущего вывода в колонку + # берем часть строки до перевода + partstr = partstr[:brfind.start()] + # остальная часть идет в остаток (без перевода) + cols[q] = cols[q][brfind.start()+1:] +# # если используется перевод каретки +# if brfind.group() == '\r': +# # то выравниваем по ширине колонки +# partstr = partstr.ljust(cols[q+1], ' ') +# else: +# # добавить отступы чтобы закончить колонку + partstr = partstr.ljust(cols[q+1], ' ') + # если взята часть строки + elif len(partstr) == cols[q+1] and partstr != cols[q]: + # если взята часть строки (разрыв в слове) + if cols[q][cols[q+1]] != ' ': + # ищем ближайший пробел справа + spacepos = partstr.rfind(' ') + # если пробел найти не удалось + if spacepos == -1: + # то на вывод идет часть строки равной ширине + cols[q] = cols[q][cols[q+1]:] + # если пробел найден + else: + # обрезаем строку до найденного пробела + partstr = partstr[:spacepos] + cols[q] = cols[q][spacepos+1:] + # если взята часть строки (разрыв на пробеле) + else: + # ислючить переносной пробел + cols[q] = cols[q][cols[q+1]+1:] + # выровнить текст по ширине колонки + partstr = partstr.ljust(cols[q+1], ' ') + #partstr = justify(partstr, cols[q+1]) + # остатки строки + else: + # добавить отступы чтобы закончить колонку + partstr = partstr.ljust(cols[q+1], ' ') + cols[q] = '' + + retstr+= partstr + cellspacing + + # остальную часть строки оставить на следующую итерацию + # если от строки что то осаталось + if len(cols[q]) > 0: + # отметить запуск еще одной итерации по параметрам + repeat = True + # следующая пара + q += 2 + # колонки отображены + retstr += "\n" + return retstr.encode('utf8') + +def columnStr(*cols): + '''Вывод данных по колонкам, причем, если данные не вмещаются в указнаную + колонку, то они переносятся на следующую строку в нужную колонку. В строку. + + Параметры: + cols множестово пар: текст, ширина колонки, причем, если у последней + колонки не указывать ширину, то она будет выведена вся. + + Возвращаемые параметры: + строка, которую можно использовать для вывода на экран + + Пример: columnWrite( "Some text", 10, "Next column", 20 ) + ''' + retstr = "" + # перевести кортеж в список, т.к. изменяется + cols = list(cols) + # перевести текст в юникод, заодно перевести числа в строку + for i in xrange(0,len(cols),2): + cols[i] = (str(cols[i])).decode('utf8') + + # флаг "есть еще текст для вывода" + repeat = True + while repeat: + # сбросить итератор на первый элемент + q = 0 + repeat = False + # пока не закончили перебирать параметры (перебираем по парам) + while q < len(cols): + # если это последний параметр, и для него не указана ширина + if q == len(cols)-1: + # выводим его полностью не смотря на ширину окна + retstr += cols[q] + " " + cols[q] = '' + else: + # вывести часть строки не больше указанной ширины колонки + retstr+=(cols[q][:cols[q+1]].ljust(cols[q+1])).encode('utf8') \ + + " " + # остальную часть строки оставить на следующую итерацию + cols[q] = cols[q][cols[q+1]:] + # если от строки что то осаталось + if len(cols[q]) > 0: + # отметить запуск еще одной итерации по параметрам + repeat = True + # следующая пара + q += 2 + # колонки отображены + retstr += "\n" + return retstr + +def columnWrite(*cols): + '''Вывод данных по колонкам, причем, если данные не вмещаются в указнаную + колонку, то они переносятся на следующую строку в нужную колонку. + + Параметры: + cols множестово пар: текст, ширина колонки, причем, если у последней + колонки не указывать ширину, то она будет выведена вся. + + Пример: columnWrite( "Some text", 10, "Next column", 20 ) + ''' + # перевести кортеж в список, т.к. изменяется + cols = list(cols) + # перевести текст в юникод, заодно перевести числа в строку + for i in xrange(0,len(cols),2): + cols[i] = (str(cols[i])).decode('utf8') + + # флаг "есть еще текст для вывода" + repeat = True + while repeat: + # сбросить итератор на первый элемент + q = 0 + repeat = False + # пока не закончили перебирать параметры (перебираем по парам) + while q < len(cols): + # если это последний параметр, и для него не указана ширина + if q == len(cols)-1: + # выводим его полностью не смотря на ширину окна + print cols[q].encode('utf8'), + cols[q] = '' + else: + # вывести часть строки не больше указанной ширины колонки + print (cols[q][:cols[q+1]].ljust(cols[q+1])).encode('utf8'), + # остальную часть строки оставить на следующую итерацию + cols[q] = cols[q][cols[q+1]:] + # если от строки что то осаталось + if len(cols[q]) > 0: + # отметить запуск еще одной итерации по параметрам + repeat = True + # следующая пара + q += 2 + # колонки отображены + print + +def justify(s,width): + '''Выровнить текст по ширине + + Параметры: + s выводимая строка + width ширина на которую надо выровнить строку + + Возвращаямые параметры: + Выровненная строка + ''' + # если подана строка без пробелов - прекратить обработку + if s.find(' ') == -1: + return s + pos = 0 + # переводим в юникод для правильного вычисления длины + try: + s = s.decode( 'utf-8' ) + # пропуск если это не utf-8 + except UnicodeEncodeError: + pass + # пока длина строки меньше указанной + while len(s) < width: + # находим очередной пробел + pos = s.find( ' ', pos ) + # если не найден искать сначала + if pos == -1: + pos = s.find(' ') + # вставить в позицию еще один пробел + s = s[:pos] +' ' +s[pos:] + # оставить удвоенный пробел + pos += 3 + # вернуть строку в utf8 если она пришла в utf8 + return s.encode('utf-8') diff --git a/pym/cl_template.py b/pym/cl_template.py new file mode 100644 index 0000000..68f0c0f --- /dev/null +++ b/pym/cl_template.py @@ -0,0 +1,3774 @@ +#-*- 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 + "!" - drop удаляет + "+" - join добавляет + "-" - replace заменяет + """ + 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 = '' + docTxt += '%s'% version + docTxt += '%s' % typeDoc + docTxt += '' + 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 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)= 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[a-zA-Z0-9\-_]+)\ +(?P\([a-zA-Z0-9_\-\+\,\*\/\.]+\))?\ +(?P[\>\<\=\!\&\|]+\ +[a-zA-Z0-9\>\<\=\!\|\&\-\+\*\/_\.\,\(\)]*)#\ +\n*(?P.+?)\n*#(?P=rTerm)#(?P[ ,\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 diff --git a/pym/cl_utils.py b/pym/cl_utils.py index b9a56d2..5a29f48 100644 --- a/pym/cl_utils.py +++ b/pym/cl_utils.py @@ -1,6 +1,6 @@ #-*- coding: utf-8 -*- -#Copyright 2008 Calculate Pack, http://www.calculate-linux.org +# 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. @@ -14,255 +14,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -import filecmp +import subprocess import string from random import choice -from re import search, compile, S import os import types -import subprocess - -def getdirlist(s_path): - #Получить список директорий по указаному пути - fdir=filecmp.dircmp(s_path, s_path) - dir_list=fdir.common_dirs - return dir_list - -def prettyColumnStr(*cols): - '''Функция преобразования строк в текстовые колонки. Если указанный текст - не помещается в колонку, то строка переносится на следующую этой же колонки - перенос текста идет по словам, и текст выравнивается по ширине колонки за - счет дополнительных пробелов между словами. Если в строке используется - перенос строки, то текст переносится не просто на следующую строку, а также - на следующую строку колонки, причем если используется \r текст выравнива- - ется по ширине, а если \n, то просто перевод строки. - - Параметры: - cols множестово пар: текст, ширина колонки, причем, если у последней - колонки не указывать ширину, то она будет выведена вся. - - Возвращаемые параметры: - строка, которую можно использовать для вывода на экран - - Пример: columnWrite( "Some text", 10, "Next column", 20 ) - ''' - # шаблон поиска переводов строк - wherenr = compile( '[\n\r]', S ) - retstr = "" - # перевести кортеж в список, т.к. изменяется - cols = list(cols) - # перевести текст в юникод, заодно перевести числа в строку - noconvert = False - space = u' ' - nospace = u'' - for i in xrange(0,len(cols),2): - cols[i] = _toUNICODE(cols[i]) - # флаг "есть еще текст для вывода" - repeat = True - while repeat: - # сбросить итератор на первый элемент - q = 0 - repeat = False - # пока не закончили перебирать параметры (перебираем по парам) - while q < len(cols): - # если это последний параметр, и для него не указана ширина - if q == len(cols)-1: - # выводим его полностью не смотря на ширину окна - retstr += cols[q] + " " - cols[q] = '' - else: - # вывести часть строки не больше указанной ширины колонки - partstr = cols[q][:cols[q+1]] - # искать перевод строки с полученной части - brfind = wherenr.search(partstr) - # если это не последняя колонка - if q + 2 < len(cols): - # добавить разделитель между колонками - cellspacing = space - else: - # разделитель не нужен - cellspacing = nospace - - # если перевод строки найден, то - if brfind != None: - # для текущего вывода в колонку - # берем часть строки до перевода - partstr = partstr[:brfind.start()] - # остальная часть идет в остаток (без перевода) - cols[q] = cols[q][brfind.start()+1:] -# # если используется перевод каретки -# if brfind.group() == '\r': -# # то выравниваем по ширине колонки -# partstr = partstr.ljust(cols[q+1], ' ') -# else: -# # добавить отступы чтобы закончить колонку - partstr = partstr.ljust(cols[q+1], ' ') - # если взята часть строки - elif len(partstr) == cols[q+1] and partstr != cols[q]: - # если взята часть строки (разрыв в слове) - if cols[q][cols[q+1]] != ' ': - # ищем ближайший пробел справа - spacepos = partstr.rfind(' ') - # если пробел найти не удалось - if spacepos == -1: - # то на вывод идет часть строки равной ширине - cols[q] = cols[q][cols[q+1]:] - # если пробел найден - else: - # обрезаем строку до найденного пробела - partstr = partstr[:spacepos] - cols[q] = cols[q][spacepos+1:] - # если взята часть строки (разрыв на пробеле) - else: - # ислючить переносной пробел - cols[q] = cols[q][cols[q+1]+1:] - # выровнить текст по ширине колонки - partstr = partstr.ljust(cols[q+1], ' ') - #partstr = justify(partstr, cols[q+1]) - # остатки строки - else: - # добавить отступы чтобы закончить колонку - partstr = partstr.ljust(cols[q+1], ' ') - cols[q] = '' - - retstr+= partstr + cellspacing - - # остальную часть строки оставить на следующую итерацию - # если от строки что то осаталось - if len(cols[q]) > 0: - # отметить запуск еще одной итерации по параметрам - repeat = True - # следующая пара - q += 2 - # колонки отображены - retstr += "\n" - return retstr.encode('utf8') - -def columnStr(*cols): - '''Вывод данных по колонкам, причем, если данные не вмещаются в указнаную - колонку, то они переносятся на следующую строку в нужную колонку. В строку. - - Параметры: - cols множестово пар: текст, ширина колонки, причем, если у последней - колонки не указывать ширину, то она будет выведена вся. - - Возвращаемые параметры: - строка, которую можно использовать для вывода на экран - - Пример: columnWrite( "Some text", 10, "Next column", 20 ) - ''' - retstr = "" - # перевести кортеж в список, т.к. изменяется - cols = list(cols) - # перевести текст в юникод, заодно перевести числа в строку - for i in xrange(0,len(cols),2): - cols[i] = (str(cols[i])).decode('utf8') +import stat - # флаг "есть еще текст для вывода" - repeat = True - while repeat: - # сбросить итератор на первый элемент - q = 0 - repeat = False - # пока не закончили перебирать параметры (перебираем по парам) - while q < len(cols): - # если это последний параметр, и для него не указана ширина - if q == len(cols)-1: - # выводим его полностью не смотря на ширину окна - retstr += cols[q] + " " - cols[q] = '' - else: - # вывести часть строки не больше указанной ширины колонки - retstr+=(cols[q][:cols[q+1]].ljust(cols[q+1])).encode('utf8') \ - + " " - # остальную часть строки оставить на следующую итерацию - cols[q] = cols[q][cols[q+1]:] - # если от строки что то осаталось - if len(cols[q]) > 0: - # отметить запуск еще одной итерации по параметрам - repeat = True - # следующая пара - q += 2 - # колонки отображены - retstr += "\n" - return retstr -def columnWrite(*cols): - '''Вывод данных по колонкам, причем, если данные не вмещаются в указнаную - колонку, то они переносятся на следующую строку в нужную колонку. - - Параметры: - cols множестово пар: текст, ширина колонки, причем, если у последней - колонки не указывать ширину, то она будет выведена вся. - - Пример: columnWrite( "Some text", 10, "Next column", 20 ) - ''' - # перевести кортеж в список, т.к. изменяется - cols = list(cols) - # перевести текст в юникод, заодно перевести числа в строку - for i in xrange(0,len(cols),2): - cols[i] = (str(cols[i])).decode('utf8') - - # флаг "есть еще текст для вывода" - repeat = True - while repeat: - # сбросить итератор на первый элемент - q = 0 - repeat = False - # пока не закончили перебирать параметры (перебираем по парам) - while q < len(cols): - # если это последний параметр, и для него не указана ширина - if q == len(cols)-1: - # выводим его полностью не смотря на ширину окна - print cols[q].encode('utf8'), - cols[q] = '' - else: - # вывести часть строки не больше указанной ширины колонки - print (cols[q][:cols[q+1]].ljust(cols[q+1])).encode('utf8'), - # остальную часть строки оставить на следующую итерацию - cols[q] = cols[q][cols[q+1]:] - # если от строки что то осаталось - if len(cols[q]) > 0: - # отметить запуск еще одной итерации по параметрам - repeat = True - # следующая пара - q += 2 - # колонки отображены - print - -def justify(s,width): - '''Выровнить текст по ширине - - Параметры: - s выводимая строка - width ширина на которую надо выровнить строку - - Возвращаямые параметры: - Выровненная строка - ''' - # если подана строка без пробелов - прекратить обработку - if s.find(' ') == -1: - return s - pos = 0 - # переводим в юникод для правильного вычисления длины - try: - s = s.decode( 'utf-8' ) - # пропуск если это не utf-8 - except UnicodeEncodeError: - pass - # пока длина строки меньше указанной - while len(s) < width: - # находим очередной пробел - pos = s.find( ' ', pos ) - # если не найден искать сначала - if pos == -1: - pos = s.find(' ') - # вставить в позицию еще один пробел - s = s[:pos] +' ' +s[pos:] - # оставить удвоенный пробел - pos += 3 - # вернуть строку в utf8 если она пришла в utf8 - return s.encode('utf-8') +def _toUNICODE(val): + """перевод текста в юникод""" + if type(val) == types.UnicodeType: + return val + else: + return str(val).decode('UTF-8') def runOsCommand(cmd, inStr=None, ret_first=None, env_dict=None): """Выполняет внешнюю программу @@ -299,27 +64,9 @@ def runOsCommand(cmd, inStr=None, ret_first=None, env_dict=None): else: return retcode, res return retcode, None - - -def genpassword(passlen=9): - '''Вернуть случайный пассворд указанной длины - Параметры: - passlen длина пароля который нужно сгенерировать - - Возвращаемые параметры: - Сгенерированный пароль указанной длины - ''' - res=''.join([choice(string.ascii_letters+string.digits)\ - for i in xrange(passlen)]) - return res - -def fillstr(char, width): - '''Заполнить строку указанным числом символов. Псеводоним символ*кол-во''' - return str(char) * width - - #вернуть пути для запуска утилит def getpathenv(): + """вернуть пути для запуска программ""" bindir=['/sbin','/bin','/usr/sbin','/usr/bin'] env=os.environ if env and env.has_key('PATH'): @@ -331,134 +78,23 @@ def getpathenv(): lpath=npath+lpath return ":".join(lpath) -#класс для работы с установленными пакетами -class pakages: - #путь к директории установленнх пакетов - pkgdir="/var/db/pkg/" - #список установленных пакетов - pkglist={} - #Объект содержащий параметры пакета - class pakage(object): - #имя пакета с версией - fullname="" - #имя пакета - name="" - #версия - ver="" - #тип пакета в портежах - portdir="" - def __init__(self, **args): - for atname,atvalue in args.items(): - setattr(self,atname, atvalue) - - def __init__(self): - self.pkglist=self.__getpkglist() - - #разбить имя пакета на тип и имя - def __getpkgver(self, fname): - res=search('^(.+)\-([0-9]+.*)$',fname) - if res: - return res.groups() - else: - return (None,None) - - #собрать установленные в системе пакеты - def __getpkglist(self): - portageDirs=[] - instaledPkg={} - #проверим на существование директории с установленными пакетами - if os.path.exists(self.pkgdir): - #получим список типов пакетов - portageDirs=getdirlist(self.pkgdir) - if len(portageDirs)>0: - #обрабатываем содержимое каждого из типов - for portageDir in portageDirs: - pkgList=getdirlist(self.pkgdir+portageDir) - for pkg in pkgList: - fullname=pkg - pkgName,pkgVer= self.__getpkgver(pkg) - pobj=self.pakage(fullname=fullname, - name=pkgName, \ - ver=pkgVer,\ - portdir=portageDir) - fpkg=portageDir+"/"+pkgName - if instaledPkg.has_key(fpkg): - instaledPkg[fpkg].append(pobj) - else: - instaledPkg[fpkg]=[pobj] - return instaledPkg - - #разбить pkgname на составляющие имени пакета - def __partname(self, pkgname): - if not pkgname.strip(): - return False - res=search('^(.+\/)?(.+)',pkgname) - tname=None - if res.group(1): - tname=res.group(1) - if res.group(2): - res2=search('^(.+)(\-[0-9]+.+$)',res.group(2)) - if res2: - name=res2.group(1) - ver=res2.group(2) - else: - name=res.group(2) - ver=None - if res: - if name and name[-1:]=='-': - name=name[:-1] - if tname and tname[-1:]=='/': - tname=tname[:-1] - if ver and ver[0]=='-': - ver=ver[1:] - return [tname, name, ver] +def genpassword(passlen=9): + '''Вернуть случайный пароль указанной длины + Параметры: + passlen длина пароля который нужно сгенерировать - #проверить установленн ли пакет - #isinstalled('dev-db/postgresql') - def isinstalled(self, pkgname): - res=self.getinstpkg(pkgname) - if len(res)>0: - return True - else: - return False + Возвращаемые параметры: + Сгенерированный пароль указанной длины + ''' + res=''.join([choice(string.ascii_letters+string.digits)\ + for i in xrange(passlen)]) + return res - #вернуть список объектов pakage() соответствующих pkgname - #getinstpkg('dev-db/postgresql') - #в случае отсутствия пакетов возвращает пустой список - def getinstpkg(self, pkgname): - pinfo=self.__partname(pkgname) - if pinfo: - ret=[] - if pinfo[0] and pinfo[1] and pinfo[2]: - if not self.pkglist.has_key(pinfo[0]+'/'+pinfo[1]): - return [] - fpkg=self.pkglist[pinfo[0]+'/'+pinfo[1]] - ret=[] - for i in fpkg: - if i.ver==pinfo[2]: - ret.append(i) - return ret - elif pinfo[0] and pinfo[1]: - if not self.pkglist.has_key(pinfo[0]+'/'+pinfo[1]): - return [] - return self.pkglist[pinfo[0]+'/'+pinfo[1]] - elif pinfo[1] and pinfo[2]: - for i in self.pkglist.keys(): - if search('^.+\/%s$'%(pinfo[1]),i): - for el in self.pkglist[i]: - if el.ver==pinfo[2]: - ret.append(el) - return ret - elif pinfo[1]: - for i in self.pkglist.keys(): - if search('^.+\/%s$'%(pinfo[1]),i): - ret+=self.pkglist[i] - return ret - return [] +def fillstr(char, width): + '''Заполнить строку указанным числом символов. Псеводоним символ*кол-во''' + return str(char) * width - def getListPkg(self): - return self.pkglist def list2str(list): '''Функция переводит список в строку''' @@ -502,9 +138,77 @@ def convertStrListDict(val): else: return val -def _toUNICODE(val): - """перевод текста в юникод""" - if type(val) == types.UnicodeType: - return val - else: - return str(val).decode('UTF-8') +def getdirlist(s_path): + """Получить список директорий по указаному пути""" + return filter(lambda x: os.path.isdir(x), os.listdir(s_path)) + +class _error: + # Здесь ошибки, если они есть + error = [] + def getError(self): + """Выдать ошибки""" + if not self.error: + return False + error = "" + for e in self.error: + error += e + "\n" + return error + + def setError(self, error): + """Установка ошибки""" + if not error in self.error: + self.error.append(error) + return True + +class scan: + """Класс для сканирования директорий""" + class _dir: + """Класс для хранения директорий""" + def __init__(self): + # Базовая директория + self.baseDir = False + # Все директории в базовой включая вложенные + self.dirs = [] + # Все файлы внутри базовой директории + self.files = [] + # Все ссылки внутри базовой директории + self.links = [] + # Все сокеты внутри базовой директории + self.sockets = [] + + + def __scanDir(self, scanDir, dirObj, flagDir=False): + """Сканирование одной директории""" + if flagDir or stat.S_ISDIR(os.stat(scanDir)[stat.ST_MODE]): + for fileOrDir in os.listdir(scanDir): + absPath = os.path.join(scanDir,fileOrDir) + statInfo = os.stat(absPath)[stat.ST_MODE] + if stat.S_ISDIR(statInfo): + dirObj.dirs.append(absPath) + self.__scanDir(absPath, dirObj, True) + elif stat.S_ISLNK(statInfo): + dest = absPath + src = os.readlink(absPath) + dirObj.links.append((src,dest)) + elif stat.S_ISREG(statInfo): + dirObj.files.append(absPath) + elif stat.S_ISSOCK(statInfo): + dirObj.sockets.append(absPath) + return dirObj + + def scanDirs(self, scanDirs): + """Сканирование директорий на вход список + + Выход список объктов _dirProf + """ + dirs = [] + for scanDir in scanDirs: + dirP = scan._dir() + try: + self.__scanDir(scanDir, dirP) + except OSError, e: + print e.strerror, e.filename + return [] + dirP.baseDir = scanDir + dirs.append(dirP) + return dirs \ No newline at end of file diff --git a/pym/cl_vars.py b/pym/cl_vars.py index 48f8e54..4097326 100644 --- a/pym/cl_vars.py +++ b/pym/cl_vars.py @@ -1,6 +1,6 @@ #-*- coding: utf-8 -*- -#Copyright 2008 Calculate Pack, http://www.calculate-linux.org +# 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. @@ -14,6 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. + #Допустимые ключи значений # mode - режим переменной r-не переназначается из командной строки, # w-переназначается из командной строки @@ -57,10 +58,10 @@ class Data: # архитектура компьютера (i686,x86_64) os_arch_machine = {} - #проход при наложении профилей 1,2,3,4,5 и.т д + #проход при наложении шаблонов 1,2,3,4,5 и.т д cl_pass_step = {'mode':"w"} - # обрабатываемый файл профиля + # обрабатываемый файл шаблона cl_pass_file = {'mode':"w"} # корневой раздел файловой системы @@ -81,7 +82,7 @@ class Data: # версия системы os_linux_ver = {} - # Тип профиля + # Тип шаблона cl_pass_type = {'mode':"w"} # Действие программы diff --git a/pym/format/__init__.py b/pym/format/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pym/format/apache.py b/pym/format/apache.py new file mode 100644 index 0000000..0b1ffc2 --- /dev/null +++ b/pym/format/apache.py @@ -0,0 +1,218 @@ +#-*- 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 re +from xml import xpath +from cl_template import blocText, xmlDoc +from format.bind import bind + +class apache(bind): + """Класс для обработки конфигурационного файла типа apache + + """ + _comment = "#" + configName = "apache" + configVersion = "0.1" + __headerArea = "[^\<\> \t]+[ \t]+[^\<\> \t]+" + __openArea = "[ \t]*\<%s\>"%(__headerArea) + __closeArea = "[ \t]*\<\/[^\<\>]+\>" + sepFields = "\n" + reOpen = re.compile(__openArea) + reClose = re.compile(__closeArea) + reCloseArea = re.compile(__closeArea + "\s*\Z") + reComment = re.compile("[ \t]*%s"%(_comment)) + reSepFields = re.compile(sepFields) + reSeparator = re.compile("[ \t]+") + reHeader = re.compile(__headerArea) + + def __init__(self,text): + self.text = text + self.blocTextObj = blocText() + # Объект документ + self.docObj = self.textToXML() + # Создаем поля-массивы + self.docObj.postParserList() + # Создаем поля разделенные массивы + self.docObj.postParserListSeplist(self.docObj.body) + # XML документ + self.doc = self.docObj.doc + + def postXML(self): + """Последующая постобработка XML""" + # Для добавления перевода строки перед закрывающим тегом + # конфигурационного файла + xmlFields = xpath.Evaluate("child::fields", self.docObj.body) + if not (xmlFields and\ + self.docObj.getTypeField(xmlFields[-1]) == "br"): + self.docObj.body.appendChild(self.docObj.createField("br", + [],"",[], + False,False)) + xmlAreas = xpath.Evaluate("child::area", self.docObj.body) + for xmlArea in xmlAreas: + xmlFields = xpath.Evaluate("child::field", xmlArea) + if not (xmlFields and\ + self.docObj.getTypeField(xmlFields[-1]) == "br"): + xmlArea.appendChild(self.docObj.createField("br", + [],"",[], + False,False)) + + def join(self, apacheObj): + """Объединяем конфигурации""" + if isinstance(apacheObj, apache): + #print self.docObj.doc.toprettyxml() + self.docObj.joinDoc(apacheObj.doc) + self.postXML() + + # Делим область на составные части + def findOpenClose(self, text, reOpen, reClose, reComment, reHeader): + """Делит область на составные части + + начальный текстовый блок, + открывающий блок, + блок-тело, + закрывающий блок + """ + firstBloc = "" + startBloc = "" + bodyBloc = "" + endBloc = "" + textLines = text.splitlines() + findOpen = False + if textLines: + findOpen = reOpen.search(textLines[0]) + openBl = reOpen.search(text) + if findOpen and reComment.split(text)[0].strip(): + blocA = text[openBl.end():] + firstBloc = "" + startBloc = text[openBl.start():openBl.end()] + headBl = reHeader.search(startBloc) + if headBl: + firstBloc = headBl.group(0) + closeBl = reClose.search(blocA) + endBloc = blocA[closeBl.start():closeBl.end()] + bodyBloc = blocA[:closeBl.start()] + return (firstBloc, startBloc, bodyBloc, endBloc) + else: + return (firstBloc, startBloc, text, endBloc) + + # Делим текст на области включая вложенные (areas массив областей) + def splitToAllArea(self, text, areas, reOpen, reClose, reCloseArea, + reComment, reSepFields, reHeader): + """Делит текст на области включая вложенные + + возвращает список объектов областей (переменная areas) + """ + class area: + def __init__(self): + self.header = False + self.start = False + self.fields = [] + self.end = False + + blocs = self.blocTextObj.splitTxtToBloc(text,reOpen,reClose, + reComment,reSepFields) + for i in blocs: + areaA = area() + first,start,body,end = self.findOpenClose(i, reOpen, reCloseArea, + reComment, reHeader) + areaA.header = first.replace(" ","").replace("\t","") + areaA.start = start + areaA.end = end + + if areaA.end: + blocsA = self.blocTextObj.splitTxtToBloc(body,reOpen,reClose, + reComment,reSepFields) + if blocsA and blocsA[0] == body: + areaA.fields.append(body) + areas.append(areaA) + else: + for ar in blocsA: + self.splitToAllArea(ar, areaA.fields, reOpen, + reClose, + reCloseArea, reComment, + reSepFields, reHeader) + areas.append(areaA) + else: + areaA.fields.append(body) + areas.append(areaA) + + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + #print "#"+brBloc[z]+"#" + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len (nameValue) == 1: + field.name = "" + field.value = textLine.replace(self.sepFields,"") + field.br = textLine + fields.append(field) + field = fieldData() + + if len(nameValue) == 3: + valueList = nameValue[2:] + nameValue =["".join(nameValue[:2])," ".join(valueList)] + + if len(nameValue) > 3: + valueList = nameValue[1:] + nameValue =[nameValue[0]," ".join(valueList).replace(\ + self.sepFields,"")] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + + def textToXML(self): + """Преобразуем тект в XML""" + areas = [] + self.splitToAllArea(self.text, areas, self.reOpen, self.reClose, + self.reCloseArea,self.reComment,self.reSepFields, + self.reHeader) + docObj = xmlDoc() + + # Создание объекта документ c пустым разделителем между полями + docObj.createDoc(self.configName, self.configVersion) + if not areas: + return docObj + self.createXML(areas, docObj.getNodeBody(), docObj) + return docObj \ No newline at end of file diff --git a/pym/format/bind.py b/pym/format/bind.py new file mode 100644 index 0000000..7028196 --- /dev/null +++ b/pym/format/bind.py @@ -0,0 +1,315 @@ +#-*- 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 re +from cl_template import objShare, blocText, xmlDoc + +class bind(objShare): + """Класс для обработки конфигурационного файла типа bind + + """ + _comment = "//" + configName = "bind" + configVersion = "0.1" + __openArea = "{" + __closeArea = "[ \t]*\}[ \t]*;[ \t]*" + sepFields = ";" + reOpen = re.compile(__openArea) + reClose = re.compile(__closeArea) + reCloseArea = re.compile(__closeArea + "\s*\Z") + reComment = re.compile("[ \t]+%s|^%s|(?<=;)%s"%(_comment,_comment,_comment)) + reSepFields = re.compile(sepFields) + reSeparator = re.compile("[ \t]+") + + def __init__(self,text): + self.text = text + self.blocTextObj = blocText() + # Объект документ + self.docObj = self.textToXML() + # Создаем поля-массивы + self.docObj.postParserList() + # XML документ + self.doc = self.docObj.doc + + # Делим область на составные части + def findOpenClose(self, text, reOpen, reClose, reComment): + """Делит область на составные части + + начальный текстовый блок, + открывающий блок, + блок-тело, + закрывающий блок + """ + firstBloc = "" + startBloc = "" + bodyBloc = "" + endBloc = "" + textLines = text.splitlines() + findOpen = False + if textLines: + findOpen = reOpen.search(textLines[0]) + openBl = reOpen.search(text) + if findOpen and reComment.split(text)[0].strip(): + blocA = text[openBl.end():] + firstBloc = text[:openBl.start()] + startBloc = text[openBl.start():openBl.end()] + closeBl = reClose.search(blocA) + endBloc = blocA[closeBl.start():closeBl.end()] + bodyBloc = blocA[:closeBl.start()] + return (firstBloc, startBloc, bodyBloc, endBloc) + else: + return (firstBloc, startBloc, text, endBloc) + + + # Делим текст на области включая вложенные (areas массив областей) + def splitToAllArea(self, text, areas, reOpen, reClose, reCloseArea, + reComment, reSepFields): + """Делит текст на области включая вложенные + + возвращает список объектов областей (переменная areas) + """ + class area: + def __init__(self): + self.header = False + self.start = False + self.fields = [] + self.end = False + + blocs = self.blocTextObj.splitTxtToBloc(text,reOpen,reClose, + reComment,reSepFields) + for i in blocs: + areaA = area() + first,start,body,end = self.findOpenClose(i, reOpen, reCloseArea, + reComment) + areaA.header = first.replace(" ","").replace("\t","") + areaA.start = first + start + areaA.end = end + + if areaA.end: + blocsA = self.blocTextObj.splitTxtToBloc(body,reOpen,reClose, + reComment,reSepFields) + if blocsA and blocsA[0] == body: + areaA.fields.append(body) + areas.append(areaA) + else: + for ar in blocsA: + self.splitToAllArea(ar, areaA.fields, reOpen, + reClose, + reCloseArea, reComment, + reSepFields) + areas.append(areaA) + else: + areaA.fields.append(body) + areas.append(areaA) + return areas + + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len (nameValue) == 1: + field.name = "" + field.value = textLine.replace(self.sepFields,"") + field.br = textLine + fields.append(field) + field = fieldData() + + if len(nameValue) > 2: + valueList = nameValue[1:] + nameValue =[nameValue[0]," ".join(valueList).replace(\ + self.sepFields,"")] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + def createCaptionTerm(self, header, start, end, docObj): + """Создание пустой области с заголовком + + при создании области проверяется первый символ заголовка + и добавляется тег action + "!" - drop + "-" - replace + """ + areaAction = False + if header: + if header[0] == "!": + docObj.createCaption(header[1:], [start, + end.replace("\n","")]) + areaAction = "drop" + elif header[0] == "-": + docObj.createCaption(header[1:], [start, + end.replace("\n","")]) + areaAction = "replace" + else: + docObj.createCaption(header, [start, + end.replace("\n","")]) + else: + docObj.createCaption(header, [start, + end.replace("\n","")]) + + areaXML = docObj.createArea() + if areaAction: + docObj.setActionArea(areaXML, areaAction) + return areaXML + + def createXML(self, areas, rootNode, docObj): + """Создаем из массивов областей XML""" + for i in areas: + if str(i.__class__.__name__) == "area": + if i.header and i.start: + areaXML = self.createCaptionTerm(i.header, i.start, + i.end.replace("\n",""), + docObj) + else: + areaXML = rootNode + for f in i.fields: + if str(f.__class__.__name__) == "area": + if f.header and f.start: + areaXMLChild = self.createCaptionTerm(f.header, + f.start, + f.end.replace("\n",""), + docObj) + + self.createXML(f.fields, areaXMLChild, docObj) + + areaXML.appendChild(areaXMLChild) + else: + self.createXML(f.fields, areaXML, docObj) + if "\n" in f.end: + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + areaXML.appendChild(fieldXMLBr) + else: + if not f: + continue + fields = self.splitToFields(f) + for field in fields: + if field.name != False: + fieldXML = self.createFieldTerm(field.name, + field.value, + field.br, docObj) + areaXML.appendChild(fieldXML) + if field.br[-1] == "\n": + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + areaXML.appendChild(fieldXMLBr) + elif field.comment != False: + fieldXML = docObj.createField("comment", + [field.comment], + "", [], + False, False) + areaXML.appendChild(fieldXML) + elif field.br != False: + brText = field.br.replace("\n","") + if brText: + fieldXML = docObj.createField('br', + [brText], + "", [], + False, False) + else: + fieldXML = docObj.createField('br', + [], + "", [], + False, False) + areaXML.appendChild(fieldXML) + + if i.header and i.start: + rootNode.appendChild(areaXML) + if "\n" in i.end: + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + rootNode.appendChild(fieldXMLBr) + + else: + fields = self.splitToFields(i) + for field in fields: + if field.name != False: + fieldXML = self.createFieldTerm(field.name, + field.value, + field.br, docObj) + rootNode.appendChild(fieldXML) + if field.br[-1] == "\n": + fieldXMLBr = docObj.createField("br",[],"", [], + False, False) + rootNode.appendChild(fieldXMLBr) + elif field.comment != False: + fieldXML = docObj.createField("comment", + [field.comment], + "", [], + False, False) + rootNode.appendChild(fieldXML) + elif field.br != False: + brText = field.br.replace("\n","") + if brText: + fieldXML = docObj.createField('br', [brText],"",[], + False, False) + else: + fieldXML = docObj.createField('br', [], "", [], + False, False) + rootNode.appendChild(fieldXML) + #rootNode.appendChild(areaXML) + + def textToXML(self): + """Преобразуем текст в XML""" + areas = [] + if self.text.strip(): + self.splitToAllArea(self.text, areas, self.reOpen, self.reClose, + self.reCloseArea,self.reComment,self.reSepFields) + docObj = xmlDoc() + # Создание объекта документ c пустым разделителем между полями + docObj.createDoc(self.configName, self.configVersion) + if not areas: + return docObj + self.createXML(areas, docObj.getNodeBody(), docObj) + return docObj + + + def join(self, bindObj): + """Объединяем конфигурации""" + if isinstance(bindObj, bind): + self.docObj.joinDoc(bindObj.doc) diff --git a/pym/format/compiz.py b/pym/format/compiz.py new file mode 100644 index 0000000..40ae487 --- /dev/null +++ b/pym/format/compiz.py @@ -0,0 +1,41 @@ +#-*- 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 re +from format.samba import samba + +class compiz(samba): + """Класс для обработки конфигурационного файла типа compiz + + """ + _comment = "#" + configName = "compiz" + configVersion = "0.1" + reHeader = re.compile("^[\t ]*\[[^\[\]]+\].*\n",re.M) + reBody = re.compile(".+",re.M|re.S) + reComment = re.compile("\s*%s.*"%(_comment)) + reSeparator = re.compile("\s*=\s*") + sepFields = "\n" + reSepFields = re.compile(sepFields) + + def __init__(self,text): + samba.__init__(self,text) + + def join(self, compizObj): + """Объединяем конфигурации""" + if isinstance(compizObj, compiz): + self.docObj.joinDoc(compizObj.doc) + self.postXML() diff --git a/pym/format/dhcp.py b/pym/format/dhcp.py new file mode 100644 index 0000000..4f47666 --- /dev/null +++ b/pym/format/dhcp.py @@ -0,0 +1,100 @@ +#-*- 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 re +from format.bind import bind + +class dhcp(bind): + """Класс для обработки конфигурационного файла типа dhcp + + """ + _comment = "#" + configName = "dhcp" + configVersion = "0.1" + __openArea = "{" + __closeArea = "[ \t]*\}[ \t]*" + sepFields = ";" + reOpen = re.compile(__openArea) + reClose = re.compile(__closeArea) + reCloseArea = re.compile(__closeArea + "\s*\Z") + reComment = re.compile("^[ \t]*%s"%(_comment)) + reSepFields = re.compile(sepFields) + reSeparator = re.compile("[ \t]+") + + def __init__(self,text): + bind.__init__(self,text) + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len (nameValue) == 1: + field.name = textLine.replace(self.sepFields,"").strip() + field.value = "" + field.br = textLine + fields.append(field) + field = fieldData() + + if len(nameValue) > 2: + nameCheck = nameValue[0] + if nameValue[0][:1] in ["+","-","!"]: + nameCheck = nameValue[0][1:] + if nameCheck == "option" or nameCheck == "hardware" or\ + nameCheck == "set": + valueList = nameValue[2:] + nameValue =[nameValue[0]+nameValue[1], + " ".join(valueList).replace(\ + self.sepFields,"")] + else: + valueList = nameValue[1:] + nameValue =[nameValue[0]," ".join(valueList).replace(\ + self.sepFields,"")] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + def join(self, dhcpObj): + """Объединяем конфигурации""" + if isinstance(dhcpObj, dhcp): + self.docObj.joinDoc(dhcpObj.doc) diff --git a/pym/format/dovecot.py b/pym/format/dovecot.py new file mode 100644 index 0000000..5130771 --- /dev/null +++ b/pym/format/dovecot.py @@ -0,0 +1,65 @@ +#-*- 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 re +from xml import xpath +from format.bind import bind + +class dovecot(bind): + """Класс для обработки конфигурационного файла типа dovecot + + """ + _comment = "#" + configName = "dovecot" + configVersion = "0.1" + __openArea = "{" + __closeArea = "[ \t]*\}[ \t]*" + sepFields = "\n" + reOpen = re.compile(__openArea) + reClose = re.compile(__closeArea) + reCloseArea = re.compile(__closeArea + "\s*\Z") + reComment = re.compile("[ \t]*%s" %(_comment)) + reSepFields = re.compile(sepFields) + # разделитель названия и значения переменной + reSeparator = re.compile("\s*=\s*") + + def __init__(self, text): + bind.__init__(self,text) + + def postXML(self, xmlArea=False): + """Последующая постобработка XML""" + # Добавляем перевод строки если его нет в конец области + if not xmlArea: + xmlArea = self.docObj.body + xmlFields = xpath.Evaluate("child::field", xmlArea) + if xmlFields and not (\ + self.docObj.getTypeField(xmlFields[-1]) == "br" or\ + self.docObj.getTypeField(xmlFields[-1]) == "comment"): + xmlArea.appendChild(self.docObj.createField("br", + [],"",[], + False,False)) + xmlAreas = xpath.Evaluate("child::area", xmlArea) + for area in xmlAreas: + self.postXML(area) + + def join(self, dovecotObj): + """Объединяем конфигурации""" + if isinstance(dovecotObj, dovecot): + #print self.docObj.doc.toprettyxml() + self.docObj.joinDoc(dovecotObj.doc) + # Для добавления перевода строки перед закрывающим тегом + # конфигурационного файла + self.postXML() \ No newline at end of file diff --git a/pym/format/kde.py b/pym/format/kde.py new file mode 100644 index 0000000..5c9b3fd --- /dev/null +++ b/pym/format/kde.py @@ -0,0 +1,160 @@ +#-*- 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 re +from xml import xpath +from cl_template import xmlDoc +from format.samba import samba + +class kde(samba): + """Класс для обработки конфигурационного файла типа kde + + """ + _comment = "#" + configName = "kde" + configVersion = "0.1" + reHeader = re.compile("^[\t ]*\[[^\[\]]+\].*\n?",re.M) + reBody = re.compile(".+",re.M|re.S) + reComment = re.compile("^\s*%s.*"%(_comment)) + reSeparator = re.compile("=") + sepFields = "\n" + reSepFields = re.compile(sepFields) + + def __init__(self,text): + samba.__init__(self,text) + + + def _textToXML(self): + """Преобразует текст в XML""" + blTmp = self.blocTextObj.findBloc(self.text,self.reHeader,self.reBody) + blocs = self.getFullAreas(blTmp) + headers = [] + startHeaders = [] + finHeaders = [] + docObj = xmlDoc() + docObj.createDoc(self.configName, self.configVersion) + rootNode = docObj.getNodeBody() + # Если пустой текст то создаем пустой документ + if not blocs: + return docObj + + for h in blocs[0]: + reH = re.compile("]\s*$") + #listfinH = h.split("]") + listfinH = reH.split(h) + finH = listfinH[0] + if "[" in finH: + startHeaders.append(finH + "]") + else: + startHeaders.append(finH) + if len(listfinH) == 2: + finHeaders.append(listfinH[1]) + else: + finHeaders.append("") + head=finH.replace("][",".").replace("[","").replace("]","").strip() + headers.append(head) + bodys = blocs[1] + + z = 0 + for h in headers: + if not bodys[z]: + z += 1 + continue + areaAction = False + if h: + if h[0] == "!": + docObj.createCaption(h[1:], [startHeaders[z],""]) + areaAction = "drop" + elif h[0] == "-": + docObj.createCaption(h[1:], [startHeaders[z],""]) + areaAction = "replace" + else: + docObj.createCaption(h, [startHeaders[z],""]) + else: + docObj.createCaption(h, [startHeaders[z],""]) + + if "\n" in blocs[0][z]: + if self.reComment.search(finHeaders[z]): + docObj.createField('comment', [finHeaders[z]]) + elif not finHeaders[z].strip() and\ + finHeaders[z].replace("\n",""): + docObj.createField('br', + [finHeaders[z].replace("\n","")]) + else: + docObj.createField('br') + fields = self._splitToFields(bodys[z]) + for f in fields: + if f.name != False and f.value!=False and f.br!=False: + # Обработка условий для samba + if f.name[0] == "!" or f.name[0] == "-" or\ + f.name[0] == "+": + qns = self.removeSymbolTerm(f.br) + xmlField = docObj.createField("var", + [qns], + f.name[1:], [f.value]) + if f.name[0] == "!": + # Удаляемое в дальнейшем поле + docObj.setActionField(xmlField, "drop") + else: + docObj.createField("var",[f.br.replace("\n","")], + f.name, [f.value]) + docObj.createField('br') + elif f.comment != False: + docObj.createField('comment', [f.comment]) + elif f.br != False: + docObj.createField('br', [f.br.replace("\n","")]) + if h.strip(): + area = docObj.createArea() + if areaAction: + docObj.setActionArea(area, areaAction) + rootNode.appendChild(area) + else: + fieldsNodes = docObj.tmpFields.getFields() + for fieldNode in fieldsNodes: + rootNode.appendChild(fieldNode) + docObj.clearTmpFields() + z += 1 + #print docObj.doc.toprettyxml() + return docObj + + def postXML(self): + """Последующая постобработка XML""" + # Для добавления перевода строки между областями если его нет + #print self.docObj.body.toprettyxml() + xmlAreas = xpath.Evaluate("child::area", self.docObj.body) + for xmlArea in xmlAreas: + if xmlArea.previousSibling and\ + self.docObj.getTypeField(xmlArea.previousSibling) == "br": + continue + firstArea = False + xmlFields = xpath.Evaluate("child::field", xmlArea) + if not (xmlFields and\ + (self.docObj.getTypeField(xmlFields[-1]) == "br" or\ + self.docObj.getTypeField(xmlFields[-1]) == "comment")): + if xmlArea.nextSibling: + parentNode = xmlArea.parentNode + nextNode = xmlArea.nextSibling + parentNode.insertBefore(self.docObj.createField("br", + [],"",[], + False,False), + nextNode) + + def join(self, kdeObj): + """Объединяем конфигурации""" + if isinstance(kdeObj, kde): + self.docObj.joinDoc(kdeObj.doc) + self.postXML() + diff --git a/pym/format/ldap.py b/pym/format/ldap.py new file mode 100644 index 0000000..5df868c --- /dev/null +++ b/pym/format/ldap.py @@ -0,0 +1,183 @@ +#-*- 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 re +from cl_template import blocText, xmlDoc +from format.samba import samba + +class ldap(samba): + """Класс для обработки конфигурационного файла типа ldap + + """ + _comment = "#" + configName = "ldap" + configVersion = "0.1" + # Регулярное выражение для заголовка области + reHeader = re.compile("^[\t ]*(access|syncrepl)[^\n]+\n?") + # Регулярное выражения для области + reArea = re.compile("([\t ]*(access|syncrepl)[^\n]+\ +\n([\t ]+[^\n]+\n?)+)",re.M|re.S) + reComment = re.compile("\s*%s.*"%(_comment)) + # разделитель между переменной и значением переменной + reSeparator = re.compile("\s+|\s*=\s*") + # разделитель полей + sepFields = "\n" + # регулярное выражение для разделителя полей + reSepFields = re.compile(sepFields) + + def __init__(self,text): + self.text = text + self.blocTextObj = blocText() + self._splitToFields = self.splitToFields + # Объект документ + self.docObj = self._textToXML() + # Создаем поля-массивы + self.docObj.postParserList() + # Создаем поля разделенные массивы + self.docObj.postParserListSeplist(self.docObj.body) + # XML документ + self.doc = self.docObj.doc + + def join(self, ldapObj): + """Объединяем конфигурации""" + if isinstance(ldapObj, ldap): + self.docObj.joinDoc(ldapObj.doc) + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len(nameValue) > 2: + valueList = nameValue[2:] + nameValue =[nameValue[0]+nameValue[1]," ".join(valueList)] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + def _textToXML(self): + """Преобразует текст в XML""" + blTmp = self.blocTextObj.findArea(self.text,self.reHeader,self.reArea) + blocs = self.getFullAreas(blTmp) + headers = [] + startHeaders = [] + finHeaders = [] + docObj = xmlDoc() + docObj.createDoc(self.configName, self.configVersion) + rootNode = docObj.getNodeBody() + # Если пустой текст то создаем пустой документ + if not blocs: + return docObj + + for h in blocs[0]: + headers.append(h.rstrip()) + bodys = blocs[1] + + z = 0 + for h in headers: + if not bodys[z]: + z += 1 + continue + areaAction = False + if h: + if h[0] == "!": + header = self.removeSymbolTerm(h.strip()) + headerQuote = self.removeSymbolTerm(h) + docObj.createCaption(header,[headerQuote,""]) + areaAction = "drop" + elif h[0] == "-": + header = self.removeSymbolTerm(h.strip()) + headerQuote = self.removeSymbolTerm(h) + docObj.createCaption(header,[headerQuote,""]) + areaAction = "replace" + else: + docObj.createCaption(h.strip(), [h.rstrip(),""]) + else: + docObj.createCaption(h.strip(), [h.rstrip(),""]) + + if "\n" in blocs[0][z]: + resHead = self.reComment.search(h) + if resHead: + docObj.createField('comment', + blocs[0][z][resHead.start():]) + else: + docObj.createField('br') + + fields = self._splitToFields(bodys[z]) + for f in fields: + if f.name != False and f.value!=False and f.br!=False: + # Обработка условий для samba + if f.name[0] == "!" or f.name[0] == "-" or\ + f.name[0] == "+": + qns = self.removeSymbolTerm(f.br) + xmlField = docObj.createField("var", + [qns], + f.name[1:], [f.value]) + if f.name[0] == "!": + # Удаляемое в дальнейшем поле + docObj.setActionField(xmlField, "drop") + elif f.name[0] == "+": + # Добавляем уникальное поле + xmlField.setAttribute("type", "seplist") + docObj.setActionField(xmlField, "join") + else: + docObj.createField("var",[f.br.replace("\n","")], + f.name, [f.value]) + docObj.createField('br') + elif f.comment != False: + docObj.createField('comment', [f.comment]) + elif f.br != False: + docObj.createField('br', [f.br.replace("\n","")]) + if h.strip(): + area = docObj.createArea() + if areaAction: + docObj.setActionArea(area, areaAction) + rootNode.appendChild(area) + else: + fieldsNodes = docObj.tmpFields.getFields() + for fieldNode in fieldsNodes: + rootNode.appendChild(fieldNode) + docObj.clearTmpFields() + z += 1 + #print docObj.doc.toprettyxml() + return docObj diff --git a/pym/format/plasma.py b/pym/format/plasma.py new file mode 100644 index 0000000..d5e9500 --- /dev/null +++ b/pym/format/plasma.py @@ -0,0 +1,597 @@ +#-*- 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 re +import types +import copy +from xml import xpath +from cl_template import xmlDoc +from format.samba import samba + +class xmlDocPlasma: + """Класс для замены метода joinArea в xmlDoc для plasma""" + # заменяемый метод для xmlDoc + 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"): + 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) + + # Заменяем QUOTE + oldAreaNode = oName.parentNode.parentNode + oldAreaQuote = xpath.Evaluate('child::caption/quote', + oldAreaNode)[0] + if oldAreaQuote and\ + not oldAreaQuote.firstChild: + newAreaQuote = xpath.Evaluate('child::caption/quote', + xmlNewArea)[0] + oldAreaCaption = xpath.Evaluate('child::caption', + oldAreaNode)[0] + if newAreaQuote and oldAreaCaption: + oldAreaCaption.replaceChild(newAreaQuote, oldAreaQuote) + + 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"): + appendArea(baseNode, xmlNewArea) + else: + tmpXmlNewAreas = xpath.Evaluate('child::area',xmlNewArea) + for na in tmpXmlNewAreas: + for bn in baseNodes: + self.joinArea(bn, na) + return True + +class plasma(samba): + """Класс для обработки конфигурационного файла типа kde + + """ + _comment = "#" + configName = "plasma" + configVersion = "0.1" + reHeader = re.compile("^[\t ]*\[[^\[\]]+\].*\n?",re.M) + reBody = re.compile(".+",re.M|re.S) + reComment = re.compile("^\s*%s.*"%(_comment)) + reSeparator = re.compile("=") + sepFields = "\n" + reSepFields = re.compile(sepFields) + + def __init__(self,text): + samba.__init__(self,text) + + # Делим текст на области включая вложенные (areas массив областей) + def splitToAllArea(self, text, areas): + """Делит текст на области включая вложенные + + возвращает список объектов областей (переменная areas) + """ + class area: + def __init__(self): + self.header = False + self.start = False + self.fields = [] + self.end = False + + + def findPathArea(listPath, areaF): + """Ищет путь в области + + areaF - объект area + listPath - cписок названий областей + """ + ret = False + if not listPath: + return ret + flagList = False + if type(areaF) == types.ListType: + fields = areaF + flagList = True + else: + fields = areaF.fields + if areaF.header == listPath[0]: + ret = areaF + else: + return ret + for i in fields: + if str(i.__class__.__name__) == "area": + add = False + if not flagList: + add = listPath.pop(0) + if not listPath: + break + ret = False + if i.header == listPath[0]: + ret = findPathArea(listPath, i) + break + else: + if add: + listPath.insert(0,add) + if ret == areaF and len(listPath)>1: + ret = False + return ret + + + blTmp = self.blocTextObj.findBloc(self.text,self.reHeader,self.reBody) + blocs = self.getFullAreas(blTmp) + reH = re.compile("\[([^\[\]]+)\]") + # Список имен блоков + namesBlockList = [] + # Временные поля + fieldsTmp = [] + # Добавляем заголовки + z = 0 + for h in blocs[0]: + if not h: + if blocs[1][z] == "": + fieldsTmp.append("") + #fieldsTmp.append("\n") + else: + fieldsTmp.append(blocs[1][z]) + #print '"' + blocs[1][z] + '"' + z += 1 + continue + #print '"' + blocs[1][z] + '"' + z += 1 + slpNamesBlock = reH.split(h) + # Отступ слева для заголовка + indentionLeft = slpNamesBlock[0] + namesBlock = filter(lambda x: x.strip(), slpNamesBlock) + #namesBlock = map(lambda x: self.removeSymbolTerm(x), namesBlock) + findArea = findPathArea(copy.copy(namesBlock), areas) + namesBlockList.append(namesBlock) + if findArea: + if len(namesBlock) > 1: + namesBlockView = map(lambda x: self.removeSymbolTerm(x), + namesBlock) + else: + namesBlockView = namesBlock + findArea.start = indentionLeft + "[" + \ + "][".join(namesBlockView) + "]" + else: + i = 0 + lenNamesBlock = len(namesBlock) + namesBlockTmp = [] + for nameB in namesBlock: + namesBlockTmp.append(nameB) + findArea = findPathArea(copy.copy(namesBlockTmp), areas) + i += 1 + if not findArea: + areaNew = area() + areaNew.header = nameB + if lenNamesBlock == i: + if len(namesBlock) > 1: + namesBlockView = map(\ + lambda x: self.removeSymbolTerm(x), + namesBlock) + else: + namesBlockView = namesBlock + areaNew.start = indentionLeft + "[" + \ + "][".join(namesBlockView) + "]" + else: + areaNew.start = "" + areaNew.end = "" + if i == 1: + if lenNamesBlock == i: + areas += fieldsTmp + areas.append(areaNew) + findAreaPrev = areas[-1] + else: + if lenNamesBlock == i: + findAreaPrev.fields += fieldsTmp + findAreaPrev.fields.append(areaNew) + findAreaPrev = findAreaPrev.fields[-1] + else: + findAreaPrev = findArea + fieldsTmp = [] + i = 0 + delt = 0 + # Добавляем тела + for body in blocs[1]: + #print "#" + body + "#" + #print + if not blocs[0][i]: + i += 1 + delt +=1 + continue + ## В случае последнего комментария не добавляем перевод строки + #if self.reComment.search(body.splitlines()[-1]): + body = "\n" + body + + namesBlock = namesBlockList[i-delt] + findArea = findPathArea(copy.copy(namesBlock), areas) + if findArea: + #if findArea.fields: + #if type(findArea.fields[0]) == types.StringType: + #findArea.fields.pop(0) + findArea.fields.insert(0, body) + i += 1 + + #eee = 1 + #def prAreas(ar, eee): + #for a in ar: + #if type(a) == types.StringType: + #print 'field', a + #else: + #print "--------------------" + #print "HEADER =", a.header + #print "START =", a.start + #print "FIELDS =", a.fields + #print "LEVEL", eee + #if type(a) != types.StringType: + #if a.fields: + #eee += 1 + #prAreas(a.fields, eee) + + #prAreas(areas, eee) + return areas + + def createCaptionTerm(self, header, start, end, docObj): + """Создание пустой области с заголовком + + при создании области проверяется первый символ заголовка + и добавляется тег action + "!" - drop + "-" - replace + """ + areaAction = False + if header: + if header[0] == "!": + docObj.createCaption(header[1:], [start, + end.replace("\n","")]) + areaAction = "drop" + elif header[0] == "-": + docObj.createCaption(header[1:], [start, + end.replace("\n","")]) + areaAction = "replace" + else: + docObj.createCaption(header, [start, + end.replace("\n","")]) + else: + docObj.createCaption(header, [start, + end.replace("\n","")]) + + areaXML = docObj.createArea() + if areaAction: + docObj.setActionArea(areaXML, areaAction) + return areaXML + + def createXML(self, areas, rootNode, docObj): + """Создаем из массивов областей XML""" + for i in areas: + if str(i.__class__.__name__) == "area": + if i.header: + areaXML = self.createCaptionTerm(i.header, i.start, + i.end.replace("\n",""), + docObj) + for f in i.fields: + if str(f.__class__.__name__) == "area": + if f.header: + areaXMLChild = self.createCaptionTerm(f.header, + f.start, + f.end.replace("\n",""), + docObj) + + self.createXML(f.fields, areaXMLChild, docObj) + + areaXML.appendChild(areaXMLChild) + else: + self.createXML(f.fields, areaXML, docObj) + if "\n" in f.end: + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + areaXML.appendChild(fieldXMLBr) + else: + if not f: + continue + fields = self.splitToFields(f) + for field in fields: + if field.name != False: + fieldXML = self.createFieldTerm(field.name, + field.value, + field.br, docObj) + areaXML.appendChild(fieldXML) + if field.br[-1] == "\n": + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + areaXML.appendChild(fieldXMLBr) + elif field.comment != False: + fieldXML = docObj.createField("comment", + [field.comment], + "", [], + False, False) + areaXML.appendChild(fieldXML) + elif field.br != False: + brText = field.br.replace("\n","") + if brText: + fieldXML = docObj.createField('br', + [brText], + "", [], + False, False) + else: + fieldXML = docObj.createField('br', + [], + "", [], + False, False) + if areaXML: + areaXML.appendChild(fieldXML) + + if i.header: + rootNode.appendChild(areaXML) + if "\n" in i.end: + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + rootNode.appendChild(fieldXMLBr) + + else: + if not i: + continue + fields = self.splitToFields(i) + for field in fields: + if field.name != False: + fieldXML = self.createFieldTerm(field.name, + field.value, + field.br, docObj) + rootNode.appendChild(fieldXML) + if field.br[-1] == "\n": + fieldXMLBr = docObj.createField("br",[],"", [], + False, False) + rootNode.appendChild(fieldXMLBr) + elif field.comment != False: + fieldXML = docObj.createField("comment", + [field.comment], + "", [], + False, False) + rootNode.appendChild(fieldXML) + elif field.br != False: + brText = field.br.replace("\n","") + if brText: + fieldXML = docObj.createField('br', [brText],"",[], + False, False) + else: + fieldXML = docObj.createField('br', [], "", [], + False, False) + rootNode.appendChild(fieldXML) + #rootNode.appendChild(areaXML) + + def _textToXML(self): + """Преобразуем текст в XML""" + areas = [] + if self.text.strip(): + self.splitToAllArea(self.text, areas) + #docObj = xmlDoc() + # Создаем новый класс xmlDoc с измененным методом joinArea + newClass = type("newXmlDocPlalma",(xmlDocPlasma,xmlDoc,object),{}) + # Создаем экземпляр нового класса + docObj = newClass() + # Создание объекта документ c пустым разделителем между полями + docObj.createDoc(self.configName, self.configVersion) + if not areas: + return docObj + self.createXML(areas, docObj.getNodeBody(), docObj) + return docObj + + + def postXML(self): + """Последующая постобработка XML""" + # Для добавления перевода строки между областями если его нет + #print self.docObj.body.toprettyxml() + 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 + + xmlAreas = xpath.Evaluate("descendant::area", self.docObj.body) + #print "-------------------------------------------------------" + #print xmlAreas + #if xmlAreas: + #prXmlArea = xmlAreas[0] + for xmlArea in xmlAreas: + # Перед пустой областью и после нее удаляем переводы строк + if getQuotesArea(xmlArea) == ["",""]: + #areaTXT = xpath.Evaluate("child::caption/name", xmlArea)[0] + #print "CL_AREA", areaTXT.firstChild + if xmlArea.previousSibling and\ + self.docObj.getTypeField(xmlArea.previousSibling) == "br": + parentNode = xmlArea.previousSibling.parentNode + if xmlArea.previousSibling.previousSibling and\ + self.docObj.getTypeField(xmlArea.previousSibling.previousSibling) == "br": + parentNode.removeChild(\ + xmlArea.previousSibling.previousSibling) + parentNode.removeChild(xmlArea.previousSibling) + if xmlArea.nextSibling and\ + self.docObj.getTypeField(xmlArea.nextSibling) == "br": + parentNode = xmlArea.nextSibling.parentNode + if xmlArea.nextSibling.nextSibling and\ + self.docObj.getTypeField(xmlArea.nextSibling.nextSibling) == "br": + parentNode.removeChild(xmlArea.nextSibling.nextSibling) + parentNode.removeChild(xmlArea.nextSibling) + continue + + # Собираем поля в кучку + xmlChildAreas = xpath.Evaluate("child::area", xmlArea) + if xmlChildAreas: + childNodes = self.docObj.getFieldsArea(xmlArea) + firstChildArea = xmlChildAreas[0] + if firstChildArea.previousSibling and\ + self.docObj.getTypeField(firstChildArea.previousSibling)=="br": + if firstChildArea.previousSibling.previousSibling: + if self.docObj.getTypeField(\ + firstChildArea.previousSibling.previousSibling)=="br": + firstChildArea = firstChildArea.previousSibling + flagFoundArea = False + it = 0 + lenChild = len(childNodes) + for node in childNodes: + it += 1 + if node.tagName == "area": + flagFoundArea = True + continue + if flagFoundArea and node.tagName == "field": + if self.docObj.getTypeField(node) == "var": + xmlArea.insertBefore(node, firstChildArea) + if it < lenChild: + if self.docObj.getTypeField(childNodes[it])==\ + "br": + xmlArea.insertBefore(childNodes[it], + firstChildArea) + + # Добавляем BR если его нет первым полем + xmlFields = xpath.Evaluate("child::field", xmlArea) + if not (xmlFields and\ + (self.docObj.getTypeField(xmlFields[0]) == "br" or\ + self.docObj.getTypeField(xmlFields[0]) == "comment")): + if xmlFields: + xmlArea.insertBefore(self.docObj.createField("br", + [],"",[], + False,False), + xmlFields[0]) + # Если последним полем BR, удаляем его + if xmlFields and self.docObj.getTypeField(xmlFields[-1]) == "br": + #print "DEL_BR", xmlFields[-1].nextSibling + #and\ + if not xmlFields[-1].nextSibling: + xmlArea.removeChild(xmlFields[-1]) + + # Если предыдущим полем не (BR или комментарий) - добавляем BR + if xmlArea.previousSibling and\ + not (self.docObj.getTypeField(xmlArea.previousSibling) == "br" or\ + self.docObj.getTypeField(xmlArea.previousSibling) == "comment"): + parentNode = xmlArea.parentNode + parentNode.insertBefore(self.docObj.createField("br", + [],"",[], + False,False), + xmlArea) + # Если есть предыдущее поле, и поле предыдущеее предыдущему + # не равно BR или комментарий то добавляем BR + if xmlArea.previousSibling: + prPrSibling = xmlArea.previousSibling.previousSibling + if prPrSibling and\ + not (self.docObj.getTypeField(prPrSibling) == "br" or\ + self.docObj.getTypeField(prPrSibling) == "comment"): + parentNode = xmlArea.parentNode + parentNode.insertBefore(self.docObj.createField("br", + [],"",[], + False,False), + xmlArea) + # Если после есть BR а за ним ничего нет, удаляем BR + if xmlArea.nextSibling and\ + self.docObj.getTypeField(xmlArea.nextSibling) == "br": + if not xmlArea.nextSibling.nextSibling: + parentNode = xmlArea.nextSibling.parentNode + parentNode.removeChild(xmlArea.nextSibling) + + #xmlName = xpath.Evaluate("child::caption/name", xmlArea)[0] + #print "------------------------------------" + #print "Name =", xmlName.firstChild + #if xmlArea.previousSibling: + #print "PR_TYPE =", self.docObj.getTypeField(xmlArea.previousSibling) + + + def join(self, kdeObj): + """Объединяем конфигурации""" + if isinstance(kdeObj, plasma): + self.docObj.joinDoc(kdeObj.doc) + self.postXML() + + diff --git a/pym/format/postfix.py b/pym/format/postfix.py new file mode 100644 index 0000000..4dc2a34 --- /dev/null +++ b/pym/format/postfix.py @@ -0,0 +1,117 @@ +#-*- 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 re +from cl_template import xmlDoc +from format.apache import apache + +class postfix(apache): + """Класс для обработки конфигурационного файла типа postfix + + """ + _comment = "#" + configName = "postfix" + configVersion = "0.1" + sepFields = "\n" + reComment = re.compile("[ \t]*%s"%(_comment)) + reSepFields = re.compile(sepFields) + # разделитель названия и значения переменной + reSeparator = re.compile("\s*=\s*") + def __init__(self,text): + self.text = text + # Объект документ + self.docObj = self.textToXML() + # Создаем поля разделенные массивы + self.docObj.postParserListSeplist(self.docObj.body) + # XML документ + self.doc = self.docObj.doc + + def join(self, postfixObj): + """Объединяем конфигурации""" + if isinstance(postfixObj, postfix): + self.docObj.joinDoc(postfixObj.doc) + + def textToXML(self): + """Преобразуем текст в XML""" + class area: + def __init__(self): + self.header = False + self.start = False + self.fields = [] + self.end = False + areas = [] + oneArea = area() + oneArea.header = "" + oneArea.start = "" + oneArea.fields = [self.text] + oneArea.end = "" + areas.append(oneArea) + docObj = xmlDoc() + # Создание объекта документ c пустым разделителем между полями + docObj.createDoc(self.configName, self.configVersion) + if not areas: + return docObj + self.createXML(areas, docObj.getNodeBody(), docObj) + return docObj + + + def setDataField(self, txtLines, endtxtLines): + """Cоздаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + #print "#"+brBloc[z]+"#" + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len (nameValue) == 1: + field.name = "" + field.value = textLine.replace(self.sepFields,"") + field.br = textLine + fields.append(field) + field = fieldData() + + if len(nameValue) > 2: + valueList = nameValue[1:] + nameValue =[nameValue[0],"=".join(valueList).replace(\ + self.sepFields,"")] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields diff --git a/pym/format/procmail.py b/pym/format/procmail.py new file mode 100644 index 0000000..50fa05f --- /dev/null +++ b/pym/format/procmail.py @@ -0,0 +1,115 @@ +#-*- 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 re +from cl_template import objShare, xmlDoc + +class procmail(objShare): + """Класс для обработки конфигурационного файла типа procmail + + """ + _comment = "#" + configName = "procmail" + configVersion = "0.1" + sepFields = "\n" + reComment = re.compile("[ \t]*%s" %(_comment)) + reSepFields = re.compile(sepFields) + # разделитель названия и значения переменной + reSeparator = re.compile("=") + def __init__(self, text): + self.text = text + self.docObj = self.textToXML() + self.doc = self.docObj.doc + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + def textToXML(self): + docObj = xmlDoc() + docObj.createDoc(self.configName, self.configVersion) + if self.text: + nodeBody = docObj.getNodeBody() + fields = self.splitToFields(self.text) + for field in fields: + if field.name != False: + fieldXML = self.createFieldTerm(field.name, + field.value, + field.br, docObj) + nodeBody.appendChild(fieldXML) + if field.br[-1] == "\n": + fieldXMLBr = docObj.createField("br",[], + "",[], + False, False) + nodeBody.appendChild(fieldXMLBr) + elif field.comment != False: + fieldXML = docObj.createField("comment", + [field.comment], + "", [], + False, False) + nodeBody.appendChild(fieldXML) + elif field.br != False: + brText = field.br.replace("\n","") + if brText: + fieldXML = docObj.createField('br', + [brText], + "", [], + False, False) + else: + fieldXML = docObj.createField('br', + [], + "", [], + False, False) + nodeBody.appendChild(fieldXML) + return docObj + + def join(self, procmailObj): + """Объединяем конфигурации""" + if isinstance(procmailObj, procmail): + #print self.docObj.doc.toprettyxml() + self.docObj.joinDoc(procmailObj.doc) diff --git a/pym/format/samba.py b/pym/format/samba.py new file mode 100644 index 0000000..981cc52 --- /dev/null +++ b/pym/format/samba.py @@ -0,0 +1,261 @@ +#-*- 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 re +from xml import xpath +from cl_template import objShare, blocText, xmlDoc + +class samba(objShare): + """Класс для обработки конфигурационного файла типа samba + + """ + _comment = "#" + configName = "samba" + configVersion = "0.1" + reHeader = re.compile("^[\t ]*\[[^\[\]]+\].*\n",re.M) + reBody = re.compile(".+",re.M|re.S) + reComment = re.compile("\s*%s.*|\s*;.*"%(_comment)) + reSeparator = re.compile("\s*=\s*") + sepFields = "\n" + reSepFields = re.compile(sepFields) + + def __init__(self,text): + self.text = text + self.blocTextObj = blocText() + self._splitToFields = self.splitToFields + # Объект документ + self.docObj = self._textToXML() + # XML документ + self.doc = self.docObj.doc + + def postXML(self): + """Последующая постобработка XML""" + # Для добавления перевода строки между областями если его нет + #print self.docObj.body.toprettyxml() + xmlAreas = xpath.Evaluate("child::area", self.docObj.body) + for xmlArea in xmlAreas: + if xmlArea.previousSibling and\ + self.docObj.getTypeField(xmlArea.previousSibling) == "br": + continue + firstArea = False + xmlFields = xpath.Evaluate("child::field", xmlArea) + if not (xmlFields and\ + (self.docObj.getTypeField(xmlFields[-1]) == "br" or\ + self.docObj.getTypeField(xmlFields[-1]) == "comment")): + if xmlArea.nextSibling: + parentNode = xmlArea.parentNode + nextNode = xmlArea.nextSibling + parentNode.insertBefore(self.docObj.createField("br", + [],"",[], + False,False), + nextNode) + + + def join(self, sambaObj): + """Объединяем конфигурации""" + if isinstance(sambaObj, samba): + self.docObj.joinDoc(sambaObj.doc) + self.postXML() + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + z += 1 + findComment = self.reComment.search(textLine) + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + elif findComment: + field.comment = textLine + fields.append(field) + field = fieldData() + else: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len(nameValue) > 2: + valueList = nameValue[1:] + nameValue =[nameValue[0],"=".join(valueList)] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + + def splitCleanBloc(self, txtBloc): + """Делим блок на две части (переменные, пустые строки в конце)""" + txtLines = txtBloc.split("\n") + firstBloc = [] + nextBloc = [] + txtLines.reverse() + z = 0 + for txtLine in txtLines: + if not txtLine.strip(): + nextBloc.append(txtLine) + else: + break + z += 1 + txtLines.reverse() + firstBloc = txtLines[:-z] + nextBloc.reverse() + if nextBloc: + firstBloc.append("") + if nextBloc and "\n".join(nextBloc): + return ("\n".join(firstBloc), "\n".join(nextBloc)) + else: + return False + + def getFullAreas(self, blocs): + """Делит текст на области, (Заголовок, тело) + + Возвращает два списка: заголовки, тела + """ + headsAreas = [] + bodyAreas = [] + if not blocs: + return [] + lenBlocs = len(blocs[0]) + for i in range(lenBlocs): + txtBloc = blocs[1][i] + clean = self.splitCleanBloc(txtBloc) + if clean: + headsAreas.append(blocs[0][i]) + bodyAreas.append(clean[0]) + headsAreas.append("") + bodyAreas.append(clean[1]) + else: + headsAreas.append(blocs[0][i]) + bodyAreas.append(blocs[1][i]) + return (headsAreas, bodyAreas) + + def createTxtConfig(self, strHeader, dictVar): + """Cоздает область с заголовком + + создает текст конфигурационного файла в формате samba из + заголовка (строка) и словаря переменных + """ + if not strHeader: + return "" + outTxt = "[" + strHeader + "]\n" + for key in dictVar.keys(): + outTxt += "%s = %s\n" %(key,dictVar[key]) + return outTxt + + def _textToXML(self): + """Преобразует текст в XML""" + blTmp = self.blocTextObj.findBloc(self.text,self.reHeader,self.reBody) + blocs = self.getFullAreas(blTmp) + headers = [] + startHeaders = [] + finHeaders = [] + docObj = xmlDoc() + docObj.createDoc(self.configName, self.configVersion) + rootNode = docObj.getNodeBody() + # Если пустой текст то создаем пустой документ + if not blocs: + return docObj + + for h in blocs[0]: + listfinH = h.split("]") + finH = listfinH[0] + if "[" in finH: + startHeaders.append(finH + "]") + else: + startHeaders.append(finH) + if len(listfinH) == 2: + finHeaders.append(listfinH[1]) + else: + finHeaders.append("") + headers.append(finH.replace("[","").replace("]","").strip()) + bodys = blocs[1] + + z = 0 + for h in headers: + if not bodys[z]: + z += 1 + continue + areaAction = False + if h: + if h[0] == "!": + docObj.createCaption(h[1:], [startHeaders[z],""]) + areaAction = "drop" + elif h[0] == "-": + docObj.createCaption(h[1:], [startHeaders[z],""]) + areaAction = "replace" + else: + docObj.createCaption(h, [startHeaders[z],""]) + else: + docObj.createCaption(h, [startHeaders[z],""]) + + if "\n" in blocs[0][z]: + if self.reComment.search(finHeaders[z]): + docObj.createField('comment', [finHeaders[z]]) + elif not finHeaders[z].strip() and\ + finHeaders[z].replace("\n",""): + docObj.createField('br', + [finHeaders[z].replace("\n","")]) + else: + docObj.createField('br') + fields = self._splitToFields(bodys[z]) + for f in fields: + if f.name != False and f.value!=False and f.br!=False: + # Обработка условий для samba + if f.name[0] == "!" or f.name[0] == "-" or\ + f.name[0] == "+": + qns = self.removeSymbolTerm(f.br) + xmlField = docObj.createField("var", + [qns], + f.name[1:], [f.value]) + if f.name[0] == "!": + # Удаляемое в дальнейшем поле + docObj.setActionField(xmlField, "drop") + else: + docObj.createField("var",[f.br.replace("\n","")], + f.name, [f.value]) + docObj.createField('br') + elif f.comment != False: + docObj.createField('comment', [f.comment]) + elif f.br != False: + docObj.createField('br', [f.br.replace("\n","")]) + if h.strip(): + area = docObj.createArea() + if areaAction: + docObj.setActionArea(area, areaAction) + rootNode.appendChild(area) + else: + fieldsNodes = docObj.tmpFields.getFields() + for fieldNode in fieldsNodes: + rootNode.appendChild(fieldNode) + docObj.clearTmpFields() + z += 1 + #print docObj.doc.toprettyxml() + return docObj + diff --git a/pym/format/squid.py b/pym/format/squid.py new file mode 100644 index 0000000..fba8153 --- /dev/null +++ b/pym/format/squid.py @@ -0,0 +1,86 @@ +#-*- 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 re +from format.procmail import procmail + +class squid(procmail): + """Класс для обработки конфигурационного файла типа squid + + """ + configName = "squid" + configVersion = "0.1" + # разделитель названия и значения переменной + reSeparator = re.compile(" ") + + def __init__(self, text): + procmail.__init__(self, text) + # Создаем поля-массивы + self.docObj.postParserList() + # Создаем поля разделенные массивы + self.docObj.postParserListSeplist(self.docObj.body) + + def setDataField(self, txtLines, endtxtLines): + """Создаем список объектов с переменными""" + class fieldData: + def __init__(self): + self.name = False + self.value = False + self.comment = False + self.br = False + fields = [] + field = fieldData() + z = 0 + for k in txtLines: + textLine = k + endtxtLines[z] + z += 1 + findComment = self.reComment.search(textLine) + flagVariable = True + if not textLine.strip(): + field.br = textLine + fields.append(field) + field = fieldData() + flagVariable = False + elif findComment: + if textLine[:findComment.start()].strip(): + field.comment = textLine[findComment.start():] + textLine = textLine[:findComment.start()] + else: + field.comment = textLine + flagVariable = False + fields.append(field) + field = fieldData() + if flagVariable: + pars = textLine.strip() + nameValue = self.reSeparator.split(pars) + if len(nameValue) > 2: + valueList = nameValue[1:] + nameValue =[nameValue[0]," ".join(valueList)] + if len(nameValue) == 2: + name = nameValue[0] + value = nameValue[1].replace(self.sepFields,"") + field.name = name.replace(" ","").replace("\t","") + field.value = value + field.br = textLine + fields.append(field) + field = fieldData() + return fields + + def join(self, squidObj): + """Объединяем конфигурации""" + if isinstance(squidObj, squid): + #print squidObj.getConfig() + self.docObj.joinDoc(squidObj.doc) diff --git a/pym/format/xml_gconf.py b/pym/format/xml_gconf.py new file mode 100644 index 0000000..84894bd --- /dev/null +++ b/pym/format/xml_gconf.py @@ -0,0 +1,262 @@ +#-*- 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 time +from xml import xpath +import xml.dom.minidom +from format.xml_xfce import xml_xfce +# Перевод cообщений модуля +from cl_lang import lang +tr = lang() +tr.setLocalDomain('cl_lib') +tr.setLanguage(sys.modules[__name__]) + + +class xml_gconf(xml_xfce): + """Класс для объединения gconf-xml файлов""" + # root нода + rootNode = False + # body нода + bodyNode = False + # Документ + doc = False + # Текст шаблона + text = "" + # Текущее время в секундах + currentTime = "" + # Комментарий + _comment = ("") + # поддерживаемые аттрибуты тега entry. Пример + supportEntryTypes = ("int", "bool", "float", "string", "list", "pair") + + def __init__(self, text): + self.text = text + # Создаем пустой объект + self.docObj = type("_empty_class", (object,), {})() + # Названия аттрибутов для пустого объекта + emptyMethods = ["getNodeBody","removeComment","insertBRtoBody", + "insertBeforeSepAreas"] + # Добавляем необходимые аттрибуты пустому объекту + for method in emptyMethods: + setattr(self.docObj, method, self.emptyMethod) + # Пустой метод (не нужно имя файла для корневой ноды) + setattr(self, "setNameBodyNode", self.emptyMethod) + # Создаем XML документ + self.doc = self.textToXML() + + def getCurrentTime(self): + """Получение текущего времени в секундах""" + return str(int(time.time())) + + def textToXML(self): + """Создание из текста XML документа + Храним xml в своем формате + """ + if not self.text.strip(): + self.text = '''''' + try: + self.doc = xml.dom.minidom.parseString(self.text) + except: + self.setError(_("Can not text template is XML")) + return False + self.rootNode = self.doc.documentElement + self.bodyNode = self.rootNode + return self.doc + + def cmpListsNodesEntry(self, listXmlA, listXmlB): + """Сравнение содержимого двух списков XML нод""" + getTextsNodes = lambda y: map(lambda x:\ + x.toxml().replace(" ","").replace("\t","").replace("\n",""), + map(lambda x: x.removeAttribute("mtime") or x, + map(lambda x: x.cloneNode(True), + filter(lambda x: x.nodeType==x.ELEMENT_NODE, y)))) + if set(getTextsNodes(listXmlA))==set(getTextsNodes(listXmlB)): + return True + return False + + def _join(self, xmlNewNode, xmlOldNode, flagRootNode=True, levelNumber=0): + """Объединение корневой ноды шаблона и корневой ноды файла""" + if levelNumber>1: + return True + xmlNode = xmlNewNode + childNodes = xmlNode.childNodes + nextOldNode = xmlOldNode + flagError = False + if xmlNode.nodeType == xmlNode.ELEMENT_NODE: + n = xmlNode + tagName = n.tagName + nAction = u'' + nName = u'' + nType = u'' + nValue = u'' + attrName = '' + attrType = '' + if flagRootNode: + if not tagName == "gconf": + self.setError(_("The text is not a valid gconf-XML format \ +(not found '...')")) + return False + else: + if not tagName == "entry": + self.setError(_("The text is not a valid gconf-XML format \ +(found '<%s>..'")%(tagName,tagName)) + return False + if not n.hasAttribute("name"): + self.setError(_('Not found arrtibute "name" in tag entry')) + return False + if not n.hasAttribute("type"): + self.setError(_('Not found arrtibute "type" in tag entry')) + return False + nName = n.getAttribute("name") + attrName = u"attribute::name='%s'"%nName + nType = n.getAttribute("type") + # Проверка правильности аттрибута type + if not nType in self.supportEntryTypes: + self.setError(\ + _('Incorrect arrtibute "type" - ')%nType) + return False + attrType = u"attribute::type='%s'"%nType + if n.hasAttribute("value"): + nValue = n.getAttribute("value") + if n.hasAttribute("action"): + nAction = n.getAttribute("action") + if not nAction in ("join","replace","drop"): + textError = _('''In the text, XML template, look \ +for a reserved attribute 'action' with the incorrect value.\n\ +Valid values attribute 'action': \ +(action="join", action="replace", action="drop")''') + self.setError(textError) + return False + if xmlOldNode.parentNode: + findStr = u"child::%s"%tagName + strAttr = [attrName, attrType] + findAttr = filter(lambda x: x, strAttr) + findAttrStr = '' + if findAttr: + strAttr = u' and '.join(findAttr) + findAttrStr = "[%s]"%strAttr + findPath = u"child::%s%s"%(tagName,findAttrStr) + # Рабочая нода + if flagRootNode: + workNode = xmlOldNode.parentNode + else: + workNode = xmlOldNode + oldNodes = xpath.Evaluate(findPath, workNode) + # Новая нода список + flagArray = False + if nType == "list" or nType == "pair": + flagArray = True + flagDrop = False + flagJoin = True + flagReplace = False + if nType=="string" or nAction=="replace": + flagJoin = False + flagReplace = True + elif nAction == "drop": + flagJoin = False + flagDrop = True + if flagRootNode: + textError = _('Incorrect action="drop" in root node') + self.setError(textError) + return False + if oldNodes: + if len(oldNodes)>1: + textError = _("The uncertainty in this template are \ +the same nodes at one level") + self.setError(textError) + return False + nextOldNode = oldNodes[0] + # Замещаем ноду в случае массива + if flagArray and not flagDrop: + replaceXmlNode = xmlNode.cloneNode(True) + # Сравнение содержимого нод + if not self.cmpListsNodesEntry([replaceXmlNode], + [nextOldNode]): + replaceXmlNode.setAttribute("mtime", + self.currentTime) + if nAction: + replaceXmlNode.removeAttribute("action") + workNode.replaceChild(replaceXmlNode, + nextOldNode) + flagJoin = False + flagReplace = False + childNodes = False + # Объединение нод + if flagJoin: + if nextOldNode.hasAttribute("value"): + oValue = nextOldNode.getAttribute("value") + if nValue != oValue: + nextOldNode.setAttribute("mtime", + self.currentTime) + nextOldNode.setAttribute("value",nValue) + # Замещение ноды + elif flagReplace: + replaceXmlNode = xmlNode.cloneNode(True) + # Сравнение содержимого нод + if not self.cmpListsNodesEntry([replaceXmlNode], + [nextOldNode]): + replaceXmlNode.setAttribute("mtime", + self.currentTime) + if not\ + self._removeDropNodesAndAttrAction(\ + replaceXmlNode): + return False + workNode.replaceChild(replaceXmlNode, + nextOldNode) + childNodes = False + # Удаление ноды + elif flagDrop: + workNode.removeChild(nextOldNode) + childNodes = False + else: + # Добавление ноды + childNodes = False + if not flagDrop: + appendXmlNode = xmlNode.cloneNode(True) + appendXmlNode.setAttribute("mtime", self.currentTime) + if not\ + self._removeDropNodesAndAttrAction(appendXmlNode): + return False + workNode.appendChild(appendXmlNode) + if childNodes: + for node in childNodes: + levelNumber +=1 + if not self._join(node, nextOldNode, False, levelNumber): + flagError = True + break + levelNumber -= 1 + if flagError: + return False + return True + + def join(self, xml_gconfObj): + """Объединяем конфигурации""" + # Получаем текущее время + self.currentTime = self.getCurrentTime() + if isinstance(xml_gconfObj, xml_gconf): + try: + self.joinDoc(xml_gconfObj.doc) + except: + self.setError(_("Can not join template")) + return False + return True + + def getConfig(self): + """Получение текстового файла из XML документа""" + data = self.doc.toprettyxml().split("\n") + data = filter(lambda x: x.strip(), data) + return "\n".join(data).replace("\t"," ") diff --git a/pym/format/xml_xfce.py b/pym/format/xml_xfce.py new file mode 100644 index 0000000..9acc947 --- /dev/null +++ b/pym/format/xml_xfce.py @@ -0,0 +1,268 @@ +#-*- 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 +from xml import xpath +import xml.dom.minidom +from cl_utils import _error +# Перевод cообщений модуля +from cl_lang import lang +tr = lang() +tr.setLocalDomain('cl_lib') +tr.setLanguage(sys.modules[__name__]) + +class xml_xfce(_error): + """Класс для объединения xfce-xml файлов""" + # root нода + rootNode = False + # body нода + bodyNode = False + # Документ + doc = False + # Текст шаблона + text = "" + # Комментарий + _comment = ("") + + def __init__(self, text): + self.text = text + # Создаем пустой объект + self.docObj = type("_empty_class", (object,), {})() + # Названия аттрибутов для пустого объекта + emptyMethods = ["getNodeBody","removeComment","insertBRtoBody", + "insertBeforeSepAreas"] + # Добавляем необходимые аттрибуты пустому объекту + for method in emptyMethods: + setattr(self.docObj, method, self.emptyMethod) + # Создаем XML документ + self.doc = self.textToXML() + + def emptyMethod(self, *arg , **argv): + """Пустой метод""" + return True + + def setNameBodyNode(self, name): + """Устанавливает название для корневой ноды документа""" + if not self.bodyNode: + return False + self.bodyNode.setAttribute("name", name) + return True + + def textToXML(self): + """Создание из текста XML документа + Храним xml в своем формате + """ + if not self.text.strip(): + self.text = ''' + +''' + try: + self.doc = xml.dom.minidom.parseString(self.text) + except: + self.setError(_("Can not text template is XML")) + return False + self.rootNode = self.doc.documentElement + self.bodyNode = self.rootNode + return self.doc + + def join(self, xml_xfceObj): + """Объединяем конфигурации""" + if isinstance(xml_xfceObj, xml_xfce): + try: + self.joinDoc(xml_xfceObj.doc) + except: + self.setError(_("Can not join template")) + return False + return True + + def _removeDropNodesAndAttrAction(self, xmlNode): + """Удаляет ноды с аттрибутом action='drop' + + Также удаляет аттрибут action у всех нод + """ + flagError = False + childNodes = xmlNode.childNodes + if xmlNode.nodeType ==xmlNode.ELEMENT_NODE: + if xmlNode.hasAttribute("action"): + nAction = xmlNode.getAttribute("action") + if not nAction in ("join","replace","drop"): + textError = _('''In the text, XML template, look \ +for a reserved attribute 'action' with the incorrect value.\n\ +Valid values attribute 'action': \ +(action="join", action="replace", action="drop")''') + self.setError(textError) + return False + xmlNode.removeAttribute("action") + if nAction == "drop": + parentNode = xmlNode.parentNode + if parentNode: + parentNode.removeChild(xmlNode) + if childNodes: + for node in childNodes: + if not self._removeDropNodesAndAttrAction(node): + flagError = True + break + if flagError: + return False + return True + + def postXML(self): + """Последующая постобработка XML""" + # Удаляем теги action и удаляемые ноды + self._removeDropNodesAndAttrAction(self.bodyNode) + + def _join(self, xmlNewNode, xmlOldNode, flagRootNode=True): + """Объединение корневой ноды шаблона и корневой ноды файла""" + xmlNode = xmlNewNode + childNodes = xmlNode.childNodes + nextOldNode = xmlOldNode + flagError = False + if xmlNode.nodeType ==xmlNode.ELEMENT_NODE: + n = xmlNode + path = u'' + nName = u'' + nType = u'' + nValue = u'' + nAction = u'' + attrName = '' + attrType = '' + path = n.tagName + if n.hasAttribute("name"): + nName = n.getAttribute("name") + attrName = u"attribute::name='%s'"%nName + if n.hasAttribute("type"): + nType = n.getAttribute("type") + attrType = u"attribute::type='%s'"%nType + if n.hasAttribute("value"): + nValue = n.getAttribute("value") + if n.hasAttribute("action"): + nAction = n.getAttribute("action") + if not nAction in ("join","replace","drop"): + textError = _('''In the text, XML template, look \ +for a reserved attribute 'action' with the incorrect value.\n\ +Valid values attribute 'action': \ +(action="join", action="replace", action="drop")''') + self.setError(textError) + return False + if xmlOldNode.parentNode: + findStr = u"child::%s"%path + strAttr = [attrName, attrType] + findAttr = filter(lambda x: x, strAttr) + findAttrStr = '' + if findAttr: + strAttr = u' and '.join(findAttr) + findAttrStr = "[%s]"%strAttr + findPath = u"child::%s%s"%(path,findAttrStr) + # Рабочая нода + if flagRootNode: + workNode = xmlOldNode.parentNode + else: + workNode = xmlOldNode + oldNodes = xpath.Evaluate(findPath, workNode) + #print findPath + #print workNode + #print "----------------------------" + # Новая нода список + flagArray = False + if nType == "array": + flagArray = True + flagDrop = False + flagJoin = True + flagReplace = False + if nAction == "replace": + flagJoin = False + flagReplace = True + elif nAction == "drop": + flagJoin = False + flagDrop = True + if flagRootNode: + textError = _('Incorrect action="drop" in root node') + self.setError(textError) + return False + if oldNodes: + if len(oldNodes)>1: + textError = _("The uncertainty in this template are \ +the same nodes at one level") + self.setError(textError) + return False + nextOldNode = oldNodes[0] + # Замещаем ноду в случае массива + if flagArray and not flagDrop: + replaceXmlNode = xmlNode.cloneNode(True) + if nAction: + replaceXmlNode.removeAttribute("action") + workNode.replaceChild(replaceXmlNode, + nextOldNode) + flagJoin = False + flagReplace = False + childNodes = False + # Объединение нод + if flagJoin: + if nextOldNode.hasAttribute("value"): + oValue = nextOldNode.getAttribute("value") + if nValue != oValue: + nextOldNode.setAttribute("value",nValue) + # Замещение ноды + elif flagReplace: + replaceXmlNode = xmlNode.cloneNode(True) + if not\ + self._removeDropNodesAndAttrAction(replaceXmlNode): + return False + workNode.replaceChild(replaceXmlNode, + nextOldNode) + childNodes = False + # Удаление ноды + elif flagDrop: + workNode.removeChild(nextOldNode) + childNodes = False + else: + # Добавление ноды + childNodes = False + if not flagDrop: + appendXmlNode = xmlNode.cloneNode(True) + if not\ + self._removeDropNodesAndAttrAction(appendXmlNode): + return False + workNode.appendChild(appendXmlNode) + if childNodes: + for node in childNodes: + if not self._join(node, nextOldNode, False): + flagError = True + break + if flagError: + return False + return True + + def joinDoc(self, doc): + """Объединение документа шаблона и документа файла""" + if not self.doc: + self.setError(_("Can not text file is XML")) + return False + if not doc: + self.setError(_("Can not text template is XML")) + return False + # Импортируем корневую ноду нового документа в текущий документ + #newImportBodyNode = self.doc.importNode(doc.documentElement, True) + # Объединение корневой ноды шаблона и корневой ноды файла + if not self._join(doc.documentElement, self.bodyNode): + return False + return True + + def getConfig(self): + """Получение текстового файла из XML документа""" + data = self.doc.toprettyxml(encoding='UTF-8').split("\n") + data = filter(lambda x: x.strip(), data) + return "\n".join(data).replace("\t"," ").decode("UTF-8") diff --git a/pym/format/xml_xfcepanel.py b/pym/format/xml_xfcepanel.py new file mode 100644 index 0000000..4e9eb06 --- /dev/null +++ b/pym/format/xml_xfcepanel.py @@ -0,0 +1,199 @@ +#-*- 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 +from xml import xpath +import xml.dom.minidom +from format.xml_xfce import xml_xfce + +# Перевод cообщений модуля +from cl_lang import lang +tr = lang() +tr.setLocalDomain('cl_lib') +tr.setLanguage(sys.modules[__name__]) + +class xml_xfcepanel(xml_xfce): + """Класс для объединения xfce-panel файлов""" + def __init__(self, text): + xml_xfce.__init__(self, text) + self.panelNumbers = {} + + def textToXML(self): + """Создание из текста XML документа + Храним xml в своем формате + """ + if not self.text.strip(): + self.text = ''' + + +''' + try: + self.doc = xml.dom.minidom.parseString(self.text) + except: + self.setError(_("Can not text profile is XML")) + return False + self.rootNode = self.doc.documentElement + self.bodyNode = self.rootNode + return self.doc + + def setNameBodyNode(self, name): + """Пустой метод""" + return True + + def _join(self, xmlNewNode, xmlOldNode, flagRootNode=True, levelNumber=0): + """Объединение корневой ноды профиля и корневой ноды файла""" + xmlNode = xmlNewNode + childNodes = xmlNode.childNodes + nextOldNode = xmlOldNode + flagError = False + if xmlNode.nodeType ==xmlNode.ELEMENT_NODE: + n = xmlNode + path = u'' + nName = u'' + flagArray = False + nValue = u'' + nAction = u'' + attrName = '' + attrType = '' + path = n.tagName + if path == "items": + flagArray = True + if not flagArray: + if n.hasAttribute("name"): + nName = n.getAttribute("name") + attrName = u"attribute::name='%s'"%nName + if n.hasAttribute("value"): + nValue = n.getAttribute("value") + if n.hasAttribute("action"): + nAction = n.getAttribute("action") + if not nAction in ("join","replace","drop"): + textError = _('''In the text, XML profile, look \ +for a reserved attribute 'action' with the incorrect value.\n\ +Valid values attribute 'action': \ +(action="join", action="replace", action="drop")''') + self.setError(textError) + return False + if xmlOldNode.parentNode: + findStr = u"child::%s"%path + findAttrStr = "" + if attrName: + findAttrStr = "[%s]"%attrName + findPath = u"child::%s%s"%(path,findAttrStr) + # Рабочая нода + if flagRootNode: + workNode = xmlOldNode.parentNode + else: + workNode = xmlOldNode + oldNodes = xpath.Evaluate(findPath, workNode) + flagDrop = False + flagJoin = True + flagReplace = False + flagAppend = False + if nAction == "replace": + flagJoin = False + flagReplace = True + elif nAction == "drop": + flagJoin = False + flagDrop = True + if flagRootNode: + textError = _('Incorrect action="drop" in root node') + self.setError(textError) + return False + if path == "panel": + flagJoin = False + if levelNumber in self.panelNumbers.keys(): + self.panelNumbers[levelNumber] += 1 + else: + self.panelNumbers[levelNumber] = 0 + if oldNodes: + if len(oldNodes)>1 and path != "panel": + textError = _("The uncertainty in this profile are \ +the same nodes at one level") + self.setError(textError) + return False + if path == "panel": + if len(oldNodes)<=self.panelNumbers[levelNumber]: + nextOldNode = oldNodes[-1] + # Добавляем ноду + if not flagDrop: + flagAppend = True + flagReplace = False + childNodes = False + else: + nextOldNode=oldNodes[self.panelNumbers[levelNumber]] + else: + nextOldNode = oldNodes[0] + # Замещаем ноду в случае массива + if flagArray and not flagDrop: + replaceXmlNode = xmlNode.cloneNode(True) + if nAction: + replaceXmlNode.removeAttribute("action") + workNode.replaceChild(replaceXmlNode, + nextOldNode) + flagJoin = False + flagReplace = False + childNodes = False + # Объединение нод + if flagJoin: + if nextOldNode.hasAttribute("value"): + oValue = nextOldNode.getAttribute("value") + if nValue != oValue: + nextOldNode.setAttribute("value",nValue) + # Замещение ноды + elif flagReplace: + replaceXmlNode = xmlNode.cloneNode(True) + if not\ + self._removeDropNodesAndAttrAction(replaceXmlNode): + return False + workNode.replaceChild(replaceXmlNode, + nextOldNode) + childNodes = False + # Удаление ноды + elif flagDrop: + workNode.removeChild(nextOldNode) + childNodes = False + else: + flagAppend = True + flagDrop = False + if flagAppend and not flagDrop: + # Добавление ноды + childNodes = False + if not flagDrop: + appendXmlNode = xmlNode.cloneNode(True) + if not\ + self._removeDropNodesAndAttrAction(appendXmlNode): + return False + workNode.appendChild(appendXmlNode) + if childNodes: + for node in childNodes: + levelNumber +=1 + if not self._join(node, nextOldNode, False, levelNumber): + flagError = True + break + levelNumber -= 1 + if flagError: + return False + return True + + def join(self, xml_xfceObj): + """Объединяем конфигурации""" + if isinstance(xml_xfceObj, xml_xfcepanel): + try: + self.joinDoc(xml_xfceObj.doc) + except: + self.setError(_("Can not join profile")) + return False + return True diff --git a/setup.py b/setup.py index f074608..c3e9359 100755 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ # setup.py --- Setup script for calculate-server -#Copyright 2008 Calculate Pack, http://www.calculate-linux.org +# 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. @@ -23,14 +23,14 @@ from distutils.core import setup setup( name = 'calculate-lib', - version = "2.1.4", + version = "2.2.0", description = "The library for Calculate 2", author = "Calculate Pack", author_email = "support@calculate.ru", url = "http://calculate-linux.org", license = "http://www.apache.org/licenses/LICENSE-2.0", package_dir = {'calculate-lib': "."}, - packages = ['calculate-lib.pym'], + packages = ['calculate-lib.pym','calculate-lib.pym.format'], data_files = [("/usr/share/calculate/i18n",['i18n/cl_lib_ru.mo']), ("/var/calculate/remote",[])], )