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