1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2017 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Rasmus Lerdorf <rasmus@php.net>                             |
16    |          Stig S���ther Bakken <ssb@php.net>                          |
17    |          Zeev Suraski <zeev@zend.com>                                |
18    +----------------------------------------------------------------------+
19  */
20 
21 /* $Id$ */
22 
23 #include <stdio.h>
24 #include "php.h"
25 #include "php_rand.h"
26 #include "php_string.h"
27 #include "php_variables.h"
28 #ifdef HAVE_LOCALE_H
29 # include <locale.h>
30 #endif
31 #ifdef HAVE_LANGINFO_H
32 # include <langinfo.h>
33 #endif
34 #ifdef HAVE_MONETARY_H
35 # include <monetary.h>
36 #endif
37 /*
38  * This define is here because some versions of libintl redefine setlocale
39  * to point to libintl_setlocale.  That's a ridiculous thing to do as far
40  * as I am concerned, but with this define and the subsequent undef we
41  * limit the damage to just the actual setlocale() call in this file
42  * without turning zif_setlocale into zif_libintl_setlocale.  -Rasmus
43  */
44 #define php_my_setlocale setlocale
45 #ifdef HAVE_LIBINTL
46 # include <libintl.h> /* For LC_MESSAGES */
47  #ifdef setlocale
48  # undef setlocale
49  #endif
50 #endif
51 
52 #include "scanf.h"
53 #include "zend_API.h"
54 #include "zend_execute.h"
55 #include "php_globals.h"
56 #include "basic_functions.h"
57 #include "zend_smart_str.h"
58 #include <Zend/zend_exceptions.h>
59 #ifdef ZTS
60 #include "TSRM.h"
61 #endif
62 
63 /* For str_getcsv() support */
64 #include "ext/standard/file.h"
65 /* For php_next_utf8_char() */
66 #include "ext/standard/html.h"
67 
68 #define STR_PAD_LEFT			0
69 #define STR_PAD_RIGHT			1
70 #define STR_PAD_BOTH			2
71 #define PHP_PATHINFO_DIRNAME 	1
72 #define PHP_PATHINFO_BASENAME 	2
73 #define PHP_PATHINFO_EXTENSION 	4
74 #define PHP_PATHINFO_FILENAME 	8
75 #define PHP_PATHINFO_ALL	(PHP_PATHINFO_DIRNAME | PHP_PATHINFO_BASENAME | PHP_PATHINFO_EXTENSION | PHP_PATHINFO_FILENAME)
76 
77 #define STR_STRSPN				0
78 #define STR_STRCSPN				1
79 
80 /* {{{ register_string_constants
81  */
register_string_constants(INIT_FUNC_ARGS)82 void register_string_constants(INIT_FUNC_ARGS)
83 {
84 	REGISTER_LONG_CONSTANT("STR_PAD_LEFT", STR_PAD_LEFT, CONST_CS | CONST_PERSISTENT);
85 	REGISTER_LONG_CONSTANT("STR_PAD_RIGHT", STR_PAD_RIGHT, CONST_CS | CONST_PERSISTENT);
86 	REGISTER_LONG_CONSTANT("STR_PAD_BOTH", STR_PAD_BOTH, CONST_CS | CONST_PERSISTENT);
87 	REGISTER_LONG_CONSTANT("PATHINFO_DIRNAME", PHP_PATHINFO_DIRNAME, CONST_CS | CONST_PERSISTENT);
88 	REGISTER_LONG_CONSTANT("PATHINFO_BASENAME", PHP_PATHINFO_BASENAME, CONST_CS | CONST_PERSISTENT);
89 	REGISTER_LONG_CONSTANT("PATHINFO_EXTENSION", PHP_PATHINFO_EXTENSION, CONST_CS | CONST_PERSISTENT);
90 	REGISTER_LONG_CONSTANT("PATHINFO_FILENAME", PHP_PATHINFO_FILENAME, CONST_CS | CONST_PERSISTENT);
91 
92 #ifdef HAVE_LOCALECONV
93 	/* If last members of struct lconv equal CHAR_MAX, no grouping is done */
94 
95 /* This is bad, but since we are going to be hardcoding in the POSIX stuff anyway... */
96 # ifndef HAVE_LIMITS_H
97 # define CHAR_MAX 127
98 # endif
99 
100 	REGISTER_LONG_CONSTANT("CHAR_MAX", CHAR_MAX, CONST_CS | CONST_PERSISTENT);
101 #endif
102 
103 #ifdef HAVE_LOCALE_H
104 	REGISTER_LONG_CONSTANT("LC_CTYPE", LC_CTYPE, CONST_CS | CONST_PERSISTENT);
105 	REGISTER_LONG_CONSTANT("LC_NUMERIC", LC_NUMERIC, CONST_CS | CONST_PERSISTENT);
106 	REGISTER_LONG_CONSTANT("LC_TIME", LC_TIME, CONST_CS | CONST_PERSISTENT);
107 	REGISTER_LONG_CONSTANT("LC_COLLATE", LC_COLLATE, CONST_CS | CONST_PERSISTENT);
108 	REGISTER_LONG_CONSTANT("LC_MONETARY", LC_MONETARY, CONST_CS | CONST_PERSISTENT);
109 	REGISTER_LONG_CONSTANT("LC_ALL", LC_ALL, CONST_CS | CONST_PERSISTENT);
110 # ifdef LC_MESSAGES
111 	REGISTER_LONG_CONSTANT("LC_MESSAGES", LC_MESSAGES, CONST_CS | CONST_PERSISTENT);
112 # endif
113 #endif
114 
115 }
116 /* }}} */
117 
118 int php_tag_find(char *tag, size_t len, const char *set);
119 
120 #ifdef PHP_WIN32
121 # define SET_ALIGNED(alignment, decl) __declspec(align(alignment)) decl
122 #elif HAVE_ATTRIBUTE_ALIGNED
123 # define SET_ALIGNED(alignment, decl) decl __attribute__ ((__aligned__ (alignment)))
124 #else
125 # define SET_ALIGNED(alignment, decl) decl
126 #endif
127 
128 /* this is read-only, so it's ok */
129 SET_ALIGNED(16, static char hexconvtab[]) = "0123456789abcdef";
130 
131 /* localeconv mutex */
132 #ifdef ZTS
133 static MUTEX_T locale_mutex = NULL;
134 #endif
135 
136 /* {{{ php_bin2hex
137  */
php_bin2hex(const unsigned char * old,const size_t oldlen)138 static zend_string *php_bin2hex(const unsigned char *old, const size_t oldlen)
139 {
140 	zend_string *result;
141 	size_t i, j;
142 
143 	result = zend_string_safe_alloc(oldlen, 2 * sizeof(char), 0, 0);
144 
145 	for (i = j = 0; i < oldlen; i++) {
146 		ZSTR_VAL(result)[j++] = hexconvtab[old[i] >> 4];
147 		ZSTR_VAL(result)[j++] = hexconvtab[old[i] & 15];
148 	}
149 	ZSTR_VAL(result)[j] = '\0';
150 
151 	return result;
152 }
153 /* }}} */
154 
155 /* {{{ php_hex2bin
156  */
php_hex2bin(const unsigned char * old,const size_t oldlen)157 static zend_string *php_hex2bin(const unsigned char *old, const size_t oldlen)
158 {
159 	size_t target_length = oldlen >> 1;
160 	zend_string *str = zend_string_alloc(target_length, 0);
161 	unsigned char *ret = (unsigned char *)ZSTR_VAL(str);
162 	size_t i, j;
163 
164 	for (i = j = 0; i < target_length; i++) {
165 		unsigned char c = old[j++];
166 		unsigned char l = c & ~0x20;
167 		int is_letter = ((unsigned int) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(unsigned int) - 1);
168 		unsigned char d;
169 
170 		/* basically (c >= '0' && c <= '9') || (l >= 'A' && l <= 'F') */
171 		if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(unsigned int) - 1)) | is_letter)) {
172 			d = (l - 0x10 - 0x27 * is_letter) << 4;
173 		} else {
174 			zend_string_free(str);
175 			return NULL;
176 		}
177 		c = old[j++];
178 		l = c & ~0x20;
179 		is_letter = ((unsigned int) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(unsigned int) - 1);
180 		if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(unsigned int) - 1)) | is_letter)) {
181 			d |= l - 0x10 - 0x27 * is_letter;
182 		} else {
183 			zend_string_free(str);
184 			return NULL;
185 		}
186 		ret[i] = d;
187 	}
188 	ret[i] = '\0';
189 
190 	return str;
191 }
192 /* }}} */
193 
194 #ifdef HAVE_LOCALECONV
195 /* {{{ localeconv_r
196  * glibc's localeconv is not reentrant, so lets make it so ... sorta */
localeconv_r(struct lconv * out)197 PHPAPI struct lconv *localeconv_r(struct lconv *out)
198 {
199 
200 # ifdef ZTS
201 	tsrm_mutex_lock( locale_mutex );
202 # endif
203 
204 /*  cur->locinfo is struct __crt_locale_info which implementation is
205 	hidden in vc14. TODO revisit this and check if a workaround available
206 	and needed. */
207 #if defined(PHP_WIN32) && _MSC_VER < 1900 && defined(ZTS)
208 	{
209 		/* Even with the enabled per thread locale, localeconv
210 			won't check any locale change in the master thread. */
211 		_locale_t cur = _get_current_locale();
212 		*out = *cur->locinfo->lconv;
213 		_free_locale(cur);
214 	}
215 #else
216 	/* localeconv doesn't return an error condition */
217 	*out = *localeconv();
218 #endif
219 
220 # ifdef ZTS
221 	tsrm_mutex_unlock( locale_mutex );
222 # endif
223 
224 	return out;
225 }
226 /* }}} */
227 
228 # ifdef ZTS
229 /* {{{ PHP_MINIT_FUNCTION
230  */
PHP_MINIT_FUNCTION(localeconv)231 PHP_MINIT_FUNCTION(localeconv)
232 {
233 	locale_mutex = tsrm_mutex_alloc();
234 	return SUCCESS;
235 }
236 /* }}} */
237 
238 /* {{{ PHP_MSHUTDOWN_FUNCTION
239  */
PHP_MSHUTDOWN_FUNCTION(localeconv)240 PHP_MSHUTDOWN_FUNCTION(localeconv)
241 {
242 	tsrm_mutex_free( locale_mutex );
243 	locale_mutex = NULL;
244 	return SUCCESS;
245 }
246 /* }}} */
247 # endif
248 #endif
249 
250 /* {{{ proto string bin2hex(string data)
251    Converts the binary representation of data to hex */
PHP_FUNCTION(bin2hex)252 PHP_FUNCTION(bin2hex)
253 {
254 	zend_string *result;
255 	zend_string *data;
256 
257 	ZEND_PARSE_PARAMETERS_START(1, 1)
258 		Z_PARAM_STR(data)
259 	ZEND_PARSE_PARAMETERS_END();
260 
261 	result = php_bin2hex((unsigned char *)ZSTR_VAL(data), ZSTR_LEN(data));
262 
263 	if (!result) {
264 		RETURN_FALSE;
265 	}
266 
267 	RETURN_STR(result);
268 }
269 /* }}} */
270 
271 /* {{{ proto string hex2bin(string data)
272    Converts the hex representation of data to binary */
PHP_FUNCTION(hex2bin)273 PHP_FUNCTION(hex2bin)
274 {
275 	zend_string *result, *data;
276 
277 	ZEND_PARSE_PARAMETERS_START(1, 1)
278 		Z_PARAM_STR(data)
279 	ZEND_PARSE_PARAMETERS_END();
280 
281 	if (ZSTR_LEN(data) % 2 != 0) {
282 		php_error_docref(NULL, E_WARNING, "Hexadecimal input string must have an even length");
283 		RETURN_FALSE;
284 	}
285 
286 	result = php_hex2bin((unsigned char *)ZSTR_VAL(data), ZSTR_LEN(data));
287 
288 	if (!result) {
289 		php_error_docref(NULL, E_WARNING, "Input string must be hexadecimal string");
290 		RETURN_FALSE;
291 	}
292 
293 	RETVAL_STR(result);
294 }
295 /* }}} */
296 
php_spn_common_handler(INTERNAL_FUNCTION_PARAMETERS,int behavior)297 static void php_spn_common_handler(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
298 {
299 	zend_string *s11, *s22;
300 	zend_long start = 0, len = 0;
301 
302 	ZEND_PARSE_PARAMETERS_START(2, 4)
303 		Z_PARAM_STR(s11)
304 		Z_PARAM_STR(s22)
305 		Z_PARAM_OPTIONAL
306 		Z_PARAM_LONG(start)
307 		Z_PARAM_LONG(len)
308 	ZEND_PARSE_PARAMETERS_END();
309 
310 	if (ZEND_NUM_ARGS() < 4) {
311 		len = ZSTR_LEN(s11);
312 	}
313 
314 	/* look at substr() function for more information */
315 
316 	if (start < 0) {
317 		start += (zend_long)ZSTR_LEN(s11);
318 		if (start < 0) {
319 			start = 0;
320 		}
321 	} else if ((size_t)start > ZSTR_LEN(s11)) {
322 		RETURN_FALSE;
323 	}
324 
325 	if (len < 0) {
326 		len += (ZSTR_LEN(s11) - start);
327 		if (len < 0) {
328 			len = 0;
329 		}
330 	}
331 
332 	if (len > (zend_long)ZSTR_LEN(s11) - start) {
333 		len = ZSTR_LEN(s11) - start;
334 	}
335 
336 	if(len == 0) {
337 		RETURN_LONG(0);
338 	}
339 
340 	if (behavior == STR_STRSPN) {
341 		RETURN_LONG(php_strspn(ZSTR_VAL(s11) + start /*str1_start*/,
342 						ZSTR_VAL(s22) /*str2_start*/,
343 						ZSTR_VAL(s11) + start + len /*str1_end*/,
344 						ZSTR_VAL(s22) + ZSTR_LEN(s22) /*str2_end*/));
345 	} else if (behavior == STR_STRCSPN) {
346 		RETURN_LONG(php_strcspn(ZSTR_VAL(s11) + start /*str1_start*/,
347 						ZSTR_VAL(s22) /*str2_start*/,
348 						ZSTR_VAL(s11) + start + len /*str1_end*/,
349 						ZSTR_VAL(s22) + ZSTR_LEN(s22) /*str2_end*/));
350 	}
351 
352 }
353 /* }}} */
354 
355 /* {{{ proto int strspn(string str, string mask [, int start [, int len]])
356    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)357 PHP_FUNCTION(strspn)
358 {
359 	php_spn_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, STR_STRSPN);
360 }
361 /* }}} */
362 
363 /* {{{ proto int strcspn(string str, string mask [, int start [, int len]])
364    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)365 PHP_FUNCTION(strcspn)
366 {
367 	php_spn_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, STR_STRCSPN);
368 }
369 /* }}} */
370 
371 /* {{{ PHP_MINIT_FUNCTION(nl_langinfo) */
372 #if HAVE_NL_LANGINFO
PHP_MINIT_FUNCTION(nl_langinfo)373 PHP_MINIT_FUNCTION(nl_langinfo)
374 {
375 #define REGISTER_NL_LANGINFO_CONSTANT(x)	REGISTER_LONG_CONSTANT(#x, x, CONST_CS | CONST_PERSISTENT)
376 #ifdef ABDAY_1
377 	REGISTER_NL_LANGINFO_CONSTANT(ABDAY_1);
378 	REGISTER_NL_LANGINFO_CONSTANT(ABDAY_2);
379 	REGISTER_NL_LANGINFO_CONSTANT(ABDAY_3);
380 	REGISTER_NL_LANGINFO_CONSTANT(ABDAY_4);
381 	REGISTER_NL_LANGINFO_CONSTANT(ABDAY_5);
382 	REGISTER_NL_LANGINFO_CONSTANT(ABDAY_6);
383 	REGISTER_NL_LANGINFO_CONSTANT(ABDAY_7);
384 #endif
385 #ifdef DAY_1
386 	REGISTER_NL_LANGINFO_CONSTANT(DAY_1);
387 	REGISTER_NL_LANGINFO_CONSTANT(DAY_2);
388 	REGISTER_NL_LANGINFO_CONSTANT(DAY_3);
389 	REGISTER_NL_LANGINFO_CONSTANT(DAY_4);
390 	REGISTER_NL_LANGINFO_CONSTANT(DAY_5);
391 	REGISTER_NL_LANGINFO_CONSTANT(DAY_6);
392 	REGISTER_NL_LANGINFO_CONSTANT(DAY_7);
393 #endif
394 #ifdef ABMON_1
395 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_1);
396 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_2);
397 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_3);
398 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_4);
399 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_5);
400 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_6);
401 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_7);
402 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_8);
403 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_9);
404 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_10);
405 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_11);
406 	REGISTER_NL_LANGINFO_CONSTANT(ABMON_12);
407 #endif
408 #ifdef MON_1
409 	REGISTER_NL_LANGINFO_CONSTANT(MON_1);
410 	REGISTER_NL_LANGINFO_CONSTANT(MON_2);
411 	REGISTER_NL_LANGINFO_CONSTANT(MON_3);
412 	REGISTER_NL_LANGINFO_CONSTANT(MON_4);
413 	REGISTER_NL_LANGINFO_CONSTANT(MON_5);
414 	REGISTER_NL_LANGINFO_CONSTANT(MON_6);
415 	REGISTER_NL_LANGINFO_CONSTANT(MON_7);
416 	REGISTER_NL_LANGINFO_CONSTANT(MON_8);
417 	REGISTER_NL_LANGINFO_CONSTANT(MON_9);
418 	REGISTER_NL_LANGINFO_CONSTANT(MON_10);
419 	REGISTER_NL_LANGINFO_CONSTANT(MON_11);
420 	REGISTER_NL_LANGINFO_CONSTANT(MON_12);
421 #endif
422 #ifdef AM_STR
423 	REGISTER_NL_LANGINFO_CONSTANT(AM_STR);
424 #endif
425 #ifdef PM_STR
426 	REGISTER_NL_LANGINFO_CONSTANT(PM_STR);
427 #endif
428 #ifdef D_T_FMT
429 	REGISTER_NL_LANGINFO_CONSTANT(D_T_FMT);
430 #endif
431 #ifdef D_FMT
432 	REGISTER_NL_LANGINFO_CONSTANT(D_FMT);
433 #endif
434 #ifdef T_FMT
435 	REGISTER_NL_LANGINFO_CONSTANT(T_FMT);
436 #endif
437 #ifdef T_FMT_AMPM
438 	REGISTER_NL_LANGINFO_CONSTANT(T_FMT_AMPM);
439 #endif
440 #ifdef ERA
441 	REGISTER_NL_LANGINFO_CONSTANT(ERA);
442 #endif
443 #ifdef ERA_YEAR
444 	REGISTER_NL_LANGINFO_CONSTANT(ERA_YEAR);
445 #endif
446 #ifdef ERA_D_T_FMT
447 	REGISTER_NL_LANGINFO_CONSTANT(ERA_D_T_FMT);
448 #endif
449 #ifdef ERA_D_FMT
450 	REGISTER_NL_LANGINFO_CONSTANT(ERA_D_FMT);
451 #endif
452 #ifdef ERA_T_FMT
453 	REGISTER_NL_LANGINFO_CONSTANT(ERA_T_FMT);
454 #endif
455 #ifdef ALT_DIGITS
456 	REGISTER_NL_LANGINFO_CONSTANT(ALT_DIGITS);
457 #endif
458 #ifdef INT_CURR_SYMBOL
459 	REGISTER_NL_LANGINFO_CONSTANT(INT_CURR_SYMBOL);
460 #endif
461 #ifdef CURRENCY_SYMBOL
462 	REGISTER_NL_LANGINFO_CONSTANT(CURRENCY_SYMBOL);
463 #endif
464 #ifdef CRNCYSTR
465 	REGISTER_NL_LANGINFO_CONSTANT(CRNCYSTR);
466 #endif
467 #ifdef MON_DECIMAL_POINT
468 	REGISTER_NL_LANGINFO_CONSTANT(MON_DECIMAL_POINT);
469 #endif
470 #ifdef MON_THOUSANDS_SEP
471 	REGISTER_NL_LANGINFO_CONSTANT(MON_THOUSANDS_SEP);
472 #endif
473 #ifdef MON_GROUPING
474 	REGISTER_NL_LANGINFO_CONSTANT(MON_GROUPING);
475 #endif
476 #ifdef POSITIVE_SIGN
477 	REGISTER_NL_LANGINFO_CONSTANT(POSITIVE_SIGN);
478 #endif
479 #ifdef NEGATIVE_SIGN
480 	REGISTER_NL_LANGINFO_CONSTANT(NEGATIVE_SIGN);
481 #endif
482 #ifdef INT_FRAC_DIGITS
483 	REGISTER_NL_LANGINFO_CONSTANT(INT_FRAC_DIGITS);
484 #endif
485 #ifdef FRAC_DIGITS
486 	REGISTER_NL_LANGINFO_CONSTANT(FRAC_DIGITS);
487 #endif
488 #ifdef P_CS_PRECEDES
489 	REGISTER_NL_LANGINFO_CONSTANT(P_CS_PRECEDES);
490 #endif
491 #ifdef P_SEP_BY_SPACE
492 	REGISTER_NL_LANGINFO_CONSTANT(P_SEP_BY_SPACE);
493 #endif
494 #ifdef N_CS_PRECEDES
495 	REGISTER_NL_LANGINFO_CONSTANT(N_CS_PRECEDES);
496 #endif
497 #ifdef N_SEP_BY_SPACE
498 	REGISTER_NL_LANGINFO_CONSTANT(N_SEP_BY_SPACE);
499 #endif
500 #ifdef P_SIGN_POSN
501 	REGISTER_NL_LANGINFO_CONSTANT(P_SIGN_POSN);
502 #endif
503 #ifdef N_SIGN_POSN
504 	REGISTER_NL_LANGINFO_CONSTANT(N_SIGN_POSN);
505 #endif
506 #ifdef DECIMAL_POINT
507 	REGISTER_NL_LANGINFO_CONSTANT(DECIMAL_POINT);
508 #endif
509 #ifdef RADIXCHAR
510 	REGISTER_NL_LANGINFO_CONSTANT(RADIXCHAR);
511 #endif
512 #ifdef THOUSANDS_SEP
513 	REGISTER_NL_LANGINFO_CONSTANT(THOUSANDS_SEP);
514 #endif
515 #ifdef THOUSEP
516 	REGISTER_NL_LANGINFO_CONSTANT(THOUSEP);
517 #endif
518 #ifdef GROUPING
519 	REGISTER_NL_LANGINFO_CONSTANT(GROUPING);
520 #endif
521 #ifdef YESEXPR
522 	REGISTER_NL_LANGINFO_CONSTANT(YESEXPR);
523 #endif
524 #ifdef NOEXPR
525 	REGISTER_NL_LANGINFO_CONSTANT(NOEXPR);
526 #endif
527 #ifdef YESSTR
528 	REGISTER_NL_LANGINFO_CONSTANT(YESSTR);
529 #endif
530 #ifdef NOSTR
531 	REGISTER_NL_LANGINFO_CONSTANT(NOSTR);
532 #endif
533 #ifdef CODESET
534 	REGISTER_NL_LANGINFO_CONSTANT(CODESET);
535 #endif
536 #undef REGISTER_NL_LANGINFO_CONSTANT
537 	return SUCCESS;
538 }
539 /* }}} */
540 
541 /* {{{ proto string nl_langinfo(int item)
542    Query language and locale information */
PHP_FUNCTION(nl_langinfo)543 PHP_FUNCTION(nl_langinfo)
544 {
545 	zend_long item;
546 	char *value;
547 
548 	ZEND_PARSE_PARAMETERS_START(1, 1)
549 		Z_PARAM_LONG(item)
550 	ZEND_PARSE_PARAMETERS_END();
551 
552 	switch(item) { /* {{{ */
553 #ifdef ABDAY_1
554 		case ABDAY_1:
555 		case ABDAY_2:
556 		case ABDAY_3:
557 		case ABDAY_4:
558 		case ABDAY_5:
559 		case ABDAY_6:
560 		case ABDAY_7:
561 #endif
562 #ifdef DAY_1
563 		case DAY_1:
564 		case DAY_2:
565 		case DAY_3:
566 		case DAY_4:
567 		case DAY_5:
568 		case DAY_6:
569 		case DAY_7:
570 #endif
571 #ifdef ABMON_1
572 		case ABMON_1:
573 		case ABMON_2:
574 		case ABMON_3:
575 		case ABMON_4:
576 		case ABMON_5:
577 		case ABMON_6:
578 		case ABMON_7:
579 		case ABMON_8:
580 		case ABMON_9:
581 		case ABMON_10:
582 		case ABMON_11:
583 		case ABMON_12:
584 #endif
585 #ifdef MON_1
586 		case MON_1:
587 		case MON_2:
588 		case MON_3:
589 		case MON_4:
590 		case MON_5:
591 		case MON_6:
592 		case MON_7:
593 		case MON_8:
594 		case MON_9:
595 		case MON_10:
596 		case MON_11:
597 		case MON_12:
598 #endif
599 #ifdef AM_STR
600 		case AM_STR:
601 #endif
602 #ifdef PM_STR
603 		case PM_STR:
604 #endif
605 #ifdef D_T_FMT
606 		case D_T_FMT:
607 #endif
608 #ifdef D_FMT
609 		case D_FMT:
610 #endif
611 #ifdef T_FMT
612 		case T_FMT:
613 #endif
614 #ifdef T_FMT_AMPM
615 		case T_FMT_AMPM:
616 #endif
617 #ifdef ERA
618 		case ERA:
619 #endif
620 #ifdef ERA_YEAR
621 		case ERA_YEAR:
622 #endif
623 #ifdef ERA_D_T_FMT
624 		case ERA_D_T_FMT:
625 #endif
626 #ifdef ERA_D_FMT
627 		case ERA_D_FMT:
628 #endif
629 #ifdef ERA_T_FMT
630 		case ERA_T_FMT:
631 #endif
632 #ifdef ALT_DIGITS
633 		case ALT_DIGITS:
634 #endif
635 #ifdef INT_CURR_SYMBOL
636 		case INT_CURR_SYMBOL:
637 #endif
638 #ifdef CURRENCY_SYMBOL
639 		case CURRENCY_SYMBOL:
640 #endif
641 #ifdef CRNCYSTR
642 		case CRNCYSTR:
643 #endif
644 #ifdef MON_DECIMAL_POINT
645 		case MON_DECIMAL_POINT:
646 #endif
647 #ifdef MON_THOUSANDS_SEP
648 		case MON_THOUSANDS_SEP:
649 #endif
650 #ifdef MON_GROUPING
651 		case MON_GROUPING:
652 #endif
653 #ifdef POSITIVE_SIGN
654 		case POSITIVE_SIGN:
655 #endif
656 #ifdef NEGATIVE_SIGN
657 		case NEGATIVE_SIGN:
658 #endif
659 #ifdef INT_FRAC_DIGITS
660 		case INT_FRAC_DIGITS:
661 #endif
662 #ifdef FRAC_DIGITS
663 		case FRAC_DIGITS:
664 #endif
665 #ifdef P_CS_PRECEDES
666 		case P_CS_PRECEDES:
667 #endif
668 #ifdef P_SEP_BY_SPACE
669 		case P_SEP_BY_SPACE:
670 #endif
671 #ifdef N_CS_PRECEDES
672 		case N_CS_PRECEDES:
673 #endif
674 #ifdef N_SEP_BY_SPACE
675 		case N_SEP_BY_SPACE:
676 #endif
677 #ifdef P_SIGN_POSN
678 		case P_SIGN_POSN:
679 #endif
680 #ifdef N_SIGN_POSN
681 		case N_SIGN_POSN:
682 #endif
683 #ifdef DECIMAL_POINT
684 		case DECIMAL_POINT:
685 #elif defined(RADIXCHAR)
686 		case RADIXCHAR:
687 #endif
688 #ifdef THOUSANDS_SEP
689 		case THOUSANDS_SEP:
690 #elif defined(THOUSEP)
691 		case THOUSEP:
692 #endif
693 #ifdef GROUPING
694 		case GROUPING:
695 #endif
696 #ifdef YESEXPR
697 		case YESEXPR:
698 #endif
699 #ifdef NOEXPR
700 		case NOEXPR:
701 #endif
702 #ifdef YESSTR
703 		case YESSTR:
704 #endif
705 #ifdef NOSTR
706 		case NOSTR:
707 #endif
708 #ifdef CODESET
709 		case CODESET:
710 #endif
711 			break;
712 		default:
713 			php_error_docref(NULL, E_WARNING, "Item '" ZEND_LONG_FMT "' is not valid", item);
714 			RETURN_FALSE;
715 	}
716 	/* }}} */
717 
718 	value = nl_langinfo(item);
719 	if (value == NULL) {
720 		RETURN_FALSE;
721 	} else {
722 		RETURN_STRING(value);
723 	}
724 }
725 #endif
726 /* }}} */
727 
728 #ifdef HAVE_STRCOLL
729 /* {{{ proto int strcoll(string str1, string str2)
730    Compares two strings using the current locale */
PHP_FUNCTION(strcoll)731 PHP_FUNCTION(strcoll)
732 {
733 	zend_string *s1, *s2;
734 
735 	ZEND_PARSE_PARAMETERS_START(2, 2)
736 		Z_PARAM_STR(s1)
737 		Z_PARAM_STR(s2)
738 	ZEND_PARSE_PARAMETERS_END();
739 
740 	RETURN_LONG(strcoll((const char *) ZSTR_VAL(s1),
741 	                    (const char *) ZSTR_VAL(s2)));
742 }
743 /* }}} */
744 #endif
745 
746 /* {{{ php_charmask
747  * Fills a 256-byte bytemask with input. You can specify a range like 'a..z',
748  * it needs to be incrementing.
749  * Returns: FAILURE/SUCCESS whether the input was correct (i.e. no range errors)
750  */
php_charmask(unsigned char * input,size_t len,char * mask)751 static inline int php_charmask(unsigned char *input, size_t len, char *mask)
752 {
753 	unsigned char *end;
754 	unsigned char c;
755 	int result = SUCCESS;
756 
757 	memset(mask, 0, 256);
758 	for (end = input+len; input < end; input++) {
759 		c=*input;
760 		if ((input+3 < end) && input[1] == '.' && input[2] == '.'
761 				&& input[3] >= c) {
762 			memset(mask+c, 1, input[3] - c + 1);
763 			input+=3;
764 		} else if ((input+1 < end) && input[0] == '.' && input[1] == '.') {
765 			/* Error, try to be as helpful as possible:
766 			   (a range ending/starting with '.' won't be captured here) */
767 			if (end-len >= input) { /* there was no 'left' char */
768 				php_error_docref(NULL, E_WARNING, "Invalid '..'-range, no character to the left of '..'");
769 				result = FAILURE;
770 				continue;
771 			}
772 			if (input+2 >= end) { /* there is no 'right' char */
773 				php_error_docref(NULL, E_WARNING, "Invalid '..'-range, no character to the right of '..'");
774 				result = FAILURE;
775 				continue;
776 			}
777 			if (input[-1] > input[2]) { /* wrong order */
778 				php_error_docref(NULL, E_WARNING, "Invalid '..'-range, '..'-range needs to be incrementing");
779 				result = FAILURE;
780 				continue;
781 			}
782 			/* FIXME: better error (a..b..c is the only left possibility?) */
783 			php_error_docref(NULL, E_WARNING, "Invalid '..'-range");
784 			result = FAILURE;
785 			continue;
786 		} else {
787 			mask[c]=1;
788 		}
789 	}
790 	return result;
791 }
792 /* }}} */
793 
794 /* {{{ php_trim_int()
795  * mode 1 : trim left
796  * mode 2 : trim right
797  * mode 3 : trim left and right
798  * what indicates which chars are to be trimmed. NULL->default (' \t\n\r\v\0')
799  */
php_trim_int(zend_string * str,char * what,size_t what_len,int mode)800 static zend_always_inline zend_string *php_trim_int(zend_string *str, char *what, size_t what_len, int mode)
801 {
802 	const char *start = ZSTR_VAL(str);
803 	const char *end = start + ZSTR_LEN(str);
804 	char mask[256];
805 
806 	if (what) {
807 		if (what_len == 1) {
808 			char p = *what;
809 			if (mode & 1) {
810 				while (start != end) {
811 					if (*start == p) {
812 						start++;
813 					} else {
814 						break;
815 					}
816 				}
817 			}
818 			if (mode & 2) {
819 				while (start != end) {
820 					if (*(end-1) == p) {
821 						end--;
822 					} else {
823 						break;
824 					}
825 				}
826 			}
827 		} else {
828 			php_charmask((unsigned char*)what, what_len, mask);
829 
830 			if (mode & 1) {
831 				while (start != end) {
832 					if (mask[(unsigned char)*start]) {
833 						start++;
834 					} else {
835 						break;
836 					}
837 				}
838 			}
839 			if (mode & 2) {
840 				while (start != end) {
841 					if (mask[(unsigned char)*(end-1)]) {
842 						end--;
843 					} else {
844 						break;
845 					}
846 				}
847 			}
848 		}
849 	} else {
850 		if (mode & 1) {
851 			while (start != end) {
852 				unsigned char c = (unsigned char)*start;
853 
854 				if (c <= ' ' &&
855 				    (c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\v' || c == '\0')) {
856 					start++;
857 				} else {
858 					break;
859 				}
860 			}
861 		}
862 		if (mode & 2) {
863 			while (start != end) {
864 				unsigned char c = (unsigned char)*(end-1);
865 
866 				if (c <= ' ' &&
867 				    (c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\v' || c == '\0')) {
868 					end--;
869 				} else {
870 					break;
871 				}
872 			}
873 		}
874 	}
875 
876 	if (ZSTR_LEN(str) == end - start) {
877 		return zend_string_copy(str);
878 	} else if (end - start == 0) {
879 		return ZSTR_EMPTY_ALLOC();
880 	} else {
881 		return zend_string_init(start, end - start, 0);
882 	}
883 }
884 /* }}} */
885 
886 /* {{{ php_trim_int()
887  * mode 1 : trim left
888  * mode 2 : trim right
889  * mode 3 : trim left and right
890  * what indicates which chars are to be trimmed. NULL->default (' \t\n\r\v\0')
891  */
php_trim(zend_string * str,char * what,size_t what_len,int mode)892 PHPAPI zend_string *php_trim(zend_string *str, char *what, size_t what_len, int mode)
893 {
894 	return php_trim_int(str, what, what_len, mode);
895 }
896 /* }}} */
897 
898 /* {{{ php_do_trim
899  * Base for trim(), rtrim() and ltrim() functions.
900  */
php_do_trim(INTERNAL_FUNCTION_PARAMETERS,int mode)901 static zend_always_inline void php_do_trim(INTERNAL_FUNCTION_PARAMETERS, int mode)
902 {
903 	zend_string *str;
904 	zend_string *what = NULL;
905 
906 	ZEND_PARSE_PARAMETERS_START(1, 2)
907 		Z_PARAM_STR(str)
908 		Z_PARAM_OPTIONAL
909 		Z_PARAM_STR(what)
910 	ZEND_PARSE_PARAMETERS_END();
911 
912 	ZVAL_STR(return_value, php_trim_int(str, (what ? ZSTR_VAL(what) : NULL), (what ? ZSTR_LEN(what) : 0), mode));
913 }
914 /* }}} */
915 
916 /* {{{ proto string trim(string str [, string character_mask])
917    Strips whitespace from the beginning and end of a string */
PHP_FUNCTION(trim)918 PHP_FUNCTION(trim)
919 {
920 	php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3);
921 }
922 /* }}} */
923 
924 /* {{{ proto string rtrim(string str [, string character_mask])
925    Removes trailing whitespace */
PHP_FUNCTION(rtrim)926 PHP_FUNCTION(rtrim)
927 {
928 	php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 2);
929 }
930 /* }}} */
931 
932 /* {{{ proto string ltrim(string str [, string character_mask])
933    Strips whitespace from the beginning of a string */
PHP_FUNCTION(ltrim)934 PHP_FUNCTION(ltrim)
935 {
936 	php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
937 }
938 /* }}} */
939 
940 /* {{{ proto string wordwrap(string str [, int width [, string break [, boolean cut]]])
941    Wraps buffer to selected number of characters using string break char */
PHP_FUNCTION(wordwrap)942 PHP_FUNCTION(wordwrap)
943 {
944 	zend_string *text;
945 	char *breakchar = "\n";
946 	size_t newtextlen, chk, breakchar_len = 1;
947 	size_t alloced;
948 	zend_long current = 0, laststart = 0, lastspace = 0;
949 	zend_long linelength = 75;
950 	zend_bool docut = 0;
951 	zend_string *newtext;
952 
953 	ZEND_PARSE_PARAMETERS_START(1, 4)
954 		Z_PARAM_STR(text)
955 		Z_PARAM_OPTIONAL
956 		Z_PARAM_LONG(linelength)
957 		Z_PARAM_STRING(breakchar, breakchar_len)
958 		Z_PARAM_BOOL(docut)
959 	ZEND_PARSE_PARAMETERS_END();
960 
961 	if (ZSTR_LEN(text) == 0) {
962 		RETURN_EMPTY_STRING();
963 	}
964 
965 	if (breakchar_len == 0) {
966 		php_error_docref(NULL, E_WARNING, "Break string cannot be empty");
967 		RETURN_FALSE;
968 	}
969 
970 	if (linelength == 0 && docut) {
971 		php_error_docref(NULL, E_WARNING, "Can't force cut when width is zero");
972 		RETURN_FALSE;
973 	}
974 
975 	/* Special case for a single-character break as it needs no
976 	   additional storage space */
977 	if (breakchar_len == 1 && !docut) {
978 		newtext = zend_string_init(ZSTR_VAL(text), ZSTR_LEN(text), 0);
979 
980 		laststart = lastspace = 0;
981 		for (current = 0; current < (zend_long)ZSTR_LEN(text); current++) {
982 			if (ZSTR_VAL(text)[current] == breakchar[0]) {
983 				laststart = lastspace = current + 1;
984 			} else if (ZSTR_VAL(text)[current] == ' ') {
985 				if (current - laststart >= linelength) {
986 					ZSTR_VAL(newtext)[current] = breakchar[0];
987 					laststart = current + 1;
988 				}
989 				lastspace = current;
990 			} else if (current - laststart >= linelength && laststart != lastspace) {
991 				ZSTR_VAL(newtext)[lastspace] = breakchar[0];
992 				laststart = lastspace + 1;
993 			}
994 		}
995 
996 		RETURN_NEW_STR(newtext);
997 	} else {
998 		/* Multiple character line break or forced cut */
999 		if (linelength > 0) {
1000 			chk = (size_t)(ZSTR_LEN(text)/linelength + 1);
1001 			newtext = zend_string_safe_alloc(chk, breakchar_len, ZSTR_LEN(text), 0);
1002 			alloced = ZSTR_LEN(text) + chk * breakchar_len + 1;
1003 		} else {
1004 			chk = ZSTR_LEN(text);
1005 			alloced = ZSTR_LEN(text) * (breakchar_len + 1) + 1;
1006 			newtext = zend_string_safe_alloc(ZSTR_LEN(text), breakchar_len + 1, 0, 0);
1007 		}
1008 
1009 		/* now keep track of the actual new text length */
1010 		newtextlen = 0;
1011 
1012 		laststart = lastspace = 0;
1013 		for (current = 0; current < (zend_long)ZSTR_LEN(text); current++) {
1014 			if (chk == 0) {
1015 				alloced += (size_t) (((ZSTR_LEN(text) - current + 1)/linelength + 1) * breakchar_len) + 1;
1016 				newtext = zend_string_extend(newtext, alloced, 0);
1017 				chk = (size_t) ((ZSTR_LEN(text) - current)/linelength) + 1;
1018 			}
1019 			/* when we hit an existing break, copy to new buffer, and
1020 			 * fix up laststart and lastspace */
1021 			if (ZSTR_VAL(text)[current] == breakchar[0]
1022 				&& current + breakchar_len < ZSTR_LEN(text)
1023 				&& !strncmp(ZSTR_VAL(text) + current, breakchar, breakchar_len)) {
1024 				memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart + breakchar_len);
1025 				newtextlen += current - laststart + breakchar_len;
1026 				current += breakchar_len - 1;
1027 				laststart = lastspace = current + 1;
1028 				chk--;
1029 			}
1030 			/* if it is a space, check if it is at the line boundary,
1031 			 * copy and insert a break, or just keep track of it */
1032 			else if (ZSTR_VAL(text)[current] == ' ') {
1033 				if (current - laststart >= linelength) {
1034 					memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart);
1035 					newtextlen += current - laststart;
1036 					memcpy(ZSTR_VAL(newtext) + newtextlen, breakchar, breakchar_len);
1037 					newtextlen += breakchar_len;
1038 					laststart = current + 1;
1039 					chk--;
1040 				}
1041 				lastspace = current;
1042 			}
1043 			/* if we are cutting, and we've accumulated enough
1044 			 * characters, and we haven't see a space for this line,
1045 			 * copy and insert a break. */
1046 			else if (current - laststart >= linelength
1047 					&& docut && laststart >= lastspace) {
1048 				memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart);
1049 				newtextlen += current - laststart;
1050 				memcpy(ZSTR_VAL(newtext) + newtextlen, breakchar, breakchar_len);
1051 				newtextlen += breakchar_len;
1052 				laststart = lastspace = current;
1053 				chk--;
1054 			}
1055 			/* if the current word puts us over the linelength, copy
1056 			 * back up until the last space, insert a break, and move
1057 			 * up the laststart */
1058 			else if (current - laststart >= linelength
1059 					&& laststart < lastspace) {
1060 				memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, lastspace - laststart);
1061 				newtextlen += lastspace - laststart;
1062 				memcpy(ZSTR_VAL(newtext) + newtextlen, breakchar, breakchar_len);
1063 				newtextlen += breakchar_len;
1064 				laststart = lastspace = lastspace + 1;
1065 				chk--;
1066 			}
1067 		}
1068 
1069 		/* copy over any stragglers */
1070 		if (laststart != current) {
1071 			memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart);
1072 			newtextlen += current - laststart;
1073 		}
1074 
1075 		ZSTR_VAL(newtext)[newtextlen] = '\0';
1076 		/* free unused memory */
1077 		newtext = zend_string_truncate(newtext, newtextlen, 0);
1078 
1079 		RETURN_NEW_STR(newtext);
1080 	}
1081 }
1082 /* }}} */
1083 
1084 /* {{{ php_explode
1085  */
php_explode(const zend_string * delim,zend_string * str,zval * return_value,zend_long limit)1086 PHPAPI void php_explode(const zend_string *delim, zend_string *str, zval *return_value, zend_long limit)
1087 {
1088 	char *p1 = ZSTR_VAL(str);
1089 	char *endp = ZSTR_VAL(str) + ZSTR_LEN(str);
1090 	char *p2 = (char *) php_memnstr(ZSTR_VAL(str), ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
1091 	zval  tmp;
1092 
1093 	if (p2 == NULL) {
1094 		ZVAL_STR_COPY(&tmp, str);
1095 		zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
1096 	} else {
1097 		do {
1098 			size_t l = p2 - p1;
1099 
1100 			if (l == 0) {
1101 				ZVAL_EMPTY_STRING(&tmp);
1102 			} else if (l == 1) {
1103 				ZVAL_INTERNED_STR(&tmp, ZSTR_CHAR((zend_uchar)(*p1)));
1104 			} else {
1105 				ZVAL_STRINGL(&tmp, p1, p2 - p1);
1106 			}
1107 			zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
1108 			p1 = p2 + ZSTR_LEN(delim);
1109 			p2 = (char *) php_memnstr(p1, ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
1110 		} while (p2 != NULL && --limit > 1);
1111 
1112 		if (p1 <= endp) {
1113 			ZVAL_STRINGL(&tmp, p1, endp - p1);
1114 			zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
1115 		}
1116 	}
1117 }
1118 /* }}} */
1119 
1120 /* {{{ php_explode_negative_limit
1121  */
php_explode_negative_limit(const zend_string * delim,zend_string * str,zval * return_value,zend_long limit)1122 PHPAPI void php_explode_negative_limit(const zend_string *delim, zend_string *str, zval *return_value, zend_long limit)
1123 {
1124 #define EXPLODE_ALLOC_STEP 64
1125 	char *p1 = ZSTR_VAL(str);
1126 	char *endp = ZSTR_VAL(str) + ZSTR_LEN(str);
1127 	char *p2 = (char *) php_memnstr(ZSTR_VAL(str), ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
1128 	zval  tmp;
1129 
1130 	if (p2 == NULL) {
1131 		/*
1132 		do nothing since limit <= -1, thus if only one chunk - 1 + (limit) <= 0
1133 		by doing nothing we return empty array
1134 		*/
1135 	} else {
1136 		size_t allocated = EXPLODE_ALLOC_STEP, found = 0;
1137 		zend_long i, to_return;
1138 		char **positions = emalloc(allocated * sizeof(char *));
1139 
1140 		positions[found++] = p1;
1141 		do {
1142 			if (found >= allocated) {
1143 				allocated = found + EXPLODE_ALLOC_STEP;/* make sure we have enough memory */
1144 				positions = erealloc(positions, allocated*sizeof(char *));
1145 			}
1146 			positions[found++] = p1 = p2 + ZSTR_LEN(delim);
1147 			p2 = (char *) php_memnstr(p1, ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
1148 		} while (p2 != NULL);
1149 
1150 		to_return = limit + found;
1151 		/* limit is at least -1 therefore no need of bounds checking : i will be always less than found */
1152 		for (i = 0; i < to_return; i++) { /* this checks also for to_return > 0 */
1153 			ZVAL_STRINGL(&tmp, positions[i], (positions[i+1] - ZSTR_LEN(delim)) - positions[i]);
1154 			zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
1155 		}
1156 		efree(positions);
1157 	}
1158 #undef EXPLODE_ALLOC_STEP
1159 }
1160 /* }}} */
1161 
1162 /* {{{ proto array explode(string separator, string str [, int limit])
1163    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)1164 PHP_FUNCTION(explode)
1165 {
1166 	zend_string *str, *delim;
1167 	zend_long limit = ZEND_LONG_MAX; /* No limit */
1168 	zval tmp;
1169 
1170 	ZEND_PARSE_PARAMETERS_START(2, 3)
1171 		Z_PARAM_STR(delim)
1172 		Z_PARAM_STR(str)
1173 		Z_PARAM_OPTIONAL
1174 		Z_PARAM_LONG(limit)
1175 	ZEND_PARSE_PARAMETERS_END();
1176 
1177 	if (ZSTR_LEN(delim) == 0) {
1178 		php_error_docref(NULL, E_WARNING, "Empty delimiter");
1179 		RETURN_FALSE;
1180 	}
1181 
1182 	array_init(return_value);
1183 
1184 	if (ZSTR_LEN(str) == 0) {
1185 	  	if (limit >= 0) {
1186 			ZVAL_EMPTY_STRING(&tmp);
1187 			zend_hash_index_add_new(Z_ARRVAL_P(return_value), 0, &tmp);
1188 		}
1189 		return;
1190 	}
1191 
1192 	if (limit > 1) {
1193 		php_explode(delim, str, return_value, limit);
1194 	} else if (limit < 0) {
1195 		php_explode_negative_limit(delim, str, return_value, limit);
1196 	} else {
1197 		ZVAL_STR_COPY(&tmp, str);
1198 		zend_hash_index_add_new(Z_ARRVAL_P(return_value), 0, &tmp);
1199 	}
1200 }
1201 /* }}} */
1202 
1203 /* {{{ proto string join(array src, string glue)
1204    An alias for implode */
1205 /* }}} */
1206 
1207 /* {{{ php_implode
1208  */
php_implode(const zend_string * glue,zval * pieces,zval * return_value)1209 PHPAPI void php_implode(const zend_string *glue, zval *pieces, zval *return_value)
1210 {
1211 	zval         *tmp;
1212 	int           numelems;
1213 	zend_string  *str;
1214 	char         *cptr;
1215 	size_t        len = 0;
1216 	zend_string **strings, **strptr;
1217 
1218 	numelems = zend_hash_num_elements(Z_ARRVAL_P(pieces));
1219 
1220 	if (numelems == 0) {
1221 		RETURN_EMPTY_STRING();
1222 	} else if (numelems == 1) {
1223 		/* loop to search the first not undefined element... */
1224 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pieces), tmp) {
1225 			RETURN_STR(zval_get_string(tmp));
1226 		} ZEND_HASH_FOREACH_END();
1227 	}
1228 
1229 	strings = emalloc((sizeof(zend_long) + sizeof(zend_string *)) * numelems);
1230 	strptr = strings - 1;
1231 
1232 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pieces), tmp) {
1233 		if (Z_TYPE_P(tmp) == IS_LONG) {
1234 			zend_long val = Z_LVAL_P(tmp);
1235 
1236 			*++strptr = NULL;
1237 			((zend_long *) (strings + numelems))[strptr - strings] = Z_LVAL_P(tmp);
1238 			if (val <= 0) {
1239 				len++;
1240 			}
1241 			while (val) {
1242 				val /= 10;
1243 				len++;
1244 			}
1245 		} else {
1246 			*++strptr = zval_get_string(tmp);
1247 			len += ZSTR_LEN(*strptr);
1248 		}
1249 	} ZEND_HASH_FOREACH_END();
1250 	/* numelems can not be 0, we checked above */
1251 	str = zend_string_safe_alloc(numelems - 1, ZSTR_LEN(glue), len, 0);
1252 	cptr = ZSTR_VAL(str) + ZSTR_LEN(str);
1253 	*cptr = 0;
1254 
1255 	do {
1256 		if (*strptr) {
1257 			cptr -= ZSTR_LEN(*strptr);
1258 			memcpy(cptr, ZSTR_VAL(*strptr), ZSTR_LEN(*strptr));
1259 			zend_string_release(*strptr);
1260 		} else {
1261 			char *oldPtr = cptr;
1262 			char oldVal = *cptr;
1263 			zend_long val = ((zend_long *) (strings + numelems))[strptr - strings];
1264 			cptr = zend_print_long_to_buf(cptr, val);
1265 			*oldPtr = oldVal;
1266 		}
1267 
1268 		cptr -= ZSTR_LEN(glue);
1269 		memcpy(cptr, ZSTR_VAL(glue), ZSTR_LEN(glue));
1270 	} while (--strptr > strings);
1271 
1272 	if (*strptr) {
1273 		memcpy(ZSTR_VAL(str), ZSTR_VAL(*strptr), ZSTR_LEN(*strptr));
1274 		zend_string_release(*strptr);
1275 	} else {
1276 		char *oldPtr = cptr;
1277 		char oldVal = *cptr;
1278 		zend_print_long_to_buf(cptr, ((zend_long *) (strings + numelems))[strptr - strings]);
1279 		*oldPtr = oldVal;
1280 	}
1281 
1282 	efree(strings);
1283 	RETURN_NEW_STR(str);
1284 }
1285 /* }}} */
1286 
1287 /* {{{ proto string implode([string glue,] array pieces)
1288    Joins array elements placing glue string between items and return one string */
PHP_FUNCTION(implode)1289 PHP_FUNCTION(implode)
1290 {
1291 	zval *arg1, *arg2 = NULL, *pieces;
1292 	zend_string *glue, *tmp_glue;
1293 
1294 	ZEND_PARSE_PARAMETERS_START(1, 2)
1295 		Z_PARAM_ZVAL(arg1)
1296 		Z_PARAM_OPTIONAL
1297 		Z_PARAM_ZVAL(arg2)
1298 	ZEND_PARSE_PARAMETERS_END();
1299 
1300 	if (arg2 == NULL) {
1301 		if (Z_TYPE_P(arg1) != IS_ARRAY) {
1302 			php_error_docref(NULL, E_WARNING, "Argument must be an array");
1303 			return;
1304 		}
1305 
1306 		glue = ZSTR_EMPTY_ALLOC();
1307 		tmp_glue = NULL;
1308 		pieces = arg1;
1309 	} else {
1310 		if (Z_TYPE_P(arg1) == IS_ARRAY) {
1311 			glue = zval_get_tmp_string(arg2, &tmp_glue);
1312 			pieces = arg1;
1313 		} else if (Z_TYPE_P(arg2) == IS_ARRAY) {
1314 			glue = zval_get_tmp_string(arg1, &tmp_glue);
1315 			pieces = arg2;
1316 		} else {
1317 			php_error_docref(NULL, E_WARNING, "Invalid arguments passed");
1318 			return;
1319 		}
1320 	}
1321 
1322 	php_implode(glue, pieces, return_value);
1323 	zend_tmp_string_release(tmp_glue);
1324 }
1325 /* }}} */
1326 
1327 #define STRTOK_TABLE(p) BG(strtok_table)[(unsigned char) *p]
1328 
1329 /* {{{ proto string strtok([string str,] string token)
1330    Tokenize a string */
PHP_FUNCTION(strtok)1331 PHP_FUNCTION(strtok)
1332 {
1333 	zend_string *str, *tok = NULL;
1334 	char *token;
1335 	char *token_end;
1336 	char *p;
1337 	char *pe;
1338 	size_t skipped = 0;
1339 
1340 	ZEND_PARSE_PARAMETERS_START(1, 2)
1341 		Z_PARAM_STR(str)
1342 		Z_PARAM_OPTIONAL
1343 		Z_PARAM_STR(tok)
1344 	ZEND_PARSE_PARAMETERS_END();
1345 
1346 	if (ZEND_NUM_ARGS() == 1) {
1347 		tok = str;
1348 	} else {
1349 		zval_ptr_dtor(&BG(strtok_zval));
1350 		ZVAL_STRINGL(&BG(strtok_zval), ZSTR_VAL(str), ZSTR_LEN(str));
1351 		BG(strtok_last) = BG(strtok_string) = Z_STRVAL(BG(strtok_zval));
1352 		BG(strtok_len) = ZSTR_LEN(str);
1353 	}
1354 
1355 	p = BG(strtok_last); /* Where we start to search */
1356 	pe = BG(strtok_string) + BG(strtok_len);
1357 
1358 	if (!p || p >= pe) {
1359 		RETURN_FALSE;
1360 	}
1361 
1362 	token = ZSTR_VAL(tok);
1363 	token_end = token + ZSTR_LEN(tok);
1364 
1365 	while (token < token_end) {
1366 		STRTOK_TABLE(token++) = 1;
1367 	}
1368 
1369 	/* Skip leading delimiters */
1370 	while (STRTOK_TABLE(p)) {
1371 		if (++p >= pe) {
1372 			/* no other chars left */
1373 			BG(strtok_last) = NULL;
1374 			RETVAL_FALSE;
1375 			goto restore;
1376 		}
1377 		skipped++;
1378 	}
1379 
1380 	/* We know at this place that *p is no delimiter, so skip it */
1381 	while (++p < pe) {
1382 		if (STRTOK_TABLE(p)) {
1383 			goto return_token;
1384 		}
1385 	}
1386 
1387 	if (p - BG(strtok_last)) {
1388 return_token:
1389 		RETVAL_STRINGL(BG(strtok_last) + skipped, (p - BG(strtok_last)) - skipped);
1390 		BG(strtok_last) = p + 1;
1391 	} else {
1392 		RETVAL_FALSE;
1393 		BG(strtok_last) = NULL;
1394 	}
1395 
1396 	/* Restore table -- usually faster then memset'ing the table on every invocation */
1397 restore:
1398 	token = ZSTR_VAL(tok);
1399 
1400 	while (token < token_end) {
1401 		STRTOK_TABLE(token++) = 0;
1402 	}
1403 }
1404 /* }}} */
1405 
1406 /* {{{ php_strtoupper
1407  */
php_strtoupper(char * s,size_t len)1408 PHPAPI char *php_strtoupper(char *s, size_t len)
1409 {
1410 	unsigned char *c, *e;
1411 
1412 	c = (unsigned char *)s;
1413 	e = (unsigned char *)c+len;
1414 
1415 	while (c < e) {
1416 		*c = toupper(*c);
1417 		c++;
1418 	}
1419 	return s;
1420 }
1421 /* }}} */
1422 
1423 /* {{{ php_string_toupper
1424  */
php_string_toupper(zend_string * s)1425 PHPAPI zend_string *php_string_toupper(zend_string *s)
1426 {
1427 	unsigned char *c, *e;
1428 
1429 	c = (unsigned char *)ZSTR_VAL(s);
1430 	e = c + ZSTR_LEN(s);
1431 
1432 	while (c < e) {
1433 		if (islower(*c)) {
1434 			register unsigned char *r;
1435 			zend_string *res = zend_string_alloc(ZSTR_LEN(s), 0);
1436 
1437 			if (c != (unsigned char*)ZSTR_VAL(s)) {
1438 				memcpy(ZSTR_VAL(res), ZSTR_VAL(s), c - (unsigned char*)ZSTR_VAL(s));
1439 			}
1440 			r = c + (ZSTR_VAL(res) - ZSTR_VAL(s));
1441 			while (c < e) {
1442 				*r = toupper(*c);
1443 				r++;
1444 				c++;
1445 			}
1446 			*r = '\0';
1447 			return res;
1448 		}
1449 		c++;
1450 	}
1451 	return zend_string_copy(s);
1452 }
1453 /* }}} */
1454 
1455 /* {{{ proto string strtoupper(string str)
1456    Makes a string uppercase */
PHP_FUNCTION(strtoupper)1457 PHP_FUNCTION(strtoupper)
1458 {
1459 	zend_string *arg;
1460 
1461 	ZEND_PARSE_PARAMETERS_START(1, 1)
1462 		Z_PARAM_STR(arg)
1463 	ZEND_PARSE_PARAMETERS_END();
1464 
1465 	RETURN_STR(php_string_toupper(arg));
1466 }
1467 /* }}} */
1468 
1469 /* {{{ php_strtolower
1470  */
php_strtolower(char * s,size_t len)1471 PHPAPI char *php_strtolower(char *s, size_t len)
1472 {
1473 	unsigned char *c, *e;
1474 
1475 	c = (unsigned char *)s;
1476 	e = c+len;
1477 
1478 	while (c < e) {
1479 		*c = tolower(*c);
1480 		c++;
1481 	}
1482 	return s;
1483 }
1484 /* }}} */
1485 
1486 /* {{{ php_string_tolower
1487  */
php_string_tolower(zend_string * s)1488 PHPAPI zend_string *php_string_tolower(zend_string *s)
1489 {
1490 	unsigned char *c, *e;
1491 
1492 	c = (unsigned char *)ZSTR_VAL(s);
1493 	e = c + ZSTR_LEN(s);
1494 
1495 	while (c < e) {
1496 		if (isupper(*c)) {
1497 			register unsigned char *r;
1498 			zend_string *res = zend_string_alloc(ZSTR_LEN(s), 0);
1499 
1500 			if (c != (unsigned char*)ZSTR_VAL(s)) {
1501 				memcpy(ZSTR_VAL(res), ZSTR_VAL(s), c - (unsigned char*)ZSTR_VAL(s));
1502 			}
1503 			r = c + (ZSTR_VAL(res) - ZSTR_VAL(s));
1504 			while (c < e) {
1505 				*r = tolower(*c);
1506 				r++;
1507 				c++;
1508 			}
1509 			*r = '\0';
1510 			return res;
1511 		}
1512 		c++;
1513 	}
1514 	return zend_string_copy(s);
1515 }
1516 /* }}} */
1517 
1518 /* {{{ proto string strtolower(string str)
1519    Makes a string lowercase */
PHP_FUNCTION(strtolower)1520 PHP_FUNCTION(strtolower)
1521 {
1522 	zend_string *str;
1523 
1524 	ZEND_PARSE_PARAMETERS_START(1, 1)
1525 		Z_PARAM_STR(str)
1526 	ZEND_PARSE_PARAMETERS_END();
1527 
1528 	RETURN_STR(php_string_tolower(str));
1529 }
1530 /* }}} */
1531 
1532 /* {{{ php_basename
1533  */
php_basename(const char * s,size_t len,char * suffix,size_t sufflen)1534 PHPAPI zend_string *php_basename(const char *s, size_t len, char *suffix, size_t sufflen)
1535 {
1536 	char *c, *comp, *cend;
1537 	size_t inc_len, cnt;
1538 	int state;
1539 	zend_string *ret;
1540 
1541 	c = comp = cend = (char*)s;
1542 	cnt = len;
1543 	state = 0;
1544 	while (cnt > 0) {
1545 		inc_len = (*c == '\0' ? 1 : php_mblen(c, cnt));
1546 
1547 		switch (inc_len) {
1548 			case -2:
1549 			case -1:
1550 				inc_len = 1;
1551 				php_mb_reset();
1552 				break;
1553 			case 0:
1554 				goto quit_loop;
1555 			case 1:
1556 #if defined(PHP_WIN32)
1557 				if (*c == '/' || *c == '\\') {
1558 #else
1559 				if (*c == '/') {
1560 #endif
1561 					if (state == 1) {
1562 						state = 0;
1563 						cend = c;
1564 					}
1565 #if defined(PHP_WIN32)
1566 				/* Catch relative paths in c:file.txt style. They're not to confuse
1567 				   with the NTFS streams. This part ensures also, that no drive
1568 				   letter traversing happens. */
1569 				} else if ((*c == ':' && (c - comp == 1))) {
1570 					if (state == 0) {
1571 						comp = c;
1572 						state = 1;
1573 					} else {
1574 						cend = c;
1575 						state = 0;
1576 					}
1577 #endif
1578 				} else {
1579 					if (state == 0) {
1580 						comp = c;
1581 						state = 1;
1582 					}
1583 				}
1584 				break;
1585 			default:
1586 				if (state == 0) {
1587 					comp = c;
1588 					state = 1;
1589 				}
1590 				break;
1591 		}
1592 		c += inc_len;
1593 		cnt -= inc_len;
1594 	}
1595 
1596 quit_loop:
1597 	if (state == 1) {
1598 		cend = c;
1599 	}
1600 	if (suffix != NULL && sufflen < (size_t)(cend - comp) &&
1601 			memcmp(cend - sufflen, suffix, sufflen) == 0) {
1602 		cend -= sufflen;
1603 	}
1604 
1605 	len = cend - comp;
1606 
1607 	ret = zend_string_init(comp, len, 0);
1608 	return ret;
1609 }
1610 /* }}} */
1611 
1612 /* {{{ proto string basename(string path [, string suffix])
1613    Returns the filename component of the path */
1614 PHP_FUNCTION(basename)
1615 {
1616 	char *string, *suffix = NULL;
1617 	size_t   string_len, suffix_len = 0;
1618 
1619 	ZEND_PARSE_PARAMETERS_START(1, 2)
1620 		Z_PARAM_STRING(string, string_len)
1621 		Z_PARAM_OPTIONAL
1622 		Z_PARAM_STRING(suffix, suffix_len)
1623 	ZEND_PARSE_PARAMETERS_END();
1624 
1625 	RETURN_STR(php_basename(string, string_len, suffix, suffix_len));
1626 }
1627 /* }}} */
1628 
1629 /* {{{ php_dirname
1630    Returns directory name component of path */
1631 PHPAPI size_t php_dirname(char *path, size_t len)
1632 {
1633 	return zend_dirname(path, len);
1634 }
1635 /* }}} */
1636 
1637 /* {{{ proto string dirname(string path[, int levels])
1638    Returns the directory name component of the path */
1639 PHP_FUNCTION(dirname)
1640 {
1641 	char *str;
1642 	size_t str_len;
1643 	zend_string *ret;
1644 	zend_long levels = 1;
1645 
1646 	ZEND_PARSE_PARAMETERS_START(1, 2)
1647 		Z_PARAM_STRING(str, str_len)
1648 		Z_PARAM_OPTIONAL
1649 		Z_PARAM_LONG(levels)
1650 	ZEND_PARSE_PARAMETERS_END();
1651 
1652 	ret = zend_string_init(str, str_len, 0);
1653 
1654 	if (levels == 1) {
1655 		/* Defaut case */
1656 #ifdef PHP_WIN32
1657 		ZSTR_LEN(ret) = php_win32_ioutil_dirname(ZSTR_VAL(ret), str_len);
1658 #else
1659 		ZSTR_LEN(ret) = zend_dirname(ZSTR_VAL(ret), str_len);
1660 #endif
1661 	} else if (levels < 1) {
1662 		php_error_docref(NULL, E_WARNING, "Invalid argument, levels must be >= 1");
1663 		zend_string_free(ret);
1664 		return;
1665 	} else {
1666 		/* Some levels up */
1667 		do {
1668 #ifdef PHP_WIN32
1669 			ZSTR_LEN(ret) = php_win32_ioutil_dirname(ZSTR_VAL(ret), str_len = ZSTR_LEN(ret));
1670 #else
1671 			ZSTR_LEN(ret) = zend_dirname(ZSTR_VAL(ret), str_len = ZSTR_LEN(ret));
1672 #endif
1673 		} while (ZSTR_LEN(ret) < str_len && --levels);
1674 	}
1675 
1676 	RETURN_NEW_STR(ret);
1677 }
1678 /* }}} */
1679 
1680 /* {{{ proto array pathinfo(string path[, int options])
1681    Returns information about a certain string */
1682 PHP_FUNCTION(pathinfo)
1683 {
1684 	zval tmp;
1685 	char *path, *dirname;
1686 	size_t path_len;
1687 	int have_basename;
1688 	zend_long opt = PHP_PATHINFO_ALL;
1689 	zend_string *ret = NULL;
1690 
1691 	ZEND_PARSE_PARAMETERS_START(1, 2)
1692 		Z_PARAM_STRING(path, path_len)
1693 		Z_PARAM_OPTIONAL
1694 		Z_PARAM_LONG(opt)
1695 	ZEND_PARSE_PARAMETERS_END();
1696 
1697 	have_basename = ((opt & PHP_PATHINFO_BASENAME) == PHP_PATHINFO_BASENAME);
1698 
1699 	array_init(&tmp);
1700 
1701 	if ((opt & PHP_PATHINFO_DIRNAME) == PHP_PATHINFO_DIRNAME) {
1702 		dirname = estrndup(path, path_len);
1703 		php_dirname(dirname, path_len);
1704 		if (*dirname) {
1705 			add_assoc_string(&tmp, "dirname", dirname);
1706 		}
1707 		efree(dirname);
1708 	}
1709 
1710 	if (have_basename) {
1711 		ret = php_basename(path, path_len, NULL, 0);
1712 		add_assoc_str(&tmp, "basename", zend_string_copy(ret));
1713 	}
1714 
1715 	if ((opt & PHP_PATHINFO_EXTENSION) == PHP_PATHINFO_EXTENSION) {
1716 		const char *p;
1717 		ptrdiff_t idx;
1718 
1719 		if (!have_basename) {
1720 			ret = php_basename(path, path_len, NULL, 0);
1721 		}
1722 
1723 		p = zend_memrchr(ZSTR_VAL(ret), '.', ZSTR_LEN(ret));
1724 
1725 		if (p) {
1726 			idx = p - ZSTR_VAL(ret);
1727 			add_assoc_stringl(&tmp, "extension", ZSTR_VAL(ret) + idx + 1, ZSTR_LEN(ret) - idx - 1);
1728 		}
1729 	}
1730 
1731 	if ((opt & PHP_PATHINFO_FILENAME) == PHP_PATHINFO_FILENAME) {
1732 		const char *p;
1733 		ptrdiff_t idx;
1734 
1735 		/* Have we already looked up the basename? */
1736 		if (!have_basename && !ret) {
1737 			ret = php_basename(path, path_len, NULL, 0);
1738 		}
1739 
1740 		p = zend_memrchr(ZSTR_VAL(ret), '.', ZSTR_LEN(ret));
1741 
1742 		idx = p ? (p - ZSTR_VAL(ret)) : (ptrdiff_t)ZSTR_LEN(ret);
1743 		add_assoc_stringl(&tmp, "filename", ZSTR_VAL(ret), idx);
1744 	}
1745 
1746 	if (ret) {
1747 		zend_string_release(ret);
1748 	}
1749 
1750 	if (opt == PHP_PATHINFO_ALL) {
1751 		ZVAL_COPY_VALUE(return_value, &tmp);
1752 	} else {
1753 		zval *element;
1754 		if ((element = zend_hash_get_current_data(Z_ARRVAL(tmp))) != NULL) {
1755 			ZVAL_DEREF(element);
1756 			ZVAL_COPY(return_value, element);
1757 		} else {
1758 			ZVAL_EMPTY_STRING(return_value);
1759 		}
1760 		zval_ptr_dtor(&tmp);
1761 	}
1762 }
1763 /* }}} */
1764 
1765 /* {{{ php_stristr
1766    case insensitve strstr */
1767 PHPAPI char *php_stristr(char *s, char *t, size_t s_len, size_t t_len)
1768 {
1769 	php_strtolower(s, s_len);
1770 	php_strtolower(t, t_len);
1771 	return (char*)php_memnstr(s, t, t_len, s + s_len);
1772 }
1773 /* }}} */
1774 
1775 /* {{{ php_strspn
1776  */
1777 PHPAPI size_t php_strspn(char *s1, char *s2, char *s1_end, char *s2_end)
1778 {
1779 	register const char *p = s1, *spanp;
1780 	register char c = *p;
1781 
1782 cont:
1783 	for (spanp = s2; p != s1_end && spanp != s2_end;) {
1784 		if (*spanp++ == c) {
1785 			c = *(++p);
1786 			goto cont;
1787 		}
1788 	}
1789 	return (p - s1);
1790 }
1791 /* }}} */
1792 
1793 /* {{{ php_strcspn
1794  */
1795 PHPAPI size_t php_strcspn(char *s1, char *s2, char *s1_end, char *s2_end)
1796 {
1797 	register const char *p, *spanp;
1798 	register char c = *s1;
1799 
1800 	for (p = s1;;) {
1801 		spanp = s2;
1802 		do {
1803 			if (*spanp == c || p == s1_end) {
1804 				return p - s1;
1805 			}
1806 		} while (spanp++ < (s2_end - 1));
1807 		c = *++p;
1808 	}
1809 	/* NOTREACHED */
1810 }
1811 /* }}} */
1812 
1813 /* {{{ php_needle_char
1814  */
1815 static int php_needle_char(zval *needle, char *target)
1816 {
1817 	switch (Z_TYPE_P(needle)) {
1818 		case IS_LONG:
1819 			*target = (char)Z_LVAL_P(needle);
1820 			return SUCCESS;
1821 		case IS_NULL:
1822 		case IS_FALSE:
1823 			*target = '\0';
1824 			return SUCCESS;
1825 		case IS_TRUE:
1826 			*target = '\1';
1827 			return SUCCESS;
1828 		case IS_DOUBLE:
1829 			*target = (char)(int)Z_DVAL_P(needle);
1830 			return SUCCESS;
1831 		case IS_OBJECT:
1832 			*target = (char) zval_get_long(needle);
1833 			return SUCCESS;
1834 		default:
1835 			php_error_docref(NULL, E_WARNING, "needle is not a string or an integer");
1836 			return FAILURE;
1837 	}
1838 }
1839 /* }}} */
1840 
1841 /* {{{ proto string stristr(string haystack, string needle[, bool part])
1842    Finds first occurrence of a string within another, case insensitive */
1843 PHP_FUNCTION(stristr)
1844 {
1845 	zval *needle;
1846 	zend_string *haystack;
1847 	char *found = NULL;
1848 	size_t  found_offset;
1849 	char *haystack_dup;
1850 	char needle_char[2];
1851 	zend_bool part = 0;
1852 
1853 	ZEND_PARSE_PARAMETERS_START(2, 3)
1854 		Z_PARAM_STR(haystack)
1855 		Z_PARAM_ZVAL(needle)
1856 		Z_PARAM_OPTIONAL
1857 		Z_PARAM_BOOL(part)
1858 	ZEND_PARSE_PARAMETERS_END();
1859 
1860 	haystack_dup = estrndup(ZSTR_VAL(haystack), ZSTR_LEN(haystack));
1861 
1862 	if (Z_TYPE_P(needle) == IS_STRING) {
1863 		char *orig_needle;
1864 		if (!Z_STRLEN_P(needle)) {
1865 			php_error_docref(NULL, E_WARNING, "Empty needle");
1866 			efree(haystack_dup);
1867 			RETURN_FALSE;
1868 		}
1869 		orig_needle = estrndup(Z_STRVAL_P(needle), Z_STRLEN_P(needle));
1870 		found = php_stristr(haystack_dup, orig_needle, ZSTR_LEN(haystack), Z_STRLEN_P(needle));
1871 		efree(orig_needle);
1872 	} else {
1873 		if (php_needle_char(needle, needle_char) != SUCCESS) {
1874 			efree(haystack_dup);
1875 			RETURN_FALSE;
1876 		}
1877 		needle_char[1] = 0;
1878 
1879 		found = php_stristr(haystack_dup, needle_char, ZSTR_LEN(haystack), 1);
1880 	}
1881 
1882 	if (found) {
1883 		found_offset = found - haystack_dup;
1884 		if (part) {
1885 			RETVAL_STRINGL(ZSTR_VAL(haystack), found_offset);
1886 		} else {
1887 			RETVAL_STRINGL(ZSTR_VAL(haystack) + found_offset, ZSTR_LEN(haystack) - found_offset);
1888 		}
1889 	} else {
1890 		RETVAL_FALSE;
1891 	}
1892 
1893 	efree(haystack_dup);
1894 }
1895 /* }}} */
1896 
1897 /* {{{ proto string strstr(string haystack, string needle[, bool part])
1898    Finds first occurrence of a string within another */
1899 PHP_FUNCTION(strstr)
1900 {
1901 	zval *needle;
1902 	zend_string *haystack;
1903 	char *found = NULL;
1904 	char needle_char[2];
1905 	zend_long found_offset;
1906 	zend_bool part = 0;
1907 
1908 	ZEND_PARSE_PARAMETERS_START(2, 3)
1909 		Z_PARAM_STR(haystack)
1910 		Z_PARAM_ZVAL(needle)
1911 		Z_PARAM_OPTIONAL
1912 		Z_PARAM_BOOL(part)
1913 	ZEND_PARSE_PARAMETERS_END();
1914 
1915 	if (Z_TYPE_P(needle) == IS_STRING) {
1916 		if (!Z_STRLEN_P(needle)) {
1917 			php_error_docref(NULL, E_WARNING, "Empty needle");
1918 			RETURN_FALSE;
1919 		}
1920 
1921 		found = (char*)php_memnstr(ZSTR_VAL(haystack), Z_STRVAL_P(needle), Z_STRLEN_P(needle), ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1922 	} else {
1923 		if (php_needle_char(needle, needle_char) != SUCCESS) {
1924 			RETURN_FALSE;
1925 		}
1926 		needle_char[1] = 0;
1927 
1928 		found = (char*)php_memnstr(ZSTR_VAL(haystack), needle_char, 1, ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1929 	}
1930 
1931 	if (found) {
1932 		found_offset = found - ZSTR_VAL(haystack);
1933 		if (part) {
1934 			RETURN_STRINGL(ZSTR_VAL(haystack), found_offset);
1935 		} else {
1936 			RETURN_STRINGL(found, ZSTR_LEN(haystack) - found_offset);
1937 		}
1938 	}
1939 	RETURN_FALSE;
1940 }
1941 /* }}} */
1942 
1943 /* {{{ proto string strchr(string haystack, string needle)
1944    An alias for strstr */
1945 /* }}} */
1946 
1947 /* {{{ proto int strpos(string haystack, string needle [, int offset])
1948    Finds position of first occurrence of a string within another */
1949 PHP_FUNCTION(strpos)
1950 {
1951 	zval *needle;
1952 	zend_string *haystack;
1953 	char *found = NULL;
1954 	char  needle_char[2];
1955 	zend_long  offset = 0;
1956 
1957 	ZEND_PARSE_PARAMETERS_START(2, 3)
1958 		Z_PARAM_STR(haystack)
1959 		Z_PARAM_ZVAL(needle)
1960 		Z_PARAM_OPTIONAL
1961 		Z_PARAM_LONG(offset)
1962 	ZEND_PARSE_PARAMETERS_END();
1963 
1964 	if (offset < 0) {
1965 		offset += (zend_long)ZSTR_LEN(haystack);
1966 	}
1967 	if (offset < 0 || (size_t)offset > ZSTR_LEN(haystack)) {
1968 		php_error_docref(NULL, E_WARNING, "Offset not contained in string");
1969 		RETURN_FALSE;
1970 	}
1971 
1972 	if (Z_TYPE_P(needle) == IS_STRING) {
1973 		if (!Z_STRLEN_P(needle)) {
1974 			php_error_docref(NULL, E_WARNING, "Empty needle");
1975 			RETURN_FALSE;
1976 		}
1977 
1978 		found = (char*)php_memnstr(ZSTR_VAL(haystack) + offset,
1979 			                Z_STRVAL_P(needle),
1980 			                Z_STRLEN_P(needle),
1981 			                ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1982 	} else {
1983 		if (php_needle_char(needle, needle_char) != SUCCESS) {
1984 			RETURN_FALSE;
1985 		}
1986 		needle_char[1] = 0;
1987 
1988 		found = (char*)php_memnstr(ZSTR_VAL(haystack) + offset,
1989 							needle_char,
1990 							1,
1991 		                    ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1992 	}
1993 
1994 	if (found) {
1995 		RETURN_LONG(found - ZSTR_VAL(haystack));
1996 	} else {
1997 		RETURN_FALSE;
1998 	}
1999 }
2000 /* }}} */
2001 
2002 /* {{{ proto int stripos(string haystack, string needle [, int offset])
2003    Finds position of first occurrence of a string within another, case insensitive */
2004 PHP_FUNCTION(stripos)
2005 {
2006 	char *found = NULL;
2007 	zend_string *haystack;
2008 	zend_long offset = 0;
2009 	char needle_char[2];
2010 	zval *needle;
2011 	zend_string *needle_dup = NULL, *haystack_dup;
2012 
2013 	ZEND_PARSE_PARAMETERS_START(2, 3)
2014 		Z_PARAM_STR(haystack)
2015 		Z_PARAM_ZVAL(needle)
2016 		Z_PARAM_OPTIONAL
2017 		Z_PARAM_LONG(offset)
2018 	ZEND_PARSE_PARAMETERS_END();
2019 
2020 	if (offset < 0) {
2021 		offset += (zend_long)ZSTR_LEN(haystack);
2022 	}
2023 	if (offset < 0 || (size_t)offset > ZSTR_LEN(haystack)) {
2024 		php_error_docref(NULL, E_WARNING, "Offset not contained in string");
2025 		RETURN_FALSE;
2026 	}
2027 
2028 	if (ZSTR_LEN(haystack) == 0) {
2029 		RETURN_FALSE;
2030 	}
2031 
2032 	if (Z_TYPE_P(needle) == IS_STRING) {
2033 		if (Z_STRLEN_P(needle) == 0 || Z_STRLEN_P(needle) > ZSTR_LEN(haystack)) {
2034 			RETURN_FALSE;
2035 		}
2036 
2037 		haystack_dup = php_string_tolower(haystack);
2038 		needle_dup = php_string_tolower(Z_STR_P(needle));
2039 		found = (char*)php_memnstr(ZSTR_VAL(haystack_dup) + offset,
2040 				ZSTR_VAL(needle_dup), ZSTR_LEN(needle_dup), ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack));
2041 	} else {
2042 		if (php_needle_char(needle, needle_char) != SUCCESS) {
2043 			RETURN_FALSE;
2044 		}
2045 		haystack_dup = php_string_tolower(haystack);
2046 		needle_char[0] = tolower(needle_char[0]);
2047 		needle_char[1] = '\0';
2048 		found = (char*)php_memnstr(ZSTR_VAL(haystack_dup) + offset,
2049 							needle_char,
2050 							sizeof(needle_char) - 1,
2051 							ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack));
2052 	}
2053 
2054 
2055 	if (found) {
2056 		RETVAL_LONG(found - ZSTR_VAL(haystack_dup));
2057 	} else {
2058 		RETVAL_FALSE;
2059 	}
2060 
2061 	zend_string_release(haystack_dup);
2062 	if (needle_dup) {
2063 		zend_string_release(needle_dup);
2064 	}
2065 }
2066 /* }}} */
2067 
2068 /* {{{ proto int strrpos(string haystack, string needle [, int offset])
2069    Finds position of last occurrence of a string within another string */
2070 PHP_FUNCTION(strrpos)
2071 {
2072 	zval *zneedle;
2073 	char *needle;
2074 	zend_string *haystack;
2075 	size_t needle_len;
2076 	zend_long offset = 0;
2077 	char *p, *e, ord_needle[2];
2078 	char *found;
2079 
2080 	ZEND_PARSE_PARAMETERS_START(2, 3)
2081 		Z_PARAM_STR(haystack)
2082 		Z_PARAM_ZVAL(zneedle)
2083 		Z_PARAM_OPTIONAL
2084 		Z_PARAM_LONG(offset)
2085 	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
2086 
2087 	if (Z_TYPE_P(zneedle) == IS_STRING) {
2088 		needle = Z_STRVAL_P(zneedle);
2089 		needle_len = Z_STRLEN_P(zneedle);
2090 	} else {
2091 		if (php_needle_char(zneedle, ord_needle) != SUCCESS) {
2092 			RETURN_FALSE;
2093 		}
2094 		ord_needle[1] = '\0';
2095 		needle = ord_needle;
2096 		needle_len = 1;
2097 	}
2098 
2099 	if ((ZSTR_LEN(haystack) == 0) || (needle_len == 0)) {
2100 		RETURN_FALSE;
2101 	}
2102 
2103 	if (offset >= 0) {
2104 		if ((size_t)offset > ZSTR_LEN(haystack)) {
2105 			php_error_docref(NULL, E_WARNING, "Offset is greater than the length of haystack string");
2106 			RETURN_FALSE;
2107 		}
2108 		p = ZSTR_VAL(haystack) + (size_t)offset;
2109 		e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
2110 	} else {
2111 		if (offset < -INT_MAX || (size_t)(-offset) > ZSTR_LEN(haystack)) {
2112 			php_error_docref(NULL, E_WARNING, "Offset is greater than the length of haystack string");
2113 			RETURN_FALSE;
2114 		}
2115 		p = ZSTR_VAL(haystack);
2116 		if ((size_t)-offset < needle_len) {
2117 			e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
2118 		} else {
2119 			e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack) + offset + needle_len;
2120 		}
2121 	}
2122 
2123 	if ((found = (char *)zend_memnrstr(p, needle, needle_len, e))) {
2124 		RETURN_LONG(found - ZSTR_VAL(haystack));
2125 	}
2126 
2127 	RETURN_FALSE;
2128 }
2129 /* }}} */
2130 
2131 /* {{{ proto int strripos(string haystack, string needle [, int offset])
2132    Finds position of last occurrence of a string within another string */
2133 PHP_FUNCTION(strripos)
2134 {
2135 	zval *zneedle;
2136 	zend_string *needle;
2137 	zend_string *haystack;
2138 	zend_long offset = 0;
2139 	char *p, *e;
2140 	char *found;
2141 	zend_string *needle_dup, *haystack_dup, *ord_needle = NULL;
2142 	ALLOCA_FLAG(use_heap);
2143 
2144 	ZEND_PARSE_PARAMETERS_START(2, 3)
2145 		Z_PARAM_STR(haystack)
2146 		Z_PARAM_ZVAL(zneedle)
2147 		Z_PARAM_OPTIONAL
2148 		Z_PARAM_LONG(offset)
2149 	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
2150 
2151 	ZSTR_ALLOCA_ALLOC(ord_needle, 1, use_heap);
2152 	if (Z_TYPE_P(zneedle) == IS_STRING) {
2153 		needle = Z_STR_P(zneedle);
2154 	} else {
2155 		if (php_needle_char(zneedle, ZSTR_VAL(ord_needle)) != SUCCESS) {
2156 			ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2157 			RETURN_FALSE;
2158 		}
2159 		ZSTR_VAL(ord_needle)[1] = '\0';
2160 		needle = ord_needle;
2161 	}
2162 
2163 	if ((ZSTR_LEN(haystack) == 0) || (ZSTR_LEN(needle) == 0)) {
2164 		ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2165 		RETURN_FALSE;
2166 	}
2167 
2168 	if (ZSTR_LEN(needle) == 1) {
2169 		/* Single character search can shortcut memcmps
2170 		   Can also avoid tolower emallocs */
2171 		if (offset >= 0) {
2172 			if ((size_t)offset > ZSTR_LEN(haystack)) {
2173 				ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2174 				php_error_docref(NULL, E_WARNING, "Offset is greater than the length of haystack string");
2175 				RETURN_FALSE;
2176 			}
2177 			p = ZSTR_VAL(haystack) + (size_t)offset;
2178 			e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack) - 1;
2179 		} else {
2180 			p = ZSTR_VAL(haystack);
2181 			if (offset < -INT_MAX || (size_t)(-offset) > ZSTR_LEN(haystack)) {
2182 				ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2183 				php_error_docref(NULL, E_WARNING, "Offset is greater than the length of haystack string");
2184 				RETURN_FALSE;
2185 			}
2186 			e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack) + (size_t)offset;
2187 		}
2188 		/* Borrow that ord_needle buffer to avoid repeatedly tolower()ing needle */
2189 		*ZSTR_VAL(ord_needle) = tolower(*ZSTR_VAL(needle));
2190 		while (e >= p) {
2191 			if (tolower(*e) == *ZSTR_VAL(ord_needle)) {
2192 				ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2193 				RETURN_LONG(e - p + (offset > 0 ? offset : 0));
2194 			}
2195 			e--;
2196 		}
2197 		ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2198 		RETURN_FALSE;
2199 	}
2200 
2201 	haystack_dup = php_string_tolower(haystack);
2202 	if (offset >= 0) {
2203 		if ((size_t)offset > ZSTR_LEN(haystack)) {
2204 			zend_string_release(haystack_dup);
2205 			ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2206 			php_error_docref(NULL, E_WARNING, "Offset is greater than the length of haystack string");
2207 			RETURN_FALSE;
2208 		}
2209 		p = ZSTR_VAL(haystack_dup) + offset;
2210 		e = ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack);
2211 	} else {
2212 		if (offset < -INT_MAX || (size_t)(-offset) > ZSTR_LEN(haystack)) {
2213 			zend_string_release(haystack_dup);
2214 			ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2215 			php_error_docref(NULL, E_WARNING, "Offset is greater than the length of haystack string");
2216 			RETURN_FALSE;
2217 		}
2218 		p = ZSTR_VAL(haystack_dup);
2219 		if ((size_t)-offset < ZSTR_LEN(needle)) {
2220 			e = ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack);
2221 		} else {
2222 			e = ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack) + offset + ZSTR_LEN(needle);
2223 		}
2224 	}
2225 
2226 	needle_dup = php_string_tolower(needle);
2227 	if ((found = (char *)zend_memnrstr(p, ZSTR_VAL(needle_dup), ZSTR_LEN(needle_dup), e))) {
2228 		RETVAL_LONG(found - ZSTR_VAL(haystack_dup));
2229 		zend_string_release(needle_dup);
2230 		zend_string_release(haystack_dup);
2231 		ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2232 	} else {
2233 		zend_string_release(needle_dup);
2234 		zend_string_release(haystack_dup);
2235 		ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2236 		RETURN_FALSE;
2237 	}
2238 }
2239 /* }}} */
2240 
2241 /* {{{ proto string strrchr(string haystack, string needle)
2242    Finds the last occurrence of a character in a string within another */
2243 PHP_FUNCTION(strrchr)
2244 {
2245 	zval *needle;
2246 	zend_string *haystack;
2247 	const char *found = NULL;
2248 	zend_long found_offset;
2249 
2250 	ZEND_PARSE_PARAMETERS_START(2, 2)
2251 		Z_PARAM_STR(haystack)
2252 		Z_PARAM_ZVAL(needle)
2253 	ZEND_PARSE_PARAMETERS_END();
2254 
2255 	if (Z_TYPE_P(needle) == IS_STRING) {
2256 		found = zend_memrchr(ZSTR_VAL(haystack), *Z_STRVAL_P(needle), ZSTR_LEN(haystack));
2257 	} else {
2258 		char needle_chr;
2259 		if (php_needle_char(needle, &needle_chr) != SUCCESS) {
2260 			RETURN_FALSE;
2261 		}
2262 
2263 		found = zend_memrchr(ZSTR_VAL(haystack),  needle_chr, ZSTR_LEN(haystack));
2264 	}
2265 
2266 	if (found) {
2267 		found_offset = found - ZSTR_VAL(haystack);
2268 		RETURN_STRINGL(found, ZSTR_LEN(haystack) - found_offset);
2269 	} else {
2270 		RETURN_FALSE;
2271 	}
2272 }
2273 /* }}} */
2274 
2275 /* {{{ php_chunk_split
2276  */
2277 static zend_string *php_chunk_split(char *src, size_t srclen, char *end, size_t endlen, size_t chunklen)
2278 {
2279 	char *p, *q;
2280 	size_t chunks; /* complete chunks! */
2281 	size_t restlen;
2282 	size_t out_len;
2283 	zend_string *dest;
2284 
2285 	chunks = srclen / chunklen;
2286 	restlen = srclen - chunks * chunklen; /* srclen % chunklen */
2287 
2288 	if (chunks > INT_MAX - 1) {
2289 		return NULL;
2290 	}
2291 	out_len = chunks + 1;
2292 	if (endlen !=0 && out_len > INT_MAX/endlen) {
2293 		return NULL;
2294 	}
2295 	out_len *= endlen;
2296 	if (out_len > INT_MAX - srclen - 1) {
2297 		return NULL;
2298 	}
2299 	out_len += srclen + 1;
2300 
2301 	dest = zend_string_alloc(out_len * sizeof(char), 0);
2302 
2303 	for (p = src, q = ZSTR_VAL(dest); p < (src + srclen - chunklen + 1); ) {
2304 		memcpy(q, p, chunklen);
2305 		q += chunklen;
2306 		memcpy(q, end, endlen);
2307 		q += endlen;
2308 		p += chunklen;
2309 	}
2310 
2311 	if (restlen) {
2312 		memcpy(q, p, restlen);
2313 		q += restlen;
2314 		memcpy(q, end, endlen);
2315 		q += endlen;
2316 	}
2317 
2318 	*q = '\0';
2319 	ZSTR_LEN(dest) = q - ZSTR_VAL(dest);
2320 
2321 	return dest;
2322 }
2323 /* }}} */
2324 
2325 /* {{{ proto string chunk_split(string str [, int chunklen [, string ending]])
2326    Returns split line */
2327 PHP_FUNCTION(chunk_split)
2328 {
2329 	zend_string *str;
2330 	char *end    = "\r\n";
2331 	size_t endlen   = 2;
2332 	zend_long chunklen = 76;
2333 	zend_string *result;
2334 
2335 	ZEND_PARSE_PARAMETERS_START(1, 3)
2336 		Z_PARAM_STR(str)
2337 		Z_PARAM_OPTIONAL
2338 		Z_PARAM_LONG(chunklen)
2339 		Z_PARAM_STRING(end, endlen)
2340 	ZEND_PARSE_PARAMETERS_END();
2341 
2342 	if (chunklen <= 0) {
2343 		php_error_docref(NULL, E_WARNING, "Chunk length should be greater than zero");
2344 		RETURN_FALSE;
2345 	}
2346 
2347 	if ((size_t)chunklen > ZSTR_LEN(str)) {
2348 		/* to maintain BC, we must return original string + ending */
2349 		result = zend_string_safe_alloc(ZSTR_LEN(str), 1, endlen, 0);
2350 		memcpy(ZSTR_VAL(result), ZSTR_VAL(str), ZSTR_LEN(str));
2351 		memcpy(ZSTR_VAL(result) + ZSTR_LEN(str), end, endlen);
2352 		ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
2353 		RETURN_NEW_STR(result);
2354 	}
2355 
2356 	if (!ZSTR_LEN(str)) {
2357 		RETURN_EMPTY_STRING();
2358 	}
2359 
2360 	result = php_chunk_split(ZSTR_VAL(str), ZSTR_LEN(str), end, endlen, (size_t)chunklen);
2361 
2362 	if (result) {
2363 		RETURN_STR(result);
2364 	} else {
2365 		RETURN_FALSE;
2366 	}
2367 }
2368 /* }}} */
2369 
2370 /* {{{ proto string substr(string str, int start [, int length])
2371    Returns part of a string */
2372 PHP_FUNCTION(substr)
2373 {
2374 	zend_string *str;
2375 	zend_long l = 0, f;
2376 	int argc = ZEND_NUM_ARGS();
2377 
2378 	ZEND_PARSE_PARAMETERS_START(2, 3)
2379 		Z_PARAM_STR(str)
2380 		Z_PARAM_LONG(f)
2381 		Z_PARAM_OPTIONAL
2382 		Z_PARAM_LONG(l)
2383 	ZEND_PARSE_PARAMETERS_END();
2384 
2385 	if (argc > 2) {
2386 		if ((l < 0 && (size_t)(-l) > ZSTR_LEN(str))) {
2387 			RETURN_FALSE;
2388 		} else if (l > (zend_long)ZSTR_LEN(str)) {
2389 			l = ZSTR_LEN(str);
2390 		}
2391 	} else {
2392 		l = ZSTR_LEN(str);
2393 	}
2394 
2395 	if (f > (zend_long)ZSTR_LEN(str)) {
2396 		RETURN_FALSE;
2397 	} else if (f < 0 && (size_t)-f > ZSTR_LEN(str)) {
2398 		f = 0;
2399 	}
2400 
2401 	if (l < 0 && (l + (zend_long)ZSTR_LEN(str) - f) < 0) {
2402 		RETURN_FALSE;
2403 	}
2404 
2405 	/* if "from" position is negative, count start position from the end
2406 	 * of the string
2407 	 */
2408 	if (f < 0) {
2409 		f = (zend_long)ZSTR_LEN(str) + f;
2410 		if (f < 0) {
2411 			f = 0;
2412 		}
2413 	}
2414 
2415 	/* if "length" position is negative, set it to the length
2416 	 * needed to stop that many chars from the end of the string
2417 	 */
2418 	if (l < 0) {
2419 		l = ((zend_long)ZSTR_LEN(str) - f) + l;
2420 		if (l < 0) {
2421 			l = 0;
2422 		}
2423 	}
2424 
2425 	if (f > (zend_long)ZSTR_LEN(str)) {
2426 		RETURN_FALSE;
2427 	}
2428 
2429 	if ((size_t)l > ZSTR_LEN(str) - (size_t)f) {
2430 		l = ZSTR_LEN(str) - f;
2431 	}
2432 
2433 	if (l == 0) {
2434 		RETURN_EMPTY_STRING();
2435 	} else if (l == 1) {
2436 		RETURN_INTERNED_STR(ZSTR_CHAR((zend_uchar)(ZSTR_VAL(str)[f])));
2437 	} else if (l == ZSTR_LEN(str)) {
2438 		RETURN_STR_COPY(str);
2439 	}
2440 
2441 	RETURN_STRINGL(ZSTR_VAL(str) + f, l);
2442 }
2443 /* }}} */
2444 
2445 /* {{{ proto mixed substr_replace(mixed str, mixed repl, mixed start [, mixed length])
2446    Replaces part of a string with another string */
2447 PHP_FUNCTION(substr_replace)
2448 {
2449 	zval *str;
2450 	zval *from;
2451 	zval *len = NULL;
2452 	zval *repl;
2453 	zend_long l = 0;
2454 	zend_long f;
2455 	int argc = ZEND_NUM_ARGS();
2456 	zend_string *result;
2457 	HashPosition from_idx, repl_idx, len_idx;
2458 	zval *tmp_str = NULL, *tmp_from = NULL, *tmp_repl = NULL, *tmp_len= NULL;
2459 
2460 	ZEND_PARSE_PARAMETERS_START(3, 4)
2461 		Z_PARAM_ZVAL(str)
2462 		Z_PARAM_ZVAL(repl)
2463 		Z_PARAM_ZVAL(from)
2464 		Z_PARAM_OPTIONAL
2465 		Z_PARAM_ZVAL(len)
2466 	ZEND_PARSE_PARAMETERS_END();
2467 
2468 	if (Z_TYPE_P(str) != IS_ARRAY) {
2469 		convert_to_string_ex(str);
2470 	}
2471 	if (Z_TYPE_P(repl) != IS_ARRAY) {
2472 		convert_to_string_ex(repl);
2473 	}
2474 	if (Z_TYPE_P(from) != IS_ARRAY) {
2475 		convert_to_long_ex(from);
2476 	}
2477 
2478 	if (argc > 3) {
2479 		if (Z_TYPE_P(len) != IS_ARRAY) {
2480 			convert_to_long_ex(len);
2481 			l = zval_get_long(len);
2482 		}
2483 	} else {
2484 		if (Z_TYPE_P(str) != IS_ARRAY) {
2485 			l = Z_STRLEN_P(str);
2486 		}
2487 	}
2488 
2489 	if (Z_TYPE_P(str) == IS_STRING) {
2490 		if (
2491 			(argc == 3 && Z_TYPE_P(from) == IS_ARRAY) ||
2492 			(argc == 4 && Z_TYPE_P(from) != Z_TYPE_P(len))
2493 		) {
2494 			php_error_docref(NULL, E_WARNING, "'start' and 'length' should be of same type - numerical or array ");
2495 			RETURN_STR_COPY(Z_STR_P(str));
2496 		}
2497 		if (argc == 4 && Z_TYPE_P(from) == IS_ARRAY) {
2498 			if (zend_hash_num_elements(Z_ARRVAL_P(from)) != zend_hash_num_elements(Z_ARRVAL_P(len))) {
2499 				php_error_docref(NULL, E_WARNING, "'start' and 'length' should have the same number of elements");
2500 				RETURN_STR_COPY(Z_STR_P(str));
2501 			}
2502 		}
2503 	}
2504 
2505 	if (Z_TYPE_P(str) != IS_ARRAY) {
2506 		if (Z_TYPE_P(from) != IS_ARRAY) {
2507 			zend_string *repl_str;
2508 			zend_string *tmp_repl_str = NULL;
2509 			f = Z_LVAL_P(from);
2510 
2511 			/* if "from" position is negative, count start position from the end
2512 			 * of the string
2513 			 */
2514 			if (f < 0) {
2515 				f = (zend_long)Z_STRLEN_P(str) + f;
2516 				if (f < 0) {
2517 					f = 0;
2518 				}
2519 			} else if ((size_t)f > Z_STRLEN_P(str)) {
2520 				f = Z_STRLEN_P(str);
2521 			}
2522 			/* if "length" position is negative, set it to the length
2523 			 * needed to stop that many chars from the end of the string
2524 			 */
2525 			if (l < 0) {
2526 				l = ((zend_long)Z_STRLEN_P(str) - f) + l;
2527 				if (l < 0) {
2528 					l = 0;
2529 				}
2530 			}
2531 
2532 			if ((size_t)l > Z_STRLEN_P(str) || (l < 0 && (size_t)(-l) > Z_STRLEN_P(str))) {
2533 				l = Z_STRLEN_P(str);
2534 			}
2535 
2536 			if ((f + l) > (zend_long)Z_STRLEN_P(str)) {
2537 				l = Z_STRLEN_P(str) - f;
2538 			}
2539 			if (Z_TYPE_P(repl) == IS_ARRAY) {
2540 				repl_idx = 0;
2541 				while (repl_idx < Z_ARRVAL_P(repl)->nNumUsed) {
2542 					tmp_repl = &Z_ARRVAL_P(repl)->arData[repl_idx].val;
2543 					if (Z_TYPE_P(tmp_repl) != IS_UNDEF) {
2544 						break;
2545 					}
2546 					repl_idx++;
2547 				}
2548 				if (repl_idx < Z_ARRVAL_P(repl)->nNumUsed) {
2549 					repl_str = zval_get_tmp_string(tmp_repl, &tmp_repl_str);
2550 				} else {
2551 					repl_str = STR_EMPTY_ALLOC();
2552 				}
2553 			} else {
2554 				repl_str = Z_STR_P(repl);
2555 			}
2556 
2557 			result = zend_string_safe_alloc(1, Z_STRLEN_P(str) - l + ZSTR_LEN(repl_str), 0, 0);
2558 
2559 			memcpy(ZSTR_VAL(result), Z_STRVAL_P(str), f);
2560 			if (ZSTR_LEN(repl_str)) {
2561 				memcpy((ZSTR_VAL(result) + f), ZSTR_VAL(repl_str), ZSTR_LEN(repl_str));
2562 			}
2563 			memcpy((ZSTR_VAL(result) + f + ZSTR_LEN(repl_str)), Z_STRVAL_P(str) + f + l, Z_STRLEN_P(str) - f - l);
2564 			ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
2565 			zend_tmp_string_release(tmp_repl_str);
2566 			RETURN_NEW_STR(result);
2567 		} else {
2568 			php_error_docref(NULL, E_WARNING, "Functionality of 'start' and 'length' as arrays is not implemented");
2569 			RETURN_STR_COPY(Z_STR_P(str));
2570 		}
2571 	} else { /* str is array of strings */
2572 		zend_string *str_index = NULL;
2573 		size_t result_len;
2574 		zend_ulong num_index;
2575 
2576 		array_init(return_value);
2577 
2578 		from_idx = len_idx = repl_idx = 0;
2579 
2580 		ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(str), num_index, str_index, tmp_str) {
2581 			zend_string *tmp_orig_str;
2582 			zend_string *orig_str = zval_get_tmp_string(tmp_str, &tmp_orig_str);
2583 
2584 			if (Z_TYPE_P(from) == IS_ARRAY) {
2585 				while (from_idx < Z_ARRVAL_P(from)->nNumUsed) {
2586 					tmp_from = &Z_ARRVAL_P(from)->arData[from_idx].val;
2587 					if (Z_TYPE_P(tmp_from) != IS_UNDEF) {
2588 						break;
2589 					}
2590 					from_idx++;
2591 				}
2592 				if (from_idx < Z_ARRVAL_P(from)->nNumUsed) {
2593 					f = zval_get_long(tmp_from);
2594 
2595 					if (f < 0) {
2596 						f = (zend_long)ZSTR_LEN(orig_str) + f;
2597 						if (f < 0) {
2598 							f = 0;
2599 						}
2600 					} else if (f > (zend_long)ZSTR_LEN(orig_str)) {
2601 						f = ZSTR_LEN(orig_str);
2602 					}
2603 					from_idx++;
2604 				} else {
2605 					f = 0;
2606 				}
2607 			} else {
2608 				f = Z_LVAL_P(from);
2609 				if (f < 0) {
2610 					f = (zend_long)ZSTR_LEN(orig_str) + f;
2611 					if (f < 0) {
2612 						f = 0;
2613 					}
2614 				} else if (f > (zend_long)ZSTR_LEN(orig_str)) {
2615 					f = ZSTR_LEN(orig_str);
2616 				}
2617 			}
2618 
2619 			if (argc > 3 && Z_TYPE_P(len) == IS_ARRAY) {
2620 				while (len_idx < Z_ARRVAL_P(len)->nNumUsed) {
2621 					tmp_len = &Z_ARRVAL_P(len)->arData[len_idx].val;
2622 					if (Z_TYPE_P(tmp_len) != IS_UNDEF) {
2623 						break;
2624 					}
2625 					len_idx++;
2626 				}
2627 				if (len_idx < Z_ARRVAL_P(len)->nNumUsed) {
2628 					l = zval_get_long(tmp_len);
2629 					len_idx++;
2630 				} else {
2631 					l = ZSTR_LEN(orig_str);
2632 				}
2633 			} else if (argc > 3) {
2634 				l = Z_LVAL_P(len);
2635 			} else {
2636 				l = ZSTR_LEN(orig_str);
2637 			}
2638 
2639 			if (l < 0) {
2640 				l = (ZSTR_LEN(orig_str) - f) + l;
2641 				if (l < 0) {
2642 					l = 0;
2643 				}
2644 			}
2645 
2646 			if ((f + l) > (zend_long)ZSTR_LEN(orig_str)) {
2647 				l = ZSTR_LEN(orig_str) - f;
2648 			}
2649 
2650 			result_len = ZSTR_LEN(orig_str) - l;
2651 
2652 			if (Z_TYPE_P(repl) == IS_ARRAY) {
2653 				while (repl_idx < Z_ARRVAL_P(repl)->nNumUsed) {
2654 					tmp_repl = &Z_ARRVAL_P(repl)->arData[repl_idx].val;
2655 					if (Z_TYPE_P(tmp_repl) != IS_UNDEF) {
2656 						break;
2657 					}
2658 					repl_idx++;
2659 				}
2660 				if (repl_idx < Z_ARRVAL_P(repl)->nNumUsed) {
2661 					zend_string *tmp_repl_str;
2662 					zend_string *repl_str = zval_get_tmp_string(tmp_repl, &tmp_repl_str);
2663 
2664 					result_len += ZSTR_LEN(repl_str);
2665 					repl_idx++;
2666 					result = zend_string_safe_alloc(1, result_len, 0, 0);
2667 
2668 					memcpy(ZSTR_VAL(result), ZSTR_VAL(orig_str), f);
2669 					memcpy((ZSTR_VAL(result) + f), ZSTR_VAL(repl_str), ZSTR_LEN(repl_str));
2670 					memcpy((ZSTR_VAL(result) + f + ZSTR_LEN(repl_str)), ZSTR_VAL(orig_str) + f + l, ZSTR_LEN(orig_str) - f - l);
2671 					zend_tmp_string_release(tmp_repl_str);
2672 				} else {
2673 					result = zend_string_safe_alloc(1, result_len, 0, 0);
2674 
2675 					memcpy(ZSTR_VAL(result), ZSTR_VAL(orig_str), f);
2676 					memcpy((ZSTR_VAL(result) + f), ZSTR_VAL(orig_str) + f + l, ZSTR_LEN(orig_str) - f - l);
2677 				}
2678 			} else {
2679 				result_len += Z_STRLEN_P(repl);
2680 
2681 				result = zend_string_safe_alloc(1, result_len, 0, 0);
2682 
2683 				memcpy(ZSTR_VAL(result), ZSTR_VAL(orig_str), f);
2684 				memcpy((ZSTR_VAL(result) + f), Z_STRVAL_P(repl), Z_STRLEN_P(repl));
2685 				memcpy((ZSTR_VAL(result) + f + Z_STRLEN_P(repl)), ZSTR_VAL(orig_str) + f + l, ZSTR_LEN(orig_str) - f - l);
2686 			}
2687 
2688 			ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
2689 
2690 			if (str_index) {
2691 				zval tmp;
2692 
2693 				ZVAL_NEW_STR(&tmp, result);
2694 				zend_symtable_update(Z_ARRVAL_P(return_value), str_index, &tmp);
2695 			} else {
2696 				add_index_str(return_value, num_index, result);
2697 			}
2698 
2699 			zend_tmp_string_release(tmp_orig_str);
2700 		} ZEND_HASH_FOREACH_END();
2701 	} /* if */
2702 }
2703 /* }}} */
2704 
2705 /* {{{ proto string quotemeta(string str)
2706    Quotes meta characters */
2707 PHP_FUNCTION(quotemeta)
2708 {
2709 	zend_string *old;
2710 	char *old_end;
2711 	char *p, *q;
2712 	char c;
2713 	zend_string *str;
2714 
2715 	ZEND_PARSE_PARAMETERS_START(1, 1)
2716 		Z_PARAM_STR(old)
2717 	ZEND_PARSE_PARAMETERS_END();
2718 
2719 	old_end = ZSTR_VAL(old) + ZSTR_LEN(old);
2720 
2721 	if (ZSTR_VAL(old) == old_end) {
2722 		RETURN_FALSE;
2723 	}
2724 
2725 	str = zend_string_safe_alloc(2, ZSTR_LEN(old), 0, 0);
2726 
2727 	for (p = ZSTR_VAL(old), q = ZSTR_VAL(str); p != old_end; p++) {
2728 		c = *p;
2729 		switch (c) {
2730 			case '.':
2731 			case '\\':
2732 			case '+':
2733 			case '*':
2734 			case '?':
2735 			case '[':
2736 			case '^':
2737 			case ']':
2738 			case '$':
2739 			case '(':
2740 			case ')':
2741 				*q++ = '\\';
2742 				/* break is missing _intentionally_ */
2743 			default:
2744 				*q++ = c;
2745 		}
2746 	}
2747 
2748 	*q = '\0';
2749 
2750 	RETURN_NEW_STR(zend_string_truncate(str, q - ZSTR_VAL(str), 0));
2751 }
2752 /* }}} */
2753 
2754 /* {{{ proto int ord(string character)
2755    Returns ASCII value of character
2756    Warning: This function is special-cased by zend_compile.c and so is bypassed for constant string argument */
2757 PHP_FUNCTION(ord)
2758 {
2759 	char   *str;
2760 	size_t str_len;
2761 
2762 	ZEND_PARSE_PARAMETERS_START(1, 1)
2763 		Z_PARAM_STRING(str, str_len)
2764 	ZEND_PARSE_PARAMETERS_END();
2765 
2766 	RETURN_LONG((unsigned char) str[0]);
2767 }
2768 /* }}} */
2769 
2770 /* {{{ proto string chr(int ascii)
2771    Converts ASCII code to a character
2772    Warning: This function is special-cased by zend_compile.c and so is bypassed for constant integer argument */
2773 PHP_FUNCTION(chr)
2774 {
2775 	zend_long c;
2776 
2777 	if (ZEND_NUM_ARGS() != 1) {
2778 		WRONG_PARAM_COUNT;
2779 	}
2780 
2781 	ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 1, 1)
2782 		Z_PARAM_LONG(c)
2783 	ZEND_PARSE_PARAMETERS_END_EX(c = 0);
2784 
2785 	c &= 0xff;
2786 	ZVAL_INTERNED_STR(return_value, ZSTR_CHAR(c));
2787 }
2788 /* }}} */
2789 
2790 /* {{{ php_ucfirst
2791    Uppercase the first character of the word in a native string */
2792 static zend_string* php_ucfirst(zend_string *str)
2793 {
2794 	unsigned char r = toupper(ZSTR_VAL(str)[0]);
2795 	if (r == ZSTR_VAL(str)[0]) {
2796 		return zend_string_copy(str);
2797 	} else {
2798 		zend_string *s = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
2799 		ZSTR_VAL(s)[0] = r;
2800 		return s;
2801 	}
2802 }
2803 /* }}} */
2804 
2805 /* {{{ proto string ucfirst(string str)
2806    Makes a string's first character uppercase */
2807 PHP_FUNCTION(ucfirst)
2808 {
2809 	zend_string *str;
2810 
2811 	ZEND_PARSE_PARAMETERS_START(1, 1)
2812 		Z_PARAM_STR(str)
2813 	ZEND_PARSE_PARAMETERS_END();
2814 
2815 	if (!ZSTR_LEN(str)) {
2816 		RETURN_EMPTY_STRING();
2817 	}
2818 
2819 	RETURN_STR(php_ucfirst(str));
2820 }
2821 /* }}} */
2822 
2823 /* {{{
2824    Lowercase the first character of the word in a native string */
2825 static zend_string* php_lcfirst(zend_string *str)
2826 {
2827 	unsigned char r = tolower(ZSTR_VAL(str)[0]);
2828 	if (r == ZSTR_VAL(str)[0]) {
2829 		return zend_string_copy(str);
2830 	} else {
2831 		zend_string *s = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
2832 		ZSTR_VAL(s)[0] = r;
2833 		return s;
2834 	}
2835 }
2836 /* }}} */
2837 
2838 /* {{{ proto string lcfirst(string str)
2839    Make a string's first character lowercase */
2840 PHP_FUNCTION(lcfirst)
2841 {
2842 	zend_string  *str;
2843 
2844 	ZEND_PARSE_PARAMETERS_START(1, 1)
2845 		Z_PARAM_STR(str)
2846 	ZEND_PARSE_PARAMETERS_END();
2847 
2848 	if (!ZSTR_LEN(str)) {
2849 		RETURN_EMPTY_STRING();
2850 	}
2851 
2852 	RETURN_STR(php_lcfirst(str));
2853 }
2854 /* }}} */
2855 
2856 /* {{{ proto string ucwords(string str [, string delims])
2857    Uppercase the first character of every word in a string */
2858 PHP_FUNCTION(ucwords)
2859 {
2860 	zend_string *str;
2861 	char *delims = " \t\r\n\f\v";
2862 	register char *r, *r_end;
2863 	size_t delims_len = 6;
2864 	char mask[256];
2865 
2866 	ZEND_PARSE_PARAMETERS_START(1, 2)
2867 		Z_PARAM_STR(str)
2868 		Z_PARAM_OPTIONAL
2869 		Z_PARAM_STRING(delims, delims_len)
2870 	ZEND_PARSE_PARAMETERS_END();
2871 
2872 	if (!ZSTR_LEN(str)) {
2873 		RETURN_EMPTY_STRING();
2874 	}
2875 
2876 	php_charmask((unsigned char *)delims, delims_len, mask);
2877 
2878 	ZVAL_STRINGL(return_value, ZSTR_VAL(str), ZSTR_LEN(str));
2879 	r = Z_STRVAL_P(return_value);
2880 
2881 	*r = toupper((unsigned char) *r);
2882 	for (r_end = r + Z_STRLEN_P(return_value) - 1; r < r_end; ) {
2883 		if (mask[(unsigned char)*r++]) {
2884 			*r = toupper((unsigned char) *r);
2885 		}
2886 	}
2887 }
2888 /* }}} */
2889 
2890 /* {{{ php_strtr
2891  */
2892 PHPAPI char *php_strtr(char *str, size_t len, char *str_from, char *str_to, size_t trlen)
2893 {
2894 	size_t i;
2895 
2896 	if (UNEXPECTED(trlen < 1)) {
2897 		return str;
2898 	} else if (trlen == 1) {
2899 		char ch_from = *str_from;
2900 		char ch_to = *str_to;
2901 
2902 		for (i = 0; i < len; i++) {
2903 			if (str[i] == ch_from) {
2904 				str[i] = ch_to;
2905 			}
2906 		}
2907 	} else {
2908 		unsigned char xlat[256], j = 0;
2909 
2910 		do { xlat[j] = j; } while (++j != 0);
2911 
2912 		for (i = 0; i < trlen; i++) {
2913 			xlat[(size_t)(unsigned char) str_from[i]] = str_to[i];
2914 		}
2915 
2916 		for (i = 0; i < len; i++) {
2917 			str[i] = xlat[(size_t)(unsigned char) str[i]];
2918 		}
2919 	}
2920 
2921 	return str;
2922 }
2923 /* }}} */
2924 
2925 /* {{{ php_strtr_ex
2926  */
2927 static zend_string *php_strtr_ex(zend_string *str, char *str_from, char *str_to, size_t trlen)
2928 {
2929 	zend_string *new_str = NULL;
2930 	size_t i;
2931 
2932 	if (UNEXPECTED(trlen < 1)) {
2933 		return zend_string_copy(str);
2934 	} else if (trlen == 1) {
2935 		char ch_from = *str_from;
2936 		char ch_to = *str_to;
2937 
2938 		for (i = 0; i < ZSTR_LEN(str); i++) {
2939 			if (ZSTR_VAL(str)[i] == ch_from) {
2940 				new_str = zend_string_alloc(ZSTR_LEN(str), 0);
2941 				memcpy(ZSTR_VAL(new_str), ZSTR_VAL(str), i);
2942 				ZSTR_VAL(new_str)[i] = ch_to;
2943 				break;
2944 			}
2945 		}
2946 		for (; i < ZSTR_LEN(str); i++) {
2947 			ZSTR_VAL(new_str)[i] = (ZSTR_VAL(str)[i] != ch_from) ? ZSTR_VAL(str)[i] : ch_to;
2948 		}
2949 	} else {
2950 		unsigned char xlat[256], j = 0;
2951 
2952 		do { xlat[j] = j; } while (++j != 0);
2953 
2954 		for (i = 0; i < trlen; i++) {
2955 			xlat[(size_t)(unsigned char) str_from[i]] = str_to[i];
2956 		}
2957 
2958 		for (i = 0; i < ZSTR_LEN(str); i++) {
2959 			if (ZSTR_VAL(str)[i] != xlat[(size_t)(unsigned char) ZSTR_VAL(str)[i]]) {
2960 				new_str = zend_string_alloc(ZSTR_LEN(str), 0);
2961 				memcpy(ZSTR_VAL(new_str), ZSTR_VAL(str), i);
2962 				ZSTR_VAL(new_str)[i] = xlat[(size_t)(unsigned char) ZSTR_VAL(str)[i]];
2963 				break;
2964 			}
2965 		}
2966 
2967 		for (;i < ZSTR_LEN(str); i++) {
2968 			ZSTR_VAL(new_str)[i] = xlat[(size_t)(unsigned char) ZSTR_VAL(str)[i]];
2969 		}
2970 	}
2971 
2972 	if (!new_str) {
2973 		return zend_string_copy(str);
2974 	}
2975 
2976 	ZSTR_VAL(new_str)[ZSTR_LEN(new_str)] = 0;
2977 	return new_str;
2978 }
2979 /* }}} */
2980 
2981 /* {{{ php_strtr_array */
2982 static void php_strtr_array(zval *return_value, zend_string *input, HashTable *pats)
2983 {
2984 	char *str = ZSTR_VAL(input);
2985 	size_t slen = ZSTR_LEN(input);
2986 	zend_ulong num_key;
2987 	zend_string *str_key;
2988 	size_t len, pos, old_pos;
2989 	int num_keys = 0;
2990 	size_t minlen = 128*1024;
2991 	size_t maxlen = 0;
2992 	HashTable str_hash;
2993 	zval *entry;
2994 	char *key;
2995 	smart_str result = {0};
2996 	zend_ulong bitset[256/sizeof(zend_ulong)];
2997 	zend_ulong *num_bitset;
2998 
2999 	/* we will collect all possible key lengths */
3000 	num_bitset = ecalloc((slen + sizeof(zend_ulong)) / sizeof(zend_ulong), sizeof(zend_ulong));
3001 	memset(bitset, 0, sizeof(bitset));
3002 
3003 	/* check if original array has numeric keys */
3004 	ZEND_HASH_FOREACH_STR_KEY(pats, str_key) {
3005 		if (UNEXPECTED(!str_key)) {
3006 			num_keys = 1;
3007 		} else {
3008 			len = ZSTR_LEN(str_key);
3009 			if (UNEXPECTED(len < 1)) {
3010 				efree(num_bitset);
3011 				RETURN_FALSE;
3012 			} else if (UNEXPECTED(len > slen)) {
3013 				/* skip long patterns */
3014 				continue;
3015 			}
3016 			if (len > maxlen) {
3017 				maxlen = len;
3018 			}
3019 			if (len < minlen) {
3020 				minlen = len;
3021 			}
3022 			/* remember possible key length */
3023 			num_bitset[len / sizeof(zend_ulong)] |= Z_UL(1) << (len % sizeof(zend_ulong));
3024 			bitset[((unsigned char)ZSTR_VAL(str_key)[0]) / sizeof(zend_ulong)] |= Z_UL(1) << (((unsigned char)ZSTR_VAL(str_key)[0]) % sizeof(zend_ulong));
3025 		}
3026 	} ZEND_HASH_FOREACH_END();
3027 
3028 	if (UNEXPECTED(num_keys)) {
3029 		zend_string *key_used;
3030 		/* we have to rebuild HashTable with numeric keys */
3031 		zend_hash_init(&str_hash, zend_hash_num_elements(pats), NULL, NULL, 0);
3032 		ZEND_HASH_FOREACH_KEY_VAL(pats, num_key, str_key, entry) {
3033 			if (UNEXPECTED(!str_key)) {
3034 				key_used = zend_long_to_str(num_key);
3035 				len = ZSTR_LEN(key_used);
3036 				if (UNEXPECTED(len > slen)) {
3037 					/* skip long patterns */
3038 					continue;
3039 				}
3040 				if (len > maxlen) {
3041 					maxlen = len;
3042 				}
3043 				if (len < minlen) {
3044 					minlen = len;
3045 				}
3046 				/* remember possible key length */
3047 				num_bitset[len / sizeof(zend_ulong)] |= Z_UL(1) << (len % sizeof(zend_ulong));
3048 				bitset[((unsigned char)ZSTR_VAL(key_used)[0]) / sizeof(zend_ulong)] |= Z_UL(1) << (((unsigned char)ZSTR_VAL(key_used)[0]) % sizeof(zend_ulong));
3049 			} else {
3050 				key_used = str_key;
3051 				len = ZSTR_LEN(key_used);
3052 				if (UNEXPECTED(len > slen)) {
3053 					/* skip long patterns */
3054 					continue;
3055 				}
3056 			}
3057 			zend_hash_add(&str_hash, key_used, entry);
3058 			if (UNEXPECTED(!str_key)) {
3059 				zend_string_release(key_used);
3060 			}
3061 		} ZEND_HASH_FOREACH_END();
3062 		pats = &str_hash;
3063 	}
3064 
3065 	if (UNEXPECTED(minlen > maxlen)) {
3066 		/* return the original string */
3067 		if (pats == &str_hash) {
3068 			zend_hash_destroy(&str_hash);
3069 		}
3070 		efree(num_bitset);
3071 		RETURN_STR_COPY(input);
3072 	}
3073 
3074 	old_pos = pos = 0;
3075 	while (pos <= slen - minlen) {
3076 		key = str + pos;
3077 		if (bitset[((unsigned char)key[0]) / sizeof(zend_ulong)] & (Z_UL(1) << (((unsigned char)key[0]) % sizeof(zend_ulong)))) {
3078 			len = maxlen;
3079 			if (len > slen - pos) {
3080 				len = slen - pos;
3081 			}
3082 			while (len >= minlen) {
3083 				if ((num_bitset[len / sizeof(zend_ulong)] & (Z_UL(1) << (len % sizeof(zend_ulong))))) {
3084 					entry = zend_hash_str_find(pats, key, len);
3085 					if (entry != NULL) {
3086 						zend_string *tmp;
3087 						zend_string *s = zval_get_tmp_string(entry, &tmp);
3088 						smart_str_appendl(&result, str + old_pos, pos - old_pos);
3089 						smart_str_append(&result, s);
3090 						old_pos = pos + len;
3091 						pos = old_pos - 1;
3092 						zend_tmp_string_release(tmp);
3093 						break;
3094 					}
3095 				}
3096 				len--;
3097 			}
3098 		}
3099 		pos++;
3100 	}
3101 
3102 	if (result.s) {
3103 		smart_str_appendl(&result, str + old_pos, slen - old_pos);
3104 		smart_str_0(&result);
3105 		RETVAL_NEW_STR(result.s);
3106 	} else {
3107 		smart_str_free(&result);
3108 		RETVAL_STR_COPY(input);
3109 	}
3110 
3111 	if (pats == &str_hash) {
3112 		zend_hash_destroy(&str_hash);
3113 	}
3114 	efree(num_bitset);
3115 }
3116 /* }}} */
3117 
3118 /* {{{ php_char_to_str_ex
3119  */
3120 static zend_string* php_char_to_str_ex(zend_string *str, char from, char *to, size_t to_len, int case_sensitivity, zend_long *replace_count)
3121 {
3122 	zend_string *result;
3123 	size_t char_count = 0;
3124 	char lc_from = 0;
3125 	char *source, *target, *source_end= ZSTR_VAL(str) + ZSTR_LEN(str);
3126 
3127 	if (case_sensitivity) {
3128 		char *p = ZSTR_VAL(str), *e = p + ZSTR_LEN(str);
3129 		while ((p = memchr(p, from, (e - p)))) {
3130 			char_count++;
3131 			p++;
3132 		}
3133 	} else {
3134 		lc_from = tolower(from);
3135 		for (source = ZSTR_VAL(str); source < source_end; source++) {
3136 			if (tolower(*source) == lc_from) {
3137 				char_count++;
3138 			}
3139 		}
3140 	}
3141 
3142 	if (char_count == 0) {
3143 		return zend_string_copy(str);
3144 	}
3145 
3146 	if (to_len > 0) {
3147 		result = zend_string_safe_alloc(char_count, to_len - 1, ZSTR_LEN(str), 0);
3148 	} else {
3149 		result = zend_string_alloc(ZSTR_LEN(str) - char_count, 0);
3150 	}
3151 	target = ZSTR_VAL(result);
3152 
3153 	if (case_sensitivity) {
3154 		char *p = ZSTR_VAL(str), *e = p + ZSTR_LEN(str), *s = ZSTR_VAL(str);
3155 		while ((p = memchr(p, from, (e - p)))) {
3156 			memcpy(target, s, (p - s));
3157 			target += p - s;
3158 			memcpy(target, to, to_len);
3159 			target += to_len;
3160 			p++;
3161 			s = p;
3162 			if (replace_count) {
3163 				*replace_count += 1;
3164 			}
3165 		}
3166 		if (s < e) {
3167 			memcpy(target, s, (e - s));
3168 			target += e - s;
3169 		}
3170 	} else {
3171 		for (source = ZSTR_VAL(str); source < source_end; source++) {
3172 			if (tolower(*source) == lc_from) {
3173 				if (replace_count) {
3174 					*replace_count += 1;
3175 				}
3176 				memcpy(target, to, to_len);
3177 				target += to_len;
3178 			} else {
3179 				*target = *source;
3180 				target++;
3181 			}
3182 		}
3183 	}
3184 	*target = 0;
3185 	return result;
3186 }
3187 /* }}} */
3188 
3189 /* {{{ php_str_to_str_ex
3190  */
3191 static zend_string *php_str_to_str_ex(zend_string *haystack,
3192 	char *needle, size_t needle_len, char *str, size_t str_len, zend_long *replace_count)
3193 {
3194 	zend_string *new_str;
3195 
3196 	if (needle_len < ZSTR_LEN(haystack)) {
3197 		char *end;
3198 		char *e, *s, *p, *r;
3199 
3200 		if (needle_len == str_len) {
3201 			new_str = NULL;
3202 			end = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
3203 			for (p = ZSTR_VAL(haystack); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3204 				if (!new_str) {
3205 					new_str = zend_string_init(ZSTR_VAL(haystack), ZSTR_LEN(haystack), 0);
3206 				}
3207 				memcpy(ZSTR_VAL(new_str) + (r - ZSTR_VAL(haystack)), str, str_len);
3208 				(*replace_count)++;
3209 			}
3210 			if (!new_str) {
3211 				goto nothing_todo;
3212 			}
3213 			return new_str;
3214 		} else {
3215 			size_t count = 0;
3216 			char *o = ZSTR_VAL(haystack);
3217 			char *n = needle;
3218 			char *endp = o + ZSTR_LEN(haystack);
3219 
3220 			while ((o = (char*)php_memnstr(o, n, needle_len, endp))) {
3221 				o += needle_len;
3222 				count++;
3223 			}
3224 			if (count == 0) {
3225 				/* Needle doesn't occur, shortcircuit the actual replacement. */
3226 				goto nothing_todo;
3227 			}
3228 			if (str_len > needle_len) {
3229 				new_str = zend_string_safe_alloc(count, str_len - needle_len, ZSTR_LEN(haystack), 0);
3230 			} else {
3231 				new_str = zend_string_alloc(count * (str_len - needle_len) + ZSTR_LEN(haystack), 0);
3232 			}
3233 
3234 			e = s = ZSTR_VAL(new_str);
3235 			end = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
3236 			for (p = ZSTR_VAL(haystack); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3237 				memcpy(e, p, r - p);
3238 				e += r - p;
3239 				memcpy(e, str, str_len);
3240 				e += str_len;
3241 				(*replace_count)++;
3242 			}
3243 
3244 			if (p < end) {
3245 				memcpy(e, p, end - p);
3246 				e += end - p;
3247 			}
3248 
3249 			*e = '\0';
3250 			return new_str;
3251 		}
3252 	} else if (needle_len > ZSTR_LEN(haystack) || memcmp(ZSTR_VAL(haystack), needle, ZSTR_LEN(haystack))) {
3253 nothing_todo:
3254 		return zend_string_copy(haystack);
3255 	} else {
3256 		if (str_len == 0) {
3257 			new_str = ZSTR_EMPTY_ALLOC();
3258 		} else if (str_len == 1) {
3259 			new_str = ZSTR_CHAR((zend_uchar)(*str));
3260 		} else {
3261 			new_str = zend_string_init(str, str_len, 0);
3262 		}
3263 
3264 		(*replace_count)++;
3265 		return new_str;
3266 	}
3267 }
3268 /* }}} */
3269 
3270 /* {{{ php_str_to_str_i_ex
3271  */
3272 static zend_string *php_str_to_str_i_ex(zend_string *haystack, char *lc_haystack,
3273 	zend_string *needle, char *str, size_t str_len, zend_long *replace_count)
3274 {
3275 	zend_string *new_str = NULL;
3276 	zend_string *lc_needle;
3277 
3278 	if (ZSTR_LEN(needle) < ZSTR_LEN(haystack)) {
3279 		char *end;
3280 		char *e, *s, *p, *r;
3281 
3282 		if (ZSTR_LEN(needle) == str_len) {
3283 			lc_needle = php_string_tolower(needle);
3284 			end = lc_haystack + ZSTR_LEN(haystack);
3285 			for (p = lc_haystack; (r = (char*)php_memnstr(p, ZSTR_VAL(lc_needle), ZSTR_LEN(lc_needle), end)); p = r + ZSTR_LEN(lc_needle)) {
3286 				if (!new_str) {
3287 					new_str = zend_string_init(ZSTR_VAL(haystack), ZSTR_LEN(haystack), 0);
3288 				}
3289 				memcpy(ZSTR_VAL(new_str) + (r - lc_haystack), str, str_len);
3290 				(*replace_count)++;
3291 			}
3292 			zend_string_release(lc_needle);
3293 
3294 			if (!new_str) {
3295 				goto nothing_todo;
3296 			}
3297 			return new_str;
3298 		} else {
3299 			size_t count = 0;
3300 			char *o = lc_haystack;
3301 			char *n;
3302 			char *endp = o + ZSTR_LEN(haystack);
3303 
3304 			lc_needle = php_string_tolower(needle);
3305 			n = ZSTR_VAL(lc_needle);
3306 
3307 			while ((o = (char*)php_memnstr(o, n, ZSTR_LEN(lc_needle), endp))) {
3308 				o += ZSTR_LEN(lc_needle);
3309 				count++;
3310 			}
3311 			if (count == 0) {
3312 				/* Needle doesn't occur, shortcircuit the actual replacement. */
3313 				zend_string_release(lc_needle);
3314 				goto nothing_todo;
3315 			}
3316 
3317 			if (str_len > ZSTR_LEN(lc_needle)) {
3318 				new_str = zend_string_safe_alloc(count, str_len - ZSTR_LEN(lc_needle), ZSTR_LEN(haystack), 0);
3319 			} else {
3320 				new_str = zend_string_alloc(count * (str_len - ZSTR_LEN(lc_needle)) + ZSTR_LEN(haystack), 0);
3321 			}
3322 
3323 			e = s = ZSTR_VAL(new_str);
3324 			end = lc_haystack + ZSTR_LEN(haystack);
3325 
3326 			for (p = lc_haystack; (r = (char*)php_memnstr(p, ZSTR_VAL(lc_needle), ZSTR_LEN(lc_needle), end)); p = r + ZSTR_LEN(lc_needle)) {
3327 				memcpy(e, ZSTR_VAL(haystack) + (p - lc_haystack), r - p);
3328 				e += r - p;
3329 				memcpy(e, str, str_len);
3330 				e += str_len;
3331 				(*replace_count)++;
3332 			}
3333 
3334 			if (p < end) {
3335 				memcpy(e, ZSTR_VAL(haystack) + (p - lc_haystack), end - p);
3336 				e += end - p;
3337 			}
3338 			*e = '\0';
3339 
3340 			zend_string_release(lc_needle);
3341 
3342 			return new_str;
3343 		}
3344 	} else if (ZSTR_LEN(needle) > ZSTR_LEN(haystack)) {
3345 nothing_todo:
3346 		return zend_string_copy(haystack);
3347 	} else {
3348 		lc_needle = php_string_tolower(needle);
3349 
3350 		if (memcmp(lc_haystack, ZSTR_VAL(lc_needle), ZSTR_LEN(lc_needle))) {
3351 			zend_string_release(lc_needle);
3352 			goto nothing_todo;
3353 		}
3354 		zend_string_release(lc_needle);
3355 
3356 		new_str = zend_string_init(str, str_len, 0);
3357 
3358 		(*replace_count)++;
3359 		return new_str;
3360 	}
3361 }
3362 /* }}} */
3363 
3364 /* {{{ php_str_to_str
3365  */
3366 PHPAPI zend_string *php_str_to_str(char *haystack, size_t length, char *needle, size_t needle_len, char *str, size_t str_len)
3367 {
3368 	zend_string *new_str;
3369 
3370 	if (needle_len < length) {
3371 		char *end;
3372 		char *e, *s, *p, *r;
3373 
3374 		if (needle_len == str_len) {
3375 			new_str = zend_string_init(haystack, length, 0);
3376 			end = ZSTR_VAL(new_str) + length;
3377 			for (p = ZSTR_VAL(new_str); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3378 				memcpy(r, str, str_len);
3379 			}
3380 			return new_str;
3381 		} else {
3382 			if (str_len < needle_len) {
3383 				new_str = zend_string_alloc(length, 0);
3384 			} else {
3385 				size_t count = 0;
3386 				char *o = haystack;
3387 				char *n = needle;
3388 				char *endp = o + length;
3389 
3390 				while ((o = (char*)php_memnstr(o, n, needle_len, endp))) {
3391 					o += needle_len;
3392 					count++;
3393 				}
3394 				if (count == 0) {
3395 					/* Needle doesn't occur, shortcircuit the actual replacement. */
3396 					new_str = zend_string_init(haystack, length, 0);
3397 					return new_str;
3398 				} else {
3399 					if (str_len > needle_len) {
3400 						new_str = zend_string_safe_alloc(count, str_len - needle_len, length, 0);
3401 					} else {
3402 						new_str = zend_string_alloc(count * (str_len - needle_len) + length, 0);
3403 					}
3404 				}
3405 			}
3406 
3407 			e = s = ZSTR_VAL(new_str);
3408 			end = haystack + length;
3409 			for (p = haystack; (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3410 				memcpy(e, p, r - p);
3411 				e += r - p;
3412 				memcpy(e, str, str_len);
3413 				e += str_len;
3414 			}
3415 
3416 			if (p < end) {
3417 				memcpy(e, p, end - p);
3418 				e += end - p;
3419 			}
3420 
3421 			*e = '\0';
3422 			new_str = zend_string_truncate(new_str, e - s, 0);
3423 			return new_str;
3424 		}
3425 	} else if (needle_len > length || memcmp(haystack, needle, length)) {
3426 		new_str = zend_string_init(haystack, length, 0);
3427 		return new_str;
3428 	} else {
3429 		new_str = zend_string_init(str, str_len, 0);
3430 
3431 		return new_str;
3432 	}
3433 }
3434 /* }}} */
3435 
3436 /* {{{ proto string strtr(string str, string from[, string to])
3437    Translates characters in str using given translation tables */
3438 PHP_FUNCTION(strtr)
3439 {
3440 	zval *from;
3441 	zend_string *str;
3442 	char *to = NULL;
3443 	size_t to_len = 0;
3444 	int ac = ZEND_NUM_ARGS();
3445 
3446 	ZEND_PARSE_PARAMETERS_START(2, 3)
3447 		Z_PARAM_STR(str)
3448 		Z_PARAM_ZVAL(from)
3449 		Z_PARAM_OPTIONAL
3450 		Z_PARAM_STRING(to, to_len)
3451 	ZEND_PARSE_PARAMETERS_END();
3452 
3453 	if (ac == 2 && Z_TYPE_P(from) != IS_ARRAY) {
3454 		php_error_docref(NULL, E_WARNING, "The second argument is not an array");
3455 		RETURN_FALSE;
3456 	}
3457 
3458 	/* shortcut for empty string */
3459 	if (ZSTR_LEN(str) == 0) {
3460 		RETURN_EMPTY_STRING();
3461 	}
3462 
3463 	if (ac == 2) {
3464 		HashTable *pats = Z_ARRVAL_P(from);
3465 
3466 		if (zend_hash_num_elements(pats) < 1) {
3467 			RETURN_STR_COPY(str);
3468 		} else if (zend_hash_num_elements(pats) == 1) {
3469 			zend_long num_key;
3470 			zend_string *str_key, *replace, *tmp_replace;
3471 			zval *entry, tmp;
3472 
3473 			ZEND_HASH_FOREACH_KEY_VAL(pats, num_key, str_key, entry) {
3474 				ZVAL_UNDEF(&tmp);
3475 				if (UNEXPECTED(!str_key)) {
3476 					ZVAL_LONG(&tmp, num_key);
3477 					convert_to_string(&tmp);
3478