aboutsummaryrefslogtreecommitdiff
path: root/src/libs
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/strutil.cpp31
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/StrUtilTest.cpp210
2 files changed, 233 insertions, 8 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/strutil.cpp b/src/libs/dutil/WixToolset.DUtil/strutil.cpp
index 0a00c690..013c1b12 100644
--- a/src/libs/dutil/WixToolset.DUtil/strutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/strutil.cpp
@@ -1872,9 +1872,14 @@ NOTE: returns 0 if the multisz in not properly terminated with two nulls
1872extern "C" HRESULT DAPI MultiSzLen( 1872extern "C" HRESULT DAPI MultiSzLen(
1873 __in_ecount(*pcch) __nullnullterminated LPCWSTR pwzMultiSz, 1873 __in_ecount(*pcch) __nullnullterminated LPCWSTR pwzMultiSz,
1874 __out SIZE_T* pcch 1874 __out SIZE_T* pcch
1875) 1875 )
1876{ 1876{
1877 Assert(pcch); 1877 Assert(pcch);
1878 if (!pwzMultiSz)
1879 {
1880 *pcch = 0;
1881 return S_OK;
1882 }
1878 1883
1879 HRESULT hr = S_OK; 1884 HRESULT hr = S_OK;
1880 LPCWSTR wz = pwzMultiSz; 1885 LPCWSTR wz = pwzMultiSz;
@@ -1941,10 +1946,10 @@ extern "C" HRESULT DAPI MultiSzPrepend(
1941 hr = ::StringCchLengthW(pwzInsert, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchInsert)); 1946 hr = ::StringCchLengthW(pwzInsert, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchInsert));
1942 StrExitOnRootFailure(hr, "failed to get length of insert string"); 1947 StrExitOnRootFailure(hr, "failed to get length of insert string");
1943 1948
1944 cchResult = cchInsert + cchMultiSz + 1; 1949 cchResult = cchInsert + (cchMultiSz ? cchMultiSz : 1) + 1;
1945 1950
1946 // Allocate the result buffer 1951 // Allocate the result buffer
1947 hr = StrAlloc(&pwzResult, cchResult + 1); 1952 hr = StrAlloc(&pwzResult, cchResult);
1948 StrExitOnFailure(hr, "failed to allocate result string"); 1953 StrExitOnFailure(hr, "failed to allocate result string");
1949 1954
1950 // Prepend 1955 // Prepend
@@ -1954,8 +1959,7 @@ extern "C" HRESULT DAPI MultiSzPrepend(
1954 // If there was no MULTISZ, double null terminate our result, otherwise, copy the MULTISZ in 1959 // If there was no MULTISZ, double null terminate our result, otherwise, copy the MULTISZ in
1955 if (0 == cchMultiSz) 1960 if (0 == cchMultiSz)
1956 { 1961 {
1957 pwzResult[cchResult] = L'\0'; 1962 pwzResult[cchResult - 2] = L'\0';
1958 ++cchResult;
1959 } 1963 }
1960 else 1964 else
1961 { 1965 {
@@ -1967,6 +1971,7 @@ extern "C" HRESULT DAPI MultiSzPrepend(
1967 } 1971 }
1968 1972
1969 // Set the result 1973 // Set the result
1974 pwzResult[cchResult - 1] = L'\0';
1970 *ppwzMultiSz = pwzResult; 1975 *ppwzMultiSz = pwzResult;
1971 1976
1972 if (pcchMultiSz) 1977 if (pcchMultiSz)
@@ -2257,19 +2262,29 @@ extern "C" HRESULT DAPI MultiSzInsertString(
2257 // 2262 //
2258 // Insert the string 2263 // Insert the string
2259 // 2264 //
2260 cchResult = cchMultiSz + cchString + 1; 2265 cchResult = (cchMultiSz ? cchMultiSz : 1) + cchString + 1;
2261 2266
2262 hr = StrAlloc(&pwzResult, cchResult); 2267 hr = StrAlloc(&pwzResult, cchResult);
2263 StrExitOnFailure(hr, "failed to allocate result string for MULTISZ insert"); 2268 StrExitOnFailure(hr, "failed to allocate result string for MULTISZ insert");
2264 2269
2265 // Copy the part before the insert 2270 // Copy the part before the insert
2266 ::CopyMemory(pwzResult, *ppwzMultiSz, cchProgress * sizeof(WCHAR)); 2271 if (cchProgress)
2272 {
2273 ::CopyMemory(pwzResult, *ppwzMultiSz, cchProgress * sizeof(WCHAR));
2274 }
2267 2275
2268 // Copy the insert part 2276 // Copy the insert part
2269 ::CopyMemory(pwzResult + cchProgress, pwzInsert, (cchString + 1) * sizeof(WCHAR)); 2277 ::CopyMemory(pwzResult + cchProgress, pwzInsert, (cchString + 1) * sizeof(WCHAR));
2270 2278
2271 // Copy the part after the insert 2279 // Copy the part after the insert
2272 ::CopyMemory(pwzResult + cchProgress + cchString + 1, wz, (cchMultiSz - cchProgress) * sizeof(WCHAR)); 2280 if (cchMultiSz > cchProgress)
2281 {
2282 ::CopyMemory(pwzResult + cchProgress + cchString + 1, wz, (cchMultiSz - cchProgress) * sizeof(WCHAR));
2283 }
2284
2285 // Ensure double-null termination
2286 pwzResult[cchResult - 1] = L'\0';
2287 pwzResult[cchResult - 2] = L'\0';
2273 2288
2274 // Free the old buffer 2289 // Free the old buffer
2275 ReleaseNullStr(*ppwzMultiSz); 2290 ReleaseNullStr(*ppwzMultiSz);
diff --git a/src/libs/dutil/test/DUtilUnitTest/StrUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/StrUtilTest.cpp
index 89ed3dbb..7ad1ebdf 100644
--- a/src/libs/dutil/test/DUtilUnitTest/StrUtilTest.cpp
+++ b/src/libs/dutil/test/DUtilUnitTest/StrUtilTest.cpp
@@ -80,7 +80,217 @@ namespace DutilTests
80 TestStrAnsiAllocString(b, 0, "abCd"); 80 TestStrAnsiAllocString(b, 0, "abCd");
81 } 81 }
82 82
83 [Fact]
84 void StrUtilMultiSzLenNullTest()
85 {
86 HRESULT hr = S_OK;
87 SIZE_T cchMultiSz = 7;
88
89 DutilInitialize(&DutilTestTraceError);
90
91 try
92 {
93 hr = MultiSzLen(NULL, &cchMultiSz);
94 NativeAssert::Succeeded(hr, "Failed to get MULTISZ length for null.");
95 NativeAssert::Equal<SIZE_T>(0, cchMultiSz);
96 }
97 finally
98 {
99 DutilUninitialize();
100 }
101 }
102
103 [Fact]
104 void StrUtilMultiSzPrependEmptyTest()
105 {
106 HRESULT hr = S_OK;
107 LPWSTR sczMultiSz = NULL;
108 SIZE_T cchMultiSz = 0;
109 const WCHAR expected[] = { L'f', L'i', L'r', L's', L't', L'\0', L'\0' };
110
111 DutilInitialize(&DutilTestTraceError);
112
113 try
114 {
115 hr = MultiSzPrepend(&sczMultiSz, &cchMultiSz, L"first");
116 NativeAssert::Succeeded(hr, "Failed to prepend into empty MULTISZ.");
117 VerifyMultiSz(sczMultiSz, expected, sizeof(expected) / sizeof(expected[0]));
118 NativeAssert::Equal<SIZE_T>(sizeof(expected) / sizeof(expected[0]), cchMultiSz);
119 }
120 finally
121 {
122 ReleaseNullStr(sczMultiSz);
123 DutilUninitialize();
124 }
125 }
126
127 [Fact]
128 void StrUtilMultiSzInsertEmptyTest()
129 {
130 HRESULT hr = S_OK;
131 LPWSTR sczMultiSz = NULL;
132 SIZE_T cchMultiSz = 0;
133 const WCHAR expected[] = { L'i', L'n', L's', L'e', L'r', L't', L'\0', L'\0' };
134
135 DutilInitialize(&DutilTestTraceError);
136
137 try
138 {
139 hr = MultiSzInsertString(&sczMultiSz, &cchMultiSz, 0, L"insert");
140 NativeAssert::Succeeded(hr, "Failed to insert into empty MULTISZ.");
141 VerifyMultiSz(sczMultiSz, expected, sizeof(expected) / sizeof(expected[0]));
142 NativeAssert::Equal<SIZE_T>(sizeof(expected) / sizeof(expected[0]), cchMultiSz);
143 }
144 finally
145 {
146 ReleaseNullStr(sczMultiSz);
147 DutilUninitialize();
148 }
149 }
150
151 [Fact]
152 void StrUtilMultiSzFindTest()
153 {
154 HRESULT hr = S_OK;
155 LPWSTR sczMultiSz = NULL;
156 DWORD_PTR dwIndex = 0;
157 LPCWSTR wzFound = NULL;
158 const WCHAR source[] = { L'o', L'n', L'e', L'\0', L't', L'w', L'o', L'\0', L't', L'h', L'r', L'e', L'e', L'\0', L'\0' };
159
160 DutilInitialize(&DutilTestTraceError);
161
162 try
163 {
164 CreateMultiSz(&sczMultiSz, source, sizeof(source) / sizeof(source[0]));
165
166 hr = MultiSzFindString(sczMultiSz, L"two", &dwIndex, &wzFound);
167 NativeAssert::Succeeded(hr, "Failed to find string in MULTISZ.");
168 NativeAssert::Equal<DWORD_PTR>(1, dwIndex);
169 NativeAssert::StringEqual(L"two", wzFound);
170
171 hr = MultiSzFindSubstring(sczMultiSz, L"re", &dwIndex, &wzFound);
172 NativeAssert::Succeeded(hr, "Failed to find substring in MULTISZ.");
173 NativeAssert::Equal<DWORD_PTR>(2, dwIndex);
174 NativeAssert::StringEqual(L"three", wzFound);
175
176 hr = MultiSzFindString(sczMultiSz, L"missing", &dwIndex, &wzFound);
177 NativeAssert::SpecificReturnCode(S_FALSE, hr, "Expected not found for missing string.");
178
179 hr = MultiSzFindSubstring(sczMultiSz, L"zzz", &dwIndex, &wzFound);
180 NativeAssert::SpecificReturnCode(S_FALSE, hr, "Expected not found for missing substring.");
181 }
182 finally
183 {
184 ReleaseNullStr(sczMultiSz);
185 DutilUninitialize();
186 }
187 }
188
189 [Fact]
190 void StrUtilMultiSzRemoveTest()
191 {
192 HRESULT hr = S_OK;
193 LPWSTR sczMultiSz = NULL;
194 const WCHAR source[] = { L'o', L'n', L'e', L'\0', L't', L'w', L'o', L'\0', L't', L'h', L'r', L'e', L'e', L'\0', L'\0' };
195 const WCHAR expected[] = { L'o', L'n', L'e', L'\0', L't', L'h', L'r', L'e', L'e', L'\0', L'\0' };
196
197 DutilInitialize(&DutilTestTraceError);
198
199 try
200 {
201 CreateMultiSz(&sczMultiSz, source, sizeof(source) / sizeof(source[0]));
202
203 hr = MultiSzRemoveString(&sczMultiSz, 1);
204 NativeAssert::Succeeded(hr, "Failed to remove string from MULTISZ.");
205 VerifyMultiSz(sczMultiSz, expected, sizeof(expected) / sizeof(expected[0]));
206
207 hr = MultiSzRemoveString(&sczMultiSz, 10);
208 NativeAssert::SpecificReturnCode(S_FALSE, hr, "Expected S_FALSE when removing out of range.");
209 }
210 finally
211 {
212 ReleaseNullStr(sczMultiSz);
213 DutilUninitialize();
214 }
215 }
216
217 [Fact]
218 void StrUtilMultiSzInsertTest()
219 {
220 HRESULT hr = S_OK;
221 LPWSTR sczMultiSz = NULL;
222 SIZE_T cchMultiSz = 0;
223 const WCHAR source[] = { L'o', L'n', L'e', L'\0', L't', L'w', L'o', L'\0', L'\0' };
224 const WCHAR expected[] = { L'o', L'n', L'e', L'\0', L't', L'h', L'r', L'e', L'e', L'\0', L't', L'w', L'o', L'\0', L'\0' };
225
226 DutilInitialize(&DutilTestTraceError);
227
228 try
229 {
230 CreateMultiSz(&sczMultiSz, source, sizeof(source) / sizeof(source[0]));
231 cchMultiSz = sizeof(source) / sizeof(source[0]);
232
233 hr = MultiSzInsertString(&sczMultiSz, &cchMultiSz, 1, L"three");
234 NativeAssert::Succeeded(hr, "Failed to insert string into MULTISZ.");
235 VerifyMultiSz(sczMultiSz, expected, sizeof(expected) / sizeof(expected[0]));
236 NativeAssert::Equal<SIZE_T>(sizeof(expected) / sizeof(expected[0]), cchMultiSz);
237
238 hr = MultiSzInsertString(&sczMultiSz, &cchMultiSz, 10, L"bad");
239 NativeAssert::SpecificReturnCode(HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND), hr, "Expected insert to fail for invalid index.");
240 }
241 finally
242 {
243 ReleaseNullStr(sczMultiSz);
244 DutilUninitialize();
245 }
246 }
247
248 [Fact]
249 void StrUtilMultiSzReplaceTest()
250 {
251 HRESULT hr = S_OK;
252 LPWSTR sczMultiSz = NULL;
253 const WCHAR source[] = { L'o', L'n', L'e', L'\0', L't', L'w', L'o', L'\0', L't', L'h', L'r', L'e', L'e', L'\0', L'\0' };
254 const WCHAR expected[] = { L'o', L'n', L'e', L'\0', L't', L'w', L'o', L'\0', L'f', L'o', L'u', L'r', L'\0', L'\0' };
255
256 DutilInitialize(&DutilTestTraceError);
257
258 try
259 {
260 CreateMultiSz(&sczMultiSz, source, sizeof(source) / sizeof(source[0]));
261
262 hr = MultiSzReplaceString(&sczMultiSz, 2, L"four");
263 NativeAssert::Succeeded(hr, "Failed to replace string in MULTISZ.");
264 VerifyMultiSz(sczMultiSz, expected, sizeof(expected) / sizeof(expected[0]));
265 }
266 finally
267 {
268 ReleaseNullStr(sczMultiSz);
269 DutilUninitialize();
270 }
271 }
272
83 private: 273 private:
274 void CreateMultiSz(LPWSTR* ppwzMultiSz, const WCHAR* pwzSource, SIZE_T cchSource)
275 {
276 HRESULT hr = S_OK;
277
278 hr = StrAlloc(ppwzMultiSz, cchSource);
279 NativeAssert::Succeeded(hr, "Failed to allocate MULTISZ.");
280 ::CopyMemory(*ppwzMultiSz, pwzSource, cchSource * sizeof(WCHAR));
281 }
282
283 void VerifyMultiSz(LPCWSTR wzMultiSz, const WCHAR* pwzExpected, SIZE_T cchExpected)
284 {
285 HRESULT hr = S_OK;
286 SIZE_T cchMultiSz = 0;
287
288 hr = MultiSzLen(wzMultiSz, &cchMultiSz);
289 NativeAssert::Succeeded(hr, "Failed to get MULTISZ length.");
290 NativeAssert::Equal<SIZE_T>(cchExpected, cchMultiSz);
291 NativeAssert::True(0 == ::memcmp(wzMultiSz, pwzExpected, cchExpected * sizeof(WCHAR)));
292 }
293
84 void TestTrim(LPCWSTR wzInput, LPCWSTR wzExpectedResult) 294 void TestTrim(LPCWSTR wzInput, LPCWSTR wzExpectedResult)
85 { 295 {
86 HRESULT hr = S_OK; 296 HRESULT hr = S_OK;