From nobody Mon May 6 02:07:46 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+42909+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+42909+1787277+3901457@groups.io; dmarc=fail(p=none dis=none) header.from=intel.com ARC-Seal: i=1; a=rsa-sha256; t=1561599580; cv=none; d=zoho.com; s=zohoarc; b=S2G4VDles3CpQnqwIDSm9BDDNwmCu0DbomV4KJ9jqx4p3qyRwL7NNobaz53SyXSvdZwJSo0HINRimLJjiN9y19U3kY+i+J33wXwrDQ6UWr8p3T82bxeMWQmf/3POWCvEd6S7/JsYuoC8KuG5It28bubMT1s1qwVjlbMfS5N5Wig= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1561599580; h=Content-Type:Cc:Date:From:List-Id:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Sender:Subject:To:ARC-Authentication-Results; bh=hDXQrNpJqH6J448rU2jJbZKIFGeGWgXis+ErQyRl6iM=; b=SuHiOPL2nPkgnF8UD41LRMQ4MgRRfdcxXhE4BDABR/BtVGP/s+NkxjMCdn7y+dUkYuGX+2QrHgSrTscj8pghdEDMTYH6/FSn62i6HbsI643QCcnFwux4zneuy03t7Z599jrmnmayiNcSXcGgBM4272w4olvfEdBg9ah1wngjjx4= 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+42909+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 1561599580497851.2565619645345; Wed, 26 Jun 2019 18:39:40 -0700 (PDT) Return-Path: X-Received: from mga01.intel.com (mga01.intel.com [192.55.52.88]) by groups.io with SMTP; Wed, 26 Jun 2019 18:39:39 -0700 X-Amp-Result: UNKNOWN X-Amp-Original-Verdict: FILE UNKNOWN X-Amp-File-Uploaded: False X-Received: from orsmga002.jf.intel.com ([10.7.209.21]) by fmsmga101.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 26 Jun 2019 18:39:38 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.63,422,1557212400"; d="dat'59?scan'59,208,59";a="172948880" X-Received: from fmsmsx104.amr.corp.intel.com ([10.18.124.202]) by orsmga002.jf.intel.com with ESMTP; 26 Jun 2019 18:39:37 -0700 X-Received: from fmsmsx112.amr.corp.intel.com (10.18.116.6) by fmsmsx104.amr.corp.intel.com (10.18.124.202) with Microsoft SMTP Server (TLS) id 14.3.439.0; Wed, 26 Jun 2019 18:39:35 -0700 X-Received: from shsmsx154.ccr.corp.intel.com (10.239.6.54) by FMSMSX112.amr.corp.intel.com (10.18.116.6) with Microsoft SMTP Server (TLS) id 14.3.439.0; Wed, 26 Jun 2019 18:39:34 -0700 X-Received: from shsmsx101.ccr.corp.intel.com ([169.254.1.87]) by SHSMSX154.ccr.corp.intel.com ([169.254.7.156]) with mapi id 14.03.0439.000; Thu, 27 Jun 2019 09:39:32 +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 V5] Platform/Intel:Add UniTool into edk2-platforms/Platform/Intel/Tools Thread-Topic: [edk2-platform patch V5] Platform/Intel:Add UniTool into edk2-platforms/Platform/Intel/Tools Thread-Index: AdUsiOj8sq0fFMwTTsOrK49L5pWwqg== Date: Thu, 27 Jun 2019 01:39:31 +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_FAD0D7E0AE0FA54D987F6E72435CAFD50AF84FD8SHSMSX101ccrcor_" Content-Language: en-US DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=groups.io; q=dns/txt; s=20140610; t=1561599579; bh=AqANOyJiLoWqRkE/RdqZHUQj4n0uK1if1cEs7oOeg5Y=; h=CC:Content-Type:Date:From:Reply-To:Subject:To; b=vgg6hU39Y/RPSNKjBEQRGsYqhq2ZIHxQAnkSrp6pJoysxvz2WqFhvnLiLivop/KTIlH pwLHX4KOxSpJ3VD7s5+Pp39SFzKJFwst0oRa9FvnO2HCNP3J5pRQUA6GAIRhuGjyRZ5ed TOMxVlH3wEUpYv3cGxST0ZQKPLIzWup4rxc= X-Zoho-Virus-Status: 1 X-ZohoMail-DKIM: pass (identity @groups.io) --_000_FAD0D7E0AE0FA54D987F6E72435CAFD50AF84FD8SHSMSX101ccrcor_ 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 FileHeader to script Platform/Intel/Tools/UniTool/README.txt | 41 +++ Platform/Intel/Tools/UniTool/UniTool.py | 488 ++++++++++++++++++++++++++++= ++++ 2 files changed, 529 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..69da4aca24 --- /dev/null +++ b/Platform/Intel/Tools/UniTool/README.txt @@ -0,0 +1,41 @@ + +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..27fe13cbc1 --- /dev/null +++ b/Platform/Intel/Tools/UniTool/UniTool.py @@ -0,0 +1,488 @@ +## @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 +FileHeader =3D '//\n// FILE auto-generated by UniTool\n//\n\n#langdef uqi= "uqi"\n\n' +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 'The script generate UQI unicode string for HII questi= on PROMPT string.\n' +__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])) + +# ********************************************************************** +# 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, + 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 FileHeader + 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 FileHeader + 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 (#42909): https://edk2.groups.io/g/devel/message/42909 Mute This Topic: https://groups.io/mt/32224892/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_FAD0D7E0AE0FA54D987F6E72435CAFD50AF84FD8SHSMSX101ccrcor_ Content-Disposition: attachment; filename="winmail.dat" Content-Transfer-Encoding: base64 Content-Type: application/ms-tnef; name="winmail.dat" eJ8+ItwHAQaQCAAEAAAAAAABAAEAAQeQBgAIAAAA5AQAAAAAAADoAAEJgAEAIQAAADAxQ0QwMTQ1 QzdBQTcwNEQ5NkUyNTA3MTdGQjk5REIyACkHAQ2ABAACAAAAAgACAAEFgAMADgAAAOMHBgAbAAEA JwAfAAQAVgEBIIADAA4AAADjBwYAGwABACcAHwAEAFYBAQiABwAYAAAASVBNLk1pY3Jvc29mdCBN YWlsLk5vdGUAMQgBBIABAF0AAABbZWRrMi1wbGF0Zm9ybSBwYXRjaCBWNV0gUGxhdGZvcm0vSW50 ZWw6QWRkIFVuaVRvb2wgaW50byBlZGsyLXBsYXRmb3Jtcy9QbGF0Zm9ybS9JbnRlbC9Ub29scwCM IQELgAEAIQAAADAxQ0QwMTQ1QzdBQTcwNEQ5NkUyNTA3MTdGQjk5REIyACkHAQOQBgD4MAAANAAA AAIBfwABAAAASAAAADxGQUQwRDdFMEFFMEZBNTREOTg3RjZFNzI0MzVDQUZENTBBRjg0RkQ4QFNI U01TWDEwMS5jY3IuY29ycC5pbnRlbC5jb20+AAsAHw4BAAAAAgEJEAEAAACvJgAAqyYAAAZuAABM WkZ1m/8muWEACmZiaWQEAABjY8BwZzEyNTIA/gND8HRleHQB9wKkA+MCAARjaArAc2V0MCDvB20C gwBQEU0yCoAGtAKAln0KgAjIOwliMTkOwL8JwxZyCjIWcQKAFWIqCbBzCfAEkGF0BbIOUANgc6Jv AYAgRXgRwW4YMF0GUnYEkBe2AhByAMB0fQhQbhoxECAFwAWgG2RkmiADUiAQIheyXHYIkOR3awuA ZDUdUwTwB0ANF3AwCnEX8mJrbWsGcwGQACAgQk1fQuBFR0lOfQr8AfEL8REfsFo6aAJAcHM6wC8v YnVnegMQC2AkLnQHMG5vBaFlLgEFsGcvc2hvd19BIlEuY2dpPw3QPagxODUd4GwLgGUKgaUlFFUD AFRvBvAgBADGIAIgGeBweXQj0AOgGwT1GJAgGDUZ4FVRSSwgKCXhGjFzB0AgUdsKUB9gaQIgJQVJ AQACMOMGkAiRKSB1AwAFoAEAPScwdAUQGcAccAWxSEkFKJBxKXUgUFJPTfRQVCuFLiV2KIErlR5Q 9QOgYhngdRIAHGAnsQ3Q0SpzeSBlANBoLDst5rklBVRoK3EFAwQgZisQ3mMswwPwIqAnMHkzcCsA 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 AGoAdQB4AC4AZgBhAG4AQABpAG4AdABlAGwALgBjAG8AbQAAAAAAHwD4PwEAAAAYAAAARgBhAG4A LAAgAFoAaABpAGoAdQBYAAAAHwAjQAEAAAAqAAAAegBoAGkAagB1AHgALgBmAGEAbgBAAGkAbgB0 AGUAbAAuAGMAbwBtAAAAAAAfACJAAQAAAAoAAABTAE0AVABQAAAAAAACAfk/AQAAAGQAAAAAAAAA gSsfpL6jEBmdbgDdAQ9UAgAAAIBGAGEAbgAsACAAWgBoAGkAagB1AFgAAABTAE0AVABQAAAAegBo AGkAagB1AHgALgBmAGEAbgBAAGkAbgB0AGUAbAAuAGMAbwBtAAAAHwAJXQEAAAAqAAAAegBoAGkA agB1AHgALgBmAGEAbgBAAGkAbgB0AGUAbAAuAGMAbwBtAAAAAAALAEA6AQAAAB8AGgABAAAAEgAA AEkAUABNAC4ATgBvAHQAZQAAAAAAAwDxPwkEAAALAEA6AQAAAAMA/T/kBAAAAgELMAEAAAAQAAAA Ac0BRceqcE2W4lBxf7mdsgMAFwABAAAAQAA5AIBLWiqJLNUBQAAIMJpClCqJLNUBHwAAgIYDAgAA AAAAwAAAAAAAAEYBAAAAHgAAAGEAYwBjAGUAcAB0AGwAYQBuAGcAdQBhAGcAZQAAAAAAAQAAAAwA AABlAG4ALQBVAFMAAAAfADcAAQAAALoAAABbAGUAZABrADIALQBwAGwAYQB0AGYAbwByAG0AIABw AGEAdABjAGgAIABWADUAXQAgAFAAbABhAHQAZgBvAHIAbQAvAEkAbgB0AGUAbAA6AEEAZABkACAA VQBuAGkAVABvAG8AbAAgAGkAbgB0AG8AIABlAGQAawAyAC0AcABsAGEAdABmAG8AcgBtAHMALwBQ AGwAYQB0AGYAbwByAG0ALwBJAG4AdABlAGwALwBUAG8AbwBsAHMAAAAAAB8APQABAAAAAgAAAAAA AAADADYAAAAAAAIBcQABAAAAFgAAAAHVLIjo/LKtHxTME07DqyuPS+aVsKoAAB8AcAABAAAAugAA AFsAZQBkAGsAMgAtAHAAbABhAHQAZgBvAHIAbQAgAHAAYQB0AGMAaAAgAFYANQBdACAAUABsAGEA dABmAG8AcgBtAC8ASQBuAHQAZQBsADoAQQBkAGQAIABVAG4AaQBUAG8AbwBsACAAaQBuAHQAbwAg AGUAZABrADIALQBwAGwAYQB0AGYAbwByAG0AcwAvAFAAbABhAHQAZgBvAHIAbQAvAEkAbgB0AGUA bAAvAFQAbwBvAGwAcwAAAAAAHwA1EAEAAACQAAAAPABGAEEARAAwAEQANwBFADAAQQBFADAARgBB ADUANABEADkAOAA3AEYANgBFADcAMgA0ADMANQBDAEEARgBEADUAMABBAEYAOAA0AEYARAA4AEAA UwBIAFMATQBTAFgAMQAwADEALgBjAGMAcgAuAGMAbwByAHAALgBpAG4AdABlAGwALgBjAG8AbQA+ AAAAAwDeP59OAABAAAcwOuGRKoks1QECAQsAAQAAABAAAAABzQFFx6pwTZbiUHF/uZ2yAwAmAAAA AAACAUcAAQAAADMAAABjPVVTO2E9TUNJO3A9SW50ZWw7bD1TSFNNU1gxMDEtMTkwNjI3MDEzOTMx Wi0xMzE1NwAAAgEQMAEAAABGAAAAAAAAACZ3vTk77DhJpKYVPcule0IHAPrQ1+CuD6VNmH9uckNc r9UAAABEEV4AAKaTaDaIdmxLp0rT60eaTGQAAAlUj80AAAAAHwD6PwEAAAAYAAAARgBhAG4ALAAg AFoAaABpAGoAdQBYAAAAAwAJWQEAAABAAACACCAGAAAAAADAAAAAAAAARgAAAAC/hQAAkIyvKYks 1QELAACACCAGAAAAAADAAAAAAAAARgAAAACChQAAAAAAAAMAAIAIIAYAAAAAAMAAAAAAAABGAAAA AOuFAAAJBAAAHwAAgIYDAgAAAAAAwAAAAAAAAEYBAAAAGAAAAGQAbABwAC0AcAByAG8AZAB1AGMA dAAAAAEAAAAaAAAAZABsAHAAZQAtAHcAaQBuAGQAbwB3AHMAAAAAAB8AAICGAwIAAAAAAMAAAAAA AABGAQAAABgAAABkAGwAcAAtAHYAZQByAHMAaQBvAG4AAAABAAAAFgAAADEAMQAuADAALgA2ADAA MAAuADcAAAAAAB8AAICGAwIAAAAAAMAAAAAAAABGAQAAABoAAABkAGwAcAAtAHIAZQBhAGMAdABp AG8AbgAAAAAAAQAAABQAAABuAG8ALQBhAGMAdABpAG8AbgAAAAMADTT9PwAAHwAAgIYDAgAAAAAA wAAAAAAAAEYBAAAAIAAAAHgALQBtAHMALQBoAGEAcwAtAGEAdAB0AGEAYwBoAAAAAQAAAAIAAAAA AAAAHwAAgIYDAgAAAAAAwAAAAAAAAEYBAAAAIgAAAHgALQBvAHIAaQBnAGkAbgBhAHQAaQBuAGcA LQBpAHAAAAAAAAEAAAAgAAAAWwAxADAALgAyADMAOQAuADEAMgA3AC4ANAAwAF0AAAAFvw== --_000_FAD0D7E0AE0FA54D987F6E72435CAFD50AF84FD8SHSMSX101ccrcor_--