#!/usr/bin/env python #_____________________________________________ # Caolan McNamara caolanm@redhat.com # converted from original java written by Andreas Schluens so we can continue # to build 680 OpenOffice.org series without java # this is not really a replacement for the existing java tool, just the # minimum required to make it work for now, the existing tool is still # the canonical base, changes to it will have to be mirrored here until # there is a java which is available for use by all #_____________________________________________ import sys, string, os.path, codecs CFGFILE = os.environ["SRCDIR"] + "/filter/source/config/tools/merge/FCFGMerge.cfg" PROP_XMLVERSION = "xmlversion" # // <= global cfg file PROP_XMLENCODING = "xmlencoding" # // <= global cfg file PROP_XMLPATH = "xmlpath" # // <= global cfg file PROP_XMLPACKAGE = "xmlpackage" # // <= global cfg file PROP_SETNAME_TYPES = "setname_types" # // <= global cfg file PROP_SETNAME_FILTERS = "setname_filters" # // <= global cfg file PROP_SETNAME_LOADERS = "setname_frameloaders" # // <= global cfg file PROP_SETNAME_HANDLERS = "setname_contenthandlers" # // <= global cfg file PROP_SUBDIR_TYPES = "subdir_types" # // <= global cfg file PROP_SUBDIR_FILTERS = "subdir_filters" # // <= global cfg file PROP_SUBDIR_LOADERS = "subdir_frameloaders" # // <= global cfg file PROP_SUBDIR_HANDLERS = "subdir_contenthandlers" # // <= global cfg file PROP_EXTENSION_XCU = "extension_xcu" # // <= global cfg file PROP_EXTENSION_PKG = "extension_pkg" # // <= global cfg file PROP_DELIMITER = "delimiter" # // <= global cfg file PROP_TRIM = "trim" # // <= global cfg file PROP_DECODE = "decode" # // <= global cfg file PROP_FRAGMENTSDIR = "fragmentsdir" # // <= cmdline PROP_TEMPDIR = "tempdir" # // <= cmdline PROP_OUTDIR = "outdir" # // <= cmdline PROP_PKG = "pkg" # // <= cmdline PROP_TCFG = "tcfg" # // <= cmdline PROP_FCFG = "fcfg" # // <= cmdline PROP_LCFG = "lcfg" # // <= cmdline PROP_CCFG = "ccfg" # // <= cmdline PROP_LANGUAGEPACK = "languagepack" # // <= cmdline PROP_VERBOSE = "verbose" # // <= cmdline PROP_SHARE_SUBDIR_NAME = "share_subdir_name" # // <= cmdline PROP_ITEMS = "items" # // <= pkg cfg files! #---begin java.util.Properties copy---# """ An incomplete clean room implementation of java.util.Properties written in Python. Copyright (C) 2002,2004 - Ollie Rutherfurd Based on: http://java.sun.com/j2se/1.3/docs/api/java/util/Properties.html Missing: - Currently, u\XXXX sequences are escaped when saving, but not unescaped when read.. License: Python License Example Usage: >>> from properties import Properties >>> props = Properties() >>> props['one'] = '1' >>> props['your name'] = "I don't know" >>> print('\n'.join(list(props.keys()))) your name one >>> from StringIO import StringIO >>> buff = StringIO() >>> props.store(buff, "a little example...") >>> buff.seek(0) >>> print(buff.read()) # a little example... your\ name=I\ don\'t\ know one=1 >>> print(props['your name']) I don't know $Id: pyAltFCFGMerge,v 1.3 2007-12-07 10:57:44 vg Exp $ """ __all__ = ['Properties'] def dec2hex(n): h = hex(n)[2:].upper() return '\\u' + '0' * (4 - len(h)) + h def escapestr(s): buff = [] # QUESTION: escape leading or trailing spaces? for c in s: if c == '\\': buff.append('\\\\') elif c == '\t': buff.append('\\t') elif c == '\n': buff.append('\\n') elif c == '\r': buff.append('\\r') elif c == ' ': buff.append('\\ ') elif c == "'": buff.append("\\'") elif c == '"': buff.append('\\"') elif c == '#': buff.append('\\#') elif c == '!': buff.append('\\!') elif c == '=': buff.append('\\=') elif 32 <= ord(c) <= 126: buff.append(c) else: buff.append(dec2hex(c)) return ''.join(buff) # TODO: add support for \uXXXX? def unescapestr(line): buff = [] escape = 0 for i in range(len(line)): c = line[i] if c == '\\': if escape: escape = 0 buff.append('\\') continue else: # this is to deal with '\' # acting as a line continuation # character if i == len(line) - 1: buff.append('\\') break else: escape = 1 continue elif c == 'n': if escape: escape = 0 buff.append('\n') continue elif c == 'r': if escape: escape = 0 buff.append('\r') continue elif c == 't': if escape: escape = 0 buff.append('\t') continue buff.append(c) # make sure escape doesn't stay one # all expected escape sequences either break # or continue, so this should be safe if escape: escape = 0 return ''.join(buff) class Properties(dict): def __init__(self, defaults={}): dict.__init__(self) for n,v in list(defaults.items()): self[n] = v def __getittem__(self,key): try: return dict.__getittem__(self,key) except KeyError: return None def read(self,filename): """ Reads properties from a file (java Property class reads from an input stream -- see load()). """ f = None try: f = open(filename) self.load(f) finally: if f: f.close() def load(self, buff): """ Reads properties from a stream (StringIO, file, etc...) """ props = readprops(buff) for n,v in list(props.items()): self[n] = v def readprops(buff): name,value = None,'' props = {} continued = 0 while 1: line = buff.readline() if not line: break line = line.strip() # empty line if not line: continue # comment if line[0] in ('#','!'): continue # find name i,escaped = 0,0 while i < len(line): c = line[i] if c == '\\': if escaped: escaped = 0 else: escaped = 1 i += 1 continue elif c in (' ', '\t', ':', '=') and not escaped: name = unescapestr(line[:i]) break # make sure escaped doesn't stay on if escaped: escaped = 0 i += 1 # no delimiter was found, name is entire line, there is no value if name is None: name = unescapestr(line.lstrip()) # skip delimiter while line[i:i + 1] in ('\t', ' ', ':', '='): i += 1 value = unescapestr(line[i:].strip()) while value[-1:] == '\\': value = value[:-1] # remove \ line = buff.readline() if not line: break value += unescapestr(line.strip()) props[name] = value return props #---end java.util.Properties copy---# # Its a simple command line tool, which can merge different XML fragments # together. Such fragments must exist as files on disk, will be moved into # one file together on disk. # # @author Andreas Schluens # def run(sCmdLine): aCfg = ConfigHelper(CFGFILE, sCmdLine) if aCfg.getValueWithDefault(PROP_VERBOSE,None) == None: sys.stdout=None printCopyright() # help requested? if aCfg.isHelp(): printHelp() sys.exit(-1) #create new merge object and start operation aMerger = Merger(aCfg) aMerger.merge() sys.exit(0) def printOut(s): if sys.stdout is None: return print(s) #prints out a copyright message on stdout. def printCopyright(): printOut("FCFGMerge") printOut("Copyright: 2003 by Red Hat, Inc., based on FCFGMerge.java` by Sun") printOut("All Rights Reserved.") # Prints out a help message on stdout. def printHelp(): printOut("____________________________________________________________") printOut("usage: FCFGMerge cfg=" ) printOut("parameters:" ) printOut("\tcfg=" ) printOut("\t\tmust point to a system file, which contains" ) printOut("\t\tall necessary configuration data for the merge process.") printOut("\tFurther cou can specify every parameter allowed in the" ) printOut("\tconfig file as command line parameter too, to overwrite" ) printOut("\tthe value from the file." ) # Return a list of tokens given a base string and a string of # separators, optionally including the separators if asked for""" def StringTokenizer(mstring, separators, isSepIncluded = 0): token = '' tokenList = [] for c in mstring: if c in separators: if token != '': tokenList.append(token) token = '' if isSepIncluded: tokenList.append(c) else: token += c if token: tokenList.append(token) return tokenList # Can be used to analyze command line parameters # and merge it together with might existing config # files. That provides the possibility to overwrite # config values via command line parameter. # # @author Andreas Schluens class ConfigHelper: def __init__(self, sPropFile, lCommandLineArgs): self.m_bEmpty = 1 # first load prop file, so its values can be overwritten # by command line args later # Do it only, if a valid file name was given. # But in case this file name is wrong, throw an exception. # So the outside code can react! if sPropFile is not None and len(sPropFile) > 0: self.props = Properties() self.props.read(sPropFile) count = 0 if lCommandLineArgs is not None: count = len(lCommandLineArgs) self.m_bEmpty = (count < 1) # printOut(lCommandLineArgs, "and len is", count) for arg in range(count): # is it a named-value argument? # Note: We ignores double "=" signs! => search from left to right pos = lCommandLineArgs[arg].find('=') if pos != -1: sArg = lCommandLineArgs[arg][0:pos] sValue = lCommandLineArgs[arg][pos + 1:] self.props[sArg] = sValue continue # is it a boolean argument? # Note: Because "--" and "-" will be interpreted as the same # we search from right to left! pos = lCommandLineArgs[arg].rfind('-') if pos == -1: pos = lCommandLineArgs[arg].rfind('/') if pos != -1: sArg = lCommandLineArgs[arg][pos + 1:] self.props[sArg] = 1 continue raise Exception("Invalid command line detected. The argument \"" + \ lCommandLineArgs[arg] + "\" use an unsupported format.") def isHelp(self): return ("help" in self.props) or ("?" in self.props) or ("?" in self.props) def getValue(self, sProp): if sProp not in self.props: raise Exception("The requested config value \"" + sProp + "\" "\ "does not exists!"); return self.props[sProp]; def getValueWithDefault(self, sProp, default): if sProp not in self.props: return default; return self.props[sProp]; def getStringList(self, sProp, sDelimiter, bTrim, bDecode): if sProp not in self.props: raise Exception("The requested config value \"" + sProp + "\" does "\ "not exists!"); sValue = self.props[sProp] lValue = [] lTokens = StringTokenizer(sValue, sDelimiter) for sToken in lTokens: if bTrim: sToken = sToken.strip() # remove "" if ((bDecode) and (sToken.find("\"") == 0) and \ (sToken.rfind("\"") == len(sToken) - 1)): sToken = sToken[1, len(sToken) - 1] lValue.append(sToken) return lValue def generateHeader(sVersion, sEncoding, sPath, sPackage, bLanguagePack): sHeader = "\n" if bLanguagePack: sHeader += "\n" else: sHeader += "\n" return sHeader def generateFooter(): return "\n" # Can merge different xml fragments together. # @author Caolan McNamara converted from the original java by Andreas Schluens class Merger: def __init__(self, aCfg): self.m_aCfg = aCfg self.m_aFragmentsDir = self.m_aCfg.getValue(PROP_FRAGMENTSDIR) sDelimiter = self.m_aCfg.getValue(PROP_DELIMITER) bTrim = self.m_aCfg.getValue(PROP_TRIM) bDecode = self.m_aCfg.getValue(PROP_DECODE) try: aFcfg = ConfigHelper(self.m_aCfg.getValue(PROP_TCFG), None) self.m_lTypes = aFcfg.getStringList(PROP_ITEMS, sDelimiter, bTrim, bDecode) except: self.m_lTypes = [] try: aFcfg = ConfigHelper(self.m_aCfg.getValue(PROP_FCFG), None) self.m_lFilters = aFcfg.getStringList(PROP_ITEMS, sDelimiter, bTrim, bDecode) except: self.m_lFilters = [] try: aFcfg = ConfigHelper(self.m_aCfg.getValue(PROP_LCFG), None) self.m_lLoaders = aFcfg.getStringList(PROP_ITEMS, sDelimiter, bTrim, bDecode) except: self.m_lLoaders = [] try: aFcfg = ConfigHelper(self.m_aCfg.getValue(PROP_CCFG), None) self.m_lHandlers = aFcfg.getStringList(PROP_ITEMS, sDelimiter, bTrim, bDecode) except: self.m_lHandlers = [] # Merges the xml sets returned by getFragments(...), adds an xml header # and footer and writes the result to a file. def merge(self): sPackage = self.m_aCfg.getValue(PROP_PKG) printOut("create package \"" + sPackage + "\" ...") printOut("generate package header ... ") sBuffer = generateHeader(\ self.m_aCfg.getValue(PROP_XMLVERSION ),\ self.m_aCfg.getValue(PROP_XMLENCODING),\ self.m_aCfg.getValue(PROP_XMLPATH ),\ self.m_aCfg.getValue(PROP_XMLPACKAGE ),\ self.m_aCfg.getValueWithDefault(PROP_LANGUAGEPACK, False)) # counts all transferred fragments # Can be used later to decide, if a generated package file # contains "nothing"! nItemCount = 0 for i in range(4): sSetName = None sSubDir = None lFragments = None try: if i == 0: # types printOut("generate set for types ... ") sSetName = self.m_aCfg.getValue(PROP_SETNAME_TYPES) sSubDir = self.m_aCfg.getValue(PROP_SUBDIR_TYPES) lFragments = self.m_lTypes elif i == 1: # filters printOut("generate set for filter ... ") sSetName = self.m_aCfg.getValue(PROP_SETNAME_FILTERS) sSubDir = self.m_aCfg.getValue(PROP_SUBDIR_FILTERS) lFragments = self.m_lFilters elif i == 2: # loaders printOut("generate set for frame loader ... ") sSetName = self.m_aCfg.getValue(PROP_SETNAME_LOADERS) sSubDir = self.m_aCfg.getValue(PROP_SUBDIR_LOADERS) lFragments = self.m_lLoaders elif i == 3: # handlers printOut("generate set for content handler ... ") sSetName = self.m_aCfg.getValue(PROP_SETNAME_HANDLERS) sSubDir = self.m_aCfg.getValue(PROP_SUBDIR_HANDLERS) lFragments = self.m_lHandlers except: continue nItemCount = nItemCount + len(lFragments) sBuffer = sBuffer + self.getFragments(\ os.path.join(self.m_aFragmentsDir, sSubDir), \ sSetName, lFragments, 1) printOut("generate package footer ... ") sBuffer = sBuffer + generateFooter() # Attention! # If the package seem to be empty, it make no sense to generate a # corresponding xml file. We should suppress writing of this file on # disk completely ... if nItemCount < 1: printOut("Package is empty and will not result into a xml file on "\ "disk!? Please check configuration file.") return printOut("package contains " + str(nItemCount) + " items") aPackage = codecs.open(sPackage, 'w', "utf-8") printOut("write temp package \"" + sPackage) aPackage.write(sBuffer) aPackage.close() # Reads the fragment files with the file names lFragments in directory aDir, # formats them and returns a string that contains the merged fragments. def getFragments(self, aDir, sSetName, lFragments, nPrettyTabs): sBuffer = '' sExtXcu = self.m_aCfg.getValue(PROP_EXTENSION_XCU); sShareSubdirName = self.m_aCfg.getValue(PROP_SHARE_SUBDIR_NAME) if len(sShareSubdirName) < 1: raise Exception("no share subdir set") if len(lFragments) < 1: return sBuffer for tabs in range(nPrettyTabs): sBuffer = sBuffer + "\t" sBuffer = sBuffer + "\n" nPrettyTabs = nPrettyTabs + 1 for sFragment in lFragments: sFragPath = os.path.join(aDir, sFragment + "." + sExtXcu) try: aFragmentFile = codecs.open(sFragPath, "r", "utf-8") except: # handle simple files only and check for existence! raise Exception("fragment \"" + sFragPath + "\" does not exists.") printOut("merge fragment \"" + sFragPath + "\" ...") data = aFragmentFile.read() aFragmentFile.close() data = data.replace("$(share_subdir_name)", sShareSubdirName) sBuffer = sBuffer + data sBuffer = sBuffer + "\n" nPrettyTabs = nPrettyTabs - 1 for tabs in range(nPrettyTabs): sBuffer = sBuffer + "\t" sBuffer = sBuffer + "\n" return sBuffer run(sys.argv)