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