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