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