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