aboutsummaryrefslogtreecommitdiff
path: root/src/dutil/strutil.cpp
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2017-09-03 11:22:38 -0700
committerRob Mensching <rob@firegiant.com>2017-09-03 13:33:33 -0700
commit5d8375007754101ff2889d0e79486c8f9b7cf5ab (patch)
treea76d6fb6a38dd9f04a93ffcfd9d64e76779b3414 /src/dutil/strutil.cpp
parent8e8da6dbc051ec884b5d439bb4f44dc027d05bbf (diff)
downloadwix-5d8375007754101ff2889d0e79486c8f9b7cf5ab.tar.gz
wix-5d8375007754101ff2889d0e79486c8f9b7cf5ab.tar.bz2
wix-5d8375007754101ff2889d0e79486c8f9b7cf5ab.zip
Initial commit
Diffstat (limited to 'src/dutil/strutil.cpp')
-rw-r--r--src/dutil/strutil.cpp2730
1 files changed, 2730 insertions, 0 deletions
diff --git a/src/dutil/strutil.cpp b/src/dutil/strutil.cpp
new file mode 100644
index 00000000..2e5e2f96
--- /dev/null
+++ b/src/dutil/strutil.cpp
@@ -0,0 +1,2730 @@
1// 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.
2
3#include "precomp.h"
4
5#define ARRAY_GROWTH_SIZE 5
6
7// Forward declarations.
8static HRESULT AllocHelper(
9 __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
10 __in DWORD_PTR cch,
11 __in BOOL fZeroOnRealloc
12 );
13static HRESULT AllocStringHelper(
14 __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz,
15 __in_z LPCWSTR wzSource,
16 __in DWORD_PTR cchSource,
17 __in BOOL fZeroOnRealloc
18 );
19static HRESULT AllocConcatHelper(
20 __deref_out_z LPWSTR* ppwz,
21 __in_z LPCWSTR wzSource,
22 __in DWORD_PTR cchSource,
23 __in BOOL fZeroOnRealloc
24 );
25static HRESULT AllocFormattedArgsHelper(
26 __deref_out_z LPWSTR* ppwz,
27 __in BOOL fZeroOnRealloc,
28 __in __format_string LPCWSTR wzFormat,
29 __in va_list args
30 );
31static HRESULT StrAllocStringMapInvariant(
32 __deref_out_z LPWSTR* pscz,
33 __in_z LPCWSTR wzSource,
34 __in int cchSource,
35 __in DWORD dwMapFlags
36 );
37
38/********************************************************************
39StrAlloc - allocates or reuses dynamic string memory
40
41NOTE: caller is responsible for freeing ppwz even if function fails
42********************************************************************/
43extern "C" HRESULT DAPI StrAlloc(
44 __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
45 __in DWORD_PTR cch
46 )
47{
48 return AllocHelper(ppwz, cch, FALSE);
49}
50
51/********************************************************************
52StrAllocSecure - allocates or reuses dynamic string memory
53If the memory needs to reallocated, calls SecureZeroMemory on the
54original block of memory after it is moved.
55
56NOTE: caller is responsible for freeing ppwz even if function fails
57********************************************************************/
58extern "C" HRESULT DAPI StrAllocSecure(
59 __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
60 __in DWORD_PTR cch
61 )
62{
63 return AllocHelper(ppwz, cch, TRUE);
64}
65
66/********************************************************************
67AllocHelper - allocates or reuses dynamic string memory
68If fZeroOnRealloc is true and the memory needs to reallocated,
69calls SecureZeroMemory on original block of memory after it is moved.
70
71NOTE: caller is responsible for freeing ppwz even if function fails
72********************************************************************/
73static HRESULT AllocHelper(
74 __deref_out_ecount_part(cch, 0) LPWSTR* ppwz,
75 __in DWORD_PTR cch,
76 __in BOOL fZeroOnRealloc
77 )
78{
79 Assert(ppwz && cch);
80
81 HRESULT hr = S_OK;
82 LPWSTR pwz = NULL;
83
84 if (cch >= MAXDWORD / sizeof(WCHAR))
85 {
86 hr = E_OUTOFMEMORY;
87 ExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch);
88 }
89
90 if (*ppwz)
91 {
92 if (fZeroOnRealloc)
93 {
94 LPVOID pvNew = NULL;
95 hr = MemReAllocSecure(*ppwz, sizeof(WCHAR)* cch, FALSE, &pvNew);
96 ExitOnFailure(hr, "Failed to reallocate string");
97 pwz = static_cast<LPWSTR>(pvNew);
98 }
99 else
100 {
101 pwz = static_cast<LPWSTR>(MemReAlloc(*ppwz, sizeof(WCHAR)* cch, FALSE));
102 }
103 }
104 else
105 {
106 pwz = static_cast<LPWSTR>(MemAlloc(sizeof(WCHAR) * cch, TRUE));
107 }
108
109 ExitOnNull(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch);
110
111 *ppwz = pwz;
112LExit:
113 return hr;
114}
115
116
117/********************************************************************
118StrTrimCapacity - Frees any unnecessary memory associated with a string.
119 Purely used for optimization, generally only when a string
120 has been changing size, and will no longer grow.
121
122NOTE: caller is responsible for freeing ppwz even if function fails
123********************************************************************/
124HRESULT DAPI StrTrimCapacity(
125 __deref_out_z LPWSTR* ppwz
126 )
127{
128 Assert(ppwz);
129
130 HRESULT hr = S_OK;
131 DWORD_PTR cchLen = 0;
132
133 hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchLen));
134 ExitOnFailure(hr, "Failed to calculate length of string");
135
136 ++cchLen; // Add 1 for null-terminator
137
138 hr = StrAlloc(ppwz, cchLen);
139 ExitOnFailure(hr, "Failed to reallocate string");
140
141LExit:
142 return hr;
143}
144
145
146/********************************************************************
147StrTrimWhitespace - allocates or reuses dynamic string memory and copies
148 in an existing string, excluding whitespace
149
150NOTE: caller is responsible for freeing ppwz even if function fails
151********************************************************************/
152HRESULT DAPI StrTrimWhitespace(
153 __deref_out_z LPWSTR* ppwz,
154 __in_z LPCWSTR wzSource
155 )
156{
157 HRESULT hr = S_OK;
158 int i = 0;
159 LPWSTR sczResult = NULL;
160
161 // Ignore beginning whitespace
162 while (L' ' == *wzSource || L'\t' == *wzSource)
163 {
164 wzSource++;
165 }
166
167 i = lstrlenW(wzSource);
168 // Overwrite ending whitespace with null characters
169 if (0 < i)
170 {
171 // start from the last non-null-terminator character in the array
172 for (i = i - 1; i > 0; --i)
173 {
174 if (L' ' != wzSource[i] && L'\t' != wzSource[i])
175 {
176 break;
177 }
178 }
179
180 ++i;
181 }
182
183 hr = StrAllocString(&sczResult, wzSource, i);
184 ExitOnFailure(hr, "Failed to copy result string");
185
186 // Output result
187 *ppwz = sczResult;
188 sczResult = NULL;
189
190LExit:
191 ReleaseStr(sczResult);
192
193 return hr;
194}
195
196
197/********************************************************************
198StrAnsiAlloc - allocates or reuses dynamic ANSI string memory
199
200NOTE: caller is responsible for freeing ppsz even if function fails
201********************************************************************/
202extern "C" HRESULT DAPI StrAnsiAlloc(
203 __deref_out_ecount_part(cch, 0) LPSTR* ppsz,
204 __in DWORD_PTR cch
205 )
206{
207 Assert(ppsz && cch);
208
209 HRESULT hr = S_OK;
210 LPSTR psz = NULL;
211
212 if (cch >= MAXDWORD / sizeof(WCHAR))
213 {
214 hr = E_OUTOFMEMORY;
215 ExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch);
216 }
217
218 if (*ppsz)
219 {
220 psz = static_cast<LPSTR>(MemReAlloc(*ppsz, sizeof(CHAR) * cch, FALSE));
221 }
222 else
223 {
224 psz = static_cast<LPSTR>(MemAlloc(sizeof(CHAR) * cch, TRUE));
225 }
226
227 ExitOnNull(psz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch);
228
229 *ppsz = psz;
230LExit:
231 return hr;
232}
233
234
235/********************************************************************
236StrAnsiTrimCapacity - Frees any unnecessary memory associated with a string.
237 Purely used for optimization, generally only when a string
238 has been changing size, and will no longer grow.
239
240NOTE: caller is responsible for freeing ppwz even if function fails
241********************************************************************/
242HRESULT DAPI StrAnsiTrimCapacity(
243 __deref_out_z LPSTR* ppz
244 )
245{
246 Assert(ppz);
247
248 HRESULT hr = S_OK;
249 DWORD_PTR cchLen = 0;
250
251#pragma prefast(push)
252#pragma prefast(disable:25068)
253 hr = ::StringCchLengthA(*ppz, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchLen));
254#pragma prefast(pop)
255 ExitOnFailure(hr, "Failed to calculate length of string");
256
257 ++cchLen; // Add 1 for null-terminator
258
259 hr = StrAnsiAlloc(ppz, cchLen);
260 ExitOnFailure(hr, "Failed to reallocate string");
261
262LExit:
263 return hr;
264}
265
266
267/********************************************************************
268StrAnsiTrimWhitespace - allocates or reuses dynamic string memory and copies
269 in an existing string, excluding whitespace
270
271NOTE: caller is responsible for freeing ppz even if function fails
272********************************************************************/
273HRESULT DAPI StrAnsiTrimWhitespace(
274 __deref_out_z LPSTR* ppz,
275 __in_z LPCSTR szSource
276 )
277{
278 HRESULT hr = S_OK;
279 int i = 0;
280 LPSTR sczResult = NULL;
281
282 // Ignore beginning whitespace
283 while (' ' == *szSource || '\t' == *szSource)
284 {
285 szSource++;
286 }
287
288 i = lstrlen(szSource);
289 // Overwrite ending whitespace with null characters
290 if (0 < i)
291 {
292 // start from the last non-null-terminator character in the array
293 for (i = i - 1; i > 0; --i)
294 {
295 if (L' ' != szSource[i] && L'\t' != szSource[i])
296 {
297 break;
298 }
299 }
300
301 ++i;
302 }
303
304 hr = StrAnsiAllocStringAnsi(&sczResult, szSource, i);
305 ExitOnFailure(hr, "Failed to copy result string");
306
307 // Output result
308 *ppz = sczResult;
309 sczResult = NULL;
310
311LExit:
312 ReleaseStr(sczResult);
313
314 return hr;
315}
316
317/********************************************************************
318StrAllocString - allocates or reuses dynamic string memory and copies in an existing string
319
320NOTE: caller is responsible for freeing ppwz even if function fails
321NOTE: cchSource does not have to equal the length of wzSource
322NOTE: if cchSource == 0, length of wzSource is used instead
323********************************************************************/
324extern "C" HRESULT DAPI StrAllocString(
325 __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz,
326 __in_z LPCWSTR wzSource,
327 __in DWORD_PTR cchSource
328 )
329{
330 return AllocStringHelper(ppwz, wzSource, cchSource, FALSE);
331}
332
333/********************************************************************
334StrAllocStringSecure - allocates or reuses dynamic string memory and
335copies in an existing string. If the memory needs to reallocated,
336calls SecureZeroMemory on original block of memory after it is moved.
337
338NOTE: caller is responsible for freeing ppwz even if function fails
339NOTE: cchSource does not have to equal the length of wzSource
340NOTE: if cchSource == 0, length of wzSource is used instead
341********************************************************************/
342extern "C" HRESULT DAPI StrAllocStringSecure(
343 __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz,
344 __in_z LPCWSTR wzSource,
345 __in DWORD_PTR cchSource
346 )
347{
348 return AllocStringHelper(ppwz, wzSource, cchSource, TRUE);
349}
350
351/********************************************************************
352AllocStringHelper - allocates or reuses dynamic string memory and copies in an existing string
353If fZeroOnRealloc is true and the memory needs to reallocated,
354calls SecureZeroMemory on original block of memory after it is moved.
355
356NOTE: caller is responsible for freeing ppwz even if function fails
357NOTE: cchSource does not have to equal the length of wzSource
358NOTE: if cchSource == 0, length of wzSource is used instead
359********************************************************************/
360static HRESULT AllocStringHelper(
361 __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz,
362 __in_z LPCWSTR wzSource,
363 __in DWORD_PTR cchSource,
364 __in BOOL fZeroOnRealloc
365 )
366{
367 Assert(ppwz && wzSource); // && *wzSource);
368
369 HRESULT hr = S_OK;
370 DWORD_PTR cch = 0;
371
372 if (*ppwz)
373 {
374 cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1)
375 if (-1 == cch)
376 {
377 hr = E_INVALIDARG;
378 ExitOnFailure(hr, "failed to get size of destination string");
379 }
380 cch /= sizeof(WCHAR); //convert the count in bytes to count in characters
381 }
382
383 if (0 == cchSource)
384 {
385 cchSource = lstrlenW(wzSource);
386 }
387
388 DWORD_PTR cchNeeded;
389 hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator
390 ExitOnFailure(hr, "source string is too long");
391
392 if (cch < cchNeeded)
393 {
394 cch = cchNeeded;
395 hr = AllocHelper(ppwz, cch, fZeroOnRealloc);
396 ExitOnFailure(hr, "failed to allocate string from string.");
397 }
398
399 // copy everything (the NULL terminator will be included)
400 hr = ::StringCchCopyNExW(*ppwz, cch, wzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
401
402LExit:
403 return hr;
404}
405
406
407/********************************************************************
408StrAnsiAllocString - allocates or reuses dynamic ANSI string memory and copies in an existing string
409
410NOTE: caller is responsible for freeing ppsz even if function fails
411NOTE: cchSource must equal the length of wzSource (not including the NULL terminator)
412NOTE: if cchSource == 0, length of wzSource is used instead
413********************************************************************/
414extern "C" HRESULT DAPI StrAnsiAllocString(
415 __deref_out_ecount_z(cchSource+1) LPSTR* ppsz,
416 __in_z LPCWSTR wzSource,
417 __in DWORD_PTR cchSource,
418 __in UINT uiCodepage
419 )
420{
421 Assert(ppsz && wzSource);
422
423 HRESULT hr = S_OK;
424 LPSTR psz = NULL;
425 DWORD_PTR cch = 0;
426 DWORD_PTR cchDest = cchSource; // at least enough
427
428 if (*ppsz)
429 {
430 cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1)
431 if (-1 == cch)
432 {
433 hr = E_INVALIDARG;
434 ExitOnFailure(hr, "failed to get size of destination string");
435 }
436 cch /= sizeof(CHAR); //convert the count in bytes to count in characters
437 }
438
439 if (0 == cchSource)
440 {
441 cchDest = ::WideCharToMultiByte(uiCodepage, 0, wzSource, -1, NULL, 0, NULL, NULL);
442 if (0 == cchDest)
443 {
444 ExitWithLastError(hr, "failed to get required size for conversion to ANSI: %ls", wzSource);
445 }
446
447 --cchDest; // subtract one because WideChageToMultiByte includes space for the NULL terminator that we track below
448 }
449 else if (L'\0' == wzSource[cchSource - 1]) // if the source already had a null terminator, don't count that in the character count because we track it below
450 {
451 cchDest = cchSource - 1;
452 }
453
454 if (cch < cchDest + 1)
455 {
456 cch = cchDest + 1; // add one for the NULL terminator
457 if (cch >= MAXDWORD / sizeof(WCHAR))
458 {
459 hr = E_OUTOFMEMORY;
460 ExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch);
461 }
462
463 if (*ppsz)
464 {
465 psz = static_cast<LPSTR>(MemReAlloc(*ppsz, sizeof(CHAR) * cch, TRUE));
466 }
467 else
468 {
469 psz = static_cast<LPSTR>(MemAlloc(sizeof(CHAR) * cch, TRUE));
470 }
471 ExitOnNull(psz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch);
472
473 *ppsz = psz;
474 }
475
476 if (0 == ::WideCharToMultiByte(uiCodepage, 0, wzSource, 0 == cchSource ? -1 : (int)cchSource, *ppsz, (int)cch, NULL, NULL))
477 {
478 ExitWithLastError(hr, "failed to convert to ansi: %ls", wzSource);
479 }
480 (*ppsz)[cchDest] = L'\0';
481
482LExit:
483 return hr;
484}
485
486
487/********************************************************************
488StrAllocStringAnsi - allocates or reuses dynamic string memory and copies in an existing ANSI string
489
490NOTE: caller is responsible for freeing ppwz even if function fails
491NOTE: cchSource must equal the length of wzSource (not including the NULL terminator)
492NOTE: if cchSource == 0, length of wzSource is used instead
493********************************************************************/
494extern "C" HRESULT DAPI StrAllocStringAnsi(
495 __deref_out_ecount_z(cchSource+1) LPWSTR* ppwz,
496 __in_z LPCSTR szSource,
497 __in DWORD_PTR cchSource,
498 __in UINT uiCodepage
499 )
500{
501 Assert(ppwz && szSource);
502
503 HRESULT hr = S_OK;
504 LPWSTR pwz = NULL;
505 DWORD_PTR cch = 0;
506 DWORD_PTR cchDest = cchSource; // at least enough
507
508 if (*ppwz)
509 {
510 cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1)
511 if (-1 == cch)
512 {
513 hr = E_INVALIDARG;
514 ExitOnFailure(hr, "failed to get size of destination string");
515 }
516 cch /= sizeof(WCHAR); //convert the count in bytes to count in characters
517 }
518
519 if (0 == cchSource)
520 {
521 cchDest = ::MultiByteToWideChar(uiCodepage, 0, szSource, -1, NULL, 0);
522 if (0 == cchDest)
523 {
524 ExitWithLastError(hr, "failed to get required size for conversion to unicode: %s", szSource);
525 }
526
527 --cchDest; //subtract one because MultiByteToWideChar includes space for the NULL terminator that we track below
528 }
529 else if (L'\0' == szSource[cchSource - 1]) // if the source already had a null terminator, don't count that in the character count because we track it below
530 {
531 cchDest = cchSource - 1;
532 }
533
534 if (cch < cchDest + 1)
535 {
536 cch = cchDest + 1;
537 if (cch >= MAXDWORD / sizeof(WCHAR))
538 {
539 hr = E_OUTOFMEMORY;
540 ExitOnFailure(hr, "Not enough memory to allocate string of size: %u", cch);
541 }
542
543 if (*ppwz)
544 {
545 pwz = static_cast<LPWSTR>(MemReAlloc(*ppwz, sizeof(WCHAR) * cch, TRUE));
546 }
547 else
548 {
549 pwz = static_cast<LPWSTR>(MemAlloc(sizeof(WCHAR) * cch, TRUE));
550 }
551
552 ExitOnNull(pwz, hr, E_OUTOFMEMORY, "failed to allocate string, len: %u", cch);
553
554 *ppwz = pwz;
555 }
556
557 if (0 == ::MultiByteToWideChar(uiCodepage, 0, szSource, 0 == cchSource ? -1 : (int)cchSource, *ppwz, (int)cch))
558 {
559 ExitWithLastError(hr, "failed to convert to unicode: %s", szSource);
560 }
561 (*ppwz)[cchDest] = L'\0';
562
563LExit:
564 return hr;
565}
566
567
568/********************************************************************
569StrAnsiAllocStringAnsi - allocates or reuses dynamic string memory and copies in an existing string
570
571NOTE: caller is responsible for freeing ppsz even if function fails
572NOTE: cchSource does not have to equal the length of wzSource
573NOTE: if cchSource == 0, length of wzSource is used instead
574********************************************************************/
575HRESULT DAPI StrAnsiAllocStringAnsi(
576 __deref_out_ecount_z(cchSource+1) LPSTR* ppsz,
577 __in_z LPCSTR szSource,
578 __in DWORD_PTR cchSource
579 )
580{
581 Assert(ppsz && szSource); // && *szSource);
582
583 HRESULT hr = S_OK;
584 DWORD_PTR cch = 0;
585
586 if (*ppsz)
587 {
588 cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1)
589 if (-1 == cch)
590 {
591 hr = E_INVALIDARG;
592 ExitOnFailure(hr, "failed to get size of destination string");
593 }
594 cch /= sizeof(CHAR); //convert the count in bytes to count in characters
595 }
596
597 if (0 == cchSource)
598 {
599 cchSource = lstrlenA(szSource);
600 }
601
602 DWORD_PTR cchNeeded;
603 hr = ::ULongPtrAdd(cchSource, 1, &cchNeeded); // add one for the null terminator
604 ExitOnFailure(hr, "source string is too long");
605
606 if (cch < cchNeeded)
607 {
608 cch = cchNeeded;
609 hr = StrAnsiAlloc(ppsz, cch);
610 ExitOnFailure(hr, "failed to allocate string from string.");
611 }
612
613 // copy everything (the NULL terminator will be included)
614#pragma prefast(push)
615#pragma prefast(disable:25068)
616 hr = ::StringCchCopyNExA(*ppsz, cch, szSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
617#pragma prefast(pop)
618
619LExit:
620 return hr;
621}
622
623
624/********************************************************************
625StrAllocPrefix - allocates or reuses dynamic string memory and
626 prefixes a string
627
628NOTE: caller is responsible for freeing ppwz even if function fails
629NOTE: cchPrefix does not have to equal the length of wzPrefix
630NOTE: if cchPrefix == 0, length of wzPrefix is used instead
631********************************************************************/
632extern "C" HRESULT DAPI StrAllocPrefix(
633 __deref_out_z LPWSTR* ppwz,
634 __in_z LPCWSTR wzPrefix,
635 __in DWORD_PTR cchPrefix
636 )
637{
638 Assert(ppwz && wzPrefix);
639
640 HRESULT hr = S_OK;
641 DWORD_PTR cch = 0;
642 DWORD_PTR cchLen = 0;
643
644 if (*ppwz)
645 {
646 cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1)
647 if (-1 == cch)
648 {
649 hr = E_INVALIDARG;
650 ExitOnFailure(hr, "failed to get size of destination string");
651 }
652 cch /= sizeof(WCHAR); //convert the count in bytes to count in characters
653
654 hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchLen));
655 ExitOnFailure(hr, "Failed to calculate length of string");
656 }
657
658 Assert(cchLen <= cch);
659
660 if (0 == cchPrefix)
661 {
662 hr = ::StringCchLengthW(wzPrefix, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchPrefix));
663 ExitOnFailure(hr, "Failed to calculate length of string");
664 }
665
666 if (cch - cchLen < cchPrefix + 1)
667 {
668 cch = cchPrefix + cchLen + 1;
669 hr = StrAlloc(ppwz, cch);
670 ExitOnFailure(hr, "failed to allocate string from string: %ls", wzPrefix);
671 }
672
673 if (*ppwz)
674 {
675 DWORD_PTR cb = cch * sizeof(WCHAR);
676 DWORD_PTR cbPrefix = cchPrefix * sizeof(WCHAR);
677
678 memmove(*ppwz + cchPrefix, *ppwz, cb - cbPrefix);
679 memcpy(*ppwz, wzPrefix, cbPrefix);
680 }
681 else
682 {
683 hr = E_UNEXPECTED;
684 ExitOnFailure(hr, "for some reason our buffer is still null");
685 }
686
687LExit:
688 return hr;
689}
690
691
692/********************************************************************
693StrAllocConcat - allocates or reuses dynamic string memory and adds an existing string
694
695NOTE: caller is responsible for freeing ppwz even if function fails
696NOTE: cchSource does not have to equal the length of wzSource
697NOTE: if cchSource == 0, length of wzSource is used instead
698********************************************************************/
699extern "C" HRESULT DAPI StrAllocConcat(
700 __deref_out_z LPWSTR* ppwz,
701 __in_z LPCWSTR wzSource,
702 __in DWORD_PTR cchSource
703 )
704{
705 return AllocConcatHelper(ppwz, wzSource, cchSource, FALSE);
706}
707
708
709/********************************************************************
710StrAllocConcatSecure - allocates or reuses dynamic string memory and
711adds an existing string. If the memory needs to reallocated, calls
712SecureZeroMemory on the original block of memory after it is moved.
713
714NOTE: caller is responsible for freeing ppwz even if function fails
715NOTE: cchSource does not have to equal the length of wzSource
716NOTE: if cchSource == 0, length of wzSource is used instead
717********************************************************************/
718extern "C" HRESULT DAPI StrAllocConcatSecure(
719 __deref_out_z LPWSTR* ppwz,
720 __in_z LPCWSTR wzSource,
721 __in DWORD_PTR cchSource
722 )
723{
724 return AllocConcatHelper(ppwz, wzSource, cchSource, TRUE);
725}
726
727
728/********************************************************************
729AllocConcatHelper - allocates or reuses dynamic string memory and adds an existing string
730If fZeroOnRealloc is true and the memory needs to reallocated,
731calls SecureZeroMemory on original block of memory after it is moved.
732
733NOTE: caller is responsible for freeing ppwz even if function fails
734NOTE: cchSource does not have to equal the length of wzSource
735NOTE: if cchSource == 0, length of wzSource is used instead
736********************************************************************/
737static HRESULT AllocConcatHelper(
738 __deref_out_z LPWSTR* ppwz,
739 __in_z LPCWSTR wzSource,
740 __in DWORD_PTR cchSource,
741 __in BOOL fZeroOnRealloc
742 )
743{
744 Assert(ppwz && wzSource); // && *wzSource);
745
746 HRESULT hr = S_OK;
747 DWORD_PTR cch = 0;
748 DWORD_PTR cchLen = 0;
749
750 if (*ppwz)
751 {
752 cch = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1)
753 if (-1 == cch)
754 {
755 hr = E_INVALIDARG;
756 ExitOnFailure(hr, "failed to get size of destination string");
757 }
758 cch /= sizeof(WCHAR); //convert the count in bytes to count in characters
759
760 hr = ::StringCchLengthW(*ppwz, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchLen));
761 ExitOnFailure(hr, "Failed to calculate length of string");
762 }
763
764 Assert(cchLen <= cch);
765
766 if (0 == cchSource)
767 {
768 hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchSource));
769 ExitOnFailure(hr, "Failed to calculate length of string");
770 }
771
772 if (cch - cchLen < cchSource + 1)
773 {
774 cch = (cchSource + cchLen + 1) * 2;
775 hr = AllocHelper(ppwz, cch, fZeroOnRealloc);
776 ExitOnFailure(hr, "failed to allocate string from string: %ls", wzSource);
777 }
778
779 if (*ppwz)
780 {
781 hr = ::StringCchCatNExW(*ppwz, cch, wzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
782 }
783 else
784 {
785 hr = E_UNEXPECTED;
786 ExitOnFailure(hr, "for some reason our buffer is still null");
787 }
788
789LExit:
790 return hr;
791}
792
793
794/********************************************************************
795StrAnsiAllocConcat - allocates or reuses dynamic string memory and adds an existing string
796
797NOTE: caller is responsible for freeing ppz even if function fails
798NOTE: cchSource does not have to equal the length of pzSource
799NOTE: if cchSource == 0, length of pzSource is used instead
800********************************************************************/
801extern "C" HRESULT DAPI StrAnsiAllocConcat(
802 __deref_out_z LPSTR* ppz,
803 __in_z LPCSTR pzSource,
804 __in DWORD_PTR cchSource
805 )
806{
807 Assert(ppz && pzSource); // && *pzSource);
808
809 HRESULT hr = S_OK;
810 DWORD_PTR cch = 0;
811 DWORD_PTR cchLen = 0;
812
813 if (*ppz)
814 {
815 cch = MemSize(*ppz); // get the count in bytes so we can check if it failed (returns -1)
816 if (-1 == cch)
817 {
818 hr = E_INVALIDARG;
819 ExitOnFailure(hr, "failed to get size of destination string");
820 }
821 cch /= sizeof(CHAR); // convert the count in bytes to count in characters
822
823#pragma prefast(push)
824#pragma prefast(disable:25068)
825 hr = ::StringCchLengthA(*ppz, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchLen));
826#pragma prefast(pop)
827 ExitOnFailure(hr, "Failed to calculate length of string");
828 }
829
830 Assert(cchLen <= cch);
831
832 if (0 == cchSource)
833 {
834#pragma prefast(push)
835#pragma prefast(disable:25068)
836 hr = ::StringCchLengthA(pzSource, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchSource));
837#pragma prefast(pop)
838 ExitOnFailure(hr, "Failed to calculate length of string");
839 }
840
841 if (cch - cchLen < cchSource + 1)
842 {
843 cch = (cchSource + cchLen + 1) * 2;
844 hr = StrAnsiAlloc(ppz, cch);
845 ExitOnFailure(hr, "failed to allocate string from string: %ls", pzSource);
846 }
847
848 if (*ppz)
849 {
850#pragma prefast(push)
851#pragma prefast(disable:25068)
852 hr = ::StringCchCatNExA(*ppz, cch, pzSource, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
853#pragma prefast(pop)
854 }
855 else
856 {
857 hr = E_UNEXPECTED;
858 ExitOnFailure(hr, "for some reason our buffer is still null");
859 }
860
861LExit:
862 return hr;
863}
864
865
866/********************************************************************
867StrAllocFormatted - allocates or reuses dynamic string memory and formats it
868
869NOTE: caller is responsible for freeing ppwz even if function fails
870********************************************************************/
871extern "C" HRESULT __cdecl StrAllocFormatted(
872 __deref_out_z LPWSTR* ppwz,
873 __in __format_string LPCWSTR wzFormat,
874 ...
875 )
876{
877 Assert(ppwz && wzFormat && *wzFormat);
878
879 HRESULT hr = S_OK;
880 va_list args;
881
882 va_start(args, wzFormat);
883 hr = StrAllocFormattedArgs(ppwz, wzFormat, args);
884 va_end(args);
885
886 return hr;
887}
888
889
890/********************************************************************
891StrAllocConcatFormatted - allocates or reuses dynamic string memory
892and adds a formatted string
893
894NOTE: caller is responsible for freeing ppwz even if function fails
895********************************************************************/
896extern "C" HRESULT __cdecl StrAllocConcatFormatted(
897 __deref_out_z LPWSTR* ppwz,
898 __in __format_string LPCWSTR wzFormat,
899 ...
900 )
901{
902 Assert(ppwz && wzFormat && *wzFormat);
903
904 HRESULT hr = S_OK;
905 LPWSTR sczFormatted = NULL;
906 va_list args;
907
908 va_start(args, wzFormat);
909 hr = StrAllocFormattedArgs(&sczFormatted, wzFormat, args);
910 va_end(args);
911 ExitOnFailure(hr, "Failed to allocate formatted string");
912
913 hr = StrAllocConcat(ppwz, sczFormatted, 0);
914
915LExit:
916 ReleaseStr(sczFormatted);
917
918 return hr;
919}
920
921
922/********************************************************************
923StrAllocFormattedSecure - allocates or reuses dynamic string memory
924and formats it. If the memory needs to reallocated,
925calls SecureZeroMemory on original block of memory after it is moved.
926
927NOTE: caller is responsible for freeing ppwz even if function fails
928********************************************************************/
929extern "C" HRESULT __cdecl StrAllocFormattedSecure(
930 __deref_out_z LPWSTR* ppwz,
931 __in __format_string LPCWSTR wzFormat,
932 ...
933 )
934{
935 Assert(ppwz && wzFormat && *wzFormat);
936
937 HRESULT hr = S_OK;
938 va_list args;
939
940 va_start(args, wzFormat);
941 hr = StrAllocFormattedArgsSecure(ppwz, wzFormat, args);
942 va_end(args);
943
944 return hr;
945}
946
947
948/********************************************************************
949StrAnsiAllocFormatted - allocates or reuses dynamic ANSI string memory and formats it
950
951NOTE: caller is responsible for freeing ppsz even if function fails
952********************************************************************/
953extern "C" HRESULT DAPI StrAnsiAllocFormatted(
954 __deref_out_z LPSTR* ppsz,
955 __in __format_string LPCSTR szFormat,
956 ...
957 )
958{
959 Assert(ppsz && szFormat && *szFormat);
960
961 HRESULT hr = S_OK;
962 va_list args;
963
964 va_start(args, szFormat);
965 hr = StrAnsiAllocFormattedArgs(ppsz, szFormat, args);
966 va_end(args);
967
968 return hr;
969}
970
971
972/********************************************************************
973StrAllocFormattedArgs - allocates or reuses dynamic string memory
974and formats it with the passed in args
975
976NOTE: caller is responsible for freeing ppwz even if function fails
977********************************************************************/
978extern "C" HRESULT DAPI StrAllocFormattedArgs(
979 __deref_out_z LPWSTR* ppwz,
980 __in __format_string LPCWSTR wzFormat,
981 __in va_list args
982 )
983{
984 return AllocFormattedArgsHelper(ppwz, FALSE, wzFormat, args);
985}
986
987
988/********************************************************************
989StrAllocFormattedArgsSecure - allocates or reuses dynamic string memory
990and formats it with the passed in args.
991
992If the memory needs to reallocated, calls SecureZeroMemory on the
993original block of memory after it is moved.
994
995NOTE: caller is responsible for freeing ppwz even if function fails
996********************************************************************/
997extern "C" HRESULT DAPI StrAllocFormattedArgsSecure(
998 __deref_out_z LPWSTR* ppwz,
999 __in __format_string LPCWSTR wzFormat,
1000 __in va_list args
1001 )
1002{
1003 return AllocFormattedArgsHelper(ppwz, TRUE, wzFormat, args);
1004}
1005
1006
1007/********************************************************************
1008AllocFormattedArgsHelper - allocates or reuses dynamic string memory
1009and formats it with the passed in args.
1010
1011If fZeroOnRealloc is true and the memory needs to reallocated,
1012calls SecureZeroMemory on original block of memory after it is moved.
1013
1014NOTE: caller is responsible for freeing ppwz even if function fails
1015********************************************************************/
1016static HRESULT AllocFormattedArgsHelper(
1017 __deref_out_z LPWSTR* ppwz,
1018 __in BOOL fZeroOnRealloc,
1019 __in __format_string LPCWSTR wzFormat,
1020 __in va_list args
1021 )
1022{
1023 Assert(ppwz && wzFormat && *wzFormat);
1024
1025 HRESULT hr = S_OK;
1026 SIZE_T cch = 0;
1027 LPWSTR pwzOriginal = NULL;
1028 SIZE_T cbOriginal = 0;
1029 SIZE_T cchOriginal = 0;
1030
1031 if (*ppwz)
1032 {
1033 cbOriginal = MemSize(*ppwz); // get the count in bytes so we can check if it failed (returns -1)
1034 if (-1 == cbOriginal)
1035 {
1036 hr = E_INVALIDARG;
1037 ExitOnFailure(hr, "failed to get size of destination string");
1038 }
1039
1040 cch = cbOriginal / sizeof(WCHAR); //convert the count in bytes to count in characters
1041 cchOriginal = lstrlenW(*ppwz);
1042 }
1043
1044 if (0 == cch) // if there is no space in the string buffer
1045 {
1046 cch = 256;
1047
1048 hr = AllocHelper(ppwz, cch, fZeroOnRealloc);
1049 ExitOnFailure(hr, "failed to allocate string to format: %ls", wzFormat);
1050 }
1051
1052 // format the message (grow until it fits or there is a failure)
1053 do
1054 {
1055 hr = ::StringCchVPrintfW(*ppwz, cch, wzFormat, args);
1056 if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
1057 {
1058 if (!pwzOriginal)
1059 {
1060 // this allows you to pass the original string as a formatting argument and not crash
1061 // save the original string and free it after the printf is complete
1062 pwzOriginal = *ppwz;
1063 *ppwz = NULL;
1064
1065 // StringCchVPrintfW starts writing to the string...
1066 // NOTE: this hack only works with sprintf(&pwz, "%s ...", pwz, ...);
1067 pwzOriginal[cchOriginal] = 0;
1068 }
1069
1070 cch *= 2;
1071
1072 hr = AllocHelper(ppwz, cch, fZeroOnRealloc);
1073 ExitOnFailure(hr, "failed to allocate string to format: %ls", wzFormat);
1074
1075 hr = S_FALSE;
1076 }
1077 } while (S_FALSE == hr);
1078 ExitOnFailure(hr, "failed to format string");
1079
1080LExit:
1081 if (pwzOriginal && fZeroOnRealloc)
1082 {
1083 SecureZeroMemory(pwzOriginal, cbOriginal);
1084 }
1085
1086 ReleaseStr(pwzOriginal);
1087
1088 return hr;
1089}
1090
1091
1092/********************************************************************
1093StrAnsiAllocFormattedArgs - allocates or reuses dynamic ANSI string memory
1094and formats it with the passed in args
1095
1096NOTE: caller is responsible for freeing ppsz even if function fails
1097********************************************************************/
1098extern "C" HRESULT DAPI StrAnsiAllocFormattedArgs(
1099 __deref_out_z LPSTR* ppsz,
1100 __in __format_string LPCSTR szFormat,
1101 __in va_list args
1102 )
1103{
1104 Assert(ppsz && szFormat && *szFormat);
1105
1106 HRESULT hr = S_OK;
1107 DWORD_PTR cch = *ppsz ? MemSize(*ppsz) / sizeof(CHAR) : 0;
1108 LPSTR pszOriginal = NULL;
1109 DWORD cchOriginal = 0;
1110
1111 if (*ppsz)
1112 {
1113 cch = MemSize(*ppsz); // get the count in bytes so we can check if it failed (returns -1)
1114 if (-1 == cch)
1115 {
1116 hr = E_INVALIDARG;
1117 ExitOnFailure(hr, "failed to get size of destination string");
1118 }
1119 cch /= sizeof(CHAR); //convert the count in bytes to count in characters
1120
1121 cchOriginal = lstrlenA(*ppsz);
1122 }
1123
1124 if (0 == cch) // if there is no space in the string buffer
1125 {
1126 cch = 256;
1127 hr = StrAnsiAlloc(ppsz, cch);
1128 ExitOnFailure(hr, "failed to allocate string to format: %s", szFormat);
1129 }
1130
1131 // format the message (grow until it fits or there is a failure)
1132 do
1133 {
1134#pragma prefast(push)
1135#pragma prefast(disable:25068) // We intentionally don't use the unicode API here
1136 hr = ::StringCchVPrintfA(*ppsz, cch, szFormat, args);
1137#pragma prefast(pop)
1138 if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
1139 {
1140 if (!pszOriginal)
1141 {
1142 // this allows you to pass the original string as a formatting argument and not crash
1143 // save the original string and free it after the printf is complete
1144 pszOriginal = *ppsz;
1145 *ppsz = NULL;
1146 // StringCchVPrintfW starts writing to the string...
1147 // NOTE: this hack only works with sprintf(&pwz, "%s ...", pwz, ...);
1148 pszOriginal[cchOriginal] = 0;
1149 }
1150 cch *= 2;
1151 hr = StrAnsiAlloc(ppsz, cch);
1152 ExitOnFailure(hr, "failed to allocate string to format: %ls", szFormat);
1153 hr = S_FALSE;
1154 }
1155 } while (S_FALSE == hr);
1156 ExitOnFailure(hr, "failed to format string");
1157
1158LExit:
1159 ReleaseStr(pszOriginal);
1160
1161 return hr;
1162}
1163
1164
1165/********************************************************************
1166StrAllocFromError - returns the string for a particular error.
1167
1168********************************************************************/
1169extern "C" HRESULT DAPI StrAllocFromError(
1170 __inout LPWSTR *ppwzMessage,
1171 __in HRESULT hrError,
1172 __in_opt HMODULE hModule,
1173 ...
1174 )
1175{
1176 HRESULT hr = S_OK;
1177 DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_SYSTEM;
1178 LPVOID pvMessage = NULL;
1179 DWORD cchMessage = 0;
1180
1181 if (hModule)
1182 {
1183 dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
1184 }
1185
1186 va_list args;
1187 va_start(args, hModule);
1188 cchMessage = ::FormatMessageW(dwFlags, static_cast<LPCVOID>(hModule), hrError, 0, reinterpret_cast<LPWSTR>(&pvMessage), 0, &args);
1189 va_end(args);
1190
1191 if (0 == cchMessage)
1192 {
1193 ExitWithLastError(hr, "Failed to format message for error: 0x%x", hrError);
1194 }
1195
1196 hr = StrAllocString(ppwzMessage, reinterpret_cast<LPCWSTR>(pvMessage), cchMessage);
1197 ExitOnFailure(hr, "Failed to allocate string for message.");
1198
1199LExit:
1200 if (pvMessage)
1201 {
1202 ::LocalFree(pvMessage);
1203 }
1204
1205 return hr;
1206}
1207
1208
1209/********************************************************************
1210StrMaxLength - returns maximum number of characters that can be stored in dynamic string p
1211
1212NOTE: assumes Unicode string
1213********************************************************************/
1214extern "C" HRESULT DAPI StrMaxLength(
1215 __in LPCVOID p,
1216 __out DWORD_PTR* pcch
1217 )
1218{
1219 Assert(pcch);
1220
1221 HRESULT hr = S_OK;
1222
1223 if (p)
1224 {
1225 *pcch = MemSize(p); // get size of entire buffer
1226 if (-1 == *pcch)
1227 {
1228 ExitFunction1(hr = E_FAIL);
1229 }
1230
1231 *pcch /= sizeof(WCHAR); // reduce to count of characters
1232 }
1233 else
1234 {
1235 *pcch = 0;
1236 }
1237 Assert(S_OK == hr);
1238
1239LExit:
1240 return hr;
1241}
1242
1243
1244/********************************************************************
1245StrSize - returns count of bytes in dynamic string p
1246
1247********************************************************************/
1248extern "C" HRESULT DAPI StrSize(
1249 __in LPCVOID p,
1250 __out DWORD_PTR* pcb
1251 )
1252{
1253 Assert(p && pcb);
1254
1255 HRESULT hr = S_OK;
1256
1257 *pcb = MemSize(p);
1258 if (-1 == *pcb)
1259 {
1260 hr = E_FAIL;
1261 }
1262
1263 return hr;
1264}
1265
1266/********************************************************************
1267StrFree - releases dynamic string memory allocated by any StrAlloc*() functions
1268
1269********************************************************************/
1270extern "C" HRESULT DAPI StrFree(
1271 __in LPVOID p
1272 )
1273{
1274 Assert(p);
1275
1276 HRESULT hr = MemFree(p);
1277 ExitOnFailure(hr, "failed to free string");
1278
1279LExit:
1280 return hr;
1281}
1282
1283
1284/****************************************************************************
1285StrReplaceStringAll - Replaces wzOldSubString in ppOriginal with a wzNewSubString.
1286Replaces all instances.
1287
1288****************************************************************************/
1289extern "C" HRESULT DAPI StrReplaceStringAll(
1290 __inout LPWSTR* ppwzOriginal,
1291 __in_z LPCWSTR wzOldSubString,
1292 __in_z LPCWSTR wzNewSubString
1293 )
1294{
1295 HRESULT hr = S_OK;
1296 DWORD dwStartIndex = 0;
1297
1298 do
1299 {
1300 hr = StrReplaceString(ppwzOriginal, &dwStartIndex, wzOldSubString, wzNewSubString);
1301 ExitOnFailure(hr, "Failed to replace substring");
1302 }
1303 while (S_OK == hr);
1304
1305 hr = (0 == dwStartIndex) ? S_FALSE : S_OK;
1306
1307LExit:
1308 return hr;
1309}
1310
1311
1312/****************************************************************************
1313StrReplaceString - Replaces wzOldSubString in ppOriginal with a wzNewSubString.
1314Search for old substring starts at dwStartIndex. Does only 1 replace.
1315
1316****************************************************************************/
1317extern "C" HRESULT DAPI StrReplaceString(
1318 __inout LPWSTR* ppwzOriginal,
1319 __inout DWORD* pdwStartIndex,
1320 __in_z LPCWSTR wzOldSubString,
1321 __in_z LPCWSTR wzNewSubString
1322 )
1323{
1324 Assert(ppwzOriginal && wzOldSubString && wzNewSubString);
1325
1326 HRESULT hr = S_FALSE;
1327 LPCWSTR wzSubLocation = NULL;
1328 LPWSTR pwzBuffer = NULL;
1329
1330 if (!*ppwzOriginal)
1331 {
1332 ExitFunction();
1333 }
1334
1335 wzSubLocation = wcsstr(*ppwzOriginal + *pdwStartIndex, wzOldSubString);
1336 if (!wzSubLocation)
1337 {
1338 ExitFunction();
1339 }
1340
1341 hr = ::PtrdiffTToDWord(wzSubLocation - *ppwzOriginal, pdwStartIndex);
1342 ExitOnFailure(hr, "Failed to diff pointers.");
1343
1344 hr = StrAllocString(&pwzBuffer, *ppwzOriginal, wzSubLocation - *ppwzOriginal);
1345 ExitOnFailure(hr, "Failed to duplicate string.");
1346
1347 pwzBuffer[wzSubLocation - *ppwzOriginal] = '\0';
1348
1349 hr = StrAllocConcat(&pwzBuffer, wzNewSubString, 0);
1350 ExitOnFailure(hr, "Failed to append new string.");
1351
1352 hr = StrAllocConcat(&pwzBuffer, wzSubLocation + wcslen(wzOldSubString), 0);
1353 ExitOnFailure(hr, "Failed to append post string.");
1354
1355 hr = StrFree(*ppwzOriginal);
1356 ExitOnFailure(hr, "Failed to free original string.");
1357
1358 *ppwzOriginal = pwzBuffer;
1359 *pdwStartIndex = *pdwStartIndex + static_cast<DWORD>(wcslen(wzNewSubString));
1360 hr = S_OK;
1361
1362LExit:
1363 return hr;
1364}
1365
1366
1367static inline BYTE HexCharToByte(
1368 __in WCHAR wc
1369 )
1370{
1371 Assert(L'0' <= wc && wc <= L'9' || L'a' <= wc && wc <= L'f' || L'A' <= wc && wc <= L'F'); // make sure wc is a hex character
1372
1373 BYTE b;
1374 if (L'0' <= wc && wc <= L'9')
1375 {
1376 b = (BYTE)(wc - L'0');
1377 }
1378 else if ('a' <= wc && wc <= 'f')
1379 {
1380 b = (BYTE)(wc - L'0' - (L'a' - L'9' - 1));
1381 }
1382 else // must be (L'A' <= wc && wc <= L'F')
1383 {
1384 b = (BYTE)(wc - L'0' - (L'A' - L'9' - 1));
1385 }
1386
1387 Assert(0 <= b && b <= 15);
1388 return b;
1389}
1390
1391
1392/****************************************************************************
1393StrHexEncode - converts an array of bytes to a text string
1394
1395NOTE: wzDest must have space for cbSource * 2 + 1 characters
1396****************************************************************************/
1397extern "C" HRESULT DAPI StrHexEncode(
1398 __in_ecount(cbSource) const BYTE* pbSource,
1399 __in DWORD_PTR cbSource,
1400 __out_ecount(cchDest) LPWSTR wzDest,
1401 __in DWORD_PTR cchDest
1402 )
1403{
1404 Assert(pbSource && wzDest);
1405
1406 HRESULT hr = S_OK;
1407 DWORD i;
1408 BYTE b;
1409
1410 if (cchDest < 2 * cbSource + 1)
1411 {
1412 ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
1413 }
1414
1415 for (i = 0; i < cbSource; ++i)
1416 {
1417 b = (*pbSource) >> 4;
1418 *(wzDest++) = (WCHAR)(L'0' + b + ((b < 10) ? 0 : L'A'-L'9'-1));
1419 b = (*pbSource) & 0xF;
1420 *(wzDest++) = (WCHAR)(L'0' + b + ((b < 10) ? 0 : L'A'-L'9'-1));
1421
1422 ++pbSource;
1423 }
1424
1425 *wzDest = 0;
1426
1427LExit:
1428 return hr;
1429}
1430
1431
1432/****************************************************************************
1433StrAllocHexEncode - converts an array of bytes to an allocated text string
1434
1435****************************************************************************/
1436HRESULT DAPI StrAllocHexEncode(
1437 __in_ecount(cbSource) const BYTE* pbSource,
1438 __in DWORD_PTR cbSource,
1439 __deref_out_ecount_z(2*(cbSource+1)) LPWSTR* ppwzDest
1440 )
1441{
1442 HRESULT hr = S_OK;
1443 DWORD_PTR cchSource = sizeof(WCHAR) * (cbSource + 1);
1444
1445 hr = StrAlloc(ppwzDest, cchSource);
1446 ExitOnFailure(hr, "Failed to allocate hex string.");
1447
1448 hr = StrHexEncode(pbSource, cbSource, *ppwzDest, cchSource);
1449 ExitOnFailure(hr, "Failed to encode hex string.");
1450
1451LExit:
1452 return hr;
1453}
1454
1455
1456/****************************************************************************
1457StrHexDecode - converts a string of text to array of bytes
1458
1459NOTE: wzSource must contain even number of characters
1460****************************************************************************/
1461extern "C" HRESULT DAPI StrHexDecode(
1462 __in_z LPCWSTR wzSource,
1463 __out_bcount(cbDest) BYTE* pbDest,
1464 __in DWORD_PTR cbDest
1465 )
1466{
1467 Assert(wzSource && pbDest);
1468
1469 HRESULT hr = S_OK;
1470 DWORD cchSource = lstrlenW(wzSource);
1471 DWORD i;
1472 BYTE b;
1473
1474 Assert(0 == cchSource % 2);
1475 if (cbDest < cchSource / 2)
1476 {
1477 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
1478 ExitOnRootFailure(hr, "Insufficient buffer to decode string '%ls' len: %u into %u bytes.", wzSource, cchSource, cbDest);
1479 }
1480
1481 for (i = 0; i < cchSource / 2; ++i)
1482 {
1483 b = HexCharToByte(*wzSource++);
1484 (*pbDest) = b << 4;
1485
1486 b = HexCharToByte(*wzSource++);
1487 (*pbDest) |= b & 0xF;
1488
1489 ++pbDest;
1490 }
1491
1492LExit:
1493 return hr;
1494}
1495
1496
1497/****************************************************************************
1498StrAllocHexDecode - allocates a byte array hex-converted from string of text
1499
1500NOTE: wzSource must contain even number of characters
1501****************************************************************************/
1502extern "C" HRESULT DAPI StrAllocHexDecode(
1503 __in_z LPCWSTR wzSource,
1504 __out_bcount(*pcbDest) BYTE** ppbDest,
1505 __out_opt DWORD* pcbDest
1506 )
1507{
1508 Assert(wzSource && *wzSource && ppbDest);
1509
1510 HRESULT hr = S_OK;
1511 size_t cch = 0;
1512 BYTE* pb = NULL;
1513 DWORD cb = 0;
1514
1515 hr = ::StringCchLengthW(wzSource, STRSAFE_MAX_CCH, &cch);
1516 ExitOnFailure(hr, "Failed to calculate length of source string.");
1517
1518 if (cch % 2)
1519 {
1520 hr = E_INVALIDARG;
1521 ExitOnFailure(hr, "Invalid source parameter, string must be even length or it cannot be decoded.");
1522 }
1523
1524 cb = static_cast<DWORD>(cch / 2);
1525 pb = static_cast<BYTE*>(MemAlloc(cb, TRUE));
1526 ExitOnNull(pb, hr, E_OUTOFMEMORY, "Failed to allocate memory for hex decode.");
1527
1528 hr = StrHexDecode(wzSource, pb, cb);
1529 ExitOnFailure(hr, "Failed to decode source string.");
1530
1531 *ppbDest = pb;
1532 pb = NULL;
1533
1534 if (pcbDest)
1535 {
1536 *pcbDest = cb;
1537 }
1538
1539LExit:
1540 ReleaseMem(pb);
1541
1542 return hr;
1543}
1544
1545
1546/****************************************************************************
1547Base85 encoding/decoding data
1548
1549****************************************************************************/
1550const WCHAR Base85EncodeTable[] = L"!%'()*+,-./0123456789:;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{|}~";
1551
1552const BYTE Base85DecodeTable[256] =
1553{
1554 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1555 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1556 85, 0, 85, 85, 85, 1, 85, 2, 3, 4, 5, 6, 7, 8, 9, 10,
1557 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 85, 85, 85, 23,
1558 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
1559 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 85, 52, 53, 54,
1560 85, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
1561 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85,
1562 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1563 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1564 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1565 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1566 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1567 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1568 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
1569 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85
1570};
1571
1572const UINT Base85PowerTable[4] = { 1, 85, 85*85, 85*85*85 };
1573
1574
1575/****************************************************************************
1576StrAllocBase85Encode - converts an array of bytes into an XML compatible string
1577
1578****************************************************************************/
1579extern "C" HRESULT DAPI StrAllocBase85Encode(
1580 __in_bcount_opt(cbSource) const BYTE* pbSource,
1581 __in DWORD_PTR cbSource,
1582 __deref_out_z LPWSTR* pwzDest
1583 )
1584{
1585 HRESULT hr = S_OK;
1586 DWORD_PTR cchDest = 0;
1587 LPWSTR wzDest;
1588 DWORD_PTR iSource = 0;
1589 DWORD_PTR iDest = 0;
1590
1591 if (!pwzDest || !pbSource)
1592 {
1593 return E_INVALIDARG;
1594 }
1595
1596 // calc actual size of output
1597 cchDest = cbSource / 4;
1598 cchDest += cchDest * 4;
1599 if (cbSource & 3)
1600 {
1601 cchDest += (cbSource & 3) + 1;
1602 }
1603 ++cchDest; // add room for null terminator
1604
1605 hr = StrAlloc(pwzDest, cchDest);
1606 ExitOnFailure(hr, "failed to allocate destination string");
1607
1608 wzDest = *pwzDest;
1609
1610 // first, encode full words
1611 for (iSource = 0, iDest = 0; (iSource + 4 < cbSource) && (iDest + 5 < cchDest); iSource += 4, iDest += 5)
1612 {
1613 DWORD n = pbSource[iSource] + (pbSource[iSource + 1] << 8) + (pbSource[iSource + 2] << 16) + (pbSource[iSource + 3] << 24);
1614 DWORD k = n / 85;
1615
1616 //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable));
1617 wzDest[iDest] = Base85EncodeTable[n - k * 85];
1618 n = k / 85;
1619
1620 //Assert(0 <= (k - n * 85) && (k - n * 85) < countof(Base85EncodeTable));
1621 wzDest[iDest + 1] = Base85EncodeTable[k - n * 85];
1622 k = n / 85;
1623
1624 //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable));
1625 wzDest[iDest + 2] = Base85EncodeTable[n - k * 85];
1626 n = k / 85;
1627
1628 //Assert(0 <= (k - n * 85) && (k - n * 85) < countof(Base85EncodeTable));
1629 wzDest[iDest + 3] = Base85EncodeTable[k - n * 85];
1630
1631 __assume(n <= DWORD_MAX / 85 / 85 / 85 / 85);
1632
1633 //Assert(0 <= n && n < countof(Base85EncodeTable));
1634 wzDest[iDest + 4] = Base85EncodeTable[n];
1635 }
1636
1637 // encode any remaining bytes
1638 if (iSource < cbSource)
1639 {
1640 DWORD n = 0;
1641 for (DWORD i = 0; iSource + i < cbSource; ++i)
1642 {
1643 n += pbSource[iSource + i] << (i << 3);
1644 }
1645
1646 for (/* iSource already initialized */; iSource < cbSource && iDest < cchDest; ++iSource, ++iDest)
1647 {
1648 DWORD k = n / 85;
1649
1650 //Assert(0 <= (n - k * 85) && (n - k * 85) < countof(Base85EncodeTable));
1651 wzDest[iDest] = Base85EncodeTable[n - k * 85];
1652
1653 n = k;
1654 }
1655
1656 wzDest[iDest] = Base85EncodeTable[n];
1657 ++iDest;
1658 }
1659 Assert(iSource == cbSource);
1660 Assert(iDest == cchDest - 1);
1661
1662 wzDest[iDest] = L'\0';
1663 hr = S_OK;
1664
1665LExit:
1666 return hr;
1667}
1668
1669
1670/****************************************************************************
1671StrAllocBase85Decode - converts a string of text to array of bytes
1672
1673NOTE: Use MemFree() to release the allocated stream of bytes
1674****************************************************************************/
1675extern "C" HRESULT DAPI StrAllocBase85Decode(
1676 __in_z LPCWSTR wzSource,
1677 __deref_out_bcount(*pcbDest) BYTE** ppbDest,
1678 __out DWORD_PTR* pcbDest
1679 )
1680{
1681 HRESULT hr = S_OK;
1682 DWORD_PTR cchSource = lstrlenW(wzSource);
1683 DWORD_PTR i, n, k;
1684
1685 BYTE* pbDest;
1686 DWORD_PTR cbDest;
1687
1688 if (!wzSource || !ppbDest || !pcbDest)
1689 {
1690 return E_INVALIDARG;
1691 }
1692
1693 // evaluate size of output and check it
1694 k = cchSource / 5;
1695 cbDest = k << 2;
1696 k = cchSource - k * 5;
1697 if (k)
1698 {
1699 if (1 == k)
1700 {
1701 // decode error -- encoded size cannot equal 1 mod 5
1702 return E_UNEXPECTED;
1703 }
1704
1705 cbDest += k - 1;
1706 }
1707
1708 *ppbDest = static_cast<BYTE*>(MemAlloc(cbDest, FALSE));
1709 ExitOnNull(*ppbDest, hr, E_OUTOFMEMORY, "failed allocate memory to decode the string");
1710
1711 pbDest = *ppbDest;
1712 *pcbDest = cbDest;
1713
1714 // decode full words first
1715 while (5 <= cchSource)
1716 {
1717 k = Base85DecodeTable[wzSource[0]];
1718 if (85 == k)
1719 {
1720 // illegal symbol
1721 return E_UNEXPECTED;
1722 }
1723 n = k;
1724
1725 k = Base85DecodeTable[wzSource[1]];
1726 if (85 == k)
1727 {
1728 // illegal symbol
1729 return E_UNEXPECTED;
1730 }
1731 n += k * 85;
1732
1733 k = Base85DecodeTable[wzSource[2]];
1734 if (85 == k)
1735 {
1736 // illegal symbol
1737 return E_UNEXPECTED;
1738 }
1739 n += k * (85 * 85);
1740
1741 k = Base85DecodeTable[wzSource[3]];
1742 if (85 == k)
1743 {
1744 // illegal symbol
1745 return E_UNEXPECTED;
1746 }
1747 n += k * (85 * 85 * 85);
1748
1749 k = Base85DecodeTable[wzSource[4]];
1750 if (85 == k)
1751 {
1752 // illegal symbol
1753 return E_UNEXPECTED;
1754 }
1755 k *= (85 * 85 * 85 * 85);
1756
1757 // if (k + n > (1u << 32)) <=> (k > ~n) then decode error
1758 if (k > ~n)
1759 {
1760 // overflow
1761 return E_UNEXPECTED;
1762 }
1763
1764 n += k;
1765
1766 pbDest[0] = (BYTE) n;
1767 pbDest[1] = (BYTE) (n >> 8);
1768 pbDest[2] = (BYTE) (n >> 16);
1769 pbDest[3] = (BYTE) (n >> 24);
1770
1771 wzSource += 5;
1772 pbDest += 4;
1773 cchSource -= 5;
1774 }
1775
1776 if (cchSource)
1777 {
1778 n = 0;
1779 for (i = 0; i < cchSource; ++i)
1780 {
1781 k = Base85DecodeTable[wzSource[i]];
1782 if (85 == k)
1783 {
1784 // illegal symbol
1785 return E_UNEXPECTED;
1786 }
1787
1788 n += k * Base85PowerTable[i];
1789 }
1790
1791 for (i = 1; i < cchSource; ++i)
1792 {
1793 *pbDest++ = (BYTE)n;
1794 n >>= 8;
1795 }
1796
1797 if (0 != n)
1798 {
1799 // decode error
1800 return E_UNEXPECTED;
1801 }
1802 }
1803
1804 hr = S_OK;
1805
1806LExit:
1807 return hr;
1808}
1809
1810
1811/****************************************************************************
1812MultiSzLen - calculates the length of a MULTISZ string including all nulls
1813including the double null terminator at the end of the MULTISZ.
1814
1815NOTE: returns 0 if the multisz in not properly terminated with two nulls
1816****************************************************************************/
1817extern "C" HRESULT DAPI MultiSzLen(
1818 __in __nullnullterminated LPCWSTR pwzMultiSz,
1819 __out DWORD_PTR* pcch
1820 )
1821{
1822 Assert(pcch);
1823
1824 HRESULT hr = S_OK;
1825 LPCWSTR wz = pwzMultiSz;
1826 DWORD_PTR dwMaxSize = 0;
1827
1828 hr = StrMaxLength(pwzMultiSz, &dwMaxSize);
1829 ExitOnFailure(hr, "failed to get the max size of a string while calculating MULTISZ length");
1830
1831 *pcch = 0;
1832 while (*pcch < dwMaxSize)
1833 {
1834 if (L'\0' == *wz && L'\0' == *(wz + 1))
1835 {
1836 break;
1837 }
1838
1839 ++wz;
1840 *pcch = *pcch + 1;
1841 }
1842
1843 // Add two for the last 2 NULLs (that we looked ahead at)
1844 *pcch = *pcch + 2;
1845
1846 // If we've walked off the end then the length is 0
1847 if (*pcch > dwMaxSize)
1848 {
1849 *pcch = 0;
1850 }
1851
1852LExit:
1853 return hr;
1854}
1855
1856
1857/****************************************************************************
1858MultiSzPrepend - prepends a string onto the front of a MUTLISZ
1859
1860****************************************************************************/
1861extern "C" HRESULT DAPI MultiSzPrepend(
1862 __deref_inout_ecount(*pcchMultiSz) __nullnullterminated LPWSTR* ppwzMultiSz,
1863 __inout_opt DWORD_PTR *pcchMultiSz,
1864 __in __nullnullterminated LPCWSTR pwzInsert
1865 )
1866{
1867 Assert(ppwzMultiSz && pwzInsert && *pwzInsert);
1868
1869 HRESULT hr =S_OK;
1870 LPWSTR pwzResult = NULL;
1871 DWORD_PTR cchResult = 0;
1872 DWORD_PTR cchInsert = 0;
1873 DWORD_PTR cchMultiSz = 0;
1874
1875 // Get the lengths of the MULTISZ (and prime it if it's not initialized)
1876 if (pcchMultiSz && 0 != *pcchMultiSz)
1877 {
1878 cchMultiSz = *pcchMultiSz;
1879 }
1880 else
1881 {
1882 hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz);
1883 ExitOnFailure(hr, "failed to get length of multisz");
1884 }
1885
1886 cchInsert = lstrlenW(pwzInsert);
1887
1888 cchResult = cchInsert + cchMultiSz + 1;
1889
1890 // Allocate the result buffer
1891 hr = StrAlloc(&pwzResult, cchResult + 1);
1892 ExitOnFailure(hr, "failed to allocate result string");
1893
1894 // Prepend
1895 hr = ::StringCchCopyW(pwzResult, cchResult, pwzInsert);
1896 ExitOnFailure(hr, "failed to copy prepend string: %ls", pwzInsert);
1897
1898 // If there was no MULTISZ, double null terminate our result, otherwise, copy the MULTISZ in
1899 if (0 == cchMultiSz)
1900 {
1901 pwzResult[cchResult] = L'\0';
1902 ++cchResult;
1903 }
1904 else
1905 {
1906 // Copy the rest
1907 ::CopyMemory(pwzResult + cchInsert + 1, *ppwzMultiSz, cchMultiSz * sizeof(WCHAR));
1908
1909 // Free the old buffer
1910 ReleaseNullStr(*ppwzMultiSz);
1911 }
1912
1913 // Set the result
1914 *ppwzMultiSz = pwzResult;
1915
1916 if (pcchMultiSz)
1917 {
1918 *pcchMultiSz = cchResult;
1919 }
1920
1921 pwzResult = NULL;
1922
1923LExit:
1924 ReleaseNullStr(pwzResult);
1925
1926 return hr;
1927}
1928
1929/****************************************************************************
1930MultiSzFindSubstring - case insensitive find of a string in a MULTISZ that contains the
1931specified sub string and returns the index of the
1932string in the MULTISZ, the address, neither, or both
1933
1934NOTE: returns S_FALSE if the string is not found
1935****************************************************************************/
1936extern "C" HRESULT DAPI MultiSzFindSubstring(
1937 __in __nullnullterminated LPCWSTR pwzMultiSz,
1938 __in __nullnullterminated LPCWSTR pwzSubstring,
1939 __out_opt DWORD_PTR* pdwIndex,
1940 __deref_opt_out __nullnullterminated LPCWSTR* ppwzFoundIn
1941 )
1942{
1943 Assert(pwzMultiSz && *pwzMultiSz && pwzSubstring && *pwzSubstring);
1944
1945 HRESULT hr = S_FALSE; // Assume we won't find it (the glass is half empty)
1946 LPCWSTR wz = pwzMultiSz;
1947 DWORD_PTR dwIndex = 0;
1948 DWORD_PTR cchMultiSz = 0;
1949 DWORD_PTR cchProgress = 0;
1950
1951 hr = MultiSzLen(pwzMultiSz, &cchMultiSz);
1952 ExitOnFailure(hr, "failed to get the length of a MULTISZ string");
1953
1954 // Find the string containing the sub string
1955 hr = S_OK;
1956 while (NULL == wcsistr(wz, pwzSubstring))
1957 {
1958 // Slide through to the end of the current string
1959 while (L'\0' != *wz && cchProgress < cchMultiSz)
1960 {
1961 ++wz;
1962 ++cchProgress;
1963 }
1964
1965 // If we're done, we're done
1966 if (L'\0' == *(wz + 1) || cchProgress >= cchMultiSz)
1967 {
1968 hr = S_FALSE;
1969 break;
1970 }
1971
1972 // Move on to the next string
1973 ++wz;
1974 ++dwIndex;
1975 }
1976 Assert(S_OK == hr || S_FALSE == hr);
1977
1978 // If we found it give them what they want
1979 if (S_OK == hr)
1980 {
1981 if (pdwIndex)
1982 {
1983 *pdwIndex = dwIndex;
1984 }
1985
1986 if (ppwzFoundIn)
1987 {
1988 *ppwzFoundIn = wz;
1989 }
1990 }
1991
1992LExit:
1993 return hr;
1994}
1995
1996/****************************************************************************
1997MultiSzFindString - finds a string in a MULTISZ and returns the index of
1998the string in the MULTISZ, the address or both
1999
2000NOTE: returns S_FALSE if the string is not found
2001****************************************************************************/
2002extern "C" HRESULT DAPI MultiSzFindString(
2003 __in __nullnullterminated LPCWSTR pwzMultiSz,
2004 __in __nullnullterminated LPCWSTR pwzString,
2005 __out_opt DWORD_PTR* pdwIndex,
2006 __deref_opt_out __nullnullterminated LPCWSTR* ppwzFound
2007 )
2008{
2009 Assert(pwzMultiSz && *pwzMultiSz && pwzString && *pwzString && (pdwIndex || ppwzFound));
2010
2011 HRESULT hr = S_FALSE; // Assume we won't find it
2012 LPCWSTR wz = pwzMultiSz;
2013 DWORD_PTR dwIndex = 0;
2014 DWORD_PTR cchMutliSz = 0;
2015 DWORD_PTR cchProgress = 0;
2016
2017 hr = MultiSzLen(pwzMultiSz, &cchMutliSz);
2018 ExitOnFailure(hr, "failed to get the length of a MULTISZ string");
2019
2020 // Find the string
2021 hr = S_OK;
2022 while (0 != lstrcmpW(wz, pwzString))
2023 {
2024 // Slide through to the end of the current string
2025 while (L'\0' != *wz && cchProgress < cchMutliSz)
2026 {
2027 ++wz;
2028 ++cchProgress;
2029 }
2030
2031 // If we're done, we're done
2032 if (L'\0' == *(wz + 1) || cchProgress >= cchMutliSz)
2033 {
2034 hr = S_FALSE;
2035 break;
2036 }
2037
2038 // Move on to the next string
2039 ++wz;
2040 ++dwIndex;
2041 }
2042 Assert(S_OK == hr || S_FALSE == hr);
2043
2044 // If we found it give them what they want
2045 if (S_OK == hr)
2046 {
2047 if (pdwIndex)
2048 {
2049 *pdwIndex = dwIndex;
2050 }
2051
2052 if (ppwzFound)
2053 {
2054 *ppwzFound = wz;
2055 }
2056 }
2057
2058LExit:
2059 return hr;
2060}
2061
2062/****************************************************************************
2063MultiSzRemoveString - removes string from a MULTISZ at the specified
2064index
2065
2066NOTE: does an in place removal without shrinking the memory allocation
2067
2068NOTE: returns S_FALSE if the MULTISZ has fewer strings than dwIndex
2069****************************************************************************/
2070extern "C" HRESULT DAPI MultiSzRemoveString(
2071 __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz,
2072 __in DWORD_PTR dwIndex
2073 )
2074{
2075 Assert(ppwzMultiSz && *ppwzMultiSz);
2076
2077 HRESULT hr = S_OK;
2078 LPCWSTR wz = *ppwzMultiSz;
2079 LPCWSTR wzNext = NULL;
2080 DWORD_PTR dwCurrentIndex = 0;
2081 DWORD_PTR cchMultiSz = 0;
2082 DWORD_PTR cchProgress = 0;
2083
2084 hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz);
2085 ExitOnFailure(hr, "failed to get the length of a MULTISZ string");
2086
2087 // Find the index we want to remove
2088 hr = S_OK;
2089 while (dwCurrentIndex < dwIndex)
2090 {
2091 // Slide through to the end of the current string
2092 while (L'\0' != *wz && cchProgress < cchMultiSz)
2093 {
2094 ++wz;
2095 ++cchProgress;
2096 }
2097
2098 // If we're done, we're done
2099 if (L'\0' == *(wz + 1) || cchProgress >= cchMultiSz)
2100 {
2101 hr = S_FALSE;
2102 break;
2103 }
2104
2105 // Move on to the next string
2106 ++wz;
2107 ++cchProgress;
2108 ++dwCurrentIndex;
2109 }
2110 Assert(S_OK == hr || S_FALSE == hr);
2111
2112 // If we found the index to be removed
2113 if (S_OK == hr)
2114 {
2115 wzNext = wz;
2116
2117 // Slide through to the end of the current string
2118 while (L'\0' != *wzNext && cchProgress < cchMultiSz)
2119 {
2120 ++wzNext;
2121 ++cchProgress;
2122 }
2123
2124 // Something weird has happened if we're past the end of the MULTISZ
2125 if (cchProgress > cchMultiSz)
2126 {
2127 hr = E_UNEXPECTED;
2128 ExitOnFailure(hr, "failed to move past the string to be removed from MULTISZ");
2129 }
2130
2131 // Move on to the next character
2132 ++wzNext;
2133 ++cchProgress;
2134
2135 ::MoveMemory((LPVOID)wz, (LPVOID)wzNext, (cchMultiSz - cchProgress) * sizeof(WCHAR));
2136 }
2137
2138LExit:
2139 return hr;
2140}
2141
2142/****************************************************************************
2143MultiSzInsertString - inserts new string at the specified index
2144
2145****************************************************************************/
2146extern "C" HRESULT DAPI MultiSzInsertString(
2147 __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz,
2148 __inout_opt DWORD_PTR *pcchMultiSz,
2149 __in DWORD_PTR dwIndex,
2150 __in __nullnullterminated LPCWSTR pwzInsert
2151 )
2152{
2153 Assert(ppwzMultiSz && pwzInsert && *pwzInsert);
2154
2155 HRESULT hr = S_OK;
2156 LPCWSTR wz = *ppwzMultiSz;
2157 DWORD_PTR dwCurrentIndex = 0;
2158 DWORD_PTR cchProgress = 0;
2159 LPWSTR pwzResult = NULL;
2160 DWORD_PTR cchResult = 0;
2161 DWORD_PTR cchString = lstrlenW(pwzInsert);
2162 DWORD_PTR cchMultiSz = 0;
2163
2164 if (pcchMultiSz && 0 != *pcchMultiSz)
2165 {
2166 cchMultiSz = *pcchMultiSz;
2167 }
2168 else
2169 {
2170 hr = MultiSzLen(*ppwzMultiSz, &cchMultiSz);
2171 ExitOnFailure(hr, "failed to get the length of a MULTISZ string");
2172 }
2173
2174 // Find the index we want to insert at
2175 hr = S_OK;
2176 while (dwCurrentIndex < dwIndex)
2177 {
2178 // Slide through to the end of the current string
2179 while (L'\0' != *wz && cchProgress < cchMultiSz)
2180 {
2181 ++wz;
2182 ++cchProgress;
2183 }
2184
2185 // If we're done, we're done
2186 if ((dwCurrentIndex + 1 != dwIndex && L'\0' == *(wz + 1)) || cchProgress >= cchMultiSz)
2187 {
2188 hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND);
2189 ExitOnRootFailure(hr, "requested to insert into an invalid index: %u in a MULTISZ", dwIndex);
2190 }
2191
2192 // Move on to the next string
2193 ++wz;
2194 ++cchProgress;
2195 ++dwCurrentIndex;
2196 }
2197
2198 //
2199 // Insert the string
2200 //
2201 cchResult = cchMultiSz + cchString + 1;
2202
2203 hr = StrAlloc(&pwzResult, cchResult);
2204 ExitOnFailure(hr, "failed to allocate result string for MULTISZ insert");
2205
2206 // Copy the part before the insert
2207 ::CopyMemory(pwzResult, *ppwzMultiSz, cchProgress * sizeof(WCHAR));
2208
2209 // Copy the insert part
2210 ::CopyMemory(pwzResult + cchProgress, pwzInsert, (cchString + 1) * sizeof(WCHAR));
2211
2212 // Copy the part after the insert
2213 ::CopyMemory(pwzResult + cchProgress + cchString + 1, wz, (cchMultiSz - cchProgress) * sizeof(WCHAR));
2214
2215 // Free the old buffer
2216 ReleaseNullStr(*ppwzMultiSz);
2217
2218 // Set the result
2219 *ppwzMultiSz = pwzResult;
2220
2221 // If they wanted the resulting length, let 'em have it
2222 if (pcchMultiSz)
2223 {
2224 *pcchMultiSz = cchResult;
2225 }
2226
2227 pwzResult = NULL;
2228
2229LExit:
2230 ReleaseStr(pwzResult);
2231
2232 return hr;
2233}
2234
2235/****************************************************************************
2236MultiSzReplaceString - replaces string at the specified index with a new one
2237
2238****************************************************************************/
2239extern "C" HRESULT DAPI MultiSzReplaceString(
2240 __deref_inout __nullnullterminated LPWSTR* ppwzMultiSz,
2241 __in DWORD_PTR dwIndex,
2242 __in __nullnullterminated LPCWSTR pwzString
2243 )
2244{
2245 Assert(ppwzMultiSz && pwzString && *pwzString);
2246
2247 HRESULT hr = S_OK;
2248
2249 hr = MultiSzRemoveString(ppwzMultiSz, dwIndex);
2250 ExitOnFailure(hr, "failed to remove string from MULTISZ at the specified index: %u", dwIndex);
2251
2252 hr = MultiSzInsertString(ppwzMultiSz, NULL, dwIndex, pwzString);
2253 ExitOnFailure(hr, "failed to insert string into MULTISZ at the specified index: %u", dwIndex);
2254
2255LExit:
2256 return hr;
2257}
2258
2259
2260/****************************************************************************
2261wcsistr - case insensitive find a substring
2262
2263****************************************************************************/
2264extern "C" LPCWSTR DAPI wcsistr(
2265 __in_z LPCWSTR wzString,
2266 __in_z LPCWSTR wzCharSet
2267 )
2268{
2269 LPCWSTR wzSource = wzString;
2270 LPCWSTR wzSearch = NULL;
2271 DWORD_PTR cchSourceIndex = 0;
2272
2273 // Walk through wzString (the source string) one character at a time
2274 while (*wzSource)
2275 {
2276 cchSourceIndex = 0;
2277 wzSearch = wzCharSet;
2278
2279 // Look ahead in the source string until we get a full match or we hit the end of the source
2280 while (L'\0' != wzSource[cchSourceIndex] && L'\0' != *wzSearch && towlower(wzSource[cchSourceIndex]) == towlower(*wzSearch))
2281 {
2282 ++cchSourceIndex;
2283 ++wzSearch;
2284 }
2285
2286 // If we found it, return the point that we found it at
2287 if (L'\0' == *wzSearch)
2288 {
2289 return wzSource;
2290 }
2291
2292 // Walk ahead one character
2293 ++wzSource;
2294 }
2295
2296 return NULL;
2297}
2298
2299/****************************************************************************
2300StrStringToInt16 - converts a string to a signed 16-bit integer.
2301
2302****************************************************************************/
2303extern "C" HRESULT DAPI StrStringToInt16(
2304 __in_z LPCWSTR wzIn,
2305 __in DWORD cchIn,
2306 __out SHORT* psOut
2307 )
2308{
2309 HRESULT hr = S_OK;
2310 LONGLONG ll = 0;
2311
2312 hr = StrStringToInt64(wzIn, cchIn, &ll);
2313 ExitOnFailure(hr, "Failed to parse int64.");
2314
2315 if (SHORT_MAX < ll || SHORT_MIN > ll)
2316 {
2317 ExitFunction1(hr = DISP_E_OVERFLOW);
2318 }
2319 *psOut = (SHORT)ll;
2320
2321LExit:
2322 return hr;
2323}
2324
2325/****************************************************************************
2326StrStringToUInt16 - converts a string to an unsigned 16-bit integer.
2327
2328****************************************************************************/
2329extern "C" HRESULT DAPI StrStringToUInt16(
2330 __in_z LPCWSTR wzIn,
2331 __in DWORD cchIn,
2332 __out USHORT* pusOut
2333 )
2334{
2335 HRESULT hr = S_OK;
2336 ULONGLONG ull = 0;
2337
2338 hr = StrStringToUInt64(wzIn, cchIn, &ull);
2339 ExitOnFailure(hr, "Failed to parse uint64.");
2340
2341 if (USHORT_MAX < ull)
2342 {
2343 ExitFunction1(hr = DISP_E_OVERFLOW);
2344 }
2345 *pusOut = (USHORT)ull;
2346
2347LExit:
2348 return hr;
2349}
2350
2351/****************************************************************************
2352StrStringToInt32 - converts a string to a signed 32-bit integer.
2353
2354****************************************************************************/
2355extern "C" HRESULT DAPI StrStringToInt32(
2356 __in_z LPCWSTR wzIn,
2357 __in DWORD cchIn,
2358 __out INT* piOut
2359 )
2360{
2361 HRESULT hr = S_OK;
2362 LONGLONG ll = 0;
2363
2364 hr = StrStringToInt64(wzIn, cchIn, &ll);
2365 ExitOnFailure(hr, "Failed to parse int64.");
2366
2367 if (INT_MAX < ll || INT_MIN > ll)
2368 {
2369 ExitFunction1(hr = DISP_E_OVERFLOW);
2370 }
2371 *piOut = (INT)ll;
2372
2373LExit:
2374 return hr;
2375}
2376
2377/****************************************************************************
2378StrStringToUInt32 - converts a string to an unsigned 32-bit integer.
2379
2380****************************************************************************/
2381extern "C" HRESULT DAPI StrStringToUInt32(
2382 __in_z LPCWSTR wzIn,
2383 __in DWORD cchIn,
2384 __out UINT* puiOut
2385 )
2386{
2387 HRESULT hr = S_OK;
2388 ULONGLONG ull = 0;
2389
2390 hr = StrStringToUInt64(wzIn, cchIn, &ull);
2391 ExitOnFailure(hr, "Failed to parse uint64.");
2392
2393 if (UINT_MAX < ull)
2394 {
2395 ExitFunction1(hr = DISP_E_OVERFLOW);
2396 }
2397 *puiOut = (UINT)ull;
2398
2399LExit:
2400 return hr;
2401}
2402
2403/****************************************************************************
2404StrStringToInt64 - converts a string to a signed 64-bit integer.
2405
2406****************************************************************************/
2407extern "C" HRESULT DAPI StrStringToInt64(
2408 __in_z LPCWSTR wzIn,
2409 __in DWORD cchIn,
2410 __out LONGLONG* pllOut
2411 )
2412{
2413 HRESULT hr = S_OK;
2414 DWORD i = 0;
2415 INT iSign = 1;
2416 INT nDigit = 0;
2417 LARGE_INTEGER liValue = { };
2418
2419 // get string length if not provided
2420 if (0 >= cchIn)
2421 {
2422 cchIn = lstrlenW(wzIn);
2423 if (0 >= cchIn)
2424 {
2425 ExitFunction1(hr = E_INVALIDARG);
2426 }
2427 }
2428
2429 // check sign
2430 if (L'-' == wzIn[0])
2431 {
2432 if (1 >= cchIn)
2433 {
2434 ExitFunction1(hr = E_INVALIDARG);
2435 }
2436 i = 1;
2437 iSign = -1;
2438 }
2439
2440 // read digits
2441 while (i < cchIn)
2442 {
2443 nDigit = wzIn[i] - L'0';
2444 if (0 > nDigit || 9 < nDigit)
2445 {
2446 ExitFunction1(hr = E_INVALIDARG);
2447 }
2448 liValue.QuadPart = liValue.QuadPart * 10 + nDigit * iSign;
2449
2450 if ((liValue.HighPart ^ iSign) & INT_MIN)
2451 {
2452 ExitFunction1(hr = DISP_E_OVERFLOW);
2453 }
2454 ++i;
2455 }
2456
2457 *pllOut = liValue.QuadPart;
2458
2459LExit:
2460 return hr;
2461}
2462
2463/****************************************************************************
2464StrStringToUInt64 - converts a string to an unsigned 64-bit integer.
2465
2466****************************************************************************/
2467extern "C" HRESULT DAPI StrStringToUInt64(
2468 __in_z LPCWSTR wzIn,
2469 __in DWORD cchIn,
2470 __out ULONGLONG* pullOut
2471 )
2472{
2473 HRESULT hr = S_OK;
2474 DWORD i = 0;
2475 DWORD nDigit = 0;
2476 ULONGLONG ullValue = 0;
2477 ULONGLONG ull = 0;
2478
2479 // get string length if not provided
2480 if (0 >= cchIn)
2481 {
2482 cchIn = lstrlenW(wzIn);
2483 if (0 >= cchIn)
2484 {
2485 ExitFunction1(hr = E_INVALIDARG);
2486 }
2487 }
2488
2489 // read digits
2490 while (i < cchIn)
2491 {
2492 nDigit = wzIn[i] - L'0';
2493 if (9 < nDigit)
2494 {
2495 ExitFunction1(hr = E_INVALIDARG);
2496 }
2497 ull = (ULONGLONG)(ullValue * 10 + nDigit);
2498
2499 if (ull < ullValue)
2500 {
2501 ExitFunction1(hr = DISP_E_OVERFLOW);
2502 }
2503 ullValue = ull;
2504 ++i;
2505 }
2506
2507 *pullOut = ullValue;
2508
2509LExit:
2510 return hr;
2511}
2512
2513/****************************************************************************
2514StrStringToUpper - alters the given string in-place to be entirely uppercase
2515
2516****************************************************************************/
2517void DAPI StrStringToUpper(
2518 __inout_z LPWSTR wzIn
2519 )
2520{
2521 ::CharUpperBuffW(wzIn, lstrlenW(wzIn));
2522}
2523
2524/****************************************************************************
2525StrStringToLower - alters the given string in-place to be entirely lowercase
2526
2527****************************************************************************/
2528void DAPI StrStringToLower(
2529 __inout_z LPWSTR wzIn
2530 )
2531{
2532 ::CharLowerBuffW(wzIn, lstrlenW(wzIn));
2533}
2534
2535/****************************************************************************
2536StrAllocStringToUpperInvariant - creates an upper-case copy of a string.
2537
2538****************************************************************************/
2539extern "C" HRESULT DAPI StrAllocStringToUpperInvariant(
2540 __deref_out_z LPWSTR* pscz,
2541 __in_z LPCWSTR wzSource,
2542 __in int cchSource
2543 )
2544{
2545 return StrAllocStringMapInvariant(pscz, wzSource, cchSource, LCMAP_UPPERCASE);
2546}
2547
2548/****************************************************************************
2549StrAllocStringToLowerInvariant - creates an lower-case copy of a string.
2550
2551****************************************************************************/
2552extern "C" HRESULT DAPI StrAllocStringToLowerInvariant(
2553 __deref_out_z LPWSTR* pscz,
2554 __in_z LPCWSTR wzSource,
2555 __in int cchSource
2556 )
2557{
2558 return StrAllocStringMapInvariant(pscz, wzSource, cchSource, LCMAP_LOWERCASE);
2559}
2560
2561/****************************************************************************
2562StrArrayAllocString - Allocates a string array.
2563
2564****************************************************************************/
2565extern "C" HRESULT DAPI StrArrayAllocString(
2566 __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray,
2567 __inout LPUINT pcStrArray,
2568 __in_z LPCWSTR wzSource,
2569 __in DWORD_PTR cchSource
2570 )
2571{
2572 HRESULT hr = S_OK;
2573 UINT cNewStrArray;
2574
2575 hr = ::UIntAdd(*pcStrArray, 1, &cNewStrArray);
2576 ExitOnFailure(hr, "Failed to increment the string array element count.");
2577
2578 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(prgsczStrArray), cNewStrArray, sizeof(LPWSTR), ARRAY_GROWTH_SIZE);
2579 ExitOnFailure(hr, "Failed to allocate memory for the string array.");
2580
2581 hr = StrAllocString(&(*prgsczStrArray)[*pcStrArray], wzSource, cchSource);
2582 ExitOnFailure(hr, "Failed to allocate and assign the string.");
2583
2584 *pcStrArray = cNewStrArray;
2585
2586LExit:
2587 return hr;
2588}
2589
2590/****************************************************************************
2591StrArrayFree - Frees a string array.
2592
2593Use ReleaseNullStrArray to nullify the arguments.
2594
2595****************************************************************************/
2596extern "C" HRESULT DAPI StrArrayFree(
2597 __in_ecount(cStrArray) LPWSTR *rgsczStrArray,
2598 __in UINT cStrArray
2599 )
2600{
2601 HRESULT hr = S_OK;
2602
2603 for (UINT i = 0; i < cStrArray; ++i)
2604 {
2605 if (NULL != rgsczStrArray[i])
2606 {
2607 hr = StrFree(rgsczStrArray[i]);
2608 ExitOnFailure(hr, "Failed to free the string at index %u.", i);
2609 }
2610 }
2611
2612 hr = MemFree(rgsczStrArray);
2613 ExitOnFailure(hr, "Failed to free memory for the string array.");
2614
2615LExit:
2616 return hr;
2617}
2618
2619/****************************************************************************
2620StrSplitAllocArray - Splits a string into an array.
2621
2622****************************************************************************/
2623extern "C" HRESULT DAPI StrSplitAllocArray(
2624 __deref_inout_ecount_opt(*pcStrArray) LPWSTR **prgsczStrArray,
2625 __inout LPUINT pcStrArray,
2626 __in_z LPCWSTR wzSource,
2627 __in_z LPCWSTR wzDelim
2628 )
2629{
2630 HRESULT hr = S_OK;
2631 LPWSTR sczCopy = NULL;
2632 LPWSTR wzContext = NULL;
2633
2634 // Copy wzSource so it is not modified.
2635 hr = StrAllocString(&sczCopy, wzSource, 0);
2636 ExitOnFailure(hr, "Failed to copy the source string.");
2637
2638 for (LPCWSTR wzToken = ::wcstok_s(sczCopy, wzDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, wzDelim, &wzContext))
2639 {
2640 hr = StrArrayAllocString(prgsczStrArray, pcStrArray, wzToken, 0);
2641 ExitOnFailure(hr, "Failed to add the string to the string array.");
2642 }
2643
2644LExit:
2645 ReleaseStr(sczCopy);
2646
2647 return hr;
2648}
2649
2650/****************************************************************************
2651StrAllocStringMapInvariant - helper function for the ToUpper and ToLower.
2652
2653Note: Assumes source and destination buffers will be the same.
2654****************************************************************************/
2655static HRESULT StrAllocStringMapInvariant(
2656 __deref_out_z LPWSTR* pscz,
2657 __in_z LPCWSTR wzSource,
2658 __in int cchSource,
2659 __in DWORD dwMapFlags
2660 )
2661{
2662 HRESULT hr = S_OK;
2663
2664 hr = StrAllocString(pscz, wzSource, cchSource);
2665 ExitOnFailure(hr, "Failed to allocate a copy of the source string.");
2666
2667 if (0 == cchSource)
2668 {
2669 // Need the actual string size for LCMapString. This includes the null-terminator
2670 // but LCMapString doesn't care either way.
2671 hr = ::StringCchLengthW(*pscz, INT_MAX, reinterpret_cast<size_t*>(&cchSource));
2672 ExitOnFailure(hr, "Failed to get the length of the string.");
2673 }
2674
2675 // Convert the copy of the string to upper or lower case in-place.
2676 if (0 == ::LCMapStringW(LOCALE_INVARIANT, dwMapFlags, *pscz, cchSource, *pscz, cchSource))
2677 {
2678 ExitWithLastError(hr, "Failed to convert the string case.");
2679 }
2680
2681LExit:
2682 return hr;
2683}
2684
2685/****************************************************************************
2686StrSecureZeroString - zeroes out string to the make sure the contents
2687don't remain in memory.
2688
2689****************************************************************************/
2690extern "C" DAPI_(HRESULT) StrSecureZeroString(
2691 __in LPWSTR pwz
2692 )
2693{
2694 HRESULT hr = S_OK;
2695 DWORD_PTR cch;
2696
2697 if (pwz)
2698 {
2699 cch = MemSize(pwz);
2700 if (-1 == cch)
2701 {
2702 hr = E_INVALIDARG;
2703 ExitOnFailure(hr, "Failed to get size of string");
2704 }
2705 else
2706 {
2707 SecureZeroMemory(pwz, cch);
2708 }
2709 }
2710
2711LExit:
2712 return hr;
2713}
2714
2715/****************************************************************************
2716StrSecureZeroFreeString - zeroes out string to the make sure the contents
2717don't remain in memory, then frees the string.
2718
2719****************************************************************************/
2720extern "C" DAPI_(HRESULT) StrSecureZeroFreeString(
2721 __in LPWSTR pwz
2722 )
2723{
2724 HRESULT hr = S_OK;
2725
2726 hr = StrSecureZeroString(pwz);
2727 ReleaseStr(pwz);
2728
2729 return hr;
2730}