PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LTP GCOV extension - code coverage report
Current view: directory - standard - string.c
Test: PHP Code Coverage
Date: 2009-11-23 Instrumented lines: 3703
Code covered: 80.8 % Executed lines: 2993
Legend: not executed executed

       1                 : /*
       2                 :    +----------------------------------------------------------------------+
       3                 :    | PHP Version 6                                                        |
       4                 :    +----------------------------------------------------------------------+
       5                 :    | Copyright (c) 1997-2009 The PHP Group                                |
       6                 :    +----------------------------------------------------------------------+
       7                 :    | This source file is subject to version 3.01 of the PHP license,      |
       8                 :    | that is bundled with this package in the file LICENSE, and is        |
       9                 :    | available through the world-wide-web at the following url:           |
      10                 :    | http://www.php.net/license/3_01.txt                                  |
      11                 :    | If you did not receive a copy of the PHP license and are unable to   |
      12                 :    | obtain it through the world-wide-web, please send a note to          |
      13                 :    | license@php.net so we can mail you a copy immediately.               |
      14                 :    +----------------------------------------------------------------------+
      15                 :    | Authors: Rasmus Lerdorf <rasmus@php.net>                             |
      16                 :    |          Stig Sæther Bakken <ssb@php.net>                            |
      17                 :    |          Zeev Suraski <zeev@zend.com>                                |
      18                 :    +----------------------------------------------------------------------+
      19                 :  */
      20                 : 
      21                 : /* $Id: string.c 290368 2009-11-06 09:02:52Z scottmac $ */
      22                 : 
      23                 : /* Synced with php 3.0 revision 1.193 1999-06-16 [ssb] */
      24                 : 
      25                 : #include <stdio.h>
      26                 : #include "php.h"
      27                 : #include "php_rand.h"
      28                 : #include "php_string.h"
      29                 : #include "php_variables.h"
      30                 : #ifdef HAVE_LOCALE_H
      31                 : # include <locale.h>
      32                 : #endif
      33                 : #ifdef HAVE_LANGINFO_H
      34                 : # include <langinfo.h>
      35                 : #endif
      36                 : #ifdef HAVE_MONETARY_H
      37                 : # include <monetary.h>
      38                 : #endif
      39                 : #ifdef HAVE_LIBINTL
      40                 : # include <libintl.h> /* For LC_MESSAGES */
      41                 : #endif
      42                 : 
      43                 : #include "scanf.h"
      44                 : #include "zend_API.h"
      45                 : #include "zend_execute.h"
      46                 : #include "php_globals.h"
      47                 : #include "basic_functions.h"
      48                 : #include "php_smart_str.h"
      49                 : #ifdef ZTS
      50                 : #include "TSRM.h"
      51                 : #endif
      52                 : 
      53                 : #include "unicode/uchar.h"
      54                 : #include "unicode/ubrk.h"
      55                 : 
      56                 : /* For str_getcsv() support */
      57                 : #include "ext/standard/file.h"
      58                 : 
      59                 : #define STR_PAD_LEFT                    0
      60                 : #define STR_PAD_RIGHT                   1
      61                 : #define STR_PAD_BOTH                    2
      62                 : #define PHP_PATHINFO_DIRNAME    1
      63                 : #define PHP_PATHINFO_BASENAME   2
      64                 : #define PHP_PATHINFO_EXTENSION  4
      65                 : #define PHP_PATHINFO_FILENAME   8
      66                 : #define PHP_PATHINFO_ALL        (PHP_PATHINFO_DIRNAME | PHP_PATHINFO_BASENAME | PHP_PATHINFO_EXTENSION | PHP_PATHINFO_FILENAME)
      67                 : 
      68                 : #define STR_STRSPN                              0
      69                 : #define STR_STRCSPN                             1
      70                 : 
      71                 : /* {{{ register_string_constants
      72                 :  */
      73                 : void register_string_constants(INIT_FUNC_ARGS)
      74           17007 : {
      75           17007 :         REGISTER_LONG_CONSTANT("STR_PAD_LEFT", STR_PAD_LEFT, CONST_CS | CONST_PERSISTENT);
      76           17007 :         REGISTER_LONG_CONSTANT("STR_PAD_RIGHT", STR_PAD_RIGHT, CONST_CS | CONST_PERSISTENT);
      77           17007 :         REGISTER_LONG_CONSTANT("STR_PAD_BOTH", STR_PAD_BOTH, CONST_CS | CONST_PERSISTENT);
      78           17007 :         REGISTER_LONG_CONSTANT("PATHINFO_DIRNAME", PHP_PATHINFO_DIRNAME, CONST_CS | CONST_PERSISTENT);
      79           17007 :         REGISTER_LONG_CONSTANT("PATHINFO_BASENAME", PHP_PATHINFO_BASENAME, CONST_CS | CONST_PERSISTENT);
      80           17007 :         REGISTER_LONG_CONSTANT("PATHINFO_EXTENSION", PHP_PATHINFO_EXTENSION, CONST_CS | CONST_PERSISTENT);
      81           17007 :         REGISTER_LONG_CONSTANT("PATHINFO_FILENAME", PHP_PATHINFO_FILENAME, CONST_CS | CONST_PERSISTENT);
      82                 : 
      83                 : #ifdef HAVE_LOCALECONV
      84                 :         /* If last members of struct lconv equal CHAR_MAX, no grouping is done */
      85                 : 
      86                 : /* This is bad, but since we are going to be hardcoding in the POSIX stuff anyway... */
      87                 : # ifndef HAVE_LIMITS_H
      88                 : # define CHAR_MAX 127
      89                 : # endif
      90                 : 
      91           17007 :         REGISTER_LONG_CONSTANT("CHAR_MAX", CHAR_MAX, CONST_CS | CONST_PERSISTENT);
      92                 : #endif
      93                 : 
      94                 : #ifdef HAVE_LOCALE_H
      95           17007 :         REGISTER_LONG_CONSTANT("LC_CTYPE", LC_CTYPE, CONST_CS | CONST_PERSISTENT);
      96           17007 :         REGISTER_LONG_CONSTANT("LC_NUMERIC", LC_NUMERIC, CONST_CS | CONST_PERSISTENT);
      97           17007 :         REGISTER_LONG_CONSTANT("LC_TIME", LC_TIME, CONST_CS | CONST_PERSISTENT);
      98           17007 :         REGISTER_LONG_CONSTANT("LC_COLLATE", LC_COLLATE, CONST_CS | CONST_PERSISTENT);
      99           17007 :         REGISTER_LONG_CONSTANT("LC_MONETARY", LC_MONETARY, CONST_CS | CONST_PERSISTENT);
     100           17007 :         REGISTER_LONG_CONSTANT("LC_ALL", LC_ALL, CONST_CS | CONST_PERSISTENT);
     101                 : # ifdef LC_MESSAGES
     102           17007 :         REGISTER_LONG_CONSTANT("LC_MESSAGES", LC_MESSAGES, CONST_CS | CONST_PERSISTENT);
     103                 : # endif
     104                 : #endif
     105                 : 
     106           17007 : }
     107                 : /* }}} */
     108                 : 
     109                 : int php_tag_find(char *tag, int len, char *set);
     110                 : static void php_ucwords(char *str, int str_len);
     111                 : static UChar* php_u_strtr_array(UChar *str, int slen, HashTable *hash, int minlen, int maxlen, int *outlen TSRMLS_DC);
     112                 : 
     113                 : /* this is read-only, so it's ok */
     114                 : static char hexconvtab[] = "0123456789abcdef";
     115                 : 
     116                 : /* localeconv mutex */
     117                 : #ifdef ZTS
     118                 : static MUTEX_T locale_mutex = NULL;
     119                 : #endif
     120                 : 
     121                 : /* {{{ php_bin2hex
     122                 :  */
     123                 : static char *php_bin2hex(const unsigned char *old, const size_t oldlen, size_t *newlen)
     124            4090 : {
     125            4090 :         register unsigned char *result = NULL;
     126                 :         size_t i, j;
     127                 : 
     128            4090 :         result = (unsigned char *) safe_emalloc(oldlen * 2, sizeof(char), 1);
     129                 : 
     130           47490 :         for (i = j = 0; i < oldlen; i++) {
     131           43400 :                 result[j++] = hexconvtab[old[i] >> 4];
     132           43400 :                 result[j++] = hexconvtab[old[i] & 15];
     133                 :         }
     134            4090 :         result[j] = '\0';
     135                 : 
     136            4090 :         if (newlen)
     137            4090 :                 *newlen = oldlen * 2 * sizeof(char);
     138                 : 
     139            4090 :         return (char*)result;
     140                 : }
     141                 : /* }}} */
     142                 : 
     143                 : #ifdef HAVE_LOCALECONV
     144                 : /* {{{ localeconv_r
     145                 :  * glibc's localeconv is not reentrant, so lets make it so ... sorta */
     146                 : PHPAPI struct lconv *localeconv_r(struct lconv *out)
     147               1 : {
     148                 :         struct lconv *res;
     149                 : 
     150                 : # ifdef ZTS
     151                 :         tsrm_mutex_lock( locale_mutex );
     152                 : # endif
     153                 : 
     154                 :         /* localeconv doesn't return an error condition */
     155               1 :         res = localeconv();
     156                 : 
     157               1 :         *out = *res;
     158                 : 
     159                 : # ifdef ZTS
     160                 :         tsrm_mutex_unlock( locale_mutex );
     161                 : # endif
     162                 : 
     163               1 :         return out;
     164                 : }
     165                 : /* }}} */
     166                 : 
     167                 : # ifdef ZTS
     168                 : /* {{{ PHP_MINIT_FUNCTION
     169                 :  */
     170                 : PHP_MINIT_FUNCTION(localeconv)
     171                 : {
     172                 :         locale_mutex = tsrm_mutex_alloc();
     173                 :         return SUCCESS;
     174                 : }
     175                 : /* }}} */
     176                 : 
     177                 : /* {{{ PHP_MSHUTDOWN_FUNCTION
     178                 :  */
     179                 : PHP_MSHUTDOWN_FUNCTION(localeconv)
     180                 : {
     181                 :         tsrm_mutex_free( locale_mutex );
     182                 :         locale_mutex = NULL;
     183                 :         return SUCCESS;
     184                 : }
     185                 : /* }}} */
     186                 : # endif
     187                 : #endif
     188                 : 
     189                 : /* {{{ proto string bin2hex(string data) U
     190                 :    Converts the binary representation of data to hex */
     191                 : PHP_FUNCTION(bin2hex)
     192            8281 : {
     193                 :         unsigned char *data;
     194                 :         int data_len;
     195                 :         char *result;
     196                 :         size_t newlen;
     197                 : 
     198            8281 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "S", &data, &data_len) == FAILURE) {
     199            4191 :                 return;
     200                 :         }
     201                 : 
     202            4090 :         result = php_bin2hex(data, data_len, &newlen);
     203                 : 
     204            4090 :         if (!result) {
     205               0 :                 RETURN_FALSE;
     206                 :         }
     207                 : 
     208            4090 :         RETVAL_ASCII_STRINGL(result, newlen, ZSTR_AUTOFREE);
     209                 : }
     210                 : /* }}} */
     211                 : 
     212                 : static void php_spn_common_handler(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
     213           13045 : {
     214                 :         void *s1, *s2;
     215                 :         int len1, len2;
     216                 :         zend_uchar type1, type2;
     217           13045 :         long start = 0, len = 0; /* For UNICODE, these are codepoint units */
     218                 : 
     219           13045 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "TT|ll",
     220                 :                                                           &s1, &len1, &type1, &s2, &len2, &type2,
     221                 :                                                           &start, &len) == FAILURE) {
     222             144 :                 return;
     223                 :         }
     224                 : 
     225           12901 :         if (ZEND_NUM_ARGS() < 4) {
     226            4086 :                 len = len1;
     227                 :         }
     228                 : 
     229                 :         /* look at substr() function for more information */
     230                 : 
     231           12901 :         if (start < 0) {
     232            4336 :                 start += len1;
     233            4336 :                 if (start < 0) {
     234            2306 :                         start = 0;
     235                 :                 }
     236            8565 :         } else if (start > len1) {
     237            2662 :                 RETURN_FALSE;
     238                 :         }
     239                 : 
     240           10239 :         if (len < 0) {
     241            2227 :                 len += (len1 - start);
     242            2227 :                 if (len < 0) {
     243            1286 :                         len = 0;
     244                 :                 }
     245                 :         }
     246                 : 
     247           10239 :         if (len > len1 - start) {
     248            3429 :                 len = len1 - start;
     249                 :         }
     250                 : 
     251           10239 :         if(len == 0) {
     252            3625 :                 RETURN_LONG(0);
     253                 :         }
     254                 : 
     255            6614 :         if (type1 == IS_UNICODE) {
     256                 :                 UChar *u_start, *u_end;
     257            6614 :                 int32_t i = 0;
     258                 : 
     259            6614 :                 U16_FWD_N((UChar*)s1, i, len1, start);
     260            6614 :                 u_start = (UChar *)s1 + i;
     261            6614 :                 U16_FWD_N((UChar *)s1, i, len1, len);
     262            6614 :                 u_end = (UChar *)s1 + i;
     263                 : 
     264            6614 :                 if (behavior == STR_STRSPN) {
     265            3381 :                         RETURN_LONG(php_u_strspn(u_start /*str1_start*/,
     266                 :                                                                          (UChar *)s2 /*str2_start*/,
     267                 :                                                                          u_end /*str1_end*/,
     268                 :                                                                          (UChar *)s2 + len2 /*str2_end*/));
     269            3233 :                 } else if (behavior == STR_STRCSPN) {
     270            3233 :                         RETURN_LONG(php_u_strcspn(u_start /*str1_start*/,
     271                 :                                                                           (UChar *)s2 /*str2_start*/,
     272                 :                                                                           u_end /*str1_end*/,
     273                 :                                                                           (UChar *)s2 + len2 /*str2_end*/));
     274                 :                 }
     275                 :         } else {
     276               0 :                 if (behavior == STR_STRSPN) {
     277               0 :                         RETURN_LONG(php_strspn((char *)s1 + start /*str1_start*/,
     278                 :                                                                    (char *)s2 /*str2_start*/,
     279                 :                                                                    (char *)s1 + start + len /*str1_end*/,
     280                 :                                                                    (char *)s2 + len2 /*str2_end*/));
     281               0 :                 } else if (behavior == STR_STRCSPN) {
     282               0 :                         RETURN_LONG(php_strcspn((char *)s1 + start /*str1_start*/,
     283                 :                                                                         (char *)s2 /*str2_start*/,
     284                 :                                                                         (char *)s1 + start + len /*str1_end*/,
     285                 :                                                                         (char *)s2 + len2 /*str2_end*/));
     286                 :                 }
     287                 :         }
     288                 : 
     289                 : }
     290                 : /* }}} */
     291                 : 
     292                 : /* {{{ proto int strspn(string str, string mask [, start [, len]]) U
     293                 :    Finds length of initial segment consisting entirely of characters found in mask. If start or/and length is provided works like strspn(substr($s,$start,$len),$good_chars) */
     294                 : PHP_FUNCTION(strspn)
     295            6718 : {
     296            6718 :         php_spn_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, STR_STRSPN);
     297            6718 : }
     298                 : /* }}} */
     299                 : 
     300                 : /* {{{ proto int strcspn(string str, string mask [, start [, len]]) U
     301                 :    Finds length of initial segment consisting entirely of characters not found in mask. If start or/and length is provide works like strcspn(substr($s,$start,$len),$bad_chars) */
     302                 : PHP_FUNCTION(strcspn)
     303            6327 : {
     304            6327 :         php_spn_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, STR_STRCSPN);
     305            6327 : }
     306                 : /* }}} */
     307                 : 
     308                 : /* {{{ PHP_MINIT_FUNCTION(nl_langinfo) */
     309                 : #if HAVE_NL_LANGINFO
     310                 : PHP_MINIT_FUNCTION(nl_langinfo)
     311           17007 : {
     312                 : #define REGISTER_NL_LANGINFO_CONSTANT(x)        REGISTER_LONG_CONSTANT(#x, x, CONST_CS | CONST_PERSISTENT)
     313                 : #ifdef ABDAY_1
     314           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ABDAY_1);
     315           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ABDAY_2);
     316           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ABDAY_3);
     317           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ABDAY_4);
     318           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ABDAY_5);
     319           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ABDAY_6);
     320           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ABDAY_7);
     321                 : #endif
     322                 : #ifdef DAY_1
     323           17007 :         REGISTER_NL_LANGINFO_CONSTANT(DAY_1);
     324           17007 :         REGISTER_NL_LANGINFO_CONSTANT(DAY_2);
     325           17007 :         REGISTER_NL_LANGINFO_CONSTANT(DAY_3);
     326           17007 :         REGISTER_NL_LANGINFO_CONSTANT(DAY_4);
     327           17007 :         REGISTER_NL_LANGINFO_CONSTANT(DAY_5);
     328           17007 :         REGISTER_NL_LANGINFO_CONSTANT(DAY_6);
     329           17007 :         REGISTER_NL_LANGINFO_CONSTANT(DAY_7);
     330                 : #endif
     331                 : #ifdef ABMON_1
     332           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ABMON_1);
     333           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ABMON_2);
     334           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ABMON_3);
     335           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ABMON_4);
     336           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ABMON_5);
     337           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ABMON_6);
     338           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ABMON_7);
     339           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ABMON_8);
     340           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ABMON_9);
     341           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ABMON_10);
     342           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ABMON_11);
     343           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ABMON_12);
     344                 : #endif
     345                 : #ifdef MON_1
     346           17007 :         REGISTER_NL_LANGINFO_CONSTANT(MON_1);
     347           17007 :         REGISTER_NL_LANGINFO_CONSTANT(MON_2);
     348           17007 :         REGISTER_NL_LANGINFO_CONSTANT(MON_3);
     349           17007 :         REGISTER_NL_LANGINFO_CONSTANT(MON_4);
     350           17007 :         REGISTER_NL_LANGINFO_CONSTANT(MON_5);
     351           17007 :         REGISTER_NL_LANGINFO_CONSTANT(MON_6);
     352           17007 :         REGISTER_NL_LANGINFO_CONSTANT(MON_7);
     353           17007 :         REGISTER_NL_LANGINFO_CONSTANT(MON_8);
     354           17007 :         REGISTER_NL_LANGINFO_CONSTANT(MON_9);
     355           17007 :         REGISTER_NL_LANGINFO_CONSTANT(MON_10);
     356           17007 :         REGISTER_NL_LANGINFO_CONSTANT(MON_11);
     357           17007 :         REGISTER_NL_LANGINFO_CONSTANT(MON_12);
     358                 : #endif
     359                 : #ifdef AM_STR
     360           17007 :         REGISTER_NL_LANGINFO_CONSTANT(AM_STR);
     361                 : #endif
     362                 : #ifdef PM_STR
     363           17007 :         REGISTER_NL_LANGINFO_CONSTANT(PM_STR);
     364                 : #endif
     365                 : #ifdef D_T_FMT
     366           17007 :         REGISTER_NL_LANGINFO_CONSTANT(D_T_FMT);
     367                 : #endif
     368                 : #ifdef D_FMT
     369           17007 :         REGISTER_NL_LANGINFO_CONSTANT(D_FMT);
     370                 : #endif
     371                 : #ifdef T_FMT
     372           17007 :         REGISTER_NL_LANGINFO_CONSTANT(T_FMT);
     373                 : #endif
     374                 : #ifdef T_FMT_AMPM
     375           17007 :         REGISTER_NL_LANGINFO_CONSTANT(T_FMT_AMPM);
     376                 : #endif
     377                 : #ifdef ERA
     378           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ERA);
     379                 : #endif
     380                 : #ifdef ERA_YEAR
     381                 :         REGISTER_NL_LANGINFO_CONSTANT(ERA_YEAR);
     382                 : #endif
     383                 : #ifdef ERA_D_T_FMT
     384           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ERA_D_T_FMT);
     385                 : #endif
     386                 : #ifdef ERA_D_FMT
     387           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ERA_D_FMT);
     388                 : #endif
     389                 : #ifdef ERA_T_FMT
     390           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ERA_T_FMT);
     391                 : #endif
     392                 : #ifdef ALT_DIGITS
     393           17007 :         REGISTER_NL_LANGINFO_CONSTANT(ALT_DIGITS);
     394                 : #endif
     395                 : #ifdef INT_CURR_SYMBOL
     396                 :         REGISTER_NL_LANGINFO_CONSTANT(INT_CURR_SYMBOL);
     397                 : #endif
     398                 : #ifdef CURRENCY_SYMBOL
     399                 :         REGISTER_NL_LANGINFO_CONSTANT(CURRENCY_SYMBOL);
     400                 : #endif
     401                 : #ifdef CRNCYSTR
     402           17007 :         REGISTER_NL_LANGINFO_CONSTANT(CRNCYSTR);
     403                 : #endif
     404                 : #ifdef MON_DECIMAL_POINT
     405                 :         REGISTER_NL_LANGINFO_CONSTANT(MON_DECIMAL_POINT);
     406                 : #endif
     407                 : #ifdef MON_THOUSANDS_SEP
     408                 :         REGISTER_NL_LANGINFO_CONSTANT(MON_THOUSANDS_SEP);
     409                 : #endif
     410                 : #ifdef MON_GROUPING
     411                 :         REGISTER_NL_LANGINFO_CONSTANT(MON_GROUPING);
     412                 : #endif
     413                 : #ifdef POSITIVE_SIGN
     414                 :         REGISTER_NL_LANGINFO_CONSTANT(POSITIVE_SIGN);
     415                 : #endif
     416                 : #ifdef NEGATIVE_SIGN
     417                 :         REGISTER_NL_LANGINFO_CONSTANT(NEGATIVE_SIGN);
     418                 : #endif
     419                 : #ifdef INT_FRAC_DIGITS
     420                 :         REGISTER_NL_LANGINFO_CONSTANT(INT_FRAC_DIGITS);
     421                 : #endif
     422                 : #ifdef FRAC_DIGITS
     423                 :         REGISTER_NL_LANGINFO_CONSTANT(FRAC_DIGITS);
     424                 : #endif
     425                 : #ifdef P_CS_PRECEDES
     426                 :         REGISTER_NL_LANGINFO_CONSTANT(P_CS_PRECEDES);
     427                 : #endif
     428                 : #ifdef P_SEP_BY_SPACE
     429                 :         REGISTER_NL_LANGINFO_CONSTANT(P_SEP_BY_SPACE);
     430                 : #endif
     431                 : #ifdef N_CS_PRECEDES
     432                 :         REGISTER_NL_LANGINFO_CONSTANT(N_CS_PRECEDES);
     433                 : #endif
     434                 : #ifdef N_SEP_BY_SPACE
     435                 :         REGISTER_NL_LANGINFO_CONSTANT(N_SEP_BY_SPACE);
     436                 : #endif
     437                 : #ifdef P_SIGN_POSN
     438                 :         REGISTER_NL_LANGINFO_CONSTANT(P_SIGN_POSN);
     439                 : #endif
     440                 : #ifdef N_SIGN_POSN
     441                 :         REGISTER_NL_LANGINFO_CONSTANT(N_SIGN_POSN);
     442                 : #endif
     443                 : #ifdef DECIMAL_POINT
     444                 :         REGISTER_NL_LANGINFO_CONSTANT(DECIMAL_POINT);
     445                 : #endif
     446                 : #ifdef RADIXCHAR
     447           17007 :         REGISTER_NL_LANGINFO_CONSTANT(RADIXCHAR);
     448                 : #endif
     449                 : #ifdef THOUSANDS_SEP
     450                 :         REGISTER_NL_LANGINFO_CONSTANT(THOUSANDS_SEP);
     451                 : #endif
     452                 : #ifdef THOUSEP
     453           17007 :         REGISTER_NL_LANGINFO_CONSTANT(THOUSEP);
     454                 : #endif
     455                 : #ifdef GROUPING
     456                 :         REGISTER_NL_LANGINFO_CONSTANT(GROUPING);
     457                 : #endif
     458                 : #ifdef YESEXPR
     459           17007 :         REGISTER_NL_LANGINFO_CONSTANT(YESEXPR);
     460                 : #endif
     461                 : #ifdef NOEXPR
     462           17007 :         REGISTER_NL_LANGINFO_CONSTANT(NOEXPR);
     463                 : #endif
     464                 : #ifdef YESSTR
     465                 :         REGISTER_NL_LANGINFO_CONSTANT(YESSTR);
     466                 : #endif
     467                 : #ifdef NOSTR
     468                 :         REGISTER_NL_LANGINFO_CONSTANT(NOSTR);
     469                 : #endif
     470                 : #ifdef CODESET
     471           17007 :         REGISTER_NL_LANGINFO_CONSTANT(CODESET);
     472                 : #endif
     473                 : #undef REGISTER_NL_LANGINFO_CONSTANT
     474           17007 :         return SUCCESS;
     475                 : }
     476                 : /* }}} */
     477                 : 
     478                 : /* {{{ proto string nl_langinfo(int item) U
     479                 :    Query language and locale information */
     480                 : PHP_FUNCTION(nl_langinfo)
     481              28 : {
     482                 :         long item;
     483                 :         char *value;
     484                 : 
     485              28 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &item) == FAILURE) {
     486               7 :                 return;
     487                 :         }
     488                 : 
     489              21 :         php_error_docref(NULL TSRMLS_CC, E_DEPRECATED, "deprecated in Unicode mode, please use ICU locale functions");
     490                 : 
     491              21 :         switch(item) { /* {{{ */
     492                 : #ifdef ABDAY_1
     493                 :                 case ABDAY_1:
     494                 :                 case ABDAY_2:
     495                 :                 case ABDAY_3:
     496                 :                 case ABDAY_4:
     497                 :                 case ABDAY_5:
     498                 :                 case ABDAY_6:
     499                 :                 case ABDAY_7:
     500                 : #endif
     501                 : #ifdef DAY_1
     502                 :                 case DAY_1:
     503                 :                 case DAY_2:
     504                 :                 case DAY_3:
     505                 :                 case DAY_4:
     506                 :                 case DAY_5:
     507                 :                 case DAY_6:
     508                 :                 case DAY_7:
     509                 : #endif
     510                 : #ifdef ABMON_1
     511                 :                 case ABMON_1:
     512                 :                 case ABMON_2:
     513                 :                 case ABMON_3:
     514                 :                 case ABMON_4:
     515                 :                 case ABMON_5:
     516                 :                 case ABMON_6:
     517                 :                 case ABMON_7:
     518                 :                 case ABMON_8:
     519                 :                 case ABMON_9:
     520                 :                 case ABMON_10:
     521                 :                 case ABMON_11:
     522                 :                 case ABMON_12:
     523                 : #endif
     524                 : #ifdef MON_1
     525                 :                 case MON_1:
     526                 :                 case MON_2:
     527                 :                 case MON_3:
     528                 :                 case MON_4:
     529                 :                 case MON_5:
     530                 :                 case MON_6:
     531                 :                 case MON_7:
     532                 :                 case MON_8:
     533                 :                 case MON_9:
     534                 :                 case MON_10:
     535                 :                 case MON_11:
     536                 :                 case MON_12:
     537                 : #endif
     538                 : #ifdef AM_STR
     539                 :                 case AM_STR:
     540                 : #endif
     541                 : #ifdef PM_STR
     542                 :                 case PM_STR:
     543                 : #endif
     544                 : #ifdef D_T_FMT
     545                 :                 case D_T_FMT:
     546                 : #endif
     547                 : #ifdef D_FMT
     548                 :                 case D_FMT:
     549                 : #endif
     550                 : #ifdef T_FMT
     551                 :                 case T_FMT:
     552                 : #endif
     553                 : #ifdef T_FMT_AMPM
     554                 :                 case T_FMT_AMPM:
     555                 : #endif
     556                 : #ifdef ERA
     557                 :                 case ERA:
     558                 : #endif
     559                 : #ifdef ERA_YEAR
     560                 :                 case ERA_YEAR:
     561                 : #endif
     562                 : #ifdef ERA_D_T_FMT
     563                 :                 case ERA_D_T_FMT:
     564                 : #endif
     565                 : #ifdef ERA_D_FMT
     566                 :                 case ERA_D_FMT:
     567                 : #endif
     568                 : #ifdef ERA_T_FMT
     569                 :                 case ERA_T_FMT:
     570                 : #endif
     571                 : #ifdef ALT_DIGITS
     572                 :                 case ALT_DIGITS:
     573                 : #endif
     574                 : #ifdef INT_CURR_SYMBOL
     575                 :                 case INT_CURR_SYMBOL:
     576                 : #endif
     577                 : #ifdef CURRENCY_SYMBOL
     578                 :                 case CURRENCY_SYMBOL:
     579                 : #endif
     580                 : #ifdef CRNCYSTR
     581                 :                 case CRNCYSTR:
     582                 : #endif
     583                 : #ifdef MON_DECIMAL_POINT
     584                 :                 case MON_DECIMAL_POINT:
     585                 : #endif
     586                 : #ifdef MON_THOUSANDS_SEP
     587                 :                 case MON_THOUSANDS_SEP:
     588                 : #endif
     589                 : #ifdef MON_GROUPING
     590                 :                 case MON_GROUPING:
     591                 : #endif
     592                 : #ifdef POSITIVE_SIGN
     593                 :                 case POSITIVE_SIGN:
     594                 : #endif
     595                 : #ifdef NEGATIVE_SIGN
     596                 :                 case NEGATIVE_SIGN:
     597                 : #endif
     598                 : #ifdef INT_FRAC_DIGITS
     599                 :                 case INT_FRAC_DIGITS:
     600                 : #endif
     601                 : #ifdef FRAC_DIGITS
     602                 :                 case FRAC_DIGITS:
     603                 : #endif
     604                 : #ifdef P_CS_PRECEDES
     605                 :                 case P_CS_PRECEDES:
     606                 : #endif
     607                 : #ifdef P_SEP_BY_SPACE
     608                 :                 case P_SEP_BY_SPACE:
     609                 : #endif
     610                 : #ifdef N_CS_PRECEDES
     611                 :                 case N_CS_PRECEDES:
     612                 : #endif
     613                 : #ifdef N_SEP_BY_SPACE
     614                 :                 case N_SEP_BY_SPACE:
     615                 : #endif
     616                 : #ifdef P_SIGN_POSN
     617                 :                 case P_SIGN_POSN:
     618                 : #endif
     619                 : #ifdef N_SIGN_POSN
     620                 :                 case N_SIGN_POSN:
     621                 : #endif
     622                 : #ifdef DECIMAL_POINT
     623                 :                 case DECIMAL_POINT:
     624                 : #elif defined(RADIXCHAR)
     625                 :                 case RADIXCHAR:
     626                 : #endif
     627                 : #ifdef THOUSANDS_SEP
     628                 :                 case THOUSANDS_SEP:
     629                 : #elif defined(THOUSEP)
     630                 :                 case THOUSEP:
     631                 : #endif
     632                 : #ifdef GROUPING
     633                 :                 case GROUPING:
     634                 : #endif
     635                 : #ifdef YESEXPR
     636                 :                 case YESEXPR:
     637                 : #endif
     638                 : #ifdef NOEXPR
     639                 :                 case NOEXPR:
     640                 : #endif
     641                 : #ifdef YESSTR
     642                 :                 case YESSTR:
     643                 : #endif
     644                 : #ifdef NOSTR
     645                 :                 case NOSTR:
     646                 : #endif
     647                 : #ifdef CODESET
     648                 :                 case CODESET:
     649                 : #endif
     650                 :                         break;
     651                 :                 default:
     652              16 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Item '%ld' is not valid", item);
     653              16 :                         RETURN_FALSE;
     654                 :         }
     655                 :         /* }}} */
     656                 : 
     657               5 :         value = nl_langinfo(item);
     658               5 :         if (value == NULL) {
     659               0 :                 RETURN_FALSE;
     660                 :         } else {
     661               5 :                 RETURN_STRING(value, 1);
     662                 :         }
     663                 : }
     664                 : #endif
     665                 : /* }}} */
     666                 : 
     667                 : /* {{{ proto int strcoll(string str1, string str2) U
     668                 :    Compares two strings using the current locale */
     669                 : PHP_FUNCTION(strcoll)
     670               5 : {
     671                 :         zstr s1, s2;
     672                 :         int s1_len, s2_len;
     673                 :         zend_uchar str_type;
     674                 : 
     675               5 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "TT", &s1, &s1_len,
     676                 :                                                           &str_type, &s2, &s2_len, &str_type) == FAILURE) {
     677               4 :                 return;
     678                 :         }
     679                 : 
     680               1 :         if (str_type == IS_UNICODE) {
     681               1 :                 RETURN_LONG(ZEND_COLL_RESULT(ucol_strcoll(UG(default_collator)->coll, s1.u, s1_len, s2.u, s2_len)));
     682                 :         } else {
     683                 : #ifdef HAVE_STRCOLL
     684               0 :                 RETURN_LONG(strcoll((const char *) s1.s,
     685                 :                                                         (const char *) s2.s));
     686                 : #else
     687                 :                 RETURN_FALSE;
     688                 : #endif
     689                 :         }
     690                 : 
     691                 : }
     692                 : /* }}} */
     693                 : 
     694                 : /* {{{ php_charmask
     695                 :  * Fills a 256-byte bytemask with input. You can specify a range like 'a..z',
     696                 :  * it needs to be incrementing.
     697                 :  * Returns: FAILURE/SUCCESS whether the input was correct (i.e. no range errors)
     698                 :  */
     699                 : static inline int php_charmask(unsigned char *input, int len, char *mask TSRMLS_DC)
     700           45614 : {
     701                 :         unsigned char *end;
     702                 :         unsigned char c;
     703           45614 :         int result = SUCCESS;
     704                 : 
     705           45614 :         memset(mask, 0, 256);
     706          314340 :         for (end = input+len; input < end; input++) {
     707          268726 :                 c=*input;
     708          268731 :                 if ((input+3 < end) && input[1] == '.' && input[2] == '.'
     709                 :                                 && input[3] >= c) {
     710               5 :                         memset(mask+c, 1, input[3] - c + 1);
     711               5 :                         input+=3;
     712          268721 :                 } else if ((input+1 < end) && input[0] == '.' && input[1] == '.') {
     713                 :                         /* Error, try to be as helpful as possible:
     714                 :                            (a range ending/starting with '.' won't be captured here) */
     715               2 :                         if (end-len >= input) { /* there was no 'left' char */
     716               0 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid '..'-range, no character to the left of '..'");
     717               0 :                                 result = FAILURE;
     718               0 :                                 continue;
     719                 :                         }
     720               2 :                         if (input+2 >= end) { /* there is no 'right' char */
     721               0 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid '..'-range, no character to the right of '..'");
     722               0 :                                 result = FAILURE;
     723               0 :                                 continue;
     724                 :                         }
     725               2 :                         if (input[-1] > input[2]) { /* wrong order */
     726               2 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid '..'-range, '..'-range needs to be incrementing");
     727               2 :                                 result = FAILURE;
     728               2 :                                 continue;
     729                 :                         }
     730                 :                         /* FIXME: better error (a..b..c is the only left possibility?) */
     731               0 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid '..'-range");
     732               0 :                         result = FAILURE;
     733               0 :                         continue;
     734                 :                 } else {
     735          268719 :                         mask[c]=1;
     736                 :                 }
     737                 :         }
     738           45614 :         return result;
     739                 : }
     740                 : /* }}} */
     741                 : 
     742                 : /* {{{ php_trim()
     743                 :  * mode 1 : trim left
     744                 :  * mode 2 : trim right
     745                 :  * mode 3 : trim left and right
     746                 :  * what indicates which chars are to be trimmed. NULL->default (' \t\n\r\v\0')
     747                 :  */
     748                 : PHPAPI char *php_trim(char *c, int len, char *what, int what_len, zval *return_value, int mode TSRMLS_DC)
     749           44376 : {
     750                 :         register int i;
     751           44376 :         int trimmed = 0;
     752                 :         char mask[256];
     753                 : 
     754           44376 :         if (what) {
     755               0 :                 php_charmask((unsigned char*)what, what_len, mask TSRMLS_CC);
     756                 :         } else {
     757           44376 :                 php_charmask((unsigned char*)" \n\r\t\v\0", 6, mask TSRMLS_CC);
     758                 :         }
     759                 : 
     760           44376 :         if (mode & 1) {
     761           47651 :                 for (i = 0; i < len; i++) {
     762           33080 :                         if (mask[(unsigned char)c[i]]) {
     763            3275 :                                 trimmed++;
     764                 :                         } else {
     765           29805 :                                 break;
     766                 :                         }
     767                 :                 }
     768           44376 :                 len -= trimmed;
     769           44376 :                 c += trimmed;
     770                 :         }
     771           44376 :         if (mode & 2) {
     772           37769 :                 for (i = len - 1; i >= 0; i--) {
     773           37733 :                         if (mask[(unsigned char)c[i]]) {
     774            8892 :                                 len--;
     775                 :                         } else {
     776           28841 :                                 break;
     777                 :                         }
     778                 :                 }
     779                 :         }
     780                 : 
     781           44376 :         if (return_value) {
     782           44374 :                 RETVAL_STRINGL(c, len, 1);
     783                 :         } else {
     784               2 :                 return estrndup(c, len);
     785                 :         }
     786           44374 :         return "";
     787                 : }
     788                 : /* }}} */
     789                 : 
     790                 : /* {{{ php_expand_uchar_range()
     791                 :  * Expands possible ranges of the form 'a..b' in input charlist,
     792                 :  * where a < b in code-point order
     793                 :  */
     794                 : static int php_expand_uchar_range(UChar **range, int *range_len TSRMLS_DC)
     795             257 : {
     796                 :         UChar32 *codepts, *tmp, *input, *end, c;
     797                 :         int32_t len, tmp_len, idx;
     798                 :         UErrorCode err;
     799             257 :         int expanded = 0;
     800             257 :         int result = SUCCESS;
     801                 : 
     802                 :         /* First, convert UTF-16 to UTF-32 */
     803             257 :         len = *range_len;
     804             257 :         codepts = (UChar32 *)emalloc((len+1)*sizeof(UChar32));
     805             257 :         err = U_ZERO_ERROR;
     806             257 :         u_strToUTF32((UChar32 *)codepts, len+1, &len, *range, len, &err);
     807                 : 
     808                 :         /* Expand ranges, if any - taken from php_charmask() */
     809             257 :         tmp_len = len;
     810             257 :         tmp = (UChar32 *)emalloc((tmp_len+1)*sizeof(UChar32));
     811             257 :         input = codepts;
     812            1235 :         for ( idx = 0, end = input+len ; input < end ; input++ ) {
     813             978 :                 c = input[0];
     814             998 :                 if ( (input+3 < end) && input[1] == '.' && input[2] == '.' && input[3] >= c ) {
     815              20 :                         tmp_len += (input[3] - c + 1 - 4);
     816              20 :                         tmp = (UChar32 *)erealloc(tmp, (tmp_len+1)*sizeof(UChar32));
     817             960 :                         for ( ; c <= input[3] ; c++ ) {
     818             940 :                                 if ( U_IS_UNICODE_CHAR(c) ) tmp[idx++] = c;
     819                 :                         }
     820              20 :                         input += 3;
     821              20 :                         expanded++;
     822             958 :                 } else if ( (input+1 < end) && input[0] == '.' && input[1] == '.' ) {
     823                 :                         /* Error, try to be as helpful as possible:
     824                 :                            (a range ending/starting with '.' won't be captured here) */
     825              12 :                         if ( end-len >= input ) { /* There is no 'left' char */
     826               3 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid '..'-range, no character to the left of '..'");
     827               3 :                                 result = FAILURE;
     828               3 :                                 continue;
     829                 :                         }
     830               9 :                         if ( input+2 >= end ) { /* There is no 'right' char */
     831               3 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid '..'-range, no character to the right of '..'");
     832               3 :                                 result = FAILURE;
     833               3 :                                 continue;
     834                 :                         }
     835               6 :                         if ( input[-1] > input[2] ) { /* Wrong order */
     836               3 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid '..'-range, '..'-range needs to be incrementing");
     837               3 :                                 result = FAILURE;
     838               3 :                                 continue;
     839                 :                         }
     840                 :                         /* FIXME: Better error (a..b..c is the only left possibility?) */
     841               3 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid '..'-range");
     842               3 :                         result = FAILURE;
     843               3 :                         continue;
     844                 :                 } else {
     845             946 :                         tmp[idx++] = c;
     846                 :                 }
     847                 :         }
     848                 : 
     849                 :         /* If any ranges were expanded, convert the expanded results back to UTF-16 */
     850             257 :         if ( expanded > 0 ) {
     851              20 :                 len = tmp_len;
     852              20 :                 *range = (UChar *)erealloc(*range, (len+1)*sizeof(UChar));
     853              20 :                 err = U_ZERO_ERROR;
     854              20 :                 u_strFromUTF32(*range, len+1, &len, tmp, tmp_len, &err);
     855              20 :                 if ( err == U_BUFFER_OVERFLOW_ERROR ) {
     856               0 :                         err = U_ZERO_ERROR;
     857               0 :                         *range = (UChar *)erealloc(*range, (len+1)*sizeof(UChar));
     858               0 :                         u_strFromUTF32(*range, len+1, NULL, tmp, tmp_len, &err);
     859               0 :                         if ( U_FAILURE(err) ) { /* Internal ICU error */
     860               0 :                                 result = FAILURE;
     861                 :                         }
     862                 :                 }
     863              20 :                 *range_len = len;
     864                 :         }
     865                 : 
     866             257 :         efree(tmp);
     867             257 :         efree(codepts);
     868                 : 
     869             257 :         return result;
     870                 : }
     871                 : /* }}} */
     872                 : 
     873                 : /* {{{ php_u_trim()
     874                 :  * Unicode capable version of php_trim()
     875                 :  */
     876                 : static UChar *php_u_trim(UChar *c, int len, UChar *what, int what_len, zval *return_value, int mode TSRMLS_DC)
     877          776423 : {
     878                 :         int32_t i, k;
     879          776423 :         UChar   ch = 0;
     880          776423 :         int32_t start = 0, end = len;
     881                 : 
     882          776423 :         if ( what ) {
     883             242 :                 what = eustrndup(what, what_len);
     884             242 :                 php_expand_uchar_range(&what, &what_len TSRMLS_CC);
     885                 :         } else {
     886          776181 :                 what = USTR_MAKE(" \n\r\t\v\0");
     887          776181 :                 what_len = sizeof(" \n\r\t\v\0") - 1;
     888                 :         }
     889                 : 
     890          776423 :         if ( mode & 1 ) {
     891         1553441 :                 for ( i = k = 0 ; i < end ; ) {
     892          681435 :                         U16_NEXT(c, k, end, ch);
     893          681435 :                         if ( what ) {
     894          681435 :                                 if (u_memchr32(what, ch, what_len) == NULL) {
     895          680458 :                                         break;
     896                 :                                 }
     897                 :                         } else {
     898               0 :                                 if ( u_isWhitespace(ch) == FALSE ) {
     899               0 :                                         break;
     900                 :                                 }
     901                 :                         }
     902             977 :                         i = k;
     903                 :                 }
     904          776232 :                 start = i;
     905                 :         }
     906          776423 :         if ( mode & 2 ) {
     907         1584084 :                 for ( i = k = end ; i > start ; ) {
     908          711912 :                         U16_PREV(c, 0, k, ch);
     909          711912 :                         if ( what ) {
     910          711912 :                                 if (u_memchr32(what, ch, what_len) == NULL) {
     911          680554 :                                         break;
     912                 :                                 }
     913                 :                         } else {
     914               0 :                                 if ( u_isWhitespace(ch) == FALSE ) {
     915               0 :                                         break;
     916                 :                                 }
     917                 :                         }
     918           31358 :                         i = k;
     919                 :                 }
     920          776363 :                 end = i;
     921                 :         }
     922          776423 :         if ( what )
     923                 :         {
     924          776423 :                 efree( what );
     925                 :         }
     926                 : 
     927          776423 :         if ( start < len ) {
     928          680619 :                 if ( return_value ) {
     929          680619 :                         RETVAL_UNICODEL(c+start, end-start, 1);
     930          680619 :                         return NULL;
     931                 :                 } else {
     932               0 :                         return eustrndup(c+start, end-start);
     933                 :                 }
     934                 :         } else { /* Trimmed the whole string */
     935           95804 :                 if ( return_value ) {
     936           95804 :                         RETVAL_EMPTY_UNICODE();
     937           95804 :                         return NULL;
     938                 :                 } else {
     939               0 :                         return (USTR_MAKE(""));
     940                 :                 }
     941                 :         }
     942                 : }
     943                 : /* }}} */
     944                 : 
     945                 : /* {{{ php_do_trim
     946                 :  * Base for trim(), rtrim() and ltrim() functions.
     947                 :  */
     948                 : static void php_do_trim(INTERNAL_FUNCTION_PARAMETERS, int mode)
     949          803789 : {
     950          803789 :         zstr str, what = NULL_ZSTR;
     951                 :         int str_len, what_len;
     952                 :         zend_uchar str_type;
     953                 : 
     954          803789 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "T|T", &str, &str_len,
     955                 :                                                           &str_type, &what, &what_len, &str_type) == FAILURE) {
     956              57 :                 return;
     957                 :         }
     958                 : 
     959          803732 :         if (ZEND_NUM_ARGS() > 1) {
     960             242 :                 if (str_type == IS_UNICODE) {
     961             242 :                         php_u_trim(str.u, str_len, what.u, what_len, return_value, mode TSRMLS_CC);
     962                 :                 } else {
     963               0 :                         php_trim(str.s, str_len, what.s, what_len, return_value, mode TSRMLS_CC);
     964                 :                 }
     965                 :         } else {
     966          803490 :                 if (str_type == IS_UNICODE) {
     967          776181 :                         php_u_trim(str.u, str_len, NULL, 0, return_value, mode TSRMLS_CC);
     968                 :                 } else {
     969           27309 :                         php_trim(str.s, str_len, NULL, 0, return_value, mode TSRMLS_CC);
     970                 :                 }
     971                 :         }
     972                 : }
     973                 : /* }}} */
     974                 : 
     975                 : /* {{{ proto string trim(string str [, string character_mask]) U
     976                 :    Strips whitespace from the beginning and end of a string */
     977                 : PHP_FUNCTION(trim)
     978          787995 : {
     979          787995 :         php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3);
     980          787995 : }
     981                 : /* }}} */
     982                 : 
     983                 : /* {{{ proto string rtrim(string str [, string character_mask]) U
     984                 :    Removes trailing whitespace */
     985                 : PHP_FUNCTION(rtrim)
     986             223 : {
     987             223 :         php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 2);
     988             223 : }
     989                 : /* }}} */
     990                 : 
     991                 : /* {{{ proto string ltrim(string str [, string character_mask]) U
     992                 :    Strips whitespace from the beginning of a string */
     993                 : PHP_FUNCTION(ltrim)
     994           15571 : {
     995           15571 :         php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
     996           15571 : }
     997                 : /* }}} */
     998                 : 
     999                 : /* {{{ proto string wordwrap(string str [, int width [, string break [, boolean cut]]])
    1000                 :    Wraps buffer to selected number of characters using string break char */
    1001                 : PHP_FUNCTION(wordwrap)
    1002             357 : {
    1003             357 :         const char *text, *breakchar = "\n";
    1004                 :         char *newtext;
    1005             357 :         int textlen, breakcharlen = 1, newtextlen, chk;
    1006                 :         size_t alloced;
    1007             357 :         long current = 0, laststart = 0, lastspace = 0;
    1008             357 :         long linelength = 75;
    1009             357 :         zend_bool docut = 0;
    1010                 : 
    1011             357 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lsb", &text, &textlen, &linelength, &breakchar, &breakcharlen, &docut) == FAILURE) {
    1012             108 :                 return;
    1013                 :         }
    1014                 : 
    1015             249 :         if (textlen == 0) {
    1016              40 :                 RETURN_EMPTY_STRING();
    1017                 :         }
    1018                 : 
    1019             209 :         if (breakcharlen == 0) {
    1020              25 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Break string cannot be empty");
    1021              25 :                 RETURN_FALSE;
    1022                 :         }
    1023                 : 
    1024             184 :         if (linelength == 0 && docut) {
    1025               8 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't force cut when width is zero");
    1026               8 :                 RETURN_FALSE;
    1027                 :         }
    1028                 : 
    1029                 :         /* Special case for a single-character break as it needs no
    1030                 :            additional storage space */
    1031             176 :         if (breakcharlen == 1 && !docut) {
    1032              50 :                 newtext = estrndup(text, textlen);
    1033                 : 
    1034              50 :                 laststart = lastspace = 0;
    1035            1009 :                 for (current = 0; current < textlen; current++) {
    1036             959 :                         if (text[current] == breakchar[0]) {
    1037               4 :                                 laststart = lastspace = current + 1;
    1038             955 :                         } else if (text[current] == ' ') {
    1039              72 :                                 if (current - laststart >= linelength) {
    1040              30 :                                         newtext[current] = breakchar[0];
    1041              30 :                                         laststart = current + 1;
    1042                 :                                 }
    1043              72 :                                 lastspace = current;
    1044             883 :                         } else if (current - laststart >= linelength && laststart != lastspace) {
    1045             220 :                                 newtext[lastspace] = breakchar[0];
    1046             220 :                                 laststart = lastspace + 1;
    1047                 :                         }
    1048                 :                 }
    1049                 : 
    1050              50 :                 RETURN_STRINGL(newtext, textlen, 0);
    1051                 :         } else {
    1052                 :                 /* Multiple character line break or forced cut */
    1053             126 :                 if (linelength > 0) {
    1054             102 :                         chk = (int)(textlen/linelength + 1);
    1055             102 :                         newtext = safe_emalloc(chk, breakcharlen, textlen + 1);
    1056             102 :                         alloced = textlen + chk * breakcharlen + 1;
    1057                 :                 } else {
    1058              24 :                         chk = textlen;
    1059              24 :                         alloced = textlen * (breakcharlen + 1) + 1;
    1060              24 :                         newtext = safe_emalloc(textlen, (breakcharlen + 1), 1);
    1061                 :                 }
    1062                 : 
    1063                 :                 /* now keep track of the actual new text length */
    1064             126 :                 newtextlen = 0;
    1065                 : 
    1066             126 :                 laststart = lastspace = 0;
    1067            2742 :                 for (current = 0; current < textlen; current++) {
    1068            2616 :                         if (chk <= 0) {
    1069               6 :                                 alloced += (int) (((textlen - current + 1)/linelength + 1) * breakcharlen) + 1;
    1070               6 :                                 newtext = erealloc(newtext, alloced);
    1071               6 :                                 chk = (int) ((textlen - current)/linelength) + 1;
    1072                 :                         }
    1073                 :                         /* when we hit an existing break, copy to new buffer, and
    1074                 :                          * fix up laststart and lastspace */
    1075            2630 :                         if (text[current] == breakchar[0]
    1076                 :                                 && current + breakcharlen < textlen
    1077                 :                                 && !strncmp(text+current, breakchar, breakcharlen)) {
    1078              14 :                                 memcpy(newtext+newtextlen, text+laststart, current-laststart+breakcharlen);
    1079              14 :                                 newtextlen += current-laststart+breakcharlen;
    1080              14 :                                 current += breakcharlen - 1;
    1081              14 :                                 laststart = lastspace = current + 1;
    1082              14 :                                 chk--;
    1083                 :                         }
    1084                 :                         /* if it is a space, check if it is at the line boundary,
    1085                 :                          * copy and insert a break, or just keep track of it */
    1086            2602 :                         else if (text[current] == ' ') {
    1087             214 :                                 if (current - laststart >= linelength) {
    1088              85 :                                         memcpy(newtext+newtextlen, text+laststart, current-laststart);
    1089              85 :                                         newtextlen += current - laststart;
    1090              85 :                                         memcpy(newtext+newtextlen, breakchar, breakcharlen);
    1091              85 :                                         newtextlen += breakcharlen;
    1092              85 :                                         laststart = current + 1;
    1093              85 :                                         chk--;
    1094                 :                                 }
    1095             214 :                                 lastspace = current;
    1096                 :                         }
    1097                 :                         /* if we are cutting, and we've accumulated enough
    1098                 :                          * characters, and we haven't see a space for this line,
    1099                 :                          * copy and insert a break. */
    1100            2556 :                         else if (current - laststart >= linelength
    1101                 :                                         && docut && laststart >= lastspace) {
    1102             168 :                                 memcpy(newtext+newtextlen, text+laststart, current-laststart);
    1103             168 :                                 newtextlen += current - laststart;
    1104             168 :                                 memcpy(newtext+newtextlen, breakchar, breakcharlen);
    1105             168 :                                 newtextlen += breakcharlen;
    1106             168 :                                 laststart = lastspace = current;
    1107             168 :                                 chk--;
    1108                 :                         }
    1109                 :                         /* if the current word puts us over the linelength, copy
    1110                 :                          * back up until the last space, insert a break, and move
    1111                 :                          * up the laststart */
    1112            2220 :                         else if (current - laststart >= linelength
    1113                 :                                         && laststart < lastspace) {
    1114             107 :                                 memcpy(newtext+newtextlen, text+laststart, lastspace-laststart);
    1115             107 :                                 newtextlen += lastspace - laststart;
    1116             107 :                                 memcpy(newtext+newtextlen, breakchar, breakcharlen);
    1117             107 :                                 newtextlen += breakcharlen;
    1118             107 :                                 laststart = lastspace = lastspace + 1;
    1119             107 :                                 chk--;
    1120                 :                         }
    1121                 :                 }
    1122                 : 
    1123                 :                 /* copy over any stragglers */
    1124             126 :                 if (laststart != current) {
    1125             126 :                         memcpy(newtext+newtextlen, text+laststart, current-laststart);
    1126             126 :                         newtextlen += current - laststart;
    1127                 :                 }
    1128                 : 
    1129             126 :                 newtext[newtextlen] = '\0';
    1130                 :                 /* free unused memory */
    1131             126 :                 newtext = erealloc(newtext, newtextlen+1);
    1132                 : 
    1133             126 :                 RETURN_STRINGL(newtext, newtextlen, 0);
    1134                 :         }
    1135                 : }
    1136                 : /* }}} */
    1137                 : 
    1138                 : /* {{{ php_explode
    1139                 :  */
    1140                 : PHPAPI void php_explode(char *delim, uint delim_len, char *str, uint str_len, zend_uchar str_type, zval *return_value, long limit)
    1141            1528 : {
    1142                 :         char *p1, *p2, *endp;
    1143                 : 
    1144            1528 :         endp = str + str_len;
    1145            1528 :         p1 = str;
    1146            1528 :         p2 = php_memnstr(str, delim, delim_len, endp);
    1147                 : 
    1148            1528 :         if ( p2 == NULL ) {
    1149              64 :                 add_next_index_stringl(return_value, p1, str_len, 1);
    1150                 :         } else {
    1151                 :                 do {
    1152           82923 :                         add_next_index_stringl(return_value, p1, p2-p1, 1);
    1153           82923 :                         p1 = p2 + delim_len;
    1154                 :                 } while ( (p2 = php_memnstr(p1, delim, delim_len, endp)) != NULL &&
    1155           82923 :                                   --limit > 1 );
    1156                 : 
    1157            1464 :                 if ( p1 <= endp ) {
    1158            1464 :                         add_next_index_stringl(return_value, p1, endp-p1, 1);
    1159                 :                 }
    1160                 :         }
    1161            1528 : }
    1162                 : /* }}} */
    1163                 : 
    1164                 : /* {{{ php_explode_negative_limit
    1165                 :  */
    1166                 : PHPAPI void php_explode_negative_limit(char *delim, uint delim_len, char *str, uint str_len, zend_uchar str_type, zval *return_value, long limit)
    1167               2 : {
    1168                 : #define EXPLODE_ALLOC_STEP 64
    1169                 :         char *p1, *p2, *endp;
    1170                 : 
    1171               2 :         endp = str + str_len;
    1172               2 :         p1 = str;
    1173               2 :         p2 = php_memnstr(str, delim, delim_len, endp);
    1174                 : 
    1175               2 :         if ( p2 == NULL ) {
    1176                 :                 /*
    1177                 :                 do nothing since limit <= -1, thus if only one chunk - 1 + (limit) <= 0
    1178                 :                 by doing nothing we return empty array
    1179                 :                 */
    1180                 :         } else {
    1181               2 :                 int allocated = EXPLODE_ALLOC_STEP, found = 0;
    1182                 :                 long i, to_return;
    1183               2 :                 char **positions = emalloc(allocated * sizeof(char *));
    1184                 : 
    1185               2 :                 positions[found++] = p1;
    1186                 :                 do {
    1187               6 :                         if ( found >= allocated ) {
    1188               0 :                                 allocated = found + EXPLODE_ALLOC_STEP;/* make sure we have enough memory */
    1189               0 :                                 positions = erealloc(positions, allocated*sizeof(char *));
    1190                 :                         }
    1191               6 :                         positions[found++] = p1 = p2 + delim_len;
    1192               6 :                 } while ( (p2 = php_memnstr(p1, delim, delim_len, endp)) != NULL );
    1193                 : 
    1194               2 :                 to_return = limit + found;
    1195                 :                 /* limit is at least -1 therefore no need of bounds checking : i will be always less than found */
    1196               7 :                 for ( i = 0 ; i < to_return ; i++ ) { /* this checks also for to_return > 0 */
    1197               5 :                         add_next_index_stringl(return_value, positions[i],
    1198                 :                                                                            (positions[i+1]-delim_len) - positions[i], 1);
    1199                 :                 }
    1200               2 :                 efree(positions);
    1201                 :         }
    1202                 : #undef EXPLODE_ALLOC_STEP
    1203               2 : }
    1204                 : /* }}} */
    1205                 : 
    1206                 : /* {{{ php_u_explode
    1207                 :  * Unicode capable version of php_explode()
    1208                 :  */
    1209                 : static void php_u_explode(UChar *delim, uint delim_len, UChar *str, uint str_len, zval *return_value, long limit)
    1210          474509 : {
    1211                 :         UChar *p1, *p2, *endp;
    1212                 : 
    1213          474509 :         endp = str + str_len;
    1214          474509 :         p1 = str;
    1215          474509 :         p2 = zend_u_memnstr(str, delim, delim_len, endp);
    1216                 : 
    1217          474509 :         if ( p2 == NULL ) {
    1218             102 :                 add_next_index_unicodel(return_value, p1, str_len, 1);
    1219                 :         } else {
    1220                 :                 do {
    1221          517461 :                         add_next_index_unicodel(return_value, p1, p2-p1, 1);
    1222          517461 :                         p1 = (UChar *)p2 + delim_len;
    1223                 :                 } while ((p2 = zend_u_memnstr(p1, delim, delim_len, endp)) != NULL &&
    1224          517461 :                                   --limit > 1 );
    1225                 : 
    1226          474407 :                 if ( p1 <= endp ) {
    1227          474407 :                         add_next_index_unicodel(return_value, p1, endp-p1, 1);
    1228                 :                 }
    1229                 :         }
    1230          474509 : }
    1231                 : /* }}} */
    1232                 : 
    1233                 : /* {{{ php_u_explode_negative_limit
    1234                 :  * Unicode capable version of php_explode_negative_limit()
    1235                 :  */
    1236                 : static void php_u_explode_negative_limit(UChar *delim, uint delim_len, UChar *str, uint str_len, zval *return_value, long limit)
    1237              20 : {
    1238                 : #define EXPLODE_ALLOC_STEP 64
    1239                 :         UChar *p1, *p2, *endp;
    1240                 : 
    1241              20 :         endp = str + str_len;
    1242              20 :         p1 = str;
    1243              20 :         p2 = zend_u_memnstr(str, delim, delim_len, endp);
    1244                 : 
    1245              20 :         if ( p2 == NULL ) {
    1246                 :                 /*
    1247                 :                 do nothing since limit <= -1, thus if only one chunk - 1 + (limit) <= 0
    1248                 :                 by doing nothing we return empty array
    1249                 :                 */
    1250                 :         } else {
    1251              19 :                 int allocated = EXPLODE_ALLOC_STEP, found = 0;
    1252                 :                 long i, to_return;
    1253              19 :                 UChar **positions = emalloc(allocated * sizeof(UChar *));
    1254                 : 
    1255              19 :                 positions[found++] = p1;
    1256                 :                 do {
    1257             108 :                         if ( found >= allocated ) {
    1258               0 :                                 allocated = found + EXPLODE_ALLOC_STEP;/* make sure we have enough memory */
    1259               0 :                                 positions = erealloc(positions, allocated*sizeof(UChar *));
    1260                 :                         }
    1261             108 :                         positions[found++] = p1 = p2 + delim_len;
    1262             108 :                 } while ( (p2 = zend_u_memnstr(p1, delim, delim_len, endp)) != NULL );
    1263                 : 
    1264              19 :                 to_return = limit + found;
    1265                 :                 /* limit is at least -1 therefore no need of bounds checking : i will be always less than found */
    1266             109 :                 for ( i = 0 ; i < to_return ; i++ ) { /* this checks also for to_return > 0 */
    1267              90 :                         add_next_index_unicodel(return_value, positions[i],
    1268                 :                                                                         (positions[i+1]-delim_len) - positions[i], 1);
    1269                 :                 }
    1270              19 :                 efree(positions);
    1271                 :         }
    1272                 : #undef EXPLODE_ALLOC_STEP
    1273              20 : }
    1274                 : /* }}} */
    1275                 : 
    1276                 : /* {{{ proto array explode(string separator, string str [, int limit]) U
    1277                 :    Splits a string on string separator and return array of components. If limit is positive only limit number of components is returned. If limit is negative all components except the last abs(limit) are returned. */
    1278                 : PHP_FUNCTION(explode)
    1279          476155 : {
    1280                 :         void            *str, *delim;
    1281                 :         int                     str_len, delim_len;
    1282                 :         zend_uchar      str_type, delim_type;
    1283          476155 :         long            limit = LONG_MAX; /* No limit */
    1284                 : 
    1285          476155 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "TT|l", &delim, &delim_len, &delim_type,
    1286                 :                                                           &str, &str_len, &str_type, &limit) == FAILURE) {
    1287              22 :                 return;
    1288                 :         }
    1289                 : 
    1290          476133 :         if ( delim_len == 0 ) {
    1291              28 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty delimiter");
    1292              28 :                 RETURN_FALSE;
    1293                 :         }
    1294                 : 
    1295          476105 :         array_init(return_value);
    1296                 : 
    1297          476105 :         if ( str_len == 0 ) {
    1298              16 :                 if (limit >= 0) {
    1299              15 :                         if ( str_type == IS_UNICODE ) {
    1300              12 :                                 add_next_index_unicodel(return_value, USTR_MAKE(""), sizeof("")-1, 0);
    1301                 :                         } else {
    1302               3 :                                 add_next_index_stringl(return_value, "", sizeof("")-1, 1);
    1303                 :                         }
    1304                 :                 }
    1305              16 :                 return;
    1306                 :         }
    1307                 : 
    1308          476089 :         if (limit > 1) {
    1309          476037 :                 if ( str_type == IS_UNICODE ) {
    1310          474509 :                         php_u_explode((UChar *)delim, delim_len, (UChar *)str, str_len, return_value, limit);
    1311                 :                 } else {
    1312            1528 :                         php_explode((char *)delim, delim_len, (char *)str, str_len, str_type, return_value, limit);
    1313                 :                 }
    1314              52 :         } else if (limit < 0) {
    1315              22 :                 if ( str_type == IS_UNICODE ) {
    1316              20 :                         php_u_explode_negative_limit((UChar *)delim, delim_len, (UChar *)str, str_len, return_value, limit);
    1317                 :                 } else {
    1318               2 :                         php_explode_negative_limit((char *)delim, delim_len, (char *)str, str_len, str_type, return_value, limit);
    1319                 :                 }
    1320                 :         } else {
    1321              30 :                 if ( str_type == IS_UNICODE ) {
    1322              28 :                         add_index_unicodel(return_value, 0, (UChar *)str, str_len, 1);
    1323                 :                 } else {
    1324               2 :                         add_index_stringl(return_value, 0, (char *)str, str_len, 1);
    1325                 :                 }
    1326                 :         }
    1327                 : }
    1328                 : /* }}} */
    1329                 : 
    1330                 : /* {{{ proto string join([string glue,] array pieces) U
    1331                 :    An alias for implode */
    1332                 : /* }}} */
    1333                 : 
    1334                 : /* {{{ php_implode
    1335                 :  */
    1336                 : PHPAPI void php_implode(zval *delim, zval *arr, zval *retval TSRMLS_DC)
    1337           92253 : {
    1338                 :         zend_uchar              return_type;
    1339           92253 :         int                             numelems, i=0;
    1340                 :         HashPosition    pos;
    1341                 :         zval                    **tmp;
    1342                 : 
    1343           92253 :         Z_TYPE_P(retval) = return_type = Z_TYPE_P(delim); /* ... to start off */
    1344                 : 
    1345                 :         /* Setup return value */
    1346           92253 :         if (return_type == IS_UNICODE) {
    1347           91370 :                 ZVAL_EMPTY_UNICODE(retval);
    1348                 :         } else {
    1349             883 :                 ZVAL_EMPTY_STRING(retval);
    1350                 :         }
    1351                 : 
    1352           92253 :         numelems = zend_hash_num_elements(Z_ARRVAL_P(arr));
    1353           92253 :         if (numelems == 0) {
    1354              29 :                 return;
    1355                 :         }
    1356                 : 
    1357           92224 :         zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(arr), &pos);
    1358          597811 :         while (zend_hash_get_current_data_ex(Z_ARRVAL_P(arr), (void **)&tmp, &pos) == SUCCESS) {
    1359          413363 :                 if (Z_TYPE_PP(tmp) != return_type) {
    1360          368504 :                         if (return_type == IS_UNICODE) {
    1361          360566 :                                 if (Z_TYPE_PP(tmp) != IS_UNICODE) {
    1362          360566 :                                         SEPARATE_ZVAL(tmp);
    1363          360566 :                                         convert_to_unicode_ex(tmp);
    1364                 :                                 }
    1365            7938 :                         } else if (return_type == IS_STRING) {
    1366            7938 :                                 if (Z_TYPE_PP(tmp) != IS_STRING) {
    1367            7938 :                                         SEPARATE_ZVAL(tmp);
    1368            7938 :                                         convert_to_string_ex(tmp);
    1369                 :                                 }
    1370                 :                         } else {
    1371               0 :                                 if (Z_TYPE_PP(tmp) == IS_UNICODE) {
    1372                 :                                         /* Convert IS_STRING up to IS_UNICODE */
    1373               0 :                                         convert_to_unicode_ex(&retval);
    1374               0 :                                         convert_to_unicode_ex(&delim);
    1375               0 :                                         Z_TYPE_P(retval) = return_type = IS_UNICODE;
    1376                 :                                 } else {
    1377               0 :                                         SEPARATE_ZVAL(tmp);
    1378               0 :                                         convert_to_string_ex(tmp);
    1379                 :                                 }
    1380                 :                         }
    1381                 :                 }
    1382                 : 
    1383                 :                 /* Append elem */
    1384          413363 :                 if (return_type == IS_UNICODE) {
    1385          362076 :                         Z_USTRVAL_P(retval) = eurealloc(Z_USTRVAL_P(retval),
    1386                 :                                                                                         Z_USTRLEN_P(retval)+Z_USTRLEN_PP(tmp)+1);
    1387          362076 :                         memcpy(Z_USTRVAL_P(retval)+Z_USTRLEN_P(retval), Z_USTRVAL_PP(tmp), UBYTES(Z_USTRLEN_PP(tmp)+1));
    1388          362076 :                         Z_USTRLEN_P(retval) += Z_USTRLEN_PP(tmp);
    1389          362076 :                         if (++i < numelems) { /* Append delim */
    1390          270735 :                                 Z_USTRVAL_P(retval) = eurealloc(Z_USTRVAL_P(retval),
    1391                 :                                                                                                 Z_USTRLEN_P(retval)+Z_USTRLEN_P(delim)+1);
    1392          270735 :                                 memcpy(Z_USTRVAL_P(retval)+Z_USTRLEN_P(retval), Z_USTRVAL_P(delim), UBYTES(Z_USTRLEN_P(delim)+1));
    1393          270735 :                                 Z_USTRLEN_P(retval) += Z_USTRLEN_P(delim);
    1394                 :                         }
    1395                 :                 } else {
    1396           51287 :                         Z_STRVAL_P(retval) = (char *)erealloc(Z_STRVAL_P(retval),
    1397                 :                                                                                                   Z_STRLEN_P(retval)+Z_STRLEN_PP(tmp)+1);
    1398           51287 :                         memcpy(Z_STRVAL_P(retval)+Z_STRLEN_P(retval), Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)+1);
    1399           51287 :                         Z_STRLEN_P(retval) += Z_STRLEN_PP(tmp);
    1400           51287 :                         if (++i < numelems) { /* Append delim */
    1401           50404 :                                 Z_STRVAL_P(retval) = (char *)erealloc(Z_STRVAL_P(retval),
    1402                 :                                                                                                           Z_STRLEN_P(retval)+Z_STRLEN_P(delim)+1);
    1403           50404 :                                 memcpy(Z_STRVAL_P(retval)+Z_STRLEN_P(retval), Z_STRVAL_P(delim), Z_STRLEN_P(delim)+1);
    1404           50404 :                                 Z_STRLEN_P(retval) += Z_STRLEN_P(delim);
    1405                 :                         }
    1406                 :                 }
    1407                 : 
    1408          413363 :                 zend_hash_move_forward_ex(Z_ARRVAL_P(arr), &pos);
    1409                 :         }
    1410                 : 
    1411           92224 :         return;
    1412                 : }
    1413                 : /* }}} */
    1414                 : 
    1415                 : /* {{{ proto string implode([string glue,] array pieces) U
    1416                 :    Joins array elements placing glue string between items and return one string */
    1417                 : PHP_FUNCTION(implode)
    1418           92289 : {
    1419           92289 :         zval **arg1 = NULL, **arg2 = NULL, *delim, *arr;
    1420                 :         
    1421           92289 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|Z", &arg1, &arg2) == FAILURE) {
    1422               4 :                 return;
    1423                 :         }
    1424                 : 
    1425           92285 :         if (ZEND_NUM_ARGS() == 1) {
    1426               7 :                 if (Z_TYPE_PP(arg1) != IS_ARRAY) {
    1427               5 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument must be an array");
    1428               5 :                         return;
    1429                 :                 } else {
    1430               2 :                         SEPARATE_ZVAL(arg1);
    1431               2 :                         arr = *arg1; 
    1432               2 :                         MAKE_STD_ZVAL(delim);
    1433               2 :                         ZVAL_UNICODEL(delim, EMPTY_STR, sizeof("")-1, 0);
    1434                 :                 }
    1435                 :         } else {
    1436           92278 :                 if (Z_TYPE_PP(arg1) == IS_ARRAY) {
    1437              36 :                         SEPARATE_ZVAL(arg1);
    1438              36 :                         arr = *arg1;
    1439              36 :                         if (Z_TYPE_PP(arg2) != IS_UNICODE && Z_TYPE_PP(arg2) != IS_STRING) {
    1440              10 :                                 convert_to_unicode_ex(arg2);
    1441                 :                         }
    1442              36 :                         delim = *arg2;
    1443           92242 :                 } else if (Z_TYPE_PP(arg2) == IS_ARRAY) {
    1444           92215 :                         SEPARATE_ZVAL(arg2);
    1445           92215 :                         arr = *arg2;
    1446           92215 :                         if (Z_TYPE_PP(arg1) != IS_UNICODE && Z_TYPE_PP(arg1) != IS_STRING) {
    1447              30 :                                 convert_to_unicode_ex(arg1);
    1448                 :                         }
    1449           92215 :                         delim = *arg1;
    1450                 :                 } else {
    1451              27 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid arguments passed");
    1452              27 :                         return;
    1453                 :                 }
    1454                 :         }
    1455                 : 
    1456           92253 :         php_implode(delim, arr, return_value TSRMLS_CC);
    1457                 : 
    1458           92253 :         if (ZEND_NUM_ARGS() == 1) {
    1459               2 :                 FREE_ZVAL(delim);
    1460                 :         }
    1461                 : }
    1462                 : /* }}} */
    1463                 : 
    1464                 : #define STRTOK_TABLE(p) BG(strtok_table)[(unsigned char) *p]
    1465                 : 
    1466                 : /* {{{ proto string strtok([string str,] string token) U
    1467                 :    Tokenize a string */
    1468                 : PHP_FUNCTION(strtok)
    1469             363 : {
    1470                 :         void *tok, *str;
    1471                 :         int tok_len, str_len;
    1472                 :         zend_uchar tok_type, str_type;
    1473                 :         zval *zv;
    1474                 :         char *token, *token_end, *p, *pe;
    1475                 :         UChar *u_token, *u_p, *u_pe;
    1476                 : 
    1477                 :         UChar32 ch, th;
    1478             363 :         int32_t start = 0, end, i, j, rem_len;
    1479                 :         int delim_found, token_present;
    1480             363 :         int skipped = 0;
    1481                 : 
    1482             363 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "T|T",
    1483                 :                                                           &str, &str_len, &str_type,
    1484                 :                                                           &tok, &tok_len, &tok_type) == FAILURE) {
    1485              14 :                 return;
    1486                 :         }
    1487                 : 
    1488             349 :         switch (ZEND_NUM_ARGS()) {
    1489                 :                 case 1:
    1490             254 :                         tok = str;
    1491             254 :                         tok_len = str_len;
    1492             254 :                         tok_type = str_type;
    1493             254 :                         break;
    1494                 : 
    1495                 :                 default:
    1496                 :                 case 2:
    1497              95 :                         if (BG(strtok_zval)) {
    1498              85 :                                 zval_ptr_dtor(&BG(strtok_zval));
    1499                 :                         }
    1500              95 :                         MAKE_STD_ZVAL(zv);
    1501              95 :                         if (str_type == IS_UNICODE) {
    1502              95 :                                 ZVAL_UNICODEL(zv, (UChar *)str, str_len, 1);
    1503              95 :                                 BG(strtok_last) = BG(strtok_string) = Z_USTRVAL_P(zv);
    1504                 :                         } else {
    1505               0 :                                 ZVAL_STRINGL(zv, (char *)str, str_len, 1);
    1506               0 :                                 BG(strtok_last) = BG(strtok_string) = Z_STRVAL_P(zv);
    1507                 :                         }
    1508              95 :                         BG(strtok_zval) = zv;
    1509              95 :                         BG(strtok_len) = str_len;
    1510                 :                         break;
    1511                 :         }
    1512                 : 
    1513             349 :         if (BG(strtok_zval) && tok_type != Z_TYPE_P(BG(strtok_zval))) {
    1514               0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Delimiter type must match string type");
    1515               0 :                 RETURN_FALSE;
    1516                 :         }
    1517                 : 
    1518             349 :         if (tok_type == IS_UNICODE) {
    1519             349 :                 u_p = (UChar *)BG(strtok_last); /* Where we start to search */
    1520             349 :                 u_pe = (UChar *)BG(strtok_string) + BG(strtok_len);
    1521             349 :                 u_token = (UChar *)tok;
    1522             349 :                 if (!u_p || u_p >= u_pe) {
    1523             165 :                         RETURN_FALSE;
    1524                 :                 }
    1525             184 :                 rem_len = u_pe - u_p;
    1526                 : 
    1527                 :                 /* Skip leading delimiters */
    1528             184 :                 token_present = 0;
    1529             473 :                 for (i = 0 ; (u_p + i) < u_pe ; ) {
    1530             277 :                         delim_found = 0;
    1531             277 :                         U16_NEXT(u_p, i, rem_len, ch);
    1532            1557 :                         for (j = 0 ; j < tok_len ; ) {
    1533            1108 :                                 U16_NEXT(u_token, j, tok_len, th);
    1534            1108 :                                 if ( ch == th ) {
    1535             105 :                                         delim_found = 1;
    1536             105 :                                         break;
    1537                 :                                 }
    1538                 :                         }
    1539             277 :                         if (delim_found == 0) {
    1540             172 :                                 U16_BACK_1(u_p, 0, i); /* U16_NEXT() post-incrs 'i' */
    1541             172 :                                 start = i;
    1542             172 :                                 token_present = 1;
    1543             172 :                                 break;
    1544                 :                         }
    1545                 :                 }
    1546             184 :                 if (token_present == 0) {
    1547              12 :                         BG(strtok_last) = NULL;
    1548              12 :                         RETURN_FALSE;
    1549                 :                 }
    1550                 : 
    1551                 :                 /* Seek to next delimiter */
    1552             172 :                 delim_found = 0;
    1553            1698 :                 for (i = start ; (u_p + i) < u_pe ; ) {
    1554            1469 :                         U16_NEXT(u_p, i, rem_len, ch);
    1555            6512 :                         for (j = 0 ; j < tok_len ; ) {
    1556            3689 :                                 U16_NEXT(u_token, j, tok_len, th);
    1557            3689 :                                 if ( ch == th ) {
    1558             115 :                                         delim_found = 1;
    1559             115 :                                         break;
    1560                 :                                 }
    1561                 :                         }
    1562            1469 :                         if (delim_found) {
    1563             115 :                                 U16_BACK_1(u_p, 0, i); /* 'i' was beyond delimiter */
    1564             115 :                                 break;
    1565                 :                         }
    1566                 :                 }
    1567             172 :                 end = i;
    1568                 : 
    1569             172 :                 if (end - start) {
    1570             172 :                         RETVAL_UNICODEL(u_p + start, end - start, 1);
    1571                 :                         /* skip matched token */
    1572             172 :                         U16_FWD_1(u_p, end, rem_len);
    1573             172 :                         BG(strtok_last) = u_p + end;
    1574             172 :                         return;
    1575                 :                 } else {
    1576               0 :                         BG(strtok_last) = NULL;
    1577               0 :                         RETURN_FALSE;
    1578                 :                 }
    1579                 :         } else {
    1580               0 :                 p = (char *)BG(strtok_last); /* Where we start to search */
    1581               0 :                 pe = (char *)BG(strtok_string) + BG(strtok_len);
    1582               0 :                 if (!p || p >= pe) {
    1583               0 :                         RETURN_FALSE;
    1584                 :                 }
    1585               0 :                 token = (char *)tok;
    1586               0 :                 token_end = token + tok_len;
    1587               0 :                 while (token < token_end) {
    1588               0 :                         STRTOK_TABLE(token++) = 1;
    1589                 :                 }
    1590                 : 
    1591                 :                 /* Skip leading delimiters */
    1592               0 :                 while (STRTOK_TABLE(p)) {
    1593               0 :                         if (++p >= pe) {
    1594                 :                                 /* no other chars left */
    1595               0 :                                 BG(strtok_last) = NULL;
    1596               0 :                                 RETVAL_FALSE;
    1597               0 :                                 goto restore;
    1598                 :                         }
    1599               0 :                         skipped++;
    1600                 :                 }
    1601                 :                 /* We know at this place that *p is no delimiter, so skip it */
    1602               0 :                 while (++p < pe) {
    1603               0 :                         if (STRTOK_TABLE(p)) {
    1604               0 :                                 goto return_token;
    1605                 :                         }
    1606                 :                 }
    1607                 : 
    1608               0 :                 if (p - (char *)BG(strtok_last)) {
    1609               0 : return_token:
    1610               0 :                         RETVAL_STRINGL((char *)BG(strtok_last) + skipped, (p - (char *)BG(strtok_last)) - skipped, 1);
    1611               0 :                         BG(strtok_last) = p + 1;
    1612                 :                 } else {
    1613               0 :                         RETVAL_FALSE;
    1614               0 :                         BG(strtok_last) = NULL;
    1615                 :                 }
    1616                 : 
    1617                 :                 /* Restore table -- usually faster then memset'ing the table on every invocation */
    1618               0 : restore:
    1619               0 :                 token = (char *)tok;
    1620               0 :                 while (token < token_end) {
    1621               0 :                         STRTOK_TABLE(token++) = 0;
    1622                 :                 }
    1623                 :         }
    1624                 : }
    1625                 : /* }}} */
    1626                 : 
    1627                 : /* {{{ php_strtoupper
    1628                 :  */
    1629                 : PHPAPI char *php_strtoupper(char *s, size_t len)
    1630             300 : {
    1631                 :         unsigned char *c, *e;
    1632                 : 
    1633             300 :         c = (unsigned char*)s;
    1634             300 :         e = c+len;
    1635                 : 
    1636            1086 :         while (c < e) {
    1637             486 :                 *c = toupper(*c);
    1638             486 :                 c++;
    1639                 :         }
    1640             300 :         return s;
    1641                 : }
    1642                 : /* }}} */
    1643                 : 
    1644                 : /* {{{ php_u_strtoupper
    1645                 :  */
    1646                 : PHPAPI UChar* php_u_strtoupper(UChar *s, int *len, const char* locale)
    1647             641 : {
    1648             641 :         UChar *dest = NULL;
    1649                 :         int dest_len;
    1650                 :         UErrorCode status;
    1651                 : 
    1652             641 :         dest_len = *len;
    1653                 :         while (1) {
    1654             641 :                 status = U_ZERO_ERROR;
    1655             641 :                 dest = eurealloc(dest, dest_len+1);
    1656             641 :                 dest_len = u_strToUpper(dest, dest_len, s, *len, locale, &status);
    1657             641 :                 if (status != U_BUFFER_OVERFLOW_ERROR) {
    1658             641 :                         break;
    1659                 :                 }
    1660               0 :         }
    1661                 : 
    1662             641 :         if (U_SUCCESS(status)) {
    1663             641 :                 dest[dest_len] = 0;
    1664             641 :                 *len = dest_len;
    1665             641 :                 return dest;
    1666                 :         } else {
    1667               0 :                 efree(dest);
    1668               0 :                 return NULL;
    1669                 :         }
    1670                 : }
    1671                 : /* }}} */
    1672                 : 
    1673                 : /* {{{ proto string strtoupper(string str) U
    1674                 :    Makes a string uppercase */
    1675                 : PHP_FUNCTION(strtoupper)
    1676             737 : {
    1677                 :         zstr str;
    1678                 :         int str_len;
    1679                 :         zend_uchar str_type;
    1680                 : 
    1681             737 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t", &str, &str_len, &str_type) == FAILURE) {
    1682               3 :                 return;
    1683                 :         }
    1684                 : 
    1685             734 :         if (str_type == IS_UNICODE) {
    1686                 :                 UChar *result;
    1687             472 :                 if ((result = php_u_strtoupper(str.u, &str_len, UG(default_locale)))) {
    1688             472 :                         RETURN_UNICODEL(result, str_len, 0);
    1689                 :                 } else {
    1690               0 :                         RETURN_EMPTY_UNICODE();
    1691                 :                 }
    1692                 :         } else {
    1693             262 :                 RETVAL_STRINGL(str.s, str_len, 1);
    1694             262 :                 php_strtoupper(Z_STRVAL_P(return_value), Z_STRLEN_P(return_value));
    1695                 :         }
    1696                 : }
    1697                 : /* }}} */
    1698                 : 
    1699                 : /* {{{ php_u_strtolower
    1700                 :  */
    1701                 : PHPAPI UChar *php_u_strtolower(UChar *s, int *len, const char* locale)
    1702          373431 : {
    1703          373431 :         UChar *dest = NULL;
    1704                 :         int dest_len;
    1705          373431 :         UErrorCode status = U_ZERO_ERROR;
    1706                 : 
    1707          373431 :         dest_len = *len;
    1708                 :         while (1) {
    1709          373431 :                 status = U_ZERO_ERROR;
    1710          373431 :                 dest = eurealloc(dest, dest_len+1);
    1711          373431 :                 dest_len = u_strToLower(dest, dest_len, s, *len, locale, &status);
    1712          373431 :                 if (status != U_BUFFER_OVERFLOW_ERROR) {
    1713          373431 :                         break;
    1714                 :                 }
    1715               0 :         }
    1716                 : 
    1717          373431 :         if (U_SUCCESS(status)) {
    1718          373431 :                 dest[dest_len] = 0;
    1719          373431 :                 *len = dest_len;
    1720          373431 :                 return dest;
    1721                 :         } else {
    1722               0 :                 efree(dest);
    1723               0 :                 return NULL;
    1724                 :         }
    1725                 : }
    1726                 : /* }}} */
    1727                 : 
    1728                 : /* {{{ php_strtolower
    1729                 :  */
    1730                 : PHPAPI char *php_strtolower(char *s, size_t len)
    1731          201032 : {
    1732                 :         unsigned char *c, *e;
    1733                 : 
    1734          201032 :         c = (unsigned char*)s;
    1735          201032 :         e = c+len;
    1736                 : 
    1737         7392244 :         while (c < e) {
    1738         6990180 :                 *c = tolower(*c);
    1739         6990180 :                 c++;
    1740                 :         }
    1741          201032 :         return s;
    1742                 : }
    1743                 : /* }}} */
    1744                 : 
    1745                 : /* {{{ proto string strtolower(string str) U
    1746                 :    Makes a string lowercase */
    1747                 : PHP_FUNCTION(strtolower)
    1748          373379 : {
    1749                 :         zstr str;
    1750                 :         int str_len;
    1751                 :         zend_uchar str_type;
    1752                 : 
    1753          373379 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t", &str, &str_len, &str_type) == FAILURE) {
    1754               3 :                 return;
    1755                 :         }
    1756                 : 
    1757          373376 :         if (str_type == IS_UNICODE) {
    1758                 :                 UChar *result;
    1759          373118 :                 if ((result = php_u_strtolower(str.u, &str_len, UG(default_locale)))) {
    1760          373118 :                         RETURN_UNICODEL(result, str_len, 0);
    1761                 :                 } else {
    1762               0 :                         RETURN_EMPTY_UNICODE();
    1763                 :                 }
    1764                 :         } else {
    1765             258 :                 RETVAL_STRINGL(str.s, str_len, 1);
    1766             258 :                 php_strtolower(Z_STRVAL_P(return_value), Z_STRLEN_P(return_value));
    1767                 :         }
    1768                 : }
    1769                 : /* }}} */
    1770                 : 
    1771                 : /* {{{ php_strtotitle
    1772                 :  */
    1773                 : PHPAPI char *php_strtotitle(char *s, size_t len)
    1774               0 : {
    1775               0 :         s[0] = toupper(s[0]);
    1776               0 :         return s;
    1777                 : }
    1778                 : /* }}} */
    1779                 : 
    1780                 : /* {{{ php_u_strtotitle
    1781                 :  */
    1782                 : PHPAPI UChar* php_u_strtotitle(UChar *s, int32_t *len, const char* locale)
    1783               0 : {
    1784               0 :         UChar *dest = NULL;
    1785                 :         int32_t dest_len;
    1786               0 :         UErrorCode status = U_ZERO_ERROR;
    1787                 :         UBreakIterator *brkiter;
    1788                 : 
    1789               0 :         dest_len = *len;
    1790               0 :         brkiter = ubrk_open(UBRK_WORD, locale, s, *len, &status);
    1791                 :         while (1) {
    1792               0 :                 status = U_ZERO_ERROR;
    1793               0 :                 dest = eurealloc(dest, dest_len+1);
    1794               0 :                 dest_len = u_strToTitle(dest, dest_len, s, *len, NULL, locale, &status);
    1795               0 :                 if (status != U_BUFFER_OVERFLOW_ERROR) {
    1796               0 :                         break;
    1797                 :                 }
    1798               0 :         }
    1799               0 :         ubrk_close(brkiter);
    1800                 : 
    1801               0 :         if (U_SUCCESS(status)) {
    1802               0 :                 dest[dest_len] = 0;
    1803               0 :                 *len = dest_len;
    1804               0 :                 return dest;
    1805                 :         } else {
    1806               0 :                 efree(dest);
    1807               0 :                 return NULL;
    1808                 :         }
    1809                 : }
    1810                 : /* }}} */
    1811                 : 
    1812                 : /* {{{ proto string strtotitle(string str) U
    1813                 :    Makes a string titlecase */
    1814                 : PHP_FUNCTION(strtotitle)
    1815               0 : {
    1816                 :         zstr str;
    1817                 :         int str_len;
    1818                 :         zend_uchar str_type;
    1819                 : 
    1820               0 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t", &str, &str_len, &str_type) == FAILURE) {
    1821               0 :                 return;
    1822                 :         }
    1823                 : 
    1824               0 :         if (str_type == IS_UNICODE && str_len == 0) {
    1825               0 :                 RETURN_EMPTY_UNICODE();
    1826               0 :         } else if (str_len == 0) {
    1827               0 :                 RETURN_EMPTY_STRING();
    1828                 :         }
    1829                 : 
    1830               0 :         if (str_type == IS_UNICODE) {
    1831                 :                 UChar *result;
    1832               0 :                 if ((result = php_u_strtotitle(str.u, &str_len, UG(default_locale)))) {
    1833               0 :                         RETURN_UNICODEL(result, str_len, 0);
    1834                 :                 } else {
    1835               0 :                         RETURN_EMPTY_UNICODE();
    1836                 :                 }
    1837                 :         } else {
    1838               0 :                 RETVAL_STRINGL(str.s, str_len, 1);
    1839               0 :                 php_ucwords(Z_STRVAL_P(return_value), str_len);
    1840                 :         }
    1841                 : }
    1842                 : /* }}} */
    1843                 : 
    1844                 : /* {{{ php_u_basename
    1845                 :  */
    1846                 : PHPAPI void php_u_basename(UChar *s, size_t len, UChar *suffix, size_t sufflen, UChar **p_ret, size_t *p_len TSRMLS_DC)
    1847           12504 : {
    1848                 :         UChar *end, *c, *comp, *cend;
    1849                 :         int state;
    1850                 : 
    1851           12504 :         c = comp = cend = s;
    1852           12504 :         end = s + len;
    1853           12504 :         state = 0;
    1854          773275 :         while (c < end) {
    1855                 : #if defined(PHP_WIN32) || defined(NETWARE)
    1856                 :                 if (*c == (UChar) 0x2f /*'/'*/ || *c == (UChar) 0x5c /*'\\'*/) {
    1857                 : #else
    1858          748267 :                 if (*c == (UChar) 0x2f /*'/'*/) {
    1859                 : #endif
    1860           86086 :                         if (state == 1) {
    1861           74130 :                                 state = 0;
    1862           74130 :                                 cend = c;
    1863                 :                         }
    1864                 :                 } else {
    1865          662181 :                         if (state == 0) {
    1866           86403 :                                 comp = c;
    1867           86403 :                                 state = 1;
    1868                 :                         }
    1869                 :                 }
    1870          748267 :                 c++;
    1871                 :         }
    1872                 : 
    1873           12504 :         if (state == 1) {
    1874           12273 :                 cend = c;
    1875                 :         }
    1876           12504 :         if (suffix != NULL && sufflen < (cend - comp) &&
    1877                 :                         u_memcmp(cend - sufflen, suffix, sufflen) == 0) {
    1878           11650 :                 cend -= sufflen;
    1879                 :         }
    1880                 : 
    1881           12504 :         len = cend - comp;
    1882                 : 
    1883           12504 :         if (p_ret) {
    1884           12504 :                 *p_ret = eustrndup(comp, len);
    1885                 :         }
    1886           12504 :         if (p_len) {
    1887           12504 :                 *p_len = len;
    1888                 :         }
    1889           12504 : }
    1890                 : /* }}} */
    1891                 : 
    1892                 : /* {{{ php_basename
    1893                 :  */
    1894                 : PHPAPI void php_basename(char *s, size_t len, char *suffix, size_t sufflen, char **p_ret, size_t *p_len TSRMLS_DC)
    1895             735 : {
    1896             735 :         char *ret = NULL, *c, *comp, *cend;
    1897                 :         size_t inc_len, cnt;
    1898                 :         int state;
    1899                 : 
    1900             735 :         c = comp = cend = s;
    1901             735 :         cnt = len;
    1902             735 :         state = 0;
    1903           11162 :         while (cnt > 0) {
    1904            9692 :                 inc_len = (*c == '\0' ? 1: php_mblen(c, cnt));
    1905                 : 
    1906            9692 :                 switch (inc_len) {
    1907                 :                         case -2:
    1908                 :                         case -1:
    1909               0 :                                 inc_len = 1;
    1910               0 :                                 php_mblen(NULL, 0);
    1911               0 :                                 break;
    1912                 :                         case 0:
    1913               0 :                                 goto quit_loop;
    1914                 :                         case 1:
    1915                 : #if defined(PHP_WIN32) || defined(NETWARE)
    1916                 :                                 if (*c == '/' || *c == '\\') {
    1917                 : #else
    1918            9692 :                                 if (*c == '/') {
    1919                 : #endif
    1920             284 :                                         if (state == 1) {
    1921             243 :                                                 state = 0;
    1922             243 :                                                 cend = c;
    1923                 :                                         }
    1924                 :                                 } else {
    1925            9408 :                                         if (state == 0) {
    1926             973 :                                                 comp = c;
    1927             973 :                                                 state = 1;
    1928                 :                                         }
    1929                 :                                 }
    1930            9692 :                                 break;
    1931                 :                         default:
    1932               0 :                                 if (state == 0) {
    1933               0 :                                         comp = c;
    1934               0 :                                         state = 1;
    1935                 :                                 }
    1936                 :                                 break;
    1937                 :                 }
    1938            9692 :                 c += inc_len;
    1939            9692 :                 cnt -= inc_len;
    1940                 :         }
    1941                 : 
    1942             735 : quit_loop:
    1943             735 :         if (state == 1) {
    1944             730 :                 cend = c;
    1945                 :         }
    1946             735 :         if (suffix != NULL && sufflen < (cend - comp) &&
    1947                 :                         memcmp(cend - sufflen, suffix, sufflen) == 0) {
    1948               2 :                 cend -= sufflen;
    1949                 :         }
    1950                 : 
    1951             735 :         len = cend - comp;
    1952             735 :         if (p_ret) {
    1953             735 :                 ret = emalloc(len + 1);
    1954             735 :                 memcpy(ret, comp, len);
    1955             735 :                 ret[len] = '\0';
    1956             735 :                 *p_ret = ret;
    1957                 :         }
    1958             735 :         if (p_len) {
    1959             706 :                 *p_len = len;
    1960                 :         }
    1961             735 : }
    1962                 : /* }}} */
    1963                 : 
    1964                 : /* {{{ proto string basename(string path [, string suffix]) U
    1965                 :    Returns the filename component of the path */
    1966                 : PHP_FUNCTION(basename)
    1967           12097 : {
    1968           12097 :         zstr string, suffix = NULL_ZSTR, ret;
    1969           12097 :         int string_len, suffix_len = 0;
    1970                 :         zend_uchar string_type, suffix_type;
    1971                 :         size_t ret_len;
    1972                 : 
    1973           12097 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "T|T", &string, &string_len, &string_type, &suffix, &suffix_len, &suffix_type) == FAILURE) {
    1974              30 :                 return;
    1975                 :         }
    1976                 : 
    1977           12067 :         if (string_type == IS_UNICODE) {
    1978           12060 :                 php_u_basename(string.u, string_len, suffix.u, suffix_len, &ret.u, &ret_len TSRMLS_CC);
    1979           12060 :                 RETURN_UNICODEL(ret.u, (int)ret_len, 0);
    1980                 :         } else {
    1981               7 :                 php_basename(string.s, string_len, suffix.s, suffix_len, &ret.s, &ret_len TSRMLS_CC);
    1982               7 :                 RETURN_STRINGL(ret.s, (int)ret_len, 0);
    1983                 :         }
    1984                 : }
    1985                 : /* }}} */
    1986                 : 
    1987                 : /* {{{ php_u_dirname
    1988                 :    Returns directory name component of path */
    1989                 : PHPAPI size_t php_u_dirname(UChar *path, size_t len)
    1990           18558 : {
    1991           18558 :         return zend_u_dirname(path, len);
    1992                 : }
    1993                 : /* }}} */
    1994                 : 
    1995                 : /* {{{ php_dirname
    1996                 :    Returns directory name component of path */
    1997                 : PHPAPI size_t php_dirname(char *path, size_t len)
    1998            3199 : {
    1999            3199 :         return zend_dirname(path, len);
    2000                 : }
    2001                 : /* }}} */
    2002                 : 
    2003                 : /* {{{ proto string dirname(string path) U
    2004                 :    Returns the directory name component of the path */
    2005                 : PHP_FUNCTION(dirname)
    2006           21328 : {
    2007                 :         zstr str;
    2008                 :         int str_len;
    2009                 :         zend_uchar str_type;
    2010                 :         zstr ret;
    2011                 :         size_t ret_len;
    2012                 : 
    2013           21328 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t", &str, &str_len,
    2014                 :                                                           &str_type) == FAILURE) {
    2015               9 :                 return;
    2016                 :         }
    2017                 : 
    2018           21319 :         if (str_type == IS_UNICODE) {
    2019           18313 :                 ret.u = eustrndup(str.u, str_len);
    2020           18313 :                 ret_len = php_u_dirname(ret.u, str_len);
    2021                 :         } else {
    2022            3006 :                 ret.s = estrndup(str.s, str_len);
    2023            3006 :                 ret_len = php_dirname(ret.s, str_len);
    2024                 :         }
    2025                 : 
    2026           21319 :         RETURN_ZSTRL(str_type, ret, (int)ret_len, 0);
    2027                 : }
    2028                 : /* }}} */
    2029                 : 
    2030                 : /* {{{ proto array pathinfo(string path[, int options]) U
    2031                 :    Returns information about a certain string */
    2032                 : PHP_FUNCTION(pathinfo)
    2033             605 : {
    2034             605 :         zstr path, ret = NULL_ZSTR;
    2035                 :         int path_len, have_basename, have_ext, have_filename;
    2036                 :         zend_uchar path_type;
    2037                 :         size_t ret_len;
    2038                 :         zval *tmp;
    2039             605 :         long opt = PHP_PATHINFO_ALL;
    2040                 : 
    2041             605 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t|l", &path, &path_len, &path_type, &opt) == FAILURE) {
    2042              25 :                 return;
    2043                 :         }
    2044                 : 
    2045             580 :         have_basename = ((opt & PHP_PATHINFO_BASENAME) == PHP_PATHINFO_BASENAME);
    2046             580 :         have_filename = ((opt & PHP_PATHINFO_FILENAME) == PHP_PATHINFO_FILENAME);
    2047             580 :         have_ext = ((opt & PHP_PATHINFO_EXTENSION) == PHP_PATHINFO_EXTENSION);
    2048                 : 
    2049             580 :         MAKE_STD_ZVAL(tmp);
    2050             580 :         array_init(tmp);
    2051                 : 
    2052             580 :         if ((opt & PHP_PATHINFO_DIRNAME) == PHP_PATHINFO_DIRNAME) {
    2053             247 :                 if (path_type == IS_UNICODE) {
    2054             245 :                         ret.u = eustrndup(path.u, path_len);
    2055             245 :                         ret_len = php_u_dirname(ret.u, path_len);
    2056                 :                 } else {
    2057               2 :                         ret.s = estrndup(path.s, path_len);
    2058               2 :                         ret_len = php_dirname(ret.s, path_len);
    2059                 :                 }
    2060             247 :                 if (ret_len > 0) {
    2061             224 :                         add_ascii_assoc_zstrl(tmp, "dirname", path_type, ret, ret_len, 1);
    2062                 :                 }
    2063             247 :                 efree(ret.v);
    2064             247 :                 ret = NULL_ZSTR;
    2065                 :         }
    2066                 : 
    2067             580 :         if (have_basename || have_ext || have_filename) {
    2068             447 :                 if (path_type == IS_UNICODE) {
    2069             443 :                         php_u_basename(path.u, path_len, NULL, 0, &ret.u, &ret_len TSRMLS_CC);
    2070                 :                 } else {
    2071               4 :                         php_basename(path.s, path_len, NULL, 0, &ret.s, &ret_len TSRMLS_CC);
    2072                 :                 }
    2073                 :         }
    2074                 :         
    2075             580 :         if (have_basename) {
    2076             227 :                 add_ascii_assoc_zstrl(tmp, "basename", path_type, ret, ret_len, 0);
    2077                 :         }
    2078                 : 
    2079             580 :         if (have_ext) {
    2080                 :                 zstr ext;
    2081             228 :                 int ext_len = 0;
    2082                 : 
    2083             228 :                 if (path_type == IS_UNICODE) {
    2084             226 :                         ext.u = u_memrchr32(ret.u, (UChar) 0x2e /*'.'*/, ret_len);
    2085             226 :                         if (ext.u) {
    2086             120 :                                 ext.u++;
    2087             120 :                                 ext_len = ret_len - (ext.u - ret.u);
    2088                 :                         }
    2089                 :                 } else {
    2090               2 :                         ext.s = zend_memrchr(ret.s, '.', ret_len);
    2091               2 :                         if (ext.s) {
    2092               2 :                                 ext.s++;
    2093               2 :                                 ext_len = ret_len - (ext.s - ret.s);
    2094                 :                         }
    2095                 :                 }
    2096                 : 
    2097             228 :                 if (ext.v) {
    2098             122 :                         add_ascii_assoc_zstrl(tmp, "extension", path_type, ext, ext_len, 1);
    2099                 :                 }
    2100                 :         }
    2101                 : 
    2102             580 :         if (have_filename) {
    2103                 :                 zstr p;
    2104                 :                 int idx;
    2105                 : 
    2106             232 :                 if (path_type == IS_UNICODE) {
    2107             230 :                         p.u = u_memrchr32(ret.u, (UChar) 0x2e /*'.'*/, ret_len);
    2108             230 :                         idx = p.u ? (p.u - ret.u) : ret_len;
    2109                 :                 } else {
    2110               2 :                         p.s = zend_memrchr(ret.s, '.', ret_len);
    2111               2 :                         idx = p.s ? (p.s - ret.s) : ret_len;
    2112                 :                 }
    2113                 : 
    2114             232 :                 add_ascii_assoc_zstrl(tmp, "filename", path_type, ret, idx, 1);
    2115                 :         }
    2116                 : 
    2117             580 :         if (!have_basename && ret.v) {
    2118             220 :                 efree(ret.v);
    2119                 :         }
    2120                 : 
    2121             580 :         if (opt == PHP_PATHINFO_ALL) {
    2122             113 :                 RETURN_ZVAL(tmp, 0, 1);
    2123                 :         } else {
    2124                 :                 zval **element;
    2125             467 :                 if (zend_hash_get_current_data(Z_ARRVAL_P(tmp), (void **) &element) == SUCCESS) {
    2126             393 :                         RETVAL_ZVAL(*element, 1, 0);
    2127                 :                 } else {
    2128              74 :                         ZVAL_EMPTY_UNICODE(return_value);
    2129                 :                 }
    2130                 :         }
    2131                 : 
    2132             467 :         zval_ptr_dtor(&tmp);
    2133                 : }
    2134                 : /* }}} */
    2135                 : 
    2136                 : /* {{{ php_u_stristr
    2137                 :    Unicode version of case insensitve strstr */
    2138                 : PHPAPI UChar *php_u_stristr(UChar *str, UChar *pat, int str_len, int pat_len, zend_bool find_first TSRMLS_DC)
    2139            7908 : {
    2140                 :         UChar *str_fold, *pat_fold;
    2141                 :         int str_fold_len, pat_fold_len;
    2142                 :         UChar *result, *found;
    2143                 :         int offset;
    2144            7908 :         UErrorCode status = U_ZERO_ERROR;
    2145                 : 
    2146            7908 :         zend_case_fold_string(&str_fold, &str_fold_len, str, str_len, U_FOLD_CASE_DEFAULT, &status);
    2147            7908 :         if (str_fold_len == str_len) {
    2148            7908 :                 zend_case_fold_string(&pat_fold, &pat_fold_len, pat, pat_len, U_FOLD_CASE_DEFAULT, &status);
    2149            7908 :                 if (find_first) {
    2150            7567 :                         found = u_strFindFirst(str_fold, str_fold_len, pat_fold, pat_fold_len);
    2151                 :                 } else {
    2152             341 :                         found = u_strFindLast(str_fold, str_fold_len, pat_fold, pat_fold_len);
    2153                 :                 }
    2154            7908 :                 if (found) {
    2155            2045 :                         result = str + (found - str_fold);
    2156                 :                 } else {
    2157            5863 :                         result = NULL;
    2158                 :                 }
    2159            7908 :                 efree(pat_fold);
    2160                 :         } else {
    2161               0 :                 usearch_setText(UG(root_search), str, str_len, &status);
    2162               0 :                 usearch_setPattern(UG(root_search), pat, pat_len, &status);
    2163               0 :                 usearch_setOffset(UG(root_search), 0, &status);
    2164                 : 
    2165               0 :                 if (find_first) {
    2166               0 :                         offset = usearch_first(UG(root_search), &status);
    2167                 :                 } else {
    2168               0 :                         offset = usearch_last(UG(root_search), &status);
    2169                 :                 }
    2170               0 :                 if (offset != USEARCH_DONE) {
    2171               0 :                         result = str + offset;
    2172                 :                 } else {
    2173               0 :                         result = NULL;
    2174                 :                 }
    2175                 :         }
    2176            7908 :         efree(str_fold);
    2177                 : 
    2178            7908 :         return result;
    2179                 : }
    2180                 : 
    2181                 : #if 0
    2182                 : PHPAPI UChar *php_u_stristr(UChar *s, UChar *t, int s_len, int t_len TSRMLS_DC)
    2183                 : {
    2184                 :         int32_t i,j, last;
    2185                 :         UChar32 ch1, ch2;
    2186                 : 
    2187                 :         /* Have to do this by hand since lower-casing can change lengths
    2188                 :            by changing codepoints, and an offset within the lower-case &
    2189                 :            upper-case strings might be different codepoints.
    2190                 : 
    2191                 :            Find an occurrence of the first codepoint of 't' in 's', and
    2192                 :            starting from this point, match the rest of the codepoints of
    2193                 :            't' with those in 's'. Comparisons are performed against
    2194                 :            lower-case equivalents of the codepoints being matched.
    2195                 : 
    2196                 :            'i' & 'j' are indices used for extracting codepoints 'ch1' &
    2197                 :            'ch2'. 'last' is offset in 's' where the search for 't'
    2198                 :            started, and indicates beginning of 't' in 's' for a successful
    2199                 :            match.
    2200                 :         */
    2201                 : 
    2202                 :         i = 0;
    2203                 :         while (i <= (s_len-t_len)) {
    2204                 :                 last = i;
    2205                 :                 U16_NEXT(s, i, s_len, ch1);
    2206                 :                 j = 0;
    2207                 :                 U16_NEXT(t, j, t_len, ch2);
    2208                 :                 if (u_tolower(ch1) == u_tolower(ch2)) {
    2209                 :                         while (j < t_len) {
    2210                 :                                 U16_NEXT(s, i, s_len, ch1);
    2211                 :                                 U16_NEXT(t, j, t_len, ch2);
    2212                 :                                 if (u_tolower(ch1) != u_tolower(ch2)) {
    2213                 :                                         /* U16_NEXT() incr 'i' beyond 'ch1', re-adjust to
    2214                 :                                            restart compare
    2215                 :                                         */
    2216                 :                                         U16_BACK_1(s, 0, i);
    2217                 :                                         break;
    2218                 :                                 }
    2219                 :                         }
    2220                 :                         if (u_tolower(ch1) == u_tolower(ch2)) {
    2221                 :                                 return s+last;
    2222                 :                         }
    2223                 :                 }
    2224                 :         }
    2225                 :         return NULL;
    2226                 : }
    2227                 : #endif
    2228                 : /* }}} */
    2229                 : 
    2230                 : /* {{{ php_stristr
    2231                 :    case insensitve strstr */
    2232                 : PHPAPI char *php_stristr(char *s, char *t, size_t s_len, size_t t_len)
    2233             122 : {
    2234             122 :         php_strtolower(s, s_len);
    2235             122 :         php_strtolower(t, t_len);
    2236             122 :         return php_memnstr(s, t, t_len, s + s_len);
    2237                 : }
    2238                 : /* }}} */
    2239                 : 
    2240                 : /* {{{ php_u_strspn
    2241                 :  */
    2242                 : PHPAPI int php_u_strspn(UChar *s1, UChar *s2, UChar *s1_end, UChar *s2_end)
    2243            3381 : {
    2244            3381 :         int32_t len1 = s1_end - s1;
    2245            3381 :         int32_t len2 = s2_end - s2;
    2246                 :         int32_t i;
    2247                 :         int codepts;
    2248                 :         UChar32 ch;
    2249                 : 
    2250            6578 :         for (i = 0, codepts = 0 ; i < len1 ; codepts++) {
    2251            6136 :                 U16_NEXT(s1, i, len1, ch);
    2252            6136 :                 if (u_memchr32(s2, ch, len2) == NULL) {
    2253            2939 :                         break;
    2254                 :                 }
    2255                 :         }
    2256            3381 :         return codepts;
    2257                 : }
    2258                 : /* }}} */
    2259                 : 
    2260                 : /* {{{ php_strspn
    2261                 :  */
    2262                 : PHPAPI size_t php_strspn(char *s1, char *s2, char *s1_end, char *s2_end)
    2263               0 : {
    2264               0 :         register const char *p = s1, *spanp;
    2265               0 :         register char c = *p;
    2266                 : 
    2267               0 : cont:
    2268               0 :         for (spanp = s2; p != s1_end && spanp != s2_end;) {
    2269               0 :                 if (*spanp++ == c) {
    2270               0 :                         c = *(++p);
    2271               0 :                         goto cont;
    2272                 :                 }
    2273                 :         }
    2274               0 :         return (p - s1);
    2275                 : }
    2276                 : /* }}} */
    2277                 : 
    2278                 : /* {{{ php_u_strcspn
    2279                 :  */
    2280                 : PHPAPI int php_u_strcspn(UChar *s1, UChar *s2, UChar *s1_end, UChar *s2_end)
    2281            3233 : {
    2282            3233 :         int32_t len1 = s1_end - s1;
    2283            3233 :         int32_t len2 = s2_end - s2;
    2284                 :         int32_t i;
    2285                 :         int codepts;
    2286                 :         UChar32 ch;
    2287                 : 
    2288           27898 :         for (i = 0, codepts = 0 ; i < len1 ; ) {
    2289           22548 :                 U16_NEXT(s1, i, len1, ch);
    2290           22548 :                 if (len2) {
    2291           15319 :                         if (u_memchr32(s2, ch, len2)) {
    2292             857 :                                 break;
    2293                 :                         }
    2294            7229 :                 } else if (ch == (UChar32)0x00){
    2295             259 :                         break;
    2296                 :                 }
    2297           21432 :                 codepts++;
    2298                 :         }
    2299            3233 :         return codepts;
    2300                 : }
    2301                 : /* }}} */
    2302                 : 
    2303                 : /* {{{ php_strcspn
    2304                 :  */
    2305                 : PHPAPI size_t php_strcspn(char *s1, char *s2, char *s1_end, char *s2_end)
    2306               0 : {
    2307                 :         register const char *p, *spanp;
    2308               0 :         register char c = *s1;
    2309                 : 
    2310               0 :         for (p = s1;;) {
    2311               0 :                 spanp = s2;
    2312                 :                 do {
    2313               0 :                         if (*spanp == c || p == s1_end) {
    2314               0 :                                 return p - s1;
    2315                 :                         }
    2316               0 :                 } while (spanp++ < (s2_end - 1));
    2317               0 :                 c = *++p;
    2318               0 :         }
    2319                 :         /* NOTREACHED */
    2320                 : }
    2321                 : /* }}} */
    2322                 : 
    2323                 : /* {{{ php_needle_char
    2324                 :  */
    2325                 : static long php_needle_char(zval *needle TSRMLS_DC)
    2326             331 : {
    2327             331 :         switch (Z_TYPE_P(needle)) {
    2328                 :                 case IS_LONG:
    2329                 :                 case IS_BOOL:
    2330                 :                 case IS_RESOURCE:
    2331             164 :                         return Z_LVAL_P(needle);
    2332                 :                 case IS_DOUBLE:
    2333              52 :                         return (long)Z_DVAL_P(needle);
    2334                 :                 case IS_NULL:
    2335              87 :                         return 0;
    2336                 :                 case IS_OBJECT:
    2337                 :                         {
    2338               9 :                                 zval holder = *needle;
    2339               9 :                                 zval_copy_ctor(&(holder));
    2340               9 :                                 convert_to_long(&(holder));
    2341               9 :                                 if(Z_TYPE(holder) != IS_LONG) {
    2342               0 :                                         return -1;
    2343                 :                                 }
    2344               9 :                                 return (char)Z_LVAL(holder);
    2345                 :                         }
    2346                 :                 default: {
    2347              19 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "needle is not a string or an integer");
    2348              19 :                         return -1;
    2349                 :                  }
    2350                 :         }
    2351                 : }
    2352                 : /* }}} */
    2353                 : 
    2354                 : static zstr php_needle_to_type(zval **needle, zend_uchar haystack_type, int *target_len, char *char_buf, UChar *uchar_buf TSRMLS_DC)
    2355          893312 : {
    2356                 :         zstr target;
    2357                 : 
    2358          893312 :         target.v = NULL;
    2359         1786307 :         if (Z_TYPE_PP(needle) == IS_UNICODE || Z_TYPE_PP(needle) == IS_STRING) {
    2360          893043 :                 if (!Z_UNILEN_PP(needle)) {
    2361              48 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty delimiter");
    2362              48 :                         return target;
    2363                 :                 }
    2364          892995 :                 if (haystack_type != Z_TYPE_PP(needle)) {
    2365           30263 :                         convert_to_explicit_type_ex(needle, haystack_type);
    2366                 :                 }
    2367          892995 :                 target = Z_UNIVAL_PP(needle);
    2368          892995 :                 *target_len = Z_UNILEN_PP(needle);
    2369                 :         } else {
    2370             269 :                 long needleval = php_needle_char(*needle TSRMLS_CC);
    2371             269 :                 if(needleval == -1) {
    2372              14 :                         return target;
    2373                 :                 }
    2374             255 :                 *target_len = 0;
    2375             255 :                 if (haystack_type == IS_UNICODE) {
    2376             255 :                         if (needleval < 0 || needleval > 0x10FFFF) {
    2377              29 :                                 php_error(E_WARNING, "Needle argument codepoint value out of range (0 - 0x10FFFF)");
    2378              29 :                                 return target;
    2379                 :                         }
    2380             226 :                         *target_len = zend_codepoint_to_uchar((UChar)needleval, uchar_buf);
    2381             226 :                         uchar_buf[*target_len] = 0;
    2382             226 :                         target.u = uchar_buf;
    2383                 :                 } else {
    2384               0 :                         char_buf[(*target_len)++] = (char)needleval;
    2385               0 :                         char_buf[*target_len] = 0;
    2386               0 :                         target.s = char_buf;
    2387                 :                 }
    2388                 :         }
    2389          893221 :         return target;
    2390                 : }
    2391                 : 
    2392                 : /* {{{ proto string stristr(string haystack, string needle[, bool part]) U
    2393                 :    Finds first occurrence of a string within another, case insensitive */
    2394                 : PHP_FUNCTION(stristr)
    2395            7367 : {
    2396                 :         zstr haystack, target;
    2397            7367 :         int haystack_len, needle_len = 0;
    2398                 :         zend_uchar haystack_type;
    2399                 :         zval **needle;
    2400                 :         char needle_char[2];
    2401                 :         UChar u_needle_char[3];
    2402            7367 :         void *found = NULL;
    2403            7367 :         zend_bool part = 0;
    2404            7367 :         char *haystack_copy = NULL;
    2405                 :         int found_offset;
    2406                 :         void *start, *end;
    2407                 : 
    2408            7367 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "tZ|b", &haystack, &haystack_len, &haystack_type, &needle, &part) == FAILURE) {
    2409              11 :                 return;
    2410                 :         }
    2411                 : 
    2412            7356 :         target = php_needle_to_type(needle, haystack_type, &needle_len, needle_char, u_needle_char TSRMLS_CC);
    2413                 : 
    2414            7356 :         if(target.v == NULL) {
    2415              12 :                 RETURN_FALSE;
    2416                 :         }
    2417                 : 
    2418            7344 :         if (needle_len > haystack_len) {
    2419             130 :                 RETURN_FALSE;
    2420                 :         }
    2421                 : 
    2422            7214 :         if (haystack_type == IS_UNICODE) {
    2423            7092 :                 found = php_u_stristr(haystack.u, target.u, haystack_len, needle_len, 1 TSRMLS_CC);
    2424                 :         } else {
    2425             122 :                 haystack_copy = estrndup(haystack.s, haystack_len);
    2426             122 :                 found = php_stristr(haystack.s, target.s, haystack_len, needle_len);
    2427                 :         }
    2428                 : 
    2429            7214 :         if (found) {
    2430            1587 :                 if (haystack_type == IS_UNICODE) {
    2431            1473 :                         start = part ? haystack.u : found;
    2432            1473 :                         end = part ? found : (haystack.u + haystack_len);
    2433            1473 :                         RETVAL_UNICODEL((UChar *)start, (UChar *)end-(UChar *)start, 1);
    2434                 :                 } else {
    2435             114 :                         found_offset = (char *)found - haystack.s;
    2436             114 :                         start = part ? haystack_copy : haystack_copy + found_offset;
    2437             114 :                         end = part ? haystack_copy + found_offset : (haystack_copy + haystack_len);
    2438             114 :                         RETVAL_STRINGL((char *)start, (char *)end-(char *)start, 1);
    2439                 :                 }
    2440                 :         } else {
    2441            5627 :                 RETVAL_FALSE;
    2442                 :         }
    2443                 : 
    2444            7214 :         if (haystack_type != IS_UNICODE) {
    2445             122 :                 efree(haystack_copy);
    2446                 :         }
    2447                 : }
    2448                 : /* }}} */
    2449                 : 
    2450                 : /* {{{ proto string strstr(string haystack, string needle[, bool part]) U
    2451                 :    Finds first occurrence of a string within another */
    2452                 : PHP_FUNCTION(strstr)
    2453            7743 : {
    2454                 :         zstr haystack, target;
    2455            7743 :         int haystack_len, needle_len = 0;
    2456                 :         zend_uchar haystack_type;
    2457                 :         zval **needle;
    2458                 :         char needle_char[2];
    2459                 :         UChar u_needle_char[3];
    2460            7743 :         void *found = NULL;
    2461                 :         size_t found_offset;
    2462            7743 :         zend_bool part = 0;
    2463                 : 
    2464            7743 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "tZ|b", &haystack, &haystack_len, &haystack_type, &needle, &part) == FAILURE) {
    2465               6 :                 return;
    2466                 :         }
    2467                 : 
    2468            7737 :         target = php_needle_to_type(needle, haystack_type, &needle_len, needle_char, u_needle_char TSRMLS_CC);
    2469                 : 
    2470            7737 :         if(target.v == NULL) {
    2471               7 :                 RETURN_FALSE;
    2472                 :         }
    2473                 : 
    2474            7730 :         if (needle_len > haystack_len) {
    2475               7 :                 RETURN_FALSE;
    2476                 :         }
    2477                 : 
    2478            7723 :         if (haystack_type == IS_UNICODE) {
    2479            7684 :                 found = zend_u_memnstr(haystack.u, target.u, needle_len, haystack.u + haystack_len);
    2480                 :         } else {
    2481              39 :                 found = php_memnstr(haystack.s, target.s, needle_len, haystack.s + haystack_len);
    2482                 :         }
    2483                 : 
    2484            7723 :         if (found) {
    2485            2863 :                 switch (haystack_type) {
    2486                 :                         case IS_UNICODE:
    2487            2840 :                                 found_offset = (UChar*)found - haystack.u;
    2488            2840 :                                 if (part) {
    2489                 :                                         UChar *ret;
    2490               0 :                                         ret = eumalloc(found_offset + 1);
    2491               0 :                                         u_strncpy(ret, haystack.u, found_offset);
    2492               0 :                                         ret[found_offset] = '\0';
    2493               0 :                                         RETURN_UNICODEL(ret , found_offset, 0);
    2494                 :                                 } else {
    2495            2840 :                                         RETURN_UNICODEL(found, haystack_len - found_offset, 1);
    2496                 :                                 }
    2497                 :                                 break;
    2498                 : 
    2499                 :                         case IS_STRING:
    2500              23 :                                 found_offset = (char *)found - haystack.s;
    2501              23 :                                 if (part) {
    2502                 :                                         char *ret;
    2503               0 :                                         ret = emalloc(found_offset + 1);
    2504               0 :                                         strncpy(ret, haystack.s, found_offset);
    2505               0 :                                         ret[found_offset] = '\0';
    2506               0 :                                         RETURN_STRINGL(ret , found_offset, 0);
    2507                 :                                 } else {
    2508              23 :                                         RETURN_STRINGL(found, haystack_len - found_offset, 1);
    2509                 :                                 }
    2510                 :                                 break;
    2511                 :                 }
    2512                 :         } else {
    2513            4860 :                 RETURN_FALSE;
    2514                 :         }
    2515                 : }
    2516                 : /* }}} */
    2517                 : 
    2518                 : /* {{{ proto string strchr(string haystack, string needle[, bool part]) U
    2519                 :    An alias for strstr */
    2520                 : /* }}} */
    2521                 : 
    2522                 : /* {{{ proto int strpos(string haystack, mixed needle [, int offset]) U
    2523                 :    Finds position of first occurrence of a string within another */
    2524                 : PHP_FUNCTION(strpos)
    2525          876934 : {
    2526                 :         zstr haystack, target;
    2527          876934 :         int haystack_len, needle_len = 0;
    2528                 :         zend_uchar haystack_type;
    2529                 :         zval **needle;
    2530                 :         char needle_char[2];
    2531                 :         UChar u_needle_char[3];
    2532          876934 :         void *found = NULL;
    2533          876934 :         long   offset = 0;
    2534          876934 :         int32_t cu_offset = 0;
    2535                 : 
    2536          876934 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "tZ|l", &haystack,
    2537                 :                                                           &haystack_len, &haystack_type, &needle, &offset) == FAILURE) {
    2538               9 :                 return;
    2539                 :         }
    2540                 : 
    2541                 :         /*
    2542                 :          * Unicode note: it's okay to not convert offset to code unit offset here.
    2543                 :          * We'll just do a rough check that the offset does not exceed length in
    2544                 :          * code units, and leave the rest to zend_u_memnstr().
    2545                 :          */
    2546          876925 :         if (offset < 0 || offset > haystack_len) {
    2547               7 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset not contained in string");
    2548               7 :                 RETURN_FALSE;
    2549                 :         }
    2550                 : 
    2551          876918 :         target = php_needle_to_type(needle, haystack_type, &needle_len, needle_char, u_needle_char TSRMLS_CC);
    2552                 : 
    2553          876918 :         if(target.v == NULL) {
    2554               7 :                 RETURN_FALSE;
    2555                 :         }
    2556                 : 
    2557          876911 :         if (needle_len > haystack_len) {
    2558            1829 :                 RETURN_FALSE;
    2559                 :         }
    2560                 : 
    2561          875082 :         if (haystack_type == IS_UNICODE) {
    2562                 :                 /* calculate code unit offset */
    2563          850627 :                 U16_FWD_N(haystack.u, cu_offset, haystack_len, offset);
    2564          850627 :                 found = zend_u_memnstr(haystack.u + cu_offset, target.u, needle_len, haystack.u + haystack_len);
    2565                 :         } else {
    2566           24455 :                 found = php_memnstr(haystack.s + offset, target.s, needle_len, haystack.s + haystack_len);
    2567                 :         }
    2568                 : 
    2569          875082 :         if (found) {
    2570          396759 :                 if (haystack_type == IS_UNICODE) {
    2571                 :                         /* Simple subtraction will not suffice, since there may be
    2572                 :                            supplementary codepoints. We count how many codepoints there are
    2573                 :                            between the starting offset and the found location and add them
    2574                 :                            to the starting codepoint offset. */
    2575          384255 :                         RETURN_LONG(offset + u_countChar32(haystack.u + cu_offset,
    2576                 :                                                                                            (UChar*)found - (haystack.u + cu_offset)));
    2577                 :                 } else {
    2578           12504 :                         RETURN_LONG((char *)found - haystack.s);
    2579                 :                 }
    2580                 :         } else {
    2581          478323 :                 RETURN_FALSE;
    2582                 :         }
    2583                 : }
    2584                 : /* }}} */
    2585                 : 
    2586                 : /* {{{ proto int stripos(string haystack, string needle [, int offset]) U
    2587                 :    Finds position of first occurrence of a string within another, case insensitive */
    2588                 : PHP_FUNCTION(stripos)
    2589             603 : {
    2590                 :         zstr haystack, target;
    2591             603 :         int haystack_len, needle_len = 0;
    2592                 :         zend_uchar haystack_type;
    2593                 :         zval **needle;
    2594                 :         char needle_char[2];
    2595                 :         UChar u_needle_char[3];
    2596             603 :         void *found = NULL;
    2597             603 :         long offset = 0;
    2598             603 :         char *haystack_dup = NULL, *needle_dup = NULL;
    2599             603 :         int cu_offset = 0;
    2600                 : 
    2601             603 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "tZ|l", &haystack, &haystack_len, &haystack_type, &needle, &offset) == FAILURE) {
    2602              39 :                 return;
    2603                 :         }
    2604                 : 
    2605                 :         /*
    2606                 :          * Unicode note: it's okay to not convert offset to code unit offset here.
    2607                 :          * We'll just do a rough check that the offset does not exceed length in
    2608                 :          * code units, and leave the rest to zend_u_memnstr().
    2609                 :          */
    2610             564 :         if (offset < 0 || offset > haystack_len) {
    2611              22 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset not contained in string");
    2612              22 :                 RETURN_FALSE;
    2613                 :         }
    2614                 : 
    2615             542 :         if (haystack_len == 0) {
    2616              30 :                 RETURN_FALSE;
    2617                 :         }
    2618                 : 
    2619             512 :         target = php_needle_to_type(needle, haystack_type, &needle_len, needle_char, u_needle_char TSRMLS_CC);
    2620                 : 
    2621             512 :         if(target.v == NULL) {
    2622              22 :                 RETURN_FALSE;
    2623                 :         }
    2624                 : 
    2625             490 :         if (needle_len > haystack_len) {
    2626              12 :                 RETURN_FALSE;
    2627                 :         }
    2628                 : 
    2629             478 :         if(haystack_type == IS_UNICODE) {
    2630             475 :                 U16_FWD_N(haystack.u, cu_offset, haystack_len, offset);
    2631             475 :                 found = php_u_stristr(haystack.u + cu_offset, target.u, haystack_len - cu_offset, needle_len, 1 TSRMLS_CC);
    2632                 :         } else {
    2633               3 :                         haystack_dup = estrndup(haystack.s, haystack_len);
    2634               3 :                         php_strtolower(haystack_dup, haystack_len);
    2635               3 :                         needle_dup = estrndup(target.s, needle_len);
    2636               3 :                         php_strtolower(needle_dup, Z_STRLEN_PP(needle));
    2637               3 :                         found = php_memnstr(haystack_dup + offset,
    2638                 :                                                                 needle_dup, needle_len,
    2639                 :                                                                 haystack_dup + haystack_len);
    2640               3 :                         efree(haystack_dup);
    2641               3 :                         efree(needle_dup);
    2642                 :         }
    2643                 : 
    2644             478 :         if (found) {
    2645             321 :                 if (haystack_type == IS_UNICODE) {
    2646                 :                         /* Simple subtraction will not suffice, since there may be
    2647                 :                            supplementary codepoints. We count how many codepoints there are
    2648                 :                            between the starting offset and the found location and add them
    2649                 :                            to the starting codepoint offset. */
    2650             319 :                         RETURN_LONG(offset + u_countChar32(haystack.u + cu_offset,
    2651                 :                                                                                            (UChar*)found - (haystack.u + cu_offset)));
    2652                 :                 } else {
    2653               2 :                         RETURN_LONG((char *)found - haystack_dup);
    2654                 :                 }
    2655                 :         } else {
    2656             157 :                 RETURN_FALSE;
    2657                 :         }
    2658                 : }
    2659                 : /* }}} */
    2660                 : 
    2661                 : /* {{{ proto int strrpos(string haystack, string needle [, int offset]) U
    2662                 :    Finds position of last occurrence of a string within another string */
    2663                 : PHP_FUNCTION(strrpos)
    2664             467 : {
    2665                 :         zstr haystack, target;
    2666             467 :         int haystack_len, needle_len = 0;
    2667                 :         zend_uchar haystack_type;
    2668                 :         zval **needle;
    2669                 :         char needle_char[2];
    2670                 :         UChar u_needle_char[3];
    2671             467 :         long offset = 0;
    2672                 :         char *p, *e;
    2673                 :         UChar *pos, *u_p, *u_e;
    2674             467 :         int cu_offset = 0;
    2675                 : 
    2676             467 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "tZ|l", &haystack, &haystack_len, &haystack_type, &needle, &offset) == FAILURE) {
    2677              39 :                 return;
    2678                 :         }
    2679                 : 
    2680             428 :         target = php_needle_to_type(needle, haystack_type, &needle_len, needle_char, u_needle_char TSRMLS_CC);
    2681                 : 
    2682             428 :         if(target.v == NULL) {
    2683              32 :                 RETURN_FALSE;
    2684                 :         }
    2685                 : 
    2686             396 :         if ((haystack_len == 0) || (needle_len == 0) || needle_len > haystack_len) {
    2687              41 :                 RETURN_FALSE;
    2688                 :         }
    2689                 : 
    2690             355 :         if (haystack_type == IS_UNICODE) {
    2691             351 :                 if (offset >= 0) {
    2692             336 :                         U16_FWD_N(haystack.u, cu_offset, haystack_len, offset);
    2693             336 :                         if (cu_offset > haystack_len - needle_len) {
    2694              24 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset is greater than the length of haystack string");
    2695              24 :                                 RETURN_FALSE;
    2696                 :                         }
    2697             312 :                         u_p = haystack.u + cu_offset;
    2698             312 :                         u_e = haystack.u + haystack_len - needle_len;
    2699                 :                 } else {
    2700              15 :                         u_p = haystack.u;
    2701              15 :                         if (-offset > haystack_len || offset < -INT_MAX) {
    2702               9 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset is greater than the length of haystack string");
    2703               9 :                                 RETURN_FALSE;
    2704                 :                         } else {
    2705               6 :                                 cu_offset = haystack_len;
    2706               6 :                                 U16_BACK_N(haystack.u, 0, cu_offset, -offset);
    2707               6 :                                 if (cu_offset == 0) {
    2708               0 :                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset is greater than the length of haystack string");
    2709               0 :                                         RETURN_FALSE;
    2710                 :                                 }
    2711               6 :                                 if (needle_len > haystack_len - cu_offset) {
    2712               3 :                                         u_e = haystack.u + haystack_len - needle_len;
    2713                 :                                 } else {
    2714               3 :                                         u_e = haystack.u + cu_offset;
    2715                 :                                 }
    2716                 :                         }
    2717                 :                 }
    2718                 : 
    2719             318 :                 pos = u_strFindLast(u_p, u_e-u_p+needle_len, target.u, needle_len);
    2720             318 :                 if (pos) {
    2721             197 :                         if (offset > 0) {
    2722              44 :                                 RETURN_LONG(offset + u_countChar32(u_p, (UChar*)pos - u_p));
    2723                 :                         } else {
    2724             153 :                                 RETURN_LONG(u_countChar32(haystack.u, (UChar*)pos - haystack.u));
    2725                 :                         }
    2726                 :                 } else {
    2727             121 :                         RETURN_FALSE;
    2728                 :                 }
    2729                 :         } else {
    2730               4 :                 if (offset >= 0) {
    2731               0 :                         if (offset > haystack_len) {
    2732               0 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset is greater than the length of haystack string");
    2733               0 :                                 RETURN_FALSE;
    2734                 :                         }
    2735               0 :                         p = haystack.s + offset;
    2736               0 :                         e = haystack.s + haystack_len - needle_len;
    2737                 :                 } else {
    2738               4 :                         if (-offset > haystack_len || offset < -INT_MAX) {
    2739               2 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset is greater than the length of haystack string");
    2740               2 :                                 RETURN_FALSE;
    2741                 :                         }
    2742                 : 
    2743               2 :                         p = haystack.s;
    2744               2 :                         if (needle_len > -offset) {
    2745               0 :                                 e = haystack.s + haystack_len - needle_len;
    2746                 :                         } else {
    2747               2 :                                 e = haystack.s + haystack_len + offset;
    2748                 :                         }
    2749                 :                 }
    2750                 : 
    2751               2 :                 if (needle_len == 1) {
    2752                 :                         /* Single character search can shortcut memcmps */
    2753              13 :                         while (e >= p) {
    2754              10 :                                 if (*e == *target.s) {
    2755               1 :                                         RETURN_LONG(e - p + (offset > 0 ? offset : 0));
    2756                 :                                 }
    2757               9 :                                 e--;
    2758                 :                         }
    2759               1 :                         RETURN_FALSE;
    2760                 :                 }
    2761                 : 
    2762               0 :                 while (e >= p) {
    2763               0 :                         if (memcmp(e, target.s, needle_len) == 0) {
    2764               0 :                                 RETURN_LONG(e - p + (offset > 0 ? offset : 0));
    2765                 :                         }
    2766               0 :                         e--;
    2767                 :                 }
    2768                 :         }
    2769                 : 
    2770               0 :         RETURN_FALSE;
    2771                 : }
    2772                 : /* }}} */
    2773                 : 
    2774                 : /* {{{ proto int strripos(string haystack, string needle [, int offset]) U
    2775                 :    Finds position of last occurrence of a string within another string */
    2776                 : PHP_FUNCTION(strripos)
    2777             365 : {
    2778                 :         zstr haystack, target;
    2779             365 :         int haystack_len, needle_len = 0;
    2780                 :         zend_uchar haystack_type;
    2781                 :         zval **needle;
    2782                 :         char needle_char[2];
    2783                 :         UChar u_needle_char[3];
    2784             365 :         long offset = 0;
    2785                 :         char *p, *e;
    2786                 :         UChar *u_p, *u_e, *pos;
    2787             365 :         char *needle_dup = NULL, *haystack_dup = NULL;
    2788             365 :         int cu_offset = 0;
    2789                 : 
    2790             365 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "tZ|l", &haystack, &haystack_len, &haystack_type, &needle, &offset) == FAILURE) {
    2791               4 :                 RETURN_FALSE;
    2792                 :         }
    2793                 : 
    2794             361 :         target = php_needle_to_type(needle, haystack_type, &needle_len, needle_char, u_needle_char TSRMLS_CC);
    2795                 : 
    2796             361 :         if(target.v == NULL) {
    2797              11 :                 RETURN_FALSE;
    2798                 :         }
    2799                 : 
    2800             350 :         if ((haystack_len == 0) || (needle_len == 0) || needle_len > haystack_len) {
    2801               1 :                 RETURN_FALSE;
    2802                 :         }
    2803                 : 
    2804             349 :         if (haystack_type == IS_UNICODE) {
    2805             349 :                 if (offset >= 0) {
    2806             262 :                         U16_FWD_N(haystack.u, cu_offset, haystack_len, offset);
    2807             262 :                         if (cu_offset > haystack_len) {
    2808               0 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset is greater than the length of haystack string");
    2809               0 :                                 RETURN_FALSE;
    2810                 :                         }
    2811             262 :                         u_p = haystack.u + cu_offset;
    2812             262 :                         u_e = haystack.u + haystack_len - needle_len;
    2813                 :                 } else {
    2814              87 :                         u_p = haystack.u;
    2815              87 :                         if (-offset > haystack_len || offset < -INT_MAX) {
    2816               8 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset is greater than the length of haystack string");
    2817               8 :                                 RETURN_FALSE;
    2818                 :                         } else {
    2819              79 :                                 cu_offset = haystack_len;
    2820              79 :                                 U16_BACK_N(haystack.u, 0, cu_offset, -offset);
    2821              79 :                                 if (cu_offset == 0) {
    2822               0 :                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset is greater than the length of haystack string");
    2823               0 :                                         RETURN_FALSE;
    2824                 :                                 }
    2825              79 :                                 if (needle_len > haystack_len - cu_offset) {
    2826              36 :                                         u_e = haystack.u + haystack_len - needle_len;
    2827                 :                                 } else {
    2828              43 :                                         u_e = haystack.u + cu_offset;
    2829                 :                                 }
    2830                 :                         }
    2831                 :                 }
    2832                 : 
    2833             341 :                 pos = php_u_stristr(u_p, target.u, u_e-u_p+needle_len, needle_len, 0 TSRMLS_CC);
    2834             341 :                 if (pos) {
    2835             253 :                         if (offset > 0) {
    2836              98 :                                 RETURN_LONG(offset + u_countChar32(u_p, (UChar*)pos - u_p));
    2837                 :                         } else {
    2838             155 :                                 RETURN_LONG(u_countChar32(haystack.u, (UChar*)pos - haystack.u));
    2839                 :                         }
    2840                 :                 } else {
    2841              88 :                         RETURN_FALSE;
    2842                 :                 }
    2843                 :         } else {
    2844               0 :                 if (needle_len == 1) {
    2845                 :                         /* Single character search can shortcut memcmps
    2846                 :                            Can also avoid tolower emallocs */
    2847               0 :                         if (offset >= 0) {
    2848               0 :                                 if (offset > haystack_len) {
    2849               0 :                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset is greater than the length of haystack string");
    2850               0 :                                         RETURN_FALSE;
    2851                 :                                 }
    2852               0 :                                 p = haystack.s + offset;
    2853               0 :                                 e = haystack.s + haystack_len - 1;
    2854                 :                         } else {
    2855               0 :                                 p = haystack.s;
    2856               0 :                                 if (-offset > haystack_len || offset < INT_MAX) {
    2857               0 :                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset is greater than the length of haystack string");
    2858               0 :                                         RETURN_FALSE;
    2859                 :                                 } else {
    2860               0 :                                         e = haystack.s + haystack_len + offset;
    2861                 :                                 }
    2862                 :                         }
    2863                 :                         /* Borrow that needle_char buffer to avoid repeatedly tolower()ing needle */
    2864               0 :                         *needle_char = tolower(*target.s);
    2865               0 :                         while (e >= p) {
    2866               0 :                                 if (tolower(*e) == *needle_char) {
    2867               0 :                                         RETURN_LONG(e - p + (offset > 0 ? offset : 0));
    2868                 :                                 }
    2869               0 :                                 e--;
    2870                 :                         }
    2871               0 :                         RETURN_FALSE;
    2872                 :                 }
    2873                 : 
    2874               0 :                 needle_dup = estrndup(target.s, needle_len);
    2875               0 :                 php_strtolower(needle_dup, needle_len);
    2876               0 :                 haystack_dup = estrndup(haystack.s, haystack_len);
    2877               0 :                 php_strtolower(haystack_dup, haystack_len);
    2878                 : 
    2879               0 :                 if (offset >= 0) {
    2880               0 :                         if (offset > haystack_len) {
    2881               0 :                                 efree(haystack_dup);
    2882               0 :                                 efree(needle_dup);
    2883               0 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset is greater than the length of haystack string");
    2884               0 :                                 RETURN_FALSE;
    2885                 :                         }
    2886               0 :                         p = haystack_dup + offset;
    2887               0 :                         e = haystack_dup + haystack_len - needle_len;
    2888                 :                 } else {
    2889               0 :                         if (-offset > haystack_len || offset < -INT_MAX) {
    2890               0 :                                 efree(haystack_dup);
    2891               0 :                                 efree(needle_dup);
    2892               0 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset is greater than the length of haystack string");
    2893               0 :                                 RETURN_FALSE;
    2894                 :                         } 
    2895               0 :                         p = haystack_dup;
    2896               0 :                         if (needle_len > -offset) {
    2897               0 :                                 e = haystack_dup + haystack_len - needle_len;
    2898                 :                         } else {
    2899               0 :                                 e = haystack_dup + haystack_len + offset;
    2900                 :                         }
    2901                 :                 }
    2902                 : 
    2903               0 :                 while (e >= p) {
    2904               0 :                         if (memcmp(e, needle_dup, needle_len) == 0) {
    2905               0 :                                 efree(haystack_dup);
    2906               0 :                                 efree(needle_dup);
    2907               0 :                                 RETURN_LONG(e - p + (offset > 0 ? offset : 0));
    2908                 :                         }
    2909               0 :                         e--;
    2910                 :                 }
    2911                 : 
    2912               0 :                 efree(haystack_dup);
    2913               0 :                 efree(needle_dup);
    2914                 : 
    2915               0 :                 RETURN_FALSE;
    2916                 :         }
    2917                 : }
    2918                 : /* }}} */
    2919                 : 
    2920                 : /* {{{ proto string strrchr(string haystack, string needle) U
    2921                 :    Finds the last occurrence of a character in a string within another */
    2922                 : PHP_FUNCTION(strrchr)
    2923             246 : {
    2924                 :         zstr haystack;
    2925                 :         int haystack_len;
    2926                 :         zend_uchar haystack_type;
    2927                 :         zval **needle;
    2928                 :         UChar32 ch;
    2929             246 :         void *found = NULL;
    2930                 :         int found_offset;
    2931                 : 
    2932             246 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "tZ", &haystack, &haystack_len, &haystack_type, &needle) == FAILURE) {
    2933              15 :                 return;
    2934                 :         }
    2935                 : 
    2936             383 :         if (Z_TYPE_PP(needle) == IS_UNICODE || Z_TYPE_PP(needle) == IS_STRING) {
    2937             169 :                 if (Z_TYPE_PP(needle) != haystack_type) {
    2938               1 :                         convert_to_explicit_type_ex(needle, haystack_type);
    2939                 :                 }
    2940             169 :                 if (Z_USTRLEN_PP(needle) == 0) {
    2941              17 :                         RETURN_FALSE;
    2942                 :                 }
    2943             152 :                 if (haystack_type == IS_UNICODE) {
    2944             151 :                         ch = zend_get_codepoint_at(Z_USTRVAL_PP(needle), Z_USTRLEN_PP(needle), 0);
    2945             151 :                         found = u_memrchr32(haystack.u, ch, haystack_len);
    2946                 :                 } else {
    2947               1 :                         found = zend_memrchr(haystack.s, *Z_STRVAL_PP(needle), haystack_len);
    2948                 :                 }
    2949                 :         } else {
    2950              62 :                 long needleval = php_needle_char(*needle TSRMLS_CC);
    2951              62 :                 if(needleval == -1) {
    2952               5 :                         RETURN_FALSE;
    2953                 :                 }
    2954              57 :                 if (haystack_type == IS_UNICODE) {
    2955              56 :                         if (needleval < 0 || needleval > 0x10FFFF) {
    2956               6 :                                 php_error(E_WARNING, "Needle argument codepoint value out of range (0 - 0x10FFFF)");
    2957               6 :                                 RETURN_FALSE;
    2958                 :                         }
    2959              50 :                         found = u_memrchr32(haystack.u, (UChar32)needleval, haystack_len);
    2960                 :                 } else {
    2961               1 :                         found = zend_memrchr(haystack.s, (char)needleval, haystack_len);
    2962                 :                 }
    2963                 :         }
    2964                 : 
    2965             203 :         if (found) {
    2966             144 :                 if (haystack_type == IS_UNICODE) {
    2967             142 :                         found_offset = (UChar *)found - haystack.u;
    2968             142 :                         RETURN_UNICODEL((UChar *)found, haystack_len - found_offset, 1);
    2969                 :                 } else {
    2970               2 :                         found_offset = (char *)found - haystack.s;
    2971               2 :                         RETURN_STRINGL((char *)found, haystack_len - found_offset, 1);
    2972                 :                 }
    2973                 :         } else {
    2974              59 :                 RETURN_FALSE;
    2975                 :         }
    2976                 : }
    2977                 : /* }}} */
    2978                 : 
    2979                 : /* {{{ php_chunk_split
    2980                 :  */
    2981                 : static char* php_chunk_split(char *src, int srclen, char *end, int endlen, int chunklen, int *destlen, zend_uchar str_type)
    2982             128 : {
    2983                 :         char *dest;
    2984                 :         char *p, *q;
    2985                 :         int chunks; /* complete chunks! */
    2986                 :         int restlen;
    2987             128 :         int charsize = sizeof(char);
    2988                 :         int out_len;
    2989                 : 
    2990             128 :         if (str_type == IS_UNICODE) {
    2991             128 :                 charsize = sizeof(UChar);
    2992                 :         }
    2993                 : 
    2994             128 :         chunks = srclen / chunklen;
    2995             128 :         restlen = srclen - chunks * chunklen; /* srclen % chunklen */
    2996                 : 
    2997             128 :         if(chunks > INT_MAX - 1) {
    2998               0 :                 return NULL;
    2999                 :         }
    3000             128 :         out_len = chunks + 1;
    3001             128 :         if(endlen !=0 && out_len > INT_MAX/endlen) {
    3002               2 :                 return NULL;
    3003                 :         }
    3004             126 :         out_len *= endlen;
    3005             126 :         if(out_len > INT_MAX - srclen - 1) {
    3006               0 :                 return NULL;
    3007                 :         }
    3008             126 :         out_len += srclen + 1;
    3009                 : 
    3010             126 :         if (out_len > INT_MAX/charsize) {
    3011               0 :                 return NULL;
    3012                 :         }
    3013                 : 
    3014             126 :         dest = safe_emalloc(out_len, charsize, 0);
    3015                 : 
    3016            1285 :         for (p = src, q = dest; p < (src + charsize * (srclen - chunklen + 1)); ) {
    3017            1033 :                 memcpy(q, p, chunklen * charsize);
    3018            1033 :                 q += chunklen * charsize;
    3019            1033 :                 memcpy(q, end, endlen * charsize);
    3020            1033 :                 q += endlen * charsize;
    3021            1033 :                 p += chunklen * charsize;
    3022                 :         }
    3023                 : 
    3024             126 :         if (restlen) {
    3025             114 :                 memcpy(q, p, restlen * charsize);
    3026             114 :                 q += restlen * charsize;
    3027             114 :                 memcpy(q, end, endlen * charsize);
    3028             114 :                 q += endlen * charsize;
    3029                 :         }
    3030                 : 
    3031             126 :         if (str_type == IS_UNICODE) {
    3032             126 :                 *(UChar*)q = 0;
    3033                 :         } else {
    3034               0 :                 *q = '\0';
    3035                 :         }
    3036             126 :         if (destlen) {
    3037             126 :                 *destlen = (q - dest) / charsize;
    3038                 :         }
    3039                 : 
    3040             126 :         return (dest);
    3041                 : }
    3042                 : /* }}} */
    3043                 : 
    3044                 : /* {{{ proto string chunk_split(string str [, int chunklen [, string ending]]) U
    3045                 :    Returns split line */
    3046                 : PHP_FUNCTION(chunk_split)
    3047             185 : {
    3048             185 :         zstr str, ending = NULL_ZSTR;
    3049                 :         int str_len, ending_len;
    3050                 :         zstr result;
    3051             185 :         char *end    = "\r\n";
    3052             185 :         UChar u_end[3] = { 0x0d, 0x0a, 0x0 };
    3053             185 :         long chunklen = 76;
    3054                 :         int result_len;
    3055                 :         zend_uchar str_type;
    3056                 : 
    3057             185 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "T|lT", &str, &str_len,
    3058                 :                                                           &str_type, &chunklen, &ending, &ending_len, &str_type) == FAILURE) {
    3059              25 :                 return;
    3060                 :         }
    3061                 : 
    3062             160 :         if (chunklen <= 0) {
    3063              10 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Chunk length should be greater than zero");
    3064              10 :                 RETURN_FALSE;
    3065                 :         }
    3066                 : 
    3067             150 :         if (!ending.v) {
    3068              14 :                 ending = (str_type == IS_UNICODE) ? ZSTR(u_end) : ZSTR(end);
    3069              14 :                 ending_len = 2;
    3070                 :         }
    3071                 : 
    3072             150 :         if (chunklen > str_len) {
    3073                 :                 /* to maintain BC, we must return original string + ending */
    3074              22 :                 result_len = ending_len + str_len;
    3075              22 :                 if (str_type == IS_UNICODE) {
    3076              22 :                         result.u = eumalloc(result_len + 1);
    3077              22 :                         u_memcpy(result.u, str.u, str_len);
    3078              22 :                         u_memcpy(result.u + str_len, ending.u, ending_len);
    3079              22 :                         result.u[result_len] = 0;
    3080                 :                 } else {
    3081               0 :                         result.s = emalloc(result_len + 1);
    3082               0 :                         memcpy(result.s, str.s, str_len);
    3083               0 :                         memcpy(result.s + str_len, ending.s, ending_len);
    3084               0 :                         result.s[result_len] = '\0';
    3085                 :                 }
    3086              22 :                 RETURN_ZSTRL(str_type, result, result_len, 0);
    3087                 :         }
    3088                 : 
    3089             128 :         if (!str_len) {
    3090               0 :                 RETURN_EMPTY_UNICODE();
    3091                 :         }
    3092                 : 
    3093             128 :         result.v = php_chunk_split(str.v, str_len, ending.v, ending_len, chunklen, &result_len, str_type);
    3094                 : 
    3095             128 :         if (result.v) {
    3096             126 :                 RETURN_ZSTRL(str_type, result, result_len, 0);
    3097                 :         } else {
    3098               2 :                 RETURN_FALSE;
    3099                 :         }
    3100                 : }
    3101                 : /* }}} */
    3102                 : 
    3103                 : /* {{{ proto string substr(string str, int start [, int length]) U
    3104                 :    Returns part of a string */
    3105                 : PHP_FUNCTION(substr)
    3106          136291 : {
    3107                 :         void *str;
    3108                 :         int str_len;
    3109                 :         int cp_len;
    3110                 :         zend_uchar str_type;
    3111          136291 :         long l = -1;
    3112                 :         long f;
    3113                 : 
    3114          136291 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "tl|l", &str, &str_len, &str_type, &f, &l) == FAILURE) {
    3115               6 :                 return;
    3116                 :         }
    3117                 : 
    3118          136285 :         if (str_type == IS_UNICODE) {
    3119          134058 :                 cp_len = u_countChar32(str, str_len);
    3120                 :         } else {
    3121            2227 :                 cp_len = str_len;
    3122                 :         }
    3123                 : 
    3124          136285 :         if (ZEND_NUM_ARGS() == 2) {
    3125          109781 :                 l = cp_len;
    3126                 :         }
    3127                 : 
    3128                 :         /* if "from" position is negative, count start position from the end
    3129                 :          * of the string
    3130                 :          */
    3131          136285 :         if (f < 0) {
    3132          109912 :                 f = cp_len + f;
    3133          109912 :                 if (f < 0) {
    3134           12628 :                         f = 0;
    3135                 :                 }
    3136                 :         }
    3137                 : 
    3138                 :         /* if "length" position is negative, set it to the length
    3139                 :          * needed to stop that many chars from the end of the string
    3140                 :          */
    3141          136285 :         if (l < 0) {
    3142            6147 :                 l = (cp_len - f) + l;
    3143            6147 :                 if (l < 0) {
    3144            6098 :                         l = 0;
    3145                 :                 }
    3146                 :         }
    3147                 : 
    3148          136285 :         if (f >= cp_len) {
    3149            6309 :                 RETURN_FALSE;
    3150                 :         }
    3151                 : 
    3152          129976 :         if (((unsigned) f + (unsigned) l) > cp_len) {
    3153           96731 :                 l = cp_len - f;
    3154                 :         }
    3155                 : 
    3156          129976 :         if (str_type == IS_UNICODE) {
    3157          127749 :                 int32_t start = 0, end = 0;
    3158          127749 :                 U16_FWD_N((UChar*)str, end, str_len, f);
    3159          127749 :                 start = end;
    3160          127749 :                 U16_FWD_N((UChar*)str, end, str_len, l);
    3161          127749 :                 RETURN_UNICODEL((UChar*)str + start, end-start, 1);
    3162                 :         } else {
    3163            2227 :                 RETURN_STRINGL((char*)str + f, l, 1);
    3164                 :         }
    3165                 : }
    3166                 : /* }}} */
    3167                 : 
    3168                 : /* {{{ php_adjust_limits
    3169                 :  */
    3170                 : static int php_adjust_limits(zval **str, int *f, int *l)
    3171              78 : {
    3172                 :         int str_codepts;
    3173              78 :         int ret = 1;
    3174                 : 
    3175              78 :         if (Z_TYPE_PP(str) == IS_UNICODE) {
    3176              78 :                 str_codepts = u_countChar32(Z_USTRVAL_PP(str), Z_USTRLEN_PP(str));
    3177                 :         } else {
    3178               0 :                 str_codepts = Z_STRLEN_PP(str);
    3179                 :         }
    3180                 : 
    3181                 :         /* If "from" position is negative, count start position from the end
    3182                 :          * of the string */
    3183              78 :         if (*f < 0) {
    3184               0 :                 *f = str_codepts + *f;
    3185               0 :                 if (*f < 0) {
    3186               0 :                         *f = 0;
    3187                 :                 }
    3188              78 :         } else if (*f > str_codepts) {
    3189               1 :                 *f = str_codepts;
    3190                 :         }
    3191                 :         /* If "length" position is negative, set it to the length
    3192                 :          * needed to stop that many codepts/chars from the end of the string */
    3193              78 :         if (*l < 0) {
    3194              20 :                 *l = str_codepts - *f + *l;
    3195              20 :                 if (*l < 0) {
    3196               0 :                         *l = 0;
    3197                 :                 }
    3198                 :         }
    3199              78 :         if (*f > str_codepts || (*f < 0 && -(*f) > str_codepts)) {
    3200               0 :                 ret = 0;
    3201              78 :         } else if (*l > str_codepts || (*l < 0 && -(*l) > str_codepts)) {
    3202               2 :                 *l = str_codepts;
    3203                 :         }
    3204              78 :         if (((unsigned)(*f) + (unsigned)(*l)) > str_codepts) {
    3205              17 :                 *l = str_codepts - *f;
    3206                 :         }
    3207              78 :         return ret;
    3208                 : }
    3209                 : /* }}} */
    3210                 : 
    3211                 : /* {{{ php_do_substr_replace
    3212                 :  */
    3213                 : PHPAPI int php_do_substr_replace(void **result, zval **str, zval **repl, int f, int l TSRMLS_DC)
    3214              78 : {
    3215                 :         void *buf;
    3216                 :         int32_t buf_len, idx;
    3217                 :         UChar ch;
    3218                 :         int repl_len;
    3219                 : 
    3220              78 :         if (repl) {
    3221              63 :                 repl_len = Z_UNILEN_PP(repl);
    3222                 :         } else {
    3223              15 :                 repl_len = 0;
    3224                 :         }
    3225                 : 
    3226              78 :         if (Z_TYPE_PP(str) == IS_UNICODE) {
    3227              78 :                 buf = eumalloc(Z_USTRLEN_PP(str) -l + repl_len + 1);
    3228                 : 
    3229                 :                 /* buf_len is codept count here */
    3230              78 :                 buf_len = 0; idx = 0;
    3231             395 :                 while (f-- > 0) {
    3232             239 :                         U16_NEXT(Z_USTRVAL_PP(str), idx, Z_USTRLEN_PP(str), ch);
    3233             239 :                         buf_len += zend_codepoint_to_uchar(ch, (UChar *)buf + buf_len);
    3234                 :                 }
    3235              78 :                 if (repl != NULL) {
    3236              63 :                         u_memcpy((UChar *)buf + buf_len, Z_USTRVAL_PP(repl), repl_len);
    3237              63 :                         buf_len += repl_len;
    3238                 :                 }
    3239              78 :                 U16_FWD_N(Z_USTRVAL_PP(str), idx, Z_USTRLEN_PP(str), l);
    3240              78 :                 u_memcpy((UChar *)buf + buf_len, Z_USTRVAL_PP(str) + idx, Z_USTRLEN_PP(str) - idx);
    3241              78 :                 buf_len += (Z_USTRLEN_PP(str) - idx);
    3242                 : 
    3243              78 :                 *((UChar *)buf + buf_len) = 0;
    3244              78 :                 buf = eurealloc(buf, buf_len + 1);
    3245                 :         } else {
    3246                 :                 /* buf_len is char count here */
    3247               0 :                 buf_len = Z_STRLEN_PP(str) - l + repl_len;
    3248               0 :                 buf = emalloc(buf_len + 1);
    3249                 : 
    3250               0 :                 memcpy(buf, Z_STRVAL_PP(str), f);
    3251               0 :                 if (repl != NULL) {
    3252               0 :                         memcpy((char *)buf + f, Z_STRVAL_PP(repl), repl_len);
    3253                 :                 }
    3254               0 :                 memcpy((char *)buf + f + repl_len, Z_STRVAL_PP(str) + f + l, Z_STRLEN_PP(str) - f - l);
    3255                 : 
    3256               0 :                 *((char *)buf + buf_len) = '\0';
    3257                 :         }
    3258                 : 
    3259              78 :         *result = buf;
    3260              78 :         return buf_len;
    3261                 : }
    3262                 : /* }}} */
    3263                 : 
    3264                 : /* {{{ proto mixed substr_replace(mixed str, mixed repl, mixed start [, mixed length]) U
    3265                 :    Replaces part of a string with another string */
    3266                 : PHP_FUNCTION(substr_replace)
    3267              54 : {
    3268                 :         zval **str;
    3269                 :         zval **from;
    3270              54 :         zval **len = NULL;
    3271                 :         zval **repl;
    3272                 :         void *result;
    3273                 :         int result_len;
    3274              54 :         int l = 0;
    3275                 :         int f;
    3276              54 :         int argc = ZEND_NUM_ARGS();
    3277                 : 
    3278                 :         HashPosition pos_str, pos_from, pos_repl, pos_len;
    3279              54 :         zval **tmp_str = NULL, **tmp_from = NULL, **tmp_repl = NULL, **tmp_len= NULL;
    3280                 :         zend_uchar str_type;
    3281                 : 
    3282              54 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZZ|Z", &str, &repl, &from, &len) == FAILURE) {
    3283               3 :                 return;
    3284                 :         }
    3285                 : 
    3286              51 :         if (Z_TYPE_PP(str) != IS_ARRAY && Z_TYPE_PP(str) != IS_UNICODE &&
    3287                 :             Z_TYPE_PP(str) != IS_STRING) {
    3288               0 :                 convert_to_unicode_ex(str);
    3289                 :         }
    3290              51 :         if (Z_TYPE_PP(repl) != IS_ARRAY && Z_TYPE_PP(repl) != IS_UNICODE &&
    3291                 :             Z_TYPE_PP(repl) != IS_STRING) {
    3292               1 :                 convert_to_unicode_ex(repl);
    3293                 :         }
    3294              51 :         if (Z_TYPE_PP(from) != IS_ARRAY) {
    3295              16 :                 convert_to_long_ex(from);
    3296              16 :                 f = Z_LVAL_PP(from);
    3297                 :         }
    3298              51 :         if (argc > 3) {
    3299              42 :                 SEPARATE_ZVAL(len);
    3300              42 :                 if (Z_TYPE_PP(len) != IS_ARRAY) {
    3301              23 :                         convert_to_long_ex(len);
    3302              23 :                         l = Z_LVAL_PP(len);
    3303                 :                 }
    3304                 :         } else {
    3305               9 :                 if (Z_TYPE_PP(str) != IS_ARRAY) {
    3306               3 :                         l = Z_UNILEN_PP(str);
    3307                 :                 }
    3308                 :         }
    3309                 : 
    3310              51 :         if (Z_TYPE_PP(str) != IS_ARRAY) {
    3311              11 :                 if (
    3312                 :                         (argc == 3 && Z_TYPE_PP(from) == IS_ARRAY) ||
    3313                 :                         (argc == 4 && Z_TYPE_PP(from) != Z_TYPE_PP(len))
    3314                 :                 ) {
    3315               2 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "'from' and 'len' should be of same type - numerical or array");
    3316               2 :                         RETURN_ZVAL(*str, 1, 0);
    3317                 :                 }
    3318               9 :                 if (argc == 4 && Z_TYPE_PP(from) == IS_ARRAY) {
    3319               2 :                         if (zend_hash_num_elements(Z_ARRVAL_PP(from)) != zend_hash_num_elements(Z_ARRVAL_PP(len))) {
    3320               1 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "'from' and 'len' should have the same number of elements");
    3321               1 :                                 RETURN_ZVAL(*str, 1, 0);
    3322                 :                         }
    3323                 :                 }
    3324                 :         }
    3325                 : 
    3326              48 :         if (Z_TYPE_PP(str) != IS_ARRAY) {
    3327               8 :                 if (Z_TYPE_PP(from) != IS_ARRAY ) {
    3328               7 :                         if (Z_TYPE_PP(repl) == IS_ARRAY) {
    3329               2 :                                 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(repl), &pos_repl);
    3330               2 :                                 if (SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_PP(repl), (void **) &tmp_repl, &pos_repl)) {
    3331               2 :                                         if (Z_TYPE_PP(repl) != IS_UNICODE && Z_TYPE_PP(repl) != IS_STRING) {
    3332               2 :                                                 convert_to_unicode_ex(tmp_repl);
    3333                 :                                         }
    3334                 :                                 } else {
    3335               0 :                                         tmp_repl = NULL;
    3336                 :                                 }
    3337                 :                         } else {
    3338               5 :                                 tmp_repl = repl;
    3339                 :                         }
    3340                 : 
    3341               7 :                         if (tmp_repl && Z_TYPE_PP(str) != Z_TYPE_PP(tmp_repl)) {
    3342               0 :                                 str_type = zend_get_unified_string_type(2 TSRMLS_CC, Z_TYPE_PP(str), Z_TYPE_PP(tmp_repl));
    3343               0 :                                 convert_to_explicit_type_ex(str, str_type);
    3344               0 :                                 convert_to_explicit_type_ex(tmp_repl, str_type);
    3345                 :                         }
    3346               7 :                         if (!php_adjust_limits(str, &f, &l)) {
    3347               0 :                                 RETURN_FALSE;
    3348                 :                         }
    3349               7 :                         result_len = php_do_substr_replace(&result, str, tmp_repl, f, l TSRMLS_CC);
    3350                 : 
    3351               7 :                         if (Z_TYPE_PP(str) == IS_UNICODE) {
    3352               7 :                                 RETURN_UNICODEL((UChar *)result, result_len, 0);
    3353                 :                         } else {
    3354               0 :                                 RETURN_STRINGL((char *)result, result_len, 0);
    3355                 :                         }
    3356                 :                 } else {
    3357               1 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Functionality of 'from' and 'len' as arrays is not implemented");
    3358               1 :                         RETURN_ZVAL(*str, 1, 0);
    3359                 :                 }
    3360                 :         } else { /* str is array of strings */
    3361              40 :                 array_init(return_value);
    3362                 : 
    3363              40 :                 if (Z_TYPE_PP(from) == IS_ARRAY) {
    3364              32 :                         zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(from), &pos_from);
    3365                 :                 }
    3366              40 :                 if (argc > 3 && Z_TYPE_PP(len) == IS_ARRAY) {
    3367              16 :                         zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(len), &pos_len);
    3368                 :                 }
    3369              40 :                 if (Z_TYPE_PP(repl) == IS_ARRAY) {
    3370              19 :                         zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(repl), &pos_repl);
    3371                 :                 }
    3372                 : 
    3373              40 :                 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(str), &pos_str);
    3374             151 :                 while (zend_hash_get_current_data_ex(Z_ARRVAL_PP(str), (void **) &tmp_str, &pos_str) == SUCCESS) {
    3375              71 :                         if (Z_TYPE_PP(tmp_str) != IS_UNICODE && Z_TYPE_PP(tmp_str) != IS_STRING) {
    3376               2 :                                 convert_to_unicode_ex(tmp_str);
    3377                 :                         }
    3378                 : 
    3379              71 :                         if (Z_TYPE_PP(from) == IS_ARRAY) {
    3380              56 :                                 if (SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_PP(from), (void **) &tmp_from, &pos_from)) {
    3381              50 :                                         convert_to_long_ex(tmp_from);
    3382              50 :                                         f = Z_LVAL_PP(tmp_from);
    3383              50 :                                         zend_hash_move_forward_ex(Z_ARRVAL_PP(from), &pos_from);
    3384                 :                                 } else {
    3385               6 :                                         f = 0;
    3386                 :                                 }
    3387                 :                         }
    3388                 : 
    3389             100 :                         if (argc > 3 && (Z_TYPE_PP(len) == IS_ARRAY)) {
    3390              29 :                                 if (SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_PP(len), (void **) &tmp_len, &pos_len)) {
    3391              23 :                                         convert_to_long_ex(tmp_len);
    3392              23 :                                         l = Z_LVAL_PP(tmp_len);
    3393              23 :                                         zend_hash_move_forward_ex(Z_ARRVAL_PP(len), &pos_len);
    3394                 :                                 } else {
    3395               6 :                                         l = Z_UNILEN_PP(tmp_str);
    3396                 :                                 }
    3397              42 :                         } else if (argc > 3) {
    3398                 :                                 /* 'l' parsed & set at top of funcn */
    3399              36 :                                 l = Z_LVAL_PP(len);
    3400                 :                         } else {
    3401               6 :                                 l = Z_UNILEN_PP(tmp_str);
    3402                 :                         }
    3403                 : 
    3404              71 :                         if (Z_TYPE_PP(repl) == IS_ARRAY) {
    3405              34 :                                 if (SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_PP(repl), (void **) &tmp_repl, &pos_repl)) {
    3406              19 :                                         if (Z_TYPE_PP(tmp_repl) != IS_UNICODE && Z_TYPE_PP(tmp_repl) != IS_STRING) {
    3407               0 :                                                 convert_to_unicode_ex(tmp_repl);
    3408                 :                                         }
    3409              19 :                                         zend_hash_move_forward_ex(Z_ARRVAL_PP(repl), &pos_repl);
    3410                 :                                 } else {
    3411              15 :                                         tmp_repl = NULL;
    3412                 :                                 }
    3413                 :                         } else {
    3414              37 :                                 tmp_repl = repl;
    3415                 :                         }
    3416                 : 
    3417              71 :                         if (tmp_repl && Z_TYPE_PP(tmp_str) != Z_TYPE_PP(tmp_repl)) {
    3418               0 :                                 str_type = zend_get_unified_string_type(2 TSRMLS_CC, Z_TYPE_PP(tmp_str), Z_TYPE_PP(tmp_repl));
    3419               0 :                                 convert_to_explicit_type_ex(tmp_str, str_type);
    3420               0 :                                 convert_to_explicit_type_ex(tmp_repl, str_type);
    3421                 :                         }
    3422              71 :                         php_adjust_limits(tmp_str, &f, &l);
    3423              71 :                         result_len = php_do_substr_replace(&result, tmp_str, tmp_repl, f, l TSRMLS_CC);
    3424                 : 
    3425              71 :                         if (Z_TYPE_PP(tmp_str) == IS_UNICODE) {
    3426              71 :                                 add_next_index_unicodel(return_value, (UChar *)result, result_len, 0);
    3427                 :                         } else {
    3428               0 :                                 add_next_index_stringl(return_value, (char *)result, result_len, 0);
    3429                 :                         }
    3430                 : 
    3431              71 :                         zend_hash_move_forward_ex(Z_ARRVAL_PP(str), &pos_str);
    3432                 :                 } /*while*/
    3433                 :         } /* if */
    3434                 : }
    3435                 : /* }}} */
    3436                 : 
    3437                 : /* {{{ proto string quotemeta(string str) U
    3438                 :    Quotes meta characters */
    3439                 : PHP_FUNCTION(quotemeta)
    3440               6 : {
    3441                 :         zstr str, old;
    3442                 :         zstr old_end;
    3443                 :         int  old_len;
    3444                 :         zstr p, q;
    3445                 :         char c;
    3446                 :         UChar cp;
    3447                 :         zend_uchar type;
    3448                 : 
    3449               6 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t", &old, &old_len, &type) == FAILURE) {
    3450               2 :                 return;
    3451                 :         }
    3452                 : 
    3453               4 :         if (old_len == 0) {
    3454               0 :                 RETURN_FALSE;
    3455                 :         }
    3456                 : 
    3457                 : 
    3458               4 :         if (type == IS_UNICODE) {
    3459               4 :                 old_end.u = old.u + old_len;
    3460               4 :                 str.u = safe_emalloc(2, UBYTES(old_len), 1);
    3461                 : 
    3462              60 :                 for (p.u = old.u, q.u = str.u; p.u != old_end.u; p.u++) {
    3463              56 :                         cp = *p.u;
    3464              56 :                         switch (cp) {
    3465                 :                                 case '.':
    3466                 :                                 case '\\':
    3467                 :                                 case '+':
    3468                 :                                 case '*':
    3469                 :                                 case '?':
    3470                 :                                 case '[':
    3471                 :                                 case '^':
    3472                 :                                 case ']':
    3473                 :                                 case '$':
    3474                 :                                 case '(':
    3475                 :                                 case ')':
    3476              24 :                                         *q.u++ = '\\';
    3477                 :                                         /* break is missing _intentionally_ */
    3478                 :                                 default:
    3479              56 :                                         *q.u++ = cp;
    3480                 :                         }
    3481                 :                 }
    3482               4 :                 *q.u = 0;
    3483               4 :                 RETURN_UNICODEL(eurealloc(str.u, q.u - str.u + 1), q.u - str.u, 0);
    3484                 :         } else {
    3485               0 :                 old_end.s = old.s + old_len;
    3486               0 :                 str.s = safe_emalloc(2, old_len, 1);
    3487                 : 
    3488               0 :                 for (p.s = old.s, q.s = str.s; p.s != old_end.s; p.s++) {
    3489               0 :                         c = *p.s;
    3490               0 :                         switch (c) {
    3491                 :                                 case '.':
    3492                 :                                 case '\\':
    3493                 :                                 case '+':
    3494                 :                                 case '*':
    3495                 :                                 case '?':
    3496                 :                                 case '[':
    3497                 :                                 case '^':
    3498                 :                                 case ']':
    3499                 :                                 case '$':
    3500                 :                                 case '(':
    3501                 :                                 case ')':
    3502               0 :                                         *q.s++ = '\\';
    3503                 :                                         /* break is missing _intentionally_ */
    3504                 :                                 default:
    3505               0 :                                         *q.s++ = c;
    3506                 :                         }
    3507                 :                 }
    3508               0 :                 *q.s = 0;
    3509               0 :                 RETURN_STRINGL(erealloc(str.s, q.s - str.s + 1), q.s - str.s, 0);
    3510                 :         }
    3511                 : 
    3512                 : }
    3513                 : /* }}} */
    3514                 : 
    3515                 : /* {{{ proto int ord(string character) U
    3516                 :    Returns the codepoint value of a character */
    3517                 : PHP_FUNCTION(ord)
    3518           22480 : {
    3519                 :         zstr str;
    3520                 :         int str_len;
    3521                 :         zend_uchar str_type;
    3522                 : 
    3523           22480 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t", &str, &str_len, &str_type) == FAILURE) {
    3524               6 :                 return;
    3525                 :         }
    3526                 : 
    3527           22474 :         if (str_type == IS_UNICODE) {
    3528             802 :                 RETURN_LONG(zend_get_codepoint_at(str.u, str_len, 0));
    3529                 :         } else {
    3530           21672 :                 RETURN_LONG((unsigned char) str.s[0]);
    3531                 :         }
    3532                 : }
    3533                 : /* }}} */
    3534                 : 
    3535                 : /* {{{ proto string chr(int codepoint) U
    3536                 :    Converts a codepoint number to a character */
    3537                 : PHP_FUNCTION(chr)
    3538          637486 : {
    3539                 :         UChar buf[2];
    3540                 :         int buf_len;
    3541                 :         long num;
    3542                 : 
    3543          637486 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &num) == FAILURE) {
    3544               8 :                 return;
    3545                 :         }
    3546                 : 
    3547          637478 :         if (num > UCHAR_MAX_VALUE) {
    3548               1 :                 php_error(E_WARNING, "Codepoint value cannot be greater than %X", UCHAR_MAX_VALUE);
    3549               1 :                 return;
    3550                 :         }
    3551          637477 :         buf_len = zend_codepoint_to_uchar((uint32_t) num, buf);
    3552          637477 :         RETURN_UNICODEL(buf, buf_len, 1);
    3553                 : }
    3554                 : /* }}} */
    3555                 : 
    3556                 : /* {{{ php_u_lcfirst()
    3557                 :    Makes an Unicode string's first character lowercase */
    3558                 : static void php_u_lcfirst(UChar *ustr, int ustr_len, zval *return_value TSRMLS_DC)
    3559              41 : {
    3560              41 :         UChar tmp[3] = { 0, 0, 0 }; /* UChar32 will be converted to upto 2 UChar units */
    3561              41 :         int tmp_len = 0;
    3562              41 :         int pos = 0;
    3563              41 :         UErrorCode status = U_ZERO_ERROR;
    3564                 : 
    3565              41 :         U16_FWD_1(ustr, pos, ustr_len);
    3566              41 :         tmp_len = u_strToLower(tmp, sizeof(tmp)/sizeof(UChar), ustr, pos, UG(default_locale), &status);
    3567                 : 
    3568              41 :         Z_USTRVAL_P(return_value) = eumalloc(tmp_len + ustr_len - pos+1);
    3569                 :         
    3570              41 :         Z_USTRVAL_P(return_value)[0] = tmp[0];
    3571              41 :         if (tmp_len > 1) {
    3572               0 :                 Z_USTRVAL_P(return_value)[1] = tmp[1];
    3573                 :         }
    3574              41 :         u_memcpy(Z_USTRVAL_P(return_value)+tmp_len, ustr + pos, ustr_len - pos+1);
    3575              41 :         Z_USTRLEN_P(return_value) = tmp_len + ustr_len - pos;
    3576              41 : }
    3577                 : /* }}} */
    3578                 : 
    3579                 : /* {{{ php_lcfirst 
    3580                 :    Lowercase the first character of the word in a native string */
    3581                 : static void php_lcfirst(char *str)
    3582               0 : {
    3583                 :         register char *r;
    3584               0 :         r = str;
    3585               0 :         *r = tolower((unsigned char) *r);
    3586               0 : }
    3587                 : /* }}} */
    3588                 : 
    3589                 : /* {{{ proto string lcfirst(string str) U
    3590                 :    Makes a string's first character lowercase */
    3591                 : PHP_FUNCTION(lcfirst)
    3592              49 : {
    3593                 :         zstr       str;
    3594                 :         int        str_len;
    3595                 :         zend_uchar str_type;
    3596                 : 
    3597              49 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t", &str, &str_len, &str_type) == FAILURE) {
    3598               4 :                 return;
    3599                 :         }
    3600                 :         
    3601              45 :         if (str_len == 0) {
    3602               4 :                 if (str_type == IS_UNICODE) {
    3603               4 :                         RETURN_EMPTY_UNICODE();
    3604                 :                 } else {
    3605               0 :                         RETURN_EMPTY_STRING();
    3606                 :                 }
    3607                 :         }       
    3608                 : 
    3609              41 :         if (str_type == IS_UNICODE) {
    3610              41 :                 Z_TYPE_P(return_value) = IS_UNICODE;
    3611              41 :                 php_u_lcfirst(str.u, str_len, return_value TSRMLS_CC);
    3612                 :         } else {
    3613               0 :                 ZVAL_STRINGL(return_value, str.s, str_len, ZSTR_DUPLICATE);
    3614               0 :                 php_lcfirst(Z_STRVAL_P(return_value));
    3615                 :         }       
    3616                 : }
    3617                 : /* }}} */
    3618                 : 
    3619                 : /* {{{ php_u_ucfirst()
    3620                 :    Makes an Unicode string's first character uppercase */
    3621                 : static void php_u_ucfirst(UChar *ustr, int ustr_len, zval *return_value TSRMLS_DC)
    3622              59 : {
    3623              59 :         UChar tmp[3] = { 0, 0, 0 }; /* UChar32 will be converted to upto 2 UChar units */
    3624              59 :         int tmp_len = 0;
    3625              59 :         int pos = 0;
    3626              59 :         UErrorCode status = U_ZERO_ERROR;
    3627                 : 
    3628              59 :         U16_FWD_1(ustr, pos, ustr_len);
    3629              59 :         tmp_len = u_strToUpper(tmp, sizeof(tmp)/sizeof(UChar), ustr, pos, UG(default_locale), &status);
    3630                 : 
    3631              59 :         Z_USTRVAL_P(return_value) = eumalloc(tmp_len + ustr_len - pos+1);
    3632                 :         
    3633              59 :         Z_USTRVAL_P(return_value)[0] = tmp[0];
    3634              59 :         if (tmp_len > 1) {
    3635               1 :                 Z_USTRVAL_P(return_value)[1] = tmp[1];
    3636                 :         }
    3637              59 :         u_memcpy(Z_USTRVAL_P(return_value)+tmp_len, ustr + pos, ustr_len - pos+1);
    3638              59 :         Z_USTRLEN_P(return_value) = tmp_len + ustr_len - pos;
    3639              59 : }
    3640                 : /* }}} */
    3641                 : 
    3642                 : /* {{{ php_ucfirst 
    3643                 :    Uppercase the first character of the word in a native string */
    3644                 : static void php_ucfirst(char *str)
    3645               0 : {
    3646                 :         register char *r;
    3647               0 :         r = str;
    3648               0 :         *r = toupper((unsigned char) *r);
    3649               0 : }
    3650                 : /* }}} */
    3651                 : 
    3652                 : /* {{{ proto string ucfirst(string str) U
    3653                 :    Makes a string's first character uppercase */
    3654                 : PHP_FUNCTION(ucfirst)
    3655              67 : {
    3656                 :         zstr       str;
    3657                 :         int        str_len;
    3658                 :         zend_uchar str_type;
    3659                 : 
    3660              67 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t", &str, &str_len, &str_type) == FAILURE) {
    3661               4 :                 return;
    3662                 :         }
    3663                 :         
    3664              63 :         if (str_len == 0) {
    3665               4 :                 if (str_type == IS_UNICODE) {
    3666               4 :                         RETURN_EMPTY_UNICODE();
    3667                 :                 } else {
    3668               0 :                         RETURN_EMPTY_STRING();
    3669                 :                 }
    3670                 :         }       
    3671                 : 
    3672              59 :         if (str_type == IS_UNICODE) {
    3673              59 :                 Z_TYPE_P(return_value) = IS_UNICODE;
    3674              59 :                 php_u_ucfirst(str.u, str_len, return_value TSRMLS_CC);
    3675                 :         } else {
    3676               0 :                 ZVAL_STRINGL(return_value, str.s, str_len, ZSTR_DUPLICATE);
    3677               0 :                 php_ucfirst(Z_STRVAL_P(return_value));
    3678                 :         }       
    3679                 : }
    3680                 : /* }}} */
    3681                 : 
    3682                 : /* {{{ php_ucwords()
    3683                 :    Uppercase the first character of every word in a native string */
    3684                 : static void php_ucwords(char *str, int str_len)
    3685               6 : {
    3686                 :         register char *r, *r_end;
    3687                 : 
    3688               6 :         r = str;
    3689               6 :         *r = toupper((unsigned char) *r);
    3690             114 :         for (r_end = r + str_len - 1; r < r_end; ) {
    3691             102 :                 if (isspace((int) *(unsigned char *)r++)) {
    3692              18 :                         *r = toupper((unsigned char) *r);
    3693                 :                 }
    3694                 :         }
    3695               6 : }
    3696                 : /* }}} */
    3697                 : 
    3698                 : /* {{{ php_u_ucwords() U
    3699                 :    Uppercase the first character of every word in an Unicode string */
    3700                 : static void php_u_ucwords(UChar *ustr, int ustr_len, zval *retval TSRMLS_DC)
    3701              87 : {
    3702              87 :         UChar32 cp = 0;
    3703                 :         UChar *tmp;
    3704                 :         int retval_len;
    3705              87 :         int pos = 0, last_pos = 0;
    3706              87 :         int tmp_len = 0;
    3707              87 :         zend_bool last_was_space = TRUE;
    3708              87 :         UErrorCode status = U_ZERO_ERROR;
    3709                 : 
    3710                 :         /*
    3711                 :          * We can calculate maximum resulting length precisely considering that not
    3712                 :          * more than half of the codepoints in the string can follow a whitespace
    3713                 :          * and that maximum expansion is 2 UChar's.
    3714                 :          */
    3715              87 :         retval_len = ((3 * ustr_len) >> 1) + 2;
    3716              87 :         tmp = eumalloc(retval_len);
    3717                 : 
    3718            1915 :         while (pos < ustr_len) {
    3719                 : 
    3720            1741 :                 U16_NEXT(ustr, pos, ustr_len, cp);
    3721                 : 
    3722            1741 :                 if (u_isWhitespace(cp) == TRUE) {
    3723             188 :                         tmp_len += zend_codepoint_to_uchar(cp, tmp + tmp_len);
    3724             188 :                         last_was_space = TRUE;
    3725                 :                 } else {
    3726            1553 :                         if (last_was_space) {
    3727             244 :                                 tmp_len += u_strToUpper(tmp + tmp_len, retval_len - tmp_len, ustr + last_pos, 1, UG(default_locale), &status);
    3728             244 :                                 last_was_space = FALSE;
    3729                 :                         } else {
    3730            1309 :                                 tmp_len += zend_codepoint_to_uchar(cp, tmp + tmp_len);
    3731                 :                         }
    3732                 :                 }
    3733                 : 
    3734            1741 :                 last_pos = pos;
    3735                 :         }
    3736              87 :         tmp[tmp_len] = 0;
    3737                 : 
    3738                 :         /*
    3739                 :          * Try to avoid another alloc if the difference between allocated size and
    3740                 :          * real length is "small".
    3741                 :          */
    3742              87 :         if (retval_len - tmp_len > 256) {
    3743               0 :                 ZVAL_UNICODEL(retval, tmp, tmp_len, 1);
    3744               0 :                 efree(tmp);
    3745                 :         } else {
    3746              87 :                 ZVAL_UNICODEL(retval, tmp, tmp_len, 0);
    3747                 :         }
    3748              87 : }
    3749                 : /* }}} */
    3750                 : 
    3751                 : /* {{{ proto string ucwords(string str) U
    3752                 :    Uppercase the first character of every word in a string */
    3753                 : PHP_FUNCTION(ucwords)
    3754             111 : {
    3755                 :         zstr str;
    3756                 :         int str_len;
    3757                 :         zend_uchar str_type;
    3758                 : 
    3759             111 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t", &str, &str_len, &str_type) == FAILURE) {
    3760               8 :                 return;
    3761                 :         }
    3762                 : 
    3763             103 :         if (str_len == 0) {
    3764              10 :                 if (str_type == IS_UNICODE) {
    3765              10 :                         RETURN_EMPTY_UNICODE();
    3766                 :                 } else {
    3767               0 :                         RETURN_EMPTY_STRING();
    3768                 :                 }
    3769                 :         }
    3770                 : 
    3771              93 :         if (str_type == IS_UNICODE) {
    3772              87 :                 Z_TYPE_P(return_value) = IS_UNICODE;
    3773              87 :                 php_u_ucwords(str.u, str_len, return_value TSRMLS_CC);
    3774                 :         } else {
    3775               6 :                 ZVAL_STRINGL(return_value, str.s, str_len, 1);
    3776               6 :                 php_ucwords(Z_STRVAL_P(return_value), str_len);
    3777                 :         }
    3778                 : }
    3779                 : /* }}} */
    3780                 : 
    3781                 : /* {{{ php_strtr
    3782                 :  */
    3783                 : PHPAPI char *php_strtr(char *str, int len, char *str_from, char *str_to, int trlen)
    3784              32 : {
    3785                 :         int i;
    3786                 :         unsigned char xlat[256];
    3787                 : 
    3788              32 :         if ((trlen < 1) || (len < 1)) {
    3789               0 :                 return str;
    3790                 :         }
    3791                 : 
    3792              32 :         for (i = 0; i < 256; xlat[i] = i, i++);
    3793                 : 
    3794            1544 :         for (i = 0; i < trlen; i++) {
    3795            1512 :                 xlat[(unsigned char) str_from[i]] = str_to[i];
    3796                 :         }
    3797                 : 
    3798           66356 :         for (i = 0; i < len; i++) {
    3799           66324 :                 str[i] = xlat[(unsigned char) str[i]];
    3800                 :         }
    3801                 : 
    3802              32 :         return str;
    3803                 : }
    3804                 : /* }}} */
    3805                 : 
    3806                 : /* {{{ php_u_strtr
    3807                 :  */
    3808                 : static void text_iter_helper_move(UChar *text, int32_t text_len, int32_t *offset, int32_t *cp_offset)
    3809              30 : {
    3810                 :     UChar32 cp;
    3811                 :     int32_t tmp, tmp2;
    3812                 : 
    3813              30 :     if (*offset == UBRK_DONE) {
    3814               0 :         return;
    3815                 :     }
    3816                 : 
    3817              30 :         if (*offset == text_len) {
    3818               7 :                 *offset    = UBRK_DONE;
    3819               7 :                 *cp_offset = UBRK_DONE;
    3820                 :         } else {
    3821              23 :                 U16_NEXT(text, (*offset), text_len, cp);
    3822              23 :                 (*cp_offset)++;
    3823                 :                                                                                                                                                                                                                 
    3824              23 :                 if (u_getCombiningClass(cp) == 0) {
    3825              23 :                         tmp = *offset;
    3826              23 :                         tmp2 = *cp_offset;
    3827                 :                         /*
    3828                 :                          * At the end of the from cp will be 0 because of the NULL
    3829                 :                          * terminating NULL, so combining class will be 0 as well.
    3830                 :                          */
    3831              52 :                         while (tmp < text_len) {
    3832              21 :                                 U16_NEXT(text, tmp, text_len, cp);
    3833              21 :                                 tmp2++;
    3834              21 :                                 if (u_getCombiningClass(cp) == 0) {
    3835              15 :                                         break;
    3836                 :                                 } else {
    3837               6 :                                         *offset    = tmp;
    3838               6 :                                         *cp_offset = tmp2;
    3839                 :                                 }
    3840                 :                         }
    3841                 :                 }
    3842                 :         }
    3843                 : }
    3844                 : 
    3845                 : PHPAPI UChar *php_u_strtr(UChar *str, int len, UChar *str_from, int str_from_len, UChar *str_to, int str_to_len, int trlen, int *outlen TSRMLS_DC)
    3846             119 : {
    3847                 :         int i;
    3848             119 :         int can_optimize = 1;
    3849                 : 
    3850             119 :         if ((trlen < 1) || (len < 1)) {
    3851              16 :                 *outlen = len;
    3852              16 :                 return str;
    3853                 :         }
    3854                 : 
    3855                 :         /* First loop to see if we can use the optimized version */
    3856            1596 :         for (i = 0; i < trlen; i++)  {
    3857            1496 :                 if (str_from[i] > 255 || str_to[i] > 255) {
    3858               3 :                         can_optimize = 0;
    3859               3 :                         break;
    3860                 :                 }
    3861                 :         }
    3862             103 :         if (can_optimize) {
    3863             149 :                 for (i = trlen; i < str_from_len; i++) {
    3864              50 :                         if (str_from[i] > 255) {
    3865               1 :                                 can_optimize = 0;
    3866               1 :                                 break;
    3867                 :                         }
    3868                 :                 }
    3869                 :         }
    3870             103 :         if (can_optimize) {
    3871             144 :                 for (i = trlen; i < str_to_len; i++) {
    3872              45 :                         if (str_to[i] > 255) {
    3873               0 :                                 can_optimize = 0;
    3874               0 :                                 break;
    3875                 :                         }
    3876                 :                 }
    3877                 :         }
    3878                 : 
    3879             103 :         if (can_optimize) {
    3880                 :                 UChar xlat[256];
    3881              99 :                 UChar *tmp_str = eustrndup(str, len);
    3882                 : 
    3883              99 :                 for (i = 0; i < 256; xlat[i] = i, i++);
    3884                 : 
    3885            1587 :                 for (i = 0; i < trlen; i++) {
    3886            1488 :                         xlat[str_from[i]] = str_to[i];
    3887                 :                 }
    3888                 : 
    3889           17143 :                 for (i = 0; i < len; i++) {
    3890           17044 :                         if (str[i] < 256) {
    3891           17044 :                                 tmp_str[i] = xlat[str[i]];
    3892                 :                         }
    3893                 :                 }
    3894                 : 
    3895              99 :                 *outlen = len;
    3896              99 :                 return tmp_str;
    3897                 :         } else {
    3898                 :                 /* We use the character break iterator here to assemble an mapping
    3899                 :                  * array in such a way that we can reuse the code in php_u_strtr_array
    3900                 :                  * to do the replacements in order to avoid duplicating code. */
    3901                 :                 HashTable *tmp_hash;
    3902               4 :                 int minlen = 128*1024, maxlen = 0;
    3903               4 :                 int32_t prev_from_offset = 0, from_offset = 0, from_cp_offset = 0;
    3904               4 :                 int32_t prev_to_offset = 0, to_offset = 0, to_cp_offset = 0;
    3905                 :                 zval *entry;
    3906                 :                 UChar *key_string;
    3907                 : 
    3908               4 :                 tmp_hash = emalloc(sizeof(HashTable));
    3909               4 :                 zend_hash_init(tmp_hash, 0, NULL, ZVAL_PTR_DTOR, 0);
    3910                 : 
    3911                 :                 /* Loop over the two strings and prepare the hash entries */
    3912                 :                 do
    3913                 :                 {
    3914              15 :                         text_iter_helper_move(str_from, str_from_len, &from_offset, &from_cp_offset);
    3915              15 :                         text_iter_helper_move(str_to, str_to_len, &to_offset, &to_cp_offset);
    3916                 : 
    3917              15 :                         if (from_offset != -1 && to_offset != -1) {
    3918              11 :                                 if (from_cp_offset - prev_from_offset > maxlen) {
    3919               4 :                                         maxlen = from_cp_offset - prev_from_offset;
    3920                 :                                 }
    3921              11 :                                 if (from_cp_offset - prev_from_offset < minlen) {
    3922               6 :                                         minlen = from_cp_offset - prev_from_offset;
    3923                 :                                 }
    3924                 : 
    3925              11 :                                 MAKE_STD_ZVAL(entry);
    3926              11 :                                 ZVAL_UNICODEL(entry, str_to + prev_to_offset, to_cp_offset - prev_to_offset, 1);
    3927              11 :                                 Z_USTRVAL_P(entry)[to_cp_offset - prev_to_offset] = 0;
    3928                 : 
    3929              11 :                                 key_string = eumalloc(from_cp_offset - prev_from_offset + 1);
    3930              11 :                                 u_memcpy(key_string, str_from + prev_from_offset, from_cp_offset - prev_from_offset);
    3931              11 :                                 key_string[from_cp_offset - prev_from_offset] = 0;
    3932                 : 
    3933              11 :                                 zend_u_hash_add(tmp_hash, IS_UNICODE, ZSTR(key_string), from_cp_offset - prev_from_offset + 1, &entry, sizeof(zval*), NULL);
    3934              11 :                                 efree(key_string);
    3935                 :                         }
    3936                 : 
    3937              15 :                         prev_from_offset = from_offset;
    3938              15 :                         prev_to_offset = to_offset;
    3939              15 :                 } while (from_offset != -1 && to_offset != -1);
    3940                 : 
    3941                 :                 /* Run the replacement */
    3942               4 :                 str = php_u_strtr_array(str, len, tmp_hash, minlen, maxlen, outlen TSRMLS_CC);
    3943               4 :                 zend_hash_destroy(tmp_hash);
    3944               4 :                 efree(tmp_hash);
    3945                 : 
    3946               4 :                 return str;
    3947                 :         }
    3948                 : }
    3949                 : /* }}} */
    3950                 : 
    3951                 : static HashTable* php_u_strtr_array_prepare_hashtable(HashTable *hash, int *minlen_out, int *maxlen_out TSRMLS_DC) /* {{{ */
    3952              45 : {
    3953              45 :         HashTable *tmp_hash = emalloc(sizeof(HashTable));
    3954                 :         HashPosition hpos;
    3955                 :         zval **entry;
    3956                 :         zstr   string_key;
    3957                 :         uint   string_key_len;
    3958              45 :         int minlen = 128*1024;
    3959                 :         ulong num_key;
    3960              45 :         int maxlen = 0, len;
    3961                 :         zval   ctmp;
    3962                 : 
    3963              45 :         zend_hash_init(tmp_hash, zend_hash_num_elements(hash), NULL, NULL, 0);
    3964              45 :         zend_hash_internal_pointer_reset_ex(hash, &hpos);
    3965             207 :         while (zend_hash_get_current_data_ex(hash, (void **)&entry, &hpos) == SUCCESS) {
    3966             121 :                 switch (zend_hash_get_current_key_ex(hash, &string_key, &string_key_len, &num_key, 0, &hpos)) {
    3967                 :                         case HASH_KEY_IS_UNICODE:
    3968             110 :                                 len = string_key_len-1;
    3969             110 :                                 if (len < 1) {
    3970               4 :                                         zend_hash_destroy(tmp_hash);
    3971               4 :                                         efree(tmp_hash);
    3972               4 :                                         return NULL;
    3973                 :                                 }
    3974             106 :                                 zend_u_hash_add(tmp_hash, IS_UNICODE, string_key, string_key_len, entry, sizeof(zval*), NULL);
    3975             106 :                                 if (len > maxlen) {
    3976              69 :                                         maxlen = len;
    3977                 :                                 }
    3978             106 :                                 if (len < minlen) {
    3979              32 :                                         minlen = len;
    3980                 :                                 }
    3981             106 :                                 break;
    3982                 : 
    3983                 :                         case HASH_KEY_IS_LONG:
    3984              11 :                                 Z_TYPE(ctmp) = IS_LONG;
    3985              11 :                                 Z_LVAL(ctmp) = num_key;
    3986                 : 
    3987              11 :                                 convert_to_unicode(&ctmp);
    3988              11 :                                 len = Z_USTRLEN(ctmp);
    3989              11 :                                 zend_u_hash_add(tmp_hash, IS_UNICODE, Z_UNIVAL(ctmp), len+1, entry, sizeof(zval*), NULL);
    3990              11 :                                 zval_dtor(&ctmp);
    3991                 : 
    3992              11 :                                 if (len > maxlen) {
    3993              10 :                                         maxlen = len;
    3994                 :                                 }
    3995              11 :                                 if (len < minlen) {
    3996              10 :                                         minlen = len;
    3997                 :                                 }
    3998                 :                                 break;
    3999                 :                 }
    4000             117 :                 zend_hash_move_forward_ex(hash, &hpos);
    4001                 :         }
    4002              41 :         *minlen_out = minlen;
    4003              41 :         *maxlen_out = maxlen;
    4004              41 :         return tmp_hash;
    4005                 : }
    4006                 : /* }}} */
    4007                 : 
    4008                 : /* {{{ php_u_strtr_array
    4009                 :  */
    4010                 : static UChar* php_u_strtr_array(UChar *str, int slen, HashTable *hash, int minlen, int maxlen, int *outlen TSRMLS_DC)
    4011              45 : {
    4012                 :         zval **trans;
    4013                 :         UChar *key;
    4014                 :         int pos, found, len;
    4015              45 :         smart_str result = {0};
    4016                 : 
    4017              45 :         key = eumalloc(maxlen+1);
    4018              45 :         pos = 0;
    4019                 : 
    4020             687 :         while (pos < slen) {
    4021             597 :                 if ((pos + maxlen) > slen) {
    4022              77 :                         maxlen = slen - pos;
    4023                 :                 }
    4024                 : 
    4025             597 :                 found = 0;
    4026             597 :                 u_memcpy(key, str+pos, maxlen);
    4027                 : 
    4028            1789 :                 for (len = maxlen; len >= minlen; len--) {
    4029            1288 :                         key[len] = 0;
    4030                 : 
    4031            1288 :                         if (zend_u_hash_find(hash, IS_UNICODE, ZSTR(key), len+1, (void**)&trans) == SUCCESS) {
    4032                 :                                 UChar *tval;
    4033                 :                                 int tlen;
    4034                 :                                 zval tmp;
    4035                 : 
    4036              96 :                                 if (Z_TYPE_PP(trans) != IS_UNICODE) {
    4037              16 :                                         tmp = **trans;
    4038              16 :                                         zval_copy_ctor(&tmp);
    4039              16 :                                         convert_to_unicode(&tmp);
    4040              16 :                                         tval = Z_USTRVAL(tmp);
    4041              16 :                                         tlen = Z_USTRLEN(tmp);
    4042                 :                                 } else {
    4043              80 :                                         tval = Z_USTRVAL_PP(trans);
    4044              80 :                                         tlen = Z_USTRLEN_PP(trans);
    4045                 :                                 }
    4046                 : 
    4047              96 :                                 smart_str_appendl(&result, tval, UBYTES(tlen));
    4048              96 :                                 pos += len;
    4049              96 :                                 found = 1;
    4050                 : 
    4051              96 :                                 if (Z_TYPE_PP(trans) != IS_UNICODE) {
    4052              16 :                                         zval_dtor(&tmp);
    4053                 :                                 }
    4054              96 :                                 break;
    4055                 :                         }
    4056                 :                 }
    4057                 : 
    4058             597 :                 if (! found) {
    4059             501 :                         smart_str_append2c(&result, str[pos]);
    4060             501 :                         pos++;
    4061                 :                 }
    4062                 :         }
    4063                 : 
    4064              45 :         efree(key);
    4065              45 :         smart_str_0(&result);
    4066              45 :         *outlen = result.len >> 1;
    4067              45 :         return (UChar*) result.c;
    4068                 : }
    4069                 : /* }}} */
    4070                 : 
    4071                 : /* {{{ php_strtr_array
    4072                 :  */
    4073                 : static void php_strtr_array(zval *return_value, char *str, int slen, HashTable *hash)
    4074               0 : {
    4075                 :         zval **entry;
    4076                 :         zstr   string_key;
    4077                 :         uint   string_key_len;
    4078                 :         zval **trans;
    4079                 :         zval   ctmp;
    4080                 :         ulong num_key;
    4081               0 :         int minlen = 128*1024;
    4082               0 :         int maxlen = 0, pos, len, found;
    4083                 :         char *key;
    4084                 :         HashPosition hpos;
    4085               0 :         smart_str result = {0};
    4086                 :         HashTable tmp_hash;
    4087                 : 
    4088               0 :         zend_hash_init(&tmp_hash, 0, NULL, NULL, 0);
    4089               0 :         zend_hash_internal_pointer_reset_ex(hash, &hpos);
    4090               0 :         while (zend_hash_get_current_data_ex(hash, (void **)&entry, &hpos) == SUCCESS) {
    4091               0 :                 switch (zend_hash_get_current_key_ex(hash, &string_key, &string_key_len, &num_key, 0, &hpos)) {
    4092                 :                         case HASH_KEY_IS_STRING:
    4093               0 :                                 len = string_key_len-1;
    4094               0 :                                 if (len < 1) {
    4095               0 :                                         zend_hash_destroy(&tmp_hash);
    4096               0 :                                         RETURN_FALSE;
    4097                 :                                 }
    4098               0 :                                 zend_u_hash_add(&tmp_hash, IS_STRING, string_key, string_key_len, entry, sizeof(zval*), NULL);
    4099               0 :                                 if (len > maxlen) {
    4100               0 :                                         maxlen = len;
    4101                 :                                 }
    4102               0 :                                 if (len < minlen) {
    4103               0 :                                         minlen = len;
    4104                 :                                 }
    4105               0 :                                 break;
    4106                 : 
    4107                 :                         case HASH_KEY_IS_LONG:
    4108               0 :                                 Z_TYPE(ctmp) = IS_LONG;
    4109               0 :                                 Z_LVAL(ctmp) = num_key;
    4110                 : 
    4111               0 :                                 convert_to_string(&ctmp);
    4112               0 :                                 len = Z_STRLEN(ctmp);
    4113               0 :                                 zend_hash_add(&tmp_hash, Z_STRVAL(ctmp), len+1, entry, sizeof(zval*), NULL);
    4114               0 :                                 zval_dtor(&ctmp);
    4115                 : 
    4116               0 :                                 if (len > maxlen) {
    4117               0 :                                         maxlen = len;
    4118                 :                                 }
    4119               0 :                                 if (len < minlen) {
    4120               0 :                                         minlen = len;
    4121                 :                                 }
    4122                 :                                 break;
    4123                 :                 }
    4124               0 :                 zend_hash_move_forward_ex(hash, &hpos);
    4125                 :         }
    4126                 : 
    4127               0 :         key = emalloc(maxlen+1);
    4128               0 :         pos = 0;
    4129                 : 
    4130               0 :         while (pos < slen) {
    4131               0 :                 if ((pos + maxlen) > slen) {
    4132               0 :                         maxlen = slen - pos;
    4133                 :                 }
    4134                 : 
    4135               0 :                 found = 0;
    4136               0 :                 memcpy(key, str+pos, maxlen);
    4137                 : 
    4138               0 :                 for (len = maxlen; len >= minlen; len--) {
    4139               0 :                         key[len] = 0;
    4140                 : 
    4141               0 :                         if (zend_hash_find(&tmp_hash, key, len+1, (void**)&trans) == SUCCESS) {
    4142                 :                                 char *tval;
    4143                 :                                 int tlen;
    4144                 :                                 zval tmp;
    4145                 : 
    4146               0 :                                 if (Z_TYPE_PP(trans) != IS_STRING) {
    4147               0 :                                         tmp = **trans;
    4148               0 :                                         zval_copy_ctor(&tmp);
    4149               0 :                                         convert_to_string(&tmp);
    4150               0 :                                         tval = Z_STRVAL(tmp);
    4151               0 :                                         tlen = Z_STRLEN(tmp);
    4152                 :                                 } else {
    4153               0 :                                         tval = Z_STRVAL_PP(trans);
    4154               0 :                                         tlen = Z_STRLEN_PP(trans);
    4155                 :                                 }
    4156                 : 
    4157               0 :                                 smart_str_appendl(&result, tval, tlen);
    4158               0 :                                 pos += len;
    4159               0 :                                 found = 1;
    4160                 : 
    4161               0 :                                 if (Z_TYPE_PP(trans) != IS_STRING) {
    4162               0 :                                         zval_dtor(&tmp);
    4163                 :                                 }
    4164               0 :                                 break;
    4165                 :                         }
    4166                 :                 }
    4167                 : 
    4168               0 :                 if (! found) {
    4169               0 :                         smart_str_appendc(&result, str[pos++]);
    4170                 :                 }
    4171                 :         }
    4172                 : 
    4173               0 :         efree(key);
    4174               0 :         zend_hash_destroy(&tmp_hash);
    4175               0 :         smart_str_0(&result);
    4176               0 :         RETVAL_STRINGL(result.c, result.len, 0);
    4177                 : }
    4178                 : /* }}} */
    4179                 : 
    4180                 : /* {{{ proto string strtr(string str, string from[, string to]) U
    4181                 :    Translates characters in str using given translation tables */
    4182                 : PHP_FUNCTION(strtr)
    4183             223 : {
    4184             223 :         zstr str, to = NULL_ZSTR;
    4185             223 :         int str_len, to_len = 0;
    4186                 :         zend_uchar str_type, to_type;
    4187                 :         zval **from;
    4188             223 :         int ac = ZEND_NUM_ARGS();
    4189                 :         
    4190             223 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "TZ|T", &str, &str_len, &str_type, &from,
    4191                 :                 &to, &to_len, &to_type) == FAILURE) {
    4192              23 :                 return;
    4193                 :         }
    4194                 : 
    4195             200 :         if (ac == 2 && Z_TYPE_PP(from) != IS_ARRAY) {
    4196              31 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "The second argument is not an array");
    4197              31 :                 RETURN_FALSE;
    4198                 :         }
    4199                 : 
    4200                 :         /* shortcut for empty string */
    4201             169 :         if (str_len == 0) {
    4202              26 :                 if (str_type == IS_UNICODE) {
    4203              26 :                         RETURN_EMPTY_UNICODE();
    4204                 :                 } else {
    4205               0 :                         RETURN_EMPTY_STRING();
    4206                 :                 }
    4207                 :         }
    4208                 : 
    4209             143 :         if (str_type == IS_UNICODE) {
    4210             142 :                 int outlen = 0;
    4211                 :                 UChar *outstr;
    4212                 : 
    4213             142 :                 if (ac == 2) {
    4214              45 :                         int minlen = 0, maxlen = 0;
    4215                 :                         HashTable *hash;
    4216                 : 
    4217              45 :                         hash = php_u_strtr_array_prepare_hashtable(HASH_OF(*from), &minlen, &maxlen TSRMLS_CC);
    4218              45 :                         if (hash) {
    4219              41 :                                 outstr = php_u_strtr_array(str.u, str_len, hash, minlen, maxlen, &outlen TSRMLS_CC);
    4220              41 :                                 zend_hash_destroy(hash);
    4221              41 :                                 efree(hash);
    4222              41 :                                 RETVAL_UNICODEL(outstr, outlen, 0);
    4223              41 :                                 Z_TYPE_P(return_value) = IS_UNICODE;
    4224                 :                         } else {
    4225               4 :                                 RETURN_UNICODEL(str.u, str_len, 1);
    4226                 :                         }
    4227                 :                 } else {
    4228              97 :                         convert_to_unicode_ex(from);
    4229                 : 
    4230              97 :                         outstr = php_u_strtr(str.u, str_len, Z_USTRVAL_PP(from), Z_USTRLEN_PP(from), to.u, to_len,
    4231                 :                                           MIN(Z_USTRLEN_PP(from), to_len), &outlen TSRMLS_CC);
    4232                 : 
    4233              97 :                         if (str.u == outstr) {
    4234              16 :                                 ZVAL_UNICODEL(return_value, outstr, outlen, 1);
    4235                 :                         } else {
    4236              81 :                                 ZVAL_UNICODEL(return_value, outstr, outlen, 0);
    4237                 :                         }
    4238                 :                 }
    4239                 :         } else {
    4240               1 :                 if (ac == 2) {
    4241               0 :                         php_strtr_array(return_value, str.s, str_len, HASH_OF(*from));
    4242                 :                 } else {
    4243               1 :                         convert_to_string_ex(from);
    4244                 : 
    4245               1 :                         ZVAL_STRINGL(return_value, str.s, str_len, 1);
    4246                 : 
    4247               1 :                         php_strtr(Z_STRVAL_P(return_value), Z_STRLEN_P(return_value), Z_STRVAL_PP(from), to.s, MIN(Z_STRLEN_PP(from), to_len));
    4248                 :                 }
    4249                 :         }
    4250                 : }
    4251                 : /* }}} */
    4252                 : 
    4253                 : /* {{{ proto string strrev(string str) U
    4254                 :    Reverse a string */
    4255                 : PHP_FUNCTION(strrev)
    4256             101 : {
    4257                 :         zstr str;
    4258                 :         int str_len;
    4259                 :         zend_uchar str_type;
    4260             101 :         char *s, *e, *n = NULL, *p;
    4261                 :         int32_t i, x1, x2;
    4262                 :         UChar32 ch;
    4263             101 :         UChar *u_s, *u_n = NULL, *u_p;
    4264                 : 
    4265             101 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t", &str, &str_len, &str_type) == FAILURE) {
    4266               8 :                 return;
    4267                 :         }
    4268                 : 
    4269              93 :         if (str_type == IS_UNICODE) {
    4270              92 :                 u_n = eumalloc(str_len+1);
    4271              92 :                 u_p = u_n;
    4272              92 :                 u_s = str.u;
    4273                 : 
    4274              92 :                 i = str_len;
    4275            1780 :                 while (i > 0) {
    4276            1596 :                         U16_PREV(u_s, 0, i, ch);
    4277            1596 :                         if (u_getCombiningClass(ch) == 0) {
    4278            1596 :                                 u_p += zend_codepoint_to_uchar(ch, u_p);
    4279                 :                         } else {
    4280               0 :                                 x2 = i;
    4281                 :                                 do {
    4282               0 :                                         U16_PREV(u_s, 0, i, ch);
    4283               0 :                                 } while (u_getCombiningClass(ch) != 0);
    4284               0 :                                 x1 = i;
    4285               0 :                                 while (x1 <= x2) {
    4286               0 :                                         U16_NEXT(u_s, x1, str_len, ch);
    4287               0 :                                         u_p += zend_codepoint_to_uchar(ch, u_p);
    4288                 :                                 }
    4289                 :                         }
    4290                 :                 }
    4291              92 :                 *u_p = 0;
    4292                 :         } else {
    4293               1 :                 n = emalloc(str_len+1);
    4294               1 :                 p = n;
    4295               1 :                 s = str.s;
    4296               1 :                 e = s + str_len;
    4297                 : 
    4298            2525 :                 while (--e >= s) {
    4299            2523 :                         *(p++) = *e;
    4300                 :                 }
    4301               1 :                 *p = '\0';
    4302                 :         }
    4303                 : 
    4304              93 :         if (str_type == IS_UNICODE) {
    4305              92 :                 RETVAL_UNICODEL(u_n, str_len, 0);
    4306                 :         } else {
    4307               1 :                 RETVAL_STRINGL(n, str_len, 0);
    4308                 :         }
    4309                 : }
    4310                 : /* }}} */
    4311                 : 
    4312                 : /* {{{ php_u_similar_str
    4313                 :  */
    4314                 : static void php_u_similar_str(const UChar *txt1, int len1,
    4315                 :                                                           const UChar *txt2, int len2,
    4316                 :                                                           int *pos1, int *end1,
    4317                 :                                                           int *pos2, int *end2, int *max)
    4318              10 : {
    4319                 :         int32_t i1, i2, j1, j2, l;
    4320                 :         UChar32 ch1, ch2;
    4321                 : 
    4322              10 :         *max = 0;
    4323             114 :         for (i1 = 0 ; i1 < len1 ; ) {
    4324             696 :                 for (i2 = 0 ; i2 < len2 ; ) {
    4325             508 :                         l = 0 ; j1 = 0 ; j2 = 0;
    4326            1136 :                         while ((i1+j1 < len1) && (i2+j2 < len2)) {
    4327             602 :                                 U16_NEXT(txt1+i1, j1, len1-i1, ch1);
    4328             602 :                                 U16_NEXT(txt2+i2, j2, len2-i2, ch2);
    4329             602 :                                 if (ch1 != ch2) {
    4330             482 :                                         U16_BACK_1(txt1+i1, 0, j1);
    4331             482 :                                         U16_BACK_1(txt2+i2, 0, j2);
    4332             482 :                                         break;
    4333                 :                                 }
    4334             120 :                                 l++;
    4335                 :                         }
    4336             508 :                         if (l > *max) {
    4337               6 :                                 *max = l;
    4338               6 :                                 *pos1 = i1; *end1 = j1;
    4339               6 :                                 *pos2 = i2; *end2 = j2;
    4340                 :                         }
    4341             508 :                         U16_FWD_1(txt2, i2, len2);
    4342                 :                 }
    4343              94 :                 U16_FWD_1(txt1, i1, len1);
    4344                 :         }
    4345              10 : }
    4346                 : /* }}} */
    4347                 : 
    4348                 : /* {{{ php_similar_str
    4349                 :  */
    4350                 : static void php_similar_str(const char *txt1, int len1, const char *txt2, int len2, int *pos1, int *pos2, int *max)
    4351               0 : {
    4352                 :         char *p, *q;
    4353               0 :         char *end1 = (char *) txt1 + len1;
    4354               0 :         char *end2 = (char *) txt2 + len2;
    4355                 :         int l;
    4356                 : 
    4357               0 :         *max = 0;
    4358               0 :         for (p = (char *) txt1; p < end1; p++) {
    4359               0 :                 for (q = (char *) txt2; q < end2; q++) {
    4360               0 :                         for (l = 0; (p + l < end1) && (q + l < end2) && (p[l] == q[l]); l++);
    4361               0 :                         if (l > *max) {
    4362               0 :                                 *max = l;
    4363               0 :                                 *pos1 = p - txt1;
    4364               0 :                                 *pos2 = q - txt2;
    4365                 :                         }
    4366                 :                 }
    4367                 :         }
    4368               0 : }
    4369                 : /* }}} */
    4370                 : 
    4371                 : /* {{{ php_similar_char
    4372                 :  */
    4373                 : static int php_similar_char(const char *txt1, int len1, const char *txt2, int len2)
    4374               0 : {
    4375                 :         int sum;
    4376                 :         int pos1, pos2, max;
    4377                 : 
    4378               0 :         php_similar_str(txt1, len1, txt2, len2, &pos1, &pos2, &max);
    4379               0 :         if ((sum = max)) {
    4380               0 :                 if (pos1 && pos2) {
    4381               0 :                         sum += php_similar_char(txt1, pos1,
    4382                 :                                                                         txt2, pos2);
    4383                 :                 }
    4384               0 :                 if ((pos1 + max < len1) && (pos2 + max < len2)) {
    4385               0 :                         sum += php_similar_char(txt1 + pos1 + max, len1 - pos1 - max,
    4386                 :                                                                         txt2 + pos2 + max, len2 - pos2 - max);
    4387                 :                 }
    4388                 :         }
    4389                 : 
    4390               0 :         return sum;
    4391                 : }
    4392                 : /* }}} */
    4393                 : 
    4394                 : /* {{{ php_u_similar_char
    4395                 :  */
    4396                 : static int php_u_similar_char(const UChar *txt1, int len1, const UChar *txt2, int len2)
    4397              10 : {
    4398                 :         int sum, max;
    4399                 :         int pos1, pos2, end1, end2;
    4400                 : 
    4401              10 :         php_u_similar_str(txt1, len1, txt2, len2, &pos1, &end1, &pos2, &end2, &max);
    4402              10 :         if ((sum = max)) {
    4403               6 :                 if (pos1 && pos2) {
    4404               0 :                         sum += php_u_similar_char(txt1, pos1, txt2, pos2);
    4405                 :                 }
    4406               6 :                 if ((pos1 + end1 < len1) && (pos2 + end2 < len2)) {
    4407               2 :                         sum += php_u_similar_char((UChar *)txt1+pos1+end1, len1-pos1-end1,
    4408                 :                                                                         (UChar *)txt2+pos2+end2, len2-pos2-end2);
    4409                 :                 }
    4410                 :         }
    4411              10 :         return sum;
    4412                 : }
    4413                 : /* }}} */
    4414                 : 
    4415                 : /* {{{ proto int similar_text(string str1, string str2 [, float percent]) U
    4416                 :    Calculates the similarity between two strings */
    4417                 : PHP_FUNCTION(similar_text)
    4418              10 : {
    4419                 :         zstr t1, t2;
    4420                 :         int t1_len, t2_len;
    4421                 :         zend_uchar t1_type, t2_type;
    4422              10 :         zval *percent = NULL;
    4423                 :         int sim;
    4424                 : 
    4425              10 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "TT|z", &t1, &t1_len,
    4426                 :                                                           &t1_type, &t2, &t2_len, &t2_type, &percent) == FAILURE)  {
    4427               2 :                 return;
    4428                 :         }
    4429                 : 
    4430               8 :         if (percent) {
    4431               4 :                 zval_dtor(percent);
    4432               4 :                 Z_TYPE_P(percent) = IS_DOUBLE;
    4433                 :         }
    4434                 : 
    4435               8 :         if (t1_len + t2_len == 0) {
    4436               0 :                 if (percent) {
    4437               0 :                         Z_DVAL_P(percent) = 0;
    4438                 :                 }
    4439                 : 
    4440               0 :                 RETURN_LONG(0);
    4441                 :         }
    4442                 : 
    4443                 :         /* t1_type and t2_type are guaranteed to be the same */
    4444               8 :         if (t1_type == IS_UNICODE) {
    4445               8 :                 sim = php_u_similar_char(t1.u, t1_len, t2.u, t2_len);
    4446                 :         } else {
    4447               0 :                 sim = php_similar_char(t1.s, t1_len, t2.s, t2_len);
    4448                 :         }
    4449                 : 
    4450               8 :         if (percent) {
    4451               4 :                 if (t1_type == IS_UNICODE) {
    4452               4 :                         Z_DVAL_P(percent) = sim * 200.0 / (u_countChar32(t1.u, t1_len) + u_countChar32(t2.u, t2_len));
    4453                 :                 } else {
    4454               0 :                         Z_DVAL_P(percent) = sim * 200.0 / (t1_len + t2_len);
    4455                 :                 }
    4456                 :         }
    4457                 : 
    4458               8 :         RETURN_LONG(sim);
    4459                 : }
    4460                 : /* }}} */
    4461                 : 
    4462                 : /* {{{ php_u_stripslashes
    4463                 :  *
    4464                 :  * be careful, this edits the string in-place */
    4465                 : PHPAPI void php_u_stripslashes(UChar *str, int *len TSRMLS_DC)
    4466             103 : {
    4467             103 :         int32_t tmp_len = 0, i = 0, src_len = *len;
    4468                 :         UChar32 ch1, ch2;
    4469                 : 
    4470             103 :         ch1 = -1; ch2 = -1;
    4471            1609 :         while (i < src_len) {
    4472            1403 :                 U16_NEXT(str, i, src_len, ch1);
    4473            1403 :                 if (ch1 == '\\') {
    4474             115 :                         if (i < src_len) {
    4475             115 :                                 U16_NEXT(str, i, src_len, ch2);
    4476             115 :                                 if (ch2 == '0') {
    4477              15 :                                         tmp_len += zend_codepoint_to_uchar('\0', str+tmp_len);
    4478                 :                                 } else {
    4479             100 :                                         tmp_len += zend_codepoint_to_uchar(ch2, str+tmp_len);
    4480                 :                                 }
    4481                 :                         }
    4482                 :                 } else {
    4483            1288 :                         tmp_len += zend_codepoint_to_uchar(ch1, str+tmp_len);
    4484                 :                 }
    4485                 :         }
    4486             103 :         *(str+tmp_len) = 0;
    4487             103 :         str = eurealloc(str, tmp_len+1);
    4488             103 :         *len = tmp_len;
    4489                 :         return;
    4490                 : }
    4491                 : /* }}} */
    4492                 : 
    4493                 : /* {{{ php_stripslashes
    4494                 :  *
    4495                 :  * be careful, this edits the string in-place */
    4496                 : PHPAPI void php_stripslashes(char *str, int *len TSRMLS_DC)
    4497               0 : {
    4498                 :         char *s, *t;
    4499                 :         int l;
    4500                 : 
    4501               0 :         if (len != NULL) {
    4502               0 :                 l = *len;
    4503                 :         } else {
    4504               0 :                 l = strlen(str);
    4505                 :         }
    4506               0 :         s = str;
    4507               0 :         t = str;
    4508                 : 
    4509               0 :         while (l > 0) {
    4510               0 :                 if (*t == '\\') {
    4511               0 :                         t++;                            /* skip the slash */
    4512               0 :                         if (len != NULL) {
    4513               0 :                                 (*len)--;
    4514                 :                         }
    4515               0 :                         l--;
    4516               0 :                         if (l > 0) {
    4517               0 :                                 if (*t == '0') {
    4518               0 :                                         *s++='\0';
    4519               0 :                                         t++;
    4520                 :                                 } else {
    4521               0 :                                         *s++ = *t++;    /* preserve the next character */
    4522                 :                                 }
    4523               0 :                                 l--;
    4524                 :                         }
    4525                 :                 } else {
    4526               0 :                         *s++ = *t++;
    4527               0 :                         l--;
    4528                 :                 }
    4529                 :         }
    4530               0 :         if (s != t) {
    4531               0 :                 *s = '\0';
    4532                 :         }
    4533               0 : }
    4534                 : /* }}} */
    4535                 : 
    4536                 : /* {{{ proto binary addcslashes(binary str, binary charlist) U
    4537                 :    Escapes all chars mentioned in charlist with backslash. It creates octal representations if asked to backslash characters with 8th bit set or with ASCII<32 (except '\n', '\r', '\t' etc...) */
    4538                 : PHP_FUNCTION(addcslashes)
    4539              45 : {
    4540                 :         char *str, *what;
    4541                 :         int str_len, what_len;
    4542                 : 
    4543              45 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "SS", &str, &str_len,
    4544                 :                                                           &what, &what_len) == FAILURE) {
    4545               5 :                 return;
    4546                 :         }
    4547                 : 
    4548              40 :         if (str_len == 0) {
    4549               6 :                 RETURN_EMPTY_STRING();
    4550                 :         }
    4551                 : 
    4552              34 :         if (what_len == 0) {
    4553               5 :                 RETURN_STRINGL(str, str_len, 1);
    4554                 :         }
    4555                 : 
    4556              29 :         Z_STRVAL_P(return_value) = php_addcslashes(str, str_len, &Z_STRLEN_P(return_value), 0, what, what_len TSRMLS_CC);
    4557              29 :         RETURN_STRINGL(Z_STRVAL_P(return_value), Z_STRLEN_P(return_value), 0);
    4558                 : }
    4559                 : /* }}} */
    4560                 : 
    4561                 : /* {{{ proto string addslashes(string str) U
    4562                 :    Escapes single quote, double quotes and backslash characters in a string with backslashes */
    4563                 : PHP_FUNCTION(addslashes)
    4564          372453 : {
    4565                 :         zstr str;
    4566          372453 :         int str_len, tmp_len = 0;
    4567                 :         zend_uchar str_type;
    4568          372453 :         void *tmp = NULL;
    4569                 : 
    4570          372453 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t", &str, &str_len, &str_type) == FAILURE) {
    4571               8 :                 return;
    4572                 :         }
    4573                 : 
    4574          372445 :         if (str_type == IS_UNICODE && str_len == 0) {
    4575           95594 :                 RETURN_EMPTY_UNICODE();
    4576          276851 :         } else if (str_type == IS_STRING && str_len == 0) {
    4577               0 :                 RETURN_EMPTY_STRING();
    4578                 :         }
    4579                 : 
    4580          276851 :         if (str_type == IS_UNICODE) {
    4581          276850 :                 tmp = (UChar *)php_u_addslashes(str.u, str_len, &tmp_len, 0 TSRMLS_CC);
    4582          276850 :                 RETURN_UNICODEL((UChar *)tmp, tmp_len, 0);
    4583                 :         } else {
    4584               1 :                 tmp = (char *)php_addslashes(str.s, str_len, &tmp_len, 0 TSRMLS_CC);
    4585               1 :                 RETURN_STRINGL((char *)tmp, tmp_len, 0);
    4586                 :         }
    4587                 : }
    4588                 : /* }}} */
    4589                 : 
    4590                 : /* {{{ proto binary stripcslashes(binary str) U
    4591                 :    Strips backslashes from a string. Uses C-style conventions */
    4592                 : PHP_FUNCTION(stripcslashes)
    4593              35 : {
    4594                 :         char *str;
    4595                 :         int str_len;
    4596                 : 
    4597              35 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "S", &str, &str_len) == FAILURE) {
    4598              10 :                 return;
    4599                 :         }
    4600                 : 
    4601              25 :         ZVAL_STRINGL(return_value, str, str_len, 1);
    4602              25 :         php_stripcslashes(Z_STRVAL_P(return_value), &Z_STRLEN_P(return_value));
    4603                 : }
    4604                 : /* }}} */
    4605                 : 
    4606                 : /* {{{ proto string stripslashes(string str) U
    4607                 :    Strips backslashes from a string */
    4608                 : PHP_FUNCTION(stripslashes)
    4609             111 : {
    4610                 :         zstr str;
    4611                 :         int str_len;
    4612                 :         zend_uchar str_type;
    4613                 : 
    4614             111 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "t", &str, &str_len, &str_type) == FAILURE) {
    4615               8 :                 return;
    4616                 :         }
    4617                 : 
    4618             103 :         if (str_type == IS_UNICODE) {
    4619             103 :                 ZVAL_UNICODEL(return_value, str.u, str_len, 1);
    4620             103 :                 php_u_stripslashes(Z_USTRVAL_P(return_value), &Z_USTRLEN_P(return_value) TSRMLS_CC);
    4621                 :         } else {
    4622               0 :                 ZVAL_STRINGL(return_value, str.s, str_len, 1);
    4623               0 :                 php_stripslashes(Z_STRVAL_P(return_value), &Z_STRLEN_P(return_value) TSRMLS_CC);
    4624                 :         }
    4625                 : }
    4626                 : /* }}} */
    4627                 : 
    4628                 : #ifndef HAVE_STRERROR
    4629                 : /* {{{ php_strerror
    4630                 :  */
    4631                 : char *php_strerror(int errnum)
    4632                 : {
    4633                 :         extern int sys_nerr;
    4634                 :         extern char *sys_errlist[];
    4635                 :         TSRMLS_FETCH();
    4636                 : 
    4637                 :         if ((unsigned int) errnum < sys_nerr) {
    4638                 :                 return(sys_errlist[errnum]);
    4639                 :         }
    4640                 : 
    4641                 :         (void) snprintf(BG(str_ebuf), sizeof(php_basic_globals.str_ebuf), "Unknown error: %d", errnum);
    4642                 :         return(BG(str_ebuf));
    4643                 : }
    4644                 : /* }}} */
    4645                 : #endif
    4646                 : 
    4647                 : /* {{{ php_stripcslashes
    4648                 :  */
    4649                 : PHPAPI void php_stripcslashes(char *str, int *len)
    4650              25 : {
    4651                 :         char *source, *target, *end;
    4652              25 :         int  nlen = *len, i;
    4653                 :         char numtmp[4];
    4654                 : 
    4655             132 :         for (source=str, end=str+nlen, target=str; source < end; source++) {
    4656             142 :                 if (*source == '\\' && source+1 < end) {
    4657              35 :                         source++;
    4658              35 :                         switch (*source) {
    4659               2 :                                 case 'n':  *target++='\n'; nlen--; break;
    4660               2 :                                 case 'r':  *target++='\r'; nlen--; break;
    4661               0 :                                 case 'a':  *target++='\a'; nlen--; break;
    4662               0 :                                 case 't':  *target++='\t'; nlen--; break;
    4663               0 :                                 case 'v':  *target++='\v'; nlen--; break;
    4664               0 :                                 case 'b':  *target++='\b'; nlen--; break;
    4665               0 :                                 case 'f':  *target++='\f'; nlen--; break;
    4666               0 :                                 case '\\': *target++='\\'; nlen--; break;
    4667                 :                                 case 'x':
    4668              11 :                                         if (source+1 < end && isxdigit((int)(*(source+1)))) {
    4669              11 :                                                 numtmp[0] = *++source;
    4670              22 :                                                 if (source+1 < end && isxdigit((int)(*(source+1)))) {
    4671              11 :                                                         numtmp[1] = *++source;
    4672              11 :                                                         numtmp[2] = '\0';
    4673              11 :                                                         nlen-=3;
    4674                 :                                                 } else {
    4675               0 :                                                         numtmp[1] = '\0';
    4676               0 :                                                         nlen-=2;
    4677                 :                                                 }
    4678              11 :                                                 *target++=(char)strtol(numtmp, NULL, 16);
    4679              11 :                                                 break;
    4680                 :                                         }
    4681                 :                                         /* break is left intentionally */
    4682                 :                                 default:
    4683              20 :                                         i=0;
    4684              73 :                                         while (source < end && *source >= '0' && *source <= '7' && i<3) {
    4685              33 :                                                 numtmp[i++] = *source++;
    4686                 :                                         }
    4687              20 :                                         if (i) {
    4688              11 :                                                 numtmp[i]='\0';
    4689              11 :                                                 *target++=(char)strtol(numtmp, NULL, 8);
    4690              11 :                                                 nlen-=i;
    4691              11 :                                                 source--;
    4692                 :                                         } else {
    4693               9 :                                                 *target++=*source;
    4694               9 :                                                 nlen--;
    4695                 :                                         }
    4696                 :                         }
    4697                 :                 } else {
    4698              72 :                         *target++=*source;
    4699                 :                 }
    4700                 :         }
    4701                 : 
    4702              25 :         if (nlen != 0) {
    4703              18 :                 *target='\0';
    4704                 :         }
    4705                 : 
    4706              25 :         *len = nlen;
    4707              25 : }
    4708                 : /* }}} */
    4709                 : 
    4710                 : /* {{{ php_addcslashes
    4711                 :  */
    4712                 : PHPAPI char *php_addcslashes(char *str, int length, int *new_length, int should_free, char *what, int wlength TSRMLS_DC)
    4713            1238 : {
    4714                 :         char flags[256];
    4715            1238 :         char *new_str = safe_emalloc(4, (length?length:(length=strlen(str))), 1);
    4716                 :         char *source, *target;
    4717                 :         char *end;
    4718                 :         char c;
    4719                 :         int  newlen;
    4720                 : 
    4721            1238 :         if (!wlength) {
    4722               0 :                 wlength = strlen(what);
    4723                 :         }
    4724                 : 
    4725            1238 :         if (!length) {
    4726               7 :                 length = strlen(str);
    4727                 :         }
    4728                 : 
    4729            1238 :         php_charmask((unsigned char*)what, wlength, flags TSRMLS_CC);
    4730                 : 
    4731           18177 :         for (source = str, end = source + length, target = new_str; source < end; source++) {
    4732           16939 :                 c = *source;
    4733           16939 :                 if (flags[(unsigned char)c]) {
    4734             103 :                         if ((unsigned char) c < 32 || (unsigned char) c > 126) {
    4735              15 :                                 *target++ = '\\';
    4736              15 :                                 switch (c) {
    4737               2 :                                         case '\n': *target++ = 'n'; break;
    4738               2 :                                         case '\t': *target++ = 't'; break;
    4739               2 :                                         case '\r': *target++ = 'r'; break;
    4740               0 :                                         case '\a': *target++ = 'a'; break;
    4741               2 :                                         case '\v': *target++ = 'v'; break;
    4742               0 :                                         case '\b': *target++ = 'b'; break;
    4743               2 :                                         case '\f': *target++ = 'f'; break;
    4744               5 :                                         default: target += sprintf(target, "%03o", (unsigned char) c);
    4745                 :                                 }
    4746              15 :                                 continue;
    4747                 :                         }
    4748              88 :                         *target++ = '\\';
    4749                 :                 }
    4750           16924 :                 *target++ = c;
    4751                 :         }
    4752            1238 :         *target = 0;
    4753            1238 :         newlen = target - new_str;
    4754            1238 :         if (target - new_str < length * 4) {
    4755            1231 :                 new_str = erealloc(new_str, newlen + 1);
    4756                 :         }
    4757            1238 :         if (new_length) {
    4758            1238 :                 *new_length = newlen;
    4759                 :         }
    4760            1238 :         if (should_free) {
    4761               0 :                 STR_FREE(str);
    4762                 :         }
    4763            1238 :         return new_str;
    4764                 : }
    4765                 : /* }}} */
    4766                 : 
    4767                 : /* {{{ php_u_addslashes
    4768                 :  */
    4769                 : PHPAPI UChar *php_u_addslashes(UChar *str, int length, int *new_length, int should_free TSRMLS_DC)
    4770          276850 : {
    4771          276850 :         return php_u_addslashes_ex(str, length, new_length, should_free TSRMLS_CC);
    4772                 : }
    4773                 : /* }}} */
    4774                 : 
    4775                 : /* {{{ php_u_addslashes_ex
    4776                 :  */
    4777                 : PHPAPI UChar *php_u_addslashes_ex(UChar *str, int length, int *new_length, int should_free TSRMLS_DC)
    4778          276850 : {
    4779                 :         UChar *buf;
    4780          276850 :         int32_t buf_len = 0, i = 0;
    4781                 :         UChar32 ch;
    4782                 : 
    4783          276850 :         if (!new_length) {
    4784               0 :                 new_length = &buf_len;
    4785                 :         }
    4786          276850 :         if (!str) {
    4787               0 :                 *new_length = 0;
    4788               0 :                 return str;
    4789                 :         }
    4790                 : 
    4791          276850 :         buf = eumalloc(length * 2 + 1);
    4792                 : 
    4793         1510850 :         while (i < length) {
    4794          957150 :                 U16_NEXT(str, i, length, ch);
    4795          957150 :                 switch (ch) {
    4796                 :                         case '\0':
    4797              28 :                                 *(buf+buf_len) = (UChar)0x5C; buf_len++; /* \ */
    4798              28 :                                 *(buf+buf_len) = (UChar)0x30; buf_len++; /* 0 */
    4799              28 :                                 break;
    4800                 :                         case '\'':
    4801                 :                         case '\"':
    4802                 :                         case '\\':
    4803             231 :                                         *(buf+buf_len) = (UChar)0x5C; buf_len++; /* \ */
    4804                 :                                         /* break is missing *intentionally* */
    4805                 :                         default:
    4806          957122 :                                         buf_len += zend_codepoint_to_uchar(ch, buf+buf_len);
    4807                 :                                         break;
    4808                 :                 }
    4809                 :         }
    4810                 : 
    4811          276850 :         *(buf+buf_len) = 0;
    4812                 : 
    4813          276850 :         if (should_free) {
    4814               0 :                 STR_FREE(str);
    4815                 :         }
    4816          276850 :         buf = eurealloc(buf, buf_len+1);
    4817          276850 :         *new_length = buf_len;
    4818          276850 :         return buf;
    4819                 : }
    4820                 : /* }}} */
    4821                 : 
    4822                 : /* {{{ php_addslashes
    4823                 :  */
    4824                 : PHPAPI char *php_addslashes(char *str, int length, int *new_length, int should_free TSRMLS_DC)
    4825             105 : {
    4826             105 :         return php_addslashes_ex(str, length, new_length, should_free TSRMLS_CC);
    4827                 : }
    4828                 : /* }}} */
    4829                 : 
    4830                 : /* {{{ php_addslashes_ex
    4831                 :  */
    4832                 : PHPAPI char *php_addslashes_ex(char *str, int length, int *new_length, int should_free TSRMLS_DC)
    4833             113 : {
    4834                 :         /* maximum string length, worst case situation */
    4835                 :         char *new_str;
    4836                 :         char *source, *target;
    4837                 :         char *end;
    4838                 :         int local_new_length;
    4839                 : 
    4840             113 :         if (!new_length) {
    4841               9 :                 new_length = &local_new_length;
    4842                 :         }
    4843             113 :         if (!str) {
    4844               0 :                 *new_length = 0;
    4845               0 :                 return str;
    4846                 :         }
    4847             113 :         new_str = (char *) safe_emalloc(2, (length ? length : (length = strlen(str))), 1);
    4848             113 :         source = str;
    4849             113 :         end = source + length;
    4850             113 :         target = new_str;
    4851                 : 
    4852             908 :         while (source < end) {
    4853             682 :                 switch (*source) {
    4854                 :                         case '\0':
    4855               1 :                                 *target++ = '\\';
    4856               1 :                                 *target++ = '0';
    4857               1 :                                 break;
    4858                 :                         case '\'':
    4859                 :                                 case '\"':
    4860                 :                         case '\\':
    4861              11 :                                         *target++ = '\\';
    4862                 :                                         /* break is missing *intentionally* */
    4863                 :                         default:
    4864             681 :                                         *target++ = *source;
    4865                 :                                         break;
    4866                 :                 }
    4867                 : 
    4868             682 :                 source++;
    4869                 :         }
    4870                 : 
    4871             113 :         *target = 0;
    4872             113 :         *new_length = target - new_str;
    4873             113 :         if (should_free) {
    4874               0 :                 STR_FREE(str);
    4875                 :         }
    4876             113 :         new_str = (char *) erealloc(new_str, *new_length + 1);
    4877             113 :         return new_str;
    4878                 : }
    4879                 : /* }}} */
    4880                 : 
    4881                 : #define _HEB_BLOCK_TYPE_ENG 1
    4882                 : #define _HEB_BLOCK_TYPE_HEB 2
    4883                 : #define isheb(c)      (((((unsigned char) c) >= 224) && (((unsigned char) c) <= 250)) ? 1 : 0)
    4884                 : #define _isblank(c)   (((((unsigned char) c) == ' '  || ((unsigned char) c) == '\t')) ? 1 : 0)
    4885