Annotation of embedaddon/php/ext/intl/grapheme/grapheme_string.c, revision 1.1.1.4

1.1       misho       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: 
                     26: #include <unicode/utypes.h>
                     27: #include <unicode/ucol.h>
                     28: #include <unicode/ustring.h>
                     29: #include <unicode/ubrk.h>
                     30: 
                     31: #include "ext/standard/php_string.h"
                     32: 
                     33: /* }}} */
                     34: 
                     35: #define GRAPHEME_EXTRACT_TYPE_COUNT            0
                     36: #define GRAPHEME_EXTRACT_TYPE_MAXBYTES 1
                     37: #define GRAPHEME_EXTRACT_TYPE_MAXCHARS 2
                     38: #define GRAPHEME_EXTRACT_TYPE_MIN      GRAPHEME_EXTRACT_TYPE_COUNT
                     39: #define GRAPHEME_EXTRACT_TYPE_MAX      GRAPHEME_EXTRACT_TYPE_MAXCHARS
                     40: 
                     41: 
                     42: /* {{{ grapheme_register_constants
                     43:  * Register API constants
                     44:  */
                     45: void grapheme_register_constants( INIT_FUNC_ARGS )
                     46: {
                     47:        REGISTER_LONG_CONSTANT("GRAPHEME_EXTR_COUNT", GRAPHEME_EXTRACT_TYPE_COUNT, CONST_CS | CONST_PERSISTENT);
                     48:        REGISTER_LONG_CONSTANT("GRAPHEME_EXTR_MAXBYTES", GRAPHEME_EXTRACT_TYPE_MAXBYTES, CONST_CS | CONST_PERSISTENT);
                     49:        REGISTER_LONG_CONSTANT("GRAPHEME_EXTR_MAXCHARS", GRAPHEME_EXTRACT_TYPE_MAXCHARS, CONST_CS | CONST_PERSISTENT);
                     50: }
                     51: /* }}} */
                     52: 
                     53: /* {{{ proto int grapheme_strlen(string str)
                     54:    Get number of graphemes in a string */
                     55: PHP_FUNCTION(grapheme_strlen)
                     56: {
                     57:        unsigned char* string;
                     58:        int string_len;
                     59:        UChar* ustring = NULL;
                     60:        int ustring_len = 0;
                     61:        int ret_len;
                     62:        UErrorCode status;
                     63: 
                     64:        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", (char **)&string, &string_len) == FAILURE) {
                     65: 
                     66:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
                     67:                         "grapheme_strlen: unable to parse input param", 0 TSRMLS_CC );
                     68: 
                     69:                RETURN_FALSE;
                     70:        }
                     71: 
                     72:        ret_len = grapheme_ascii_check(string, string_len);
                     73: 
                     74:        if ( ret_len >= 0 )
                     75:                RETURN_LONG(ret_len);
                     76: 
                     77:        /* convert the string to UTF-16. */
                     78:        status = U_ZERO_ERROR;
                     79:        intl_convert_utf8_to_utf16(&ustring, &ustring_len, (char*) string, string_len, &status );
                     80: 
                     81:        if ( U_FAILURE( status ) ) {
                     82:                /* Set global error code. */
                     83:                intl_error_set_code( NULL, status TSRMLS_CC );
                     84: 
                     85:                /* Set error messages. */
                     86:                intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 0 TSRMLS_CC );
                     87:                if (ustring) {
                     88:                        efree( ustring );
                     89:                }
                     90:                RETURN_NULL();
                     91:        }
                     92: 
                     93:        ret_len = grapheme_split_string(ustring, ustring_len, NULL, 0 TSRMLS_CC );
                     94: 
                     95:        if (ustring) {
                     96:                efree( ustring );
                     97:        }
                     98: 
                     99:        if (ret_len >= 0) {
                    100:                RETVAL_LONG(ret_len);
                    101:        } else {
                    102:                RETVAL_FALSE;
                    103:        }
                    104: }
                    105: /* }}} */
                    106: 
                    107: /* {{{ proto int grapheme_strpos(string haystack, string needle [, int offset ])
                    108:    Find position of first occurrence of a string within another */
                    109: PHP_FUNCTION(grapheme_strpos)
                    110: {
                    111:        unsigned char *haystack, *needle;
                    112:        int haystack_len, needle_len;
                    113:        unsigned char *found;
                    114:        long loffset = 0;
                    115:        int32_t offset = 0;
1.1.1.4 ! misho     116:        int ret_pos;
1.1       misho     117: 
                    118:        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", (char **)&haystack, &haystack_len, (char **)&needle, &needle_len, &loffset) == FAILURE) {
                    119: 
                    120:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
                    121:                         "grapheme_strpos: unable to parse input param", 0 TSRMLS_CC );
                    122: 
                    123:                RETURN_FALSE;
                    124:        }
                    125: 
                    126:        if ( OUTSIDE_STRING(loffset, haystack_len) ) {
                    127: 
                    128:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_strpos: Offset not contained in string", 1 TSRMLS_CC );
                    129: 
                    130:                RETURN_FALSE;
                    131:        }
                    132: 
                    133:        /* we checked that it will fit: */
                    134:        offset = (int32_t) loffset;
                    135: 
                    136:        /* the offset is 'grapheme count offset' so it still might be invalid - we'll check it later */
                    137: 
                    138:        if (needle_len == 0) {
                    139: 
                    140:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_strpos: Empty delimiter", 1 TSRMLS_CC );
                    141: 
                    142:                RETURN_FALSE;
                    143:        }
                    144: 
                    145: 
                    146:        /* quick check to see if the string might be there
                    147:         * I realize that 'offset' is 'grapheme count offset' but will work in spite of that
                    148:        */
                    149:        found = (unsigned char *)php_memnstr((char *)haystack + offset, (char *)needle, needle_len, (char *)haystack + haystack_len);
                    150: 
                    151:        /* if it isn't there the we are done */
                    152:        if (!found) {
                    153:                RETURN_FALSE;
                    154:        }
                    155: 
                    156:        /* if it is there, and if the haystack is ascii, we are all done */
                    157:        if ( grapheme_ascii_check(haystack, haystack_len) >= 0 ) {
                    158: 
                    159:                RETURN_LONG(found - haystack);
                    160:        }
                    161: 
                    162:        /* do utf16 part of the strpos */
1.1.1.4 ! misho     163:        ret_pos = grapheme_strpos_utf16(haystack, haystack_len, needle, needle_len, offset, NULL, 0 /* fIgnoreCase */, 0 /* last */ TSRMLS_CC );
1.1       misho     164: 
                    165:        if ( ret_pos >= 0 ) {
1.1.1.4 ! misho     166:                RETURN_LONG(ret_pos);
1.1       misho     167:        } else {
                    168:                RETURN_FALSE;
                    169:        }
                    170: 
                    171: }
                    172: /* }}} */
                    173: 
                    174: /* {{{ proto int grapheme_stripos(string haystack, string needle [, int offset ])
                    175:    Find position of first occurrence of a string within another, ignoring case differences */
                    176: PHP_FUNCTION(grapheme_stripos)
                    177: {
                    178:        unsigned char *haystack, *needle, *haystack_dup, *needle_dup;
                    179:        int haystack_len, needle_len;
                    180:        unsigned char *found;
                    181:        long loffset = 0;
                    182:        int32_t offset = 0;
1.1.1.4 ! misho     183:        int ret_pos;
1.1       misho     184:        int is_ascii;
                    185: 
                    186:        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", (char **)&haystack, &haystack_len, (char **)&needle, &needle_len, &loffset) == FAILURE) {
                    187: 
                    188:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
                    189:                         "grapheme_stripos: unable to parse input param", 0 TSRMLS_CC );
                    190: 
                    191:                RETURN_FALSE;
                    192:        }
                    193: 
                    194:        if ( OUTSIDE_STRING(loffset, haystack_len) ) {
                    195: 
                    196:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_stripos: Offset not contained in string", 1 TSRMLS_CC );
                    197: 
                    198:                RETURN_FALSE;
                    199:        }
                    200: 
                    201:        /* we checked that it will fit: */
                    202:        offset = (int32_t) loffset;
                    203: 
                    204:        /* the offset is 'grapheme count offset' so it still might be invalid - we'll check it later */
                    205: 
                    206:        if (needle_len == 0) {
                    207: 
                    208:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_stripos: Empty delimiter", 1 TSRMLS_CC );
                    209: 
                    210:                RETURN_FALSE;
                    211:        }
                    212: 
                    213: 
                    214:        is_ascii = ( grapheme_ascii_check(haystack, haystack_len) >= 0 );
                    215: 
                    216:        if ( is_ascii ) {
                    217:                needle_dup = (unsigned char *)estrndup((char *)needle, needle_len);
                    218:                php_strtolower((char *)needle_dup, needle_len);
                    219:                haystack_dup = (unsigned char *)estrndup((char *)haystack, haystack_len);
                    220:                php_strtolower((char *)haystack_dup, haystack_len);
                    221: 
                    222:                found = (unsigned char*) php_memnstr((char *)haystack_dup + offset, (char *)needle_dup, needle_len, (char *)haystack_dup + haystack_len);
                    223: 
                    224:                efree(haystack_dup);
                    225:                efree(needle_dup);
                    226: 
                    227:                if (found) {
                    228:                        RETURN_LONG(found - haystack_dup);
                    229:                }
                    230: 
                    231:                /* if needle was ascii too, we are all done, otherwise we need to try using Unicode to see what we get */
                    232:                if ( grapheme_ascii_check(needle, needle_len) >= 0 ) {
                    233:                        RETURN_FALSE;
                    234:                }
                    235:        }
                    236: 
                    237:        /* do utf16 part of the strpos */
1.1.1.4 ! misho     238:        ret_pos = grapheme_strpos_utf16(haystack, haystack_len, needle, needle_len, offset, NULL, 1 /* fIgnoreCase */, 0 /*last */ TSRMLS_CC );
1.1       misho     239: 
                    240:        if ( ret_pos >= 0 ) {
1.1.1.4 ! misho     241:                RETURN_LONG(ret_pos);
1.1       misho     242:        } else {
                    243:                RETURN_FALSE;
                    244:        }
                    245: 
                    246: }
                    247: /* }}} */
                    248: 
                    249: /* {{{ proto int grapheme_strrpos(string haystack, string needle [, int offset])
                    250:    Find position of last occurrence of a string within another */
                    251: PHP_FUNCTION(grapheme_strrpos)
                    252: {
                    253:        unsigned char *haystack, *needle;
                    254:        int haystack_len, needle_len;
                    255:        long loffset = 0;
                    256:        int32_t offset = 0;
                    257:        int32_t ret_pos;
                    258:        int is_ascii;
                    259: 
                    260:        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", (char **)&haystack, &haystack_len, (char **)&needle, &needle_len, &loffset) == FAILURE) {
                    261: 
                    262:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
                    263:                         "grapheme_strrpos: unable to parse input param", 0 TSRMLS_CC );
                    264: 
                    265:                RETURN_FALSE;
                    266:        }
                    267: 
                    268:        if ( OUTSIDE_STRING(loffset, haystack_len) ) {
                    269: 
                    270:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_strpos: Offset not contained in string", 1 TSRMLS_CC );
                    271: 
                    272:                RETURN_FALSE;
                    273:        }
                    274: 
                    275:        /* we checked that it will fit: */
                    276:        offset = (int32_t) loffset;
                    277: 
                    278:        /* the offset is 'grapheme count offset' so it still might be invalid - we'll check it later */
                    279: 
                    280:        if (needle_len == 0) {
                    281: 
                    282:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_strpos: Empty delimiter", 1 TSRMLS_CC );
                    283: 
                    284:                RETURN_FALSE;
                    285:        }
                    286: 
                    287:        is_ascii = grapheme_ascii_check(haystack, haystack_len) >= 0;
                    288: 
                    289:        if ( is_ascii ) {
                    290: 
                    291:                ret_pos = grapheme_strrpos_ascii(haystack, haystack_len, needle, needle_len, offset);
                    292: 
                    293: 
                    294:                if ( ret_pos >= 0 ) {
                    295:                        RETURN_LONG(ret_pos);
                    296:                }
                    297: 
                    298:                /* if the needle was ascii too, we are done */
                    299: 
                    300:                if (  grapheme_ascii_check(needle, needle_len) >= 0 ) {
                    301:                        RETURN_FALSE;
                    302:                }
                    303: 
                    304:                /* else we need to continue via utf16 */
                    305:        }
                    306: 
1.1.1.4 ! misho     307:        ret_pos = grapheme_strpos_utf16(haystack, haystack_len, needle, needle_len, offset, NULL, 0 /* f_ignore_case */, 1/* last */ TSRMLS_CC);
1.1       misho     308: 
                    309:        if ( ret_pos >= 0 ) {
                    310:                RETURN_LONG(ret_pos);
                    311:        } else {
                    312:                RETURN_FALSE;
                    313:        }
                    314: 
                    315: 
                    316: }
                    317: /* }}} */
                    318: 
                    319: /* {{{ proto int grapheme_strripos(string haystack, string needle [, int offset])
                    320:    Find position of last occurrence of a string within another, ignoring case */
                    321: PHP_FUNCTION(grapheme_strripos)
                    322: {
                    323:        unsigned char *haystack, *needle;
                    324:        int haystack_len, needle_len;
                    325:        long loffset = 0;
                    326:        int32_t offset = 0;
                    327:        int32_t ret_pos;
                    328:        int is_ascii;
                    329: 
                    330:        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", (char **)&haystack, &haystack_len, (char **)&needle, &needle_len, &loffset) == FAILURE) {
                    331: 
                    332:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
                    333:                         "grapheme_strrpos: unable to parse input param", 0 TSRMLS_CC );
                    334: 
                    335:                RETURN_FALSE;
                    336:        }
                    337: 
                    338:        if ( OUTSIDE_STRING(loffset, haystack_len) ) {
                    339: 
                    340:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_strpos: Offset not contained in string", 1 TSRMLS_CC );
                    341: 
                    342:                RETURN_FALSE;
                    343:        }
                    344: 
                    345:        /* we checked that it will fit: */
                    346:        offset = (int32_t) loffset;
                    347: 
                    348:        /* the offset is 'grapheme count offset' so it still might be invalid - we'll check it later */
                    349: 
                    350:        if (needle_len == 0) {
                    351: 
                    352:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_strpos: Empty delimiter", 1 TSRMLS_CC );
                    353: 
                    354:                RETURN_FALSE;
                    355:        }
                    356: 
                    357:        is_ascii = grapheme_ascii_check(haystack, haystack_len) >= 0;
                    358: 
                    359:        if ( is_ascii ) {
                    360:                unsigned char *needle_dup, *haystack_dup;
                    361: 
                    362:                needle_dup = (unsigned char *)estrndup((char *)needle, needle_len);
                    363:                php_strtolower((char *)needle_dup, needle_len);
                    364:                haystack_dup = (unsigned char *)estrndup((char *)haystack, haystack_len);
                    365:                php_strtolower((char *)haystack_dup, haystack_len);
                    366: 
                    367:                ret_pos = grapheme_strrpos_ascii(haystack_dup, haystack_len, needle_dup, needle_len, offset);
                    368: 
                    369:                efree(haystack_dup);
                    370:                efree(needle_dup);
                    371: 
                    372:                if ( ret_pos >= 0 ) {
                    373:                        RETURN_LONG(ret_pos);
                    374:                }
                    375: 
                    376:                /* if the needle was ascii too, we are done */
                    377: 
                    378:                if (  grapheme_ascii_check(needle, needle_len) >= 0 ) {
                    379:                        RETURN_FALSE;
                    380:                }
                    381: 
                    382:                /* else we need to continue via utf16 */
                    383:        }
                    384: 
1.1.1.4 ! misho     385:        ret_pos = grapheme_strpos_utf16(haystack, haystack_len, needle, needle_len, offset, NULL,  1 /* f_ignore_case */, 1 /*last */ TSRMLS_CC);
1.1       misho     386: 
                    387:        if ( ret_pos >= 0 ) {
                    388:                RETURN_LONG(ret_pos);
                    389:        } else {
                    390:                RETURN_FALSE;
                    391:        }
                    392: 
                    393: 
                    394: }
                    395: /* }}} */
                    396: 
                    397: /* {{{ proto string grapheme_substr(string str, int start [, int length])
                    398:    Returns part of a string */
                    399: PHP_FUNCTION(grapheme_substr)
                    400: {
                    401:        unsigned char *str, *sub_str;
                    402:        UChar *ustr;
                    403:        int str_len, sub_str_len, ustr_len;
                    404:        long lstart = 0, length = 0;
                    405:        int32_t start = 0;
                    406:        int iter_val;
                    407:        UErrorCode status;
                    408:        unsigned char u_break_iterator_buffer[U_BRK_SAFECLONE_BUFFERSIZE];
                    409:        UBreakIterator* bi = NULL;
                    410:        int sub_str_start_pos, sub_str_end_pos;
                    411:        int32_t (*iter_func)(UBreakIterator *);
                    412: 
                    413:        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|l", (char **)&str, &str_len, &lstart, &length) == FAILURE) {
                    414: 
                    415:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
                    416:                         "grapheme_substr: unable to parse input param", 0 TSRMLS_CC );
                    417: 
                    418:                RETURN_FALSE;
                    419:        }
                    420: 
                    421:        if ( OUTSIDE_STRING(lstart, str_len) ) {
                    422: 
                    423:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_substr: start not contained in string", 1 TSRMLS_CC );
                    424: 
                    425:                RETURN_FALSE;
                    426:        }
                    427: 
                    428:        /* we checked that it will fit: */
                    429:        start = (int32_t) lstart;
                    430: 
                    431:        /* the offset is 'grapheme count offset' so it still might be invalid - we'll check it later */
                    432: 
                    433:        if ( grapheme_ascii_check(str, str_len) >= 0 ) {
                    434:                grapheme_substr_ascii((char *)str, str_len, start, length, ZEND_NUM_ARGS(), (char **) &sub_str, &sub_str_len);
                    435: 
                    436:                if ( NULL == sub_str ) {
1.1.1.4 ! misho     437:                        intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_substr: invalid parameters", 1 TSRMLS_CC );
1.1       misho     438:                        RETURN_FALSE;
                    439:                }
                    440: 
                    441:                RETURN_STRINGL(((char *)sub_str), sub_str_len, 1);
                    442:        }
                    443: 
                    444:        ustr = NULL;
                    445:        ustr_len = 0;
                    446:        status = U_ZERO_ERROR;
                    447:        intl_convert_utf8_to_utf16(&ustr, &ustr_len, (char *)str, str_len, &status);
                    448: 
                    449:        if ( U_FAILURE( status ) ) {
                    450:                /* Set global error code. */
                    451:                intl_error_set_code( NULL, status TSRMLS_CC );
                    452: 
                    453:                /* Set error messages. */
                    454:                intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 0 TSRMLS_CC );
                    455:                if (ustr) {
                    456:                        efree( ustr );
                    457:                }
                    458:                RETURN_FALSE;
                    459:        }
                    460: 
                    461:        bi = grapheme_get_break_iterator((void*)u_break_iterator_buffer, &status TSRMLS_CC );
                    462: 
                    463:        if( U_FAILURE(status) ) {
                    464:                RETURN_FALSE;
                    465:        }
                    466: 
                    467:        ubrk_setText(bi, ustr, ustr_len,        &status);
                    468: 
                    469:        if ( start < 0 ) {
                    470:                iter_func = ubrk_previous;
                    471:                ubrk_last(bi);
                    472:                iter_val = 1;
                    473:        }
                    474:        else {
                    475:                iter_func = ubrk_next;
                    476:                iter_val = -1;
                    477:        }
                    478: 
                    479:        sub_str_start_pos = 0;
                    480: 
                    481:        while ( start ) {
                    482:                sub_str_start_pos = iter_func(bi);
                    483: 
                    484:                if ( UBRK_DONE == sub_str_start_pos ) {
                    485:                        break;
                    486:                }
                    487: 
                    488:                start += iter_val;
                    489:        }
                    490: 
                    491:        if ( 0 != start || sub_str_start_pos >= ustr_len ) {
                    492: 
                    493:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_substr: start not contained in string", 1 TSRMLS_CC );
                    494: 
                    495:                if (ustr) {
                    496:                        efree(ustr);
                    497:                }
                    498:                ubrk_close(bi);
                    499:                RETURN_FALSE;
                    500:        }
                    501: 
                    502:        if (ZEND_NUM_ARGS() <= 2) {
                    503: 
                    504:                /* no length supplied, return the rest of the string */
                    505: 
                    506:                sub_str = NULL;
                    507:                sub_str_len = 0;
                    508:                status = U_ZERO_ERROR;
                    509:                intl_convert_utf16_to_utf8((char **)&sub_str, &sub_str_len, ustr + sub_str_start_pos, ustr_len - sub_str_start_pos, &status);
                    510: 
                    511:                if (ustr) {
                    512:                        efree( ustr );
                    513:                }
                    514:                ubrk_close( bi );
                    515: 
                    516:                if ( U_FAILURE( status ) ) {
                    517:                        /* Set global error code. */
                    518:                        intl_error_set_code( NULL, status TSRMLS_CC );
                    519: 
                    520:                        /* Set error messages. */
                    521:                        intl_error_set_custom_msg( NULL, "Error converting output string to UTF-8", 0 TSRMLS_CC );
                    522: 
                    523:                        if (sub_str) {
                    524:                                efree( sub_str );
                    525:                        }
                    526: 
                    527:                        RETURN_FALSE;
                    528:                }
                    529: 
                    530:                /* return the allocated string, not a duplicate */
                    531:                RETURN_STRINGL(((char *)sub_str), sub_str_len, 0);
                    532:        }
                    533: 
1.1.1.4 ! misho     534:        if(length == 0) {
        !           535:                /* empty length - we've validated start, we can return "" now */
        !           536:                if (ustr) {
        !           537:                        efree(ustr);
        !           538:                }
        !           539:                ubrk_close(bi);
        !           540:                RETURN_EMPTY_STRING();          
        !           541:        }
        !           542: 
1.1       misho     543:        /* find the end point of the string to return */
                    544: 
                    545:        if ( length < 0 ) {
                    546:                iter_func = ubrk_previous;
                    547:                ubrk_last(bi);
                    548:                iter_val = 1;
                    549:        }
                    550:        else {
                    551:                iter_func = ubrk_next;
                    552:                iter_val = -1;
                    553:        }
                    554: 
                    555:        sub_str_end_pos = 0;
                    556: 
                    557:        while ( length ) {
                    558:                sub_str_end_pos = iter_func(bi);
                    559: 
                    560:                if ( UBRK_DONE == sub_str_end_pos ) {
                    561:                        break;
                    562:                }
                    563: 
                    564:                length += iter_val;
                    565:        }
                    566: 
1.1.1.4 ! misho     567:        ubrk_close(bi);
        !           568: 
1.1.1.2   misho     569:        if ( UBRK_DONE == sub_str_end_pos) {
                    570:                if(length < 0) {
                    571:                        intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_substr: length not contained in string", 1 TSRMLS_CC );
1.1       misho     572: 
1.1.1.2   misho     573:                        efree(ustr);
                    574:                        RETURN_FALSE;
                    575:                } else {
                    576:                        sub_str_end_pos = ustr_len;
                    577:                }
1.1       misho     578:        }
1.1.1.4 ! misho     579:        
        !           580:        if(sub_str_start_pos > sub_str_end_pos) {
        !           581:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_substr: length is beyond start", 1 TSRMLS_CC );
        !           582: 
        !           583:                efree(ustr);
        !           584:                RETURN_FALSE;
        !           585:        }
1.1       misho     586: 
                    587:        sub_str = NULL;
                    588:        status = U_ZERO_ERROR;
                    589:        intl_convert_utf16_to_utf8((char **)&sub_str, &sub_str_len, ustr + sub_str_start_pos, ( sub_str_end_pos - sub_str_start_pos ), &status);
                    590: 
                    591:        efree( ustr );
                    592: 
                    593:        if ( U_FAILURE( status ) ) {
                    594:                /* Set global error code. */
                    595:                intl_error_set_code( NULL, status TSRMLS_CC );
                    596: 
                    597:                /* Set error messages. */
                    598:                intl_error_set_custom_msg( NULL, "Error converting output string to UTF-8", 0 TSRMLS_CC );
                    599: 
                    600:                if ( NULL != sub_str )
                    601:                        efree( sub_str );
                    602: 
                    603:                RETURN_FALSE;
                    604:        }
                    605: 
                    606:         /* return the allocated string, not a duplicate */
                    607:        RETURN_STRINGL(((char *)sub_str), sub_str_len, 0);
                    608: 
                    609: }
                    610: /* }}} */
                    611: 
                    612: /* {{{ strstr_common_handler */
                    613: static void strstr_common_handler(INTERNAL_FUNCTION_PARAMETERS, int f_ignore_case)
                    614: {
                    615:        unsigned char *haystack, *needle, *found;
                    616:        int haystack_len, needle_len;
                    617:        int ret_pos, uchar_pos;
                    618:        zend_bool part = 0;
                    619: 
                    620:        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|b", (char **)&haystack, &haystack_len, (char **)&needle, &needle_len, &part) == FAILURE) {
                    621: 
                    622:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
                    623:                         "grapheme_strstr: unable to parse input param", 0 TSRMLS_CC );
                    624: 
                    625:                RETURN_FALSE;
                    626:        }
                    627: 
                    628:        if (needle_len == 0) {
                    629: 
                    630:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_strpos: Empty delimiter", 1 TSRMLS_CC );
                    631: 
                    632:                RETURN_FALSE;
                    633:        }
                    634: 
                    635: 
                    636:        if ( !f_ignore_case ) {
                    637: 
                    638:                /* ASCII optimization: quick check to see if the string might be there
                    639:                 * I realize that 'offset' is 'grapheme count offset' but will work in spite of that
                    640:                */
                    641:                found = (unsigned char *)php_memnstr((char *)haystack, (char *)needle, needle_len, (char *)haystack + haystack_len);
                    642: 
                    643:                /* if it isn't there the we are done */
                    644:                if ( !found ) {
                    645:                        RETURN_FALSE;
                    646:                }
                    647: 
                    648:                /* if it is there, and if the haystack is ascii, we are all done */
                    649:                if ( grapheme_ascii_check(haystack, haystack_len) >= 0 ) {
                    650:                        size_t found_offset = found - haystack;
                    651: 
                    652:                        if (part) {
                    653:                                RETURN_STRINGL(((char *)haystack) , found_offset, 1);
                    654:                        } else {
                    655:                                RETURN_STRINGL(((char *)found), haystack_len - found_offset, 1);
                    656:                        }
                    657:                }
                    658: 
                    659:        }
                    660: 
                    661:        /* need to work in utf16 */
1.1.1.4 ! misho     662:        ret_pos = grapheme_strpos_utf16(haystack, haystack_len, needle, needle_len, 0, &uchar_pos, f_ignore_case, 0 /*last */ TSRMLS_CC );
1.1       misho     663: 
                    664:        if ( ret_pos < 0 ) {
                    665:                RETURN_FALSE;
                    666:        }
                    667: 
                    668:        /* uchar_pos is the 'nth' Unicode character position of the needle */
                    669: 
                    670:        ret_pos = 0;
                    671:        U8_FWD_N(haystack, ret_pos, haystack_len, uchar_pos);
                    672: 
                    673:        if (part) {
                    674:                RETURN_STRINGL(((char *)haystack), ret_pos, 1);
                    675:        }
                    676:        else {
                    677:                RETURN_STRINGL(((char *)haystack) + ret_pos, haystack_len - ret_pos, 1);
                    678:        }
                    679: 
                    680: }
                    681: /* }}} */
                    682: 
                    683: /* {{{ proto string grapheme_strstr(string haystack, string needle[, bool part])
                    684:    Finds first occurrence of a string within another */
                    685: PHP_FUNCTION(grapheme_strstr)
                    686: {
                    687:        strstr_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0 /* f_ignore_case */);
                    688: }
                    689: /* }}} */
                    690: 
                    691: /* {{{ proto string grapheme_stristr(string haystack, string needle[, bool part])
                    692:    Finds first occurrence of a string within another */
                    693: PHP_FUNCTION(grapheme_stristr)
                    694: {
                    695:        strstr_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1 /* f_ignore_case */);
                    696: }
                    697: /* }}} */
                    698: 
                    699: /* {{{ grapheme_extract_charcount_iter - grapheme iterator for grapheme_extract MAXCHARS */
                    700: static inline int32_t
                    701: grapheme_extract_charcount_iter(UBreakIterator *bi, int32_t csize, unsigned char *pstr, int32_t str_len)
                    702: {
                    703:        int pos = 0, prev_pos = 0;
                    704:        int ret_pos = 0, prev_ret_pos = 0;
                    705: 
                    706:        while ( 1 ) {
                    707:                pos = ubrk_next(bi);
                    708: 
                    709:                if ( UBRK_DONE == pos ) {
                    710:                        break;
                    711:                }
                    712: 
                    713:                /* if we are beyond our limit, then the loop is done */
                    714:                if ( pos > csize ) {
                    715:                        break;
                    716:                }
                    717: 
                    718:                /* update our pointer in the original UTF-8 buffer by as many characters
                    719:                   as ubrk_next iterated over */
                    720: 
                    721:                prev_ret_pos = ret_pos;
                    722:                U8_FWD_N(pstr, ret_pos, str_len, pos - prev_pos);
                    723: 
                    724:                if ( prev_ret_pos == ret_pos ) {
                    725:                        /* something wrong - malformed utf8? */
                    726:                        break;
                    727:                }
                    728: 
                    729:                prev_pos = pos;
                    730:        }
                    731: 
                    732:        return ret_pos;
                    733: }
                    734: /* }}} */
                    735: 
                    736: /* {{{ grapheme_extract_bytecount_iter - grapheme iterator for grapheme_extract MAXBYTES */
                    737: static inline int32_t
                    738: grapheme_extract_bytecount_iter(UBreakIterator *bi, int32_t bsize, unsigned char *pstr, int32_t str_len)
                    739: {
                    740:        int pos = 0, prev_pos = 0;
                    741:        int ret_pos = 0, prev_ret_pos = 0;
                    742: 
                    743:        while ( 1 ) {
                    744:                pos = ubrk_next(bi);
                    745: 
                    746:                if ( UBRK_DONE == pos ) {
                    747:                        break;
                    748:                }
                    749: 
                    750:                prev_ret_pos = ret_pos;
                    751:                U8_FWD_N(pstr, ret_pos, str_len, pos - prev_pos);
                    752: 
                    753:                if ( ret_pos > bsize ) {
                    754:                        ret_pos = prev_ret_pos;
                    755:                        break;
                    756:                }
                    757: 
                    758:                if ( prev_ret_pos == ret_pos ) {
                    759:                        /* something wrong - malformed utf8? */
                    760:                        break;
                    761:                }
                    762: 
                    763:                prev_pos = pos;
                    764:        }
                    765: 
                    766:        return ret_pos;
                    767: }
                    768: /* }}} */
                    769: 
                    770: /* {{{ grapheme_extract_count_iter - grapheme iterator for grapheme_extract COUNT */
                    771: static inline int32_t
                    772: grapheme_extract_count_iter(UBreakIterator *bi, int32_t size, unsigned char *pstr, int32_t str_len)
                    773: {
                    774:        int pos = 0, next_pos = 0;
                    775:        int ret_pos = 0;
                    776: 
                    777:        while ( size ) {
                    778:                next_pos = ubrk_next(bi);
                    779: 
                    780:                if ( UBRK_DONE == next_pos ) {
                    781:                        break;
                    782:                }
                    783:                pos = next_pos;
                    784:                size--;
                    785:        }
                    786: 
                    787:        /* pos is one past the last UChar - and represent the number of code units to
                    788:                advance in the utf-8 buffer
                    789:        */
                    790: 
                    791:        U8_FWD_N(pstr, ret_pos, str_len, pos);
                    792: 
                    793:        return ret_pos;
                    794: }
                    795: /* }}} */
                    796: 
                    797: /* {{{ grapheme extract iter function pointer array */
                    798: typedef int32_t (*grapheme_extract_iter)(UBreakIterator * /*bi*/, int32_t /*size*/, unsigned char * /*pstr*/, int32_t /*str_len*/);
                    799: 
                    800: static grapheme_extract_iter grapheme_extract_iters[] = {
                    801:        &grapheme_extract_count_iter,
                    802:        &grapheme_extract_bytecount_iter,
                    803:        &grapheme_extract_charcount_iter,
                    804: };
                    805: /* }}} */
                    806: 
                    807: /* {{{ proto string grapheme_extract(string str, int size[, int extract_type[, int start[, int next]]])
                    808:        Function to extract a sequence of default grapheme clusters */
                    809: PHP_FUNCTION(grapheme_extract)
                    810: {
                    811:        unsigned char *str, *pstr;
                    812:        UChar *ustr;
                    813:        int str_len, ustr_len;
                    814:        long size; /* maximum number of grapheme clusters, bytes, or characters (based on extract_type) to return */
                    815:        long lstart = 0; /* starting position in str in bytes */
                    816:        int32_t start = 0;
                    817:        long extract_type = GRAPHEME_EXTRACT_TYPE_COUNT;
                    818:        UErrorCode status;
                    819:        unsigned char u_break_iterator_buffer[U_BRK_SAFECLONE_BUFFERSIZE];
                    820:        UBreakIterator* bi = NULL;
                    821:        int ret_pos;
                    822:        zval *next = NULL; /* return offset of next part of the string */
                    823: 
                    824:        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|llz", (char **)&str, &str_len, &size, &extract_type, &lstart, &next) == FAILURE) {
                    825: 
                    826:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
                    827:                         "grapheme_extract: unable to parse input param", 0 TSRMLS_CC );
                    828: 
                    829:                RETURN_FALSE;
                    830:        }
                    831: 
                    832:        if ( NULL != next ) {
                    833:                if ( !PZVAL_IS_REF(next) ) {
                    834:                        intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
                    835:                                 "grapheme_extract: 'next' was not passed by reference", 0 TSRMLS_CC );
                    836: 
                    837:                        RETURN_FALSE;
                    838:                }
                    839:                else {
                    840:                        /* initialize next */
1.1.1.3   misho     841:                        zval_dtor(next);
1.1       misho     842:             ZVAL_LONG(next, lstart);
                    843:                }
                    844:        }
                    845: 
                    846:        if ( extract_type < GRAPHEME_EXTRACT_TYPE_MIN || extract_type > GRAPHEME_EXTRACT_TYPE_MAX ) {
                    847: 
                    848:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
                    849:                         "grapheme_extract: unknown extract type param", 0 TSRMLS_CC );
                    850: 
                    851:                RETURN_FALSE;
                    852:        }
                    853: 
                    854:        if ( lstart > INT32_MAX || lstart < 0 || lstart >= str_len ) {
                    855:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_extract: start not contained in string", 0 TSRMLS_CC );
                    856:                RETURN_FALSE;
                    857:        }
                    858: 
                    859:        if ( size > INT32_MAX || size < 0) {
                    860:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_extract: size is invalid", 0 TSRMLS_CC );
                    861:                RETURN_FALSE;
                    862:        }
                    863:        if (size == 0) {
                    864:                RETURN_EMPTY_STRING();
                    865:        }
                    866: 
                    867:        /* we checked that it will fit: */
                    868:        start = (int32_t) lstart;
                    869: 
                    870:        pstr = str + start;
                    871: 
                    872:        /* just in case pstr points in the middle of a character, move forward to the start of the next char */
                    873:        if ( !UTF8_IS_SINGLE(*pstr) && !U8_IS_LEAD(*pstr) ) {
                    874:                unsigned char *str_end = str + str_len;
                    875: 
                    876:                while ( !UTF8_IS_SINGLE(*pstr) && !U8_IS_LEAD(*pstr) ) {
                    877:                        pstr++;
                    878:                        if ( pstr >= str_end ) {
                    879:                                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
                    880:                                                                "grapheme_extract: invalid input string", 0 TSRMLS_CC );
                    881: 
                    882:                                RETURN_FALSE;
                    883:                        }
                    884:                }
                    885:        }
                    886: 
                    887:        str_len -= (pstr - str);
                    888: 
                    889:        /* if the string is all ASCII up to size+1 - or str_len whichever is first - then we are done.
                    890:                (size + 1 because the size-th character might be the beginning of a grapheme cluster)
                    891:         */
                    892: 
                    893:        if ( -1 != grapheme_ascii_check(pstr, size + 1 < str_len ? size + 1 : str_len ) ) {
                    894:         long nsize = ( size < str_len ? size : str_len );
                    895:                if ( NULL != next ) {
                    896:                        ZVAL_LONG(next, start+nsize);
                    897:                }
                    898:                RETURN_STRINGL(((char *)pstr), nsize, 1);
                    899:        }
                    900: 
                    901:        /* convert the strings to UTF-16. */
                    902:        ustr = NULL;
                    903:        ustr_len = 0;
                    904:        status = U_ZERO_ERROR;
                    905:        intl_convert_utf8_to_utf16(&ustr, &ustr_len, (char *)pstr, str_len, &status );
                    906: 
                    907:        if ( U_FAILURE( status ) ) {
                    908:                /* Set global error code. */
                    909:                intl_error_set_code( NULL, status TSRMLS_CC );
                    910: 
                    911:                /* Set error messages. */
                    912:                intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 0 TSRMLS_CC );
                    913: 
                    914:                if ( NULL != ustr )
                    915:                        efree( ustr );
                    916: 
                    917:                RETURN_FALSE;
                    918:        }
                    919: 
                    920:        bi = NULL;
                    921:        status = U_ZERO_ERROR;
                    922:        bi = grapheme_get_break_iterator(u_break_iterator_buffer, &status TSRMLS_CC );
                    923: 
                    924:        ubrk_setText(bi, ustr, ustr_len, &status);
                    925: 
                    926:        /* if the caller put us in the middle of a grapheme, we can't detect it in all cases since we
                    927:                can't back up. So, we will not do anything. */
                    928: 
                    929:        /* now we need to find the end of the chunk the user wants us to return */
                    930: 
                    931:        ret_pos = (*grapheme_extract_iters[extract_type])(bi, size, pstr, str_len);
                    932: 
                    933:        if (ustr) {
                    934:                efree(ustr);
                    935:        }
                    936:        ubrk_close(bi);
                    937: 
                    938:        if ( NULL != next ) {
                    939:                ZVAL_LONG(next, start+ret_pos);
                    940:        }
                    941: 
                    942:        RETURN_STRINGL(((char *)pstr), ret_pos, 1);
                    943: }
                    944: 
                    945: /* }}} */
                    946: 
                    947: /*
                    948:  * Local variables:
                    949:  * tab-width: 4
                    950:  * c-basic-offset: 4
                    951:  * End:
                    952:  * vim600: fdm=marker
                    953:  * vim: noet sw=4 ts=4
                    954:  */
                    955: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>