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