root/ext/standard/string.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. register_string_constants
  2. php_bin2hex
  3. php_hex2bin
  4. localeconv_r
  5. PHP_MINIT_FUNCTION
  6. PHP_MSHUTDOWN_FUNCTION
  7. PHP_FUNCTION
  8. PHP_FUNCTION
  9. php_spn_common_handler
  10. PHP_FUNCTION
  11. PHP_FUNCTION
  12. PHP_MINIT_FUNCTION
  13. PHP_FUNCTION
  14. PHP_FUNCTION
  15. php_charmask
  16. php_trim
  17. php_do_trim
  18. PHP_FUNCTION
  19. PHP_FUNCTION
  20. PHP_FUNCTION
  21. PHP_FUNCTION
  22. php_explode
  23. php_explode_negative_limit
  24. PHP_FUNCTION
  25. php_implode
  26. PHP_FUNCTION
  27. PHP_FUNCTION
  28. php_strtoupper
  29. php_string_toupper
  30. PHP_FUNCTION
  31. php_strtolower
  32. php_string_tolower
  33. PHP_FUNCTION
  34. php_basename
  35. PHP_FUNCTION
  36. php_dirname
  37. PHP_FUNCTION
  38. PHP_FUNCTION
  39. php_stristr
  40. php_strspn
  41. php_strcspn
  42. php_needle_char
  43. PHP_FUNCTION
  44. PHP_FUNCTION
  45. PHP_FUNCTION
  46. PHP_FUNCTION
  47. PHP_FUNCTION
  48. PHP_FUNCTION
  49. PHP_FUNCTION
  50. php_chunk_split
  51. PHP_FUNCTION
  52. PHP_FUNCTION
  53. PHP_FUNCTION
  54. PHP_FUNCTION
  55. PHP_FUNCTION
  56. PHP_FUNCTION
  57. php_ucfirst
  58. PHP_FUNCTION
  59. php_lcfirst
  60. PHP_FUNCTION
  61. PHP_FUNCTION
  62. php_strtr
  63. php_strtr_ex
  64. php_strtr_array
  65. php_char_to_str_ex
  66. php_str_to_str_ex
  67. php_str_to_str_i_ex
  68. php_str_to_str
  69. PHP_FUNCTION
  70. PHP_FUNCTION
  71. php_similar_str
  72. php_similar_char
  73. PHP_FUNCTION
  74. php_stripslashes
  75. PHP_FUNCTION
  76. PHP_FUNCTION
  77. PHP_FUNCTION
  78. PHP_FUNCTION
  79. php_strerror
  80. php_stripcslashes
  81. php_addcslashes
  82. php_addslashes
  83. php_str_replace_in_subject
  84. php_str_replace_common
  85. PHP_FUNCTION
  86. PHP_FUNCTION
  87. php_hebrev
  88. PHP_FUNCTION
  89. PHP_FUNCTION
  90. PHP_FUNCTION
  91. PHP_FUNCTION
  92. PHP_FUNCTION
  93. PHP_FUNCTION
  94. php_tag_find
  95. php_strip_tags
  96. php_strip_tags_ex
  97. PHP_FUNCTION
  98. PHP_FUNCTION
  99. PHP_FUNCTION
  100. php_strnatcmp
  101. string_natural_compare_function_ex
  102. string_natural_case_compare_function
  103. string_natural_compare_function
  104. PHP_FUNCTION
  105. PHP_FUNCTION
  106. PHP_FUNCTION
  107. PHP_FUNCTION
  108. PHP_FUNCTION
  109. PHP_FUNCTION
  110. PHP_FUNCTION
  111. php_string_shuffle
  112. PHP_FUNCTION
  113. PHP_FUNCTION
  114. PHP_FUNCTION
  115. PHP_FUNCTION
  116. PHP_FUNCTION
  117. PHP_FUNCTION

   1 /*
   2    +----------------------------------------------------------------------+
   3    | PHP Version 7                                                        |
   4    +----------------------------------------------------------------------+
   5    | Copyright (c) 1997-2016 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$ */
  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 /*
  40  * This define is here because some versions of libintl redefine setlocale
  41  * to point to libintl_setlocale.  That's a ridiculous thing to do as far
  42  * as I am concerned, but with this define and the subsequent undef we
  43  * limit the damage to just the actual setlocale() call in this file
  44  * without turning zif_setlocale into zif_libintl_setlocale.  -Rasmus
  45  */
  46 #define php_my_setlocale setlocale
  47 #ifdef HAVE_LIBINTL
  48 # include <libintl.h> /* For LC_MESSAGES */
  49  #ifdef setlocale
  50  # undef setlocale
  51  #endif
  52 #endif
  53 
  54 #include "scanf.h"
  55 #include "zend_API.h"
  56 #include "zend_execute.h"
  57 #include "php_globals.h"
  58 #include "basic_functions.h"
  59 #include "zend_smart_str.h"
  60 #include <Zend/zend_exceptions.h>
  61 #ifdef ZTS
  62 #include "TSRM.h"
  63 #endif
  64 
  65 /* For str_getcsv() support */
  66 #include "ext/standard/file.h"
  67 
  68 #define STR_PAD_LEFT                    0
  69 #define STR_PAD_RIGHT                   1
  70 #define STR_PAD_BOTH                    2
  71 #define PHP_PATHINFO_DIRNAME    1
  72 #define PHP_PATHINFO_BASENAME   2
  73 #define PHP_PATHINFO_EXTENSION  4
  74 #define PHP_PATHINFO_FILENAME   8
  75 #define PHP_PATHINFO_ALL        (PHP_PATHINFO_DIRNAME | PHP_PATHINFO_BASENAME | PHP_PATHINFO_EXTENSION | PHP_PATHINFO_FILENAME)
  76 
  77 #define STR_STRSPN                              0
  78 #define STR_STRCSPN                             1
  79 
  80 /* {{{ register_string_constants
  81  */
  82 void register_string_constants(INIT_FUNC_ARGS)
  83 {
  84         REGISTER_LONG_CONSTANT("STR_PAD_LEFT", STR_PAD_LEFT, CONST_CS | CONST_PERSISTENT);
  85         REGISTER_LONG_CONSTANT("STR_PAD_RIGHT", STR_PAD_RIGHT, CONST_CS | CONST_PERSISTENT);
  86         REGISTER_LONG_CONSTANT("STR_PAD_BOTH", STR_PAD_BOTH, CONST_CS | CONST_PERSISTENT);
  87         REGISTER_LONG_CONSTANT("PATHINFO_DIRNAME", PHP_PATHINFO_DIRNAME, CONST_CS | CONST_PERSISTENT);
  88         REGISTER_LONG_CONSTANT("PATHINFO_BASENAME", PHP_PATHINFO_BASENAME, CONST_CS | CONST_PERSISTENT);
  89         REGISTER_LONG_CONSTANT("PATHINFO_EXTENSION", PHP_PATHINFO_EXTENSION, CONST_CS | CONST_PERSISTENT);
  90         REGISTER_LONG_CONSTANT("PATHINFO_FILENAME", PHP_PATHINFO_FILENAME, CONST_CS | CONST_PERSISTENT);
  91 
  92 #ifdef HAVE_LOCALECONV
  93         /* If last members of struct lconv equal CHAR_MAX, no grouping is done */
  94 
  95 /* This is bad, but since we are going to be hardcoding in the POSIX stuff anyway... */
  96 # ifndef HAVE_LIMITS_H
  97 # define CHAR_MAX 127
  98 # endif
  99 
 100         REGISTER_LONG_CONSTANT("CHAR_MAX", CHAR_MAX, CONST_CS | CONST_PERSISTENT);
 101 #endif
 102 
 103 #ifdef HAVE_LOCALE_H
 104         REGISTER_LONG_CONSTANT("LC_CTYPE", LC_CTYPE, CONST_CS | CONST_PERSISTENT);
 105         REGISTER_LONG_CONSTANT("LC_NUMERIC", LC_NUMERIC, CONST_CS | CONST_PERSISTENT);
 106         REGISTER_LONG_CONSTANT("LC_TIME", LC_TIME, CONST_CS | CONST_PERSISTENT);
 107         REGISTER_LONG_CONSTANT("LC_COLLATE", LC_COLLATE, CONST_CS | CONST_PERSISTENT);
 108         REGISTER_LONG_CONSTANT("LC_MONETARY", LC_MONETARY, CONST_CS | CONST_PERSISTENT);
 109         REGISTER_LONG_CONSTANT("LC_ALL", LC_ALL, CONST_CS | CONST_PERSISTENT);
 110 # ifdef LC_MESSAGES
 111         REGISTER_LONG_CONSTANT("LC_MESSAGES", LC_MESSAGES, CONST_CS | CONST_PERSISTENT);
 112 # endif
 113 #endif
 114 
 115 }
 116 /* }}} */
 117 
 118 int php_tag_find(char *tag, size_t len, const char *set);
 119 
 120 #ifdef PHP_WIN32
 121 # define SET_ALIGNED(alignment, decl) __declspec(align(alignment)) decl
 122 #elif HAVE_ATTRIBUTE_ALIGNED
 123 # define SET_ALIGNED(alignment, decl) decl __attribute__ ((__aligned__ (alignment)))
 124 #else
 125 # define SET_ALIGNED(alignment, decl) decl
 126 #endif
 127 
 128 /* this is read-only, so it's ok */
 129 SET_ALIGNED(16, static char hexconvtab[]) = "0123456789abcdef";
 130 
 131 /* localeconv mutex */
 132 #ifdef ZTS
 133 static MUTEX_T locale_mutex = NULL;
 134 #endif
 135 
 136 /* {{{ php_bin2hex
 137  */
 138 static zend_string *php_bin2hex(const unsigned char *old, const size_t oldlen)
 139 {
 140         zend_string *result;
 141         size_t i, j;
 142 
 143         result = zend_string_safe_alloc(oldlen, 2 * sizeof(char), 0, 0);
 144 
 145         for (i = j = 0; i < oldlen; i++) {
 146                 ZSTR_VAL(result)[j++] = hexconvtab[old[i] >> 4];
 147                 ZSTR_VAL(result)[j++] = hexconvtab[old[i] & 15];
 148         }
 149         ZSTR_VAL(result)[j] = '\0';
 150 
 151         return result;
 152 }
 153 /* }}} */
 154 
 155 /* {{{ php_hex2bin
 156  */
 157 static zend_string *php_hex2bin(const unsigned char *old, const size_t oldlen)
 158 {
 159         size_t target_length = oldlen >> 1;
 160         zend_string *str = zend_string_alloc(target_length, 0);
 161         unsigned char *ret = (unsigned char *)ZSTR_VAL(str);
 162         size_t i, j;
 163 
 164         for (i = j = 0; i < target_length; i++) {
 165                 unsigned char c = old[j++];
 166                 unsigned char l = c & ~0x20;
 167                 int is_letter = ((unsigned int) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(unsigned int) - 1);
 168                 unsigned char d;
 169 
 170                 /* basically (c >= '0' && c <= '9') || (l >= 'A' && l <= 'F') */ 
 171                 if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(unsigned int) - 1)) | is_letter)) {
 172                         d = (l - 0x10 - 0x27 * is_letter) << 4;
 173                 } else {
 174                         zend_string_free(str);
 175                         return NULL;
 176                 }
 177                 c = old[j++];
 178                 l = c & ~0x20;
 179                 is_letter = ((unsigned int) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(unsigned int) - 1);
 180                 if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(unsigned int) - 1)) | is_letter)) {
 181                         d |= l - 0x10 - 0x27 * is_letter;
 182                 } else {
 183                         zend_string_free(str);
 184                         return NULL;
 185                 }
 186                 ret[i] = d;
 187         }
 188         ret[i] = '\0';
 189 
 190         return str;
 191 }
 192 /* }}} */
 193 
 194 #ifdef HAVE_LOCALECONV
 195 /* {{{ localeconv_r
 196  * glibc's localeconv is not reentrant, so lets make it so ... sorta */
 197 PHPAPI struct lconv *localeconv_r(struct lconv *out)
 198 {
 199         struct lconv *res;
 200 
 201 # ifdef ZTS
 202         tsrm_mutex_lock( locale_mutex );
 203 # endif
 204 
 205 /*  cur->locinfo is struct __crt_locale_info which implementation is
 206         hidden in vc14. TODO revisit this and check if a workaround available
 207         and needed. */
 208 #if defined(PHP_WIN32) && _MSC_VER < 1900 && defined(ZTS)
 209         {
 210                 /* Even with the enabled per thread locale, localeconv
 211                         won't check any locale change in the master thread. */
 212                 _locale_t cur = _get_current_locale();
 213 
 214                 res = cur->locinfo->lconv;
 215         }
 216 #else
 217         /* localeconv doesn't return an error condition */
 218         res = localeconv();
 219 #endif
 220 
 221         *out = *res;
 222 
 223 # ifdef ZTS
 224         tsrm_mutex_unlock( locale_mutex );
 225 # endif
 226 
 227         return out;
 228 }
 229 /* }}} */
 230 
 231 # ifdef ZTS
 232 /* {{{ PHP_MINIT_FUNCTION
 233  */
 234 PHP_MINIT_FUNCTION(localeconv)
 235 {
 236         locale_mutex = tsrm_mutex_alloc();
 237         return SUCCESS;
 238 }
 239 /* }}} */
 240 
 241 /* {{{ PHP_MSHUTDOWN_FUNCTION
 242  */
 243 PHP_MSHUTDOWN_FUNCTION(localeconv)
 244 {
 245         tsrm_mutex_free( locale_mutex );
 246         locale_mutex = NULL;
 247         return SUCCESS;
 248 }
 249 /* }}} */
 250 # endif
 251 #endif
 252 
 253 /* {{{ proto string bin2hex(string data)
 254    Converts the binary representation of data to hex */
 255 PHP_FUNCTION(bin2hex)
 256 {
 257         zend_string *result;
 258         zend_string *data;
 259 
 260         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &data) == FAILURE) {
 261                 return;
 262         }
 263 
 264         result = php_bin2hex((unsigned char *)ZSTR_VAL(data), ZSTR_LEN(data));
 265 
 266         if (!result) {
 267                 RETURN_FALSE;
 268         }
 269 
 270         RETURN_STR(result);
 271 }
 272 /* }}} */
 273 
 274 /* {{{ proto string hex2bin(string data)
 275    Converts the hex representation of data to binary */
 276 PHP_FUNCTION(hex2bin)
 277 {
 278         zend_string *result, *data;
 279 
 280         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &data) == FAILURE) {
 281                 return;
 282         }
 283 
 284         if (ZSTR_LEN(data) % 2 != 0) {
 285                 php_error_docref(NULL, E_WARNING, "Hexadecimal input string must have an even length");
 286                 RETURN_FALSE;
 287         }
 288 
 289         result = php_hex2bin((unsigned char *)ZSTR_VAL(data), ZSTR_LEN(data));
 290 
 291         if (!result) {
 292                 php_error_docref(NULL, E_WARNING, "Input string must be hexadecimal string");
 293                 RETURN_FALSE;
 294         }
 295 
 296         RETVAL_STR(result);
 297 }
 298 /* }}} */
 299 
 300 static void php_spn_common_handler(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
 301 {
 302         zend_string *s11, *s22;
 303         zend_long start = 0, len = 0;
 304 
 305         if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|ll", &s11,
 306                                 &s22, &start, &len) == FAILURE) {
 307                 return;
 308         }
 309 
 310         if (ZEND_NUM_ARGS() < 4) {
 311                 len = ZSTR_LEN(s11);
 312         }
 313 
 314         /* look at substr() function for more information */
 315 
 316         if (start < 0) {
 317                 start += (zend_long)ZSTR_LEN(s11);
 318                 if (start < 0) {
 319                         start = 0;
 320                 }
 321         } else if ((size_t)start > ZSTR_LEN(s11)) {
 322                 RETURN_FALSE;
 323         }
 324 
 325         if (len < 0) {
 326                 len += (ZSTR_LEN(s11) - start);
 327                 if (len < 0) {
 328                         len = 0;
 329                 }
 330         }
 331 
 332         if (len > (zend_long)ZSTR_LEN(s11) - start) {
 333                 len = ZSTR_LEN(s11) - start;
 334         }
 335 
 336         if(len == 0) {
 337                 RETURN_LONG(0);
 338         }
 339 
 340         if (behavior == STR_STRSPN) {
 341                 RETURN_LONG(php_strspn(ZSTR_VAL(s11) + start /*str1_start*/,
 342                                                 ZSTR_VAL(s22) /*str2_start*/,
 343                                                 ZSTR_VAL(s11) + start + len /*str1_end*/,
 344                                                 ZSTR_VAL(s22) + ZSTR_LEN(s22) /*str2_end*/));
 345         } else if (behavior == STR_STRCSPN) {
 346                 RETURN_LONG(php_strcspn(ZSTR_VAL(s11) + start /*str1_start*/,
 347                                                 ZSTR_VAL(s22) /*str2_start*/,
 348                                                 ZSTR_VAL(s11) + start + len /*str1_end*/,
 349                                                 ZSTR_VAL(s22) + ZSTR_LEN(s22) /*str2_end*/));
 350         }
 351 
 352 }
 353 /* }}} */
 354 
 355 /* {{{ proto int strspn(string str, string mask [, int start [, int len]])
 356    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) */
 357 PHP_FUNCTION(strspn)
 358 {
 359         php_spn_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, STR_STRSPN);
 360 }
 361 /* }}} */
 362 
 363 /* {{{ proto int strcspn(string str, string mask [, int start [, int len]])
 364    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) */
 365 PHP_FUNCTION(strcspn)
 366 {
 367         php_spn_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, STR_STRCSPN);
 368 }
 369 /* }}} */
 370 
 371 /* {{{ PHP_MINIT_FUNCTION(nl_langinfo) */
 372 #if HAVE_NL_LANGINFO
 373 PHP_MINIT_FUNCTION(nl_langinfo)
 374 {
 375 #define REGISTER_NL_LANGINFO_CONSTANT(x)        REGISTER_LONG_CONSTANT(#x, x, CONST_CS | CONST_PERSISTENT)
 376 #ifdef ABDAY_1
 377         REGISTER_NL_LANGINFO_CONSTANT(ABDAY_1);
 378         REGISTER_NL_LANGINFO_CONSTANT(ABDAY_2);
 379         REGISTER_NL_LANGINFO_CONSTANT(ABDAY_3);
 380         REGISTER_NL_LANGINFO_CONSTANT(ABDAY_4);
 381         REGISTER_NL_LANGINFO_CONSTANT(ABDAY_5);
 382         REGISTER_NL_LANGINFO_CONSTANT(ABDAY_6);
 383         REGISTER_NL_LANGINFO_CONSTANT(ABDAY_7);
 384 #endif
 385 #ifdef DAY_1
 386         REGISTER_NL_LANGINFO_CONSTANT(DAY_1);
 387         REGISTER_NL_LANGINFO_CONSTANT(DAY_2);
 388         REGISTER_NL_LANGINFO_CONSTANT(DAY_3);
 389         REGISTER_NL_LANGINFO_CONSTANT(DAY_4);
 390         REGISTER_NL_LANGINFO_CONSTANT(DAY_5);
 391         REGISTER_NL_LANGINFO_CONSTANT(DAY_6);
 392         REGISTER_NL_LANGINFO_CONSTANT(DAY_7);
 393 #endif
 394 #ifdef ABMON_1
 395         REGISTER_NL_LANGINFO_CONSTANT(ABMON_1);
 396         REGISTER_NL_LANGINFO_CONSTANT(ABMON_2);
 397         REGISTER_NL_LANGINFO_CONSTANT(ABMON_3);
 398         REGISTER_NL_LANGINFO_CONSTANT(ABMON_4);
 399         REGISTER_NL_LANGINFO_CONSTANT(ABMON_5);
 400         REGISTER_NL_LANGINFO_CONSTANT(ABMON_6);
 401         REGISTER_NL_LANGINFO_CONSTANT(ABMON_7);
 402         REGISTER_NL_LANGINFO_CONSTANT(ABMON_8);
 403         REGISTER_NL_LANGINFO_CONSTANT(ABMON_9);
 404         REGISTER_NL_LANGINFO_CONSTANT(ABMON_10);
 405         REGISTER_NL_LANGINFO_CONSTANT(ABMON_11);
 406         REGISTER_NL_LANGINFO_CONSTANT(ABMON_12);
 407 #endif
 408 #ifdef MON_1
 409         REGISTER_NL_LANGINFO_CONSTANT(MON_1);
 410         REGISTER_NL_LANGINFO_CONSTANT(MON_2);
 411         REGISTER_NL_LANGINFO_CONSTANT(MON_3);
 412         REGISTER_NL_LANGINFO_CONSTANT(MON_4);
 413         REGISTER_NL_LANGINFO_CONSTANT(MON_5);
 414         REGISTER_NL_LANGINFO_CONSTANT(MON_6);
 415         REGISTER_NL_LANGINFO_CONSTANT(MON_7);
 416         REGISTER_NL_LANGINFO_CONSTANT(MON_8);
 417         REGISTER_NL_LANGINFO_CONSTANT(MON_9);
 418         REGISTER_NL_LANGINFO_CONSTANT(MON_10);
 419         REGISTER_NL_LANGINFO_CONSTANT(MON_11);
 420         REGISTER_NL_LANGINFO_CONSTANT(MON_12);
 421 #endif
 422 #ifdef AM_STR
 423         REGISTER_NL_LANGINFO_CONSTANT(AM_STR);
 424 #endif
 425 #ifdef PM_STR
 426         REGISTER_NL_LANGINFO_CONSTANT(PM_STR);
 427 #endif
 428 #ifdef D_T_FMT
 429         REGISTER_NL_LANGINFO_CONSTANT(D_T_FMT);
 430 #endif
 431 #ifdef D_FMT
 432         REGISTER_NL_LANGINFO_CONSTANT(D_FMT);
 433 #endif
 434 #ifdef T_FMT
 435         REGISTER_NL_LANGINFO_CONSTANT(T_FMT);
 436 #endif
 437 #ifdef T_FMT_AMPM
 438         REGISTER_NL_LANGINFO_CONSTANT(T_FMT_AMPM);
 439 #endif
 440 #ifdef ERA
 441         REGISTER_NL_LANGINFO_CONSTANT(ERA);
 442 #endif
 443 #ifdef ERA_YEAR
 444         REGISTER_NL_LANGINFO_CONSTANT(ERA_YEAR);
 445 #endif
 446 #ifdef ERA_D_T_FMT
 447         REGISTER_NL_LANGINFO_CONSTANT(ERA_D_T_FMT);
 448 #endif
 449 #ifdef ERA_D_FMT
 450         REGISTER_NL_LANGINFO_CONSTANT(ERA_D_FMT);
 451 #endif
 452 #ifdef ERA_T_FMT
 453         REGISTER_NL_LANGINFO_CONSTANT(ERA_T_FMT);
 454 #endif
 455 #ifdef ALT_DIGITS
 456         REGISTER_NL_LANGINFO_CONSTANT(ALT_DIGITS);
 457 #endif
 458 #ifdef INT_CURR_SYMBOL
 459         REGISTER_NL_LANGINFO_CONSTANT(INT_CURR_SYMBOL);
 460 #endif
 461 #ifdef CURRENCY_SYMBOL
 462         REGISTER_NL_LANGINFO_CONSTANT(CURRENCY_SYMBOL);
 463 #endif
 464 #ifdef CRNCYSTR
 465         REGISTER_NL_LANGINFO_CONSTANT(CRNCYSTR);
 466 #endif
 467 #ifdef MON_DECIMAL_POINT
 468         REGISTER_NL_LANGINFO_CONSTANT(MON_DECIMAL_POINT);
 469 #endif
 470 #ifdef MON_THOUSANDS_SEP
 471         REGISTER_NL_LANGINFO_CONSTANT(MON_THOUSANDS_SEP);
 472 #endif
 473 #ifdef MON_GROUPING
 474         REGISTER_NL_LANGINFO_CONSTANT(MON_GROUPING);
 475 #endif
 476 #ifdef POSITIVE_SIGN
 477         REGISTER_NL_LANGINFO_CONSTANT(POSITIVE_SIGN);
 478 #endif
 479 #ifdef NEGATIVE_SIGN
 480         REGISTER_NL_LANGINFO_CONSTANT(NEGATIVE_SIGN);
 481 #endif
 482 #ifdef INT_FRAC_DIGITS
 483         REGISTER_NL_LANGINFO_CONSTANT(INT_FRAC_DIGITS);
 484 #endif
 485 #ifdef FRAC_DIGITS
 486         REGISTER_NL_LANGINFO_CONSTANT(FRAC_DIGITS);
 487 #endif
 488 #ifdef P_CS_PRECEDES
 489         REGISTER_NL_LANGINFO_CONSTANT(P_CS_PRECEDES);
 490 #endif
 491 #ifdef P_SEP_BY_SPACE
 492         REGISTER_NL_LANGINFO_CONSTANT(P_SEP_BY_SPACE);
 493 #endif
 494 #ifdef N_CS_PRECEDES
 495         REGISTER_NL_LANGINFO_CONSTANT(N_CS_PRECEDES);
 496 #endif
 497 #ifdef N_SEP_BY_SPACE
 498         REGISTER_NL_LANGINFO_CONSTANT(N_SEP_BY_SPACE);
 499 #endif
 500 #ifdef P_SIGN_POSN
 501         REGISTER_NL_LANGINFO_CONSTANT(P_SIGN_POSN);
 502 #endif
 503 #ifdef N_SIGN_POSN
 504         REGISTER_NL_LANGINFO_CONSTANT(N_SIGN_POSN);
 505 #endif
 506 #ifdef DECIMAL_POINT
 507         REGISTER_NL_LANGINFO_CONSTANT(DECIMAL_POINT);
 508 #endif
 509 #ifdef RADIXCHAR
 510         REGISTER_NL_LANGINFO_CONSTANT(RADIXCHAR);
 511 #endif
 512 #ifdef THOUSANDS_SEP
 513         REGISTER_NL_LANGINFO_CONSTANT(THOUSANDS_SEP);
 514 #endif
 515 #ifdef THOUSEP
 516         REGISTER_NL_LANGINFO_CONSTANT(THOUSEP);
 517 #endif
 518 #ifdef GROUPING
 519         REGISTER_NL_LANGINFO_CONSTANT(GROUPING);
 520 #endif
 521 #ifdef YESEXPR
 522         REGISTER_NL_LANGINFO_CONSTANT(YESEXPR);
 523 #endif
 524 #ifdef NOEXPR
 525         REGISTER_NL_LANGINFO_CONSTANT(NOEXPR);
 526 #endif
 527 #ifdef YESSTR
 528         REGISTER_NL_LANGINFO_CONSTANT(YESSTR);
 529 #endif
 530 #ifdef NOSTR
 531         REGISTER_NL_LANGINFO_CONSTANT(NOSTR);
 532 #endif
 533 #ifdef CODESET
 534         REGISTER_NL_LANGINFO_CONSTANT(CODESET);
 535 #endif
 536 #undef REGISTER_NL_LANGINFO_CONSTANT
 537         return SUCCESS;
 538 }
 539 /* }}} */
 540 
 541 /* {{{ proto string nl_langinfo(int item)
 542    Query language and locale information */
 543 PHP_FUNCTION(nl_langinfo)
 544 {
 545         zend_long item;
 546         char *value;
 547 
 548         if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &item) == FAILURE) {
 549                 return;
 550         }
 551 
 552         switch(item) { /* {{{ */
 553 #ifdef ABDAY_1
 554                 case ABDAY_1:
 555                 case ABDAY_2:
 556                 case ABDAY_3:
 557                 case ABDAY_4:
 558                 case ABDAY_5:
 559                 case ABDAY_6:
 560                 case ABDAY_7:
 561 #endif
 562 #ifdef DAY_1
 563                 case DAY_1:
 564                 case DAY_2:
 565                 case DAY_3:
 566                 case DAY_4:
 567                 case DAY_5:
 568                 case DAY_6:
 569                 case DAY_7:
 570 #endif
 571 #ifdef ABMON_1
 572                 case ABMON_1:
 573                 case ABMON_2:
 574                 case ABMON_3:
 575                 case ABMON_4:
 576                 case ABMON_5:
 577                 case ABMON_6:
 578                 case ABMON_7:
 579                 case ABMON_8:
 580                 case ABMON_9:
 581                 case ABMON_10:
 582                 case ABMON_11:
 583                 case ABMON_12:
 584 #endif
 585 #ifdef MON_1
 586                 case MON_1:
 587                 case MON_2:
 588                 case MON_3:
 589                 case MON_4:
 590                 case MON_5:
 591                 case MON_6:
 592                 case MON_7:
 593                 case MON_8:
 594                 case MON_9:
 595                 case MON_10:
 596                 case MON_11:
 597                 case MON_12:
 598 #endif
 599 #ifdef AM_STR
 600                 case AM_STR:
 601 #endif
 602 #ifdef PM_STR
 603                 case PM_STR:
 604 #endif
 605 #ifdef D_T_FMT
 606                 case D_T_FMT:
 607 #endif
 608 #ifdef D_FMT
 609                 case D_FMT:
 610 #endif
 611 #ifdef T_FMT
 612                 case T_FMT:
 613 #endif
 614 #ifdef T_FMT_AMPM
 615                 case T_FMT_AMPM:
 616 #endif
 617 #ifdef ERA
 618                 case ERA:
 619 #endif
 620 #ifdef ERA_YEAR
 621                 case ERA_YEAR:
 622 #endif
 623 #ifdef ERA_D_T_FMT
 624                 case ERA_D_T_FMT:
 625 #endif
 626 #ifdef ERA_D_FMT
 627                 case ERA_D_FMT:
 628 #endif
 629 #ifdef ERA_T_FMT
 630                 case ERA_T_FMT:
 631 #endif
 632 #ifdef ALT_DIGITS
 633                 case ALT_DIGITS:
 634 #endif
 635 #ifdef INT_CURR_SYMBOL
 636                 case INT_CURR_SYMBOL:
 637 #endif
 638 #ifdef CURRENCY_SYMBOL
 639                 case CURRENCY_SYMBOL:
 640 #endif
 641 #ifdef CRNCYSTR
 642                 case CRNCYSTR:
 643 #endif
 644 #ifdef MON_DECIMAL_POINT
 645                 case MON_DECIMAL_POINT:
 646 #endif
 647 #ifdef MON_THOUSANDS_SEP
 648                 case MON_THOUSANDS_SEP:
 649 #endif
 650 #ifdef MON_GROUPING
 651                 case MON_GROUPING:
 652 #endif
 653 #ifdef POSITIVE_SIGN
 654                 case POSITIVE_SIGN:
 655 #endif
 656 #ifdef NEGATIVE_SIGN
 657                 case NEGATIVE_SIGN:
 658 #endif
 659 #ifdef INT_FRAC_DIGITS
 660                 case INT_FRAC_DIGITS:
 661 #endif
 662 #ifdef FRAC_DIGITS
 663                 case FRAC_DIGITS:
 664 #endif
 665 #ifdef P_CS_PRECEDES
 666                 case P_CS_PRECEDES:
 667 #endif
 668 #ifdef P_SEP_BY_SPACE
 669                 case P_SEP_BY_SPACE:
 670 #endif
 671 #ifdef N_CS_PRECEDES
 672                 case N_CS_PRECEDES:
 673 #endif
 674 #ifdef N_SEP_BY_SPACE
 675                 case N_SEP_BY_SPACE:
 676 #endif
 677 #ifdef P_SIGN_POSN
 678                 case P_SIGN_POSN:
 679 #endif
 680 #ifdef N_SIGN_POSN
 681                 case N_SIGN_POSN:
 682 #endif
 683 #ifdef DECIMAL_POINT
 684                 case DECIMAL_POINT:
 685 #elif defined(RADIXCHAR)
 686                 case RADIXCHAR:
 687 #endif
 688 #ifdef THOUSANDS_SEP
 689                 case THOUSANDS_SEP:
 690 #elif defined(THOUSEP)
 691                 case THOUSEP:
 692 #endif
 693 #ifdef GROUPING
 694                 case GROUPING:
 695 #endif
 696 #ifdef YESEXPR
 697                 case YESEXPR:
 698 #endif
 699 #ifdef NOEXPR
 700                 case NOEXPR:
 701 #endif
 702 #ifdef YESSTR
 703                 case YESSTR:
 704 #endif
 705 #ifdef NOSTR
 706                 case NOSTR:
 707 #endif
 708 #ifdef CODESET
 709                 case CODESET:
 710 #endif
 711                         break;
 712                 default:
 713                         php_error_docref(NULL, E_WARNING, "Item '" ZEND_LONG_FMT "' is not valid", item);
 714                         RETURN_FALSE;
 715         }
 716         /* }}} */
 717 
 718         value = nl_langinfo(item);
 719         if (value == NULL) {
 720                 RETURN_FALSE;
 721         } else {
 722                 RETURN_STRING(value);
 723         }
 724 }
 725 #endif
 726 /* }}} */
 727 
 728 #ifdef HAVE_STRCOLL
 729 /* {{{ proto int strcoll(string str1, string str2)
 730    Compares two strings using the current locale */
 731 PHP_FUNCTION(strcoll)
 732 {
 733         zend_string *s1, *s2;
 734 
 735         if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &s1, &s2) == FAILURE) {
 736                 return;
 737         }
 738 
 739         RETURN_LONG(strcoll((const char *) ZSTR_VAL(s1),
 740                             (const char *) ZSTR_VAL(s2)));
 741 }
 742 /* }}} */
 743 #endif
 744 
 745 /* {{{ php_charmask
 746  * Fills a 256-byte bytemask with input. You can specify a range like 'a..z',
 747  * it needs to be incrementing.
 748  * Returns: FAILURE/SUCCESS whether the input was correct (i.e. no range errors)
 749  */
 750 static inline int php_charmask(unsigned char *input, size_t len, char *mask)
 751 {
 752         unsigned char *end;
 753         unsigned char c;
 754         int result = SUCCESS;
 755 
 756         memset(mask, 0, 256);
 757         for (end = input+len; input < end; input++) {
 758                 c=*input;
 759                 if ((input+3 < end) && input[1] == '.' && input[2] == '.'
 760                                 && input[3] >= c) {
 761                         memset(mask+c, 1, input[3] - c + 1);
 762                         input+=3;
 763                 } else if ((input+1 < end) && input[0] == '.' && input[1] == '.') {
 764                         /* Error, try to be as helpful as possible:
 765                            (a range ending/starting with '.' won't be captured here) */
 766                         if (end-len >= input) { /* there was no 'left' char */
 767                                 php_error_docref(NULL, E_WARNING, "Invalid '..'-range, no character to the left of '..'");
 768                                 result = FAILURE;
 769                                 continue;
 770                         }
 771                         if (input+2 >= end) { /* there is no 'right' char */
 772                                 php_error_docref(NULL, E_WARNING, "Invalid '..'-range, no character to the right of '..'");
 773                                 result = FAILURE;
 774                                 continue;
 775                         }
 776                         if (input[-1] > input[2]) { /* wrong order */
 777                                 php_error_docref(NULL, E_WARNING, "Invalid '..'-range, '..'-range needs to be incrementing");
 778                                 result = FAILURE;
 779                                 continue;
 780                         }
 781                         /* FIXME: better error (a..b..c is the only left possibility?) */
 782                         php_error_docref(NULL, E_WARNING, "Invalid '..'-range");
 783                         result = FAILURE;
 784                         continue;
 785                 } else {
 786                         mask[c]=1;
 787                 }
 788         }
 789         return result;
 790 }
 791 /* }}} */
 792 
 793 /* {{{ php_trim()
 794  * mode 1 : trim left
 795  * mode 2 : trim right
 796  * mode 3 : trim left and right
 797  * what indicates which chars are to be trimmed. NULL->default (' \t\n\r\v\0')
 798  */
 799 PHPAPI zend_string *php_trim(zend_string *str, char *what, size_t what_len, int mode)
 800 {
 801         const char *c = ZSTR_VAL(str);
 802         size_t len = ZSTR_LEN(str);
 803         register size_t i;
 804         size_t trimmed = 0;
 805         char mask[256];
 806 
 807         if (what) {
 808                 if (what_len == 1) {
 809                         char p = *what;
 810                         if (mode & 1) {
 811                                 for (i = 0; i < len; i++) {
 812                                         if (c[i] == p) {
 813                                                 trimmed++;
 814                                         } else {
 815                                                 break;
 816                                         }
 817                                 }
 818                                 len -= trimmed;
 819                                 c += trimmed;
 820                         }
 821                         if (mode & 2) {
 822                                 if (len > 0) {
 823                                         i = len - 1;
 824                                         do {
 825                                                 if (c[i] == p) {
 826                                                         len--;
 827                                                 } else {
 828                                                         break;
 829                                                 }
 830                                         } while (i-- != 0);
 831                                 }
 832                         }
 833                 } else {
 834                         php_charmask((unsigned char*)what, what_len, mask);
 835 
 836                         if (mode & 1) {
 837                                 for (i = 0; i < len; i++) {
 838                                         if (mask[(unsigned char)c[i]]) {
 839                                                 trimmed++;
 840                                         } else {
 841                                                 break;
 842                                         }
 843                                 }
 844                                 len -= trimmed;
 845                                 c += trimmed;
 846                         }
 847                         if (mode & 2) {
 848                                 if (len > 0) {
 849                                         i = len - 1;
 850                                         do {
 851                                                 if (mask[(unsigned char)c[i]]) {
 852                                                         len--;
 853                                                 } else {
 854                                                         break;
 855                                                 }
 856                                         } while (i-- != 0);
 857                                 }
 858                         }
 859                 }
 860         } else {
 861                 if (mode & 1) {
 862                         for (i = 0; i < len; i++) {
 863                                 if ((unsigned char)c[i] <= ' ' &&
 864                                     (c[i] == ' ' || c[i] == '\n' || c[i] == '\r' || c[i] == '\t' || c[i] == '\v' || c[i] == '\0')) {
 865                                         trimmed++;
 866                                 } else {
 867                                         break;
 868                                 }
 869                         }
 870                         len -= trimmed;
 871                         c += trimmed;
 872                 }
 873                 if (mode & 2) {
 874                         if (len > 0) {
 875                                 i = len - 1;
 876                                 do {
 877                                         if ((unsigned char)c[i] <= ' ' &&
 878                                             (c[i] == ' ' || c[i] == '\n' || c[i] == '\r' || c[i] == '\t' || c[i] == '\v' || c[i] == '\0')) {
 879                                                 len--;
 880                                         } else {
 881                                                 break;
 882                                         }
 883                                 } while (i-- != 0);
 884                         }
 885                 }
 886         }
 887 
 888         if (ZSTR_LEN(str) == len) {
 889                 return zend_string_copy(str);
 890         } else {
 891                 return zend_string_init(c, len, 0);
 892         }
 893 }
 894 /* }}} */
 895 
 896 /* {{{ php_do_trim
 897  * Base for trim(), rtrim() and ltrim() functions.
 898  */
 899 static void php_do_trim(INTERNAL_FUNCTION_PARAMETERS, int mode)
 900 {
 901         zend_string *str;
 902         zend_string *what = NULL;
 903 
 904 #ifndef FAST_ZPP
 905         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|S", &str, &what) == FAILURE) {
 906                 return;
 907         }
 908 #else
 909         ZEND_PARSE_PARAMETERS_START(1, 2)
 910                 Z_PARAM_STR(str)
 911                 Z_PARAM_OPTIONAL
 912                 Z_PARAM_STR(what)
 913         ZEND_PARSE_PARAMETERS_END();
 914 #endif
 915 
 916         ZVAL_STR(return_value, php_trim(str, (what ? ZSTR_VAL(what) : NULL), (what ? ZSTR_LEN(what) : 0), mode));
 917 }
 918 /* }}} */
 919 
 920 /* {{{ proto string trim(string str [, string character_mask])
 921    Strips whitespace from the beginning and end of a string */
 922 PHP_FUNCTION(trim)
 923 {
 924         php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3);
 925 }
 926 /* }}} */
 927 
 928 /* {{{ proto string rtrim(string str [, string character_mask])
 929    Removes trailing whitespace */
 930 PHP_FUNCTION(rtrim)
 931 {
 932         php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 2);
 933 }
 934 /* }}} */
 935 
 936 /* {{{ proto string ltrim(string str [, string character_mask])
 937    Strips whitespace from the beginning of a string */
 938 PHP_FUNCTION(ltrim)
 939 {
 940         php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
 941 }
 942 /* }}} */
 943 
 944 /* {{{ proto string wordwrap(string str [, int width [, string break [, boolean cut]]])
 945    Wraps buffer to selected number of characters using string break char */
 946 PHP_FUNCTION(wordwrap)
 947 {
 948         zend_string *text;
 949         char *breakchar = "\n";
 950         size_t newtextlen, chk, breakchar_len = 1;
 951         size_t alloced;
 952         zend_long current = 0, laststart = 0, lastspace = 0;
 953         zend_long linelength = 75;
 954         zend_bool docut = 0;
 955         zend_string *newtext;
 956 
 957         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|lsb", &text, &linelength, &breakchar, &breakchar_len, &docut) == FAILURE) {
 958                 return;
 959         }
 960 
 961         if (ZSTR_LEN(text) == 0) {
 962                 RETURN_EMPTY_STRING();
 963         }
 964 
 965         if (breakchar_len == 0) {
 966                 php_error_docref(NULL, E_WARNING, "Break string cannot be empty");
 967                 RETURN_FALSE;
 968         }
 969 
 970         if (linelength == 0 && docut) {
 971                 php_error_docref(NULL, E_WARNING, "Can't force cut when width is zero");
 972                 RETURN_FALSE;
 973         }
 974 
 975         /* Special case for a single-character break as it needs no
 976            additional storage space */
 977         if (breakchar_len == 1 && !docut) {
 978                 newtext = zend_string_init(ZSTR_VAL(text), ZSTR_LEN(text), 0);
 979 
 980                 laststart = lastspace = 0;
 981                 for (current = 0; current < ZSTR_LEN(text); current++) {
 982                         if (ZSTR_VAL(text)[current] == breakchar[0]) {
 983                                 laststart = lastspace = current + 1;
 984                         } else if (ZSTR_VAL(text)[current] == ' ') {
 985                                 if (current - laststart >= linelength) {
 986                                         ZSTR_VAL(newtext)[current] = breakchar[0];
 987                                         laststart = current + 1;
 988                                 }
 989                                 lastspace = current;
 990                         } else if (current - laststart >= linelength && laststart != lastspace) {
 991                                 ZSTR_VAL(newtext)[lastspace] = breakchar[0];
 992                                 laststart = lastspace + 1;
 993                         }
 994                 }
 995 
 996                 RETURN_NEW_STR(newtext);
 997         } else {
 998                 /* Multiple character line break or forced cut */
 999                 if (linelength > 0) {
1000                         chk = (size_t)(ZSTR_LEN(text)/linelength + 1);
1001                         newtext = zend_string_safe_alloc(chk, breakchar_len, ZSTR_LEN(text), 0);
1002                         alloced = ZSTR_LEN(text) + chk * breakchar_len + 1;
1003                 } else {
1004                         chk = ZSTR_LEN(text);
1005                         alloced = ZSTR_LEN(text) * (breakchar_len + 1) + 1;
1006                         newtext = zend_string_safe_alloc(ZSTR_LEN(text), breakchar_len + 1, 0, 0);
1007                 }
1008 
1009                 /* now keep track of the actual new text length */
1010                 newtextlen = 0;
1011 
1012                 laststart = lastspace = 0;
1013                 for (current = 0; current < ZSTR_LEN(text); current++) {
1014                         if (chk <= 0) {
1015                                 alloced += (size_t) (((ZSTR_LEN(text) - current + 1)/linelength + 1) * breakchar_len) + 1;
1016                                 newtext = zend_string_extend(newtext, alloced, 0);
1017                                 chk = (size_t) ((ZSTR_LEN(text) - current)/linelength) + 1;
1018                         }
1019                         /* when we hit an existing break, copy to new buffer, and
1020                          * fix up laststart and lastspace */
1021                         if (ZSTR_VAL(text)[current] == breakchar[0]
1022                                 && current + breakchar_len < ZSTR_LEN(text)
1023                                 && !strncmp(ZSTR_VAL(text) + current, breakchar, breakchar_len)) {
1024                                 memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart + breakchar_len);
1025                                 newtextlen += current - laststart + breakchar_len;
1026                                 current += breakchar_len - 1;
1027                                 laststart = lastspace = current + 1;
1028                                 chk--;
1029                         }
1030                         /* if it is a space, check if it is at the line boundary,
1031                          * copy and insert a break, or just keep track of it */
1032                         else if (ZSTR_VAL(text)[current] == ' ') {
1033                                 if (current - laststart >= linelength) {
1034                                         memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart);
1035                                         newtextlen += current - laststart;
1036                                         memcpy(ZSTR_VAL(newtext) + newtextlen, breakchar, breakchar_len);
1037                                         newtextlen += breakchar_len;
1038                                         laststart = current + 1;
1039                                         chk--;
1040                                 }
1041                                 lastspace = current;
1042                         }
1043                         /* if we are cutting, and we've accumulated enough
1044                          * characters, and we haven't see a space for this line,
1045                          * copy and insert a break. */
1046                         else if (current - laststart >= linelength
1047                                         && docut && laststart >= lastspace) {
1048                                 memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart);
1049                                 newtextlen += current - laststart;
1050                                 memcpy(ZSTR_VAL(newtext) + newtextlen, breakchar, breakchar_len);
1051                                 newtextlen += breakchar_len;
1052                                 laststart = lastspace = current;
1053                                 chk--;
1054                         }
1055                         /* if the current word puts us over the linelength, copy
1056                          * back up until the last space, insert a break, and move
1057                          * up the laststart */
1058                         else if (current - laststart >= linelength
1059                                         && laststart < lastspace) {
1060                                 memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, lastspace - laststart);
1061                                 newtextlen += lastspace - laststart;
1062                                 memcpy(ZSTR_VAL(newtext) + newtextlen, breakchar, breakchar_len);
1063                                 newtextlen += breakchar_len;
1064                                 laststart = lastspace = lastspace + 1;
1065                                 chk--;
1066                         }
1067                 }
1068 
1069                 /* copy over any stragglers */
1070                 if (laststart != current) {
1071                         memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart);
1072                         newtextlen += current - laststart;
1073                 }
1074 
1075                 ZSTR_VAL(newtext)[newtextlen] = '\0';
1076                 /* free unused memory */
1077                 newtext = zend_string_truncate(newtext, newtextlen, 0);
1078 
1079                 RETURN_NEW_STR(newtext);
1080         }
1081 }
1082 /* }}} */
1083 
1084 /* {{{ php_explode
1085  */
1086 PHPAPI void php_explode(const zend_string *delim, zend_string *str, zval *return_value, zend_long limit)
1087 {
1088         char *p1 = ZSTR_VAL(str);
1089         char *endp = ZSTR_VAL(str) + ZSTR_LEN(str);
1090         char *p2 = (char *) php_memnstr(ZSTR_VAL(str), ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
1091         zval  tmp;
1092 
1093         if (p2 == NULL) {
1094                 ZVAL_STR_COPY(&tmp, str);
1095                 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
1096         } else {
1097                 do {
1098                         ZVAL_STRINGL(&tmp, p1, p2 - p1);
1099                         zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
1100                         p1 = p2 + ZSTR_LEN(delim);
1101                         p2 = (char *) php_memnstr(p1, ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
1102                 } while (p2 != NULL && --limit > 1);
1103 
1104                 if (p1 <= endp) {
1105                         ZVAL_STRINGL(&tmp, p1, endp - p1);
1106                         zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
1107                 }
1108         }
1109 }
1110 /* }}} */
1111 
1112 /* {{{ php_explode_negative_limit
1113  */
1114 PHPAPI void php_explode_negative_limit(const zend_string *delim, zend_string *str, zval *return_value, zend_long limit)
1115 {
1116 #define EXPLODE_ALLOC_STEP 64
1117         char *p1 = ZSTR_VAL(str);
1118         char *endp = ZSTR_VAL(str) + ZSTR_LEN(str);
1119         char *p2 = (char *) php_memnstr(ZSTR_VAL(str), ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
1120         zval  tmp;
1121 
1122         if (p2 == NULL) {
1123                 /*
1124                 do nothing since limit <= -1, thus if only one chunk - 1 + (limit) <= 0
1125                 by doing nothing we return empty array
1126                 */
1127         } else {
1128                 size_t allocated = EXPLODE_ALLOC_STEP, found = 0;
1129                 zend_long i, to_return;
1130                 char **positions = emalloc(allocated * sizeof(char *));
1131 
1132                 positions[found++] = p1;
1133                 do {
1134                         if (found >= allocated) {
1135                                 allocated = found + EXPLODE_ALLOC_STEP;/* make sure we have enough memory */
1136                                 positions = erealloc(positions, allocated*sizeof(char *));
1137                         }
1138                         positions[found++] = p1 = p2 + ZSTR_LEN(delim);
1139                         p2 = (char *) php_memnstr(p1, ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
1140                 } while (p2 != NULL);
1141 
1142                 to_return = limit + found;
1143                 /* limit is at least -1 therefore no need of bounds checking : i will be always less than found */
1144                 for (i = 0; i < to_return; i++) { /* this checks also for to_return > 0 */
1145                         ZVAL_STRINGL(&tmp, positions[i], (positions[i+1] - ZSTR_LEN(delim)) - positions[i]);
1146                         zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
1147                 }
1148                 efree(positions);
1149         }
1150 #undef EXPLODE_ALLOC_STEP
1151 }
1152 /* }}} */
1153 
1154 /* {{{ proto array explode(string separator, string str [, int limit])
1155    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. */
1156 PHP_FUNCTION(explode)
1157 {
1158         zend_string *str, *delim;
1159         zend_long limit = ZEND_LONG_MAX; /* No limit */
1160         zval tmp;
1161 
1162 #ifndef FAST_ZPP
1163         if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|l", &delim, &str, &limit) == FAILURE) {
1164                 return;
1165         }
1166 #else
1167         ZEND_PARSE_PARAMETERS_START(2, 3)
1168                 Z_PARAM_STR(delim)
1169                 Z_PARAM_STR(str)
1170                 Z_PARAM_OPTIONAL
1171                 Z_PARAM_LONG(limit)
1172         ZEND_PARSE_PARAMETERS_END();
1173 #endif
1174 
1175         if (ZSTR_LEN(delim) == 0) {
1176                 php_error_docref(NULL, E_WARNING, "Empty delimiter");
1177                 RETURN_FALSE;
1178         }
1179 
1180         array_init(return_value);
1181 
1182         if (ZSTR_LEN(str) == 0) {
1183                 if (limit >= 0) {
1184                         ZVAL_EMPTY_STRING(&tmp);
1185                         zend_hash_index_add_new(Z_ARRVAL_P(return_value), 0, &tmp);
1186                 }
1187                 return;
1188         }
1189 
1190         if (limit > 1) {
1191                 php_explode(delim, str, return_value, limit);
1192         } else if (limit < 0) {
1193                 php_explode_negative_limit(delim, str, return_value, limit);
1194         } else {
1195                 ZVAL_STR_COPY(&tmp, str);
1196                 zend_hash_index_add_new(Z_ARRVAL_P(return_value), 0, &tmp);
1197         }
1198 }
1199 /* }}} */
1200 
1201 /* {{{ proto string join(array src, string glue)
1202    An alias for implode */
1203 /* }}} */
1204 
1205 /* {{{ php_implode
1206  */
1207 PHPAPI void php_implode(const zend_string *delim, zval *arr, zval *return_value)
1208 {
1209         zval         *tmp;
1210         int           numelems;
1211         zend_string  *str;
1212         char         *cptr;
1213         size_t        len = 0;
1214         zend_string **strings, **strptr;
1215 
1216         numelems = zend_hash_num_elements(Z_ARRVAL_P(arr));
1217 
1218         if (numelems == 0) {
1219                 RETURN_EMPTY_STRING();
1220         } else if (numelems == 1) {
1221                 /* loop to search the first not undefined element... */
1222                 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), tmp) {
1223                         RETURN_STR(zval_get_string(tmp));
1224                 } ZEND_HASH_FOREACH_END();
1225         }
1226 
1227         strings = emalloc((sizeof(zend_long) + sizeof(zend_string *)) * numelems);
1228         strptr = strings - 1;
1229 
1230         ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), tmp) {
1231                 if (Z_TYPE_P(tmp) == IS_LONG) {
1232                         double val = Z_LVAL_P(tmp);
1233                         *++strptr = NULL;
1234                         ((zend_long *) (strings + numelems))[strptr - strings] = Z_LVAL_P(tmp);
1235                         if (val < 0) {
1236                                 val = -10 * val;
1237                         }
1238                         if (val < 10) {
1239                                 len++;
1240                         } else {
1241                                 len += (int) log10(10 * (double) val);
1242                         }
1243                 } else {
1244                         *++strptr = zval_get_string(tmp);
1245                         len += ZSTR_LEN(*strptr);
1246                 }
1247         } ZEND_HASH_FOREACH_END();
1248         /* numelems can not be 0, we checked above */
1249         str = zend_string_safe_alloc(numelems - 1, ZSTR_LEN(delim), len, 0);
1250         cptr = ZSTR_VAL(str) + ZSTR_LEN(str);
1251         *cptr = 0;
1252 
1253         do {
1254                 if (*strptr) {
1255                         cptr -= ZSTR_LEN(*strptr);
1256                         memcpy(cptr, ZSTR_VAL(*strptr), ZSTR_LEN(*strptr));
1257                         zend_string_release(*strptr);
1258                 } else {
1259                         char *oldPtr = cptr;
1260                         char oldVal = *cptr;
1261                         zend_long val = ((zend_long *) (strings + numelems))[strptr - strings];
1262                         cptr = zend_print_long_to_buf(cptr, val);
1263                         *oldPtr = oldVal;
1264                 }
1265 
1266                 cptr -= ZSTR_LEN(delim);
1267                 memcpy(cptr, ZSTR_VAL(delim), ZSTR_LEN(delim));
1268         } while (--strptr > strings);
1269 
1270         if (*strptr) {
1271                 memcpy(ZSTR_VAL(str), ZSTR_VAL(*strptr), ZSTR_LEN(*strptr));
1272                 zend_string_release(*strptr);
1273         } else {
1274                 char *oldPtr = cptr;
1275                 char oldVal = *cptr;
1276                 zend_print_long_to_buf(cptr, ((zend_long *) (strings + numelems))[strptr - strings]);
1277                 *oldPtr = oldVal;
1278         }
1279 
1280         efree(strings);
1281         RETURN_NEW_STR(str);
1282 }
1283 /* }}} */
1284 
1285 /* {{{ proto string implode([string glue,] array pieces)
1286    Joins array elements placing glue string between items and return one string */
1287 PHP_FUNCTION(implode)
1288 {
1289         zval *arg1, *arg2 = NULL, *arr;
1290         zend_string *delim;
1291 
1292 #ifndef FAST_ZPP
1293         if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|z", &arg1, &arg2) == FAILURE) {
1294                 return;
1295         }
1296 #else
1297         ZEND_PARSE_PARAMETERS_START(1, 2)
1298                 Z_PARAM_ZVAL(arg1)
1299                 Z_PARAM_OPTIONAL
1300                 Z_PARAM_ZVAL(arg2)
1301         ZEND_PARSE_PARAMETERS_END();
1302 #endif
1303 
1304         if (arg2 == NULL) {
1305                 if (Z_TYPE_P(arg1) != IS_ARRAY) {
1306                         php_error_docref(NULL, E_WARNING, "Argument must be an array");
1307                         return;
1308                 }
1309 
1310                 delim = ZSTR_EMPTY_ALLOC();
1311                 arr = arg1;
1312         } else {
1313                 if (Z_TYPE_P(arg1) == IS_ARRAY) {
1314                         delim = zval_get_string(arg2);
1315                         arr = arg1;
1316                 } else if (Z_TYPE_P(arg2) == IS_ARRAY) {
1317                         delim = zval_get_string(arg1);
1318                         arr = arg2;
1319                 } else {
1320                         php_error_docref(NULL, E_WARNING, "Invalid arguments passed");
1321                         return;
1322                 }
1323         }
1324 
1325         php_implode(delim, arr, return_value);
1326         zend_string_release(delim);
1327 }
1328 /* }}} */
1329 
1330 #define STRTOK_TABLE(p) BG(strtok_table)[(unsigned char) *p]
1331 
1332 /* {{{ proto string strtok([string str,] string token)
1333    Tokenize a string */
1334 PHP_FUNCTION(strtok)
1335 {
1336         zend_string *str, *tok = NULL;
1337         char *token;
1338         char *token_end;
1339         char *p;
1340         char *pe;
1341         size_t skipped = 0;
1342 
1343 #ifndef FAST_ZPP
1344         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|S", &str, &tok) == FAILURE) {
1345                 return;
1346         }
1347 #else
1348         ZEND_PARSE_PARAMETERS_START(1, 2)
1349                 Z_PARAM_STR(str)
1350                 Z_PARAM_OPTIONAL
1351                 Z_PARAM_STR(tok)
1352         ZEND_PARSE_PARAMETERS_END();
1353 #endif
1354 
1355         if (ZEND_NUM_ARGS() == 1) {
1356                 tok = str;
1357         } else {
1358                 zval_ptr_dtor(&BG(strtok_zval));
1359                 ZVAL_STRINGL(&BG(strtok_zval), ZSTR_VAL(str), ZSTR_LEN(str));
1360                 BG(strtok_last) = BG(strtok_string) = Z_STRVAL(BG(strtok_zval));
1361                 BG(strtok_len) = ZSTR_LEN(str);
1362         }
1363 
1364         p = BG(strtok_last); /* Where we start to search */
1365         pe = BG(strtok_string) + BG(strtok_len);
1366 
1367         if (!p || p >= pe) {
1368                 RETURN_FALSE;
1369         }
1370 
1371         token = ZSTR_VAL(tok);
1372         token_end = token + ZSTR_LEN(tok);
1373 
1374         while (token < token_end) {
1375                 STRTOK_TABLE(token++) = 1;
1376         }
1377 
1378         /* Skip leading delimiters */
1379         while (STRTOK_TABLE(p)) {
1380                 if (++p >= pe) {
1381                         /* no other chars left */
1382                         BG(strtok_last) = NULL;
1383                         RETVAL_FALSE;
1384                         goto restore;
1385                 }
1386                 skipped++;
1387         }
1388 
1389         /* We know at this place that *p is no delimiter, so skip it */
1390         while (++p < pe) {
1391                 if (STRTOK_TABLE(p)) {
1392                         goto return_token;
1393                 }
1394         }
1395 
1396         if (p - BG(strtok_last)) {
1397 return_token:
1398                 RETVAL_STRINGL(BG(strtok_last) + skipped, (p - BG(strtok_last)) - skipped);
1399                 BG(strtok_last) = p + 1;
1400         } else {
1401                 RETVAL_FALSE;
1402                 BG(strtok_last) = NULL;
1403         }
1404 
1405         /* Restore table -- usually faster then memset'ing the table on every invocation */
1406 restore:
1407         token = ZSTR_VAL(tok);
1408 
1409         while (token < token_end) {
1410                 STRTOK_TABLE(token++) = 0;
1411         }
1412 }
1413 /* }}} */
1414 
1415 /* {{{ php_strtoupper
1416  */
1417 PHPAPI char *php_strtoupper(char *s, size_t len)
1418 {
1419         unsigned char *c, *e;
1420 
1421         c = (unsigned char *)s;
1422         e = (unsigned char *)c+len;
1423 
1424         while (c < e) {
1425                 *c = toupper(*c);
1426                 c++;
1427         }
1428         return s;
1429 }
1430 /* }}} */
1431 
1432 /* {{{ php_string_toupper
1433  */
1434 PHPAPI zend_string *php_string_toupper(zend_string *s)
1435 {
1436         unsigned char *c, *e;
1437 
1438         c = (unsigned char *)ZSTR_VAL(s);
1439         e = c + ZSTR_LEN(s);
1440 
1441         while (c < e) {
1442                 if (!isupper(*c)) {
1443                         register unsigned char *r;
1444                         zend_string *res = zend_string_alloc(ZSTR_LEN(s), 0);
1445 
1446                         if (c != (unsigned char*)ZSTR_VAL(s)) {
1447                                 memcpy(ZSTR_VAL(res), ZSTR_VAL(s), c - (unsigned char*)ZSTR_VAL(s));
1448                         }
1449                         r = c + (ZSTR_VAL(res) - ZSTR_VAL(s));
1450                         while (c < e) {
1451                                 *r = toupper(*c);
1452                                 r++;
1453                                 c++;
1454                         }
1455                         *r = '\0';
1456                         return res;
1457                 }
1458                 c++;
1459         }
1460         return zend_string_copy(s);
1461 }
1462 /* }}} */
1463 
1464 /* {{{ proto string strtoupper(string str)
1465    Makes a string uppercase */
1466 PHP_FUNCTION(strtoupper)
1467 {
1468         zend_string *arg;
1469 
1470 #ifndef FAST_ZPP
1471         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &arg) == FAILURE) {
1472                 return;
1473         }
1474 #else
1475         ZEND_PARSE_PARAMETERS_START(1, 1)
1476                 Z_PARAM_STR(arg)
1477         ZEND_PARSE_PARAMETERS_END();
1478 #endif
1479 
1480         RETURN_STR(php_string_toupper(arg));
1481 }
1482 /* }}} */
1483 
1484 /* {{{ php_strtolower
1485  */
1486 PHPAPI char *php_strtolower(char *s, size_t len)
1487 {
1488         unsigned char *c, *e;
1489 
1490         c = (unsigned char *)s;
1491         e = c+len;
1492 
1493         while (c < e) {
1494                 *c = tolower(*c);
1495                 c++;
1496         }
1497         return s;
1498 }
1499 /* }}} */
1500 
1501 /* {{{ php_string_tolower
1502  */
1503 PHPAPI zend_string *php_string_tolower(zend_string *s)
1504 {
1505         unsigned char *c, *e;
1506 
1507         c = (unsigned char *)ZSTR_VAL(s);
1508         e = c + ZSTR_LEN(s);
1509 
1510         while (c < e) {
1511                 if (!islower(*c)) {
1512                         register unsigned char *r;
1513                         zend_string *res = zend_string_alloc(ZSTR_LEN(s), 0);
1514 
1515                         if (c != (unsigned char*)ZSTR_VAL(s)) {
1516                                 memcpy(ZSTR_VAL(res), ZSTR_VAL(s), c - (unsigned char*)ZSTR_VAL(s));
1517                         }
1518                         r = c + (ZSTR_VAL(res) - ZSTR_VAL(s));
1519                         while (c < e) {
1520                                 *r = tolower(*c);
1521                                 r++;
1522                                 c++;
1523                         }
1524                         *r = '\0';
1525                         return res;
1526                 }
1527                 c++;
1528         }
1529         return zend_string_copy(s);
1530 }
1531 /* }}} */
1532 
1533 /* {{{ proto string strtolower(string str)
1534    Makes a string lowercase */
1535 PHP_FUNCTION(strtolower)
1536 {
1537         zend_string *str;
1538 
1539 #ifndef FAST_ZPP
1540         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) {
1541                 return;
1542         }
1543 #else
1544         ZEND_PARSE_PARAMETERS_START(1, 1)
1545                 Z_PARAM_STR(str)
1546         ZEND_PARSE_PARAMETERS_END();
1547 #endif
1548 
1549         RETURN_STR(php_string_tolower(str));
1550 }
1551 /* }}} */
1552 
1553 /* {{{ php_basename
1554  */
1555 PHPAPI zend_string *php_basename(const char *s, size_t len, char *suffix, size_t sufflen)
1556 {
1557         char *c, *comp, *cend;
1558         size_t inc_len, cnt;
1559         int state;
1560         zend_string *ret;
1561 
1562         c = comp = cend = (char*)s;
1563         cnt = len;
1564         state = 0;
1565         while (cnt > 0) {
1566                 inc_len = (*c == '\0' ? 1 : php_mblen(c, cnt));
1567 
1568                 switch (inc_len) {
1569                         case -2:
1570                         case -1:
1571                                 inc_len = 1;
1572                                 php_mb_reset();
1573                                 break;
1574                         case 0:
1575                                 goto quit_loop;
1576                         case 1:
1577 #if defined(PHP_WIN32) || defined(NETWARE)
1578                                 if (*c == '/' || *c == '\\') {
1579 #else
1580                                 if (*c == '/') {
1581 #endif
1582                                         if (state == 1) {
1583                                                 state = 0;
1584                                                 cend = c;
1585                                         }
1586 #if defined(PHP_WIN32) || defined(NETWARE)
1587                                 /* Catch relative paths in c:file.txt style. They're not to confuse
1588                                    with the NTFS streams. This part ensures also, that no drive
1589                                    letter traversing happens. */
1590                                 } else if ((*c == ':' && (c - comp == 1))) {
1591                                         if (state == 0) {
1592                                                 comp = c;
1593                                                 state = 1;
1594                                         } else {
1595                                                 cend = c;
1596                                                 state = 0;
1597                                         }
1598 #endif
1599                                 } else {
1600                                         if (state == 0) {
1601                                                 comp = c;
1602                                                 state = 1;
1603                                         }
1604                                 }
1605                                 break;
1606                         default:
1607                                 if (state == 0) {
1608                                         comp = c;
1609                                         state = 1;
1610                                 }
1611                                 break;
1612                 }
1613                 c += inc_len;
1614                 cnt -= inc_len;
1615         }
1616 
1617 quit_loop:
1618         if (state == 1) {
1619                 cend = c;
1620         }
1621         if (suffix != NULL && sufflen < (size_t)(cend - comp) &&
1622                         memcmp(cend - sufflen, suffix, sufflen) == 0) {
1623                 cend -= sufflen;
1624         }
1625 
1626         len = cend - comp;
1627 
1628         ret = zend_string_init(comp, len, 0);
1629         return ret;
1630 }
1631 /* }}} */
1632 
1633 /* {{{ proto string basename(string path [, string suffix])
1634    Returns the filename component of the path */
1635 PHP_FUNCTION(basename)
1636 {
1637         char *string, *suffix = NULL;
1638         size_t   string_len, suffix_len = 0;
1639 
1640         if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &string, &string_len, &suffix, &suffix_len) == FAILURE) {
1641                 return;
1642         }
1643 
1644         RETURN_STR(php_basename(string, string_len, suffix, suffix_len));
1645 }
1646 /* }}} */
1647 
1648 /* {{{ php_dirname
1649    Returns directory name component of path */
1650 PHPAPI size_t php_dirname(char *path, size_t len)
1651 {
1652         return zend_dirname(path, len);
1653 }
1654 /* }}} */
1655 
1656 /* {{{ proto string dirname(string path[, int levels])
1657    Returns the directory name component of the path */
1658 PHP_FUNCTION(dirname)
1659 {
1660         char *str;
1661         size_t str_len;
1662         zend_string *ret;
1663         zend_long levels = 1;
1664 
1665         if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &str, &str_len, &levels) == FAILURE) {
1666                 return;
1667         }
1668 
1669         ret = zend_string_init(str, str_len, 0);
1670 
1671         if (levels == 1) {
1672                 /* Defaut case */
1673                 ZSTR_LEN(ret) = zend_dirname(ZSTR_VAL(ret), str_len);
1674         } else if (levels < 1) {
1675                 php_error_docref(NULL, E_WARNING, "Invalid argument, levels must be >= 1");
1676                 zend_string_free(ret);
1677                 return;
1678         } else {
1679                 /* Some levels up */
1680                 do {
1681                         ZSTR_LEN(ret) = zend_dirname(ZSTR_VAL(ret), str_len = ZSTR_LEN(ret));
1682                 } while (ZSTR_LEN(ret) < str_len && --levels);
1683         }
1684 
1685         RETURN_NEW_STR(ret);
1686 }
1687 /* }}} */
1688 
1689 /* {{{ proto array pathinfo(string path[, int options])
1690    Returns information about a certain string */
1691 PHP_FUNCTION(pathinfo)
1692 {
1693         zval tmp;
1694         char *path, *dirname;
1695         size_t path_len;
1696         int have_basename;
1697         zend_long opt = PHP_PATHINFO_ALL;
1698         zend_string *ret = NULL;
1699 
1700         if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &path, &path_len, &opt) == FAILURE) {
1701                 return;
1702         }
1703 
1704         have_basename = ((opt & PHP_PATHINFO_BASENAME) == PHP_PATHINFO_BASENAME);
1705 
1706         array_init(&tmp);
1707 
1708         if ((opt & PHP_PATHINFO_DIRNAME) == PHP_PATHINFO_DIRNAME) {
1709                 dirname = estrndup(path, path_len);
1710                 php_dirname(dirname, path_len);
1711                 if (*dirname) {
1712                         add_assoc_string(&tmp, "dirname", dirname);
1713                 }
1714                 efree(dirname);
1715         }
1716 
1717         if (have_basename) {
1718                 ret = php_basename(path, path_len, NULL, 0);
1719                 add_assoc_str(&tmp, "basename", zend_string_copy(ret));
1720         }
1721 
1722         if ((opt & PHP_PATHINFO_EXTENSION) == PHP_PATHINFO_EXTENSION) {
1723                 const char *p;
1724                 ptrdiff_t idx;
1725 
1726                 if (!have_basename) {
1727                         ret = php_basename(path, path_len, NULL, 0);
1728                 }
1729 
1730                 p = zend_memrchr(ZSTR_VAL(ret), '.', ZSTR_LEN(ret));
1731 
1732                 if (p) {
1733                         idx = p - ZSTR_VAL(ret);
1734                         add_assoc_stringl(&tmp, "extension", ZSTR_VAL(ret) + idx + 1, ZSTR_LEN(ret) - idx - 1);
1735                 }
1736         }
1737 
1738         if ((opt & PHP_PATHINFO_FILENAME) == PHP_PATHINFO_FILENAME) {
1739                 const char *p;
1740                 ptrdiff_t idx;
1741 
1742                 /* Have we already looked up the basename? */
1743                 if (!have_basename && !ret) {
1744                         ret = php_basename(path, path_len, NULL, 0);
1745                 }
1746 
1747                 p = zend_memrchr(ZSTR_VAL(ret), '.', ZSTR_LEN(ret));
1748 
1749                 idx = p ? (p - ZSTR_VAL(ret)) : ZSTR_LEN(ret);
1750                 add_assoc_stringl(&tmp, "filename", ZSTR_VAL(ret), idx);
1751         }
1752 
1753         if (ret) {
1754                 zend_string_release(ret);
1755         }
1756 
1757         if (opt == PHP_PATHINFO_ALL) {
1758                 ZVAL_COPY_VALUE(return_value, &tmp);
1759         } else {
1760                 zval *element;
1761                 if ((element = zend_hash_get_current_data(Z_ARRVAL(tmp))) != NULL) {
1762                         ZVAL_DEREF(element);
1763                         ZVAL_COPY(return_value, element);
1764                 } else {
1765                         ZVAL_EMPTY_STRING(return_value);
1766                 }
1767                 zval_ptr_dtor(&tmp);
1768         }
1769 }
1770 /* }}} */
1771 
1772 /* {{{ php_stristr
1773    case insensitve strstr */
1774 PHPAPI char *php_stristr(char *s, char *t, size_t s_len, size_t t_len)
1775 {
1776         php_strtolower(s, s_len);
1777         php_strtolower(t, t_len);
1778         return (char*)php_memnstr(s, t, t_len, s + s_len);
1779 }
1780 /* }}} */
1781 
1782 /* {{{ php_strspn
1783  */
1784 PHPAPI size_t php_strspn(char *s1, char *s2, char *s1_end, char *s2_end)
1785 {
1786         register const char *p = s1, *spanp;
1787         register char c = *p;
1788 
1789 cont:
1790         for (spanp = s2; p != s1_end && spanp != s2_end;) {
1791                 if (*spanp++ == c) {
1792                         c = *(++p);
1793                         goto cont;
1794                 }
1795         }
1796         return (p - s1);
1797 }
1798 /* }}} */
1799 
1800 /* {{{ php_strcspn
1801  */
1802 PHPAPI size_t php_strcspn(char *s1, char *s2, char *s1_end, char *s2_end)
1803 {
1804         register const char *p, *spanp;
1805         register char c = *s1;
1806 
1807         for (p = s1;;) {
1808                 spanp = s2;
1809                 do {
1810                         if (*spanp == c || p == s1_end) {
1811                                 return p - s1;
1812                         }
1813                 } while (spanp++ < (s2_end - 1));
1814                 c = *++p;
1815         }
1816         /* NOTREACHED */
1817 }
1818 /* }}} */
1819 
1820 /* {{{ php_needle_char
1821  */
1822 static int php_needle_char(zval *needle, char *target)
1823 {
1824         switch (Z_TYPE_P(needle)) {
1825                 case IS_LONG:
1826                         *target = (char)Z_LVAL_P(needle);
1827                         return SUCCESS;
1828                 case IS_NULL:
1829                 case IS_FALSE:
1830                         *target = '\0';
1831                         return SUCCESS;
1832                 case IS_TRUE:
1833                         *target = '\1';
1834                         return SUCCESS;
1835                 case IS_DOUBLE:
1836                         *target = (char)(int)Z_DVAL_P(needle);
1837                         return SUCCESS;
1838                 case IS_OBJECT:
1839                         *target = (char) zval_get_long(needle);
1840                         return SUCCESS;
1841                 default:
1842                         php_error_docref(NULL, E_WARNING, "needle is not a string or an integer");
1843                         return FAILURE;
1844         }
1845 }
1846 /* }}} */
1847 
1848 /* {{{ proto string stristr(string haystack, string needle[, bool part])
1849    Finds first occurrence of a string within another, case insensitive */
1850 PHP_FUNCTION(stristr)
1851 {
1852         zval *needle;
1853         zend_string *haystack;
1854         char *found = NULL;
1855         size_t  found_offset;
1856         char *haystack_dup;
1857         char needle_char[2];
1858         zend_bool part = 0;
1859 
1860         if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz|b", &haystack, &needle, &part) == FAILURE) {
1861                 return;
1862         }
1863 
1864         haystack_dup = estrndup(ZSTR_VAL(haystack), ZSTR_LEN(haystack));
1865 
1866         if (Z_TYPE_P(needle) == IS_STRING) {
1867                 char *orig_needle;
1868                 if (!Z_STRLEN_P(needle)) {
1869                         php_error_docref(NULL, E_WARNING, "Empty needle");
1870                         efree(haystack_dup);
1871                         RETURN_FALSE;
1872                 }
1873                 orig_needle = estrndup(Z_STRVAL_P(needle), Z_STRLEN_P(needle));
1874                 found = php_stristr(haystack_dup, orig_needle, ZSTR_LEN(haystack), Z_STRLEN_P(needle));
1875                 efree(orig_needle);
1876         } else {
1877                 if (php_needle_char(needle, needle_char) != SUCCESS) {
1878                         efree(haystack_dup);
1879                         RETURN_FALSE;
1880                 }
1881                 needle_char[1] = 0;
1882 
1883                 found = php_stristr(haystack_dup, needle_char, ZSTR_LEN(haystack), 1);
1884         }
1885 
1886         if (found) {
1887                 found_offset = found - haystack_dup;
1888                 if (part) {
1889                         RETVAL_STRINGL(ZSTR_VAL(haystack), found_offset);
1890                 } else {
1891                         RETVAL_STRINGL(ZSTR_VAL(haystack) + found_offset, ZSTR_LEN(haystack) - found_offset);
1892                 }
1893         } else {
1894                 RETVAL_FALSE;
1895         }
1896 
1897         efree(haystack_dup);
1898 }
1899 /* }}} */
1900 
1901 /* {{{ proto string strstr(string haystack, string needle[, bool part])
1902    Finds first occurrence of a string within another */
1903 PHP_FUNCTION(strstr)
1904 {
1905         zval *needle;
1906         zend_string *haystack;
1907         char *found = NULL;
1908         char needle_char[2];
1909         zend_long found_offset;
1910         zend_bool part = 0;
1911 
1912         if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz|b", &haystack, &needle, &part) == FAILURE) {
1913                 return;
1914         }
1915 
1916         if (Z_TYPE_P(needle) == IS_STRING) {
1917                 if (!Z_STRLEN_P(needle)) {
1918                         php_error_docref(NULL, E_WARNING, "Empty needle");
1919                         RETURN_FALSE;
1920                 }
1921 
1922                 found = (char*)php_memnstr(ZSTR_VAL(haystack), Z_STRVAL_P(needle), Z_STRLEN_P(needle), ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1923         } else {
1924                 if (php_needle_char(needle, needle_char) != SUCCESS) {
1925                         RETURN_FALSE;
1926                 }
1927                 needle_char[1] = 0;
1928 
1929                 found = (char*)php_memnstr(ZSTR_VAL(haystack), needle_char, 1, ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1930         }
1931 
1932         if (found) {
1933                 found_offset = found - ZSTR_VAL(haystack);
1934                 if (part) {
1935                         RETURN_STRINGL(ZSTR_VAL(haystack), found_offset);
1936                 } else {
1937                         RETURN_STRINGL(found, ZSTR_LEN(haystack) - found_offset);
1938                 }
1939         }
1940         RETURN_FALSE;
1941 }
1942 /* }}} */
1943 
1944 /* {{{ proto string strchr(string haystack, string needle)
1945    An alias for strstr */
1946 /* }}} */
1947 
1948 /* {{{ proto int strpos(string haystack, string needle [, int offset])
1949    Finds position of first occurrence of a string within another */
1950 PHP_FUNCTION(strpos)
1951 {
1952         zval *needle;
1953         zend_string *haystack;
1954         char *found = NULL;
1955         char  needle_char[2];
1956         zend_long  offset = 0;
1957 
1958 #ifndef FAST_ZPP
1959         if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz|l", &haystack, &needle, &offset) == FAILURE) {
1960                 return;
1961         }
1962 #else
1963         ZEND_PARSE_PARAMETERS_START(2, 3)
1964                 Z_PARAM_STR(haystack)
1965                 Z_PARAM_ZVAL(needle)
1966                 Z_PARAM_OPTIONAL
1967                 Z_PARAM_LONG(offset)
1968         ZEND_PARSE_PARAMETERS_END();
1969 #endif
1970 
1971         if (offset < 0 || (size_t)offset > ZSTR_LEN(haystack)) {
1972                 php_error_docref(NULL, E_WARNING, "Offset not contained in string");
1973                 RETURN_FALSE;
1974         }
1975 
1976         if (Z_TYPE_P(needle) == IS_STRING) {
1977                 if (!Z_STRLEN_P(needle)) {
1978                         php_error_docref(NULL, E_WARNING, "Empty needle");
1979                         RETURN_FALSE;
1980                 }
1981 
1982                 found = (char*)php_memnstr(ZSTR_VAL(haystack) + offset,
1983                                         Z_STRVAL_P(needle),
1984                                         Z_STRLEN_P(needle),
1985                                         ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1986         } else {
1987                 if (php_needle_char(needle, needle_char) != SUCCESS) {
1988                         RETURN_FALSE;
1989                 }
1990                 needle_char[1] = 0;
1991 
1992                 found = (char*)php_memnstr(ZSTR_VAL(haystack) + offset,
1993                                                         needle_char,
1994                                                         1,
1995                                     ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1996         }
1997 
1998         if (found) {
1999                 RETURN_LONG(found - ZSTR_VAL(haystack));
2000         } else {
2001                 RETURN_FALSE;
2002         }
2003 }
2004 /* }}} */
2005 
2006 /* {{{ proto int stripos(string haystack, string needle [, int offset])
2007    Finds position of first occurrence of a string within another, case insensitive */
2008 PHP_FUNCTION(stripos)
2009 {
2010         char *found = NULL;
2011         zend_string *haystack;
2012         zend_long offset = 0;
2013         char needle_char[2];
2014         zval *needle;
2015         zend_string *needle_dup = NULL, *haystack_dup;
2016 
2017         if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz|l", &haystack, &needle, &offset) == FAILURE) {
2018                 return;
2019         }
2020 
2021         if (offset < 0 || (size_t)offset > ZSTR_LEN(haystack)) {
2022                 php_error_docref(NULL, E_WARNING, "Offset not contained in string");
2023                 RETURN_FALSE;
2024         }
2025 
2026         if (ZSTR_LEN(haystack) == 0) {
2027                 RETURN_FALSE;
2028         }
2029 
2030         if (Z_TYPE_P(needle) == IS_STRING) {
2031                 if (Z_STRLEN_P(needle) == 0 || Z_STRLEN_P(needle) > ZSTR_LEN(haystack)) {
2032                         RETURN_FALSE;
2033                 }
2034 
2035                 haystack_dup = php_string_tolower(haystack);
2036                 needle_dup = php_string_tolower(Z_STR_P(needle));
2037                 found = (char*)php_memnstr(ZSTR_VAL(haystack_dup) + offset,
2038                                 ZSTR_VAL(needle_dup), ZSTR_LEN(needle_dup), ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack));
2039         } else {
2040                 if (php_needle_char(needle, needle_char) != SUCCESS) {
2041                         RETURN_FALSE;
2042                 }
2043                 haystack_dup = php_string_tolower(haystack);
2044                 needle_char[0] = tolower(needle_char[0]);
2045                 needle_char[1] = '\0';
2046                 found = (char*)php_memnstr(ZSTR_VAL(haystack_dup) + offset,
2047                                                         needle_char,
2048                                                         sizeof(needle_char) - 1,
2049                                                         ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack));
2050         }
2051 
2052 
2053         if (found) {
2054                 RETVAL_LONG(found - ZSTR_VAL(haystack_dup));
2055         } else {
2056                 RETVAL_FALSE;
2057         }
2058 
2059         zend_string_release(haystack_dup);
2060         if (needle_dup) {
2061                 zend_string_release(needle_dup);
2062         }
2063 }
2064 /* }}} */
2065 
2066 /* {{{ proto int strrpos(string haystack, string needle [, int offset])
2067    Finds position of last occurrence of a string within another string */
2068 PHP_FUNCTION(strrpos)
2069 {
2070         zval *zneedle;
2071         char *needle;
2072         zend_string *haystack;
2073         size_t needle_len;
2074         zend_long offset = 0;
2075         char *p, *e, ord_needle[2];
2076         char *found;
2077 
2078 #ifndef FAST_ZPP
2079         if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz|l", &haystack, &zneedle, &offset) == FAILURE) {
2080                 RETURN_FALSE;
2081         }
2082 #else
2083         ZEND_PARSE_PARAMETERS_START(2, 3)
2084                 Z_PARAM_STR(haystack)
2085                 Z_PARAM_ZVAL(zneedle)
2086                 Z_PARAM_OPTIONAL
2087                 Z_PARAM_LONG(offset)
2088         ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
2089 #endif
2090 
2091         if (Z_TYPE_P(zneedle) == IS_STRING) {
2092                 needle = Z_STRVAL_P(zneedle);
2093                 needle_len = Z_STRLEN_P(zneedle);
2094         } else {
2095                 if (php_needle_char(zneedle, ord_needle) != SUCCESS) {
2096                         RETURN_FALSE;
2097                 }
2098                 ord_needle[1] = '\0';
2099                 needle = ord_needle;
2100                 needle_len = 1;
2101         }
2102 
2103         if ((ZSTR_LEN(haystack) == 0) || (needle_len == 0)) {
2104                 RETURN_FALSE;
2105         }
2106 
2107         if (offset >= 0) {
2108                 if ((size_t)offset > ZSTR_LEN(haystack)) {
2109                         php_error_docref(NULL, E_WARNING, "Offset is greater than the length of haystack string");
2110                         RETURN_FALSE;
2111                 }
2112                 p = ZSTR_VAL(haystack) + (size_t)offset;
2113                 e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
2114         } else {
2115                 if (offset < -INT_MAX || (size_t)(-offset) > ZSTR_LEN(haystack)) {
2116                         php_error_docref(NULL, E_WARNING, "Offset is greater than the length of haystack string");
2117                         RETURN_FALSE;
2118                 }
2119                 p = ZSTR_VAL(haystack);
2120                 if (-offset < needle_len) {
2121                         e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
2122                 } else {
2123                         e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack) + offset + needle_len;
2124                 }
2125         }
2126 
2127         if ((found = (char *)zend_memnrstr(p, needle, needle_len, e))) {
2128                 RETURN_LONG(found - ZSTR_VAL(haystack));
2129         }
2130 
2131         RETURN_FALSE;
2132 }
2133 /* }}} */
2134 
2135 /* {{{ proto int strripos(string haystack, string needle [, int offset])
2136    Finds position of last occurrence of a string within another string */
2137 PHP_FUNCTION(strripos)
2138 {
2139         zval *zneedle;
2140         zend_string *needle;
2141         zend_string *haystack;
2142         zend_long offset = 0;
2143         char *p, *e;
2144         char *found;
2145         zend_string *needle_dup, *haystack_dup, *ord_needle = NULL;
2146         ALLOCA_FLAG(use_heap);
2147 
2148 
2149         if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz|l", &haystack, &zneedle, &offset) == FAILURE) {
2150                 RETURN_FALSE;
2151         }
2152 
2153         ZSTR_ALLOCA_ALLOC(ord_needle, 1, use_heap);
2154         if (Z_TYPE_P(zneedle) == IS_STRING) {
2155                 needle = Z_STR_P(zneedle);
2156         } else {
2157                 if (php_needle_char(zneedle, ZSTR_VAL(ord_needle)) != SUCCESS) {
2158                         ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2159                         RETURN_FALSE;
2160                 }
2161                 ZSTR_VAL(ord_needle)[1] = '\0';
2162                 needle = ord_needle;
2163         }
2164 
2165         if ((ZSTR_LEN(haystack) == 0) || (ZSTR_LEN(needle) == 0)) {
2166                 ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2167                 RETURN_FALSE;
2168         }
2169 
2170         if (ZSTR_LEN(needle) == 1) {
2171                 /* Single character search can shortcut memcmps
2172                    Can also avoid tolower emallocs */
2173                 if (offset >= 0) {
2174                         if ((size_t)offset > ZSTR_LEN(haystack)) {
2175                                 ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2176                                 php_error_docref(NULL, E_WARNING, "Offset is greater than the length of haystack string");
2177                                 RETURN_FALSE;
2178                         }
2179                         p = ZSTR_VAL(haystack) + (size_t)offset;
2180                         e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack) - 1;
2181                 } else {
2182                         p = ZSTR_VAL(haystack);
2183                         if (offset < -INT_MAX || (size_t)(-offset) > ZSTR_LEN(haystack)) {
2184                                 ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2185                                 php_error_docref(NULL, E_WARNING, "Offset is greater than the length of haystack string");
2186                                 RETURN_FALSE;
2187                         }
2188                         e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack) + (size_t)offset;
2189                 }
2190                 /* Borrow that ord_needle buffer to avoid repeatedly tolower()ing needle */
2191                 *ZSTR_VAL(ord_needle) = tolower(*ZSTR_VAL(needle));
2192                 while (e >= p) {
2193                         if (tolower(*e) == *ZSTR_VAL(ord_needle)) {
2194                                 ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2195                                 RETURN_LONG(e - p + (offset > 0 ? offset : 0));
2196                         }
2197                         e--;
2198                 }
2199                 ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2200                 RETURN_FALSE;
2201         }
2202 
2203         haystack_dup = php_string_tolower(haystack);
2204         if (offset >= 0) {
2205                 if ((size_t)offset > ZSTR_LEN(haystack)) {
2206                         zend_string_release(haystack_dup);
2207                         ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2208                         php_error_docref(NULL, E_WARNING, "Offset is greater than the length of haystack string");
2209                         RETURN_FALSE;
2210                 }
2211                 p = ZSTR_VAL(haystack_dup) + offset;
2212                 e = ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack);
2213         } else {
2214                 if (offset < -INT_MAX || (size_t)(-offset) > ZSTR_LEN(haystack)) {
2215                         zend_string_release(haystack_dup);
2216                         ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2217                         php_error_docref(NULL, E_WARNING, "Offset is greater than the length of haystack string");
2218                         RETURN_FALSE;
2219                 }
2220                 p = ZSTR_VAL(haystack_dup);
2221                 if (-offset < ZSTR_LEN(needle)) {
2222                         e = ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack);
2223                 } else {
2224                         e = ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack) + offset + ZSTR_LEN(needle);
2225                 }
2226         }
2227 
2228         needle_dup = php_string_tolower(needle);
2229         if ((found = (char *)zend_memnrstr(p, ZSTR_VAL(needle_dup), ZSTR_LEN(needle_dup), e))) {
2230                 RETVAL_LONG(found - ZSTR_VAL(haystack_dup));
2231                 zend_string_release(needle_dup);
2232                 zend_string_release(haystack_dup);
2233                 ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2234         } else {
2235                 zend_string_release(needle_dup);
2236                 zend_string_release(haystack_dup);
2237                 ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2238                 RETURN_FALSE;
2239         }
2240 }
2241 /* }}} */
2242 
2243 /* {{{ proto string strrchr(string haystack, string needle)
2244    Finds the last occurrence of a character in a string within another */
2245 PHP_FUNCTION(strrchr)
2246 {
2247         zval *needle;
2248         zend_string *haystack;
2249         const char *found = NULL;
2250         zend_long found_offset;
2251 
2252         if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz", &haystack, &needle) == FAILURE) {
2253                 return;
2254         }
2255 
2256         if (Z_TYPE_P(needle) == IS_STRING) {
2257                 found = zend_memrchr(ZSTR_VAL(haystack), *Z_STRVAL_P(needle), ZSTR_LEN(haystack));
2258         } else {
2259                 char needle_chr;
2260                 if (php_needle_char(needle, &needle_chr) != SUCCESS) {
2261                         RETURN_FALSE;
2262                 }
2263 
2264                 found = zend_memrchr(ZSTR_VAL(haystack),  needle_chr, ZSTR_LEN(haystack));
2265         }
2266 
2267         if (found) {
2268                 found_offset = found - ZSTR_VAL(haystack);
2269                 RETURN_STRINGL(found, ZSTR_LEN(haystack) - found_offset);
2270         } else {
2271                 RETURN_FALSE;
2272         }
2273 }
2274 /* }}} */
2275 
2276 /* {{{ php_chunk_split
2277  */
2278 static zend_string *php_chunk_split(char *src, size_t srclen, char *end, size_t endlen, size_t chunklen)
2279 {
2280         char *p, *q;
2281         size_t chunks; /* complete chunks! */
2282         size_t restlen;
2283         size_t out_len;
2284         zend_string *dest;
2285 
2286         chunks = srclen / chunklen;
2287         restlen = srclen - chunks * chunklen; /* srclen % chunklen */
2288 
2289         if (chunks > INT_MAX - 1) {
2290                 return NULL;
2291         }
2292         out_len = chunks + 1;
2293         if (endlen !=0 && out_len > INT_MAX/endlen) {
2294                 return NULL;
2295         }
2296         out_len *= endlen;
2297         if (out_len > INT_MAX - srclen - 1) {
2298                 return NULL;
2299         }
2300         out_len += srclen + 1;
2301 
2302         dest = zend_string_alloc(out_len * sizeof(char), 0);
2303 
2304         for (p = src, q = ZSTR_VAL(dest); p < (src + srclen - chunklen + 1); ) {
2305                 memcpy(q, p, chunklen);
2306                 q += chunklen;
2307                 memcpy(q, end, endlen);
2308                 q += endlen;
2309                 p += chunklen;
2310         }
2311 
2312         if (restlen) {
2313                 memcpy(q, p, restlen);
2314                 q += restlen;
2315                 memcpy(q, end, endlen);
2316                 q += endlen;
2317         }
2318 
2319         *q = '\0';
2320         ZSTR_LEN(dest) = q - ZSTR_VAL(dest);
2321 
2322         return dest;
2323 }
2324 /* }}} */
2325 
2326 /* {{{ proto string chunk_split(string str [, int chunklen [, string ending]])
2327    Returns split line */
2328 PHP_FUNCTION(chunk_split)
2329 {
2330         zend_string *str;
2331         char *end    = "\r\n";
2332         size_t endlen   = 2;
2333         zend_long chunklen = 76;
2334         zend_string *result;
2335 
2336         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|ls", &str, &chunklen, &end, &endlen) == FAILURE) {
2337                 return;
2338         }
2339 
2340         if (chunklen <= 0) {
2341                 php_error_docref(NULL, E_WARNING, "Chunk length should be greater than zero");
2342                 RETURN_FALSE;
2343         }
2344 
2345         if ((size_t)chunklen > ZSTR_LEN(str)) {
2346                 /* to maintain BC, we must return original string + ending */
2347                 result = zend_string_safe_alloc(ZSTR_LEN(str), 1, endlen, 0);
2348                 memcpy(ZSTR_VAL(result), ZSTR_VAL(str), ZSTR_LEN(str));
2349                 memcpy(ZSTR_VAL(result) + ZSTR_LEN(str), end, endlen);
2350                 ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
2351                 RETURN_NEW_STR(result);
2352         }
2353 
2354         if (!ZSTR_LEN(str)) {
2355                 RETURN_EMPTY_STRING();
2356         }
2357 
2358         result = php_chunk_split(ZSTR_VAL(str), ZSTR_LEN(str), end, endlen, (size_t)chunklen);
2359 
2360         if (result) {
2361                 RETURN_STR(result);
2362         } else {
2363                 RETURN_FALSE;
2364         }
2365 }
2366 /* }}} */
2367 
2368 /* {{{ proto string substr(string str, int start [, int length])
2369    Returns part of a string */
2370 PHP_FUNCTION(substr)
2371 {
2372         zend_string *str;
2373         zend_long l = 0, f;
2374         int argc = ZEND_NUM_ARGS();
2375 
2376 #ifndef FAST_ZPP
2377         if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sl|l", &str, &f, &l) == FAILURE) {
2378                 return;
2379         }
2380 #else
2381         ZEND_PARSE_PARAMETERS_START(2, 3)
2382                 Z_PARAM_STR(str)
2383                 Z_PARAM_LONG(f)
2384                 Z_PARAM_OPTIONAL
2385                 Z_PARAM_LONG(l)
2386         ZEND_PARSE_PARAMETERS_END();
2387 #endif
2388 
2389         if (argc > 2) {
2390                 if ((l < 0 && (size_t)(-l) > ZSTR_LEN(str))) {
2391                         RETURN_FALSE;
2392                 } else if (l > (zend_long)ZSTR_LEN(str)) {
2393                         l = ZSTR_LEN(str);
2394                 }
2395         } else {
2396                 l = ZSTR_LEN(str);
2397         }
2398 
2399         if (f > (zend_long)ZSTR_LEN(str)) {
2400                 RETURN_FALSE;
2401         } else if (f < 0 && -f > ZSTR_LEN(str)) {
2402                 f = 0;
2403         }
2404 
2405         if (l < 0 && (l + (zend_long)ZSTR_LEN(str) - f) < 0) {
2406                 RETURN_FALSE;
2407         }
2408 
2409         /* if "from" position is negative, count start position from the end
2410          * of the string
2411          */
2412         if (f < 0) {
2413                 f = (zend_long)ZSTR_LEN(str) + f;
2414                 if (f < 0) {
2415                         f = 0;
2416                 }
2417         }
2418 
2419         /* if "length" position is negative, set it to the length
2420          * needed to stop that many chars from the end of the string
2421          */
2422         if (l < 0) {
2423                 l = ((zend_long)ZSTR_LEN(str) - f) + l;
2424                 if (l < 0) {
2425                         l = 0;
2426                 }
2427         }
2428 
2429         if (f > (zend_long)ZSTR_LEN(str)) {
2430                 RETURN_FALSE;
2431         }
2432 
2433         if ((f + l) > (zend_long)ZSTR_LEN(str)) {
2434                 l = ZSTR_LEN(str) - f;
2435         }
2436 
2437         RETURN_STRINGL(ZSTR_VAL(str) + f, l);
2438 }
2439 /* }}} */
2440 
2441 /* {{{ proto mixed substr_replace(mixed str, mixed repl, mixed start [, mixed length])
2442    Replaces part of a string with another string */
2443 PHP_FUNCTION(substr_replace)
2444 {
2445         zval *str;
2446         zval *from;
2447         zval *len = NULL;
2448         zval *repl;
2449         zend_long l = 0;
2450         zend_long f;
2451         int argc = ZEND_NUM_ARGS();
2452         zend_string *result;
2453         HashPosition from_idx, repl_idx, len_idx;
2454         zval *tmp_str = NULL, *tmp_from = NULL, *tmp_repl = NULL, *tmp_len= NULL;
2455 
2456         if (zend_parse_parameters(ZEND_NUM_ARGS(), "zzz|z/", &str, &repl, &from, &len) == FAILURE) {
2457                 return;
2458         }
2459 
2460         if (Z_TYPE_P(str) != IS_ARRAY) {
2461                 convert_to_string_ex(str);
2462         }
2463         if (Z_TYPE_P(repl) != IS_ARRAY) {
2464                 convert_to_string_ex(repl);
2465         }
2466         if (Z_TYPE_P(from) != IS_ARRAY) {
2467                 convert_to_long_ex(from);
2468         }
2469 
2470         if (argc > 3) {
2471                 if (Z_TYPE_P(len) != IS_ARRAY) {
2472                         l = zval_get_long(len);
2473                 }
2474         } else {
2475                 if (Z_TYPE_P(str) != IS_ARRAY) {
2476                         l = Z_STRLEN_P(str);
2477                 }
2478         }
2479 
2480         if (Z_TYPE_P(str) == IS_STRING) {
2481                 if (
2482                         (argc == 3 && Z_TYPE_P(from) == IS_ARRAY) ||
2483                         (argc == 4 && Z_TYPE_P(from) != Z_TYPE_P(len))
2484                 ) {
2485                         php_error_docref(NULL, E_WARNING, "'from' and 'len' should be of same type - numerical or array ");
2486                         RETURN_STR_COPY(Z_STR_P(str));
2487                 }
2488                 if (argc == 4 && Z_TYPE_P(from) == IS_ARRAY) {
2489                         if (zend_hash_num_elements(Z_ARRVAL_P(from)) != zend_hash_num_elements(Z_ARRVAL_P(len))) {
2490                                 php_error_docref(NULL, E_WARNING, "'from' and 'len' should have the same number of elements");
2491                                 RETURN_STR_COPY(Z_STR_P(str));
2492                         }
2493                 }
2494         }
2495 
2496         if (Z_TYPE_P(str) != IS_ARRAY) {
2497                 if (Z_TYPE_P(from) != IS_ARRAY) {
2498                         zend_string *repl_str;
2499                         zend_bool repl_release = 0;
2500                         f = Z_LVAL_P(from);
2501 
2502                         /* if "from" position is negative, count start position from the end
2503                          * of the string
2504                          */
2505                         if (f < 0) {
2506                                 f = (zend_long)Z_STRLEN_P(str) + f;
2507                                 if (f < 0) {
2508                                         f = 0;
2509                                 }
2510                         } else if (f > Z_STRLEN_P(str)) {
2511                                 f = Z_STRLEN_P(str);
2512                         }
2513                         /* if "length" position is negative, set it to the length
2514                          * needed to stop that many chars from the end of the string
2515                          */
2516                         if (l < 0) {
2517                                 l = ((zend_long)Z_STRLEN_P(str) - f) + l;
2518                                 if (l < 0) {
2519                                         l = 0;
2520                                 }
2521                         }
2522 
2523                         if (l > Z_STRLEN_P(str) || (l < 0 && (size_t)(-l) > Z_STRLEN_P(str))) {
2524                                 l = Z_STRLEN_P(str);
2525                         }
2526 
2527                         if ((f + l) > (zend_long)Z_STRLEN_P(str)) {
2528                                 l = Z_STRLEN_P(str) - f;
2529                         }
2530                         if (Z_TYPE_P(repl) == IS_ARRAY) {
2531                                 repl_idx = 0;
2532                                 while (repl_idx < Z_ARRVAL_P(repl)->nNumUsed) {
2533                                         tmp_repl = &Z_ARRVAL_P(repl)->arData[repl_idx].val;
2534                                         if (Z_TYPE_P(tmp_repl) != IS_UNDEF) {
2535                                                 break;
2536                                         }
2537                                         repl_idx++;
2538                                 }
2539                                 if (repl_idx < Z_ARRVAL_P(repl)->nNumUsed) {
2540                                         repl_str = zval_get_string(tmp_repl);
2541                                         repl_release = 1;
2542                                 } else {
2543                                         repl_str = STR_EMPTY_ALLOC();
2544                                 }
2545                         } else {
2546                                 repl_str = Z_STR_P(repl);
2547                         }
2548 
2549                         result = zend_string_alloc(Z_STRLEN_P(str) - l + ZSTR_LEN(repl_str), 0);
2550 
2551                         memcpy(ZSTR_VAL(result), Z_STRVAL_P(str), f);
2552                         if (ZSTR_LEN(repl_str)) {
2553                                 memcpy((ZSTR_VAL(result) + f), ZSTR_VAL(repl_str), ZSTR_LEN(repl_str));
2554                         }
2555                         memcpy((ZSTR_VAL(result) + f + ZSTR_LEN(repl_str)), Z_STRVAL_P(str) + f + l, Z_STRLEN_P(str) - f - l);
2556                         ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
2557                         if (repl_release) {
2558                                 zend_string_release(repl_str);
2559                         }
2560                         RETURN_NEW_STR(result);
2561                 } else {
2562                         php_error_docref(NULL, E_WARNING, "Functionality of 'from' and 'len' as arrays is not implemented");
2563                         RETURN_STR_COPY(Z_STR_P(str));
2564                 }
2565         } else { /* str is array of strings */
2566                 zend_string *str_index = NULL;
2567                 size_t result_len;
2568                 zend_ulong num_index;
2569 
2570                 array_init(return_value);
2571 
2572                 from_idx = len_idx = repl_idx = 0;
2573 
2574                 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(str), num_index, str_index, tmp_str) {
2575                         zend_string *orig_str = zval_get_string(tmp_str);
2576 
2577                         if (Z_TYPE_P(from) == IS_ARRAY) {
2578                                 while (from_idx < Z_ARRVAL_P(from)->nNumUsed) {
2579                                         tmp_from = &Z_ARRVAL_P(from)->arData[from_idx].val;
2580                                         if (Z_TYPE_P(tmp_from) != IS_UNDEF) {
2581                                                 break;
2582                                         }
2583                                         from_idx++;
2584                                 }
2585                                 if (from_idx < Z_ARRVAL_P(from)->nNumUsed) {
2586                                         f = zval_get_long(tmp_from);
2587 
2588                                         if (f < 0) {
2589                                                 f = (zend_long)ZSTR_LEN(orig_str) + f;
2590                                                 if (f < 0) {
2591                                                         f = 0;
2592                                                 }
2593                                         } else if (f > (zend_long)ZSTR_LEN(orig_str)) {
2594                                                 f = ZSTR_LEN(orig_str);
2595                                         }
2596                                         from_idx++;
2597                                 } else {
2598                                         f = 0;
2599                                 }
2600                         } else {
2601                                 f = Z_LVAL_P(from);
2602                                 if (f < 0) {
2603                                         f = (zend_long)ZSTR_LEN(orig_str) + f;
2604                                         if (f < 0) {
2605                                                 f = 0;
2606                                         }
2607                                 } else if (f > (zend_long)ZSTR_LEN(orig_str)) {
2608                                         f = ZSTR_LEN(orig_str);
2609                                 }
2610                         }
2611 
2612                         if (argc > 3 && Z_TYPE_P(len) == IS_ARRAY) {
2613                                 while (len_idx < Z_ARRVAL_P(len)->nNumUsed) {
2614                                         tmp_len = &Z_ARRVAL_P(len)->arData[len_idx].val;
2615                                         if (Z_TYPE_P(tmp_len) != IS_UNDEF) {
2616                                                 break;
2617                                         }
2618                                         len_idx++;
2619                                 }
2620                                 if (len_idx < Z_ARRVAL_P(len)->nNumUsed) {
2621                                         l = zval_get_long(tmp_len);
2622                                         len_idx++;
2623                                 } else {
2624                                         l = ZSTR_LEN(orig_str);
2625                                 }
2626                         } else if (argc > 3) {
2627                                 l = Z_LVAL_P(len);
2628                         } else {
2629                                 l = ZSTR_LEN(orig_str);
2630                         }
2631 
2632                         if (l < 0) {
2633                                 l = (ZSTR_LEN(orig_str) - f) + l;
2634                                 if (l < 0) {
2635                                         l = 0;
2636                                 }
2637                         }
2638 
2639                         if ((f + l) > (zend_long)ZSTR_LEN(orig_str)) {
2640                                 l = ZSTR_LEN(orig_str) - f;
2641                         }
2642 
2643                         result_len = ZSTR_LEN(orig_str) - l;
2644 
2645                         if (Z_TYPE_P(repl) == IS_ARRAY) {
2646                                 while (repl_idx < Z_ARRVAL_P(repl)->nNumUsed) {
2647                                         tmp_repl = &Z_ARRVAL_P(repl)->arData[repl_idx].val;
2648                                         if (Z_TYPE_P(tmp_repl) != IS_UNDEF) {
2649                                                 break;
2650                                         }
2651                                         repl_idx++;
2652                                 }
2653                                 if (repl_idx < Z_ARRVAL_P(repl)->nNumUsed) {
2654                                         zend_string *repl_str = zval_get_string(tmp_repl);
2655 
2656                                         result_len += ZSTR_LEN(repl_str);
2657                                         repl_idx++;
2658                                         result = zend_string_alloc(result_len, 0);
2659 
2660                                         memcpy(ZSTR_VAL(result), ZSTR_VAL(orig_str), f);
2661                                         memcpy((ZSTR_VAL(result) + f), ZSTR_VAL(repl_str), ZSTR_LEN(repl_str));
2662                                         memcpy((ZSTR_VAL(result) + f + ZSTR_LEN(repl_str)), ZSTR_VAL(orig_str) + f + l, ZSTR_LEN(orig_str) - f - l);
2663                                         zend_string_release(repl_str);
2664                                 } else {
2665                                         result = zend_string_alloc(result_len, 0);
2666 
2667                                         memcpy(ZSTR_VAL(result), ZSTR_VAL(orig_str), f);
2668                                         memcpy((ZSTR_VAL(result) + f), ZSTR_VAL(orig_str) + f + l, ZSTR_LEN(orig_str) - f - l);
2669                                 }
2670                         } else {
2671                                 result_len += Z_STRLEN_P(repl);
2672 
2673                                 result = zend_string_alloc(result_len, 0);
2674 
2675                                 memcpy(ZSTR_VAL(result), ZSTR_VAL(orig_str), f);
2676                                 memcpy((ZSTR_VAL(result) + f), Z_STRVAL_P(repl), Z_STRLEN_P(repl));
2677                                 memcpy((ZSTR_VAL(result) + f + Z_STRLEN_P(repl)), ZSTR_VAL(orig_str) + f + l, ZSTR_LEN(orig_str) - f - l);
2678                         }
2679 
2680                         ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
2681 
2682                         if (str_index) {
2683                                 zval tmp;
2684 
2685                                 ZVAL_NEW_STR(&tmp, result);
2686                                 zend_symtable_update(Z_ARRVAL_P(return_value), str_index, &tmp);
2687                         } else {
2688                                 add_index_str(return_value, num_index, result);
2689                         }
2690 
2691                         zend_string_release(orig_str);
2692                 } ZEND_HASH_FOREACH_END();
2693         } /* if */
2694 }
2695 /* }}} */
2696 
2697 /* {{{ proto string quotemeta(string str)
2698    Quotes meta characters */
2699 PHP_FUNCTION(quotemeta)
2700 {
2701         zend_string *old;
2702         char *old_end;
2703         char *p, *q;
2704         char c;
2705         zend_string *str;
2706 
2707         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &old) == FAILURE) {
2708                 return;
2709         }
2710 
2711         old_end = ZSTR_VAL(old) + ZSTR_LEN(old);
2712 
2713         if (ZSTR_VAL(old) == old_end) {
2714                 RETURN_FALSE;
2715         }
2716 
2717         str = zend_string_safe_alloc(2, ZSTR_LEN(old), 0, 0);
2718 
2719         for (p = ZSTR_VAL(old), q = ZSTR_VAL(str); p != old_end; p++) {
2720                 c = *p;
2721                 switch (c) {
2722                         case '.':
2723                         case '\\':
2724                         case '+':
2725                         case '*':
2726                         case '?':
2727                         case '[':
2728                         case '^':
2729                         case ']':
2730                         case '$':
2731                         case '(':
2732                         case ')':
2733                                 *q++ = '\\';
2734                                 /* break is missing _intentionally_ */
2735                         default:
2736                                 *q++ = c;
2737                 }
2738         }
2739 
2740         *q = '\0';
2741 
2742         RETURN_NEW_STR(zend_string_truncate(str, q - ZSTR_VAL(str), 0));
2743 }
2744 /* }}} */
2745 
2746 /* {{{ proto int ord(string character)
2747    Returns ASCII value of character */
2748 PHP_FUNCTION(ord)
2749 {
2750         char   *str;
2751         size_t str_len;
2752 
2753 #ifndef FAST_ZPP
2754         if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &str_len) == FAILURE) {
2755                 return;
2756         }
2757 #else
2758         ZEND_PARSE_PARAMETERS_START(1, 1)
2759                 Z_PARAM_STRING(str, str_len)
2760         ZEND_PARSE_PARAMETERS_END();
2761 #endif
2762 
2763         RETURN_LONG((unsigned char) str[0]);
2764 }
2765 /* }}} */
2766 
2767 /* {{{ proto string chr(int ascii)
2768    Converts ASCII code to a character */
2769 PHP_FUNCTION(chr)
2770 {
2771         zend_long c;
2772 
2773         if (ZEND_NUM_ARGS() != 1) {
2774                 WRONG_PARAM_COUNT;
2775         }
2776 
2777 #ifndef FAST_ZPP
2778         if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "l", &c) == FAILURE) {
2779                 c = 0;
2780         }
2781 #else
2782         ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 1, 1)
2783                 Z_PARAM_LONG(c)
2784         ZEND_PARSE_PARAMETERS_END_EX(c = 0);
2785 #endif
2786 
2787         c &= 0xff;
2788         if (CG(one_char_string)[c]) {
2789                 ZVAL_INTERNED_STR(return_value, CG(one_char_string)[c]);
2790         } else {
2791                 ZVAL_NEW_STR(return_value, zend_string_alloc(1, 0));
2792                 Z_STRVAL_P(return_value)[0] = (char)c;
2793                 Z_STRVAL_P(return_value)[1] = '\0';
2794         }
2795 }
2796 /* }}} */
2797 
2798 /* {{{ php_ucfirst
2799    Uppercase the first character of the word in a native string */
2800 static void php_ucfirst(char *str)
2801 {
2802         register char *r;
2803         r = str;
2804         *r = toupper((unsigned char) *r);
2805 }
2806 /* }}} */
2807 
2808 /* {{{ proto string ucfirst(string str)
2809    Makes a string's first character uppercase */
2810 PHP_FUNCTION(ucfirst)
2811 {
2812         zend_string *str;
2813 
2814 #ifndef FAST_ZPP
2815         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) {
2816                 return;
2817         }
2818 #else
2819         ZEND_PARSE_PARAMETERS_START(1, 1)
2820                 Z_PARAM_STR(str)
2821         ZEND_PARSE_PARAMETERS_END();
2822 #endif
2823 
2824         if (!ZSTR_LEN(str)) {
2825                 RETURN_EMPTY_STRING();
2826         }
2827 
2828         ZVAL_STRINGL(return_value, ZSTR_VAL(str), ZSTR_LEN(str));
2829         php_ucfirst(Z_STRVAL_P(return_value));
2830 }
2831 /* }}} */
2832 
2833 /* {{{
2834    Lowercase the first character of the word in a native string */
2835 static void php_lcfirst(char *str)
2836 {
2837         register char *r;
2838         r = str;
2839         *r = tolower((unsigned char) *r);
2840 }
2841 /* }}} */
2842 
2843 /* {{{ proto string lcfirst(string str)
2844    Make a string's first character lowercase */
2845 PHP_FUNCTION(lcfirst)
2846 {
2847         zend_string  *str;
2848 
2849         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) {
2850                 return;
2851         }
2852 
2853         if (!ZSTR_LEN(str)) {
2854                 RETURN_EMPTY_STRING();
2855         }
2856 
2857         ZVAL_STRINGL(return_value, ZSTR_VAL(str), ZSTR_LEN(str));
2858         php_lcfirst(Z_STRVAL_P(return_value));
2859 }
2860 /* }}} */
2861 
2862 /* {{{ proto string ucwords(string str [, string delims])
2863    Uppercase the first character of every word in a string */
2864 PHP_FUNCTION(ucwords)
2865 {
2866         zend_string *str;
2867         char *delims = " \t\r\n\f\v";
2868         register char *r, *r_end;
2869         size_t delims_len = 6;
2870         char mask[256];
2871 
2872 #ifndef FAST_ZPP
2873         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|s", &str, &delims, &delims_len) == FAILURE) {
2874                 return;
2875         }
2876 #else
2877         ZEND_PARSE_PARAMETERS_START(1, 2)
2878                 Z_PARAM_STR(str)
2879                 Z_PARAM_OPTIONAL
2880                 Z_PARAM_STRING(delims, delims_len)
2881         ZEND_PARSE_PARAMETERS_END();
2882 #endif
2883 
2884         if (!ZSTR_LEN(str)) {
2885                 RETURN_EMPTY_STRING();
2886         }
2887 
2888         php_charmask((unsigned char *)delims, delims_len, mask);
2889 
2890         ZVAL_STRINGL(return_value, ZSTR_VAL(str), ZSTR_LEN(str));
2891         r = Z_STRVAL_P(return_value);
2892 
2893         *r = toupper((unsigned char) *r);
2894         for (r_end = r + Z_STRLEN_P(return_value) - 1; r < r_end; ) {
2895                 if (mask[(unsigned char)*r++]) {
2896                         *r = toupper((unsigned char) *r);
2897                 }
2898         }
2899 }
2900 /* }}} */
2901 
2902 /* {{{ php_strtr
2903  */
2904 PHPAPI char *php_strtr(char *str, size_t len, char *str_from, char *str_to, size_t trlen)
2905 {
2906         size_t i;
2907 
2908         if (UNEXPECTED(trlen < 1)) {
2909                 return str;
2910         } else if (trlen == 1) {
2911                 char ch_from = *str_from;
2912                 char ch_to = *str_to;
2913 
2914                 for (i = 0; i < len; i++) {
2915                         if (str[i] == ch_from) {
2916                                 str[i] = ch_to;
2917                         }
2918                 }
2919         } else {
2920                 unsigned char xlat[256], j = 0;
2921 
2922                 do { xlat[j] = j; } while (++j != 0);
2923 
2924                 for (i = 0; i < trlen; i++) {
2925                         xlat[(size_t)(unsigned char) str_from[i]] = str_to[i];
2926                 }
2927                 
2928                 for (i = 0; i < len; i++) {
2929                         str[i] = xlat[(size_t)(unsigned char) str[i]];
2930                 }
2931         }
2932 
2933         return str;
2934 }
2935 /* }}} */
2936 
2937 /* {{{ php_strtr_ex
2938  */
2939 static zend_string *php_strtr_ex(zend_string *str, char *str_from, char *str_to, size_t trlen)
2940 {
2941         zend_string *new_str = NULL;
2942         size_t i;
2943 
2944         if (UNEXPECTED(trlen < 1)) {
2945                 return zend_string_copy(str);
2946         } else if (trlen == 1) {
2947                 char ch_from = *str_from;
2948                 char ch_to = *str_to;
2949 
2950                 for (i = 0; i < ZSTR_LEN(str); i++) {
2951                         if (ZSTR_VAL(str)[i] == ch_from) {
2952                                 new_str = zend_string_alloc(ZSTR_LEN(str), 0);
2953                                 memcpy(ZSTR_VAL(new_str), ZSTR_VAL(str), i);
2954                                 ZSTR_VAL(new_str)[i] = ch_to;
2955                                 break;
2956                         }
2957                 }
2958                 for (; i < ZSTR_LEN(str); i++) {
2959                         ZSTR_VAL(new_str)[i] = (ZSTR_VAL(str)[i] != ch_from) ? ZSTR_VAL(str)[i] : ch_to;
2960                 }
2961         } else {
2962                 unsigned char xlat[256], j = 0;
2963 
2964                 do { xlat[j] = j; } while (++j != 0);
2965 
2966                 for (i = 0; i < trlen; i++) {
2967                         xlat[(size_t)(unsigned char) str_from[i]] = str_to[i];
2968                 }
2969 
2970                 for (i = 0; i < ZSTR_LEN(str); i++) {
2971                         if (ZSTR_VAL(str)[i] != xlat[(size_t)(unsigned char) ZSTR_VAL(str)[i]]) {
2972                                 new_str = zend_string_alloc(ZSTR_LEN(str), 0);
2973                                 memcpy(ZSTR_VAL(new_str), ZSTR_VAL(str), i);
2974                                 ZSTR_VAL(new_str)[i] = xlat[(size_t)(unsigned char) ZSTR_VAL(str)[i]];
2975                                 break;
2976                         }
2977                 }
2978 
2979                 for (;i < ZSTR_LEN(str); i++) {
2980                         ZSTR_VAL(new_str)[i] = xlat[(size_t)(unsigned char) ZSTR_VAL(str)[i]];
2981                 }
2982         }
2983 
2984         if (!new_str) {
2985                 return zend_string_copy(str);
2986         }
2987 
2988         ZSTR_VAL(new_str)[ZSTR_LEN(new_str)] = 0;
2989         return new_str;
2990 }
2991 /* }}} */
2992 
2993 /* {{{ php_strtr_array */
2994 static void php_strtr_array(zval *return_value, zend_string *input, HashTable *pats)
2995 {
2996         char *str = ZSTR_VAL(input);
2997         size_t slen = ZSTR_LEN(input);
2998         zend_ulong num_key;
2999         zend_string *str_key;
3000         size_t len, pos, old_pos;
3001         int num_keys = 0;
3002         size_t minlen = 128*1024;
3003         size_t maxlen = 0;
3004         HashTable str_hash;
3005         zval *entry;
3006         char *key;
3007         smart_str result = {0};
3008         zend_ulong bitset[256/sizeof(zend_ulong)];
3009         zend_ulong *num_bitset;
3010 
3011         /* we will collect all possible key lengths */
3012         num_bitset = ecalloc((slen + sizeof(zend_ulong)) / sizeof(zend_ulong), sizeof(zend_ulong));
3013         memset(bitset, 0, sizeof(bitset));
3014 
3015         /* check if original array has numeric keys */
3016         ZEND_HASH_FOREACH_STR_KEY(pats, str_key) {
3017                 if (UNEXPECTED(!str_key)) {
3018                         num_keys = 1;
3019                 } else {
3020                         len = ZSTR_LEN(str_key);
3021                         if (UNEXPECTED(len < 1)) {
3022                                 RETURN_FALSE;
3023                         } else if (UNEXPECTED(len > slen)) {
3024                                 /* skip long patterns */
3025                                 continue;
3026                         }
3027                         if (len > maxlen) {
3028                                 maxlen = len;
3029                         }
3030                         if (len < minlen) {
3031                                 minlen = len;
3032                         }
3033                         /* remember possible key length */
3034                         num_bitset[len / sizeof(zend_ulong)] |= Z_UL(1) << (len % sizeof(zend_ulong));
3035                         bitset[((unsigned char)ZSTR_VAL(str_key)[0]) / sizeof(zend_ulong)] |= Z_UL(1) << (((unsigned char)ZSTR_VAL(str_key)[0]) % sizeof(zend_ulong));
3036                 }
3037         } ZEND_HASH_FOREACH_END();
3038 
3039         if (UNEXPECTED(num_keys)) {
3040                 zend_string *key_used;
3041                 /* we have to rebuild HashTable with numeric keys */
3042                 zend_hash_init(&str_hash, zend_hash_num_elements(pats), NULL, NULL, 0);
3043                 ZEND_HASH_FOREACH_KEY_VAL(pats, num_key, str_key, entry) {
3044                         if (UNEXPECTED(!str_key)) {
3045                                 key_used = zend_long_to_str(num_key);
3046                                 len = ZSTR_LEN(key_used);
3047                                 if (UNEXPECTED(len > slen)) {
3048                                         /* skip long patterns */
3049                                         continue;
3050                                 }
3051                                 if (len > maxlen) {
3052                                         maxlen = len;
3053                                 }
3054                                 if (len < minlen) {
3055                                         minlen = len;
3056                                 }
3057                                 /* remember possible key length */
3058                                 num_bitset[len / sizeof(zend_ulong)] |= Z_UL(1) << (len % sizeof(zend_ulong));
3059                                 bitset[((unsigned char)ZSTR_VAL(key_used)[0]) / sizeof(zend_ulong)] |= Z_UL(1) << (((unsigned char)ZSTR_VAL(key_used)[0]) % sizeof(zend_ulong));
3060                         } else {
3061                                 key_used = str_key;
3062                                 len = ZSTR_LEN(key_used);
3063                                 if (UNEXPECTED(len > slen)) {
3064                                         /* skip long patterns */
3065                                         continue;
3066                                 }
3067                         }
3068                         zend_hash_add(&str_hash, key_used, entry);
3069                         if (UNEXPECTED(!str_key)) {
3070                                 zend_string_release(key_used);
3071                         }
3072                 } ZEND_HASH_FOREACH_END();
3073                 pats = &str_hash;
3074         }
3075 
3076         if (UNEXPECTED(minlen > maxlen)) {
3077                 /* return the original string */
3078                 if (pats == &str_hash) {
3079                         zend_hash_destroy(&str_hash);
3080                 }
3081                 efree(num_bitset);
3082                 RETURN_STR_COPY(input);
3083         }
3084 
3085         old_pos = pos = 0;
3086         while (pos <= slen - minlen) {
3087                 key = str + pos;
3088                 if (bitset[((unsigned char)key[0]) / sizeof(zend_ulong)] & (Z_UL(1) << (((unsigned char)key[0]) % sizeof(zend_ulong)))) {
3089                         len = maxlen;
3090                         if (len > slen - pos) {
3091                                 len = slen - pos;
3092                         }
3093                         while (len >= minlen) {
3094                                 if ((num_bitset[len / sizeof(zend_ulong)] & (Z_UL(1) << (len % sizeof(zend_ulong))))) {
3095                                         entry = zend_hash_str_find(pats, key, len);
3096                                         if (entry != NULL) {
3097                                                 zend_string *s = zval_get_string(entry);
3098                                                 smart_str_appendl(&result, str + old_pos, pos - old_pos);
3099                                                 smart_str_append(&result, s);
3100                                                 old_pos = pos + len;
3101                                                 pos = old_pos - 1;
3102                                                 zend_string_release(s);
3103                                                 break;
3104                                         }
3105                                 }
3106                                 len--;
3107                         }
3108                 }
3109                 pos++;
3110         }
3111 
3112         if (result.s) {
3113                 smart_str_appendl(&result, str + old_pos, slen - old_pos);
3114                 smart_str_0(&result);
3115                 RETVAL_NEW_STR(result.s);
3116         } else {
3117                 smart_str_free(&result);
3118                 RETVAL_STR_COPY(input);
3119         }
3120 
3121         if (pats == &str_hash) {
3122                 zend_hash_destroy(&str_hash);
3123         }
3124         efree(num_bitset);
3125 }
3126 /* }}} */
3127 
3128 /* {{{ php_char_to_str_ex
3129  */
3130 static zend_string* php_char_to_str_ex(zend_string *str, char from, char *to, size_t to_len, int case_sensitivity, zend_long *replace_count)
3131 {
3132         zend_string *result;
3133         size_t char_count = 0;
3134         char lc_from = 0;
3135         char *source, *target, *source_end= ZSTR_VAL(str) + ZSTR_LEN(str);
3136 
3137         if (case_sensitivity) {
3138                 char *p = ZSTR_VAL(str), *e = p + ZSTR_LEN(str);
3139                 while ((p = memchr(p, from, (e - p)))) {
3140                         char_count++;
3141                         p++;
3142                 }
3143         } else {
3144                 lc_from = tolower(from);
3145                 for (source = ZSTR_VAL(str); source < source_end; source++) {
3146                         if (tolower(*source) == lc_from) {
3147                                 char_count++;
3148                         }
3149                 }
3150         }
3151 
3152         if (char_count == 0) {
3153                 return zend_string_copy(str);
3154         }
3155 
3156         if (to_len > 0) {
3157                 result = zend_string_safe_alloc(char_count, to_len - 1, ZSTR_LEN(str), 0);
3158         } else {
3159                 result = zend_string_alloc(ZSTR_LEN(str) - char_count, 0);
3160         }
3161         target = ZSTR_VAL(result);
3162 
3163         if (case_sensitivity) {
3164                 char *p = ZSTR_VAL(str), *e = p + ZSTR_LEN(str), *s = ZSTR_VAL(str);
3165                 while ((p = memchr(p, from, (e - p)))) {
3166                         memcpy(target, s, (p - s));
3167                         target += p - s;
3168                         memcpy(target, to, to_len);
3169                         target += to_len;
3170                         p++;
3171                         s = p;
3172                         if (replace_count) {
3173                                 *replace_count += 1;
3174                         }
3175                 }
3176                 if (s < e) {
3177                         memcpy(target, s, (e - s));
3178                         target += e - s;
3179                 }
3180         } else {
3181                 for (source = ZSTR_VAL(str); source < source_end; source++) {
3182                         if (tolower(*source) == lc_from) {
3183                                 if (replace_count) {
3184                                         *replace_count += 1;
3185                                 }
3186                                 memcpy(target, to, to_len);
3187                                 target += to_len;
3188                         } else {
3189                                 *target = *source;
3190                                 target++;
3191                         }
3192                 }
3193         }
3194         *target = 0;
3195         return result;
3196 }
3197 /* }}} */
3198 
3199 /* {{{ php_str_to_str_ex
3200  */
3201 static zend_string *php_str_to_str_ex(zend_string *haystack,
3202         char *needle, size_t needle_len, char *str, size_t str_len, zend_long *replace_count)
3203 {
3204         zend_string *new_str;
3205 
3206         if (needle_len < ZSTR_LEN(haystack)) {
3207                 char *end;
3208                 char *e, *s, *p, *r;
3209 
3210                 if (needle_len == str_len) {
3211                         new_str = NULL;
3212                         end = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
3213                         for (p = ZSTR_VAL(haystack); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3214                                 if (!new_str) {
3215                                         new_str = zend_string_init(ZSTR_VAL(haystack), ZSTR_LEN(haystack), 0);
3216                                 }
3217                                 memcpy(ZSTR_VAL(new_str) + (r - ZSTR_VAL(haystack)), str, str_len);
3218                                 (*replace_count)++;
3219                         }
3220                         if (!new_str) {
3221                                 goto nothing_todo;
3222                         }
3223                         return new_str;
3224                 } else {
3225                         size_t count = 0;
3226                         char *o = ZSTR_VAL(haystack);
3227                         char *n = needle;
3228                         char *endp = o + ZSTR_LEN(haystack);
3229 
3230                         while ((o = (char*)php_memnstr(o, n, needle_len, endp))) {
3231                                 o += needle_len;
3232                                 count++;
3233                         }
3234                         if (count == 0) {
3235                                 /* Needle doesn't occur, shortcircuit the actual replacement. */
3236                                 goto nothing_todo;
3237                         }
3238                         if (str_len > needle_len) {
3239                                 new_str = zend_string_safe_alloc(count, str_len - needle_len, ZSTR_LEN(haystack), 0);
3240                         } else {
3241                                 new_str = zend_string_alloc(count * (str_len - needle_len) + ZSTR_LEN(haystack), 0);
3242                         }
3243 
3244                         e = s = ZSTR_VAL(new_str);
3245                         end = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
3246                         for (p = ZSTR_VAL(haystack); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3247                                 memcpy(e, p, r - p);
3248                                 e += r - p;
3249                                 memcpy(e, str, str_len);
3250                                 e += str_len;
3251                                 (*replace_count)++;
3252                         }
3253 
3254                         if (p < end) {
3255                                 memcpy(e, p, end - p);
3256                                 e += end - p;
3257                         }
3258 
3259                         *e = '\0';
3260                         return new_str;
3261                 }
3262         } else if (needle_len > ZSTR_LEN(haystack) || memcmp(ZSTR_VAL(haystack), needle, ZSTR_LEN(haystack))) {
3263 nothing_todo:
3264                 return zend_string_copy(haystack);
3265         } else {
3266                 new_str = zend_string_init(str, str_len, 0);
3267                 (*replace_count)++;
3268                 return new_str;
3269         }
3270 }
3271 /* }}} */
3272 
3273 /* {{{ php_str_to_str_i_ex
3274  */
3275 static zend_string *php_str_to_str_i_ex(zend_string *haystack, char *lc_haystack,
3276         zend_string *needle, char *str, size_t str_len, zend_long *replace_count)
3277 {
3278         zend_string *new_str = NULL;
3279         zend_string *lc_needle;
3280 
3281         if (ZSTR_LEN(needle) < ZSTR_LEN(haystack)) {
3282                 char *end;
3283                 char *e, *s, *p, *r;
3284 
3285                 if (ZSTR_LEN(needle) == str_len) {
3286                         lc_needle = php_string_tolower(needle);
3287                         end = lc_haystack + ZSTR_LEN(haystack);
3288                         for (p = lc_haystack; (r = (char*)php_memnstr(p, ZSTR_VAL(lc_needle), ZSTR_LEN(lc_needle), end)); p = r + ZSTR_LEN(lc_needle)) {
3289                                 if (!new_str) {
3290                                         new_str = zend_string_init(ZSTR_VAL(haystack), ZSTR_LEN(haystack), 0);
3291                                 }
3292                                 memcpy(ZSTR_VAL(new_str) + (r - lc_haystack), str, str_len);
3293                                 (*replace_count)++;
3294                         }
3295                         zend_string_release(lc_needle);
3296 
3297                         if (!new_str) {
3298                                 goto nothing_todo;
3299                         }
3300                         return new_str;
3301                 } else {
3302                         size_t count = 0;
3303                         char *o = lc_haystack;
3304                         char *n;
3305                         char *endp = o + ZSTR_LEN(haystack);
3306 
3307                         lc_needle = php_string_tolower(needle);
3308                         n = ZSTR_VAL(lc_needle);
3309 
3310                         while ((o = (char*)php_memnstr(o, n, ZSTR_LEN(lc_needle), endp))) {
3311                                 o += ZSTR_LEN(lc_needle);
3312                                 count++;
3313                         }
3314                         if (count == 0) {
3315                                 /* Needle doesn't occur, shortcircuit the actual replacement. */
3316                                 zend_string_release(lc_needle);
3317                                 goto nothing_todo;
3318                         }
3319                         
3320                         if (str_len > ZSTR_LEN(lc_needle)) {
3321                                 new_str = zend_string_safe_alloc(count, str_len - ZSTR_LEN(lc_needle), ZSTR_LEN(haystack), 0);
3322                         } else {
3323                                 new_str = zend_string_alloc(count * (str_len - ZSTR_LEN(lc_needle)) + ZSTR_LEN(haystack), 0);
3324                         }
3325 
3326                         e = s = ZSTR_VAL(new_str);
3327                         end = lc_haystack + ZSTR_LEN(haystack);
3328 
3329                         for (p = lc_haystack; (r = (char*)php_memnstr(p, ZSTR_VAL(lc_needle), ZSTR_LEN(lc_needle), end)); p = r + ZSTR_LEN(lc_needle)) {
3330                                 memcpy(e, ZSTR_VAL(haystack) + (p - lc_haystack), r - p);
3331                                 e += r - p;
3332                                 memcpy(e, str, str_len);
3333                                 e += str_len;
3334                                 (*replace_count)++;
3335                         }
3336 
3337                         if (p < end) {
3338                                 memcpy(e, ZSTR_VAL(haystack) + (p - lc_haystack), end - p);
3339                                 e += end - p;
3340                         }
3341                         *e = '\0';
3342 
3343                         zend_string_release(lc_needle);
3344 
3345                         return new_str;
3346                 }
3347         } else if (ZSTR_LEN(needle) > ZSTR_LEN(haystack)) {
3348 nothing_todo:
3349                 return zend_string_copy(haystack);
3350         } else {
3351                 lc_needle = php_string_tolower(needle);
3352 
3353                 if (memcmp(lc_haystack, ZSTR_VAL(lc_needle), ZSTR_LEN(lc_needle))) {
3354                         zend_string_release(lc_needle);
3355                         goto nothing_todo;
3356                 }
3357                 zend_string_release(lc_needle);
3358 
3359                 new_str = zend_string_init(str, str_len, 0);
3360 
3361                 (*replace_count)++;
3362                 return new_str;
3363         }
3364 }
3365 /* }}} */
3366 
3367 /* {{{ php_str_to_str
3368  */
3369 PHPAPI zend_string *php_str_to_str(char *haystack, size_t length, char *needle, size_t needle_len, char *str, size_t str_len)
3370 {
3371         zend_string *new_str;
3372 
3373         if (needle_len < length) {
3374                 char *end;
3375                 char *e, *s, *p, *r;
3376 
3377                 if (needle_len == str_len) {
3378                         new_str = zend_string_init(haystack, length, 0);
3379                         end = ZSTR_VAL(new_str) + length;
3380                         for (p = ZSTR_VAL(new_str); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3381                                 memcpy(r, str, str_len);
3382                         }
3383                         return new_str;
3384                 } else {
3385                         if (str_len < needle_len) {
3386                                 new_str = zend_string_alloc(length, 0);
3387                         } else {
3388                                 size_t count = 0;
3389                                 char *o = haystack;
3390                                 char *n = needle;
3391                                 char *endp = o + length;
3392 
3393                                 while ((o = (char*)php_memnstr(o, n, needle_len, endp))) {
3394                                         o += needle_len;
3395                                         count++;
3396                                 }
3397                                 if (count == 0) {
3398                                         /* Needle doesn't occur, shortcircuit the actual replacement. */
3399                                         new_str = zend_string_init(haystack, length, 0);
3400                                         return new_str;
3401                                 } else {
3402                                         if (str_len > needle_len) {
3403                                                 new_str = zend_string_safe_alloc(count, str_len - needle_len, length, 0);
3404                                         } else {
3405                                                 new_str = zend_string_alloc(count * (str_len - needle_len) + length, 0);
3406                                         }
3407                                 }
3408                         }
3409 
3410                         e = s = ZSTR_VAL(new_str);
3411                         end = haystack + length;
3412                         for (p = haystack; (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3413                                 memcpy(e, p, r - p);
3414                                 e += r - p;
3415                                 memcpy(e, str, str_len);
3416                                 e += str_len;
3417                         }
3418 
3419                         if (p < end) {
3420                                 memcpy(e, p, end - p);
3421                                 e += end - p;
3422                         }
3423 
3424                         *e = '\0';
3425                         new_str = zend_string_truncate(new_str, e - s, 0);
3426                         return new_str;
3427                 }
3428         } else if (needle_len > length || memcmp(haystack, needle, length)) {
3429                 new_str = zend_string_init(haystack, length, 0);
3430                 return new_str;
3431         } else {
3432                 new_str = zend_string_init(str, str_len, 0);
3433 
3434                 return new_str;
3435         }
3436 }
3437 /* }}} */
3438 
3439 /* {{{ proto string strtr(string str, string from[, string to])
3440    Translates characters in str using given translation tables */
3441 PHP_FUNCTION(strtr)
3442 {
3443         zval *from;
3444         zend_string *str;
3445         char *to = NULL;
3446         size_t to_len = 0;
3447         int ac = ZEND_NUM_ARGS();
3448 
3449 #ifndef FAST_ZPP
3450         if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz|s", &str, &from, &to, &to_len) == FAILURE) {
3451                 return;
3452         }
3453 #else
3454         ZEND_PARSE_PARAMETERS_START(2, 3)
3455                 Z_PARAM_STR(str)
3456                 Z_PARAM_ZVAL(from)
3457                 Z_PARAM_OPTIONAL
3458                 Z_PARAM_STRING(to, to_len)
3459         ZEND_PARSE_PARAMETERS_END();
3460 #endif
3461 
3462         if (ac == 2 && Z_TYPE_P(from) != IS_ARRAY) {
3463                 php_error_docref(NULL, E_WARNING, "The second argument is not an array");
3464                 RETURN_FALSE;
3465         }
3466 
3467         /* shortcut for empty string */
3468         if (ZSTR_LEN(str) == 0) {
3469                 RETURN_EMPTY_STRING();
3470         }
3471 
3472         if (ac == 2) {
3473                 HashTable *pats = Z_ARRVAL_P(from);
3474 
3475                 if (zend_hash_num_elements(pats) < 1) {
3476                         RETURN_STR_COPY(str);
3477                 } else if (zend_hash_num_elements(pats) == 1) {
3478                         zend_long num_key;
3479                         zend_string *str_key, *replace;
3480                         zval *entry, tmp;
3481 
3482                         ZEND_HASH_FOREACH_KEY_VAL(pats, num_key, str_key, entry) {
3483                                 ZVAL_UNDEF(&tmp);
3484                                 if (UNEXPECTED(!str_key)) {
3485                                         ZVAL_LONG(&tmp, num_key);
3486                                         convert_to_string(&tmp);
3487                                         str_key = Z_STR(tmp);
3488                                 }               
3489                                 replace = zval_get_string(entry);
3490                                 if (ZSTR_LEN(str_key) < 1) {
3491                                         RETVAL_STR_COPY(str);
3492                                 } else if (ZSTR_LEN(str_key) == 1) {
3493                                         RETVAL_STR(php_char_to_str_ex(str,
3494                                                                 ZSTR_VAL(str_key)[0],
3495                                                                 ZSTR_VAL(replace),
3496                                                                 ZSTR_LEN(replace),
3497                                                                 1,
3498                                                                 NULL));
3499                                 } else {
3500                                         zend_long dummy;
3501                                         RETVAL_STR(php_str_to_str_ex(str,
3502                                                                 ZSTR_VAL(str_key), ZSTR_LEN(str_key),
3503                                                                 ZSTR_VAL(replace), ZSTR_LEN(replace), &dummy));
3504                                 }
3505                                 zend_string_release(replace);
3506                                 zval_dtor(&tmp);
3507                                 return;
3508                         } ZEND_HASH_FOREACH_END();
3509                 } else {
3510                         php_strtr_array(return_value, str, pats);
3511                 }
3512         } else {
3513                 convert_to_string_ex(from);
3514 
3515                 RETURN_STR(php_strtr_ex(str,
3516                                   Z_STRVAL_P(from),
3517                                   to,
3518                                   MIN(Z_STRLEN_P(from), to_len)));
3519         }
3520 }
3521 /* }}} */
3522 
3523 /* {{{ proto string strrev(string str)
3524    Reverse a string */
3525 PHP_FUNCTION(strrev)
3526 {
3527         zend_string *str;
3528         char *e, *p;
3529         zend_string *n;
3530 
3531         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) {
3532                 return;
3533         }
3534 
3535         n = zend_string_alloc(ZSTR_LEN(str), 0);
3536         p = ZSTR_VAL(n);
3537 
3538         e = ZSTR_VAL(str) + ZSTR_LEN(str);
3539 
3540         while (--e >= ZSTR_VAL(str)) {
3541                 *p++ = *e;
3542         }
3543 
3544         *p = '\0';
3545 
3546         RETVAL_NEW_STR(n);
3547 }
3548 /* }}} */
3549 
3550 /* {{{ php_similar_str
3551  */
3552 static void php_similar_str(const char *txt1, size_t len1, const char *txt2, size_t len2, size_t *pos1, size_t *pos2, size_t *max)
3553 {
3554         char *p, *q;
3555         char *end1 = (char *) txt1 + len1;
3556         char *end2 = (char *) txt2 + len2;
3557         size_t l;
3558 
3559         *max = 0;
3560         for (p = (char *) txt1; p < end1; p++) {
3561                 for (q = (char *) txt2; q < end2; q++) {
3562                         for (l = 0; (p + l < end1) && (q + l < end2) && (p[l] == q[l]); l++);
3563                         if (l > *max) {
3564                                 *max = l;
3565                                 *pos1 = p - txt1;
3566                                 *pos2 = q - txt2;
3567                         }
3568                 }
3569         }
3570 }
3571 /* }}} */
3572 
3573 /* {{{ php_similar_char
3574  */
3575 static size_t php_similar_char(const char *txt1, size_t len1, const char *txt2, size_t len2)
3576 {
3577         size_t sum;
3578         size_t pos1 = 0, pos2 = 0, max;
3579 
3580         php_similar_str(txt1, len1, txt2, len2, &pos1, &pos2, &max);
3581         if ((sum = max)) {
3582                 if (pos1 && pos2) {
3583                         sum += php_similar_char(txt1, pos1,
3584                                                                         txt2, pos2);
3585                 }
3586                 if ((pos1 + max < len1) && (pos2 + max < len2)) {
3587                         sum += php_similar_char(txt1 + pos1 + max, len1 - pos1 - max,
3588                                                                         txt2 + pos2 + max, len2 - pos2 - max);
3589                 }
3590         }
3591 
3592         return sum;
3593 }
3594 /* }}} */
3595 
3596 /* {{{ proto int similar_text(string str1, string str2 [, float percent])
3597    Calculates the similarity between two strings */
3598 PHP_FUNCTION(similar_text)
3599 {
3600         zend_string *t1, *t2;
3601         zval *percent = NULL;
3602         int ac = ZEND_NUM_ARGS();
3603         size_t sim;
3604 
3605         if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|z/", &t1, &t2, &percent) == FAILURE) {
3606                 return;
3607         }
3608 
3609         if (ac > 2) {
3610                 convert_to_double_ex(percent);
3611         }
3612 
3613         if (ZSTR_LEN(t1) + ZSTR_LEN(t2) == 0) {
3614                 if (ac > 2) {
3615                         Z_DVAL_P(percent) = 0;
3616                 }
3617 
3618                 RETURN_LONG(0);
3619         }
3620 
3621         sim = php_similar_char(ZSTR_VAL(t1), ZSTR_LEN(t1), ZSTR_VAL(t2), ZSTR_LEN(t2));
3622 
3623         if (ac > 2) {
3624                 Z_DVAL_P(percent) = sim * 200.0 / (ZSTR_LEN(t1) + ZSTR_LEN(t2));
3625         }
3626 
3627         RETURN_LONG(sim);
3628 }
3629 /* }}} */
3630 
3631 /* {{{ php_stripslashes
3632  *
3633  * be careful, this edits the string in-place */
3634 PHPAPI void php_stripslashes(zend_string *str)
3635 {
3636         char *s, *t;
3637         size_t l;
3638 
3639         s = ZSTR_VAL(str);
3640         t = ZSTR_VAL(str);
3641         l = ZSTR_LEN(str);
3642 
3643         while (l > 0) {
3644                 if (*t == '\\') {
3645                         t++;                            /* skip the slash */
3646                         ZSTR_LEN(str)--;
3647                         l--;
3648                         if (l > 0) {
3649                                 if (*t == '0') {
3650                                         *s++='\0';
3651                                         t++;
3652                                 } else {
3653                                         *s++ = *t++;    /* preserve the next character */
3654                                 }
3655                                 l--;
3656                         }
3657                 } else {
3658                         *s++ = *t++;
3659                         l--;
3660                 }
3661         }
3662         if (s != t) {
3663                 *s = '\0';
3664         }
3665 }
3666 /* }}} */
3667 
3668 /* {{{ proto string addcslashes(string str, string charlist)
3669    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...) */
3670 PHP_FUNCTION(addcslashes)
3671 {
3672         zend_string *str, *what;
3673 
3674         if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &str, &what) == FAILURE) {
3675                 return;
3676         }
3677 
3678         if (ZSTR_LEN(str) == 0) {
3679                 RETURN_EMPTY_STRING();
3680         }
3681 
3682         if (ZSTR_LEN(what) == 0) {
3683                 RETURN_STRINGL(ZSTR_VAL(str), ZSTR_LEN(str));
3684         }
3685 
3686         RETURN_STR(php_addcslashes(str, 0, ZSTR_VAL(what), ZSTR_LEN(what)));
3687 }
3688 /* }}} */
3689 
3690 /* {{{ proto string addslashes(string str)
3691    Escapes single quote, double quotes and backslash characters in a string with backslashes */
3692 PHP_FUNCTION(addslashes)
3693 {
3694         zend_string *str;
3695 
3696 #ifndef FAST_ZPP
3697         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) {
3698                 return;
3699         }
3700 #else
3701         ZEND_PARSE_PARAMETERS_START(1, 1)
3702                 Z_PARAM_STR(str)
3703         ZEND_PARSE_PARAMETERS_END();
3704 #endif
3705 
3706         if (ZSTR_LEN(str) == 0) {
3707                 RETURN_EMPTY_STRING();
3708         }
3709 
3710         RETURN_STR(php_addslashes(str, 0));
3711 }
3712 /* }}} */
3713 
3714 /* {{{ proto string stripcslashes(string str)
3715    Strips backslashes from a string. Uses C-style conventions */
3716 PHP_FUNCTION(stripcslashes)
3717 {
3718         zend_string *str;
3719 
3720         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) {
3721                 return;
3722         }
3723 
3724         ZVAL_STRINGL(return_value, ZSTR_VAL(str), ZSTR_LEN(str));
3725         php_stripcslashes(Z_STR_P(return_value));
3726 }
3727 /* }}} */
3728 
3729 /* {{{ proto string stripslashes(string str)
3730    Strips backslashes from a string */
3731 PHP_FUNCTION(stripslashes)
3732 {
3733         zend_string *str;
3734 
3735         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) {
3736                 return;
3737         }
3738 
3739         ZVAL_STRINGL(return_value, ZSTR_VAL(str), ZSTR_LEN(str));
3740         php_stripslashes(Z_STR_P(return_value));
3741 }
3742 /* }}} */
3743 
3744 #ifndef HAVE_STRERROR
3745 /* {{{ php_strerror
3746  */
3747 char *php_strerror(int errnum)
3748 {
3749         extern int sys_nerr;
3750         extern char *sys_errlist[];
3751 
3752         if ((unsigned int) errnum < sys_nerr) {
3753                 return(sys_errlist[errnum]);
3754         }
3755 
3756         (void) snprintf(BG(str_ebuf), sizeof(php_basic_globals.str_ebuf), "Unknown error: %d", errnum);
3757         return(BG(str_ebuf));
3758 }
3759 /* }}} */
3760 #endif
3761 
3762 /* {{{ php_stripcslashes
3763  */
3764 PHPAPI void php_stripcslashes(zend_string *str)
3765 {
3766         char *source, *target, *end;
3767         size_t  nlen = ZSTR_LEN(str), i;
3768         char numtmp[4];
3769 
3770         for (source = (char*)ZSTR_VAL(str), end = source + ZSTR_LEN(str), target = ZSTR_VAL(str); source < end; source++) {
3771                 if (*source == '\\' && source + 1 < end) {
3772                         source++;
3773                         switch (*source) {
3774                                 case 'n':  *target++='\n'; nlen--; break;
3775                                 case 'r':  *target++='\r'; nlen--; break;
3776                                 case 'a':  *target++='\a'; nlen--; break;
3777                                 case 't':  *target++='\t'; nlen--; break;
3778                                 case 'v':  *target++='\v'; nlen--; break;
3779                                 case 'b':  *target++='\b'; nlen--; break;
3780                                 case 'f':  *target++='\f'; nlen--; break;
3781                                 case '\\': *target++='\\'; nlen--; break;
3782                                 case 'x':
3783                                         if (source+1 < end && isxdigit((int)(*(source+1)))) {
3784                                                 numtmp[0] = *++source;
3785                                                 if (source+1 < end && isxdigit((int)(*(source+1)))) {
3786                                                         numtmp[1] = *++source;
3787                                                         numtmp[2] = '\0';
3788                                                         nlen-=3;
3789                                                 } else {
3790                                                         numtmp[1] = '\0';
3791                                                         nlen-=2;
3792                                                 }
3793                                                 *target++=(char)strtol(numtmp, NULL, 16);
3794                                                 break;
3795                                         }
3796                                         /* break is left intentionally */
3797                                 default:
3798                                         i=0;
3799                                         while (source < end && *source >= '0' && *source <= '7' && i<3) {
3800                                                 numtmp[i++] = *source++;
3801                                         }
3802                                         if (i) {
3803                                                 numtmp[i]='\0';
3804                                                 *target++=(char)strtol(numtmp, NULL, 8);
3805                                                 nlen-=i;
3806                                                 source--;
3807                                         } else {
3808                                                 *target++=*source;
3809                                                 nlen--;
3810                                         }
3811                         }
3812                 } else {
3813                         *target++=*source;
3814                 }
3815         }
3816 
3817         if (nlen != 0) {
3818                 *target='\0';
3819         }
3820 
3821         ZSTR_LEN(str) = nlen;
3822 }
3823 /* }}} */
3824 
3825 /* {{{ php_addcslashes
3826  */
3827 PHPAPI zend_string *php_addcslashes(zend_string *str, int should_free, char *what, size_t wlength)
3828 {
3829         char flags[256];
3830         char *source, *target;
3831         char *end;
3832         char c;
3833         size_t  newlen;
3834         zend_string *new_str = zend_string_safe_alloc(4, ZSTR_LEN(str), 0, 0);
3835 
3836         php_charmask((unsigned char *)what, wlength, flags);
3837 
3838         for (source = (char*)ZSTR_VAL(str), end = source + ZSTR_LEN(str), target = ZSTR_VAL(new_str); source < end; source++) {
3839                 c = *source;
3840                 if (flags[(unsigned char)c]) {
3841                         if ((unsigned char) c < 32 || (unsigned char) c > 126) {
3842                                 *target++ = '\\';
3843                                 switch (c) {
3844                                         case '\n': *target++ = 'n'; break;
3845                                         case '\t': *target++ = 't'; break;
3846                                         case '\r': *target++ = 'r'; break;
3847                                         case '\a': *target++ = 'a'; break;
3848                                         case '\v': *target++ = 'v'; break;
3849                                         case '\b': *target++ = 'b'; break;
3850                                         case '\f': *target++ = 'f'; break;
3851                                         default: target += sprintf(target, "%03o", (unsigned char) c);
3852                                 }
3853                                 continue;
3854                         }
3855                         *target++ = '\\';
3856                 }
3857                 *target++ = c;
3858         }
3859         *target = 0;
3860         newlen = target - ZSTR_VAL(new_str);
3861         if (newlen < ZSTR_LEN(str) * 4) {
3862                 new_str = zend_string_truncate(new_str, newlen, 0);
3863         }
3864         if (should_free) {
3865                 zend_string_release(str);
3866         }
3867         return new_str;
3868 }
3869 /* }}} */
3870 
3871 /* {{{ php_addslashes
3872  */
3873 PHPAPI zend_string *php_addslashes(zend_string *str, int should_free)
3874 {
3875         /* maximum string length, worst case situation */
3876         char *source, *target;
3877         char *end;
3878         size_t offset;
3879         zend_string *new_str;
3880 
3881         if (!str) {
3882                 return ZSTR_EMPTY_ALLOC();
3883         }
3884 
3885         source = ZSTR_VAL(str);
3886         end = source + ZSTR_LEN(str);
3887 
3888         while (source < end) {
3889                 switch (*source) {
3890                         case '\0':
3891                         case '\'':
3892                         case '\"':
3893                         case '\\':
3894                                 goto do_escape;
3895                         default:
3896                                 source++;
3897                                 break;
3898                 }
3899         }
3900 
3901         if (!should_free) {
3902                 return zend_string_copy(str);
3903         }
3904 
3905         return str;
3906 
3907 do_escape:
3908         offset = source - (char *)ZSTR_VAL(str);
3909         new_str = zend_string_safe_alloc(2, ZSTR_LEN(str) - offset, offset, 0);
3910         memcpy(ZSTR_VAL(new_str), ZSTR_VAL(str), offset);
3911         target = ZSTR_VAL(new_str) + offset;
3912 
3913         while (source < end) {
3914                 switch (*source) {
3915                         case '\0':
3916                                 *target++ = '\\';
3917                                 *target++ = '0';
3918                                 break;
3919                         case '\'':
3920                         case '\"':
3921                         case '\\':
3922                                 *target++ = '\\';
3923                                 /* break is missing *intentionally* */
3924                         default:
3925                                 *target++ = *source;
3926                                 break;
3927                 }
3928 
3929                 source++;
3930         }
3931 
3932         *target = 0;
3933         if (should_free) {
3934                 zend_string_release(str);
3935         }
3936 
3937         if (ZSTR_LEN(new_str) - (target - ZSTR_VAL(new_str)) > 16) {
3938                 new_str = zend_string_truncate(new_str, target - ZSTR_VAL(new_str), 0);
3939         } else {
3940                 ZSTR_LEN(new_str) = target - ZSTR_VAL(new_str);
3941         }
3942 
3943         return new_str;
3944 }
3945 /* }}} */
3946 
3947 #define _HEB_BLOCK_TYPE_ENG 1
3948 #define _HEB_BLOCK_TYPE_HEB 2
3949 #define isheb(c)      (((((unsigned char) c) >= 224) && (((unsigned char) c) <= 250)) ? 1 : 0)
3950 #define _isblank(c)   (((((unsigned char) c) == ' '  || ((unsigned char) c) == '\t')) ? 1 : 0)
3951 #define _isnewline(c) (((((unsigned char) c) == '\n' || ((unsigned char) c) == '\r')) ? 1 : 0)
3952 
3953 /* {{{ php_str_replace_in_subject
3954  */
3955 static zend_long php_str_replace_in_subject(zval *search, zval *replace, zval *subject, zval *result, int case_sensitivity)
3956 {
3957         zval            *search_entry,
3958                                 *replace_entry = NULL;
3959         zend_string     *tmp_result,
3960                                 *replace_entry_str = NULL;
3961         char            *replace_value = NULL;
3962         size_t           replace_len = 0;
3963         zend_long        replace_count = 0;
3964         zend_string     *subject_str;
3965         zend_string *lc_subject_str = NULL;
3966         uint32_t     replace_idx;
3967 
3968         /* Make sure we're dealing with strings. */
3969         subject_str = zval_get_string(subject);
3970         if (ZSTR_LEN(subject_str) == 0) {
3971                 zend_string_release(subject_str);
3972                 ZVAL_EMPTY_STRING(result);
3973                 return 0;
3974         }
3975 
3976         /* If search is an array */
3977         if (Z_TYPE_P(search) == IS_ARRAY) {
3978                 /* Duplicate subject string for repeated replacement */
3979                 ZVAL_STR_COPY(result, subject_str);
3980 
3981                 if (Z_TYPE_P(replace) == IS_ARRAY) {
3982                         replace_idx = 0;
3983                 } else {
3984                         /* Set replacement value to the passed one */
3985                         replace_value = Z_STRVAL_P(replace);
3986                         replace_len = Z_STRLEN_P(replace);
3987                 }
3988 
3989                 /* For each entry in the search array, get the entry */
3990                 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(search), search_entry) {
3991                         /* Make sure we're dealing with strings. */
3992                         zend_string *search_str = zval_get_string(search_entry);
3993                         if (ZSTR_LEN(search_str) == 0) {
3994                                 if (Z_TYPE_P(replace) == IS_ARRAY) {
3995                                         replace_idx++;
3996                                 }
3997                                 zend_string_release(search_str);
3998                                 continue;
3999                         }
4000 
4001                         /* If replace is an array. */
4002                         if (Z_TYPE_P(replace) == IS_ARRAY) {
4003                                 /* Get current entry */
4004                                 while (replace_idx < Z_ARRVAL_P(replace)->nNumUsed) {
4005                                         replace_entry = &Z_ARRVAL_P(replace)->arData[replace_idx].val;
4006                                         if (Z_TYPE_P(replace_entry) != IS_UNDEF) {
4007                                                 break;
4008                                         }
4009                                         replace_idx++;
4010                                 }
4011                                 if (replace_idx < Z_ARRVAL_P(replace)->nNumUsed) {
4012                                         /* Make sure we're dealing with strings. */
4013                                         replace_entry_str = zval_get_string(replace_entry);
4014 
4015                                         /* Set replacement value to the one we got from array */
4016                                         replace_value = ZSTR_VAL(replace_entry_str);
4017                                         replace_len = ZSTR_LEN(replace_entry_str);
4018 
4019                                         replace_idx++;
4020                                 } else {
4021                                         /* We've run out of replacement strings, so use an empty one. */
4022                                         replace_value = "";
4023                                         replace_len = 0;
4024                                 }
4025                         }
4026 
4027                         if (ZSTR_LEN(search_str) == 1) {
4028                                 zend_long old_replace_count = replace_count;
4029 
4030                                 tmp_result = php_char_to_str_ex(Z_STR_P(result),
4031                                                                 ZSTR_VAL(search_str)[0],
4032                                                                 replace_value,
4033                                                                 replace_len,
4034                                                                 case_sensitivity,
4035                                                                 &replace_count);
4036                                 if (lc_subject_str && replace_count != old_replace_count) {
4037                                         zend_string_release(lc_subject_str);
4038                                         lc_subject_str = NULL;
4039                                 }
4040                         } else if (ZSTR_LEN(search_str) > 1) {
4041                                 if (case_sensitivity) {
4042                                         tmp_result = php_str_to_str_ex(Z_STR_P(result),
4043                                                         ZSTR_VAL(search_str), ZSTR_LEN(search_str),
4044                                                         replace_value, replace_len, &replace_count);
4045                                 } else {
4046                                         zend_long old_replace_count = replace_count;
4047 
4048                                         if (!lc_subject_str) {
4049                                                 lc_subject_str = php_string_tolower(Z_STR_P(result));
4050                                         }
4051                                         tmp_result = php_str_to_str_i_ex(Z_STR_P(result), ZSTR_VAL(lc_subject_str),
4052                                                         search_str, replace_value, replace_len, &replace_count);
4053                                         if (replace_count != old_replace_count) {
4054                                                 zend_string_release(lc_subject_str);
4055                                                 lc_subject_str = NULL;
4056                                         }
4057                                 }                               
4058                         }
4059 
4060                         zend_string_release(search_str);
4061 
4062                         if (replace_entry_str) {
4063                                 zend_string_release(replace_entry_str);
4064                                 replace_entry_str = NULL;
4065                         }
4066                         zend_string_release(Z_STR_P(result));
4067                         ZVAL_STR(result, tmp_result);
4068 
4069                         if (Z_STRLEN_P(result) == 0) {
4070                                 if (lc_subject_str) {
4071                                         zend_string_release(lc_subject_str);
4072                                 }
4073                                 zend_string_release(subject_str);
4074                                 return replace_count;
4075                         }
4076                 } ZEND_HASH_FOREACH_END();
4077                 if (lc_subject_str) {
4078                         zend_string_release(lc_subject_str);
4079                 }
4080         } else {
4081                 ZEND_ASSERT(Z_TYPE_P(search) == IS_STRING);
4082                 if (Z_STRLEN_P(search) == 1) {
4083                         ZVAL_STR(result,
4084                                 php_char_to_str_ex(subject_str,
4085                                                         Z_STRVAL_P(search)[0],
4086                                                         Z_STRVAL_P(replace),
4087                                                         Z_STRLEN_P(replace),
4088                                                         case_sensitivity,
4089                                                         &replace_count));
4090                 } else if (Z_STRLEN_P(search) > 1) {
4091                         if (case_sensitivity) {
4092                                 ZVAL_STR(result, php_str_to_str_ex(subject_str,
4093                                                 Z_STRVAL_P(search), Z_STRLEN_P(search),
4094                                                 Z_STRVAL_P(replace), Z_STRLEN_P(replace), &replace_count));
4095                         } else {
4096                                 lc_subject_str = php_string_tolower(subject_str);
4097                                 ZVAL_STR(result, php_str_to_str_i_ex(subject_str, ZSTR_VAL(lc_subject_str),
4098                                                 Z_STR_P(search),
4099                                                 Z_STRVAL_P(replace), Z_STRLEN_P(replace), &replace_count));
4100                                 zend_string_release(lc_subject_str);
4101                         }
4102                 } else {
4103                         ZVAL_STR_COPY(result, subject_str);
4104                 }
4105         }
4106         zend_string_release(subject_str);
4107         return replace_count;
4108 }
4109 /* }}} */
4110 
4111 /* {{{ php_str_replace_common
4112  */
4113 static void php_str_replace_common(INTERNAL_FUNCTION_PARAMETERS, int case_sensitivity)
4114 {
4115         zval *subject, *search, *replace, *subject_entry, *zcount = NULL;
4116         zval result;
4117         zend_string *string_key;
4118         zend_ulong num_key;
4119         zend_long count = 0;
4120         int argc = ZEND_NUM_ARGS();
4121 
4122 #ifndef FAST_ZPP
4123         if (zend_parse_parameters(ZEND_NUM_ARGS(), "zzz|z/", &search, &replace, &subject, &zcount) == FAILURE) {
4124                 return;
4125         }
4126 #else
4127         ZEND_PARSE_PARAMETERS_START(3, 4)
4128                 Z_PARAM_ZVAL(search)
4129                 Z_PARAM_ZVAL(replace)
4130                 Z_PARAM_ZVAL(subject)
4131                 Z_PARAM_OPTIONAL
4132                 Z_PARAM_ZVAL_EX(zcount, 0, 1)
4133         ZEND_PARSE_PARAMETERS_END();
4134 #endif
4135 
4136         /* Make sure we're dealing with strings and do the replacement. */
4137         if (Z_TYPE_P(search) != IS_ARRAY) {
4138                 convert_to_string_ex(search);
4139                 if (Z_TYPE_P(replace) != IS_STRING) {
4140                         convert_to_string_ex(replace);
4141                 }
4142         } else if (Z_TYPE_P(replace) != IS_ARRAY) {
4143                 convert_to_string_ex(replace);
4144         }
4145 
4146         /* if subject is an array */
4147         if (Z_TYPE_P(subject) == IS_ARRAY) {
4148                 array_init(return_value);
4149 
4150                 /* For each subject entry, convert it to string, then perform replacement
4151                    and add the result to the return_value array. */
4152                 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(subject), num_key, string_key, subject_entry) {
4153                         if (Z_TYPE_P(subject_entry) != IS_ARRAY && Z_TYPE_P(subject_entry) != IS_OBJECT) {
4154                                 count += php_str_replace_in_subject(search, replace, subject_entry, &result, case_sensitivity);
4155                         } else {
4156                                 ZVAL_COPY(&result, subject_entry);
4157                         }
4158                         /* Add to return array */
4159                         if (string_key) {
4160                                 zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, &result);
4161                         } else {
4162                                 zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, &result);
4163                         }
4164                 } ZEND_HASH_FOREACH_END();
4165         } else {        /* if subject is not an array */
4166                 count = php_str_replace_in_subject(search, replace, subject, return_value, case_sensitivity);
4167         }
4168         if (argc > 3) {
4169                 zval_ptr_dtor(zcount);
4170                 ZVAL_LONG(zcount, count);
4171         }
4172 }
4173 /* }}} */
4174 
4175 /* {{{ proto mixed str_replace(mixed search, mixed replace, mixed subject [, int &replace_count])
4176    Replaces all occurrences of search in haystack with replace */
4177 PHP_FUNCTION(str_replace)
4178 {
4179         php_str_replace_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4180 }
4181 /* }}} */
4182 
4183 /* {{{ proto mixed str_ireplace(mixed search, mixed replace, mixed subject [, int &replace_count])
4184    Replaces all occurrences of search in haystack with replace / case-insensitive */
4185 PHP_FUNCTION(str_ireplace)
4186 {
4187         php_str_replace_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4188 }
4189 /* }}} */
4190 
4191 /* {{{ php_hebrev
4192  *
4193  * Converts Logical Hebrew text (Hebrew Windows style) to Visual text
4194  * Cheers/complaints/flames - Zeev Suraski <zeev@php.net>
4195  */
4196 static void php_hebrev(INTERNAL_FUNCTION_PARAMETERS, int convert_newlines)
4197 {
4198         char *str;
4199         char *heb_str, *tmp, *target;
4200         size_t block_start, block_end, block_type, block_length, i;
4201         zend_long max_chars=0;
4202         size_t begin, end, char_count, orig_begin;
4203         size_t str_len;
4204         zend_string *broken_str;
4205 
4206         if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &str, &str_len, &max_chars) == FAILURE) {
4207                 return;
4208         }
4209 
4210         if (str_len == 0) {
4211                 RETURN_FALSE;
4212         }
4213 
4214         tmp = str;
4215         block_start=block_end=0;
4216 
4217         heb_str = (char *) emalloc(str_len+1);
4218         target = heb_str+str_len;
4219         *target = 0;
4220         target--;
4221 
4222         block_length=0;
4223 
4224         if (isheb(*tmp)) {
4225                 block_type = _HEB_BLOCK_TYPE_HEB;
4226         } else {
4227                 block_type = _HEB_BLOCK_TYPE_ENG;
4228         }
4229 
4230         do {
4231                 if (block_type == _HEB_BLOCK_TYPE_HEB) {
4232                         while ((isheb((int)*(tmp+1)) || _isblank((int)*(tmp+1)) || ispunct((int)*(tmp+1)) || (int)*(tmp+1)=='\n' ) && block_end<str_len-1) {
4233                                 tmp++;
4234                                 block_end++;
4235                                 block_length++;
4236                         }
4237                         for (i = block_start+1; i<= block_end+1; i++) {
4238                                 *target = str[i-1];
4239                                 switch (*target) {
4240                                         case '(':
4241                                                 *target = ')';
4242                                                 break;
4243                                         case ')':
4244                                                 *target = '(';
4245                                                 break;
4246                                         case '[':
4247                                                 *target = ']';
4248                                                 break;
4249                                         case ']':
4250                                                 *target = '[';
4251                                                 break;
4252                                         case '{':
4253                                                 *target = '}';
4254                                                 break;
4255                                         case '}':
4256                                                 *target = '{';
4257                                                 break;
4258                                         case '<':
4259                                                 *target = '>';
4260                                                 break;
4261                                         case '>':
4262                                                 *target = '<';
4263                                                 break;
4264                                         case '\\':
4265                                                 *target = '/';
4266                                                 break;
4267                                         case '/':
4268                                                 *target = '\\';
4269                                                 break;
4270                                         default:
4271                                                 break;
4272                                 }
4273                                 target--;
4274                         }
4275                         block_type = _HEB_BLOCK_TYPE_ENG;
4276                 } else {
4277                         while (!isheb(*(tmp+1)) && (int)*(tmp+1)!='\n' && block_end < str_len-1) {
4278                                 tmp++;
4279                                 block_end++;
4280                                 block_length++;
4281                         }
4282                         while ((_isblank((int)*tmp) || ispunct((int)*tmp)) && *tmp!='/' && *tmp!='-' && block_end > block_start) {
4283                                 tmp--;
4284                                 block_end--;
4285                         }
4286                         for (i = block_end+1; i >= block_start+1; i--) {
4287                                 *target = str[i-1];
4288                                 target--;
4289                         }
4290                         block_type = _HEB_BLOCK_TYPE_HEB;
4291                 }
4292                 block_start=block_end+1;
4293         } while (block_end < str_len-1);
4294 
4295 
4296         broken_str = zend_string_alloc(str_len, 0);
4297         begin = end = str_len-1;
4298         target = ZSTR_VAL(broken_str);
4299 
4300         while (1) {
4301                 char_count=0;
4302                 while ((!max_chars || (max_chars > 0 && char_count < max_chars)) && begin > 0) {
4303                         char_count++;
4304                         begin--;
4305                         if (begin <= 0 || _isnewline(heb_str[begin])) {
4306                                 while (begin > 0 && _isnewline(heb_str[begin-1])) {
4307                                         begin--;
4308                                         char_count++;
4309                                 }
4310                                 break;
4311                         }
4312                 }
4313                 if (max_chars >= 0 && char_count == max_chars) { /* try to avoid breaking words */
4314                         size_t new_char_count=char_count, new_begin=begin;
4315 
4316                         while (new_char_count > 0) {
4317                                 if (_isblank(heb_str[new_begin]) || _isnewline(heb_str[new_begin])) {
4318                                         break;
4319                                 }
4320                                 new_begin++;
4321                                 new_char_count--;
4322                         }
4323                         if (new_char_count > 0) {
4324                                 begin=new_begin;
4325                         }
4326                 }
4327                 orig_begin=begin;
4328 
4329                 if (_isblank(heb_str[begin])) {
4330                         heb_str[begin]='\n';
4331                 }
4332                 while (begin <= end && _isnewline(heb_str[begin])) { /* skip leading newlines */
4333                         begin++;
4334                 }
4335                 for (i = begin; i <= end; i++) { /* copy content */
4336                         *target = heb_str[i];
4337                         target++;
4338                 }
4339                 for (i = orig_begin; i <= end && _isnewline(heb_str[i]); i++) {
4340                         *target = heb_str[i];
4341                         target++;
4342                 }
4343                 begin=orig_begin;
4344 
4345                 if (begin <= 0) {
4346                         *target = 0;
4347                         break;
4348                 }
4349                 begin--;
4350                 end=begin;
4351         }
4352         efree(heb_str);
4353 
4354         if (convert_newlines) {
4355                 RETVAL_STR(php_char_to_str_ex(broken_str, '\n', "<br />\n", 7, 1, NULL));
4356                 zend_string_release(broken_str);
4357         } else {
4358                 RETURN_NEW_STR(broken_str);
4359         }
4360 }
4361 /* }}} */
4362 
4363 /* {{{ proto string hebrev(string str [, int max_chars_per_line])
4364    Converts logical Hebrew text to visual text */
4365 PHP_FUNCTION(hebrev)
4366 {
4367         php_hebrev(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4368 }
4369 /* }}} */
4370 
4371 /* {{{ proto string hebrevc(string str [, int max_chars_per_line])
4372    Converts logical Hebrew text to visual text with newline conversion */
4373 PHP_FUNCTION(hebrevc)
4374 {
4375         php_hebrev(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4376 }
4377 /* }}} */
4378 
4379 /* {{{ proto string nl2br(string str [, bool is_xhtml])
4380    Converts newlines to HTML line breaks */
4381 PHP_FUNCTION(nl2br)
4382 {
4383         /* in brief this inserts <br /> or <br> before matched regexp \n\r?|\r\n? */
4384         char    *tmp;
4385         zend_string *str;
4386         char    *end, *target;
4387         size_t  repl_cnt = 0;
4388         zend_bool       is_xhtml = 1;
4389         zend_string *result;
4390 
4391 #ifndef FAST_ZPP
4392         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|b", &str, &is_xhtml) == FAILURE) {
4393                 return;
4394         }
4395 #else
4396         ZEND_PARSE_PARAMETERS_START(1, 2)
4397                 Z_PARAM_STR(str)
4398                 Z_PARAM_OPTIONAL
4399                 Z_PARAM_BOOL(is_xhtml)
4400         ZEND_PARSE_PARAMETERS_END();
4401 #endif
4402 
4403         tmp = ZSTR_VAL(str);
4404         end = ZSTR_VAL(str) + ZSTR_LEN(str);
4405 
4406         /* it is really faster to scan twice and allocate mem once instead of scanning once
4407            and constantly reallocing */
4408         while (tmp < end) {
4409                 if (*tmp == '\r') {
4410                         if (*(tmp+1) == '\n') {
4411                                 tmp++;
4412                         }
4413                         repl_cnt++;
4414                 } else if (*tmp == '\n') {
4415                         if (*(tmp+1) == '\r') {
4416                                 tmp++;
4417                         }
4418                         repl_cnt++;
4419                 }
4420 
4421                 tmp++;
4422         }
4423 
4424         if (repl_cnt == 0) {
4425                 RETURN_STR_COPY(str);
4426         }
4427 
4428         {
4429                 size_t repl_len = is_xhtml ? (sizeof("<br />") - 1) : (sizeof("<br>") - 1);
4430 
4431                 result = zend_string_safe_alloc(repl_cnt, repl_len, ZSTR_LEN(str), 0);
4432                 target = ZSTR_VAL(result);
4433         }
4434 
4435         tmp = ZSTR_VAL(str);
4436         while (tmp < end) {
4437                 switch (*tmp) {
4438                         case '\r':
4439                         case '\n':
4440                                 *target++ = '<';
4441                                 *target++ = 'b';
4442                                 *target++ = 'r';
4443 
4444                                 if (is_xhtml) {
4445                                         *target++ = ' ';
4446                                         *target++ = '/';
4447                                 }
4448 
4449                                 *target++ = '>';
4450 
4451                                 if ((*tmp == '\r' && *(tmp+1) == '\n') || (*tmp == '\n' && *(tmp+1) == '\r')) {
4452                                         *target++ = *tmp++;
4453                                 }
4454                                 /* lack of a break; is intentional */
4455                         default:
4456                                 *target++ = *tmp;
4457                 }
4458 
4459                 tmp++;
4460         }
4461 
4462         *target = '\0';
4463 
4464         RETURN_NEW_STR(result);
4465 }
4466 /* }}} */
4467 
4468 /* {{{ proto string strip_tags(string str [, string allowable_tags])
4469    Strips HTML and PHP tags from a string */
4470 PHP_FUNCTION(strip_tags)
4471 {
4472         zend_string *buf;
4473         zend_string *str;
4474         zval *allow=NULL;
4475         char *allowed_tags=NULL;
4476         size_t allowed_tags_len=0;
4477 
4478         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|z", &str, &allow) == FAILURE) {
4479                 return;
4480         }
4481 
4482         /* To maintain a certain BC, we allow anything for the second parameter and return original string */
4483         if (allow) {
4484                 convert_to_string(allow);
4485                 allowed_tags = Z_STRVAL_P(allow);
4486                 allowed_tags_len = Z_STRLEN_P(allow);
4487         }
4488 
4489         buf = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
4490         ZSTR_LEN(buf) = php_strip_tags_ex(ZSTR_VAL(buf), ZSTR_LEN(str), NULL, allowed_tags, allowed_tags_len, 0);
4491         RETURN_NEW_STR(buf);
4492 }
4493 /* }}} */
4494 
4495 /* {{{ proto string setlocale(mixed category, string locale [, string ...])
4496    Set locale information */
4497 PHP_FUNCTION(setlocale)
4498 {
4499         zval *args = NULL;
4500         zval *plocale;
4501         zend_string *loc;
4502         char *retval;
4503         zend_long cat;
4504         int num_args, i = 0;
4505         uint32_t idx;
4506 
4507         if (zend_parse_parameters(ZEND_NUM_ARGS(), "l+", &cat, &args, &num_args) == FAILURE) {
4508                 return;
4509         }
4510 
4511 #ifdef HAVE_SETLOCALE
4512         idx = 0;
4513         while (1) {
4514                 if (Z_TYPE(args[0]) == IS_ARRAY) {
4515                         while (idx < Z_ARRVAL(args[0])->nNumUsed) {
4516                                 plocale = &Z_ARRVAL(args[0])->arData[idx].val;
4517                                 if (Z_TYPE_P(plocale) != IS_UNDEF) {
4518                                         break;
4519                                 }
4520                                 idx++;
4521                         }
4522                         if (idx >= Z_ARRVAL(args[0])->nNumUsed) {
4523                                 break;
4524                         }
4525                 } else {
4526                         plocale = &args[i];
4527                 }
4528 
4529                 loc = zval_get_string(plocale);
4530 
4531                 if (!strcmp("0", ZSTR_VAL(loc))) {
4532                         zend_string_release(loc);
4533                         loc = NULL;
4534                 } else {
4535                         if (ZSTR_LEN(loc) >= 255) {
4536                                 php_error_docref(NULL, E_WARNING, "Specified locale name is too long");
4537                                 zend_string_release(loc);
4538                                 break;
4539                         }
4540                 }
4541 
4542                 retval = php_my_setlocale(cat, loc ? ZSTR_VAL(loc) : NULL);
4543                 zend_update_current_locale();
4544                 if (retval) {
4545                         if (loc) {
4546                                 /* Remember if locale was changed */
4547                                 size_t len = strlen(retval);
4548 
4549                                 BG(locale_changed) = 1;
4550                                 if (cat == LC_CTYPE || cat == LC_ALL) {
4551                                         if (BG(locale_string)) {
4552                                                 zend_string_release(BG(locale_string));
4553                                         }
4554                                         if (len == ZSTR_LEN(loc) && !memcmp(ZSTR_VAL(loc), retval, len)) {
4555                                                 BG(locale_string) = zend_string_copy(loc);
4556                                                 RETURN_STR(BG(locale_string));
4557                                         } else {
4558                                                 BG(locale_string) = zend_string_init(retval, len, 0);
4559                                                 zend_string_release(loc);
4560                                                 RETURN_STR_COPY(BG(locale_string));
4561                                         }
4562                                 } else if (len == ZSTR_LEN(loc) && !memcmp(ZSTR_VAL(loc), retval, len)) {
4563                                         RETURN_STR(loc);
4564                                 }
4565                                 zend_string_release(loc);
4566                         }
4567                         RETURN_STRING(retval);
4568                 }
4569                 if (loc) {
4570                         zend_string_release(loc);
4571                 }
4572 
4573                 if (Z_TYPE(args[0]) == IS_ARRAY) {
4574                         idx++;
4575                 } else {
4576                         if (++i >= num_args) break;
4577                 }
4578         }
4579 
4580 #endif
4581         RETURN_FALSE;
4582 }
4583 /* }}} */
4584 
4585 /* {{{ proto void parse_str(string encoded_string [, array result])
4586    Parses GET/POST/COOKIE data and sets global variables */
4587 PHP_FUNCTION(parse_str)
4588 {
4589         char *arg;
4590         zval *arrayArg = NULL;
4591         char *res = NULL;
4592         size_t arglen;
4593 
4594         if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|z/", &arg, &arglen, &arrayArg) == FAILURE) {
4595                 return;
4596         }
4597 
4598         res = estrndup(arg, arglen);
4599 
4600         if (arrayArg == NULL) {
4601                 zval tmp;
4602                 zend_array *symbol_table = zend_rebuild_symbol_table();
4603 
4604                 ZVAL_ARR(&tmp, symbol_table);
4605                 sapi_module.treat_data(PARSE_STRING, res, &tmp);
4606         } else  {
4607                 zval ret;
4608 
4609                 /* Clear out the array that was passed in. */
4610                 zval_dtor(arrayArg);
4611                 array_init(&ret);
4612                 sapi_module.treat_data(PARSE_STRING, res, &ret);
4613                 ZVAL_COPY_VALUE(arrayArg, &ret);
4614         }
4615 }
4616 /* }}} */
4617 
4618 #define PHP_TAG_BUF_SIZE 1023
4619 
4620 /* {{{ php_tag_find
4621  *
4622  * Check if tag is in a set of tags
4623  *
4624  * states:
4625  *
4626  * 0 start tag
4627  * 1 first non-whitespace char seen
4628  */
4629 int php_tag_find(char *tag, size_t len, const char *set) {
4630         char c, *n, *t;
4631         int state=0, done=0;
4632         char *norm;
4633 
4634         if (len <= 0) {
4635                 return 0;
4636         }
4637 
4638         norm = emalloc(len+1);
4639 
4640         n = norm;
4641         t = tag;
4642         c = tolower(*t);
4643         /*
4644            normalize the tag removing leading and trailing whitespace
4645            and turn any <a whatever...> into just <a> and any </tag>
4646            into <tag>
4647         */
4648         while (!done) {
4649                 switch (c) {
4650                         case '<':
4651                                 *(n++) = c;
4652                                 break;
4653                         case '>':
4654                                 done =1;
4655                                 break;
4656                         default:
4657                                 if (!isspace((int)c)) {
4658                                         if (state == 0) {
4659                                                 state=1;
4660                                         }
4661                                         if (c != '/') {
4662                                                 *(n++) = c;
4663                                         }
4664                                 } else {
4665                                         if (state == 1)
4666                                                 done=1;
4667                                 }
4668                                 break;
4669                 }
4670                 c = tolower(*(++t));
4671         }
4672         *(n++) = '>';
4673         *n = '\0';
4674         if (strstr(set, norm)) {
4675                 done=1;
4676         } else {
4677                 done=0;
4678         }
4679         efree(norm);
4680         return done;
4681 }
4682 /* }}} */
4683 
4684 PHPAPI size_t php_strip_tags(char *rbuf, size_t len, int *stateptr, const char *allow, size_t allow_len) /* {{{ */
4685 {
4686         return php_strip_tags_ex(rbuf, len, stateptr, allow, allow_len, 0);
4687 }
4688 /* }}} */
4689 
4690 /* {{{ php_strip_tags
4691 
4692         A simple little state-machine to strip out html and php tags
4693 
4694         State 0 is the output state, State 1 means we are inside a
4695         normal html tag and state 2 means we are inside a php tag.
4696 
4697         The state variable is passed in to allow a function like fgetss
4698         to maintain state across calls to the function.
4699 
4700         lc holds the last significant character read and br is a bracket
4701         counter.
4702 
4703         When an allow string is passed in we keep track of the string
4704         in state 1 and when the tag is closed check it against the
4705         allow string to see if we should allow it.
4706 
4707         swm: Added ability to strip <?xml tags without assuming it PHP
4708         code.
4709 */
4710 PHPAPI size_t php_strip_tags_ex(char *rbuf, size_t len, int *stateptr, const char *allow, size_t allow_len, zend_bool allow_tag_spaces)
4711 {
4712         char *tbuf, *buf, *p, *tp, *rp, c, lc;
4713         int br, depth=0, in_q = 0;
4714         int state = 0;
4715         size_t pos, i = 0;
4716         char *allow_free = NULL;
4717         const char *allow_actual;
4718         char is_xml = 0;
4719 
4720         if (stateptr)
4721                 state = *stateptr;
4722 
4723         buf = estrndup(rbuf, len);
4724         c = *buf;
4725         lc = '\0';
4726         p = buf;
4727         rp = rbuf;
4728         br = 0;
4729         if (allow) {
4730                 allow_free = zend_str_tolower_dup_ex(allow, allow_len);
4731                 allow_actual = allow_free ? allow_free : allow;
4732                 tbuf = emalloc(PHP_TAG_BUF_SIZE + 1);
4733                 tp = tbuf;
4734         } else {
4735                 tbuf = tp = NULL;
4736         }
4737 
4738         while (i < len) {
4739                 switch (c) {
4740                         case '\0':
4741                                 break;
4742                         case '<':
4743                                 if (in_q) {
4744                                         break;
4745                                 }
4746                                 if (isspace(*(p + 1)) && !allow_tag_spaces) {
4747                                         goto reg_char;
4748                                 }
4749                                 if (state == 0) {
4750                                         lc = '<';
4751                                         state = 1;
4752                                         if (allow) {
4753                                                 if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4754                                                         pos = tp - tbuf;
4755                                                         tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4756                                                         tp = tbuf + pos;
4757                                                 }
4758                                                 *(tp++) = '<';
4759                                         }
4760                                 } else if (state == 1) {
4761                                         depth++;
4762                                 }
4763                                 break;
4764 
4765                         case '(':
4766                                 if (state == 2) {
4767                                         if (lc != '"' && lc != '\'') {
4768                                                 lc = '(';
4769                                                 br++;
4770                                         }
4771                                 } else if (allow && state == 1) {
4772                                         if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4773                                                 pos = tp - tbuf;
4774                                                 tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4775                                                 tp = tbuf + pos;
4776                                         }
4777                                         *(tp++) = c;
4778                                 } else if (state == 0) {
4779                                         *(rp++) = c;
4780                                 }
4781                                 break;
4782 
4783                         case ')':
4784                                 if (state == 2) {
4785                                         if (lc != '"' && lc != '\'') {
4786                                                 lc = ')';
4787                                                 br--;
4788                                         }
4789                                 } else if (allow && state == 1) {
4790                                         if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4791                                                 pos = tp - tbuf;
4792                                                 tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4793                                                 tp = tbuf + pos;
4794                                         }
4795                                         *(tp++) = c;
4796                                 } else if (state == 0) {
4797                                         *(rp++) = c;
4798                                 }
4799                                 break;
4800 
4801                         case '>':
4802                                 if (depth) {
4803                                         depth--;
4804                                         break;
4805                                 }
4806 
4807                                 if (in_q) {
4808                                         break;
4809                                 }
4810 
4811                                 switch (state) {
4812                                         case 1: /* HTML/XML */
4813                                                 lc = '>';
4814                                                 if (is_xml && *(p -1) == '-') {
4815                                                         break;
4816                                                 }
4817                                                 in_q = state = is_xml = 0;
4818                                                 if (allow) {
4819                                                         if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4820                                                                 pos = tp - tbuf;
4821                                                                 tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4822                                                                 tp = tbuf + pos;
4823                                                         }
4824                                                         *(tp++) = '>';
4825                                                         *tp='\0';
4826                                                         if (php_tag_find(tbuf, tp-tbuf, allow_actual)) {
4827                                                                 memcpy(rp, tbuf, tp-tbuf);
4828                                                                 rp += tp-tbuf;
4829                                                         }
4830                                                         tp = tbuf;
4831                                                 }
4832                                                 break;
4833 
4834                                         case 2: /* PHP */
4835                                                 if (!br && lc != '\"' && *(p-1) == '?') {
4836                                                         in_q = state = 0;
4837                                                         tp = tbuf;
4838                                                 }
4839                                                 break;
4840 
4841                                         case 3:
4842                                                 in_q = state = 0;
4843                                                 tp = tbuf;
4844                                                 break;
4845 
4846                                         case 4: /* JavaScript/CSS/etc... */
4847                                                 if (p >= buf + 2 && *(p-1) == '-' && *(p-2) == '-') {
4848                                                         in_q = state = 0;
4849                                                         tp = tbuf;
4850                                                 }
4851                                                 break;
4852 
4853                                         default:
4854                                                 *(rp++) = c;
4855                                                 break;
4856                                 }
4857                                 break;
4858 
4859                         case '"':
4860                         case '\'':
4861                                 if (state == 4) {
4862                                         /* Inside <!-- comment --> */
4863                                         break;
4864                                 } else if (state == 2 && *(p-1) != '\\') {
4865                                         if (lc == c) {
4866                                                 lc = '\0';
4867                                         } else if (lc != '\\') {
4868                                                 lc = c;
4869                                         }
4870                                 } else if (state == 0) {
4871                                         *(rp++) = c;
4872                                 } else if (allow && state == 1) {
4873                                         if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4874                                                 pos = tp - tbuf;
4875                                                 tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4876                                                 tp = tbuf + pos;
4877                                         }
4878                                         *(tp++) = c;
4879                                 }
4880                                 if (state && p != buf && (state == 1 || *(p-1) != '\\') && (!in_q || *p == in_q)) {
4881                                         if (in_q) {
4882                                                 in_q = 0;
4883                                         } else {
4884                                                 in_q = *p;
4885                                         }
4886                                 }
4887                                 break;
4888 
4889                         case '!':
4890                                 /* JavaScript & Other HTML scripting languages */
4891                                 if (state == 1 && *(p-1) == '<') {
4892                                         state = 3;
4893                                         lc = c;
4894                                 } else {
4895                                         if (state == 0) {
4896                                                 *(rp++) = c;
4897                                         } else if (allow && state == 1) {
4898                                                 if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4899                                                         pos = tp - tbuf;
4900                                                         tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4901                                                         tp = tbuf + pos;
4902                                                 }
4903                                                 *(tp++) = c;
4904                                         }
4905                                 }
4906                                 break;
4907 
4908                         case '-':
4909                                 if (state == 3 && p >= buf + 2 && *(p-1) == '-' && *(p-2) == '!') {
4910                                         state = 4;
4911                                 } else {
4912                                         goto reg_char;
4913                                 }
4914                                 break;
4915 
4916                         case '?':
4917 
4918                                 if (state == 1 && *(p-1) == '<') {
4919                                         br=0;
4920                                         state=2;
4921                                         break;
4922                                 }
4923 
4924                         case 'E':
4925                         case 'e':
4926                                 /* !DOCTYPE exception */
4927                                 if (state==3 && p > buf+6
4928                                                      && tolower(*(p-1)) == 'p'
4929                                                  && tolower(*(p-2)) == 'y'
4930                                                      && tolower(*(p-3)) == 't'
4931                                                      && tolower(*(p-4)) == 'c'
4932                                                      && tolower(*(p-5)) == 'o'
4933                                                      && tolower(*(p-6)) == 'd') {
4934                                         state = 1;
4935                                         break;
4936                                 }
4937                                 /* fall-through */
4938 
4939                         case 'l':
4940                         case 'L':
4941 
4942                                 /* swm: If we encounter '<?xml' then we shouldn't be in
4943                                  * state == 2 (PHP). Switch back to HTML.
4944                                  */
4945 
4946                                 if (state == 2 && p > buf+4 && strncasecmp(p-4, "<?xm", 4) == 0) {
4947                                         state = 1; is_xml=1;
4948                                         break;
4949                                 }
4950 
4951                                 /* fall-through */
4952                         default:
4953 reg_char:
4954                                 if (state == 0) {
4955                                         *(rp++) = c;
4956                                 } else if (allow && state == 1) {
4957                                         if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4958                                                 pos = tp - tbuf;
4959                                                 tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4960                                                 tp = tbuf + pos;
4961                                         }
4962                                         *(tp++) = c;
4963                                 }
4964                                 break;
4965                 }
4966                 c = *(++p);
4967                 i++;
4968         }
4969         if (rp < rbuf + len) {
4970                 *rp = '\0';
4971         }
4972         efree(buf);
4973         if (allow) {
4974                 efree(tbuf);
4975                 if (allow_free) {
4976                         efree(allow_free);
4977                 }
4978         }
4979         if (stateptr)
4980                 *stateptr = state;
4981 
4982         return (size_t)(rp - rbuf);
4983 }
4984 /* }}} */
4985 
4986 /* {{{ proto array str_getcsv(string input[, string delimiter[, string enclosure[, string escape]]])
4987 Parse a CSV string into an array */
4988 PHP_FUNCTION(str_getcsv)
4989 {
4990         zend_string *str;
4991         char delim = ',', enc = '"', esc = '\\';
4992         char *delim_str = NULL, *enc_str = NULL, *esc_str = NULL;
4993         size_t delim_len = 0, enc_len = 0, esc_len = 0;
4994 
4995         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|sss", &str, &delim_str, &delim_len,
4996                 &enc_str, &enc_len, &esc_str, &esc_len) == FAILURE) {
4997                 return;
4998         }
4999 
5000         delim = delim_len ? delim_str[0] : delim;
5001         enc = enc_len ? enc_str[0] : enc;
5002         esc = esc_len ? esc_str[0] : esc;
5003 
5004         php_fgetcsv(NULL, delim, enc, esc, ZSTR_LEN(str), ZSTR_VAL(str), return_value);
5005 }
5006 /* }}} */
5007 
5008 /* {{{ proto string str_repeat(string input, int mult)
5009    Returns the input string repeat mult times */
5010 PHP_FUNCTION(str_repeat)
5011 {
5012         zend_string             *input_str;             /* Input string */
5013         zend_long               mult;                   /* Multiplier */
5014         zend_string     *result;                /* Resulting string */
5015         size_t          result_len;             /* Length of the resulting string */
5016 
5017         if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sl", &input_str, &mult) == FAILURE) {
5018                 return;
5019         }
5020 
5021         if (mult < 0) {
5022                 php_error_docref(NULL, E_WARNING, "Second argument has to be greater than or equal to 0");
5023                 return;
5024         }
5025 
5026         /* Don't waste our time if it's empty */
5027         /* ... or if the multiplier is zero */
5028         if (ZSTR_LEN(input_str) == 0 || mult == 0)
5029                 RETURN_EMPTY_STRING();
5030 
5031         /* Initialize the result string */
5032         result = zend_string_safe_alloc(ZSTR_LEN(input_str), mult, 0, 0);
5033         result_len = ZSTR_LEN(input_str) * mult;
5034 
5035         /* Heavy optimization for situations where input string is 1 byte long */
5036         if (ZSTR_LEN(input_str) == 1) {
5037                 memset(ZSTR_VAL(result), *ZSTR_VAL(input_str), mult);
5038         } else {
5039                 char *s, *e, *ee;
5040                 ptrdiff_t l=0;
5041                 memcpy(ZSTR_VAL(result), ZSTR_VAL(input_str), ZSTR_LEN(input_str));
5042                 s = ZSTR_VAL(result);
5043                 e = ZSTR_VAL(result) + ZSTR_LEN(input_str);
5044                 ee = ZSTR_VAL(result) + result_len;
5045 
5046                 while (e<ee) {
5047                         l = (e-s) < (ee-e) ? (e-s) : (ee-e);
5048                         memmove(e, s, l);
5049                         e += l;
5050                 }
5051         }
5052 
5053         ZSTR_VAL(result)[result_len] = '\0';
5054 
5055         RETURN_NEW_STR(result);
5056 }
5057 /* }}} */
5058 
5059 /* {{{ proto mixed count_chars(string input [, int mode])
5060    Returns info about what characters are used in input */
5061 PHP_FUNCTION(count_chars)
5062 {
5063         zend_string *input;
5064         int chars[256];
5065         zend_long mymode=0;
5066         unsigned char *buf;
5067         int inx;
5068         char retstr[256];
5069         size_t retlen=0;
5070         size_t tmp = 0;
5071 
5072         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|l", &input, &mymode) == FAILURE) {
5073                 return;
5074         }
5075 
5076         if (mymode < 0 || mymode > 4) {
5077                 php_error_docref(NULL, E_WARNING, "Unknown mode");
5078                 RETURN_FALSE;
5079         }
5080 
5081         buf = (unsigned char *) ZSTR_VAL(input);
5082         memset((void*) chars, 0, sizeof(chars));
5083 
5084         while (tmp < ZSTR_LEN(input)) {
5085                 chars[*buf]++;
5086                 buf++;
5087                 tmp++;
5088         }
5089 
5090         if (mymode < 3) {
5091                 array_init(return_value);
5092         }
5093 
5094         for (inx = 0; inx < 256; inx++) {
5095                 switch (mymode) {
5096                         case 0:
5097                                 add_index_long(return_value, inx, chars[inx]);
5098                                 break;
5099                         case 1:
5100                                 if (chars[inx] != 0) {
5101                                         add_index_long(return_value, inx, chars[inx]);
5102                                 }
5103                                 break;
5104                         case 2:
5105                                 if (chars[inx] == 0) {
5106                                         add_index_long(return_value, inx, chars[inx]);
5107                                 }
5108                                 break;
5109                         case 3:
5110                                 if (chars[inx] != 0) {
5111                                         retstr[retlen++] = inx;
5112                                 }
5113                                 break;
5114                         case 4:
5115                                 if (chars[inx] == 0) {
5116                                         retstr[retlen++] = inx;
5117                                 }
5118                                 break;
5119                 }
5120         }
5121 
5122         if (mymode >= 3 && mymode <= 4) {
5123                 RETURN_STRINGL(retstr, retlen);
5124         }
5125 }
5126 /* }}} */
5127 
5128 /* {{{ php_strnatcmp
5129  */
5130 static void php_strnatcmp(INTERNAL_FUNCTION_PARAMETERS, int fold_case)
5131 {
5132         zend_string *s1, *s2;
5133 
5134         if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &s1, &s2) == FAILURE) {
5135                 return;
5136         }
5137 
5138         RETURN_LONG(strnatcmp_ex(ZSTR_VAL(s1), ZSTR_LEN(s1),
5139                                                          ZSTR_VAL(s2), ZSTR_LEN(s2),
5140                                                          fold_case));
5141 }
5142 /* }}} */
5143 
5144 PHPAPI int string_natural_compare_function_ex(zval *result, zval *op1, zval *op2, zend_bool case_insensitive) /* {{{ */
5145 {
5146         zend_string *str1 = zval_get_string(op1);
5147         zend_string *str2 = zval_get_string(op2);
5148 
5149         ZVAL_LONG(result, strnatcmp_ex(ZSTR_VAL(str1), ZSTR_LEN(str1), ZSTR_VAL(str2), ZSTR_LEN(str2), case_insensitive));
5150 
5151         zend_string_release(str1);
5152         zend_string_release(str2);
5153         return SUCCESS;
5154 }
5155 /* }}} */
5156 
5157 PHPAPI int string_natural_case_compare_function(zval *result, zval *op1, zval *op2) /* {{{ */
5158 {
5159         return string_natural_compare_function_ex(result, op1, op2, 1);
5160 }
5161 /* }}} */
5162 
5163 PHPAPI int string_natural_compare_function(zval *result, zval *op1, zval *op2) /* {{{ */
5164 {
5165         return string_natural_compare_function_ex(result, op1, op2, 0);
5166 }
5167 /* }}} */
5168 
5169 /* {{{ proto int strnatcmp(string s1, string s2)
5170    Returns the result of string comparison using 'natural' algorithm */
5171 PHP_FUNCTION(strnatcmp)
5172 {
5173         php_strnatcmp(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
5174 }
5175 /* }}} */
5176 
5177 /* {{{ proto array localeconv(void)
5178    Returns numeric formatting information based on the current locale */
5179 PHP_FUNCTION(localeconv)
5180 {
5181         zval grouping, mon_grouping;
5182         int len, i;
5183 
5184         /* We don't need no stinkin' parameters... */
5185         if (zend_parse_parameters_none() == FAILURE) {
5186                 return;
5187         }
5188 
5189         array_init(return_value);
5190         array_init(&grouping);
5191         array_init(&mon_grouping);
5192 
5193 #ifdef HAVE_LOCALECONV
5194         {
5195                 struct lconv currlocdata;
5196 
5197                 localeconv_r( &currlocdata );
5198 
5199                 /* Grab the grouping data out of the array */
5200                 len = (int)strlen(currlocdata.grouping);
5201 
5202                 for (i = 0; i < len; i++) {
5203                         add_index_long(&grouping, i, currlocdata.grouping[i]);
5204                 }
5205 
5206                 /* Grab the monetary grouping data out of the array */
5207                 len = (int)strlen(currlocdata.mon_grouping);
5208 
5209                 for (i = 0; i < len; i++) {
5210                         add_index_long(&mon_grouping, i, currlocdata.mon_grouping[i]);
5211                 }
5212 
5213                 add_assoc_string(return_value, "decimal_point",     currlocdata.decimal_point);
5214                 add_assoc_string(return_value, "thousands_sep",     currlocdata.thousands_sep);
5215                 add_assoc_string(return_value, "int_curr_symbol",   currlocdata.int_curr_symbol);
5216                 add_assoc_string(return_value, "currency_symbol",   currlocdata.currency_symbol);
5217                 add_assoc_string(return_value, "mon_decimal_point", currlocdata.mon_decimal_point);
5218                 add_assoc_string(return_value, "mon_thousands_sep", currlocdata.mon_thousands_sep);
5219                 add_assoc_string(return_value, "positive_sign",     currlocdata.positive_sign);
5220                 add_assoc_string(return_value, "negative_sign",     currlocdata.negative_sign);
5221                 add_assoc_long(  return_value, "int_frac_digits",   currlocdata.int_frac_digits);
5222                 add_assoc_long(  return_value, "frac_digits",       currlocdata.frac_digits);
5223                 add_assoc_long(  return_value, "p_cs_precedes",     currlocdata.p_cs_precedes);
5224                 add_assoc_long(  return_value, "p_sep_by_space",    currlocdata.p_sep_by_space);
5225                 add_assoc_long(  return_value, "n_cs_precedes",     currlocdata.n_cs_precedes);
5226                 add_assoc_long(  return_value, "n_sep_by_space",    currlocdata.n_sep_by_space);
5227                 add_assoc_long(  return_value, "p_sign_posn",       currlocdata.p_sign_posn);
5228                 add_assoc_long(  return_value, "n_sign_posn",       currlocdata.n_sign_posn);
5229         }
5230 #else
5231         /* Ok, it doesn't look like we have locale info floating around, so I guess it
5232            wouldn't hurt to just go ahead and return the POSIX locale information?  */
5233 
5234         add_index_long(&grouping, 0, -1);
5235         add_index_long(&mon_grouping, 0, -1);
5236 
5237         add_assoc_string(return_value, "decimal_point",     "\x2E");
5238         add_assoc_string(return_value, "thousands_sep",     "");
5239         add_assoc_string(return_value, "int_curr_symbol",   "");
5240         add_assoc_string(return_value, "currency_symbol",   "");
5241         add_assoc_string(return_value, "mon_decimal_point", "\x2E");
5242         add_assoc_string(return_value, "mon_thousands_sep", "");
5243         add_assoc_string(return_value, "positive_sign",     "");
5244         add_assoc_string(return_value, "negative_sign",     "");
5245         add_assoc_long(  return_value, "int_frac_digits",   CHAR_MAX);
5246         add_assoc_long(  return_value, "frac_digits",       CHAR_MAX);
5247         add_assoc_long(  return_value, "p_cs_precedes",     CHAR_MAX);
5248         add_assoc_long(  return_value, "p_sep_by_space",    CHAR_MAX);
5249         add_assoc_long(  return_value, "n_cs_precedes",     CHAR_MAX);
5250         add_assoc_long(  return_value, "n_sep_by_space",    CHAR_MAX);
5251         add_assoc_long(  return_value, "p_sign_posn",       CHAR_MAX);
5252         add_assoc_long(  return_value, "n_sign_posn",       CHAR_MAX);
5253 #endif
5254 
5255         zend_hash_str_update(Z_ARRVAL_P(return_value), "grouping", sizeof("grouping")-1, &grouping);
5256         zend_hash_str_update(Z_ARRVAL_P(return_value), "mon_grouping", sizeof("mon_grouping")-1, &mon_grouping);
5257 }
5258 /* }}} */
5259 
5260 /* {{{ proto int strnatcasecmp(string s1, string s2)
5261    Returns the result of case-insensitive string comparison using 'natural' algorithm */
5262 PHP_FUNCTION(strnatcasecmp)
5263 {
5264         php_strnatcmp(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
5265 }
5266 /* }}} */
5267 
5268 /* {{{ proto int substr_count(string haystack, string needle [, int offset [, int length]])
5269    Returns the number of times a substring occurs in the string */
5270 PHP_FUNCTION(substr_count)
5271 {
5272         char *haystack, *needle;
5273         zend_long offset = 0, length = 0;
5274         int ac = ZEND_NUM_ARGS();
5275         int count = 0;
5276         size_t haystack_len, needle_len;
5277         char *p, *endp, cmp;
5278 
5279         if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|ll", &haystack, &haystack_len, &needle, &needle_len, &offset, &length) == FAILURE) {
5280                 return;
5281         }
5282 
5283         if (needle_len == 0) {
5284                 php_error_docref(NULL, E_WARNING, "Empty substring");
5285                 RETURN_FALSE;
5286         }
5287 
5288         p = haystack;
5289         endp = p + haystack_len;
5290 
5291         if (offset < 0) {
5292                 php_error_docref(NULL, E_WARNING, "Offset should be greater than or equal to 0");
5293                 RETURN_FALSE;
5294         }
5295 
5296         if ((size_t)offset > haystack_len) {
5297                 php_error_docref(NULL, E_WARNING, "Offset value " ZEND_LONG_FMT " exceeds string length", offset);
5298                 RETURN_FALSE;
5299         }
5300         p += offset;
5301 
5302         if (ac == 4) {
5303 
5304                 if (length <= 0) {
5305                         php_error_docref(NULL, E_WARNING, "Length should be greater than 0");
5306                         RETURN_FALSE;
5307                 }
5308                 if (length > (haystack_len - offset)) {
5309                         php_error_docref(NULL, E_WARNING, "Length value " ZEND_LONG_FMT " exceeds string length", length);
5310                         RETURN_FALSE;
5311                 }
5312                 endp = p + length;
5313         }
5314 
5315         if (needle_len == 1) {
5316                 cmp = needle[0];
5317 
5318                 while ((p = memchr(p, cmp, endp - p))) {
5319                         count++;
5320                         p++;
5321                 }
5322         } else {
5323                 while ((p = (char*)php_memnstr(p, needle, needle_len, endp))) {
5324                         p += needle_len;
5325                         count++;
5326                 }
5327         }
5328 
5329         RETURN_LONG(count);
5330 }
5331 /* }}} */
5332 
5333 /* {{{ proto string str_pad(string input, int pad_length [, string pad_string [, int pad_type]])
5334    Returns input string padded on the left or right to specified length with pad_string */
5335 PHP_FUNCTION(str_pad)
5336 {
5337         /* Input arguments */
5338         zend_string *input;                             /* Input string */
5339         zend_long pad_length;                   /* Length to pad to */
5340 
5341         /* Helper variables */
5342         size_t num_pad_chars;           /* Number of padding characters (total - input size) */
5343         char *pad_str = " "; /* Pointer to padding string */
5344         size_t pad_str_len = 1;
5345         zend_long   pad_type_val = STR_PAD_RIGHT; /* The padding type value */
5346         size_t     i, left_pad=0, right_pad=0;
5347         zend_string *result = NULL;     /* Resulting string */
5348 
5349         if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sl|sl", &input, &pad_length, &pad_str, &pad_str_len, &pad_type_val) == FAILURE) {
5350                 return;
5351         }
5352 
5353         /* If resulting string turns out to be shorter than input string,
5354            we simply copy the input and return. */
5355         if (pad_length < 0  || (size_t)pad_length <= ZSTR_LEN(input)) {
5356                 RETURN_STRINGL(ZSTR_VAL(input), ZSTR_LEN(input));
5357         }
5358 
5359         if (pad_str_len == 0) {
5360                 php_error_docref(NULL, E_WARNING, "Padding string cannot be empty");
5361                 return;
5362         }
5363 
5364         if (pad_type_val < STR_PAD_LEFT || pad_type_val > STR_PAD_BOTH) {
5365                 php_error_docref(NULL, E_WARNING, "Padding type has to be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH");
5366                 return;
5367         }
5368 
5369         num_pad_chars = pad_length - ZSTR_LEN(input);
5370         if (num_pad_chars >= INT_MAX) {
5371                 php_error_docref(NULL, E_WARNING, "Padding length is too long");
5372                 return;
5373         }
5374 
5375         result = zend_string_safe_alloc(ZSTR_LEN(input), 1, num_pad_chars, 0);
5376         ZSTR_LEN(result) = 0;
5377 
5378         /* We need to figure out the left/right padding lengths. */
5379         switch (pad_type_val) {
5380                 case STR_PAD_RIGHT:
5381                         left_pad = 0;
5382                         right_pad = num_pad_chars;
5383                         break;
5384 
5385                 case STR_PAD_LEFT:
5386                         left_pad = num_pad_chars;
5387                         right_pad = 0;
5388                         break;
5389 
5390                 case STR_PAD_BOTH:
5391                         left_pad = num_pad_chars / 2;
5392                         right_pad = num_pad_chars - left_pad;
5393                         break;
5394         }
5395 
5396         /* First we pad on the left. */
5397         for (i = 0; i < left_pad; i++)
5398                 ZSTR_VAL(result)[ZSTR_LEN(result)++] = pad_str[i % pad_str_len];
5399 
5400         /* Then we copy the input string. */
5401         memcpy(ZSTR_VAL(result) + ZSTR_LEN(result), ZSTR_VAL(input), ZSTR_LEN(input));
5402         ZSTR_LEN(result) += ZSTR_LEN(input);
5403 
5404         /* Finally, we pad on the right. */
5405         for (i = 0; i < right_pad; i++)
5406                 ZSTR_VAL(result)[ZSTR_LEN(result)++] = pad_str[i % pad_str_len];
5407 
5408         ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
5409 
5410         RETURN_NEW_STR(result);
5411 }
5412 /* }}} */
5413 
5414 /* {{{ proto mixed sscanf(string str, string format [, string ...])
5415    Implements an ANSI C compatible sscanf */
5416 PHP_FUNCTION(sscanf)
5417 {
5418         zval *args = NULL;
5419         char *str, *format;
5420         size_t str_len, format_len;
5421         int result, num_args = 0;
5422 
5423         if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss*", &str, &str_len, &format, &format_len,
5424                 &args, &num_args) == FAILURE) {
5425                 return;
5426         }
5427 
5428         result = php_sscanf_internal(str, format, num_args, args, 0, return_value);
5429 
5430         if (SCAN_ERROR_WRONG_PARAM_COUNT == result) {
5431                 WRONG_PARAM_COUNT;
5432         }
5433 }
5434 /* }}} */
5435 
5436 static char rot13_from[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
5437 static char rot13_to[] = "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM";
5438 
5439 /* {{{ proto string str_rot13(string str)
5440    Perform the rot13 transform on a string */
5441 PHP_FUNCTION(str_rot13)
5442 {
5443         zend_string *arg;
5444 
5445         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &arg) == FAILURE) {
5446                 return;
5447         }
5448 
5449         if (ZSTR_LEN(arg) == 0) {
5450                 RETURN_EMPTY_STRING();
5451         } else {
5452                 RETURN_STR(php_strtr_ex(arg, rot13_from, rot13_to, 52));
5453         }
5454 }
5455 /* }}} */
5456 
5457 static void php_string_shuffle(char *str, zend_long len) /* {{{ */
5458 {
5459         zend_long n_elems, rnd_idx, n_left;
5460         char temp;
5461         /* The implementation is stolen from array_data_shuffle       */
5462         /* Thus the characteristics of the randomization are the same */
5463         n_elems = len;
5464 
5465         if (n_elems <= 1) {
5466                 return;
5467         }
5468 
5469         n_left = n_elems;
5470 
5471         while (--n_left) {
5472                 rnd_idx = php_rand();
5473                 RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX);
5474                 if (rnd_idx != n_left) {
5475                         temp = str[n_left];
5476                         str[n_left] = str[rnd_idx];
5477                         str[rnd_idx] = temp;
5478                 }
5479         }
5480 }
5481 /* }}} */
5482 
5483 /* {{{ proto void str_shuffle(string str)
5484    Shuffles string. One permutation of all possible is created */
5485 PHP_FUNCTION(str_shuffle)
5486 {
5487         zend_string *arg;
5488 
5489         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &arg) == FAILURE) {
5490                 return;
5491         }
5492 
5493         RETVAL_STRINGL(ZSTR_VAL(arg), ZSTR_LEN(arg));
5494         if (Z_STRLEN_P(return_value) > 1) {
5495                 php_string_shuffle(Z_STRVAL_P(return_value), (zend_long) Z_STRLEN_P(return_value));
5496         }
5497 }
5498 /* }}} */
5499 
5500 /* {{{ proto mixed str_word_count(string str, [int format [, string charlist]])
5501         Counts the number of words inside a string. If format of 1 is specified,
5502         then the function will return an array containing all the words
5503         found inside the string. If format of 2 is specified, then the function
5504         will return an associated array where the position of the word is the key
5505         and the word itself is the value.
5506 
5507         For the purpose of this function, 'word' is defined as a locale dependent
5508         string containing alphabetic characters, which also may contain, but not start
5509         with "'" and "-" characters.
5510 */
5511 PHP_FUNCTION(str_word_count)
5512 {
5513         zend_string *str;
5514         char *char_list = NULL, *p, *e, *s, ch[256];
5515         size_t char_list_len = 0, word_count = 0;
5516         zend_long type = 0;
5517 
5518         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|ls", &str, &type, &char_list, &char_list_len) == FAILURE) {
5519                 return;
5520         }
5521 
5522         switch(type) {
5523                 case 1:
5524                 case 2:
5525                         array_init(return_value);
5526                         if (!ZSTR_LEN(str)) {
5527                                 return;
5528                         }
5529                         break;
5530                 case 0:
5531                         if (!ZSTR_LEN(str)) {
5532                                 RETURN_LONG(0);
5533                         }
5534                         /* nothing to be done */
5535                         break;
5536                 default:
5537                         php_error_docref(NULL, E_WARNING, "Invalid format value " ZEND_LONG_FMT, type);
5538                         RETURN_FALSE;
5539         }
5540 
5541         if (char_list) {
5542                 php_charmask((unsigned char *)char_list, char_list_len, ch);
5543         }
5544 
5545         p = ZSTR_VAL(str);
5546         e = ZSTR_VAL(str) + ZSTR_LEN(str);
5547 
5548         /* first character cannot be ' or -, unless explicitly allowed by the user */
5549         if ((*p == '\'' && (!char_list || !ch['\''])) || (*p == '-' && (!char_list || !ch['-']))) {
5550                 p++;
5551         }
5552         /* last character cannot be -, unless explicitly allowed by the user */
5553         if (*(e - 1) == '-' && (!char_list || !ch['-'])) {
5554                 e--;
5555         }
5556 
5557         while (p < e) {
5558                 s = p;
5559                 while (p < e && (isalpha((unsigned char)*p) || (char_list && ch[(unsigned char)*p]) || *p == '\'' || *p == '-')) {
5560                         p++;
5561                 }
5562                 if (p > s) {
5563                         switch (type)
5564                         {
5565                                 case 1:
5566                                         add_next_index_stringl(return_value, s, p - s);
5567                                         break;
5568                                 case 2:
5569                                         add_index_stringl(return_value, (s - ZSTR_VAL(str)), s, p - s);
5570                                         break;
5571                                 default:
5572                                         word_count++;
5573                                         break;
5574                         }
5575                 }
5576                 p++;
5577         }
5578 
5579         if (!type) {
5580                 RETURN_LONG(word_count);
5581         }
5582 }
5583 
5584 /* }}} */
5585 
5586 #if HAVE_STRFMON
5587 /* {{{ proto string money_format(string format , float value)
5588    Convert monetary value(s) to string */
5589 PHP_FUNCTION(money_format)
5590 {
5591         size_t format_len = 0;
5592         char *format, *p, *e;
5593         double value;
5594         zend_bool check = 0;
5595         zend_string *str;
5596         ssize_t res_len;
5597 
5598         if (zend_parse_parameters(ZEND_NUM_ARGS(), "sd", &format, &format_len, &value) == FAILURE) {
5599                 return;
5600         }
5601 
5602         p = format;
5603         e = p + format_len;
5604         while ((p = memchr(p, '%', (e - p)))) {
5605                 if (*(p + 1) == '%') {
5606                         p += 2;
5607                 } else if (!check) {
5608                         check = 1;
5609                         p++;
5610                 } else {
5611                         php_error_docref(NULL, E_WARNING, "Only a single %%i or %%n token can be used");
5612                         RETURN_FALSE;
5613                 }
5614         }
5615 
5616         str = zend_string_safe_alloc(format_len, 1, 1024, 0);
5617         if ((res_len = strfmon(ZSTR_VAL(str), ZSTR_LEN(str), format, value)) < 0) {
5618                 zend_string_free(str);
5619                 RETURN_FALSE;
5620         }
5621         ZSTR_LEN(str) = (size_t)res_len;
5622         ZSTR_VAL(str)[ZSTR_LEN(str)] = '\0';
5623 
5624         RETURN_NEW_STR(zend_string_truncate(str, ZSTR_LEN(str), 0));
5625 }
5626 /* }}} */
5627 #endif
5628 
5629 /* {{{ proto array str_split(string str [, int split_length])
5630    Convert a string to an array. If split_length is specified, break the string down into chunks each split_length characters long. */
5631 PHP_FUNCTION(str_split)
5632 {
5633         zend_string *str;
5634         zend_long split_length = 1;
5635         char *p;
5636         size_t n_reg_segments;
5637 
5638         if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|l", &str, &split_length) == FAILURE) {
5639                 return;
5640         }
5641 
5642         if (split_length <= 0) {
5643                 php_error_docref(NULL, E_WARNING, "The length of each segment must be greater than zero");
5644                 RETURN_FALSE;
5645         }
5646 
5647 
5648         if (0 == ZSTR_LEN(str) || (size_t)split_length >= ZSTR_LEN(str)) {
5649                 array_init_size(return_value, 1);
5650                 add_next_index_stringl(return_value, ZSTR_VAL(str), ZSTR_LEN(str));
5651                 return;
5652         }
5653 
5654         array_init_size(return_value, (uint32_t)(((ZSTR_LEN(str) - 1) / split_length) + 1));
5655 
5656         n_reg_segments = ZSTR_LEN(str) / split_length;
5657         p = ZSTR_VAL(str);
5658 
5659         while (n_reg_segments-- > 0) {
5660                 add_next_index_stringl(return_value, p, split_length);
5661                 p += split_length;
5662         }
5663 
5664         if (p != (ZSTR_VAL(str) + ZSTR_LEN(str))) {
5665                 add_next_index_stringl(return_value, p, (ZSTR_VAL(str) + ZSTR_LEN(str) - p));
5666         }
5667 }
5668 /* }}} */
5669 
5670 /* {{{ proto array strpbrk(string haystack, string char_list)
5671    Search a string for any of a set of characters */
5672 PHP_FUNCTION(strpbrk)
5673 {
5674         zend_string *haystack, *char_list;
5675         char *haystack_ptr, *cl_ptr;
5676 
5677         if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &haystack, &char_list) == FAILURE) {
5678                 RETURN_FALSE;
5679         }
5680 
5681         if (!ZSTR_LEN(char_list)) {
5682                 php_error_docref(NULL, E_WARNING, "The character list cannot be empty");
5683                 RETURN_FALSE;
5684         }
5685 
5686         for (haystack_ptr = ZSTR_VAL(haystack); haystack_ptr < (ZSTR_VAL(haystack) + ZSTR_LEN(haystack)); ++haystack_ptr) {
5687                 for (cl_ptr = ZSTR_VAL(char_list); cl_ptr < (ZSTR_VAL(char_list) + ZSTR_LEN(char_list)); ++cl_ptr) {
5688                         if (*cl_ptr == *haystack_ptr) {
5689                                 RETURN_STRINGL(haystack_ptr, (ZSTR_VAL(haystack) + ZSTR_LEN(haystack) - haystack_ptr));
5690                         }
5691                 }
5692         }
5693 
5694         RETURN_FALSE;
5695 }
5696 /* }}} */
5697 
5698 /* {{{ proto int substr_compare(string main_str, string str, int offset [, int length [, bool case_sensitivity]])
5699    Binary safe optionally case insensitive comparison of 2 strings from an offset, up to length characters */
5700 PHP_FUNCTION(substr_compare)
5701 {
5702         zend_string *s1, *s2;
5703         zend_long offset, len=0;
5704         zend_bool cs=0;
5705         size_t cmp_len;
5706 
5707         if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSl|lb", &s1, &s2, &offset, &len, &cs) == FAILURE) {
5708                 RETURN_FALSE;
5709         }
5710 
5711         if (ZEND_NUM_ARGS() >= 4 && len <= 0) {
5712                 if (len == 0) {
5713                         RETURN_LONG(0L);
5714                 } else {
5715                         php_error_docref(NULL, E_WARNING, "The length must be greater than or equal to zero");
5716                         RETURN_FALSE;
5717                 }
5718         }
5719 
5720         if (offset < 0) {
5721                 offset = ZSTR_LEN(s1) + offset;
5722                 offset = (offset < 0) ? 0 : offset;
5723         }
5724 
5725         if ((size_t)offset >= ZSTR_LEN(s1)) {
5726                 php_error_docref(NULL, E_WARNING, "The start position cannot exceed initial string length");
5727                 RETURN_FALSE;
5728         }
5729 
5730         cmp_len = (size_t) (len ? len : MAX(ZSTR_LEN(s2), (ZSTR_LEN(s1) - offset)));
5731 
5732         if (!cs) {
5733                 RETURN_LONG(zend_binary_strncmp(ZSTR_VAL(s1) + offset, (ZSTR_LEN(s1) - offset), ZSTR_VAL(s2), ZSTR_LEN(s2), cmp_len));
5734         } else {
5735                 RETURN_LONG(zend_binary_strncasecmp_l(ZSTR_VAL(s1) + offset, (ZSTR_LEN(s1) - offset), ZSTR_VAL(s2), ZSTR_LEN(s2), cmp_len));
5736         }
5737 }
5738 /* }}} */
5739 
5740 /*
5741  * Local variables:
5742  * tab-width: 4
5743  * c-basic-offset: 4
5744  * End:
5745  * vim600: noet sw=4 ts=4 fdm=marker
5746  * vim<600: noet sw=4 ts=4
5747  */

/* [<][>][^][v][top][bottom][index][help] */