From nobody Sat May 4 15:58:16 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+42406+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+42406+1787277+3901457@groups.io; dmarc=fail(p=none dis=none) header.from=intel.com ARC-Seal: i=1; a=rsa-sha256; t=1560478446; cv=none; d=zoho.com; s=zohoarc; b=nK7vRnBmkqI8MKSRB7iaFNWkWBqp/uhvq+Zf+pYHvFQ9uy5FP6HIOW0k1xl+/2u7byFjkC5azVDITkxMpG1E5+ooq2STbQ6/B/KFqI7mXJcGqWWZeQkFBxeXgoC3vM/b73H1Z/e6YBjFTNC4VHrH5Ti+jtXogKhEGmBdMg+bGCY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1560478446; h=Content-Type:Cc:Date:From:List-Id:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Sender:Subject:To:ARC-Authentication-Results; bh=Ep7eiOUC11FBFy4vJHlfZ5UDGFZB3VO2VIV1U8j6OnA=; b=QK1m3gLqVAORx9lkFhg4WuIVswOq/3iPS2uusqOu3ozO2ECgr9WckiJ5pqB8ezeQn7Oj486bAoO7feYzVyOqTmNYHecEL3vKA40QIcpD9idM0Z9FffiDc8QRggJL6f3z3Q7eOnYfDOtNmRk4m6hXU3HOLL8IxK/bglns5md9tN4= 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+42406+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 1560478446897799.0001207887472; Thu, 13 Jun 2019 19:14:06 -0700 (PDT) Return-Path: X-Received: from mga05.intel.com (mga05.intel.com [192.55.52.43]) by groups.io with SMTP; Thu, 13 Jun 2019 19:14:05 -0700 X-Amp-Result: UNKNOWN X-Amp-Original-Verdict: FILE UNKNOWN X-Amp-File-Uploaded: False X-Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by fmsmga105.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 13 Jun 2019 19:14:04 -0700 X-ExtLoop1: 1 X-Received: from fmsmsx104.amr.corp.intel.com ([10.18.124.202]) by fmsmga001.fm.intel.com with ESMTP; 13 Jun 2019 19:14:04 -0700 X-Received: from fmsmsx123.amr.corp.intel.com (10.18.125.38) by fmsmsx104.amr.corp.intel.com (10.18.124.202) with Microsoft SMTP Server (TLS) id 14.3.408.0; Thu, 13 Jun 2019 19:14:04 -0700 X-Received: from shsmsx103.ccr.corp.intel.com (10.239.4.69) by fmsmsx123.amr.corp.intel.com (10.18.125.38) with Microsoft SMTP Server (TLS) id 14.3.408.0; Thu, 13 Jun 2019 19:14:03 -0700 X-Received: from shsmsx101.ccr.corp.intel.com ([169.254.1.104]) by SHSMSX103.ccr.corp.intel.com ([169.254.4.83]) with mapi id 14.03.0439.000; Fri, 14 Jun 2019 10:14:02 +0800 From: "Fan, ZhijuX" To: "devel@edk2.groups.io" CC: "Gao, Liming" , "Feng, Bob C" Subject: [edk2-devel] [PATCH V2] BaseTools:Add UniTool.py to Edk2\BaseTools\Scripts Thread-Topic: [PATCH V2] BaseTools:Add UniTool.py to Edk2\BaseTools\Scripts Thread-Index: AdUiVs7No+m31e7qTUuxV66MxTQ0Yw== Date: Fri, 14 Jun 2019 02:14:00 +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_FAD0D7E0AE0FA54D987F6E72435CAFD50AF81F1FSHSMSX101ccrcor_" Content-Language: en-US DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=groups.io; q=dns/txt; s=20140610; t=1560478446; bh=OzA6qxH6dbQaE5FHunVPFi9fIYXemFRV2Gcj/VJY+tg=; h=CC:Content-Type:Date:From:Reply-To:Subject:To; b=n6yA/SDA5M0CxmKRXreLoxKff1uz+YhAeORSTgwEb9aEDHbRX9eY42RU6C4D+rmYwsM rrACqPMbPwOv40F7nux7+b227mzJ2epy1skWrRYgLyxvrAtiCn81XajRRr0j3Fu5OwpZY n8fTY6hk3M5gzpZiFY58PqMY5FazrpxzdWI= X-Zoho-Virus-Status: 1 X-ZohoMail-DKIM: pass (identity @groups.io) --_000_FAD0D7E0AE0FA54D987F6E72435CAFD50AF81F1FSHSMSX101ccrcor_ 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: Bob Feng Cc: Liming Gao Signed-off-by: Zhiju.Fan --- BaseTools/Scripts/UniTool.py | 511 +++++++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 511 insertions(+) create mode 100644 BaseTools/Scripts/UniTool.py diff --git a/BaseTools/Scripts/UniTool.py b/BaseTools/Scripts/UniTool.py new file mode 100644 index 0000000000..952be520e5 --- /dev/null +++ b/BaseTools/Scripts/UniTool.py @@ -0,0 +1,511 @@ +## @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, sys, os, getopt, codecs, fnmatch + +# 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 {} + + +# ********************************************************************** +# description: Prints help information +# +# arguments: none +# +# returns: none +# + +def Usage(): + print("Syntax: %s [-b] [-u] [-l] [-x] [-h] [-d 'rootDirectory1'] [-d = 'rootDirectory2'] [-d 'rootDirectory3']... [-q e|w] \ +'rootDirectory0' 'uqiFile'|'uqiFileDirectory' ['excludedDirectory1'] ['exc= ludedDirectory2'] ['excludedDirectory3']...\n%s" % + (os.path.basename(sys.argv[0]), + """\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 + + Options: + -h Show this help + -b Build option returns error if any new UQI = needs assigning + based on vfi/vfr/hfr/sd/sdi when no -u opt= ion is specified + -u Create new UQIs that does not already exis= t in uqiFile for + any string requiring a UQI based on vfi/vf= r/hfr/sd/sdi + NOTE: 'uqiFile' cannot be readonly! + -l Language deletion option (keeps only Engli= sh and uqi) + moves all UQIs to 'uqiFile' + NOTE: Uni files cannot be readonly! + -x Exclude 'rootDirectory'/'excludedDirectory= 1' & + 'rootDirectory'/'excludedDirectory2'... fr= om UQI list build + NOTE: Cannot be the same as rootDirectory + -d Add multiple root directories to process + -q Print warning(w) or return error(e) if dif= ferent HII questions + are referring same string token + + 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 + """)) + sys.exit() + + +# ********************************************************************** +# 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(argv): + ##### Read input arguments and options + global AllUqis, UqiList, QuestionError + try: + Opts, Args =3D getopt.getopt(argv[1:], "hulbxd:q:") # each letter= is an optional argument + except getopt.GetoptError: + Usage() + try: + DirNameList =3D [Args[0]] + QuestionOption =3D None + for EachOpt in Opts: + if EachOpt[0] =3D=3D '-d': + DirNameList.append(EachOpt[1]) + if EachOpt[0] =3D=3D '-q': + QuestionOption =3D EachOpt[1] + if (QuestionOption !=3D "e") and (QuestionOption !=3D "w"): + print("\nERROR: invalid option value for -q option\n") + raise Exception + Destname =3D Args[1] + if len(Args) > 2: + ExDirList =3D Args[2:] + except: + Usage() + + UpdateUQIs =3D False + LangOption =3D False + BuildOption =3D False + ExcludeOption =3D False + ExPathList =3D [] + + for o, a in Opts: + if o =3D=3D "-h": + Usage() + if o =3D=3D "-b": + BuildOption =3D True + if o =3D=3D "-u": + BuildOption =3D True + UpdateUQIs =3D True + if o =3D=3D "-l": + LangOption =3D True + if o =3D=3D "-x": + ExcludeOption =3D True + try: + for EachExDir in ExDirList: + for EachRootDir in DirNameList: + if EachExDir =3D=3D EachRootDir: + print("\nERROR: excludedDirectory is same as r= ootDirectory\n") + raise Exception + ExPathList.append(EachRootDir + os.sep + EachExDir) + except: + Usage() + + global Options + Options =3D {'Destname': Destname, 'DirNameList': DirNameList, 'ExPath= List': ExPathList, 'BuildOption': BuildOption, + 'UpdateUQIs': UpdateUQIs, + 'LangOption': LangOption, 'ExcludeOption': ExcludeOption, '= QuestionOption': QuestionOption} + print("UQI file: %s" % Destname) + for EachDirName in DirNameList: + Walk(EachDirName, processUni, None) + if QuestionError: + raise RuntimeError + 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]) + raise RuntimeError + AllUqis[DestFileName] =3D UqiList(DestFileBuffer) + ReturnVal =3D 0 + if BuildOption: + ReturnVal =3D newUqi() + if (ReturnVal =3D=3D 1): + print('Please fix UQI ERROR(s) above before proceeding.') + raise RuntimeError + 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], St= ringVarIds(EachGroup)[0])) + except: + print("Error opening file at %s for reading." % Filename) + + +if __name__ =3D=3D '__main__': + sys.exit(main(sys.argv)) --=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 (#42406): https://edk2.groups.io/g/devel/message/42406 Mute This Topic: https://groups.io/mt/32059653/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_FAD0D7E0AE0FA54D987F6E72435CAFD50AF81F1FSHSMSX101ccrcor_ Content-Disposition: attachment; filename="winmail.dat" Content-Transfer-Encoding: base64 Content-Type: application/ms-tnef; name="winmail.dat" eJ8+IvddAQaQCAAEAAAAAAABAAEAAQeQBgAIAAAA5AQAAAAAAADoAAEJgAEAIQAAADYyRDFGODEz OUVCN0Y4NEJBRDlBOTFEMjMyMjNDOTY2ADsHAQ2ABAACAAAAAgACAAEFgAMADgAAAOMHBgAOAAIA DgAAAAUAEwEBIIADAA4AAADjBwYADgACAA4AAAAFABMBAQiABwAYAAAASVBNLk1pY3Jvc29mdCBN YWlsLk5vdGUAMQgBBIABAD4AAABbUEFUQ0ggVjJdIEJhc2VUb29sczpBZGQgVW5pVG9vbC5weSB0 byBFZGsyXEJhc2VUb29sc1xTY3JpcHRzAFUVAQuAAQAhAAAANjJEMUY4MTM5RUI3Rjg0QkFEOUE5 MUQyMzIyM0M5NjYAOwcBA5AGAFwsAAA0AAAAAgF/AAEAAABIAAAAPEZBRDBEN0UwQUUwRkE1NEQ5 ODdGNkU3MjQzNUNBRkQ1MEFGODFGMUZAU0hTTVNYMTAxLmNjci5jb3JwLmludGVsLmNvbT4ACwAf DgEAAAACAQkQAQAAAJQiAACQIgAAqmMAAExaRnVefoI7YQAKZmJpZAQAAGNjwHBnMTI1MgD+A0Pw dGV4dAH3AqQD4wIABGNoCsBzZXQwIO8HbQKDAFARTTIKgAa0AoCWfQqACMg7CWIxOQ7AvwnDFnIK MhZxAoAVYioJsHMJ8ASQYXQFsg5QA2Bzom8BgCBFeBHBbhgwXQZSdgSQF7YCEHIAwHR9CFBuGjEQ IAXABaAbZGSaIANSIBAiF7JcdgiQ5HdrC4BkNR1TBPAHQA0XcDAKcRfyYmttawZzAZAAICBCTV9C 4EVHSU59CvwB8QvxER+wWjpoAkBwczrALy9idWd6AxALYCQudAcwbm8FoWUuAQWwZy9zaG93X0Ei US5jZ2k/DdA9qDE4NR3gbAuAZQqBpSUUVQMAVG8G8CAEAMYgAiAZ4HB5dCPQA6AbBPUYkCAYNRng VVFJLCAoJeEaMXMHQCBR2wpQH2BpAiAlBUkBAAIw4waQCJEpIHUDAAWgAQA9JzB0BRAZwBxwBbFI SQUokHEpdSBQUk9N9FBUK4UuKGIlBSuVHlD1A6BiGeB1EgAcYCexDdBRKnN5IGUA0GgsOy65JQVU aCtxBQMEIGYrEN5jLMMD8CKgJzB5MwArAO5wKGMBAQuAaSmiBCAD8D8m8CsCHHADEAeQJQViYSsv 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 gSsfpL6jEBmdbgDdAQ9UAgAAAIBGAGEAbgAsACAAWgBoAGkAagB1AFgAAABTAE0AVABQAAAAegBo AGkAagB1AHgALgBmAGEAbgBAAGkAbgB0AGUAbAAuAGMAbwBtAAAAHwAJXQEAAAAqAAAAegBoAGkA agB1AHgALgBmAGEAbgBAAGkAbgB0AGUAbAAuAGMAbwBtAAAAAAALAEA6AQAAAB8AGgABAAAAEgAA AEkAUABNAC4ATgBvAHQAZQAAAAAAAwDxPwkEAAALAEA6AQAAAAMA/T/kBAAAAgELMAEAAAAQAAAA YtH4E563+EutmpHSMiPJZgMAFwABAAAAQAA5AADkM9RWItUBQAAIMOG/qtRWItUBHwAAgIYDAgAA AAAAwAAAAAAAAEYBAAAAHgAAAGEAYwBjAGUAcAB0AGwAYQBuAGcAdQBhAGcAZQAAAAAAAQAAAAwA AABlAG4ALQBVAFMAAAAfADcAAQAAAHwAAABbAFAAQQBUAEMASAAgAFYAMgBdACAAQgBhAHMAZQBU AG8AbwBsAHMAOgBBAGQAZAAgAFUAbgBpAFQAbwBvAGwALgBwAHkAIAB0AG8AIABFAGQAawAyAFwA QgBhAHMAZQBUAG8AbwBsAHMAXABTAGMAcgBpAHAAdABzAAAAHwA9AAEAAAACAAAAAAAAAAMANgAA AAAAAgFxAAEAAAAWAAAAAdUiVs7No+m31e7qTUuxV66MxTQ0YwAAHwBwAAEAAAB8AAAAWwBQAEEA VABDAEgAIABWADIAXQAgAEIAYQBzAGUAVABvAG8AbABzADoAQQBkAGQAIABVAG4AaQBUAG8AbwBs AC4AcAB5ACAAdABvACAARQBkAGsAMgBcAEIAYQBzAGUAVABvAG8AbABzAFwAUwBjAHIAaQBwAHQA cwAAAB8ANRABAAAAkAAAADwARgBBAEQAMABEADcARQAwAEEARQAwAEYAQQA1ADQARAA5ADgANwBG ADYARQA3ADIANAAzADUAQwBBAEYARAA1ADAAQQBGADgAMQBGADEARgBAAFMASABTAE0AUwBYADEA MAAxAC4AYwBjAHIALgBjAG8AcgBwAC4AaQBuAHQAZQBsAC4AYwBvAG0APgAAAAMA3j+fTgAAQAAH MGE6odRWItUBAgELAAEAAAAQAAAAYtH4E563+EutmpHSMiPJZgMAJgAAAAAAAgFHAAEAAAAyAAAA Yz1VUzthPU1DSTtwPUludGVsO2w9U0hTTVNYMTAxLTE5MDYxNDAyMTQwMFotMjY0MQAAAAIBEDAB AAAARgAAAAAAAAAmd705O+w4SaSmFT3LpXtCBwD60Nfgrg+lTZh/bnJDXK/VAAAARBFeAACmk2g2 iHZsS6dK0+tHmkxkAAAJVI+pAAAAAB8A+j8BAAAAGAAAAEYAYQBuACwAIABaAGgAaQBqAHUAWAAA AAMACVkBAAAAQAAAgAggBgAAAAAAwAAAAAAAAEYAAAAAv4UAADCawdNWItUBCwAAgAggBgAAAAAA wAAAAAAAAEYAAAAAgoUAAAAAAAADAACACCAGAAAAAADAAAAAAAAARgAAAADrhQAACQQAAB8AAICG AwIAAAAAAMAAAAAAAABGAQAAABgAAABkAGwAcAAtAHAAcgBvAGQAdQBjAHQAAAABAAAAGgAAAGQA bABwAGUALQB3AGkAbgBkAG8AdwBzAAAAAAAfAACAhgMCAAAAAADAAAAAAAAARgEAAAAYAAAAZABs AHAALQB2AGUAcgBzAGkAbwBuAAAAAQAAABYAAAAxADEALgAwAC4ANgAwADAALgA3AAAAAAAfAACA hgMCAAAAAADAAAAAAAAARgEAAAAaAAAAZABsAHAALQByAGUAYQBjAHQAaQBvAG4AAAAAAAEAAAAU AAAAbgBvAC0AYQBjAHQAaQBvAG4AAAADAA00/T8AAB8AAICGAwIAAAAAAMAAAAAAAABGAQAAACAA AAB4AC0AbQBzAC0AaABhAHMALQBhAHQAdABhAGMAaAAAAAEAAAACAAAAAAAAAB8AAICGAwIAAAAA AMAAAAAAAABGAQAAACIAAAB4AC0AbwByAGkAZwBpAG4AYQB0AGkAbgBnAC0AaQBwAAAAAAABAAAA IAAAAFsAMQAwAC4AMgAzADkALgAxADIANwAuADQAMABdAAAAQcI= --_000_FAD0D7E0AE0FA54D987F6E72435CAFD50AF81F1FSHSMSX101ccrcor_--