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