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