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