1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 // ============================================================
6 // TextualIdentityParser.cpp
11 // Implements the TextualIdentityParser class
13 // ============================================================
15 #define DISABLE_BINDER_DEBUG_LOGGING
17 #include "textualidentityparser.hpp"
18 #include "assemblyidentity.hpp"
23 #define GO_IF_SEEN(kAssemblyIdentityFlag) \
24 if ((m_dwAttributesSeen & kAssemblyIdentityFlag) != 0) \
31 m_dwAttributesSeen |= kAssemblyIdentityFlag; \
34 #define GO_IF_WILDCARD(valueString) \
36 SmallStackSString wildCard(W("*")); \
37 if (valueString.Equals(wildCard)) \
43 #define GO_IF_VALIDATE_FAILED(validateProc, kIdentityFlag) \
44 if (!validateProc(valueString)) \
51 m_pAssemblyIdentity->SetHave(kIdentityFlag); \
54 #define FROMHEX(a) ((a)>=W('a') ? a - W('a') + 10 : a - W('0'))
55 #define TOHEX(a) ((a)>=10 ? W('a')+(a)-10 : W('0')+(a))
56 #define TOLOWER(a) (((a) >= W('A') && (a) <= W('Z')) ? (W('a') + (a - W('A'))) : (a))
58 namespace BINDER_SPACE
62 const int iPublicKeyTokenLength = 8;
64 const int iVersionMax = 65535;
65 const int iVersionParts = 4;
67 inline void UnicodeHexToBin(LPCWSTR pSrc, UINT cSrc, LPBYTE pDest)
76 for (UINT i = 0; i < cSrc-1; i+=2)
78 v = (BYTE)FROMHEX(TOLOWER(ps[i])) << 4;
79 v |= FROMHEX(TOLOWER(ps[i+1]));
84 inline void BinToUnicodeHex(const BYTE *pSrc, UINT cSrc, __out_ecount(2*cSrc) LPWSTR pDst)
89 for (x = 0, y = 0 ; x < cSrc; ++x)
94 pDst[y++] = (WCHAR)TOHEX(v);
96 pDst[y++] = (WCHAR)TOHEX(v);
100 inline BOOL EqualsCaseInsensitive(SString &a, LPCWSTR wzB)
102 SString b(SString::Literal, wzB);
104 return ::BINDER_SPACE::EqualsCaseInsensitive(a, b);
107 BOOL ValidateHex(SString &publicKeyOrToken)
109 if ((publicKeyOrToken.GetCount() == 0) || ((publicKeyOrToken.GetCount() % 2) != 0))
114 SString::Iterator cursor = publicKeyOrToken.Begin();
115 SString::Iterator end = publicKeyOrToken.End() - 1;
117 while (cursor <= end)
119 WCHAR wcCurrentChar = cursor[0];
121 if (((wcCurrentChar >= W('0')) && (wcCurrentChar <= W('9'))) ||
122 ((wcCurrentChar >= W('a')) && (wcCurrentChar <= W('f'))) ||
123 ((wcCurrentChar >= W('A')) && (wcCurrentChar <= W('F'))))
135 inline BOOL ValidatePublicKeyToken(SString &publicKeyToken)
137 return ((publicKeyToken.GetCount() == (iPublicKeyTokenLength * 2)) &&
138 ValidateHex(publicKeyToken));
141 inline BOOL ValidatePublicKey(SString &publicKey)
143 return ValidateHex(publicKey);
149 } wszKnownArchitectures[] = { { W("x86"), peI386 },
150 { W("IA64"), peIA64 },
151 { W("AMD64"), peAMD64 },
153 { W("MSIL"), peMSIL } };
155 BOOL ValidateAndConvertProcessorArchitecture(SString &processorArchitecture,
156 PEKIND *pkProcessorArchitecture)
158 for (int i = LENGTH_OF(wszKnownArchitectures); i--;)
160 if (EqualsCaseInsensitive(processorArchitecture, wszKnownArchitectures[i].strValue))
162 *pkProcessorArchitecture = wszKnownArchitectures[i].enumValue;
170 LPCWSTR PeKindToString(PEKIND kProcessorArchitecture)
172 _ASSERTE(kProcessorArchitecture != peNone);
174 for (int i = LENGTH_OF(wszKnownArchitectures); i--;)
176 if (wszKnownArchitectures[i].enumValue == kProcessorArchitecture)
178 return wszKnownArchitectures[i].strValue;
185 LPCWSTR ContentTypeToString(AssemblyContentType kContentType)
187 _ASSERTE(kContentType != AssemblyContentType_Default);
189 if (kContentType == AssemblyContentType_WindowsRuntime)
191 return W("WindowsRuntime");
196 }; // namespace (anonymous)
198 TextualIdentityParser::TextualIdentityParser(AssemblyIdentity *pAssemblyIdentity)
200 m_pAssemblyIdentity = pAssemblyIdentity;
201 m_dwAttributesSeen = AssemblyIdentity::IDENTITY_FLAG_EMPTY;
204 TextualIdentityParser::~TextualIdentityParser()
206 // Nothing to do here
209 BOOL TextualIdentityParser::IsSeparatorChar(WCHAR wcChar)
211 return ((wcChar == W(',')) || (wcChar == W('=')));
214 StringLexer::LEXEME_TYPE TextualIdentityParser::GetLexemeType(WCHAR wcChar)
219 return LEXEME_TYPE_EQUALS;
221 return LEXEME_TYPE_COMMA;
223 return LEXEME_TYPE_END_OF_STREAM;
225 return LEXEME_TYPE_STRING;
230 HRESULT TextualIdentityParser::Parse(SString &textualIdentity,
231 AssemblyIdentity *pAssemblyIdentity,
232 BOOL fPermitUnescapedQuotes)
235 BINDER_LOG_ENTER(W("TextualIdentityParser::Parse"));
237 IF_FALSE_GO(pAssemblyIdentity != NULL);
239 BINDER_LOG_STRING(W("textualIdentity"), textualIdentity);
243 TextualIdentityParser identityParser(pAssemblyIdentity);
245 if (!identityParser.Parse(textualIdentity, fPermitUnescapedQuotes))
247 IF_FAIL_GO(FUSION_E_INVALID_NAME);
250 EX_CATCH_HRESULT(hr);
253 BINDER_LOG_LEAVE_HR(W("TextualIdentityParser::Parse"), hr);
258 HRESULT TextualIdentityParser::ToString(AssemblyIdentity *pAssemblyIdentity,
259 DWORD dwIdentityFlags,
260 SString &textualIdentity)
263 BINDER_LOG_ENTER(W("TextualIdentityParser::ToString"));
265 IF_FALSE_GO(pAssemblyIdentity != NULL);
269 SmallStackSString tmpString;
271 textualIdentity.Clear();
273 if (pAssemblyIdentity->m_simpleName.IsEmpty())
278 EscapeString(pAssemblyIdentity->m_simpleName, tmpString);
279 textualIdentity.Append(tmpString);
281 if (AssemblyIdentity::Have(dwIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_VERSION))
284 tmpString.Printf(W("%d.%d.%d.%d"),
285 (DWORD)(USHORT)pAssemblyIdentity->m_version.GetMajor(),
286 (DWORD)(USHORT)pAssemblyIdentity->m_version.GetMinor(),
287 (DWORD)(USHORT)pAssemblyIdentity->m_version.GetBuild(),
288 (DWORD)(USHORT)pAssemblyIdentity->m_version.GetRevision());
290 textualIdentity.Append(W(", Version="));
291 textualIdentity.Append(tmpString);
294 if (AssemblyIdentity::Have(dwIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_CULTURE))
296 textualIdentity.Append(W(", Culture="));
297 if (pAssemblyIdentity->m_cultureOrLanguage.IsEmpty())
299 textualIdentity.Append(W("neutral"));
303 EscapeString(pAssemblyIdentity->m_cultureOrLanguage, tmpString);
304 textualIdentity.Append(tmpString);
308 if (AssemblyIdentity::Have(dwIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY))
310 textualIdentity.Append(W(", PublicKey="));
312 BlobToHex(pAssemblyIdentity->m_publicKeyOrTokenBLOB, tmpString);
313 textualIdentity.Append(tmpString);
315 else if (AssemblyIdentity::Have(dwIdentityFlags,
316 AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN))
318 textualIdentity.Append(W(", PublicKeyToken="));
320 BlobToHex(pAssemblyIdentity->m_publicKeyOrTokenBLOB, tmpString);
321 textualIdentity.Append(tmpString);
323 else if (AssemblyIdentity::Have(dwIdentityFlags,
324 AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL))
326 textualIdentity.Append(W(", PublicKeyToken=null"));
329 if (AssemblyIdentity::Have(dwIdentityFlags,
330 AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE))
332 textualIdentity.Append(W(", processorArchitecture="));
333 textualIdentity.Append(PeKindToString(pAssemblyIdentity->m_kProcessorArchitecture));
336 if (AssemblyIdentity::Have(dwIdentityFlags,
337 AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE))
339 textualIdentity.Append(W(", Retargetable=Yes"));
342 if (AssemblyIdentity::Have(dwIdentityFlags,
343 AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE))
345 textualIdentity.Append(W(", ContentType="));
346 textualIdentity.Append(ContentTypeToString(pAssemblyIdentity->m_kContentType));
349 if (AssemblyIdentity::Have(dwIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_CUSTOM))
351 textualIdentity.Append(W(", Custom="));
353 BlobToHex(pAssemblyIdentity->m_customBLOB, tmpString);
354 textualIdentity.Append(tmpString);
356 else if (AssemblyIdentity::Have(dwIdentityFlags,
357 AssemblyIdentity::IDENTITY_FLAG_CUSTOM_NULL))
359 textualIdentity.Append(W(", Custom=null"));
362 EX_CATCH_HRESULT(hr);
365 BINDER_LOG_LEAVE_HR(W("TextualIdentityParser::ToString"), hr);
370 BOOL TextualIdentityParser::ParseVersion(SString &versionString,
371 AssemblyVersion *pAssemblyVersion)
373 BOOL fIsValid = FALSE;
374 DWORD dwFoundNumbers = 0;
375 bool foundUnspecifiedComponent = false;
376 const DWORD UnspecifiedNumber = (DWORD)-1;
377 DWORD dwCurrentNumber = UnspecifiedNumber;
378 DWORD dwNumbers[iVersionParts] = {UnspecifiedNumber, UnspecifiedNumber, UnspecifiedNumber, UnspecifiedNumber};
380 BINDER_LOG_ENTER(W("TextualIdentityParser::ParseVersion"));
382 if (versionString.GetCount() > 0) {
383 SString::Iterator cursor = versionString.Begin();
384 SString::Iterator end = versionString.End();
386 while (cursor <= end)
388 WCHAR wcCurrentChar = cursor[0];
390 if (dwFoundNumbers >= static_cast<DWORD>(iVersionParts))
394 else if (wcCurrentChar == W('.') || wcCurrentChar == W('\0'))
396 if (dwCurrentNumber == UnspecifiedNumber)
398 // Difference from .NET Framework, compat with Version(string) constructor: A missing version component
399 // is considered invalid.
402 // "MyAssembly, Version=."
403 // "MyAssembly, Version=1."
404 // "MyAssembly, Version=.1"
405 // "MyAssembly, Version=1..1"
409 // Compat with .NET Framework: A value of iVersionMax is considered unspecified. Once an unspecified
410 // component is found, validate the remaining components but consider them as unspecified as well.
411 if (dwCurrentNumber == iVersionMax)
413 foundUnspecifiedComponent = true;
414 dwCurrentNumber = UnspecifiedNumber;
416 else if (!foundUnspecifiedComponent)
418 dwNumbers[dwFoundNumbers] = dwCurrentNumber;
419 dwCurrentNumber = UnspecifiedNumber;
424 else if ((wcCurrentChar >= W('0')) && (wcCurrentChar <= W('9')))
426 if (dwCurrentNumber == UnspecifiedNumber)
430 dwCurrentNumber = (dwCurrentNumber * 10) + (wcCurrentChar - W('0'));
432 if (dwCurrentNumber > static_cast<DWORD>(iVersionMax))
445 // Difference from .NET Framework: If the major or minor version are unspecified, the version is considered invalid.
448 // "MyAssembly, Version="
449 // "MyAssembly, Version=1"
450 // "MyAssembly, Version=65535.1"
451 // "MyAssembly, Version=1.65535"
452 if (dwFoundNumbers < 2 || dwNumbers[0] == UnspecifiedNumber || dwNumbers[1] == UnspecifiedNumber)
457 pAssemblyVersion->SetFeatureVersion(dwNumbers[0], dwNumbers[1]);
458 pAssemblyVersion->SetServiceVersion(dwNumbers[2], dwNumbers[3]);
463 BINDER_LOG_LEAVE(W("TextualIdentityParser::ParseVersion"));
468 BOOL TextualIdentityParser::HexToBlob(SString &publicKeyOrToken,
471 SBuffer &publicKeyOrTokenBLOB)
473 // Optional input verification
476 if ((fIsToken && !ValidatePublicKeyToken(publicKeyOrToken)) ||
477 (!fIsToken && !ValidatePublicKey(publicKeyOrToken)))
483 UINT ccPublicKeyOrToken = publicKeyOrToken.GetCount();
484 BYTE *pByteBLOB = publicKeyOrTokenBLOB.OpenRawBuffer(ccPublicKeyOrToken / 2);
486 UnicodeHexToBin(publicKeyOrToken.GetUnicode(), ccPublicKeyOrToken, pByteBLOB);
487 publicKeyOrTokenBLOB.CloseRawBuffer();
493 void TextualIdentityParser::BlobToHex(SBuffer &publicKeyOrTokenBLOB,
494 SString &publicKeyOrToken)
496 UINT cbPublicKeyOrTokenBLOB = publicKeyOrTokenBLOB.GetSize();
497 WCHAR *pwzpublicKeyOrToken =
498 publicKeyOrToken.OpenUnicodeBuffer(cbPublicKeyOrTokenBLOB * 2);
500 BinToUnicodeHex(publicKeyOrTokenBLOB, cbPublicKeyOrTokenBLOB, pwzpublicKeyOrToken);
501 publicKeyOrToken.CloseBuffer(cbPublicKeyOrTokenBLOB * 2);
504 BOOL TextualIdentityParser::Parse(SString &textualIdentity, BOOL fPermitUnescapedQuotes)
506 BOOL fIsValid = TRUE;
507 BINDER_LOG_ENTER(W("TextualIdentityParser::Parse(textualIdentity)"));
508 SString unicodeTextualIdentity;
510 // Lexer modifies input string
511 textualIdentity.ConvertToUnicode(unicodeTextualIdentity);
512 Init(unicodeTextualIdentity, TRUE /* fSupportEscaping */);
514 SmallStackSString currentString;
516 // Identity format is simple name (, attr = value)*
517 GO_IF_NOT_EXPECTED(GetNextLexeme(currentString, fPermitUnescapedQuotes), LEXEME_TYPE_STRING);
518 m_pAssemblyIdentity->m_simpleName.Set(currentString);
519 m_pAssemblyIdentity->m_simpleName.Normalize();
520 m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_SIMPLE_NAME);
524 SmallStackSString attributeString;
525 SmallStackSString valueString;
527 GO_IF_END_OR_NOT_EXPECTED(GetNextLexeme(currentString), LEXEME_TYPE_COMMA);
528 GO_IF_NOT_EXPECTED(GetNextLexeme(attributeString), LEXEME_TYPE_STRING);
529 GO_IF_NOT_EXPECTED(GetNextLexeme(currentString), LEXEME_TYPE_EQUALS);
530 GO_IF_NOT_EXPECTED(GetNextLexeme(valueString), LEXEME_TYPE_STRING);
532 if (!PopulateAssemblyIdentity(attributeString, valueString))
540 BINDER_LOG_LEAVE_BOOL(W("TextualIdentityParser::Parse(textualIdentity)"), fIsValid);
544 BOOL TextualIdentityParser::ParseString(SString &textualString,
545 SString &contentString)
547 BOOL fIsValid = TRUE;
548 BINDER_LOG_ENTER(W("TextualIdentityParser::ParseString"));
549 SString unicodeTextualString;
551 // Lexer modifies input string
552 textualString.ConvertToUnicode(unicodeTextualString);
553 Init(unicodeTextualString, TRUE /* fSupportEscaping */);
555 SmallStackSString currentString;
556 GO_IF_NOT_EXPECTED(GetNextLexeme(currentString), LEXEME_TYPE_STRING);
558 contentString.Set(currentString);
559 currentString.Normalize();
562 BINDER_LOG_LEAVE_BOOL(W("TextualIdentityParser::ParseString"), fIsValid);
566 BOOL TextualIdentityParser::PopulateAssemblyIdentity(SString &attributeString,
567 SString &valueString)
569 BINDER_LOG_ENTER(W("TextualIdentityParser::PopulateAssemblyIdentity"));
570 BOOL fIsValid = TRUE;
572 if (EqualsCaseInsensitive(attributeString, W("culture")) ||
573 EqualsCaseInsensitive(attributeString, W("language")))
575 GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_CULTURE);
576 GO_IF_WILDCARD(valueString);
578 if (!EqualsCaseInsensitive(valueString, W("neutral")))
580 // culture/language is preserved as is
581 m_pAssemblyIdentity->m_cultureOrLanguage.Set(valueString);
582 m_pAssemblyIdentity->m_cultureOrLanguage.Normalize();
585 m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_CULTURE);
587 else if (EqualsCaseInsensitive(attributeString, W("version")))
589 AssemblyVersion *pAssemblyVersion = &(m_pAssemblyIdentity->m_version);
591 GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_VERSION);
592 GO_IF_WILDCARD(valueString);
594 if (ParseVersion(valueString, pAssemblyVersion))
596 m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_VERSION);
603 else if (EqualsCaseInsensitive(attributeString, W("publickeytoken")))
605 GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY);
606 GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN);
607 GO_IF_WILDCARD(valueString);
609 if (!EqualsCaseInsensitive(valueString, W("null")) &&
610 !EqualsCaseInsensitive(valueString, W("neutral")))
612 GO_IF_VALIDATE_FAILED(ValidatePublicKeyToken,
613 AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN);
614 HexToBlob(valueString,
615 FALSE /* fValidateHex */,
617 m_pAssemblyIdentity->m_publicKeyOrTokenBLOB);
621 m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL);
624 else if (EqualsCaseInsensitive(attributeString, W("publickey")))
626 GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN);
627 GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY);
629 if (!EqualsCaseInsensitive(valueString, W("null")) &&
630 !EqualsCaseInsensitive(valueString, W("neutral")))
632 GO_IF_VALIDATE_FAILED(ValidatePublicKey, AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY);
633 HexToBlob(valueString,
634 FALSE /* fValidateHex */,
635 FALSE /* fIsToken */,
636 m_pAssemblyIdentity->m_publicKeyOrTokenBLOB);
639 else if (EqualsCaseInsensitive(attributeString, W("processorarchitecture")))
641 PEKIND kProcessorArchitecture = peNone;
643 GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE);
644 GO_IF_WILDCARD(valueString);
646 if (ValidateAndConvertProcessorArchitecture(valueString, &kProcessorArchitecture))
648 m_pAssemblyIdentity->m_kProcessorArchitecture = kProcessorArchitecture;
649 m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE);
656 else if (EqualsCaseInsensitive(attributeString, W("retargetable")))
658 GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE);
660 if (EqualsCaseInsensitive(valueString, W("yes")))
662 m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE);
664 else if (!EqualsCaseInsensitive(valueString, W("no")))
669 else if (EqualsCaseInsensitive(attributeString, W("contenttype")))
671 GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE);
672 GO_IF_WILDCARD(valueString);
674 if (EqualsCaseInsensitive(valueString, W("windowsruntime")))
676 m_pAssemblyIdentity->m_kContentType = AssemblyContentType_WindowsRuntime;
677 m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE);
684 else if (EqualsCaseInsensitive(attributeString, W("custom")))
686 GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_CUSTOM);
688 if (EqualsCaseInsensitive(valueString, W("null")))
690 m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_CUSTOM_NULL);
694 GO_IF_VALIDATE_FAILED(ValidateHex, AssemblyIdentity::IDENTITY_FLAG_CUSTOM);
695 HexToBlob(valueString,
696 FALSE /* fValidateHex */,
697 FALSE /* fIsToken */,
698 m_pAssemblyIdentity->m_customBLOB);
703 // Fusion compat: Silently drop unknown attribute/value pair
704 BINDER_LOG_STRING(W("unknown attribute"), attributeString);
705 BINDER_LOG_STRING(W("unknown value"), valueString);
709 BINDER_LOG_LEAVE_HR(W("TextualIdentityParser::PopulateAssemblyIdentity"),
710 (fIsValid ? S_OK : S_FALSE));
715 void TextualIdentityParser::EscapeString(SString &input,
718 BINDER_LOG_ENTER(W("TextualIdentityParser::EscapeString"));
720 BINDER_LOG_STRING(W("input"), input);
722 BOOL fNeedQuotes = FALSE;
723 WCHAR wcQuoteCharacter = W('"');
725 SmallStackSString tmpString;
726 SString::Iterator cursor = input.Begin();
727 SString::Iterator end = input.End() - 1;
729 // Leading/Trailing white space require quotes
730 if (IsWhitespace(cursor[0]) || IsWhitespace(end[0]))
735 // Fusion textual identity compat: escape all non-quote characters even if quoted
736 while (cursor <= end)
738 WCHAR wcCurrentChar = cursor[0];
740 switch (wcCurrentChar)
744 if (fNeedQuotes && (wcQuoteCharacter != wcCurrentChar))
746 tmpString.Append(wcCurrentChar);
748 else if (!fNeedQuotes)
751 wcQuoteCharacter = (wcCurrentChar == W('"') ? W('\'') : W('"'));
752 tmpString.Append(wcCurrentChar);
756 tmpString.Append(W('\\'));
757 tmpString.Append(wcCurrentChar);
763 tmpString.Append(W('\\'));
764 tmpString.Append(wcCurrentChar);
767 tmpString.Append(W("\\t"));
770 tmpString.Append(W("\\n"));
773 tmpString.Append(W("\\r"));
776 tmpString.Append(wcCurrentChar);
786 result.Append(wcQuoteCharacter);
787 result.Append(tmpString);
788 result.Append(wcQuoteCharacter);
792 result.Set(tmpString);
795 BINDER_LOG_LEAVE(W("TextualIdentityParser::EscapeString"));