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

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;
                    116:        int ret_pos, uchar_pos;
                    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 */
                    163:        ret_pos = grapheme_strpos_utf16(haystack, haystack_len, needle, needle_len, offset, &uchar_pos, 0 /* fIgnoreCase */ TSRMLS_CC );
                    164: 
                    165:        if ( ret_pos >= 0 ) {
                    166:                RETURN_LONG(ret_pos + offset);
                    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;
                    183:        int ret_pos, uchar_pos;
                    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 */
                    238:        ret_pos = grapheme_strpos_utf16(haystack, haystack_len, needle, needle_len, offset, &uchar_pos, 1 /* fIgnoreCase */ TSRMLS_CC );
                    239: 
                    240:        if ( ret_pos >= 0 ) {
                    241:                RETURN_LONG(ret_pos + offset);
                    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: 
                    307:        ret_pos = grapheme_strrpos_utf16(haystack, haystack_len, needle, needle_len, offset, 0 /* f_ignore_case */ TSRMLS_CC);
                    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: 
                    385:        ret_pos = grapheme_strrpos_utf16(haystack, haystack_len, needle, needle_len, offset, 1 /* f_ignore_case */ TSRMLS_CC);
                    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 ) {
                    437:                        RETURN_FALSE;
                    438:                }
                    439: 
                    440:                RETURN_STRINGL(((char *)sub_str), sub_str_len, 1);
                    441:        }
                    442: 
                    443:        ustr = NULL;
                    444:        ustr_len = 0;
                    445:        status = U_ZERO_ERROR;
                    446:        intl_convert_utf8_to_utf16(&ustr, &ustr_len, (char *)str, str_len, &status);
                    447: 
                    448:        if ( U_FAILURE( status ) ) {
                    449:                /* Set global error code. */
                    450:                intl_error_set_code( NULL, status TSRMLS_CC );
                    451: 
                    452:                /* Set error messages. */
                    453:                intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 0 TSRMLS_CC );
                    454:                if (ustr) {
                    455:                        efree( ustr );
                    456:                }
                    457:                RETURN_FALSE;
                    458:        }
                    459: 
                    460:        bi = grapheme_get_break_iterator((void*)u_break_iterator_buffer, &status TSRMLS_CC );
                    461: 
                    462:        if( U_FAILURE(status) ) {
                    463:                RETURN_FALSE;
                    464:        }
                    465: 
                    466:        ubrk_setText(bi, ustr, ustr_len,        &status);
                    467: 
                    468:        if ( start < 0 ) {
                    469:                iter_func = ubrk_previous;
                    470:                ubrk_last(bi);
                    471:                iter_val = 1;
                    472:        }
                    473:        else {
                    474:                iter_func = ubrk_next;
                    475:                iter_val = -1;
                    476:        }
                    477: 
                    478:        sub_str_start_pos = 0;
                    479: 
                    480:        while ( start ) {
                    481:                sub_str_start_pos = iter_func(bi);
                    482: 
                    483:                if ( UBRK_DONE == sub_str_start_pos ) {
                    484:                        break;
                    485:                }
                    486: 
                    487:                start += iter_val;
                    488:        }
                    489: 
                    490:        if ( 0 != start || sub_str_start_pos >= ustr_len ) {
                    491: 
                    492:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_substr: start not contained in string", 1 TSRMLS_CC );
                    493: 
                    494:                if (ustr) {
                    495:                        efree(ustr);
                    496:                }
                    497:                ubrk_close(bi);
                    498:                RETURN_FALSE;
                    499:        }
                    500: 
                    501:        if (ZEND_NUM_ARGS() <= 2) {
                    502: 
                    503:                /* no length supplied, return the rest of the string */
                    504: 
                    505:                sub_str = NULL;
                    506:                sub_str_len = 0;
                    507:                status = U_ZERO_ERROR;
                    508:                intl_convert_utf16_to_utf8((char **)&sub_str, &sub_str_len, ustr + sub_str_start_pos, ustr_len - sub_str_start_pos, &status);
                    509: 
                    510:                if (ustr) {
                    511:                        efree( ustr );
                    512:                }
                    513:                ubrk_close( bi );
                    514: 
                    515:                if ( U_FAILURE( status ) ) {
                    516:                        /* Set global error code. */
                    517:                        intl_error_set_code( NULL, status TSRMLS_CC );
                    518: 
                    519:                        /* Set error messages. */
                    520:                        intl_error_set_custom_msg( NULL, "Error converting output string to UTF-8", 0 TSRMLS_CC );
                    521: 
                    522:                        if (sub_str) {
                    523:                                efree( sub_str );
                    524:                        }
                    525: 
                    526:                        RETURN_FALSE;
                    527:                }
                    528: 
                    529:                /* return the allocated string, not a duplicate */
                    530:                RETURN_STRINGL(((char *)sub_str), sub_str_len, 0);
                    531:        }
                    532: 
                    533:        /* find the end point of the string to return */
                    534: 
                    535:        if ( length < 0 ) {
                    536:                iter_func = ubrk_previous;
                    537:                ubrk_last(bi);
                    538:                iter_val = 1;
                    539:        }
                    540:        else {
                    541:                iter_func = ubrk_next;
                    542:                iter_val = -1;
                    543:        }
                    544: 
                    545:        sub_str_end_pos = 0;
                    546: 
                    547:        while ( length ) {
                    548:                sub_str_end_pos = iter_func(bi);
                    549: 
                    550:                if ( UBRK_DONE == sub_str_end_pos ) {
                    551:                        break;
                    552:                }
                    553: 
                    554:                length += iter_val;
                    555:        }
                    556: 
1.1.1.2   misho     557:        if ( UBRK_DONE == sub_str_end_pos) {
                    558:                if(length < 0) {
1.1       misho     559: 
1.1.1.2   misho     560:                        intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_substr: length not contained in string", 1 TSRMLS_CC );
1.1       misho     561: 
1.1.1.2   misho     562:                        efree(ustr);
                    563:                        ubrk_close(bi);
                    564:                        RETURN_FALSE;
                    565:                } else {
                    566:                        sub_str_end_pos = ustr_len;
                    567:                }
1.1       misho     568:        }
                    569: 
                    570:        sub_str = NULL;
                    571:        status = U_ZERO_ERROR;
                    572:        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);
                    573: 
                    574:        efree( ustr );
                    575:        ubrk_close( bi );
                    576: 
                    577:        if ( U_FAILURE( status ) ) {
                    578:                /* Set global error code. */
                    579:                intl_error_set_code( NULL, status TSRMLS_CC );
                    580: 
                    581:                /* Set error messages. */
                    582:                intl_error_set_custom_msg( NULL, "Error converting output string to UTF-8", 0 TSRMLS_CC );
                    583: 
                    584:                if ( NULL != sub_str )
                    585:                        efree( sub_str );
                    586: 
                    587:                RETURN_FALSE;
                    588:        }
                    589: 
                    590:         /* return the allocated string, not a duplicate */
                    591:        RETURN_STRINGL(((char *)sub_str), sub_str_len, 0);
                    592: 
                    593: }
                    594: /* }}} */
                    595: 
                    596: /* {{{ strstr_common_handler */
                    597: static void strstr_common_handler(INTERNAL_FUNCTION_PARAMETERS, int f_ignore_case)
                    598: {
                    599:        unsigned char *haystack, *needle, *found;
                    600:        int haystack_len, needle_len;
                    601:        int ret_pos, uchar_pos;
                    602:        zend_bool part = 0;
                    603: 
                    604:        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|b", (char **)&haystack, &haystack_len, (char **)&needle, &needle_len, &part) == FAILURE) {
                    605: 
                    606:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
                    607:                         "grapheme_strstr: unable to parse input param", 0 TSRMLS_CC );
                    608: 
                    609:                RETURN_FALSE;
                    610:        }
                    611: 
                    612:        if (needle_len == 0) {
                    613: 
                    614:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_strpos: Empty delimiter", 1 TSRMLS_CC );
                    615: 
                    616:                RETURN_FALSE;
                    617:        }
                    618: 
                    619: 
                    620:        if ( !f_ignore_case ) {
                    621: 
                    622:                /* ASCII optimization: quick check to see if the string might be there
                    623:                 * I realize that 'offset' is 'grapheme count offset' but will work in spite of that
                    624:                */
                    625:                found = (unsigned char *)php_memnstr((char *)haystack, (char *)needle, needle_len, (char *)haystack + haystack_len);
                    626: 
                    627:                /* if it isn't there the we are done */
                    628:                if ( !found ) {
                    629:                        RETURN_FALSE;
                    630:                }
                    631: 
                    632:                /* if it is there, and if the haystack is ascii, we are all done */
                    633:                if ( grapheme_ascii_check(haystack, haystack_len) >= 0 ) {
                    634:                        size_t found_offset = found - haystack;
                    635: 
                    636:                        if (part) {
                    637:                                RETURN_STRINGL(((char *)haystack) , found_offset, 1);
                    638:                        } else {
                    639:                                RETURN_STRINGL(((char *)found), haystack_len - found_offset, 1);
                    640:                        }
                    641:                }
                    642: 
                    643:        }
                    644: 
                    645:        /* need to work in utf16 */
                    646:        ret_pos = grapheme_strpos_utf16(haystack, haystack_len, needle, needle_len, 0, &uchar_pos, f_ignore_case TSRMLS_CC );
                    647: 
                    648:        if ( ret_pos < 0 ) {
                    649:                RETURN_FALSE;
                    650:        }
                    651: 
                    652:        /* uchar_pos is the 'nth' Unicode character position of the needle */
                    653: 
                    654:        ret_pos = 0;
                    655:        U8_FWD_N(haystack, ret_pos, haystack_len, uchar_pos);
                    656: 
                    657:        if (part) {
                    658:                RETURN_STRINGL(((char *)haystack), ret_pos, 1);
                    659:        }
                    660:        else {
                    661:                RETURN_STRINGL(((char *)haystack) + ret_pos, haystack_len - ret_pos, 1);
                    662:        }
                    663: 
                    664: }
                    665: /* }}} */
                    666: 
                    667: /* {{{ proto string grapheme_strstr(string haystack, string needle[, bool part])
                    668:    Finds first occurrence of a string within another */
                    669: PHP_FUNCTION(grapheme_strstr)
                    670: {
                    671:        strstr_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0 /* f_ignore_case */);
                    672: }
                    673: /* }}} */
                    674: 
                    675: /* {{{ proto string grapheme_stristr(string haystack, string needle[, bool part])
                    676:    Finds first occurrence of a string within another */
                    677: PHP_FUNCTION(grapheme_stristr)
                    678: {
                    679:        strstr_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1 /* f_ignore_case */);
                    680: }
                    681: /* }}} */
                    682: 
                    683: /* {{{ grapheme_extract_charcount_iter - grapheme iterator for grapheme_extract MAXCHARS */
                    684: static inline int32_t
                    685: grapheme_extract_charcount_iter(UBreakIterator *bi, int32_t csize, unsigned char *pstr, int32_t str_len)
                    686: {
                    687:        int pos = 0, prev_pos = 0;
                    688:        int ret_pos = 0, prev_ret_pos = 0;
                    689: 
                    690:        while ( 1 ) {
                    691:                pos = ubrk_next(bi);
                    692: 
                    693:                if ( UBRK_DONE == pos ) {
                    694:                        break;
                    695:                }
                    696: 
                    697:                /* if we are beyond our limit, then the loop is done */
                    698:                if ( pos > csize ) {
                    699:                        break;
                    700:                }
                    701: 
                    702:                /* update our pointer in the original UTF-8 buffer by as many characters
                    703:                   as ubrk_next iterated over */
                    704: 
                    705:                prev_ret_pos = ret_pos;
                    706:                U8_FWD_N(pstr, ret_pos, str_len, pos - prev_pos);
                    707: 
                    708:                if ( prev_ret_pos == ret_pos ) {
                    709:                        /* something wrong - malformed utf8? */
                    710:                        break;
                    711:                }
                    712: 
                    713:                prev_pos = pos;
                    714:        }
                    715: 
                    716:        return ret_pos;
                    717: }
                    718: /* }}} */
                    719: 
                    720: /* {{{ grapheme_extract_bytecount_iter - grapheme iterator for grapheme_extract MAXBYTES */
                    721: static inline int32_t
                    722: grapheme_extract_bytecount_iter(UBreakIterator *bi, int32_t bsize, unsigned char *pstr, int32_t str_len)
                    723: {
                    724:        int pos = 0, prev_pos = 0;
                    725:        int ret_pos = 0, prev_ret_pos = 0;
                    726: 
                    727:        while ( 1 ) {
                    728:                pos = ubrk_next(bi);
                    729: 
                    730:                if ( UBRK_DONE == pos ) {
                    731:                        break;
                    732:                }
                    733: 
                    734:                prev_ret_pos = ret_pos;
                    735:                U8_FWD_N(pstr, ret_pos, str_len, pos - prev_pos);
                    736: 
                    737:                if ( ret_pos > bsize ) {
                    738:                        ret_pos = prev_ret_pos;
                    739:                        break;
                    740:                }
                    741: 
                    742:                if ( prev_ret_pos == ret_pos ) {
                    743:                        /* something wrong - malformed utf8? */
                    744:                        break;
                    745:                }
                    746: 
                    747:                prev_pos = pos;
                    748:        }
                    749: 
                    750:        return ret_pos;
                    751: }
                    752: /* }}} */
                    753: 
                    754: /* {{{ grapheme_extract_count_iter - grapheme iterator for grapheme_extract COUNT */
                    755: static inline int32_t
                    756: grapheme_extract_count_iter(UBreakIterator *bi, int32_t size, unsigned char *pstr, int32_t str_len)
                    757: {
                    758:        int pos = 0, next_pos = 0;
                    759:        int ret_pos = 0;
                    760: 
                    761:        while ( size ) {
                    762:                next_pos = ubrk_next(bi);
                    763: 
                    764:                if ( UBRK_DONE == next_pos ) {
                    765:                        break;
                    766:                }
                    767:                pos = next_pos;
                    768:                size--;
                    769:        }
                    770: 
                    771:        /* pos is one past the last UChar - and represent the number of code units to
                    772:                advance in the utf-8 buffer
                    773:        */
                    774: 
                    775:        U8_FWD_N(pstr, ret_pos, str_len, pos);
                    776: 
                    777:        return ret_pos;
                    778: }
                    779: /* }}} */
                    780: 
                    781: /* {{{ grapheme extract iter function pointer array */
                    782: typedef int32_t (*grapheme_extract_iter)(UBreakIterator * /*bi*/, int32_t /*size*/, unsigned char * /*pstr*/, int32_t /*str_len*/);
                    783: 
                    784: static grapheme_extract_iter grapheme_extract_iters[] = {
                    785:        &grapheme_extract_count_iter,
                    786:        &grapheme_extract_bytecount_iter,
                    787:        &grapheme_extract_charcount_iter,
                    788: };
                    789: /* }}} */
                    790: 
                    791: /* {{{ proto string grapheme_extract(string str, int size[, int extract_type[, int start[, int next]]])
                    792:        Function to extract a sequence of default grapheme clusters */
                    793: PHP_FUNCTION(grapheme_extract)
                    794: {
                    795:        unsigned char *str, *pstr;
                    796:        UChar *ustr;
                    797:        int str_len, ustr_len;
                    798:        long size; /* maximum number of grapheme clusters, bytes, or characters (based on extract_type) to return */
                    799:        long lstart = 0; /* starting position in str in bytes */
                    800:        int32_t start = 0;
                    801:        long extract_type = GRAPHEME_EXTRACT_TYPE_COUNT;
                    802:        UErrorCode status;
                    803:        unsigned char u_break_iterator_buffer[U_BRK_SAFECLONE_BUFFERSIZE];
                    804:        UBreakIterator* bi = NULL;
                    805:        int ret_pos;
                    806:        zval *next = NULL; /* return offset of next part of the string */
                    807: 
                    808:        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|llz", (char **)&str, &str_len, &size, &extract_type, &lstart, &next) == FAILURE) {
                    809: 
                    810:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
                    811:                         "grapheme_extract: unable to parse input param", 0 TSRMLS_CC );
                    812: 
                    813:                RETURN_FALSE;
                    814:        }
                    815: 
                    816:        if ( NULL != next ) {
                    817:                if ( !PZVAL_IS_REF(next) ) {
                    818:                        intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
                    819:                                 "grapheme_extract: 'next' was not passed by reference", 0 TSRMLS_CC );
                    820: 
                    821:                        RETURN_FALSE;
                    822:                }
                    823:                else {
                    824:                        /* initialize next */
1.1.1.3 ! misho     825:                        zval_dtor(next);
1.1       misho     826:             ZVAL_LONG(next, lstart);
                    827:                }
                    828:        }
                    829: 
                    830:        if ( extract_type < GRAPHEME_EXTRACT_TYPE_MIN || extract_type > GRAPHEME_EXTRACT_TYPE_MAX ) {
                    831: 
                    832:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
                    833:                         "grapheme_extract: unknown extract type param", 0 TSRMLS_CC );
                    834: 
                    835:                RETURN_FALSE;
                    836:        }
                    837: 
                    838:        if ( lstart > INT32_MAX || lstart < 0 || lstart >= str_len ) {
                    839:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_extract: start not contained in string", 0 TSRMLS_CC );
                    840:                RETURN_FALSE;
                    841:        }
                    842: 
                    843:        if ( size > INT32_MAX || size < 0) {
                    844:                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_extract: size is invalid", 0 TSRMLS_CC );
                    845:                RETURN_FALSE;
                    846:        }
                    847:        if (size == 0) {
                    848:                RETURN_EMPTY_STRING();
                    849:        }
                    850: 
                    851:        /* we checked that it will fit: */
                    852:        start = (int32_t) lstart;
                    853: 
                    854:        pstr = str + start;
                    855: 
                    856:        /* just in case pstr points in the middle of a character, move forward to the start of the next char */
                    857:        if ( !UTF8_IS_SINGLE(*pstr) && !U8_IS_LEAD(*pstr) ) {
                    858:                unsigned char *str_end = str + str_len;
                    859: 
                    860:                while ( !UTF8_IS_SINGLE(*pstr) && !U8_IS_LEAD(*pstr) ) {
                    861:                        pstr++;
                    862:                        if ( pstr >= str_end ) {
                    863:                                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
                    864:                                                                "grapheme_extract: invalid input string", 0 TSRMLS_CC );
                    865: 
                    866:                                RETURN_FALSE;
                    867:                        }
                    868:                }
                    869:        }
                    870: 
                    871:        str_len -= (pstr - str);
                    872: 
                    873:        /* if the string is all ASCII up to size+1 - or str_len whichever is first - then we are done.
                    874:                (size + 1 because the size-th character might be the beginning of a grapheme cluster)
                    875:         */
                    876: 
                    877:        if ( -1 != grapheme_ascii_check(pstr, size + 1 < str_len ? size + 1 : str_len ) ) {
                    878:         long nsize = ( size < str_len ? size : str_len );
                    879:                if ( NULL != next ) {
                    880:                        ZVAL_LONG(next, start+nsize);
                    881:                }
                    882:                RETURN_STRINGL(((char *)pstr), nsize, 1);
                    883:        }
                    884: 
                    885:        /* convert the strings to UTF-16. */
                    886:        ustr = NULL;
                    887:        ustr_len = 0;
                    888:        status = U_ZERO_ERROR;
                    889:        intl_convert_utf8_to_utf16(&ustr, &ustr_len, (char *)pstr, str_len, &status );
                    890: 
                    891:        if ( U_FAILURE( status ) ) {
                    892:                /* Set global error code. */
                    893:                intl_error_set_code( NULL, status TSRMLS_CC );
                    894: 
                    895:                /* Set error messages. */
                    896:                intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 0 TSRMLS_CC );
                    897: 
                    898:                if ( NULL != ustr )
                    899:                        efree( ustr );
                    900: 
                    901:                RETURN_FALSE;
                    902:        }
                    903: 
                    904:        bi = NULL;
                    905:        status = U_ZERO_ERROR;
                    906:        bi = grapheme_get_break_iterator(u_break_iterator_buffer, &status TSRMLS_CC );
                    907: 
                    908:        ubrk_setText(bi, ustr, ustr_len, &status);
                    909: 
                    910:        /* if the caller put us in the middle of a grapheme, we can't detect it in all cases since we
                    911:                can't back up. So, we will not do anything. */
                    912: 
                    913:        /* now we need to find the end of the chunk the user wants us to return */
                    914: 
                    915:        ret_pos = (*grapheme_extract_iters[extract_type])(bi, size, pstr, str_len);
                    916: 
                    917:        if (ustr) {
                    918:                efree(ustr);
                    919:        }
                    920:        ubrk_close(bi);
                    921: 
                    922:        if ( NULL != next ) {
                    923:                ZVAL_LONG(next, start+ret_pos);
                    924:        }
                    925: 
                    926:        RETURN_STRINGL(((char *)pstr), ret_pos, 1);
                    927: }
                    928: 
                    929: /* }}} */
                    930: 
                    931: /*
                    932:  * Local variables:
                    933:  * tab-width: 4
                    934:  * c-basic-offset: 4
                    935:  * End:
                    936:  * vim600: fdm=marker
                    937:  * vim: noet sw=4 ts=4
                    938:  */
                    939: 

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