1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2017 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Wez Furlong (wez@thebrainroom.com) |
16 +----------------------------------------------------------------------+
17
18 Based on code from ucdata-2.5, which has the following Copyright:
19
20 Copyright 2001 Computing Research Labs, New Mexico State University
21
22 Permission is hereby granted, free of charge, to any person obtaining a
23 copy of this software and associated documentation files (the "Software"),
24 to deal in the Software without restriction, including without limitation
25 the rights to use, copy, modify, merge, publish, distribute, sublicense,
26 and/or sell copies of the Software, and to permit persons to whom the
27 Software is furnished to do so, subject to the following conditions:
28
29 The above copyright notice and this permission notice shall be included in
30 all copies or substantial portions of the Software.
31 */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include "php.h"
38 #include "php_ini.h"
39
40 #if HAVE_MBSTRING
41
42 /* include case folding data generated from the official UnicodeData.txt file */
43 #include "mbstring.h"
44 #include "php_unicode.h"
45 #include "unicode_data.h"
46
47 ZEND_EXTERN_MODULE_GLOBALS(mbstring)
48
49 /*
50 * A simple array of 32-bit masks for lookup.
51 */
52 static unsigned long masks32[32] = {
53 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020,
54 0x00000040, 0x00000080, 0x00000100, 0x00000200, 0x00000400, 0x00000800,
55 0x00001000, 0x00002000, 0x00004000, 0x00008000, 0x00010000, 0x00020000,
56 0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000, 0x00800000,
57 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000,
58 0x40000000, 0x80000000
59 };
60
61
prop_lookup(unsigned long code,unsigned long n)62 static int prop_lookup(unsigned long code, unsigned long n)
63 {
64 long l, r, m;
65
66 /*
67 * There is an extra node on the end of the offsets to allow this routine
68 * to work right. If the index is 0xffff, then there are no nodes for the
69 * property.
70 */
71 if ((l = _ucprop_offsets[n]) == 0xffff)
72 return 0;
73
74 /*
75 * Locate the next offset that is not 0xffff. The sentinel at the end of
76 * the array is the max index value.
77 */
78 for (m = 1; n + m < _ucprop_size && _ucprop_offsets[n + m] == 0xffff; m++)
79 ;
80
81 r = _ucprop_offsets[n + m] - 1;
82
83 while (l <= r) {
84 /*
85 * Determine a "mid" point and adjust to make sure the mid point is at
86 * the beginning of a range pair.
87 */
88 m = (l + r) >> 1;
89 m -= (m & 1);
90 if (code > _ucprop_ranges[m + 1])
91 l = m + 2;
92 else if (code < _ucprop_ranges[m])
93 r = m - 2;
94 else if (code >= _ucprop_ranges[m] && code <= _ucprop_ranges[m + 1])
95 return 1;
96 }
97 return 0;
98
99 }
100
php_unicode_is_prop(unsigned long code,unsigned long mask1,unsigned long mask2)101 MBSTRING_API int php_unicode_is_prop(unsigned long code, unsigned long mask1,
102 unsigned long mask2)
103 {
104 unsigned long i;
105
106 if (mask1 == 0 && mask2 == 0)
107 return 0;
108
109 for (i = 0; mask1 && i < 32; i++) {
110 if ((mask1 & masks32[i]) && prop_lookup(code, i))
111 return 1;
112 }
113
114 for (i = 32; mask2 && i < _ucprop_size; i++) {
115 if ((mask2 & masks32[i & 31]) && prop_lookup(code, i))
116 return 1;
117 }
118
119 return 0;
120 }
121
case_lookup(unsigned long code,long l,long r,int field)122 static unsigned long case_lookup(unsigned long code, long l, long r, int field)
123 {
124 long m;
125
126 /*
127 * Do the binary search.
128 */
129 while (l <= r) {
130 /*
131 * Determine a "mid" point and adjust to make sure the mid point is at
132 * the beginning of a case mapping triple.
133 */
134 m = (l + r) >> 1;
135 m -= (m % 3);
136 if (code > _uccase_map[m])
137 l = m + 3;
138 else if (code < _uccase_map[m])
139 r = m - 3;
140 else if (code == _uccase_map[m])
141 return _uccase_map[m + field];
142 }
143
144 return code;
145 }
146
php_turkish_toupper(unsigned long code,long l,long r,int field)147 MBSTRING_API unsigned long php_turkish_toupper(unsigned long code, long l, long r, int field)
148 {
149 if (code == 0x0069L) {
150 return 0x0130L;
151 }
152 return case_lookup(code, l, r, field);
153 }
154
php_turkish_tolower(unsigned long code,long l,long r,int field)155 MBSTRING_API unsigned long php_turkish_tolower(unsigned long code, long l, long r, int field)
156 {
157 if (code == 0x0049L) {
158 return 0x0131L;
159 }
160 return case_lookup(code, l, r, field);
161 }
162
php_unicode_toupper(unsigned long code,enum mbfl_no_encoding enc)163 MBSTRING_API unsigned long php_unicode_toupper(unsigned long code, enum mbfl_no_encoding enc)
164 {
165 int field;
166 long l, r;
167
168 if (php_unicode_is_upper(code))
169 return code;
170
171 if (php_unicode_is_lower(code)) {
172 /*
173 * The character is lower case.
174 */
175 field = 2;
176 l = _uccase_len[0];
177 r = (l + _uccase_len[1]) - 3;
178
179 if (enc == mbfl_no_encoding_8859_9) {
180 return php_turkish_toupper(code, l, r, field);
181 }
182
183 } else {
184 /*
185 * The character is title case.
186 */
187 field = 1;
188 l = _uccase_len[0] + _uccase_len[1];
189 r = _uccase_size - 3;
190 }
191 return case_lookup(code, l, r, field);
192 }
193
php_unicode_tolower(unsigned long code,enum mbfl_no_encoding enc)194 MBSTRING_API unsigned long php_unicode_tolower(unsigned long code, enum mbfl_no_encoding enc)
195 {
196 int field;
197 long l, r;
198
199 if (php_unicode_is_lower(code))
200 return code;
201
202 if (php_unicode_is_upper(code)) {
203 /*
204 * The character is upper case.
205 */
206 field = 1;
207 l = 0;
208 r = _uccase_len[0] - 3;
209
210 if (enc == mbfl_no_encoding_8859_9) {
211 return php_turkish_tolower(code, l, r, field);
212 }
213
214 } else {
215 /*
216 * The character is title case.
217 */
218 field = 2;
219 l = _uccase_len[0] + _uccase_len[1];
220 r = _uccase_size - 3;
221 }
222 return case_lookup(code, l, r, field);
223 }
224
php_unicode_totitle(unsigned long code,enum mbfl_no_encoding enc)225 MBSTRING_API unsigned long php_unicode_totitle(unsigned long code, enum mbfl_no_encoding enc)
226 {
227 int field;
228 long l, r;
229
230 if (php_unicode_is_title(code))
231 return code;
232
233 /*
234 * The offset will always be the same for converting to title case.
235 */
236 field = 2;
237
238 if (php_unicode_is_upper(code)) {
239 /*
240 * The character is upper case.
241 */
242 l = 0;
243 r = _uccase_len[0] - 3;
244 } else {
245 /*
246 * The character is lower case.
247 */
248 l = _uccase_len[0];
249 r = (l + _uccase_len[1]) - 3;
250 }
251 return case_lookup(code, l, r, field);
252
253 }
254
255
256 #define BE_ARY_TO_UINT32(ptr) (\
257 ((unsigned char*)(ptr))[0]<<24 |\
258 ((unsigned char*)(ptr))[1]<<16 |\
259 ((unsigned char*)(ptr))[2]<< 8 |\
260 ((unsigned char*)(ptr))[3] )
261
262 #define UINT32_TO_BE_ARY(ptr,val) { \
263 unsigned int v = val; \
264 ((unsigned char*)(ptr))[0] = (v>>24) & 0xff,\
265 ((unsigned char*)(ptr))[1] = (v>>16) & 0xff,\
266 ((unsigned char*)(ptr))[2] = (v>> 8) & 0xff,\
267 ((unsigned char*)(ptr))[3] = (v ) & 0xff;\
268 }
269
php_unicode_convert_case(int case_mode,const char * srcstr,size_t srclen,size_t * ret_len,const char * src_encoding)270 MBSTRING_API char *php_unicode_convert_case(int case_mode, const char *srcstr, size_t srclen, size_t *ret_len,
271 const char *src_encoding)
272 {
273 char *unicode, *newstr;
274 size_t unicode_len;
275 unsigned char *unicode_ptr;
276 size_t i;
277 enum mbfl_no_encoding _src_encoding = mbfl_name2no_encoding(src_encoding);
278
279 if (_src_encoding == mbfl_no_encoding_invalid) {
280 php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", src_encoding);
281 return NULL;
282 }
283
284 unicode = php_mb_convert_encoding(srcstr, srclen, "UCS-4BE", src_encoding, &unicode_len);
285 if (unicode == NULL)
286 return NULL;
287
288 unicode_ptr = (unsigned char *)unicode;
289
290 switch(case_mode) {
291 case PHP_UNICODE_CASE_UPPER:
292 for (i = 0; i < unicode_len; i+=4) {
293 UINT32_TO_BE_ARY(&unicode_ptr[i],
294 php_unicode_toupper(BE_ARY_TO_UINT32(&unicode_ptr[i]), _src_encoding));
295 }
296 break;
297
298 case PHP_UNICODE_CASE_LOWER:
299 for (i = 0; i < unicode_len; i+=4) {
300 UINT32_TO_BE_ARY(&unicode_ptr[i],
301 php_unicode_tolower(BE_ARY_TO_UINT32(&unicode_ptr[i]), _src_encoding));
302 }
303 break;
304
305 case PHP_UNICODE_CASE_TITLE: {
306 int mode = 0;
307
308 for (i = 0; i < unicode_len; i+=4) {
309 int res = php_unicode_is_prop(
310 BE_ARY_TO_UINT32(&unicode_ptr[i]),
311 UC_MN|UC_ME|UC_CF|UC_LM|UC_SK|UC_LU|UC_LL|UC_LT|UC_PO|UC_OS, 0);
312 if (mode) {
313 if (res) {
314 UINT32_TO_BE_ARY(&unicode_ptr[i],
315 php_unicode_tolower(BE_ARY_TO_UINT32(&unicode_ptr[i]), _src_encoding));
316 } else {
317 mode = 0;
318 }
319 } else {
320 if (res) {
321 mode = 1;
322 UINT32_TO_BE_ARY(&unicode_ptr[i],
323 php_unicode_totitle(BE_ARY_TO_UINT32(&unicode_ptr[i]), _src_encoding));
324 }
325 }
326 }
327 } break;
328
329 }
330
331 newstr = php_mb_convert_encoding(unicode, unicode_len, src_encoding, "UCS-4BE", ret_len);
332 efree(unicode);
333
334 return newstr;
335 }
336
337
338 #endif /* HAVE_MBSTRING */
339
340 /*
341 * Local variables:
342 * tab-width: 4
343 * c-basic-offset: 4
344 * End:
345 * vim600: sw=4 ts=4 fdm=marker
346 * vim<600: sw=4 ts=4
347 */
348