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