1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 5 |
4 : +----------------------------------------------------------------------+
5 : | This source file is subject to version 3.01 of the PHP license, |
6 : | that is bundled with this package in the file LICENSE, and is |
7 : | available through the world-wide-web at the following url: |
8 : | http://www.php.net/license/3_01.txt |
9 : | If you did not receive a copy of the PHP license and are unable to |
10 : | obtain it through the world-wide-web, please send a note to |
11 : | license@php.net so we can mail you a copy immediately. |
12 : +----------------------------------------------------------------------+
13 : | Author: Ed Batutis <ed@batutis.com> |
14 : +----------------------------------------------------------------------+
15 : */
16 :
17 : /* {{{ includes */
18 : #ifdef HAVE_CONFIG_H
19 : #include "config.h"
20 : #endif
21 :
22 : #include <php.h>
23 : #include "grapheme.h"
24 : #include "grapheme_util.h"
25 : #include "intl_common.h"
26 :
27 : #include <unicode/utypes.h>
28 : #include <unicode/ucol.h>
29 : #include <unicode/ustring.h>
30 : #include <unicode/ubrk.h>
31 :
32 : #include "ext/standard/php_string.h"
33 :
34 : ZEND_EXTERN_MODULE_GLOBALS( intl )
35 :
36 : /* }}} */
37 :
38 : /* {{{ grapheme_close_global_iterator - clean up */
39 : void
40 : grapheme_close_global_iterator( TSRMLS_D )
41 1 : {
42 1 : UBreakIterator *global_break_iterator = INTL_G( grapheme_iterator );
43 :
44 1 : if ( NULL != global_break_iterator ) {
45 1 : ubrk_close(global_break_iterator);
46 : }
47 1 : }
48 : /* }}} */
49 :
50 : /* {{{ grapheme_intl_case_fold: convert string to lowercase */
51 : void
52 : grapheme_intl_case_fold(UChar** ptr_to_free, UChar **str, int32_t *str_len, UErrorCode *pstatus )
53 146 : {
54 : UChar *dest;
55 : int32_t dest_len, size_required;
56 :
57 : /* allocate a destination string that is a bit larger than the src, hoping that is enough */
58 146 : dest_len = (*str_len) + ( *str_len / 10 );
59 146 : dest = (UChar*) eumalloc(dest_len);
60 :
61 146 : *pstatus = U_ZERO_ERROR;
62 146 : size_required = u_strFoldCase(dest, dest_len, *str, *str_len, U_FOLD_CASE_DEFAULT, pstatus);
63 :
64 146 : dest_len = size_required;
65 :
66 146 : if ( U_BUFFER_OVERFLOW_ERROR == *pstatus ) {
67 :
68 0 : dest = (UChar*) eurealloc(dest, dest_len);
69 :
70 0 : *pstatus = U_ZERO_ERROR;
71 0 : size_required = u_strFoldCase(dest, dest_len, *str, *str_len, U_FOLD_CASE_DEFAULT, pstatus);
72 : }
73 :
74 146 : if ( U_FAILURE(*pstatus) ) {
75 0 : return;
76 : }
77 :
78 146 : if ( NULL != ptr_to_free) {
79 146 : efree(*ptr_to_free);
80 146 : *ptr_to_free = dest;
81 : }
82 :
83 146 : *str = dest;
84 146 : *str_len = dest_len;
85 :
86 146 : return;
87 : }
88 : /* }}} */
89 :
90 : /* {{{ grapheme_substr_ascii f='from' - starting point, l='length' */
91 : void
92 : grapheme_substr_ascii(char *str, int str_len, int f, int l, int argc, char **sub_str, int *sub_str_len)
93 6 : {
94 6 : *sub_str = NULL;
95 :
96 6 : if (argc > 2) {
97 4 : if ((l < 0 && -l > str_len)) {
98 0 : return;
99 4 : } else if (l > str_len) {
100 1 : l = str_len;
101 : }
102 : } else {
103 2 : l = str_len;
104 : }
105 :
106 6 : if (f > str_len || (f < 0 && -f > str_len)) {
107 0 : return;
108 : }
109 :
110 6 : if (l < 0 && (l + str_len - f) < 0) {
111 0 : return;
112 : }
113 :
114 : /* if "from" position is negative, count start position from the end
115 : * of the string
116 : */
117 6 : if (f < 0) {
118 0 : f = str_len + f;
119 0 : if (f < 0) {
120 0 : f = 0;
121 : }
122 : }
123 :
124 :
125 : /* if "length" position is negative, set it to the length
126 : * needed to stop that many chars from the end of the string
127 : */
128 6 : if (l < 0) {
129 0 : l = (str_len - f) + l;
130 0 : if (l < 0) {
131 0 : l = 0;
132 : }
133 : }
134 :
135 6 : if (f >= str_len) {
136 0 : return;
137 : }
138 :
139 6 : if ((f + l) > str_len) {
140 2 : l = str_len - f;
141 : }
142 :
143 6 : *sub_str = str + f;
144 6 : *sub_str_len = l;
145 :
146 6 : return;
147 : }
148 : /* }}} */
149 :
150 : /* {{{ grapheme_strrpos_utf16 - strrpos using utf16 */
151 : int
152 : grapheme_strrpos_utf16(unsigned char *haystack, int32_t haystack_len, unsigned char*needle, int32_t needle_len, int32_t offset, int f_ignore_case TSRMLS_DC)
153 38 : {
154 : UChar *uhaystack, *puhaystack, *uhaystack_end, *uneedle;
155 : int32_t uhaystack_len, uneedle_len;
156 : UErrorCode status;
157 : unsigned char u_break_iterator_buffer[U_BRK_SAFECLONE_BUFFERSIZE];
158 38 : UBreakIterator* bi = NULL;
159 : int ret_pos, pos;
160 :
161 : /* convert the strings to UTF-16. */
162 38 : uhaystack = NULL;
163 38 : uhaystack_len = 0;
164 38 : status = U_ZERO_ERROR;
165 38 : intl_convert_utf8_to_utf16(&uhaystack, &uhaystack_len, (char *) haystack, haystack_len, &status );
166 :
167 38 : if ( U_FAILURE( status ) ) {
168 : /* Set global error code. */
169 0 : intl_error_set_code( NULL, status TSRMLS_CC );
170 :
171 : /* Set error messages. */
172 0 : intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 1 TSRMLS_CC );
173 0 : efree( uhaystack );
174 0 : return -1;
175 : }
176 :
177 38 : if ( f_ignore_case ) {
178 19 : grapheme_intl_case_fold(&uhaystack, &uhaystack, &uhaystack_len, &status );
179 : }
180 :
181 : /* get a pointer to the haystack taking into account the offset */
182 38 : bi = NULL;
183 38 : status = U_ZERO_ERROR;
184 38 : bi = grapheme_get_break_iterator(u_break_iterator_buffer, &status TSRMLS_CC );
185 :
186 38 : puhaystack = grapheme_get_haystack_offset(bi, uhaystack, uhaystack_len, offset);
187 :
188 38 : if ( NULL == puhaystack ) {
189 0 : intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_strpos: Offset not contained in string", 1 TSRMLS_CC );
190 0 : efree( uhaystack );
191 0 : ubrk_close (bi);
192 0 : return -1;
193 : }
194 :
195 38 : uneedle = NULL;
196 38 : uneedle_len = 0;
197 38 : status = U_ZERO_ERROR;
198 38 : intl_convert_utf8_to_utf16(&uneedle, &uneedle_len, (char *) needle, needle_len, &status );
199 :
200 38 : if ( U_FAILURE( status ) ) {
201 : /* Set global error code. */
202 0 : intl_error_set_code( NULL, status TSRMLS_CC );
203 :
204 : /* Set error messages. */
205 0 : intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 1 TSRMLS_CC );
206 0 : efree( uhaystack );
207 0 : efree( uneedle );
208 0 : ubrk_close (bi);
209 0 : return -1;
210 : }
211 :
212 38 : if ( f_ignore_case ) {
213 19 : grapheme_intl_case_fold(&uneedle, &uneedle, &uneedle_len, &status );
214 : }
215 :
216 38 : ret_pos = -1; /* -1 represents 'not found' */
217 :
218 : /* back up until there's needle_len characters to compare */
219 :
220 38 : uhaystack_end = uhaystack + uhaystack_len;
221 38 : pos = ubrk_last(bi);
222 38 : puhaystack = uhaystack + pos;
223 :
224 166 : while ( uhaystack_end - puhaystack < uneedle_len ) {
225 :
226 94 : pos = ubrk_previous(bi);
227 :
228 94 : if ( UBRK_DONE == pos ) {
229 4 : break;
230 : }
231 :
232 90 : puhaystack = uhaystack + pos;
233 : }
234 :
235 : /* is there enough haystack left to hold the needle? */
236 38 : if ( ( uhaystack_end - puhaystack ) < uneedle_len ) {
237 : /* not enough, not found */
238 4 : goto exit;
239 : }
240 :
241 108 : while ( UBRK_DONE != pos ) {
242 :
243 66 : if (!u_memcmp(uneedle, puhaystack, uneedle_len)) { /* needle_len - 1 in zend memnstr? */
244 :
245 : /* does the grapheme in the haystack end at the same place as the last grapheme in the needle? */
246 :
247 30 : if ( ubrk_isBoundary(bi, pos + uneedle_len) ) {
248 :
249 : /* found it, get grapheme count offset */
250 26 : ret_pos = grapheme_count_graphemes(bi, uhaystack, pos);
251 26 : break;
252 : }
253 :
254 : /* set position back */
255 4 : ubrk_isBoundary(bi, pos);
256 : }
257 :
258 40 : pos = ubrk_previous(bi);
259 40 : puhaystack = uhaystack + pos;
260 : }
261 :
262 38 : exit:
263 38 : efree( uhaystack );
264 38 : efree( uneedle );
265 38 : ubrk_close (bi);
266 :
267 38 : return ret_pos;
268 : }
269 :
270 : /* }}} */
271 :
272 : /* {{{ grapheme_strpos_utf16 - strrpos using utf16*/
273 : int
274 : grapheme_strpos_utf16(unsigned char *haystack, int32_t haystack_len, unsigned char*needle, int32_t needle_len, int32_t offset, int32_t *puchar_pos, int f_ignore_case TSRMLS_DC)
275 88 : {
276 : UChar *uhaystack, *puhaystack, *uneedle;
277 : int32_t uhaystack_len, uneedle_len;
278 : int ret_pos;
279 : unsigned char u_break_iterator_buffer[U_BRK_SAFECLONE_BUFFERSIZE];
280 : UBreakIterator* bi;
281 : UErrorCode status;
282 :
283 88 : *puchar_pos = -1;
284 :
285 : /* convert the strings to UTF-16. */
286 :
287 88 : uhaystack = NULL;
288 88 : uhaystack_len = 0;
289 88 : status = U_ZERO_ERROR;
290 88 : intl_convert_utf8_to_utf16(&uhaystack, &uhaystack_len, (char *) haystack, haystack_len, &status );
291 :
292 88 : if ( U_FAILURE( status ) ) {
293 : /* Set global error code. */
294 0 : intl_error_set_code( NULL, status TSRMLS_CC );
295 :
296 : /* Set error messages. */
297 0 : intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 1 TSRMLS_CC );
298 0 : efree( uhaystack );
299 0 : return -1;
300 : }
301 :
302 : /* get a pointer to the haystack taking into account the offset */
303 88 : bi = NULL;
304 88 : status = U_ZERO_ERROR;
305 88 : bi = grapheme_get_break_iterator(u_break_iterator_buffer, &status TSRMLS_CC );
306 :
307 88 : puhaystack = grapheme_get_haystack_offset(bi, uhaystack, uhaystack_len, offset);
308 88 : uhaystack_len = (uhaystack_len - ( puhaystack - uhaystack));
309 :
310 88 : if ( NULL == puhaystack ) {
311 :
312 0 : intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_strpos: Offset not contained in string", 1 TSRMLS_CC );
313 :
314 0 : efree( uhaystack );
315 0 : ubrk_close (bi);
316 :
317 0 : return -1;
318 : }
319 :
320 88 : if ( f_ignore_case ) {
321 54 : grapheme_intl_case_fold(&uhaystack, &puhaystack, &uhaystack_len, &status );
322 : }
323 :
324 88 : uneedle = NULL;
325 88 : uneedle_len = 0;
326 88 : status = U_ZERO_ERROR;
327 88 : intl_convert_utf8_to_utf16(&uneedle, &uneedle_len, (char *) needle, needle_len, &status );
328 :
329 88 : if ( U_FAILURE( status ) ) {
330 : /* Set global error code. */
331 0 : intl_error_set_code( NULL, status TSRMLS_CC );
332 :
333 : /* Set error messages. */
334 0 : intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 1 TSRMLS_CC );
335 0 : efree( uhaystack );
336 0 : efree( uneedle );
337 0 : ubrk_close (bi);
338 :
339 0 : return -1;
340 : }
341 :
342 88 : if ( f_ignore_case ) {
343 54 : grapheme_intl_case_fold(&uneedle, &uneedle, &uneedle_len, &status );
344 : }
345 :
346 88 : ret_pos = grapheme_memnstr_grapheme(bi, puhaystack, uneedle, uneedle_len, puhaystack + uhaystack_len );
347 :
348 88 : *puchar_pos = ubrk_current(bi);
349 :
350 88 : efree( uhaystack );
351 88 : efree( uneedle );
352 88 : ubrk_close (bi);
353 :
354 88 : return ret_pos;
355 : }
356 :
357 : /* }}} */
358 :
359 : /* {{{ grapheme_ascii_check: ASCII check */
360 : int grapheme_ascii_check(const unsigned char *day, int32_t len)
361 349 : {
362 349 : int ret_len = len;
363 1489 : while ( len-- ) {
364 1000 : if ( *day++ > 0x7f )
365 209 : return -1;
366 : }
367 :
368 140 : return ret_len;
369 : }
370 :
371 : /* }}} */
372 :
373 : /* {{{ grapheme_split_string: find and optionally return grapheme boundaries */
374 : int grapheme_split_string(const UChar *text, int32_t text_length, int boundary_array[], int boundary_array_len TSRMLS_DC )
375 4 : {
376 : unsigned char u_break_iterator_buffer[U_BRK_SAFECLONE_BUFFERSIZE];
377 4 : UErrorCode status = U_ZERO_ERROR;
378 : int ret_len, pos;
379 : UBreakIterator* bi;
380 :
381 4 : bi = grapheme_get_break_iterator((void*)u_break_iterator_buffer, &status TSRMLS_CC );
382 :
383 4 : if( U_FAILURE(status) ) {
384 0 : return -1;
385 : }
386 :
387 4 : ubrk_setText(bi, text, text_length, &status);
388 :
389 4 : pos = 0;
390 :
391 29 : for ( ret_len = 0; pos != UBRK_DONE; ) {
392 :
393 21 : pos = ubrk_next(bi);
394 :
395 21 : if ( pos != UBRK_DONE ) {
396 :
397 17 : if ( NULL != boundary_array && ret_len < boundary_array_len ) {
398 0 : boundary_array[ret_len] = pos;
399 : }
400 :
401 17 : ret_len++;
402 : }
403 : }
404 :
405 4 : ubrk_close(bi);
406 :
407 4 : return ret_len;
408 : }
409 : /* }}} */
410 :
411 : /* {{{ grapheme_count_graphemes */
412 : inline int32_t
413 : grapheme_count_graphemes(UBreakIterator *bi, UChar *string, int32_t string_len)
414 97 : {
415 97 : int ret_len = 0;
416 97 : int pos = 0;
417 97 : UErrorCode status = U_ZERO_ERROR;
418 :
419 97 : ubrk_setText(bi, string, string_len, &status);
420 :
421 : do {
422 :
423 341 : pos = ubrk_next(bi);
424 :
425 341 : if ( UBRK_DONE != pos ) {
426 244 : ret_len++;
427 : }
428 :
429 341 : } while ( UBRK_DONE != pos );
430 :
431 97 : return ret_len;
432 : }
433 : /* }}} */
434 :
435 : /* {{{ grapheme_memnstr_grapheme: find needle in haystack using grapheme boundaries */
436 : inline int32_t
437 : grapheme_memnstr_grapheme(UBreakIterator *bi, UChar *haystack, UChar *needle, int32_t needle_len, UChar *end)
438 88 : {
439 88 : UChar *p = haystack;
440 88 : UChar ne = needle[needle_len-1];
441 : UErrorCode status;
442 : int32_t grapheme_offset;
443 :
444 88 : end -= needle_len;
445 :
446 232 : while (p <= end) {
447 :
448 142 : if ((p = u_memchr(p, *needle, (end-p+1))) && ne == p[needle_len-1]) {
449 :
450 89 : if (!u_memcmp(needle, p, needle_len - 1)) { /* needle_len - 1 works because if needle_len is 1, we've already tested the char */
451 :
452 : /* does the grapheme end here? */
453 :
454 89 : status = U_ZERO_ERROR;
455 89 : ubrk_setText (bi, haystack, (end - haystack) + needle_len, &status);
456 :
457 89 : if ( ubrk_isBoundary (bi, (p - haystack) + needle_len) ) {
458 :
459 : /* found it, get grapheme count offset */
460 71 : grapheme_offset = grapheme_count_graphemes(bi, haystack, (p - haystack));
461 :
462 71 : return grapheme_offset;
463 : }
464 : }
465 : }
466 :
467 71 : if (p == NULL) {
468 15 : return -1;
469 : }
470 :
471 56 : p++;
472 : }
473 :
474 2 : return -1;
475 : }
476 :
477 : /* }}} */
478 :
479 : /* {{{ grapheme_memrstr_grapheme: reverse find needle in haystack using grapheme boundaries */
480 : inline void *grapheme_memrchr_grapheme(const void *s, int c, int32_t n)
481 0 : {
482 : register unsigned char *e;
483 :
484 0 : if (n <= 0) {
485 0 : return NULL;
486 : }
487 :
488 0 : for (e = (unsigned char *)s + n - 1; e >= (unsigned char *)s; e--) {
489 0 : if (*e == (unsigned char)c) {
490 0 : return (void *)e;
491 : }
492 : }
493 :
494 0 : return NULL;
495 : }
496 : /* }}} */
497 :
498 : /* {{{ grapheme_get_haystack_offset - bump the haystack pointer based on the grapheme count offset */
499 : UChar *
500 : grapheme_get_haystack_offset(UBreakIterator* bi, UChar *uhaystack, int32_t uhaystack_len, int32_t offset)
501 126 : {
502 : UErrorCode status;
503 : int32_t pos;
504 : int32_t (*iter_op)(UBreakIterator* bi);
505 : int iter_incr;
506 :
507 126 : if ( NULL != bi ) {
508 126 : status = U_ZERO_ERROR;
509 126 : ubrk_setText (bi, uhaystack, uhaystack_len, &status);
510 : }
511 :
512 126 : if ( 0 == offset ) {
513 110 : return uhaystack;
514 : }
515 :
516 16 : if ( offset < 0 ) {
517 0 : iter_op = ubrk_previous;
518 0 : ubrk_last(bi); /* one past the end */
519 0 : iter_incr = 1;
520 : }
521 : else {
522 16 : iter_op = ubrk_next;
523 16 : iter_incr = -1;
524 : }
525 :
526 16 : pos = 0;
527 :
528 64 : while ( pos != UBRK_DONE && offset != 0 ) {
529 :
530 32 : pos = iter_op(bi);
531 :
532 32 : if ( UBRK_DONE != pos ) {
533 32 : offset += iter_incr;
534 : }
535 : }
536 :
537 16 : if ( offset != 0 ) {
538 0 : return NULL;
539 : }
540 :
541 16 : return uhaystack + pos;
542 : }
543 : /* }}} */
544 :
545 : /* {{{ grapheme_strrpos_ascii: borrowed from the php ext/standard/string.c */
546 : int32_t
547 : grapheme_strrpos_ascii(unsigned char *haystack, int32_t haystack_len, unsigned char *needle, int32_t needle_len, int32_t offset)
548 38 : {
549 : unsigned char *p, *e;
550 :
551 38 : if (offset >= 0) {
552 38 : p = haystack + offset;
553 38 : e = haystack + haystack_len - needle_len;
554 : } else {
555 0 : p = haystack;
556 0 : if (needle_len > -offset) {
557 0 : e = haystack + haystack_len - needle_len;
558 : } else {
559 0 : e = haystack + haystack_len + offset;
560 : }
561 : }
562 :
563 38 : if (needle_len == 1) {
564 : /* Single character search can shortcut memcmps */
565 52 : while (e >= p) {
566 34 : if (*e == *needle) {
567 10 : return (e - p + (offset > 0 ? offset : 0));
568 : }
569 24 : e--;
570 : }
571 4 : return -1;
572 : }
573 :
574 56 : while (e >= p) {
575 22 : if (memcmp(e, needle, needle_len) == 0) {
576 14 : return (e - p + (offset > 0 ? offset : 0));
577 : }
578 8 : e--;
579 : }
580 :
581 10 : return -1;
582 : }
583 :
584 : /* }}} */
585 :
586 : /* {{{ grapheme_get_break_iterator: get a clone of the global character break iterator */
587 : UBreakIterator*
588 : grapheme_get_break_iterator(void *stack_buffer, UErrorCode *status TSRMLS_DC )
589 244 : {
590 : int32_t buffer_size;
591 :
592 244 : UBreakIterator *global_break_iterator = INTL_G( grapheme_iterator );
593 :
594 244 : if ( NULL == global_break_iterator ) {
595 :
596 1 : global_break_iterator = ubrk_open(UBRK_CHARACTER,
597 : NULL, /* icu default locale - locale has no effect on this iterator */
598 : NULL, /* text not set in global iterator */
599 : 0, /* text length = 0 */
600 : status);
601 :
602 1 : INTL_G(grapheme_iterator) = global_break_iterator;
603 : }
604 :
605 244 : buffer_size = U_BRK_SAFECLONE_BUFFERSIZE;
606 :
607 244 : return ubrk_safeClone(global_break_iterator, stack_buffer, &buffer_size, status);
608 : }
609 : /* }}} */
610 :
611 : /*
612 : * Local variables:
613 : * tab-width: 4
614 : * c-basic-offset: 4
615 : * End:
616 : * vim600: fdm=marker
617 : * vim: noet sw=4 ts=4
618 : */
619 :
|