diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:21:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:21:29 +0000 |
commit | 29cd838eab01ed7110f3ccb2e8c6a35c8a31dbcc (patch) | |
tree | 63ef546b10a81d461e5cf5ed9e98a68cd7dee1aa /src/lib/version_compare.c | |
parent | Initial commit. (diff) | |
download | kbuild-29cd838eab01ed7110f3ccb2e8c6a35c8a31dbcc.tar.xz kbuild-29cd838eab01ed7110f3ccb2e8c6a35c8a31dbcc.zip |
Adding upstream version 1:0.1.9998svn3589+dfsg.upstream/1%0.1.9998svn3589+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib/version_compare.c')
-rw-r--r-- | src/lib/version_compare.c | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/src/lib/version_compare.c b/src/lib/version_compare.c new file mode 100644 index 0000000..ee265ff --- /dev/null +++ b/src/lib/version_compare.c @@ -0,0 +1,276 @@ +/* $Id: version_compare.c 3551 2022-01-29 02:57:33Z bird $ */ +/** @file + * version_compare - version compare. + */ + +/* + * Copyright (c) 2020 knut st. osmundsen <bird-kBuild-spamxx@anduin.net> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Alternatively, the content of this file may be used under the terms of the + * GPL version 2 or later, or LGPL version 2.1 or later. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "version_compare.h" +#include <string.h> +#include <stdlib.h> +#include <ctype.h> + + +/** + * Simple quantification of the pre-release designations (alpha2, beta1, rc1). + * + * @returns ~0U if not a pre-release designation, lesser values if it is. + * @note Case is ignored. + */ +static const char *check_release_type(char ch, const char *psz, unsigned *puValue) +{ + const char * const pszStart = psz; + *puValue = ~0U; + switch (ch) + { + default: + return psz; + + case 'r': + case 'R': + ch = *psz++; + if (ch != 'c' && ch != 'C') + return pszStart; + *puValue = ~0U/4 * 2; + break; + case 'b': + case 'B': + ch = *psz++; + if (ch != 'e' && ch != 'E') + return pszStart; + ch = *psz++; + if (ch != 't' && ch != 'T') + return pszStart; + ch = *psz++; + if (ch != 'a' && ch != 'A') + return pszStart; + *puValue = ~0U/4; + break; + case 'a': + case 'A': + ch = *psz++; + if (ch != 'l' && ch != 'L') + return pszStart; + ch = *psz++; + if (ch != 'p' && ch != 'P') + return pszStart; + ch = *psz++; + if (ch != 'h' && ch != 'H') + return pszStart; + ch = *psz++; + if (ch != 'a' && ch != 'A') + return pszStart; + *puValue = 0; + break; + } + + /* The next must be an non-alpha character, if a digit we add it to the value. */ + ch = *psz; + if (isdigit(ch)) + { + long int lSub = strtol(psz, (char **)&psz, 10); + if (lSub >= ~0U / 4) + lSub = ~0U / 4 - 1; + *puValue += (unsigned)lSub; + } + else if (isalpha(ch)) + return pszStart; + return psz; +} + + +/** + * Deals with returns, mainly from the string compare part. + */ +static int compare_failed(char ch1, char ch2) +{ + if (ch1 == '~') + return -1; + if (ch2 == '~') + return 1; + if (ch1 == '\0' || ch1 == '/') /* treat '/' similar to '\0' to deal with the v14.2/ vs v14.2.11.9/ case. */ + return -1; + if (ch2 == '\0' || ch2 == '/') + return 1; + if (isdigit(ch1)) + return -1; + if (isdigit(ch2)) + return 1; + if (isalpha(ch1)) + return isalpha(ch2) ? (int)ch1 - (int)ch2 : -1; + if (isalpha(ch2)) + return 1; + return (int)ch1 - (int)ch2; +} + + +int version_compare(const char *psz1, const char *psz2) +{ + for (;;) + { + int diff; + + /* Work non-digits: */ + char ch1 = *psz1++; + char ch2 = *psz2++; + for (;;) + { + if (ch1 == ch2) + { + if (ch1 != '\0') + { + if (isdigit(ch1)) + break; + ch1 = *psz1++; + ch2 = *psz2++; + } + else + return 0; + } + else if (isdigit(ch1) && isdigit(ch2)) + break; + else + return compare_failed(ch1, ch2); + } + + /* Skip leading zeros */ + while (ch1 == '0') + ch1 = *psz1++; + + while (ch2 == '0') + ch2 = *psz2++; + + /* Compare digits. */ + for (diff = 0;;) + { + if (isdigit(ch1)) + { + if (isdigit(ch2)) + { + if (diff == 0) + diff = (int)ch1 - (int)ch2; + ch1 = *psz1++; + ch2 = *psz2++; + } + else + return 1; /* The number in psz1 is longer and therefore larger. */ + } + else if (isdigit(ch2)) + return -1; /* The number in psz1 is shorter and therefore smaller. */ + else if (diff != 0) + return diff; + else + break; + } + + /* Neither ch1 nor ch2 is a digit at this point, but complete the + comparisons of the two before looping. We check for alpha, beta, rc + suffixes here (mainly to correctly order 1.2.3r4567890 after 1.2.3rc1) */ + { + unsigned uType1 = ~0; + unsigned uType2 = ~0; + psz1 = check_release_type(ch1, psz1, &uType1); + psz2 = check_release_type(ch2, psz2, &uType2); + if (uType1 != uType2) + return uType1 < uType2 ? -1 : 1; + if (ch1 != ch2 && uType1 == ~0U) + return compare_failed(ch1, ch2); + if (ch1 == '\0') + return 0; + } + } +} + + +#ifdef TEST +# include <stdio.h> + +int main() +{ + static const struct + { + int rcExpect; + const char *psz1, *psz2; + } s_aTests[] = + { + { 0, "", "" }, + { 0, "a", "a" }, + { 0, "ab", "ab" }, + { 0, "abc", "abc" }, + { 0, "001", "1" }, + { 0, "000", "0" }, + { -1, "0", "a" }, + { -1, "0", "1" }, + { -1, "0", "9" }, + { -1, "0", "99" }, + { -1, "9", "99" }, + { -1, "98", "99" }, + { 0, "asdfasdf", "asdfasdf" }, + { -1, "asdfasdf", "asdfasdfz" }, + { +1, "asdfasdfz", "asdfasdf" }, + { 0, "a1s2d3f4", "a1s2d3f4" }, + { 0, "a01s002d003f004", "a1s2d3f4" }, + { 0, "a1s2d3f4", "a01s002d003f004" }, + { 0, "kBuild-0.099.7", "kBuild-0.99.00007" }, + { +1, "kBuild-0.099.7", "kBuild-0.99.00007rc1" }, + { +1, "kBuild-0.099.7rc2", "kBuild-0.99.7beta3" }, + { -1, "kBuild-0.099.7alpha", "kBuild-0.99.7beta3" }, + { -1, "kBuild-0.099.7alpha", "kBuild-0.99.7beta3" }, + { -1, "kBuild-0.099.7alpha", "kBuild-0.99.7alpha1" }, + { 0, "kBuild-0.099.7ALPHA1", "kBuild-0.99.7alpha1" }, + { -1, "kBuild-0.099.7BETA1", "kBuild-0.99.7rC1" }, + { -1, "kBuild-0.099", "kBuild-0.99.0" }, + { +1, "kBuild-0.099", "kBuild-0.99~" }, + { +1, "1.2.3r4567890", "1.2.3rc1" }, + { +1, "1.2.3r4567890", "1.2.3RC1" }, + { -1, "/tools/win.amd64/vcc/v14.2/Tools", "/tools/win.amd64/vcc/v14.2.11.9/Tools" }, + { -1, "/tools/win.amd64/vcc/v14.2/Tools", "/tools/win.amd64/vcc/v14.211.9/Tools" }, + { -1, "/tools/win.amd64/vcc/v14.2/Tools", "/tools/win.amd64/vcc/v14.2r2/Tools" }, + { -1, "/tools/win.amd64/vcc/v14.2/Tools", "/tools/win.amd64/vcc/v14.2-r2/Tools" }, + }; + unsigned cErrors = 0; + unsigned i; + + for (i = 0; i < sizeof(s_aTests) / sizeof(s_aTests[0]); i++) + { + int rc = version_compare(s_aTests[i].psz1, s_aTests[i].psz2); + int rcExpect = s_aTests[i].rcExpect; + if (rc != (rcExpect < 0 ? -1 : rcExpect > 0 ? 1 : 0)) + { + fprintf(stderr, "error: Test #%u: %d, expected %d: '%s' vs '%s'\n", + i, rc, rcExpect, s_aTests[i].psz1, s_aTests[i].psz2); + cErrors++; + } + } + + return cErrors == 0 ? 0 : 1; +} +#endif + |