From 7f642e51670bc38a4ef782a363936850bc2b0ba9 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Thu, 22 Apr 2021 06:38:23 -0700 Subject: Move dutil into libs/dutil --- src/libs/dutil/WixToolset.DUtil/iniutil.cpp | 768 ++++++++++++++++++++++++++++ 1 file changed, 768 insertions(+) create mode 100644 src/libs/dutil/WixToolset.DUtil/iniutil.cpp (limited to 'src/libs/dutil/WixToolset.DUtil/iniutil.cpp') diff --git a/src/libs/dutil/WixToolset.DUtil/iniutil.cpp b/src/libs/dutil/WixToolset.DUtil/iniutil.cpp new file mode 100644 index 00000000..70b62995 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/iniutil.cpp @@ -0,0 +1,768 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define IniExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__) +#define IniExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_INIUTIL, p, x, e, s, __VA_ARGS__) +#define IniExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_INIUTIL, p, x, s, __VA_ARGS__) +#define IniExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_INIUTIL, p, x, e, s, __VA_ARGS__) +#define IniExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_INIUTIL, p, x, s, __VA_ARGS__) +#define IniExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_INIUTIL, e, x, s, __VA_ARGS__) +#define IniExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_INIUTIL, g, x, s, __VA_ARGS__) + +const LPCWSTR wzSectionSeparator = L"\\"; + +struct INI_STRUCT +{ + LPWSTR sczPath; // the path to the INI file to be parsed + + LPWSTR sczOpenTagPrefix; // For regular ini, this would be '[' + LPWSTR sczOpenTagPostfix; // For regular ini, this would be ']' + + LPWSTR sczValuePrefix; // for regular ini, this would be NULL + LPWSTR sczValueSeparator; // for regular ini, this would be '=' + + LPWSTR *rgsczValueSeparatorExceptions; + DWORD cValueSeparatorExceptions; + + LPWSTR sczCommentLinePrefix; // for regular ini, this would be ';' + + INI_VALUE *rgivValues; + DWORD cValues; + + LPWSTR *rgsczLines; + DWORD cLines; + + FILE_ENCODING feEncoding; + BOOL fModified; +}; + +const int INI_HANDLE_BYTES = sizeof(INI_STRUCT); + +static HRESULT GetSectionPrefixFromName( + __in_z LPCWSTR wzName, + __deref_inout_z LPWSTR* psczOutput + ); +static void UninitializeIniValue( + INI_VALUE *pivValue + ); + +extern "C" HRESULT DAPI IniInitialize( + __out_bcount(INI_HANDLE_BYTES) INI_HANDLE* piHandle + ) +{ + HRESULT hr = S_OK; + + // Allocate the handle + *piHandle = static_cast(MemAlloc(sizeof(INI_STRUCT), TRUE)); + IniExitOnNull(*piHandle, hr, E_OUTOFMEMORY, "Failed to allocate ini object"); + +LExit: + return hr; +} + +extern "C" void DAPI IniUninitialize( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle + ) +{ + INI_STRUCT *pi = static_cast(piHandle); + + ReleaseStr(pi->sczPath); + ReleaseStr(pi->sczOpenTagPrefix); + ReleaseStr(pi->sczOpenTagPostfix); + ReleaseStr(pi->sczValuePrefix); + ReleaseStr(pi->sczValueSeparator); + + for (DWORD i = 0; i < pi->cValueSeparatorExceptions; ++i) + { + ReleaseStr(pi->rgsczValueSeparatorExceptions + i); + } + + ReleaseStr(pi->sczCommentLinePrefix); + + for (DWORD i = 0; i < pi->cValues; ++i) + { + UninitializeIniValue(pi->rgivValues + i); + } + ReleaseMem(pi->rgivValues); + + ReleaseStrArray(pi->rgsczLines, pi->cLines); + + ReleaseMem(pi); +} + +extern "C" HRESULT DAPI IniSetOpenTag( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzOpenTagPrefix, + __in_z_opt LPCWSTR wzOpenTagPostfix + ) +{ + HRESULT hr = S_OK; + + INI_STRUCT *pi = static_cast(piHandle); + + if (wzOpenTagPrefix) + { + hr = StrAllocString(&pi->sczOpenTagPrefix, wzOpenTagPrefix, 0); + IniExitOnFailure(hr, "Failed to copy open tag prefix to ini struct: %ls", wzOpenTagPrefix); + } + else + { + ReleaseNullStr(pi->sczOpenTagPrefix); + } + + if (wzOpenTagPostfix) + { + hr = StrAllocString(&pi->sczOpenTagPostfix, wzOpenTagPostfix, 0); + IniExitOnFailure(hr, "Failed to copy open tag postfix to ini struct: %ls", wzOpenTagPostfix); + } + else + { + ReleaseNullStr(pi->sczOpenTagPrefix); + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI IniSetValueStyle( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzValuePrefix, + __in_z_opt LPCWSTR wzValueSeparator + ) +{ + HRESULT hr = S_OK; + + INI_STRUCT *pi = static_cast(piHandle); + + if (wzValuePrefix) + { + hr = StrAllocString(&pi->sczValuePrefix, wzValuePrefix, 0); + IniExitOnFailure(hr, "Failed to copy value prefix to ini struct: %ls", wzValuePrefix); + } + else + { + ReleaseNullStr(pi->sczValuePrefix); + } + + if (wzValueSeparator) + { + hr = StrAllocString(&pi->sczValueSeparator, wzValueSeparator, 0); + IniExitOnFailure(hr, "Failed to copy value separator to ini struct: %ls", wzValueSeparator); + } + else + { + ReleaseNullStr(pi->sczValueSeparator); + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI IniSetValueSeparatorException( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z LPCWSTR wzValueNamePrefix + ) +{ + HRESULT hr = S_OK; + DWORD dwInsertedIndex = 0; + + INI_STRUCT *pi = static_cast(piHandle); + + hr = MemEnsureArraySize(reinterpret_cast(&pi->rgsczValueSeparatorExceptions), pi->cValueSeparatorExceptions + 1, sizeof(LPWSTR), 5); + IniExitOnFailure(hr, "Failed to increase array size for value separator exceptions"); + dwInsertedIndex = pi->cValueSeparatorExceptions; + ++pi->cValueSeparatorExceptions; + + hr = StrAllocString(&pi->rgsczValueSeparatorExceptions[dwInsertedIndex], wzValueNamePrefix, 0); + IniExitOnFailure(hr, "Failed to copy value separator exception"); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI IniSetCommentStyle( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzLinePrefix + ) +{ + HRESULT hr = S_OK; + + INI_STRUCT *pi = static_cast(piHandle); + + if (wzLinePrefix) + { + hr = StrAllocString(&pi->sczCommentLinePrefix, wzLinePrefix, 0); + IniExitOnFailure(hr, "Failed to copy comment line prefix to ini struct: %ls", wzLinePrefix); + } + else + { + ReleaseNullStr(pi->sczCommentLinePrefix); + } + +LExit: + return hr; +} + +extern "C" HRESULT DAPI IniParse( + __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in LPCWSTR wzPath, + __out_opt FILE_ENCODING *pfeEncodingFound + ) +{ + HRESULT hr = S_OK; + DWORD dwValuePrefixLength = 0; + DWORD dwValueSeparatorExceptionLength = 0; + LPWSTR sczContents = NULL; + LPWSTR sczCurrentSection = NULL; + LPWSTR sczName = NULL; + LPWSTR sczNameTrimmed = NULL; + LPWSTR sczValue = NULL; + LPWSTR sczValueTrimmed = NULL; + LPWSTR wzOpenTagPrefix = NULL; + LPWSTR wzOpenTagPostfix = NULL; + LPWSTR wzValuePrefix = NULL; + LPWSTR wzValueNameStart = NULL; + LPWSTR wzValueSeparator = NULL; + LPWSTR wzCommentLinePrefix = NULL; + LPWSTR wzValueBegin = NULL; + LPCWSTR wzTemp = NULL; + + INI_STRUCT *pi = static_cast(piHandle); + + BOOL fSections = (NULL != pi->sczOpenTagPrefix) && (NULL != pi->sczOpenTagPostfix); + BOOL fValuePrefix = (NULL != pi->sczValuePrefix); + + hr = StrAllocString(&pi->sczPath, wzPath, 0); + IniExitOnFailure(hr, "Failed to copy path to ini struct: %ls", wzPath); + + hr = FileToString(pi->sczPath, &sczContents, &pi->feEncoding); + IniExitOnFailure(hr, "Failed to convert file to string: %ls", pi->sczPath); + + if (pfeEncodingFound) + { + *pfeEncodingFound = pi->feEncoding; + } + + if (!sczContents || !*sczContents) + { + // Empty string, nothing to parse + ExitFunction1(hr = S_OK); + } + + dwValuePrefixLength = lstrlenW(pi->sczValuePrefix); + hr = StrSplitAllocArray(&pi->rgsczLines, reinterpret_cast(&pi->cLines), sczContents, L"\n"); + IniExitOnFailure(hr, "Failed to split INI file into lines"); + + for (DWORD i = 0; i < pi->cLines; ++i) + { + if (!*pi->rgsczLines[i] || '\r' == *pi->rgsczLines[i]) + { + continue; + } + + if (pi->sczCommentLinePrefix) + { + wzCommentLinePrefix = wcsstr(pi->rgsczLines[i], pi->sczCommentLinePrefix); + + if (wzCommentLinePrefix && wzCommentLinePrefix <= pi->rgsczLines[i] + 1) + { + continue; + } + } + + if (pi->sczOpenTagPrefix) + { + wzOpenTagPrefix = wcsstr(pi->rgsczLines[i], pi->sczOpenTagPrefix); + if (wzOpenTagPrefix) + { + // If there is an open tag prefix but there is anything but whitespace before it, then it's NOT an open tag prefix + // This is important, for example, to support values with names like "Array[0]=blah" in INI format + for (wzTemp = pi->rgsczLines[i]; wzTemp < wzOpenTagPrefix; ++wzTemp) + { + if (*wzTemp != L' ' && *wzTemp != L'\t') + { + wzOpenTagPrefix = NULL; + break; + } + } + } + } + + if (pi->sczOpenTagPostfix) + { + wzOpenTagPostfix = wcsstr(pi->rgsczLines[i], pi->sczOpenTagPostfix); + } + + if (pi->sczValuePrefix) + { + wzValuePrefix = wcsstr(pi->rgsczLines[i], pi->sczValuePrefix); + if (wzValuePrefix != NULL) + { + wzValueNameStart = wzValuePrefix + dwValuePrefixLength; + } + } + else + { + wzValueNameStart = pi->rgsczLines[i]; + } + + if (pi->sczValueSeparator && NULL != wzValueNameStart && *wzValueNameStart != L'\0') + { + dwValueSeparatorExceptionLength = 0; + for (DWORD j = 0; j < pi->cValueSeparatorExceptions; ++j) + { + if (pi->rgsczLines[i] == wcsstr(pi->rgsczLines[i], pi->rgsczValueSeparatorExceptions[j])) + { + dwValueSeparatorExceptionLength = lstrlenW(pi->rgsczValueSeparatorExceptions[j]); + break; + } + } + + wzValueSeparator = wcsstr(wzValueNameStart + dwValueSeparatorExceptionLength, pi->sczValueSeparator); + } + + // Don't keep the endline + if (pi->rgsczLines[i][lstrlenW(pi->rgsczLines[i])-1] == L'\r') + { + pi->rgsczLines[i][lstrlenW(pi->rgsczLines[i])-1] = L'\0'; + } + + if (fSections && wzOpenTagPrefix && wzOpenTagPostfix && wzOpenTagPrefix < wzOpenTagPostfix && (NULL == wzCommentLinePrefix || wzOpenTagPrefix < wzCommentLinePrefix)) + { + // There is an section starting here, let's keep track of it and move on + hr = StrAllocString(&sczCurrentSection, wzOpenTagPrefix + lstrlenW(pi->sczOpenTagPrefix), wzOpenTagPostfix - (wzOpenTagPrefix + lstrlenW(pi->sczOpenTagPrefix))); + IniExitOnFailure(hr, "Failed to record section name for line: %ls of INI file: %ls", pi->rgsczLines[i], pi->sczPath); + + // Sections will be calculated dynamically after any set operations, so don't include this in the list of lines to remember for output + ReleaseNullStr(pi->rgsczLines[i]); + } + else if (wzValueSeparator && (NULL == wzCommentLinePrefix || wzValueSeparator < wzCommentLinePrefix) + && (!fValuePrefix || wzValuePrefix)) + { + if (fValuePrefix) + { + wzValueBegin = wzValuePrefix + lstrlenW(pi->sczValuePrefix); + } + else + { + wzValueBegin = pi->rgsczLines[i]; + } + + hr = MemEnsureArraySize(reinterpret_cast(&pi->rgivValues), pi->cValues + 1, sizeof(INI_VALUE), 100); + IniExitOnFailure(hr, "Failed to increase array size for value array"); + + if (sczCurrentSection) + { + hr = StrAllocString(&sczName, sczCurrentSection, 0); + IniExitOnFailure(hr, "Failed to copy current section name"); + + hr = StrAllocConcat(&sczName, wzSectionSeparator, 0); + IniExitOnFailure(hr, "Failed to copy current section name"); + } + + hr = StrAllocConcat(&sczName, wzValueBegin, wzValueSeparator - wzValueBegin); + IniExitOnFailure(hr, "Failed to copy name"); + + hr = StrAllocString(&sczValue, wzValueSeparator + lstrlenW(pi->sczValueSeparator), 0); + IniExitOnFailure(hr, "Failed to copy value"); + + hr = StrTrimWhitespace(&sczNameTrimmed, sczName); + IniExitOnFailure(hr, "Failed to trim whitespace from name"); + + hr = StrTrimWhitespace(&sczValueTrimmed, sczValue); + IniExitOnFailure(hr, "Failed to trim whitespace from value"); + + pi->rgivValues[pi->cValues].wzName = const_cast(sczNameTrimmed); + sczNameTrimmed = NULL; + pi->rgivValues[pi->cValues].wzValue = const_cast(sczValueTrimmed); + sczValueTrimmed = NULL; + pi->rgivValues[pi->cValues].dwLineNumber = i + 1; + + ++pi->cValues; + + // Values will be calculated dynamically after any set operations, so don't include this in the list of lines to remember for output + ReleaseNullStr(pi->rgsczLines[i]); + } + else + { + // Must be a comment, so ignore it and keep it in the list to output + } + + ReleaseNullStr(sczName); + } + +LExit: + ReleaseStr(sczCurrentSection); + ReleaseStr(sczContents); + ReleaseStr(sczName); + ReleaseStr(sczNameTrimmed); + ReleaseStr(sczValue); + ReleaseStr(sczValueTrimmed); + + return hr; +} + +extern "C" HRESULT DAPI IniGetValueList( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __deref_out_ecount_opt(*pcValues) INI_VALUE** prgivValues, + __out DWORD *pcValues + ) +{ + HRESULT hr = S_OK; + + INI_STRUCT *pi = static_cast(piHandle); + + *prgivValues = pi->rgivValues; + *pcValues = pi->cValues; + + return hr; +} + +extern "C" HRESULT DAPI IniGetValue( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in LPCWSTR wzValueName, + __deref_out_z LPWSTR* psczValue + ) +{ + HRESULT hr = S_OK; + + INI_STRUCT *pi = static_cast(piHandle); + INI_VALUE *pValue = NULL; + + for (DWORD i = 0; i < pi->cValues; ++i) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pi->rgivValues[i].wzName, -1, wzValueName, -1)) + { + pValue = pi->rgivValues + i; + break; + } + } + + if (NULL == pValue) + { + hr = E_NOTFOUND; + IniExitOnFailure(hr, "Failed to check for INI value: %ls", wzValueName); + } + + if (NULL == pValue->wzValue) + { + ExitFunction1(hr = E_NOTFOUND); + } + + hr = StrAllocString(psczValue, pValue->wzValue, 0); + IniExitOnFailure(hr, "Failed to make copy of value while looking up INI value named: %ls", wzValueName); + +LExit: + return hr; +} + +extern "C" HRESULT DAPI IniSetValue( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in LPCWSTR wzValueName, + __in_z_opt LPCWSTR wzValue + ) +{ + HRESULT hr = S_OK; + LPWSTR sczSectionPrefix = NULL; // includes section name and backslash + LPWSTR sczName = NULL; + LPWSTR sczValue = NULL; + DWORD dwInsertIndex = DWORD_MAX; + + INI_STRUCT *pi = static_cast(piHandle); + INI_VALUE *pValue = NULL; + + for (DWORD i = 0; i < pi->cValues; ++i) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pi->rgivValues[i].wzName, -1, wzValueName, -1)) + { + pValue = pi->rgivValues + i; + break; + } + } + + // We're killing the value + if (NULL == wzValue) + { + if (pValue && pValue->wzValue) + { + pi->fModified = TRUE; + sczValue = const_cast(pValue->wzValue); + pValue->wzValue = NULL; + ReleaseNullStr(sczValue); + } + + ExitFunction(); + } + else + { + if (pValue) + { + if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, pValue->wzValue, -1, wzValue, -1)) + { + pi->fModified = TRUE; + hr = StrAllocString(const_cast(&pValue->wzValue), wzValue, 0); + IniExitOnFailure(hr, "Failed to update value INI value named: %ls", wzValueName); + } + + ExitFunction1(hr = S_OK); + } + else + { + if (wzValueName) + { + hr = GetSectionPrefixFromName(wzValueName, &sczSectionPrefix); + IniExitOnFailure(hr, "Failed to get section prefix from value name: %ls", wzValueName); + } + + // If we have a section prefix, figure out the index to insert it (at the end of the section it belongs in) + if (sczSectionPrefix) + { + for (DWORD i = 0; i < pi->cValues; ++i) + { + if (0 == wcsncmp(pi->rgivValues[i].wzName, sczSectionPrefix, lstrlenW(sczSectionPrefix))) + { + dwInsertIndex = i; + } + else if (DWORD_MAX != dwInsertIndex) + { + break; + } + } + } + else + { + for (DWORD i = 0; i < pi->cValues; ++i) + { + if (NULL == wcsstr(pi->rgivValues[i].wzName, wzSectionSeparator)) + { + dwInsertIndex = i; + } + else if (DWORD_MAX != dwInsertIndex) + { + break; + } + } + } + + // Otherwise, just add it to the end + if (DWORD_MAX == dwInsertIndex) + { + dwInsertIndex = pi->cValues; + } + + pi->fModified = TRUE; + hr = MemInsertIntoArray(reinterpret_cast(&pi->rgivValues), dwInsertIndex, 1, pi->cValues + 1, sizeof(INI_VALUE), 100); + IniExitOnFailure(hr, "Failed to insert value into array"); + + hr = StrAllocString(&sczName, wzValueName, 0); + IniExitOnFailure(hr, "Failed to copy name"); + + hr = StrAllocString(&sczValue, wzValue, 0); + IniExitOnFailure(hr, "Failed to copy value"); + + pi->rgivValues[dwInsertIndex].wzName = const_cast(sczName); + sczName = NULL; + pi->rgivValues[dwInsertIndex].wzValue = const_cast(sczValue); + sczValue = NULL; + + ++pi->cValues; + } + } + +LExit: + ReleaseStr(sczName); + ReleaseStr(sczValue); + + return hr; +} + +extern "C" HRESULT DAPI IniWriteFile( + __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle, + __in_z_opt LPCWSTR wzPath, + __in FILE_ENCODING feOverrideEncoding + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCurrentSectionPrefix = NULL; + LPWSTR sczNewSectionPrefix = NULL; + LPWSTR sczContents = NULL; + LPCWSTR wzName = NULL; + DWORD dwLineArrayIndex = 1; + FILE_ENCODING feEncoding; + + INI_STRUCT *pi = static_cast(piHandle); + + if (FILE_ENCODING_UNSPECIFIED == feOverrideEncoding) + { + feEncoding = pi->feEncoding; + } + else + { + feEncoding = feOverrideEncoding; + } + + if (FILE_ENCODING_UNSPECIFIED == feEncoding) + { + feEncoding = FILE_ENCODING_UTF16_WITH_BOM; + } + + if (!pi->fModified) + { + ExitFunction1(hr = S_OK); + } + if (NULL == wzPath && NULL == pi->sczPath) + { + ExitFunction1(hr = E_NOTFOUND); + } + + BOOL fSections = (pi->sczOpenTagPrefix) && (pi->sczOpenTagPostfix); + + hr = StrAllocString(&sczContents, L"", 0); + IniExitOnFailure(hr, "Failed to begin contents string as empty string"); + + // Insert any beginning lines we didn't understand like comments + if (0 < pi->cLines) + { + while (pi->rgsczLines[dwLineArrayIndex]) + { + hr = StrAllocConcat(&sczContents, pi->rgsczLines[dwLineArrayIndex], 0); + IniExitOnFailure(hr, "Failed to add previous line to ini output buffer in-memory"); + + hr = StrAllocConcat(&sczContents, L"\r\n", 2); + IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); + + ++dwLineArrayIndex; + } + } + + for (DWORD i = 0; i < pi->cValues; ++i) + { + // Skip if this value was killed off + if (NULL == pi->rgivValues[i].wzValue) + { + continue; + } + + // Now generate any lines for the current value like value line and maybe also a new section line before it + + // First see if we need to write a section line + hr = GetSectionPrefixFromName(pi->rgivValues[i].wzName, &sczNewSectionPrefix); + IniExitOnFailure(hr, "Failed to get section prefix from name: %ls", pi->rgivValues[i].wzName); + + // If the new section prefix is different, write a section out for it + if (fSections && sczNewSectionPrefix && (NULL == sczCurrentSectionPrefix || CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczNewSectionPrefix, -1, sczCurrentSectionPrefix, -1))) + { + hr = StrAllocConcat(&sczContents, pi->sczOpenTagPrefix, 0); + IniExitOnFailure(hr, "Failed to concat open tag prefix to string"); + + // Exclude section separator (i.e. backslash) from new section prefix + hr = StrAllocConcat(&sczContents, sczNewSectionPrefix, lstrlenW(sczNewSectionPrefix)-lstrlenW(wzSectionSeparator)); + IniExitOnFailure(hr, "Failed to concat section name to string"); + + hr = StrAllocConcat(&sczContents, pi->sczOpenTagPostfix, 0); + IniExitOnFailure(hr, "Failed to concat open tag postfix to string"); + + hr = StrAllocConcat(&sczContents, L"\r\n", 2); + IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); + + ReleaseNullStr(sczCurrentSectionPrefix); + sczCurrentSectionPrefix = sczNewSectionPrefix; + sczNewSectionPrefix = NULL; + } + + // Inserting lines we read before the current value if appropriate + while (pi->rgivValues[i].dwLineNumber > dwLineArrayIndex) + { + // Skip any lines were purposely forgot + if (NULL == pi->rgsczLines[dwLineArrayIndex]) + { + ++dwLineArrayIndex; + continue; + } + + hr = StrAllocConcat(&sczContents, pi->rgsczLines[dwLineArrayIndex++], 0); + IniExitOnFailure(hr, "Failed to add previous line to ini output buffer in-memory"); + + hr = StrAllocConcat(&sczContents, L"\r\n", 2); + IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); + } + + wzName = pi->rgivValues[i].wzName; + if (fSections) + { + wzName += lstrlenW(sczCurrentSectionPrefix); + } + + // OK, now just write the name/value pair, if it isn't deleted + if (pi->sczValuePrefix) + { + hr = StrAllocConcat(&sczContents, pi->sczValuePrefix, 0); + IniExitOnFailure(hr, "Failed to concat value prefix to ini output buffer"); + } + + hr = StrAllocConcat(&sczContents, wzName, 0); + IniExitOnFailure(hr, "Failed to concat value name to ini output buffer"); + + hr = StrAllocConcat(&sczContents, pi->sczValueSeparator, 0); + IniExitOnFailure(hr, "Failed to concat value separator to ini output buffer"); + + hr = StrAllocConcat(&sczContents, pi->rgivValues[i].wzValue, 0); + IniExitOnFailure(hr, "Failed to concat value to ini output buffer"); + + hr = StrAllocConcat(&sczContents, L"\r\n", 2); + IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory"); + } + + // If no path was specified, use the path to the file we parsed + if (NULL == wzPath) + { + wzPath = pi->sczPath; + } + + hr = FileFromString(wzPath, 0, sczContents, feEncoding); + IniExitOnFailure(hr, "Failed to write INI contents out to file: %ls", wzPath); + +LExit: + ReleaseStr(sczContents); + ReleaseStr(sczCurrentSectionPrefix); + ReleaseStr(sczNewSectionPrefix); + + return hr; +} + +static void UninitializeIniValue( + INI_VALUE *pivValue + ) +{ + ReleaseStr(const_cast(pivValue->wzName)); + ReleaseStr(const_cast(pivValue->wzValue)); +} + +static HRESULT GetSectionPrefixFromName( + __in_z LPCWSTR wzName, + __deref_inout_z LPWSTR* psczOutput + ) +{ + HRESULT hr = S_OK; + LPCWSTR wzSectionDelimiter = NULL; + + ReleaseNullStr(*psczOutput); + + wzSectionDelimiter = wcsstr(wzName, wzSectionSeparator); + if (wzSectionDelimiter && wzSectionDelimiter != wzName) + { + hr = StrAllocString(psczOutput, wzName, wzSectionDelimiter - wzName + 1); + IniExitOnFailure(hr, "Failed to copy section prefix"); + } + +LExit: + return hr; +} -- cgit v1.2.3-55-g6feb