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