1 : /* -*- mode: c; c-file-style: "k&r" -*-
2 :
3 : Modified for PHP by Andrei Zmievski <andrei@ispi.net>
4 :
5 : strnatcmp.c -- Perform 'natural order' comparisons of strings in C.
6 : Copyright (C) 2000 by Martin Pool <mbp@humbug.org.au>
7 :
8 : This software is provided 'as-is', without any express or implied
9 : warranty. In no event will the authors be held liable for any damages
10 : arising from the use of this software.
11 :
12 : Permission is granted to anyone to use this software for any purpose,
13 : including commercial applications, and to alter it and redistribute it
14 : freely, subject to the following restrictions:
15 :
16 : 1. The origin of this software must not be misrepresented; you must not
17 : claim that you wrote the original software. If you use this software
18 : in a product, an acknowledgment in the product documentation would be
19 : appreciated but is not required.
20 : 2. Altered source versions must be plainly marked as such, and must not be
21 : misrepresented as being the original software.
22 : 3. This notice may not be removed or altered from any source distribution.
23 : */
24 :
25 : #include <ctype.h>
26 : #include <string.h>
27 : #include <assert.h>
28 : #include <stdio.h>
29 :
30 : #include "php.h"
31 : #include "php_string.h"
32 :
33 : #if defined(__GNUC__)
34 : # define UNUSED __attribute__((__unused__))
35 : #else
36 : # define UNUSED
37 : #endif
38 :
39 : #if 0
40 : static char const *version UNUSED =
41 : "$Id: strnatcmp.c 288896 2009-09-28 13:29:53Z rasmus $";
42 : #endif
43 : /* {{{ compare_right
44 : */
45 : static int
46 : compare_right(char const **a, char const *aend, char const **b, char const *bend)
47 148 : {
48 148 : int bias = 0;
49 :
50 : /* The longest run of digits wins. That aside, the greatest
51 : value wins, but we can't know that it will until we've scanned
52 : both numbers to know that they have the same magnitude, so we
53 : remember it in BIAS. */
54 268 : for(;; (*a)++, (*b)++) {
55 416 : if ((*a == aend || !isdigit((int)(unsigned char)**a)) &&
56 : (*b == bend || !isdigit((int)(unsigned char)**b)))
57 81 : return bias;
58 335 : else if (*a == aend || !isdigit((int)(unsigned char)**a))
59 28 : return -1;
60 307 : else if (*b == bend || !isdigit((int)(unsigned char)**b))
61 39 : return +1;
62 268 : else if (**a < **b) {
63 81 : if (!bias)
64 52 : bias = -1;
65 187 : } else if (**a > **b) {
66 94 : if (!bias)
67 66 : bias = +1;
68 : }
69 268 : }
70 :
71 : return 0;
72 : }
73 : /* }}} */
74 :
75 : /* {{{ compare_left
76 : */
77 : static int
78 : compare_left(char const **a, char const *aend, char const **b, char const *bend)
79 95 : {
80 : /* Compare two left-aligned numbers: the first to have a
81 : different value wins. */
82 27 : for(;; (*a)++, (*b)++) {
83 95 : if ((*a == aend || !isdigit((int)(unsigned char)**a)) &&
84 : (*b == bend || !isdigit((int)(unsigned char)**b)))
85 21 : return 0;
86 74 : else if (*a == aend || !isdigit((int)(unsigned char)**a))
87 0 : return -1;
88 74 : else if (*b == bend || !isdigit((int)(unsigned char)**b))
89 0 : return +1;
90 74 : else if (**a < **b)
91 28 : return -1;
92 46 : else if (**a > **b)
93 19 : return +1;
94 27 : }
95 :
96 : return 0;
97 : }
98 : /* }}} */
99 :
100 : /* {{{ strnatcmp_ex
101 : */
102 : PHPAPI int strnatcmp_ex(char const *a, size_t a_len, char const *b, size_t b_len, int fold_case)
103 499 : {
104 : unsigned char ca, cb;
105 : char const *ap, *bp;
106 499 : char const *aend = a + a_len,
107 499 : *bend = b + b_len;
108 : int fractional, result;
109 499 : short leading = 1;
110 :
111 499 : if (a_len == 0 || b_len == 0)
112 22 : return a_len - b_len;
113 :
114 477 : ap = a;
115 477 : bp = b;
116 : while (1) {
117 811 : ca = *ap; cb = *bp;
118 :
119 : /* skip over leading zeros */
120 1690 : while (leading && ca == '0' && (ap+1 < aend) && isdigit(*(ap+1))) {
121 68 : ca = *++ap;
122 : }
123 :
124 1667 : while (leading && cb == '0' && (bp+1 < bend) && isdigit(*(bp+1))) {
125 45 : cb = *++bp;
126 : }
127 :
128 811 : leading = 0;
129 :
130 : /* Skip consecutive whitespace */
131 1635 : while (isspace((int)(unsigned char)ca)) {
132 13 : ca = *++ap;
133 : }
134 :
135 1636 : while (isspace((int)(unsigned char)cb)) {
136 14 : cb = *++bp;
137 : }
138 :
139 : /* process run of digits */
140 811 : if (isdigit((int)(unsigned char)ca) && isdigit((int)(unsigned char)cb)) {
141 216 : fractional = (ca == '0' || cb == '0');
142 :
143 216 : if (fractional)
144 68 : result = compare_left(&ap, aend, &bp, bend);
145 : else
146 148 : result = compare_right(&ap, aend, &bp, bend);
147 :
148 216 : if (result != 0)
149 185 : return result;
150 31 : else if (ap == aend && bp == bend)
151 : /* End of the strings. Let caller sort them out. */
152 11 : return 0;
153 : else {
154 : /* Keep on comparing from the current point. */
155 20 : ca = *ap; cb = *bp;
156 : }
157 : }
158 :
159 615 : if (fold_case) {
160 493 : ca = toupper((int)(unsigned char)ca);
161 493 : cb = toupper((int)(unsigned char)cb);
162 : }
163 :
164 615 : if (ca < cb)
165 95 : return -1;
166 520 : else if (ca > cb)
167 156 : return +1;
168 :
169 364 : ++ap; ++bp;
170 364 : if (ap >= aend && bp >= bend)
171 : /* The strings compare the same. Perhaps the caller
172 : will want to call strcmp to break the tie. */
173 25 : return 0;
174 339 : else if (ap >= aend)
175 2 : return -1;
176 337 : else if (bp >= bend)
177 3 : return 1;
178 334 : }
179 : }
180 : /* }}} */
181 :
182 : /*
183 : * Local variables:
184 : * tab-width: 4
185 : * c-basic-offset: 4
186 : * End:
187 : * vim600: sw=4 ts=4 fdm=marker
188 : * vim<600: sw=4 ts=4
189 : */
|