/*++ Copyright (C) 1996-2001 Microsoft Corporation Module Name: Abstract: History: --*/ #include "precomp.h" #include "txttempl.h" #include #include #include "var.h" #include CTextTemplate::CTextTemplate(LPCWSTR wszTemplate) : m_wsTemplate(wszTemplate) { } CTextTemplate::~CTextTemplate() { } void CTextTemplate::SetTemplate(LPCWSTR wszTemplate) { m_wsTemplate = wszTemplate; } // replace escape sequences with proper characters // currently enabled for: // \t; \n; \r; // anything else is translated literally, minus the backwhack // returned string may or may not be same string as passed in // if not, then arg string is deleted & a new one returned. // -=> Thou Hast Been Forewarned! BSTR CTextTemplate::ReturnEscapedReturns(BSTR str) { BSTR newStr = str; // if we find a backwhack if (NULL != wcschr(str, L'\\')) { if (newStr = SysAllocString(str)) { WCHAR *pSource, *pDest; ZeroMemory(newStr, (wcslen(str)+1) *2); pDest = newStr; pSource = str; do { if (*pSource == L'\\') { pSource++; switch (*pSource) { case L'n' : case L'N' : *pDest = L'\n'; break; case L't' : case L'T' : *pDest = L'\t'; break; case L'r' : case L'R' : *pDest = L'\r'; break; default: *pDest = *pSource; } } else *pDest = *pSource; pDest++; } while (*++pSource); *pDest = '\0'; SysFreeString(str); } else // graceful degradation: return untranslated string if we're out of memory // user sees ugly escape sequence but is better than failing altogether. newStr = str; } return newStr; }; // v is an array (caller's supposed to check) // str is a string representing that array // this fcn checks for single element arrays // and if so, magically transforms // "{element}" to "element" // BSTR returned may or may not be the same as the one passed in. BSTR CTextTemplate::ProcessArray(const VARIANT& v, BSTR str) { if (SafeArrayGetDim(v.parray) == 1) { long lBound =0, uBound =0; SafeArrayGetLBound(v.parray, 1, &lBound); SafeArrayGetUBound(v.parray, 1, &uBound); UINT nStrLen = wcslen(str); assert( nStrLen >= 2 ); // check if there's one element if (uBound == lBound) { // single dimensioned array, with a single element. // nuke the curlies by copying everything but. UINT lastChar = nStrLen - 2; for (UINT i = 1; i <= lastChar; i++) str[i-1] = str[i]; str[lastChar] = L'\0'; } else { // // convert the curlies to parentheses. note that this // only works for single dimensional arrays. // str[0] = '('; str[nStrLen-1] = ')'; } } return str; } // concatentates property onto string // does so without quotes around the property, instead of: // str "prop" // you get: // str prop // we do *not* check for escapes in this function: we blindly strip off the leading & trailing quote void CTextTemplate::ConcatWithoutQuotes(WString& str, BSTR& property) { // dump the quotes if ((property[0] == L'\"') && (property[wcslen(property) -1] == L'\"')) { // hop past the first one WCHAR* p = property; p++; str += p; // null out the last one p = (wchar_t*)str; p[wcslen(p) -1] = L'\0'; } else str += property; } BSTR CTextTemplate::Apply(IWbemClassObject* pObj) { WString wsText; WCHAR* pwc = (WCHAR*)m_wsTemplate; while(*pwc) { if(*pwc != L'%') { wsText += *pwc; } else { pwc++; if(*pwc == L'%') { // Double % // ======== wsText += L'%'; } else { // It's a property --- find the end // ================================ WCHAR *pwcEnd = wcschr(pwc, L'%'); if(pwcEnd == NULL) { // No end --- fail // =============== wsText += L""; break; } else { // Look for the optional formatting string. WCHAR *pszFormat = wcschr(pwc, '('); // If we found a paren before what we thought was // the end, look for the end of the formatting string. // Once we find it, look again for the real end. We do // this in case the % we found was actually part of the // formatting string. if (pszFormat && pszFormat < pwcEnd) { pszFormat = wcschr(pszFormat + 1, ')'); if (pszFormat) pwcEnd = wcschr(pszFormat + 1, '%'); } wmilib::auto_buffer wszName(new WCHAR[pwcEnd - pwc + 1]); if (NULL == wszName.get()) return NULL; wcsncpy(wszName.get(), pwc, pwcEnd - pwc); wszName[pwcEnd-pwc] = 0; // Look for the optional formatting string. if ((pszFormat = wcschr(wszName.get(), '(')) != NULL) { WCHAR *pszEndFormat; *pszFormat = 0; pszFormat++; pszEndFormat = wcschr(pszFormat, ')'); if (pszEndFormat) *pszEndFormat = 0; else // In case of a bad format string. pszFormat = NULL; } // Get it // ====== if(!wbem_wcsicmp(wszName.get(), L"__TEXT")) { BSTR strText = NULL; pObj->GetObjectText(0, &strText); if(strText != NULL) { CSysFreeMe fm(strText); wsText += strText; } else wsText += L""; } else if(IsEmbeddedObjectProperty(wszName.get())) { // We have embedded object(s) // ========================== BSTR bstr = HandleEmbeddedObjectProperties(wszName.get(), pObj); if (bstr) { CSysFreeMeRef fmref(bstr); // we want to do this here, rather than in the HandleEmbeddedObjectProperties // because that call can go recursive, thereby removing too many backwhacks! bstr = ReturnEscapedReturns(bstr); if (bstr) { ConcatWithoutQuotes(wsText, bstr); } } } else { _variant_t v; CIMTYPE ct; HRESULT hres = pObj->Get(wszName.get(), 0, &v, &ct, NULL); // Append its value // ================ if (WBEM_E_NOT_FOUND == hres) wsText += L""; else if(FAILED(hres)) wsText += L""; else if (V_VT(&v) == VT_NULL) { wsText += L""; } else if (V_VT(&v) == VT_UNKNOWN) { BSTR strText = NULL; IWbemClassObject* pEmbeddedObj; if (SUCCEEDED(V_UNKNOWN(&v)->QueryInterface(IID_IWbemClassObject, (void**)&pEmbeddedObj))) { pEmbeddedObj->GetObjectText(0, &strText); pEmbeddedObj->Release(); } CSysFreeMe fm(strText); if(strText != NULL) wsText += strText; else wsText += L""; } else if ( V_VT(&v) == (VT_UNKNOWN | VT_ARRAY) ) { // We have an array of objects // ============================================== long ix[2] = {0,0}; long lLower, lUpper; int iDim = SafeArrayGetDim(v.parray); SafeArrayGetLBound(v.parray,1,&lLower); SafeArrayGetUBound(v.parray, 1, &lUpper); wsText += L"{"; for(ix[0] = lLower; ix[0] <= lUpper; ix[0]++) { IUnknown HUGEP *pUnk; // this array access does not copy anything, hence success assumed SafeArrayGetElement( v.parray, &(ix[0]), &pUnk); BSTR strText = NULL; IWbemClassObject* pEmbeddedObj; if (SUCCEEDED(pUnk->QueryInterface( IID_IWbemClassObject, (void**)&pEmbeddedObj))) { pEmbeddedObj->GetObjectText(0, &strText); pEmbeddedObj->Release(); } CSysFreeMe fm(strText); if(strText != NULL) wsText += strText; else wsText += L""; if(ix[0] < lUpper) { wsText += L", "; } } wsText += L"}"; } else { CVar Var; Var.SetVariant(&v); BSTR str = Var.GetText(0, ct, pszFormat); CSysFreeMeRef fmRef(str); if (str == NULL) { wsText += L""; } else { if (V_VT(&v) & VT_ARRAY) str = ProcessArray(v, str); if (str) { str = ReturnEscapedReturns(str); if (str) { ConcatWithoutQuotes(wsText, str); } } } } } // Move the pointer pwc = pwcEnd; } } } pwc++; } BSTR str = SysAllocString(wsText); return str; } BSTR CTextTemplate::HandleEmbeddedObjectProperties(WCHAR* wszTemplate, IWbemClassObject* pObj) { WString wsText; // Get the embedded object/array // ============================= WCHAR* pwc = wszTemplate; WCHAR* pwcEnd = wcschr(wszTemplate, L'.'); if(!pwcEnd) { BSTR bstr = SysAllocString(L""); return bstr; } WCHAR* wszName = new WCHAR[pwcEnd - pwc + 1]; if (NULL == wszName) return SysAllocString(L""); CVectorDeleteMe dm(wszName); wcsncpy(wszName, pwc, pwcEnd - pwc); wszName[pwcEnd-pwc] = 0; _variant_t v; HRESULT hres = pObj->Get(wszName, 0, &v, NULL, NULL); if (WBEM_E_NOT_FOUND == hres) return SysAllocString(L""); else if(FAILED(hres)) return SysAllocString(L""); else if (V_VT(&v) == VT_NULL) return SysAllocString(L""); pwc = wcschr(wszTemplate, L'.'); WCHAR wszProperty[1024]; StringCchCopyW(wszProperty, 1024, (pwc + 1)); if(V_VT(&v) == VT_UNKNOWN) { // We have a single object, so process it // ======================================= BSTR bstr = GetPropertyFromIUnknown(wszProperty, V_UNKNOWN(&v)); if (bstr) { CSysFreeMe fm(bstr); wsText += bstr; } } else if((V_VT(&v) & VT_ARRAY) && (V_VT(&v) & VT_UNKNOWN)) { // We have an array of objects, so process the elements // ==================================================== long ix[2] = {0,0}; long lLower, lUpper; int iDim = SafeArrayGetDim(v.parray); SafeArrayGetLBound(v.parray, 1, &lLower); SafeArrayGetUBound(v.parray, 1, &lUpper); wsText += L"{"; for(ix[0] = lLower; ix[0] <= lUpper; ix[0]++){ IUnknown HUGEP *pUnk; // no copy performed when accessing the n-th element, hence no failure SafeArrayGetElement(v.parray, &(ix[0]), &pUnk); BSTR bstr = GetPropertyFromIUnknown(wszProperty, pUnk); if (bstr) { CSysFreeMe fm(bstr); wsText += bstr; } if(ix[0] < lUpper) { wsText += L", "; } } wsText += L"}"; } else { // We have something else, which we shouldn't // ========================================== wsText += L""; } BSTR str = SysAllocString(wsText); return str; } BOOL CTextTemplate::IsEmbeddedObjectProperty(WCHAR * wszProperty) { WCHAR* pwcStart = wcschr(wszProperty, L'['); if(pwcStart) { return TRUE; } pwcStart = wcschr(wszProperty, L'.'); if(pwcStart) { return TRUE; } return FALSE; } BSTR CTextTemplate::GetPropertyFromIUnknown(WCHAR *wszProperty, IUnknown *pUnk) { BSTR bstrRetVal = NULL; IWbemClassObject *pEmbedded = NULL; // Get an IWbemClassObject pointer // =============================== HRESULT hres = pUnk->QueryInterface( IID_IWbemClassObject, (void **)&pEmbedded ); if(SUCCEEDED(hres)) { CReleaseMe rm(pEmbedded); // For each object get the desired property if(IsEmbeddedObjectProperty(wszProperty)) { // We have more embedded object(s) // =============================== BSTR bstr = HandleEmbeddedObjectProperties( wszProperty, pEmbedded ); if (bstr) { bstrRetVal = SysAllocString(bstr); SysFreeString(bstr); } } else { _variant_t vProp; CIMTYPE ct; HRESULT hRes = pEmbedded->Get( wszProperty, 0, &vProp, &ct, NULL ); if (WBEM_E_NOT_FOUND == hRes) { bstrRetVal = SysAllocString(L""); } else if(FAILED(hRes)) { bstrRetVal = SysAllocString(L""); } else if (V_VT(&vProp) == VT_NULL) { bstrRetVal = SysAllocString(L""); } else { BSTR str = NULL; if ( V_VT(&vProp) == ( VT_UNKNOWN | VT_ARRAY ) ) { WString wsText; // We have an array of objects // ============================================== long ix[2] = {0,0}; long lLower, lUpper; int iDim = SafeArrayGetDim(vProp.parray); HRESULT hr=SafeArrayGetLBound(vProp.parray,1,&lLower); hr = SafeArrayGetUBound(vProp.parray, 1, &lUpper); wsText += L"{"; for(ix[0] = lLower; ix[0] <= lUpper; ix[0]++) { IUnknown *pUnkHere = NULL; hr = SafeArrayGetElement( vProp.parray, &(ix[0]), &pUnkHere ); BSTR strText = NULL; IWbemClassObject* pEmbeddedObj = NULL; if (SUCCEEDED(pUnkHere->QueryInterface( IID_IWbemClassObject, (void**)&pEmbeddedObj))) { pEmbeddedObj->GetObjectText(0, &strText); pEmbeddedObj->Release(); } CSysFreeMe sfm(strText); if(strText != NULL) wsText += strText; else wsText += L""; if(ix[0] < lUpper) { wsText += L", "; } } wsText += L"}"; str = SysAllocString( wsText ); } else if ( V_VT(&vProp) != VT_UNKNOWN ) { CVar Var; Var.SetVariant(&vProp); str = Var.GetText( 0, ct ); } else { IWbemClassObject* pEmbedded2; hres = V_UNKNOWN(&vProp)->QueryInterface( IID_IWbemClassObject, (void**)&pEmbedded2 ); if ( SUCCEEDED(hres) ) { pEmbedded2->GetObjectText( 0, &str ); pEmbedded2->Release(); } } if( str == NULL ) { bstrRetVal = SysAllocString(L""); } else { bstrRetVal = str; } if ( V_VT(&vProp) & VT_ARRAY ) { bstrRetVal = ProcessArray(vProp, bstrRetVal); } } } } return bstrRetVal; }