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 } 92