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