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