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