1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ***************************************************************************/
22
23 #include "curl_setup.h"
24
25 #include <curl/curl.h>
26
27 #include "strcase.h"
28
29 static char raw_tolower(char in);
30
31 /* Portable, consistent toupper (remember EBCDIC). Do not use toupper() because
32 its behavior is altered by the current locale. */
Curl_raw_toupper(char in)33 char Curl_raw_toupper(char in)
34 {
35 #if !defined(CURL_DOES_CONVERSIONS)
36 if(in >= 'a' && in <= 'z')
37 return (char)('A' + in - 'a');
38 #else
39 switch(in) {
40 case 'a':
41 return 'A';
42 case 'b':
43 return 'B';
44 case 'c':
45 return 'C';
46 case 'd':
47 return 'D';
48 case 'e':
49 return 'E';
50 case 'f':
51 return 'F';
52 case 'g':
53 return 'G';
54 case 'h':
55 return 'H';
56 case 'i':
57 return 'I';
58 case 'j':
59 return 'J';
60 case 'k':
61 return 'K';
62 case 'l':
63 return 'L';
64 case 'm':
65 return 'M';
66 case 'n':
67 return 'N';
68 case 'o':
69 return 'O';
70 case 'p':
71 return 'P';
72 case 'q':
73 return 'Q';
74 case 'r':
75 return 'R';
76 case 's':
77 return 'S';
78 case 't':
79 return 'T';
80 case 'u':
81 return 'U';
82 case 'v':
83 return 'V';
84 case 'w':
85 return 'W';
86 case 'x':
87 return 'X';
88 case 'y':
89 return 'Y';
90 case 'z':
91 return 'Z';
92 }
93 #endif
94
95 return in;
96 }
97
98
99 /* Portable, consistent tolower (remember EBCDIC). Do not use tolower() because
100 its behavior is altered by the current locale. */
raw_tolower(char in)101 static char raw_tolower(char in)
102 {
103 #if !defined(CURL_DOES_CONVERSIONS)
104 if(in >= 'A' && in <= 'Z')
105 return (char)('a' + in - 'A');
106 #else
107 switch(in) {
108 case 'A':
109 return 'a';
110 case 'B':
111 return 'b';
112 case 'C':
113 return 'c';
114 case 'D':
115 return 'd';
116 case 'E':
117 return 'e';
118 case 'F':
119 return 'f';
120 case 'G':
121 return 'g';
122 case 'H':
123 return 'h';
124 case 'I':
125 return 'i';
126 case 'J':
127 return 'j';
128 case 'K':
129 return 'k';
130 case 'L':
131 return 'l';
132 case 'M':
133 return 'm';
134 case 'N':
135 return 'n';
136 case 'O':
137 return 'o';
138 case 'P':
139 return 'p';
140 case 'Q':
141 return 'q';
142 case 'R':
143 return 'r';
144 case 'S':
145 return 's';
146 case 'T':
147 return 't';
148 case 'U':
149 return 'u';
150 case 'V':
151 return 'v';
152 case 'W':
153 return 'w';
154 case 'X':
155 return 'x';
156 case 'Y':
157 return 'y';
158 case 'Z':
159 return 'z';
160 }
161 #endif
162
163 return in;
164 }
165
166
167 /*
168 * Curl_strcasecompare() is for doing "raw" case insensitive strings. This is
169 * meant to be locale independent and only compare strings we know are safe
170 * for this. See
171 * https://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for some
172 * further explanation to why this function is necessary.
173 *
174 * The function is capable of comparing a-z case insensitively even for
175 * non-ascii.
176 *
177 * @unittest: 1301
178 */
179
Curl_strcasecompare(const char * first,const char * second)180 int Curl_strcasecompare(const char *first, const char *second)
181 {
182 while(*first && *second) {
183 if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second))
184 /* get out of the loop as soon as they don't match */
185 break;
186 first++;
187 second++;
188 }
189 /* we do the comparison here (possibly again), just to make sure that if the
190 loop above is skipped because one of the strings reached zero, we must not
191 return this as a successful match */
192 return (Curl_raw_toupper(*first) == Curl_raw_toupper(*second));
193 }
194
Curl_safe_strcasecompare(const char * first,const char * second)195 int Curl_safe_strcasecompare(const char *first, const char *second)
196 {
197 if(first && second)
198 /* both pointers point to something then compare them */
199 return Curl_strcasecompare(first, second);
200
201 /* if both pointers are NULL then treat them as equal */
202 return (NULL == first && NULL == second);
203 }
204
205 /*
206 * @unittest: 1301
207 */
Curl_strncasecompare(const char * first,const char * second,size_t max)208 int Curl_strncasecompare(const char *first, const char *second, size_t max)
209 {
210 while(*first && *second && max) {
211 if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second)) {
212 break;
213 }
214 max--;
215 first++;
216 second++;
217 }
218 if(0 == max)
219 return 1; /* they are equal this far */
220
221 return Curl_raw_toupper(*first) == Curl_raw_toupper(*second);
222 }
223
224 /* Copy an upper case version of the string from src to dest. The
225 * strings may overlap. No more than n characters of the string are copied
226 * (including any NUL) and the destination string will NOT be
227 * NUL-terminated if that limit is reached.
228 */
Curl_strntoupper(char * dest,const char * src,size_t n)229 void Curl_strntoupper(char *dest, const char *src, size_t n)
230 {
231 if(n < 1)
232 return;
233
234 do {
235 *dest++ = Curl_raw_toupper(*src);
236 } while(*src++ && --n);
237 }
238
239 /* Copy a lower case version of the string from src to dest. The
240 * strings may overlap. No more than n characters of the string are copied
241 * (including any NUL) and the destination string will NOT be
242 * NUL-terminated if that limit is reached.
243 */
Curl_strntolower(char * dest,const char * src,size_t n)244 void Curl_strntolower(char *dest, const char *src, size_t n)
245 {
246 if(n < 1)
247 return;
248
249 do {
250 *dest++ = raw_tolower(*src);
251 } while(*src++ && --n);
252 }
253
254 /* Compare case-sensitive NUL-terminated strings, taking care of possible
255 * null pointers. Return true if arguments match.
256 */
Curl_safecmp(char * a,char * b)257 bool Curl_safecmp(char *a, char *b)
258 {
259 if(a && b)
260 return !strcmp(a, b);
261 return !a && !b;
262 }
263
264 /*
265 * Curl_timestrcmp() returns 0 if the two strings are identical. The time this
266 * function spends is a function of the shortest string, not of the contents.
267 */
Curl_timestrcmp(const char * a,const char * b)268 int Curl_timestrcmp(const char *a, const char *b)
269 {
270 int match = 0;
271 int i = 0;
272
273 if(a && b) {
274 while(1) {
275 match |= a[i]^b[i];
276 if(!a[i] || !b[i])
277 break;
278 i++;
279 }
280 }
281 else
282 return a || b;
283 return match;
284 }
285
286 /* --- public functions --- */
287
curl_strequal(const char * first,const char * second)288 int curl_strequal(const char *first, const char *second)
289 {
290 return Curl_strcasecompare(first, second);
291 }
curl_strnequal(const char * first,const char * second,size_t max)292 int curl_strnequal(const char *first, const char *second, size_t max)
293 {
294 return Curl_strncasecompare(first, second, max);
295 }
296