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			double val = Z_LVAL_P(tmp);
1233			*++strptr = NULL;
1234			((zend_long *) (strings + numelems))[strptr - strings] = Z_LVAL_P(tmp);
1235			if (val < 0) {
1236				val = -10 * val;
1237			}
1238			if (val < 10) {
1239				len++;
1240			} else {
1241				len += (int) log10(10 * (double) val);
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 || (size_t)offset > ZSTR_LEN(haystack)) {
1972		php_error_docref(NULL, E_WARNING, "Offset not contained in string");
1973		RETURN_FALSE;
1974	}
1975
1976	if (Z_TYPE_P(needle) == IS_STRING) {
1977		if (!Z_STRLEN_P(needle)) {
1978			php_error_docref(NULL, E_WARNING, "Empty needle");
1979			RETURN_FALSE;
1980		}
1981
1982		found = (char*)php_memnstr(ZSTR_VAL(haystack) + offset,
1983			                Z_STRVAL_P(needle),
1984			                Z_STRLEN_P(needle),
1985			                ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1986	} else {
1987		if (php_needle_char(needle, needle_char) != SUCCESS) {
1988			RETURN_FALSE;
1989		}
1990		needle_char[1] = 0;
1991
1992		found = (char*)php_memnstr(ZSTR_VAL(haystack) + offset,
1993							needle_char,
1994							1,
1995		                    ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1996	}
1997
1998	if (found) {
1999		RETURN_LONG(found - ZSTR_VAL(haystack));
2000	} else {
2001		RETURN_FALSE;
2002	}
2003}
2004/* }}} */
2005
2006/* {{{ proto int stripos(string haystack, string needle [, int offset])
2007   Finds position of first occurrence of a string within another, case insensitive */
2008PHP_FUNCTION(stripos)
2009{
2010	char *found = NULL;
2011	zend_string *haystack;
2012	zend_long offset = 0;
2013	char needle_char[2];
2014	zval *needle;
2015	zend_string *needle_dup = NULL, *haystack_dup;
2016
2017	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz|l", &haystack, &needle, &offset) == FAILURE) {
2018		return;
2019	}
2020
2021	if (offset < 0 || (size_t)offset > ZSTR_LEN(haystack)) {
2022		php_error_docref(NULL, E_WARNING, "Offset not contained in string");
2023		RETURN_FALSE;
2024	}
2025
2026	if (ZSTR_LEN(haystack) == 0) {
2027		RETURN_FALSE;
2028	}
2029
2030	if (Z_TYPE_P(needle) == IS_STRING) {
2031		if (Z_STRLEN_P(needle) == 0 || Z_STRLEN_P(needle) > ZSTR_LEN(haystack)) {
2032			RETURN_FALSE;
2033		}
2034
2035		haystack_dup = php_string_tolower(haystack);
2036		needle_dup = php_string_tolower(Z_STR_P(needle));
2037		found = (char*)php_memnstr(ZSTR_VAL(haystack_dup) + offset,
2038				ZSTR_VAL(needle_dup), ZSTR_LEN(needle_dup), ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack));
2039	} else {
2040		if (php_needle_char(needle, needle_char) != SUCCESS) {
2041			RETURN_FALSE;
2042		}
2043		haystack_dup = php_string_tolower(haystack);
2044		needle_char[0] = tolower(needle_char[0]);
2045		needle_char[1] = '\0';
2046		found = (char*)php_memnstr(ZSTR_VAL(haystack_dup) + offset,
2047							needle_char,
2048							sizeof(needle_char) - 1,
2049							ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack));
2050	}
2051
2052
2053	if (found) {
2054		RETVAL_LONG(found - ZSTR_VAL(haystack_dup));
2055	} else {
2056		RETVAL_FALSE;
2057	}
2058
2059	zend_string_release(haystack_dup);
2060	if (needle_dup) {
2061		zend_string_release(needle_dup);
2062	}
2063}
2064/* }}} */
2065
2066/* {{{ proto int strrpos(string haystack, string needle [, int offset])
2067   Finds position of last occurrence of a string within another string */
2068PHP_FUNCTION(strrpos)
2069{
2070	zval *zneedle;
2071	char *needle;
2072	zend_string *haystack;
2073	size_t needle_len;
2074	zend_long offset = 0;
2075	char *p, *e, ord_needle[2];
2076	char *found;
2077
2078#ifndef FAST_ZPP
2079	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz|l", &haystack, &zneedle, &offset) == FAILURE) {
2080		RETURN_FALSE;
2081	}
2082#else
2083	ZEND_PARSE_PARAMETERS_START(2, 3)
2084		Z_PARAM_STR(haystack)
2085		Z_PARAM_ZVAL(zneedle)
2086		Z_PARAM_OPTIONAL
2087		Z_PARAM_LONG(offset)
2088	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
2089#endif
2090
2091	if (Z_TYPE_P(zneedle) == IS_STRING) {
2092		needle = Z_STRVAL_P(zneedle);
2093		needle_len = Z_STRLEN_P(zneedle);
2094	} else {
2095		if (php_needle_char(zneedle, ord_needle) != SUCCESS) {
2096			RETURN_FALSE;
2097		}
2098		ord_needle[1] = '\0';
2099		needle = ord_needle;
2100		needle_len = 1;
2101	}
2102
2103	if ((ZSTR_LEN(haystack) == 0) || (needle_len == 0)) {
2104		RETURN_FALSE;
2105	}
2106
2107	if (offset >= 0) {
2108		if ((size_t)offset > ZSTR_LEN(haystack)) {
2109			php_error_docref(NULL, E_WARNING, "Offset is greater than the length of haystack string");
2110			RETURN_FALSE;
2111		}
2112		p = ZSTR_VAL(haystack) + (size_t)offset;
2113		e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
2114	} else {
2115		if (offset < -INT_MAX || (size_t)(-offset) > ZSTR_LEN(haystack)) {
2116			php_error_docref(NULL, E_WARNING, "Offset is greater than the length of haystack string");
2117			RETURN_FALSE;
2118		}
2119		p = ZSTR_VAL(haystack);
2120		if (-offset < needle_len) {
2121			e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
2122		} else {
2123			e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack) + offset + needle_len;
2124		}
2125	}
2126
2127	if ((found = (char *)zend_memnrstr(p, needle, needle_len, e))) {
2128		RETURN_LONG(found - ZSTR_VAL(haystack));
2129	}
2130
2131	RETURN_FALSE;
2132}
2133/* }}} */
2134
2135/* {{{ proto int strripos(string haystack, string needle [, int offset])
2136   Finds position of last occurrence of a string within another string */
2137PHP_FUNCTION(strripos)
2138{
2139	zval *zneedle;
2140	zend_string *needle;
2141	zend_string *haystack;
2142	zend_long offset = 0;
2143	char *p, *e;
2144	char *found;
2145	zend_string *needle_dup, *haystack_dup, *ord_needle = NULL;
2146	ALLOCA_FLAG(use_heap);
2147
2148
2149	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz|l", &haystack, &zneedle, &offset) == FAILURE) {
2150		RETURN_FALSE;
2151	}
2152
2153	ZSTR_ALLOCA_ALLOC(ord_needle, 1, use_heap);
2154	if (Z_TYPE_P(zneedle) == IS_STRING) {
2155		needle = Z_STR_P(zneedle);
2156	} else {
2157		if (php_needle_char(zneedle, ZSTR_VAL(ord_needle)) != SUCCESS) {
2158			ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2159			RETURN_FALSE;
2160		}
2161		ZSTR_VAL(ord_needle)[1] = '\0';
2162		needle = ord_needle;
2163	}
2164
2165	if ((ZSTR_LEN(haystack) == 0) || (ZSTR_LEN(needle) == 0)) {
2166		ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2167		RETURN_FALSE;
2168	}
2169
2170	if (ZSTR_LEN(needle) == 1) {
2171		/* Single character search can shortcut memcmps
2172		   Can also avoid tolower emallocs */
2173		if (offset >= 0) {
2174			if ((size_t)offset > ZSTR_LEN(haystack)) {
2175				ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2176				php_error_docref(NULL, E_WARNING, "Offset is greater than the length of haystack string");
2177				RETURN_FALSE;
2178			}
2179			p = ZSTR_VAL(haystack) + (size_t)offset;
2180			e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack) - 1;
2181		} else {
2182			p = ZSTR_VAL(haystack);
2183			if (offset < -INT_MAX || (size_t)(-offset) > ZSTR_LEN(haystack)) {
2184				ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2185				php_error_docref(NULL, E_WARNING, "Offset is greater than the length of haystack string");
2186				RETURN_FALSE;
2187			}
2188			e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack) + (size_t)offset;
2189		}
2190		/* Borrow that ord_needle buffer to avoid repeatedly tolower()ing needle */
2191		*ZSTR_VAL(ord_needle) = tolower(*ZSTR_VAL(needle));
2192		while (e >= p) {
2193			if (tolower(*e) == *ZSTR_VAL(ord_needle)) {
2194				ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2195				RETURN_LONG(e - p + (offset > 0 ? offset : 0));
2196			}
2197			e--;
2198		}
2199		ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2200		RETURN_FALSE;
2201	}
2202
2203	haystack_dup = php_string_tolower(haystack);
2204	if (offset >= 0) {
2205		if ((size_t)offset > ZSTR_LEN(haystack)) {
2206			zend_string_release(haystack_dup);
2207			ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2208			php_error_docref(NULL, E_WARNING, "Offset is greater than the length of haystack string");
2209			RETURN_FALSE;
2210		}
2211		p = ZSTR_VAL(haystack_dup) + offset;
2212		e = ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack);
2213	} else {
2214		if (offset < -INT_MAX || (size_t)(-offset) > ZSTR_LEN(haystack)) {
2215			zend_string_release(haystack_dup);
2216			ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2217			php_error_docref(NULL, E_WARNING, "Offset is greater than the length of haystack string");
2218			RETURN_FALSE;
2219		}
2220		p = ZSTR_VAL(haystack_dup);
2221		if (-offset < ZSTR_LEN(needle)) {
2222			e = ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack);
2223		} else {
2224			e = ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack) + offset + ZSTR_LEN(needle);
2225		}
2226	}
2227
2228	needle_dup = php_string_tolower(needle);
2229	if ((found = (char *)zend_memnrstr(p, ZSTR_VAL(needle_dup), ZSTR_LEN(needle_dup), e))) {
2230		RETVAL_LONG(found - ZSTR_VAL(haystack_dup));
2231		zend_string_release(needle_dup);
2232		zend_string_release(haystack_dup);
2233		ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2234	} else {
2235		zend_string_release(needle_dup);
2236		zend_string_release(haystack_dup);
2237		ZSTR_ALLOCA_FREE(ord_needle, use_heap);
2238		RETURN_FALSE;
2239	}
2240}
2241/* }}} */
2242
2243/* {{{ proto string strrchr(string haystack, string needle)
2244   Finds the last occurrence of a character in a string within another */
2245PHP_FUNCTION(strrchr)
2246{
2247	zval *needle;
2248	zend_string *haystack;
2249	const char *found = NULL;
2250	zend_long found_offset;
2251
2252	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz", &haystack, &needle) == FAILURE) {
2253		return;
2254	}
2255
2256	if (Z_TYPE_P(needle) == IS_STRING) {
2257		found = zend_memrchr(ZSTR_VAL(haystack), *Z_STRVAL_P(needle), ZSTR_LEN(haystack));
2258	} else {
2259		char needle_chr;
2260		if (php_needle_char(needle, &needle_chr) != SUCCESS) {
2261			RETURN_FALSE;
2262		}
2263
2264		found = zend_memrchr(ZSTR_VAL(haystack),  needle_chr, ZSTR_LEN(haystack));
2265	}
2266
2267	if (found) {
2268		found_offset = found - ZSTR_VAL(haystack);
2269		RETURN_STRINGL(found, ZSTR_LEN(haystack) - found_offset);
2270	} else {
2271		RETURN_FALSE;
2272	}
2273}
2274/* }}} */
2275
2276/* {{{ php_chunk_split
2277 */
2278static zend_string *php_chunk_split(char *src, size_t srclen, char *end, size_t endlen, size_t chunklen)
2279{
2280	char *p, *q;
2281	size_t chunks; /* complete chunks! */
2282	size_t restlen;
2283	size_t out_len;
2284	zend_string *dest;
2285
2286	chunks = srclen / chunklen;
2287	restlen = srclen - chunks * chunklen; /* srclen % chunklen */
2288
2289	if (chunks > INT_MAX - 1) {
2290		return NULL;
2291	}
2292	out_len = chunks + 1;
2293	if (endlen !=0 && out_len > INT_MAX/endlen) {
2294		return NULL;
2295	}
2296	out_len *= endlen;
2297	if (out_len > INT_MAX - srclen - 1) {
2298		return NULL;
2299	}
2300	out_len += srclen + 1;
2301
2302	dest = zend_string_alloc(out_len * sizeof(char), 0);
2303
2304	for (p = src, q = ZSTR_VAL(dest); p < (src + srclen - chunklen + 1); ) {
2305		memcpy(q, p, chunklen);
2306		q += chunklen;
2307		memcpy(q, end, endlen);
2308		q += endlen;
2309		p += chunklen;
2310	}
2311
2312	if (restlen) {
2313		memcpy(q, p, restlen);
2314		q += restlen;
2315		memcpy(q, end, endlen);
2316		q += endlen;
2317	}
2318
2319	*q = '\0';
2320	ZSTR_LEN(dest) = q - ZSTR_VAL(dest);
2321
2322	return dest;
2323}
2324/* }}} */
2325
2326/* {{{ proto string chunk_split(string str [, int chunklen [, string ending]])
2327   Returns split line */
2328PHP_FUNCTION(chunk_split)
2329{
2330	zend_string *str;
2331	char *end    = "\r\n";
2332	size_t endlen   = 2;
2333	zend_long chunklen = 76;
2334	zend_string *result;
2335
2336	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|ls", &str, &chunklen, &end, &endlen) == FAILURE) {
2337		return;
2338	}
2339
2340	if (chunklen <= 0) {
2341		php_error_docref(NULL, E_WARNING, "Chunk length should be greater than zero");
2342		RETURN_FALSE;
2343	}
2344
2345	if ((size_t)chunklen > ZSTR_LEN(str)) {
2346		/* to maintain BC, we must return original string + ending */
2347		result = zend_string_safe_alloc(ZSTR_LEN(str), 1, endlen, 0);
2348		memcpy(ZSTR_VAL(result), ZSTR_VAL(str), ZSTR_LEN(str));
2349		memcpy(ZSTR_VAL(result) + ZSTR_LEN(str), end, endlen);
2350		ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
2351		RETURN_NEW_STR(result);
2352	}
2353
2354	if (!ZSTR_LEN(str)) {
2355		RETURN_EMPTY_STRING();
2356	}
2357
2358	result = php_chunk_split(ZSTR_VAL(str), ZSTR_LEN(str), end, endlen, (size_t)chunklen);
2359
2360	if (result) {
2361		RETURN_STR(result);
2362	} else {
2363		RETURN_FALSE;
2364	}
2365}
2366/* }}} */
2367
2368/* {{{ proto string substr(string str, int start [, int length])
2369   Returns part of a string */
2370PHP_FUNCTION(substr)
2371{
2372	zend_string *str;
2373	zend_long l = 0, f;
2374	int argc = ZEND_NUM_ARGS();
2375
2376#ifndef FAST_ZPP
2377	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sl|l", &str, &f, &l) == FAILURE) {
2378		return;
2379	}
2380#else
2381	ZEND_PARSE_PARAMETERS_START(2, 3)
2382		Z_PARAM_STR(str)
2383		Z_PARAM_LONG(f)
2384		Z_PARAM_OPTIONAL
2385		Z_PARAM_LONG(l)
2386	ZEND_PARSE_PARAMETERS_END();
2387#endif
2388
2389	if (argc > 2) {
2390		if ((l < 0 && (size_t)(-l) > ZSTR_LEN(str))) {
2391			RETURN_FALSE;
2392		} else if (l > (zend_long)ZSTR_LEN(str)) {
2393			l = ZSTR_LEN(str);
2394		}
2395	} else {
2396		l = ZSTR_LEN(str);
2397	}
2398
2399	if (f > (zend_long)ZSTR_LEN(str)) {
2400		RETURN_FALSE;
2401	} else if (f < 0 && -f > ZSTR_LEN(str)) {
2402		f = 0;
2403	}
2404
2405	if (l < 0 && (l + (zend_long)ZSTR_LEN(str) - f) < 0) {
2406		RETURN_FALSE;
2407	}
2408
2409	/* if "from" position is negative, count start position from the end
2410	 * of the string
2411	 */
2412	if (f < 0) {
2413		f = (zend_long)ZSTR_LEN(str) + f;
2414		if (f < 0) {
2415			f = 0;
2416		}
2417	}
2418
2419	/* if "length" position is negative, set it to the length
2420	 * needed to stop that many chars from the end of the string
2421	 */
2422	if (l < 0) {
2423		l = ((zend_long)ZSTR_LEN(str) - f) + l;
2424		if (l < 0) {
2425			l = 0;
2426		}
2427	}
2428
2429	if ((f + l) > (zend_long)ZSTR_LEN(str)) {
2430		l = ZSTR_LEN(str) - f;
2431	}
2432
2433	RETURN_STRINGL(ZSTR_VAL(str) + f, l);
2434}
2435/* }}} */
2436
2437/* {{{ proto mixed substr_replace(mixed str, mixed repl, mixed start [, mixed length])
2438   Replaces part of a string with another string */
2439PHP_FUNCTION(substr_replace)
2440{
2441	zval *str;
2442	zval *from;
2443	zval *len = NULL;
2444	zval *repl;
2445	zend_long l = 0;
2446	zend_long f;
2447	int argc = ZEND_NUM_ARGS();
2448	zend_string *result;
2449	HashPosition from_idx, repl_idx, len_idx;
2450	zval *tmp_str = NULL, *tmp_from = NULL, *tmp_repl = NULL, *tmp_len= NULL;
2451
2452	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zzz|z/", &str, &repl, &from, &len) == FAILURE) {
2453		return;
2454	}
2455
2456	if (Z_TYPE_P(str) != IS_ARRAY) {
2457		convert_to_string_ex(str);
2458	}
2459	if (Z_TYPE_P(repl) != IS_ARRAY) {
2460		convert_to_string_ex(repl);
2461	}
2462	if (Z_TYPE_P(from) != IS_ARRAY) {
2463		convert_to_long_ex(from);
2464	}
2465
2466	if (argc > 3) {
2467		if (Z_TYPE_P(len) != IS_ARRAY) {
2468			l = zval_get_long(len);
2469		}
2470	} else {
2471		if (Z_TYPE_P(str) != IS_ARRAY) {
2472			l = Z_STRLEN_P(str);
2473		}
2474	}
2475
2476	if (Z_TYPE_P(str) == IS_STRING) {
2477		if (
2478			(argc == 3 && Z_TYPE_P(from) == IS_ARRAY) ||
2479			(argc == 4 && Z_TYPE_P(from) != Z_TYPE_P(len))
2480		) {
2481			php_error_docref(NULL, E_WARNING, "'from' and 'len' should be of same type - numerical or array ");
2482			RETURN_STR_COPY(Z_STR_P(str));
2483		}
2484		if (argc == 4 && Z_TYPE_P(from) == IS_ARRAY) {
2485			if (zend_hash_num_elements(Z_ARRVAL_P(from)) != zend_hash_num_elements(Z_ARRVAL_P(len))) {
2486				php_error_docref(NULL, E_WARNING, "'from' and 'len' should have the same number of elements");
2487				RETURN_STR_COPY(Z_STR_P(str));
2488			}
2489		}
2490	}
2491
2492	if (Z_TYPE_P(str) != IS_ARRAY) {
2493		if (Z_TYPE_P(from) != IS_ARRAY) {
2494			zend_string *repl_str;
2495			zend_bool repl_release = 0;
2496			f = Z_LVAL_P(from);
2497
2498			/* if "from" position is negative, count start position from the end
2499			 * of the string
2500			 */
2501			if (f < 0) {
2502				f = (zend_long)Z_STRLEN_P(str) + f;
2503				if (f < 0) {
2504					f = 0;
2505				}
2506			} else if (f > Z_STRLEN_P(str)) {
2507				f = Z_STRLEN_P(str);
2508			}
2509			/* if "length" position is negative, set it to the length
2510			 * needed to stop that many chars from the end of the string
2511			 */
2512			if (l < 0) {
2513				l = ((zend_long)Z_STRLEN_P(str) - f) + l;
2514				if (l < 0) {
2515					l = 0;
2516				}
2517			}
2518
2519			if (l > Z_STRLEN_P(str) || (l < 0 && (size_t)(-l) > Z_STRLEN_P(str))) {
2520				l = Z_STRLEN_P(str);
2521			}
2522
2523			if ((f + l) > (zend_long)Z_STRLEN_P(str)) {
2524				l = Z_STRLEN_P(str) - f;
2525			}
2526			if (Z_TYPE_P(repl) == IS_ARRAY) {
2527				repl_idx = 0;
2528				while (repl_idx < Z_ARRVAL_P(repl)->nNumUsed) {
2529					tmp_repl = &Z_ARRVAL_P(repl)->arData[repl_idx].val;
2530					if (Z_TYPE_P(tmp_repl) != IS_UNDEF) {
2531						break;
2532					}
2533					repl_idx++;
2534				}
2535				if (repl_idx < Z_ARRVAL_P(repl)->nNumUsed) {
2536					repl_str = zval_get_string(tmp_repl);
2537					repl_release = 1;
2538				} else {
2539					repl_str = STR_EMPTY_ALLOC();
2540				}
2541			} else {
2542				repl_str = Z_STR_P(repl);
2543			}
2544
2545			result = zend_string_alloc(Z_STRLEN_P(str) - l + ZSTR_LEN(repl_str), 0);
2546
2547			memcpy(ZSTR_VAL(result), Z_STRVAL_P(str), f);
2548			if (ZSTR_LEN(repl_str)) {
2549				memcpy((ZSTR_VAL(result) + f), ZSTR_VAL(repl_str), ZSTR_LEN(repl_str));
2550			}
2551			memcpy((ZSTR_VAL(result) + f + ZSTR_LEN(repl_str)), Z_STRVAL_P(str) + f + l, Z_STRLEN_P(str) - f - l);
2552			ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
2553			if (repl_release) {
2554				zend_string_release(repl_str);
2555			}
2556			RETURN_NEW_STR(result);
2557		} else {
2558			php_error_docref(NULL, E_WARNING, "Functionality of 'from' and 'len' as arrays is not implemented");
2559			RETURN_STR_COPY(Z_STR_P(str));
2560		}
2561	} else { /* str is array of strings */
2562		zend_string *str_index = NULL;
2563		size_t result_len;
2564		zend_ulong num_index;
2565
2566		array_init(return_value);
2567
2568		from_idx = len_idx = repl_idx = 0;
2569
2570		ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(str), num_index, str_index, tmp_str) {
2571			zend_string *orig_str = zval_get_string(tmp_str);
2572
2573			if (Z_TYPE_P(from) == IS_ARRAY) {
2574				while (from_idx < Z_ARRVAL_P(from)->nNumUsed) {
2575					tmp_from = &Z_ARRVAL_P(from)->arData[from_idx].val;
2576					if (Z_TYPE_P(tmp_from) != IS_UNDEF) {
2577						break;
2578					}
2579					from_idx++;
2580				}
2581				if (from_idx < Z_ARRVAL_P(from)->nNumUsed) {
2582					f = zval_get_long(tmp_from);
2583
2584					if (f < 0) {
2585						f = (zend_long)ZSTR_LEN(orig_str) + f;
2586						if (f < 0) {
2587							f = 0;
2588						}
2589					} else if (f > (zend_long)ZSTR_LEN(orig_str)) {
2590						f = ZSTR_LEN(orig_str);
2591					}
2592					from_idx++;
2593				} else {
2594					f = 0;
2595				}
2596			} else {
2597				f = Z_LVAL_P(from);
2598				if (f < 0) {
2599					f = (zend_long)ZSTR_LEN(orig_str) + f;
2600					if (f < 0) {
2601						f = 0;
2602					}
2603				} else if (f > (zend_long)ZSTR_LEN(orig_str)) {
2604					f = ZSTR_LEN(orig_str);
2605				}
2606			}
2607
2608			if (argc > 3 && Z_TYPE_P(len) == IS_ARRAY) {
2609				while (len_idx < Z_ARRVAL_P(len)->nNumUsed) {
2610					tmp_len = &Z_ARRVAL_P(len)->arData[len_idx].val;
2611					if (Z_TYPE_P(tmp_len) != IS_UNDEF) {
2612						break;
2613					}
2614					len_idx++;
2615				}
2616				if (len_idx < Z_ARRVAL_P(len)->nNumUsed) {
2617					l = zval_get_long(tmp_len);
2618					len_idx++;
2619				} else {
2620					l = ZSTR_LEN(orig_str);
2621				}
2622			} else if (argc > 3) {
2623				l = Z_LVAL_P(len);
2624			} else {
2625				l = ZSTR_LEN(orig_str);
2626			}
2627
2628			if (l < 0) {
2629				l = (ZSTR_LEN(orig_str) - f) + l;
2630				if (l < 0) {
2631					l = 0;
2632				}
2633			}
2634
2635			if ((f + l) > (zend_long)ZSTR_LEN(orig_str)) {
2636				l = ZSTR_LEN(orig_str) - f;
2637			}
2638
2639			result_len = ZSTR_LEN(orig_str) - l;
2640
2641			if (Z_TYPE_P(repl) == IS_ARRAY) {
2642				while (repl_idx < Z_ARRVAL_P(repl)->nNumUsed) {
2643					tmp_repl = &Z_ARRVAL_P(repl)->arData[repl_idx].val;
2644					if (Z_TYPE_P(tmp_repl) != IS_UNDEF) {
2645						break;
2646					}
2647					repl_idx++;
2648				}
2649				if (repl_idx < Z_ARRVAL_P(repl)->nNumUsed) {
2650					zend_string *repl_str = zval_get_string(tmp_repl);
2651
2652					result_len += ZSTR_LEN(repl_str);
2653					repl_idx++;
2654					result = zend_string_alloc(result_len, 0);
2655
2656					memcpy(ZSTR_VAL(result), ZSTR_VAL(orig_str), f);
2657					memcpy((ZSTR_VAL(result) + f), ZSTR_VAL(repl_str), ZSTR_LEN(repl_str));
2658					memcpy((ZSTR_VAL(result) + f + ZSTR_LEN(repl_str)), ZSTR_VAL(orig_str) + f + l, ZSTR_LEN(orig_str) - f - l);
2659					zend_string_release(repl_str);
2660				} else {
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(orig_str) + f + l, ZSTR_LEN(orig_str) - f - l);
2665				}
2666			} else {
2667				result_len += Z_STRLEN_P(repl);
2668
2669				result = zend_string_alloc(result_len, 0);
2670
2671				memcpy(ZSTR_VAL(result), ZSTR_VAL(orig_str), f);
2672				memcpy((ZSTR_VAL(result) + f), Z_STRVAL_P(repl), Z_STRLEN_P(repl));
2673				memcpy((ZSTR_VAL(result) + f + Z_STRLEN_P(repl)), ZSTR_VAL(orig_str) + f + l, ZSTR_LEN(orig_str) - f - l);
2674			}
2675
2676			ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
2677
2678			if (str_index) {
2679				zval tmp;
2680
2681				ZVAL_NEW_STR(&tmp, result);
2682				zend_symtable_update(Z_ARRVAL_P(return_value), str_index, &tmp);
2683			} else {
2684				add_index_str(return_value, num_index, result);
2685			}
2686
2687			zend_string_release(orig_str);
2688		} ZEND_HASH_FOREACH_END();
2689	} /* if */
2690}
2691/* }}} */
2692
2693/* {{{ proto string quotemeta(string str)
2694   Quotes meta characters */
2695PHP_FUNCTION(quotemeta)
2696{
2697	zend_string *old;
2698	char *old_end;
2699	char *p, *q;
2700	char c;
2701	zend_string *str;
2702
2703	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &old) == FAILURE) {
2704		return;
2705	}
2706
2707	old_end = ZSTR_VAL(old) + ZSTR_LEN(old);
2708
2709	if (ZSTR_VAL(old) == old_end) {
2710		RETURN_FALSE;
2711	}
2712
2713	str = zend_string_safe_alloc(2, ZSTR_LEN(old), 0, 0);
2714
2715	for (p = ZSTR_VAL(old), q = ZSTR_VAL(str); p != old_end; p++) {
2716		c = *p;
2717		switch (c) {
2718			case '.':
2719			case '\\':
2720			case '+':
2721			case '*':
2722			case '?':
2723			case '[':
2724			case '^':
2725			case ']':
2726			case '$':
2727			case '(':
2728			case ')':
2729				*q++ = '\\';
2730				/* break is missing _intentionally_ */
2731			default:
2732				*q++ = c;
2733		}
2734	}
2735
2736	*q = '\0';
2737
2738	RETURN_NEW_STR(zend_string_truncate(str, q - ZSTR_VAL(str), 0));
2739}
2740/* }}} */
2741
2742/* {{{ proto int ord(string character)
2743   Returns ASCII value of character */
2744PHP_FUNCTION(ord)
2745{
2746	char   *str;
2747	size_t str_len;
2748
2749#ifndef FAST_ZPP
2750	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &str_len) == FAILURE) {
2751		return;
2752	}
2753#else
2754	ZEND_PARSE_PARAMETERS_START(1, 1)
2755		Z_PARAM_STRING(str, str_len)
2756	ZEND_PARSE_PARAMETERS_END();
2757#endif
2758
2759	RETURN_LONG((unsigned char) str[0]);
2760}
2761/* }}} */
2762
2763/* {{{ proto string chr(int ascii)
2764   Converts ASCII code to a character */
2765PHP_FUNCTION(chr)
2766{
2767	zend_long c;
2768
2769	if (ZEND_NUM_ARGS() != 1) {
2770		WRONG_PARAM_COUNT;
2771	}
2772
2773#ifndef FAST_ZPP
2774	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "l", &c) == FAILURE) {
2775		c = 0;
2776	}
2777#else
2778	ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 1, 1)
2779		Z_PARAM_LONG(c)
2780	ZEND_PARSE_PARAMETERS_END_EX(c = 0);
2781#endif
2782
2783	c &= 0xff;
2784	if (CG(one_char_string)[c]) {
2785		ZVAL_INTERNED_STR(return_value, CG(one_char_string)[c]);
2786	} else {
2787		ZVAL_NEW_STR(return_value, zend_string_alloc(1, 0));
2788		Z_STRVAL_P(return_value)[0] = (char)c;
2789		Z_STRVAL_P(return_value)[1] = '\0';
2790	}
2791}
2792/* }}} */
2793
2794/* {{{ php_ucfirst
2795   Uppercase the first character of the word in a native string */
2796static void php_ucfirst(char *str)
2797{
2798	register char *r;
2799	r = str;
2800	*r = toupper((unsigned char) *r);
2801}
2802/* }}} */
2803
2804/* {{{ proto string ucfirst(string str)
2805   Makes a string's first character uppercase */
2806PHP_FUNCTION(ucfirst)
2807{
2808	zend_string *str;
2809
2810#ifndef FAST_ZPP
2811	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) {
2812		return;
2813	}
2814#else
2815	ZEND_PARSE_PARAMETERS_START(1, 1)
2816		Z_PARAM_STR(str)
2817	ZEND_PARSE_PARAMETERS_END();
2818#endif
2819
2820	if (!ZSTR_LEN(str)) {
2821		RETURN_EMPTY_STRING();
2822	}
2823
2824	ZVAL_STRINGL(return_value, ZSTR_VAL(str), ZSTR_LEN(str));
2825	php_ucfirst(Z_STRVAL_P(return_value));
2826}
2827/* }}} */
2828
2829/* {{{
2830   Lowercase the first character of the word in a native string */
2831static void php_lcfirst(char *str)
2832{
2833	register char *r;
2834	r = str;
2835	*r = tolower((unsigned char) *r);
2836}
2837/* }}} */
2838
2839/* {{{ proto string lcfirst(string str)
2840   Make a string's first character lowercase */
2841PHP_FUNCTION(lcfirst)
2842{
2843	zend_string  *str;
2844
2845	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) {
2846		return;
2847	}
2848
2849	if (!ZSTR_LEN(str)) {
2850		RETURN_EMPTY_STRING();
2851	}
2852
2853	ZVAL_STRINGL(return_value, ZSTR_VAL(str), ZSTR_LEN(str));
2854	php_lcfirst(Z_STRVAL_P(return_value));
2855}
2856/* }}} */
2857
2858/* {{{ proto string ucwords(string str [, string delims])
2859   Uppercase the first character of every word in a string */
2860PHP_FUNCTION(ucwords)
2861{
2862	zend_string *str;
2863	char *delims = " \t\r\n\f\v";
2864	register char *r, *r_end;
2865	size_t delims_len = 6;
2866	char mask[256];
2867
2868#ifndef FAST_ZPP
2869	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|s", &str, &delims, &delims_len) == FAILURE) {
2870		return;
2871	}
2872#else
2873	ZEND_PARSE_PARAMETERS_START(1, 2)
2874		Z_PARAM_STR(str)
2875		Z_PARAM_OPTIONAL
2876		Z_PARAM_STRING(delims, delims_len)
2877	ZEND_PARSE_PARAMETERS_END();
2878#endif
2879
2880	if (!ZSTR_LEN(str)) {
2881		RETURN_EMPTY_STRING();
2882	}
2883
2884	php_charmask((unsigned char *)delims, delims_len, mask);
2885
2886	ZVAL_STRINGL(return_value, ZSTR_VAL(str), ZSTR_LEN(str));
2887	r = Z_STRVAL_P(return_value);
2888
2889	*r = toupper((unsigned char) *r);
2890	for (r_end = r + Z_STRLEN_P(return_value) - 1; r < r_end; ) {
2891		if (mask[(unsigned char)*r++]) {
2892			*r = toupper((unsigned char) *r);
2893		}
2894	}
2895}
2896/* }}} */
2897
2898/* {{{ php_strtr
2899 */
2900PHPAPI char *php_strtr(char *str, size_t len, char *str_from, char *str_to, size_t trlen)
2901{
2902	size_t i;
2903
2904	if (UNEXPECTED(trlen < 1)) {
2905		return str;
2906	} else if (trlen == 1) {
2907		char ch_from = *str_from;
2908		char ch_to = *str_to;
2909
2910		for (i = 0; i < len; i++) {
2911			if (str[i] == ch_from) {
2912				str[i] = ch_to;
2913			}
2914		}
2915	} else {
2916		unsigned char xlat[256], j = 0;
2917
2918		do { xlat[j] = j; } while (++j != 0);
2919
2920		for (i = 0; i < trlen; i++) {
2921			xlat[(size_t)(unsigned char) str_from[i]] = str_to[i];
2922		}
2923
2924		for (i = 0; i < len; i++) {
2925			str[i] = xlat[(size_t)(unsigned char) str[i]];
2926		}
2927	}
2928
2929	return str;
2930}
2931/* }}} */
2932
2933/* {{{ php_strtr_ex
2934 */
2935static zend_string *php_strtr_ex(zend_string *str, char *str_from, char *str_to, size_t trlen)
2936{
2937	zend_string *new_str = NULL;
2938	size_t i;
2939
2940	if (UNEXPECTED(trlen < 1)) {
2941		return zend_string_copy(str);
2942	} else if (trlen == 1) {
2943		char ch_from = *str_from;
2944		char ch_to = *str_to;
2945
2946		for (i = 0; i < ZSTR_LEN(str); i++) {
2947			if (ZSTR_VAL(str)[i] == ch_from) {
2948				new_str = zend_string_alloc(ZSTR_LEN(str), 0);
2949				memcpy(ZSTR_VAL(new_str), ZSTR_VAL(str), i);
2950				ZSTR_VAL(new_str)[i] = ch_to;
2951				break;
2952			}
2953		}
2954		for (; i < ZSTR_LEN(str); i++) {
2955			ZSTR_VAL(new_str)[i] = (ZSTR_VAL(str)[i] != ch_from) ? ZSTR_VAL(str)[i] : ch_to;
2956		}
2957	} else {
2958		unsigned char xlat[256], j = 0;
2959
2960		do { xlat[j] = j; } while (++j != 0);
2961
2962		for (i = 0; i < trlen; i++) {
2963			xlat[(size_t)(unsigned char) str_from[i]] = str_to[i];
2964		}
2965
2966		for (i = 0; i < ZSTR_LEN(str); i++) {
2967			if (ZSTR_VAL(str)[i] != xlat[(size_t)(unsigned char) ZSTR_VAL(str)[i]]) {
2968				new_str = zend_string_alloc(ZSTR_LEN(str), 0);
2969				memcpy(ZSTR_VAL(new_str), ZSTR_VAL(str), i);
2970				ZSTR_VAL(new_str)[i] = xlat[(size_t)(unsigned char) ZSTR_VAL(str)[i]];
2971				break;
2972			}
2973		}
2974
2975		for (;i < ZSTR_LEN(str); i++) {
2976			ZSTR_VAL(new_str)[i] = xlat[(size_t)(unsigned char) ZSTR_VAL(str)[i]];
2977		}
2978	}
2979
2980	if (!new_str) {
2981		return zend_string_copy(str);
2982	}
2983
2984	ZSTR_VAL(new_str)[ZSTR_LEN(new_str)] = 0;
2985	return new_str;
2986}
2987/* }}} */
2988
2989/* {{{ php_strtr_array */
2990static void php_strtr_array(zval *return_value, zend_string *input, HashTable *pats)
2991{
2992	char *str = ZSTR_VAL(input);
2993	size_t slen = ZSTR_LEN(input);
2994	zend_ulong num_key;
2995	zend_string *str_key;
2996	size_t len, pos, old_pos;
2997	int num_keys = 0;
2998	size_t minlen = 128*1024;
2999	size_t maxlen = 0;
3000	HashTable str_hash;
3001	zval *entry;
3002	char *key;
3003	smart_str result = {0};
3004	zend_ulong bitset[256/sizeof(zend_ulong)];
3005	zend_ulong *num_bitset;
3006
3007	/* we will collect all possible key lengths */
3008	num_bitset = ecalloc((slen + sizeof(zend_ulong)) / sizeof(zend_ulong), sizeof(zend_ulong));
3009	memset(bitset, 0, sizeof(bitset));
3010
3011	/* check if original array has numeric keys */
3012	ZEND_HASH_FOREACH_STR_KEY(pats, str_key) {
3013		if (UNEXPECTED(!str_key)) {
3014			num_keys = 1;
3015		} else {
3016			len = ZSTR_LEN(str_key);
3017			if (UNEXPECTED(len < 1)) {
3018				RETURN_FALSE;
3019			} else if (UNEXPECTED(len > slen)) {
3020				/* skip long patterns */
3021				continue;
3022			}
3023			if (len > maxlen) {
3024				maxlen = len;
3025			}
3026			if (len < minlen) {
3027				minlen = len;
3028			}
3029			/* remember possible key length */
3030			num_bitset[len / sizeof(zend_ulong)] |= Z_UL(1) << (len % sizeof(zend_ulong));
3031			bitset[((unsigned char)ZSTR_VAL(str_key)[0]) / sizeof(zend_ulong)] |= Z_UL(1) << (((unsigned char)ZSTR_VAL(str_key)[0]) % sizeof(zend_ulong));
3032		}
3033	} ZEND_HASH_FOREACH_END();
3034
3035	if (UNEXPECTED(num_keys)) {
3036		zend_string *key_used;
3037		/* we have to rebuild HashTable with numeric keys */
3038		zend_hash_init(&str_hash, zend_hash_num_elements(pats), NULL, NULL, 0);
3039		ZEND_HASH_FOREACH_KEY_VAL(pats, num_key, str_key, entry) {
3040			if (UNEXPECTED(!str_key)) {
3041				key_used = zend_long_to_str(num_key);
3042				len = ZSTR_LEN(key_used);
3043				if (UNEXPECTED(len > slen)) {
3044					/* skip long patterns */
3045					continue;
3046				}
3047				if (len > maxlen) {
3048					maxlen = len;
3049				}
3050				if (len < minlen) {
3051					minlen = len;
3052				}
3053				/* remember possible key length */
3054				num_bitset[len / sizeof(zend_ulong)] |= Z_UL(1) << (len % sizeof(zend_ulong));
3055				bitset[((unsigned char)ZSTR_VAL(key_used)[0]) / sizeof(zend_ulong)] |= Z_UL(1) << (((unsigned char)ZSTR_VAL(key_used)[0]) % sizeof(zend_ulong));
3056			} else {
3057				key_used = str_key;
3058				len = ZSTR_LEN(key_used);
3059				if (UNEXPECTED(len > slen)) {
3060					/* skip long patterns */
3061					continue;
3062				}
3063			}
3064			zend_hash_add(&str_hash, key_used, entry);
3065			if (UNEXPECTED(!str_key)) {
3066				zend_string_release(key_used);
3067			}
3068		} ZEND_HASH_FOREACH_END();
3069		pats = &str_hash;
3070	}
3071
3072	if (UNEXPECTED(minlen > maxlen)) {
3073		/* return the original string */
3074		if (pats == &str_hash) {
3075			zend_hash_destroy(&str_hash);
3076		}
3077		efree(num_bitset);
3078		RETURN_STR_COPY(input);
3079	}
3080
3081	old_pos = pos = 0;
3082	while (pos <= slen - minlen) {
3083		key = str + pos;
3084		if (bitset[((unsigned char)key[0]) / sizeof(zend_ulong)] & (Z_UL(1) << (((unsigned char)key[0]) % sizeof(zend_ulong)))) {
3085			len = maxlen;
3086			if (len > slen - pos) {
3087				len = slen - pos;
3088			}
3089			while (len >= minlen) {
3090				if ((num_bitset[len / sizeof(zend_ulong)] & (Z_UL(1) << (len % sizeof(zend_ulong))))) {
3091					entry = zend_hash_str_find(pats, key, len);
3092					if (entry != NULL) {
3093						zend_string *s = zval_get_string(entry);
3094						smart_str_appendl(&result, str + old_pos, pos - old_pos);
3095						smart_str_append(&result, s);
3096						old_pos = pos + len;
3097						pos = old_pos - 1;
3098						zend_string_release(s);
3099						break;
3100					}
3101				}
3102				len--;
3103			}
3104		}
3105		pos++;
3106	}
3107
3108	if (result.s) {
3109		smart_str_appendl(&result, str + old_pos, slen - old_pos);
3110		smart_str_0(&result);
3111		RETVAL_NEW_STR(result.s);
3112	} else {
3113		smart_str_free(&result);
3114		RETVAL_STR_COPY(input);
3115	}
3116
3117	if (pats == &str_hash) {
3118		zend_hash_destroy(&str_hash);
3119	}
3120	efree(num_bitset);
3121}
3122/* }}} */
3123
3124/* {{{ php_char_to_str_ex
3125 */
3126static 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)
3127{
3128	zend_string *result;
3129	size_t char_count = 0;
3130	char lc_from = 0;
3131	char *source, *target, *source_end= ZSTR_VAL(str) + ZSTR_LEN(str);
3132
3133	if (case_sensitivity) {
3134		char *p = ZSTR_VAL(str), *e = p + ZSTR_LEN(str);
3135		while ((p = memchr(p, from, (e - p)))) {
3136			char_count++;
3137			p++;
3138		}
3139	} else {
3140		lc_from = tolower(from);
3141		for (source = ZSTR_VAL(str); source < source_end; source++) {
3142			if (tolower(*source) == lc_from) {
3143				char_count++;
3144			}
3145		}
3146	}
3147
3148	if (char_count == 0) {
3149		return zend_string_copy(str);
3150	}
3151
3152	if (to_len > 0) {
3153		result = zend_string_safe_alloc(char_count, to_len - 1, ZSTR_LEN(str), 0);
3154	} else {
3155		result = zend_string_alloc(ZSTR_LEN(str) - char_count, 0);
3156	}
3157	target = ZSTR_VAL(result);
3158
3159	if (case_sensitivity) {
3160		char *p = ZSTR_VAL(str), *e = p + ZSTR_LEN(str), *s = ZSTR_VAL(str);
3161		while ((p = memchr(p, from, (e - p)))) {
3162			memcpy(target, s, (p - s));
3163			target += p - s;
3164			memcpy(target, to, to_len);
3165			target += to_len;
3166			p++;
3167			s = p;
3168			if (replace_count) {
3169				*replace_count += 1;
3170			}
3171		}
3172		if (s < e) {
3173			memcpy(target, s, (e - s));
3174			target += e - s;
3175		}
3176	} else {
3177		for (source = ZSTR_VAL(str); source < source_end; source++) {
3178			if (tolower(*source) == lc_from) {
3179				if (replace_count) {
3180					*replace_count += 1;
3181				}
3182				memcpy(target, to, to_len);
3183				target += to_len;
3184			} else {
3185				*target = *source;
3186				target++;
3187			}
3188		}
3189	}
3190	*target = 0;
3191	return result;
3192}
3193/* }}} */
3194
3195/* {{{ php_str_to_str_ex
3196 */
3197static zend_string *php_str_to_str_ex(zend_string *haystack,
3198	char *needle, size_t needle_len, char *str, size_t str_len, zend_long *replace_count)
3199{
3200	zend_string *new_str;
3201
3202	if (needle_len < ZSTR_LEN(haystack)) {
3203		char *end;
3204		char *e, *s, *p, *r;
3205
3206		if (needle_len == str_len) {
3207			new_str = NULL;
3208			end = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
3209			for (p = ZSTR_VAL(haystack); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3210				if (!new_str) {
3211					new_str = zend_string_init(ZSTR_VAL(haystack), ZSTR_LEN(haystack), 0);
3212				}
3213				memcpy(ZSTR_VAL(new_str) + (r - ZSTR_VAL(haystack)), str, str_len);
3214				(*replace_count)++;
3215			}
3216			if (!new_str) {
3217				goto nothing_todo;
3218			}
3219			return new_str;
3220		} else {
3221			size_t count = 0;
3222			char *o = ZSTR_VAL(haystack);
3223			char *n = needle;
3224			char *endp = o + ZSTR_LEN(haystack);
3225
3226			while ((o = (char*)php_memnstr(o, n, needle_len, endp))) {
3227				o += needle_len;
3228				count++;
3229			}
3230			if (count == 0) {
3231				/* Needle doesn't occur, shortcircuit the actual replacement. */
3232				goto nothing_todo;
3233			}
3234			if (str_len > needle_len) {
3235				new_str = zend_string_safe_alloc(count, str_len - needle_len, ZSTR_LEN(haystack), 0);
3236			} else {
3237				new_str = zend_string_alloc(count * (str_len - needle_len) + ZSTR_LEN(haystack), 0);
3238			}
3239
3240			e = s = ZSTR_VAL(new_str);
3241			end = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
3242			for (p = ZSTR_VAL(haystack); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3243				memcpy(e, p, r - p);
3244				e += r - p;
3245				memcpy(e, str, str_len);
3246				e += str_len;
3247				(*replace_count)++;
3248			}
3249
3250			if (p < end) {
3251				memcpy(e, p, end - p);
3252				e += end - p;
3253			}
3254
3255			*e = '\0';
3256			return new_str;
3257		}
3258	} else if (needle_len > ZSTR_LEN(haystack) || memcmp(ZSTR_VAL(haystack), needle, ZSTR_LEN(haystack))) {
3259nothing_todo:
3260		return zend_string_copy(haystack);
3261	} else {
3262		new_str = zend_string_init(str, str_len, 0);
3263		(*replace_count)++;
3264		return new_str;
3265	}
3266}
3267/* }}} */
3268
3269/* {{{ php_str_to_str_i_ex
3270 */
3271static zend_string *php_str_to_str_i_ex(zend_string *haystack, char *lc_haystack,
3272	zend_string *needle, char *str, size_t str_len, zend_long *replace_count)
3273{
3274	zend_string *new_str = NULL;
3275	zend_string *lc_needle;
3276
3277	if (ZSTR_LEN(needle) < ZSTR_LEN(haystack)) {
3278		char *end;
3279		char *e, *s, *p, *r;
3280
3281		if (ZSTR_LEN(needle) == str_len) {
3282			lc_needle = php_string_tolower(needle);
3283			end = lc_haystack + ZSTR_LEN(haystack);
3284			for (p = lc_haystack; (r = (char*)php_memnstr(p, ZSTR_VAL(lc_needle), ZSTR_LEN(lc_needle), end)); p = r + ZSTR_LEN(lc_needle)) {
3285				if (!new_str) {
3286					new_str = zend_string_init(ZSTR_VAL(haystack), ZSTR_LEN(haystack), 0);
3287				}
3288				memcpy(ZSTR_VAL(new_str) + (r - lc_haystack), str, str_len);
3289				(*replace_count)++;
3290			}
3291			zend_string_release(lc_needle);
3292
3293			if (!new_str) {
3294				goto nothing_todo;
3295			}
3296			return new_str;
3297		} else {
3298			size_t count = 0;
3299			char *o = lc_haystack;
3300			char *n;
3301			char *endp = o + ZSTR_LEN(haystack);
3302
3303			lc_needle = php_string_tolower(needle);
3304			n = ZSTR_VAL(lc_needle);
3305
3306			while ((o = (char*)php_memnstr(o, n, ZSTR_LEN(lc_needle), endp))) {
3307				o += ZSTR_LEN(lc_needle);
3308				count++;
3309			}
3310			if (count == 0) {
3311				/* Needle doesn't occur, shortcircuit the actual replacement. */
3312				zend_string_release(lc_needle);
3313				goto nothing_todo;
3314			}
3315
3316			if (str_len > ZSTR_LEN(lc_needle)) {
3317				new_str = zend_string_safe_alloc(count, str_len - ZSTR_LEN(lc_needle), ZSTR_LEN(haystack), 0);
3318			} else {
3319				new_str = zend_string_alloc(count * (str_len - ZSTR_LEN(lc_needle)) + ZSTR_LEN(haystack), 0);
3320			}
3321
3322			e = s = ZSTR_VAL(new_str);
3323			end = lc_haystack + ZSTR_LEN(haystack);
3324
3325			for (p = lc_haystack; (r = (char*)php_memnstr(p, ZSTR_VAL(lc_needle), ZSTR_LEN(lc_needle), end)); p = r + ZSTR_LEN(lc_needle)) {
3326				memcpy(e, ZSTR_VAL(haystack) + (p - lc_haystack), r - p);
3327				e += r - p;
3328				memcpy(e, str, str_len);
3329				e += str_len;
3330				(*replace_count)++;
3331			}
3332
3333			if (p < end) {
3334				memcpy(e, ZSTR_VAL(haystack) + (p - lc_haystack), end - p);
3335				e += end - p;
3336			}
3337			*e = '\0';
3338
3339			zend_string_release(lc_needle);
3340
3341			return new_str;
3342		}
3343	} else if (ZSTR_LEN(needle) > ZSTR_LEN(haystack)) {
3344nothing_todo:
3345		return zend_string_copy(haystack);
3346	} else {
3347		lc_needle = php_string_tolower(needle);
3348
3349		if (memcmp(lc_haystack, ZSTR_VAL(lc_needle), ZSTR_LEN(lc_needle))) {
3350			zend_string_release(lc_needle);
3351			goto nothing_todo;
3352		}
3353		zend_string_release(lc_needle);
3354
3355		new_str = zend_string_init(str, str_len, 0);
3356
3357		(*replace_count)++;
3358		return new_str;
3359	}
3360}
3361/* }}} */
3362
3363/* {{{ php_str_to_str
3364 */
3365PHPAPI zend_string *php_str_to_str(char *haystack, size_t length, char *needle, size_t needle_len, char *str, size_t str_len)
3366{
3367	zend_string *new_str;
3368
3369	if (needle_len < length) {
3370		char *end;
3371		char *e, *s, *p, *r;
3372
3373		if (needle_len == str_len) {
3374			new_str = zend_string_init(haystack, length, 0);
3375			end = ZSTR_VAL(new_str) + length;
3376			for (p = ZSTR_VAL(new_str); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3377				memcpy(r, str, str_len);
3378			}
3379			return new_str;
3380		} else {
3381			if (str_len < needle_len) {
3382				new_str = zend_string_alloc(length, 0);
3383			} else {
3384				size_t count = 0;
3385				char *o = haystack;
3386				char *n = needle;
3387				char *endp = o + length;
3388
3389				while ((o = (char*)php_memnstr(o, n, needle_len, endp))) {
3390					o += needle_len;
3391					count++;
3392				}
3393				if (count == 0) {
3394					/* Needle doesn't occur, shortcircuit the actual replacement. */
3395					new_str = zend_string_init(haystack, length, 0);
3396					return new_str;
3397				} else {
3398					if (str_len > needle_len) {
3399						new_str = zend_string_safe_alloc(count, str_len - needle_len, length, 0);
3400					} else {
3401						new_str = zend_string_alloc(count * (str_len - needle_len) + length, 0);
3402					}
3403				}
3404			}
3405
3406			e = s = ZSTR_VAL(new_str);
3407			end = haystack + length;
3408			for (p = haystack; (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3409				memcpy(e, p, r - p);
3410				e += r - p;
3411				memcpy(e, str, str_len);
3412				e += str_len;
3413			}
3414
3415			if (p < end) {
3416				memcpy(e, p, end - p);
3417				e += end - p;
3418			}
3419
3420			*e = '\0';
3421			new_str = zend_string_truncate(new_str, e - s, 0);
3422			return new_str;
3423		}
3424	} else if (needle_len > length || memcmp(haystack, needle, length)) {
3425		new_str = zend_string_init(haystack, length, 0);
3426		return new_str;
3427	} else {
3428		new_str = zend_string_init(str, str_len, 0);
3429
3430		return new_str;
3431	}
3432}
3433/* }}} */
3434
3435/* {{{ proto string strtr(string str, string from[, string to])
3436   Translates characters in str using given translation tables */
3437PHP_FUNCTION(strtr)
3438{
3439	zval *from;
3440	zend_string *str;
3441	char *to = NULL;
3442	size_t to_len = 0;
3443	int ac = ZEND_NUM_ARGS();
3444
3445#ifndef FAST_ZPP
3446	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz|s", &str, &from, &to, &to_len) == FAILURE) {
3447		return;
3448	}
3449#else
3450	ZEND_PARSE_PARAMETERS_START(2, 3)
3451		Z_PARAM_STR(str)
3452		Z_PARAM_ZVAL(from)
3453		Z_PARAM_OPTIONAL
3454		Z_PARAM_STRING(to, to_len)
3455	ZEND_PARSE_PARAMETERS_END();
3456#endif
3457
3458	if (ac == 2 && Z_TYPE_P(from) != IS_ARRAY) {
3459		php_error_docref(NULL, E_WARNING, "The second argument is not an array");
3460		RETURN_FALSE;
3461	}
3462
3463	/* shortcut for empty string */
3464	if (ZSTR_LEN(str) == 0) {
3465		RETURN_EMPTY_STRING();
3466	}
3467
3468	if (ac == 2) {
3469		HashTable *pats = Z_ARRVAL_P(from);
3470
3471		if (zend_hash_num_elements(pats) < 1) {
3472			RETURN_STR_COPY(str);
3473		} else if (zend_hash_num_elements(pats) == 1) {
3474			zend_long num_key;
3475			zend_string *str_key, *replace;
3476			zval *entry, tmp;
3477
3478			ZEND_HASH_FOREACH_KEY_VAL(pats, num_key, str_key, entry) {
3479				ZVAL_UNDEF(&tmp);
3480				if (UNEXPECTED(!str_key)) {
3481					ZVAL_LONG(&tmp, num_key);
3482					convert_to_string(&tmp);
3483					str_key = Z_STR(tmp);
3484				}
3485				replace = zval_get_string(entry);
3486				if (ZSTR_LEN(str_key) < 1) {
3487					RETVAL_STR_COPY(str);
3488				} else if (ZSTR_LEN(str_key) == 1) {
3489					RETVAL_STR(php_char_to_str_ex(str,
3490								ZSTR_VAL(str_key)[0],
3491								ZSTR_VAL(replace),
3492								ZSTR_LEN(replace),
3493								1,
3494								NULL));
3495				} else {
3496					zend_long dummy;
3497					RETVAL_STR(php_str_to_str_ex(str,
3498								ZSTR_VAL(str_key), ZSTR_LEN(str_key),
3499								ZSTR_VAL(replace), ZSTR_LEN(replace), &dummy));
3500				}
3501				zend_string_release(replace);
3502				zval_dtor(&tmp);
3503				return;
3504			} ZEND_HASH_FOREACH_END();
3505		} else {
3506			php_strtr_array(return_value, str, pats);
3507		}
3508	} else {
3509		convert_to_string_ex(from);
3510
3511		RETURN_STR(php_strtr_ex(str,
3512				  Z_STRVAL_P(from),
3513				  to,
3514				  MIN(Z_STRLEN_P(from), to_len)));
3515	}
3516}
3517/* }}} */
3518
3519/* {{{ proto string strrev(string str)
3520   Reverse a string */
3521PHP_FUNCTION(strrev)
3522{
3523	zend_string *str;
3524	char *e, *p;
3525	zend_string *n;
3526
3527	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) {
3528		return;
3529	}
3530
3531	n = zend_string_alloc(ZSTR_LEN(str), 0);
3532	p = ZSTR_VAL(n);
3533
3534	e = ZSTR_VAL(str) + ZSTR_LEN(str);
3535
3536	while (--e >= ZSTR_VAL(str)) {
3537		*p++ = *e;
3538	}
3539
3540	*p = '\0';
3541
3542	RETVAL_NEW_STR(n);
3543}
3544/* }}} */
3545
3546/* {{{ php_similar_str
3547 */
3548static 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)
3549{
3550	char *p, *q;
3551	char *end1 = (char *) txt1 + len1;
3552	char *end2 = (char *) txt2 + len2;
3553	size_t l;
3554
3555	*max = 0;
3556	for (p = (char *) txt1; p < end1; p++) {
3557		for (q = (char *) txt2; q < end2; q++) {
3558			for (l = 0; (p + l < end1) && (q + l < end2) && (p[l] == q[l]); l++);
3559			if (l > *max) {
3560				*max = l;
3561				*pos1 = p - txt1;
3562				*pos2 = q - txt2;
3563			}
3564		}
3565	}
3566}
3567/* }}} */
3568
3569/* {{{ php_similar_char
3570 */
3571static size_t php_similar_char(const char *txt1, size_t len1, const char *txt2, size_t len2)
3572{
3573	size_t sum;
3574	size_t pos1 = 0, pos2 = 0, max;
3575
3576	php_similar_str(txt1, len1, txt2, len2, &pos1, &pos2, &max);
3577	if ((sum = max)) {
3578		if (pos1 && pos2) {
3579			sum += php_similar_char(txt1, pos1,
3580									txt2, pos2);
3581		}
3582		if ((pos1 + max < len1) && (pos2 + max < len2)) {
3583			sum += php_similar_char(txt1 + pos1 + max, len1 - pos1 - max,
3584									txt2 + pos2 + max, len2 - pos2 - max);
3585		}
3586	}
3587
3588	return sum;
3589}
3590/* }}} */
3591
3592/* {{{ proto int similar_text(string str1, string str2 [, float percent])
3593   Calculates the similarity between two strings */
3594PHP_FUNCTION(similar_text)
3595{
3596	zend_string *t1, *t2;
3597	zval *percent = NULL;
3598	int ac = ZEND_NUM_ARGS();
3599	size_t sim;
3600
3601	if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|z/", &t1, &t2, &percent) == FAILURE) {
3602		return;
3603	}
3604
3605	if (ac > 2) {
3606		convert_to_double_ex(percent);
3607	}
3608
3609	if (ZSTR_LEN(t1) + ZSTR_LEN(t2) == 0) {
3610		if (ac > 2) {
3611			Z_DVAL_P(percent) = 0;
3612		}
3613
3614		RETURN_LONG(0);
3615	}
3616
3617	sim = php_similar_char(ZSTR_VAL(t1), ZSTR_LEN(t1), ZSTR_VAL(t2), ZSTR_LEN(t2));
3618
3619	if (ac > 2) {
3620		Z_DVAL_P(percent) = sim * 200.0 / (ZSTR_LEN(t1) + ZSTR_LEN(t2));
3621	}
3622
3623	RETURN_LONG(sim);
3624}
3625/* }}} */
3626
3627/* {{{ php_stripslashes
3628 *
3629 * be careful, this edits the string in-place */
3630PHPAPI void php_stripslashes(zend_string *str)
3631{
3632	char *s, *t;
3633	size_t l;
3634
3635	s = ZSTR_VAL(str);
3636	t = ZSTR_VAL(str);
3637	l = ZSTR_LEN(str);
3638
3639	while (l > 0) {
3640		if (*t == '\\') {
3641			t++;				/* skip the slash */
3642			ZSTR_LEN(str)--;
3643			l--;
3644			if (l > 0) {
3645				if (*t == '0') {
3646					*s++='\0';
3647					t++;
3648				} else {
3649					*s++ = *t++;	/* preserve the next character */
3650				}
3651				l--;
3652			}
3653		} else {
3654			*s++ = *t++;
3655			l--;
3656		}
3657	}
3658	if (s != t) {
3659		*s = '\0';
3660	}
3661}
3662/* }}} */
3663
3664/* {{{ proto string addcslashes(string str, string charlist)
3665   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...) */
3666PHP_FUNCTION(addcslashes)
3667{
3668	zend_string *str, *what;
3669
3670	if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &str, &what) == FAILURE) {
3671		return;
3672	}
3673
3674	if (ZSTR_LEN(str) == 0) {
3675		RETURN_EMPTY_STRING();
3676	}
3677
3678	if (ZSTR_LEN(what) == 0) {
3679		RETURN_STRINGL(ZSTR_VAL(str), ZSTR_LEN(str));
3680	}
3681
3682	RETURN_STR(php_addcslashes(str, 0, ZSTR_VAL(what), ZSTR_LEN(what)));
3683}
3684/* }}} */
3685
3686/* {{{ proto string addslashes(string str)
3687   Escapes single quote, double quotes and backslash characters in a string with backslashes */
3688PHP_FUNCTION(addslashes)
3689{
3690	zend_string *str;
3691
3692#ifndef FAST_ZPP
3693	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) {
3694		return;
3695	}
3696#else
3697	ZEND_PARSE_PARAMETERS_START(1, 1)
3698		Z_PARAM_STR(str)
3699	ZEND_PARSE_PARAMETERS_END();
3700#endif
3701
3702	if (ZSTR_LEN(str) == 0) {
3703		RETURN_EMPTY_STRING();
3704	}
3705
3706	RETURN_STR(php_addslashes(str, 0));
3707}
3708/* }}} */
3709
3710/* {{{ proto string stripcslashes(string str)
3711   Strips backslashes from a string. Uses C-style conventions */
3712PHP_FUNCTION(stripcslashes)
3713{
3714	zend_string *str;
3715
3716	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) {
3717		return;
3718	}
3719
3720	ZVAL_STRINGL(return_value, ZSTR_VAL(str), ZSTR_LEN(str));
3721	php_stripcslashes(Z_STR_P(return_value));
3722}
3723/* }}} */
3724
3725/* {{{ proto string stripslashes(string str)
3726   Strips backslashes from a string */
3727PHP_FUNCTION(stripslashes)
3728{
3729	zend_string *str;
3730
3731	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) {
3732		return;
3733	}
3734
3735	ZVAL_STRINGL(return_value, ZSTR_VAL(str), ZSTR_LEN(str));
3736	php_stripslashes(Z_STR_P(return_value));
3737}
3738/* }}} */
3739
3740#ifndef HAVE_STRERROR
3741/* {{{ php_strerror
3742 */
3743char *php_strerror(int errnum)
3744{
3745	extern int sys_nerr;
3746	extern char *sys_errlist[];
3747
3748	if ((unsigned int) errnum < sys_nerr) {
3749		return(sys_errlist[errnum]);
3750	}
3751
3752	(void) snprintf(BG(str_ebuf), sizeof(php_basic_globals.str_ebuf), "Unknown error: %d", errnum);
3753	return(BG(str_ebuf));
3754}
3755/* }}} */
3756#endif
3757
3758/* {{{ php_stripcslashes
3759 */
3760PHPAPI void php_stripcslashes(zend_string *str)
3761{
3762	char *source, *target, *end;
3763	size_t  nlen = ZSTR_LEN(str), i;
3764	char numtmp[4];
3765
3766	for (source = (char*)ZSTR_VAL(str), end = source + ZSTR_LEN(str), target = ZSTR_VAL(str); source < end; source++) {
3767		if (*source == '\\' && source + 1 < end) {
3768			source++;
3769			switch (*source) {
3770				case 'n':  *target++='\n'; nlen--; break;
3771				case 'r':  *target++='\r'; nlen--; break;
3772				case 'a':  *target++='\a'; nlen--; break;
3773				case 't':  *target++='\t'; nlen--; break;
3774				case 'v':  *target++='\v'; nlen--; break;
3775				case 'b':  *target++='\b'; nlen--; break;
3776				case 'f':  *target++='\f'; nlen--; break;
3777				case '\\': *target++='\\'; nlen--; break;
3778				case 'x':
3779					if (source+1 < end && isxdigit((int)(*(source+1)))) {
3780						numtmp[0] = *++source;
3781						if (source+1 < end && isxdigit((int)(*(source+1)))) {
3782							numtmp[1] = *++source;
3783							numtmp[2] = '\0';
3784							nlen-=3;
3785						} else {
3786							numtmp[1] = '\0';
3787							nlen-=2;
3788						}
3789						*target++=(char)strtol(numtmp, NULL, 16);
3790						break;
3791					}
3792					/* break is left intentionally */
3793				default:
3794					i=0;
3795					while (source < end && *source >= '0' && *source <= '7' && i<3) {
3796						numtmp[i++] = *source++;
3797					}
3798					if (i) {
3799						numtmp[i]='\0';
3800						*target++=(char)strtol(numtmp, NULL, 8);
3801						nlen-=i;
3802						source--;
3803					} else {
3804						*target++=*source;
3805						nlen--;
3806					}
3807			}
3808		} else {
3809			*target++=*source;
3810		}
3811	}
3812
3813	if (nlen != 0) {
3814		*target='\0';
3815	}
3816
3817	ZSTR_LEN(str) = nlen;
3818}
3819/* }}} */
3820
3821/* {{{ php_addcslashes
3822 */
3823PHPAPI zend_string *php_addcslashes(zend_string *str, int should_free, char *what, size_t wlength)
3824{
3825	char flags[256];
3826	char *source, *target;
3827	char *end;
3828	char c;
3829	size_t  newlen;
3830	zend_string *new_str = zend_string_safe_alloc(4, ZSTR_LEN(str), 0, 0);
3831
3832	php_charmask((unsigned char *)what, wlength, flags);
3833
3834	for (source = (char*)ZSTR_VAL(str), end = source + ZSTR_LEN(str), target = ZSTR_VAL(new_str); source < end; source++) {
3835		c = *source;
3836		if (flags[(unsigned char)c]) {
3837			if ((unsigned char) c < 32 || (unsigned char) c > 126) {
3838				*target++ = '\\';
3839				switch (c) {
3840					case '\n': *target++ = 'n'; break;
3841					case '\t': *target++ = 't'; break;
3842					case '\r': *target++ = 'r'; break;
3843					case '\a': *target++ = 'a'; break;
3844					case '\v': *target++ = 'v'; break;
3845					case '\b': *target++ = 'b'; break;
3846					case '\f': *target++ = 'f'; break;
3847					default: target += sprintf(target, "%03o", (unsigned char) c);
3848				}
3849				continue;
3850			}
3851			*target++ = '\\';
3852		}
3853		*target++ = c;
3854	}
3855	*target = 0;
3856	newlen = target - ZSTR_VAL(new_str);
3857	if (newlen < ZSTR_LEN(str) * 4) {
3858		new_str = zend_string_truncate(new_str, newlen, 0);
3859	}
3860	if (should_free) {
3861		zend_string_release(str);
3862	}
3863	return new_str;
3864}
3865/* }}} */
3866
3867/* {{{ php_addslashes
3868 */
3869PHPAPI zend_string *php_addslashes(zend_string *str, int should_free)
3870{
3871	/* maximum string length, worst case situation */
3872	char *source, *target;
3873	char *end;
3874	size_t offset;
3875	zend_string *new_str;
3876
3877	if (!str) {
3878		return ZSTR_EMPTY_ALLOC();
3879	}
3880
3881	source = ZSTR_VAL(str);
3882	end = source + ZSTR_LEN(str);
3883
3884	while (source < end) {
3885		switch (*source) {
3886			case '\0':
3887			case '\'':
3888			case '\"':
3889			case '\\':
3890				goto do_escape;
3891			default:
3892				source++;
3893				break;
3894		}
3895	}
3896
3897	if (!should_free) {
3898		return zend_string_copy(str);
3899	}
3900
3901	return str;
3902
3903do_escape:
3904	offset = source - (char *)ZSTR_VAL(str);
3905	new_str = zend_string_safe_alloc(2, ZSTR_LEN(str) - offset, offset, 0);
3906	memcpy(ZSTR_VAL(new_str), ZSTR_VAL(str), offset);
3907	target = ZSTR_VAL(new_str) + offset;
3908
3909	while (source < end) {
3910		switch (*source) {
3911			case '\0':
3912				*target++ = '\\';
3913				*target++ = '0';
3914				break;
3915			case '\'':
3916			case '\"':
3917			case '\\':
3918				*target++ = '\\';
3919				/* break is missing *intentionally* */
3920			default:
3921				*target++ = *source;
3922				break;
3923		}
3924
3925		source++;
3926	}
3927
3928	*target = 0;
3929	if (should_free) {
3930		zend_string_release(str);
3931	}
3932
3933	if (ZSTR_LEN(new_str) - (target - ZSTR_VAL(new_str)) > 16) {
3934		new_str = zend_string_truncate(new_str, target - ZSTR_VAL(new_str), 0);
3935	} else {
3936		ZSTR_LEN(new_str) = target - ZSTR_VAL(new_str);
3937	}
3938
3939	return new_str;
3940}
3941/* }}} */
3942
3943#define _HEB_BLOCK_TYPE_ENG 1
3944#define _HEB_BLOCK_TYPE_HEB 2
3945#define isheb(c)      (((((unsigned char) c) >= 224) && (((unsigned char) c) <= 250)) ? 1 : 0)
3946#define _isblank(c)   (((((unsigned char) c) == ' '  || ((unsigned char) c) == '\t')) ? 1 : 0)
3947#define _isnewline(c) (((((unsigned char) c) == '\n' || ((unsigned char) c) == '\r')) ? 1 : 0)
3948
3949/* {{{ php_str_replace_in_subject
3950 */
3951static zend_long php_str_replace_in_subject(zval *search, zval *replace, zval *subject, zval *result, int case_sensitivity)
3952{
3953	zval		*search_entry,
3954				*replace_entry = NULL;
3955	zend_string	*tmp_result,
3956				*replace_entry_str = NULL;
3957	char		*replace_value = NULL;
3958	size_t		 replace_len = 0;
3959	zend_long	 replace_count = 0;
3960	zend_string	*subject_str;
3961	zend_string *lc_subject_str = NULL;
3962	uint32_t     replace_idx;
3963
3964	/* Make sure we're dealing with strings. */
3965	subject_str = zval_get_string(subject);
3966	if (ZSTR_LEN(subject_str) == 0) {
3967		zend_string_release(subject_str);
3968		ZVAL_EMPTY_STRING(result);
3969		return 0;
3970	}
3971
3972	/* If search is an array */
3973	if (Z_TYPE_P(search) == IS_ARRAY) {
3974		/* Duplicate subject string for repeated replacement */
3975		ZVAL_STR_COPY(result, subject_str);
3976
3977		if (Z_TYPE_P(replace) == IS_ARRAY) {
3978			replace_idx = 0;
3979		} else {
3980			/* Set replacement value to the passed one */
3981			replace_value = Z_STRVAL_P(replace);
3982			replace_len = Z_STRLEN_P(replace);
3983		}
3984
3985		/* For each entry in the search array, get the entry */
3986		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(search), search_entry) {
3987			/* Make sure we're dealing with strings. */
3988			zend_string *search_str = zval_get_string(search_entry);
3989			if (ZSTR_LEN(search_str) == 0) {
3990				if (Z_TYPE_P(replace) == IS_ARRAY) {
3991					replace_idx++;
3992				}
3993				zend_string_release(search_str);
3994				continue;
3995			}
3996
3997			/* If replace is an array. */
3998			if (Z_TYPE_P(replace) == IS_ARRAY) {
3999				/* Get current entry */
4000				while (replace_idx < Z_ARRVAL_P(replace)->nNumUsed) {
4001					replace_entry = &Z_ARRVAL_P(replace)->arData[replace_idx].val;
4002					if (Z_TYPE_P(replace_entry) != IS_UNDEF) {
4003						break;
4004					}
4005					replace_idx++;
4006				}
4007				if (replace_idx < Z_ARRVAL_P(replace)->nNumUsed) {
4008					/* Make sure we're dealing with strings. */
4009					replace_entry_str = zval_get_string(replace_entry);
4010
4011					/* Set replacement value to the one we got from array */
4012					replace_value = ZSTR_VAL(replace_entry_str);
4013					replace_len = ZSTR_LEN(replace_entry_str);
4014
4015					replace_idx++;
4016				} else {
4017					/* We've run out of replacement strings, so use an empty one. */
4018					replace_value = "";
4019					replace_len = 0;
4020				}
4021			}
4022
4023			if (ZSTR_LEN(search_str) == 1) {
4024				zend_long old_replace_count = replace_count;
4025
4026				tmp_result = php_char_to_str_ex(Z_STR_P(result),
4027								ZSTR_VAL(search_str)[0],
4028								replace_value,
4029								replace_len,
4030								case_sensitivity,
4031								&replace_count);
4032				if (lc_subject_str && replace_count != old_replace_count) {
4033					zend_string_release(lc_subject_str);
4034					lc_subject_str = NULL;
4035				}
4036			} else if (ZSTR_LEN(search_str) > 1) {
4037				if (case_sensitivity) {
4038					tmp_result = php_str_to_str_ex(Z_STR_P(result),
4039							ZSTR_VAL(search_str), ZSTR_LEN(search_str),
4040							replace_value, replace_len, &replace_count);
4041				} else {
4042					zend_long old_replace_count = replace_count;
4043
4044					if (!lc_subject_str) {
4045						lc_subject_str = php_string_tolower(Z_STR_P(result));
4046					}
4047					tmp_result = php_str_to_str_i_ex(Z_STR_P(result), ZSTR_VAL(lc_subject_str),
4048							search_str, replace_value, replace_len, &replace_count);
4049					if (replace_count != old_replace_count) {
4050						zend_string_release(lc_subject_str);
4051						lc_subject_str = NULL;
4052					}
4053				}
4054			}
4055
4056			zend_string_release(search_str);
4057
4058			if (replace_entry_str) {
4059				zend_string_release(replace_entry_str);
4060				replace_entry_str = NULL;
4061			}
4062			zend_string_release(Z_STR_P(result));
4063			ZVAL_STR(result, tmp_result);
4064
4065			if (Z_STRLEN_P(result) == 0) {
4066				if (lc_subject_str) {
4067					zend_string_release(lc_subject_str);
4068				}
4069				zend_string_release(subject_str);
4070				return replace_count;
4071			}
4072		} ZEND_HASH_FOREACH_END();
4073		if (lc_subject_str) {
4074			zend_string_release(lc_subject_str);
4075		}
4076	} else {
4077		ZEND_ASSERT(Z_TYPE_P(search) == IS_STRING);
4078		if (Z_STRLEN_P(search) == 1) {
4079			ZVAL_STR(result,
4080				php_char_to_str_ex(subject_str,
4081							Z_STRVAL_P(search)[0],
4082							Z_STRVAL_P(replace),
4083							Z_STRLEN_P(replace),
4084							case_sensitivity,
4085							&replace_count));
4086		} else if (Z_STRLEN_P(search) > 1) {
4087			if (case_sensitivity) {
4088				ZVAL_STR(result, php_str_to_str_ex(subject_str,
4089						Z_STRVAL_P(search), Z_STRLEN_P(search),
4090						Z_STRVAL_P(replace), Z_STRLEN_P(replace), &replace_count));
4091			} else {
4092				lc_subject_str = php_string_tolower(subject_str);
4093				ZVAL_STR(result, php_str_to_str_i_ex(subject_str, ZSTR_VAL(lc_subject_str),
4094						Z_STR_P(search),
4095						Z_STRVAL_P(replace), Z_STRLEN_P(replace), &replace_count));
4096				zend_string_release(lc_subject_str);
4097			}
4098		} else {
4099			ZVAL_STR_COPY(result, subject_str);
4100		}
4101	}
4102	zend_string_release(subject_str);
4103	return replace_count;
4104}
4105/* }}} */
4106
4107/* {{{ php_str_replace_common
4108 */
4109static void php_str_replace_common(INTERNAL_FUNCTION_PARAMETERS, int case_sensitivity)
4110{
4111	zval *subject, *search, *replace, *subject_entry, *zcount = NULL;
4112	zval result;
4113	zend_string *string_key;
4114	zend_ulong num_key;
4115	zend_long count = 0;
4116	int argc = ZEND_NUM_ARGS();
4117
4118#ifndef FAST_ZPP
4119	if (zend_parse_parameters(ZEND_NUM_ARGS(), "zzz|z/", &search, &replace, &subject, &zcount) == FAILURE) {
4120		return;
4121	}
4122#else
4123	ZEND_PARSE_PARAMETERS_START(3, 4)
4124		Z_PARAM_ZVAL(search)
4125		Z_PARAM_ZVAL(replace)
4126		Z_PARAM_ZVAL(subject)
4127		Z_PARAM_OPTIONAL
4128		Z_PARAM_ZVAL_EX(zcount, 0, 1)
4129	ZEND_PARSE_PARAMETERS_END();
4130#endif
4131
4132	/* Make sure we're dealing with strings and do the replacement. */
4133	if (Z_TYPE_P(search) != IS_ARRAY) {
4134		convert_to_string_ex(search);
4135		if (Z_TYPE_P(replace) != IS_STRING) {
4136			convert_to_string_ex(replace);
4137		}
4138	} else if (Z_TYPE_P(replace) != IS_ARRAY) {
4139		convert_to_string_ex(replace);
4140	}
4141
4142	/* if subject is an array */
4143	if (Z_TYPE_P(subject) == IS_ARRAY) {
4144		array_init(return_value);
4145
4146		/* For each subject entry, convert it to string, then perform replacement
4147		   and add the result to the return_value array. */
4148		ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(subject), num_key, string_key, subject_entry) {
4149			if (Z_TYPE_P(subject_entry) != IS_ARRAY && Z_TYPE_P(subject_entry) != IS_OBJECT) {
4150				count += php_str_replace_in_subject(search, replace, subject_entry, &result, case_sensitivity);
4151			} else {
4152				ZVAL_COPY(&result, subject_entry);
4153			}
4154			/* Add to return array */
4155			if (string_key) {
4156				zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, &result);
4157			} else {
4158				zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, &result);
4159			}
4160		} ZEND_HASH_FOREACH_END();
4161	} else {	/* if subject is not an array */
4162		count = php_str_replace_in_subject(search, replace, subject, return_value, case_sensitivity);
4163	}
4164	if (argc > 3) {
4165		zval_ptr_dtor(zcount);
4166		ZVAL_LONG(zcount, count);
4167	}
4168}
4169/* }}} */
4170
4171/* {{{ proto mixed str_replace(mixed search, mixed replace, mixed subject [, int &replace_count])
4172   Replaces all occurrences of search in haystack with replace */
4173PHP_FUNCTION(str_replace)
4174{
4175	php_str_replace_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4176}
4177/* }}} */
4178
4179/* {{{ proto mixed str_ireplace(mixed search, mixed replace, mixed subject [, int &replace_count])
4180   Replaces all occurrences of search in haystack with replace / case-insensitive */
4181PHP_FUNCTION(str_ireplace)
4182{
4183	php_str_replace_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4184}
4185/* }}} */
4186
4187/* {{{ php_hebrev
4188 *
4189 * Converts Logical Hebrew text (Hebrew Windows style) to Visual text
4190 * Cheers/complaints/flames - Zeev Suraski <zeev@php.net>
4191 */
4192static void php_hebrev(INTERNAL_FUNCTION_PARAMETERS, int convert_newlines)
4193{
4194	char *str;
4195	char *heb_str, *tmp, *target;
4196	size_t block_start, block_end, block_type, block_length, i;
4197	zend_long max_chars=0;
4198	size_t begin, end, char_count, orig_begin;
4199	size_t str_len;
4200	zend_string *broken_str;
4201
4202	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &str, &str_len, &max_chars) == FAILURE) {
4203		return;
4204	}
4205
4206	if (str_len == 0) {
4207		RETURN_FALSE;
4208	}
4209
4210	tmp = str;
4211	block_start=block_end=0;
4212
4213	heb_str = (char *) emalloc(str_len+1);
4214	target = heb_str+str_len;
4215	*target = 0;
4216	target--;
4217
4218	block_length=0;
4219
4220	if (isheb(*tmp)) {
4221		block_type = _HEB_BLOCK_TYPE_HEB;
4222	} else {
4223		block_type = _HEB_BLOCK_TYPE_ENG;
4224	}
4225
4226	do {
4227		if (block_type == _HEB_BLOCK_TYPE_HEB) {
4228			while ((isheb((int)*(tmp+1)) || _isblank((int)*(tmp+1)) || ispunct((int)*(tmp+1)) || (int)*(tmp+1)=='\n' ) && block_end<str_len-1) {
4229				tmp++;
4230				block_end++;
4231				block_length++;
4232			}
4233			for (i = block_start+1; i<= block_end+1; i++) {
4234				*target = str[i-1];
4235				switch (*target) {
4236					case '(':
4237						*target = ')';
4238						break;
4239					case ')':
4240						*target = '(';
4241						break;
4242					case '[':
4243						*target = ']';
4244						break;
4245					case ']':
4246						*target = '[';
4247						break;
4248					case '{':
4249						*target = '}';
4250						break;
4251					case '}':
4252						*target = '{';
4253						break;
4254					case '<':
4255						*target = '>';
4256						break;
4257					case '>':
4258						*target = '<';
4259						break;
4260					case '\\':
4261						*target = '/';
4262						break;
4263					case '/':
4264						*target = '\\';
4265						break;
4266					default:
4267						break;
4268				}
4269				target--;
4270			}
4271			block_type = _HEB_BLOCK_TYPE_ENG;
4272		} else {
4273			while (!isheb(*(tmp+1)) && (int)*(tmp+1)!='\n' && block_end < str_len-1) {
4274				tmp++;
4275				block_end++;
4276				block_length++;
4277			}
4278			while ((_isblank((int)*tmp) || ispunct((int)*tmp)) && *tmp!='/' && *tmp!='-' && block_end > block_start) {
4279				tmp--;
4280				block_end--;
4281			}
4282			for (i = block_end+1; i >= block_start+1; i--) {
4283				*target = str[i-1];
4284				target--;
4285			}
4286			block_type = _HEB_BLOCK_TYPE_HEB;
4287		}
4288		block_start=block_end+1;
4289	} while (block_end < str_len-1);
4290
4291
4292	broken_str = zend_string_alloc(str_len, 0);
4293	begin = end = str_len-1;
4294	target = ZSTR_VAL(broken_str);
4295
4296	while (1) {
4297		char_count=0;
4298		while ((!max_chars || (max_chars > 0 && char_count < max_chars)) && begin > 0) {
4299			char_count++;
4300			begin--;
4301			if (begin <= 0 || _isnewline(heb_str[begin])) {
4302				while (begin > 0 && _isnewline(heb_str[begin-1])) {
4303					begin--;
4304					char_count++;
4305				}
4306				break;
4307			}
4308		}
4309		if (max_chars >= 0 && char_count == max_chars) { /* try to avoid breaking words */
4310			size_t new_char_count=char_count, new_begin=begin;
4311
4312			while (new_char_count > 0) {
4313				if (_isblank(heb_str[new_begin]) || _isnewline(heb_str[new_begin])) {
4314					break;
4315				}
4316				new_begin++;
4317				new_char_count--;
4318			}
4319			if (new_char_count > 0) {
4320				begin=new_begin;
4321			}
4322		}
4323		orig_begin=begin;
4324
4325		if (_isblank(heb_str[begin])) {
4326			heb_str[begin]='\n';
4327		}
4328		while (begin <= end && _isnewline(heb_str[begin])) { /* skip leading newlines */
4329			begin++;
4330		}
4331		for (i = begin; i <= end; i++) { /* copy content */
4332			*target = heb_str[i];
4333			target++;
4334		}
4335		for (i = orig_begin; i <= end && _isnewline(heb_str[i]); i++) {
4336			*target = heb_str[i];
4337			target++;
4338		}
4339		begin=orig_begin;
4340
4341		if (begin <= 0) {
4342			*target = 0;
4343			break;
4344		}
4345		begin--;
4346		end=begin;
4347	}
4348	efree(heb_str);
4349
4350	if (convert_newlines) {
4351		RETVAL_STR(php_char_to_str_ex(broken_str, '\n', "<br />\n", 7, 1, NULL));
4352		zend_string_release(broken_str);
4353	} else {
4354		RETURN_NEW_STR(broken_str);
4355	}
4356}
4357/* }}} */
4358
4359/* {{{ proto string hebrev(string str [, int max_chars_per_line])
4360   Converts logical Hebrew text to visual text */
4361PHP_FUNCTION(hebrev)
4362{
4363	php_hebrev(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4364}
4365/* }}} */
4366
4367/* {{{ proto string hebrevc(string str [, int max_chars_per_line])
4368   Converts logical Hebrew text to visual text with newline conversion */
4369PHP_FUNCTION(hebrevc)
4370{
4371	php_hebrev(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4372}
4373/* }}} */
4374
4375/* {{{ proto string nl2br(string str [, bool is_xhtml])
4376   Converts newlines to HTML line breaks */
4377PHP_FUNCTION(nl2br)
4378{
4379	/* in brief this inserts <br /> or <br> before matched regexp \n\r?|\r\n? */
4380	char	*tmp;
4381	zend_string *str;
4382	char	*end, *target;
4383	size_t	repl_cnt = 0;
4384	zend_bool	is_xhtml = 1;
4385	zend_string *result;
4386
4387#ifndef FAST_ZPP
4388	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|b", &str, &is_xhtml) == FAILURE) {
4389		return;
4390	}
4391#else
4392	ZEND_PARSE_PARAMETERS_START(1, 2)
4393		Z_PARAM_STR(str)
4394		Z_PARAM_OPTIONAL
4395		Z_PARAM_BOOL(is_xhtml)
4396	ZEND_PARSE_PARAMETERS_END();
4397#endif
4398
4399	tmp = ZSTR_VAL(str);
4400	end = ZSTR_VAL(str) + ZSTR_LEN(str);
4401
4402	/* it is really faster to scan twice and allocate mem once instead of scanning once
4403	   and constantly reallocing */
4404	while (tmp < end) {
4405		if (*tmp == '\r') {
4406			if (*(tmp+1) == '\n') {
4407				tmp++;
4408			}
4409			repl_cnt++;
4410		} else if (*tmp == '\n') {
4411			if (*(tmp+1) == '\r') {
4412				tmp++;
4413			}
4414			repl_cnt++;
4415		}
4416
4417		tmp++;
4418	}
4419
4420	if (repl_cnt == 0) {
4421		RETURN_STR_COPY(str);
4422	}
4423
4424	{
4425		size_t repl_len = is_xhtml ? (sizeof("<br />") - 1) : (sizeof("<br>") - 1);
4426
4427		result = zend_string_safe_alloc(repl_cnt, repl_len, ZSTR_LEN(str), 0);
4428		target = ZSTR_VAL(result);
4429	}
4430
4431	tmp = ZSTR_VAL(str);
4432	while (tmp < end) {
4433		switch (*tmp) {
4434			case '\r':
4435			case '\n':
4436				*target++ = '<';
4437				*target++ = 'b';
4438				*target++ = 'r';
4439
4440				if (is_xhtml) {
4441					*target++ = ' ';
4442					*target++ = '/';
4443				}
4444
4445				*target++ = '>';
4446
4447				if ((*tmp == '\r' && *(tmp+1) == '\n') || (*tmp == '\n' && *(tmp+1) == '\r')) {
4448					*target++ = *tmp++;
4449				}
4450				/* lack of a break; is intentional */
4451			default:
4452				*target++ = *tmp;
4453		}
4454
4455		tmp++;
4456	}
4457
4458	*target = '\0';
4459
4460	RETURN_NEW_STR(result);
4461}
4462/* }}} */
4463
4464/* {{{ proto string strip_tags(string str [, string allowable_tags])
4465   Strips HTML and PHP tags from a string */
4466PHP_FUNCTION(strip_tags)
4467{
4468	zend_string *buf;
4469	zend_string *str;
4470	zval *allow=NULL;
4471	char *allowed_tags=NULL;
4472	size_t allowed_tags_len=0;
4473
4474	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|z", &str, &allow) == FAILURE) {
4475		return;
4476	}
4477
4478	/* To maintain a certain BC, we allow anything for the second parameter and return original string */
4479	if (allow) {
4480		convert_to_string(allow);
4481		allowed_tags = Z_STRVAL_P(allow);
4482		allowed_tags_len = Z_STRLEN_P(allow);
4483	}
4484
4485	buf = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
4486	ZSTR_LEN(buf) = php_strip_tags_ex(ZSTR_VAL(buf), ZSTR_LEN(str), NULL, allowed_tags, allowed_tags_len, 0);
4487	RETURN_NEW_STR(buf);
4488}
4489/* }}} */
4490
4491/* {{{ proto string setlocale(mixed category, string locale [, string ...])
4492   Set locale information */
4493PHP_FUNCTION(setlocale)
4494{
4495	zval *args = NULL;
4496	zval *plocale;
4497	zend_string *loc;
4498	char *retval;
4499	zend_long cat;
4500	int num_args, i = 0;
4501	uint32_t idx;
4502
4503	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l+", &cat, &args, &num_args) == FAILURE) {
4504		return;
4505	}
4506
4507#ifdef HAVE_SETLOCALE
4508	idx = 0;
4509	while (1) {
4510		if (Z_TYPE(args[0]) == IS_ARRAY) {
4511			while (idx < Z_ARRVAL(args[0])->nNumUsed) {
4512				plocale = &Z_ARRVAL(args[0])->arData[idx].val;
4513				if (Z_TYPE_P(plocale) != IS_UNDEF) {
4514					break;
4515				}
4516				idx++;
4517			}
4518			if (idx >= Z_ARRVAL(args[0])->nNumUsed) {
4519				break;
4520			}
4521		} else {
4522			plocale = &args[i];
4523		}
4524
4525		loc = zval_get_string(plocale);
4526
4527		if (!strcmp("0", ZSTR_VAL(loc))) {
4528			zend_string_release(loc);
4529			loc = NULL;
4530		} else {
4531			if (ZSTR_LEN(loc) >= 255) {
4532				php_error_docref(NULL, E_WARNING, "Specified locale name is too long");
4533				zend_string_release(loc);
4534				break;
4535			}
4536		}
4537
4538		retval = php_my_setlocale(cat, loc ? ZSTR_VAL(loc) : NULL);
4539		zend_update_current_locale();
4540		if (retval) {
4541			if (loc) {
4542				/* Remember if locale was changed */
4543				size_t len = strlen(retval);
4544
4545				BG(locale_changed) = 1;
4546				if (cat == LC_CTYPE || cat == LC_ALL) {
4547					if (BG(locale_string)) {
4548						zend_string_release(BG(locale_string));
4549					}
4550					if (len == ZSTR_LEN(loc) && !memcmp(ZSTR_VAL(loc), retval, len)) {
4551						BG(locale_string) = zend_string_copy(loc);
4552						RETURN_STR(BG(locale_string));
4553					} else {
4554						BG(locale_string) = zend_string_init(retval, len, 0);
4555						zend_string_release(loc);
4556						RETURN_STR_COPY(BG(locale_string));
4557					}
4558				} else if (len == ZSTR_LEN(loc) && !memcmp(ZSTR_VAL(loc), retval, len)) {
4559					RETURN_STR(loc);
4560				}
4561				zend_string_release(loc);
4562			}
4563			RETURN_STRING(retval);
4564		}
4565		if (loc) {
4566			zend_string_release(loc);
4567		}
4568
4569		if (Z_TYPE(args[0]) == IS_ARRAY) {
4570			idx++;
4571		} else {
4572			if (++i >= num_args) break;
4573		}
4574	}
4575
4576#endif
4577	RETURN_FALSE;
4578}
4579/* }}} */
4580
4581/* {{{ proto void parse_str(string encoded_string [, array result])
4582   Parses GET/POST/COOKIE data and sets global variables */
4583PHP_FUNCTION(parse_str)
4584{
4585	char *arg;
4586	zval *arrayArg = NULL;
4587	char *res = NULL;
4588	size_t arglen;
4589
4590	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|z/", &arg, &arglen, &arrayArg) == FAILURE) {
4591		return;
4592	}
4593
4594	res = estrndup(arg, arglen);
4595
4596	if (arrayArg == NULL) {
4597		zval tmp;
4598		zend_array *symbol_table = zend_rebuild_symbol_table();
4599
4600		ZVAL_ARR(&tmp, symbol_table);
4601		sapi_module.treat_data(PARSE_STRING, res, &tmp);
4602	} else 	{
4603		zval ret;
4604
4605		/* Clear out the array that was passed in. */
4606		zval_dtor(arrayArg);
4607		array_init(&ret);
4608		sapi_module.treat_data(PARSE_STRING, res, &ret);
4609		ZVAL_COPY_VALUE(arrayArg, &ret);
4610	}
4611}
4612/* }}} */
4613
4614#define PHP_TAG_BUF_SIZE 1023
4615
4616/* {{{ php_tag_find
4617 *
4618 * Check if tag is in a set of tags
4619 *
4620 * states:
4621 *
4622 * 0 start tag
4623 * 1 first non-whitespace char seen
4624 */
4625int php_tag_find(char *tag, size_t len, const char *set) {
4626	char c, *n, *t;
4627	int state=0, done=0;
4628	char *norm;
4629
4630	if (len <= 0) {
4631		return 0;
4632	}
4633
4634	norm = emalloc(len+1);
4635
4636	n = norm;
4637	t = tag;
4638	c = tolower(*t);
4639	/*
4640	   normalize the tag removing leading and trailing whitespace
4641	   and turn any <a whatever...> into just <a> and any </tag>
4642	   into <tag>
4643	*/
4644	while (!done) {
4645		switch (c) {
4646			case '<':
4647				*(n++) = c;
4648				break;
4649			case '>':
4650				done =1;
4651				break;
4652			default:
4653				if (!isspace((int)c)) {
4654					if (state == 0) {
4655						state=1;
4656					}
4657					if (c != '/') {
4658						*(n++) = c;
4659					}
4660				} else {
4661					if (state == 1)
4662						done=1;
4663				}
4664				break;
4665		}
4666		c = tolower(*(++t));
4667	}
4668	*(n++) = '>';
4669	*n = '\0';
4670	if (strstr(set, norm)) {
4671		done=1;
4672	} else {
4673		done=0;
4674	}
4675	efree(norm);
4676	return done;
4677}
4678/* }}} */
4679
4680PHPAPI size_t php_strip_tags(char *rbuf, size_t len, int *stateptr, const char *allow, size_t allow_len) /* {{{ */
4681{
4682	return php_strip_tags_ex(rbuf, len, stateptr, allow, allow_len, 0);
4683}
4684/* }}} */
4685
4686/* {{{ php_strip_tags
4687
4688	A simple little state-machine to strip out html and php tags
4689
4690	State 0 is the output state, State 1 means we are inside a
4691	normal html tag and state 2 means we are inside a php tag.
4692
4693	The state variable is passed in to allow a function like fgetss
4694	to maintain state across calls to the function.
4695
4696	lc holds the last significant character read and br is a bracket
4697	counter.
4698
4699	When an allow string is passed in we keep track of the string
4700	in state 1 and when the tag is closed check it against the
4701	allow string to see if we should allow it.
4702
4703	swm: Added ability to strip <?xml tags without assuming it PHP
4704	code.
4705*/
4706PHPAPI 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)
4707{
4708	char *tbuf, *buf, *p, *tp, *rp, c, lc;
4709	int br, depth=0, in_q = 0;
4710	int state = 0;
4711	size_t pos, i = 0;
4712	char *allow_free = NULL;
4713	const char *allow_actual;
4714	char is_xml = 0;
4715
4716	if (stateptr)
4717		state = *stateptr;
4718
4719	buf = estrndup(rbuf, len);
4720	c = *buf;
4721	lc = '\0';
4722	p = buf;
4723	rp = rbuf;
4724	br = 0;
4725	if (allow) {
4726		allow_free = zend_str_tolower_dup_ex(allow, allow_len);
4727		allow_actual = allow_free ? allow_free : allow;
4728		tbuf = emalloc(PHP_TAG_BUF_SIZE + 1);
4729		tp = tbuf;
4730	} else {
4731		tbuf = tp = NULL;
4732	}
4733
4734	while (i < len) {
4735		switch (c) {
4736			case '\0':
4737				break;
4738			case '<':
4739				if (in_q) {
4740					break;
4741				}
4742				if (isspace(*(p + 1)) && !allow_tag_spaces) {
4743					goto reg_char;
4744				}
4745				if (state == 0) {
4746					lc = '<';
4747					state = 1;
4748					if (allow) {
4749						if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4750							pos = tp - tbuf;
4751							tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4752							tp = tbuf + pos;
4753						}
4754						*(tp++) = '<';
4755				 	}
4756				} else if (state == 1) {
4757					depth++;
4758				}
4759				break;
4760
4761			case '(':
4762				if (state == 2) {
4763					if (lc != '"' && lc != '\'') {
4764						lc = '(';
4765						br++;
4766					}
4767				} else if (allow && state == 1) {
4768					if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4769						pos = tp - tbuf;
4770						tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4771						tp = tbuf + pos;
4772					}
4773					*(tp++) = c;
4774				} else if (state == 0) {
4775					*(rp++) = c;
4776				}
4777				break;
4778
4779			case ')':
4780				if (state == 2) {
4781					if (lc != '"' && lc != '\'') {
4782						lc = ')';
4783						br--;
4784					}
4785				} else if (allow && state == 1) {
4786					if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4787						pos = tp - tbuf;
4788						tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4789						tp = tbuf + pos;
4790					}
4791					*(tp++) = c;
4792				} else if (state == 0) {
4793					*(rp++) = c;
4794				}
4795				break;
4796
4797			case '>':
4798				if (depth) {
4799					depth--;
4800					break;
4801				}
4802
4803				if (in_q) {
4804					break;
4805				}
4806
4807				switch (state) {
4808					case 1: /* HTML/XML */
4809						lc = '>';
4810						if (is_xml && *(p -1) == '-') {
4811							break;
4812						}
4813						in_q = state = is_xml = 0;
4814						if (allow) {
4815							if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4816								pos = tp - tbuf;
4817								tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4818								tp = tbuf + pos;
4819							}
4820							*(tp++) = '>';
4821							*tp='\0';
4822							if (php_tag_find(tbuf, tp-tbuf, allow_actual)) {
4823								memcpy(rp, tbuf, tp-tbuf);
4824								rp += tp-tbuf;
4825							}
4826							tp = tbuf;
4827						}
4828						break;
4829
4830					case 2: /* PHP */
4831						if (!br && lc != '\"' && *(p-1) == '?') {
4832							in_q = state = 0;
4833							tp = tbuf;
4834						}
4835						break;
4836
4837					case 3:
4838						in_q = state = 0;
4839						tp = tbuf;
4840						break;
4841
4842					case 4: /* JavaScript/CSS/etc... */
4843						if (p >= buf + 2 && *(p-1) == '-' && *(p-2) == '-') {
4844							in_q = state = 0;
4845							tp = tbuf;
4846						}
4847						break;
4848
4849					default:
4850						*(rp++) = c;
4851						break;
4852				}
4853				break;
4854
4855			case '"':
4856			case '\'':
4857				if (state == 4) {
4858					/* Inside <!-- comment --> */
4859					break;
4860				} else if (state == 2 && *(p-1) != '\\') {
4861					if (lc == c) {
4862						lc = '\0';
4863					} else if (lc != '\\') {
4864						lc = c;
4865					}
4866				} else if (state == 0) {
4867					*(rp++) = c;
4868				} else if (allow && state == 1) {
4869					if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4870						pos = tp - tbuf;
4871						tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4872						tp = tbuf + pos;
4873					}
4874					*(tp++) = c;
4875				}
4876				if (state && p != buf && (state == 1 || *(p-1) != '\\') && (!in_q || *p == in_q)) {
4877					if (in_q) {
4878						in_q = 0;
4879					} else {
4880						in_q = *p;
4881					}
4882				}
4883				break;
4884
4885			case '!':
4886				/* JavaScript & Other HTML scripting languages */
4887				if (state == 1 && *(p-1) == '<') {
4888					state = 3;
4889					lc = c;
4890				} else {
4891					if (state == 0) {
4892						*(rp++) = c;
4893					} else if (allow && state == 1) {
4894						if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4895							pos = tp - tbuf;
4896							tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4897							tp = tbuf + pos;
4898						}
4899						*(tp++) = c;
4900					}
4901				}
4902				break;
4903
4904			case '-':
4905				if (state == 3 && p >= buf + 2 && *(p-1) == '-' && *(p-2) == '!') {
4906					state = 4;
4907				} else {
4908					goto reg_char;
4909				}
4910				break;
4911
4912			case '?':
4913
4914				if (state == 1 && *(p-1) == '<') {
4915					br=0;
4916					state=2;
4917					break;
4918				}
4919
4920			case 'E':
4921			case 'e':
4922				/* !DOCTYPE exception */
4923				if (state==3 && p > buf+6
4924						     && tolower(*(p-1)) == 'p'
4925					         && tolower(*(p-2)) == 'y'
4926						     && tolower(*(p-3)) == 't'
4927						     && tolower(*(p-4)) == 'c'
4928						     && tolower(*(p-5)) == 'o'
4929						     && tolower(*(p-6)) == 'd') {
4930					state = 1;
4931					break;
4932				}
4933				/* fall-through */
4934
4935			case 'l':
4936			case 'L':
4937
4938				/* swm: If we encounter '<?xml' then we shouldn't be in
4939				 * state == 2 (PHP). Switch back to HTML.
4940				 */
4941
4942				if (state == 2 && p > buf+2 && strncasecmp(p-4, "<?xm", 4) == 0) {
4943					state = 1; is_xml=1;
4944					break;
4945				}
4946
4947				/* fall-through */
4948			default:
4949reg_char:
4950				if (state == 0) {
4951					*(rp++) = c;
4952				} else if (allow && state == 1) {
4953					if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4954						pos = tp - tbuf;
4955						tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4956						tp = tbuf + pos;
4957					}
4958					*(tp++) = c;
4959				}
4960				break;
4961		}
4962		c = *(++p);
4963		i++;
4964	}
4965	if (rp < rbuf + len) {
4966		*rp = '\0';
4967	}
4968	efree(buf);
4969	if (allow) {
4970		efree(tbuf);
4971		if (allow_free) {
4972			efree(allow_free);
4973		}
4974	}
4975	if (stateptr)
4976		*stateptr = state;
4977
4978	return (size_t)(rp - rbuf);
4979}
4980/* }}} */
4981
4982/* {{{ proto array str_getcsv(string input[, string delimiter[, string enclosure[, string escape]]])
4983Parse a CSV string into an array */
4984PHP_FUNCTION(str_getcsv)
4985{
4986	zend_string *str;
4987	char delim = ',', enc = '"', esc = '\\';
4988	char *delim_str = NULL, *enc_str = NULL, *esc_str = NULL;
4989	size_t delim_len = 0, enc_len = 0, esc_len = 0;
4990
4991	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|sss", &str, &delim_str, &delim_len,
4992		&enc_str, &enc_len, &esc_str, &esc_len) == FAILURE) {
4993		return;
4994	}
4995
4996	delim = delim_len ? delim_str[0] : delim;
4997	enc = enc_len ? enc_str[0] : enc;
4998	esc = esc_len ? esc_str[0] : esc;
4999
5000	php_fgetcsv(NULL, delim, enc, esc, ZSTR_LEN(str), ZSTR_VAL(str), return_value);
5001}
5002/* }}} */
5003
5004/* {{{ proto string str_repeat(string input, int mult)
5005   Returns the input string repeat mult times */
5006PHP_FUNCTION(str_repeat)
5007{
5008	zend_string		*input_str;		/* Input string */
5009	zend_long 		mult;			/* Multiplier */
5010	zend_string	*result;		/* Resulting string */
5011	size_t		result_len;		/* Length of the resulting string */
5012
5013	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sl", &input_str, &mult) == FAILURE) {
5014		return;
5015	}
5016
5017	if (mult < 0) {
5018		php_error_docref(NULL, E_WARNING, "Second argument has to be greater than or equal to 0");
5019		return;
5020	}
5021
5022	/* Don't waste our time if it's empty */
5023	/* ... or if the multiplier is zero */
5024	if (ZSTR_LEN(input_str) == 0 || mult == 0)
5025		RETURN_EMPTY_STRING();
5026
5027	/* Initialize the result string */
5028	result = zend_string_safe_alloc(ZSTR_LEN(input_str), mult, 0, 0);
5029	result_len = ZSTR_LEN(input_str) * mult;
5030
5031	/* Heavy optimization for situations where input string is 1 byte long */
5032	if (ZSTR_LEN(input_str) == 1) {
5033		memset(ZSTR_VAL(result), *ZSTR_VAL(input_str), mult);
5034	} else {
5035		char *s, *e, *ee;
5036		ptrdiff_t l=0;
5037		memcpy(ZSTR_VAL(result), ZSTR_VAL(input_str), ZSTR_LEN(input_str));
5038		s = ZSTR_VAL(result);
5039		e = ZSTR_VAL(result) + ZSTR_LEN(input_str);
5040		ee = ZSTR_VAL(result) + result_len;
5041
5042		while (e<ee) {
5043			l = (e-s) < (ee-e) ? (e-s) : (ee-e);
5044			memmove(e, s, l);
5045			e += l;
5046		}
5047	}
5048
5049	ZSTR_VAL(result)[result_len] = '\0';
5050
5051	RETURN_NEW_STR(result);
5052}
5053/* }}} */
5054
5055/* {{{ proto mixed count_chars(string input [, int mode])
5056   Returns info about what characters are used in input */
5057PHP_FUNCTION(count_chars)
5058{
5059	zend_string *input;
5060	int chars[256];
5061	zend_long mymode=0;
5062	unsigned char *buf;
5063	int inx;
5064	char retstr[256];
5065	size_t retlen=0;
5066	size_t tmp = 0;
5067
5068	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|l", &input, &mymode) == FAILURE) {
5069		return;
5070	}
5071
5072	if (mymode < 0 || mymode > 4) {
5073		php_error_docref(NULL, E_WARNING, "Unknown mode");
5074		RETURN_FALSE;
5075	}
5076
5077	buf = (unsigned char *) ZSTR_VAL(input);
5078	memset((void*) chars, 0, sizeof(chars));
5079
5080	while (tmp < ZSTR_LEN(input)) {
5081		chars[*buf]++;
5082		buf++;
5083		tmp++;
5084	}
5085
5086	if (mymode < 3) {
5087		array_init(return_value);
5088	}
5089
5090	for (inx = 0; inx < 256; inx++) {
5091		switch (mymode) {
5092	 		case 0:
5093				add_index_long(return_value, inx, chars[inx]);
5094				break;
5095	 		case 1:
5096				if (chars[inx] != 0) {
5097					add_index_long(return_value, inx, chars[inx]);
5098				}
5099				break;
5100  			case 2:
5101				if (chars[inx] == 0) {
5102					add_index_long(return_value, inx, chars[inx]);
5103				}
5104				break;
5105	  		case 3:
5106				if (chars[inx] != 0) {
5107					retstr[retlen++] = inx;
5108				}
5109				break;
5110  			case 4:
5111				if (chars[inx] == 0) {
5112					retstr[retlen++] = inx;
5113				}
5114				break;
5115		}
5116	}
5117
5118	if (mymode >= 3 && mymode <= 4) {
5119		RETURN_STRINGL(retstr, retlen);
5120	}
5121}
5122/* }}} */
5123
5124/* {{{ php_strnatcmp
5125 */
5126static void php_strnatcmp(INTERNAL_FUNCTION_PARAMETERS, int fold_case)
5127{
5128	zend_string *s1, *s2;
5129
5130	if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &s1, &s2) == FAILURE) {
5131		return;
5132	}
5133
5134	RETURN_LONG(strnatcmp_ex(ZSTR_VAL(s1), ZSTR_LEN(s1),
5135							 ZSTR_VAL(s2), ZSTR_LEN(s2),
5136							 fold_case));
5137}
5138/* }}} */
5139
5140PHPAPI int string_natural_compare_function_ex(zval *result, zval *op1, zval *op2, zend_bool case_insensitive) /* {{{ */
5141{
5142	zend_string *str1 = zval_get_string(op1);
5143	zend_string *str2 = zval_get_string(op2);
5144
5145	ZVAL_LONG(result, strnatcmp_ex(ZSTR_VAL(str1), ZSTR_LEN(str1), ZSTR_VAL(str2), ZSTR_LEN(str2), case_insensitive));
5146
5147	zend_string_release(str1);
5148	zend_string_release(str2);
5149	return SUCCESS;
5150}
5151/* }}} */
5152
5153PHPAPI int string_natural_case_compare_function(zval *result, zval *op1, zval *op2) /* {{{ */
5154{
5155	return string_natural_compare_function_ex(result, op1, op2, 1);
5156}
5157/* }}} */
5158
5159PHPAPI int string_natural_compare_function(zval *result, zval *op1, zval *op2) /* {{{ */
5160{
5161	return string_natural_compare_function_ex(result, op1, op2, 0);
5162}
5163/* }}} */
5164
5165/* {{{ proto int strnatcmp(string s1, string s2)
5166   Returns the result of string comparison using 'natural' algorithm */
5167PHP_FUNCTION(strnatcmp)
5168{
5169	php_strnatcmp(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
5170}
5171/* }}} */
5172
5173/* {{{ proto array localeconv(void)
5174   Returns numeric formatting information based on the current locale */
5175PHP_FUNCTION(localeconv)
5176{
5177	zval grouping, mon_grouping;
5178	int len, i;
5179
5180	/* We don't need no stinkin' parameters... */
5181	if (zend_parse_parameters_none() == FAILURE) {
5182		return;
5183	}
5184
5185	array_init(return_value);
5186	array_init(&grouping);
5187	array_init(&mon_grouping);
5188
5189#ifdef HAVE_LOCALECONV
5190	{
5191		struct lconv currlocdata;
5192
5193		localeconv_r( &currlocdata );
5194
5195		/* Grab the grouping data out of the array */
5196		len = (int)strlen(currlocdata.grouping);
5197
5198		for (i = 0; i < len; i++) {
5199			add_index_long(&grouping, i, currlocdata.grouping[i]);
5200		}
5201
5202		/* Grab the monetary grouping data out of the array */
5203		len = (int)strlen(currlocdata.mon_grouping);
5204
5205		for (i = 0; i < len; i++) {
5206			add_index_long(&mon_grouping, i, currlocdata.mon_grouping[i]);
5207		}
5208
5209		add_assoc_string(return_value, "decimal_point",     currlocdata.decimal_point);
5210		add_assoc_string(return_value, "thousands_sep",     currlocdata.thousands_sep);
5211		add_assoc_string(return_value, "int_curr_symbol",   currlocdata.int_curr_symbol);
5212		add_assoc_string(return_value, "currency_symbol",   currlocdata.currency_symbol);
5213		add_assoc_string(return_value, "mon_decimal_point", currlocdata.mon_decimal_point);
5214		add_assoc_string(return_value, "mon_thousands_sep", currlocdata.mon_thousands_sep);
5215		add_assoc_string(return_value, "positive_sign",     currlocdata.positive_sign);
5216		add_assoc_string(return_value, "negative_sign",     currlocdata.negative_sign);
5217		add_assoc_long(  return_value, "int_frac_digits",   currlocdata.int_frac_digits);
5218		add_assoc_long(  return_value, "frac_digits",       currlocdata.frac_digits);
5219		add_assoc_long(  return_value, "p_cs_precedes",     currlocdata.p_cs_precedes);
5220		add_assoc_long(  return_value, "p_sep_by_space",    currlocdata.p_sep_by_space);
5221		add_assoc_long(  return_value, "n_cs_precedes",     currlocdata.n_cs_precedes);
5222		add_assoc_long(  return_value, "n_sep_by_space",    currlocdata.n_sep_by_space);
5223		add_assoc_long(  return_value, "p_sign_posn",       currlocdata.p_sign_posn);
5224		add_assoc_long(  return_value, "n_sign_posn",       currlocdata.n_sign_posn);
5225	}
5226#else
5227	/* Ok, it doesn't look like we have locale info floating around, so I guess it
5228	   wouldn't hurt to just go ahead and return the POSIX locale information?  */
5229
5230	add_index_long(&grouping, 0, -1);
5231	add_index_long(&mon_grouping, 0, -1);
5232
5233	add_assoc_string(return_value, "decimal_point",     "\x2E");
5234	add_assoc_string(return_value, "thousands_sep",     "");
5235	add_assoc_string(return_value, "int_curr_symbol",   "");
5236	add_assoc_string(return_value, "currency_symbol",   "");
5237	add_assoc_string(return_value, "mon_decimal_point", "\x2E");
5238	add_assoc_string(return_value, "mon_thousands_sep", "");
5239	add_assoc_string(return_value, "positive_sign",     "");
5240	add_assoc_string(return_value, "negative_sign",     "");
5241	add_assoc_long(  return_value, "int_frac_digits",   CHAR_MAX);
5242	add_assoc_long(  return_value, "frac_digits",       CHAR_MAX);
5243	add_assoc_long(  return_value, "p_cs_precedes",     CHAR_MAX);
5244	add_assoc_long(  return_value, "p_sep_by_space",    CHAR_MAX);
5245	add_assoc_long(  return_value, "n_cs_precedes",     CHAR_MAX);
5246	add_assoc_long(  return_value, "n_sep_by_space",    CHAR_MAX);
5247	add_assoc_long(  return_value, "p_sign_posn",       CHAR_MAX);
5248	add_assoc_long(  return_value, "n_sign_posn",       CHAR_MAX);
5249#endif
5250
5251	zend_hash_str_update(Z_ARRVAL_P(return_value), "grouping", sizeof("grouping")-1, &grouping);
5252	zend_hash_str_update(Z_ARRVAL_P(return_value), "mon_grouping", sizeof("mon_grouping")-1, &mon_grouping);
5253}
5254/* }}} */
5255
5256/* {{{ proto int strnatcasecmp(string s1, string s2)
5257   Returns the result of case-insensitive string comparison using 'natural' algorithm */
5258PHP_FUNCTION(strnatcasecmp)
5259{
5260	php_strnatcmp(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
5261}
5262/* }}} */
5263
5264/* {{{ proto int substr_count(string haystack, string needle [, int offset [, int length]])
5265   Returns the number of times a substring occurs in the string */
5266PHP_FUNCTION(substr_count)
5267{
5268	char *haystack, *needle;
5269	zend_long offset = 0, length = 0;
5270	int ac = ZEND_NUM_ARGS();
5271	int count = 0;
5272	size_t haystack_len, needle_len;
5273	char *p, *endp, cmp;
5274
5275	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|ll", &haystack, &haystack_len, &needle, &needle_len, &offset, &length) == FAILURE) {
5276		return;
5277	}
5278
5279	if (needle_len == 0) {
5280		php_error_docref(NULL, E_WARNING, "Empty substring");
5281		RETURN_FALSE;
5282	}
5283
5284	p = haystack;
5285	endp = p + haystack_len;
5286
5287	if (offset < 0) {
5288		php_error_docref(NULL, E_WARNING, "Offset should be greater than or equal to 0");
5289		RETURN_FALSE;
5290	}
5291
5292	if ((size_t)offset > haystack_len) {
5293		php_error_docref(NULL, E_WARNING, "Offset value " ZEND_LONG_FMT " exceeds string length", offset);
5294		RETURN_FALSE;
5295	}
5296	p += offset;
5297
5298	if (ac == 4) {
5299
5300		if (length <= 0) {
5301			php_error_docref(NULL, E_WARNING, "Length should be greater than 0");
5302			RETURN_FALSE;
5303		}
5304		if (length > (haystack_len - offset)) {
5305			php_error_docref(NULL, E_WARNING, "Length value " ZEND_LONG_FMT " exceeds string length", length);
5306			RETURN_FALSE;
5307		}
5308		endp = p + length;
5309	}
5310
5311	if (needle_len == 1) {
5312		cmp = needle[0];
5313
5314		while ((p = memchr(p, cmp, endp - p))) {
5315			count++;
5316			p++;
5317		}
5318	} else {
5319		while ((p = (char*)php_memnstr(p, needle, needle_len, endp))) {
5320			p += needle_len;
5321			count++;
5322		}
5323	}
5324
5325	RETURN_LONG(count);
5326}
5327/* }}} */
5328
5329/* {{{ proto string str_pad(string input, int pad_length [, string pad_string [, int pad_type]])
5330   Returns input string padded on the left or right to specified length with pad_string */
5331PHP_FUNCTION(str_pad)
5332{
5333	/* Input arguments */
5334	zend_string *input;				/* Input string */
5335	zend_long pad_length;			/* Length to pad to */
5336
5337	/* Helper variables */
5338	size_t num_pad_chars;		/* Number of padding characters (total - input size) */
5339	char *pad_str = " "; /* Pointer to padding string */
5340	size_t pad_str_len = 1;
5341	zend_long   pad_type_val = STR_PAD_RIGHT; /* The padding type value */
5342	size_t	   i, left_pad=0, right_pad=0;
5343	zend_string *result = NULL;	/* Resulting string */
5344
5345	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sl|sl", &input, &pad_length, &pad_str, &pad_str_len, &pad_type_val) == FAILURE) {
5346		return;
5347	}
5348
5349	/* If resulting string turns out to be shorter than input string,
5350	   we simply copy the input and return. */
5351	if (pad_length < 0  || (size_t)pad_length <= ZSTR_LEN(input)) {
5352		RETURN_STRINGL(ZSTR_VAL(input), ZSTR_LEN(input));
5353	}
5354
5355	if (pad_str_len == 0) {
5356		php_error_docref(NULL, E_WARNING, "Padding string cannot be empty");
5357		return;
5358	}
5359
5360	if (pad_type_val < STR_PAD_LEFT || pad_type_val > STR_PAD_BOTH) {
5361		php_error_docref(NULL, E_WARNING, "Padding type has to be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH");
5362		return;
5363	}
5364
5365	num_pad_chars = pad_length - ZSTR_LEN(input);
5366	if (num_pad_chars >= INT_MAX) {
5367		php_error_docref(NULL, E_WARNING, "Padding length is too long");
5368		return;
5369	}
5370
5371	result = zend_string_alloc(ZSTR_LEN(input) + num_pad_chars, 0);
5372	ZSTR_LEN(result) = 0;
5373
5374	/* We need to figure out the left/right padding lengths. */
5375	switch (pad_type_val) {
5376		case STR_PAD_RIGHT:
5377			left_pad = 0;
5378			right_pad = num_pad_chars;
5379			break;
5380
5381		case STR_PAD_LEFT:
5382			left_pad = num_pad_chars;
5383			right_pad = 0;
5384			break;
5385
5386		case STR_PAD_BOTH:
5387			left_pad = num_pad_chars / 2;
5388			right_pad = num_pad_chars - left_pad;
5389			break;
5390	}
5391
5392	/* First we pad on the left. */
5393	for (i = 0; i < left_pad; i++)
5394		ZSTR_VAL(result)[ZSTR_LEN(result)++] = pad_str[i % pad_str_len];
5395
5396	/* Then we copy the input string. */
5397	memcpy(ZSTR_VAL(result) + ZSTR_LEN(result), ZSTR_VAL(input), ZSTR_LEN(input));
5398	ZSTR_LEN(result) += ZSTR_LEN(input);
5399
5400	/* Finally, we pad on the right. */
5401	for (i = 0; i < right_pad; i++)
5402		ZSTR_VAL(result)[ZSTR_LEN(result)++] = pad_str[i % pad_str_len];
5403
5404	ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
5405
5406	RETURN_NEW_STR(result);
5407}
5408/* }}} */
5409
5410/* {{{ proto mixed sscanf(string str, string format [, string ...])
5411   Implements an ANSI C compatible sscanf */
5412PHP_FUNCTION(sscanf)
5413{
5414	zval *args = NULL;
5415	char *str, *format;
5416	size_t str_len, format_len;
5417	int result, num_args = 0;
5418
5419	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss*", &str, &str_len, &format, &format_len,
5420		&args, &num_args) == FAILURE) {
5421		return;
5422	}
5423
5424	result = php_sscanf_internal(str, format, num_args, args, 0, return_value);
5425
5426	if (SCAN_ERROR_WRONG_PARAM_COUNT == result) {
5427		WRONG_PARAM_COUNT;
5428	}
5429}
5430/* }}} */
5431
5432static char rot13_from[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
5433static char rot13_to[] = "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM";
5434
5435/* {{{ proto string str_rot13(string str)
5436   Perform the rot13 transform on a string */
5437PHP_FUNCTION(str_rot13)
5438{
5439	zend_string *arg;
5440
5441	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &arg) == FAILURE) {
5442		return;
5443	}
5444
5445	if (ZSTR_LEN(arg) == 0) {
5446		RETURN_EMPTY_STRING();
5447	} else {
5448		RETURN_STR(php_strtr_ex(arg, rot13_from, rot13_to, 52));
5449	}
5450}
5451/* }}} */
5452
5453static void php_string_shuffle(char *str, zend_long len) /* {{{ */
5454{
5455	zend_long n_elems, rnd_idx, n_left;
5456	char temp;
5457	/* The implementation is stolen from array_data_shuffle       */
5458	/* Thus the characteristics of the randomization are the same */
5459	n_elems = len;
5460
5461	if (n_elems <= 1) {
5462		return;
5463	}
5464
5465	n_left = n_elems;
5466
5467	while (--n_left) {
5468		rnd_idx = php_rand();
5469		RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX);
5470		if (rnd_idx != n_left) {
5471			temp = str[n_left];
5472			str[n_left] = str[rnd_idx];
5473			str[rnd_idx] = temp;
5474		}
5475	}
5476}
5477/* }}} */
5478
5479/* {{{ proto void str_shuffle(string str)
5480   Shuffles string. One permutation of all possible is created */
5481PHP_FUNCTION(str_shuffle)
5482{
5483	zend_string *arg;
5484
5485	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &arg) == FAILURE) {
5486		return;
5487	}
5488
5489	RETVAL_STRINGL(ZSTR_VAL(arg), ZSTR_LEN(arg));
5490	if (Z_STRLEN_P(return_value) > 1) {
5491		php_string_shuffle(Z_STRVAL_P(return_value), (zend_long) Z_STRLEN_P(return_value));
5492	}
5493}
5494/* }}} */
5495
5496/* {{{ proto mixed str_word_count(string str, [int format [, string charlist]])
5497   	Counts the number of words inside a string. If format of 1 is specified,
5498   	then the function will return an array containing all the words
5499   	found inside the string. If format of 2 is specified, then the function
5500   	will return an associated array where the position of the word is the key
5501   	and the word itself is the value.
5502
5503   	For the purpose of this function, 'word' is defined as a locale dependent
5504   	string containing alphabetic characters, which also may contain, but not start
5505   	with "'" and "-" characters.
5506*/
5507PHP_FUNCTION(str_word_count)
5508{
5509	zend_string *str;
5510	char *char_list = NULL, *p, *e, *s, ch[256];
5511	size_t char_list_len = 0, word_count = 0;
5512	zend_long type = 0;
5513
5514	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|ls", &str, &type, &char_list, &char_list_len) == FAILURE) {
5515		return;
5516	}
5517
5518	switch(type) {
5519		case 1:
5520		case 2:
5521			array_init(return_value);
5522			if (!ZSTR_LEN(str)) {
5523				return;
5524			}
5525			break;
5526		case 0:
5527			if (!ZSTR_LEN(str)) {
5528				RETURN_LONG(0);
5529			}
5530			/* nothing to be done */
5531			break;
5532		default:
5533			php_error_docref(NULL, E_WARNING, "Invalid format value " ZEND_LONG_FMT, type);
5534			RETURN_FALSE;
5535	}
5536
5537	if (char_list) {
5538		php_charmask((unsigned char *)char_list, char_list_len, ch);
5539	}
5540
5541	p = ZSTR_VAL(str);
5542	e = ZSTR_VAL(str) + ZSTR_LEN(str);
5543
5544	/* first character cannot be ' or -, unless explicitly allowed by the user */
5545	if ((*p == '\'' && (!char_list || !ch['\''])) || (*p == '-' && (!char_list || !ch['-']))) {
5546		p++;
5547	}
5548	/* last character cannot be -, unless explicitly allowed by the user */
5549	if (*(e - 1) == '-' && (!char_list || !ch['-'])) {
5550		e--;
5551	}
5552
5553	while (p < e) {
5554		s = p;
5555		while (p < e && (isalpha((unsigned char)*p) || (char_list && ch[(unsigned char)*p]) || *p == '\'' || *p == '-')) {
5556			p++;
5557		}
5558		if (p > s) {
5559			switch (type)
5560			{
5561				case 1:
5562					add_next_index_stringl(return_value, s, p - s);
5563					break;
5564				case 2:
5565					add_index_stringl(return_value, (s - ZSTR_VAL(str)), s, p - s);
5566					break;
5567				default:
5568					word_count++;
5569					break;
5570			}
5571		}
5572		p++;
5573	}
5574
5575	if (!type) {
5576		RETURN_LONG(word_count);
5577	}
5578}
5579
5580/* }}} */
5581
5582#if HAVE_STRFMON
5583/* {{{ proto string money_format(string format , float value)
5584   Convert monetary value(s) to string */
5585PHP_FUNCTION(money_format)
5586{
5587	size_t format_len = 0;
5588	char *format, *p, *e;
5589	double value;
5590	zend_bool check = 0;
5591	zend_string *str;
5592	ssize_t res_len;
5593
5594	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sd", &format, &format_len, &value) == FAILURE) {
5595		return;
5596	}
5597
5598	p = format;
5599	e = p + format_len;
5600	while ((p = memchr(p, '%', (e - p)))) {
5601		if (*(p + 1) == '%') {
5602			p += 2;
5603		} else if (!check) {
5604			check = 1;
5605			p++;
5606		} else {
5607			php_error_docref(NULL, E_WARNING, "Only a single %%i or %%n token can be used");
5608			RETURN_FALSE;
5609		}
5610	}
5611
5612	str = zend_string_safe_alloc(format_len, 1, 1024, 0);
5613	if ((res_len = strfmon(ZSTR_VAL(str), ZSTR_LEN(str), format, value)) < 0) {
5614		zend_string_free(str);
5615		RETURN_FALSE;
5616	}
5617	ZSTR_LEN(str) = (size_t)res_len;
5618	ZSTR_VAL(str)[ZSTR_LEN(str)] = '\0';
5619
5620	RETURN_NEW_STR(zend_string_truncate(str, ZSTR_LEN(str), 0));
5621}
5622/* }}} */
5623#endif
5624
5625/* {{{ proto array str_split(string str [, int split_length])
5626   Convert a string to an array. If split_length is specified, break the string down into chunks each split_length characters long. */
5627PHP_FUNCTION(str_split)
5628{
5629	zend_string *str;
5630	zend_long split_length = 1;
5631	char *p;
5632	size_t n_reg_segments;
5633
5634	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|l", &str, &split_length) == FAILURE) {
5635		return;
5636	}
5637
5638	if (split_length <= 0) {
5639		php_error_docref(NULL, E_WARNING, "The length of each segment must be greater than zero");
5640		RETURN_FALSE;
5641	}
5642
5643
5644	if (0 == ZSTR_LEN(str) || (size_t)split_length >= ZSTR_LEN(str)) {
5645		array_init_size(return_value, 1);
5646		add_next_index_stringl(return_value, ZSTR_VAL(str), ZSTR_LEN(str));
5647		return;
5648	}
5649
5650	array_init_size(return_value, (uint32_t)(((ZSTR_LEN(str) - 1) / split_length) + 1));
5651
5652	n_reg_segments = ZSTR_LEN(str) / split_length;
5653	p = ZSTR_VAL(str);
5654
5655	while (n_reg_segments-- > 0) {
5656		add_next_index_stringl(return_value, p, split_length);
5657		p += split_length;
5658	}
5659
5660	if (p != (ZSTR_VAL(str) + ZSTR_LEN(str))) {
5661		add_next_index_stringl(return_value, p, (ZSTR_VAL(str) + ZSTR_LEN(str) - p));
5662	}
5663}
5664/* }}} */
5665
5666/* {{{ proto array strpbrk(string haystack, string char_list)
5667   Search a string for any of a set of characters */
5668PHP_FUNCTION(strpbrk)
5669{
5670	zend_string *haystack, *char_list;
5671	char *haystack_ptr, *cl_ptr;
5672
5673	if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &haystack, &char_list) == FAILURE) {
5674		RETURN_FALSE;
5675	}
5676
5677	if (!ZSTR_LEN(char_list)) {
5678		php_error_docref(NULL, E_WARNING, "The character list cannot be empty");
5679		RETURN_FALSE;
5680	}
5681
5682	for (haystack_ptr = ZSTR_VAL(haystack); haystack_ptr < (ZSTR_VAL(haystack) + ZSTR_LEN(haystack)); ++haystack_ptr) {
5683		for (cl_ptr = ZSTR_VAL(char_list); cl_ptr < (ZSTR_VAL(char_list) + ZSTR_LEN(char_list)); ++cl_ptr) {
5684			if (*cl_ptr == *haystack_ptr) {
5685				RETURN_STRINGL(haystack_ptr, (ZSTR_VAL(haystack) + ZSTR_LEN(haystack) - haystack_ptr));
5686			}
5687		}
5688	}
5689
5690	RETURN_FALSE;
5691}
5692/* }}} */
5693
5694/* {{{ proto int substr_compare(string main_str, string str, int offset [, int length [, bool case_sensitivity]])
5695   Binary safe optionally case insensitive comparison of 2 strings from an offset, up to length characters */
5696PHP_FUNCTION(substr_compare)
5697{
5698	zend_string *s1, *s2;
5699	zend_long offset, len=0;
5700	zend_bool cs=0;
5701	size_t cmp_len;
5702
5703	if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSl|lb", &s1, &s2, &offset, &len, &cs) == FAILURE) {
5704		RETURN_FALSE;
5705	}
5706
5707	if (ZEND_NUM_ARGS() >= 4 && len <= 0) {
5708		if (len == 0) {
5709			RETURN_LONG(0L);
5710		} else {
5711			php_error_docref(NULL, E_WARNING, "The length must be greater than or equal to zero");
5712			RETURN_FALSE;
5713		}
5714	}
5715
5716	if (offset < 0) {
5717		offset = ZSTR_LEN(s1) + offset;
5718		offset = (offset < 0) ? 0 : offset;
5719	}
5720
5721	if ((size_t)offset >= ZSTR_LEN(s1)) {
5722		php_error_docref(NULL, E_WARNING, "The start position cannot exceed initial string length");
5723		RETURN_FALSE;
5724	}
5725
5726	cmp_len = (size_t) (len ? len : MAX(ZSTR_LEN(s2), (ZSTR_LEN(s1) - offset)));
5727
5728	if (!cs) {
5729		RETURN_LONG(zend_binary_strncmp(ZSTR_VAL(s1) + offset, (ZSTR_LEN(s1) - offset), ZSTR_VAL(s2), ZSTR_LEN(s2), cmp_len));
5730	} else {
5731		RETURN_LONG(zend_binary_strncasecmp_l(ZSTR_VAL(s1) + offset, (ZSTR_LEN(s1) - offset), ZSTR_VAL(s2), ZSTR_LEN(s2), cmp_len));
5732	}
5733}
5734/* }}} */
5735
5736/*
5737 * Local variables:
5738 * tab-width: 4
5739 * c-basic-offset: 4
5740 * End:
5741 * vim600: noet sw=4 ts=4 fdm=marker
5742 * vim<600: noet sw=4 ts=4
5743 */
5744