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