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