Download
jcs
/wallops
/strnatcmp.c
(View History)
jcs irc: Speed up nick sorting | Latest amendment: 91 on 2024-09-12 |
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 | } |