From nobody Sun Apr 28 14:24:40 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of groups.io designates 66.175.222.12 as permitted sender) client-ip=66.175.222.12; envelope-from=bounce+27952+42807+1787277+3901457@groups.io; helo=web01.groups.io; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zoho.com: domain of groups.io designates 66.175.222.12 as permitted sender) smtp.mailfrom=bounce+27952+42807+1787277+3901457@groups.io; dmarc=fail(p=none dis=none) header.from=intel.com ARC-Seal: i=1; a=rsa-sha256; t=1561460671; cv=none; d=zoho.com; s=zohoarc; b=PG6k2zYSIrSIgy21kMFOGssyXxm5TaPxi8P/IuO0LtoHYHUYZOJiuWc0l/kFjhqtHI+sTk8aniQSb0UKkgI9u2GLgK+sGrWpGxvLoW1VLVAQ3RdMPpDCvlFhokCj9KNGSsH2/A/4SxXM89cn4a/xJtPKENBPirpAdM3Nw8K4vYk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1561460671; h=Content-Type:Cc:Date:From:List-Id:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Sender:Subject:To:ARC-Authentication-Results; bh=FnfTF9IV9pHdWOGu0wEEXXXZee5j+rTEl6850PhVlWQ=; b=mxclWvPNpCU8n8I6WRWruIHXCSncXG0Xnr7T0H05camfbHsfhrTXkeRHmoo/R5MGLsHe9AaaTV+so1tnj/Hw9rPTyRo6UUlY1beFldk7ye3NbttDLV64/UCD0gCzkL6rKK1cDO1q1dtON9FR4YgOFwoA9m8RWhkok6bEXZ8RRA4= ARC-Authentication-Results: i=1; mx.zoho.com; dkim=pass; spf=pass (zoho.com: domain of groups.io designates 66.175.222.12 as permitted sender) smtp.mailfrom=bounce+27952+42807+1787277+3901457@groups.io; dmarc=fail header.from= (p=none dis=none) header.from= Received: from web01.groups.io (web01.groups.io [66.175.222.12]) by mx.zohomail.com with SMTPS id 1561460670836184.710768994084; Tue, 25 Jun 2019 04:04:30 -0700 (PDT) Return-Path: X-Received: from mga09.intel.com (mga09.intel.com [134.134.136.24]) by groups.io with SMTP; Tue, 25 Jun 2019 04:04:29 -0700 X-Amp-Result: UNSCANNABLE X-Amp-File-Uploaded: False X-Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 25 Jun 2019 04:04:28 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.63,415,1557212400"; d="dat'59?scan'59,208,59";a="169747597" X-Received: from fmsmsx104.amr.corp.intel.com ([10.18.124.202]) by FMSMGA003.fm.intel.com with ESMTP; 25 Jun 2019 04:04:27 -0700 X-Received: from fmsmsx158.amr.corp.intel.com (10.18.116.75) by fmsmsx104.amr.corp.intel.com (10.18.124.202) with Microsoft SMTP Server (TLS) id 14.3.439.0; Tue, 25 Jun 2019 04:04:27 -0700 X-Received: from shsmsx107.ccr.corp.intel.com (10.239.4.96) by fmsmsx158.amr.corp.intel.com (10.18.116.75) with Microsoft SMTP Server (TLS) id 14.3.439.0; Tue, 25 Jun 2019 04:04:27 -0700 X-Received: from shsmsx101.ccr.corp.intel.com ([169.254.1.87]) by SHSMSX107.ccr.corp.intel.com ([169.254.9.173]) with mapi id 14.03.0439.000; Tue, 25 Jun 2019 19:04:25 +0800 From: "Fan, ZhijuX" To: "devel@edk2.groups.io" CC: "Gao, Liming" , "Feng, Bob C" , Ard Biesheuvel , "Leif Lindholm" , "Kinney, Michael D" Subject: [edk2-devel] [edk2-platform patch V3] Platform/Intel:Add UniTool into edk2-platforms/Platform/Intel/Tools Thread-Topic: [edk2-platform patch V3] Platform/Intel:Add UniTool into edk2-platforms/Platform/Intel/Tools Thread-Index: AdUrRbnIOdF+gtfTSqqvZuUVV7l2JQ== Date: Tue, 25 Jun 2019 11:04:24 +0000 Message-ID: Accept-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: dlp-product: dlpe-windows dlp-version: 11.0.600.7 dlp-reaction: no-action x-originating-ip: [10.239.127.40] MIME-Version: 1.0 Precedence: Bulk List-Unsubscribe: Sender: devel@edk2.groups.io List-Id: Mailing-List: list devel@edk2.groups.io; contact devel+owner@edk2.groups.io Reply-To: devel@edk2.groups.io,zhijux.fan@intel.com Content-Type: multipart/mixed; boundary="_000_FAD0D7E0AE0FA54D987F6E72435CAFD50AF848FCSHSMSX101ccrcor_" Content-Language: en-US DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=groups.io; q=dns/txt; s=20140610; t=1561460669; bh=Nr9VFM+zhVmVhogulqlOnwpDOsfL2Qsfp4tNaC2K2xo=; h=CC:Content-Type:Date:From:Reply-To:Subject:To; b=UvEES/2/PCzEcKLhv6qzKRftWQCZtjbCnR3nQSL7cwUOkBT93ALTyqV0OtFW93GtSDg uUQxaFI09bVrEDEwn8RL64awj6lLlvXykF2Dg4YoZET6AmazmL8GTYXQbAqhLih7eboqk NT5389ifxqho8X1pciLKuw9gE0Vxp8Lu2D0= X-Zoho-Virus-Status: 1 X-ZohoMail-DKIM: pass (identity @groups.io) --_000_FAD0D7E0AE0FA54D987F6E72435CAFD50AF848FCSHSMSX101ccrcor_ Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" BZ:https://bugzilla.tianocore.org/show_bug.cgi?id=3D1855 UniTool is one python script to generate UQI (Universal Question Identifier) unicode string for HII question PROMPT string. UQI string can be used to identify each HII question. The scripts function will sync up UQI definitions with uni files based on vfi/vfr/hfr/sd/sdi in the tree. This script can be run in both Py2 and Py3. Cc: Liming Gao Cc: Bob Feng Cc: Ard Biesheuvel Cc: Leif Lindholm Cc: Michael D Kinney Signed-off-by: Zhiju.Fan --- Add README.txt to scripts Platform/Intel/Tools/UniTool/README.txt | 46 +++ Platform/Intel/Tools/UniTool/UniTool.py | 499 ++++++++++++++++++++++++++++= ++++ 2 files changed, 545 insertions(+) create mode 100644 Platform/Intel/Tools/UniTool/README.txt create mode 100644 Platform/Intel/Tools/UniTool/UniTool.py diff --git a/Platform/Intel/Tools/UniTool/README.txt b/Platform/Intel/Tools= /UniTool/README.txt new file mode 100644 index 0000000000..3e59a6c75c --- /dev/null +++ b/Platform/Intel/Tools/UniTool/README.txt @@ -0,0 +1,46 @@ +To update the UniTool.exe utility +----------------- +- Must have python and cx_freeze installed with the default location. +- After modify the UniTool.py script as desired, launch the runit.cmd scri= pt +- New version generated in UniTool.exe + +How to use UniTool +----------------- +The usage of the tool is: +UniTool.py [-b] [-u] [-l] [-x] [-h] [-d 'rootDirectory1'] [-d 'rootDirecto= ry2'] [-d 'rootDirectory3']... [-q e|w] + 'rootDirectory0' 'uqiFile'|'uqiFileDirectory' ['excludedDirecto= ry1'] ['excludedDirectory2'] ['excludedDirectory3']... + +Function will sync up UQI definitions with uni files based on vfi/vfr/hfr/= sd/sdi in the tree. + +Required Arguments: + 'rootdirectory0' path to root directory + 'uqiFileDirectory' path to UQI file(UqiList.uni) + 'uqiFile' UQI file + +Options: + -h Show this help + -b Build option returns error if any new UQI needs as= signing + based on vfi/vfr/hfr/sd/sdi when no -u option is s= pecified + -u Create new UQIs that does not already exist in uqi= File for + any string requiring a UQI based on vfi/vfr/hfr/sd= /sdi + NOTE: 'uqiFile' cannot be readonly! + -l Language deletion option (keeps only English and u= qi) + moves all UQIs to 'uqiFile' + NOTE: Uni files cannot be readonly! + -x Exclude 'rootDirectory'/'excludedDirectory1' & + 'rootDirectory'/'excludedDirectory2'... from UQI l= ist build + NOTE: Cannot be the same as rootDirectory + -d Add multiple root directories to process + -q Print warning(w) or return error(e) if different H= II questions + are referring same string token + +Return error if any duplicated UQI string or value in UQI list or if no de= finition +for any string referred by HII question when -b or -u is specified + +NOTE: Options must be specified before parameters + +Notice +----------------- +- "S0000" style will be used if uqiFile needs to be new created. + Use the same uqi style if uqiFile is existed. For example, + if the exist UqiFile use "\x0000" style, "\x0000" will be used. diff --git a/Platform/Intel/Tools/UniTool/UniTool.py b/Platform/Intel/Tools= /UniTool/UniTool.py new file mode 100644 index 0000000000..df2b1036ce --- /dev/null +++ b/Platform/Intel/Tools/UniTool/UniTool.py @@ -0,0 +1,499 @@ +## @file +# generate UQI (Universal Question Identifier) unicode string for HII ques= tion PROMPT string. UQI string can be used to +# identify each HII question. +# +# Copyright (c) 2019, Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +import re +import sys +import os +import getopt +import codecs +import fnmatch +import logging +import argparse + +# global variable declarations +QuestionError =3D False +UqiList =3D re.compile('^#string[ \t]+([A-Z_0-9]+)[ \t]+#language[ \t]+uqi= [ \t\r\n]+"(?:[x\S]{1,2})([0-9a-fA-F]{4,5})"', + re.M).findall +AllUqis =3D {} +StringDict =3D {} +GlobalVarId =3D {} +Options =3D {} + +# Version message +__prog__ =3D 'UniTool' +__description__ =3D 'generate UQI unicode string for HII question PROMPT s= tring.' +__copyright__ =3D 'Copyright (c) 2019, Intel Corporation. All rights reser= ved.
' +__version__ =3D '%s Version %s' % (__prog__, '0.1 ') +_Usage =3D "Syntax: %s [-b] [-u] [-l] [-x] [-h] [-d 'rootDirectory1'] [-d= 'rootDirectory2'] [-d 'rootDirectory3']... \n[-q e|w]" \ + "'rootDirectory0' 'uqiFile'|'uqiFileDirectory' ['excludedDirector= y1'] ['excludedDirectory2'] ['excludedDirectory3']...\n" \ + % (os.path.basename(sys.argv[0])) +_Epilog =3D """\n\nFunction will sync up UQI definitions with uni files ba= sed on vfi/vfr/hfr/sd/sdi in the tree.\n + Required Arguments: + 'rootdirectory0' path to root directory + 'uqiFileDirectory' path to UQI file(UqiList.uni) + 'uqiFile' UQI file + + Return error if any duplicated UQI string or value in UQI list or= if no definition + for any string referred by HII question when -b or -u is specified + + NOTE: Options must be specified before parameters + """ + +# ********************************************************************** +# description: Get uni file encoding +# +# arguments: Filename - name of uni file +# +# returns: utf-8 or utf-16 +# +def GetUniFileEncoding(Filename): + # + # Detect Byte Order Mark at beginning of file. Default to UTF-8 + # + Encoding =3D 'utf-8' + + # + # Read file + # + try: + with open(Filename, mode=3D'rb') as UniFile: + FileIn =3D UniFile.read() + except: + return Encoding + + if (FileIn.startswith(codecs.BOM_UTF16_BE) or FileIn.startswith(codecs= .BOM_UTF16_LE)): + Encoding =3D 'utf-16' + + return Encoding + + +# rewrite function os.path.walk +def Walk(Top, Func, Arg): + try: + Names =3D os.listdir(Top) + except os.error: + return + Func(Arg, Top, Names) + for Name in Names: + Name =3D os.path.join(Top, Name) + if os.path.isdir(Name): + Walk(Name, Func, Arg) + + +# ********************************************************************** +# description: Parses commandline arguments and options +# Calls function processUni to build dictionary of strings +# Calls other functions according to user specified options +# +# arguments: argv - contains all input from command line +# - must contain path to root directory +# - may contain options -h, -u, -l, -b or -x before path +# +# returns: none +# +def main(): + ##### Read input arguments and options + global AllUqis, UqiList, QuestionError + parser =3D argparse.ArgumentParser(prog=3D__prog__, + description=3D__description__ + __cop= yright__, + usage=3D_Usage, + epilog=3D_Epilog, + conflict_handler=3D'resolve') + parser.add_argument('Path', nargs=3D'+', + help=3D'the path for files to be converted.It coul= d be directory or file path.') + parser.add_argument('-v', '--version', action=3D'version', version=3D_= _version__, + help=3D"show program's version number and exit") + parser.add_argument('-b', '--build', action=3D'store_true', dest=3D'Bu= ildOption', + help=3D"Build option returns error if any new UQI = needs assigning " \ + "based on vfi/vfr/hfr/sd/sdi when no -u optio= n is specified") + parser.add_argument('-u', '--updata', action=3D'store_true', dest=3D'U= pdateUQIs', + help=3D"Create new UQIs that does not already exis= t in uqiFile for" \ + "any string requiring a UQI based on vfi/vfr/= hfr/sd/sdi" \ + "NOTE: 'uqiFile' cannot be readonly!") + parser.add_argument('-l', '--lang', action=3D'store_true', dest=3D'Lan= gOption', + help=3D"Language deletion option (keeps only Engli= sh and uqi)" \ + "moves all UQIs to 'uqiFile', NOTE: Uni files= cannot be readonly!") + parser.add_argument('-x', '--exclude', action=3D'store_true', dest=3D'= ExcludeOption', + help=3D"Exclude 'rootDirectory'/'excludedDirectory= 1' &" \ + "'rootDirectory'/'excludedDirectory2'... from= UQI list build") + parser.add_argument('-d', '--dir', action=3D'append', metavar=3D'FILED= IR', dest=3D'DirName', + help=3D"Add multiple root directories to process") + parser.add_argument('-q', '--question', dest=3D'Question', choices=3D[= 'w', 'e'], + help=3D"Print warning(w) or return error(e) if dif= ferent HII questions" \ + "are referring same string token") + Opts =3D parser.parse_args() + Destname =3D '' + DirNameList =3D [] + ExDirList =3D [] + if Opts.Path: + DirNameList.append(Opts.Path[0]) + Destname =3D Opts.Path[1] + ExDirList =3D Opts.Path[2:] + if Opts.DirName: + DirNameList.extend(Opts.DirName) + QuestionOption =3D Opts.Question + ExcludeOption =3D Opts.ExcludeOption + BuildOption =3D Opts.BuildOption + UpdateUQIs =3D Opts.UpdateUQIs + LangOption =3D Opts.LangOption + ExPathList =3D [] + + if ExDirList: + try: + for EachExDir in ExDirList: + for EachRootDir in DirNameList: + if EachExDir =3D=3D EachRootDir: + print("\nERROR: excludedDirectory is same as rootD= irectory\n") + return + ExPathList.append(EachRootDir + os.sep + EachExDir) + except: + print(_Usage) + return + + global Options + Options =3D {'Destname': Destname, 'DirNameList': DirNameList, 'ExPath= List': ExPathList, 'BuildOption': BuildOption, + 'UpdateUQIs': UpdateUQIs, 'LangOption': LangOption, 'Exclud= eOption': ExcludeOption, + 'QuestionOption': QuestionOption} + print("UQI file: %s" % Destname) + for EachDirName in DirNameList: + Walk(EachDirName, processUni, None) + if QuestionError: + return + if os.path.isdir(Options['Destname']): + DestFileName =3D Options['Destname'] + os.sep + 'UqiList.uni' + else: + DestFileName =3D Options['Destname'] + if os.path.exists(DestFileName) and (DestFileName not in list(AllUqis.= keys())): + try: + Encoding =3D GetUniFileEncoding(DestFileName) + with codecs.open(DestFileName, 'r+', Encoding) as destFile: + DestFileBuffer =3D destFile.read() + except IOError as e: + print("ERROR: " + e.args[1]) + return + AllUqis[DestFileName] =3D UqiList(DestFileBuffer) + if BuildOption: + ReturnVal =3D newUqi() + if (ReturnVal =3D=3D 1): + print('Please fix UQI ERROR(s) above before proceeding.') + else: + print("No UQI issues detected\n") + return + + +# ********************************************************************** +# description: newUqi collects a list of all currently used uqi values in = the tree +# Halt build if any duplicated string or value in UQI list. +# If -u option was specified, creates new UQIs that does not +# already exist in uqiFile for any string requiring a UQI. +# +# arguments: none +# +# returns: 0 on success +# 1 on error - this should cause the build to halt +# + +Syntax =3D "S" +SyntaxRE =3D re.compile('#string[ \t]+[A-Z_0-9]+[ \t]+#language[ \t]+uqi[ = \t\r\n]+"([x\S]{1,2}).*', re.DOTALL).findall + + +def newUqi(): + global Options, GlobalVarId, AllUqis, Syntax, SyntaxRE + UqiRange =3D [] + UqiStringList =3D [] + CreateUQI =3D [] + ReturnVal =3D 0 + BaseNumSpaces =3D 47 # Used to line up the UQI values in the resultin= g uqiFile + + # Look for duplication in the current UQIs and collect current range o= f UQIs + for path in AllUqis.keys(): + for UqiString in AllUqis[path]: # path contains the path and File= name of each uni file + # Checks for duplicated strings in UQI list + for TempString in UqiStringList: + if TempString =3D=3D UqiString[0]: + print("ERROR: UQI string %s was assigned more than onc= e and will cause corruption!" % UqiString[0]) + print("Delete one occurrence of the string and rerun t= ool.") + ReturnVal =3D 1 # halt build + + UqiStringList.append(UqiString[0]) + + # Checks for duplicated UQI values in UQI list + if int(UqiString[1], 16) in UqiRange: + print("ERROR: UQI value %04x was assigned more than once a= nd will cause corruption!" % int(UqiString[1], + = 16)) + print("Delete one occurrance of the UQI and rerun tool to = create alternate value.") + ReturnVal =3D 1 # halt build + UqiRange.append(int(UqiString[1], 16)) + + for StringValue in GlobalVarId.keys(): + StringFound =3D False + for path in StringDict.keys(): + for UniString in StringDict[path]: # path contains the path a= nd Filename of each uni file + if (StringValue =3D=3D UniString): + StringFound =3D True + break + if not StringFound: + print("ERROR: No definition for %s referred by HII question" %= (StringValue)) + ReturnVal =3D 1 # halt build + + # Require a UQI for any string in vfr/vfi files + for StringValue in GlobalVarId.keys(): + # Ignore strings defined as STRING_TOKEN(0) + if (StringValue !=3D "0"): + # Check if this string already exists in the UQI list + if (StringValue not in UqiStringList) and (StringValue not in = CreateUQI): + CreateUQI.append(StringValue) + if not Options['UpdateUQIs']: + print("ERROR: No UQI for %s referred by HII question" = % (StringValue)) + ReturnVal =3D 1 # halt build after printing all error= messages + + if (ReturnVal =3D=3D 1): + return ReturnVal + + # Update uqiFile with necessary UQIs + if Options['UpdateUQIs'] and CreateUQI: + if os.path.isdir(Options['Destname']): + DestFileName =3D Options['Destname'] + os.sep + 'UqiList.uni' + else: + DestFileName =3D Options['Destname'] + try: + Encoding =3D GetUniFileEncoding(DestFileName) + with codecs.open(DestFileName, 'r+', Encoding) as OutputFile: + PlatformUQI =3D OutputFile.read() + except IOError as e: + print("ERROR: " + e.args[1]) + if (e.args[0] =3D=3D 2): + try: + with codecs.open(DestFileName, 'w', Encoding) as Outpu= tFile: + print(DestFileName + " did not exist. Creating ne= w file.") + PlatformUQI =3D '' + except: + print("Error creating " + DestFileName + ".") + return 1 + if (e.args[1] =3D=3D "Permission denied"): + print( + "\n%s is Readonly. You must uncheck the ReadOnly atti= bute to run the -u option.\n" % DestFileName) + return 1 + + # Determines and sets the UQI number format + # TODO: there is probably a more elegant way to do this... + SyntaxL =3D SyntaxRE(PlatformUQI) + if len(SyntaxL) !=3D 0: + Syntax =3D SyntaxL[0] + + # script is reading the file in and writing it back instead of app= ending because the codecs module + # automatically adds a BOM wherever you start writing. This caused= build failure. + UqiRange.sort() + if (UqiRange =3D=3D []): + NextUqi =3D 0 + else: + NextUqi =3D UqiRange[len(UqiRange) - 1] + 1 + + for StringValue in CreateUQI: + print("%s will be assigned a new UQI value" % StringValue) + UqiRange.append(NextUqi) + # + # Lines up the UQI values in the resulting uqiFile + # + Spaces =3D " " * (BaseNumSpaces - len(StringValue)) + PlatformUQI +=3D '#string %s%s #language uqi \"%s%04x\"\r\n' %= (StringValue, Spaces, Syntax, NextUqi) + print("#string %s%s #language uqi \"%s%04X\"" % (StringValue,= Spaces, Syntax, NextUqi)) + NextUqi +=3D 1 + + with codecs.open(DestFileName, 'r+', Encoding) as OutputFile: + OutputFile.seek(0) + OutputFile.write(PlatformUQI) + + return 0 + + +# ********************************************************************** +# description: Parses each uni file to collect dictionary of strings +# Removes additional languages and overwrites current uni fil= es +# if -l option was specified +# +# arguments: path - directory location of file including file name +# Filename - name of file to be modified +# +# returns: error string if failure occurred; +# none if completed sucessfully +# +# the following are global so that parsefile is quicker + +FindUniString =3D re.compile( + '^#string[ \t]+([A-Z_0-9]+)(?:[ \t\r\n]+#language[ \t]+[a-zA-Z-]{2,5}[= \t\r\n]+".*"[ \t]*[\r]?[\n]?)*', + re.M).findall + +OtherLang =3D re.compile( + '^#string[ \t]+[A-Z_0-9]+(?:[ \t\r\n]+#language[ \t]+[a-zA-Z-]{2,5}[ \= t\r\n]+".*"[ \t]*[\r]?[\n]?)*', re.M).findall +EachLang =3D re.compile('[ \t\r\n]+#language[ \t]+([a-zA-Z-]{2,5})[ \t\r\n= ]+".*"[ \t]*[\r]?[\n]?').findall + +UqiStrings =3D re.compile('^#string[ \t]+[A-Z_0-9]+[ \t]+#language[ \t]+uq= i[ \t\r\n]+".*"[ \t]*[\r]?[\n]?', re.M) + + +def parsefile(path, Filename): + global Options, StringDict, AllUqis, UqiList, FindUniString, OtherLang= , EachLang, UqiStrings + + FullPath =3D path + os.sep + Filename + + try: + UniEncoding =3D GetUniFileEncoding(FullPath) + with codecs.open(FullPath, 'r', UniEncoding) as UniFile: + Databuffer =3D UniFile.read() + except: + print("Error opening " + FullPath + " for reading.") + return + WriteFile =3D False + + if os.path.isdir(Options['Destname']): + DestFileName =3D Options['Destname'] + os.sep + 'UqiList.uni' + else: + DestFileName =3D Options['Destname'] + + if Options['LangOption']: + try: + UqiEncoding =3D GetUniFileEncoding(DestFileName) + with codecs.open(DestFileName, 'r+', UqiEncoding) as OutputFil= e: + PlatformUQI =3D OutputFile.read() + except IOError as e: + print("ERROR: " + e.args[1]) + if (e.args[0] =3D=3D 2): + try: + with codecs.open(DestFileName, 'w', UqiEncoding) as Ou= tputFile: + print(DestFileName + " did not exist. Creating ne= w file.") + PlatformUQI =3D '' + except: + print("Error creating " + DestFileName + ".") + return + else: + print("Error opening " + DestFileName + " for appending.") + return + + if (Filename !=3D DestFileName.split(os.sep)[-1]): + Uqis =3D re.findall(UqiStrings, Databuffer) + if Uqis: + for Uqi in Uqis: + PlatformUQI +=3D Uqi + with codecs.open(DestFileName, 'r+', UqiEncoding) as Outpu= tFile: + OutputFile.seek(0) + OutputFile.write(PlatformUQI) + Databuffer =3D re.sub(UqiStrings, '', Databuffer) + if Uqis: + WriteFile =3D True + print("Deleted uqis from %s" % FullPath) + stringlist =3D OtherLang(Databuffer) + for stringfound in stringlist: + ThisString =3D EachLang(stringfound) + for LanguageFound in ThisString: + if ((LanguageFound !=3D 'en') and (LanguageFound !=3D = 'en-US') and (LanguageFound !=3D 'eng') and ( + LanguageFound !=3D 'uqi')): + Databuffer =3D re.sub(re.escape(stringfound), '', = Databuffer) + WriteFile =3D True + print("Deleted %s from %s" % (LanguageFound, FullP= ath)) + if (Filename !=3D DestFileName.split(os.sep)[-1]): + # adding strings to dictionary + StringDict[r'%s' % FullPath] =3D FindUniString(Databuffer) + # adding UQIs to dictionary + AllUqis[r'%s' % FullPath] =3D UqiList(Databuffer) + + if WriteFile: + try: + with codecs.open(FullPath, 'w', UniEncoding) as UniFile: + UniFile.write(Databuffer) + except: + print("Error opening " + FullPath + " for writing.") + return + + +# ********************************************************************** +# description: Searches tree for uni files +# Calls parsefile to collect dictionary of strings in each un= i file +# Calls searchVfiFile for each vfi or vfr file found +# +# arguments: argument list is built by os.path.walk function call +# arg - None +# dirname - directory location of files +# names - specific files to search in directory +# +# returns: none +# +def processUni(args, dirname, names): + global Options + # Remove excludedDirectory + if Options['ExcludeOption']: + for EachExDir in Options['ExPathList']: + for dir in names: + if os.path.join(dirname, dir) =3D=3D EachExDir: + names.remove(dir) + + for entry in names: + FullPath =3D dirname + os.sep + entry + if fnmatch.fnmatch(FullPath, '*.uni'): + parsefile(dirname, entry) + if fnmatch.fnmatch(FullPath, '*.vf*'): + searchVfiFile(FullPath) + if fnmatch.fnmatch(FullPath, '*.sd'): + searchVfiFile(FullPath) + if fnmatch.fnmatch(FullPath, '*.sdi'): + searchVfiFile(FullPath) + if fnmatch.fnmatch(FullPath, '*.hfr'): + searchVfiFile(FullPath) + return + + +# ********************************************************************** +# description: Compose a dictionary of all strings that may need UQIs assi= gned +# to them and key is the string +# +# arguments: Filename - name of file to search for strings +# +# returns: none +# + +# separate regexes for readability +StringGroups =3D re.compile( + '^[ \t]*(?:oneof|numeric|checkbox|orderedlist)[ \t]+varid.+?(?:endoneo= f|endnumeric|endcheckbox|endorderedlist);', + re.DOTALL | re.M).findall +StringVarIds =3D re.compile( + '[ \t]*(?:oneof|numeric|checkbox|orderedlist)[ \t]+varid[ \t]*=3D[ \t]= *([a-zA-Z_0-9]+\.[a-zA-Z_0-9]+)').findall +StringTokens =3D re.compile('prompt[ \t]*=3D[ \t]*STRING_TOKEN[ \t]*\(([a-= zA-Z_0-9]+)\)').findall + + +def searchVfiFile(Filename): + global Options, GlobalVarId, StringGroups, StringVarIds, StringTokens,= QuestionError + try: + with open(Filename, 'r') as VfiFile: + Databuffer =3D VfiFile.read() + + # Finds specified lines in file + VfiStringGroup =3D StringGroups(Databuffer) + + # Searches for prompts within specified lines + for EachGroup in VfiStringGroup: + for EachString in StringTokens(EachGroup): + # Ignore strings defined as STRING_TOKEN(0), STRING_TOKEN(= STR_EMPTY) or STRING_TOKEN(STR_NULL) + if (EachString !=3D "0") and (EachString !=3D "STR_EMPTY")= and (EachString !=3D "STR_NULL"): + if EachString not in GlobalVarId: + GlobalVarId[EachString] =3D StringVarIds(EachGroup) + else: + if (GlobalVarId[EachString][0] !=3D StringVarIds(E= achGroup)[0]): + if Options['QuestionOption']: + if Options['QuestionOption'] =3D=3D "e": + QuestionError =3D True + print("ERROR:"), + if Options['QuestionOption'] =3D=3D "w": + print("WARNING:"), + print("%s referred by different HII questi= ons(%s and %s)" % ( + EachString, GlobalVarId[EachString][0]= , StringVarIds(EachGroup)[0])) + except: + print("Error opening file at %s for reading." % Filename) + + +if __name__ =3D=3D '__main__': + sys.exit(main()) --=20 2.14.1.windows.1 -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#42807): https://edk2.groups.io/g/devel/message/42807 Mute This Topic: https://groups.io/mt/32202189/1787277 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org] -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D- --_000_FAD0D7E0AE0FA54D987F6E72435CAFD50AF848FCSHSMSX101ccrcor_ Content-Disposition: attachment; filename="winmail.dat" Content-Transfer-Encoding: base64 Content-Type: application/ms-tnef; name="winmail.dat" eJ8+Ist7AQaQCAAEAAAAAAABAAEAAQeQBgAIAAAA5AQAAAAAAADoAAEJgAEAIQAAADgyOTg4QThF RTI2MEM2NDVCNjg1OERDMTMxMkE5N0RFADUHAQ2ABAACAAAAAgACAAEFgAMADgAAAOMHBgAZAAsA BAAYAAIAMgEBIIADAA4AAADjBwYAGQALAAQAGAACADIBAQiABwAYAAAASVBNLk1pY3Jvc29mdCBN YWlsLk5vdGUAMQgBBIABAF0AAABbZWRrMi1wbGF0Zm9ybSBwYXRjaCBWM10gUGxhdGZvcm0vSW50 ZWw6QWRkIFVuaVRvb2wgaW50byBlZGsyLXBsYXRmb3Jtcy9QbGF0Zm9ybS9JbnRlbC9Ub29scwCK IQELgAEAIQAAADgyOTg4QThFRTI2MEM2NDVCNjg1OERDMTMxMkE5N0RFADUHAQOQBgCQMQAANAAA AAIBfwABAAAASAAAADxGQUQwRDdFMEFFMEZBNTREOTg3RjZFNzI0MzVDQUZENTBBRjg0OEZDQFNI U01TWDEwMS5jY3IuY29ycC5pbnRlbC5jb20+AAsAHw4BAAAAAgEJEAEAAABIJwAARCcAAEJxAABM WkZ119CQNWEACmZiaWQEAABjY8BwZzEyNTIA/gND8HRleHQB9wKkA+MCAARjaArAc2V0MCDvB20C gwBQEU0yCoAGtAKAln0KgAjIOwliMTkOwL8JwxZyCjIWcQKAFWIqCbBzCfAEkGF0BbIOUANgc6Jv AYAgRXgRwW4YMF0GUnYEkBe2AhByAMB0fQhQbhoxECAFwAWgG2RkmiADUiAQIheyXHYIkOR3awuA ZDUdUwTwB0ANF3AwCnEX8mJrbWsGcwGQACAgQk1fQuBFR0lOfQr8AfEL8REfsFo6aAJAcHM6wC8v YnVnegMQC2AkLnQHMG5vBaFlLgEFsGcvc2hvd19BIlEuY2dpPw3QPagxODUd4GwLgGUKgaUlFFUD AFRvBvAgBADGIAIgGeBweXQj0AOgGwT1GJAgGDUZ4FVRSSwgKCXhGjFzB0AgUdsKUB9gaQIgJQVJ AQACMOMGkAiRKSB1AwAFoAEAPScwdAUQGcAccAWxSEkFKJBxKXUgUFJPTfRQVCuFLihiJQUrlR5Q 9QOgYhngdRIAHGAnsQ3QUSpzeSBlANBoLDsuuSUFVGgrcQUDBCBmKxDeYyzDA/AioCcweTMAKwDu cChjAQELgGkpogQgA/A/JvArAhxwAxAHkCUFYmErL5InEXYqsC83IHIv8mg3cXNkN9E1sAuAHMD/ MjEroAngMaYxtyZhBPUvFR5yKxA4QgbgNWFQeTIuIABwHGA74DM5DUNjmDogTAdwK8JHYSfApjwl ED3iLmc+QEALgC0QIGwkQANwPj0ZQm9YYiBGCfAr4DwG4GL5JEAuZkDRPy89cwcQHGBmQgiQI8Bl dRowAyA8bQsRLg3AQ4ZAJREKwG/zI3I/ykxlBpA9sR3AI9DubBywPnBGgS4lEUbzRQ+PPWQY0RHQ Q+FEIEsLgNsYUDBwPD3gScMuREAdoSdKcUHPFMBpZxhQZC2hGTBmLWJ5PaBaOgCQanUuRgORPHpN 4s54QXAAcEvPCi1QQCUFBEFkHGBSRUFETf5FItAQQCeiMmUlDC0AC2BTADAa8S9JQeIvJhJzCi8l 5S9RKXwgIDT4NiArVkBS71P/VQEl5cYuJtBV0TQ5OVYyWf8/WspWdjwANdMb0BmjZCzwIDU0NThB EgAAIDTy+CgrKVZ2BQAwkChBBGLiIB6RNjQ0Vu9Ub1Fkf15/X49gn1h9JQwN4AEgIOdQQCRgBUBh L2RPYT9RdP5iaF9pb2I7GFAH4DXSY3o3JQUdsRAwIB6gb/YuLgAzZTU5YTZjNyY1AOBP5yAvAQB2 L/xudSKgJQVWQWrfa+9iHSRAQGfQMCwSMCsxvixWEXZAcpYmEDQBZCgyvziCWMYQMC9hIuAlEHRm pv4rUEF6XHnXBdAvgAVAEdDHGjAmxjwiY3hfA1AJ4P56GeBdoQGQIqAcUTVDOIL9AQFhcmAFQAkA HlAxanvR/kEBgBuxBGEwUnhqWUEE9f82oDRxAJAJcV1AC2Ay8X7E9zshNNAkQG0cYAT0e2gHwv8o 8izSJ+YcYDhReKlylnKW/kgj4CeiL4F4lnnfeu4yIr0vgGEZ0RkwOHQmJDpyllVYyVtNgF2PMXWP cmy1j3J4j3Joj3IcYCcDYB07oESDYTMQBbB5MSfbkL+RwTKSD5GyM5NgcJCLLeCPQHEwgHx3XXKW J1XwlieRHDAnkQB1ceRpRjXhJ3yX5pFnl8C0WycQMGMKQAEAZJFs35nPk0SbP5SliF5GMv80Dz81 Hy9ANq83vzjKiMdSZecsgINiQxFndQeAAjCN+P+WtQ3gl0iWJAqwfrInwJEi/6CwkXanKZifqOso cjXSKLD3mAA9wB9gLisRXmarCai295YlrSaIXk8FMKEypxmQkHuvrJYkUyPRpFEmYTIwbF5wsolA oLYvVfBCpiBs/6KxshM7EBIQCHAGMQSQA2D/BcBGkQBwMHBt4ihyGFAJgP8EIKKAAJBNACvBla+7 7aJ/vaOJdzIwA6AjEGfQdbe2/ToScAWQKqILMLKYv0DBX/1V8ENjJLmFBCAm8BiAoLCubweRIxBo IWxjIWQwcf54rfGkIpf1K/K6/8ctuULvK5UJcKYSK8JhKGO8/6OIw8Y/zI1OT1RFPaCvGB8vEcQy OvLEoQIgbHkhr7KJAyDQj1XwTBmxdYzizwEAHnAsw7fFKGsJ4CIA/yaBz5AZYBnAJRAjwDwTl/H/ rmnVr5YjBGAaMLpBn9HDRP8nwJfn1R/Mr82yJeFcZs6vf8+6b9Ddv1XwGXGaApENJ+YvnK+R4SAm 2M/izd9//+CPk1CU4hyDKHLUQWrBt3L34d/Z382yQ9vXOIIpIAeAf4LykSuyiRxg7N9V8FDibb9/ YQUgbkGpu0NxJ6JwA2B+YweQNhay8pVA8V9V8FCbK7EFQHcKwLqyKHcq8OcFsbg0uKQoZSrwRpFn kv8EkCpxLDvwifcPx+QJcLgh//UxK7PqwyuVGJDTYCnWpWn78/i5BmSgUCUQf8EcUShy+yuVBbF2 B0AKUIdj5ra45P+/AaDIcpYsAsg7+NIcUU2Q/yw8vrO2AQWxvzG/34ipzZT/sgXuQecCK3HAFi9B GuEmsf8KwOrRG6HwhwWnO6AOULD3U4r/e2giU2/yIiuBef9uQZ+zL1ZGkcWGugQnsS9R723iYxRE QPZ4VYnR6oeX8f8NNQ55JmHE8xBxQLAYoRzg++rQ7qEs9nhGkTiCxPStscPFs4nCIlxceAzpXUD/ FxgNqjGmZ59zr2WfSpBzTx8bz2ZMbe9u/3AJZGYy+mJj0DNxALD2cc9y3x3/zx8Pdg5ZoXcoIyN3 ELDKfypQhtagc62g8yCGUtdwINpRAwZJMBTvsHLzgISBH0+QY6H9lQECArtQUk/4TVBU/YWVAP1Z OrUOI9vvgCrIaS2ExNFhhAH1qlWAJyMqyEO3wHmCsGcSaBqQKGPzgDIwMc45XUAmYzYhcnABEH/U /0MQn9E2c+sh10ACIIZQEIjBNYlTUERYLT3A8FANXbEtLXhAYVNELTLsLUODwTuxUGNBpuA57/0l BmkUkAEQGpCksD7toAD79mc/ZW9BDtIg74CFWD9lxS5iY0EOZm5tY0Bc0Hs+7X+gZxpwutg/ZQig Z/8IkRlQiF4rQX+gyeCNwP4w+YKwYWIgsUSBJeA4JPZn9SzmRbjDPRQQ13BI2K21L00hpLCEwOZw cK1yJ14SI/2UWyAXIHRdKwAoW0EtWl8wLeo5UFApUAUjJeDR5FAFT5fxUAMXIMYgXG5QUCKgKD86 W3gXIFOVkMJ7dtAyXH0pUHBQ0ShhLWZQkEZUMTQs8jVUkSInFNlWz1bwTsH4TSkuoOF4ECToOKEW Ud/XUE0wVEBUkCUGU/2jkWDrn1BZzUdKI1YIoC1wWc33BoZZ3CrIVoZlCNDwcNIRaSUGX1/wIWdh UE0hJ/94pdi3YVCDMYKjBrFhtCtr/y4/L08wWmKJZUA2VWG0Nj+HN084XzlmPEJSPmKJe4ZVYbQl 11Bf5m8Al8Al19NAYVZqsCci8DGRANUHfl8RQNISTTAMwKAQfiB4/wZgb6GPP5BPkV+Sb3XvlIj/ U1GVJg0gFyBWP1YAlu+X//+ZD5offe+cP51NU2B5r1bzr2/xQgCPAKlBLsniburRKihA4S5IcXZU 0F0p/XEoRU8hRyByEocQU1FTUf+fL6A/oU+iX6NvpHeHUYLe/6X/pwrjTKgfqS+qPuNIfM//rF+t b5Pvrv+wDwU9jin7j//8n/2v/r//z1aKAQ8CHwMv/wQ/m5/NOgaPB58Ir3pohyG9Xt8qrl+vb7B/ sY8qKsj7YzmpIEcI4IpHnWCIoCDw/7rJNYlIcY91VvHFsoTSGkD7uXDq0m8SwYpltX8qUPPUy7cj VvB1JgAtOKYiuyL8MTa5L6GBtBIcocWy1BB1tRQot3Ypj9s1h7+zICZECOFbMUJ5wtFPcuuhgKNQ Tatga8lw3BJHQd/zIrhiIJJ40MDRZj0g7nD5lsNURrtQvz9Wdr4GYdL/uyPYt8TPwEiOsNyAmyvI f/0xcHmP34oDaaD6QL6HarD1IOI9dOBicRDrAr2Vy/9fVvITEiZgTSG9lS7cYij7mEuA4WVDUM+f 89W+Bsd/71bx9NG+gyZgLhPAq2A5IOeKAmowRHMuQjBQcbDEgHG78F9CRfOD1v/YD1/+TNkwvxzG H7vDx2/UT9Vf20lppCB3Y3ArwWaHtoQWqfLwbGu8qlfjsShiQP5warCHomqwj0G/HMvP6VL/6tFZ soQRoKLvMeTi0k9DUL/jEvRD5u/z1M+qh6Ioj0H/arDk8+fzmEujMufyoCLn88/rH++T6FOEQ2pv oDDk5P/n8phP1rGEFqCw6OLzA/Bv/1by5KPn8uUq4H+tz/pv+3///I+yD7MfPXBIwYrQTvFF0P8i EBmitpid8YsxqVQqyFb7/kNYoYrQ4pdhcQpwYIAcofsPc47gbA5QGgCH06tgpMD/uHExZAI/A0uT IBXAo1Dilv0BcWNlQMGwn2KS0RlBo1D/qkgB3rYPtxSFYrfxZUByce+gMAFxiFGgMHC7IOKAkwD+ bQAmoJEgUAc/EZkkUKnD3w6lkn+TjBFvEnRhpMATFrsB1RpAaGqwc3AYMWwYMf+mFCJAqueM4Aw/ uj2hUBDJ/7yqRdDygb8dHtLJlA90AP//S5oVokoVWVVqsE4larBMW+/1ykijTRJIdi6PRv/D6QD9 YXI9cCf1zykPKcljOSdh/2M8KHBoiyf/Lo8vWgrQceH/J2Bxwy2PMi8y+tNAhqInYN+GhDEvNe82 ug6hZp5xaSD+aABypFDOgYrAYmBuMHEY4yTYhVBkZF+2pk9gPXGuaFYQuBGPUHPOgCtWH/M9f4zw bHDOgIziE5OjMj+KlAVSSvAOoW4xnrEuSf9EQsQAqsIUWKYitKOEQzlf8TpvJy12O8FFcEWAX/T3 O8EKACtDJ0YIbjUnYW43BzyPPZ8+oiJzaG939QSSZ6txJ4rQbjWhQI9w36rgo1KLMIDgiZAiQ29E f7+mAEWkBYNGeWawqxFfZaDXpTA7wWMxdM6AQgWSXeT/PH9UX0sEUqMLtbpGnWwAwP9LkIkCAMB+ YCBhYIBp4MLT/4KPWn96hor/jAmls6FRpmHfVjWmmk3PTt9FcHVFpIjQWmSeoGFQz1HbVWJyZd+J AW/AU29Uf0sTQ9Hx4mHvV/VAAThQFEFvisGhUB/B/mzR8qTATYGgwaAxfCXvQr9Zf2xveoaje47S n2JhiPP/XB+MCGtvcm96hqjkmYiekB/CwBQxqhHR8l7AbHkhV1+/YM9FcGxFpGyeAGf7Ys9R20x4 8VL/fE9KqnsCPnUw8aFxlXDi1FZEKGv9jUBwCOF18d0BIfCgsM0w+xBilSEpcW+Cf3OGzkBIYP8P FGh0E/CZh/LhqPMFEoqU73UPdh93L0VSeEWk0xGf8PehgHkvUdtFitR7T46PSqpPjRWZcBQSlZgv J4rFZPWVlzF04CaBf5RPc4aQ7/GR/zInLpggD8SgZwWDf4gfiS9FcFCyRdEUYUZ5YW5wzWFQsquR YZ/QOLFG4knbwERJUlIXljHn8veN76C/VXRBTyCpscQQs6D/tMEUG6qgQAMEpZmfmq9FcH5xRaRu 0COkUhcjhjvBY4dLcDgQhrA9Wyd3RaLddNBdn8+g31WDUG5hwTB346DsYL5Sd9lD34VXIyhn9ZBX cl2gZmZXICaBIPxISW+Qp2akoJNfsa9tlv8ZoW6wr1FuYzDguEFuRZaA/X/AbqS8jYHoMqWVJdM6 0v5z0jzDwCOwvtLdkt43uCOnn1QjItFBW13Fm3iWMfe6b9Z1tgIuO4L1v7n5TwDFnTMovYdbMF3z T7hpncBYMbrru37AWDI6vI//vZKfRb4fvypqIEDgwCafRf/BGyOGjYTCpiOGuvyNOcKm/40bsNpS qcKmUqmw2mSowqb/ZKiw2nsIwqZ7CLr8O4K8D7/GLbu3x9/mv7IVP4JFCgC+aLuzanLbL7FP3kZS lgS/anK5+d/v5Bra8t6XPbqwn+G541/n3ieQrNEoIrCw4SPwUlJPUhuwlv9e4/+0Im/AFAOWN+lR pLztn+3R/1ak7O/v+djIv9bhuu+w9IH9JUBwLIHeh+zfisE0AONP/+iZMMT0L+6v2ish9Y2EISuz +wW8QVx7nzC4dScbsP+4ZlAhufn9groIUCHYyP2B/9jIUCFSqhuwUqmqT+/1ZJr/hiFkt1Ahewkb sHsI//ONOn8A0o05A18EZsus/YHLrFy+faTb6PSYsoaCG7AlsIF+JbhX96veRp9F4m8KHVf9aaBr 8hOfRSNgpEWGQYXB/xwh96vG4SOLEv/u71dy8wH/QwMjMJxBygIg8qmg/QfBAP8X/7hidKIRw9Hj G27y2mSQ+SMELnWGULkrfsAlQBx//x2PHp8Zbxp0aiO3cCNKrtCfgPInO2lTaoGY8ihBhID3IHHz EH/AebdxKtAiP9zPvcQmbjfAXaBvIbqwR64Av4ZBdKItlic8LF/v8XdNoP+A0C2xlmDzEH9AnVAn O1Ah/nI8YS2HKAGGwFJCdKIsT98izlXQr0K8QTSmLoeCt4zx9PggSU8Xo+uCNR/omv/phbCQNaA4 MLdCw2E4rxjP+zWnKgVbI0ofkLqwIHUnN683NBYuUqk671KuA1b60d+6sFfxIHE4n1dyKEY5urDm MSr/6JknUIagb8GGcaZ4mKPpgyhzKAFihCE/h0FrMWsQpENYkFkxLie/R28h/+htFeCYo/Fwc8ux f36haACcsFiQ7J/4//mLI7wgKlX/Vw9YH1kvKlVY/WRBY25gRNRG5THBhICWYduEUZjkb8bwhHJj riCvcv2AQXVv0nRxcDD60FIiaoG/JqBrEG5QTfBVWO/7SPrQ55klruJuAmR1osCpUGTRv2/wbkWt wV7TanKYti5f/+Xv9EnG8C11fzatEOsh7TJQY8bgo9BkqQGHgdQAt4bAW8GEpWgmkH6gb2hy//KA ZP/v9PrQh4Lq8CbTEgL/dHXeM2JCtGWt8Kdg9AAt4t9cwJixZOhVWImmcw9A7/B/hxBQAG7/VdCt 9HCj7/Awc4ARtFB1Y6Ryag/v9DH/c0KuY2aAX2HrEqkwooBjEP+G4F5BX2NhtKQRaUCikHE/aU/W U3mKAGFL8LqwImZT6UB5i1JFvEGt8C5ZLbBtcIWBm6AjtGRbAbChdF0rW0EtWjBfMC05fWF9JCNs fQaRdfdxfRVekX0TsLByQ+lRfWAiKFt4sLBTIbrgezEsMg1wKS4WKpvhe+FEhfBBTEz/gYBnwJ1g hHFUX0/lWvDG8G9G9k/L+pyb8Ef6o0aQcv5JZ/EqBZvwefSIVnuQ0wu9QiBS1lG40rrcIHFTtHN7 2S/v8kNoM0wSur5GOjDr0GtLkU5wUFO24KRxjjH8NDfv8FXQ91BjAaQRT+L/bHAgIF9yTBJe3K3w UhCikV9jcWyF+V/v8VXQTPJwa//eM2KGZvJfRV2laNQoIlw1/Zi3copzXTHVThESJoISAl8qDE/P ERKLx5zpWyaCXf9wsVXQnKMx0HoRT/BpEk2R/5yyKCJssrijXTE4YOVwbHAvIQAO80/flrNDrFBj a380gJdaYwdfI2RmpI/eFVT+ZXwwn4iLy54PR/mp+UlR/4vHwOGrr1BfPIdMErRlD2BjZyPrkHNp Z69AYxBt/01yaTEpkHNQ9VAoEzGAXXLfdwMx0BewYpBm4iEPgq5K/05/sB88gMIgfGDUAHNBmxH/ c6Bds7QhXTFfcmM1KCI/MOe1cF9R8nBsLuzPvTtGOv91YKWxeCJhpJWvvTmLy/Gm/7Y/wD+lX6Zs k3yn/+R8PFLnrkg9wAGQMTZMwKqkimOPrw+wX7FnY9QlMDRL8P+yn7OvtL+1xcnvzA/VD9Yf/9cv 2D/ZT3ScyxC8f7hPuV3/BpC6aEwSu3x34mgkXVEFIP9yoODCXtO8X72vvr+/ycD9/4pj8abS/9sr EF6L9EaRZATvhxmdn9oli/RGdqAoMY5A/kb60F5Qnh+ceYv0EZBcgP/sH6kNFZGfiPCIoJ+hr6K/ /6PP4n9IVOp6rhLzNvGP/Jtd7ZxUtXD4f/96YmuRa/9Hf4ShKVHtmfvfzU8V0Vrh/4KRMZBm8hES smE/MDdhPzDlYxBibWBISQ7gbhALpP8Pgvo621/jX+Rv6R/FgUYwH24S0PFkU20NEhF2ZnL+Lw9g +CSbn+p/648JHnRwfknP0E1yp0YFU2MBz2FTAFRSSU5HX1RPYEtFTigwCQ/6DiH9emEwUxATT8VZ JhJ2NLsV/2uKXyfH//lfEVgpRYvLKAX/H58pkI2nGW//d42nwoYISocj7wFshoVbJ1VwgsD5jeNz J67/3C8Eiw4GBk+/B18IbyrvCg8LHV1QZuEh/zwkG/TRgHW0QbBz0H7xECf/Q41IrzCuPzSPODY/ GpMptP9sd9FQ9WEw0HOycCBtYJtufzdxKS8qMiGjjac4rzdxb5udgPZiLsJQThByKD8n/91gsfD3 MipQOJ8xU0QCbLL+TvdCjkBDf0BRMRBCkdHQv5MQMRApoIpAwkP4ASdOj79Pn0WfRq9ENUsOX7B5 Sv/1MVJF0NBvQzCt4ocAj1D38yFsslDmKExKMJ8xUj0Df1EBXHCdgGbAwsBSu8rgJzxyK4GxUOYh kZGQT3W8dHBYAGyyT58xVlB+oNp0EPFtjgRX+C5rkhMgnUm/eJFwhpBmUE9FdcP/z2FYjyv/BQC1 8FkQ5wFwMH+ggMrAXA83NmC1ttCuAjK/RK8xV09vZk1U31Xtd1b//1gPZg9fHExLa4C18FEgpyD7 AgIcsy6Nha3ShMH4M+H/+3GfWh0nSa9cnGsPbF8skf9ds+CTrdJgcm1+cJ96/Dm1njF6T2JsysCu AiJQNVD+bcJQz7AF0YSA8zDG4Blfh3begC+CuSJcXG6yYU8bkTewHHC1wGx5b1FZ/+4A0BDRwCkA +AD30BsC3vLthGJPhLE1AHS1oOUAuXF3koG7098BLYUwaBC1oi7/g9G18lLPgk9738Q/xYHdYH80 QX8QikCZdNHQHPHe9m59kSBiNFFaYuDAjJ+tUE/8RE8FAN7xFLEbkTSAEkHuYobC0BTdcWea4CkA z1BvLqCHgYSQG2MulLDsn3kZ9bF4TDLxleRSRSj/WimKDzdx9xH6MJX0IZAZAe4wgB+VqJZXTLbB jB8aV/pz4JBpXVEbkVuyrdLe8v/4QhHi0RMhEDTDBaDk8PfA/xshR0DG0Bxw93LClK3Sj6D/0aTe 8mek0BGmkPhvxYHRsJ+HgJARptE1IIbRZGSOQbAgQk9N0UCR0nY0UX55hSGFcD2wk7GgZG9QVO8b gtGjLnEz02b10M7AW7D7lN/ml3MtoN0wl883cuaW7X6SW0SfgrROHLBRsOagvTLxMFwfSs+vT+aW W5kC++aWIZAtMxBIEowP8moRPf9Az17P3TEt0dFTj6DPiA3g/3ASzmfSsiZ/5a/mvK+1va//GoPA fxqTIVCOIsZw3ufHWPve8luwc6QANMM8hcHPwY+//Tb2YD1xMvHSsNKwKjeQ/kLPYEywj4DItbUA mQMv7+uKbVoqK3OBIxvFLdAt0bYjctBSkHU18TxyIIPQYbpxJTA0eM+xg9By/YPRJy+d6JDItNHR lfPokLu//7lvIs4PzxjPp1jPsf8vjtHf0ufTb696zcG1f2bff2fvVn9p/7jPgrJbCaLwZe5rFt/i j6BSZZcP3J58BfewV+ceGsAq6n/rj+yf7a++KunYFWCeNC9RBQBQPbD/ovBeEffKh3L1kDUgGwAp AP9RIPRwL1E9sveBFOXp2DGd/5MApzCOQaZABaM4Ic7mjkT99TFy5fOo0TKQW7CTofEG+/OPHugt NTCIZJPBG6EmIP5jN3B/wenX6dhg0Y+A+AH+cwUALCFC0bTxQzHyAS2gvy6gEjCiwAWz8vGflWO3 wP9RI5+TR8L434K0PLJHwrTx/wLj/4aHgbsBo9H7n+oFfAR//ZOxQV2zDsb/kanUQoBj+ffDZDsB P4K0FJDkMDdiuaNgbXCfsDRAjoF1PXJ+ZqQAkrAFDxrAn1Px0W/33dA04pMhZxIzG7CUUjxA//3R 8GGflODQLwDyUBLg0JD/5y08sKJAUcEg9DLxqhELAlNwYYH7J17VJVvPoXQAXSsoW0EtWl8IMC05 FSApKD86HxTT0HQVIM7XFNVbYS0KehVhLU5QezIsNUxcfRY7egAqIhTUKjJb0HFdPxqBFtA/KYYq 3/DnnC5NKS74gNeiQDUR5x5PkcJMq4ESr/8TvxTGFVgWDxcfGC8ZPxpPNxtRHD/kM0XwwR5vKCfv Ih8jKhVAI/0pJO8l/ycA/icnv+QGq0G3RMkCKXogX/8VdhTVIu48gS0vLj8vQSdV3+jf5BV/kKHg D5coQsLgAL8Cpq5cDqVNNdihB3NE8lH94ABButCrQdkRSPU7IhH5/+AAHhfgASjVPtMxNecfAnL/ DBHwQN3xc4D940g5AqZCb/9lL76Vf7BQ71H7Q6bj/93f/0Om36Lf8UhZ4KNRxUdPgrLGRLhgkpB1 ZmaPsbNx/0l0W691L3b/XcLekniWQ6e/bkK3Ap7Veh+Ldk96V+YC+1GySQFGKBCi8EXfrQREwS39 4i5vIP5RKD0VWyf7bXI7gieuT1CRbYpzgF1f615gRJonPvUu8QFzu7Fv/19vYH9eQlsPrQRmVykC PRT/XmBkX0cfvohIj0mbiU+KXv9L/97vTcJs6OCvb8/Mr2Yij+L4Ug+xFFMzIElPVQSH+yF031RL UlJPUvAg/1Xiv2H9IF2wtSB4X6z2fQX7nHCt8jJej4K3a191fXD//XINd3MfdC+CL3s/ZWtWsv9t QLugCoCg4NugYtJ1cLgzvw4iu+L4glevjh92bSdjS/+OSlMvks9UjJ5AjCVV4on+/40fjkxY35E4 ZC+TT1T/lX9/VtSiB5afl+9nn6zYAqch37AwZWpE4Asw5hAoRMQtEP4tfXGab761MZUvla1iMTX/ 4ABQuJ+PrPU+kqXvtorb0v/Eoas/jk/NHLORrg+DP4RP/3Kvhl+Hb67M4u+pv7dv5Z/f5q1P/1EG KXHFMGKoS5Agn6j/qg+rH67MWfpUcsuAv8LPm4xlYAtENZG1kGZVIP5t1ZHXokpvrsoHZMlQP0D/ ZiIeJrNgwG/Fu60CB2StAD/xAIsgrYHKiMK/rsZUaPdi0BJHKMYozinMn6yKKQK9K1JGzpbRSM+/ fi4o1Sx9o8EnnwAvYPbD2O/Z8i3sVVPaP9mJZ9wm1u/ff/uuwNyvJzWRL2Cl3+Mvvh//vyEpce+B ntC78NKqv//iT//DX8Rv6C/GD8cYyFDH+tyc/zshyMbn66L/pA+lH+sr/PH+ZG1DB2S1kAQhbUD+ kD1Bb/0QDEeuxj2oW02wyFAn/8iJf6E/fMvv60X1J7yR9h//6xg+Zfiv+bI+9frPZ99Z6P/0H4Gv sf9Mn4WUTg9PH67L/1GGu7QA/3jOCa+bv1VvVnrfu7KfP6EPAcz8QCoVfxaP+xefGK8qFNiy0OXA MtA9I/V8oFOU4HIo4OWwBKG44L+s82MRjMNCV/xArstDKBH/tZA6R/0SsrCoILLgi2D9WP9EsNiw 9bbWAZTgKOAcVxz/9x4KmkAbYlan0FpDrQIho/52p9BEsM4AJbDOAB8TzoP3FNcU2H0hdTugxvDC oK7BfSg2IMrjp1FQ8HfAi2Bi6yChXIV3L9BrjMDZkP1zH7KgL9ki/X0hrsMtIE7/PVAib67FXRHx oy4wXREf0f+tECCgPLDl0CuzIMEcr67J/zuCtZAuEvLwsuB+4D4QMfT//RIkRK1yMKcnD/xAmIQo s/8zoS5qDjay0NiwD3AxUOWw+nNJYSh9Ij9gMAU/YDPDc/QMytBvYh5wZkb7jFIwZW1vdlpwDcFs dd2y0GT4YDZNaI5FP1Rpz/+sitISQdA/wa1yQUnwAmLC/0KvrKhdEa1yM8NGP35pXHb0am9IsCg7 V10RtWB/wf9EF0iPMw938T7ySwIBn6zG/yiBIJFID67GEQd/0DAGYdnnUROh/zHhbm2v4NIwp8D7 VnQHOipjA/QPDyQe1ksI31ETDL9WP1dNJbAqWG/KJZ8kWgc3W19cb1dNc2Ren/9fr2C/Yc9i32Pp WF9ln2av92e/aM9XmGjIAGqva79sz/8TDxQfFS93D3gfeS8Zbxp72kPuMHBKQOpwYSANHnH19aho r+AgbtAgoHEQx4Hj/NO1gHNpZ3EQJviuzM/9Icth7kDcUmtlUVF+Qv/qcM4kOP8oHfFnLjDxozHV /zVJzegySDdPOF+DyHW484HfIICv4OpwlNDZUHgboa0Ce5TRqABiuJC70FUn0YRH+RAAdXCnZbKw fEBaMnDqACdeWyBcXHRdMCooPzouYSDAfG4TKGHOQGN8G4Fja2K4b3h8rRCy0JTQZMriE/OwkSMr diCAaWQu/Cs/kYGfAZG0nwGSFp8BN5KXlQKTOTvnEHOcLkSAT1RBTEwgfKeSLE0pp8aOHFYggElk f49PkFmRH5Ivkz+URJ1lPQGdZlthLXpBLVqwXzAtOZQwnYAuofvOKdwgmf/6c1RvgkCJoD2bvCc6 QXxA+JChDFNUAFJJTkdfVE9L/EVOnWWdgNjQovydgKPPf3UfOfNyLfF1PD89Se+gR/89I5tT76CO mrDGm1SwxqVU/e+gUerQywBFEQ/jcOoEv/8GBgb08XUHwXChCPIkpbVP7+QPJKVO0fxgKE+P9Kj5 8v8kITR0x4G4oiFTIjrQRiSh/46Z0eGOmgwPu9/1AhtHR4L/pqQJEAYyzuK9nELfQ+XAhH/WAb/8 uI9DmtGF1gGk+ijvyBdwz9BH/EBJf9BHkILWPwkQOgHN0dmwCQGn+igwB+bR0Pun8V9FTVBUzlkI 4EeR0g9OVZlQbX/n2CrLWfHxIjASkNxE1t3/0tfX39jo1MISkM2vSV3LWX+KICnByMCwGdvvCj6w GVv/y1j5spr6zPnfzwo57XCHAJ/fv9y/1qHhz+LUWzD5sP/x8eNvzUTqkdvf7a9A3rOGP0JP7f/u H+8v8DdL4iJlPiLwz/aPCjmzi8DRVHIPs5D1n/qfDt9FUlJP/FI6EpCYG/4P8q/zv/TIHnf1fwL/ +58PwFdBUt5OqCH9Hwa/BD8lt/AwwD8MgZ+xKmJHwAxyKVJISfhJIHGzlcGwCOGCAgjg+ikQwCXp AAX/DU/lmMtYf6/76d2xrOvN5Ms/QYwgdP8CD/xW+OO2osvCHxN+gQjh+40mwFEuC/Ktt6sfDFVB EdxfXzPCGrBL4icasG9QywxgGrAnFGtzeVRgP0DdtmAoG6K7YOTGLS4wDEVwMi4xNB8AKxCkEW+a d1RgMQxFDEV9fQyQASEQHwBCAAEAAAAYAAAARgBhAG4ALAAgAFoAaABpAGoAdQBYAAAAHwBlAAEA AAAqAAAAegBoAGkAagB1AHgALgBmAGEAbgBAAGkAbgB0AGUAbAAuAGMAbwBtAAAAAAAfAGQAAQAA AAoAAABTAE0AVABQAAAAAAACAUEAAQAAAGQAAAAAAAAAgSsfpL6jEBmdbgDdAQ9UAgAAAIBGAGEA bgAsACAAWgBoAGkAagB1AFgAAABTAE0AVABQAAAAegBoAGkAagB1AHgALgBmAGEAbgBAAGkAbgB0 AGUAbAAuAGMAbwBtAAAAHwACXQEAAAAqAAAAegBoAGkAagB1AHgALgBmAGEAbgBAAGkAbgB0AGUA bAAuAGMAbwBtAAAAAAAfAOVfAQAAADIAAABzAGkAcAA6AHoAaABpAGoAdQB4AC4AZgBhAG4AQABp AG4AdABlAGwALgBjAG8AbQAAAAAAHwAaDAEAAAAYAAAARgBhAG4ALAAgAFoAaABpAGoAdQBYAAAA HwAfDAEAAAAqAAAAegBoAGkAagB1AHgALgBmAGEAbgBAAGkAbgB0AGUAbAAuAGMAbwBtAAAAAAAf AB4MAQAAAAoAAABTAE0AVABQAAAAAAACARkMAQAAAGQAAAAAAAAAgSsfpL6jEBmdbgDdAQ9UAgAA AIBGAGEAbgAsACAAWgBoAGkAagB1AFgAAABTAE0AVABQAAAAegBoAGkAagB1AHgALgBmAGEAbgBA AGkAbgB0AGUAbAAuAGMAbwBtAAAAHwABXQEAAAAqAAAAegBoAGkAagB1AHgALgBmAGEAbgBAAGkA bgB0AGUAbAAuAGMAbwBtAAAAAAAfAPg/AQAAABgAAABGAGEAbgAsACAAWgBoAGkAagB1AFgAAAAf ACNAAQAAACoAAAB6AGgAaQBqAHUAeAAuAGYAYQBuAEAAaQBuAHQAZQBsAC4AYwBvAG0AAAAAAB8A IkABAAAACgAAAFMATQBUAFAAAAAAAAIB+T8BAAAAZAAAAAAAAACBKx+kvqMQGZ1uAN0BD1QCAAAA gEYAYQBuACwAIABaAGgAaQBqAHUAWAAAAFMATQBUAFAAAAB6AGgAaQBqAHUAeAAuAGYAYQBuAEAA aQBuAHQAZQBsAC4AYwBvAG0AAAAfAAldAQAAACoAAAB6AGgAaQBqAHUAeAAuAGYAYQBuAEAAaQBu AHQAZQBsAC4AYwBvAG0AAAAAAAsAQDoBAAAAHwAaAAEAAAASAAAASQBQAE0ALgBOAG8AdABlAAAA AAADAPE/CQQAAAsAQDoBAAAAAwD9P+QEAAACAQswAQAAABAAAACCmIqO4mDGRbaFjcExKpfeAwAX AAEAAABAADkAACxUv0Ur1QFAAAgwvudhv0Ur1QEfAACAhgMCAAAAAADAAAAAAAAARgEAAAAeAAAA YQBjAGMAZQBwAHQAbABhAG4AZwB1AGEAZwBlAAAAAAABAAAADAAAAGUAbgAtAFUAUwAAAB8ANwAB AAAAugAAAFsAZQBkAGsAMgAtAHAAbABhAHQAZgBvAHIAbQAgAHAAYQB0AGMAaAAgAFYAMwBdACAA UABsAGEAdABmAG8AcgBtAC8ASQBuAHQAZQBsADoAQQBkAGQAIABVAG4AaQBUAG8AbwBsACAAaQBu AHQAbwAgAGUAZABrADIALQBwAGwAYQB0AGYAbwByAG0AcwAvAFAAbABhAHQAZgBvAHIAbQAvAEkA bgB0AGUAbAAvAFQAbwBvAGwAcwAAAAAAHwA9AAEAAAACAAAAAAAAAAMANgAAAAAAAgFxAAEAAAAW AAAAAdUrRbnIOdF+gtfTSqqvZuUVV7l2JQAAHwBwAAEAAAC6AAAAWwBlAGQAawAyAC0AcABsAGEA dABmAG8AcgBtACAAcABhAHQAYwBoACAAVgAzAF0AIABQAGwAYQB0AGYAbwByAG0ALwBJAG4AdABl AGwAOgBBAGQAZAAgAFUAbgBpAFQAbwBvAGwAIABpAG4AdABvACAAZQBkAGsAMgAtAHAAbABhAHQA ZgBvAHIAbQBzAC8AUABsAGEAdABmAG8AcgBtAC8ASQBuAHQAZQBsAC8AVABvAG8AbABzAAAAAAAf ADUQAQAAAJAAAAA8AEYAQQBEADAARAA3AEUAMABBAEUAMABGAEEANQA0AEQAOQA4ADcARgA2AEUA NwAyADQAMwA1AEMAQQBGAEQANQAwAEEARgA4ADQAOABGAEMAQABTAEgAUwBNAFMAWAAxADAAMQAu AGMAYwByAC4AYwBvAHIAcAAuAGkAbgB0AGUAbAAuAGMAbwBtAD4AAAADAN4/n04AAEAABzD+JF2/ RSvVAQIBCwABAAAAEAAAAIKYio7iYMZFtoWNwTEql94DACYAAAAAAAIBRwABAAAAMwAAAGM9VVM7 YT1NQ0k7cD1JbnRlbDtsPVNIU01TWDEwMS0xOTA2MjUxMTA0MjRaLTExNzM3AAACARAwAQAAAEYA AAAAAAAAJne9OTvsOEmkphU9y6V7QgcA+tDX4K4PpU2Yf25yQ1yv1QAAAEQRXgAAppNoNoh2bEun StPrR5pMZAAACVSPxwAAAAAfAPo/AQAAABgAAABGAGEAbgAsACAAWgBoAGkAagB1AFgAAAADAAlZ AQAAAEAAAIAIIAYAAAAAAMAAAAAAAABGAAAAAL+FAACAqnm+RSvVAQsAAIAIIAYAAAAAAMAAAAAA AABGAAAAAIKFAAAAAAAAAwAAgAggBgAAAAAAwAAAAAAAAEYAAAAA64UAAAkEAAAfAACAhgMCAAAA AADAAAAAAAAARgEAAAAYAAAAZABsAHAALQBwAHIAbwBkAHUAYwB0AAAAAQAAABoAAABkAGwAcABl AC0AdwBpAG4AZABvAHcAcwAAAAAAHwAAgIYDAgAAAAAAwAAAAAAAAEYBAAAAGAAAAGQAbABwAC0A dgBlAHIAcwBpAG8AbgAAAAEAAAAWAAAAMQAxAC4AMAAuADYAMAAwAC4ANwAAAAAAHwAAgIYDAgAA AAAAwAAAAAAAAEYBAAAAGgAAAGQAbABwAC0AcgBlAGEAYwB0AGkAbwBuAAAAAAABAAAAFAAAAG4A bwAtAGEAYwB0AGkAbwBuAAAAAwANNP0/AAAfAACAhgMCAAAAAADAAAAAAAAARgEAAAAgAAAAeAAt AG0AcwAtAGgAYQBzAC0AYQB0AHQAYQBjAGgAAAABAAAAAgAAAAAAAAAfAACAhgMCAAAAAADAAAAA AAAARgEAAAAiAAAAeAAtAG8AcgBpAGcAaQBuAGEAdABpAG4AZwAtAGkAcAAAAAAAAQAAACAAAABb ADEAMAAuADIAMwA5AC4AMQAyADcALgA0ADAAXQAAAG8b --_000_FAD0D7E0AE0FA54D987F6E72435CAFD50AF848FCSHSMSX101ccrcor_--