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