BaseTools: Fix flexible PCD single quote and double quote bugs

1.The " and ' inside the string, must use escape character format
  (\", \')
2.'string' and L'string' format in --pcd, it must be double quoted
  first.

  Some examples that to match --pcd format and DSC format
  --pcd                             DSC format
  L"ABC"                            L"ABC"
  "AB\\\"C"                         "AB\"C"
  "AB\\\'C"                         "AB\'C"
  L"\'AB\\\"C\'"                    L'AB\"C'
  "\'AB\\\'C\'"                     'AB\'C'
  H"{0, L\"AB\\\"B\", \'ab\\\"c\'}" {0, L"AB\"B", 'ab\"c'}

Cc: Liming Gao <liming.gao@intel.com>
Cc: Yonghong Zhu <yonghong.zhu@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Yunhua Feng <yunhuax.feng@intel.com>
Reviewed-by: Yonghong Zhu <yonghong.zhu@intel.com>
This commit is contained in:
Feng, YunhuaX
2018-02-26 16:42:30 +08:00
committed by Yonghong Zhu
parent f0c69b614c
commit ea927d2f3f
5 changed files with 123 additions and 65 deletions

View File

@ -1555,13 +1555,19 @@ class TopLevelMakefile(BuildFile):
for index, option in enumerate(GlobalData.gCommand): for index, option in enumerate(GlobalData.gCommand):
if "--pcd" == option and GlobalData.gCommand[index+1]: if "--pcd" == option and GlobalData.gCommand[index+1]:
pcdName, pcdValue = GlobalData.gCommand[index+1].split('=') pcdName, pcdValue = GlobalData.gCommand[index+1].split('=')
if pcdValue.startswith('H'): for Item in GlobalData.BuildOptionPcd:
pcdValue = 'H' + '"' + pcdValue[1:] + '"' if '.'.join(Item[0:2]) == pcdName:
ExtraOption += " --pcd " + pcdName + '=' + pcdValue pcdValue = Item[2]
elif pcdValue.startswith("L'"): if pcdValue.startswith('L') or pcdValue.startswith('"'):
ExtraOption += "--pcd " + pcdName + '=' + pcdValue pcdValue, Size = ParseFieldValue(pcdValue)
elif pcdValue.startswith('L'): NewVal = '{'
pcdValue = 'L' + '"' + pcdValue[1:] + '"' for S in range(Size):
NewVal = NewVal + '0x%02X' % ((pcdValue >> S * 8) & 0xff)
NewVal += ','
pcdValue = NewVal[:-1] + '}'
break
if pcdValue.startswith('{'):
pcdValue = 'H' + '"' + pcdValue + '"'
ExtraOption += " --pcd " + pcdName + '=' + pcdValue ExtraOption += " --pcd " + pcdName + '=' + pcdValue
else: else:
ExtraOption += " --pcd " + GlobalData.gCommand[index+1] ExtraOption += " --pcd " + GlobalData.gCommand[index+1]

View File

@ -45,15 +45,28 @@ ERR_IN_OPERAND = 'Macro after IN operator can only be: $(FAMILY), $(ARC
# For example: abc"de\"f"ghi"jkl"mn will be: ['abc', '"de\"f"', 'ghi', '"jkl"', 'mn'] # For example: abc"de\"f"ghi"jkl"mn will be: ['abc', '"de\"f"', 'ghi', '"jkl"', 'mn']
# #
def SplitString(String): def SplitString(String):
# There might be escaped quote: "abc\"def\\\"ghi" # There might be escaped quote: "abc\"def\\\"ghi", 'abc\'def\\\'ghi'
Str = String.replace('\\\\', '//').replace('\\\"', '\\\'') Str = String
RetList = [] RetList = []
InQuote = False InSingleQuote = False
InDoubleQuote = False
Item = '' Item = ''
for i, ch in enumerate(Str): for i, ch in enumerate(Str):
if ch == '"': if ch == '"' and not InSingleQuote:
InQuote = not InQuote if Str[i - 1] != '\\':
if not InQuote: InDoubleQuote = not InDoubleQuote
if not InDoubleQuote:
Item += String[i]
RetList.append(Item)
Item = ''
continue
if Item:
RetList.append(Item)
Item = ''
elif ch == "'" and not InDoubleQuote:
if Str[i - 1] != '\\':
InSingleQuote = not InSingleQuote
if not InSingleQuote:
Item += String[i] Item += String[i]
RetList.append(Item) RetList.append(Item)
Item = '' Item = ''
@ -62,7 +75,7 @@ def SplitString(String):
RetList.append(Item) RetList.append(Item)
Item = '' Item = ''
Item += String[i] Item += String[i]
if InQuote: if InSingleQuote or InDoubleQuote:
raise BadExpression(ERR_STRING_TOKEN % Item) raise BadExpression(ERR_STRING_TOKEN % Item)
if Item: if Item:
RetList.append(Item) RetList.append(Item)
@ -483,6 +496,8 @@ class ValueExpression(object):
Flag = 0 Flag = 0
for Index in range(len(self._Token)): for Index in range(len(self._Token)):
if self._Token[Index] in ['"']: if self._Token[Index] in ['"']:
if self._Token[Index - 1] == '\\':
continue
Flag += 1 Flag += 1
if Flag == 2 and self._Token.endswith('"'): if Flag == 2 and self._Token.endswith('"'):
return True return True
@ -490,6 +505,8 @@ class ValueExpression(object):
Flag = 0 Flag = 0
for Index in range(len(self._Token)): for Index in range(len(self._Token)):
if self._Token[Index] in ["'"]: if self._Token[Index] in ["'"]:
if self._Token[Index - 1] == '\\':
continue
Flag += 1 Flag += 1
if Flag == 2 and self._Token.endswith("'"): if Flag == 2 and self._Token.endswith("'"):
return True return True
@ -537,16 +554,25 @@ class ValueExpression(object):
self._Idx += 1 self._Idx += 1
# Replace escape \\\", \" # Replace escape \\\", \"
Expr = self._Expr[self._Idx:].replace('\\\\', '//').replace('\\\"', '\\\'') if self._Expr[Idx] == '"':
for Ch in Expr: Expr = self._Expr[self._Idx:].replace('\\\\', '//').replace('\\\"', '\\\'')
self._Idx += 1 for Ch in Expr:
if Ch == '"' or Ch == "'": self._Idx += 1
break if Ch == '"':
self._Token = self._LiteralToken = self._Expr[Idx:self._Idx] break
if self._Token.startswith('"') and not self._Token.endswith('"'): self._Token = self._LiteralToken = self._Expr[Idx:self._Idx]
raise BadExpression(ERR_STRING_TOKEN % self._Token) if not self._Token.endswith('"'):
if self._Token.startswith("'") and not self._Token.endswith("'"): raise BadExpression(ERR_STRING_TOKEN % self._Token)
raise BadExpression(ERR_STRING_TOKEN % self._Token) #Replace escape \\\', \'
elif self._Expr[Idx] == "'":
Expr = self._Expr[self._Idx:].replace('\\\\', '//').replace("\\\'", "\\\"")
for Ch in Expr:
self._Idx += 1
if Ch == "'":
break
self._Token = self._LiteralToken = self._Expr[Idx:self._Idx]
if not self._Token.endswith("'"):
raise BadExpression(ERR_STRING_TOKEN % self._Token)
self._Token = self._Token[1:-1] self._Token = self._Token[1:-1]
return self._Token return self._Token

View File

@ -1443,21 +1443,26 @@ def ParseConsoleLog(Filename):
def AnalyzePcdExpression(Setting): def AnalyzePcdExpression(Setting):
Setting = Setting.strip() Setting = Setting.strip()
# There might be escaped quote in a string: \", \\\" # There might be escaped quote in a string: \", \\\" , \', \\\'
Data = Setting.replace('\\\\', '//').replace('\\\"', '\\\'') Data = Setting
# There might be '|' in string and in ( ... | ... ), replace it with '-' # There might be '|' in string and in ( ... | ... ), replace it with '-'
NewStr = '' NewStr = ''
InStr = False InSingleQuoteStr = False
InDoubleQuoteStr = False
Pair = 0 Pair = 0
for ch in Data: for Index, ch in enumerate(Data):
if ch == '"': if ch == '"' and not InSingleQuoteStr:
InStr = not InStr if Data[Index - 1] != '\\':
elif ch == '(' and not InStr: InDoubleQuoteStr = not InDoubleQuoteStr
elif ch == "'" and not InDoubleQuoteStr:
if Data[Index - 1] != '\\':
InSingleQuoteStr = not InSingleQuoteStr
elif ch == '(' and not (InSingleQuoteStr or InDoubleQuoteStr):
Pair += 1 Pair += 1
elif ch == ')' and not InStr: elif ch == ')' and not (InSingleQuoteStr or InDoubleQuoteStr):
Pair -= 1 Pair -= 1
if (Pair > 0 or InStr) and ch == TAB_VALUE_SPLIT: if (Pair > 0 or InSingleQuoteStr or InDoubleQuoteStr) and ch == TAB_VALUE_SPLIT:
NewStr += '-' NewStr += '-'
else: else:
NewStr += ch NewStr += ch
@ -1549,7 +1554,7 @@ def ParseFieldValue (Value):
return Value, 16 return Value, 16
if Value.startswith('L"') and Value.endswith('"'): if Value.startswith('L"') and Value.endswith('"'):
# Unicode String # Unicode String
List = list(Value[2:-1]) List = list(eval(Value[1:])) # translate escape character
List.reverse() List.reverse()
Value = 0 Value = 0
for Char in List: for Char in List:
@ -1557,7 +1562,7 @@ def ParseFieldValue (Value):
return Value, (len(List) + 1) * 2 return Value, (len(List) + 1) * 2
if Value.startswith('"') and Value.endswith('"'): if Value.startswith('"') and Value.endswith('"'):
# ASCII String # ASCII String
List = list(Value[1:-1]) List = list(eval(Value)) # translate escape character
List.reverse() List.reverse()
Value = 0 Value = 0
for Char in List: for Char in List:
@ -1565,7 +1570,7 @@ def ParseFieldValue (Value):
return Value, len(List) + 1 return Value, len(List) + 1
if Value.startswith("L'") and Value.endswith("'"): if Value.startswith("L'") and Value.endswith("'"):
# Unicode Character Constant # Unicode Character Constant
List = list(Value[2:-1]) List = list(eval(Value[1:])) # translate escape character
if len(List) == 0: if len(List) == 0:
raise BadExpression('Length %s is %s' % (Value, len(List))) raise BadExpression('Length %s is %s' % (Value, len(List)))
List.reverse() List.reverse()
@ -1575,7 +1580,7 @@ def ParseFieldValue (Value):
return Value, len(List) * 2 return Value, len(List) * 2
if Value.startswith("'") and Value.endswith("'"): if Value.startswith("'") and Value.endswith("'"):
# Character constant # Character constant
List = list(Value[1:-1]) List = list(eval(Value)) # translate escape character
if len(List) == 0: if len(List) == 0:
raise BadExpression('Length %s is %s' % (Value, len(List))) raise BadExpression('Length %s is %s' % (Value, len(List)))
List.reverse() List.reverse()

View File

@ -45,26 +45,32 @@ def GetSplitValueList(String, SplitTag=DataType.TAB_VALUE_SPLIT, MaxSplit= -1):
ValueList = [] ValueList = []
Last = 0 Last = 0
Escaped = False Escaped = False
InString = False InSingleQuoteString = False
InDoubleQuoteString = False
InParenthesis = 0 InParenthesis = 0
for Index in range(0, len(String)): for Index in range(0, len(String)):
Char = String[Index] Char = String[Index]
if not Escaped: if not Escaped:
# Found a splitter not in a string, split it # Found a splitter not in a string, split it
if not InString and InParenthesis == 0 and Char == SplitTag: if (not InSingleQuoteString or not InDoubleQuoteString) and InParenthesis == 0 and Char == SplitTag:
ValueList.append(String[Last:Index].strip()) ValueList.append(String[Last:Index].strip())
Last = Index + 1 Last = Index + 1
if MaxSplit > 0 and len(ValueList) >= MaxSplit: if MaxSplit > 0 and len(ValueList) >= MaxSplit:
break break
if Char == '\\' and InString: if Char == '\\' and (InSingleQuoteString or InDoubleQuoteString):
Escaped = True Escaped = True
elif Char == '"': elif Char == '"' and not InSingleQuoteString:
if not InString: if not InDoubleQuoteString:
InString = True InDoubleQuoteString = True
else: else:
InString = False InDoubleQuoteString = False
elif Char == "'" and not InDoubleQuoteString:
if not InSingleQuoteString:
InSingleQuoteString = True
else:
InSingleQuoteString = False
elif Char == '(': elif Char == '(':
InParenthesis = InParenthesis + 1 InParenthesis = InParenthesis + 1
elif Char == ')': elif Char == ')':
@ -345,14 +351,17 @@ def CleanString(Line, CommentCharacter=DataType.TAB_COMMENT_SPLIT, AllowCppStyle
# #
# remove comments, but we should escape comment character in string # remove comments, but we should escape comment character in string
# #
InString = False InDoubleQuoteString = False
InSingleQuoteString = False
CommentInString = False CommentInString = False
for Index in range(0, len(Line)): for Index in range(0, len(Line)):
if Line[Index] == '"': if Line[Index] == '"' and not InSingleQuoteString:
InString = not InString InDoubleQuoteString = not InDoubleQuoteString
elif Line[Index] == CommentCharacter and InString : elif Line[Index] == "'" and not InDoubleQuoteString:
InSingleQuoteString = not InSingleQuoteString
elif Line[Index] == CommentCharacter and (InSingleQuoteString or InDoubleQuoteString):
CommentInString = True CommentInString = True
elif Line[Index] == CommentCharacter and not InString : elif Line[Index] == CommentCharacter and not (InSingleQuoteString or InDoubleQuoteString):
Line = Line[0: Index] Line = Line[0: Index]
break break
@ -402,15 +411,18 @@ def CleanString2(Line, CommentCharacter=DataType.TAB_COMMENT_SPLIT, AllowCppStyl
# #
# separate comments and statements, but we should escape comment character in string # separate comments and statements, but we should escape comment character in string
# #
InString = False InDoubleQuoteString = False
InSingleQuoteString = False
CommentInString = False CommentInString = False
Comment = '' Comment = ''
for Index in range(0, len(Line)): for Index in range(0, len(Line)):
if Line[Index] == '"': if Line[Index] == '"' and not InSingleQuoteString:
InString = not InString InDoubleQuoteString = not InDoubleQuoteString
elif Line[Index] == CommentCharacter and InString: elif Line[Index] == "'" and not InDoubleQuoteString:
InSingleQuoteString = not InSingleQuoteString
elif Line[Index] == CommentCharacter and (InDoubleQuoteString or InSingleQuoteString):
CommentInString = True CommentInString = True
elif Line[Index] == CommentCharacter and not InString: elif Line[Index] == CommentCharacter and not (InDoubleQuoteString or InSingleQuoteString):
Comment = Line[Index:].strip() Comment = Line[Index:].strip()
Line = Line[0:Index].strip() Line = Line[0:Index].strip()
break break

View File

@ -991,6 +991,8 @@ class DscBuildData(PlatformBuildClassObject):
NewValue = self.GetFieldValueFromComm(pcdvalue, TokenSpaceGuidCName, TokenCName, FieldName) NewValue = self.GetFieldValueFromComm(pcdvalue, TokenSpaceGuidCName, TokenCName, FieldName)
GlobalData.BuildOptionPcd[i] = (TokenSpaceGuidCName, TokenCName, FieldName,NewValue,("build command options",1)) GlobalData.BuildOptionPcd[i] = (TokenSpaceGuidCName, TokenCName, FieldName,NewValue,("build command options",1))
else: else:
# Replace \' to ', \\\' to \'
pcdvalue = pcdvalue.replace("\\\\\\'", '\\\\\\"').replace('\\\'', '\'').replace('\\\\\\"', "\\'")
for key in self.DecPcds: for key in self.DecPcds:
PcdItem = self.DecPcds[key] PcdItem = self.DecPcds[key]
if HasTokenSpace: if HasTokenSpace:
@ -1002,7 +1004,7 @@ class DscBuildData(PlatformBuildClassObject):
except BadExpression, Value: except BadExpression, Value:
EdkLogger.error('Parser', FORMAT_INVALID, 'PCD [%s.%s] Value "%s", %s' % EdkLogger.error('Parser', FORMAT_INVALID, 'PCD [%s.%s] Value "%s", %s' %
(TokenSpaceGuidCName, TokenCName, pcdvalue, Value)) (TokenSpaceGuidCName, TokenCName, pcdvalue, Value))
if PcdDatumType == "VOID*": if PcdDatumType not in [TAB_UINT8, TAB_UINT16, TAB_UINT32, TAB_UINT64, 'BOOLEAN']:
pcdvalue = 'H' + pcdvalue pcdvalue = 'H' + pcdvalue
elif pcdvalue.startswith("L'"): elif pcdvalue.startswith("L'"):
try: try:
@ -1010,7 +1012,7 @@ class DscBuildData(PlatformBuildClassObject):
except BadExpression, Value: except BadExpression, Value:
EdkLogger.error('Parser', FORMAT_INVALID, 'PCD [%s.%s] Value "%s", %s' % EdkLogger.error('Parser', FORMAT_INVALID, 'PCD [%s.%s] Value "%s", %s' %
(TokenSpaceGuidCName, TokenCName, pcdvalue, Value)) (TokenSpaceGuidCName, TokenCName, pcdvalue, Value))
if pcdvalue.startswith('{'): if PcdDatumType not in [TAB_UINT8, TAB_UINT16, TAB_UINT32, TAB_UINT64, 'BOOLEAN']:
pcdvalue = 'H' + pcdvalue pcdvalue = 'H' + pcdvalue
elif pcdvalue.startswith("'"): elif pcdvalue.startswith("'"):
try: try:
@ -1018,7 +1020,7 @@ class DscBuildData(PlatformBuildClassObject):
except BadExpression, Value: except BadExpression, Value:
EdkLogger.error('Parser', FORMAT_INVALID, 'PCD [%s.%s] Value "%s", %s' % EdkLogger.error('Parser', FORMAT_INVALID, 'PCD [%s.%s] Value "%s", %s' %
(TokenSpaceGuidCName, TokenCName, pcdvalue, Value)) (TokenSpaceGuidCName, TokenCName, pcdvalue, Value))
if pcdvalue.startswith('{'): if PcdDatumType not in [TAB_UINT8, TAB_UINT16, TAB_UINT32, TAB_UINT64, 'BOOLEAN']:
pcdvalue = 'H' + pcdvalue pcdvalue = 'H' + pcdvalue
elif pcdvalue.startswith('L'): elif pcdvalue.startswith('L'):
pcdvalue = 'L"' + pcdvalue[1:] + '"' pcdvalue = 'L"' + pcdvalue[1:] + '"'
@ -1031,8 +1033,12 @@ class DscBuildData(PlatformBuildClassObject):
try: try:
pcdvalue = ValueExpressionEx(pcdvalue, PcdDatumType, self._GuidDict)(True) pcdvalue = ValueExpressionEx(pcdvalue, PcdDatumType, self._GuidDict)(True)
except BadExpression, Value: except BadExpression, Value:
EdkLogger.error('Parser', FORMAT_INVALID, 'PCD [%s.%s] Value "%s", %s' % try:
(TokenSpaceGuidCName, TokenCName, pcdvalue, Value)) pcdvalue = '"' + pcdvalue + '"'
pcdvalue = ValueExpressionEx(pcdvalue, PcdDatumType, self._GuidDict)(True)
except BadExpression, Value:
EdkLogger.error('Parser', FORMAT_INVALID, 'PCD [%s.%s] Value "%s", %s' %
(TokenSpaceGuidCName, TokenCName, pcdvalue, Value))
NewValue = BuildOptionPcdValueFormat(TokenSpaceGuidCName, TokenCName, PcdDatumType, pcdvalue) NewValue = BuildOptionPcdValueFormat(TokenSpaceGuidCName, TokenCName, PcdDatumType, pcdvalue)
FoundFlag = True FoundFlag = True
else: else:
@ -1048,7 +1054,7 @@ class DscBuildData(PlatformBuildClassObject):
except BadExpression, Value: except BadExpression, Value:
EdkLogger.error('Parser', FORMAT_INVALID, 'PCD [%s.%s] Value "%s", %s' % EdkLogger.error('Parser', FORMAT_INVALID, 'PCD [%s.%s] Value "%s", %s' %
(TokenSpaceGuidCName, TokenCName, pcdvalue, Value)) (TokenSpaceGuidCName, TokenCName, pcdvalue, Value))
if PcdDatumType == "VOID*": if PcdDatumType not in [TAB_UINT8, TAB_UINT16, TAB_UINT32, TAB_UINT64,'BOOLEAN']:
pcdvalue = 'H' + pcdvalue pcdvalue = 'H' + pcdvalue
elif pcdvalue.startswith("L'"): elif pcdvalue.startswith("L'"):
try: try:
@ -1057,7 +1063,7 @@ class DscBuildData(PlatformBuildClassObject):
except BadExpression, Value: except BadExpression, Value:
EdkLogger.error('Parser', FORMAT_INVALID, 'PCD [%s.%s] Value "%s", %s' % EdkLogger.error('Parser', FORMAT_INVALID, 'PCD [%s.%s] Value "%s", %s' %
(TokenSpaceGuidCName, TokenCName, pcdvalue, Value)) (TokenSpaceGuidCName, TokenCName, pcdvalue, Value))
if pcdvalue.startswith('{'): if PcdDatumType not in [TAB_UINT8, TAB_UINT16, TAB_UINT32, TAB_UINT64, 'BOOLEAN']:
pcdvalue = 'H' + pcdvalue pcdvalue = 'H' + pcdvalue
elif pcdvalue.startswith("'"): elif pcdvalue.startswith("'"):
try: try:
@ -1066,7 +1072,7 @@ class DscBuildData(PlatformBuildClassObject):
except BadExpression, Value: except BadExpression, Value:
EdkLogger.error('Parser', FORMAT_INVALID, 'PCD [%s.%s] Value "%s", %s' % EdkLogger.error('Parser', FORMAT_INVALID, 'PCD [%s.%s] Value "%s", %s' %
(TokenSpaceGuidCName, TokenCName, pcdvalue, Value)) (TokenSpaceGuidCName, TokenCName, pcdvalue, Value))
if pcdvalue.startswith('{'): if PcdDatumType not in [TAB_UINT8, TAB_UINT16, TAB_UINT32, TAB_UINT64, 'BOOLEAN']:
pcdvalue = 'H' + pcdvalue pcdvalue = 'H' + pcdvalue
elif pcdvalue.startswith('L'): elif pcdvalue.startswith('L'):
pcdvalue = 'L"' + pcdvalue[1:] + '"' pcdvalue = 'L"' + pcdvalue[1:] + '"'
@ -1080,9 +1086,12 @@ class DscBuildData(PlatformBuildClassObject):
try: try:
pcdvalue = ValueExpressionEx(pcdvalue, PcdDatumType, self._GuidDict)(True) pcdvalue = ValueExpressionEx(pcdvalue, PcdDatumType, self._GuidDict)(True)
except BadExpression, Value: except BadExpression, Value:
EdkLogger.error('Parser', FORMAT_INVALID, try:
'PCD [%s.%s] Value "%s", %s' % pcdvalue = '"' + pcdvalue + '"'
(TokenSpaceGuidCName, TokenCName, pcdvalue, Value)) pcdvalue = ValueExpressionEx(pcdvalue, PcdDatumType, self._GuidDict)(True)
except BadExpression, Value:
EdkLogger.error('Parser', FORMAT_INVALID, 'PCD [%s.%s] Value "%s", %s' %
(TokenSpaceGuidCName, TokenCName, pcdvalue, Value))
NewValue = BuildOptionPcdValueFormat(TokenSpaceGuidCName, TokenCName, PcdDatumType, pcdvalue) NewValue = BuildOptionPcdValueFormat(TokenSpaceGuidCName, TokenCName, PcdDatumType, pcdvalue)
FoundFlag = True FoundFlag = True
else: else: