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.haxx.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 /* --- public functions --- */
255 
curl_strequal(const char * first,const char * second)256 int curl_strequal(const char *first, const char *second)
257 {
258   return Curl_strcasecompare(first, second);
259 }
curl_strnequal(const char * first,const char * second,size_t max)260 int curl_strnequal(const char *first, const char *second, size_t max)
261 {
262   return Curl_strncasecompare(first, second, max);
263 }
264