/src/php-src/ext/standard/strnatcmp.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
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 <stdio.h> |
28 | | |
29 | | #include "php.h" |
30 | | #include "php_string.h" |
31 | | |
32 | | /* {{{ compare_right */ |
33 | | static int |
34 | | compare_right(char const **a, char const *aend, char const **b, char const *bend) |
35 | 6.09k | { |
36 | 6.09k | int bias = 0; |
37 | | |
38 | | /* The longest run of digits wins. That aside, the greatest |
39 | | value wins, but we can't know that it will until we've scanned |
40 | | both numbers to know that they have the same magnitude, so we |
41 | | remember it in BIAS. */ |
42 | 57.4k | for(;; (*a)++, (*b)++) { |
43 | 57.4k | if ((*a == aend || !isdigit((int)(unsigned char)**a)) && |
44 | 57.4k | (*b == bend || !isdigit((int)(unsigned char)**b))) |
45 | 5.05k | return bias; |
46 | 52.4k | else if (*a == aend || !isdigit((int)(unsigned char)**a)) |
47 | 387 | return -1; |
48 | 52.0k | else if (*b == bend || !isdigit((int)(unsigned char)**b)) |
49 | 650 | return +1; |
50 | 51.3k | else if (**a < **b) { |
51 | 3.11k | if (!bias) |
52 | 615 | bias = -1; |
53 | 48.2k | } else if (**a > **b) { |
54 | 3.22k | if (!bias) |
55 | 936 | bias = +1; |
56 | 3.22k | } |
57 | 57.4k | } |
58 | | |
59 | 0 | return 0; |
60 | 6.09k | } |
61 | | /* }}} */ |
62 | | |
63 | | /* {{{ compare_left */ |
64 | | static int |
65 | | compare_left(char const **a, char const *aend, char const **b, char const *bend) |
66 | 2.95k | { |
67 | | /* Compare two left-aligned numbers: the first to have a |
68 | | different value wins. */ |
69 | 17.7k | for(;; (*a)++, (*b)++) { |
70 | 17.7k | if ((*a == aend || !isdigit((int)(unsigned char)**a)) && |
71 | 17.7k | (*b == bend || !isdigit((int)(unsigned char)**b))) |
72 | 1.44k | return 0; |
73 | 16.3k | else if (*a == aend || !isdigit((int)(unsigned char)**a)) |
74 | 62 | return -1; |
75 | 16.2k | else if (*b == bend || !isdigit((int)(unsigned char)**b)) |
76 | 105 | return +1; |
77 | 16.1k | else if (**a < **b) |
78 | 452 | return -1; |
79 | 15.7k | else if (**a > **b) |
80 | 884 | return +1; |
81 | 17.7k | } |
82 | | |
83 | 0 | return 0; |
84 | 2.95k | } |
85 | | /* }}} */ |
86 | | |
87 | | /* {{{ strnatcmp_ex */ |
88 | | PHPAPI int strnatcmp_ex(char const *a, size_t a_len, char const *b, size_t b_len, bool is_case_insensitive) |
89 | 47.8k | { |
90 | 47.8k | unsigned char ca, cb; |
91 | 47.8k | char const *ap, *bp; |
92 | 47.8k | char const *aend = a + a_len, |
93 | 47.8k | *bend = b + b_len; |
94 | 47.8k | int fractional, result; |
95 | | |
96 | 47.8k | if (a_len == 0 || b_len == 0) { |
97 | 1.45k | return (a_len == b_len ? 0 : (a_len > b_len ? 1 : -1)); |
98 | 1.45k | } |
99 | | |
100 | 46.4k | ap = a; |
101 | 46.4k | bp = b; |
102 | | |
103 | 46.4k | ca = *ap; cb = *bp; |
104 | | |
105 | | /* skip over leading zeros */ |
106 | 46.7k | while (ca == '0' && (ap+1 < aend) && isdigit((int)(unsigned char)*(ap+1))) { |
107 | 280 | ca = *++ap; |
108 | 280 | } |
109 | | |
110 | 46.5k | while (cb == '0' && (bp+1 < bend) && isdigit((int)(unsigned char)*(bp+1))) { |
111 | 145 | cb = *++bp; |
112 | 145 | } |
113 | | |
114 | 535k | while (1) { |
115 | | |
116 | | /* Skip consecutive whitespace */ |
117 | 535k | while (isspace((int)(unsigned char)ca)) { |
118 | 60.9k | ca = *++ap; |
119 | 60.9k | } |
120 | | |
121 | 535k | while (isspace((int)(unsigned char)cb)) { |
122 | 58.0k | cb = *++bp; |
123 | 58.0k | } |
124 | | |
125 | | /* process run of digits */ |
126 | 535k | if (isdigit((int)(unsigned char)ca) && isdigit((int)(unsigned char)cb)) { |
127 | 9.04k | fractional = (ca == '0' || cb == '0'); |
128 | | |
129 | 9.04k | if (fractional) |
130 | 2.95k | result = compare_left(&ap, aend, &bp, bend); |
131 | 6.09k | else |
132 | 6.09k | result = compare_right(&ap, aend, &bp, bend); |
133 | | |
134 | 9.04k | if (result != 0) |
135 | 3.53k | return result; |
136 | 5.51k | else if (ap == aend && bp == bend) |
137 | | /* End of the strings. Let caller sort them out. */ |
138 | 91 | return 0; |
139 | 5.42k | else if (ap == aend) |
140 | 3 | return -1; |
141 | 5.41k | else if (bp == bend) |
142 | 6 | return 1; |
143 | 5.41k | else { |
144 | | /* Keep on comparing from the current point. */ |
145 | 5.41k | ca = *ap; cb = *bp; |
146 | 5.41k | } |
147 | 9.04k | } |
148 | | |
149 | 531k | if (is_case_insensitive) { |
150 | 0 | ca = toupper((int)(unsigned char)ca); |
151 | 0 | cb = toupper((int)(unsigned char)cb); |
152 | 0 | } |
153 | | |
154 | 531k | if (ca < cb) |
155 | 9.96k | return -1; |
156 | 521k | else if (ca > cb) |
157 | 22.0k | return +1; |
158 | | |
159 | 499k | ++ap; ++bp; |
160 | 499k | if (ap >= aend && bp >= bend) |
161 | | /* The strings compare the same. Perhaps the caller |
162 | | will want to call strcmp to break the tie. */ |
163 | 9.00k | return 0; |
164 | 490k | else if (ap >= aend) |
165 | 501 | return -1; |
166 | 490k | else if (bp >= bend) |
167 | 1.25k | return 1; |
168 | | |
169 | 488k | ca = *ap; cb = *bp; |
170 | 488k | } |
171 | 46.4k | } |
172 | | /* }}} */ |