aboutsummaryrefslogtreecommitdiff
path: root/src/libs/dutil/WixToolset.DUtil/verutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/verutil.cpp')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/verutil.cpp647
1 files changed, 647 insertions, 0 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/verutil.cpp b/src/libs/dutil/WixToolset.DUtil/verutil.cpp
new file mode 100644
index 00000000..21626f94
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/verutil.cpp
@@ -0,0 +1,647 @@
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// Exit macros
6#define VerExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
7#define VerExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
8#define VerExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
9#define VerExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
10#define VerExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
11#define VerExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
12#define VerExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_VERUTIL, p, x, e, s, __VA_ARGS__)
13#define VerExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_VERUTIL, p, x, s, __VA_ARGS__)
14#define VerExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_VERUTIL, p, x, e, s, __VA_ARGS__)
15#define VerExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_VERUTIL, p, x, s, __VA_ARGS__)
16#define VerExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_VERUTIL, e, x, s, __VA_ARGS__)
17
18// constants
19const DWORD GROW_RELEASE_LABELS = 3;
20
21// Forward declarations.
22static int CompareDword(
23 __in const DWORD& dw1,
24 __in const DWORD& dw2
25 );
26static HRESULT CompareReleaseLabel(
27 __in const VERUTIL_VERSION_RELEASE_LABEL* p1,
28 __in LPCWSTR wzVersion1,
29 __in const VERUTIL_VERSION_RELEASE_LABEL* p2,
30 __in LPCWSTR wzVersion2,
31 __out int* pnResult
32 );
33static HRESULT CompareVersionSubstring(
34 __in LPCWSTR wzString1,
35 __in int cchCount1,
36 __in LPCWSTR wzString2,
37 __in int cchCount2,
38 __out int* pnResult
39 );
40
41
42DAPI_(HRESULT) VerCompareParsedVersions(
43 __in_opt VERUTIL_VERSION* pVersion1,
44 __in_opt VERUTIL_VERSION* pVersion2,
45 __out int* pnResult
46 )
47{
48 HRESULT hr = S_OK;
49 int nResult = 0;
50 DWORD cMaxReleaseLabels = 0;
51 BOOL fCompareMetadata = FALSE;
52
53 if (pVersion1 && !pVersion1->sczVersion ||
54 pVersion2 && !pVersion2->sczVersion)
55 {
56 ExitFunction1(hr = E_INVALIDARG);
57 }
58
59 if (pVersion1 == pVersion2)
60 {
61 ExitFunction1(nResult = 0);
62 }
63 else if (pVersion1 && !pVersion2)
64 {
65 ExitFunction1(nResult = 1);
66 }
67 else if (!pVersion1 && pVersion2)
68 {
69 ExitFunction1(nResult = -1);
70 }
71
72 nResult = CompareDword(pVersion1->dwMajor, pVersion2->dwMajor);
73 if (0 != nResult)
74 {
75 ExitFunction();
76 }
77
78 nResult = CompareDword(pVersion1->dwMinor, pVersion2->dwMinor);
79 if (0 != nResult)
80 {
81 ExitFunction();
82 }
83
84 nResult = CompareDword(pVersion1->dwPatch, pVersion2->dwPatch);
85 if (0 != nResult)
86 {
87 ExitFunction();
88 }
89
90 nResult = CompareDword(pVersion1->dwRevision, pVersion2->dwRevision);
91 if (0 != nResult)
92 {
93 ExitFunction();
94 }
95
96 if (pVersion1->cReleaseLabels)
97 {
98 if (pVersion2->cReleaseLabels)
99 {
100 cMaxReleaseLabels = max(pVersion1->cReleaseLabels, pVersion2->cReleaseLabels);
101 }
102 else
103 {
104 ExitFunction1(nResult = -1);
105 }
106 }
107 else if (pVersion2->cReleaseLabels)
108 {
109 ExitFunction1(nResult = 1);
110 }
111
112 if (cMaxReleaseLabels)
113 {
114 for (DWORD i = 0; i < cMaxReleaseLabels; ++i)
115 {
116 VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel1 = pVersion1->cReleaseLabels > i ? pVersion1->rgReleaseLabels + i : NULL;
117 VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel2 = pVersion2->cReleaseLabels > i ? pVersion2->rgReleaseLabels + i : NULL;
118
119 hr = CompareReleaseLabel(pReleaseLabel1, pVersion1->sczVersion, pReleaseLabel2, pVersion2->sczVersion, &nResult);
120 if (FAILED(hr) || 0 != nResult)
121 {
122 ExitFunction();
123 }
124 }
125 }
126
127 if (pVersion1->fInvalid)
128 {
129 if (!pVersion2->fInvalid)
130 {
131 ExitFunction1(nResult = -1);
132 }
133 else
134 {
135 fCompareMetadata = TRUE;
136 }
137 }
138 else if (pVersion2->fInvalid)
139 {
140 ExitFunction1(nResult = 1);
141 }
142
143 if (fCompareMetadata)
144 {
145 hr = CompareVersionSubstring(pVersion1->sczVersion + pVersion1->cchMetadataOffset, -1, pVersion2->sczVersion + pVersion2->cchMetadataOffset, -1, &nResult);
146 }
147
148LExit:
149 *pnResult = nResult;
150 return hr;
151}
152
153DAPI_(HRESULT) VerCompareStringVersions(
154 __in_z LPCWSTR wzVersion1,
155 __in_z LPCWSTR wzVersion2,
156 __in BOOL fStrict,
157 __out int* pnResult
158 )
159{
160 HRESULT hr = S_OK;
161 VERUTIL_VERSION* pVersion1 = NULL;
162 VERUTIL_VERSION* pVersion2 = NULL;
163 int nResult = 0;
164
165 hr = VerParseVersion(wzVersion1, 0, fStrict, &pVersion1);
166 VerExitOnFailure(hr, "Failed to parse Verutil version '%ls'", wzVersion1);
167
168 hr = VerParseVersion(wzVersion2, 0, fStrict, &pVersion2);
169 VerExitOnFailure(hr, "Failed to parse Verutil version '%ls'", wzVersion2);
170
171 hr = VerCompareParsedVersions(pVersion1, pVersion2, &nResult);
172 VerExitOnFailure(hr, "Failed to compare parsed Verutil versions '%ls' and '%ls'.", wzVersion1, wzVersion2);
173
174LExit:
175 *pnResult = nResult;
176
177 ReleaseVerutilVersion(pVersion1);
178 ReleaseVerutilVersion(pVersion2);
179
180 return hr;
181}
182
183DAPI_(HRESULT) VerCopyVersion(
184 __in VERUTIL_VERSION* pSource,
185 __out VERUTIL_VERSION** ppVersion
186 )
187{
188 HRESULT hr = S_OK;
189 VERUTIL_VERSION* pCopy = NULL;
190
191 pCopy = reinterpret_cast<VERUTIL_VERSION*>(MemAlloc(sizeof(VERUTIL_VERSION), TRUE));
192 VerExitOnNull(pCopy, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version copy.");
193
194 hr = StrAllocString(&pCopy->sczVersion, pSource->sczVersion, 0);
195 VerExitOnFailure(hr, "Failed to copy Verutil version string '%ls'.", pSource->sczVersion);
196
197 pCopy->dwMajor = pSource->dwMajor;
198 pCopy->dwMinor = pSource->dwMinor;
199 pCopy->dwPatch = pSource->dwPatch;
200 pCopy->dwRevision = pSource->dwRevision;
201
202 if (pSource->cReleaseLabels)
203 {
204 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pCopy->rgReleaseLabels), 0, sizeof(VERUTIL_VERSION_RELEASE_LABEL), pSource->cReleaseLabels);
205 VerExitOnFailure(hr, "Failed to allocate memory for Verutil version release labels copies.");
206
207 pCopy->cReleaseLabels = pSource->cReleaseLabels;
208
209 for (DWORD i = 0; i < pCopy->cReleaseLabels; ++i)
210 {
211 VERUTIL_VERSION_RELEASE_LABEL* pSourceLabel = pSource->rgReleaseLabels + i;
212 VERUTIL_VERSION_RELEASE_LABEL* pCopyLabel = pCopy->rgReleaseLabels + i;
213
214 pCopyLabel->cchLabelOffset = pSourceLabel->cchLabelOffset;
215 pCopyLabel->cchLabel = pSourceLabel->cchLabel;
216 pCopyLabel->fNumeric = pSourceLabel->fNumeric;
217 pCopyLabel->dwValue = pSourceLabel->dwValue;
218 }
219 }
220
221 pCopy->cchMetadataOffset = pSource->cchMetadataOffset;
222 pCopy->fInvalid = pSource->fInvalid;
223
224 *ppVersion = pCopy;
225 pCopy = NULL;
226
227LExit:
228 ReleaseVerutilVersion(pCopy);
229
230 return hr;
231}
232
233DAPI_(void) VerFreeVersion(
234 __in VERUTIL_VERSION* pVersion
235 )
236{
237 if (pVersion)
238 {
239 ReleaseStr(pVersion->sczVersion);
240 ReleaseMem(pVersion->rgReleaseLabels);
241 ReleaseMem(pVersion);
242 }
243}
244
245DAPI_(HRESULT) VerParseVersion(
246 __in_z LPCWSTR wzVersion,
247 __in SIZE_T cchVersion,
248 __in BOOL fStrict,
249 __out VERUTIL_VERSION** ppVersion
250 )
251{
252 HRESULT hr = S_OK;
253 VERUTIL_VERSION* pVersion = NULL;
254 LPCWSTR wzEnd = NULL;
255 LPCWSTR wzPartBegin = NULL;
256 LPCWSTR wzPartEnd = NULL;
257 BOOL fInvalid = FALSE;
258 BOOL fLastPart = FALSE;
259 BOOL fTrailingDot = FALSE;
260 BOOL fParsedVersionNumber = FALSE;
261 BOOL fExpectedReleaseLabels = FALSE;
262 DWORD iPart = 0;
263
264 if (!wzVersion || !ppVersion)
265 {
266 ExitFunction1(hr = E_INVALIDARG);
267 }
268
269 // Get string length if not provided.
270 if (!cchVersion)
271 {
272 hr = ::StringCchLengthW(wzVersion, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchVersion));
273 VerExitOnRootFailure(hr, "Failed to get length of version string: %ls", wzVersion);
274 }
275 else if (INT_MAX < cchVersion)
276 {
277 VerExitOnRootFailure(hr = E_INVALIDARG, "Version string is too long: %Iu", cchVersion);
278 }
279
280 if (L'v' == *wzVersion || L'V' == *wzVersion)
281 {
282 ++wzVersion;
283 --cchVersion;
284 }
285
286 pVersion = reinterpret_cast<VERUTIL_VERSION*>(MemAlloc(sizeof(VERUTIL_VERSION), TRUE));
287 VerExitOnNull(pVersion, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version '%ls'.", wzVersion);
288
289 hr = StrAllocString(&pVersion->sczVersion, wzVersion, cchVersion);
290 VerExitOnFailure(hr, "Failed to copy Verutil version string '%ls'.", wzVersion);
291
292 wzVersion = wzPartBegin = wzPartEnd = pVersion->sczVersion;
293
294 // Save end pointer.
295 wzEnd = wzVersion + cchVersion;
296
297 // Parse version number
298 while (wzPartBegin < wzEnd)
299 {
300 fTrailingDot = FALSE;
301
302 // Find end of part.
303 for (;;)
304 {
305 if (wzPartEnd >= wzEnd)
306 {
307 fLastPart = TRUE;
308 break;
309 }
310
311 switch (*wzPartEnd)
312 {
313 case L'0':
314 case L'1':
315 case L'2':
316 case L'3':
317 case L'4':
318 case L'5':
319 case L'6':
320 case L'7':
321 case L'8':
322 case L'9':
323 ++wzPartEnd;
324 continue;
325 case L'.':
326 fTrailingDot = TRUE;
327 break;
328 case L'-':
329 case L'+':
330 fLastPart = TRUE;
331 break;
332 default:
333 fInvalid = TRUE;
334 break;
335 }
336
337 break;
338 }
339
340 if (wzPartBegin == wzPartEnd)
341 {
342 fInvalid = TRUE;
343 }
344
345 if (fInvalid)
346 {
347 break;
348 }
349
350 DWORD cchPart = 0;
351 hr = ::PtrdiffTToDWord(wzPartEnd - wzPartBegin, &cchPart);
352 if (FAILED(hr))
353 {
354 fInvalid = TRUE;
355 break;
356 }
357
358 // Parse version part.
359 UINT uPart = 0;
360 hr = StrStringToUInt32(wzPartBegin, cchPart, &uPart);
361 if (FAILED(hr))
362 {
363 fInvalid = TRUE;
364 break;
365 }
366
367 switch (iPart)
368 {
369 case 0:
370 pVersion->dwMajor = uPart;
371 break;
372 case 1:
373 pVersion->dwMinor = uPart;
374 break;
375 case 2:
376 pVersion->dwPatch = uPart;
377 break;
378 case 3:
379 pVersion->dwRevision = uPart;
380 break;
381 }
382
383 if (fTrailingDot)
384 {
385 ++wzPartEnd;
386 }
387 wzPartBegin = wzPartEnd;
388 ++iPart;
389
390 if (4 <= iPart || fLastPart)
391 {
392 fParsedVersionNumber = TRUE;
393 break;
394 }
395 }
396
397 fInvalid |= !fParsedVersionNumber || fTrailingDot;
398
399 if (!fInvalid && wzPartBegin < wzEnd && *wzPartBegin == L'-')
400 {
401 wzPartBegin = wzPartEnd = wzPartBegin + 1;
402 fExpectedReleaseLabels = TRUE;
403 fLastPart = FALSE;
404 }
405
406 while (fExpectedReleaseLabels && wzPartBegin < wzEnd)
407 {
408 fTrailingDot = FALSE;
409
410 // Find end of part.
411 for (;;)
412 {
413 if (wzPartEnd >= wzEnd)
414 {
415 fLastPart = TRUE;
416 break;
417 }
418
419 if (*wzPartEnd >= L'0' && *wzPartEnd <= L'9' ||
420 *wzPartEnd >= L'A' && *wzPartEnd <= L'Z' ||
421 *wzPartEnd >= L'a' && *wzPartEnd <= L'z' ||
422 *wzPartEnd == L'-')
423 {
424 ++wzPartEnd;
425 continue;
426 }
427 else if (*wzPartEnd == L'+')
428 {
429 fLastPart = TRUE;
430 }
431 else if (*wzPartEnd == L'.')
432 {
433 fTrailingDot = TRUE;
434 }
435 else
436 {
437 fInvalid = TRUE;
438 }
439
440 break;
441 }
442
443 if (wzPartBegin == wzPartEnd)
444 {
445 fInvalid = TRUE;
446 }
447
448 if (fInvalid)
449 {
450 break;
451 }
452
453 int cchLabel = 0;
454 hr = ::PtrdiffTToInt32(wzPartEnd - wzPartBegin, &cchLabel);
455 if (FAILED(hr) || 0 > cchLabel)
456 {
457 fInvalid = TRUE;
458 break;
459 }
460
461 hr = MemReAllocArray(reinterpret_cast<LPVOID*>(&pVersion->rgReleaseLabels), pVersion->cReleaseLabels, sizeof(VERUTIL_VERSION_RELEASE_LABEL), GROW_RELEASE_LABELS - (pVersion->cReleaseLabels % GROW_RELEASE_LABELS));
462 VerExitOnFailure(hr, "Failed to allocate memory for Verutil version release labels '%ls'", wzVersion);
463
464 VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel = pVersion->rgReleaseLabels + pVersion->cReleaseLabels;
465 ++pVersion->cReleaseLabels;
466
467 // Try to parse as number.
468 UINT uLabel = 0;
469 hr = StrStringToUInt32(wzPartBegin, cchLabel, &uLabel);
470 if (SUCCEEDED(hr))
471 {
472 pReleaseLabel->fNumeric = TRUE;
473 pReleaseLabel->dwValue = uLabel;
474 }
475
476 pReleaseLabel->cchLabelOffset = wzPartBegin - pVersion->sczVersion;
477 pReleaseLabel->cchLabel = cchLabel;
478
479 if (fTrailingDot)
480 {
481 ++wzPartEnd;
482 }
483 wzPartBegin = wzPartEnd;
484
485 if (fLastPart)
486 {
487 break;
488 }
489 }
490
491 fInvalid |= fExpectedReleaseLabels && (!pVersion->cReleaseLabels || fTrailingDot);
492
493 if (!fInvalid && wzPartBegin < wzEnd)
494 {
495 if (*wzPartBegin == L'+')
496 {
497 wzPartBegin = wzPartEnd = wzPartBegin + 1;
498 }
499 else
500 {
501 fInvalid = TRUE;
502 }
503 }
504
505 if (fInvalid && fStrict)
506 {
507 ExitFunction1(hr = E_INVALIDARG);
508 }
509
510 pVersion->cchMetadataOffset = min(wzPartBegin, wzEnd) - pVersion->sczVersion;
511 pVersion->fInvalid = fInvalid;
512
513 *ppVersion = pVersion;
514 pVersion = NULL;
515 hr = S_OK;
516
517LExit:
518 ReleaseVerutilVersion(pVersion);
519
520 return hr;
521}
522
523DAPI_(HRESULT) VerVersionFromQword(
524 __in DWORD64 qwVersion,
525 __out VERUTIL_VERSION** ppVersion
526 )
527{
528 HRESULT hr = S_OK;
529 VERUTIL_VERSION* pVersion = NULL;
530
531 pVersion = reinterpret_cast<VERUTIL_VERSION*>(MemAlloc(sizeof(VERUTIL_VERSION), TRUE));
532 VerExitOnNull(pVersion, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version from QWORD.");
533
534 pVersion->dwMajor = (WORD)(qwVersion >> 48 & 0xffff);
535 pVersion->dwMinor = (WORD)(qwVersion >> 32 & 0xffff);
536 pVersion->dwPatch = (WORD)(qwVersion >> 16 & 0xffff);
537 pVersion->dwRevision = (WORD)(qwVersion & 0xffff);
538
539 hr = StrAllocFormatted(&pVersion->sczVersion, L"%lu.%lu.%lu.%lu", pVersion->dwMajor, pVersion->dwMinor, pVersion->dwPatch, pVersion->dwRevision);
540 ExitOnFailure(hr, "Failed to allocate and format the version string.");
541
542 pVersion->cchMetadataOffset = lstrlenW(pVersion->sczVersion);
543
544 *ppVersion = pVersion;
545 pVersion = NULL;
546
547LExit:
548 ReleaseVerutilVersion(pVersion);
549
550 return hr;
551}
552
553
554static int CompareDword(
555 __in const DWORD& dw1,
556 __in const DWORD& dw2
557 )
558{
559 int nResult = 0;
560
561 if (dw1 > dw2)
562 {
563 nResult = 1;
564 }
565 else if (dw1 < dw2)
566 {
567 nResult = -1;
568 }
569
570 return nResult;
571}
572
573static HRESULT CompareReleaseLabel(
574 __in const VERUTIL_VERSION_RELEASE_LABEL* p1,
575 __in LPCWSTR wzVersion1,
576 __in const VERUTIL_VERSION_RELEASE_LABEL* p2,
577 __in LPCWSTR wzVersion2,
578 __out int* pnResult
579 )
580{
581 HRESULT hr = S_OK;
582 int nResult = 0;
583
584 if (p1 == p2)
585 {
586 ExitFunction();
587 }
588 else if (p1 && !p2)
589 {
590 ExitFunction1(nResult = 1);
591 }
592 else if (!p1 && p2)
593 {
594 ExitFunction1(nResult = -1);
595 }
596
597 if (p1->fNumeric)
598 {
599 if (p2->fNumeric)
600 {
601 nResult = CompareDword(p1->dwValue, p2->dwValue);
602 }
603 else
604 {
605 nResult = -1;
606 }
607 }
608 else
609 {
610 if (p2->fNumeric)
611 {
612 nResult = 1;
613 }
614 else
615 {
616 hr = CompareVersionSubstring(wzVersion1 + p1->cchLabelOffset, p1->cchLabel, wzVersion2 + p2->cchLabelOffset, p2->cchLabel, &nResult);
617 }
618 }
619
620LExit:
621 *pnResult = nResult;
622
623 return hr;
624}
625
626static HRESULT CompareVersionSubstring(
627 __in LPCWSTR wzString1,
628 __in int cchCount1,
629 __in LPCWSTR wzString2,
630 __in int cchCount2,
631 __out int* pnResult
632 )
633{
634 HRESULT hr = S_OK;
635 int nResult = 0;
636
637 nResult = ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzString1, cchCount1, wzString2, cchCount2);
638 if (!nResult)
639 {
640 VerExitOnLastError(hr, "Failed to compare version substrings");
641 }
642
643LExit:
644 *pnResult = nResult - 2;
645
646 return hr;
647}