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