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