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