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_FUNCTION
  30. php_strtolower
  31. PHP_FUNCTION
  32. php_basename
  33. PHP_FUNCTION
  34. php_dirname
  35. PHP_FUNCTION
  36. PHP_FUNCTION
  37. php_stristr
  38. php_strspn
  39. php_strcspn
  40. php_needle_char
  41. PHP_FUNCTION
  42. PHP_FUNCTION
  43. PHP_FUNCTION
  44. PHP_FUNCTION
  45. PHP_FUNCTION
  46. PHP_FUNCTION
  47. PHP_FUNCTION
  48. php_chunk_split
  49. PHP_FUNCTION
  50. PHP_FUNCTION
  51. PHP_FUNCTION
  52. PHP_FUNCTION
  53. PHP_FUNCTION
  54. PHP_FUNCTION
  55. php_ucfirst
  56. PHP_FUNCTION
  57. php_lcfirst
  58. PHP_FUNCTION
  59. PHP_FUNCTION
  60. php_strtr
  61. php_strtr_hash
  62. php_strtr_populate_shift
  63. php_strtr_compare_hash_suffix
  64. php_strtr_free_strp
  65. php_strtr_array_prepare_repls
  66. php_strtr_array_prepare
  67. php_strtr_array_destroy_ppres
  68. php_strtr_array_do_repl
  69. php_strtr_array
  70. PHP_FUNCTION
  71. PHP_FUNCTION
  72. php_similar_str
  73. php_similar_char
  74. PHP_FUNCTION
  75. php_stripslashes
  76. PHP_FUNCTION
  77. PHP_FUNCTION
  78. PHP_FUNCTION
  79. PHP_FUNCTION
  80. php_strerror
  81. php_stripcslashes
  82. php_addcslashes
  83. php_addslashes
  84. php_char_to_str_ex
  85. php_char_to_str
  86. php_str_to_str_ex
  87. php_str_to_str
  88. php_str_replace_in_subject
  89. php_str_replace_common
  90. PHP_FUNCTION
  91. PHP_FUNCTION
  92. php_hebrev
  93. PHP_FUNCTION
  94. PHP_FUNCTION
  95. PHP_FUNCTION
  96. PHP_FUNCTION
  97. PHP_FUNCTION
  98. PHP_FUNCTION
  99. php_tag_find
  100. php_strip_tags
  101. php_strip_tags_ex
  102. PHP_FUNCTION
  103. PHP_FUNCTION
  104. PHP_FUNCTION
  105. php_strnatcmp
  106. string_natural_compare_function_ex
  107. string_natural_case_compare_function
  108. string_natural_compare_function
  109. PHP_FUNCTION
  110. PHP_FUNCTION
  111. PHP_FUNCTION
  112. PHP_FUNCTION
  113. PHP_FUNCTION
  114. PHP_FUNCTION
  115. PHP_FUNCTION
  116. php_string_shuffle
  117. PHP_FUNCTION
  118. PHP_FUNCTION
  119. PHP_FUNCTION
  120. PHP_FUNCTION
  121. PHP_FUNCTION
  122. PHP_FUNCTION

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

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