From nobody Wed May 1 13:17:00 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+42744+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+42744+1787277+3901457@groups.io; dmarc=fail(p=none dis=none) header.from=intel.com ARC-Seal: i=1; a=rsa-sha256; t=1561376314; cv=none; d=zoho.com; s=zohoarc; b=Cp73hBJLTKCmG1WEsmO1yL1nJnqFfreWCnkO9eQXjUrKeRgIxjsy+82vyH+v97qewM2ipSe5My42uu+FCWvbt/ieCAe5w+43TS7pwbwa7XuLH8iUS93xCMGFYqpVGYhCcwGeTqALvVUoh/EmZCVc+0rCQxfIheGHFQUVTNYhOdU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1561376314; h=Content-Type:Cc:Date:From:List-Id:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Sender:Subject:To:ARC-Authentication-Results; bh=16agJr0iWJ1lORX49zdtvP33cdGqviDO7WtpKZ0GHGo=; b=cD5pRJRivFnzTI/RzvNgezbZOMLoDDocWuG7Iz3WyELVRLoZcvMkvJPuiwDwDeLeB45VmnUjdyfjQ/+okI/UQgwEISsuRGhi68Zh/6xYl6qAO9aBbDRVbmrAvO/R2QR+TkZEJCUqljTo9JKXzo1ZyN7LT/9fQBbQNNDfzUyZPUs= 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+42744+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 1561376314615696.6278870682968; Mon, 24 Jun 2019 04:38:34 -0700 (PDT) Return-Path: X-Received: from mga03.intel.com (mga03.intel.com [134.134.136.65]) by groups.io with SMTP; Mon, 24 Jun 2019 04:38:33 -0700 X-Amp-Result: UNKNOWN X-Amp-Original-Verdict: FILE UNKNOWN X-Amp-File-Uploaded: False X-Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by orsmga103.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 24 Jun 2019 04:38:07 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.63,412,1557212400"; d="dat'59?scan'59,208,59";a="169433398" X-Received: from fmsmsx106.amr.corp.intel.com ([10.18.124.204]) by FMSMGA003.fm.intel.com with ESMTP; 24 Jun 2019 04:38:07 -0700 X-Received: from fmsmsx120.amr.corp.intel.com (10.18.124.208) by FMSMSX106.amr.corp.intel.com (10.18.124.204) with Microsoft SMTP Server (TLS) id 14.3.439.0; Mon, 24 Jun 2019 04:38:07 -0700 X-Received: from shsmsx102.ccr.corp.intel.com (10.239.4.154) by fmsmsx120.amr.corp.intel.com (10.18.124.208) with Microsoft SMTP Server (TLS) id 14.3.439.0; Mon, 24 Jun 2019 04:38:06 -0700 X-Received: from shsmsx101.ccr.corp.intel.com ([169.254.1.87]) by shsmsx102.ccr.corp.intel.com ([169.254.2.33]) with mapi id 14.03.0439.000; Mon, 24 Jun 2019 19:38:04 +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 V2] Platform/Intel:Add UniTool into edk2-platforms/Platform/Intel/Tools Thread-Topic: [edk2-platform patch V2] Platform/Intel:Add UniTool into edk2-platforms/Platform/Intel/Tools Thread-Index: AdUqgUd5rPpm1IluSeSazegy/+GXcg== Date: Mon, 24 Jun 2019 11:38:04 +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_FAD0D7E0AE0FA54D987F6E72435CAFD50AF8460BSHSMSX101ccrcor_" Content-Language: en-US DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=groups.io; q=dns/txt; s=20140610; t=1561376314; bh=Mwf37f21awDvyszZRyUdQkz6p0qPqRJSC1+8OwWGdAQ=; h=CC:Content-Type:Date:From:Reply-To:Subject:To; b=UOX6a1vRsbQfmsgfhMLIBhZMHdfnoQGEW3RrRcMBTfJE/Yet29HEDEA5Cwsa5Ron3CT lnK5RpmkxbXXd1M8H38GZ3QkI0PPWSa0VHpikOd009GX+VROZ4PU+agHklqVm7M6AxoTU n9JtUoGYIK62HahIW7m0L5DSifgSPOQFThw= X-Zoho-Virus-Status: 1 X-ZohoMail-DKIM: pass (identity @groups.io) --_000_FAD0D7E0AE0FA54D987F6E72435CAFD50AF8460BSHSMSX101ccrcor_ 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 --- V2:add argparse to scripts Platform/Intel/Tools/UniTool/UniTool.py | 494 ++++++++++++++++++++++++++++= ++++ 1 file changed, 494 insertions(+) create mode 100644 Platform/Intel/Tools/UniTool/UniTool.py diff --git a/Platform/Intel/Tools/UniTool/UniTool.py b/Platform/Intel/Tools= /UniTool/UniTool.py new file mode 100644 index 0000000000..298ea47909 --- /dev/null +++ b/Platform/Intel/Tools/UniTool/UniTool.py @@ -0,0 +1,494 @@ +## @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" \ + """\nFunction will sync up UQI definitions with uni files based o= n 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 + """ % (os.path.basename(sys.argv[0])) + + +# ********************************************************************** +# 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__, + conflict_handler=3D'resolve') + 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', metavar=3D'FILEDIR', action=3D'= append', 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") + parser.add_argument('-o', '--output', metavar=3D'FILENAME', dest=3D'Ou= tput', + help=3D"Uni outPut file") + Opts =3D parser.parse_args() + + ExcludeOption =3D False + DirNameList =3D Opts.DirName + QuestionOption =3D Opts.Question + Destname =3D Opts.Output + ExDirList =3D Opts.ExcludeOption + if ExDirList: + ExcludeOption =3D True + 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 (#42744): https://edk2.groups.io/g/devel/message/42744 Mute This Topic: https://groups.io/mt/32191014/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_FAD0D7E0AE0FA54D987F6E72435CAFD50AF8460BSHSMSX101ccrcor_ Content-Disposition: attachment; filename="winmail.dat" Content-Transfer-Encoding: base64 Content-Type: application/ms-tnef; name="winmail.dat" eJ8+Ihx0AQaQCAAEAAAAAAABAAEAAQeQBgAIAAAA5AQAAAAAAADoAAEJgAEAIQAAAEEyNTk2RTUy MzkyRUQ5NDI4NzA3NUI1REIzMDJDMDk2AAgHAQ2ABAACAAAAAgACAAEFgAMADgAAAOMHBgAYAAsA JgAEAAEAPgEBIIADAA4AAADjBwYAGAALACYABAABAD4BAQiABwAYAAAASVBNLk1pY3Jvc29mdCBN YWlsLk5vdGUAMQgBBIABAF0AAABbZWRrMi1wbGF0Zm9ybSBwYXRjaCBWMl0gUGxhdGZvcm0vSW50 ZWw6QWRkIFVuaVRvb2wgaW50byBlZGsyLXBsYXRmb3Jtcy9QbGF0Zm9ybS9JbnRlbC9Ub29scwCJ IQELgAEAIQAAAEEyNTk2RTUyMzkyRUQ5NDI4NzA3NUI1REIzMDJDMDk2AAgHAQOQBgCYLQAANAAA AAIBfwABAAAASAAAADxGQUQwRDdFMEFFMEZBNTREOTg3RjZFNzI0MzVDQUZENTBBRjg0NjBCQFNI U01TWDEwMS5jY3IuY29ycC5pbnRlbC5jb20+AAsAHw4BAAAAAgEJEAEAAABNIwAASSMAAI5kAABM WkZ1o6vO1WEACmZiaWQEAABjY8BwZzEyNTIA/gND8HRleHQB9wKkA+MCAARjaArAc2V0MCDvB20C gwBQEU0yCoAGtAKAln0KgAjIOwliMTkOwL8JwxZyCjIWcQKAFWIqCbBzCfAEkGF0BbIOUANgc6Jv AYAgRXgRwW4YMF0GUnYEkBe2AhByAMB0fQhQbhoxECAFwAWgG2RkmiADUiAQIheyXHYIkOR3awuA ZDUdUwTwB0ANF3AwCnEX8mJrbWsGcwGQACAgQk1fQuBFR0lOfQr8AfEL8REfsFo6aAJAcHM6wC8v YnVnegMQC2AkLnQHMG5vBaFlLgEFsGcvc2hvd19BIlEuY2dpPw3QPagxODUd4GwLgGUKgaUlFFUD AFRvBvAgBADGIAIgGeBweXQj0AOgGwT1GJAgGDUZ4FVRSSwgKCXhGjFzB0AgUdsKUB9gaQIgJQVJ AQACMOMGkAiRKSB1AwAFoAEAPScwdAUQGcAccAWxSEkFKJBxKXUgUFJPTfRQVCuFLihiJQUrlR5Q 9QOgYhngdRIAHGAnsQ3QUSpzeSBlANBoLDsuuSUFVGgrcQUDBCBmKxDeYyzDA/AioCcweTMAKwDu cChjAQELgGkpogQgA/A/JvArAhxwAxAHkCUFYmErL5InEXYqsC83IHIv8mg3cXNkN9E1sAuAHMD/ MjEroAngMaYxtyZhBPUvFR5yKxA4QgbgNWFQeTIuIABwHGA74DM5DUNjmDogTAdwK8JHYSfApjwl ED3iLmc+QEALgC0QIGwkQANwPj0ZQm9YYiBGCfAr4DwG4GL5JEAuZkDRPy89cwcQHGBmQgiQI8Bl dRowAyA8bQsRLg3AQ4ZAJREKwG/zI3I/ykxlBpA9sR3AI9DubBywPnBGgS4lEUbzRQ+PPWQY0RHQ Q+FEIEsLgNsYUDBwPD3gScMuREAdoSdKcUHPFMBpZxhQZC2hGTBmLWJ5PaBaOgCQanUuRgORPHpN 4s54QXAAcEvPCi1QQCUFYFYyOmFkHGAKwGf/CrESACeiMmUlDC0AC2AAMCka8S9JQeIvJhJzLwsl 5VS2LibQIHwgNHA5NCArVl9XTFL2Mec1wxvQGaNkLFYDC4ASAOMAIDTyKCspUvYFADCQzyhBBGJY wB6gNjRWMFN/31SPVZMlDA3gASAgUEAkYLkFQGEvXJ9dr1WUYmCv/2G/XqsYUAfgWPNb2SUFHbEN EDAgHqBoRi4uMjmCODCQNDc5MDlP2IQgLwEAdi9udSKgnyUFV0FjL2Q/Xo1AQGAgxDAsEjArMSxW Em6Q+WrmIyNvcDXSb6cn3yju/iAqXytvLH8tiXSGLxxwqH8wDzEfcBBwqAhQJtAFEGf7IdByAGNz 8AHQFnBZsGxD+XwBcnAFsBiAMXJDEDOR/3xTBCAJcFoxGjBEQHrvcDCgU1BEWC09wGMJ8A0SAC1z WEBhU0QtMswtQwtgL4EtUHGRAjD/f89q5gdwfeEFQAlwhM0zwP82FoU2GRCEzRgwGJAFMITN/XRC Y4buAvAbERHAhM0JAP5nJGAZwITNUVaETnEhCQD7NpADIHYKwAcwAmAZ4AWBzwtgfgSG5ylmRXID YAXAjj1AsAdAjrhVcWk9wC8fYJMBI1E/kXA14SgnNF4jdJRbAzAMgF0rAChbQS1aXzAtajmWMCmV 5SMLYBnAdXZhGDCV5XWUEJXjlgByBZYAbpYwIig/OlvCeJYAU11ce28gDsCMfSmWUJaxYS1mlnAy RpoRNCwd4JqAIic6LGrmIJyfnNEjUU0p/0FwHbEHQGrXfoGUAQQgkxDXmiCacGrmU3SjRA5QlGJ1 n9pHkANWCsBzUJ+tT+8FMDTzn7xwqFYpAXXSB4FHKSAYMGrmX19wA2Bn66cwkwEnJeUnprgBAAT0 /ymxp5RxS3QfdS8tOqhpBaA/fDWnlHwffS9+P39GPEK8Uj6n0Ka4KPKplyUEIHulxrTwJ7WQcgCn NlmwJ9to0FjQJ1rWpyBVpnKTAWYiBrACMGF4PaC1kSCqW02AXbjxdbkybLky2ni5Mmi5MhxgJwNg O6CPoPAJcDMQBbB5MSe6f+27gTK7z7tyM70gaOAt4AuZMbkAcXmwfHddIq+V8Zwfm+C66zC1wCeY gXJGNeEnfMIGuye1wFvWJxAwkRB1AQBkuyzD7/+9BMVfvmWZMb+fwKPJwJkx/kYy/zQPNR8vQDav N784xPOZQMi/UmWsYLsxHGAHEP+XwAeAAjAiENAvnNC60w3g/8FonNQKsDVhJ7G64suwuzb/0m/T ccK/1Ltx0jXSchCUFP4uqvG3F9bvwmGc3NmGhE790Kl0CHADoASQktJGkQBw/XmgZMtQJRAeUBxC dwkFsf+QYApBOEJx0iUQlFHgJCMQ/8u53n+r4uCCq3UJcEGAksD3HFFNkKwcdzIwA6BNgOIS87lg JlJzcAWQc6ILMN4PYZzVTk9URT2go8Zt/y+ABUAvUelHL0Ea4SaxkTH/B4AboYbnyTq10hkQVbDV Ea9EUM2BSGAHgCiGwS5RUTJ2mrBdKbcXpK8gKv/zf/SP9Z/2rxgQcLepGT2g/kcSEDWHebDKQARw jTl7af9RUdH1nNHCQvCyYCDjwPzi/xkwNYf6n3AwCXDfovxDnNAydWvgLTjiEgBCMTZf/k/kAfky JeHCQkX6JSj9/JYp0lt7ZwTTShDuIaERtkIm4BngTwsgG7FNCsD+azwQ7HKNIeRA4fJGoDXSHy3g BfFO8GqwJ5NVVEb/AHAEX5xWAyansgBDqGcJ7/8FaNEwURDdqw2fq4BNoO5+vzVDr4AW4AOnWbBb 0j260P5itwA8EM1QArURH5zSwkKXbECTAQK1LltxZCjay/3G0WU6gBS//3QLZwyfnNHXRpEDo2xA Lh9jcxIisBD5ilMuQi0wt6AJoAEQH+HPc/DlwRwfHS9fTB5QBDx/Cz8A4wyPGW8af49Jz7B3rzpg caEy5+/2d6JgawHK6lco0SioIHBZsMoiWbD/0cEEPBDv6zLuAZ+S7/HjIv/T4SoCF286ge/x3/Ms D/90+xTKyiIo0cFZsCoTLRPay/+r4i0SOEItEzA/NLMtc/Aj/GpvSFAqBC0S2s8b0e/2/5RALgI4 IzWPnNIpwy0SKkr/JZ/y7z+PQJ9Br/cv+D+DUP9Roc1QT5GLsJ5gXxL7uOBx782xo9RwqJzbQ56B zVAnt1+nUYFgpmCn8dVCYtFgbP/NsF/gylOikOdA/ZF3REdfv0hr1aDPcOXQJ7ZGkWNFUP8G0Kui 1VF4IeXQ7MhG/vsv//w08UL9EUVQuGFaEeBwytHdnlBwAECr0NWAbUVG4xH/ZjBMX1a5ajDsQ1PF 1P/WDHtWj1eUYedAWDZG9WAgaPcqoLlgXVFsXVHolGgg7Wf/H6BRX/9d49BV6QHKi7A3of8EPWPy DrRUlEYfkXpawo/1/581KqCUBSqgkjs66o6DkvK9jlYu0cZE4y4gp1I9thf/Ou9uL27p+Hlsgakc bZCua29tH3OvdHpTwWbg8a8AaP9FkubQE6HNQKhAtCC3CGn4MfEwZGRf+8aVQC12+5vwp9AteUC1 NHlhTyBwY/4neci0JWyBtCdyr32+z3AAbHA9InNob3f1SbJn7fEnzVC0JePA0fDf7WDl0s2wxtDM ECJ3L3g/v+iAeWRKo3o5HyDtkV/PoNescHlh+HF0E6BCSrKjxP+b/4gffsSGY1DV/2bf7EXg/39Q y4JF4MRQZYGmYK/AB/P/yH+OP8B2zX/Oiegz49Ho4d+J9ekagY+Cn3kwdXlky1CvnnAfMISPhZtV ljJly4HvtbCHL4g/ftNDFxGqgYu1581QEkAHcWRvzUHj0GTh/mwXEudAgUHsYTUBwhWr0r+NP6Av wHbl+9FSq6Jhy3P/j9/OiJ8vpi/Adutk3AjhEB8H4FlR7JEXErVwbHkhV5N/lI95MGx5ZGzggGf7 lo+Fm0yssYa/sA9+aq7CPnW30uQAFJAn9IoEKGv9z8BwTgGpsSIhZxDjMBJQ+1WCwhEppS+2P8B2 E2B8IP9UNJw0WRDCBzgB63NKMs0U76jPqd+q73kSeIPUxtWD0U3uEWHiUHZxRkkg4ETkSVJ6OWFw EoGEcoYE/kXEBK8Pwz9+asHF04TYGFQvJ76FZNgXMaigJh+1P8j/p0bFn8avMicu/8zQVOTi50qj u9+873kwhHL/eZHT4cAvvv/AA4YE2BEtEvfCn9VviTRBeHDsMQkw+MD/+eFZO+0guKNJxc5Pz195 ML5xg9TnpoXXaKaD0WN/MMPhALpwPVsnd4PSdwDuXdR/1Y+JQ1DmUWTgKMC7MYADcnceYySlL+Mo BCD7G8IicGbmwXjx51vZUMgPv+ZfoVbtoeQg5AGiI3P84neiBcsws4Bu2W/af3kwbx2D1G8AQFSx 0j5OQU36RYXXT+yV5Y/gH4lSSjL97IFQVML50encwjEtUuq1e2rzeJJzF1gKu8HaIrFGfzwwamDv OtP1aEIisfNiLv/T9e86aKb2t/kzaKb3m91B/wPy+QbutPWMyuH4ysHLGx33/3c63/ZOVIWh7zqG afkG/4Zp7zqYaPkGmGjvOq7I+Qafrsj+vGvQEkD4xVtd9Q9fAe/vTSvf8CU0ckVPIGjf/3OeMg/v 768TBlLKtJ4y3/hZFK8Y2g+yE1c9IsAWec8YHxyeScDhgSgi5WBpEPBSUk9SYNDLr5Kj6NL/o4BZ I8rnHhHp3CJfMR8in/cNW4LAwOMoFnokcDmhamB+cHGhE0chny9EG58daF/+VejAskAo7yNv9Xtn FcI0Z2ZLL8X3EVx70+D9ZSf/YND9VoPh+FkyQvhog+ENiP8yQQ2Ig+GGamDQhmnvLyS1/5haueGY d4Phrslg0K7INLP/weo1ksHpOB85Jvq8MkH6vHxcfen7HbTNYvJCYNAl/eUxJf1HLGsTBtP1Fy8+ 3fpXuDBrJtPT9bmA2PW6Af+5gWFBLGsZ4WirR78jrxnS2yfBWLIuaFDQ8Sgvxd5Q/THHXWMc/Rao YkaD+RNQLqcnmphQaCQudYzgJz7K/4lAamBRP1JPU19OL080neP/9NBYCuOAZaJb+50TnkHNouoo Z4Uus4B59NFfkFb/exGPA3ZudYDQ8KLhGsBHf9JwugGoYmJWW/xhHySxd/+BYLSQYnHYMCfQcgDB AFv7+YPhciuD0WJWXMG6gIYCf6hiYQ9XjomQ4/L3EWlmLse7QvToKXwgSU9MYyBCf2nfHVoeReVA amBs8PSiW74xURBvv02fJLZexVtYCv9UUBrAVTVb92v0Su6GaW+v+lLis1YvkRrAi7Fe8W1fbRnS KHr5GsAxX78dWSduULpgo4HyMXjNUx5DKPpzXMFit+G7AZ7xIDDY8/OMUIzxLid8L1a/HS1KoPvN UyYwc90xsmGbwMBgjFDHIV8tvy5LIyAqir+Lz9uM343vKooYmAFjosB5lL97pWaBuEDYMbgRzZRv GfDduDJjiODkIrQBdaOSqDH/o/C4MIbinkFPYCAwohCCsHuKGCS7SLgwzdUZ0qHCZP5113DeAJux o7CiBeJxk5NbnjLNZi6UvyS0SRnwLd51svbhwB/hwPBjGeDYgH5k3bG7QQjAuoCQgbhlaPsIsLJg b50yJ0CZvyS0uDD/u0IfsFuTRsKoNRLzlwLpFf/kINwQJ3BiopGAzWGZqIoY/c/Wc0QAJLC60ITA o7+KkDfipKVjJLAws9HpAHVj99kins8ktDGoAuMTm0CUIf8f0t3g1zCX0LqgkwGUI5Z0n9jBngDX QKX/hJZTeb3AZmGAsBrAIlMeAK5LUpZF9xHkIC5icG1w8lEV0FAj6RRb5VF0XSsAW0EtWl8wLTnT siGx5CNsO1F1LDGx1TeTUbHT5WByHhGyICIoFFt45WBTDmB7MSxiMkIwKS4q0JGwoUTxubBBTEy2 QJyAwRCSIf+JH4Slj7AZ8Hu2hIsvXEqA5kcvY3tQckmcsV7FSoDfrrS9FrBQB8uhUFILEf3Cuw5Y vrVT6SMN7ySyQ5zzt81iv456+jAFK4BRTqUQzlP0QNkh9xE0NySwipD/LBCXwdjBhKKhMFTglDLN Yv+TnOQghtDXQZgxoUUuHySx9YqQTCcwaxLzl0abspQF35JlnZRc4pD1zXdyvzOR8f8KDkXST0JG wl7MhI9F0sCH9dGpW09CXaVxipDRY2aQ/67RhLCd0oJR0XJc4mzC/ZP/kfGgYEYwoTDxofJChJ/L c/JD4QBja2lAzBqXx5Pj55km2U8S1VRlsPDUSMCLn9LPfLneuX4RwIdbMNWg/+B/hS9xVpki6RVE IJvjIFDYc2ln5ACX0G2CMp3x/15Q9vAqEFzTZkCSMqvDZpD3THCXUJuiIURC4wqDP+Tf/3FA/VDy YAjAqAHP0ahgknP/6OGR8ZQyl/Vc4s2wBPCUEfknMGwuIY/x+3r6qiDacf+s4pZkym/x+cCLJmbq //T//9of2yzIPNy/GTxxEuMIcoD5SoAxNoGA32S/I+PP5R/j5ieYlCUwNICw51/ob//pf+qF/q8A zwnPCt8L7wz//w4PqVz/0PE/7Q/uHTtQ7yj/wsLwPKyinOSSETngiPAVgv+Yk/Ef8m/zf/SJ9b2/ IyZm/we/D+tFHsC0e1GYxLvZ0l+7DuXAtEarYFzxYuBGkiD/kxDS39E5wLRGUJFAIN/dzf9KUdRI JUjVX9Zv13/Yjxc/f30UHzri0if2Jk8xWyJcVNfwkC0/NDpioFFrfD+5Yf9eESJZMJ8CD0qRj6G3 UWZQf5uyRdLnIc2wbCHNsJfQYvmiIEhJmUCi0EwEREIu+v8QHxgfGS8d3/pBevCi0gWxh5kToc1G 0XZmci9EIL8s5NBfHz8gTz3eqTBJBJAfgjLcBjoTl8GcAVNUUgBJTkdfVE9LRVhOKDA9zy7OIa8h MP+H0EgP+hnh4qr079WgSpPn//y/Lh9GGF4FwItcxVRfXlD/wmdOLzQ3wmf3Rj0KWK82LGG7RVsn VXC3gMKjc/4n478Q7zlLQsY7DzwfPS/vX68+zz/dkXBmFeF/pFC0/wZAqnQsEKiQs7FE53hNfW// ZW5z9MP4av/6I150oTcGEP8qIWWQqHKk4KIg0C7h8V3v317yVmPCZ21v4fFv0kArIuYu9xCC0HIo c+cSIOawvyvyXxBtX2YTeMKhck4sAv8zIHg/dRFl0HdRBpDH0GXQ715goVD3AyzBJ4NPhF96X297 b3j1f86UcHl/v2YSRf0FkG938OKiu8BugCfhoXL9haYogQplX2YSccOFwZEwH9JAm4D3gId7vRAn cisPtnGFplZR+sBPdXRwr4zAoXKEX2YWULNgdEWxdm3CxIy4LqBSR+B+f3jnxjCbkJsQT0Wqg5wB jU//YL85wOqwjdAbwaTw1UD/gN+Qz2v2lXXjoOLCMnlvZhfvhC+bDYmfiq13i7+Mz5rP/5PcgQug QOqwheDb4DbCUXP+LsJF4pK5gSzzFr+mX47d/id+b5Fcn8+hH2FRknMVU7/ikpUyoj6lX6+8bnUx rw+nlyz/gG0CIlBqEG33EP8EcDqRuUAn8PugTh+rnrTv4bd5IlxcbuchUFFscNNRMDqQbHmkEVki wATQfwaAXcAswCyQT8ITsrkiT/u5cWjQdOpgGcDuMcdB8JPdE8EtufCc0OpiLriR6rL/h4+3D7Cf +P/6QRIgaQGz0F+/AM40BpBRsRO2bsXgYh9pEY8i+4DBX+IQT0RPvznAE7FJcVBRaUBHAWK7gvsE 1BIxZ8+gXcAEEGNgvEFbuVBQIy7JcCFfeSpxeMZMMxHKpFJFKI7pvs+/bDEr0S7wyrRWUE3BMLTf u8poyxdM64HA308XcxVQ/mmSEVBRkHLikhOyLQJGov8F0zBAaYM6YBmwLIBP4XwA/xWQUTAsMvdU 4pLEYAZkE7L/nGQE0fsgLS/6QQZwvEDE0SfbkWngu5FkZMMBIEKsT00GAMaSdmkRebnh37owcnDI cdUkpBBUUEIGY/tjMWiTZiqQA4AUcMmfG1d+c2JgEfDMj2wyADZtAlt7eV+3dE5RcIZwcVAzETDv kN9/j+QPADZbzcIANlZQ/i1tMHzSwM8nKkX9dY+Tj/8R8WKRBhPEYARIQqCk0gMn/wdyWz8abxt8 5HXyb09D9T//+iN94MLi+zATp/wYE7IUcP5z2MBpg3FF9o/2TzH2KyCvcjEzEQdwB3AqbFBCBFD/ gXDEQP116cDNw2Svvy2O6rorqEEjUIVikGKRI6eQbYdQdWqxcTIguJDvMSVYMDR4BHG4kHK4kSf/ ZF2LYP10BpHKs4tg9L/uL+4iAs8D2ARnWARxZE4Gn/8Hpwgv5DoCgeo/m5+cr4s/n56/7Y+3co/J 17Bla0uf+xdP1RJly88RXrDF5Rcb3r1PgCofPyBPIV8ibyoemO9KINL0e+E5wFBycNewktH/LIq8 MipQaeBPwF3AheApMP974XJyLEFJpR6YZl3HwNvwf8MB2wA6Y2zhA6bDBCnxcv8as92RZ1D50Mhh JcYoT1Oovi1p8L0kyIFQYdbgY2wwv7SBHpcemJWRxEAswXM5wP9g4XeR6bF38SbBYmBjYEbw79eA OnMnsdRVY+yAhePUU/98gi2ft3RxcnyC6bE3ozRG/7xB78HYkTBfHsWwxDJT5gFfknNDhjRR3pR3 QGMsg2R+OzX/t3RJUBjwbCLYIG2ucNRwvBDDQXVyMmbYwN/HcDnPT4DUEyaRbxKQaaL9x+FnRvNQ cMkSxOAykSUh/9RUUcBjwCcQR6AFUBvtcXD/1wCGgVW06CHe0T/CpSG2uxQnXgnlWwRhdF0rAChb QS1aXzAtwjlJ4CkoPzpJkwU0h0ngA5dJlVthLXpKIQItgxB7Miw1XH2TSvuuwCoiSZQqWwUxjF0/ T0FLkD8pKhSw4RxcLk0pLjmA1wBp0fUb3k/GgkzgQUdvSH9Jhv9KGErPS99M703/Tw9QEVD/zRjz RSWBUy8oJ1bfV+q7SgBYvSlZr1q/W8AnXH//GMZ9sewE/cJeOlUfSjZJlf9XrnFBYe9i/2QBXBUd nxjV97RQ1qBEVyh3ghRgN2bjHG9DZYH1DWE8M0QnERRgQf/vkH2xDdF9tW/iRrkUYFLX/xTBXZVz k2X1G983MkDRJQD/ErHoMDKjfPk3Zncvme/zVf+0cIWvhrt4Zhi/Ep94ZhRivxSxfRkVY4aFfA+3 ckTE4PHHUHVmZsRx6DF+NJBv/6nvq7+SghNSrVZ4Z6MC68Lv05Wu38A2hDpXGsKGcughfkZc0New ep/hxHmBMqIuy7PgMxEocdVbJ6IycEL+J+MPhVGiSugwkh+TIHla+idztS4lwah75i+UL5U//5MC j8/hxJsXXcJx1JMgmR//e9/zSH1Pflu+D78egL8Tr/+CgqGoFW+kjwFvmuIXuIbP4+XUh/MgSU+J xC/hqZ/hiQtSUk9SJOCKovQh/zHgknDp4K0f4baxxdEw4rJ+MpNPt3egH6o9pb+mzXf/p9+o77bv r/+aK4tyogDU8P8/QNWgEGCXkqow7PNC4vCi7y1CjG/C36stJ5gLwwqH7//Hj4lM0wDA5Yqivr7B 38MM/42fxfiY78gPib/KP4uU1sffy1/Mr5xf4Zg3ZyHoMJoqt3mgP/Aa0Ch5hGHQLbIx/88v83Vm VWRV4iJl9RRghXj/1E/htXNS2q/rShCS+WHf///DDwHc6SHiz7f/uQ+nb7sf/7wv44wXr95/7C8a XxtthL/3hcZeMfnwYt0LxODdv97P59/f44yOulRyAED3j9BMr5ogQARqUUUAZongbQpR/wxify/j ijwk/hB0AJriUub/6CD1L/p74cI8JOHAJcC/4M/iQf9I93/jhlRoc/BHB31dhigC6QFf4UpdwmAS Rm8DVgYIBH+y7igJ7NiBJx/TwGQgK4MNrw6yLVVT+w7/DklnEOYLrxQ/44ARb/4nalFkINqfF+/y 3/PhXjH/JEHTkPCwB2r0vxcP+B/5L/8c7/rP+9j9EPy6EVxv4f2Gvxyr17/Yz9nfH+sxsWSiA/88 JEUAOOGiADNQcgEx0EEH2+OGcmhbgnD9ECf9SbRh/3Q8AK8gBSnn8VEq3x/YcyX/LW8ucnO1L4+c n46oKN+2b//mv4FfulSCz4Pf44uGRvB0/zW/rY4+b9B/ii+LOvBy0//31c82jDEAKko/S09MX01v fipJmOeQGoBnkHHjsWBT/cmgcl2gGnA5Ye2g4bOX0e/Bg3cXMQDji0Nc0THAbwf/MdLncNzg56DA IDIYeXANcP8qdgrByaBdoFEXUb9Sys8AvVAiVm9QjwPhwlZjdm9Q/3lwAsBacALAU9MDQ0mXSZh9 seF1cGAh4Pdg44Fc9iDf/6PcEYWwrIDAIGJVYZFF+ndkkGvBgA5QMjPnYGSZx1e9seHjgy0gTp7A Vy//44WR0SZjYvCR0VSR4dBVYP9xcBqQYHNVgVFv44lwQjHA/2LSJ7DnoLOgctBmtDHSWQT/4jJl Z1vPMQDNRF1zaGFjKr9C9ueQDXBEMGYQGnBzfiH+KLHi6PBkxejwaIMozP+QHG9iX/CbBjBMUmVt TG92jzBCgWx155Bk9y0gaw2dTkV0FJ6P4UoG0v92kHSB4jJ2CSTCl4J3b+FoP5HR4jJog3r/symR Nmpv/X1wKHAXkdHqILSBeNd9T/9nz6yxc7J/wjZf4YZdQVVR/3zP44ZFx+VQZMaWmYXT1r/5ZqFu bRkQBvDcgIs0O/r+KpfDKM9D5FOWf8iF00F/94r/jA1acCqNL/7lWRo79+eQH5EvjA1zZJNflG+V f/+Wj5efmKmNH5pfm2+cf52P/YxYaPzAn2+gf6GPR89I379J76vPrN+t704vTztDIvB2cH8AHzBh VM1TMSpoaP0ZECCjkFVgpdD8QTGT6kD4c2lnpdBbuOOMMeEAIfMjABESa2WGEbMCHzAC5P9tv1zd Jidi8CZjZpVqCQKo/2cIbA9tH7iIqngoQVVAGRD3HzDNQA4QeFBh4cLJkdzAXmLtUPCQiecGREdE wHU+cNwl53CxAI7ypaonXgBbIFxcdF0qKMw/OmMhVYB8bl0hAwAEY3xQQWNrYm947nzh0OeQzUBk /6IocMXjBCt2VUBpZC4rP//GQdPBxnTTwcbW08HHV8nCDcf5OxvQqFwuRE9UIEFMTCB83FJNKcvc hsLcVlVASWTED8UZX8Xfxu/H/8kE0iU90iZbAGEtekEtWl8wrC05yPDSQC7WuykQ4HPOvy8zVG+3 AL5g0HwnD28BsQAtUNXMU1RSSQBOR19UT0tFTv/SJdJADZDXvNJA2I+p326z36btJjVw/3IJ6PBH cePQE3/o8MNa5YbQFOWG2hQ8gFH/H5D/wHnRRKOlqjl/OsY7tP8mNTyBpWE9slll6g8Yz1ll+4OR MSAohE8paC6yWOFpNP/8Qe1iVhNW+gUGWWHDWRmh/8NaQM/wnynCUAd8QttkMcD/OvIDovJcd594 pfVECsH0vN/tT3haBkUKwdm6KPzXpY/7BQcxAEm0kHxQt5YxwG7BzwKRDnA9wdy6KDAbkQW7gdyx X0VNUFRZPaDzfFEGz05VzhCiPwzqABn5JrEiMEdQEQQLnQeXDJ//DagJgkdQAm9+HQAZvuBegd9g oOTZEK8+/uTZWwAYLnL/z7oBuRSPPvkiMLvAFH8Rf+cLYRaPF5RbMC5wJrEYL/8CBB9REJ8ib3We 6EZ3DyK/jyLfI+8k94CiImUiJY/PK08++ehL9ZFUcuhQKl8DL19Dn0VSUk9SOr9HUMzbMs8nbyh/ KYh3Kj+HN78wX0SAV0FSTtzh9zHfO384/yVp8KkQQUHUcQ9fInyAQTJeEkhJSSC+cehV9nA9obbC PaApRYD+JR3AOr9CDxpYABjkux6d3+ZsII0Zi3QBwOB0Ns8xFv8to+tiAIJT07NBPaHB5vURPi5A suJ3399BFXXRX1/3iKJPcICiJ09wpBBBIE9wcidJK3N5iSB0AOsgKDdQYvAgGYYtYvBBBTIunDE0 U8Bf0NjRb3eJIGYxQQVBBX19QVBV0AAAAB8AQgABAAAAGAAAAEYAYQBuACwAIABaAGgAaQBqAHUA WAAAAB8AZQABAAAAKgAAAHoAaABpAGoAdQB4AC4AZgBhAG4AQABpAG4AdABlAGwALgBjAG8AbQAA AAAAHwBkAAEAAAAKAAAAUwBNAFQAUAAAAAAAAgFBAAEAAABkAAAAAAAAAIErH6S+oxAZnW4A3QEP VAIAAACARgBhAG4ALAAgAFoAaABpAGoAdQBYAAAAUwBNAFQAUAAAAHoAaABpAGoAdQB4AC4AZgBh AG4AQABpAG4AdABlAGwALgBjAG8AbQAAAB8AAl0BAAAAKgAAAHoAaABpAGoAdQB4AC4AZgBhAG4A QABpAG4AdABlAGwALgBjAG8AbQAAAAAAHwDlXwEAAAAyAAAAcwBpAHAAOgB6AGgAaQBqAHUAeAAu AGYAYQBuAEAAaQBuAHQAZQBsAC4AYwBvAG0AAAAAAB8AGgwBAAAAGAAAAEYAYQBuACwAIABaAGgA aQBqAHUAWAAAAB8AHwwBAAAAKgAAAHoAaABpAGoAdQB4AC4AZgBhAG4AQABpAG4AdABlAGwALgBj AG8AbQAAAAAAHwAeDAEAAAAKAAAAUwBNAFQAUAAAAAAAAgEZDAEAAABkAAAAAAAAAIErH6S+oxAZ nW4A3QEPVAIAAACARgBhAG4ALAAgAFoAaABpAGoAdQBYAAAAUwBNAFQAUAAAAHoAaABpAGoAdQB4 AC4AZgBhAG4AQABpAG4AdABlAGwALgBjAG8AbQAAAB8AAV0BAAAAKgAAAHoAaABpAGoAdQB4AC4A ZgBhAG4AQABpAG4AdABlAGwALgBjAG8AbQAAAAAAHwD4PwEAAAAYAAAARgBhAG4ALAAgAFoAaABp AGoAdQBYAAAAHwAjQAEAAAAqAAAAegBoAGkAagB1AHgALgBmAGEAbgBAAGkAbgB0AGUAbAAuAGMA bwBtAAAAAAAfACJAAQAAAAoAAABTAE0AVABQAAAAAAACAfk/AQAAAGQAAAAAAAAAgSsfpL6jEBmd bgDdAQ9UAgAAAIBGAGEAbgAsACAAWgBoAGkAagB1AFgAAABTAE0AVABQAAAAegBoAGkAagB1AHgA LgBmAGEAbgBAAGkAbgB0AGUAbAAuAGMAbwBtAAAAHwAJXQEAAAAqAAAAegBoAGkAagB1AHgALgBm AGEAbgBAAGkAbgB0AGUAbAAuAGMAbwBtAAAAAAALAEA6AQAAAB8AGgABAAAAEgAAAEkAUABNAC4A TgBvAHQAZQAAAAAAAwDxPwkEAAALAEA6AQAAAAMA/T/kBAAAAgELMAEAAAAQAAAAolluUjku2UKH B1tdswLAlgMAFwABAAAAQAA5AAD27UiBKtUBQAAIMLg0EEmBKtUBHwAAgIYDAgAAAAAAwAAAAAAA AEYBAAAAHgAAAGEAYwBjAGUAcAB0AGwAYQBuAGcAdQBhAGcAZQAAAAAAAQAAAAwAAABlAG4ALQBV AFMAAAAfADcAAQAAALoAAABbAGUAZABrADIALQBwAGwAYQB0AGYAbwByAG0AIABwAGEAdABjAGgA IABWADIAXQAgAFAAbABhAHQAZgBvAHIAbQAvAEkAbgB0AGUAbAA6AEEAZABkACAAVQBuAGkAVABv AG8AbAAgAGkAbgB0AG8AIABlAGQAawAyAC0AcABsAGEAdABmAG8AcgBtAHMALwBQAGwAYQB0AGYA bwByAG0ALwBJAG4AdABlAGwALwBUAG8AbwBsAHMAAAAAAB8APQABAAAAAgAAAAAAAAADADYAAAAA AAIBcQABAAAAFgAAAAHVKoFHeaz6ZtSJbknkms3oMv/hl3IAAB8AcAABAAAAugAAAFsAZQBkAGsA MgAtAHAAbABhAHQAZgBvAHIAbQAgAHAAYQB0AGMAaAAgAFYAMgBdACAAUABsAGEAdABmAG8AcgBt AC8ASQBuAHQAZQBsADoAQQBkAGQAIABVAG4AaQBUAG8AbwBsACAAaQBuAHQAbwAgAGUAZABrADIA LQBwAGwAYQB0AGYAbwByAG0AcwAvAFAAbABhAHQAZgBvAHIAbQAvAEkAbgB0AGUAbAAvAFQAbwBv AGwAcwAAAAAAHwA1EAEAAACQAAAAPABGAEEARAAwAEQANwBFADAAQQBFADAARgBBADUANABEADkA OAA3AEYANgBFADcAMgA0ADMANQBDAEEARgBEADUAMABBAEYAOAA0ADYAMABCAEAAUwBIAFMATQBT AFgAMQAwADEALgBjAGMAcgAuAGMAbwByAHAALgBpAG4AdABlAGwALgBjAG8AbQA+AAAAAwDeP59O AABAAAcw+HELSYEq1QECAQsAAQAAABAAAACiWW5SOS7ZQocHW12zAsCWAwAmAAAAAAACAUcAAQAA ADMAAABjPVVTO2E9TUNJO3A9SW50ZWw7bD1TSFNNU1gxMDEtMTkwNjI0MTEzODA0Wi0xMDY1MAAA AgEQMAEAAABGAAAAAAAAACZ3vTk77DhJpKYVPcule0IHAPrQ1+CuD6VNmH9uckNcr9UAAABEEV4A AKaTaDaIdmxLp0rT60eaTGQAAAlUj8UAAAAAHwD6PwEAAAAYAAAARgBhAG4ALAAgAFoAaABpAGoA dQBYAAAAAwAJWQEAAABAAACACCAGAAAAAADAAAAAAAAARgAAAAC/hQAAMH5ZSIEq1QELAACACCAG AAAAAADAAAAAAAAARgAAAACChQAAAAAAAAMAAIAIIAYAAAAAAMAAAAAAAABGAAAAAOuFAAAJBAAA HwAAgIYDAgAAAAAAwAAAAAAAAEYBAAAAGAAAAGQAbABwAC0AcAByAG8AZAB1AGMAdAAAAAEAAAAa AAAAZABsAHAAZQAtAHcAaQBuAGQAbwB3AHMAAAAAAB8AAICGAwIAAAAAAMAAAAAAAABGAQAAABgA AABkAGwAcAAtAHYAZQByAHMAaQBvAG4AAAABAAAAFgAAADEAMQAuADAALgA2ADAAMAAuADcAAAAA AB8AAICGAwIAAAAAAMAAAAAAAABGAQAAABoAAABkAGwAcAAtAHIAZQBhAGMAdABpAG8AbgAAAAAA AQAAABQAAABuAG8ALQBhAGMAdABpAG8AbgAAAAMADTT9PwAAHwAAgIYDAgAAAAAAwAAAAAAAAEYB AAAAIAAAAHgALQBtAHMALQBoAGEAcwAtAGEAdAB0AGEAYwBoAAAAAQAAAAIAAAAAAAAAHwAAgIYD AgAAAAAAwAAAAAAAAEYBAAAAIgAAAHgALQBvAHIAaQBnAGkAbgBhAHQAaQBuAGcALQBpAHAAAAAA AAEAAAAgAAAAWwAxADAALgAyADMAOQAuADEAMgA3AC4ANAAwAF0AAAD8OA== --_000_FAD0D7E0AE0FA54D987F6E72435CAFD50AF8460BSHSMSX101ccrcor_--