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