in Sharpmake/Resolver.cs [315:461]
public string Resolve(string str, object fallbackValue, out bool wasChanged)
{
wasChanged = false;
// Early out
if (str == null)
return str;
StringBuilder builder = null;
// Support escape char for MemberPath
// [[MyString]] will convert to [MyString]
bool containsEscaped = false;
while (true)
{
int startMatch = 0;
int nbrReplacements = 0;
int currentSearchIndex = 0;
int endMatch = 0;
int strLength = str.Length;
while (currentSearchIndex < strLength)
{
// Find match range.
startMatch = -1;
int matchTypeIndex;
for (matchTypeIndex = 0; matchTypeIndex < _pathBeginStrings.Length; ++matchTypeIndex)
{
// Note that specifying StringComparison.Ordinal saves ~30% of the time passed in IndexOf.
startMatch = str.IndexOf(_pathBeginStrings[matchTypeIndex], currentSearchIndex, StringComparison.Ordinal);
if (startMatch != -1)
break;
}
if (startMatch == -1)
break;
endMatch = str.IndexOfAny(_pathEndCharacters, startMatch + 1);
if (endMatch == -1)
break;
if (builder == null)
builder = new StringBuilder(str.Length + 128);
// Append what's before the match
if (startMatch - currentSearchIndex > 0)
builder.Append(str, currentSearchIndex, startMatch - currentSearchIndex);
bool isValidMember = true;
int startMatchLength = _pathBeginStrings[matchTypeIndex].Length;
int memberStartIndex = startMatch + startMatchLength;
for (int i = memberStartIndex; i < endMatch; ++i)
{
char currentChar = str[i];
if (!(currentChar >= 'a' && currentChar <= 'z' ||
currentChar >= 'A' && currentChar <= 'Z' ||
currentChar >= '0' && currentChar <= '9' ||
currentChar == '.' || currentChar == '_' ||
currentChar == ':'
))
{
isValidMember = false;
}
}
// A string is escaped if the _PathBeginStrings/_PathEndStrings char is doubled (ie [[ ]])
// Also make sure that matchTypeIndex is a char, not a string
bool isEscaped = _pathBeginStrings[matchTypeIndex].Length == 1 &&
memberStartIndex > 1 && endMatch < str.Length - 1 &&
str[memberStartIndex - 2] == str[memberStartIndex - 1] &&
str[endMatch] == str[endMatch + 1];
containsEscaped |= isEscaped;
if (isValidMember && !isEscaped)
{
bool throwIfNotFound = fallbackValue == null;
string resolveResult;
try
{
resolveResult = GetMemberStringValue(str.Substring(memberStartIndex, endMatch - memberStartIndex), throwIfNotFound) ?? fallbackValue?.ToString();
}
catch (NotFoundException e)
{
throw new Error(
"Error: {0} in '{1}'\n{2}",
e.Message,
str,
e.Arguments
);
}
if (resolveResult == null)
{
// Resolve failed.
builder.Append(str, startMatch, endMatch - startMatch + 1);
}
else
{
++nbrReplacements;
builder.Append(resolveResult);
}
currentSearchIndex = endMatch + 1;
}
else
{
builder.Append(str, startMatch, startMatchLength);
currentSearchIndex = startMatch + startMatchLength;
}
}
if (nbrReplacements == 0 && currentSearchIndex == 0)
break;
builder.Append(str, currentSearchIndex, strLength - currentSearchIndex);
str = builder.ToString();
wasChanged = true;
builder.Clear();
if (nbrReplacements == 0)
break;
}
if (!containsEscaped)
return str;
// Now that we have done all replace, convert all escaped char.
foreach (string beginStr in _pathBeginStrings)
{
if (beginStr.Length != 1)
continue;
string escapedStr = beginStr + beginStr;
wasChanged = true;
str = str.Replace(escapedStr, beginStr);
}
foreach (char endChar in _pathEndCharacters)
{
string endStr = string.Empty + endChar;
string escapedStr = endStr + endStr;
wasChanged = true;
str = str.Replace(escapedStr, endStr);
}
return str;
}