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