1#!/usr/bin/env python 2#_____________________________________________ 3# Caolan McNamara caolanm@redhat.com 4# converted from original java written by Andreas Schluens so we can continue 5# to build 680 OpenOffice.org series without java 6# this is not really a replacement for the existing java tool, just the 7# minimum required to make it work for now, the existing tool is still 8# the canonical base, changes to it will have to be mirrored here until 9# there is a java which is available for use by all 10#_____________________________________________ 11 12import sys, string, os.path 13 14CFGFILE = os.environ["SOLARVER"] + "/" + os.environ["INPATH"] + "/inc/l10ntools/FCFGMerge.cfg" 15 16PROP_XMLVERSION = "xmlversion" # // <= global cfg file 17PROP_XMLENCODING = "xmlencoding" # // <= global cfg file 18PROP_XMLPATH = "xmlpath" # // <= global cfg file 19PROP_XMLPACKAGE = "xmlpackage" # // <= global cfg file 20PROP_SETNAME_TYPES = "setname_types" # // <= global cfg file 21PROP_SETNAME_FILTERS = "setname_filters" # // <= global cfg file 22PROP_SETNAME_LOADERS = "setname_frameloaders" # // <= global cfg file 23PROP_SETNAME_HANDLERS = "setname_contenthandlers" # // <= global cfg file 24PROP_SUBDIR_TYPES = "subdir_types" # // <= global cfg file 25PROP_SUBDIR_FILTERS = "subdir_filters" # // <= global cfg file 26PROP_SUBDIR_LOADERS = "subdir_frameloaders" # // <= global cfg file 27PROP_SUBDIR_HANDLERS = "subdir_contenthandlers" # // <= global cfg file 28PROP_EXTENSION_XCU = "extension_xcu" # // <= global cfg file 29PROP_EXTENSION_PKG = "extension_pkg" # // <= global cfg file 30PROP_DELIMITER = "delimiter" # // <= global cfg file 31PROP_TRIM = "trim" # // <= global cfg file 32PROP_DECODE = "decode" # // <= global cfg file 33PROP_FRAGMENTSDIR = "fragmentsdir" # // <= cmdline 34PROP_TEMPDIR = "tempdir" # // <= cmdline 35PROP_OUTDIR = "outdir" # // <= cmdline 36PROP_PKG = "pkg" # // <= cmdline 37PROP_TCFG = "tcfg" # // <= cmdline 38PROP_FCFG = "fcfg" # // <= cmdline 39PROP_LCFG = "lcfg" # // <= cmdline 40PROP_CCFG = "ccfg" # // <= cmdline 41PROP_LANGUAGEPACK = "languagepack" # // <= cmdline 42PROP_ITEMS = "items" # // <= pkg cfg files! 43 44#---begin java.util.Properties copy---# 45""" 46 47An incomplete clean room implementation of 48java.util.Properties written in Python. 49 50Copyright (C) 2002,2004 - Ollie Rutherfurd <oliver@rutherfurd.net> 51 52Based on: 53 54 http://java.sun.com/j2se/1.3/docs/api/java/util/Properties.html 55 56Missing: 57 58 - Currently, u\XXXX sequences are escaped when saving, but not unescaped 59 when read.. 60 61License: Python License 62 63Example Usage: 64 65>>> from properties import Properties 66>>> props = Properties() 67>>> props['one'] = '1' 68>>> props['your name'] = "I don't know" 69>>> print '\n'.join(props.keys()) 70your name 71one 72>>> from StringIO import StringIO 73>>> buff = StringIO() 74>>> props.store(buff, "a little example...") 75>>> buff.seek(0) 76>>> print buff.read() 77# a little example... 78your\ name=I\ don\'t\ know 79one=1 80>>> print props['your name'] 81I don't know 82 83$Id: pyAltFCFGMerge,v 1.3 2007-12-07 10:57:44 vg Exp $ 84 85""" 86 87__all__ = ['Properties'] 88 89 90def dec2hex(n): 91 92 h = hex(n)[2:].upper() 93 return '\\u' + '0' * (4 - len(h)) + h 94 95 96def escapestr(s): 97 98 buff = [] 99 # QUESTION: escape leading or trailing spaces? 100 for c in s: 101 if c == '\\': 102 buff.append('\\\\') 103 elif c == '\t': 104 buff.append('\\t') 105 elif c == '\n': 106 buff.append('\\n') 107 elif c == '\r': 108 buff.append('\\r') 109 elif c == ' ': 110 buff.append('\\ ') 111 elif c == "'": 112 buff.append("\\'") 113 elif c == '"': 114 buff.append('\\"') 115 elif c == '#': 116 buff.append('\\#') 117 elif c == '!': 118 buff.append('\\!') 119 elif c == '=': 120 buff.append('\\=') 121 elif 32 <= ord(c) <= 126: 122 buff.append(c) 123 else: 124 buff.append(dec2hex(c)) 125 126 return ''.join(buff) 127 128 129# TODO: add support for \uXXXX? 130def unescapestr(line): 131 132 buff = [] 133 escape = 0 134 for i in range(len(line)): 135 c = line[i] 136 if c == '\\': 137 if escape: 138 escape = 0 139 buff.append('\\') 140 continue 141 else: 142 # this is to deal with '\' 143 # acting as a line continuation 144 # character 145 if i == len(line) - 1: 146 buff.append('\\') 147 break 148 else: 149 escape = 1 150 continue 151 elif c == 'n': 152 if escape: 153 escape = 0 154 buff.append('\n') 155 continue 156 elif c == 'r': 157 if escape: 158 escape = 0 159 buff.append('\r') 160 continue 161 elif c == 't': 162 if escape: 163 escape = 0 164 buff.append('\t') 165 continue 166 167 buff.append(c) 168 169 # make sure escape doesn't stay one 170 # all expected escape sequences either break 171 # or continue, so this should be safe 172 if escape: 173 escape = 0 174 175 return ''.join(buff) 176 177 178 179class Properties(dict): 180 181 def __init__(self, defaults={}): 182 dict.__init__(self) 183 for n,v in defaults.items(): 184 self[n] = v 185 186 def __getittem__(self,key): 187 try: 188 return dict.__getittem__(self,key) 189 except KeyError: 190 return None 191 192 def read(self,filename): 193 """ 194 Reads properties from a file (java Property class 195 reads from an input stream -- see load()). 196 """ 197 f = None 198 try: 199 f = open(filename) 200 self.load(f) 201 finally: 202 if f: 203 f.close() 204 205 def load(self, buff): 206 """ 207 Reads properties from a stream (StringIO, file, etc...) 208 """ 209 props = readprops(buff) 210 #for n,v in props.iteritems(): 211 for n in props.keys(): 212 self[n] = props[n] 213 214def readprops(buff): 215 216 name,value = None,'' 217 props = {} 218 continued = 0 219 220 while 1: 221 line = buff.readline() 222 if not line: 223 break 224 line = line.strip() 225 226 # empty line 227 if not line: 228 continue 229 230 # comment 231 if line[0] in ('#','!'): 232 continue 233 234 # find name 235 i,escaped = 0,0 236 while i < len(line): 237 c = line[i] 238 239 if c == '\\': 240 if escaped: 241 escaped = 0 242 else: 243 escaped = 1 244 i += 1 245 continue 246 247 elif c in (' ', '\t', ':', '=') and not escaped: 248 name = unescapestr(line[:i]) 249 break 250 251 # make sure escaped doesn't stay on 252 if escaped: 253 escaped = 0 254 255 i += 1 256 257 # no dlimiter was found, name is entire line, there is no value 258 if name == None: 259 name = unescapestr(line.lstrip()) 260 261 # skip delimiter 262 while line[i:i+1] in ('\t', ' ', ':', '='): 263 i += 1 264 265 value = unescapestr(line[i:].strip()) 266 while value[-1:] == '\\': 267 value = value[:-1] # remove \ 268 line = buff.readline() 269 if not line: 270 break 271 value += unescapestr(line.strip()) 272 273 #print 'value:',value ## 274 props[name] = value 275 276 return props 277#---end java.util.Properties copy---# 278 279# Its a simple command line tool, which can merge different XML fragments 280# together. Such fragments must exist as files on disk, will be moved into 281# one file together on disk. 282# 283# @author Andreas Schluens 284# 285def run(sCmdLine): 286 printCopyright() 287 288 aCfg = ConfigHelper(CFGFILE, sCmdLine) 289 290 # help requested? 291 if aCfg.isHelp(): 292 printHelp() 293 sys.exit(-1) 294 295 #create new merge object and start operation 296 aMerger = Merger(aCfg) 297 aMerger.merge() 298 299 sys.exit(0) 300 301#prints out a copyright message on stdout. 302def printCopyright(): 303 print("FCFGMerge") 304 print("Copyright: 2003 by Red Hat, Inc., based on FCFGMerge.java` by Sun") 305 print("All Rights Reserved.") 306 307#prints out a help message on stdout. 308def printHelp(): 309 print("____________________________________________________________") 310 print("usage: FCFGMerge cfg=<file name>") 311 print("parameters:") 312 print("\tcfg=<file name>") 313 print("\t\tmust point to a system file, which contains") 314 print("\t\tall neccessary configuration data for the merge process.") 315 print("\tFurther cou can specify every parameter allowed in the") 316 print("\tconfig file as command line parameter too, to overwrite") 317 print("\tthe value from the file.") 318 319def StringTokenizer(mstring, separators, isSepIncluded=0): 320#Return a list of tokens given a base string and a string of 321#separators, optionally including the separators if asked for""" 322 token='' 323 tokenList=[] 324 for c in mstring: 325 if c in separators: 326 if token != '': 327 tokenList.append(token) 328 token='' 329 if isSepIncluded: 330 tokenList.append(c) 331 else: 332 token+=c 333 if token: 334 tokenList.append(token) 335 return tokenList 336 337# can be used to analyze command line parameters 338# and merge it together with might existing config 339# files. That provides the possibility to overwrite 340# config values via command line parameter. 341# 342# @author Andreas Schluens 343class ConfigHelper: 344 def __init__(self, sPropFile, lCommandLineArgs): 345 self.m_bEmpty = 1 346 # first load prop file, so its values can be overwritten 347 # by command line args later 348 # Do it only, if a valid file name was given. 349 # But in case this file name is wrong, throw an exception. 350 # So the outside code can react! 351 if sPropFile != None and len(sPropFile) > 0: 352 self.props = Properties() 353 self.props.read(sPropFile) 354 355 count = 0 356 if lCommandLineArgs != None: 357 count = len(lCommandLineArgs) 358 print("Count is {c}".format(c=count)) 359 self.m_bEmpty = (count < 1) 360 361 #print( lCommandLineArgs, "and len is", count ) 362 for arg in range(count): 363 # is it a named-value argument? 364 # Note: We ignores double "=" signs! => search from left to right 365 pos = lCommandLineArgs[arg].find('=') 366 if pos != -1: 367 sArg = lCommandLineArgs[arg][0:pos] 368 sValue = lCommandLineArgs[arg][pos+1:] 369 self.props[sArg] = sValue 370 continue 371 372 # is it a boolean argument? 373 # Note: Because "--" and "-" will be interpreted as the same 374 # we search from right to left! 375 pos = lCommandLineArgs[arg].rfind('-') 376 if pos == -1: 377 pos = lCommandLineArgs[arg].rfind('/') 378 if pos != -1: 379 sArg = lCommandLineArgs[arg][pos+1:] 380 self.props[sArg] = 1 381 continue 382 383 raise Exception("Invalid command line detected. The argument \""+lCommandLineArgs[arg]+"\" use an unsupported format.") 384 385# for item in self.props: 386# print item, '->', self.props[item] 387 388 def isHelp(self): 389 return ( 390 ("help" in self.props) or 391 ("?" in self.props ) or 392 ("h" in self.props ) 393 ) 394 395 def getValue(self, sProp): 396 if not sProp in self.props: 397 raise Exception("The requested config value \""+sProp+"\" "\ 398 "does not exists!"); 399 return self.props[sProp]; 400 401 def getValueWithDefault(self, sProp, default): 402 if not sProp in self.props: 403 return default; 404 return self.props[sProp]; 405 406 def getStringList(self, sProp, sDelimiter, bTrim, bDecode): 407 if not sProp in self.props: 408 raise Exception("The requested config value \""+sProp+"\" does "\ 409 "not exists!"); 410 sValue = self.props[sProp] 411 412 lValue = [] 413 lTokens = StringTokenizer(sValue, sDelimiter) 414 for sToken in lTokens: 415 if bTrim: 416 sToken = sToken.strip() 417 418 # remove "" 419 if ((bDecode) and (sToken.find("\"") == 0) and \ 420 (sToken.rfind("\"") == len(sToken)-1)): 421 sToken = sToken[1, len(sToken)-1] 422 lValue.append(sToken) 423 424 return lValue 425 426def generateHeader(sVersion, sEncoding, sPath, sPackage, bLanguagePack): 427 sHeader = "<?xml version=\"" 428 sHeader += sVersion 429 sHeader += "\" encoding=\"" 430 sHeader += sEncoding 431 sHeader += "\"?>\n" 432 433 if bLanguagePack: 434 sHeader += "<oor:component-data oor:package=\"" 435 sHeader += sPath 436 sHeader += "\" oor:name=\"" 437 sHeader += sPackage 438 sHeader += "\" xmlns:install=\"http://openoffice.org/2004/installation\"" 439 sHeader += " xmlns:oor=\"http://openoffice.org/2001/registry\"" 440 sHeader += " xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"" 441 sHeader += " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n" 442 else: 443 sHeader += "<oor:component-data xmlns:oor=\"http://openoffice.org/2001/registry\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" oor:package=\"" 444 sHeader += sPath 445 sHeader += "\" oor:name=\"" 446 sHeader += sPackage 447 sHeader += "\">\n" 448 return sHeader 449 450def generateFooter(): 451 return "</oor:component-data>\n" 452 453# can merge different xml fragments together. 454# 455# @author Caolan McNamara converted from the original java by Andreas Schluens 456# 457class Merger: 458 def __init__(self, aCfg): 459 self.m_aCfg = aCfg 460 461 self.m_aFragmentsDir = self.m_aCfg.getValue(PROP_FRAGMENTSDIR) 462 463 sDelimiter = self.m_aCfg.getValue(PROP_DELIMITER) 464 bTrim = self.m_aCfg.getValue(PROP_TRIM) 465 bDecode = self.m_aCfg.getValue(PROP_DECODE) 466 467 try: 468 aFcfg = ConfigHelper(self.m_aCfg.getValue(PROP_TCFG), None) 469 self.m_lTypes = aFcfg.getStringList(PROP_ITEMS, sDelimiter, bTrim, bDecode) 470 except: 471 self.m_lTypes = [] 472 473 try: 474 aFcfg = ConfigHelper(self.m_aCfg.getValue(PROP_FCFG), None) 475 self.m_lFilters = aFcfg.getStringList(PROP_ITEMS, sDelimiter, bTrim, bDecode) 476 except: 477 print( "Filters are empty" ) 478 self.m_lFilters = [] 479 480 try: 481 aFcfg = ConfigHelper(self.m_aCfg.getValue(PROP_LCFG), None) 482 self.m_lLoaders = aFcfg.getStringList(PROP_ITEMS, sDelimiter, bTrim, bDecode) 483 except: 484 self.m_lLoaders = [] 485 486 try: 487 aFcfg = ConfigHelper(self.m_aCfg.getValue(PROP_CCFG), None) 488 self.m_lHandlers = aFcfg.getStringList(PROP_ITEMS, sDelimiter, bTrim, bDecode) 489 except: 490 self.m_lHandlers = [] 491 492 def merge(self): 493 sPackage = self.m_aCfg.getValue(PROP_PKG) 494 495 print("create package \""+sPackage+"\" ...") 496 print("generate package header ... ") 497 498 sBuffer = generateHeader(\ 499 self.m_aCfg.getValue(PROP_XMLVERSION ),\ 500 self.m_aCfg.getValue(PROP_XMLENCODING),\ 501 self.m_aCfg.getValue(PROP_XMLPATH ),\ 502 self.m_aCfg.getValue(PROP_XMLPACKAGE ),\ 503 self.m_aCfg.getValueWithDefault(PROP_LANGUAGEPACK, False)) 504 505 # counts all transfered fragments 506 # Can be used later to decide, if a generated package file 507 # contains "nothing"! 508 nItemCount = 0 509 510 for i in range(4): 511 sSetName = None 512 sSubDir = None 513 lFragments = None 514 515 try: 516 if i == 0: #types 517 print("generate set for types ... ") 518 sSetName = self.m_aCfg.getValue(PROP_SETNAME_TYPES) 519 sSubDir = self.m_aCfg.getValue(PROP_SUBDIR_TYPES ) 520 lFragments = self.m_lTypes 521 elif i == 1: # filters 522 print("generate set for filter ... ") 523 sSetName = self.m_aCfg.getValue(PROP_SETNAME_FILTERS) 524 sSubDir = self.m_aCfg.getValue(PROP_SUBDIR_FILTERS ) 525 lFragments = self.m_lFilters 526 elif i == 2: # loaders 527 print("generate set for frame loader ... ") 528 sSetName = self.m_aCfg.getValue(PROP_SETNAME_LOADERS) 529 sSubDir = self.m_aCfg.getValue(PROP_SUBDIR_LOADERS ) 530 lFragments = self.m_lLoaders 531 elif i == 3: # handlers 532 print("generate set for content handler ... ") 533 sSetName = self.m_aCfg.getValue(PROP_SETNAME_HANDLERS) 534 sSubDir = self.m_aCfg.getValue(PROP_SUBDIR_HANDLERS ) 535 lFragments = self.m_lHandlers 536 except: 537 continue 538 539 print("Length of Fragments: {f} Set Name {setname} Subdir {subdir}". 540 format(f=len(lFragments),setname=sSetName,subdir=sSubDir)) 541 #sys.stdin.readline() 542 nItemCount = nItemCount + len(lFragments) 543 544 sBuffer = sBuffer + self.getFragments(\ 545 os.path.join(self.m_aFragmentsDir, sSubDir), \ 546 sSetName, lFragments, 1) 547 548 print("generate package footer ... ") 549 sBuffer = sBuffer + generateFooter() 550 551 # Attention! 552 # If the package seems to be empty, it make no sense to generate a 553 # corresponding xml file. We should suppress writing of this file on 554 # disk completely ... 555 if nItemCount < 1: 556 print("Package is empty and will not result into an xml file on disk!? Please check configuration file.") 557 return 558 print("package contains "+str(nItemCount)+" items") 559 560 aPackage = open(sPackage, mode="w") 561 print("write temp package {pkg}".format(pkg=sPackage)) 562 aPackage.write(sBuffer) 563 564 def getFragments(self, aDir, sSetName, lFragments, nPrettyTabs): 565 sBuffer = '' 566 sExtXcu = self.m_aCfg.getValue(PROP_EXTENSION_XCU); 567 568 if len(lFragments) < 1: 569 return sBuffer 570 571 for tabs in range(nPrettyTabs): 572 sBuffer = sBuffer + "\t" 573 sBuffer = sBuffer + "<node oor:name=\""+sSetName+"\">\n" 574 nPrettyTabs = nPrettyTabs + 1 575 576 for sFragment in lFragments: 577 sFragPath = os.path.join(aDir, sFragment+"."+sExtXcu) 578 try: 579 aFragmentFile = open(sFragPath) 580 except: 581 # handle simple files only and check for existence! 582 raise Exception("fragment \""+sFragPath+"\" does not exists.") 583 584 print("merge fragment \""+sFragPath+"\" ...") 585 sBuffer = sBuffer + aFragmentFile.read() 586 587 sBuffer = sBuffer + "\n" 588 589 nPrettyTabs = nPrettyTabs - 1 590 for tabs in range(nPrettyTabs): 591 sBuffer = sBuffer + "\t" 592 sBuffer = sBuffer + "</node>\n" 593 return sBuffer 594 595run(sys.argv) 596