| 1 |
/* -*- mode: c; c-file-style: "k&r" -*- |
| 2 |
|
| 3 |
strnatcmp.c -- Perform 'natural order' comparisons of strings in C. |
| 4 |
Copyright (C) 2000, 2004 by Martin Pool <mbp sourcefrog net> |
| 5 |
|
| 6 |
This software is provided 'as-is', without any express or implied |
| 7 |
warranty. In no event will the authors be held liable for any damages |
| 8 |
arising from the use of this software. |
| 9 |
|
| 10 |
Permission is granted to anyone to use this software for any purpose, |
| 11 |
including commercial applications, and to alter it and redistribute it |
| 12 |
freely, subject to the following restrictions: |
| 13 |
|
| 14 |
1. The origin of this software must not be misrepresented; you must not |
| 15 |
claim that you wrote the original software. If you use this software |
| 16 |
in a product, an acknowledgment in the product documentation would be |
| 17 |
appreciated but is not required. |
| 18 |
2. Altered source versions must be plainly marked as such, and must not be |
| 19 |
misrepresented as being the original software. |
| 20 |
3. This notice may not be removed or altered from any source distribution. |
| 21 |
*/ |
| 22 |
|
| 23 |
|
| 24 |
/* partial change history: |
| 25 |
* |
| 26 |
* 2004-10-10 mbp: Lift out character type dependencies into macros. |
| 27 |
* |
| 28 |
* Eric Sosman pointed out that ctype functions take a parameter whose |
| 29 |
* value must be that of an unsigned int, even on platforms that have |
| 30 |
* negative chars in their default char type. |
| 31 |
* |
| 32 |
* 2021-10-13 jcs: Modified to compile in THINK C 5 |
| 33 |
*/ |
| 34 |
|
| 35 |
#include <stddef.h> /* size_t */ |
| 36 |
#include <ctype.h> |
| 37 |
|
| 38 |
#include "strnatcmp.h" |
| 39 |
|
| 40 |
int compare_right(char const *a, char const *b); |
| 41 |
|
| 42 |
int |
| 43 |
compare_right(char const *a, char const *b) |
| 44 |
{ |
| 45 |
int bias = 0; |
| 46 |
|
| 47 |
/* The longest run of digits wins. That aside, the greatest |
| 48 |
value wins, but we can't know that it will until we've scanned |
| 49 |
both numbers to know that they have the same magnitude, so we |
| 50 |
remember it in BIAS. */ |
| 51 |
for (;; a++, b++) { |
| 52 |
if (!isdigit((unsigned char)*a) && !isdigit((unsigned char)*b)) |
| 53 |
return bias; |
| 54 |
if (!isdigit((unsigned char)*a)) |
| 55 |
return -1; |
| 56 |
if (!isdigit((unsigned char)*b)) |
| 57 |
return +1; |
| 58 |
if (*a < *b) { |
| 59 |
if (!bias) |
| 60 |
bias = -1; |
| 61 |
} else if (*a > *b) { |
| 62 |
if (!bias) |
| 63 |
bias = +1; |
| 64 |
} else if (!*a && !*b) |
| 65 |
return bias; |
| 66 |
} |
| 67 |
|
| 68 |
return 0; |
| 69 |
} |
| 70 |
|
| 71 |
|
| 72 |
static int |
| 73 |
compare_left(char const *a, char const *b) |
| 74 |
{ |
| 75 |
/* Compare two left-aligned numbers: the first to have a |
| 76 |
different value wins. */ |
| 77 |
for (;; a++, b++) { |
| 78 |
if (!isdigit((unsigned char)*a) && !isdigit((unsigned char)*b)) |
| 79 |
return 0; |
| 80 |
if (!isdigit((unsigned char)*a)) |
| 81 |
return -1; |
| 82 |
if (!isdigit((unsigned char)*b)) |
| 83 |
return +1; |
| 84 |
if (*a < *b) |
| 85 |
return -1; |
| 86 |
if (*a > *b) |
| 87 |
return +1; |
| 88 |
} |
| 89 |
|
| 90 |
return 0; |
| 91 |
} |
| 92 |
|
| 93 |
|
| 94 |
static int |
| 95 |
strnatcmp0(char const *a, char const *b, int fold_case) |
| 96 |
{ |
| 97 |
int ai, bi; |
| 98 |
char ca, cb; |
| 99 |
int fractional, result; |
| 100 |
|
| 101 |
ai = bi = 0; |
| 102 |
while (1) { |
| 103 |
ca = a[ai]; cb = b[bi]; |
| 104 |
|
| 105 |
/* skip over leading spaces or zeros */ |
| 106 |
while (isspace((unsigned char)ca)) |
| 107 |
ca = a[++ai]; |
| 108 |
|
| 109 |
while (isspace((unsigned char)cb)) |
| 110 |
cb = b[++bi]; |
| 111 |
|
| 112 |
/* process run of digits */ |
| 113 |
if (isdigit((unsigned char)ca) && isdigit((unsigned char)cb)) { |
| 114 |
fractional = (ca == '0' || cb == '0'); |
| 115 |
|
| 116 |
if (fractional) { |
| 117 |
if ((result = compare_left(a+ai, b+bi)) != 0) |
| 118 |
return result; |
| 119 |
} else { |
| 120 |
if ((result = compare_right(a+ai, b+bi)) != 0) |
| 121 |
return result; |
| 122 |
} |
| 123 |
} |
| 124 |
|
| 125 |
if (!ca && !cb) { |
| 126 |
/* The strings compare the same. Perhaps the caller |
| 127 |
will want to call strcmp to break the tie. */ |
| 128 |
return 0; |
| 129 |
} |
| 130 |
|
| 131 |
if (fold_case) { |
| 132 |
ca = toupper((unsigned char)ca); |
| 133 |
cb = toupper((unsigned char)cb); |
| 134 |
} |
| 135 |
|
| 136 |
if (ca < cb) |
| 137 |
return -1; |
| 138 |
|
| 139 |
if (ca > cb) |
| 140 |
return +1; |
| 141 |
|
| 142 |
++ai; ++bi; |
| 143 |
} |
| 144 |
} |
| 145 |
|
| 146 |
|
| 147 |
int |
| 148 |
strnatcmp(char const *a, char const *b) { |
| 149 |
return strnatcmp0(a, b, 0); |
| 150 |
} |
| 151 |
|
| 152 |
|
| 153 |
/* Compare, recognizing numeric string and ignoring case. */ |
| 154 |
int |
| 155 |
strnatcasecmp(char const *a, char const *b) { |
| 156 |
return strnatcmp0(a, b, 1); |
| 157 |
} |