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