xref: /PHP-5.5/ext/standard/string.c (revision 25bd11cf)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2015 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 /* Synced with php 3.0 revision 1.193 1999-06-16 [ssb] */
24 
25 #include <stdio.h>
26 #ifdef PHP_WIN32
27 # include "win32/php_stdint.h"
28 #else
29 # include <stdint.h>
30 #endif
31 #include "php.h"
32 #include "php_rand.h"
33 #include "php_string.h"
34 #include "php_variables.h"
35 #ifdef HAVE_LOCALE_H
36 # include <locale.h>
37 #endif
38 #ifdef HAVE_LANGINFO_H
39 # include <langinfo.h>
40 #endif
41 #ifdef HAVE_MONETARY_H
42 # include <monetary.h>
43 #endif
44 /*
45  * This define is here because some versions of libintl redefine setlocale
46  * to point to libintl_setlocale.  That's a ridiculous thing to do as far
47  * as I am concerned, but with this define and the subsequent undef we
48  * limit the damage to just the actual setlocale() call in this file
49  * without turning zif_setlocale into zif_libintl_setlocale.  -Rasmus
50  */
51 #define php_my_setlocale setlocale
52 #ifdef HAVE_LIBINTL
53 # include <libintl.h> /* For LC_MESSAGES */
54  #ifdef setlocale
55  # undef setlocale
56  #endif
57 #endif
58 
59 #include "scanf.h"
60 #include "zend_API.h"
61 #include "zend_execute.h"
62 #include "php_globals.h"
63 #include "basic_functions.h"
64 #include "php_smart_str.h"
65 #include <Zend/zend_exceptions.h>
66 #ifdef ZTS
67 #include "TSRM.h"
68 #endif
69 
70 /* For str_getcsv() support */
71 #include "ext/standard/file.h"
72 
73 #define STR_PAD_LEFT			0
74 #define STR_PAD_RIGHT			1
75 #define STR_PAD_BOTH			2
76 #define PHP_PATHINFO_DIRNAME 	1
77 #define PHP_PATHINFO_BASENAME 	2
78 #define PHP_PATHINFO_EXTENSION 	4
79 #define PHP_PATHINFO_FILENAME 	8
80 #define PHP_PATHINFO_ALL	(PHP_PATHINFO_DIRNAME | PHP_PATHINFO_BASENAME | PHP_PATHINFO_EXTENSION | PHP_PATHINFO_FILENAME)
81 
82 #define STR_STRSPN				0
83 #define STR_STRCSPN				1
84 
85 /* {{{ register_string_constants
86  */
register_string_constants(INIT_FUNC_ARGS)87 void register_string_constants(INIT_FUNC_ARGS)
88 {
89 	REGISTER_LONG_CONSTANT("STR_PAD_LEFT", STR_PAD_LEFT, CONST_CS | CONST_PERSISTENT);
90 	REGISTER_LONG_CONSTANT("STR_PAD_RIGHT", STR_PAD_RIGHT, CONST_CS | CONST_PERSISTENT);
91 	REGISTER_LONG_CONSTANT("STR_PAD_BOTH", STR_PAD_BOTH, CONST_CS | CONST_PERSISTENT);
92 	REGISTER_LONG_CONSTANT("PATHINFO_DIRNAME", PHP_PATHINFO_DIRNAME, CONST_CS | CONST_PERSISTENT);
93 	REGISTER_LONG_CONSTANT("PATHINFO_BASENAME", PHP_PATHINFO_BASENAME, CONST_CS | CONST_PERSISTENT);
94 	REGISTER_LONG_CONSTANT("PATHINFO_EXTENSION", PHP_PATHINFO_EXTENSION, CONST_CS | CONST_PERSISTENT);
95 	REGISTER_LONG_CONSTANT("PATHINFO_FILENAME", PHP_PATHINFO_FILENAME, CONST_CS | CONST_PERSISTENT);
96 
97 #ifdef HAVE_LOCALECONV
98 	/* If last members of struct lconv equal CHAR_MAX, no grouping is done */
99 
100 /* This is bad, but since we are going to be hardcoding in the POSIX stuff anyway... */
101 # ifndef HAVE_LIMITS_H
102 # define CHAR_MAX 127
103 # endif
104 
105 	REGISTER_LONG_CONSTANT("CHAR_MAX", CHAR_MAX, CONST_CS | CONST_PERSISTENT);
106 #endif
107 
108 #ifdef HAVE_LOCALE_H
109 	REGISTER_LONG_CONSTANT("LC_CTYPE", LC_CTYPE, CONST_CS | CONST_PERSISTENT);
110 	REGISTER_LONG_CONSTANT("LC_NUMERIC", LC_NUMERIC, CONST_CS | CONST_PERSISTENT);
111 	REGISTER_LONG_CONSTANT("LC_TIME", LC_TIME, CONST_CS | CONST_PERSISTENT);
112 	REGISTER_LONG_CONSTANT("LC_COLLATE", LC_COLLATE, CONST_CS | CONST_PERSISTENT);
113 	REGISTER_LONG_CONSTANT("LC_MONETARY", LC_MONETARY, CONST_CS | CONST_PERSISTENT);
114 	REGISTER_LONG_CONSTANT("LC_ALL", LC_ALL, CONST_CS | CONST_PERSISTENT);
115 # ifdef LC_MESSAGES
116 	REGISTER_LONG_CONSTANT("LC_MESSAGES", LC_MESSAGES, CONST_CS | CONST_PERSISTENT);
117 # endif
118 #endif
119 
120 }
121 /* }}} */
122 
123 int php_tag_find(char *tag, int len, char *set);
124 
125 /* this is read-only, so it's ok */
126 static char hexconvtab[] = "0123456789abcdef";
127 
128 /* localeconv mutex */
129 #ifdef ZTS
130 static MUTEX_T locale_mutex = NULL;
131 #endif
132 
133 /* {{{ php_bin2hex
134  */
php_bin2hex(const unsigned char * old,const size_t oldlen,size_t * newlen)135 static char *php_bin2hex(const unsigned char *old, const size_t oldlen, size_t *newlen)
136 {
137 	register unsigned char *result = NULL;
138 	size_t i, j;
139 
140 	if (UNEXPECTED(oldlen * 2 * sizeof(char) > INT_MAX)) {
141 		zend_error(E_ERROR, "String size overflow");
142 	}
143 	result = (unsigned char *) safe_emalloc(oldlen, 2 * sizeof(char), 1);
144 
145 	for (i = j = 0; i < oldlen; i++) {
146 		result[j++] = hexconvtab[old[i] >> 4];
147 		result[j++] = hexconvtab[old[i] & 15];
148 	}
149 	result[j] = '\0';
150 
151 	if (newlen)
152 		*newlen = oldlen * 2 * sizeof(char);
153 
154 	return (char *)result;
155 }
156 /* }}} */
157 
158 /* {{{ php_hex2bin
159  */
php_hex2bin(const unsigned char * old,const size_t oldlen,size_t * newlen)160 static char *php_hex2bin(const unsigned char *old, const size_t oldlen, size_t *newlen)
161 {
162 	size_t target_length = oldlen >> 1;
163 	register unsigned char *str = (unsigned char *)safe_emalloc(target_length, sizeof(char), 1);
164 	size_t i, j;
165 	for (i = j = 0; i < target_length; i++) {
166 		char c = old[j++];
167 		if (c >= '0' && c <= '9') {
168 			str[i] = (c - '0') << 4;
169 		} else if (c >= 'a' && c <= 'f') {
170 			str[i] = (c - 'a' + 10) << 4;
171 		} else if (c >= 'A' && c <= 'F') {
172 			str[i] = (c - 'A' + 10) << 4;
173 		} else {
174 			efree(str);
175 			return NULL;
176 		}
177 		c = old[j++];
178 		if (c >= '0' && c <= '9') {
179 			str[i] |= c - '0';
180 		} else if (c >= 'a' && c <= 'f') {
181 			str[i] |= c - 'a' + 10;
182 		} else if (c >= 'A' && c <= 'F') {
183 			str[i] |= c - 'A' + 10;
184 		} else {
185 			efree(str);
186 			return NULL;
187 		}
188 	}
189 	str[target_length] = '\0';
190 
191 	if (newlen)
192 		*newlen = target_length;
193 
194 	return (char *)str;
195 }
196 /* }}} */
197 
198 #ifdef HAVE_LOCALECONV
199 /* {{{ localeconv_r
200  * glibc's localeconv is not reentrant, so lets make it so ... sorta */
localeconv_r(struct lconv * out)201 PHPAPI struct lconv *localeconv_r(struct lconv *out)
202 {
203 	struct lconv *res;
204 
205 # ifdef ZTS
206 	tsrm_mutex_lock( locale_mutex );
207 # endif
208 
209 #if defined(PHP_WIN32) && defined(ZTS)
210 	{
211 		/* Even with the enabled per thread locale, localeconv
212 			won't check any locale change in the master thread. */
213 		_locale_t cur = _get_current_locale();
214 
215 		res = cur->locinfo->lconv;
216 	}
217 #else
218 	/* localeconv doesn't return an error condition */
219 	res = localeconv();
220 #endif
221 
222 	*out = *res;
223 
224 # ifdef ZTS
225 	tsrm_mutex_unlock( locale_mutex );
226 # endif
227 
228 	return out;
229 }
230 /* }}} */
231 
232 # ifdef ZTS
233 /* {{{ PHP_MINIT_FUNCTION
234  */
PHP_MINIT_FUNCTION(localeconv)235 PHP_MINIT_FUNCTION(localeconv)
236 {
237 	locale_mutex = tsrm_mutex_alloc();
238 	return SUCCESS;
239 }
240 /* }}} */
241 
242 /* {{{ PHP_MSHUTDOWN_FUNCTION
243  */
PHP_MSHUTDOWN_FUNCTION(localeconv)244 PHP_MSHUTDOWN_FUNCTION(localeconv)
245 {
246 	tsrm_mutex_free( locale_mutex );
247 	locale_mutex = NULL;
248 	return SUCCESS;
249 }
250 /* }}} */
251 # endif
252 #endif
253 
254 /* {{{ proto string bin2hex(string data)
255    Converts the binary representation of data to hex */
PHP_FUNCTION(bin2hex)256 PHP_FUNCTION(bin2hex)
257 {
258 	char *result, *data;
259 	size_t newlen;
260 	int datalen;
261 
262 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data, &datalen) == FAILURE) {
263 		return;
264 	}
265 
266 	result = php_bin2hex((unsigned char *)data, datalen, &newlen);
267 
268 	if (!result) {
269 		RETURN_FALSE;
270 	}
271 
272 	RETURN_STRINGL(result, newlen, 0);
273 }
274 /* }}} */
275 
276 /* {{{ proto string hex2bin(string data)
277    Converts the hex representation of data to binary */
PHP_FUNCTION(hex2bin)278 PHP_FUNCTION(hex2bin)
279 {
280 	char *result, *data;
281 	size_t newlen;
282 	int datalen;
283 
284 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data, &datalen) == FAILURE) {
285 		return;
286 	}
287 
288 	if (datalen % 2 != 0) {
289 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Hexadecimal input string must have an even length");
290 		RETURN_FALSE;
291 	}
292 
293 	result = php_hex2bin((unsigned char *)data, datalen, &newlen);
294 
295 	if (!result) {
296 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Input string must be hexadecimal string");
297 		RETURN_FALSE;
298 	}
299 
300 	RETURN_STRINGL(result, newlen, 0);
301 }
302 /* }}} */
303 
php_spn_common_handler(INTERNAL_FUNCTION_PARAMETERS,int behavior)304 static void php_spn_common_handler(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
305 {
306 	char *s11, *s22;
307 	int len1, len2;
308 	long start = 0, len = 0;
309 
310 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|ll", &s11, &len1,
311 				&s22, &len2, &start, &len) == FAILURE) {
312 		return;
313 	}
314 
315 	if (ZEND_NUM_ARGS() < 4) {
316 		len = len1;
317 	}
318 
319 	/* look at substr() function for more information */
320 
321 	if (start < 0) {
322 		start += len1;
323 		if (start < 0) {
324 			start = 0;
325 		}
326 	} else if (start > len1) {
327 		RETURN_FALSE;
328 	}
329 
330 	if (len < 0) {
331 		len += (len1 - start);
332 		if (len < 0) {
333 			len = 0;
334 		}
335 	}
336 
337 	if (len > len1 - start) {
338 		len = len1 - start;
339 	}
340 
341 	if(len == 0) {
342 		RETURN_LONG(0);
343 	}
344 
345 	if (behavior == STR_STRSPN) {
346 		RETURN_LONG(php_strspn(s11 + start /*str1_start*/,
347 						s22 /*str2_start*/,
348 						s11 + start + len /*str1_end*/,
349 						s22 + len2 /*str2_end*/));
350 	} else if (behavior == STR_STRCSPN) {
351 		RETURN_LONG(php_strcspn(s11 + start /*str1_start*/,
352 						s22 /*str2_start*/,
353 						s11 + start + len /*str1_end*/,
354 						s22 + len2 /*str2_end*/));
355 	}
356 
357 }
358 /* }}} */
359 
360 /* {{{ proto int strspn(string str, string mask [, start [, len]])
361    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)362 PHP_FUNCTION(strspn)
363 {
364 	php_spn_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, STR_STRSPN);
365 }
366 /* }}} */
367 
368 /* {{{ proto int strcspn(string str, string mask [, start [, len]])
369    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)370 PHP_FUNCTION(strcspn)
371 {
372 	php_spn_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, STR_STRCSPN);
373 }
374 /* }}} */
375 
376 /* {{{ PHP_MINIT_FUNCTION(nl_langinfo) */
377 #if HAVE_NL_LANGINFO
PHP_MINIT_FUNCTION(nl_langinfo)378 PHP_MINIT_FUNCTION(nl_langinfo)
379 {
380 #define REGISTER_NL_LANGINFO_CONSTANT(x)	REGISTER_LONG_CONSTANT(#x, x, CONST_CS | CONST_PERSISTENT)
381 #ifdef ABDAY_1
382 	REGISTER_NL_LANGINFO_CONSTANT(ABDAY_1);
383 	REGISTER_NL_LANGINFO_CONSTANT(ABDAY_2);
384 	REGISTER_NL_LANGINFO_CONSTANT(ABDAY_3);
385 	REGISTER_NL_LANGINFO_CONSTANT(ABDAY_4);
386 	REGISTER_NL_LANGINFO_CONSTANT(ABDAY_5);
387 	REGISTER_NL_LANGINFO_CONSTANT(ABDAY_6);
388 	REGISTER_NL_LANGINFO_CONSTANT(ABDAY_7);
389 #endif
390 #ifdef DAY_1
391 	REGISTER_NL_LANGINFO_CONSTANT(DAY_1);
392 	REGISTER_NL_LANGINFO_CONSTANT(DAY_2);
393 	REGISTER_NL_LANGINFO_CONSTANT(DAY_3);
394 	REGISTER_NL_LANGINFO_CONSTANT(DAY_4);
395 	REGISTER_NL_LANGINFO_CONSTANT(DAY_5);
396 	REGISTER_NL_LANGINFO_CONSTANT(DAY_6);
397 	REGISTER_NL_LANGINFO_CONSTANT(DAY_7);
398 #endif
399 #ifdef ABMON_1
400 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_1);
401 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_2);
402 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_3);
403 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_4);
404 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_5);
405 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_6);
406 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_7);
407 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_8);
408 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_9);
409 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_10);
410 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_11);
411 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_12);
412 #endif
413 #ifdef MON_1
414 	REGISTER_NL_LANGINFO_CONSTANT(MON_1);
415 	REGISTER_NL_LANGINFO_CONSTANT(MON_2);
416 	REGISTER_NL_LANGINFO_CONSTANT(MON_3);
417 	REGISTER_NL_LANGINFO_CONSTANT(MON_4);
418 	REGISTER_NL_LANGINFO_CONSTANT(MON_5);
419 	REGISTER_NL_LANGINFO_CONSTANT(MON_6);
420 	REGISTER_NL_LANGINFO_CONSTANT(MON_7);
421 	REGISTER_NL_LANGINFO_CONSTANT(MON_8);
422 	REGISTER_NL_LANGINFO_CONSTANT(MON_9);
423 	REGISTER_NL_LANGINFO_CONSTANT(MON_10);
424 	REGISTER_NL_LANGINFO_CONSTANT(MON_11);
425 	REGISTER_NL_LANGINFO_CONSTANT(MON_12);
426 #endif
427 #ifdef AM_STR
428 	REGISTER_NL_LANGINFO_CONSTANT(AM_STR);
429 #endif
430 #ifdef PM_STR
431 	REGISTER_NL_LANGINFO_CONSTANT(PM_STR);
432 #endif
433 #ifdef D_T_FMT
434 	REGISTER_NL_LANGINFO_CONSTANT(D_T_FMT);
435 #endif
436 #ifdef D_FMT
437 	REGISTER_NL_LANGINFO_CONSTANT(D_FMT);
438 #endif
439 #ifdef T_FMT
440 	REGISTER_NL_LANGINFO_CONSTANT(T_FMT);
441 #endif
442 #ifdef T_FMT_AMPM
443 	REGISTER_NL_LANGINFO_CONSTANT(T_FMT_AMPM);
444 #endif
445 #ifdef ERA
446 	REGISTER_NL_LANGINFO_CONSTANT(ERA);
447 #endif
448 #ifdef ERA_YEAR
449 	REGISTER_NL_LANGINFO_CONSTANT(ERA_YEAR);
450 #endif
451 #ifdef ERA_D_T_FMT
452 	REGISTER_NL_LANGINFO_CONSTANT(ERA_D_T_FMT);
453 #endif
454 #ifdef ERA_D_FMT
455 	REGISTER_NL_LANGINFO_CONSTANT(ERA_D_FMT);
456 #endif
457 #ifdef ERA_T_FMT
458 	REGISTER_NL_LANGINFO_CONSTANT(ERA_T_FMT);
459 #endif
460 #ifdef ALT_DIGITS
461 	REGISTER_NL_LANGINFO_CONSTANT(ALT_DIGITS);
462 #endif
463 #ifdef INT_CURR_SYMBOL
464 	REGISTER_NL_LANGINFO_CONSTANT(INT_CURR_SYMBOL);
465 #endif
466 #ifdef CURRENCY_SYMBOL
467 	REGISTER_NL_LANGINFO_CONSTANT(CURRENCY_SYMBOL);
468 #endif
469 #ifdef CRNCYSTR
470 	REGISTER_NL_LANGINFO_CONSTANT(CRNCYSTR);
471 #endif
472 #ifdef MON_DECIMAL_POINT
473 	REGISTER_NL_LANGINFO_CONSTANT(MON_DECIMAL_POINT);
474 #endif
475 #ifdef MON_THOUSANDS_SEP
476 	REGISTER_NL_LANGINFO_CONSTANT(MON_THOUSANDS_SEP);
477 #endif
478 #ifdef MON_GROUPING
479 	REGISTER_NL_LANGINFO_CONSTANT(MON_GROUPING);
480 #endif
481 #ifdef POSITIVE_SIGN
482 	REGISTER_NL_LANGINFO_CONSTANT(POSITIVE_SIGN);
483 #endif
484 #ifdef NEGATIVE_SIGN
485 	REGISTER_NL_LANGINFO_CONSTANT(NEGATIVE_SIGN);
486 #endif
487 #ifdef INT_FRAC_DIGITS
488 	REGISTER_NL_LANGINFO_CONSTANT(INT_FRAC_DIGITS);
489 #endif
490 #ifdef FRAC_DIGITS
491 	REGISTER_NL_LANGINFO_CONSTANT(FRAC_DIGITS);
492 #endif
493 #ifdef P_CS_PRECEDES
494 	REGISTER_NL_LANGINFO_CONSTANT(P_CS_PRECEDES);
495 #endif
496 #ifdef P_SEP_BY_SPACE
497 	REGISTER_NL_LANGINFO_CONSTANT(P_SEP_BY_SPACE);
498 #endif
499 #ifdef N_CS_PRECEDES
500 	REGISTER_NL_LANGINFO_CONSTANT(N_CS_PRECEDES);
501 #endif
502 #ifdef N_SEP_BY_SPACE
503 	REGISTER_NL_LANGINFO_CONSTANT(N_SEP_BY_SPACE);
504 #endif
505 #ifdef P_SIGN_POSN
506 	REGISTER_NL_LANGINFO_CONSTANT(P_SIGN_POSN);
507 #endif
508 #ifdef N_SIGN_POSN
509 	REGISTER_NL_LANGINFO_CONSTANT(N_SIGN_POSN);
510 #endif
511 #ifdef DECIMAL_POINT
512 	REGISTER_NL_LANGINFO_CONSTANT(DECIMAL_POINT);
513 #endif
514 #ifdef RADIXCHAR
515 	REGISTER_NL_LANGINFO_CONSTANT(RADIXCHAR);
516 #endif
517 #ifdef THOUSANDS_SEP
518 	REGISTER_NL_LANGINFO_CONSTANT(THOUSANDS_SEP);
519 #endif
520 #ifdef THOUSEP
521 	REGISTER_NL_LANGINFO_CONSTANT(THOUSEP);
522 #endif
523 #ifdef GROUPING
524 	REGISTER_NL_LANGINFO_CONSTANT(GROUPING);
525 #endif
526 #ifdef YESEXPR
527 	REGISTER_NL_LANGINFO_CONSTANT(YESEXPR);
528 #endif
529 #ifdef NOEXPR
530 	REGISTER_NL_LANGINFO_CONSTANT(NOEXPR);
531 #endif
532 #ifdef YESSTR
533 	REGISTER_NL_LANGINFO_CONSTANT(YESSTR);
534 #endif
535 #ifdef NOSTR
536 	REGISTER_NL_LANGINFO_CONSTANT(NOSTR);
537 #endif
538 #ifdef CODESET
539 	REGISTER_NL_LANGINFO_CONSTANT(CODESET);
540 #endif
541 #undef REGISTER_NL_LANGINFO_CONSTANT
542 	return SUCCESS;
543 }
544 /* }}} */
545 
546 /* {{{ proto string nl_langinfo(int item)
547    Query language and locale information */
PHP_FUNCTION(nl_langinfo)548 PHP_FUNCTION(nl_langinfo)
549 {
550 	long item;
551 	char *value;
552 
553 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &item) == FAILURE) {
554 		return;
555 	}
556 
557 	switch(item) { /* {{{ */
558 #ifdef ABDAY_1
559 		case ABDAY_1:
560 		case ABDAY_2:
561 		case ABDAY_3:
562 		case ABDAY_4:
563 		case ABDAY_5:
564 		case ABDAY_6:
565 		case ABDAY_7:
566 #endif
567 #ifdef DAY_1
568 		case DAY_1:
569 		case DAY_2:
570 		case DAY_3:
571 		case DAY_4:
572 		case DAY_5:
573 		case DAY_6:
574 		case DAY_7:
575 #endif
576 #ifdef ABMON_1
577 		case ABMON_1:
578 		case ABMON_2:
579 		case ABMON_3:
580 		case ABMON_4:
581 		case ABMON_5:
582 		case ABMON_6:
583 		case ABMON_7:
584 		case ABMON_8:
585 		case ABMON_9:
586 		case ABMON_10:
587 		case ABMON_11:
588 		case ABMON_12:
589 #endif
590 #ifdef MON_1
591 		case MON_1:
592 		case MON_2:
593 		case MON_3:
594 		case MON_4:
595 		case MON_5:
596 		case MON_6:
597 		case MON_7:
598 		case MON_8:
599 		case MON_9:
600 		case MON_10:
601 		case MON_11:
602 		case MON_12:
603 #endif
604 #ifdef AM_STR
605 		case AM_STR:
606 #endif
607 #ifdef PM_STR
608 		case PM_STR:
609 #endif
610 #ifdef D_T_FMT
611 		case D_T_FMT:
612 #endif
613 #ifdef D_FMT
614 		case D_FMT:
615 #endif
616 #ifdef T_FMT
617 		case T_FMT:
618 #endif
619 #ifdef T_FMT_AMPM
620 		case T_FMT_AMPM:
621 #endif
622 #ifdef ERA
623 		case ERA:
624 #endif
625 #ifdef ERA_YEAR
626 		case ERA_YEAR:
627 #endif
628 #ifdef ERA_D_T_FMT
629 		case ERA_D_T_FMT:
630 #endif
631 #ifdef ERA_D_FMT
632 		case ERA_D_FMT:
633 #endif
634 #ifdef ERA_T_FMT
635 		case ERA_T_FMT:
636 #endif
637 #ifdef ALT_DIGITS
638 		case ALT_DIGITS:
639 #endif
640 #ifdef INT_CURR_SYMBOL
641 		case INT_CURR_SYMBOL:
642 #endif
643 #ifdef CURRENCY_SYMBOL
644 		case CURRENCY_SYMBOL:
645 #endif
646 #ifdef CRNCYSTR
647 		case CRNCYSTR:
648 #endif
649 #ifdef MON_DECIMAL_POINT
650 		case MON_DECIMAL_POINT:
651 #endif
652 #ifdef MON_THOUSANDS_SEP
653 		case MON_THOUSANDS_SEP:
654 #endif
655 #ifdef MON_GROUPING
656 		case MON_GROUPING:
657 #endif
658 #ifdef POSITIVE_SIGN
659 		case POSITIVE_SIGN:
660 #endif
661 #ifdef NEGATIVE_SIGN
662 		case NEGATIVE_SIGN:
663 #endif
664 #ifdef INT_FRAC_DIGITS
665 		case INT_FRAC_DIGITS:
666 #endif
667 #ifdef FRAC_DIGITS
668 		case FRAC_DIGITS:
669 #endif
670 #ifdef P_CS_PRECEDES
671 		case P_CS_PRECEDES:
672 #endif
673 #ifdef P_SEP_BY_SPACE
674 		case P_SEP_BY_SPACE:
675 #endif
676 #ifdef N_CS_PRECEDES
677 		case N_CS_PRECEDES:
678 #endif
679 #ifdef N_SEP_BY_SPACE
680 		case N_SEP_BY_SPACE:
681 #endif
682 #ifdef P_SIGN_POSN
683 		case P_SIGN_POSN:
684 #endif
685 #ifdef N_SIGN_POSN
686 		case N_SIGN_POSN:
687 #endif
688 #ifdef DECIMAL_POINT
689 		case DECIMAL_POINT:
690 #elif defined(RADIXCHAR)
691 		case RADIXCHAR:
692 #endif
693 #ifdef THOUSANDS_SEP
694 		case THOUSANDS_SEP:
695 #elif defined(THOUSEP)
696 		case THOUSEP:
697 #endif
698 #ifdef GROUPING
699 		case GROUPING:
700 #endif
701 #ifdef YESEXPR
702 		case YESEXPR:
703 #endif
704 #ifdef NOEXPR
705 		case NOEXPR:
706 #endif
707 #ifdef YESSTR
708 		case YESSTR:
709 #endif
710 #ifdef NOSTR
711 		case NOSTR:
712 #endif
713 #ifdef CODESET
714 		case CODESET:
715 #endif
716 			break;
717 		default:
718 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Item '%ld' is not valid", item);
719 			RETURN_FALSE;
720 	}
721 	/* }}} */
722 
723 	value = nl_langinfo(item);
724 	if (value == NULL) {
725 		RETURN_FALSE;
726 	} else {
727 		RETURN_STRING(value, 1);
728 	}
729 }
730 #endif
731 /* }}} */
732 
733 #ifdef HAVE_STRCOLL
734 /* {{{ proto int strcoll(string str1, string str2)
735    Compares two strings using the current locale */
PHP_FUNCTION(strcoll)736 PHP_FUNCTION(strcoll)
737 {
738 	char *s1, *s2;
739 	int s1len, s2len;
740 
741 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &s1, &s1len, &s2, &s2len) == FAILURE) {
742 		return;
743 	}
744 
745 	RETURN_LONG(strcoll((const char *) s1,
746 	                    (const char *) s2));
747 }
748 /* }}} */
749 #endif
750 
751 /* {{{ php_charmask
752  * Fills a 256-byte bytemask with input. You can specify a range like 'a..z',
753  * it needs to be incrementing.
754  * Returns: FAILURE/SUCCESS whether the input was correct (i.e. no range errors)
755  */
php_charmask(unsigned char * input,int len,char * mask TSRMLS_DC)756 static inline int php_charmask(unsigned char *input, int len, char *mask TSRMLS_DC)
757 {
758 	unsigned char *end;
759 	unsigned char c;
760 	int result = SUCCESS;
761 
762 	memset(mask, 0, 256);
763 	for (end = input+len; input < end; input++) {
764 		c=*input;
765 		if ((input+3 < end) && input[1] == '.' && input[2] == '.'
766 				&& input[3] >= c) {
767 			memset(mask+c, 1, input[3] - c + 1);
768 			input+=3;
769 		} else if ((input+1 < end) && input[0] == '.' && input[1] == '.') {
770 			/* Error, try to be as helpful as possible:
771 			   (a range ending/starting with '.' won't be captured here) */
772 			if (end-len >= input) { /* there was no 'left' char */
773 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid '..'-range, no character to the left of '..'");
774 				result = FAILURE;
775 				continue;
776 			}
777 			if (input+2 >= end) { /* there is no 'right' char */
778 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid '..'-range, no character to the right of '..'");
779 				result = FAILURE;
780 				continue;
781 			}
782 			if (input[-1] > input[2]) { /* wrong order */
783 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid '..'-range, '..'-range needs to be incrementing");
784 				result = FAILURE;
785 				continue;
786 			}
787 			/* FIXME: better error (a..b..c is the only left possibility?) */
788 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid '..'-range");
789 			result = FAILURE;
790 			continue;
791 		} else {
792 			mask[c]=1;
793 		}
794 	}
795 	return result;
796 }
797 /* }}} */
798 
799 /* {{{ php_trim()
800  * mode 1 : trim left
801  * mode 2 : trim right
802  * mode 3 : trim left and right
803  * what indicates which chars are to be trimmed. NULL->default (' \t\n\r\v\0')
804  */
php_trim(char * c,int len,char * what,int what_len,zval * return_value,int mode TSRMLS_DC)805 PHPAPI char *php_trim(char *c, int len, char *what, int what_len, zval *return_value, int mode TSRMLS_DC)
806 {
807 	register int i;
808 	int trimmed = 0;
809 	char mask[256];
810 
811 	if (what) {
812 		php_charmask((unsigned char*)what, what_len, mask TSRMLS_CC);
813 	} else {
814 		php_charmask((unsigned char*)" \n\r\t\v\0", 6, mask TSRMLS_CC);
815 	}
816 
817 	if (mode & 1) {
818 		for (i = 0; i < len; i++) {
819 			if (mask[(unsigned char)c[i]]) {
820 				trimmed++;
821 			} else {
822 				break;
823 			}
824 		}
825 		len -= trimmed;
826 		c += trimmed;
827 	}
828 	if (mode & 2) {
829 		for (i = len - 1; i >= 0; i--) {
830 			if (mask[(unsigned char)c[i]]) {
831 				len--;
832 			} else {
833 				break;
834 			}
835 		}
836 	}
837 
838 	if (return_value) {
839 		RETVAL_STRINGL(c, len, 1);
840 	} else {
841 		return estrndup(c, len);
842 	}
843 	return "";
844 }
845 /* }}} */
846 
847 /* {{{ php_do_trim
848  * Base for trim(), rtrim() and ltrim() functions.
849  */
php_do_trim(INTERNAL_FUNCTION_PARAMETERS,int mode)850 static void php_do_trim(INTERNAL_FUNCTION_PARAMETERS, int mode)
851 {
852 	char *str;
853 	char *what = NULL;
854 	int str_len, what_len = 0;
855 
856 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &str, &str_len, &what, &what_len) == FAILURE) {
857 		return;
858 	}
859 
860 	php_trim(str, str_len, what, what_len, return_value, mode TSRMLS_CC);
861 }
862 /* }}} */
863 
864 /* {{{ proto string trim(string str [, string character_mask])
865    Strips whitespace from the beginning and end of a string */
PHP_FUNCTION(trim)866 PHP_FUNCTION(trim)
867 {
868 	php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3);
869 }
870 /* }}} */
871 
872 /* {{{ proto string rtrim(string str [, string character_mask])
873    Removes trailing whitespace */
PHP_FUNCTION(rtrim)874 PHP_FUNCTION(rtrim)
875 {
876 	php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 2);
877 }
878 /* }}} */
879 
880 /* {{{ proto string ltrim(string str [, string character_mask])
881    Strips whitespace from the beginning of a string */
PHP_FUNCTION(ltrim)882 PHP_FUNCTION(ltrim)
883 {
884 	php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
885 }
886 /* }}} */
887 
888 /* {{{ proto string wordwrap(string str [, int width [, string break [, boolean cut]]])
889    Wraps buffer to selected number of characters using string break char */
PHP_FUNCTION(wordwrap)890 PHP_FUNCTION(wordwrap)
891 {
892 	const char *text, *breakchar = "\n";
893 	char *newtext;
894 	int textlen, breakcharlen = 1, newtextlen, chk;
895 	size_t alloced;
896 	long current = 0, laststart = 0, lastspace = 0;
897 	long linelength = 75;
898 	zend_bool docut = 0;
899 
900 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lsb", &text, &textlen, &linelength, &breakchar, &breakcharlen, &docut) == FAILURE) {
901 		return;
902 	}
903 
904 	if (textlen == 0) {
905 		RETURN_EMPTY_STRING();
906 	}
907 
908 	if (breakcharlen == 0) {
909 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Break string cannot be empty");
910 		RETURN_FALSE;
911 	}
912 
913 	if (linelength == 0 && docut) {
914 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't force cut when width is zero");
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 = textlen + chk * breakcharlen + 1;
946 		} else {
947 			chk = textlen;
948 			alloced = textlen * (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 		RETURN_STRINGL(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 + l) > Z_STRLEN_PP(str)) {
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 = emalloc(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 = emalloc(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 = emalloc(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 = emalloc(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(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);
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 	data = php_strtr_array_prepare(&text, patterns, patterns_len, 2, 2);
3143 	efree(patterns);
3144 	php_strtr_array_do_repl(&text, data, return_value);
3145 	php_strtr_array_destroy_ppres(data);
3146 	zend_llist_destroy(allocs);
3147 	efree(allocs);
3148 }
3149 /* }}} */
3150 
3151 /* {{{ proto string strtr(string str, string from[, string to])
3152    Translates characters in str using given translation tables */
3153 PHP_FUNCTION(strtr)
3154 {
3155 	zval **from;
3156 	char *str, *to = NULL;
3157 	int str_len, to_len = 0;
3158 	int ac = ZEND_NUM_ARGS();
3159 
3160 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sZ|s", &str, &str_len, &from, &to, &to_len) == FAILURE) {
3161 		return;
3162 	}
3163 
3164 	if (ac == 2 && Z_TYPE_PP(from) != IS_ARRAY) {
3165 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "The second argument is not an array");
3166 		RETURN_FALSE;
3167 	}
3168 
3169 	/* shortcut for empty string */
3170 	if (str_len == 0) {
3171 		RETURN_EMPTY_STRING();
3172 	}
3173 
3174 	if (ac == 2) {
3175 		php_strtr_array(return_value, str, str_len, HASH_OF(*from));
3176 	} else {
3177 		convert_to_string_ex(from);
3178 
3179 		ZVAL_STRINGL(return_value, str, str_len, 1);
3180 
3181 		php_strtr(Z_STRVAL_P(return_value),
3182 				  Z_STRLEN_P(return_value),
3183 				  Z_STRVAL_PP(from),
3184 				  to,
3185 				  MIN(Z_STRLEN_PP(from),
3186 				  to_len));
3187 	}
3188 }
3189 /* }}} */
3190 
3191 /* {{{ proto string strrev(string str)
3192    Reverse a string */
3193 PHP_FUNCTION(strrev)
3194 {
3195 	char *str;
3196 	char *e, *n, *p;
3197 	int  str_len;
3198 
3199 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
3200 		return;
3201 	}
3202 
3203 	n = emalloc(str_len+1);
3204 	p = n;
3205 
3206 	e = str + str_len;
3207 
3208 	while (--e>=str) {
3209 		*p++ = *e;
3210 	}
3211 
3212 	*p = '\0';
3213 
3214 	RETVAL_STRINGL(n, str_len, 0);
3215 }
3216 /* }}} */
3217 
3218 /* {{{ php_similar_str
3219  */
3220 static void php_similar_str(const char *txt1, int len1, const char *txt2, int len2, int *pos1, int *pos2, int *max)
3221 {
3222 	char *p, *q;
3223 	char *end1 = (char *) txt1 + len1;
3224 	char *end2 = (char *) txt2 + len2;
3225 	int l;
3226 
3227 	*max = 0;
3228 	for (p = (char *) txt1; p < end1; p++) {
3229 		for (q = (char *) txt2; q < end2; q++) {
3230 			for (l = 0; (p + l < end1) && (q + l < end2) && (p[l] == q[l]); l++);
3231 			if (l > *max) {
3232 				*max = l;
3233 				*pos1 = p - txt1;
3234 				*pos2 = q - txt2;
3235 			}
3236 		}
3237 	}
3238 }
3239 /* }}} */
3240 
3241 /* {{{ php_similar_char
3242  */
3243 static int php_similar_char(const char *txt1, int len1, const char *txt2, int len2)
3244 {
3245 	int sum;
3246 	int pos1 = 0, pos2 = 0, max;
3247 
3248 	php_similar_str(txt1, len1, txt2, len2, &pos1, &pos2, &max);
3249 	if ((sum = max)) {
3250 		if (pos1 && pos2) {
3251 			sum += php_similar_char(txt1, pos1,
3252 									txt2, pos2);
3253 		}
3254 		if ((pos1 + max < len1) && (pos2 + max < len2)) {
3255 			sum += php_similar_char(txt1 + pos1 + max, len1 - pos1 - max,
3256 									txt2 + pos2 + max, len2 - pos2 - max);
3257 		}
3258 	}
3259 
3260 	return sum;
3261 }
3262 /* }}} */
3263 
3264 /* {{{ proto int similar_text(string str1, string str2 [, float percent])
3265    Calculates the similarity between two strings */
3266 PHP_FUNCTION(similar_text)
3267 {
3268 	char *t1, *t2;
3269 	zval **percent = NULL;
3270 	int ac = ZEND_NUM_ARGS();
3271 	int sim;
3272 	int t1_len, t2_len;
3273 
3274 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|Z", &t1, &t1_len, &t2, &t2_len, &percent) == FAILURE) {
3275 		return;
3276 	}
3277 
3278 	if (ac > 2) {
3279 		convert_to_double_ex(percent);
3280 	}
3281 
3282 	if (t1_len + t2_len == 0) {
3283 		if (ac > 2) {
3284 			Z_DVAL_PP(percent) = 0;
3285 		}
3286 
3287 		RETURN_LONG(0);
3288 	}
3289 
3290 	sim = php_similar_char(t1, t1_len, t2, t2_len);
3291 
3292 	if (ac > 2) {
3293 		Z_DVAL_PP(percent) = sim * 200.0 / (t1_len + t2_len);
3294 	}
3295 
3296 	RETURN_LONG(sim);
3297 }
3298 /* }}} */
3299 
3300 /* {{{ php_stripslashes
3301  *
3302  * be careful, this edits the string in-place */
3303 PHPAPI void php_stripslashes(char *str, int *len TSRMLS_DC)
3304 {
3305 	char *s, *t;
3306 	int l;
3307 
3308 	if (len != NULL) {
3309 		l = *len;
3310 	} else {
3311 		l = strlen(str);
3312 	}
3313 	s = str;
3314 	t = str;
3315 
3316 	while (l > 0) {
3317 		if (*t == '\\') {
3318 			t++;				/* skip the slash */
3319 			if (len != NULL) {
3320 				(*len)--;
3321 			}
3322 			l--;
3323 			if (l > 0) {
3324 				if (*t == '0') {
3325 					*s++='\0';
3326 					t++;
3327 				} else {
3328 					*s++ = *t++;	/* preserve the next character */
3329 				}
3330 				l--;
3331 			}
3332 		} else {
3333 			*s++ = *t++;
3334 			l--;
3335 		}
3336 	}
3337 	if (s != t) {
3338 		*s = '\0';
3339 	}
3340 }
3341 /* }}} */
3342 
3343 /* {{{ proto string addcslashes(string str, string charlist)
3344    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...) */
3345 PHP_FUNCTION(addcslashes)
3346 {
3347 	char *str, *what;
3348 	int str_len, what_len;
3349 
3350 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &str, &str_len, &what, &what_len) == FAILURE) {
3351 		return;
3352 	}
3353 
3354 	if (str_len == 0) {
3355 		RETURN_EMPTY_STRING();
3356 	}
3357 
3358 	if (what_len == 0) {
3359 		RETURN_STRINGL(str, str_len, 1);
3360 	}
3361 
3362 	Z_STRVAL_P(return_value) = php_addcslashes(str, str_len, &Z_STRLEN_P(return_value), 0, what, what_len TSRMLS_CC);
3363 	RETURN_STRINGL(Z_STRVAL_P(return_value), Z_STRLEN_P(return_value), 0);
3364 }
3365 /* }}} */
3366 
3367 /* {{{ proto string addslashes(string str)
3368    Escapes single quote, double quotes and backslash characters in a string with backslashes */
3369 PHP_FUNCTION(addslashes)
3370 {
3371 	char *str;
3372 	int  str_len;
3373 
3374 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
3375 		return;
3376 	}
3377 
3378 	if (str_len == 0) {
3379 		RETURN_EMPTY_STRING();
3380 	}
3381 
3382 	RETURN_STRING(php_addslashes(str,
3383 	                             str_len,
3384 	                             &Z_STRLEN_P(return_value), 0
3385 	                             TSRMLS_CC), 0);
3386 }
3387 /* }}} */
3388 
3389 /* {{{ proto string stripcslashes(string str)
3390    Strips backslashes from a string. Uses C-style conventions */
3391 PHP_FUNCTION(stripcslashes)
3392 {
3393 	char *str;
3394 	int  str_len;
3395 
3396 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
3397 		return;
3398 	}
3399 
3400 	ZVAL_STRINGL(return_value, str, str_len, 1);
3401 	php_stripcslashes(Z_STRVAL_P(return_value), &Z_STRLEN_P(return_value));
3402 }
3403 /* }}} */
3404 
3405 /* {{{ proto string stripslashes(string str)
3406    Strips backslashes from a string */
3407 PHP_FUNCTION(stripslashes)
3408 {
3409 	char *str;
3410 	int  str_len;
3411 
3412 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
3413 		return;
3414 	}
3415 
3416 	ZVAL_STRINGL(return_value, str, str_len, 1);
3417 	php_stripslashes(Z_STRVAL_P(return_value), &Z_STRLEN_P(return_value) TSRMLS_CC);
3418 }
3419 /* }}} */
3420 
3421 #ifndef HAVE_STRERROR
3422 /* {{{ php_strerror
3423  */
3424 char *php_strerror(int errnum)
3425 {
3426 	extern int sys_nerr;
3427 	extern char *sys_errlist[];
3428 	TSRMLS_FETCH();
3429 
3430 	if ((unsigned int) errnum < sys_nerr) {
3431 		return(sys_errlist[errnum]);
3432 	}
3433 
3434 	(void) snprintf(BG(str_ebuf), sizeof(php_basic_globals.str_ebuf), "Unknown error: %d", errnum);
3435 	return(BG(str_ebuf));
3436 }
3437 /* }}} */
3438 #endif
3439 
3440 /* {{{ php_stripcslashes
3441  */
3442 PHPAPI void php_stripcslashes(char *str, int *len)
3443 {
3444 	char *source, *target, *end;
3445 	int  nlen = *len, i;
3446 	char numtmp[4];
3447 
3448 	for (source=str, end=str+nlen, target=str; source < end; source++) {
3449 		if (*source == '\\' && source+1 < end) {
3450 			source++;
3451 			switch (*source) {
3452 				case 'n':  *target++='\n'; nlen--; break;
3453 				case 'r':  *target++='\r'; nlen--; break;
3454 				case 'a':  *target++='\a'; nlen--; break;
3455 				case 't':  *target++='\t'; nlen--; break;
3456 				case 'v':  *target++='\v'; nlen--; break;
3457 				case 'b':  *target++='\b'; nlen--; break;
3458 				case 'f':  *target++='\f'; nlen--; break;
3459 				case '\\': *target++='\\'; nlen--; break;
3460 				case 'x':
3461 					if (source+1 < end && isxdigit((int)(*(source+1)))) {
3462 						numtmp[0] = *++source;
3463 						if (source+1 < end && isxdigit((int)(*(source+1)))) {
3464 							numtmp[1] = *++source;
3465 							numtmp[2] = '\0';
3466 							nlen-=3;
3467 						} else {
3468 							numtmp[1] = '\0';
3469 							nlen-=2;
3470 						}
3471 						*target++=(char)strtol(numtmp, NULL, 16);
3472 						break;
3473 					}
3474 					/* break is left intentionally */
3475 				default:
3476 					i=0;
3477 					while (source < end && *source >= '0' && *source <= '7' && i<3) {
3478 						numtmp[i++] = *source++;
3479 					}
3480 					if (i) {
3481 						numtmp[i]='\0';
3482 						*target++=(char)strtol(numtmp, NULL, 8);
3483 						nlen-=i;
3484 						source--;
3485 					} else {
3486 						*target++=*source;
3487 						nlen--;
3488 					}
3489 			}
3490 		} else {
3491 			*target++=*source;
3492 		}
3493 	}
3494 
3495 	if (nlen != 0) {
3496 		*target='\0';
3497 	}
3498 
3499 	*len = nlen;
3500 }
3501 /* }}} */
3502 
3503 /* {{{ php_addcslashes
3504  */
3505 PHPAPI char *php_addcslashes(const char *str, int length, int *new_length, int should_free, char *what, int wlength TSRMLS_DC)
3506 {
3507 	char flags[256];
3508 	char *new_str = safe_emalloc(4, (length?length:(length=strlen(str))), 1);
3509 	char *source, *target;
3510 	char *end;
3511 	char c;
3512 	size_t  newlen;
3513 
3514 	if (!wlength) {
3515 		wlength = strlen(what);
3516 	}
3517 
3518 	php_charmask((unsigned char *)what, wlength, flags TSRMLS_CC);
3519 
3520 	for (source = (char*)str, end = source + length, target = new_str; source < end; source++) {
3521 		c = *source;
3522 		if (flags[(unsigned char)c]) {
3523 			if ((unsigned char) c < 32 || (unsigned char) c > 126) {
3524 				*target++ = '\\';
3525 				switch (c) {
3526 					case '\n': *target++ = 'n'; break;
3527 					case '\t': *target++ = 't'; break;
3528 					case '\r': *target++ = 'r'; break;
3529 					case '\a': *target++ = 'a'; break;
3530 					case '\v': *target++ = 'v'; break;
3531 					case '\b': *target++ = 'b'; break;
3532 					case '\f': *target++ = 'f'; break;
3533 					default: target += sprintf(target, "%03o", (unsigned char) c);
3534 				}
3535 				continue;
3536 			}
3537 			*target++ = '\\';
3538 		}
3539 		*target++ = c;
3540 	}
3541 	*target = 0;
3542 	newlen = target - new_str;
3543 	if (UNEXPECTED(newlen > INT_MAX)) {
3544 		efree(new_str);
3545 		zend_error(E_ERROR, "String size overflow");
3546 	}
3547 	if (target - new_str < length * 4) {
3548 		new_str = erealloc(new_str, newlen + 1);
3549 	}
3550 	if (new_length) {
3551 		*new_length = (int)newlen;
3552 	}
3553 	if (should_free) {
3554 		STR_FREE((char*)str);
3555 	}
3556 	return new_str;
3557 }
3558 /* }}} */
3559 
3560 /* {{{ php_addslashes
3561  */
3562 PHPAPI char *php_addslashes(char *str, int length, int *new_length, int should_free TSRMLS_DC)
3563 {
3564 	/* maximum string length, worst case situation */
3565 	char *new_str;
3566 	char *source, *target;
3567 	char *end;
3568 	int local_new_length;
3569 
3570 	if (!new_length) {
3571 		new_length = &local_new_length;
3572 	}
3573 	if (!str) {
3574 		*new_length = 0;
3575 		return str;
3576 	}
3577 	new_str = (char *) safe_emalloc(2, (length ? length : (length = strlen(str))), 1);
3578 	source = str;
3579 	end = source + length;
3580 	target = new_str;
3581 
3582 	while (source < end) {
3583 		switch (*source) {
3584 			case '\0':
3585 				*target++ = '\\';
3586 				*target++ = '0';
3587 				break;
3588 			case '\'':
3589 			case '\"':
3590 			case '\\':
3591 				*target++ = '\\';
3592 				/* break is missing *intentionally* */
3593 			default:
3594 				*target++ = *source;
3595 				break;
3596 		}
3597 
3598 		source++;
3599 	}
3600 
3601 	*target = 0;
3602 	*new_length = target - new_str;
3603 	if (UNEXPECTED(*new_length < 0)) {
3604 		zend_error(E_ERROR, "String size overflow");
3605 	}
3606 	if (should_free) {
3607 		STR_FREE(str);
3608 	}
3609 	new_str = (char *) erealloc(new_str, *new_length + 1);
3610 	return new_str;
3611 }
3612 /* }}} */
3613 
3614 #define _HEB_BLOCK_TYPE_ENG 1
3615 #define _HEB_BLOCK_TYPE_HEB 2
3616 #define isheb(c)      (((((unsigned char) c) >= 224) && (((unsigned char) c) <= 250)) ? 1 : 0)
3617 #define _isblank(c)   (((((unsigned char) c) == ' '  || ((unsigned char) c) == '\t')) ? 1 : 0)
3618 #define _isnewline(c) (((((unsigned char) c) == '\n' || ((unsigned char) c) == '\r')) ? 1 : 0)
3619 
3620 /* {{{ php_char_to_str_ex
3621  */
3622 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)
3623 {
3624 	int char_count = 0;
3625 	int replaced = 0;
3626 	char *source, *target, *tmp, *source_end=str+len, *tmp_end = NULL;
3627 
3628 	if (case_sensitivity) {
3629 		char *p = str, *e = p + len;
3630 		while ((p = memchr(p, from, (e - p)))) {
3631 			char_count++;
3632 			p++;
3633 		}
3634 	} else {
3635 		for (source = str; source < source_end; source++) {
3636 			if (tolower(*source) == tolower(from)) {
3637 				char_count++;
3638 			}
3639 		}
3640 	}
3641 
3642 	if (char_count == 0 && case_sensitivity) {
3643 		ZVAL_STRINGL(result, str, len, 1);
3644 		return 0;
3645 	}
3646 
3647 	Z_STRLEN_P(result) = len + (char_count * (to_len - 1));
3648 	Z_STRVAL_P(result) = target = safe_emalloc(char_count, to_len, len + 1);
3649 	Z_TYPE_P(result) = IS_STRING;
3650 
3651 	if (case_sensitivity) {
3652 		char *p = str, *e = p + len, *s = str;
3653 		while ((p = memchr(p, from, (e - p)))) {
3654 			memcpy(target, s, (p - s));
3655 			target += p - s;
3656 			memcpy(target, to, to_len);
3657 			target += to_len;
3658 			p++;
3659 			s = p;
3660 			if (replace_count) {
3661 				*replace_count += 1;
3662 			}
3663 		}
3664 		if (s < e) {
3665 			memcpy(target, s, (e - s));
3666 			target += e - s;
3667 		}
3668 	} else {
3669 		for (source = str; source < source_end; source++) {
3670 			if (tolower(*source) == tolower(from)) {
3671 				replaced = 1;
3672 				if (replace_count) {
3673 					*replace_count += 1;
3674 				}
3675 				for (tmp = to, tmp_end = tmp+to_len; tmp < tmp_end; tmp++) {
3676 					*target = *tmp;
3677 					target++;
3678 				}
3679 			} else {
3680 				*target = *source;
3681 				target++;
3682 			}
3683 		}
3684 	}
3685 	*target = 0;
3686 	return replaced;
3687 }
3688 /* }}} */
3689 
3690 /* {{{ php_char_to_str
3691  */
3692 PHPAPI int php_char_to_str(char *str, uint len, char from, char *to, int to_len, zval *result)
3693 {
3694 	return php_char_to_str_ex(str, len, from, to, to_len, result, 1, NULL);
3695 }
3696 /* }}} */
3697 
3698 /* {{{ php_str_to_str_ex
3699  */
3700 PHPAPI char *php_str_to_str_ex(char *haystack, int length,
3701 	char *needle, int needle_len, char *str, int str_len, int *_new_length, int case_sensitivity, int *replace_count)
3702 {
3703 	char *new_str;
3704 
3705 	if (needle_len < length) {
3706 		char *end, *haystack_dup = NULL, *needle_dup = NULL;
3707 		char *e, *s, *p, *r;
3708 
3709 		if (needle_len == str_len) {
3710 			new_str = estrndup(haystack, length);
3711 			*_new_length = length;
3712 
3713 			if (case_sensitivity) {
3714 				end = new_str + length;
3715 				for (p = new_str; (r = php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3716 					memcpy(r, str, str_len);
3717 					if (replace_count) {
3718 						(*replace_count)++;
3719 					}
3720 				}
3721 			} else {
3722 				haystack_dup = estrndup(haystack, length);
3723 				needle_dup = estrndup(needle, needle_len);
3724 				php_strtolower(haystack_dup, length);
3725 				php_strtolower(needle_dup, needle_len);
3726 				end = haystack_dup + length;
3727 				for (p = haystack_dup; (r = php_memnstr(p, needle_dup, needle_len, end)); p = r + needle_len) {
3728 					memcpy(new_str + (r - haystack_dup), str, str_len);
3729 					if (replace_count) {
3730 						(*replace_count)++;
3731 					}
3732 				}
3733 				efree(haystack_dup);
3734 				efree(needle_dup);
3735 			}
3736 			return new_str;
3737 		} else {
3738 			if (!case_sensitivity) {
3739 				haystack_dup = estrndup(haystack, length);
3740 				needle_dup = estrndup(needle, needle_len);
3741 				php_strtolower(haystack_dup, length);
3742 				php_strtolower(needle_dup, needle_len);
3743 			}
3744 
3745 			if (str_len < needle_len) {
3746 				new_str = emalloc(length + 1);
3747 			} else {
3748 				int count = 0;
3749 				char *o, *n, *endp;
3750 
3751 				if (case_sensitivity) {
3752 					o = haystack;
3753 					n = needle;
3754 				} else {
3755 					o = haystack_dup;
3756 					n = needle_dup;
3757 				}
3758 				endp = o + length;
3759 
3760 				while ((o = php_memnstr(o, n, needle_len, endp))) {
3761 					o += needle_len;
3762 					count++;
3763 				}
3764 				if (count == 0) {
3765 					/* Needle doesn't occur, shortcircuit the actual replacement. */
3766 					if (haystack_dup) {
3767 						efree(haystack_dup);
3768 					}
3769 					if (needle_dup) {
3770 						efree(needle_dup);
3771 					}
3772 					new_str = estrndup(haystack, length);
3773 					if (_new_length) {
3774 						*_new_length = length;
3775 					}
3776 					return new_str;
3777 				} else {
3778 					new_str = safe_emalloc(count, str_len - needle_len, length + 1);
3779 				}
3780 			}
3781 
3782 			e = s = new_str;
3783 
3784 			if (case_sensitivity) {
3785 				end = haystack + length;
3786 				for (p = haystack; (r = php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3787 					memcpy(e, p, r - p);
3788 					e += r - p;
3789 					memcpy(e, str, str_len);
3790 					e += str_len;
3791 					if (replace_count) {
3792 						(*replace_count)++;
3793 					}
3794 				}
3795 
3796 				if (p < end) {
3797 					memcpy(e, p, end - p);
3798 					e += end - p;
3799 				}
3800 			} else {
3801 				end = haystack_dup + length;
3802 
3803 				for (p = haystack_dup; (r = php_memnstr(p, needle_dup, needle_len, end)); p = r + needle_len) {
3804 					memcpy(e, haystack + (p - haystack_dup), r - p);
3805 					e += r - p;
3806 					memcpy(e, str, str_len);
3807 					e += str_len;
3808 					if (replace_count) {
3809 						(*replace_count)++;
3810 					}
3811 				}
3812 
3813 				if (p < end) {
3814 					memcpy(e, haystack + (p - haystack_dup), end - p);
3815 					e += end - p;
3816 				}
3817 			}
3818 
3819 			if (haystack_dup) {
3820 				efree(haystack_dup);
3821 			}
3822 			if (needle_dup) {
3823 				efree(needle_dup);
3824 			}
3825 
3826 			*e = '\0';
3827 			*_new_length = e - s;
3828 
3829 			new_str = erealloc(new_str, *_new_length + 1);
3830 			return new_str;
3831 		}
3832 	} else if (needle_len > length) {
3833 nothing_todo:
3834 		*_new_length = length;
3835 		new_str = estrndup(haystack, length);
3836 		return new_str;
3837 	} else {
3838 		if (case_sensitivity && memcmp(haystack, needle, length)) {
3839 			goto nothing_todo;
3840 		} else if (!case_sensitivity) {
3841 			char *l_haystack, *l_needle;
3842 
3843 			l_haystack = estrndup(haystack, length);
3844 			l_needle = estrndup(needle, length);
3845 
3846 			php_strtolower(l_haystack, length);
3847 			php_strtolower(l_needle, length);
3848 
3849 			if (memcmp(l_haystack, l_needle, length)) {
3850 				efree(l_haystack);
3851 				efree(l_needle);
3852 				goto nothing_todo;
3853 			}
3854 			efree(l_haystack);
3855 			efree(l_needle);
3856 		}
3857 
3858 		*_new_length = str_len;
3859 		new_str = estrndup(str, str_len);
3860 
3861 		if (replace_count) {
3862 			(*replace_count)++;
3863 		}
3864 		return new_str;
3865 	}
3866 
3867 }
3868 /* }}} */
3869 
3870 /* {{{ php_str_to_str
3871  */
3872 PHPAPI char *php_str_to_str(char *haystack, int length,
3873 	char *needle, int needle_len, char *str, int str_len, int *_new_length)
3874 {
3875 	return php_str_to_str_ex(haystack, length, needle, needle_len, str, str_len, _new_length, 1, NULL);
3876 }
3877 /* }}} */
3878 
3879 /* {{{ php_str_replace_in_subject
3880  */
3881 static void php_str_replace_in_subject(zval *search, zval *replace, zval **subject, zval *result, int case_sensitivity, int *replace_count)
3882 {
3883 	zval		**search_entry,
3884 				**replace_entry = NULL,
3885 				  temp_result;
3886 	char		*replace_value = NULL;
3887 	int			 replace_len = 0;
3888 
3889 	/* Make sure we're dealing with strings. */
3890 	convert_to_string_ex(subject);
3891 	Z_TYPE_P(result) = IS_STRING;
3892 	if (Z_STRLEN_PP(subject) == 0) {
3893 		ZVAL_STRINGL(result, "", 0, 1);
3894 		return;
3895 	}
3896 
3897 	/* If search is an array */
3898 	if (Z_TYPE_P(search) == IS_ARRAY) {
3899 		/* Duplicate subject string for repeated replacement */
3900 		MAKE_COPY_ZVAL(subject, result);
3901 
3902 		zend_hash_internal_pointer_reset(Z_ARRVAL_P(search));
3903 
3904 		if (Z_TYPE_P(replace) == IS_ARRAY) {
3905 			zend_hash_internal_pointer_reset(Z_ARRVAL_P(replace));
3906 		} else {
3907 			/* Set replacement value to the passed one */
3908 			replace_value = Z_STRVAL_P(replace);
3909 			replace_len = Z_STRLEN_P(replace);
3910 		}
3911 
3912 		/* For each entry in the search array, get the entry */
3913 		while (zend_hash_get_current_data(Z_ARRVAL_P(search), (void **) &search_entry) == SUCCESS) {
3914 			/* Make sure we're dealing with strings. */
3915 			SEPARATE_ZVAL(search_entry);
3916 			convert_to_string(*search_entry);
3917 			if (Z_STRLEN_PP(search_entry) == 0) {
3918 				zend_hash_move_forward(Z_ARRVAL_P(search));
3919 				if (Z_TYPE_P(replace) == IS_ARRAY) {
3920 					zend_hash_move_forward(Z_ARRVAL_P(replace));
3921 				}
3922 				continue;
3923 			}
3924 
3925 			/* If replace is an array. */
3926 			if (Z_TYPE_P(replace) == IS_ARRAY) {
3927 				/* Get current entry */
3928 				if (zend_hash_get_current_data(Z_ARRVAL_P(replace), (void **)&replace_entry) == SUCCESS) {
3929 					/* Make sure we're dealing with strings. */
3930 					convert_to_string_ex(replace_entry);
3931 
3932 					/* Set replacement value to the one we got from array */
3933 					replace_value = Z_STRVAL_PP(replace_entry);
3934 					replace_len = Z_STRLEN_PP(replace_entry);
3935 
3936 					zend_hash_move_forward(Z_ARRVAL_P(replace));
3937 				} else {
3938 					/* We've run out of replacement strings, so use an empty one. */
3939 					replace_value = "";
3940 					replace_len = 0;
3941 				}
3942 			}
3943 
3944 			if (Z_STRLEN_PP(search_entry) == 1) {
3945 				php_char_to_str_ex(Z_STRVAL_P(result),
3946 								Z_STRLEN_P(result),
3947 								Z_STRVAL_PP(search_entry)[0],
3948 								replace_value,
3949 								replace_len,
3950 								&temp_result,
3951 								case_sensitivity,
3952 								replace_count);
3953 			} else if (Z_STRLEN_PP(search_entry) > 1) {
3954 				Z_STRVAL(temp_result) = php_str_to_str_ex(Z_STRVAL_P(result), Z_STRLEN_P(result),
3955 														   Z_STRVAL_PP(search_entry), Z_STRLEN_PP(search_entry),
3956 														   replace_value, replace_len, &Z_STRLEN(temp_result), case_sensitivity, replace_count);
3957 			}
3958 
3959 			str_efree(Z_STRVAL_P(result));
3960 			Z_STRVAL_P(result) = Z_STRVAL(temp_result);
3961 			Z_STRLEN_P(result) = Z_STRLEN(temp_result);
3962 
3963 			if (Z_STRLEN_P(result) == 0) {
3964 				return;
3965 			}
3966 
3967 			zend_hash_move_forward(Z_ARRVAL_P(search));
3968 		}
3969 	} else {
3970 		if (Z_STRLEN_P(search) == 1) {
3971 			php_char_to_str_ex(Z_STRVAL_PP(subject),
3972 							Z_STRLEN_PP(subject),
3973 							Z_STRVAL_P(search)[0],
3974 							Z_STRVAL_P(replace),
3975 							Z_STRLEN_P(replace),
3976 							result,
3977 							case_sensitivity,
3978 							replace_count);
3979 		} else if (Z_STRLEN_P(search) > 1) {
3980 			Z_STRVAL_P(result) = php_str_to_str_ex(Z_STRVAL_PP(subject), Z_STRLEN_PP(subject),
3981 													Z_STRVAL_P(search), Z_STRLEN_P(search),
3982 													Z_STRVAL_P(replace), Z_STRLEN_P(replace), &Z_STRLEN_P(result), case_sensitivity, replace_count);
3983 		} else {
3984 			MAKE_COPY_ZVAL(subject, result);
3985 		}
3986 	}
3987 }
3988 /* }}} */
3989 
3990 /* {{{ php_str_replace_common
3991  */
3992 static void php_str_replace_common(INTERNAL_FUNCTION_PARAMETERS, int case_sensitivity)
3993 {
3994 	zval **subject, **search, **replace, **subject_entry, **zcount = NULL;
3995 	zval *result;
3996 	char *string_key;
3997 	uint string_key_len;
3998 	ulong num_key;
3999 	int count = 0;
4000 	int argc = ZEND_NUM_ARGS();
4001 
4002 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZZ|Z", &search, &replace, &subject, &zcount) == FAILURE) {
4003 		return;
4004 	}
4005 
4006 	SEPARATE_ZVAL(search);
4007 	SEPARATE_ZVAL(replace);
4008 	SEPARATE_ZVAL(subject);
4009 
4010 	/* Make sure we're dealing with strings and do the replacement. */
4011 	if (Z_TYPE_PP(search) != IS_ARRAY) {
4012 		convert_to_string_ex(search);
4013 		convert_to_string_ex(replace);
4014 	} else if (Z_TYPE_PP(replace) != IS_ARRAY) {
4015 		convert_to_string_ex(replace);
4016 	}
4017 
4018 	/* if subject is an array */
4019 	if (Z_TYPE_PP(subject) == IS_ARRAY) {
4020 		array_init(return_value);
4021 		zend_hash_internal_pointer_reset(Z_ARRVAL_PP(subject));
4022 
4023 		/* For each subject entry, convert it to string, then perform replacement
4024 		   and add the result to the return_value array. */
4025 		while (zend_hash_get_current_data(Z_ARRVAL_PP(subject), (void **)&subject_entry) == SUCCESS) {
4026 			if (Z_TYPE_PP(subject_entry) != IS_ARRAY && Z_TYPE_PP(subject_entry) != IS_OBJECT) {
4027 				MAKE_STD_ZVAL(result);
4028 				SEPARATE_ZVAL(subject_entry);
4029 				php_str_replace_in_subject(*search, *replace, subject_entry, result, case_sensitivity, (argc > 3) ? &count : NULL);
4030 			} else {
4031 				ALLOC_ZVAL(result);
4032 				Z_ADDREF_P(*subject_entry);
4033 				COPY_PZVAL_TO_ZVAL(*result, *subject_entry);
4034 			}
4035 			/* Add to return array */
4036 			switch (zend_hash_get_current_key_ex(Z_ARRVAL_PP(subject), &string_key,
4037 												&string_key_len, &num_key, 0, NULL)) {
4038 				case HASH_KEY_IS_STRING:
4039 					add_assoc_zval_ex(return_value, string_key, string_key_len, result);
4040 					break;
4041 
4042 				case HASH_KEY_IS_LONG:
4043 					add_index_zval(return_value, num_key, result);
4044 					break;
4045 			}
4046 
4047 			zend_hash_move_forward(Z_ARRVAL_PP(subject));
4048 		}
4049 	} else {	/* if subject is not an array */
4050 		php_str_replace_in_subject(*search, *replace, subject, return_value, case_sensitivity, (argc > 3) ? &count : NULL);
4051 	}
4052 	if (argc > 3) {
4053 		zval_dtor(*zcount);
4054 		ZVAL_LONG(*zcount, count);
4055 	}
4056 }
4057 /* }}} */
4058 
4059 /* {{{ proto mixed str_replace(mixed search, mixed replace, mixed subject [, int &replace_count])
4060    Replaces all occurrences of search in haystack with replace */
4061 PHP_FUNCTION(str_replace)
4062 {
4063 	php_str_replace_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4064 }
4065 /* }}} */
4066 
4067 /* {{{ proto mixed str_ireplace(mixed search, mixed replace, mixed subject [, int &replace_count])
4068    Replaces all occurrences of search in haystack with replace / case-insensitive */
4069 PHP_FUNCTION(str_ireplace)
4070 {
4071 	php_str_replace_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4072 }
4073 /* }}} */
4074 
4075 /* {{{ php_hebrev
4076  *
4077  * Converts Logical Hebrew text (Hebrew Windows style) to Visual text
4078  * Cheers/complaints/flames - Zeev Suraski <zeev@php.net>
4079  */
4080 static void php_hebrev(INTERNAL_FUNCTION_PARAMETERS, int convert_newlines)
4081 {
4082 	char *str;
4083 	char *heb_str, *tmp, *target, *broken_str;
4084 	int block_start, block_end, block_type, block_length, i;
4085 	long max_chars=0;
4086 	int begin, end, char_count, orig_begin;
4087 	int str_len;
4088 
4089 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &str, &str_len, &max_chars) == FAILURE) {
4090 		return;
4091 	}
4092 
4093 	if (str_len == 0) {
4094 		RETURN_FALSE;
4095 	}
4096 
4097 	tmp = str;
4098 	block_start=block_end=0;
4099 
4100 	heb_str = (char *) emalloc(str_len+1);
4101 	target = heb_str+str_len;
4102 	*target = 0;
4103 	target--;
4104 
4105 	block_length=0;
4106 
4107 	if (isheb(*tmp)) {
4108 		block_type = _HEB_BLOCK_TYPE_HEB;
4109 	} else {
4110 		block_type = _HEB_BLOCK_TYPE_ENG;
4111 	}
4112 
4113 	do {
4114 		if (block_type == _HEB_BLOCK_TYPE_HEB) {
4115 			while ((isheb((int)*(tmp+1)) || _isblank((int)*(tmp+1)) || ispunct((int)*(tmp+1)) || (int)*(tmp+1)=='\n' ) && block_end<str_len-1) {
4116 				tmp++;
4117 				block_end++;
4118 				block_length++;
4119 			}
4120 			for (i = block_start; i<= block_end; i++) {
4121 				*target = str[i];
4122 				switch (*target) {
4123 					case '(':
4124 						*target = ')';
4125 						break;
4126 					case ')':
4127 						*target = '(';
4128 						break;
4129 					case '[':
4130 						*target = ']';
4131 						break;
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 					default:
4154 						break;
4155 				}
4156 				target--;
4157 			}
4158 			block_type = _HEB_BLOCK_TYPE_ENG;
4159 		} else {
4160 			while (!isheb(*(tmp+1)) && (int)*(tmp+1)!='\n' && block_end < str_len-1) {
4161 				tmp++;
4162 				block_end++;
4163 				block_length++;
4164 			}
4165 			while ((_isblank((int)*tmp) || ispunct((int)*tmp)) && *tmp!='/' && *tmp!='-' && block_end > block_start) {
4166 				tmp--;
4167 				block_end--;
4168 			}
4169 			for (i = block_end; i >= block_start; i--) {
4170 				*target = str[i];
4171 				target--;
4172 			}
4173 			block_type = _HEB_BLOCK_TYPE_HEB;
4174 		}
4175 		block_start=block_end+1;
4176 	} while (block_end < str_len-1);
4177 
4178 
4179 	broken_str = (char *) emalloc(str_len+1);
4180 	begin=end=str_len-1;
4181 	target = broken_str;
4182 
4183 	while (1) {
4184 		char_count=0;
4185 		while ((!max_chars || char_count < max_chars) && begin > 0) {
4186 			char_count++;
4187 			begin--;
4188 			if (begin <= 0 || _isnewline(heb_str[begin])) {
4189 				while (begin > 0 && _isnewline(heb_str[begin-1])) {
4190 					begin--;
4191 					char_count++;
4192 				}
4193 				break;
4194 			}
4195 		}
4196 		if (char_count == max_chars) { /* try to avoid breaking words */
4197 			int new_char_count=char_count, new_begin=begin;
4198 
4199 			while (new_char_count > 0) {
4200 				if (_isblank(heb_str[new_begin]) || _isnewline(heb_str[new_begin])) {
4201 					break;
4202 				}
4203 				new_begin++;
4204 				new_char_count--;
4205 			}
4206 			if (new_char_count > 0) {
4207 				begin=new_begin;
4208 			}
4209 		}
4210 		orig_begin=begin;
4211 
4212 		if (_isblank(heb_str[begin])) {
4213 			heb_str[begin]='\n';
4214 		}
4215 		while (begin <= end && _isnewline(heb_str[begin])) { /* skip leading newlines */
4216 			begin++;
4217 		}
4218 		for (i = begin; i <= end; i++) { /* copy content */
4219 			*target = heb_str[i];
4220 			target++;
4221 		}
4222 		for (i = orig_begin; i <= end && _isnewline(heb_str[i]); i++) {
4223 			*target = heb_str[i];
4224 			target++;
4225 		}
4226 		begin=orig_begin;
4227 
4228 		if (begin <= 0) {
4229 			*target = 0;
4230 			break;
4231 		}
4232 		begin--;
4233 		end=begin;
4234 	}
4235 	efree(heb_str);
4236 
4237 	if (convert_newlines) {
4238 		php_char_to_str(broken_str, str_len,'\n', "<br />\n", 7, return_value);
4239 		efree(broken_str);
4240 	} else {
4241 		Z_STRVAL_P(return_value) = broken_str;
4242 		Z_STRLEN_P(return_value) = str_len;
4243 		Z_TYPE_P(return_value) = IS_STRING;
4244 	}
4245 }
4246 /* }}} */
4247 
4248 /* {{{ proto string hebrev(string str [, int max_chars_per_line])
4249    Converts logical Hebrew text to visual text */
4250 PHP_FUNCTION(hebrev)
4251 {
4252 	php_hebrev(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4253 }
4254 /* }}} */
4255 
4256 /* {{{ proto string hebrevc(string str [, int max_chars_per_line])
4257    Converts logical Hebrew text to visual text with newline conversion */
4258 PHP_FUNCTION(hebrevc)
4259 {
4260 	php_hebrev(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4261 }
4262 /* }}} */
4263 
4264 /* {{{ proto string nl2br(string str [, bool is_xhtml])
4265    Converts newlines to HTML line breaks */
4266 PHP_FUNCTION(nl2br)
4267 {
4268 	/* in brief this inserts <br /> or <br> before matched regexp \n\r?|\r\n? */
4269 	char		*tmp, *str;
4270 	size_t		new_length;
4271 	char		*end, *target;
4272 	int		repl_cnt = 0;
4273 	int		str_len;
4274 	zend_bool	is_xhtml = 1;
4275 
4276 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &str, &str_len, &is_xhtml) == FAILURE) {
4277 		return;
4278 	}
4279 
4280 	tmp = str;
4281 	end = str + str_len;
4282 
4283 	/* it is really faster to scan twice and allocate mem once instead of scanning once
4284 	   and constantly reallocing */
4285 	while (tmp < end) {
4286 		if (*tmp == '\r') {
4287 			if (*(tmp+1) == '\n') {
4288 				tmp++;
4289 			}
4290 			repl_cnt++;
4291 		} else if (*tmp == '\n') {
4292 			if (*(tmp+1) == '\r') {
4293 				tmp++;
4294 			}
4295 			repl_cnt++;
4296 		}
4297 
4298 		tmp++;
4299 	}
4300 
4301 	if (repl_cnt == 0) {
4302 		RETURN_STRINGL(str, str_len, 1);
4303 	}
4304 
4305 	{
4306 		size_t repl_len = is_xhtml ? (sizeof("<br />") - 1) : (sizeof("<br>") - 1);
4307 
4308 		new_length = str_len + repl_cnt * repl_len;
4309 		if (UNEXPECTED(new_length > INT_MAX)) {
4310 			zend_error(E_ERROR, "String size overflow");
4311 		}
4312 		tmp = target = safe_emalloc(repl_cnt, repl_len, str_len + 1);
4313 	}
4314 
4315 	while (str < end) {
4316 		switch (*str) {
4317 			case '\r':
4318 			case '\n':
4319 				*target++ = '<';
4320 				*target++ = 'b';
4321 				*target++ = 'r';
4322 
4323 				if (is_xhtml) {
4324 					*target++ = ' ';
4325 					*target++ = '/';
4326 				}
4327 
4328 				*target++ = '>';
4329 
4330 				if ((*str == '\r' && *(str+1) == '\n') || (*str == '\n' && *(str+1) == '\r')) {
4331 					*target++ = *str++;
4332 				}
4333 				/* lack of a break; is intentional */
4334 			default:
4335 				*target++ = *str;
4336 		}
4337 
4338 		str++;
4339 	}
4340 
4341 	*target = '\0';
4342 
4343 	RETURN_STRINGL(tmp, new_length, 0);
4344 }
4345 /* }}} */
4346 
4347 /* {{{ proto string strip_tags(string str [, string allowable_tags])
4348    Strips HTML and PHP tags from a string */
4349 PHP_FUNCTION(strip_tags)
4350 {
4351 	char *buf;
4352 	char *str;
4353 	zval **allow=NULL;
4354 	char *allowed_tags=NULL;
4355 	int allowed_tags_len=0;
4356 	int str_len;
4357 	size_t retval_len;
4358 
4359 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|Z", &str, &str_len, &allow) == FAILURE) {
4360 		return;
4361 	}
4362 
4363 	/* To maintain a certain BC, we allow anything for the second parameter and return original string */
4364 	if (allow != NULL) {
4365 		convert_to_string_ex(allow);
4366 		allowed_tags = Z_STRVAL_PP(allow);
4367 		allowed_tags_len = Z_STRLEN_PP(allow);
4368 	}
4369 
4370 	buf = estrndup(str, str_len);
4371 	retval_len = php_strip_tags_ex(buf, str_len, NULL, allowed_tags, allowed_tags_len, 0);
4372 	RETURN_STRINGL(buf, retval_len, 0);
4373 }
4374 /* }}} */
4375 
4376 /* {{{ proto string setlocale(mixed category, string locale [, string ...])
4377    Set locale information */
4378 PHP_FUNCTION(setlocale)
4379 {
4380 	zval ***args = NULL;
4381 	zval **pcategory, **plocale;
4382 	int num_args, cat, i = 0;
4383 	char *loc, *retval;
4384 
4385 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z+", &pcategory, &args, &num_args) == FAILURE) {
4386 		return;
4387 	}
4388 
4389 #ifdef HAVE_SETLOCALE
4390 	if (Z_TYPE_PP(pcategory) == IS_LONG) {
4391 		convert_to_long_ex(pcategory);
4392 		cat = Z_LVAL_PP(pcategory);
4393 	} else {
4394 		/* FIXME: The following behaviour should be removed. */
4395 		char *category;
4396 
4397 		php_error_docref(NULL TSRMLS_CC, E_DEPRECATED, "Passing locale category name as string is deprecated. Use the LC_* -constants instead");
4398 
4399 		convert_to_string_ex(pcategory);
4400 		category = Z_STRVAL_PP(pcategory);
4401 
4402 		if (!strcasecmp("LC_ALL", category)) {
4403 			cat = LC_ALL;
4404 		} else if (!strcasecmp("LC_COLLATE", category)) {
4405 			cat = LC_COLLATE;
4406 		} else if (!strcasecmp("LC_CTYPE", category)) {
4407 			cat = LC_CTYPE;
4408 #ifdef LC_MESSAGES
4409 		} else if (!strcasecmp("LC_MESSAGES", category)) {
4410 			cat = LC_MESSAGES;
4411 #endif
4412 		} else if (!strcasecmp("LC_MONETARY", category)) {
4413 			cat = LC_MONETARY;
4414 		} else if (!strcasecmp("LC_NUMERIC", category)) {
4415 			cat = LC_NUMERIC;
4416 		} else if (!strcasecmp("LC_TIME", category)) {
4417 			cat = LC_TIME;
4418 		} else {
4419 			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);
4420 
4421 			if (args) {
4422 				efree(args);
4423 			}
4424 			RETURN_FALSE;
4425 		}
4426 	}
4427 
4428 	if (Z_TYPE_PP(args[0]) == IS_ARRAY) {
4429 		zend_hash_internal_pointer_reset(Z_ARRVAL_PP(args[0]));
4430 	}
4431 
4432 	while (1) {
4433 		if (Z_TYPE_PP(args[0]) == IS_ARRAY) {
4434 			if (!zend_hash_num_elements(Z_ARRVAL_PP(args[0]))) {
4435 				break;
4436 			}
4437 			zend_hash_get_current_data(Z_ARRVAL_PP(args[0]), (void **)&plocale);
4438 		} else {
4439 			plocale = args[i];
4440 		}
4441 
4442 		convert_to_string_ex(plocale);
4443 
4444 		if (!strcmp ("0", Z_STRVAL_PP(plocale))) {
4445 			loc = NULL;
4446 		} else {
4447 			loc = Z_STRVAL_PP(plocale);
4448 			if (Z_STRLEN_PP(plocale) >= 255) {
4449 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Specified locale name is too long");
4450 				break;
4451 			}
4452 		}
4453 
4454 		retval = php_my_setlocale(cat, loc);
4455 		zend_update_current_locale();
4456 		if (retval) {
4457 			/* Remember if locale was changed */
4458 			if (loc) {
4459 				STR_FREE(BG(locale_string));
4460 				BG(locale_string) = estrdup(retval);
4461 			}
4462 
4463 			if (args) {
4464 				efree(args);
4465 			}
4466 			RETURN_STRING(retval, 1);
4467 		}
4468 
4469 		if (Z_TYPE_PP(args[0]) == IS_ARRAY) {
4470 			if (zend_hash_move_forward(Z_ARRVAL_PP(args[0])) == FAILURE) break;
4471 		} else {
4472 			if (++i >= num_args) break;
4473 		}
4474 	}
4475 
4476 #endif
4477 	if (args) {
4478 		efree(args);
4479 	}
4480 	RETURN_FALSE;
4481 }
4482 /* }}} */
4483 
4484 /* {{{ proto void parse_str(string encoded_string [, array result])
4485    Parses GET/POST/COOKIE data and sets global variables */
4486 PHP_FUNCTION(parse_str)
4487 {
4488 	char *arg;
4489 	zval *arrayArg = NULL;
4490 	char *res = NULL;
4491 	int arglen;
4492 
4493 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z", &arg, &arglen, &arrayArg) == FAILURE) {
4494 		return;
4495 	}
4496 
4497 	res = estrndup(arg, arglen);
4498 
4499 	if (arrayArg == NULL) {
4500 		zval tmp;
4501 
4502 		if (!EG(active_symbol_table)) {
4503 			zend_rebuild_symbol_table(TSRMLS_C);
4504 		}
4505 		Z_ARRVAL(tmp) = EG(active_symbol_table);
4506 		sapi_module.treat_data(PARSE_STRING, res, &tmp TSRMLS_CC);
4507 	} else 	{
4508 		zval ret;
4509 
4510 		array_init(&ret);
4511 		sapi_module.treat_data(PARSE_STRING, res, &ret TSRMLS_CC);
4512 		/* Clear out the array that was passed in. */
4513 		zval_dtor(arrayArg);
4514 		ZVAL_COPY_VALUE(arrayArg, &ret);
4515 	}
4516 }
4517 /* }}} */
4518 
4519 #define PHP_TAG_BUF_SIZE 1023
4520 
4521 /* {{{ php_tag_find
4522  *
4523  * Check if tag is in a set of tags
4524  *
4525  * states:
4526  *
4527  * 0 start tag
4528  * 1 first non-whitespace char seen
4529  */
4530 int php_tag_find(char *tag, int len, char *set) {
4531 	char c, *n, *t;
4532 	int state=0, done=0;
4533 	char *norm;
4534 
4535 	if (len <= 0) {
4536 		return 0;
4537 	}
4538 
4539 	norm = emalloc(len+1);
4540 
4541 	n = norm;
4542 	t = tag;
4543 	c = tolower(*t);
4544 	/*
4545 	   normalize the tag removing leading and trailing whitespace
4546 	   and turn any <a whatever...> into just <a> and any </tag>
4547 	   into <tag>
4548 	*/
4549 	while (!done) {
4550 		switch (c) {
4551 			case '<':
4552 				*(n++) = c;
4553 				break;
4554 			case '>':
4555 				done =1;
4556 				break;
4557 			default:
4558 				if (!isspace((int)c)) {
4559 					if (state == 0) {
4560 						state=1;
4561 					}
4562 					if (c != '/') {
4563 						*(n++) = c;
4564 					}
4565 				} else {
4566 					if (state == 1)
4567 						done=1;
4568 				}
4569 				break;
4570 		}
4571 		c = tolower(*(++t));
4572 	}
4573 	*(n++) = '>';
4574 	*n = '\0';
4575 	if (strstr(set, norm)) {
4576 		done=1;
4577 	} else {
4578 		done=0;
4579 	}
4580 	efree(norm);
4581 	return done;
4582 }
4583 /* }}} */
4584 
4585 PHPAPI size_t php_strip_tags(char *rbuf, int len, int *stateptr, char *allow, int allow_len) /* {{{ */
4586 {
4587 	return php_strip_tags_ex(rbuf, len, stateptr, allow, allow_len, 0);
4588 }
4589 /* }}} */
4590 
4591 /* {{{ php_strip_tags
4592 
4593 	A simple little state-machine to strip out html and php tags
4594 
4595 	State 0 is the output state, State 1 means we are inside a
4596 	normal html tag and state 2 means we are inside a php tag.
4597 
4598 	The state variable is passed in to allow a function like fgetss
4599 	to maintain state across calls to the function.
4600 
4601 	lc holds the last significant character read and br is a bracket
4602 	counter.
4603 
4604 	When an allow string is passed in we keep track of the string
4605 	in state 1 and when the tag is closed check it against the
4606 	allow string to see if we should allow it.
4607 
4608 	swm: Added ability to strip <?xml tags without assuming it PHP
4609 	code.
4610 */
4611 PHPAPI size_t php_strip_tags_ex(char *rbuf, int len, int *stateptr, char *allow, int allow_len, zend_bool allow_tag_spaces)
4612 {
4613 	char *tbuf, *buf, *p, *tp, *rp, c, lc;
4614 	int br, i=0, depth=0, in_q = 0;
4615 	int state = 0, pos;
4616 	char *allow_free = NULL;
4617 
4618 	if (stateptr)
4619 		state = *stateptr;
4620 
4621 	buf = estrndup(rbuf, len);
4622 	c = *buf;
4623 	lc = '\0';
4624 	p = buf;
4625 	rp = rbuf;
4626 	br = 0;
4627 	if (allow) {
4628 		if (IS_INTERNED(allow)) {
4629 			allow_free = allow = zend_str_tolower_dup(allow, allow_len);
4630 		} else {
4631 			allow_free = NULL;
4632 			php_strtolower(allow, allow_len);
4633 		}
4634 		tbuf = emalloc(PHP_TAG_BUF_SIZE + 1);
4635 		tp = tbuf;
4636 	} else {
4637 		tbuf = tp = NULL;
4638 	}
4639 
4640 	while (i < len) {
4641 		switch (c) {
4642 			case '\0':
4643 				break;
4644 			case '<':
4645 				if (in_q) {
4646 					break;
4647 				}
4648 				if (isspace(*(p + 1)) && !allow_tag_spaces) {
4649 					goto reg_char;
4650 				}
4651 				if (state == 0) {
4652 					lc = '<';
4653 					state = 1;
4654 					if (allow) {
4655 						if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4656 							pos = tp - tbuf;
4657 							tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4658 							tp = tbuf + pos;
4659 						}
4660 						*(tp++) = '<';
4661 				 	}
4662 				} else if (state == 1) {
4663 					depth++;
4664 				}
4665 				break;
4666 
4667 			case '(':
4668 				if (state == 2) {
4669 					if (lc != '"' && lc != '\'') {
4670 						lc = '(';
4671 						br++;
4672 					}
4673 				} else if (allow && state == 1) {
4674 					if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4675 						pos = tp - tbuf;
4676 						tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4677 						tp = tbuf + pos;
4678 					}
4679 					*(tp++) = c;
4680 				} else if (state == 0) {
4681 					*(rp++) = c;
4682 				}
4683 				break;
4684 
4685 			case ')':
4686 				if (state == 2) {
4687 					if (lc != '"' && lc != '\'') {
4688 						lc = ')';
4689 						br--;
4690 					}
4691 				} else if (allow && state == 1) {
4692 					if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4693 						pos = tp - tbuf;
4694 						tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4695 						tp = tbuf + pos;
4696 					}
4697 					*(tp++) = c;
4698 				} else if (state == 0) {
4699 					*(rp++) = c;
4700 				}
4701 				break;
4702 
4703 			case '>':
4704 				if (depth) {
4705 					depth--;
4706 					break;
4707 				}
4708 
4709 				if (in_q) {
4710 					break;
4711 				}
4712 
4713 				switch (state) {
4714 					case 1: /* HTML/XML */
4715 						lc = '>';
4716 						in_q = state = 0;
4717 						if (allow) {
4718 							if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4719 								pos = tp - tbuf;
4720 								tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4721 								tp = tbuf + pos;
4722 							}
4723 							*(tp++) = '>';
4724 							*tp='\0';
4725 							if (php_tag_find(tbuf, tp-tbuf, allow)) {
4726 								memcpy(rp, tbuf, tp-tbuf);
4727 								rp += tp-tbuf;
4728 							}
4729 							tp = tbuf;
4730 						}
4731 						break;
4732 
4733 					case 2: /* PHP */
4734 						if (!br && lc != '\"' && *(p-1) == '?') {
4735 							in_q = state = 0;
4736 							tp = tbuf;
4737 						}
4738 						break;
4739 
4740 					case 3:
4741 						in_q = state = 0;
4742 						tp = tbuf;
4743 						break;
4744 
4745 					case 4: /* JavaScript/CSS/etc... */
4746 						if (p >= buf + 2 && *(p-1) == '-' && *(p-2) == '-') {
4747 							in_q = state = 0;
4748 							tp = tbuf;
4749 						}
4750 						break;
4751 
4752 					default:
4753 						*(rp++) = c;
4754 						break;
4755 				}
4756 				break;
4757 
4758 			case '"':
4759 			case '\'':
4760 				if (state == 4) {
4761 					/* Inside <!-- comment --> */
4762 					break;
4763 				} else if (state == 2 && *(p-1) != '\\') {
4764 					if (lc == c) {
4765 						lc = '\0';
4766 					} else if (lc != '\\') {
4767 						lc = c;
4768 					}
4769 				} else if (state == 0) {
4770 					*(rp++) = c;
4771 				} else if (allow && state == 1) {
4772 					if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4773 						pos = tp - tbuf;
4774 						tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4775 						tp = tbuf + pos;
4776 					}
4777 					*(tp++) = c;
4778 				}
4779 				if (state && p != buf && (state == 1 || *(p-1) != '\\') && (!in_q || *p == in_q)) {
4780 					if (in_q) {
4781 						in_q = 0;
4782 					} else {
4783 						in_q = *p;
4784 					}
4785 				}
4786 				break;
4787 
4788 			case '!':
4789 				/* JavaScript & Other HTML scripting languages */
4790 				if (state == 1 && *(p-1) == '<') {
4791 					state = 3;
4792 					lc = c;
4793 				} else {
4794 					if (state == 0) {
4795 						*(rp++) = c;
4796 					} else if (allow && state == 1) {
4797 						if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4798 							pos = tp - tbuf;
4799 							tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4800 							tp = tbuf + pos;
4801 						}
4802 						*(tp++) = c;
4803 					}
4804 				}
4805 				break;
4806 
4807 			case '-':
4808 				if (state == 3 && p >= buf + 2 && *(p-1) == '-' && *(p-2) == '!') {
4809 					state = 4;
4810 				} else {
4811 					goto reg_char;
4812 				}
4813 				break;
4814 
4815 			case '?':
4816 
4817 				if (state == 1 && *(p-1) == '<') {
4818 					br=0;
4819 					state=2;
4820 					break;
4821 				}
4822 
4823 			case 'E':
4824 			case 'e':
4825 				/* !DOCTYPE exception */
4826 				if (state==3 && p > buf+6
4827 						     && tolower(*(p-1)) == 'p'
4828 					         && tolower(*(p-2)) == 'y'
4829 						     && tolower(*(p-3)) == 't'
4830 						     && tolower(*(p-4)) == 'c'
4831 						     && tolower(*(p-5)) == 'o'
4832 						     && tolower(*(p-6)) == 'd') {
4833 					state = 1;
4834 					break;
4835 				}
4836 				/* fall-through */
4837 
4838 			case 'l':
4839 			case 'L':
4840 
4841 				/* swm: If we encounter '<?xml' then we shouldn't be in
4842 				 * state == 2 (PHP). Switch back to HTML.
4843 				 */
4844 
4845 				if (state == 2 && p > buf+2 && strncasecmp(p-2, "xm", 2) == 0) {
4846 					state = 1;
4847 					break;
4848 				}
4849 
4850 				/* fall-through */
4851 			default:
4852 reg_char:
4853 				if (state == 0) {
4854 					*(rp++) = c;
4855 				} else if (allow && state == 1) {
4856 					if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4857 						pos = tp - tbuf;
4858 						tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4859 						tp = tbuf + pos;
4860 					}
4861 					*(tp++) = c;
4862 				}
4863 				break;
4864 		}
4865 		c = *(++p);
4866 		i++;
4867 	}
4868 	if (rp < rbuf + len) {
4869 		*rp = '\0';
4870 	}
4871 	efree(buf);
4872 	if (allow) {
4873 		efree(tbuf);
4874 		if (allow_free) {
4875 			efree(allow_free);
4876 		}
4877 	}
4878 	if (stateptr)
4879 		*stateptr = state;
4880 
4881 	return (size_t)(rp - rbuf);
4882 }
4883 /* }}} */
4884 
4885 /* {{{ proto array str_getcsv(string input[, string delimiter[, string enclosure[, string escape]]])
4886 Parse a CSV string into an array */
4887 PHP_FUNCTION(str_getcsv)
4888 {
4889 	char *str, delim = ',', enc = '"', esc = '\\';
4890 	char *delim_str = NULL, *enc_str = NULL, *esc_str = NULL;
4891 	int str_len = 0, delim_len = 0, enc_len = 0, esc_len = 0;
4892 
4893 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|sss", &str, &str_len, &delim_str, &delim_len,
4894 		&enc_str, &enc_len, &esc_str, &esc_len) == FAILURE) {
4895 		return;
4896 	}
4897 
4898 	delim = delim_len ? delim_str[0] : delim;
4899 	enc = enc_len ? enc_str[0] : enc;
4900 	esc = esc_len ? esc_str[0] : esc;
4901 
4902 	php_fgetcsv(NULL, delim, enc, esc, str_len, str, return_value TSRMLS_CC);
4903 }
4904 /* }}} */
4905 
4906 /* {{{ proto string str_repeat(string input, int mult)
4907    Returns the input string repeat mult times */
4908 PHP_FUNCTION(str_repeat)
4909 {
4910 	char		*input_str;		/* Input string */
4911 	int 		input_len;
4912 	long 		mult;			/* Multiplier */
4913 	char		*result;		/* Resulting string */
4914 	size_t		result_len;		/* Length of the resulting string */
4915 
4916 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &input_str, &input_len, &mult) == FAILURE) {
4917 		return;
4918 	}
4919 
4920 	if (mult < 0) {
4921 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Second argument has to be greater than or equal to 0");
4922 		return;
4923 	}
4924 
4925 	/* Don't waste our time if it's empty */
4926 	/* ... or if the multiplier is zero */
4927 	if (input_len == 0 || mult == 0)
4928 		RETURN_EMPTY_STRING();
4929 
4930 	/* Initialize the result string */
4931 	result_len = input_len * mult;
4932 	if(result_len > INT_MAX) {
4933 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Result is too big, maximum %d allowed", INT_MAX);
4934 		RETURN_EMPTY_STRING();
4935 	}
4936 	result = (char *)safe_emalloc(input_len, mult, 1);
4937 
4938 	/* Heavy optimization for situations where input string is 1 byte long */
4939 	if (input_len == 1) {
4940 		memset(result, *(input_str), mult);
4941 	} else {
4942 		char *s, *e, *ee;
4943 		int l=0;
4944 		memcpy(result, input_str, input_len);
4945 		s = result;
4946 		e = result + input_len;
4947 		ee = result + result_len;
4948 
4949 		while (e<ee) {
4950 			l = (e-s) < (ee-e) ? (e-s) : (ee-e);
4951 			memmove(e, s, l);
4952 			e += l;
4953 		}
4954 	}
4955 
4956 	result[result_len] = '\0';
4957 
4958 	RETURN_STRINGL(result, result_len, 0);
4959 }
4960 /* }}} */
4961 
4962 /* {{{ proto mixed count_chars(string input [, int mode])
4963    Returns info about what characters are used in input */
4964 PHP_FUNCTION(count_chars)
4965 {
4966 	char *input;
4967 	int chars[256];
4968 	long mymode=0;
4969 	unsigned char *buf;
4970 	int len, inx;
4971 	char retstr[256];
4972 	int retlen=0;
4973 
4974 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &input, &len, &mymode) == FAILURE) {
4975 		return;
4976 	}
4977 
4978 	if (mymode < 0 || mymode > 4) {
4979 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown mode");
4980 		RETURN_FALSE;
4981 	}
4982 
4983 	buf = (unsigned char *) input;
4984 	memset((void*) chars, 0, sizeof(chars));
4985 
4986 	while (len > 0) {
4987 		chars[*buf]++;
4988 		buf++;
4989 		len--;
4990 	}
4991 
4992 	if (mymode < 3) {
4993 		array_init(return_value);
4994 	}
4995 
4996 	for (inx = 0; inx < 256; inx++) {
4997 		switch (mymode) {
4998 	 		case 0:
4999 				add_index_long(return_value, inx, chars[inx]);
5000 				break;
5001 	 		case 1:
5002 				if (chars[inx] != 0) {
5003 					add_index_long(return_value, inx, chars[inx]);
5004 				}
5005 				break;
5006   			case 2:
5007 				if (chars[inx] == 0) {
5008 					add_index_long(return_value, inx, chars[inx]);
5009 				}
5010 				break;
5011 	  		case 3:
5012 				if (chars[inx] != 0) {
5013 					retstr[retlen++] = inx;
5014 				}
5015 				break;
5016   			case 4:
5017 				if (chars[inx] == 0) {
5018 					retstr[retlen++] = inx;
5019 				}
5020 				break;
5021 		}
5022 	}
5023 
5024 	if (mymode >= 3 && mymode <= 4) {
5025 		RETURN_STRINGL(retstr, retlen, 1);
5026 	}
5027 }
5028 /* }}} */
5029 
5030 /* {{{ php_strnatcmp
5031  */
5032 static void php_strnatcmp(INTERNAL_FUNCTION_PARAMETERS, int fold_case)
5033 {
5034 	char *s1, *s2;
5035 	int s1_len, s2_len;
5036 
5037 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &s1, &s1_len, &s2, &s2_len) == FAILURE) {
5038 		return;
5039 	}
5040 
5041 	RETURN_LONG(strnatcmp_ex(s1, s1_len,
5042 							 s2, s2_len,
5043 							 fold_case));
5044 }
5045 /* }}} */
5046 
5047 PHPAPI int string_natural_compare_function_ex(zval *result, zval *op1, zval *op2, zend_bool case_insensitive TSRMLS_DC) /* {{{ */
5048 {
5049 	zval op1_copy, op2_copy;
5050 	int use_copy1 = 0, use_copy2 = 0;
5051 
5052 	if (Z_TYPE_P(op1) != IS_STRING) {
5053 		zend_make_printable_zval(op1, &op1_copy, &use_copy1);
5054 	}
5055 	if (Z_TYPE_P(op2) != IS_STRING) {
5056 		zend_make_printable_zval(op2, &op2_copy, &use_copy2);
5057 	}
5058 
5059 	if (use_copy1) {
5060 		op1 = &op1_copy;
5061 	}
5062 	if (use_copy2) {
5063 		op2 = &op2_copy;
5064 	}
5065 
5066 	ZVAL_LONG(result, strnatcmp_ex(Z_STRVAL_P(op1), Z_STRLEN_P(op1), Z_STRVAL_P(op2), Z_STRLEN_P(op2), case_insensitive));
5067 
5068 	if (use_copy1) {
5069 		zval_dtor(op1);
5070 	}
5071 	if (use_copy2) {
5072 		zval_dtor(op2);
5073 	}
5074 	return SUCCESS;
5075 }
5076 /* }}} */
5077 
5078 PHPAPI int string_natural_case_compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* {{{ */
5079 {
5080 	return string_natural_compare_function_ex(result, op1, op2, 1 TSRMLS_CC);
5081 }
5082 /* }}} */
5083 
5084 PHPAPI int string_natural_compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* {{{ */
5085 {
5086 	return string_natural_compare_function_ex(result, op1, op2, 0 TSRMLS_CC);
5087 }
5088 /* }}} */
5089 
5090 /* {{{ proto int strnatcmp(string s1, string s2)
5091    Returns the result of string comparison using 'natural' algorithm */
5092 PHP_FUNCTION(strnatcmp)
5093 {
5094 	php_strnatcmp(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
5095 }
5096 /* }}} */
5097 
5098 /* {{{ proto array localeconv(void)
5099    Returns numeric formatting information based on the current locale */
5100 PHP_FUNCTION(localeconv)
5101 {
5102 	zval *grouping, *mon_grouping;
5103 	int len, i;
5104 
5105 	/* We don't need no stinkin' parameters... */
5106 	if (zend_parse_parameters_none() == FAILURE) {
5107 		return;
5108 	}
5109 
5110 	MAKE_STD_ZVAL(grouping);
5111 	MAKE_STD_ZVAL(mon_grouping);
5112 
5113 	array_init(return_value);
5114 	array_init(grouping);
5115 	array_init(mon_grouping);
5116 
5117 #ifdef HAVE_LOCALECONV
5118 	{
5119 		struct lconv currlocdata;
5120 
5121 		localeconv_r( &currlocdata );
5122 
5123 		/* Grab the grouping data out of the array */
5124 		len = strlen(currlocdata.grouping);
5125 
5126 		for (i = 0; i < len; i++) {
5127 			add_index_long(grouping, i, currlocdata.grouping[i]);
5128 		}
5129 
5130 		/* Grab the monetary grouping data out of the array */
5131 		len = strlen(currlocdata.mon_grouping);
5132 
5133 		for (i = 0; i < len; i++) {
5134 			add_index_long(mon_grouping, i, currlocdata.mon_grouping[i]);
5135 		}
5136 
5137 		add_assoc_string(return_value, "decimal_point",     currlocdata.decimal_point,     1);
5138 		add_assoc_string(return_value, "thousands_sep",     currlocdata.thousands_sep,     1);
5139 		add_assoc_string(return_value, "int_curr_symbol",   currlocdata.int_curr_symbol,   1);
5140 		add_assoc_string(return_value, "currency_symbol",   currlocdata.currency_symbol,   1);
5141 		add_assoc_string(return_value, "mon_decimal_point", currlocdata.mon_decimal_point, 1);
5142 		add_assoc_string(return_value, "mon_thousands_sep", currlocdata.mon_thousands_sep, 1);
5143 		add_assoc_string(return_value, "positive_sign",     currlocdata.positive_sign,     1);
5144 		add_assoc_string(return_value, "negative_sign",     currlocdata.negative_sign,     1);
5145 		add_assoc_long(  return_value, "int_frac_digits",   currlocdata.int_frac_digits     );
5146 		add_assoc_long(  return_value, "frac_digits",       currlocdata.frac_digits         );
5147 		add_assoc_long(  return_value, "p_cs_precedes",     currlocdata.p_cs_precedes       );
5148 		add_assoc_long(  return_value, "p_sep_by_space",    currlocdata.p_sep_by_space      );
5149 		add_assoc_long(  return_value, "n_cs_precedes",     currlocdata.n_cs_precedes       );
5150 		add_assoc_long(  return_value, "n_sep_by_space",    currlocdata.n_sep_by_space      );
5151 		add_assoc_long(  return_value, "p_sign_posn",       currlocdata.p_sign_posn         );
5152 		add_assoc_long(  return_value, "n_sign_posn",       currlocdata.n_sign_posn         );
5153 	}
5154 #else
5155 	/* Ok, it doesn't look like we have locale info floating around, so I guess it
5156 	   wouldn't hurt to just go ahead and return the POSIX locale information?  */
5157 
5158 	add_index_long(grouping, 0, -1);
5159 	add_index_long(mon_grouping, 0, -1);
5160 
5161 	add_assoc_string(return_value, "decimal_point",     "\x2E", 1);
5162 	add_assoc_string(return_value, "thousands_sep",     "",     1);
5163 	add_assoc_string(return_value, "int_curr_symbol",   "",     1);
5164 	add_assoc_string(return_value, "currency_symbol",   "",     1);
5165 	add_assoc_string(return_value, "mon_decimal_point", "\x2E", 1);
5166 	add_assoc_string(return_value, "mon_thousands_sep", "",     1);
5167 	add_assoc_string(return_value, "positive_sign",     "",     1);
5168 	add_assoc_string(return_value, "negative_sign",     "",     1);
5169 	add_assoc_long(  return_value, "int_frac_digits",   CHAR_MAX );
5170 	add_assoc_long(  return_value, "frac_digits",       CHAR_MAX );
5171 	add_assoc_long(  return_value, "p_cs_precedes",     CHAR_MAX );
5172 	add_assoc_long(  return_value, "p_sep_by_space",    CHAR_MAX );
5173 	add_assoc_long(  return_value, "n_cs_precedes",     CHAR_MAX );
5174 	add_assoc_long(  return_value, "n_sep_by_space",    CHAR_MAX );
5175 	add_assoc_long(  return_value, "p_sign_posn",       CHAR_MAX );
5176 	add_assoc_long(  return_value, "n_sign_posn",       CHAR_MAX );
5177 #endif
5178 
5179 	zend_hash_update(Z_ARRVAL_P(return_value), "grouping", 9, &grouping, sizeof(zval *), NULL);
5180 	zend_hash_update(Z_ARRVAL_P(return_value), "mon_grouping", 13, &mon_grouping, sizeof(zval *), NULL);
5181 }
5182 /* }}} */
5183 
5184 /* {{{ proto int strnatcasecmp(string s1, string s2)
5185    Returns the result of case-insensitive string comparison using 'natural' algorithm */
5186 PHP_FUNCTION(strnatcasecmp)
5187 {
5188 	php_strnatcmp(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
5189 }
5190 /* }}} */
5191 
5192 /* {{{ proto int substr_count(string haystack, string needle [, int offset [, int length]])
5193    Returns the number of times a substring occurs in the string */
5194 PHP_FUNCTION(substr_count)
5195 {
5196 	char *haystack, *needle;
5197 	long offset = 0, length = 0;
5198 	int ac = ZEND_NUM_ARGS();
5199 	int count = 0;
5200 	int haystack_len, needle_len;
5201 	char *p, *endp, cmp;
5202 
5203 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|ll", &haystack, &haystack_len, &needle, &needle_len, &offset, &length) == FAILURE) {
5204 		return;
5205 	}
5206 
5207 	if (needle_len == 0) {
5208 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty substring");
5209 		RETURN_FALSE;
5210 	}
5211 
5212 	p = haystack;
5213 	endp = p + haystack_len;
5214 
5215 	if (offset < 0) {
5216 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset should be greater than or equal to 0");
5217 		RETURN_FALSE;
5218 	}
5219 
5220 	if (offset > haystack_len) {
5221 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset value %ld exceeds string length", offset);
5222 		RETURN_FALSE;
5223 	}
5224 	p += offset;
5225 
5226 	if (ac == 4) {
5227 
5228 		if (length <= 0) {
5229 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Length should be greater than 0");
5230 			RETURN_FALSE;
5231 		}
5232 		if (length > (haystack_len - offset)) {
5233 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Length value %ld exceeds string length", length);
5234 			RETURN_FALSE;
5235 		}
5236 		endp = p + length;
5237 	}
5238 
5239 	if (needle_len == 1) {
5240 		cmp = needle[0];
5241 
5242 		while ((p = memchr(p, cmp, endp - p))) {
5243 			count++;
5244 			p++;
5245 		}
5246 	} else {
5247 		while ((p = php_memnstr(p, needle, needle_len, endp))) {
5248 			p += needle_len;
5249 			count++;
5250 		}
5251 	}
5252 
5253 	RETURN_LONG(count);
5254 }
5255 /* }}} */
5256 
5257 /* {{{ proto string str_pad(string input, int pad_length [, string pad_string [, int pad_type]])
5258    Returns input string padded on the left or right to specified length with pad_string */
5259 PHP_FUNCTION(str_pad)
5260 {
5261 	/* Input arguments */
5262 	char *input;				/* Input string */
5263 	int  input_len;
5264 	long pad_length;			/* Length to pad to */
5265 
5266 	/* Helper variables */
5267 	size_t	   num_pad_chars;		/* Number of padding characters (total - input size) */
5268 	char  *result = NULL;		/* Resulting string */
5269 	int	   result_len = 0;		/* Length of the resulting string */
5270 	char  *pad_str_val = " ";	/* Pointer to padding string */
5271 	int    pad_str_len = 1;		/* Length of the padding string */
5272 	long   pad_type_val = STR_PAD_RIGHT; /* The padding type value */
5273 	int	   i, left_pad=0, right_pad=0;
5274 
5275 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|sl", &input, &input_len, &pad_length,
5276 																  &pad_str_val, &pad_str_len, &pad_type_val) == FAILURE) {
5277 		return;
5278 	}
5279 
5280 	/* If resulting string turns out to be shorter than input string,
5281 	   we simply copy the input and return. */
5282 	if (pad_length <= 0 || (pad_length - input_len) <= 0) {
5283 		RETURN_STRINGL(input, input_len, 1);
5284 	}
5285 
5286 	if (pad_str_len == 0) {
5287 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Padding string cannot be empty");
5288 		return;
5289 	}
5290 
5291 	if (pad_type_val < STR_PAD_LEFT || pad_type_val > STR_PAD_BOTH) {
5292 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Padding type has to be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH");
5293 		return;
5294 	}
5295 
5296 	num_pad_chars = pad_length - input_len;
5297 	if (num_pad_chars >= INT_MAX) {
5298 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Padding length is too long");
5299 		return;
5300 	}
5301 	result = (char *)emalloc(input_len + num_pad_chars + 1);
5302 
5303 	/* We need to figure out the left/right padding lengths. */
5304 	switch (pad_type_val) {
5305 		case STR_PAD_RIGHT:
5306 			left_pad = 0;
5307 			right_pad = num_pad_chars;
5308 			break;
5309 
5310 		case STR_PAD_LEFT:
5311 			left_pad = num_pad_chars;
5312 			right_pad = 0;
5313 			break;
5314 
5315 		case STR_PAD_BOTH:
5316 			left_pad = num_pad_chars / 2;
5317 			right_pad = num_pad_chars - left_pad;
5318 			break;
5319 	}
5320 
5321 	/* First we pad on the left. */
5322 	for (i = 0; i < left_pad; i++)
5323 		result[result_len++] = pad_str_val[i % pad_str_len];
5324 
5325 	/* Then we copy the input string. */
5326 	memcpy(result + result_len, input, input_len);
5327 	result_len += input_len;
5328 
5329 	/* Finally, we pad on the right. */
5330 	for (i = 0; i < right_pad; i++)
5331 		result[result_len++] = pad_str_val[i % pad_str_len];
5332 
5333 	result[result_len] = '\0';
5334 
5335 	RETURN_STRINGL(result, result_len, 0);
5336 }
5337 /* }}} */
5338 
5339 /* {{{ proto mixed sscanf(string str, string format [, string ...])
5340    Implements an ANSI C compatible sscanf */
5341 PHP_FUNCTION(sscanf)
5342 {
5343 	zval ***args = NULL;
5344 	char *str, *format;
5345 	int str_len, format_len, result, num_args = 0;
5346 
5347 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss*", &str, &str_len, &format, &format_len,
5348 		&args, &num_args) == FAILURE) {
5349 		return;
5350 	}
5351 
5352 	result = php_sscanf_internal(str, format, num_args, args, 0, &return_value TSRMLS_CC);
5353 
5354 	if (args) {
5355 		efree(args);
5356 	}
5357 
5358 	if (SCAN_ERROR_WRONG_PARAM_COUNT == result) {
5359 		WRONG_PARAM_COUNT;
5360 	}
5361 }
5362 /* }}} */
5363 
5364 static char rot13_from[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
5365 static char rot13_to[] = "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM";
5366 
5367 /* {{{ proto string str_rot13(string str)
5368    Perform the rot13 transform on a string */
5369 PHP_FUNCTION(str_rot13)
5370 {
5371 	char *arg;
5372 	int arglen;
5373 
5374 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arglen) == FAILURE) {
5375 		return;
5376 	}
5377 
5378 	RETVAL_STRINGL(arg, arglen, 1);
5379 
5380 	php_strtr(Z_STRVAL_P(return_value), Z_STRLEN_P(return_value), rot13_from, rot13_to, 52);
5381 }
5382 /* }}} */
5383 
5384 static void php_string_shuffle(char *str, long len TSRMLS_DC) /* {{{ */
5385 {
5386 	long n_elems, rnd_idx, n_left;
5387 	char temp;
5388 	/* The implementation is stolen from array_data_shuffle       */
5389 	/* Thus the characteristics of the randomization are the same */
5390 	n_elems = len;
5391 
5392 	if (n_elems <= 1) {
5393 		return;
5394 	}
5395 
5396 	n_left = n_elems;
5397 
5398 	while (--n_left) {
5399 		rnd_idx = php_rand(TSRMLS_C);
5400 		RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX);
5401 		if (rnd_idx != n_left) {
5402 			temp = str[n_left];
5403 			str[n_left] = str[rnd_idx];
5404 			str[rnd_idx] = temp;
5405 		}
5406 	}
5407 }
5408 /* }}} */
5409 
5410 /* {{{ proto void str_shuffle(string str)
5411    Shuffles string. One permutation of all possible is created */
5412 PHP_FUNCTION(str_shuffle)
5413 {
5414 	char *arg;
5415 	int arglen;
5416 
5417 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arglen) == FAILURE) {
5418 		return;
5419 	}
5420 
5421 	RETVAL_STRINGL(arg, arglen, 1);
5422 	if (Z_STRLEN_P(return_value) > 1) {
5423 		php_string_shuffle(Z_STRVAL_P(return_value), (long) Z_STRLEN_P(return_value) TSRMLS_CC);
5424 	}
5425 }
5426 /* }}} */
5427 
5428 /* {{{ proto mixed str_word_count(string str, [int format [, string charlist]])
5429    	Counts the number of words inside a string. If format of 1 is specified,
5430    	then the function will return an array containing all the words
5431    	found inside the string. If format of 2 is specified, then the function
5432    	will return an associated array where the position of the word is the key
5433    	and the word itself is the value.
5434 
5435    	For the purpose of this function, 'word' is defined as a locale dependent
5436    	string containing alphabetic characters, which also may contain, but not start
5437    	with "'" and "-" characters.
5438 */
5439 PHP_FUNCTION(str_word_count)
5440 {
5441 	char *buf, *str, *char_list = NULL, *p, *e, *s, ch[256];
5442 	int str_len, char_list_len = 0, word_count = 0;
5443 	long type = 0;
5444 
5445 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ls", &str, &str_len, &type, &char_list, &char_list_len) == FAILURE) {
5446 		return;
5447 	}
5448 
5449 	switch(type) {
5450 		case 1:
5451 		case 2:
5452 			array_init(return_value);
5453 			if (!str_len) {
5454 				return;
5455 			}
5456 			break;
5457 		case 0:
5458 			if (!str_len) {
5459 				RETURN_LONG(0);
5460 			}
5461 			/* nothing to be done */
5462 			break;
5463 		default:
5464 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid format value %ld", type);
5465 			RETURN_FALSE;
5466 	}
5467 
5468 	if (char_list) {
5469 		php_charmask((unsigned char *)char_list, char_list_len, ch TSRMLS_CC);
5470 	}
5471 
5472 	p = str;
5473 	e = str + str_len;
5474 
5475 	/* first character cannot be ' or -, unless explicitly allowed by the user */
5476 	if ((*p == '\'' && (!char_list || !ch['\''])) || (*p == '-' && (!char_list || !ch['-']))) {
5477 		p++;
5478 	}
5479 	/* last character cannot be -, unless explicitly allowed by the user */
5480 	if (*(e - 1) == '-' && (!char_list || !ch['-'])) {
5481 		e--;
5482 	}
5483 
5484 	while (p < e) {
5485 		s = p;
5486 		while (p < e && (isalpha((unsigned char)*p) || (char_list && ch[(unsigned char)*p]) || *p == '\'' || *p == '-')) {
5487 			p++;
5488 		}
5489 		if (p > s) {
5490 			switch (type)
5491 			{
5492 				case 1:
5493 					buf = estrndup(s, (p-s));
5494 					add_next_index_stringl(return_value, buf, (p-s), 0);
5495 					break;
5496 				case 2:
5497 					buf = estrndup(s, (p-s));
5498 					add_index_stringl(return_value, (s - str), buf, p-s, 0);
5499 					break;
5500 				default:
5501 					word_count++;
5502 					break;
5503 			}
5504 		}
5505 		p++;
5506 	}
5507 
5508 	if (!type) {
5509 		RETURN_LONG(word_count);
5510 	}
5511 }
5512 
5513 /* }}} */
5514 
5515 #if HAVE_STRFMON
5516 /* {{{ proto string money_format(string format , float value)
5517    Convert monetary value(s) to string */
5518 PHP_FUNCTION(money_format)
5519 {
5520 	int format_len = 0, str_len;
5521 	char *format, *str, *p, *e;
5522 	double value;
5523 	zend_bool check = 0;
5524 
5525 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sd", &format, &format_len, &value) == FAILURE) {
5526 		return;
5527 	}
5528 
5529 	p = format;
5530 	e = p + format_len;
5531 	while ((p = memchr(p, '%', (e - p)))) {
5532 		if (*(p + 1) == '%') {
5533 			p += 2;
5534 		} else if (!check) {
5535 			check = 1;
5536 			p++;
5537 		} else {
5538 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Only a single %%i or %%n token can be used");
5539 			RETURN_FALSE;
5540 		}
5541 	}
5542 
5543 	str_len = format_len + 1024;
5544 	str = emalloc(str_len);
5545 	if ((str_len = strfmon(str, str_len, format, value)) < 0) {
5546 		efree(str);
5547 		RETURN_FALSE;
5548 	}
5549 	str[str_len] = 0;
5550 
5551 	RETURN_STRINGL(erealloc(str, str_len + 1), str_len, 0);
5552 }
5553 /* }}} */
5554 #endif
5555 
5556 /* {{{ proto array str_split(string str [, int split_length])
5557    Convert a string to an array. If split_length is specified, break the string down into chunks each split_length characters long. */
5558 PHP_FUNCTION(str_split)
5559 {
5560 	char *str;
5561 	int str_len;
5562 	long split_length = 1;
5563 	char *p;
5564 	int n_reg_segments;
5565 
5566 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &str, &str_len, &split_length) == FAILURE) {
5567 		return;
5568 	}
5569 
5570 	if (split_length <= 0) {
5571 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "The length of each segment must be greater than zero");
5572 		RETURN_FALSE;
5573 	}
5574 
5575 	array_init_size(return_value, ((str_len - 1) / split_length) + 1);
5576 
5577 	if (split_length >= str_len) {
5578 		add_next_index_stringl(return_value, str, str_len, 1);
5579 		return;
5580 	}
5581 
5582 	n_reg_segments = str_len / split_length;
5583 	p = str;
5584 
5585 	while (n_reg_segments-- > 0) {
5586 		add_next_index_stringl(return_value, p, split_length, 1);
5587 		p += split_length;
5588 	}
5589 
5590 	if (p != (str + str_len)) {
5591 		add_next_index_stringl(return_value, p, (str + str_len - p), 1);
5592 	}
5593 }
5594 /* }}} */
5595 
5596 /* {{{ proto array strpbrk(string haystack, string char_list)
5597    Search a string for any of a set of characters */
5598 PHP_FUNCTION(strpbrk)
5599 {
5600 	char *haystack, *char_list;
5601 	int haystack_len, char_list_len;
5602 	char *haystack_ptr, *cl_ptr;
5603 
5604 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &haystack, &haystack_len, &char_list, &char_list_len) == FAILURE) {
5605 		RETURN_FALSE;
5606 	}
5607 
5608 	if (!char_list_len) {
5609 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "The character list cannot be empty");
5610 		RETURN_FALSE;
5611 	}
5612 
5613 	for (haystack_ptr = haystack; haystack_ptr < (haystack + haystack_len); ++haystack_ptr) {
5614 		for (cl_ptr = char_list; cl_ptr < (char_list + char_list_len); ++cl_ptr) {
5615 			if (*cl_ptr == *haystack_ptr) {
5616 				RETURN_STRINGL(haystack_ptr, (haystack + haystack_len - haystack_ptr), 1);
5617 			}
5618 		}
5619 	}
5620 
5621 	RETURN_FALSE;
5622 }
5623 /* }}} */
5624 
5625 /* {{{ proto int substr_compare(string main_str, string str, int offset [, int length [, bool case_sensitivity]])
5626    Binary safe optionally case insensitive comparison of 2 strings from an offset, up to length characters */
5627 PHP_FUNCTION(substr_compare)
5628 {
5629 	char *s1, *s2;
5630 	int s1_len, s2_len;
5631 	long offset, len=0;
5632 	zend_bool cs=0;
5633 	uint cmp_len;
5634 
5635 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssl|lb", &s1, &s1_len, &s2, &s2_len, &offset, &len, &cs) == FAILURE) {
5636 		RETURN_FALSE;
5637 	}
5638 
5639 	if (ZEND_NUM_ARGS() >= 4 && len <= 0) {
5640 		if (len == 0) {
5641 			RETURN_LONG(0L);
5642 		} else {
5643 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "The length must be greater than or equal to zero");
5644 			RETURN_FALSE;
5645 		}
5646 	}
5647 
5648 	if (offset < 0) {
5649 		offset = s1_len + offset;
5650 		offset = (offset < 0) ? 0 : offset;
5651 	}
5652 
5653 	if (offset >= s1_len) {
5654 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "The start position cannot exceed initial string length");
5655 		RETURN_FALSE;
5656 	}
5657 
5658 	cmp_len = (uint) (len ? len : MAX(s2_len, (s1_len - offset)));
5659 
5660 	if (!cs) {
5661 		RETURN_LONG(zend_binary_strncmp(s1 + offset, (s1_len - offset), s2, s2_len, cmp_len));
5662 	} else {
5663 		RETURN_LONG(zend_binary_strncasecmp_l(s1 + offset, (s1_len - offset), s2, s2_len, cmp_len));
5664 	}
5665 }
5666 /* }}} */
5667 
5668 /*
5669  * Local variables:
5670  * tab-width: 4
5671  * c-basic-offset: 4
5672  * End:
5673  * vim600: noet sw=4 ts=4 fdm=marker
5674  * vim<600: noet sw=4 ts=4
5675  */
5676