1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
/*-
* SPDX-License-Identifier: BSD-2-Clause
* Copyright (c) 2022 Aymeric Wibo <obiwac@gmail.com>
*/
#include <ctype.h>
#include <stddef.h>
int
strverscmp(const char *s1, const char *s2)
{
size_t digit_count_1, digit_count_2;
size_t zeros_count_1, zeros_count_2;
const unsigned char *num_1, *num_2;
const unsigned char *u1 = __DECONST(const unsigned char *, s1);
const unsigned char *u2 = __DECONST(const unsigned char *, s2);
/*
* If pointers are the same, no need to go through to process of
* comparing them.
*/
if (s1 == s2)
return (0);
while (*u1 != '\0' && *u2 != '\0') {
/* If either character is not a digit, act like strcmp(3). */
if (!isdigit(*u1) || !isdigit(*u2)) {
if (*u1 != *u2)
return (*u1 - *u2);
u1++;
u2++;
continue;
}
if (*u1 == '0' || *u2 == '0') {
/*
* Treat leading zeros as if they were the fractional
* part of a number, i.e. as if they had a decimal point
* in front. First, count the leading zeros (more zeros
* == smaller number).
*/
zeros_count_1 = 0;
zeros_count_2 = 0;
for (; *u1 == '0'; u1++)
zeros_count_1++;
for (; *u2 == '0'; u2++)
zeros_count_2++;
if (zeros_count_1 != zeros_count_2)
return (zeros_count_2 - zeros_count_1);
/* Handle the case where 0 < 09. */
if (!isdigit(*u1) && isdigit(*u2))
return (1);
if (!isdigit(*u2) && isdigit(*u1))
return (-1);
} else {
/*
* No leading zeros; we're simply comparing two numbers.
* It is necessary to first count how many digits there
* are before going back to compare each digit, so that
* e.g. 7 is not considered larger than 60.
*/
num_1 = u1;
num_2 = u2;
/* Count digits (more digits == larger number). */
for (; isdigit(*u1); u1++)
;
for (; isdigit(*u2); u2++)
;
digit_count_1 = u1 - num_1;
digit_count_2 = u2 - num_2;
if (digit_count_1 != digit_count_2)
return (digit_count_1 - digit_count_2);
/*
* If there are the same number of digits, go back to
* the start of the number.
*/
u1 = num_1;
u2 = num_2;
}
/* Compare each digit until there are none left. */
for (; isdigit(*u1) && isdigit(*u2); u1++, u2++) {
if (*u1 != *u2)
return (*u1 - *u2);
}
}
return (*u1 - *u2);
}
|