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