105c9a015SAymeric Wibo /*-
205c9a015SAymeric Wibo * SPDX-License-Identifier: BSD-2-Clause
305c9a015SAymeric Wibo * Copyright (c) 2022 Aymeric Wibo <obiwac@gmail.com>
405c9a015SAymeric Wibo */
505c9a015SAymeric Wibo
605c9a015SAymeric Wibo #include <ctype.h>
705c9a015SAymeric Wibo #include <stddef.h>
805c9a015SAymeric Wibo
905c9a015SAymeric Wibo int
strverscmp(const char * s1,const char * s2)1005c9a015SAymeric Wibo strverscmp(const char *s1, const char *s2)
1105c9a015SAymeric Wibo {
1205c9a015SAymeric Wibo size_t digit_count_1, digit_count_2;
1305c9a015SAymeric Wibo size_t zeros_count_1, zeros_count_2;
1405c9a015SAymeric Wibo const unsigned char *num_1, *num_2;
1505c9a015SAymeric Wibo const unsigned char *u1 = __DECONST(const unsigned char *, s1);
1605c9a015SAymeric Wibo const unsigned char *u2 = __DECONST(const unsigned char *, s2);
1705c9a015SAymeric Wibo
1805c9a015SAymeric Wibo /*
1905c9a015SAymeric Wibo * If pointers are the same, no need to go through to process of
2005c9a015SAymeric Wibo * comparing them.
2105c9a015SAymeric Wibo */
2205c9a015SAymeric Wibo if (s1 == s2)
2305c9a015SAymeric Wibo return (0);
2405c9a015SAymeric Wibo
2505c9a015SAymeric Wibo while (*u1 != '\0' && *u2 != '\0') {
2605c9a015SAymeric Wibo /* If either character is not a digit, act like strcmp(3). */
2705c9a015SAymeric Wibo
2805c9a015SAymeric Wibo if (!isdigit(*u1) || !isdigit(*u2)) {
2905c9a015SAymeric Wibo if (*u1 != *u2)
3005c9a015SAymeric Wibo return (*u1 - *u2);
3105c9a015SAymeric Wibo u1++;
3205c9a015SAymeric Wibo u2++;
3305c9a015SAymeric Wibo continue;
3405c9a015SAymeric Wibo }
3505c9a015SAymeric Wibo if (*u1 == '0' || *u2 == '0') {
3605c9a015SAymeric Wibo /*
3705c9a015SAymeric Wibo * Treat leading zeros as if they were the fractional
3805c9a015SAymeric Wibo * part of a number, i.e. as if they had a decimal point
3905c9a015SAymeric Wibo * in front. First, count the leading zeros (more zeros
4005c9a015SAymeric Wibo * == smaller number).
4105c9a015SAymeric Wibo */
4205c9a015SAymeric Wibo zeros_count_1 = 0;
4305c9a015SAymeric Wibo zeros_count_2 = 0;
4405c9a015SAymeric Wibo for (; *u1 == '0'; u1++)
4505c9a015SAymeric Wibo zeros_count_1++;
4605c9a015SAymeric Wibo for (; *u2 == '0'; u2++)
4705c9a015SAymeric Wibo zeros_count_2++;
4805c9a015SAymeric Wibo if (zeros_count_1 != zeros_count_2)
4905c9a015SAymeric Wibo return (zeros_count_2 - zeros_count_1);
5005c9a015SAymeric Wibo
5105c9a015SAymeric Wibo /* Handle the case where 0 < 09. */
5205c9a015SAymeric Wibo if (!isdigit(*u1) && isdigit(*u2))
5305c9a015SAymeric Wibo return (1);
5405c9a015SAymeric Wibo if (!isdigit(*u2) && isdigit(*u1))
5505c9a015SAymeric Wibo return (-1);
5605c9a015SAymeric Wibo } else {
5705c9a015SAymeric Wibo /*
5805c9a015SAymeric Wibo * No leading zeros; we're simply comparing two numbers.
5905c9a015SAymeric Wibo * It is necessary to first count how many digits there
6005c9a015SAymeric Wibo * are before going back to compare each digit, so that
6105c9a015SAymeric Wibo * e.g. 7 is not considered larger than 60.
6205c9a015SAymeric Wibo */
6305c9a015SAymeric Wibo num_1 = u1;
6405c9a015SAymeric Wibo num_2 = u2;
6505c9a015SAymeric Wibo
6605c9a015SAymeric Wibo /* Count digits (more digits == larger number). */
6705c9a015SAymeric Wibo for (; isdigit(*u1); u1++)
6805c9a015SAymeric Wibo ;
6905c9a015SAymeric Wibo for (; isdigit(*u2); u2++)
7005c9a015SAymeric Wibo ;
7105c9a015SAymeric Wibo digit_count_1 = u1 - num_1;
7205c9a015SAymeric Wibo digit_count_2 = u2 - num_2;
7305c9a015SAymeric Wibo if (digit_count_1 != digit_count_2)
7405c9a015SAymeric Wibo return (digit_count_1 - digit_count_2);
7505c9a015SAymeric Wibo
7605c9a015SAymeric Wibo /*
7705c9a015SAymeric Wibo * If there are the same number of digits, go back to
7805c9a015SAymeric Wibo * the start of the number.
7905c9a015SAymeric Wibo */
8005c9a015SAymeric Wibo u1 = num_1;
8105c9a015SAymeric Wibo u2 = num_2;
8205c9a015SAymeric Wibo }
8305c9a015SAymeric Wibo
8405c9a015SAymeric Wibo /* Compare each digit until there are none left. */
8505c9a015SAymeric Wibo for (; isdigit(*u1) && isdigit(*u2); u1++, u2++) {
8605c9a015SAymeric Wibo if (*u1 != *u2)
8705c9a015SAymeric Wibo return (*u1 - *u2);
8805c9a015SAymeric Wibo }
8905c9a015SAymeric Wibo }
9005c9a015SAymeric Wibo return (*u1 - *u2);
9105c9a015SAymeric Wibo }
92