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;
2742    register char *r, *r_end;
2743    int str_len;
2744
2745    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
2746        return;
2747    }
2748
2749    if (!str_len) {
2750        RETURN_EMPTY_STRING();
2751    }
2752
2753    ZVAL_STRINGL(return_value, str, str_len, 1);
2754    r = Z_STRVAL_P(return_value);
2755
2756    *r = toupper((unsigned char) *r);
2757    for (r_end = r + Z_STRLEN_P(return_value) - 1; r < r_end; ) {
2758        if (isspace((int) *(unsigned char *)r++)) {
2759            *r = toupper((unsigned char) *r);
2760        }
2761    }
2762}
2763/* }}} */
2764
2765/* {{{ php_strtr
2766 */
2767PHPAPI char *php_strtr(char *str, int len, char *str_from, char *str_to, int trlen)
2768{
2769    int i;
2770    unsigned char xlat[256];
2771
2772    if ((trlen < 1) || (len < 1)) {
2773        return str;
2774    }
2775
2776    for (i = 0; i < 256; xlat[i] = i, i++);
2777
2778    for (i = 0; i < trlen; i++) {
2779        xlat[(unsigned char) str_from[i]] = str_to[i];
2780    }
2781
2782    for (i = 0; i < len; i++) {
2783        str[i] = xlat[(unsigned char) str[i]];
2784    }
2785
2786    return str;
2787}
2788/* }}} */
2789
2790/* {{{ Definitions for php_strtr_array */
2791typedef size_t STRLEN;  /* STRLEN should be unsigned */
2792typedef uint16_t HASH;
2793typedef struct {
2794    HASH            table_mask;
2795    STRLEN          entries[1];
2796} SHIFT_TAB;
2797typedef struct {
2798    HASH            table_mask;
2799    int             entries[1];
2800} HASH_TAB;
2801typedef struct {
2802    const char  *s;
2803    STRLEN      l;
2804} STR;
2805typedef struct _pat_and_repl {
2806    STR         pat;
2807    STR         repl;
2808} PATNREPL;
2809
2810#define S(a) ((a)->s)
2811#define L(a) ((a)->l)
2812
2813#define SHIFT_TAB_BITS  13
2814#define HASH_TAB_BITS   10 /* should be less than sizeof(HASH) * 8 */
2815#define SHIFT_TAB_SIZE  (1U << SHIFT_TAB_BITS)
2816#define HASH_TAB_SIZE   (1U << HASH_TAB_BITS)
2817
2818typedef struct {
2819    int             B;          /* size of suffixes */
2820    int             Bp;         /* size of prefixes */
2821    STRLEN          m;          /* minimum pattern length */
2822    int             patnum;     /* number of patterns */
2823    SHIFT_TAB       *shift;     /* table mapping hash to allowed shift */
2824    HASH_TAB        *hash;      /* table mapping hash to int (pair of pointers) */
2825    HASH            *prefix;    /* array of hashes of prefixes by pattern suffix hash order */
2826    PATNREPL        *patterns;  /* array of prefixes by pattern suffix hash order */
2827} PPRES;
2828/* }}} */
2829
2830/* {{{ php_strtr_hash */
2831static inline HASH php_strtr_hash(const char *str, int len)
2832{
2833    HASH    res = 0;
2834    int     i;
2835    for (i = 0; i < len; i++) {
2836        res = res * 33 + (unsigned char)str[i];
2837    }
2838
2839    return res;
2840}
2841/* }}} */
2842/* {{{ php_strtr_populate_shift */
2843static inline void php_strtr_populate_shift(PATNREPL *patterns, int patnum, int B, STRLEN m, SHIFT_TAB *shift)
2844{
2845    int     i;
2846    STRLEN  j,
2847            max_shift;
2848
2849    max_shift = m - B + 1;
2850    for (i = 0; i < SHIFT_TAB_SIZE; i++) {
2851        shift->entries[i] = max_shift;
2852    }
2853    for (i = 0; i < patnum; i++) {
2854        for (j = 0; j < m - B + 1; j++) {
2855            HASH h = php_strtr_hash(&S(&patterns[i].pat)[j], B) & shift->table_mask;
2856            assert((long long) m - (long long) j - B >= 0);
2857            shift->entries[h] = MIN(shift->entries[h], m - j - B);
2858        }
2859    }
2860}
2861/* }}} */
2862/* {{{ php_strtr_compare_hash_suffix */
2863static int php_strtr_compare_hash_suffix(const void *a, const void *b TSRMLS_DC, void *ctx_g)
2864{
2865    const PPRES     *res = ctx_g;
2866    const PATNREPL  *pnr_a = a,
2867                    *pnr_b = b;
2868    HASH            hash_a = php_strtr_hash(&S(&pnr_a->pat)[res->m - res->B], res->B)
2869                                & res->hash->table_mask,
2870                    hash_b = php_strtr_hash(&S(&pnr_b->pat)[res->m - res->B], res->B)
2871                                & res->hash->table_mask;
2872    /* TODO: don't recalculate the hashes all the time */
2873    if (hash_a > hash_b) {
2874        return 1;
2875    } else if (hash_a < hash_b) {
2876        return -1;
2877    } else {
2878        /* longer patterns must be sorted first */
2879        if (L(&pnr_a->pat) > L(&pnr_b->pat)) {
2880            return -1;
2881        } else if (L(&pnr_a->pat) < L(&pnr_b->pat)) {
2882            return 1;
2883        } else {
2884            return 0;
2885        }
2886    }
2887}
2888/* }}} */
2889/* {{{ php_strtr_free_strp */
2890static void php_strtr_free_strp(void *strp)
2891{
2892    STR_FREE(*(char**)strp);
2893}
2894/* }}} */
2895/* {{{ php_strtr_array_prepare_repls */
2896static PATNREPL *php_strtr_array_prepare_repls(int slen, HashTable *pats, zend_llist **allocs, int *outsize)
2897{
2898    PATNREPL        *patterns;
2899    HashPosition    hpos;
2900    zval            **entry;
2901    int             num_pats = zend_hash_num_elements(pats),
2902                    i;
2903
2904    patterns = safe_emalloc(num_pats, sizeof(*patterns), 0);
2905    *allocs = emalloc(sizeof **allocs);
2906    zend_llist_init(*allocs, sizeof(void*), &php_strtr_free_strp, 0);
2907
2908    for (i = 0, zend_hash_internal_pointer_reset_ex(pats, &hpos);
2909            zend_hash_get_current_data_ex(pats, (void **)&entry, &hpos) == SUCCESS;
2910            zend_hash_move_forward_ex(pats, &hpos)) {
2911        char    *string_key;
2912        uint    string_key_len;
2913        ulong   num_key;
2914        zval    *tzv = NULL;
2915
2916        switch (zend_hash_get_current_key_ex(pats, &string_key, &string_key_len, &num_key, 0, &hpos)) {
2917        case HASH_KEY_IS_LONG:
2918            string_key_len = 1 + zend_spprintf(&string_key, 0, "%ld", (long)num_key);
2919            zend_llist_add_element(*allocs, &string_key);
2920            /* break missing intentionally */
2921
2922        case HASH_KEY_IS_STRING:
2923            string_key_len--; /* exclude final '\0' */
2924            if (string_key_len == 0) { /* empty string given as pattern */
2925                efree(patterns);
2926                zend_llist_destroy(*allocs);
2927                efree(*allocs);
2928                *allocs = NULL;
2929                return NULL;
2930            }
2931            if (string_key_len > slen) { /* this pattern can never match */
2932                continue;
2933            }
2934
2935            if (Z_TYPE_PP(entry) != IS_STRING) {
2936                tzv = *entry;
2937                zval_addref_p(tzv);
2938                SEPARATE_ZVAL(&tzv);
2939                convert_to_string(tzv);
2940                entry = &tzv;
2941                zend_llist_add_element(*allocs, &Z_STRVAL_PP(entry));
2942            }
2943
2944            S(&patterns[i].pat) = string_key;
2945            L(&patterns[i].pat) = string_key_len;
2946            S(&patterns[i].repl) = Z_STRVAL_PP(entry);
2947            L(&patterns[i].repl) = Z_STRLEN_PP(entry);
2948            i++;
2949
2950            if (tzv) {
2951                efree(tzv);
2952            }
2953        }
2954    }
2955
2956    *outsize = i;
2957    return patterns;
2958}
2959/* }}} */
2960
2961/* {{{ PPRES *php_strtr_array_prepare(STR *text, PATNREPL *patterns, int patnum, int B, int Bp) */
2962static PPRES *php_strtr_array_prepare(STR *text, PATNREPL *patterns, int patnum, int B, int Bp)
2963{
2964    int     i;
2965    PPRES   *res = emalloc(sizeof *res);
2966
2967    res->m = (STRLEN)-1;
2968    for (i = 0; i < patnum; i++) {
2969        if (L(&patterns[i].pat) < res->m) {
2970            res->m = L(&patterns[i].pat);
2971        }
2972    }
2973    assert(res->m > 0);
2974    res->B  = B     = MIN(B, res->m);
2975    res->Bp = Bp    = MIN(Bp, res->m);
2976
2977    res->shift = safe_emalloc(SHIFT_TAB_SIZE, sizeof(*res->shift->entries), sizeof(*res->shift));
2978    res->shift->table_mask = SHIFT_TAB_SIZE - 1;
2979    php_strtr_populate_shift(patterns, patnum, B, res->m, res->shift);
2980
2981    res->hash = safe_emalloc(HASH_TAB_SIZE, sizeof(*res->hash->entries), sizeof(*res->hash));
2982    res->hash->table_mask = HASH_TAB_SIZE - 1;
2983
2984    res->patterns = safe_emalloc(patnum, sizeof(*res->patterns), 0);
2985    memcpy(res->patterns, patterns, sizeof(*patterns) * patnum);
2986#ifdef ZTS
2987    zend_qsort_r(res->patterns, patnum, sizeof(*res->patterns),
2988            php_strtr_compare_hash_suffix, res, NULL); /* tsrmls not needed */
2989#else
2990    zend_qsort_r(res->patterns, patnum, sizeof(*res->patterns),
2991            php_strtr_compare_hash_suffix, res);
2992#endif
2993
2994    res->prefix = safe_emalloc(patnum, sizeof(*res->prefix), 0);
2995    for (i = 0; i < patnum; i++) {
2996        res->prefix[i] = php_strtr_hash(S(&res->patterns[i].pat), Bp);
2997    }
2998
2999    /* Initialize the rest of ->hash */
3000    for (i = 0; i < HASH_TAB_SIZE; i++) {
3001        res->hash->entries[i] = -1;
3002    }
3003    {
3004        HASH last_h = -1; /* assumes not all bits are used in res->hash */
3005        /* res->patterns is already ordered by hash.
3006         * Make res->hash->entries[h] de index of the first pattern in
3007         * res->patterns that has hash h */
3008        for (i = 0; i < patnum; i++) {
3009            HASH h = php_strtr_hash(&S(&res->patterns[i].pat)[res->m - res->B], res->B)
3010                        & res->hash->table_mask;
3011            if (h != last_h) {
3012                res->hash->entries[h] = i;
3013                last_h = h;
3014            }
3015        }
3016    }
3017    res->hash->entries[HASH_TAB_SIZE] = patnum; /* OK, we effectively allocated SIZE+1 */
3018    for (i = HASH_TAB_SIZE - 1; i >= 0; i--) {
3019        if (res->hash->entries[i] == -1) {
3020            res->hash->entries[i] = res->hash->entries[i + 1];
3021        }
3022    }
3023
3024    res->patnum = patnum;
3025
3026    return res;
3027}
3028/* }}} */
3029/* {{{ php_strtr_array_destroy_ppres(PPRES *d) */
3030static void php_strtr_array_destroy_ppres(PPRES *d)
3031{
3032    efree(d->shift);
3033    efree(d->hash);
3034    efree(d->prefix);
3035    efree(d->patterns);
3036    efree(d);
3037}
3038/* }}} */
3039
3040/* {{{ php_strtr_array_do_repl(STR *text, PPRES *d, zval *return_value) */
3041static void php_strtr_array_do_repl(STR *text, PPRES *d, zval *return_value)
3042{
3043    STRLEN      pos = 0,
3044                nextwpos = 0,
3045                lastpos = L(text) - d->m;
3046    smart_str   result = {0};
3047
3048    while (pos <= lastpos) {
3049        HASH    h       = php_strtr_hash(&S(text)[pos + d->m - d->B], d->B) & d->shift->table_mask;
3050        STRLEN  shift   = d->shift->entries[h];
3051
3052        if (shift > 0) {
3053            pos += shift;
3054        } else {
3055            HASH    h2              = h & d->hash->table_mask,
3056                    prefix_h        = php_strtr_hash(&S(text)[pos], d->Bp);
3057
3058            int     offset_start    = d->hash->entries[h2],
3059                    offset_end      = d->hash->entries[h2 + 1], /* exclusive */
3060                    i               = 0;
3061
3062            for (i = offset_start; i < offset_end; i++) {
3063                PATNREPL *pnr;
3064                if (d->prefix[i] != prefix_h)
3065                    continue;
3066
3067                pnr = &d->patterns[i];
3068                if (L(&pnr->pat) > L(text) - pos ||
3069                        memcmp(S(&pnr->pat), &S(text)[pos], L(&pnr->pat)) != 0)
3070                    continue;
3071
3072                smart_str_appendl(&result, &S(text)[nextwpos], pos - nextwpos);
3073                smart_str_appendl(&result, S(&pnr->repl), L(&pnr->repl));
3074                pos += L(&pnr->pat);
3075                nextwpos = pos;
3076                goto end_outer_loop;
3077            }
3078
3079            pos++;
3080end_outer_loop: ;
3081        }
3082    }
3083
3084    smart_str_appendl(&result, &S(text)[nextwpos], L(text) - nextwpos);
3085
3086    if (result.c != NULL) {
3087        smart_str_0(&result);
3088        RETVAL_STRINGL(result.c, result.len, 0);
3089    } else {
3090        RETURN_EMPTY_STRING();
3091    }
3092}
3093/* }}} */
3094
3095/* {{{ php_strtr_array */
3096static void php_strtr_array(zval *return_value, char *str, int slen, HashTable *pats)
3097{
3098    PPRES       *data;
3099    STR         text;
3100    PATNREPL    *patterns;
3101    int         patterns_len;
3102    zend_llist  *allocs;
3103
3104    S(&text) = str;
3105    L(&text) = slen;
3106
3107    patterns = php_strtr_array_prepare_repls(slen, pats, &allocs, &patterns_len);
3108    if (patterns == NULL) {
3109        RETURN_FALSE;
3110    }
3111    data = php_strtr_array_prepare(&text, patterns, patterns_len, 2, 2);
3112    efree(patterns);
3113    php_strtr_array_do_repl(&text, data, return_value);
3114    php_strtr_array_destroy_ppres(data);
3115    zend_llist_destroy(allocs);
3116    efree(allocs);
3117}
3118/* }}} */
3119
3120/* {{{ proto string strtr(string str, string from[, string to])
3121   Translates characters in str using given translation tables */
3122PHP_FUNCTION(strtr)
3123{
3124    zval **from;
3125    char *str, *to = NULL;
3126    int str_len, to_len = 0;
3127    int ac = ZEND_NUM_ARGS();
3128
3129    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sZ|s", &str, &str_len, &from, &to, &to_len) == FAILURE) {
3130        return;
3131    }
3132
3133    if (ac == 2 && Z_TYPE_PP(from) != IS_ARRAY) {
3134        php_error_docref(NULL TSRMLS_CC, E_WARNING, "The second argument is not an array");
3135        RETURN_FALSE;
3136    }
3137
3138    /* shortcut for empty string */
3139    if (str_len == 0) {
3140        RETURN_EMPTY_STRING();
3141    }
3142
3143    if (ac == 2) {
3144        php_strtr_array(return_value, str, str_len, HASH_OF(*from));
3145    } else {
3146        convert_to_string_ex(from);
3147
3148        ZVAL_STRINGL(return_value, str, str_len, 1);
3149
3150        php_strtr(Z_STRVAL_P(return_value),
3151                  Z_STRLEN_P(return_value),
3152                  Z_STRVAL_PP(from),
3153                  to,
3154                  MIN(Z_STRLEN_PP(from),
3155                  to_len));
3156    }
3157}
3158/* }}} */
3159
3160/* {{{ proto string strrev(string str)
3161   Reverse a string */
3162PHP_FUNCTION(strrev)
3163{
3164    char *str;
3165    char *e, *n, *p;
3166    int  str_len;
3167
3168    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
3169        return;
3170    }
3171
3172    n = emalloc(str_len+1);
3173    p = n;
3174
3175    e = str + str_len;
3176
3177    while (--e>=str) {
3178        *p++ = *e;
3179    }
3180
3181    *p = '\0';
3182
3183    RETVAL_STRINGL(n, str_len, 0);
3184}
3185/* }}} */
3186
3187/* {{{ php_similar_str
3188 */
3189static void php_similar_str(const char *txt1, int len1, const char *txt2, int len2, int *pos1, int *pos2, int *max)
3190{
3191    char *p, *q;
3192    char *end1 = (char *) txt1 + len1;
3193    char *end2 = (char *) txt2 + len2;
3194    int l;
3195
3196    *max = 0;
3197    for (p = (char *) txt1; p < end1; p++) {
3198        for (q = (char *) txt2; q < end2; q++) {
3199            for (l = 0; (p + l < end1) && (q + l < end2) && (p[l] == q[l]); l++);
3200            if (l > *max) {
3201                *max = l;
3202                *pos1 = p - txt1;
3203                *pos2 = q - txt2;
3204            }
3205        }
3206    }
3207}
3208/* }}} */
3209
3210/* {{{ php_similar_char
3211 */
3212static int php_similar_char(const char *txt1, int len1, const char *txt2, int len2)
3213{
3214    int sum;
3215    int pos1 = 0, pos2 = 0, max;
3216
3217    php_similar_str(txt1, len1, txt2, len2, &pos1, &pos2, &max);
3218    if ((sum = max)) {
3219        if (pos1 && pos2) {
3220            sum += php_similar_char(txt1, pos1,
3221                                    txt2, pos2);
3222        }
3223        if ((pos1 + max < len1) && (pos2 + max < len2)) {
3224            sum += php_similar_char(txt1 + pos1 + max, len1 - pos1 - max,
3225                                    txt2 + pos2 + max, len2 - pos2 - max);
3226        }
3227    }
3228
3229    return sum;
3230}
3231/* }}} */
3232
3233/* {{{ proto int similar_text(string str1, string str2 [, float percent])
3234   Calculates the similarity between two strings */
3235PHP_FUNCTION(similar_text)
3236{
3237    char *t1, *t2;
3238    zval **percent = NULL;
3239    int ac = ZEND_NUM_ARGS();
3240    int sim;
3241    int t1_len, t2_len;
3242
3243    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|Z", &t1, &t1_len, &t2, &t2_len, &percent) == FAILURE) {
3244        return;
3245    }
3246
3247    if (ac > 2) {
3248        convert_to_double_ex(percent);
3249    }
3250
3251    if (t1_len + t2_len == 0) {
3252        if (ac > 2) {
3253            Z_DVAL_PP(percent) = 0;
3254        }
3255
3256        RETURN_LONG(0);
3257    }
3258
3259    sim = php_similar_char(t1, t1_len, t2, t2_len);
3260
3261    if (ac > 2) {
3262        Z_DVAL_PP(percent) = sim * 200.0 / (t1_len + t2_len);
3263    }
3264
3265    RETURN_LONG(sim);
3266}
3267/* }}} */
3268
3269/* {{{ php_stripslashes
3270 *
3271 * be careful, this edits the string in-place */
3272PHPAPI void php_stripslashes(char *str, int *len TSRMLS_DC)
3273{
3274    char *s, *t;
3275    int l;
3276
3277    if (len != NULL) {
3278        l = *len;
3279    } else {
3280        l = strlen(str);
3281    }
3282    s = str;
3283    t = str;
3284
3285    while (l > 0) {
3286        if (*t == '\\') {
3287            t++;                /* skip the slash */
3288            if (len != NULL) {
3289                (*len)--;
3290            }
3291            l--;
3292            if (l > 0) {
3293                if (*t == '0') {
3294                    *s++='\0';
3295                    t++;
3296                } else {
3297                    *s++ = *t++;    /* preserve the next character */
3298                }
3299                l--;
3300            }
3301        } else {
3302            *s++ = *t++;
3303            l--;
3304        }
3305    }
3306    if (s != t) {
3307        *s = '\0';
3308    }
3309}
3310/* }}} */
3311
3312/* {{{ proto string addcslashes(string str, string charlist)
3313   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...) */
3314PHP_FUNCTION(addcslashes)
3315{
3316    char *str, *what;
3317    int str_len, what_len;
3318
3319    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &str, &str_len, &what, &what_len) == FAILURE) {
3320        return;
3321    }
3322
3323    if (str_len == 0) {
3324        RETURN_EMPTY_STRING();
3325    }
3326
3327    if (what_len == 0) {
3328        RETURN_STRINGL(str, str_len, 1);
3329    }
3330
3331    Z_STRVAL_P(return_value) = php_addcslashes(str, str_len, &Z_STRLEN_P(return_value), 0, what, what_len TSRMLS_CC);
3332    RETURN_STRINGL(Z_STRVAL_P(return_value), Z_STRLEN_P(return_value), 0);
3333}
3334/* }}} */
3335
3336/* {{{ proto string addslashes(string str)
3337   Escapes single quote, double quotes and backslash characters in a string with backslashes */
3338PHP_FUNCTION(addslashes)
3339{
3340    char *str;
3341    int  str_len;
3342
3343    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
3344        return;
3345    }
3346
3347    if (str_len == 0) {
3348        RETURN_EMPTY_STRING();
3349    }
3350
3351    RETURN_STRING(php_addslashes(str,
3352                                 str_len,
3353                                 &Z_STRLEN_P(return_value), 0
3354                                 TSRMLS_CC), 0);
3355}
3356/* }}} */
3357
3358/* {{{ proto string stripcslashes(string str)
3359   Strips backslashes from a string. Uses C-style conventions */
3360PHP_FUNCTION(stripcslashes)
3361{
3362    char *str;
3363    int  str_len;
3364
3365    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
3366        return;
3367    }
3368
3369    ZVAL_STRINGL(return_value, str, str_len, 1);
3370    php_stripcslashes(Z_STRVAL_P(return_value), &Z_STRLEN_P(return_value));
3371}
3372/* }}} */
3373
3374/* {{{ proto string stripslashes(string str)
3375   Strips backslashes from a string */
3376PHP_FUNCTION(stripslashes)
3377{
3378    char *str;
3379    int  str_len;
3380
3381    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
3382        return;
3383    }
3384
3385    ZVAL_STRINGL(return_value, str, str_len, 1);
3386    php_stripslashes(Z_STRVAL_P(return_value), &Z_STRLEN_P(return_value) TSRMLS_CC);
3387}
3388/* }}} */
3389
3390#ifndef HAVE_STRERROR
3391/* {{{ php_strerror
3392 */
3393char *php_strerror(int errnum)
3394{
3395    extern int sys_nerr;
3396    extern char *sys_errlist[];
3397    TSRMLS_FETCH();
3398
3399    if ((unsigned int) errnum < sys_nerr) {
3400        return(sys_errlist[errnum]);
3401    }
3402
3403    (void) snprintf(BG(str_ebuf), sizeof(php_basic_globals.str_ebuf), "Unknown error: %d", errnum);
3404    return(BG(str_ebuf));
3405}
3406/* }}} */
3407#endif
3408
3409/* {{{ php_stripcslashes
3410 */
3411PHPAPI void php_stripcslashes(char *str, int *len)
3412{
3413    char *source, *target, *end;
3414    int  nlen = *len, i;
3415    char numtmp[4];
3416
3417    for (source=str, end=str+nlen, target=str; source < end; source++) {
3418        if (*source == '\\' && source+1 < end) {
3419            source++;
3420            switch (*source) {
3421                case 'n':  *target++='\n'; nlen--; break;
3422                case 'r':  *target++='\r'; nlen--; break;
3423                case 'a':  *target++='\a'; nlen--; break;
3424                case 't':  *target++='\t'; nlen--; break;
3425                case 'v':  *target++='\v'; nlen--; break;
3426                case 'b':  *target++='\b'; nlen--; break;
3427                case 'f':  *target++='\f'; nlen--; break;
3428                case '\\': *target++='\\'; nlen--; break;
3429                case 'x':
3430                    if (source+1 < end && isxdigit((int)(*(source+1)))) {
3431                        numtmp[0] = *++source;
3432                        if (source+1 < end && isxdigit((int)(*(source+1)))) {
3433                            numtmp[1] = *++source;
3434                            numtmp[2] = '\0';
3435                            nlen-=3;
3436                        } else {
3437                            numtmp[1] = '\0';
3438                            nlen-=2;
3439                        }
3440                        *target++=(char)strtol(numtmp, NULL, 16);
3441                        break;
3442                    }
3443                    /* break is left intentionally */
3444                default:
3445                    i=0;
3446                    while (source < end && *source >= '0' && *source <= '7' && i<3) {
3447                        numtmp[i++] = *source++;
3448                    }
3449                    if (i) {
3450                        numtmp[i]='\0';
3451                        *target++=(char)strtol(numtmp, NULL, 8);
3452                        nlen-=i;
3453                        source--;
3454                    } else {
3455                        *target++=*source;
3456                        nlen--;
3457                    }
3458            }
3459        } else {
3460            *target++=*source;
3461        }
3462    }
3463
3464    if (nlen != 0) {
3465        *target='\0';
3466    }
3467
3468    *len = nlen;
3469}
3470/* }}} */
3471
3472/* {{{ php_addcslashes
3473 */
3474PHPAPI char *php_addcslashes(const char *str, int length, int *new_length, int should_free, char *what, int wlength TSRMLS_DC)
3475{
3476    char flags[256];
3477    char *new_str = safe_emalloc(4, (length?length:(length=strlen(str))), 1);
3478    char *source, *target;
3479    char *end;
3480    char c;
3481    int  newlen;
3482
3483    if (!wlength) {
3484        wlength = strlen(what);
3485    }
3486
3487    php_charmask((unsigned char *)what, wlength, flags TSRMLS_CC);
3488
3489    for (source = (char*)str, end = source + length, target = new_str; source < end; source++) {
3490        c = *source;
3491        if (flags[(unsigned char)c]) {
3492            if ((unsigned char) c < 32 || (unsigned char) c > 126) {
3493                *target++ = '\\';
3494                switch (c) {
3495                    case '\n': *target++ = 'n'; break;
3496                    case '\t': *target++ = 't'; break;
3497                    case '\r': *target++ = 'r'; break;
3498                    case '\a': *target++ = 'a'; break;
3499                    case '\v': *target++ = 'v'; break;
3500                    case '\b': *target++ = 'b'; break;
3501                    case '\f': *target++ = 'f'; break;
3502                    default: target += sprintf(target, "%03o", (unsigned char) c);
3503                }
3504                continue;
3505            }
3506            *target++ = '\\';
3507        }
3508        *target++ = c;
3509    }
3510    *target = 0;
3511    newlen = target - new_str;
3512    if (target - new_str < length * 4) {
3513        new_str = erealloc(new_str, newlen + 1);
3514    }
3515    if (new_length) {
3516        *new_length = newlen;
3517    }
3518    if (should_free) {
3519        STR_FREE((char*)str);
3520    }
3521    return new_str;
3522}
3523/* }}} */
3524
3525/* {{{ php_addslashes
3526 */
3527PHPAPI char *php_addslashes(char *str, int length, int *new_length, int should_free TSRMLS_DC)
3528{
3529    /* maximum string length, worst case situation */
3530    char *new_str;
3531    char *source, *target;
3532    char *end;
3533    int local_new_length;
3534
3535    if (!new_length) {
3536        new_length = &local_new_length;
3537    }
3538    if (!str) {
3539        *new_length = 0;
3540        return str;
3541    }
3542    new_str = (char *) safe_emalloc(2, (length ? length : (length = strlen(str))), 1);
3543    source = str;
3544    end = source + length;
3545    target = new_str;
3546
3547    while (source < end) {
3548        switch (*source) {
3549            case '\0':
3550                *target++ = '\\';
3551                *target++ = '0';
3552                break;
3553            case '\'':
3554            case '\"':
3555            case '\\':
3556                *target++ = '\\';
3557                /* break is missing *intentionally* */
3558            default:
3559                *target++ = *source;
3560                break;
3561        }
3562
3563        source++;
3564    }
3565
3566    *target = 0;
3567    *new_length = target - new_str;
3568    if (should_free) {
3569        STR_FREE(str);
3570    }
3571    new_str = (char *) erealloc(new_str, *new_length + 1);
3572    return new_str;
3573}
3574/* }}} */
3575
3576#define _HEB_BLOCK_TYPE_ENG 1
3577#define _HEB_BLOCK_TYPE_HEB 2
3578#define isheb(c)      (((((unsigned char) c) >= 224) && (((unsigned char) c) <= 250)) ? 1 : 0)
3579#define _isblank(c)   (((((unsigned char) c) == ' '  || ((unsigned char) c) == '\t')) ? 1 : 0)
3580#define _isnewline(c) (((((unsigned char) c) == '\n' || ((unsigned char) c) == '\r')) ? 1 : 0)
3581
3582/* {{{ php_char_to_str_ex
3583 */
3584PHPAPI 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)
3585{
3586    int char_count = 0;
3587    int replaced = 0;
3588    char *source, *target, *tmp, *source_end=str+len, *tmp_end = NULL;
3589
3590    if (case_sensitivity) {
3591        char *p = str, *e = p + len;
3592        while ((p = memchr(p, from, (e - p)))) {
3593            char_count++;
3594            p++;
3595        }
3596    } else {
3597        for (source = str; source < source_end; source++) {
3598            if (tolower(*source) == tolower(from)) {
3599                char_count++;
3600            }
3601        }
3602    }
3603
3604    if (char_count == 0 && case_sensitivity) {
3605        ZVAL_STRINGL(result, str, len, 1);
3606        return 0;
3607    }
3608
3609    Z_STRLEN_P(result) = len + (char_count * (to_len - 1));
3610    Z_STRVAL_P(result) = target = safe_emalloc(char_count, to_len, len + 1);
3611    Z_TYPE_P(result) = IS_STRING;
3612
3613    if (case_sensitivity) {
3614        char *p = str, *e = p + len, *s = str;
3615        while ((p = memchr(p, from, (e - p)))) {
3616            memcpy(target, s, (p - s));
3617            target += p - s;
3618            memcpy(target, to, to_len);
3619            target += to_len;
3620            p++;
3621            s = p;
3622            if (replace_count) {
3623                *replace_count += 1;
3624            }
3625        }
3626        if (s < e) {
3627            memcpy(target, s, (e - s));
3628            target += e - s;
3629        }
3630    } else {
3631        for (source = str; source < source_end; source++) {
3632            if (tolower(*source) == tolower(from)) {
3633                replaced = 1;
3634                if (replace_count) {
3635                    *replace_count += 1;
3636                }
3637                for (tmp = to, tmp_end = tmp+to_len; tmp < tmp_end; tmp++) {
3638                    *target = *tmp;
3639                    target++;
3640                }
3641            } else {
3642                *target = *source;
3643                target++;
3644            }
3645        }
3646    }
3647    *target = 0;
3648    return replaced;
3649}
3650/* }}} */
3651
3652/* {{{ php_char_to_str
3653 */
3654PHPAPI int php_char_to_str(char *str, uint len, char from, char *to, int to_len, zval *result)
3655{
3656    return php_char_to_str_ex(str, len, from, to, to_len, result, 1, NULL);
3657}
3658/* }}} */
3659
3660/* {{{ php_str_to_str_ex
3661 */
3662PHPAPI char *php_str_to_str_ex(char *haystack, int length,
3663    char *needle, int needle_len, char *str, int str_len, int *_new_length, int case_sensitivity, int *replace_count)
3664{
3665    char *new_str;
3666
3667    if (needle_len < length) {
3668        char *end, *haystack_dup = NULL, *needle_dup = NULL;
3669        char *e, *s, *p, *r;
3670
3671        if (needle_len == str_len) {
3672            new_str = estrndup(haystack, length);
3673            *_new_length = length;
3674
3675            if (case_sensitivity) {
3676                end = new_str + length;
3677                for (p = new_str; (r = php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3678                    memcpy(r, str, str_len);
3679                    if (replace_count) {
3680                        (*replace_count)++;
3681                    }
3682                }
3683            } else {
3684                haystack_dup = estrndup(haystack, length);
3685                needle_dup = estrndup(needle, needle_len);
3686                php_strtolower(haystack_dup, length);
3687                php_strtolower(needle_dup, needle_len);
3688                end = haystack_dup + length;
3689                for (p = haystack_dup; (r = php_memnstr(p, needle_dup, needle_len, end)); p = r + needle_len) {
3690                    memcpy(new_str + (r - haystack_dup), str, str_len);
3691                    if (replace_count) {
3692                        (*replace_count)++;
3693                    }
3694                }
3695                efree(haystack_dup);
3696                efree(needle_dup);
3697            }
3698            return new_str;
3699        } else {
3700            if (!case_sensitivity) {
3701                haystack_dup = estrndup(haystack, length);
3702                needle_dup = estrndup(needle, needle_len);
3703                php_strtolower(haystack_dup, length);
3704                php_strtolower(needle_dup, needle_len);
3705            }
3706
3707            if (str_len < needle_len) {
3708                new_str = emalloc(length + 1);
3709            } else {
3710                int count = 0;
3711                char *o, *n, *endp;
3712
3713                if (case_sensitivity) {
3714                    o = haystack;
3715                    n = needle;
3716                } else {
3717                    o = haystack_dup;
3718                    n = needle_dup;
3719                }
3720                endp = o + length;
3721
3722                while ((o = php_memnstr(o, n, needle_len, endp))) {
3723                    o += needle_len;
3724                    count++;
3725                }
3726                if (count == 0) {
3727                    /* Needle doesn't occur, shortcircuit the actual replacement. */
3728                    if (haystack_dup) {
3729                        efree(haystack_dup);
3730                    }
3731                    if (needle_dup) {
3732                        efree(needle_dup);
3733                    }
3734                    new_str = estrndup(haystack, length);
3735                    if (_new_length) {
3736                        *_new_length = length;
3737                    }
3738                    return new_str;
3739                } else {
3740                    new_str = safe_emalloc(count, str_len - needle_len, length + 1);
3741                }
3742            }
3743
3744            e = s = new_str;
3745
3746            if (case_sensitivity) {
3747                end = haystack + length;
3748                for (p = haystack; (r = php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3749                    memcpy(e, p, r - p);
3750                    e += r - p;
3751                    memcpy(e, str, str_len);
3752                    e += str_len;
3753                    if (replace_count) {
3754                        (*replace_count)++;
3755                    }
3756                }
3757
3758                if (p < end) {
3759                    memcpy(e, p, end - p);
3760                    e += end - p;
3761                }
3762            } else {
3763                end = haystack_dup + length;
3764
3765                for (p = haystack_dup; (r = php_memnstr(p, needle_dup, needle_len, end)); p = r + needle_len) {
3766                    memcpy(e, haystack + (p - haystack_dup), r - p);
3767                    e += r - p;
3768                    memcpy(e, str, str_len);
3769                    e += str_len;
3770                    if (replace_count) {
3771                        (*replace_count)++;
3772                    }
3773                }
3774
3775                if (p < end) {
3776                    memcpy(e, haystack + (p - haystack_dup), end - p);
3777                    e += end - p;
3778                }
3779            }
3780
3781            if (haystack_dup) {
3782                efree(haystack_dup);
3783            }
3784            if (needle_dup) {
3785                efree(needle_dup);
3786            }
3787
3788            *e = '\0';
3789            *_new_length = e - s;
3790
3791            new_str = erealloc(new_str, *_new_length + 1);
3792            return new_str;
3793        }
3794    } else if (needle_len > length) {
3795nothing_todo:
3796        *_new_length = length;
3797        new_str = estrndup(haystack, length);
3798        return new_str;
3799    } else {
3800        if (case_sensitivity && memcmp(haystack, needle, length)) {
3801            goto nothing_todo;
3802        } else if (!case_sensitivity) {
3803            char *l_haystack, *l_needle;
3804
3805            l_haystack = estrndup(haystack, length);
3806            l_needle = estrndup(needle, length);
3807
3808            php_strtolower(l_haystack, length);
3809            php_strtolower(l_needle, length);
3810
3811            if (memcmp(l_haystack, l_needle, length)) {
3812                efree(l_haystack);
3813                efree(l_needle);
3814                goto nothing_todo;
3815            }
3816            efree(l_haystack);
3817            efree(l_needle);
3818        }
3819
3820        *_new_length = str_len;
3821        new_str = estrndup(str, str_len);
3822
3823        if (replace_count) {
3824            (*replace_count)++;
3825        }
3826        return new_str;
3827    }
3828
3829}
3830/* }}} */
3831
3832/* {{{ php_str_to_str
3833 */
3834PHPAPI char *php_str_to_str(char *haystack, int length,
3835    char *needle, int needle_len, char *str, int str_len, int *_new_length)
3836{
3837    return php_str_to_str_ex(haystack, length, needle, needle_len, str, str_len, _new_length, 1, NULL);
3838}
3839/* }}} */
3840
3841/* {{{ php_str_replace_in_subject
3842 */
3843static void php_str_replace_in_subject(zval *search, zval *replace, zval **subject, zval *result, int case_sensitivity, int *replace_count)
3844{
3845    zval        **search_entry,
3846                **replace_entry = NULL,
3847                  temp_result;
3848    char        *replace_value = NULL;
3849    int          replace_len = 0;
3850
3851    /* Make sure we're dealing with strings. */
3852    convert_to_string_ex(subject);
3853    Z_TYPE_P(result) = IS_STRING;
3854    if (Z_STRLEN_PP(subject) == 0) {
3855        ZVAL_STRINGL(result, "", 0, 1);
3856        return;
3857    }
3858
3859    /* If search is an array */
3860    if (Z_TYPE_P(search) == IS_ARRAY) {
3861        /* Duplicate subject string for repeated replacement */
3862        MAKE_COPY_ZVAL(subject, result);
3863
3864        zend_hash_internal_pointer_reset(Z_ARRVAL_P(search));
3865
3866        if (Z_TYPE_P(replace) == IS_ARRAY) {
3867            zend_hash_internal_pointer_reset(Z_ARRVAL_P(replace));
3868        } else {
3869            /* Set replacement value to the passed one */
3870            replace_value = Z_STRVAL_P(replace);
3871            replace_len = Z_STRLEN_P(replace);
3872        }
3873
3874        /* For each entry in the search array, get the entry */
3875        while (zend_hash_get_current_data(Z_ARRVAL_P(search), (void **) &search_entry) == SUCCESS) {
3876            /* Make sure we're dealing with strings. */
3877            SEPARATE_ZVAL(search_entry);
3878            convert_to_string(*search_entry);
3879            if (Z_STRLEN_PP(search_entry) == 0) {
3880                zend_hash_move_forward(Z_ARRVAL_P(search));
3881                if (Z_TYPE_P(replace) == IS_ARRAY) {
3882                    zend_hash_move_forward(Z_ARRVAL_P(replace));
3883                }
3884                continue;
3885            }
3886
3887            /* If replace is an array. */
3888            if (Z_TYPE_P(replace) == IS_ARRAY) {
3889                /* Get current entry */
3890                if (zend_hash_get_current_data(Z_ARRVAL_P(replace), (void **)&replace_entry) == SUCCESS) {
3891                    /* Make sure we're dealing with strings. */
3892                    convert_to_string_ex(replace_entry);
3893
3894                    /* Set replacement value to the one we got from array */
3895                    replace_value = Z_STRVAL_PP(replace_entry);
3896                    replace_len = Z_STRLEN_PP(replace_entry);
3897
3898                    zend_hash_move_forward(Z_ARRVAL_P(replace));
3899                } else {
3900                    /* We've run out of replacement strings, so use an empty one. */
3901                    replace_value = "";
3902                    replace_len = 0;
3903                }
3904            }
3905
3906            if (Z_STRLEN_PP(search_entry) == 1) {
3907                php_char_to_str_ex(Z_STRVAL_P(result),
3908                                Z_STRLEN_P(result),
3909                                Z_STRVAL_PP(search_entry)[0],
3910                                replace_value,
3911                                replace_len,
3912                                &temp_result,
3913                                case_sensitivity,
3914                                replace_count);
3915            } else if (Z_STRLEN_PP(search_entry) > 1) {
3916                Z_STRVAL(temp_result) = php_str_to_str_ex(Z_STRVAL_P(result), Z_STRLEN_P(result),
3917                                                           Z_STRVAL_PP(search_entry), Z_STRLEN_PP(search_entry),
3918                                                           replace_value, replace_len, &Z_STRLEN(temp_result), case_sensitivity, replace_count);
3919            }
3920
3921           str_efree(Z_STRVAL_P(result));
3922            Z_STRVAL_P(result) = Z_STRVAL(temp_result);
3923            Z_STRLEN_P(result) = Z_STRLEN(temp_result);
3924
3925            if (Z_STRLEN_P(result) == 0) {
3926                return;
3927            }
3928
3929            zend_hash_move_forward(Z_ARRVAL_P(search));
3930        }
3931    } else {
3932        if (Z_STRLEN_P(search) == 1) {
3933            php_char_to_str_ex(Z_STRVAL_PP(subject),
3934                            Z_STRLEN_PP(subject),
3935                            Z_STRVAL_P(search)[0],
3936                            Z_STRVAL_P(replace),
3937                            Z_STRLEN_P(replace),
3938                            result,
3939                            case_sensitivity,
3940                            replace_count);
3941        } else if (Z_STRLEN_P(search) > 1) {
3942            Z_STRVAL_P(result) = php_str_to_str_ex(Z_STRVAL_PP(subject), Z_STRLEN_PP(subject),
3943                                                    Z_STRVAL_P(search), Z_STRLEN_P(search),
3944                                                    Z_STRVAL_P(replace), Z_STRLEN_P(replace), &Z_STRLEN_P(result), case_sensitivity, replace_count);
3945        } else {
3946            MAKE_COPY_ZVAL(subject, result);
3947        }
3948    }
3949}
3950/* }}} */
3951
3952/* {{{ php_str_replace_common
3953 */
3954static void php_str_replace_common(INTERNAL_FUNCTION_PARAMETERS, int case_sensitivity)
3955{
3956    zval **subject, **search, **replace, **subject_entry, **zcount = NULL;
3957    zval *result;
3958    char *string_key;
3959    uint string_key_len;
3960    ulong num_key;
3961    int count = 0;
3962    int argc = ZEND_NUM_ARGS();
3963
3964    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZZ|Z", &search, &replace, &subject, &zcount) == FAILURE) {
3965        return;
3966    }
3967
3968    SEPARATE_ZVAL(search);
3969    SEPARATE_ZVAL(replace);
3970    SEPARATE_ZVAL(subject);
3971
3972    /* Make sure we're dealing with strings and do the replacement. */
3973    if (Z_TYPE_PP(search) != IS_ARRAY) {
3974        convert_to_string_ex(search);
3975        convert_to_string_ex(replace);
3976    } else if (Z_TYPE_PP(replace) != IS_ARRAY) {
3977        convert_to_string_ex(replace);
3978    }
3979
3980    /* if subject is an array */
3981    if (Z_TYPE_PP(subject) == IS_ARRAY) {
3982        array_init(return_value);
3983        zend_hash_internal_pointer_reset(Z_ARRVAL_PP(subject));
3984
3985        /* For each subject entry, convert it to string, then perform replacement
3986           and add the result to the return_value array. */
3987        while (zend_hash_get_current_data(Z_ARRVAL_PP(subject), (void **)&subject_entry) == SUCCESS) {
3988            if (Z_TYPE_PP(subject_entry) != IS_ARRAY && Z_TYPE_PP(subject_entry) != IS_OBJECT) {
3989                MAKE_STD_ZVAL(result);
3990                SEPARATE_ZVAL(subject_entry);
3991                php_str_replace_in_subject(*search, *replace, subject_entry, result, case_sensitivity, (argc > 3) ? &count : NULL);
3992            } else {
3993                ALLOC_ZVAL(result);
3994                Z_ADDREF_P(*subject_entry);
3995                COPY_PZVAL_TO_ZVAL(*result, *subject_entry);
3996            }
3997            /* Add to return array */
3998            switch (zend_hash_get_current_key_ex(Z_ARRVAL_PP(subject), &string_key,
3999                                                &string_key_len, &num_key, 0, NULL)) {
4000                case HASH_KEY_IS_STRING:
4001                    add_assoc_zval_ex(return_value, string_key, string_key_len, result);
4002                    break;
4003
4004                case HASH_KEY_IS_LONG:
4005                    add_index_zval(return_value, num_key, result);
4006                    break;
4007            }
4008
4009            zend_hash_move_forward(Z_ARRVAL_PP(subject));
4010        }
4011    } else {    /* if subject is not an array */
4012        php_str_replace_in_subject(*search, *replace, subject, return_value, case_sensitivity, (argc > 3) ? &count : NULL);
4013    }
4014    if (argc > 3) {
4015        zval_dtor(*zcount);
4016        ZVAL_LONG(*zcount, count);
4017    }
4018}
4019/* }}} */
4020
4021/* {{{ proto mixed str_replace(mixed search, mixed replace, mixed subject [, int &replace_count])
4022   Replaces all occurrences of search in haystack with replace */
4023PHP_FUNCTION(str_replace)
4024{
4025    php_str_replace_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4026}
4027/* }}} */
4028
4029/* {{{ proto mixed str_ireplace(mixed search, mixed replace, mixed subject [, int &replace_count])
4030   Replaces all occurrences of search in haystack with replace / case-insensitive */
4031PHP_FUNCTION(str_ireplace)
4032{
4033    php_str_replace_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4034}
4035/* }}} */
4036
4037/* {{{ php_hebrev
4038 *
4039 * Converts Logical Hebrew text (Hebrew Windows style) to Visual text
4040 * Cheers/complaints/flames - Zeev Suraski <zeev@php.net>
4041 */
4042static void php_hebrev(INTERNAL_FUNCTION_PARAMETERS, int convert_newlines)
4043{
4044    char *str;
4045    char *heb_str, *tmp, *target, *broken_str;
4046    int block_start, block_end, block_type, block_length, i;
4047    long max_chars=0;
4048    int begin, end, char_count, orig_begin;
4049    int str_len;
4050
4051    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &str, &str_len, &max_chars) == FAILURE) {
4052        return;
4053    }
4054
4055    if (str_len == 0) {
4056        RETURN_FALSE;
4057    }
4058
4059    tmp = str;
4060    block_start=block_end=0;
4061
4062    heb_str = (char *) emalloc(str_len+1);
4063    target = heb_str+str_len;
4064    *target = 0;
4065    target--;
4066
4067    block_length=0;
4068
4069    if (isheb(*tmp)) {
4070        block_type = _HEB_BLOCK_TYPE_HEB;
4071    } else {
4072        block_type = _HEB_BLOCK_TYPE_ENG;
4073    }
4074
4075    do {
4076        if (block_type == _HEB_BLOCK_TYPE_HEB) {
4077            while ((isheb((int)*(tmp+1)) || _isblank((int)*(tmp+1)) || ispunct((int)*(tmp+1)) || (int)*(tmp+1)=='\n' ) && block_end<str_len-1) {
4078                tmp++;
4079                block_end++;
4080                block_length++;
4081            }
4082            for (i = block_start; i<= block_end; i++) {
4083                *target = str[i];
4084                switch (*target) {
4085                    case '(':
4086                        *target = ')';
4087                        break;
4088                    case ')':
4089                        *target = '(';
4090                        break;
4091                    case '[':
4092                        *target = ']';
4093                        break;
4094                    case ']':
4095                        *target = '[';
4096                        break;
4097                    case '{':
4098                        *target = '}';
4099                        break;
4100                    case '}':
4101                        *target = '{';
4102                        break;
4103                    case '<':
4104                        *target = '>';
4105                        break;
4106                    case '>':
4107                        *target = '<';
4108                        break;
4109                    case '\\':
4110                        *target = '/';
4111                        break;
4112                    case '/':
4113                        *target = '\\';
4114                        break;
4115                    default:
4116                        break;
4117                }
4118                target--;
4119            }
4120            block_type = _HEB_BLOCK_TYPE_ENG;
4121        } else {
4122            while (!isheb(*(tmp+1)) && (int)*(tmp+1)!='\n' && block_end < str_len-1) {
4123                tmp++;
4124                block_end++;
4125                block_length++;
4126            }
4127            while ((_isblank((int)*tmp) || ispunct((int)*tmp)) && *tmp!='/' && *tmp!='-' && block_end > block_start) {
4128                tmp--;
4129                block_end--;
4130            }
4131            for (i = block_end; i >= block_start; i--) {
4132                *target = str[i];
4133                target--;
4134            }
4135            block_type = _HEB_BLOCK_TYPE_HEB;
4136        }
4137        block_start=block_end+1;
4138    } while (block_end < str_len-1);
4139
4140
4141    broken_str = (char *) emalloc(str_len+1);
4142    begin=end=str_len-1;
4143    target = broken_str;
4144
4145    while (1) {
4146        char_count=0;
4147        while ((!max_chars || char_count < max_chars) && begin > 0) {
4148            char_count++;
4149            begin--;
4150            if (begin <= 0 || _isnewline(heb_str[begin])) {
4151                while (begin > 0 && _isnewline(heb_str[begin-1])) {
4152                    begin--;
4153                    char_count++;
4154                }
4155                break;
4156            }
4157        }
4158        if (char_count == max_chars) { /* try to avoid breaking words */
4159            int new_char_count=char_count, new_begin=begin;
4160
4161            while (new_char_count > 0) {
4162                if (_isblank(heb_str[new_begin]) || _isnewline(heb_str[new_begin])) {
4163                    break;
4164                }
4165                new_begin++;
4166                new_char_count--;
4167            }
4168            if (new_char_count > 0) {
4169                begin=new_begin;
4170            }
4171        }
4172        orig_begin=begin;
4173
4174        if (_isblank(heb_str[begin])) {
4175            heb_str[begin]='\n';
4176        }
4177        while (begin <= end && _isnewline(heb_str[begin])) { /* skip leading newlines */
4178            begin++;
4179        }
4180        for (i = begin; i <= end; i++) { /* copy content */
4181            *target = heb_str[i];
4182            target++;
4183        }
4184        for (i = orig_begin; i <= end && _isnewline(heb_str[i]); i++) {
4185            *target = heb_str[i];
4186            target++;
4187        }
4188        begin=orig_begin;
4189
4190        if (begin <= 0) {
4191            *target = 0;
4192            break;
4193        }
4194        begin--;
4195        end=begin;
4196    }
4197    efree(heb_str);
4198
4199    if (convert_newlines) {
4200        php_char_to_str(broken_str, str_len,'\n', "<br />\n", 7, return_value);
4201        efree(broken_str);
4202    } else {
4203        Z_STRVAL_P(return_value) = broken_str;
4204        Z_STRLEN_P(return_value) = str_len;
4205        Z_TYPE_P(return_value) = IS_STRING;
4206    }
4207}
4208/* }}} */
4209
4210/* {{{ proto string hebrev(string str [, int max_chars_per_line])
4211   Converts logical Hebrew text to visual text */
4212PHP_FUNCTION(hebrev)
4213{
4214    php_hebrev(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4215}
4216/* }}} */
4217
4218/* {{{ proto string hebrevc(string str [, int max_chars_per_line])
4219   Converts logical Hebrew text to visual text with newline conversion */
4220PHP_FUNCTION(hebrevc)
4221{
4222    php_hebrev(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4223}
4224/* }}} */
4225
4226/* {{{ proto string nl2br(string str [, bool is_xhtml])
4227   Converts newlines to HTML line breaks */
4228PHP_FUNCTION(nl2br)
4229{
4230    /* in brief this inserts <br /> or <br> before matched regexp \n\r?|\r\n? */
4231    char        *tmp, *str;
4232    int     new_length;
4233    char        *end, *target;
4234    int     repl_cnt = 0;
4235    int     str_len;
4236    zend_bool   is_xhtml = 1;
4237
4238    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &str, &str_len, &is_xhtml) == FAILURE) {
4239        return;
4240    }
4241
4242    tmp = str;
4243    end = str + str_len;
4244
4245    /* it is really faster to scan twice and allocate mem once instead of scanning once
4246       and constantly reallocing */
4247    while (tmp < end) {
4248        if (*tmp == '\r') {
4249            if (*(tmp+1) == '\n') {
4250                tmp++;
4251            }
4252            repl_cnt++;
4253        } else if (*tmp == '\n') {
4254            if (*(tmp+1) == '\r') {
4255                tmp++;
4256            }
4257            repl_cnt++;
4258        }
4259
4260        tmp++;
4261    }
4262
4263    if (repl_cnt == 0) {
4264        RETURN_STRINGL(str, str_len, 1);
4265    }
4266
4267    {
4268        size_t repl_len = is_xhtml ? (sizeof("<br />") - 1) : (sizeof("<br>") - 1);
4269
4270        new_length = str_len + repl_cnt * repl_len;
4271        tmp = target = safe_emalloc(repl_cnt, repl_len, str_len + 1);
4272    }
4273
4274    while (str < end) {
4275        switch (*str) {
4276            case '\r':
4277            case '\n':
4278                *target++ = '<';
4279                *target++ = 'b';
4280                *target++ = 'r';
4281
4282                if (is_xhtml) {
4283                    *target++ = ' ';
4284                    *target++ = '/';
4285                }
4286
4287                *target++ = '>';
4288
4289                if ((*str == '\r' && *(str+1) == '\n') || (*str == '\n' && *(str+1) == '\r')) {
4290                    *target++ = *str++;
4291                }
4292                /* lack of a break; is intentional */
4293            default:
4294                *target++ = *str;
4295        }
4296
4297        str++;
4298    }
4299
4300    *target = '\0';
4301
4302    RETURN_STRINGL(tmp, new_length, 0);
4303}
4304/* }}} */
4305
4306/* {{{ proto string strip_tags(string str [, string allowable_tags])
4307   Strips HTML and PHP tags from a string */
4308PHP_FUNCTION(strip_tags)
4309{
4310    char *buf;
4311    char *str;
4312    zval **allow=NULL;
4313    char *allowed_tags=NULL;
4314    int allowed_tags_len=0;
4315    int str_len;
4316    size_t retval_len;
4317
4318    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|Z", &str, &str_len, &allow) == FAILURE) {
4319        return;
4320    }
4321
4322    /* To maintain a certain BC, we allow anything for the second parameter and return original string */
4323    if (allow != NULL) {
4324        convert_to_string_ex(allow);
4325        allowed_tags = Z_STRVAL_PP(allow);
4326        allowed_tags_len = Z_STRLEN_PP(allow);
4327    }
4328
4329    buf = estrndup(str, str_len);
4330    retval_len = php_strip_tags_ex(buf, str_len, NULL, allowed_tags, allowed_tags_len, 0);
4331    RETURN_STRINGL(buf, retval_len, 0);
4332}
4333/* }}} */
4334
4335/* {{{ proto string setlocale(mixed category, string locale [, string ...])
4336   Set locale information */
4337PHP_FUNCTION(setlocale)
4338{
4339    zval ***args = NULL;
4340    zval **pcategory, **plocale;
4341    int num_args, cat, i = 0;
4342    char *loc, *retval;
4343
4344    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z+", &pcategory, &args, &num_args) == FAILURE) {
4345        return;
4346    }
4347
4348#ifdef HAVE_SETLOCALE
4349    if (Z_TYPE_PP(pcategory) == IS_LONG) {
4350        convert_to_long_ex(pcategory);
4351        cat = Z_LVAL_PP(pcategory);
4352    } else {
4353        /* FIXME: The following behaviour should be removed. */
4354        char *category;
4355
4356        php_error_docref(NULL TSRMLS_CC, E_DEPRECATED, "Passing locale category name as string is deprecated. Use the LC_* -constants instead");
4357
4358        convert_to_string_ex(pcategory);
4359        category = Z_STRVAL_PP(pcategory);
4360
4361        if (!strcasecmp("LC_ALL", category)) {
4362            cat = LC_ALL;
4363        } else if (!strcasecmp("LC_COLLATE", category)) {
4364            cat = LC_COLLATE;
4365        } else if (!strcasecmp("LC_CTYPE", category)) {
4366            cat = LC_CTYPE;
4367#ifdef LC_MESSAGES
4368        } else if (!strcasecmp("LC_MESSAGES", category)) {
4369            cat = LC_MESSAGES;
4370#endif
4371        } else if (!strcasecmp("LC_MONETARY", category)) {
4372            cat = LC_MONETARY;
4373        } else if (!strcasecmp("LC_NUMERIC", category)) {
4374            cat = LC_NUMERIC;
4375        } else if (!strcasecmp("LC_TIME", category)) {
4376            cat = LC_TIME;
4377        } else {
4378            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);
4379
4380            if (args) {
4381                efree(args);
4382            }
4383            RETURN_FALSE;
4384        }
4385    }
4386
4387    if (Z_TYPE_PP(args[0]) == IS_ARRAY) {
4388        zend_hash_internal_pointer_reset(Z_ARRVAL_PP(args[0]));
4389    }
4390
4391    while (1) {
4392        if (Z_TYPE_PP(args[0]) == IS_ARRAY) {
4393            if (!zend_hash_num_elements(Z_ARRVAL_PP(args[0]))) {
4394                break;
4395            }
4396            zend_hash_get_current_data(Z_ARRVAL_PP(args[0]), (void **)&plocale);
4397        } else {
4398            plocale = args[i];
4399        }
4400
4401        convert_to_string_ex(plocale);
4402
4403        if (!strcmp ("0", Z_STRVAL_PP(plocale))) {
4404            loc = NULL;
4405        } else {
4406            loc = Z_STRVAL_PP(plocale);
4407            if (Z_STRLEN_PP(plocale) >= 255) {
4408                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Specified locale name is too long");
4409                break;
4410            }
4411        }
4412
4413        retval = php_my_setlocale(cat, loc);
4414        zend_update_current_locale();
4415        if (retval) {
4416            /* Remember if locale was changed */
4417            if (loc) {
4418                STR_FREE(BG(locale_string));
4419                BG(locale_string) = estrdup(retval);
4420            }
4421
4422            if (args) {
4423                efree(args);
4424            }
4425            RETURN_STRING(retval, 1);
4426        }
4427
4428        if (Z_TYPE_PP(args[0]) == IS_ARRAY) {
4429            if (zend_hash_move_forward(Z_ARRVAL_PP(args[0])) == FAILURE) break;
4430        } else {
4431            if (++i >= num_args) break;
4432        }
4433    }
4434
4435#endif
4436    if (args) {
4437        efree(args);
4438    }
4439    RETURN_FALSE;
4440}
4441/* }}} */
4442
4443/* {{{ proto void parse_str(string encoded_string [, array result])
4444   Parses GET/POST/COOKIE data and sets global variables */
4445PHP_FUNCTION(parse_str)
4446{
4447    char *arg;
4448    zval *arrayArg = NULL;
4449    char *res = NULL;
4450    int arglen;
4451
4452    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z", &arg, &arglen, &arrayArg) == FAILURE) {
4453        return;
4454    }
4455
4456    res = estrndup(arg, arglen);
4457
4458    if (arrayArg == NULL) {
4459        zval tmp;
4460
4461        if (!EG(active_symbol_table)) {
4462            zend_rebuild_symbol_table(TSRMLS_C);
4463        }
4464        Z_ARRVAL(tmp) = EG(active_symbol_table);
4465        sapi_module.treat_data(PARSE_STRING, res, &tmp TSRMLS_CC);
4466    } else  {
4467        zval ret;
4468
4469        array_init(&ret);
4470        sapi_module.treat_data(PARSE_STRING, res, &ret TSRMLS_CC);
4471        /* Clear out the array that was passed in. */
4472        zval_dtor(arrayArg);
4473        ZVAL_COPY_VALUE(arrayArg, &ret);
4474    }
4475}
4476/* }}} */
4477
4478#define PHP_TAG_BUF_SIZE 1023
4479
4480/* {{{ php_tag_find
4481 *
4482 * Check if tag is in a set of tags
4483 *
4484 * states:
4485 *
4486 * 0 start tag
4487 * 1 first non-whitespace char seen
4488 */
4489int php_tag_find(char *tag, int len, char *set) {
4490    char c, *n, *t;
4491    int state=0, done=0;
4492    char *norm;
4493
4494    if (len <= 0) {
4495        return 0;
4496    }
4497
4498    norm = emalloc(len+1);
4499
4500    n = norm;
4501    t = tag;
4502    c = tolower(*t);
4503    /*
4504       normalize the tag removing leading and trailing whitespace
4505       and turn any <a whatever...> into just <a> and any </tag>
4506       into <tag>
4507    */
4508    while (!done) {
4509        switch (c) {
4510            case '<':
4511                *(n++) = c;
4512                break;
4513            case '>':
4514                done =1;
4515                break;
4516            default:
4517                if (!isspace((int)c)) {
4518                    if (state == 0) {
4519                        state=1;
4520                    }
4521                    if (c != '/') {
4522                        *(n++) = c;
4523                    }
4524                } else {
4525                    if (state == 1)
4526                        done=1;
4527                }
4528                break;
4529        }
4530        c = tolower(*(++t));
4531    }
4532    *(n++) = '>';
4533    *n = '\0';
4534    if (strstr(set, norm)) {
4535        done=1;
4536    } else {
4537        done=0;
4538    }
4539    efree(norm);
4540    return done;
4541}
4542/* }}} */
4543
4544PHPAPI size_t php_strip_tags(char *rbuf, int len, int *stateptr, char *allow, int allow_len) /* {{{ */
4545{
4546    return php_strip_tags_ex(rbuf, len, stateptr, allow, allow_len, 0);
4547}
4548/* }}} */
4549
4550/* {{{ php_strip_tags
4551
4552    A simple little state-machine to strip out html and php tags
4553
4554    State 0 is the output state, State 1 means we are inside a
4555    normal html tag and state 2 means we are inside a php tag.
4556
4557    The state variable is passed in to allow a function like fgetss
4558    to maintain state across calls to the function.
4559
4560    lc holds the last significant character read and br is a bracket
4561    counter.
4562
4563    When an allow string is passed in we keep track of the string
4564    in state 1 and when the tag is closed check it against the
4565    allow string to see if we should allow it.
4566
4567    swm: Added ability to strip <?xml tags without assuming it PHP
4568    code.
4569*/
4570PHPAPI size_t php_strip_tags_ex(char *rbuf, int len, int *stateptr, char *allow, int allow_len, zend_bool allow_tag_spaces)
4571{
4572    char *tbuf, *buf, *p, *tp, *rp, c, lc;
4573    int br, i=0, depth=0, in_q = 0;
4574    int state = 0, pos;
4575    char *allow_free = NULL;
4576
4577    if (stateptr)
4578        state = *stateptr;
4579
4580    buf = estrndup(rbuf, len);
4581    c = *buf;
4582    lc = '\0';
4583    p = buf;
4584    rp = rbuf;
4585    br = 0;
4586    if (allow) {
4587        if (IS_INTERNED(allow)) {
4588            allow_free = allow = zend_str_tolower_dup(allow, allow_len);
4589        } else {
4590            allow_free = NULL;
4591            php_strtolower(allow, allow_len);
4592        }
4593        tbuf = emalloc(PHP_TAG_BUF_SIZE + 1);
4594        tp = tbuf;
4595    } else {
4596        tbuf = tp = NULL;
4597    }
4598
4599    while (i < len) {
4600        switch (c) {
4601            case '\0':
4602                break;
4603            case '<':
4604                if (in_q) {
4605                    break;
4606                }
4607                if (isspace(*(p + 1)) && !allow_tag_spaces) {
4608                    goto reg_char;
4609                }
4610                if (state == 0) {
4611                    lc = '<';
4612                    state = 1;
4613                    if (allow) {
4614                        if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4615                            pos = tp - tbuf;
4616                            tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4617                            tp = tbuf + pos;
4618                        }
4619                        *(tp++) = '<';
4620                    }
4621                } else if (state == 1) {
4622                    depth++;
4623                }
4624                break;
4625
4626            case '(':
4627                if (state == 2) {
4628                    if (lc != '"' && lc != '\'') {
4629                        lc = '(';
4630                        br++;
4631                    }
4632                } else if (allow && state == 1) {
4633                    if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4634                        pos = tp - tbuf;
4635                        tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4636                        tp = tbuf + pos;
4637                    }
4638                    *(tp++) = c;
4639                } else if (state == 0) {
4640                    *(rp++) = c;
4641                }
4642                break;
4643
4644            case ')':
4645                if (state == 2) {
4646                    if (lc != '"' && lc != '\'') {
4647                        lc = ')';
4648                        br--;
4649                    }
4650                } else if (allow && state == 1) {
4651                    if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4652                        pos = tp - tbuf;
4653                        tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4654                        tp = tbuf + pos;
4655                    }
4656                    *(tp++) = c;
4657                } else if (state == 0) {
4658                    *(rp++) = c;
4659                }
4660                break;
4661
4662            case '>':
4663                if (depth) {
4664                    depth--;
4665                    break;
4666                }
4667
4668                if (in_q) {
4669                    break;
4670                }
4671
4672                switch (state) {
4673                    case 1: /* HTML/XML */
4674                        lc = '>';
4675                        in_q = state = 0;
4676                        if (allow) {
4677                            if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4678                                pos = tp - tbuf;
4679                                tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4680                                tp = tbuf + pos;
4681                            }
4682                            *(tp++) = '>';
4683                            *tp='\0';
4684                            if (php_tag_find(tbuf, tp-tbuf, allow)) {
4685                                memcpy(rp, tbuf, tp-tbuf);
4686                                rp += tp-tbuf;
4687                            }
4688                            tp = tbuf;
4689                        }
4690                        break;
4691
4692                    case 2: /* PHP */
4693                        if (!br && lc != '\"' && *(p-1) == '?') {
4694                            in_q = state = 0;
4695                            tp = tbuf;
4696                        }
4697                        break;
4698
4699                    case 3:
4700                        in_q = state = 0;
4701                        tp = tbuf;
4702                        break;
4703
4704                    case 4: /* JavaScript/CSS/etc... */
4705                        if (p >= buf + 2 && *(p-1) == '-' && *(p-2) == '-') {
4706                            in_q = state = 0;
4707                            tp = tbuf;
4708                        }
4709                        break;
4710
4711                    default:
4712                        *(rp++) = c;
4713                        break;
4714                }
4715                break;
4716
4717            case '"':
4718            case '\'':
4719                if (state == 4) {
4720                    /* Inside <!-- comment --> */
4721                    break;
4722                } else if (state == 2 && *(p-1) != '\\') {
4723                    if (lc == c) {
4724                        lc = '\0';
4725                    } else if (lc != '\\') {
4726                        lc = c;
4727                    }
4728                } else if (state == 0) {
4729                    *(rp++) = c;
4730                } else if (allow && state == 1) {
4731                    if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4732                        pos = tp - tbuf;
4733                        tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4734                        tp = tbuf + pos;
4735                    }
4736                    *(tp++) = c;
4737                }
4738                if (state && p != buf && (state == 1 || *(p-1) != '\\') && (!in_q || *p == in_q)) {
4739                    if (in_q) {
4740                        in_q = 0;
4741                    } else {
4742                        in_q = *p;
4743                    }
4744                }
4745                break;
4746
4747            case '!':
4748                /* JavaScript & Other HTML scripting languages */
4749                if (state == 1 && *(p-1) == '<') {
4750                    state = 3;
4751                    lc = c;
4752                } else {
4753                    if (state == 0) {
4754                        *(rp++) = c;
4755                    } else if (allow && state == 1) {
4756                        if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4757                            pos = tp - tbuf;
4758                            tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4759                            tp = tbuf + pos;
4760                        }
4761                        *(tp++) = c;
4762                    }
4763                }
4764                break;
4765
4766            case '-':
4767                if (state == 3 && p >= buf + 2 && *(p-1) == '-' && *(p-2) == '!') {
4768                    state = 4;
4769                } else {
4770                    goto reg_char;
4771                }
4772                break;
4773
4774            case '?':
4775
4776                if (state == 1 && *(p-1) == '<') {
4777                    br=0;
4778                    state=2;
4779                    break;
4780                }
4781
4782            case 'E':
4783            case 'e':
4784                /* !DOCTYPE exception */
4785                if (state==3 && p > buf+6
4786                             && tolower(*(p-1)) == 'p'
4787                             && tolower(*(p-2)) == 'y'
4788                             && tolower(*(p-3)) == 't'
4789                             && tolower(*(p-4)) == 'c'
4790                             && tolower(*(p-5)) == 'o'
4791                             && tolower(*(p-6)) == 'd') {
4792                    state = 1;
4793                    break;
4794                }
4795                /* fall-through */
4796
4797            case 'l':
4798            case 'L':
4799
4800                /* swm: If we encounter '<?xml' then we shouldn't be in
4801                 * state == 2 (PHP). Switch back to HTML.
4802                 */
4803
4804                if (state == 2 && p > buf+2 && strncasecmp(p-2, "xm", 2) == 0) {
4805                    state = 1;
4806                    break;
4807                }
4808
4809                /* fall-through */
4810            default:
4811reg_char:
4812                if (state == 0) {
4813                    *(rp++) = c;
4814                } else if (allow && state == 1) {
4815                    if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
4816                        pos = tp - tbuf;
4817                        tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
4818                        tp = tbuf + pos;
4819                    }
4820                    *(tp++) = c;
4821                }
4822                break;
4823        }
4824        c = *(++p);
4825        i++;
4826    }
4827    if (rp < rbuf + len) {
4828        *rp = '\0';
4829    }
4830    efree(buf);
4831    if (allow) {
4832        efree(tbuf);
4833        if (allow_free) {
4834            efree(allow_free);
4835        }
4836    }
4837    if (stateptr)
4838        *stateptr = state;
4839
4840    return (size_t)(rp - rbuf);
4841}
4842/* }}} */
4843
4844/* {{{ proto array str_getcsv(string input[, string delimiter[, string enclosure[, string escape]]])
4845Parse a CSV string into an array */
4846PHP_FUNCTION(str_getcsv)
4847{
4848    char *str, delim = ',', enc = '"', esc = '\\';
4849    char *delim_str = NULL, *enc_str = NULL, *esc_str = NULL;
4850    int str_len = 0, delim_len = 0, enc_len = 0, esc_len = 0;
4851
4852    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|sss", &str, &str_len, &delim_str, &delim_len,
4853        &enc_str, &enc_len, &esc_str, &esc_len) == FAILURE) {
4854        return;
4855    }
4856
4857    delim = delim_len ? delim_str[0] : delim;
4858    enc = enc_len ? enc_str[0] : enc;
4859    esc = esc_len ? esc_str[0] : esc;
4860
4861    php_fgetcsv(NULL, delim, enc, esc, str_len, str, return_value TSRMLS_CC);
4862}
4863/* }}} */
4864
4865/* {{{ proto string str_repeat(string input, int mult)
4866   Returns the input string repeat mult times */
4867PHP_FUNCTION(str_repeat)
4868{
4869    char        *input_str;     /* Input string */
4870    int         input_len;
4871    long        mult;           /* Multiplier */
4872    char        *result;        /* Resulting string */
4873    size_t      result_len;     /* Length of the resulting string */
4874
4875    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &input_str, &input_len, &mult) == FAILURE) {
4876        return;
4877    }
4878
4879    if (mult < 0) {
4880        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Second argument has to be greater than or equal to 0");
4881        return;
4882    }
4883
4884    /* Don't waste our time if it's empty */
4885    /* ... or if the multiplier is zero */
4886    if (input_len == 0 || mult == 0)
4887        RETURN_EMPTY_STRING();
4888
4889    /* Initialize the result string */
4890    result_len = input_len * mult;
4891    result = (char *)safe_emalloc(input_len, mult, 1);
4892
4893    /* Heavy optimization for situations where input string is 1 byte long */
4894    if (input_len == 1) {
4895        memset(result, *(input_str), mult);
4896    } else {
4897        char *s, *e, *ee;
4898        int l=0;
4899        memcpy(result, input_str, input_len);
4900        s = result;
4901        e = result + input_len;
4902        ee = result + result_len;
4903
4904        while (e<ee) {
4905            l = (e-s) < (ee-e) ? (e-s) : (ee-e);
4906            memmove(e, s, l);
4907            e += l;
4908        }
4909    }
4910
4911    result[result_len] = '\0';
4912
4913    RETURN_STRINGL(result, result_len, 0);
4914}
4915/* }}} */
4916
4917/* {{{ proto mixed count_chars(string input [, int mode])
4918   Returns info about what characters are used in input */
4919PHP_FUNCTION(count_chars)
4920{
4921    char *input;
4922    int chars[256];
4923    long mymode=0;
4924    unsigned char *buf;
4925    int len, inx;
4926    char retstr[256];
4927    int retlen=0;
4928
4929    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &input, &len, &mymode) == FAILURE) {
4930        return;
4931    }
4932
4933    if (mymode < 0 || mymode > 4) {
4934        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown mode");
4935        RETURN_FALSE;
4936    }
4937
4938    buf = (unsigned char *) input;
4939    memset((void*) chars, 0, sizeof(chars));
4940
4941    while (len > 0) {
4942        chars[*buf]++;
4943        buf++;
4944        len--;
4945    }
4946
4947    if (mymode < 3) {
4948        array_init(return_value);
4949    }
4950
4951    for (inx = 0; inx < 256; inx++) {
4952        switch (mymode) {
4953            case 0:
4954                add_index_long(return_value, inx, chars[inx]);
4955                break;
4956            case 1:
4957                if (chars[inx] != 0) {
4958                    add_index_long(return_value, inx, chars[inx]);
4959                }
4960                break;
4961            case 2:
4962                if (chars[inx] == 0) {
4963                    add_index_long(return_value, inx, chars[inx]);
4964                }
4965                break;
4966            case 3:
4967                if (chars[inx] != 0) {
4968                    retstr[retlen++] = inx;
4969                }
4970                break;
4971            case 4:
4972                if (chars[inx] == 0) {
4973                    retstr[retlen++] = inx;
4974                }
4975                break;
4976        }
4977    }
4978
4979    if (mymode >= 3 && mymode <= 4) {
4980        RETURN_STRINGL(retstr, retlen, 1);
4981    }
4982}
4983/* }}} */
4984
4985/* {{{ php_strnatcmp
4986 */
4987static void php_strnatcmp(INTERNAL_FUNCTION_PARAMETERS, int fold_case)
4988{
4989    char *s1, *s2;
4990    int s1_len, s2_len;
4991
4992    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &s1, &s1_len, &s2, &s2_len) == FAILURE) {
4993        return;
4994    }
4995
4996    RETURN_LONG(strnatcmp_ex(s1, s1_len,
4997                             s2, s2_len,
4998                             fold_case));
4999}
5000/* }}} */
5001
5002PHPAPI int string_natural_compare_function_ex(zval *result, zval *op1, zval *op2, zend_bool case_insensitive TSRMLS_DC) /* {{{ */
5003{
5004    zval op1_copy, op2_copy;
5005    int use_copy1 = 0, use_copy2 = 0;
5006
5007    if (Z_TYPE_P(op1) != IS_STRING) {
5008        zend_make_printable_zval(op1, &op1_copy, &use_copy1);
5009    }
5010    if (Z_TYPE_P(op2) != IS_STRING) {
5011        zend_make_printable_zval(op2, &op2_copy, &use_copy2);
5012    }
5013
5014    if (use_copy1) {
5015        op1 = &op1_copy;
5016    }
5017    if (use_copy2) {
5018        op2 = &op2_copy;
5019    }
5020
5021    ZVAL_LONG(result, strnatcmp_ex(Z_STRVAL_P(op1), Z_STRLEN_P(op1), Z_STRVAL_P(op2), Z_STRLEN_P(op2), case_insensitive));
5022
5023    if (use_copy1) {
5024        zval_dtor(op1);
5025    }
5026    if (use_copy2) {
5027        zval_dtor(op2);
5028    }
5029    return SUCCESS;
5030}
5031/* }}} */
5032
5033PHPAPI int string_natural_case_compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* {{{ */
5034{
5035    return string_natural_compare_function_ex(result, op1, op2, 1 TSRMLS_CC);
5036}
5037/* }}} */
5038
5039PHPAPI int string_natural_compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* {{{ */
5040{
5041    return string_natural_compare_function_ex(result, op1, op2, 0 TSRMLS_CC);
5042}
5043/* }}} */
5044
5045/* {{{ proto int strnatcmp(string s1, string s2)
5046   Returns the result of string comparison using 'natural' algorithm */
5047PHP_FUNCTION(strnatcmp)
5048{
5049    php_strnatcmp(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
5050}
5051/* }}} */
5052
5053/* {{{ proto array localeconv(void)
5054   Returns numeric formatting information based on the current locale */
5055PHP_FUNCTION(localeconv)
5056{
5057    zval *grouping, *mon_grouping;
5058    int len, i;
5059
5060    /* We don't need no stinkin' parameters... */
5061    if (zend_parse_parameters_none() == FAILURE) {
5062        return;
5063    }
5064
5065    MAKE_STD_ZVAL(grouping);
5066    MAKE_STD_ZVAL(mon_grouping);
5067
5068    array_init(return_value);
5069    array_init(grouping);
5070    array_init(mon_grouping);
5071
5072#ifdef HAVE_LOCALECONV
5073    {
5074        struct lconv currlocdata;
5075
5076        localeconv_r( &currlocdata );
5077
5078        /* Grab the grouping data out of the array */
5079        len = strlen(currlocdata.grouping);
5080
5081        for (i = 0; i < len; i++) {
5082            add_index_long(grouping, i, currlocdata.grouping[i]);
5083        }
5084
5085        /* Grab the monetary grouping data out of the array */
5086        len = strlen(currlocdata.mon_grouping);
5087
5088        for (i = 0; i < len; i++) {
5089            add_index_long(mon_grouping, i, currlocdata.mon_grouping[i]);
5090        }
5091
5092        add_assoc_string(return_value, "decimal_point",     currlocdata.decimal_point,     1);
5093        add_assoc_string(return_value, "thousands_sep",     currlocdata.thousands_sep,     1);
5094        add_assoc_string(return_value, "int_curr_symbol",   currlocdata.int_curr_symbol,   1);
5095        add_assoc_string(return_value, "currency_symbol",   currlocdata.currency_symbol,   1);
5096        add_assoc_string(return_value, "mon_decimal_point", currlocdata.mon_decimal_point, 1);
5097        add_assoc_string(return_value, "mon_thousands_sep", currlocdata.mon_thousands_sep, 1);
5098        add_assoc_string(return_value, "positive_sign",     currlocdata.positive_sign,     1);
5099        add_assoc_string(return_value, "negative_sign",     currlocdata.negative_sign,     1);
5100        add_assoc_long(  return_value, "int_frac_digits",   currlocdata.int_frac_digits     );
5101        add_assoc_long(  return_value, "frac_digits",       currlocdata.frac_digits         );
5102        add_assoc_long(  return_value, "p_cs_precedes",     currlocdata.p_cs_precedes       );
5103        add_assoc_long(  return_value, "p_sep_by_space",    currlocdata.p_sep_by_space      );
5104        add_assoc_long(  return_value, "n_cs_precedes",     currlocdata.n_cs_precedes       );
5105        add_assoc_long(  return_value, "n_sep_by_space",    currlocdata.n_sep_by_space      );
5106        add_assoc_long(  return_value, "p_sign_posn",       currlocdata.p_sign_posn         );
5107        add_assoc_long(  return_value, "n_sign_posn",       currlocdata.n_sign_posn         );
5108    }
5109#else
5110    /* Ok, it doesn't look like we have locale info floating around, so I guess it
5111       wouldn't hurt to just go ahead and return the POSIX locale information?  */
5112
5113    add_index_long(grouping, 0, -1);
5114    add_index_long(mon_grouping, 0, -1);
5115
5116    add_assoc_string(return_value, "decimal_point",     "\x2E", 1);
5117    add_assoc_string(return_value, "thousands_sep",     "",     1);
5118    add_assoc_string(return_value, "int_curr_symbol",   "",     1);
5119    add_assoc_string(return_value, "currency_symbol",   "",     1);
5120    add_assoc_string(return_value, "mon_decimal_point", "\x2E", 1);
5121    add_assoc_string(return_value, "mon_thousands_sep", "",     1);
5122    add_assoc_string(return_value, "positive_sign",     "",     1);
5123    add_assoc_string(return_value, "negative_sign",     "",     1);
5124    add_assoc_long(  return_value, "int_frac_digits",   CHAR_MAX );
5125    add_assoc_long(  return_value, "frac_digits",       CHAR_MAX );
5126    add_assoc_long(  return_value, "p_cs_precedes",     CHAR_MAX );
5127    add_assoc_long(  return_value, "p_sep_by_space",    CHAR_MAX );
5128    add_assoc_long(  return_value, "n_cs_precedes",     CHAR_MAX );
5129    add_assoc_long(  return_value, "n_sep_by_space",    CHAR_MAX );
5130    add_assoc_long(  return_value, "p_sign_posn",       CHAR_MAX );
5131    add_assoc_long(  return_value, "n_sign_posn",       CHAR_MAX );
5132#endif
5133
5134    zend_hash_update(Z_ARRVAL_P(return_value), "grouping", 9, &grouping, sizeof(zval *), NULL);
5135    zend_hash_update(Z_ARRVAL_P(return_value), "mon_grouping", 13, &mon_grouping, sizeof(zval *), NULL);
5136}
5137/* }}} */
5138
5139/* {{{ proto int strnatcasecmp(string s1, string s2)
5140   Returns the result of case-insensitive string comparison using 'natural' algorithm */
5141PHP_FUNCTION(strnatcasecmp)
5142{
5143    php_strnatcmp(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
5144}
5145/* }}} */
5146
5147/* {{{ proto int substr_count(string haystack, string needle [, int offset [, int length]])
5148   Returns the number of times a substring occurs in the string */
5149PHP_FUNCTION(substr_count)
5150{
5151    char *haystack, *needle;
5152    long offset = 0, length = 0;
5153    int ac = ZEND_NUM_ARGS();
5154    int count = 0;
5155    int haystack_len, needle_len;
5156    char *p, *endp, cmp;
5157
5158    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|ll", &haystack, &haystack_len, &needle, &needle_len, &offset, &length) == FAILURE) {
5159        return;
5160    }
5161
5162    if (needle_len == 0) {
5163        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty substring");
5164        RETURN_FALSE;
5165    }
5166
5167    p = haystack;
5168    endp = p + haystack_len;
5169
5170    if (offset < 0) {
5171        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset should be greater than or equal to 0");
5172        RETURN_FALSE;
5173    }
5174
5175    if (offset > haystack_len) {
5176        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset value %ld exceeds string length", offset);
5177        RETURN_FALSE;
5178    }
5179    p += offset;
5180
5181    if (ac == 4) {
5182
5183        if (length <= 0) {
5184            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Length should be greater than 0");
5185            RETURN_FALSE;
5186        }
5187        if (length > (haystack_len - offset)) {
5188            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Length value %ld exceeds string length", length);
5189            RETURN_FALSE;
5190        }
5191        endp = p + length;
5192    }
5193
5194    if (needle_len == 1) {
5195        cmp = needle[0];
5196
5197        while ((p = memchr(p, cmp, endp - p))) {
5198            count++;
5199            p++;
5200        }
5201    } else {
5202        while ((p = php_memnstr(p, needle, needle_len, endp))) {
5203            p += needle_len;
5204            count++;
5205        }
5206    }
5207
5208    RETURN_LONG(count);
5209}
5210/* }}} */
5211
5212/* {{{ proto string str_pad(string input, int pad_length [, string pad_string [, int pad_type]])
5213   Returns input string padded on the left or right to specified length with pad_string */
5214PHP_FUNCTION(str_pad)
5215{
5216    /* Input arguments */
5217    char *input;                /* Input string */
5218    int  input_len;
5219    long pad_length;            /* Length to pad to */
5220
5221    /* Helper variables */
5222    size_t     num_pad_chars;       /* Number of padding characters (total - input size) */
5223    char  *result = NULL;       /* Resulting string */
5224    int    result_len = 0;      /* Length of the resulting string */
5225    char  *pad_str_val = " ";   /* Pointer to padding string */
5226    int    pad_str_len = 1;     /* Length of the padding string */
5227    long   pad_type_val = STR_PAD_RIGHT; /* The padding type value */
5228    int    i, left_pad=0, right_pad=0;
5229
5230    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|sl", &input, &input_len, &pad_length,
5231                                                                  &pad_str_val, &pad_str_len, &pad_type_val) == FAILURE) {
5232        return;
5233    }
5234
5235    /* If resulting string turns out to be shorter than input string,
5236       we simply copy the input and return. */
5237    if (pad_length <= 0 || (pad_length - input_len) <= 0) {
5238        RETURN_STRINGL(input, input_len, 1);
5239    }
5240
5241    if (pad_str_len == 0) {
5242        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Padding string cannot be empty");
5243        return;
5244    }
5245
5246    if (pad_type_val < STR_PAD_LEFT || pad_type_val > STR_PAD_BOTH) {
5247        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Padding type has to be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH");
5248        return;
5249    }
5250
5251    num_pad_chars = pad_length - input_len;
5252    if (num_pad_chars >= INT_MAX) {
5253        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Padding length is too long");
5254        return;
5255    }
5256    result = (char *)emalloc(input_len + num_pad_chars + 1);
5257
5258    /* We need to figure out the left/right padding lengths. */
5259    switch (pad_type_val) {
5260        case STR_PAD_RIGHT:
5261            left_pad = 0;
5262            right_pad = num_pad_chars;
5263            break;
5264
5265        case STR_PAD_LEFT:
5266            left_pad = num_pad_chars;
5267            right_pad = 0;
5268            break;
5269
5270        case STR_PAD_BOTH:
5271            left_pad = num_pad_chars / 2;
5272            right_pad = num_pad_chars - left_pad;
5273            break;
5274    }
5275
5276    /* First we pad on the left. */
5277    for (i = 0; i < left_pad; i++)
5278        result[result_len++] = pad_str_val[i % pad_str_len];
5279
5280    /* Then we copy the input string. */
5281    memcpy(result + result_len, input, input_len);
5282    result_len += input_len;
5283
5284    /* Finally, we pad on the right. */
5285    for (i = 0; i < right_pad; i++)
5286        result[result_len++] = pad_str_val[i % pad_str_len];
5287
5288    result[result_len] = '\0';
5289
5290    RETURN_STRINGL(result, result_len, 0);
5291}
5292/* }}} */
5293
5294/* {{{ proto mixed sscanf(string str, string format [, string ...])
5295   Implements an ANSI C compatible sscanf */
5296PHP_FUNCTION(sscanf)
5297{
5298    zval ***args = NULL;
5299    char *str, *format;
5300    int str_len, format_len, result, num_args = 0;
5301
5302    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss*", &str, &str_len, &format, &format_len,
5303        &args, &num_args) == FAILURE) {
5304        return;
5305    }
5306
5307    result = php_sscanf_internal(str, format, num_args, args, 0, &return_value TSRMLS_CC);
5308
5309    if (args) {
5310        efree(args);
5311    }
5312
5313    if (SCAN_ERROR_WRONG_PARAM_COUNT == result) {
5314        WRONG_PARAM_COUNT;
5315    }
5316}
5317/* }}} */
5318
5319static char rot13_from[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
5320static char rot13_to[] = "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM";
5321
5322/* {{{ proto string str_rot13(string str)
5323   Perform the rot13 transform on a string */
5324PHP_FUNCTION(str_rot13)
5325{
5326    char *arg;
5327    int arglen;
5328
5329    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arglen) == FAILURE) {
5330        return;
5331    }
5332
5333    RETVAL_STRINGL(arg, arglen, 1);
5334
5335    php_strtr(Z_STRVAL_P(return_value), Z_STRLEN_P(return_value), rot13_from, rot13_to, 52);
5336}
5337/* }}} */
5338
5339static void php_string_shuffle(char *str, long len TSRMLS_DC) /* {{{ */
5340{
5341    long n_elems, rnd_idx, n_left;
5342    char temp;
5343    /* The implementation is stolen from array_data_shuffle       */
5344    /* Thus the characteristics of the randomization are the same */
5345    n_elems = len;
5346
5347    if (n_elems <= 1) {
5348        return;
5349    }
5350
5351    n_left = n_elems;
5352
5353    while (--n_left) {
5354        rnd_idx = php_rand(TSRMLS_C);
5355        RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX);
5356        if (rnd_idx != n_left) {
5357            temp = str[n_left];
5358            str[n_left] = str[rnd_idx];
5359            str[rnd_idx] = temp;
5360        }
5361    }
5362}
5363/* }}} */
5364
5365/* {{{ proto void str_shuffle(string str)
5366   Shuffles string. One permutation of all possible is created */
5367PHP_FUNCTION(str_shuffle)
5368{
5369    char *arg;
5370    int arglen;
5371
5372    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arglen) == FAILURE) {
5373        return;
5374    }
5375
5376    RETVAL_STRINGL(arg, arglen, 1);
5377    if (Z_STRLEN_P(return_value) > 1) {
5378        php_string_shuffle(Z_STRVAL_P(return_value), (long) Z_STRLEN_P(return_value) TSRMLS_CC);
5379    }
5380}
5381/* }}} */
5382
5383/* {{{ proto mixed str_word_count(string str, [int format [, string charlist]])
5384    Counts the number of words inside a string. If format of 1 is specified,
5385    then the function will return an array containing all the words
5386    found inside the string. If format of 2 is specified, then the function
5387    will return an associated array where the position of the word is the key
5388    and the word itself is the value.
5389
5390    For the purpose of this function, 'word' is defined as a locale dependent
5391    string containing alphabetic characters, which also may contain, but not start
5392    with "'" and "-" characters.
5393*/
5394PHP_FUNCTION(str_word_count)
5395{
5396    char *buf, *str, *char_list = NULL, *p, *e, *s, ch[256];
5397    int str_len, char_list_len = 0, word_count = 0;
5398    long type = 0;
5399
5400    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ls", &str, &str_len, &type, &char_list, &char_list_len) == FAILURE) {
5401        return;
5402    }
5403
5404    switch(type) {
5405        case 1:
5406        case 2:
5407            array_init(return_value);
5408            if (!str_len) {
5409                return;
5410            }
5411            break;
5412        case 0:
5413            if (!str_len) {
5414                RETURN_LONG(0);
5415            }
5416            /* nothing to be done */
5417            break;
5418        default:
5419            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid format value %ld", type);
5420            RETURN_FALSE;
5421    }
5422
5423    if (char_list) {
5424        php_charmask((unsigned char *)char_list, char_list_len, ch TSRMLS_CC);
5425    }
5426
5427    p = str;
5428    e = str + str_len;
5429
5430    /* first character cannot be ' or -, unless explicitly allowed by the user */
5431    if ((*p == '\'' && (!char_list || !ch['\''])) || (*p == '-' && (!char_list || !ch['-']))) {
5432        p++;
5433    }
5434    /* last character cannot be -, unless explicitly allowed by the user */
5435    if (*(e - 1) == '-' && (!char_list || !ch['-'])) {
5436        e--;
5437    }
5438
5439    while (p < e) {
5440        s = p;
5441        while (p < e && (isalpha((unsigned char)*p) || (char_list && ch[(unsigned char)*p]) || *p == '\'' || *p == '-')) {
5442            p++;
5443        }
5444        if (p > s) {
5445            switch (type)
5446            {
5447                case 1:
5448                    buf = estrndup(s, (p-s));
5449                    add_next_index_stringl(return_value, buf, (p-s), 0);
5450                    break;
5451                case 2:
5452                    buf = estrndup(s, (p-s));
5453                    add_index_stringl(return_value, (s - str), buf, p-s, 0);
5454                    break;
5455                default:
5456                    word_count++;
5457                    break;
5458            }
5459        }
5460        p++;
5461    }
5462
5463    if (!type) {
5464        RETURN_LONG(word_count);
5465    }
5466}
5467
5468/* }}} */
5469
5470#if HAVE_STRFMON
5471/* {{{ proto string money_format(string format , float value)
5472   Convert monetary value(s) to string */
5473PHP_FUNCTION(money_format)
5474{
5475    int format_len = 0, str_len;
5476    char *format, *str, *p, *e;
5477    double value;
5478    zend_bool check = 0;
5479
5480    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sd", &format, &format_len, &value) == FAILURE) {
5481        return;
5482    }
5483
5484    p = format;
5485    e = p + format_len;
5486    while ((p = memchr(p, '%', (e - p)))) {
5487        if (*(p + 1) == '%') {
5488            p += 2;
5489        } else if (!check) {
5490            check = 1;
5491            p++;
5492        } else {
5493            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Only a single %%i or %%n token can be used");
5494            RETURN_FALSE;
5495        }
5496    }
5497
5498    str_len = format_len + 1024;
5499    str = emalloc(str_len);
5500    if ((str_len = strfmon(str, str_len, format, value)) < 0) {
5501        efree(str);
5502        RETURN_FALSE;
5503    }
5504    str[str_len] = 0;
5505
5506    RETURN_STRINGL(erealloc(str, str_len + 1), str_len, 0);
5507}
5508/* }}} */
5509#endif
5510
5511/* {{{ proto array str_split(string str [, int split_length])
5512   Convert a string to an array. If split_length is specified, break the string down into chunks each split_length characters long. */
5513PHP_FUNCTION(str_split)
5514{
5515    char *str;
5516    int str_len;
5517    long split_length = 1;
5518    char *p;
5519    int n_reg_segments;
5520
5521    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &str, &str_len, &split_length) == FAILURE) {
5522        return;
5523    }
5524
5525    if (split_length <= 0) {
5526        php_error_docref(NULL TSRMLS_CC, E_WARNING, "The length of each segment must be greater than zero");
5527        RETURN_FALSE;
5528    }
5529
5530    array_init_size(return_value, ((str_len - 1) / split_length) + 1);
5531
5532    if (split_length >= str_len) {
5533        add_next_index_stringl(return_value, str, str_len, 1);
5534        return;
5535    }
5536
5537    n_reg_segments = str_len / split_length;
5538    p = str;
5539
5540    while (n_reg_segments-- > 0) {
5541        add_next_index_stringl(return_value, p, split_length, 1);
5542        p += split_length;
5543    }
5544
5545    if (p != (str + str_len)) {
5546        add_next_index_stringl(return_value, p, (str + str_len - p), 1);
5547    }
5548}
5549/* }}} */
5550
5551/* {{{ proto array strpbrk(string haystack, string char_list)
5552   Search a string for any of a set of characters */
5553PHP_FUNCTION(strpbrk)
5554{
5555    char *haystack, *char_list;
5556    int haystack_len, char_list_len;
5557    char *haystack_ptr, *cl_ptr;
5558
5559    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &haystack, &haystack_len, &char_list, &char_list_len) == FAILURE) {
5560        RETURN_FALSE;
5561    }
5562
5563    if (!char_list_len) {
5564        php_error_docref(NULL TSRMLS_CC, E_WARNING, "The character list cannot be empty");
5565        RETURN_FALSE;
5566    }
5567
5568    for (haystack_ptr = haystack; haystack_ptr < (haystack + haystack_len); ++haystack_ptr) {
5569        for (cl_ptr = char_list; cl_ptr < (char_list + char_list_len); ++cl_ptr) {
5570            if (*cl_ptr == *haystack_ptr) {
5571                RETURN_STRINGL(haystack_ptr, (haystack + haystack_len - haystack_ptr), 1);
5572            }
5573        }
5574    }
5575
5576    RETURN_FALSE;
5577}
5578/* }}} */
5579
5580/* {{{ proto int substr_compare(string main_str, string str, int offset [, int length [, bool case_sensitivity]])
5581   Binary safe optionally case insensitive comparison of 2 strings from an offset, up to length characters */
5582PHP_FUNCTION(substr_compare)
5583{
5584    char *s1, *s2;
5585    int s1_len, s2_len;
5586    long offset, len=0;
5587    zend_bool cs=0;
5588    uint cmp_len;
5589
5590    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssl|lb", &s1, &s1_len, &s2, &s2_len, &offset, &len, &cs) == FAILURE) {
5591        RETURN_FALSE;
5592    }
5593
5594    if (ZEND_NUM_ARGS() >= 4 && len <= 0) {
5595        if (len == 0) {
5596            RETURN_LONG(0L);
5597        } else {
5598            php_error_docref(NULL TSRMLS_CC, E_WARNING, "The length must be greater than or equal to zero");
5599            RETURN_FALSE;
5600        }
5601    }
5602
5603    if (offset < 0) {
5604        offset = s1_len + offset;
5605        offset = (offset < 0) ? 0 : offset;
5606    }
5607
5608    if (offset >= s1_len) {
5609        php_error_docref(NULL TSRMLS_CC, E_WARNING, "The start position cannot exceed initial string length");
5610        RETURN_FALSE;
5611    }
5612
5613    cmp_len = (uint) (len ? len : MAX(s2_len, (s1_len - offset)));
5614
5615    if (!cs) {
5616        RETURN_LONG(zend_binary_strncmp(s1 + offset, (s1_len - offset), s2, s2_len, cmp_len));
5617    } else {
5618        RETURN_LONG(zend_binary_strncasecmp_l(s1 + offset, (s1_len - offset), s2, s2_len, cmp_len));
5619    }
5620}
5621/* }}} */
5622
5623/*
5624 * Local variables:
5625 * tab-width: 4
5626 * c-basic-offset: 4
5627 * End:
5628 * vim600: noet sw=4 ts=4 fdm=marker
5629 * vim<600: noet sw=4 ts=4
5630 */
5631