VirtualBox

source: kBuild/trunk/src/lib/version_compare.c@ 3394

Last change on this file since 3394 was 3394, checked in by bird, 4 years ago

kmk: Added version sort function: versort, rversort, versortfiles, rversortfiles, qversortfiles, qrversortfiles. Try get the real host version on windows by using RtlGetVersion.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 8.1 KB
Line 
1/* $Id: version_compare.c 3394 2020-07-01 20:24:52Z bird $ */
2/** @file
3 * version_compare - version compare.
4 */
5
6/*
7 * Copyright (c) 2020 knut st. osmundsen <[email protected]>
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25 * IN THE SOFTWARE.
26 *
27 * Alternatively, the content of this file may be used under the terms of the
28 * GPL version 2 or later, or LGPL version 2.1 or later.
29 */
30
31
32/*********************************************************************************************************************************
33* Header Files *
34*********************************************************************************************************************************/
35#include "version_compare.h"
36#include <string.h>
37#include <stdlib.h>
38#include <ctype.h>
39
40
41/**
42 * Simple quantification of the pre-release designations (alpha2, beta1, rc1).
43 *
44 * @returns ~0U if not a pre-release designation, lesser values if it is.
45 * @note Case is ignored.
46 */
47static const char *check_release_type(char ch, const char *psz, unsigned *puValue)
48{
49 const char * const pszStart = psz;
50 *puValue = ~0U;
51 switch (ch)
52 {
53 default:
54 return psz;
55
56 case 'r':
57 case 'R':
58 ch = *psz++;
59 if (ch != 'c' && ch != 'C')
60 return pszStart;
61 *puValue = ~0U/4 * 2;
62 break;
63 case 'b':
64 case 'B':
65 ch = *psz++;
66 if (ch != 'e' && ch != 'E')
67 return pszStart;
68 ch = *psz++;
69 if (ch != 't' && ch != 'T')
70 return pszStart;
71 ch = *psz++;
72 if (ch != 'a' && ch != 'A')
73 return pszStart;
74 *puValue = ~0U/4;
75 break;
76 case 'a':
77 case 'A':
78 ch = *psz++;
79 if (ch != 'l' && ch != 'L')
80 return pszStart;
81 ch = *psz++;
82 if (ch != 'p' && ch != 'P')
83 return pszStart;
84 ch = *psz++;
85 if (ch != 'h' && ch != 'H')
86 return pszStart;
87 ch = *psz++;
88 if (ch != 'a' && ch != 'A')
89 return pszStart;
90 *puValue = 0;
91 break;
92 }
93
94 /* The next must be an non-alpha character, if a digit we add it to the value. */
95 ch = *psz;
96 if (isdigit(ch))
97 {
98 long int lSub = strtol(psz, (char **)&psz, 10);
99 if (lSub >= ~0U / 4)
100 lSub = ~0U / 4 - 1;
101 *puValue += (unsigned)lSub;
102 }
103 else if (isalpha(ch))
104 return pszStart;
105 return psz;
106}
107
108
109/**
110 * Deals with returns, mainly from the string compare part.
111 */
112static int compare_failed(char ch1, char ch2)
113{
114 if (ch1 == '~')
115 return -1;
116 if (ch2 == '~')
117 return 1;
118 if (ch1 == '\0')
119 return -1;
120 if (ch2 == '\0')
121 return 1;
122 if (isdigit(ch1))
123 return -1;
124 if (isdigit(ch2))
125 return 1;
126 if (isalpha(ch1))
127 return isalpha(ch2) ? (int)ch1 - (int)ch2 : -1;
128 if (isalpha(ch2))
129 return 1;
130 return (int)ch1 - (int)ch2;
131}
132
133
134int version_compare(const char *psz1, const char *psz2)
135{
136 for (;;)
137 {
138 int diff;
139
140 /* Work non-digits: */
141 char ch1 = *psz1++;
142 char ch2 = *psz2++;
143 for (;;)
144 {
145 if (ch1 == ch2)
146 {
147 if (ch1 != '\0')
148 {
149 if (isdigit(ch1))
150 break;
151 ch1 = *psz1++;
152 ch2 = *psz2++;
153 }
154 else
155 return 0;
156 }
157 else if (isdigit(ch1) && isdigit(ch2))
158 break;
159 else
160 return compare_failed(ch1, ch2);
161 }
162
163 /* Skip leading zeros */
164 while (ch1 == '0')
165 ch1 = *psz1++;
166
167 while (ch2 == '0')
168 ch2 = *psz2++;
169
170 /* Compare digits. */
171 for (diff = 0;;)
172 {
173 if (isdigit(ch1))
174 {
175 if (isdigit(ch2))
176 {
177 if (diff == 0)
178 diff = (int)ch1 - (int)ch2;
179 ch1 = *psz1++;
180 ch2 = *psz2++;
181 }
182 else
183 return 1; /* The number in psz1 is longer and therefore larger. */
184 }
185 else if (isdigit(ch2))
186 return -1; /* The number in psz1 is shorter and therefore smaller. */
187 else if (diff != 0)
188 return diff;
189 else
190 break;
191 }
192
193 /* Neither ch1 nor ch2 is a digit at this point, but complete the
194 comparisons of the two before looping. We check for alpha, beta, rc
195 suffixes here (mainly to correctly order 1.2.3r4567890 after 1.2.3rc1) */
196 {
197 unsigned uType1 = ~0;
198 unsigned uType2 = ~0;
199 psz1 = check_release_type(ch1, psz1, &uType1);
200 psz2 = check_release_type(ch2, psz2, &uType2);
201 if (uType1 != uType2)
202 return uType1 < uType2 ? -1 : 1;
203 if (ch1 != ch2 && uType1 == ~0U)
204 return compare_failed(ch1, ch2);
205 if (ch1 == '\0')
206 return 0;
207 }
208 }
209}
210
211
212#ifdef TEST
213# include <stdio.h>
214
215int main()
216{
217 static const struct
218 {
219 int rcExpect;
220 const char *psz1, *psz2;
221 } s_aTests[] =
222 {
223 { 0, "", "" },
224 { 0, "a", "a" },
225 { 0, "ab", "ab" },
226 { 0, "abc", "abc" },
227 { 0, "001", "1" },
228 { 0, "000", "0" },
229 { -1, "0", "a" },
230 { -1, "0", "1" },
231 { -1, "0", "9" },
232 { -1, "0", "99" },
233 { -1, "9", "99" },
234 { -1, "98", "99" },
235 { 0, "asdfasdf", "asdfasdf" },
236 { -1, "asdfasdf", "asdfasdfz" },
237 { +1, "asdfasdfz", "asdfasdf" },
238 { 0, "a1s2d3f4", "a1s2d3f4" },
239 { 0, "a01s002d003f004", "a1s2d3f4" },
240 { 0, "a1s2d3f4", "a01s002d003f004" },
241 { 0, "kBuild-0.099.7", "kBuild-0.99.00007" },
242 { +1, "kBuild-0.099.7", "kBuild-0.99.00007rc1" },
243 { +1, "kBuild-0.099.7rc2", "kBuild-0.99.7beta3" },
244 { -1, "kBuild-0.099.7alpha", "kBuild-0.99.7beta3" },
245 { -1, "kBuild-0.099.7alpha", "kBuild-0.99.7beta3" },
246 { -1, "kBuild-0.099.7alpha", "kBuild-0.99.7alpha1" },
247 { 0, "kBuild-0.099.7ALPHA1", "kBuild-0.99.7alpha1" },
248 { -1, "kBuild-0.099.7BETA1", "kBuild-0.99.7rC1" },
249 { -1, "kBuild-0.099", "kBuild-0.99.0" },
250 { +1, "kBuild-0.099", "kBuild-0.99~" },
251 { +1, "1.2.3r4567890", "1.2.3rc1" },
252 { +1, "1.2.3r4567890", "1.2.3RC1" },
253 };
254 unsigned cErrors = 0;
255 unsigned i;
256
257 for (i = 0; i < sizeof(s_aTests) / sizeof(s_aTests[0]); i++)
258 {
259 int rc = version_compare(s_aTests[i].psz1, s_aTests[i].psz2);
260 int rcExpect = s_aTests[i].rcExpect;
261 if (rc != (rcExpect < 0 ? -1 : rcExpect > 0 ? 1 : 0))
262 {
263 fprintf(stderr, "error: Test #%u: %d, expected %d: '%s' vs '%s'\n",
264 i, rc, rcExpect, s_aTests[i].psz1, s_aTests[i].psz2);
265 cErrors++;
266 }
267 }
268
269 return cErrors == 0 ? 0 : 1;
270}
271#endif
272
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette