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