Back to index

php5  5.3.10
string.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Authors: Rasmus Lerdorf <rasmus@php.net>                             |
00016    |          Stig Sæther Bakken <ssb@php.net>                            |
00017    |          Zeev Suraski <zeev@zend.com>                                |
00018    +----------------------------------------------------------------------+
00019  */
00020 
00021 /* $Id: string.c 321634 2012-01-01 13:15:04Z felipe $ */
00022 
00023 /* Synced with php 3.0 revision 1.193 1999-06-16 [ssb] */
00024 
00025 #include <stdio.h>
00026 #include "php.h"
00027 #include "php_rand.h"
00028 #include "php_string.h"
00029 #include "php_variables.h"
00030 #ifdef HAVE_LOCALE_H
00031 # include <locale.h>
00032 #endif
00033 #ifdef HAVE_LANGINFO_H
00034 # include <langinfo.h>
00035 #endif
00036 #ifdef HAVE_MONETARY_H
00037 # include <monetary.h>
00038 #endif
00039 /* 
00040  * This define is here because some versions of libintl redefine setlocale
00041  * to point to libintl_setlocale.  That's a ridiculous thing to do as far
00042  * as I am concerned, but with this define and the subsequent undef we
00043  * limit the damage to just the actual setlocale() call in this file
00044  * without turning zif_setlocale into zif_libintl_setlocale.  -Rasmus
00045  */
00046 #define php_my_setlocale setlocale
00047 #ifdef HAVE_LIBINTL
00048 # include <libintl.h> /* For LC_MESSAGES */
00049  #ifdef setlocale
00050  # undef setlocale
00051  #endif
00052 #endif
00053 
00054 #include "scanf.h"
00055 #include "zend_API.h"
00056 #include "zend_execute.h"
00057 #include "php_globals.h"
00058 #include "basic_functions.h"
00059 #include "php_smart_str.h"
00060 #ifdef ZTS
00061 #include "TSRM.h"
00062 #endif
00063 
00064 /* For str_getcsv() support */
00065 #include "ext/standard/file.h"
00066 
00067 #define STR_PAD_LEFT               0
00068 #define STR_PAD_RIGHT                     1
00069 #define STR_PAD_BOTH               2
00070 #define PHP_PATHINFO_DIRNAME       1
00071 #define PHP_PATHINFO_BASENAME      2
00072 #define PHP_PATHINFO_EXTENSION     4
00073 #define PHP_PATHINFO_FILENAME      8
00074 #define PHP_PATHINFO_ALL    (PHP_PATHINFO_DIRNAME | PHP_PATHINFO_BASENAME | PHP_PATHINFO_EXTENSION | PHP_PATHINFO_FILENAME)
00075 
00076 #define STR_STRSPN                        0
00077 #define STR_STRCSPN                       1
00078 
00079 /* {{{ register_string_constants
00080  */
00081 void register_string_constants(INIT_FUNC_ARGS)
00082 {
00083        REGISTER_LONG_CONSTANT("STR_PAD_LEFT", STR_PAD_LEFT, CONST_CS | CONST_PERSISTENT);
00084        REGISTER_LONG_CONSTANT("STR_PAD_RIGHT", STR_PAD_RIGHT, CONST_CS | CONST_PERSISTENT);
00085        REGISTER_LONG_CONSTANT("STR_PAD_BOTH", STR_PAD_BOTH, CONST_CS | CONST_PERSISTENT);
00086        REGISTER_LONG_CONSTANT("PATHINFO_DIRNAME", PHP_PATHINFO_DIRNAME, CONST_CS | CONST_PERSISTENT);
00087        REGISTER_LONG_CONSTANT("PATHINFO_BASENAME", PHP_PATHINFO_BASENAME, CONST_CS | CONST_PERSISTENT);
00088        REGISTER_LONG_CONSTANT("PATHINFO_EXTENSION", PHP_PATHINFO_EXTENSION, CONST_CS | CONST_PERSISTENT);
00089        REGISTER_LONG_CONSTANT("PATHINFO_FILENAME", PHP_PATHINFO_FILENAME, CONST_CS | CONST_PERSISTENT);
00090 
00091 #ifdef HAVE_LOCALECONV
00092        /* If last members of struct lconv equal CHAR_MAX, no grouping is done */    
00093 
00094 /* This is bad, but since we are going to be hardcoding in the POSIX stuff anyway... */
00095 # ifndef HAVE_LIMITS_H
00096 # define CHAR_MAX 127
00097 # endif
00098 
00099        REGISTER_LONG_CONSTANT("CHAR_MAX", CHAR_MAX, CONST_CS | CONST_PERSISTENT);
00100 #endif
00101 
00102 #ifdef HAVE_LOCALE_H
00103        REGISTER_LONG_CONSTANT("LC_CTYPE", LC_CTYPE, CONST_CS | CONST_PERSISTENT);
00104        REGISTER_LONG_CONSTANT("LC_NUMERIC", LC_NUMERIC, CONST_CS | CONST_PERSISTENT);
00105        REGISTER_LONG_CONSTANT("LC_TIME", LC_TIME, CONST_CS | CONST_PERSISTENT);
00106        REGISTER_LONG_CONSTANT("LC_COLLATE", LC_COLLATE, CONST_CS | CONST_PERSISTENT);
00107        REGISTER_LONG_CONSTANT("LC_MONETARY", LC_MONETARY, CONST_CS | CONST_PERSISTENT);
00108        REGISTER_LONG_CONSTANT("LC_ALL", LC_ALL, CONST_CS | CONST_PERSISTENT);
00109 # ifdef LC_MESSAGES
00110        REGISTER_LONG_CONSTANT("LC_MESSAGES", LC_MESSAGES, CONST_CS | CONST_PERSISTENT);
00111 # endif
00112 #endif
00113        
00114 }
00115 /* }}} */
00116 
00117 int php_tag_find(char *tag, int len, char *set);
00118 
00119 /* this is read-only, so it's ok */
00120 static char hexconvtab[] = "0123456789abcdef";
00121 
00122 /* localeconv mutex */
00123 #ifdef ZTS
00124 static MUTEX_T locale_mutex = NULL;
00125 #endif
00126 
00127 /* {{{ php_bin2hex
00128  */
00129 static char *php_bin2hex(const unsigned char *old, const size_t oldlen, size_t *newlen)
00130 {
00131        register unsigned char *result = NULL;
00132        size_t i, j;
00133 
00134        result = (unsigned char *) safe_emalloc(oldlen * 2, sizeof(char), 1);
00135        
00136        for (i = j = 0; i < oldlen; i++) {
00137               result[j++] = hexconvtab[old[i] >> 4];
00138               result[j++] = hexconvtab[old[i] & 15];
00139        }
00140        result[j] = '\0';
00141 
00142        if (newlen) 
00143               *newlen = oldlen * 2 * sizeof(char);
00144 
00145        return (char *)result;
00146 }
00147 /* }}} */
00148 
00149 #ifdef HAVE_LOCALECONV
00150 /* {{{ localeconv_r
00151  * glibc's localeconv is not reentrant, so lets make it so ... sorta */
00152 PHPAPI struct lconv *localeconv_r(struct lconv *out)
00153 {
00154        struct lconv *res;
00155 
00156 # ifdef ZTS
00157        tsrm_mutex_lock( locale_mutex );
00158 # endif
00159 
00160        /* localeconv doesn't return an error condition */
00161        res = localeconv();
00162 
00163        *out = *res;
00164 
00165 # ifdef ZTS
00166        tsrm_mutex_unlock( locale_mutex );
00167 # endif
00168 
00169        return out;
00170 }
00171 /* }}} */
00172 
00173 # ifdef ZTS
00174 /* {{{ PHP_MINIT_FUNCTION
00175  */
00176 PHP_MINIT_FUNCTION(localeconv)
00177 {
00178        locale_mutex = tsrm_mutex_alloc();
00179        return SUCCESS;
00180 }
00181 /* }}} */
00182 
00183 /* {{{ PHP_MSHUTDOWN_FUNCTION
00184  */
00185 PHP_MSHUTDOWN_FUNCTION(localeconv)
00186 {
00187        tsrm_mutex_free( locale_mutex );
00188        locale_mutex = NULL;
00189        return SUCCESS;
00190 }
00191 /* }}} */
00192 # endif
00193 #endif
00194 
00195 /* {{{ proto string bin2hex(string data)
00196    Converts the binary representation of data to hex */
00197 PHP_FUNCTION(bin2hex)
00198 {
00199        char *result, *data;
00200        size_t newlen;
00201        int datalen;
00202 
00203        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data, &datalen) == FAILURE) {
00204               return;
00205        }
00206 
00207        result = php_bin2hex((unsigned char *)data, datalen, &newlen);
00208        
00209        if (!result) {
00210               RETURN_FALSE;
00211        }
00212 
00213        RETURN_STRINGL(result, newlen, 0);
00214 }
00215 /* }}} */
00216 
00217 static void php_spn_common_handler(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
00218 {
00219        char *s11, *s22;
00220        int len1, len2;
00221        long start = 0, len = 0;
00222        
00223        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|ll", &s11, &len1,
00224                             &s22, &len2, &start, &len) == FAILURE) {
00225               return;
00226        }
00227        
00228        if (ZEND_NUM_ARGS() < 4) {
00229               len = len1;
00230        }
00231        
00232        /* look at substr() function for more information */
00233        
00234        if (start < 0) {
00235               start += len1;
00236               if (start < 0) {
00237                      start = 0;
00238               }
00239        } else if (start > len1) {
00240               RETURN_FALSE;
00241        }
00242        
00243        if (len < 0) {
00244               len += (len1 - start);
00245               if (len < 0) {
00246                      len = 0;
00247               }
00248        }
00249        
00250        if (len > len1 - start) {
00251               len = len1 - start;
00252        }
00253 
00254        if(len == 0) {
00255               RETURN_LONG(0);
00256        }
00257 
00258        if (behavior == STR_STRSPN) {
00259               RETURN_LONG(php_strspn(s11 + start /*str1_start*/,
00260                                           s22 /*str2_start*/,
00261                                           s11 + start + len /*str1_end*/,
00262                                           s22 + len2 /*str2_end*/));
00263        } else if (behavior == STR_STRCSPN) {
00264               RETURN_LONG(php_strcspn(s11 + start /*str1_start*/,
00265                                           s22 /*str2_start*/,
00266                                           s11 + start + len /*str1_end*/,
00267                                           s22 + len2 /*str2_end*/));
00268        }
00269        
00270 }
00271 /* }}} */
00272 
00273 /* {{{ proto int strspn(string str, string mask [, start [, len]])
00274    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) */
00275 PHP_FUNCTION(strspn)
00276 {
00277        php_spn_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, STR_STRSPN);
00278 }
00279 /* }}} */
00280 
00281 /* {{{ proto int strcspn(string str, string mask [, start [, len]])
00282    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) */
00283 PHP_FUNCTION(strcspn)
00284 {
00285        php_spn_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, STR_STRCSPN);
00286 }
00287 /* }}} */
00288 
00289 /* {{{ PHP_MINIT_FUNCTION(nl_langinfo) */
00290 #if HAVE_NL_LANGINFO
00291 PHP_MINIT_FUNCTION(nl_langinfo)
00292 {
00293 #define REGISTER_NL_LANGINFO_CONSTANT(x)  REGISTER_LONG_CONSTANT(#x, x, CONST_CS | CONST_PERSISTENT)
00294 #ifdef ABDAY_1
00295        REGISTER_NL_LANGINFO_CONSTANT(ABDAY_1);
00296        REGISTER_NL_LANGINFO_CONSTANT(ABDAY_2);
00297        REGISTER_NL_LANGINFO_CONSTANT(ABDAY_3);
00298        REGISTER_NL_LANGINFO_CONSTANT(ABDAY_4);
00299        REGISTER_NL_LANGINFO_CONSTANT(ABDAY_5);
00300        REGISTER_NL_LANGINFO_CONSTANT(ABDAY_6);
00301        REGISTER_NL_LANGINFO_CONSTANT(ABDAY_7);
00302 #endif
00303 #ifdef DAY_1
00304        REGISTER_NL_LANGINFO_CONSTANT(DAY_1);
00305        REGISTER_NL_LANGINFO_CONSTANT(DAY_2);
00306        REGISTER_NL_LANGINFO_CONSTANT(DAY_3);
00307        REGISTER_NL_LANGINFO_CONSTANT(DAY_4);
00308        REGISTER_NL_LANGINFO_CONSTANT(DAY_5);
00309        REGISTER_NL_LANGINFO_CONSTANT(DAY_6);
00310        REGISTER_NL_LANGINFO_CONSTANT(DAY_7);
00311 #endif
00312 #ifdef ABMON_1
00313        REGISTER_NL_LANGINFO_CONSTANT(ABMON_1);
00314        REGISTER_NL_LANGINFO_CONSTANT(ABMON_2);
00315        REGISTER_NL_LANGINFO_CONSTANT(ABMON_3);
00316        REGISTER_NL_LANGINFO_CONSTANT(ABMON_4);
00317        REGISTER_NL_LANGINFO_CONSTANT(ABMON_5);
00318        REGISTER_NL_LANGINFO_CONSTANT(ABMON_6);
00319        REGISTER_NL_LANGINFO_CONSTANT(ABMON_7);
00320        REGISTER_NL_LANGINFO_CONSTANT(ABMON_8);
00321        REGISTER_NL_LANGINFO_CONSTANT(ABMON_9);
00322        REGISTER_NL_LANGINFO_CONSTANT(ABMON_10);
00323        REGISTER_NL_LANGINFO_CONSTANT(ABMON_11);
00324        REGISTER_NL_LANGINFO_CONSTANT(ABMON_12);
00325 #endif
00326 #ifdef MON_1
00327        REGISTER_NL_LANGINFO_CONSTANT(MON_1);
00328        REGISTER_NL_LANGINFO_CONSTANT(MON_2);
00329        REGISTER_NL_LANGINFO_CONSTANT(MON_3);
00330        REGISTER_NL_LANGINFO_CONSTANT(MON_4);
00331        REGISTER_NL_LANGINFO_CONSTANT(MON_5);
00332        REGISTER_NL_LANGINFO_CONSTANT(MON_6);
00333        REGISTER_NL_LANGINFO_CONSTANT(MON_7);
00334        REGISTER_NL_LANGINFO_CONSTANT(MON_8);
00335        REGISTER_NL_LANGINFO_CONSTANT(MON_9);
00336        REGISTER_NL_LANGINFO_CONSTANT(MON_10);
00337        REGISTER_NL_LANGINFO_CONSTANT(MON_11);
00338        REGISTER_NL_LANGINFO_CONSTANT(MON_12);
00339 #endif
00340 #ifdef AM_STR
00341        REGISTER_NL_LANGINFO_CONSTANT(AM_STR);
00342 #endif
00343 #ifdef PM_STR
00344        REGISTER_NL_LANGINFO_CONSTANT(PM_STR);
00345 #endif
00346 #ifdef D_T_FMT
00347        REGISTER_NL_LANGINFO_CONSTANT(D_T_FMT);
00348 #endif
00349 #ifdef D_FMT
00350        REGISTER_NL_LANGINFO_CONSTANT(D_FMT);
00351 #endif
00352 #ifdef T_FMT
00353        REGISTER_NL_LANGINFO_CONSTANT(T_FMT);
00354 #endif
00355 #ifdef T_FMT_AMPM
00356        REGISTER_NL_LANGINFO_CONSTANT(T_FMT_AMPM);
00357 #endif
00358 #ifdef ERA
00359        REGISTER_NL_LANGINFO_CONSTANT(ERA);
00360 #endif
00361 #ifdef ERA_YEAR
00362        REGISTER_NL_LANGINFO_CONSTANT(ERA_YEAR);
00363 #endif
00364 #ifdef ERA_D_T_FMT
00365        REGISTER_NL_LANGINFO_CONSTANT(ERA_D_T_FMT);
00366 #endif
00367 #ifdef ERA_D_FMT
00368        REGISTER_NL_LANGINFO_CONSTANT(ERA_D_FMT);
00369 #endif
00370 #ifdef ERA_T_FMT
00371        REGISTER_NL_LANGINFO_CONSTANT(ERA_T_FMT);
00372 #endif
00373 #ifdef ALT_DIGITS
00374        REGISTER_NL_LANGINFO_CONSTANT(ALT_DIGITS);
00375 #endif
00376 #ifdef INT_CURR_SYMBOL
00377        REGISTER_NL_LANGINFO_CONSTANT(INT_CURR_SYMBOL);
00378 #endif
00379 #ifdef CURRENCY_SYMBOL
00380        REGISTER_NL_LANGINFO_CONSTANT(CURRENCY_SYMBOL);
00381 #endif
00382 #ifdef CRNCYSTR
00383        REGISTER_NL_LANGINFO_CONSTANT(CRNCYSTR);
00384 #endif
00385 #ifdef MON_DECIMAL_POINT
00386        REGISTER_NL_LANGINFO_CONSTANT(MON_DECIMAL_POINT);
00387 #endif
00388 #ifdef MON_THOUSANDS_SEP
00389        REGISTER_NL_LANGINFO_CONSTANT(MON_THOUSANDS_SEP);
00390 #endif
00391 #ifdef MON_GROUPING
00392        REGISTER_NL_LANGINFO_CONSTANT(MON_GROUPING);
00393 #endif
00394 #ifdef POSITIVE_SIGN
00395        REGISTER_NL_LANGINFO_CONSTANT(POSITIVE_SIGN);
00396 #endif
00397 #ifdef NEGATIVE_SIGN
00398        REGISTER_NL_LANGINFO_CONSTANT(NEGATIVE_SIGN);
00399 #endif
00400 #ifdef INT_FRAC_DIGITS
00401        REGISTER_NL_LANGINFO_CONSTANT(INT_FRAC_DIGITS);
00402 #endif
00403 #ifdef FRAC_DIGITS
00404        REGISTER_NL_LANGINFO_CONSTANT(FRAC_DIGITS);
00405 #endif
00406 #ifdef P_CS_PRECEDES
00407        REGISTER_NL_LANGINFO_CONSTANT(P_CS_PRECEDES);
00408 #endif
00409 #ifdef P_SEP_BY_SPACE
00410        REGISTER_NL_LANGINFO_CONSTANT(P_SEP_BY_SPACE);
00411 #endif
00412 #ifdef N_CS_PRECEDES
00413        REGISTER_NL_LANGINFO_CONSTANT(N_CS_PRECEDES);
00414 #endif
00415 #ifdef N_SEP_BY_SPACE
00416        REGISTER_NL_LANGINFO_CONSTANT(N_SEP_BY_SPACE);
00417 #endif
00418 #ifdef P_SIGN_POSN
00419        REGISTER_NL_LANGINFO_CONSTANT(P_SIGN_POSN);
00420 #endif
00421 #ifdef N_SIGN_POSN
00422        REGISTER_NL_LANGINFO_CONSTANT(N_SIGN_POSN);
00423 #endif
00424 #ifdef DECIMAL_POINT
00425        REGISTER_NL_LANGINFO_CONSTANT(DECIMAL_POINT);
00426 #endif
00427 #ifdef RADIXCHAR
00428        REGISTER_NL_LANGINFO_CONSTANT(RADIXCHAR);
00429 #endif
00430 #ifdef THOUSANDS_SEP
00431        REGISTER_NL_LANGINFO_CONSTANT(THOUSANDS_SEP);
00432 #endif
00433 #ifdef THOUSEP
00434        REGISTER_NL_LANGINFO_CONSTANT(THOUSEP);
00435 #endif
00436 #ifdef GROUPING
00437        REGISTER_NL_LANGINFO_CONSTANT(GROUPING);
00438 #endif
00439 #ifdef YESEXPR
00440        REGISTER_NL_LANGINFO_CONSTANT(YESEXPR);
00441 #endif
00442 #ifdef NOEXPR
00443        REGISTER_NL_LANGINFO_CONSTANT(NOEXPR);
00444 #endif
00445 #ifdef YESSTR
00446        REGISTER_NL_LANGINFO_CONSTANT(YESSTR);
00447 #endif
00448 #ifdef NOSTR
00449        REGISTER_NL_LANGINFO_CONSTANT(NOSTR);
00450 #endif
00451 #ifdef CODESET
00452        REGISTER_NL_LANGINFO_CONSTANT(CODESET);
00453 #endif
00454 #undef REGISTER_NL_LANGINFO_CONSTANT
00455        return SUCCESS;
00456 }
00457 /* }}} */
00458 
00459 /* {{{ proto string nl_langinfo(int item)
00460    Query language and locale information */
00461 PHP_FUNCTION(nl_langinfo)
00462 {
00463        long item;
00464        char *value;
00465        
00466        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &item) == FAILURE) {
00467               return;
00468        }
00469 
00470        switch(item) { /* {{{ */
00471 #ifdef ABDAY_1
00472               case ABDAY_1:
00473               case ABDAY_2:
00474               case ABDAY_3:
00475               case ABDAY_4:
00476               case ABDAY_5:
00477               case ABDAY_6:
00478               case ABDAY_7:
00479 #endif
00480 #ifdef DAY_1
00481               case DAY_1:
00482               case DAY_2:
00483               case DAY_3:
00484               case DAY_4:
00485               case DAY_5:
00486               case DAY_6:
00487               case DAY_7:
00488 #endif
00489 #ifdef ABMON_1
00490               case ABMON_1:
00491               case ABMON_2:
00492               case ABMON_3:
00493               case ABMON_4:
00494               case ABMON_5:
00495               case ABMON_6:
00496               case ABMON_7:
00497               case ABMON_8:
00498               case ABMON_9:
00499               case ABMON_10:
00500               case ABMON_11:
00501               case ABMON_12:
00502 #endif
00503 #ifdef MON_1
00504               case MON_1:
00505               case MON_2:
00506               case MON_3:
00507               case MON_4:
00508               case MON_5:
00509               case MON_6:
00510               case MON_7:
00511               case MON_8:
00512               case MON_9:
00513               case MON_10:
00514               case MON_11:
00515               case MON_12:
00516 #endif
00517 #ifdef AM_STR
00518               case AM_STR:
00519 #endif
00520 #ifdef PM_STR
00521               case PM_STR:
00522 #endif
00523 #ifdef D_T_FMT
00524               case D_T_FMT:
00525 #endif
00526 #ifdef D_FMT
00527               case D_FMT:
00528 #endif
00529 #ifdef T_FMT
00530               case T_FMT:
00531 #endif
00532 #ifdef T_FMT_AMPM
00533               case T_FMT_AMPM:
00534 #endif
00535 #ifdef ERA
00536               case ERA:
00537 #endif
00538 #ifdef ERA_YEAR
00539               case ERA_YEAR:
00540 #endif
00541 #ifdef ERA_D_T_FMT
00542               case ERA_D_T_FMT:
00543 #endif
00544 #ifdef ERA_D_FMT
00545               case ERA_D_FMT:
00546 #endif
00547 #ifdef ERA_T_FMT
00548               case ERA_T_FMT:
00549 #endif
00550 #ifdef ALT_DIGITS
00551               case ALT_DIGITS:
00552 #endif
00553 #ifdef INT_CURR_SYMBOL
00554               case INT_CURR_SYMBOL:
00555 #endif
00556 #ifdef CURRENCY_SYMBOL
00557               case CURRENCY_SYMBOL:
00558 #endif
00559 #ifdef CRNCYSTR
00560               case CRNCYSTR:
00561 #endif
00562 #ifdef MON_DECIMAL_POINT
00563               case MON_DECIMAL_POINT:
00564 #endif
00565 #ifdef MON_THOUSANDS_SEP
00566               case MON_THOUSANDS_SEP:
00567 #endif
00568 #ifdef MON_GROUPING
00569               case MON_GROUPING:
00570 #endif
00571 #ifdef POSITIVE_SIGN
00572               case POSITIVE_SIGN:
00573 #endif
00574 #ifdef NEGATIVE_SIGN
00575               case NEGATIVE_SIGN:
00576 #endif
00577 #ifdef INT_FRAC_DIGITS
00578               case INT_FRAC_DIGITS:
00579 #endif
00580 #ifdef FRAC_DIGITS
00581               case FRAC_DIGITS:
00582 #endif
00583 #ifdef P_CS_PRECEDES
00584               case P_CS_PRECEDES:
00585 #endif
00586 #ifdef P_SEP_BY_SPACE
00587               case P_SEP_BY_SPACE:
00588 #endif
00589 #ifdef N_CS_PRECEDES
00590               case N_CS_PRECEDES:
00591 #endif
00592 #ifdef N_SEP_BY_SPACE
00593               case N_SEP_BY_SPACE:
00594 #endif
00595 #ifdef P_SIGN_POSN
00596               case P_SIGN_POSN:
00597 #endif
00598 #ifdef N_SIGN_POSN
00599               case N_SIGN_POSN:
00600 #endif
00601 #ifdef DECIMAL_POINT
00602               case DECIMAL_POINT:
00603 #elif defined(RADIXCHAR)
00604               case RADIXCHAR:
00605 #endif
00606 #ifdef THOUSANDS_SEP
00607               case THOUSANDS_SEP:
00608 #elif defined(THOUSEP)
00609               case THOUSEP:
00610 #endif
00611 #ifdef GROUPING
00612               case GROUPING:
00613 #endif
00614 #ifdef YESEXPR
00615               case YESEXPR:
00616 #endif
00617 #ifdef NOEXPR
00618               case NOEXPR:
00619 #endif
00620 #ifdef YESSTR
00621               case YESSTR:
00622 #endif
00623 #ifdef NOSTR
00624               case NOSTR:
00625 #endif
00626 #ifdef CODESET
00627               case CODESET:
00628 #endif
00629                      break;
00630               default:
00631                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Item '%ld' is not valid", item);
00632                      RETURN_FALSE;
00633        }
00634        /* }}} */
00635 
00636        value = nl_langinfo(item);
00637        if (value == NULL) {
00638               RETURN_FALSE;
00639        } else {
00640               RETURN_STRING(value, 1);
00641        }
00642 }
00643 #endif
00644 /* }}} */
00645 
00646 #ifdef HAVE_STRCOLL
00647 /* {{{ proto int strcoll(string str1, string str2)
00648    Compares two strings using the current locale */
00649 PHP_FUNCTION(strcoll)
00650 {
00651        char *s1, *s2;
00652        int s1len, s2len;
00653        
00654        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &s1, &s1len, &s2, &s2len) == FAILURE) {
00655               return;
00656        }
00657 
00658        RETURN_LONG(strcoll((const char *) s1, 
00659                            (const char *) s2));
00660 }
00661 /* }}} */
00662 #endif
00663 
00664 /* {{{ php_charmask
00665  * Fills a 256-byte bytemask with input. You can specify a range like 'a..z',
00666  * it needs to be incrementing.  
00667  * Returns: FAILURE/SUCCESS whether the input was correct (i.e. no range errors)
00668  */
00669 static inline int php_charmask(unsigned char *input, int len, char *mask TSRMLS_DC)
00670 {
00671        unsigned char *end;
00672        unsigned char c;
00673        int result = SUCCESS;
00674 
00675        memset(mask, 0, 256);
00676        for (end = input+len; input < end; input++) {
00677               c=*input; 
00678               if ((input+3 < end) && input[1] == '.' && input[2] == '.' 
00679                             && input[3] >= c) {
00680                      memset(mask+c, 1, input[3] - c + 1);
00681                      input+=3;
00682               } else if ((input+1 < end) && input[0] == '.' && input[1] == '.') {
00683                      /* Error, try to be as helpful as possible:
00684                         (a range ending/starting with '.' won't be captured here) */
00685                      if (end-len >= input) { /* there was no 'left' char */
00686                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid '..'-range, no character to the left of '..'");
00687                             result = FAILURE;
00688                             continue;
00689                      }
00690                      if (input+2 >= end) { /* there is no 'right' char */
00691                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid '..'-range, no character to the right of '..'");
00692                             result = FAILURE;
00693                             continue;
00694                      }
00695                      if (input[-1] > input[2]) { /* wrong order */
00696                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid '..'-range, '..'-range needs to be incrementing");
00697                             result = FAILURE;
00698                             continue;
00699                      } 
00700                      /* FIXME: better error (a..b..c is the only left possibility?) */
00701                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid '..'-range");
00702                      result = FAILURE;
00703                      continue;
00704               } else {
00705                      mask[c]=1;
00706               }
00707        }
00708        return result;
00709 }
00710 /* }}} */
00711 
00712 /* {{{ php_trim()
00713  * mode 1 : trim left
00714  * mode 2 : trim right
00715  * mode 3 : trim left and right
00716  * what indicates which chars are to be trimmed. NULL->default (' \t\n\r\v\0')
00717  */
00718 PHPAPI char *php_trim(char *c, int len, char *what, int what_len, zval *return_value, int mode TSRMLS_DC)
00719 {
00720        register int i;
00721        int trimmed = 0;
00722        char mask[256];
00723 
00724        if (what) {
00725               php_charmask((unsigned char*)what, what_len, mask TSRMLS_CC);
00726        } else {
00727               php_charmask((unsigned char*)" \n\r\t\v\0", 6, mask TSRMLS_CC);
00728        }
00729 
00730        if (mode & 1) {
00731               for (i = 0; i < len; i++) {
00732                      if (mask[(unsigned char)c[i]]) {
00733                             trimmed++;
00734                      } else {
00735                             break;
00736                      }
00737               }
00738               len -= trimmed;
00739               c += trimmed;
00740        }
00741        if (mode & 2) {
00742               for (i = len - 1; i >= 0; i--) {
00743                      if (mask[(unsigned char)c[i]]) {
00744                             len--;
00745                      } else {
00746                             break;
00747                      }
00748               }
00749        }
00750 
00751        if (return_value) {
00752               RETVAL_STRINGL(c, len, 1);
00753        } else {
00754               return estrndup(c, len);
00755        }
00756        return "";
00757 }
00758 /* }}} */
00759 
00760 /* {{{ php_do_trim
00761  * Base for trim(), rtrim() and ltrim() functions.
00762  */
00763 static void php_do_trim(INTERNAL_FUNCTION_PARAMETERS, int mode)
00764 {
00765        char *str;
00766        char *what = NULL;
00767        int str_len, what_len = 0;
00768        
00769        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &str, &str_len, &what, &what_len) == FAILURE) {
00770               return;
00771        }
00772        
00773        php_trim(str, str_len, what, what_len, return_value, mode TSRMLS_CC);
00774 }
00775 /* }}} */
00776 
00777 /* {{{ proto string trim(string str [, string character_mask])
00778    Strips whitespace from the beginning and end of a string */
00779 PHP_FUNCTION(trim)
00780 {
00781        php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3);
00782 }
00783 /* }}} */
00784 
00785 /* {{{ proto string rtrim(string str [, string character_mask])
00786    Removes trailing whitespace */
00787 PHP_FUNCTION(rtrim)
00788 {
00789        php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 2);
00790 }
00791 /* }}} */
00792 
00793 /* {{{ proto string ltrim(string str [, string character_mask])
00794    Strips whitespace from the beginning of a string */
00795 PHP_FUNCTION(ltrim)
00796 {
00797        php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
00798 }
00799 /* }}} */
00800 
00801 /* {{{ proto string wordwrap(string str [, int width [, string break [, boolean cut]]])
00802    Wraps buffer to selected number of characters using string break char */
00803 PHP_FUNCTION(wordwrap)
00804 {
00805        const char *text, *breakchar = "\n";
00806        char *newtext;
00807        int textlen, breakcharlen = 1, newtextlen, chk;
00808        size_t alloced;
00809        long current = 0, laststart = 0, lastspace = 0;
00810        long linelength = 75;
00811        zend_bool docut = 0;
00812 
00813        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lsb", &text, &textlen, &linelength, &breakchar, &breakcharlen, &docut) == FAILURE) {
00814               return;
00815        }
00816 
00817        if (textlen == 0) {
00818               RETURN_EMPTY_STRING();
00819        }
00820 
00821        if (breakcharlen == 0) {
00822               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Break string cannot be empty");
00823               RETURN_FALSE;
00824        }
00825 
00826        if (linelength == 0 && docut) {
00827               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't force cut when width is zero");
00828               RETURN_FALSE;
00829        }
00830 
00831        /* Special case for a single-character break as it needs no
00832           additional storage space */
00833        if (breakcharlen == 1 && !docut) {
00834               newtext = estrndup(text, textlen);
00835 
00836               laststart = lastspace = 0;
00837               for (current = 0; current < textlen; current++) {
00838                      if (text[current] == breakchar[0]) {
00839                             laststart = lastspace = current + 1;
00840                      } else if (text[current] == ' ') {
00841                             if (current - laststart >= linelength) {
00842                                    newtext[current] = breakchar[0];
00843                                    laststart = current + 1;
00844                             }
00845                             lastspace = current;
00846                      } else if (current - laststart >= linelength && laststart != lastspace) {
00847                             newtext[lastspace] = breakchar[0];
00848                             laststart = lastspace + 1;
00849                      }
00850               }
00851 
00852               RETURN_STRINGL(newtext, textlen, 0);
00853        } else {
00854               /* Multiple character line break or forced cut */
00855               if (linelength > 0) {
00856                      chk = (int)(textlen/linelength + 1);
00857                      newtext = safe_emalloc(chk, breakcharlen, textlen + 1);
00858                      alloced = textlen + chk * breakcharlen + 1;
00859               } else {
00860                      chk = textlen;
00861                      alloced = textlen * (breakcharlen + 1) + 1;
00862                      newtext = safe_emalloc(textlen, (breakcharlen + 1), 1);
00863               }
00864 
00865               /* now keep track of the actual new text length */
00866               newtextlen = 0;
00867 
00868               laststart = lastspace = 0;
00869               for (current = 0; current < textlen; current++) {
00870                      if (chk <= 0) {
00871                             alloced += (int) (((textlen - current + 1)/linelength + 1) * breakcharlen) + 1;
00872                             newtext = erealloc(newtext, alloced);
00873                             chk = (int) ((textlen - current)/linelength) + 1;
00874                      }
00875                      /* when we hit an existing break, copy to new buffer, and
00876                       * fix up laststart and lastspace */
00877                      if (text[current] == breakchar[0]
00878                             && current + breakcharlen < textlen
00879                             && !strncmp(text+current, breakchar, breakcharlen)) {
00880                             memcpy(newtext+newtextlen, text+laststart, current-laststart+breakcharlen);
00881                             newtextlen += current-laststart+breakcharlen;
00882                             current += breakcharlen - 1;
00883                             laststart = lastspace = current + 1;
00884                             chk--;
00885                      }
00886                      /* if it is a space, check if it is at the line boundary,
00887                       * copy and insert a break, or just keep track of it */
00888                      else if (text[current] == ' ') {
00889                             if (current - laststart >= linelength) {
00890                                    memcpy(newtext+newtextlen, text+laststart, current-laststart);
00891                                    newtextlen += current - laststart;
00892                                    memcpy(newtext+newtextlen, breakchar, breakcharlen);
00893                                    newtextlen += breakcharlen;
00894                                    laststart = current + 1;
00895                                    chk--;
00896                             }
00897                             lastspace = current;
00898                      }
00899                      /* if we are cutting, and we've accumulated enough
00900                       * characters, and we haven't see a space for this line,
00901                       * copy and insert a break. */
00902                      else if (current - laststart >= linelength
00903                                    && docut && laststart >= lastspace) {
00904                             memcpy(newtext+newtextlen, text+laststart, current-laststart);
00905                             newtextlen += current - laststart;
00906                             memcpy(newtext+newtextlen, breakchar, breakcharlen);
00907                             newtextlen += breakcharlen;
00908                             laststart = lastspace = current;
00909                             chk--;
00910                      }
00911                      /* if the current word puts us over the linelength, copy
00912                       * back up until the last space, insert a break, and move
00913                       * up the laststart */
00914                      else if (current - laststart >= linelength
00915                                    && laststart < lastspace) {
00916                             memcpy(newtext+newtextlen, text+laststart, lastspace-laststart);
00917                             newtextlen += lastspace - laststart;
00918                             memcpy(newtext+newtextlen, breakchar, breakcharlen);
00919                             newtextlen += breakcharlen;
00920                             laststart = lastspace = lastspace + 1;
00921                             chk--;
00922                      }
00923               }
00924 
00925               /* copy over any stragglers */
00926               if (laststart != current) {
00927                      memcpy(newtext+newtextlen, text+laststart, current-laststart);
00928                      newtextlen += current - laststart;
00929               }
00930 
00931               newtext[newtextlen] = '\0';
00932               /* free unused memory */
00933               newtext = erealloc(newtext, newtextlen+1);
00934 
00935               RETURN_STRINGL(newtext, newtextlen, 0);
00936        }
00937 }
00938 /* }}} */
00939 
00940 /* {{{ php_explode
00941  */
00942 PHPAPI void php_explode(zval *delim, zval *str, zval *return_value, long limit) 
00943 {
00944        char *p1, *p2, *endp;
00945 
00946        endp = Z_STRVAL_P(str) + Z_STRLEN_P(str);
00947 
00948        p1 = Z_STRVAL_P(str);
00949        p2 = php_memnstr(Z_STRVAL_P(str), Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp);
00950 
00951        if (p2 == NULL) {
00952               add_next_index_stringl(return_value, p1, Z_STRLEN_P(str), 1);
00953        } else {
00954               do {
00955                      add_next_index_stringl(return_value, p1, p2 - p1, 1);
00956                      p1 = p2 + Z_STRLEN_P(delim);
00957               } while ((p2 = php_memnstr(p1, Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp)) != NULL &&
00958                              --limit > 1);
00959 
00960               if (p1 <= endp)
00961                      add_next_index_stringl(return_value, p1, endp-p1, 1);
00962        }
00963 }
00964 /* }}} */
00965 
00966 /* {{{ php_explode_negative_limit
00967  */
00968 PHPAPI void php_explode_negative_limit(zval *delim, zval *str, zval *return_value, long limit) 
00969 {
00970 #define EXPLODE_ALLOC_STEP 64
00971        char *p1, *p2, *endp;
00972        
00973        endp = Z_STRVAL_P(str) + Z_STRLEN_P(str);
00974 
00975        p1 = Z_STRVAL_P(str);
00976        p2 = php_memnstr(Z_STRVAL_P(str), Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp);
00977 
00978        if (p2 == NULL) {
00979               /*
00980               do nothing since limit <= -1, thus if only one chunk - 1 + (limit) <= 0
00981               by doing nothing we return empty array
00982               */
00983        } else {
00984               int allocated = EXPLODE_ALLOC_STEP, found = 0;
00985               long i, to_return;
00986               char **positions = emalloc(allocated * sizeof(char *));
00987 
00988               positions[found++] = p1;
00989               do {
00990                      if (found >= allocated) {
00991                             allocated = found + EXPLODE_ALLOC_STEP;/* make sure we have enough memory */
00992                             positions = erealloc(positions, allocated*sizeof(char *));
00993                      }
00994                      positions[found++] = p1 = p2 + Z_STRLEN_P(delim);
00995               } while ((p2 = php_memnstr(p1, Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp)) != NULL);
00996               
00997               to_return = limit + found;
00998               /* limit is at least -1 therefore no need of bounds checking : i will be always less than found */
00999               for (i = 0;i < to_return;i++) { /* this checks also for to_return > 0 */
01000                      add_next_index_stringl(return_value, positions[i], 
01001                                    (positions[i+1] - Z_STRLEN_P(delim)) - positions[i],
01002                                    1
01003                             );
01004               }
01005               efree(positions);
01006        }
01007 #undef EXPLODE_ALLOC_STEP
01008 }
01009 /* }}} */
01010 
01011 /* {{{ proto array explode(string separator, string str [, int limit])
01012    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. */
01013 PHP_FUNCTION(explode)
01014 {
01015        char *str, *delim;
01016        int str_len = 0, delim_len = 0;
01017        long limit = LONG_MAX; /* No limit */
01018        zval zdelim, zstr;
01019        
01020        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &delim, &delim_len, &str, &str_len, &limit) == FAILURE) {
01021               return;
01022        }
01023        
01024        if (delim_len == 0) {
01025               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty delimiter");
01026               RETURN_FALSE;
01027        }
01028 
01029        array_init(return_value);
01030 
01031        if (str_len == 0) {
01032               if (limit >= 0) {
01033                      add_next_index_stringl(return_value, "", sizeof("") - 1, 1);
01034               } 
01035               return;
01036        }
01037 
01038        ZVAL_STRINGL(&zstr, str, str_len, 0);
01039        ZVAL_STRINGL(&zdelim, delim, delim_len, 0);
01040        if (limit > 1) {
01041               php_explode(&zdelim, &zstr, return_value, limit);
01042        } else if (limit < 0) {
01043               php_explode_negative_limit(&zdelim, &zstr, return_value, limit);
01044        } else {
01045               add_index_stringl(return_value, 0, str, str_len, 1);
01046        }
01047 }
01048 /* }}} */
01049 
01050 /* {{{ proto string join(array src, string glue)
01051    An alias for implode */
01052 /* }}} */
01053 
01054 /* {{{ php_implode
01055  */
01056 PHPAPI void php_implode(zval *delim, zval *arr, zval *return_value TSRMLS_DC) 
01057 {
01058        zval         **tmp;
01059        HashPosition   pos;
01060        smart_str      implstr = {0};
01061        int            numelems, i = 0;
01062        zval tmp_val;
01063        int str_len;
01064 
01065        numelems = zend_hash_num_elements(Z_ARRVAL_P(arr));
01066 
01067        if (numelems == 0) {
01068               RETURN_EMPTY_STRING();
01069        }
01070 
01071        zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(arr), &pos);
01072 
01073        while (zend_hash_get_current_data_ex(Z_ARRVAL_P(arr), (void **) &tmp, &pos) == SUCCESS) {
01074               switch ((*tmp)->type) {
01075                      case IS_STRING:
01076                             smart_str_appendl(&implstr, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
01077                             break;
01078 
01079                      case IS_LONG: {
01080                             char stmp[MAX_LENGTH_OF_LONG + 1];
01081                             str_len = slprintf(stmp, sizeof(stmp), "%ld", Z_LVAL_PP(tmp));
01082                             smart_str_appendl(&implstr, stmp, str_len);
01083                      }
01084                             break;
01085 
01086                      case IS_BOOL:
01087                             if (Z_LVAL_PP(tmp) == 1) {
01088                                    smart_str_appendl(&implstr, "1", sizeof("1")-1);
01089                             }
01090                             break;
01091                      
01092                      case IS_NULL:
01093                             break;
01094 
01095                      case IS_DOUBLE: {
01096                             char *stmp;
01097                             str_len = spprintf(&stmp, 0, "%.*G", (int) EG(precision), Z_DVAL_PP(tmp));
01098                             smart_str_appendl(&implstr, stmp, str_len);
01099                             efree(stmp);
01100                      }
01101                             break;
01102 
01103                      case IS_OBJECT: {
01104                             int copy;
01105                             zval expr;
01106                             zend_make_printable_zval(*tmp, &expr, &copy);
01107                             smart_str_appendl(&implstr, Z_STRVAL(expr), Z_STRLEN(expr));
01108                             if (copy) {
01109                                    zval_dtor(&expr);
01110                             }
01111                      }
01112                             break;
01113 
01114                      default:
01115                             tmp_val = **tmp;
01116                             zval_copy_ctor(&tmp_val);
01117                             convert_to_string(&tmp_val);
01118                             smart_str_appendl(&implstr, Z_STRVAL(tmp_val), Z_STRLEN(tmp_val));
01119                             zval_dtor(&tmp_val);
01120                             break;
01121                             
01122               }
01123 
01124               if (++i != numelems) {
01125                      smart_str_appendl(&implstr, Z_STRVAL_P(delim), Z_STRLEN_P(delim));
01126               }
01127               zend_hash_move_forward_ex(Z_ARRVAL_P(arr), &pos);
01128        }
01129        smart_str_0(&implstr);
01130 
01131        if (implstr.len) {
01132               RETURN_STRINGL(implstr.c, implstr.len, 0);
01133        } else {
01134               smart_str_free(&implstr);
01135               RETURN_EMPTY_STRING();
01136        }
01137 }
01138 /* }}} */
01139 
01140 /* {{{ proto string implode([string glue,] array pieces)
01141    Joins array elements placing glue string between items and return one string */
01142 PHP_FUNCTION(implode)
01143 {
01144        zval **arg1 = NULL, **arg2 = NULL, *delim, *arr;
01145 
01146        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|Z", &arg1, &arg2) == FAILURE) {
01147               return;
01148        }
01149        
01150        if (arg2 == NULL) {
01151               if (Z_TYPE_PP(arg1) != IS_ARRAY) {
01152                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument must be an array");
01153                      return;
01154               }
01155 
01156               MAKE_STD_ZVAL(delim);
01157 #define _IMPL_EMPTY ""
01158               ZVAL_STRINGL(delim, _IMPL_EMPTY, sizeof(_IMPL_EMPTY) - 1, 0);
01159 
01160               SEPARATE_ZVAL(arg1);
01161               arr = *arg1;
01162        } else {
01163               if (Z_TYPE_PP(arg1) == IS_ARRAY) {
01164                      arr = *arg1;
01165                      convert_to_string_ex(arg2);
01166                      delim = *arg2;
01167               } else if (Z_TYPE_PP(arg2) == IS_ARRAY) {
01168                      arr = *arg2;
01169                      convert_to_string_ex(arg1);
01170                      delim = *arg1;
01171               } else {
01172                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid arguments passed");
01173                      return;
01174               }
01175        }
01176        
01177        php_implode(delim, arr, return_value TSRMLS_CC);
01178 
01179        if (arg2 == NULL) {
01180               FREE_ZVAL(delim);
01181        }
01182 }
01183 /* }}} */
01184 
01185 #define STRTOK_TABLE(p) BG(strtok_table)[(unsigned char) *p]   
01186 
01187 /* {{{ proto string strtok([string str,] string token)
01188    Tokenize a string */
01189 PHP_FUNCTION(strtok)
01190 {             
01191        char *str, *tok = NULL;
01192        int str_len, tok_len = 0;
01193        zval *zv;
01194        
01195        char *token;
01196        char *token_end;
01197        char *p;
01198        char *pe;
01199        int skipped = 0;
01200        
01201        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &str, &str_len, &tok, &tok_len) == FAILURE) {
01202               return;
01203        }
01204 
01205        if (ZEND_NUM_ARGS() == 1) {
01206               tok = str;
01207               tok_len = str_len;
01208        } else {
01209               if (BG(strtok_zval)) {
01210                      zval_ptr_dtor(&BG(strtok_zval));
01211               }
01212               MAKE_STD_ZVAL(zv);
01213               ZVAL_STRINGL(zv, str, str_len, 1);
01214 
01215               BG(strtok_zval) = zv;
01216               BG(strtok_last) = BG(strtok_string) = Z_STRVAL_P(zv);
01217               BG(strtok_len) = str_len;
01218        }
01219        
01220        p = BG(strtok_last); /* Where we start to search */
01221        pe = BG(strtok_string) + BG(strtok_len);
01222 
01223        if (!p || p >= pe) {
01224               RETURN_FALSE;
01225        }
01226        
01227        token = tok;
01228        token_end = token + tok_len;
01229 
01230        while (token < token_end) {
01231               STRTOK_TABLE(token++) = 1;
01232        }
01233        
01234        /* Skip leading delimiters */
01235        while (STRTOK_TABLE(p)) {
01236               if (++p >= pe) {
01237                      /* no other chars left */
01238                      BG(strtok_last) = NULL;
01239                      RETVAL_FALSE;
01240                      goto restore;
01241               }
01242               skipped++;
01243        }
01244        
01245        /* We know at this place that *p is no delimiter, so skip it */       
01246        while (++p < pe) {
01247               if (STRTOK_TABLE(p)) {
01248                      goto return_token;   
01249               }
01250        }
01251        
01252        if (p - BG(strtok_last)) {
01253 return_token:
01254               RETVAL_STRINGL(BG(strtok_last) + skipped, (p - BG(strtok_last)) - skipped, 1);
01255               BG(strtok_last) = p + 1;
01256        } else {
01257               RETVAL_FALSE;
01258               BG(strtok_last) = NULL;
01259        }
01260 
01261        /* Restore table -- usually faster then memset'ing the table on every invocation */
01262 restore:
01263        token = tok;
01264        
01265        while (token < token_end) {
01266               STRTOK_TABLE(token++) = 0;
01267        }
01268 }
01269 /* }}} */
01270 
01271 /* {{{ php_strtoupper
01272  */
01273 PHPAPI char *php_strtoupper(char *s, size_t len)
01274 {
01275        unsigned char *c, *e;
01276        
01277        c = (unsigned char *)s;
01278        e = (unsigned char *)c+len;
01279 
01280        while (c < e) {
01281               *c = toupper(*c);
01282               c++;
01283        }
01284        return s;
01285 }
01286 /* }}} */
01287 
01288 /* {{{ proto string strtoupper(string str)
01289    Makes a string uppercase */
01290 PHP_FUNCTION(strtoupper)
01291 {
01292        char *arg;
01293        int arglen;
01294        
01295        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arglen) == FAILURE) {
01296               return;
01297        }
01298 
01299        arg = estrndup(arg, arglen);
01300        php_strtoupper(arg, arglen);       
01301        RETURN_STRINGL(arg, arglen, 0);
01302 }
01303 /* }}} */
01304 
01305 /* {{{ php_strtolower
01306  */
01307 PHPAPI char *php_strtolower(char *s, size_t len)
01308 {
01309        unsigned char *c, *e;
01310        
01311        c = (unsigned char *)s;
01312        e = c+len;
01313 
01314        while (c < e) {
01315               *c = tolower(*c);
01316               c++;
01317        }
01318        return s;
01319 }
01320 /* }}} */
01321 
01322 /* {{{ proto string strtolower(string str)
01323    Makes a string lowercase */
01324 PHP_FUNCTION(strtolower)
01325 {
01326        char *str;
01327        int arglen;
01328        
01329        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &arglen) == FAILURE) {
01330               return;
01331        }
01332 
01333        str = estrndup(str, arglen);
01334        php_strtolower(str, arglen);
01335        RETURN_STRINGL(str, arglen, 0);
01336 }
01337 /* }}} */
01338 
01339 /* {{{ php_basename
01340  */
01341 PHPAPI void php_basename(char *s, size_t len, char *suffix, size_t sufflen, char **p_ret, size_t *p_len TSRMLS_DC)
01342 {
01343        char *ret = NULL, *c, *comp, *cend;
01344        size_t inc_len, cnt;
01345        int state;
01346 
01347        c = comp = cend = s;
01348        cnt = len;
01349        state = 0;
01350        while (cnt > 0) {
01351               inc_len = (*c == '\0' ? 1: php_mblen(c, cnt));
01352 
01353               switch (inc_len) {
01354                      case -2:
01355                      case -1:
01356                             inc_len = 1;
01357                             php_mblen(NULL, 0);
01358                             break;
01359                      case 0:
01360                             goto quit_loop;
01361                      case 1:
01362 #if defined(PHP_WIN32) || defined(NETWARE)
01363                             if (*c == '/' || *c == '\\') {
01364 #else
01365                             if (*c == '/') {
01366 #endif
01367                                    if (state == 1) {
01368                                           state = 0;
01369                                           cend = c;
01370                                    }
01371                             } else {
01372                                    if (state == 0) {
01373                                           comp = c;
01374                                           state = 1;
01375                                    }
01376                             }
01377                             break;
01378                      default:
01379                             if (state == 0) {
01380                                    comp = c;
01381                                    state = 1;
01382                             }
01383                             break;
01384               }
01385               c += inc_len;
01386               cnt -= inc_len;
01387        }
01388 
01389 quit_loop:
01390        if (state == 1) {
01391               cend = c;
01392        }
01393        if (suffix != NULL && sufflen < (uint)(cend - comp) &&
01394                      memcmp(cend - sufflen, suffix, sufflen) == 0) {
01395               cend -= sufflen;
01396        }
01397 
01398        len = cend - comp;
01399 
01400        if (p_ret) {
01401               ret = emalloc(len + 1);
01402               memcpy(ret, comp, len);
01403               ret[len] = '\0';
01404               *p_ret = ret;
01405        }
01406        if (p_len) {
01407               *p_len = len;
01408        }
01409 }
01410 /* }}} */
01411 
01412 /* {{{ proto string basename(string path [, string suffix])
01413    Returns the filename component of the path */
01414 PHP_FUNCTION(basename)
01415 {
01416        char *string, *suffix = NULL, *ret;
01417        int   string_len, suffix_len = 0;
01418        size_t ret_len;
01419 
01420        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &string, &string_len, &suffix, &suffix_len) == FAILURE) {
01421               return;
01422        }
01423 
01424        php_basename(string, string_len, suffix, suffix_len, &ret, &ret_len TSRMLS_CC);
01425        RETURN_STRINGL(ret, (int)ret_len, 0);
01426 }
01427 /* }}} */
01428 
01429 /* {{{ php_dirname
01430    Returns directory name component of path */
01431 PHPAPI size_t php_dirname(char *path, size_t len)
01432 {
01433        return zend_dirname(path, len);
01434 }
01435 /* }}} */
01436 
01437 /* {{{ proto string dirname(string path)
01438    Returns the directory name component of the path */
01439 PHP_FUNCTION(dirname)
01440 {
01441        char *str;
01442        char *ret;
01443        int str_len;
01444        size_t ret_len;
01445 
01446        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
01447               return;
01448        }
01449        
01450        ret = estrndup(str, str_len);
01451        ret_len = php_dirname(ret, str_len);
01452 
01453        RETURN_STRINGL(ret, ret_len, 0);
01454 }
01455 /* }}} */
01456 
01457 /* {{{ proto array pathinfo(string path[, int options])
01458    Returns information about a certain string */
01459 PHP_FUNCTION(pathinfo)
01460 {
01461        zval *tmp;
01462        char *path, *ret = NULL;
01463        int path_len, have_basename;
01464        size_t ret_len;
01465        long opt = PHP_PATHINFO_ALL;
01466 
01467        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &path, &path_len, &opt) == FAILURE) {
01468               return;
01469        }
01470 
01471        have_basename = ((opt & PHP_PATHINFO_BASENAME) == PHP_PATHINFO_BASENAME);
01472        
01473        MAKE_STD_ZVAL(tmp);
01474        array_init(tmp);
01475        
01476        if ((opt & PHP_PATHINFO_DIRNAME) == PHP_PATHINFO_DIRNAME) {
01477               ret = estrndup(path, path_len);
01478               php_dirname(ret, path_len);
01479               if (*ret) {
01480                      add_assoc_string(tmp, "dirname", ret, 1);
01481               }
01482               efree(ret);
01483               ret = NULL;
01484        }
01485        
01486        if (have_basename) {
01487               php_basename(path, path_len, NULL, 0, &ret, &ret_len TSRMLS_CC);
01488               add_assoc_stringl(tmp, "basename", ret, ret_len, 0);
01489        }
01490        
01491        if ((opt & PHP_PATHINFO_EXTENSION) == PHP_PATHINFO_EXTENSION) {
01492               char *p;
01493               int idx;
01494 
01495               if (!have_basename) {
01496                      php_basename(path, path_len, NULL, 0, &ret, &ret_len TSRMLS_CC);
01497               }
01498 
01499               p = zend_memrchr(ret, '.', ret_len);
01500 
01501               if (p) {
01502                      idx = p - ret;
01503                      add_assoc_stringl(tmp, "extension", ret + idx + 1, ret_len - idx - 1, 1);
01504               }
01505        }
01506        
01507        if ((opt & PHP_PATHINFO_FILENAME) == PHP_PATHINFO_FILENAME) {
01508               char *p;
01509               int idx;
01510 
01511               /* Have we alrady looked up the basename? */
01512               if (!have_basename && !ret) {
01513                      php_basename(path, path_len, NULL, 0, &ret, &ret_len TSRMLS_CC);
01514               }
01515 
01516               p = zend_memrchr(ret, '.', ret_len);
01517 
01518               idx = p ? (p - ret) : ret_len;
01519               add_assoc_stringl(tmp, "filename", ret, idx, 1);
01520        }
01521 
01522        if (!have_basename && ret) {
01523               efree(ret);
01524        }
01525 
01526        if (opt == PHP_PATHINFO_ALL) {
01527               RETURN_ZVAL(tmp, 0, 1);
01528        } else {
01529               zval **element;
01530               if (zend_hash_get_current_data(Z_ARRVAL_P(tmp), (void **) &element) == SUCCESS) {
01531                      RETVAL_ZVAL(*element, 1, 0);
01532               } else {
01533                      ZVAL_EMPTY_STRING(return_value);
01534               }
01535        }
01536 
01537        zval_ptr_dtor(&tmp);
01538 }
01539 /* }}} */
01540 
01541 /* {{{ php_stristr
01542    case insensitve strstr */
01543 PHPAPI char *php_stristr(char *s, char *t, size_t s_len, size_t t_len)
01544 {
01545        php_strtolower(s, s_len);
01546        php_strtolower(t, t_len);
01547        return php_memnstr(s, t, t_len, s + s_len);
01548 }
01549 /* }}} */
01550 
01551 /* {{{ php_strspn
01552  */
01553 PHPAPI size_t php_strspn(char *s1, char *s2, char *s1_end, char *s2_end)
01554 {
01555        register const char *p = s1, *spanp;
01556        register char c = *p;
01557 
01558 cont:
01559        for (spanp = s2; p != s1_end && spanp != s2_end;) {
01560               if (*spanp++ == c) {
01561                      c = *(++p);
01562                      goto cont;
01563               }
01564        }
01565        return (p - s1);
01566 }
01567 /* }}} */
01568 
01569 /* {{{ php_strcspn
01570  */
01571 PHPAPI size_t php_strcspn(char *s1, char *s2, char *s1_end, char *s2_end)
01572 {
01573        register const char *p, *spanp;
01574        register char c = *s1;
01575 
01576        for (p = s1;;) {
01577               spanp = s2;
01578               do {
01579                      if (*spanp == c || p == s1_end) {
01580                             return p - s1;
01581                      }
01582               } while (spanp++ < (s2_end - 1));
01583               c = *++p;
01584        }
01585        /* NOTREACHED */
01586 }
01587 /* }}} */
01588 
01589 /* {{{ php_needle_char
01590  */
01591 static int php_needle_char(zval *needle, char *target TSRMLS_DC)
01592 {
01593        switch (Z_TYPE_P(needle)) {
01594               case IS_LONG:
01595               case IS_BOOL:
01596                      *target = (char)Z_LVAL_P(needle);
01597                      return SUCCESS;
01598               case IS_NULL:
01599                      *target = '\0';
01600                      return SUCCESS;
01601               case IS_DOUBLE:
01602                      *target = (char)(int)Z_DVAL_P(needle);
01603                      return SUCCESS;
01604               case IS_OBJECT:
01605                      {
01606                             zval holder = *needle;
01607                             zval_copy_ctor(&(holder));
01608                             convert_to_long(&(holder));
01609                             if(Z_TYPE(holder) != IS_LONG) {
01610                                    return FAILURE;
01611                             }
01612                             *target = (char)Z_LVAL(holder);
01613                             return SUCCESS;
01614                      }
01615               default: {
01616                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "needle is not a string or an integer");
01617                      return FAILURE;
01618                }
01619        }
01620 }
01621 /* }}} */
01622 
01623 /* {{{ proto string stristr(string haystack, string needle[, bool part])
01624    Finds first occurrence of a string within another, case insensitive */
01625 PHP_FUNCTION(stristr)
01626 {
01627        zval *needle;
01628        char *haystack;
01629        int haystack_len;
01630        char *found = NULL;
01631        int  found_offset;
01632        char *haystack_dup;
01633        char needle_char[2];
01634        zend_bool part = 0;
01635        
01636        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|b", &haystack, &haystack_len, &needle, &part) == FAILURE) {
01637               return;
01638        }
01639 
01640        haystack_dup = estrndup(haystack, haystack_len);
01641 
01642        if (Z_TYPE_P(needle) == IS_STRING) {
01643               char *orig_needle;
01644               if (!Z_STRLEN_P(needle)) {
01645                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty delimiter");
01646                      efree(haystack_dup);
01647                      RETURN_FALSE;
01648               }
01649               orig_needle = estrndup(Z_STRVAL_P(needle), Z_STRLEN_P(needle));
01650               found = php_stristr(haystack_dup, orig_needle,   haystack_len, Z_STRLEN_P(needle));
01651               efree(orig_needle);
01652        } else {
01653               if (php_needle_char(needle, needle_char TSRMLS_CC) != SUCCESS) {
01654                      efree(haystack_dup);
01655                      RETURN_FALSE;
01656               }
01657               needle_char[1] = 0;
01658 
01659               found = php_stristr(haystack_dup, needle_char,   haystack_len, 1);
01660        }
01661 
01662        if (found) {
01663               found_offset = found - haystack_dup;
01664               if (part) {
01665                      RETVAL_STRINGL(haystack, found_offset, 1);
01666               } else {
01667                      RETVAL_STRINGL(haystack + found_offset, haystack_len - found_offset, 1);
01668               }                    
01669        } else {
01670               RETVAL_FALSE;
01671        }
01672 
01673        efree(haystack_dup);
01674 }
01675 /* }}} */
01676 
01677 /* {{{ proto string strstr(string haystack, string needle[, bool part])
01678    Finds first occurrence of a string within another */
01679 PHP_FUNCTION(strstr)
01680 {
01681        zval *needle;
01682        char *haystack;
01683        int haystack_len;
01684        char *found = NULL;
01685        char needle_char[2];
01686        long found_offset;
01687        zend_bool part = 0;
01688        
01689        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|b", &haystack, &haystack_len, &needle, &part) == FAILURE) {
01690               return;
01691        }
01692 
01693        if (Z_TYPE_P(needle) == IS_STRING) {
01694               if (!Z_STRLEN_P(needle)) {
01695                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty delimiter");
01696                      RETURN_FALSE;
01697               }
01698 
01699               found = php_memnstr(haystack, Z_STRVAL_P(needle), Z_STRLEN_P(needle), haystack + haystack_len);
01700        } else {
01701               if (php_needle_char(needle, needle_char TSRMLS_CC) != SUCCESS) {
01702                      RETURN_FALSE;
01703               }
01704               needle_char[1] = 0;
01705 
01706               found = php_memnstr(haystack, needle_char,       1, haystack + haystack_len);
01707        }
01708 
01709        if (found) {
01710               found_offset = found - haystack;
01711               if (part) {
01712                      RETURN_STRINGL(haystack, found_offset, 1);
01713               } else {
01714                      RETURN_STRINGL(found, haystack_len - found_offset, 1);
01715               }
01716        }
01717        RETURN_FALSE;
01718 }
01719 /* }}} */
01720 
01721 /* {{{ proto string strchr(string haystack, string needle)
01722    An alias for strstr */
01723 /* }}} */
01724 
01725 /* {{{ proto int strpos(string haystack, string needle [, int offset])
01726    Finds position of first occurrence of a string within another */
01727 PHP_FUNCTION(strpos)
01728 {
01729        zval *needle;
01730        char *haystack;
01731        char *found = NULL;
01732        char  needle_char[2];
01733        long  offset = 0;
01734        int   haystack_len;
01735        
01736        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &haystack, &haystack_len, &needle, &offset) == FAILURE) {
01737               return;
01738        }
01739 
01740        if (offset < 0 || offset > haystack_len) {
01741               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset not contained in string");
01742               RETURN_FALSE;
01743        }
01744 
01745        if (Z_TYPE_P(needle) == IS_STRING) {
01746               if (!Z_STRLEN_P(needle)) {
01747                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty delimiter");
01748                      RETURN_FALSE;
01749               }
01750 
01751               found = php_memnstr(haystack + offset,
01752                                      Z_STRVAL_P(needle),
01753                                      Z_STRLEN_P(needle),
01754                                      haystack + haystack_len);
01755        } else {
01756               if (php_needle_char(needle, needle_char TSRMLS_CC) != SUCCESS) {
01757                      RETURN_FALSE;
01758               }
01759               needle_char[1] = 0;
01760 
01761               found = php_memnstr(haystack + offset,
01762                                                  needle_char,
01763                                                  1,
01764                                   haystack + haystack_len);
01765        }
01766 
01767        if (found) {
01768               RETURN_LONG(found - haystack);
01769        } else {
01770               RETURN_FALSE;
01771        }
01772 }
01773 /* }}} */
01774 
01775 /* {{{ proto int stripos(string haystack, string needle [, int offset])
01776    Finds position of first occurrence of a string within another, case insensitive */
01777 PHP_FUNCTION(stripos)
01778 {
01779        char *found = NULL;
01780        char *haystack;
01781        int haystack_len;
01782        long offset = 0;
01783        char *needle_dup = NULL, *haystack_dup;
01784        char needle_char[2];
01785        zval *needle;
01786 
01787        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &haystack, &haystack_len, &needle, &offset) == FAILURE) {
01788               return;
01789        }
01790 
01791        if (offset < 0 || offset > haystack_len) {
01792               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset not contained in string");
01793               RETURN_FALSE;
01794        }
01795 
01796        if (haystack_len == 0) {
01797               RETURN_FALSE;
01798        }
01799 
01800        haystack_dup = estrndup(haystack, haystack_len);
01801        php_strtolower(haystack_dup, haystack_len);
01802 
01803        if (Z_TYPE_P(needle) == IS_STRING) {
01804               if (Z_STRLEN_P(needle) == 0 || Z_STRLEN_P(needle) > haystack_len) {
01805                      efree(haystack_dup);
01806                      RETURN_FALSE;
01807               }
01808 
01809               needle_dup = estrndup(Z_STRVAL_P(needle), Z_STRLEN_P(needle));
01810               php_strtolower(needle_dup, Z_STRLEN_P(needle));
01811               found = php_memnstr(haystack_dup + offset, needle_dup, Z_STRLEN_P(needle), haystack_dup + haystack_len);
01812        } else {
01813               if (php_needle_char(needle, needle_char TSRMLS_CC) != SUCCESS) {
01814                      efree(haystack_dup);
01815                      RETURN_FALSE;
01816               }
01817               needle_char[0] = tolower(needle_char[0]);
01818               needle_char[1] = '\0';
01819               found = php_memnstr(haystack_dup + offset, 
01820                                                  needle_char, 
01821                                                  sizeof(needle_char) - 1, 
01822                                                  haystack_dup + haystack_len);
01823        }
01824 
01825        efree(haystack_dup);
01826        if (needle_dup) {
01827               efree(needle_dup);
01828        }
01829 
01830        if (found) {
01831               RETURN_LONG(found - haystack_dup);
01832        } else {
01833               RETURN_FALSE;
01834        }
01835 }
01836 /* }}} */
01837 
01838 /* {{{ proto int strrpos(string haystack, string needle [, int offset])
01839    Finds position of last occurrence of a string within another string */
01840 PHP_FUNCTION(strrpos)
01841 {
01842        zval *zneedle;
01843        char *needle, *haystack;
01844        int needle_len, haystack_len;
01845        long offset = 0;
01846        char *p, *e, ord_needle[2];
01847 
01848        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &haystack, &haystack_len, &zneedle, &offset) == FAILURE) {
01849               RETURN_FALSE;
01850        }
01851 
01852        if (Z_TYPE_P(zneedle) == IS_STRING) {
01853               needle = Z_STRVAL_P(zneedle);
01854               needle_len = Z_STRLEN_P(zneedle);
01855        } else {
01856               if (php_needle_char(zneedle, ord_needle TSRMLS_CC) != SUCCESS) {
01857                      RETURN_FALSE;
01858               }
01859               ord_needle[1] = '\0';
01860               needle = ord_needle;
01861               needle_len = 1;
01862        }
01863 
01864        if ((haystack_len == 0) || (needle_len == 0)) {
01865               RETURN_FALSE;
01866        }
01867 
01868        if (offset >= 0) {
01869               if (offset > haystack_len) {
01870                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset is greater than the length of haystack string");
01871                      RETURN_FALSE;
01872               }
01873               p = haystack + offset;
01874               e = haystack + haystack_len - needle_len;
01875        } else {
01876               if (offset < -INT_MAX || -offset > haystack_len) {
01877                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset is greater than the length of haystack string");
01878                      RETURN_FALSE;
01879               }
01880 
01881               p = haystack;
01882               if (needle_len > -offset) {
01883                      e = haystack + haystack_len - needle_len;
01884               } else {
01885                      e = haystack + haystack_len + offset;
01886               }
01887        }
01888 
01889        if (needle_len == 1) {
01890               /* Single character search can shortcut memcmps */
01891               while (e >= p) {
01892                      if (*e == *needle) {
01893                             RETURN_LONG(e - p + (offset > 0 ? offset : 0));
01894                      }
01895                      e--;
01896               }
01897               RETURN_FALSE;
01898        }
01899 
01900        while (e >= p) {
01901               if (memcmp(e, needle, needle_len) == 0) {
01902                      RETURN_LONG(e - p + (offset > 0 ? offset : 0));
01903               }
01904               e--;
01905        }
01906 
01907        RETURN_FALSE;
01908 }
01909 /* }}} */
01910 
01911 /* {{{ proto int strripos(string haystack, string needle [, int offset])
01912    Finds position of last occurrence of a string within another string */
01913 PHP_FUNCTION(strripos)
01914 {
01915        zval *zneedle;
01916        char *needle, *haystack;
01917        int needle_len, haystack_len;
01918        long offset = 0;
01919        char *p, *e, ord_needle[2];
01920        char *needle_dup, *haystack_dup;
01921 
01922        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &haystack, &haystack_len, &zneedle, &offset) == FAILURE) {
01923               RETURN_FALSE;
01924        }
01925 
01926        if (Z_TYPE_P(zneedle) == IS_STRING) {
01927               needle = Z_STRVAL_P(zneedle);
01928               needle_len = Z_STRLEN_P(zneedle);
01929        } else {
01930               if (php_needle_char(zneedle, ord_needle TSRMLS_CC) != SUCCESS) {
01931                      RETURN_FALSE;
01932               }
01933               ord_needle[1] = '\0';
01934               needle = ord_needle;
01935               needle_len = 1;
01936        }
01937 
01938        if ((haystack_len == 0) || (needle_len == 0)) {
01939               RETURN_FALSE;
01940        }
01941 
01942        if (needle_len == 1) {
01943               /* Single character search can shortcut memcmps 
01944                  Can also avoid tolower emallocs */
01945               if (offset >= 0) {
01946                      if (offset > haystack_len) {
01947                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset is greater than the length of haystack string");
01948                             RETURN_FALSE;
01949                      }
01950                      p = haystack + offset;
01951                      e = haystack + haystack_len - 1;
01952               } else {
01953                      p = haystack;
01954                      if (offset < -INT_MAX || -offset > haystack_len) {
01955                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset is greater than the length of haystack string");
01956                             RETURN_FALSE;
01957                      }
01958                      e = haystack + haystack_len + offset;
01959               }
01960               /* Borrow that ord_needle buffer to avoid repeatedly tolower()ing needle */
01961               *ord_needle = tolower(*needle);
01962               while (e >= p) {
01963                      if (tolower(*e) == *ord_needle) {
01964                             RETURN_LONG(e - p + (offset > 0 ? offset : 0));
01965                      }
01966                      e--;
01967               }
01968               RETURN_FALSE;
01969        }
01970 
01971        needle_dup = estrndup(needle, needle_len);
01972        php_strtolower(needle_dup, needle_len);
01973        haystack_dup = estrndup(haystack, haystack_len);
01974        php_strtolower(haystack_dup, haystack_len);
01975 
01976        if (offset >= 0) {
01977               if (offset > haystack_len) {
01978                      efree(needle_dup);
01979                      efree(haystack_dup);
01980                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset is greater than the length of haystack string");
01981                      RETURN_FALSE;
01982               }
01983               p = haystack_dup + offset;
01984               e = haystack_dup + haystack_len - needle_len;
01985        } else {
01986               if (offset < -INT_MAX || -offset > haystack_len) {
01987                      efree(needle_dup);
01988                      efree(haystack_dup);
01989                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset is greater than the length of haystack string");
01990                      RETURN_FALSE;
01991               }
01992               p = haystack_dup;
01993               if (needle_len > -offset) {
01994                      e = haystack_dup + haystack_len - needle_len;
01995               } else {
01996                      e = haystack_dup + haystack_len + offset;
01997               }
01998        }
01999 
02000        while (e >= p) {
02001               if (memcmp(e, needle_dup, needle_len) == 0) {
02002                      efree(haystack_dup);
02003                      efree(needle_dup);
02004                      RETURN_LONG(e - p + (offset > 0 ? offset : 0));
02005               }
02006               e--;
02007        }
02008 
02009        efree(haystack_dup);
02010        efree(needle_dup);
02011        RETURN_FALSE;
02012 }
02013 /* }}} */
02014 
02015 /* {{{ proto string strrchr(string haystack, string needle)
02016    Finds the last occurrence of a character in a string within another */
02017 PHP_FUNCTION(strrchr)
02018 {
02019        zval *needle;
02020        char *haystack;
02021        char *found = NULL;
02022        long found_offset;
02023        int  haystack_len;
02024        
02025        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &haystack, &haystack_len, &needle) == FAILURE) {
02026               return;
02027        }
02028 
02029        if (Z_TYPE_P(needle) == IS_STRING) {
02030               found = zend_memrchr(haystack, *Z_STRVAL_P(needle), haystack_len);
02031        } else {
02032               char needle_chr;
02033               if (php_needle_char(needle, &needle_chr TSRMLS_CC) != SUCCESS) {
02034                      RETURN_FALSE;
02035               }
02036 
02037               found = zend_memrchr(haystack,  needle_chr, haystack_len);
02038        }
02039 
02040        if (found) {
02041               found_offset = found - haystack;
02042               RETURN_STRINGL(found, haystack_len - found_offset, 1);
02043        } else {
02044               RETURN_FALSE;
02045        }
02046 }
02047 /* }}} */
02048 
02049 /* {{{ php_chunk_split
02050  */
02051 static char *php_chunk_split(char *src, int srclen, char *end, int endlen, int chunklen, int *destlen)
02052 {
02053        char *dest;
02054        char *p, *q;
02055        int chunks; /* complete chunks! */
02056        int restlen;
02057        int out_len; 
02058 
02059        chunks = srclen / chunklen;
02060        restlen = srclen - chunks * chunklen; /* srclen % chunklen */
02061 
02062        if(chunks > INT_MAX - 1) {
02063               return NULL;
02064        }
02065        out_len = chunks + 1;
02066        if(endlen !=0 && out_len > INT_MAX/endlen) {
02067               return NULL;
02068        }
02069        out_len *= endlen;
02070        if(out_len > INT_MAX - srclen - 1) {
02071               return NULL;
02072        }
02073        out_len += srclen + 1;
02074 
02075        dest = safe_emalloc((int)out_len, sizeof(char), 0);
02076 
02077        for (p = src, q = dest; p < (src + srclen - chunklen + 1); ) {
02078               memcpy(q, p, chunklen);
02079               q += chunklen;
02080               memcpy(q, end, endlen);
02081               q += endlen;
02082               p += chunklen;
02083        }
02084 
02085        if (restlen) {
02086               memcpy(q, p, restlen);
02087               q += restlen;
02088               memcpy(q, end, endlen);
02089               q += endlen;
02090        }
02091 
02092        *q = '\0';
02093        if (destlen) {
02094               *destlen = q - dest;
02095        }
02096 
02097        return(dest);
02098 }
02099 /* }}} */
02100 
02101 /* {{{ proto string chunk_split(string str [, int chunklen [, string ending]])
02102    Returns split line */
02103 PHP_FUNCTION(chunk_split) 
02104 {
02105        char *str;
02106        char *result;
02107        char *end    = "\r\n";
02108        int endlen   = 2;
02109        long chunklen = 76;
02110        int result_len;
02111        int str_len;
02112        
02113        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ls", &str, &str_len, &chunklen, &end, &endlen) == FAILURE) {
02114               return;
02115        }
02116 
02117        if (chunklen <= 0) {
02118               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Chunk length should be greater than zero");
02119               RETURN_FALSE;
02120        }
02121 
02122        if (chunklen > str_len) {
02123               /* to maintain BC, we must return original string + ending */
02124               result_len = endlen + str_len;
02125               result = emalloc(result_len + 1);
02126               memcpy(result, str, str_len);
02127               memcpy(result + str_len, end, endlen);
02128               result[result_len] = '\0'; 
02129               RETURN_STRINGL(result, result_len, 0);    
02130        }
02131 
02132        if (!str_len) {
02133               RETURN_EMPTY_STRING();
02134        }
02135 
02136        result = php_chunk_split(str, str_len, end, endlen, chunklen, &result_len);
02137 
02138        if (result) {
02139               RETURN_STRINGL(result, result_len, 0);
02140        } else {
02141               RETURN_FALSE;
02142        }
02143 }
02144 /* }}} */
02145 
02146 /* {{{ proto string substr(string str, int start [, int length])
02147    Returns part of a string */
02148 PHP_FUNCTION(substr)
02149 {
02150        char *str;
02151        long l = 0, f;
02152        int str_len;
02153        int argc = ZEND_NUM_ARGS();
02154        
02155        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|l", &str, &str_len, &f, &l) == FAILURE) {
02156               return;
02157        }
02158 
02159        if (argc > 2) {
02160               if ((l < 0 && -l > str_len)) {
02161                      RETURN_FALSE;
02162               } else if (l > str_len) {
02163                      l = str_len;
02164               }
02165        } else {
02166               l = str_len;
02167        }
02168        
02169        if (f > str_len) {
02170               RETURN_FALSE;
02171        } else if (f < 0 && -f > str_len) {
02172               f = 0;
02173        }
02174 
02175        if (l < 0 && (l + str_len - f) < 0) {
02176               RETURN_FALSE;
02177        }
02178 
02179        /* if "from" position is negative, count start position from the end
02180         * of the string
02181         */
02182        if (f < 0) {
02183               f = str_len + f;
02184               if (f < 0) {
02185                      f = 0;
02186               }
02187        }
02188 
02189        /* if "length" position is negative, set it to the length
02190         * needed to stop that many chars from the end of the string
02191         */
02192        if (l < 0) {
02193               l = (str_len - f) + l;
02194               if (l < 0) {
02195                      l = 0;
02196               }
02197        }
02198 
02199        if (f >= str_len) {
02200               RETURN_FALSE;
02201        }
02202 
02203        if ((f + l) > str_len) {
02204               l = str_len - f;
02205        }
02206 
02207        RETURN_STRINGL(str + f, l, 1);
02208 }
02209 /* }}} */
02210 
02211 /* {{{ proto mixed substr_replace(mixed str, mixed repl, mixed start [, mixed length])
02212    Replaces part of a string with another string */
02213 PHP_FUNCTION(substr_replace)
02214 {
02215        zval **str;
02216        zval **from;
02217        zval **len = NULL;
02218        zval **repl;
02219        char *result;
02220        int result_len;
02221        int l = 0;
02222        int f;
02223        int argc = ZEND_NUM_ARGS();
02224 
02225        HashPosition pos_str, pos_from, pos_repl, pos_len;
02226        zval **tmp_str = NULL, **tmp_from = NULL, **tmp_repl = NULL, **tmp_len= NULL;
02227 
02228        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZZ|Z", &str, &repl, &from, &len) == FAILURE) {
02229               return;
02230        }
02231        
02232        if (Z_TYPE_PP(str) != IS_ARRAY) {
02233               if (Z_ISREF_PP(str)) {
02234                      SEPARATE_ZVAL(str);
02235               }
02236               convert_to_string_ex(str);
02237        }
02238        if (Z_TYPE_PP(repl) != IS_ARRAY) {
02239               if (Z_ISREF_PP(repl)) {
02240                      SEPARATE_ZVAL(repl);
02241               }
02242               convert_to_string_ex(repl);
02243        }
02244        if (Z_TYPE_PP(from) != IS_ARRAY) {
02245               if (Z_ISREF_PP(from)) {
02246                      SEPARATE_ZVAL(from);
02247               }
02248               convert_to_long_ex(from);
02249        }
02250 
02251        if (argc > 3) {
02252               SEPARATE_ZVAL(len);
02253               if (Z_TYPE_PP(len) != IS_ARRAY) {
02254                      convert_to_long_ex(len);
02255                      l = Z_LVAL_PP(len);
02256               }
02257        } else {
02258               if (Z_TYPE_PP(str) != IS_ARRAY) {
02259                      l = Z_STRLEN_PP(str);
02260               }
02261        }
02262 
02263        if (Z_TYPE_PP(str) == IS_STRING) {
02264               if (
02265                      (argc == 3 && Z_TYPE_PP(from) == IS_ARRAY) || 
02266                      (argc == 4 && Z_TYPE_PP(from) != Z_TYPE_PP(len))
02267               ) {
02268                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "'from' and 'len' should be of same type - numerical or array ");
02269                      RETURN_STRINGL(Z_STRVAL_PP(str), Z_STRLEN_PP(str), 1);         
02270               }
02271               if (argc == 4 && Z_TYPE_PP(from) == IS_ARRAY) {
02272                      if (zend_hash_num_elements(Z_ARRVAL_PP(from)) != zend_hash_num_elements(Z_ARRVAL_PP(len))) {
02273                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "'from' and 'len' should have the same number of elements");
02274                             RETURN_STRINGL(Z_STRVAL_PP(str), Z_STRLEN_PP(str), 1);         
02275                      }
02276               }
02277        }
02278        
02279        if (Z_TYPE_PP(str) != IS_ARRAY) {
02280               if (Z_TYPE_PP(from) != IS_ARRAY) {
02281                      int repl_len = 0;
02282 
02283                      f = Z_LVAL_PP(from);
02284 
02285                      /* if "from" position is negative, count start position from the end
02286                       * of the string
02287                       */
02288                      if (f < 0) {
02289                             f = Z_STRLEN_PP(str) + f;
02290                             if (f < 0) {
02291                                    f = 0;
02292                             }
02293                      } else if (f > Z_STRLEN_PP(str)) {
02294                             f = Z_STRLEN_PP(str);
02295                      }
02296                      /* if "length" position is negative, set it to the length
02297                       * needed to stop that many chars from the end of the string
02298                       */
02299                      if (l < 0) {
02300                             l = (Z_STRLEN_PP(str) - f) + l;
02301                             if (l < 0) {
02302                                    l = 0;
02303                             }
02304                      }
02305 
02306                      if (f > Z_STRLEN_PP(str) || (f < 0 && -f > Z_STRLEN_PP(str))) {
02307                             RETURN_FALSE;
02308                      } else if (l > Z_STRLEN_PP(str) || (l < 0 && -l > Z_STRLEN_PP(str))) {
02309                             l = Z_STRLEN_PP(str);
02310                      }
02311 
02312                      if ((f + l) > Z_STRLEN_PP(str)) {
02313                             l = Z_STRLEN_PP(str) - f;
02314                      }
02315                      if (Z_TYPE_PP(repl) == IS_ARRAY) {
02316                             zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(repl), &pos_repl);
02317                             if (SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_PP(repl), (void **) &tmp_repl, &pos_repl)) {
02318                                    convert_to_string_ex(tmp_repl);
02319                                    repl_len = Z_STRLEN_PP(tmp_repl);
02320                             }
02321                      } else {
02322                             repl_len = Z_STRLEN_PP(repl);
02323                      }
02324                      result_len = Z_STRLEN_PP(str) - l + repl_len;
02325                      result = emalloc(result_len + 1);
02326 
02327                      memcpy(result, Z_STRVAL_PP(str), f);
02328                      if (repl_len) {
02329                             memcpy((result + f), (Z_TYPE_PP(repl) == IS_ARRAY ? Z_STRVAL_PP(tmp_repl) : Z_STRVAL_PP(repl)), repl_len);
02330                      }
02331                      memcpy((result + f + repl_len), Z_STRVAL_PP(str) + f + l, Z_STRLEN_PP(str) - f - l);
02332                      result[result_len] = '\0';
02333                      RETURN_STRINGL(result, result_len, 0);
02334               } else {
02335                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Functionality of 'from' and 'len' as arrays is not implemented");
02336                      RETURN_STRINGL(Z_STRVAL_PP(str), Z_STRLEN_PP(str), 1);  
02337               }
02338        } else { /* str is array of strings */
02339               char *str_index = NULL;
02340               uint str_index_len;
02341               ulong num_index;
02342 
02343               array_init(return_value);
02344 
02345               if (Z_TYPE_PP(from) == IS_ARRAY) {
02346                      zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(from), &pos_from);
02347               }
02348 
02349               if (argc > 3 && Z_TYPE_PP(len) == IS_ARRAY) {
02350                      zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(len), &pos_len);
02351               }
02352 
02353               if (Z_TYPE_PP(repl) == IS_ARRAY) {
02354                      zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(repl), &pos_repl);
02355               }
02356 
02357               zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(str), &pos_str);
02358               while (zend_hash_get_current_data_ex(Z_ARRVAL_PP(str), (void **) &tmp_str, &pos_str) == SUCCESS) {
02359                      zval *orig_str;
02360                      zval dummy;
02361                      if(Z_TYPE_PP(tmp_str) != IS_STRING) {
02362                             dummy = **tmp_str;
02363                             orig_str = &dummy;
02364                             zval_copy_ctor(orig_str);
02365                             convert_to_string(orig_str);
02366                      } else {
02367                             orig_str = *tmp_str;
02368                      }
02369 
02370                      if (Z_TYPE_PP(from) == IS_ARRAY) {
02371                             if (SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_PP(from), (void **) &tmp_from, &pos_from)) {
02372                                    if(Z_TYPE_PP(tmp_from) != IS_LONG) {
02373                                           zval dummy = **tmp_from;
02374                                           zval_copy_ctor(&dummy);
02375                                           convert_to_long(&dummy);
02376                                           f = Z_LVAL(dummy);
02377                                    } else {
02378                                           f = Z_LVAL_PP(tmp_from);
02379                                    }
02380 
02381                                    if (f < 0) {
02382                                           f = Z_STRLEN_P(orig_str) + f;
02383                                           if (f < 0) {
02384                                                  f = 0;
02385                                           }
02386                                    } else if (f > Z_STRLEN_P(orig_str)) {
02387                                           f = Z_STRLEN_P(orig_str);
02388                                    }
02389                                    zend_hash_move_forward_ex(Z_ARRVAL_PP(from), &pos_from);
02390                             } else {
02391                                    f = 0;
02392                             }
02393                      } else {
02394                             f = Z_LVAL_PP(from);
02395                             if (f < 0) {
02396                                    f = Z_STRLEN_P(orig_str) + f;
02397                                    if (f < 0) {
02398                                           f = 0;
02399                                    }
02400                             } else if (f > Z_STRLEN_P(orig_str)) {
02401                                    f = Z_STRLEN_P(orig_str);
02402                             }
02403                      }
02404 
02405                      if (argc > 3 && Z_TYPE_PP(len) == IS_ARRAY) {
02406                             if (SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_PP(len), (void **) &tmp_len, &pos_len)) {
02407                                    if(Z_TYPE_PP(tmp_len) != IS_LONG) {
02408                                           zval dummy = **tmp_len;
02409                                           zval_copy_ctor(&dummy);
02410                                           convert_to_long(&dummy);
02411                                           l = Z_LVAL(dummy);
02412                                    } else {
02413                                           l = Z_LVAL_PP(tmp_len);
02414                                    }
02415                                    zend_hash_move_forward_ex(Z_ARRVAL_PP(len), &pos_len);
02416                             } else {
02417                                    l = Z_STRLEN_P(orig_str);
02418                             }
02419                      } else if (argc > 3) { 
02420                             l = Z_LVAL_PP(len);
02421                      } else {
02422                             l = Z_STRLEN_P(orig_str);
02423                      }
02424 
02425                      if (l < 0) {
02426                             l = (Z_STRLEN_P(orig_str) - f) + l;
02427                             if (l < 0) {
02428                                    l = 0;
02429                             }
02430                      }
02431 
02432                      if ((f + l) > Z_STRLEN_P(orig_str)) {
02433                             l = Z_STRLEN_P(orig_str) - f;
02434                      }
02435 
02436                      result_len = Z_STRLEN_P(orig_str) - l;
02437 
02438                      if (Z_TYPE_PP(repl) == IS_ARRAY) {
02439                             if (SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_PP(repl), (void **) &tmp_repl, &pos_repl)) {
02440                                    zval *repl_str;
02441                                    zval zrepl;
02442                                    if(Z_TYPE_PP(tmp_repl) != IS_STRING) {
02443                                           zrepl = **tmp_repl;
02444                                           repl_str = &zrepl;
02445                                           zval_copy_ctor(repl_str);
02446                                           convert_to_string(repl_str);
02447                                    } else {
02448                                           repl_str = *tmp_repl;
02449                                    }
02450 
02451                                    result_len += Z_STRLEN_P(repl_str);
02452                                    zend_hash_move_forward_ex(Z_ARRVAL_PP(repl), &pos_repl);       
02453                                    result = emalloc(result_len + 1);
02454 
02455                                    memcpy(result, Z_STRVAL_P(orig_str), f);
02456                                    memcpy((result + f), Z_STRVAL_P(repl_str), Z_STRLEN_P(repl_str));
02457                                    memcpy((result + f + Z_STRLEN_P(repl_str)), Z_STRVAL_P(orig_str) + f + l, Z_STRLEN_P(orig_str) - f - l);
02458                                    if(Z_TYPE_PP(tmp_repl) != IS_STRING) {
02459                                           zval_dtor(repl_str);
02460                                    }
02461                             } else {
02462                                    result = emalloc(result_len + 1);
02463        
02464                                    memcpy(result, Z_STRVAL_P(orig_str), f);
02465                                    memcpy((result + f), Z_STRVAL_P(orig_str) + f + l, Z_STRLEN_P(orig_str) - f - l);
02466                             }
02467                      } else {
02468                             result_len += Z_STRLEN_PP(repl);
02469 
02470                             result = emalloc(result_len + 1);
02471 
02472                             memcpy(result, Z_STRVAL_P(orig_str), f);
02473                             memcpy((result + f), Z_STRVAL_PP(repl), Z_STRLEN_PP(repl));
02474                             memcpy((result + f + Z_STRLEN_PP(repl)), Z_STRVAL_P(orig_str) + f + l, Z_STRLEN_P(orig_str) - f - l);
02475                      }
02476 
02477                      result[result_len] = '\0';
02478 
02479                      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) {
02480                             add_assoc_stringl_ex(return_value, str_index, str_index_len, result, result_len, 0);
02481                      } else {
02482                             add_index_stringl(return_value, num_index, result, result_len, 0);
02483                      }
02484 
02485                      if(Z_TYPE_PP(tmp_str) != IS_STRING) {
02486                             zval_dtor(orig_str);
02487                      }
02488                      zend_hash_move_forward_ex(Z_ARRVAL_PP(str), &pos_str);
02489               } /*while*/
02490        } /* if */
02491 }
02492 /* }}} */
02493 
02494 /* {{{ proto string quotemeta(string str)
02495    Quotes meta characters */
02496 PHP_FUNCTION(quotemeta)
02497 {
02498        char *str, *old;
02499        char *old_end;
02500        char *p, *q;
02501        char c;
02502        int  old_len;
02503 
02504        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &old, &old_len) == FAILURE) {
02505               return;
02506        }
02507        
02508        old_end = old + old_len;
02509               
02510        if (old == old_end) {
02511               RETURN_FALSE;
02512        }
02513        
02514        str = safe_emalloc(2, old_len, 1);
02515        
02516        for (p = old, q = str; p != old_end; p++) {
02517               c = *p;
02518               switch (c) {
02519                      case '.':
02520                      case '\\':
02521                      case '+':
02522                      case '*':
02523                      case '?':
02524                      case '[':
02525                      case '^':
02526                      case ']':
02527                      case '$':
02528                      case '(':
02529                      case ')':
02530                             *q++ = '\\';
02531                             /* break is missing _intentionally_ */
02532                      default:
02533                             *q++ = c;
02534               }
02535        }
02536        *q = 0;
02537 
02538        RETURN_STRINGL(erealloc(str, q - str + 1), q - str, 0);
02539 }
02540 /* }}} */
02541 
02542 /* {{{ proto int ord(string character)
02543    Returns ASCII value of character */
02544 PHP_FUNCTION(ord)
02545 {
02546        char *str;
02547        int   str_len;
02548        
02549        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
02550               return;
02551        }
02552        
02553        RETURN_LONG((unsigned char) str[0]);
02554 }
02555 /* }}} */
02556 
02557 /* {{{ proto string chr(int ascii)
02558    Converts ASCII code to a character */
02559 PHP_FUNCTION(chr)
02560 {
02561        long c;
02562        char temp[2];
02563 
02564        if (ZEND_NUM_ARGS() != 1) {
02565               WRONG_PARAM_COUNT;
02566        }
02567 
02568        if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "l", &c) == FAILURE) {
02569               c = 0;
02570        }
02571 
02572        temp[0] = (char)c;
02573        temp[1] = '\0';
02574 
02575        RETURN_STRINGL(temp, 1, 1);
02576 }
02577 /* }}} */
02578 
02579 /* {{{ php_ucfirst
02580    Uppercase the first character of the word in a native string */
02581 static void php_ucfirst(char *str) 
02582 {
02583        register char *r;
02584        r = str;
02585        *r = toupper((unsigned char) *r);
02586 }
02587 /* }}} */
02588 
02589 /* {{{ proto string ucfirst(string str)
02590    Makes a string's first character uppercase */
02591 PHP_FUNCTION(ucfirst)
02592 {
02593        char *str;
02594        int  str_len;
02595        
02596        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
02597               return;
02598        }
02599 
02600        if (!str_len) {
02601               RETURN_EMPTY_STRING();
02602        }
02603 
02604        ZVAL_STRINGL(return_value, str, str_len, 1);
02605        php_ucfirst(Z_STRVAL_P(return_value));
02606 }
02607 /* }}} */
02608 
02609 /* {{{
02610    Lowercase the first character of the word in a native string */
02611 static void php_lcfirst(char *str)
02612 {
02613        register char *r;
02614        r = str;
02615        *r = tolower((unsigned char) *r);
02616 }
02617 /* }}} */
02618 
02619 /* {{{ proto string lcfirst(string str)
02620    Make a string's first character lowercase */
02621 PHP_FUNCTION(lcfirst)
02622 {
02623        char  *str;
02624        int   str_len;
02625 
02626        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
02627               return;
02628        }
02629 
02630        if (!str_len) {
02631               RETURN_EMPTY_STRING();
02632        }
02633 
02634        ZVAL_STRINGL(return_value, str, str_len, 1);
02635        php_lcfirst(Z_STRVAL_P(return_value));
02636 }
02637 /* }}} */
02638 
02639 /* {{{ proto string ucwords(string str)
02640    Uppercase the first character of every word in a string */
02641 PHP_FUNCTION(ucwords)
02642 {
02643        char *str;
02644        register char *r, *r_end;
02645        int str_len;
02646        
02647        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
02648               return;
02649        }
02650 
02651        if (!str_len) {
02652               RETURN_EMPTY_STRING();
02653        }
02654 
02655        ZVAL_STRINGL(return_value, str, str_len, 1);
02656        r = Z_STRVAL_P(return_value);
02657 
02658        *r = toupper((unsigned char) *r);
02659        for (r_end = r + Z_STRLEN_P(return_value) - 1; r < r_end; ) {
02660               if (isspace((int) *(unsigned char *)r++)) {
02661                      *r = toupper((unsigned char) *r);
02662               }
02663        }
02664 }
02665 /* }}} */
02666 
02667 /* {{{ php_strtr
02668  */
02669 PHPAPI char *php_strtr(char *str, int len, char *str_from, char *str_to, int trlen)
02670 {
02671        int i;
02672        unsigned char xlat[256];
02673 
02674        if ((trlen < 1) || (len < 1)) {
02675               return str;
02676        }
02677 
02678        for (i = 0; i < 256; xlat[i] = i, i++);
02679 
02680        for (i = 0; i < trlen; i++) {
02681               xlat[(unsigned char) str_from[i]] = str_to[i];
02682        }
02683 
02684        for (i = 0; i < len; i++) {
02685               str[i] = xlat[(unsigned char) str[i]];
02686        }
02687 
02688        return str;
02689 }
02690 /* }}} */
02691 
02692 /* {{{ php_strtr_array
02693  */
02694 static void php_strtr_array(zval *return_value, char *str, int slen, HashTable *hash)
02695 {
02696        zval **entry;
02697        char  *string_key;
02698        uint   string_key_len;
02699        zval **trans;
02700        zval   ctmp;
02701        ulong num_key;
02702        int minlen = 128*1024;
02703        int maxlen = 0, pos, len, found;
02704        char *key;
02705        HashPosition hpos;
02706        smart_str result = {0};
02707        HashTable tmp_hash;
02708        
02709        zend_hash_init(&tmp_hash, zend_hash_num_elements(hash), NULL, NULL, 0);
02710        zend_hash_internal_pointer_reset_ex(hash, &hpos);
02711        while (zend_hash_get_current_data_ex(hash, (void **)&entry, &hpos) == SUCCESS) {
02712               switch (zend_hash_get_current_key_ex(hash, &string_key, &string_key_len, &num_key, 0, &hpos)) {
02713                      case HASH_KEY_IS_STRING:
02714                             len = string_key_len-1;
02715                             if (len < 1) {
02716                                    zend_hash_destroy(&tmp_hash);
02717                                    RETURN_FALSE;
02718                             }
02719                             zend_hash_add(&tmp_hash, string_key, string_key_len, entry, sizeof(zval*), NULL);
02720                             if (len > maxlen) {
02721                                    maxlen = len;
02722                             }
02723                             if (len < minlen) {
02724                                    minlen = len;
02725                             }
02726                             break; 
02727                      
02728                      case HASH_KEY_IS_LONG:
02729                             Z_TYPE(ctmp) = IS_LONG;
02730                             Z_LVAL(ctmp) = num_key;
02731                      
02732                             convert_to_string(&ctmp);
02733                             len = Z_STRLEN(ctmp);
02734                             zend_hash_add(&tmp_hash, Z_STRVAL(ctmp), len+1, entry, sizeof(zval*), NULL);
02735                             zval_dtor(&ctmp);
02736 
02737                             if (len > maxlen) {
02738                                    maxlen = len;
02739                             }
02740                             if (len < minlen) {
02741                                    minlen = len;
02742                             }
02743                             break;
02744               }
02745               zend_hash_move_forward_ex(hash, &hpos);
02746        }
02747 
02748        key = emalloc(maxlen+1);
02749        pos = 0;
02750 
02751        while (pos < slen) {
02752               if ((pos + maxlen) > slen) {
02753                      maxlen = slen - pos;
02754               }
02755 
02756               found = 0;
02757               memcpy(key, str+pos, maxlen);
02758 
02759               for (len = maxlen; len >= minlen; len--) {
02760                      key[len] = 0;
02761                      
02762                      if (zend_hash_find(&tmp_hash, key, len+1, (void**)&trans) == SUCCESS) {
02763                             char *tval;
02764                             int tlen;
02765                             zval tmp;
02766 
02767                             if (Z_TYPE_PP(trans) != IS_STRING) {
02768                                    tmp = **trans;
02769                                    zval_copy_ctor(&tmp);
02770                                    convert_to_string(&tmp);
02771                                    tval = Z_STRVAL(tmp);
02772                                    tlen = Z_STRLEN(tmp);
02773                             } else {
02774                                    tval = Z_STRVAL_PP(trans);
02775                                    tlen = Z_STRLEN_PP(trans);
02776                             }
02777 
02778                             smart_str_appendl(&result, tval, tlen);
02779                             pos += len;
02780                             found = 1;
02781 
02782                             if (Z_TYPE_PP(trans) != IS_STRING) {
02783                                    zval_dtor(&tmp);
02784                             }
02785                             break;
02786                      } 
02787               }
02788 
02789               if (! found) {
02790                      smart_str_appendc(&result, str[pos++]);
02791               }
02792        }
02793 
02794        efree(key);
02795        zend_hash_destroy(&tmp_hash);
02796        smart_str_0(&result);
02797        RETVAL_STRINGL(result.c, result.len, 0);
02798 }
02799 /* }}} */
02800 
02801 /* {{{ proto string strtr(string str, string from[, string to])
02802    Translates characters in str using given translation tables */
02803 PHP_FUNCTION(strtr)
02804 {                                                       
02805        zval **from;
02806        char *str, *to = NULL;
02807        int str_len, to_len = 0;
02808        int ac = ZEND_NUM_ARGS();
02809        
02810        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sZ|s", &str, &str_len, &from, &to, &to_len) == FAILURE) {
02811               return;
02812        }
02813        
02814        if (ac == 2 && Z_TYPE_PP(from) != IS_ARRAY) {
02815               php_error_docref(NULL TSRMLS_CC, E_WARNING, "The second argument is not an array");
02816               RETURN_FALSE;
02817        }
02818 
02819        /* shortcut for empty string */
02820        if (str_len == 0) {
02821               RETURN_EMPTY_STRING();
02822        }
02823 
02824        if (ac == 2) {
02825               php_strtr_array(return_value, str, str_len, HASH_OF(*from));
02826        } else {
02827               convert_to_string_ex(from);
02828 
02829               ZVAL_STRINGL(return_value, str, str_len, 1);
02830               
02831               php_strtr(Z_STRVAL_P(return_value),
02832                               Z_STRLEN_P(return_value),
02833                               Z_STRVAL_PP(from),
02834                               to,
02835                               MIN(Z_STRLEN_PP(from), 
02836                               to_len));
02837        }
02838 }
02839 /* }}} */
02840 
02841 /* {{{ proto string strrev(string str)
02842    Reverse a string */
02843 PHP_FUNCTION(strrev)
02844 {
02845        char *str;
02846        char *e, *n, *p;
02847        int  str_len;
02848        
02849        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
02850               return;
02851        }
02852        
02853        n = emalloc(str_len+1);
02854        p = n;
02855        
02856        e = str + str_len;
02857        
02858        while (--e>=str) {
02859               *p++ = *e;
02860        }
02861        
02862        *p = '\0';
02863        
02864        RETVAL_STRINGL(n, str_len, 0);
02865 }
02866 /* }}} */
02867 
02868 /* {{{ php_similar_str
02869  */
02870 static void php_similar_str(const char *txt1, int len1, const char *txt2, int len2, int *pos1, int *pos2, int *max)
02871 {
02872        char *p, *q;
02873        char *end1 = (char *) txt1 + len1;
02874        char *end2 = (char *) txt2 + len2;
02875        int l;
02876        
02877        *max = 0;
02878        for (p = (char *) txt1; p < end1; p++) {
02879               for (q = (char *) txt2; q < end2; q++) {
02880                      for (l = 0; (p + l < end1) && (q + l < end2) && (p[l] == q[l]); l++);
02881                      if (l > *max) {
02882                             *max = l;
02883                             *pos1 = p - txt1;
02884                             *pos2 = q - txt2;
02885                      }
02886               }
02887        }
02888 }
02889 /* }}} */
02890 
02891 /* {{{ php_similar_char
02892  */
02893 static int php_similar_char(const char *txt1, int len1, const char *txt2, int len2)
02894 {
02895        int sum;
02896        int pos1, pos2, max;
02897 
02898        php_similar_str(txt1, len1, txt2, len2, &pos1, &pos2, &max);
02899        if ((sum = max)) {
02900               if (pos1 && pos2) {
02901                      sum += php_similar_char(txt1, pos1, 
02902                                                                txt2, pos2);
02903               }
02904               if ((pos1 + max < len1) && (pos2 + max < len2)) {
02905                      sum += php_similar_char(txt1 + pos1 + max, len1 - pos1 - max, 
02906                                                                txt2 + pos2 + max, len2 - pos2 - max);
02907               }
02908        }
02909 
02910        return sum;
02911 }
02912 /* }}} */
02913 
02914 /* {{{ proto int similar_text(string str1, string str2 [, float percent])
02915    Calculates the similarity between two strings */
02916 PHP_FUNCTION(similar_text)
02917 {
02918        char *t1, *t2;
02919        zval **percent = NULL;
02920        int ac = ZEND_NUM_ARGS();
02921        int sim;
02922        int t1_len, t2_len;
02923        
02924        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|Z", &t1, &t1_len, &t2, &t2_len, &percent) == FAILURE) {
02925               return;
02926        }
02927        
02928        if (ac > 2) {
02929               convert_to_double_ex(percent);
02930        }
02931        
02932        if (t1_len + t2_len == 0) {
02933               if (ac > 2) {
02934                      Z_DVAL_PP(percent) = 0;
02935               }
02936 
02937               RETURN_LONG(0);
02938        }
02939        
02940        sim = php_similar_char(t1, t1_len, t2, t2_len);  
02941 
02942        if (ac > 2) {
02943               Z_DVAL_PP(percent) = sim * 200.0 / (t1_len + t2_len);
02944        }
02945 
02946        RETURN_LONG(sim);
02947 }
02948 /* }}} */
02949 
02950 /* {{{ php_stripslashes
02951  *
02952  * be careful, this edits the string in-place */
02953 PHPAPI void php_stripslashes(char *str, int *len TSRMLS_DC)
02954 {
02955        char *s, *t;
02956        int l;
02957 
02958        if (len != NULL) {
02959               l = *len;
02960        } else {
02961               l = strlen(str);
02962        }
02963        s = str;
02964        t = str;
02965 
02966        if (PG(magic_quotes_sybase)) {
02967               while (l > 0) {
02968                      if (*t == '\'') {
02969                             if ((l > 0) && (t[1] == '\'')) {
02970                                    t++;
02971                                    if (len != NULL) {
02972                                           (*len)--;
02973                                    }
02974                                    l--;
02975                             }
02976                             *s++ = *t++;
02977                      } else if (*t == '\\' && t[1] == '0' && l > 0) {
02978                             *s++='\0';
02979                             t+=2;
02980                             if (len != NULL) {
02981                                    (*len)--;
02982                             }
02983                             l--;
02984                      } else {
02985                             *s++ = *t++;
02986                      }
02987                      l--;
02988               }
02989               *s = '\0';
02990               
02991               return;
02992        }
02993 
02994        while (l > 0) {
02995               if (*t == '\\') {
02996                      t++;                        /* skip the slash */
02997                      if (len != NULL) {
02998                             (*len)--;
02999                      }
03000                      l--;
03001                      if (l > 0) {
03002                             if (*t == '0') {
03003                                    *s++='\0';
03004                                    t++;
03005                             } else {
03006                                    *s++ = *t++;  /* preserve the next character */
03007                             }
03008                             l--;
03009                      }
03010               } else {
03011                      *s++ = *t++;
03012                      l--;
03013               }
03014        }
03015        if (s != t) {
03016               *s = '\0';
03017        }
03018 }
03019 /* }}} */
03020 
03021 /* {{{ proto string addcslashes(string str, string charlist)
03022    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...) */
03023 PHP_FUNCTION(addcslashes)
03024 {
03025        char *str, *what;
03026        int str_len, what_len;
03027 
03028        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &str, &str_len, &what, &what_len) == FAILURE) {
03029               return;
03030        }
03031 
03032        if (str_len == 0) {
03033               RETURN_EMPTY_STRING();
03034        }
03035 
03036        if (what_len == 0) {
03037               RETURN_STRINGL(str, str_len, 1);
03038        }
03039 
03040        Z_STRVAL_P(return_value) = php_addcslashes(str, str_len, &Z_STRLEN_P(return_value), 0, what, what_len TSRMLS_CC);
03041        RETURN_STRINGL(Z_STRVAL_P(return_value), Z_STRLEN_P(return_value), 0);
03042 }
03043 /* }}} */
03044 
03045 /* {{{ proto string addslashes(string str)
03046    Escapes single quote, double quotes and backslash characters in a string with backslashes */
03047 PHP_FUNCTION(addslashes)
03048 {
03049        char *str;
03050        int  str_len;
03051        
03052        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
03053               return;
03054        }
03055 
03056        if (str_len == 0) {
03057               RETURN_EMPTY_STRING();
03058        }
03059 
03060        RETURN_STRING(php_addslashes(str,
03061                                     str_len, 
03062                                     &Z_STRLEN_P(return_value), 0 
03063                                     TSRMLS_CC), 0);
03064 }
03065 /* }}} */
03066 
03067 /* {{{ proto string stripcslashes(string str)
03068    Strips backslashes from a string. Uses C-style conventions */
03069 PHP_FUNCTION(stripcslashes)
03070 {
03071        char *str;
03072        int  str_len;
03073        
03074        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
03075               return;
03076        }
03077 
03078        ZVAL_STRINGL(return_value, str, str_len, 1);
03079        php_stripcslashes(Z_STRVAL_P(return_value), &Z_STRLEN_P(return_value));
03080 }
03081 /* }}} */
03082 
03083 /* {{{ proto string stripslashes(string str)
03084    Strips backslashes from a string */
03085 PHP_FUNCTION(stripslashes)
03086 {
03087        char *str;
03088        int  str_len;
03089        
03090        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
03091               return;
03092        }
03093 
03094        ZVAL_STRINGL(return_value, str, str_len, 1);
03095        php_stripslashes(Z_STRVAL_P(return_value), &Z_STRLEN_P(return_value) TSRMLS_CC);
03096 }
03097 /* }}} */
03098 
03099 #ifndef HAVE_STRERROR
03100 /* {{{ php_strerror
03101  */
03102 char *php_strerror(int errnum) 
03103 {
03104        extern int sys_nerr;
03105        extern char *sys_errlist[];
03106        TSRMLS_FETCH();
03107 
03108        if ((unsigned int) errnum < sys_nerr) {
03109               return(sys_errlist[errnum]);
03110        }
03111 
03112        (void) snprintf(BG(str_ebuf), sizeof(php_basic_globals.str_ebuf), "Unknown error: %d", errnum);
03113        return(BG(str_ebuf));
03114 }
03115 /* }}} */
03116 #endif
03117 
03118 /* {{{ php_stripcslashes
03119  */
03120 PHPAPI void php_stripcslashes(char *str, int *len)
03121 {
03122        char *source, *target, *end;
03123        int  nlen = *len, i;
03124        char numtmp[4];
03125 
03126        for (source=str, end=str+nlen, target=str; source < end; source++) {
03127               if (*source == '\\' && source+1 < end) {
03128                      source++;
03129                      switch (*source) {
03130                             case 'n':  *target++='\n'; nlen--; break;
03131                             case 'r':  *target++='\r'; nlen--; break;
03132                             case 'a':  *target++='\a'; nlen--; break;
03133                             case 't':  *target++='\t'; nlen--; break;
03134                             case 'v':  *target++='\v'; nlen--; break;
03135                             case 'b':  *target++='\b'; nlen--; break;
03136                             case 'f':  *target++='\f'; nlen--; break;
03137                             case '\\': *target++='\\'; nlen--; break;
03138                             case 'x':
03139                                    if (source+1 < end && isxdigit((int)(*(source+1)))) {
03140                                           numtmp[0] = *++source;
03141                                           if (source+1 < end && isxdigit((int)(*(source+1)))) {
03142                                                  numtmp[1] = *++source;
03143                                                  numtmp[2] = '\0';
03144                                                  nlen-=3;
03145                                           } else {
03146                                                  numtmp[1] = '\0';
03147                                                  nlen-=2;
03148                                           }
03149                                           *target++=(char)strtol(numtmp, NULL, 16);
03150                                           break;
03151                                    }
03152                                    /* break is left intentionally */
03153                             default: 
03154                                    i=0; 
03155                                    while (source < end && *source >= '0' && *source <= '7' && i<3) {
03156                                           numtmp[i++] = *source++;
03157                                    }
03158                                    if (i) {
03159                                           numtmp[i]='\0';
03160                                           *target++=(char)strtol(numtmp, NULL, 8);
03161                                           nlen-=i;
03162                                           source--;
03163                                    } else {
03164                                           *target++=*source;
03165                                           nlen--;
03166                                    }
03167                      }
03168               } else {
03169                      *target++=*source;
03170               }
03171        }
03172 
03173        if (nlen != 0) {
03174               *target='\0';
03175        }
03176 
03177        *len = nlen;
03178 }
03179 /* }}} */
03180                      
03181 /* {{{ php_addcslashes
03182  */
03183 PHPAPI char *php_addcslashes(char *str, int length, int *new_length, int should_free, char *what, int wlength TSRMLS_DC)
03184 {
03185        char flags[256];
03186        char *new_str = safe_emalloc(4, (length?length:(length=strlen(str))), 1);
03187        char *source, *target;
03188        char *end;
03189        char c;
03190        int  newlen;
03191 
03192        if (!wlength) {
03193               wlength = strlen(what);
03194        }
03195 
03196        php_charmask((unsigned char *)what, wlength, flags TSRMLS_CC);
03197 
03198        for (source = str, end = source + length, target = new_str; source < end; source++) {
03199               c = *source; 
03200               if (flags[(unsigned char)c]) {
03201                      if ((unsigned char) c < 32 || (unsigned char) c > 126) {
03202                             *target++ = '\\';
03203                             switch (c) {
03204                                    case '\n': *target++ = 'n'; break;
03205                                    case '\t': *target++ = 't'; break;
03206                                    case '\r': *target++ = 'r'; break;
03207                                    case '\a': *target++ = 'a'; break;
03208                                    case '\v': *target++ = 'v'; break;
03209                                    case '\b': *target++ = 'b'; break;
03210                                    case '\f': *target++ = 'f'; break;
03211                                    default: target += sprintf(target, "%03o", (unsigned char) c);
03212                             }
03213                             continue;
03214                      } 
03215                      *target++ = '\\';
03216               }
03217               *target++ = c;
03218        }
03219        *target = 0;
03220        newlen = target - new_str;
03221        if (target - new_str < length * 4) {
03222               new_str = erealloc(new_str, newlen + 1);
03223        }
03224        if (new_length) {
03225               *new_length = newlen;
03226        }
03227        if (should_free) {
03228               STR_FREE(str);
03229        }
03230        return new_str;
03231 }
03232 /* }}} */
03233 
03234 /* {{{ php_addslashes
03235  */
03236 PHPAPI char *php_addslashes(char *str, int length, int *new_length, int should_free TSRMLS_DC)
03237 {
03238        return php_addslashes_ex(str, length, new_length, should_free, 0 TSRMLS_CC);
03239 }
03240 /* }}} */
03241 
03242 /* {{{ php_addslashes_ex
03243  */
03244 PHPAPI char *php_addslashes_ex(char *str, int length, int *new_length, int should_free, int ignore_sybase TSRMLS_DC)
03245 {
03246        /* maximum string length, worst case situation */
03247        char *new_str;
03248        char *source, *target;
03249        char *end;
03250        int local_new_length;
03251                
03252        if (!new_length) {
03253               new_length = &local_new_length;
03254        }
03255        if (!str) {
03256               *new_length = 0;
03257               return str;
03258        }
03259        new_str = (char *) safe_emalloc(2, (length ? length : (length = strlen(str))), 1);
03260        source = str;
03261        end = source + length;
03262        target = new_str;
03263        
03264        if (!ignore_sybase && PG(magic_quotes_sybase)) {
03265               while (source < end) {
03266                      switch (*source) {
03267                             case '\0':
03268                                    *target++ = '\\';
03269                                    *target++ = '0';
03270                                    break;
03271                             case '\'':
03272                                    *target++ = '\'';
03273                                    *target++ = '\'';
03274                                    break;
03275                             default:
03276                                    *target++ = *source;
03277                                    break;
03278                      }
03279                      source++;
03280               }
03281        } else {
03282               while (source < end) {
03283                      switch (*source) {
03284                             case '\0':
03285                                    *target++ = '\\';
03286                                    *target++ = '0';
03287                                    break;
03288                             case '\'':
03289                             case '\"':
03290                             case '\\':
03291                                    *target++ = '\\';
03292                                    /* break is missing *intentionally* */
03293                             default:
03294                                    *target++ = *source;
03295                                    break; 
03296                      }
03297               
03298                      source++;
03299               }
03300        }
03301        
03302        *target = 0;
03303        *new_length = target - new_str;
03304        if (should_free) {
03305               STR_FREE(str);
03306        }
03307        new_str = (char *) erealloc(new_str, *new_length + 1);
03308        return new_str;
03309 }
03310 /* }}} */
03311 
03312 #define _HEB_BLOCK_TYPE_ENG 1
03313 #define _HEB_BLOCK_TYPE_HEB 2
03314 #define isheb(c)      (((((unsigned char) c) >= 224) && (((unsigned char) c) <= 250)) ? 1 : 0)
03315 #define _isblank(c)   (((((unsigned char) c) == ' '  || ((unsigned char) c) == '\t')) ? 1 : 0)
03316 #define _isnewline(c) (((((unsigned char) c) == '\n' || ((unsigned char) c) == '\r')) ? 1 : 0)
03317 
03318 /* {{{ php_char_to_str_ex
03319  */
03320 PHPAPI 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)
03321 {
03322        int char_count = 0;
03323        int replaced = 0;
03324        char *source, *target, *tmp, *source_end=str+len, *tmp_end = NULL;
03325               
03326        if (case_sensitivity) {
03327               char *p = str, *e = p + len;
03328               while ((p = memchr(p, from, (e - p)))) {
03329                      char_count++;
03330                      p++;
03331               }
03332        } else {
03333               for (source = str; source < source_end; source++) {
03334                      if (tolower(*source) == tolower(from)) {
03335                             char_count++;
03336                      }
03337               }
03338        }
03339 
03340        if (char_count == 0 && case_sensitivity) {
03341               ZVAL_STRINGL(result, str, len, 1);
03342               return 0;
03343        }
03344        
03345        Z_STRLEN_P(result) = len + (char_count * (to_len - 1));
03346        Z_STRVAL_P(result) = target = safe_emalloc(char_count, to_len, len + 1);
03347        Z_TYPE_P(result) = IS_STRING;
03348 
03349        if (case_sensitivity) {
03350               char *p = str, *e = p + len, *s = str;
03351               while ((p = memchr(p, from, (e - p)))) {
03352                      memcpy(target, s, (p - s));
03353                      target += p - s;
03354                      memcpy(target, to, to_len);
03355                      target += to_len;
03356                      p++;
03357                      s = p;
03358                      if (replace_count) {
03359                             *replace_count += 1;
03360                      }
03361               }
03362               if (s < e) {
03363                      memcpy(target, s, (e - s));
03364                      target += e - s;
03365               }
03366        } else {
03367               for (source = str; source < source_end; source++) {
03368                      if (tolower(*source) == tolower(from)) {
03369                             replaced = 1;
03370                             if (replace_count) {
03371                                    *replace_count += 1;
03372                             }
03373                             for (tmp = to, tmp_end = tmp+to_len; tmp < tmp_end; tmp++) {
03374                                    *target = *tmp;
03375                                    target++;
03376                             }
03377                      } else {
03378                             *target = *source;
03379                             target++;
03380                      }
03381               }
03382        }
03383        *target = 0;
03384        return replaced;
03385 }
03386 /* }}} */
03387 
03388 /* {{{ php_char_to_str
03389  */
03390 PHPAPI int php_char_to_str(char *str, uint len, char from, char *to, int to_len, zval *result)
03391 {
03392        return php_char_to_str_ex(str, len, from, to, to_len, result, 1, NULL);
03393 }
03394 /* }}} */
03395 
03396 /* {{{ php_str_to_str_ex
03397  */
03398 PHPAPI char *php_str_to_str_ex(char *haystack, int length, 
03399        char *needle, int needle_len, char *str, int str_len, int *_new_length, int case_sensitivity, int *replace_count)
03400 {
03401        char *new_str;
03402 
03403        if (needle_len < length) {
03404               char *end, *haystack_dup = NULL, *needle_dup = NULL;
03405               char *e, *s, *p, *r;
03406 
03407               if (needle_len == str_len) {
03408                      new_str = estrndup(haystack, length);
03409                      *_new_length = length;
03410 
03411                      if (case_sensitivity) {
03412                             end = new_str + length;
03413                             for (p = new_str; (r = php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
03414                                    memcpy(r, str, str_len);
03415                                    if (replace_count) {
03416                                           (*replace_count)++;
03417                                    }
03418                             }
03419                      } else {
03420                             haystack_dup = estrndup(haystack, length);
03421                             needle_dup = estrndup(needle, needle_len);
03422                             php_strtolower(haystack_dup, length);
03423                             php_strtolower(needle_dup, needle_len);
03424                             end = haystack_dup + length;
03425                             for (p = haystack_dup; (r = php_memnstr(p, needle_dup, needle_len, end)); p = r + needle_len) {
03426                                    memcpy(new_str + (r - haystack_dup), str, str_len);
03427                                    if (replace_count) {
03428                                           (*replace_count)++;
03429                                    }
03430                             }
03431                             efree(haystack_dup);
03432                             efree(needle_dup);
03433                      }
03434                      return new_str;
03435               } else {
03436                      if (!case_sensitivity) {
03437                             haystack_dup = estrndup(haystack, length);
03438                             needle_dup = estrndup(needle, needle_len);
03439                             php_strtolower(haystack_dup, length);
03440                             php_strtolower(needle_dup, needle_len);
03441                      }
03442 
03443                      if (str_len < needle_len) {
03444                             new_str = emalloc(length + 1);
03445                      } else {
03446                             int count = 0;
03447                             char *o, *n, *endp;
03448 
03449                             if (case_sensitivity) {
03450                                    o = haystack;
03451                                    n = needle;
03452                             } else {
03453                                    o = haystack_dup;
03454                                    n = needle_dup;
03455                             }
03456                             endp = o + length;
03457 
03458                             while ((o = php_memnstr(o, n, needle_len, endp))) {
03459                                    o += needle_len;
03460                                    count++;
03461                             }
03462                             if (count == 0) {
03463                                    /* Needle doesn't occur, shortcircuit the actual replacement. */
03464                                    if (haystack_dup) {
03465                                           efree(haystack_dup);
03466                                    }
03467                                    if (needle_dup) {
03468                                           efree(needle_dup);
03469                                    }
03470                                    new_str = estrndup(haystack, length);
03471                                    if (_new_length) {
03472                                           *_new_length = length;
03473                                    }
03474                                    return new_str;
03475                             } else {
03476                                    new_str = safe_emalloc(count, str_len - needle_len, length + 1);
03477                             }
03478                      }
03479 
03480                      e = s = new_str;
03481 
03482                      if (case_sensitivity) {
03483                             end = haystack + length;
03484                             for (p = haystack; (r = php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
03485                                    memcpy(e, p, r - p);
03486                                    e += r - p;
03487                                    memcpy(e, str, str_len);
03488                                    e += str_len;
03489                                    if (replace_count) {
03490                                           (*replace_count)++;
03491                                    }
03492                             }
03493 
03494                             if (p < end) {
03495                                    memcpy(e, p, end - p);
03496                                    e += end - p;
03497                             }
03498                      } else {
03499                             end = haystack_dup + length;
03500 
03501                             for (p = haystack_dup; (r = php_memnstr(p, needle_dup, needle_len, end)); p = r + needle_len) {
03502                                    memcpy(e, haystack + (p - haystack_dup), r - p);
03503                                    e += r - p;
03504                                    memcpy(e, str, str_len);
03505                                    e += str_len;
03506                                    if (replace_count) {
03507                                           (*replace_count)++;
03508                                    }
03509                             }
03510 
03511                             if (p < end) {
03512                                    memcpy(e, haystack + (p - haystack_dup), end - p);
03513                                    e += end - p;
03514                             }
03515                      }
03516 
03517                      if (haystack_dup) {
03518                             efree(haystack_dup);
03519                      }
03520                      if (needle_dup) {
03521                             efree(needle_dup);
03522                      }
03523 
03524                      *e = '\0';
03525                      *_new_length = e - s;
03526 
03527                      new_str = erealloc(new_str, *_new_length + 1);
03528                      return new_str;
03529               }
03530        } else if (needle_len > length) {
03531 nothing_todo:
03532               *_new_length = length;
03533               new_str = estrndup(haystack, length);
03534               return new_str;
03535        } else {
03536               if (case_sensitivity && memcmp(haystack, needle, length)) {
03537                      goto nothing_todo;
03538               } else if (!case_sensitivity) {
03539                      char *l_haystack, *l_needle;
03540 
03541                      l_haystack = estrndup(haystack, length);
03542                      l_needle = estrndup(needle, length);
03543 
03544                      php_strtolower(l_haystack, length);
03545                      php_strtolower(l_needle, length);
03546 
03547                      if (memcmp(l_haystack, l_needle, length)) {
03548                             efree(l_haystack);
03549                             efree(l_needle);
03550                             goto nothing_todo;
03551                      }
03552                      efree(l_haystack);
03553                      efree(l_needle);
03554               }
03555 
03556               *_new_length = str_len;
03557               new_str = estrndup(str, str_len);
03558 
03559               if (replace_count) {
03560                      (*replace_count)++;
03561               }
03562               return new_str;
03563        }
03564 
03565 }
03566 /* }}} */
03567 
03568 /* {{{ php_str_to_str
03569  */
03570 PHPAPI char *php_str_to_str(char *haystack, int length, 
03571        char *needle, int needle_len, char *str, int str_len, int *_new_length)
03572 {
03573        return php_str_to_str_ex(haystack, length, needle, needle_len, str, str_len, _new_length, 1, NULL);
03574 } 
03575 /* }}} */
03576 
03577 /* {{{ php_str_replace_in_subject
03578  */
03579 static void php_str_replace_in_subject(zval *search, zval *replace, zval **subject, zval *result, int case_sensitivity, int *replace_count)
03580 {
03581        zval          **search_entry,
03582                             **replace_entry = NULL,
03583                               temp_result;
03584        char          *replace_value = NULL;
03585        int                   replace_len = 0;
03586 
03587        /* Make sure we're dealing with strings. */      
03588        convert_to_string_ex(subject);
03589        Z_TYPE_P(result) = IS_STRING;
03590        if (Z_STRLEN_PP(subject) == 0) {
03591               ZVAL_STRINGL(result, "", 0, 1);
03592               return;
03593        }
03594        
03595        /* If search is an array */
03596        if (Z_TYPE_P(search) == IS_ARRAY) {
03597               /* Duplicate subject string for repeated replacement */
03598               MAKE_COPY_ZVAL(subject, result);
03599               
03600               zend_hash_internal_pointer_reset(Z_ARRVAL_P(search));
03601 
03602               if (Z_TYPE_P(replace) == IS_ARRAY) {
03603                      zend_hash_internal_pointer_reset(Z_ARRVAL_P(replace));
03604               } else {
03605                      /* Set replacement value to the passed one */
03606                      replace_value = Z_STRVAL_P(replace);
03607                      replace_len = Z_STRLEN_P(replace);
03608               }
03609 
03610               /* For each entry in the search array, get the entry */
03611               while (zend_hash_get_current_data(Z_ARRVAL_P(search), (void **) &search_entry) == SUCCESS) {
03612                      /* Make sure we're dealing with strings. */      
03613                      SEPARATE_ZVAL(search_entry);
03614                      convert_to_string(*search_entry);
03615                      if (Z_STRLEN_PP(search_entry) == 0) {
03616                             zend_hash_move_forward(Z_ARRVAL_P(search));
03617                             if (Z_TYPE_P(replace) == IS_ARRAY) {
03618                                    zend_hash_move_forward(Z_ARRVAL_P(replace));
03619                             }
03620                             continue;
03621                      }
03622 
03623                      /* If replace is an array. */
03624                      if (Z_TYPE_P(replace) == IS_ARRAY) {
03625                             /* Get current entry */
03626                             if (zend_hash_get_current_data(Z_ARRVAL_P(replace), (void **)&replace_entry) == SUCCESS) {
03627                                    /* Make sure we're dealing with strings. */      
03628                                    convert_to_string_ex(replace_entry);
03629                                    
03630                                    /* Set replacement value to the one we got from array */
03631                                    replace_value = Z_STRVAL_PP(replace_entry);
03632                                    replace_len = Z_STRLEN_PP(replace_entry);
03633 
03634                                    zend_hash_move_forward(Z_ARRVAL_P(replace));
03635                             } else {
03636                                    /* We've run out of replacement strings, so use an empty one. */
03637                                    replace_value = "";
03638                                    replace_len = 0;
03639                             }
03640                      }
03641                      
03642                      if (Z_STRLEN_PP(search_entry) == 1) {
03643                             php_char_to_str_ex(Z_STRVAL_P(result),
03644                                                         Z_STRLEN_P(result),
03645                                                         Z_STRVAL_PP(search_entry)[0],
03646                                                         replace_value,
03647                                                         replace_len,
03648                                                         &temp_result,
03649                                                         case_sensitivity,
03650                                                         replace_count);
03651                      } else if (Z_STRLEN_PP(search_entry) > 1) {
03652                             Z_STRVAL(temp_result) = php_str_to_str_ex(Z_STRVAL_P(result), Z_STRLEN_P(result),
03653                                                                                                      Z_STRVAL_PP(search_entry), Z_STRLEN_PP(search_entry),
03654                                                                                                      replace_value, replace_len, &Z_STRLEN(temp_result), case_sensitivity, replace_count);
03655                      }
03656 
03657                      efree(Z_STRVAL_P(result));
03658                      Z_STRVAL_P(result) = Z_STRVAL(temp_result);
03659                      Z_STRLEN_P(result) = Z_STRLEN(temp_result);
03660 
03661                      if (Z_STRLEN_P(result) == 0) {
03662                             return;
03663                      }
03664 
03665                      zend_hash_move_forward(Z_ARRVAL_P(search));
03666               }
03667        } else {
03668               if (Z_STRLEN_P(search) == 1) {
03669                      php_char_to_str_ex(Z_STRVAL_PP(subject),
03670                                                  Z_STRLEN_PP(subject),
03671                                                  Z_STRVAL_P(search)[0],
03672                                                  Z_STRVAL_P(replace),
03673                                                  Z_STRLEN_P(replace),
03674                                                  result,
03675                                                  case_sensitivity,
03676                                                  replace_count);
03677               } else if (Z_STRLEN_P(search) > 1) {
03678                      Z_STRVAL_P(result) = php_str_to_str_ex(Z_STRVAL_PP(subject), Z_STRLEN_PP(subject),
03679                                                                                            Z_STRVAL_P(search), Z_STRLEN_P(search),
03680                                                                                            Z_STRVAL_P(replace), Z_STRLEN_P(replace), &Z_STRLEN_P(result), case_sensitivity, replace_count);
03681               } else {
03682                      MAKE_COPY_ZVAL(subject, result);
03683               }
03684        }
03685 }
03686 /* }}} */
03687 
03688 /* {{{ php_str_replace_common
03689  */
03690 static void php_str_replace_common(INTERNAL_FUNCTION_PARAMETERS, int case_sensitivity)
03691 {
03692        zval **subject, **search, **replace, **subject_entry, **zcount = NULL;
03693        zval *result;
03694        char *string_key;
03695        uint string_key_len;
03696        ulong num_key;
03697        int count = 0;
03698        int argc = ZEND_NUM_ARGS();
03699 
03700        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZZ|Z", &search, &replace, &subject, &zcount) == FAILURE) {
03701               return;
03702        }
03703 
03704        SEPARATE_ZVAL(search);
03705        SEPARATE_ZVAL(replace);
03706        SEPARATE_ZVAL(subject);
03707 
03708        /* Make sure we're dealing with strings and do the replacement. */
03709        if (Z_TYPE_PP(search) != IS_ARRAY) {
03710               convert_to_string_ex(search);
03711               convert_to_string_ex(replace);
03712        } else if (Z_TYPE_PP(replace) != IS_ARRAY) {
03713               convert_to_string_ex(replace);
03714        }
03715 
03716        /* if subject is an array */
03717        if (Z_TYPE_PP(subject) == IS_ARRAY) {
03718               array_init(return_value);
03719               zend_hash_internal_pointer_reset(Z_ARRVAL_PP(subject));
03720 
03721               /* For each subject entry, convert it to string, then perform replacement
03722                  and add the result to the return_value array. */
03723               while (zend_hash_get_current_data(Z_ARRVAL_PP(subject), (void **)&subject_entry) == SUCCESS) {
03724                      if (Z_TYPE_PP(subject_entry) != IS_ARRAY && Z_TYPE_PP(subject_entry) != IS_OBJECT) {
03725                             MAKE_STD_ZVAL(result);
03726                             SEPARATE_ZVAL(subject_entry);
03727                             php_str_replace_in_subject(*search, *replace, subject_entry, result, case_sensitivity, (argc > 3) ? &count : NULL);
03728                      } else {
03729                             ALLOC_ZVAL(result);
03730                             Z_ADDREF_P(*subject_entry);
03731                             COPY_PZVAL_TO_ZVAL(*result, *subject_entry);
03732                      }
03733                      /* Add to return array */
03734                      switch (zend_hash_get_current_key_ex(Z_ARRVAL_PP(subject), &string_key,
03735                                                                                     &string_key_len, &num_key, 0, NULL)) {
03736                             case HASH_KEY_IS_STRING:
03737                                    add_assoc_zval_ex(return_value, string_key, string_key_len, result);
03738                                    break;
03739 
03740                             case HASH_KEY_IS_LONG:
03741                                    add_index_zval(return_value, num_key, result);
03742                                    break;
03743                      }
03744               
03745                      zend_hash_move_forward(Z_ARRVAL_PP(subject));
03746               }
03747        } else {      /* if subject is not an array */
03748               php_str_replace_in_subject(*search, *replace, subject, return_value, case_sensitivity, (argc > 3) ? &count : NULL);
03749        }      
03750        if (argc > 3) {
03751               zval_dtor(*zcount);
03752               ZVAL_LONG(*zcount, count);
03753        }
03754 }
03755 /* }}} */
03756 
03757 /* {{{ proto mixed str_replace(mixed search, mixed replace, mixed subject [, int &replace_count])
03758    Replaces all occurrences of search in haystack with replace */
03759 PHP_FUNCTION(str_replace)
03760 {
03761        php_str_replace_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
03762 }
03763 /* }}} */
03764 
03765 /* {{{ proto mixed str_ireplace(mixed search, mixed replace, mixed subject [, int &replace_count])
03766    Replaces all occurrences of search in haystack with replace / case-insensitive */
03767 PHP_FUNCTION(str_ireplace)
03768 {
03769        php_str_replace_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
03770 }
03771 /* }}} */
03772 
03773 /* {{{ php_hebrev
03774  *
03775  * Converts Logical Hebrew text (Hebrew Windows style) to Visual text
03776  * Cheers/complaints/flames - Zeev Suraski <zeev@php.net>
03777  */
03778 static void php_hebrev(INTERNAL_FUNCTION_PARAMETERS, int convert_newlines)
03779 {
03780        char *str;
03781        char *heb_str, *tmp, *target, *broken_str;
03782        int block_start, block_end, block_type, block_length, i;
03783        long max_chars=0;
03784        int begin, end, char_count, orig_begin;
03785        int str_len;
03786        
03787        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &str, &str_len, &max_chars) == FAILURE) {
03788               return;
03789        }
03790        
03791        if (str_len == 0) {
03792               RETURN_FALSE;
03793        }
03794 
03795        tmp = str;
03796        block_start=block_end=0;
03797 
03798        heb_str = (char *) emalloc(str_len+1);
03799        target = heb_str+str_len;
03800        *target = 0;
03801        target--;
03802 
03803        block_length=0;
03804 
03805        if (isheb(*tmp)) {
03806               block_type = _HEB_BLOCK_TYPE_HEB;
03807        } else {
03808               block_type = _HEB_BLOCK_TYPE_ENG;
03809        }
03810        
03811        do {
03812               if (block_type == _HEB_BLOCK_TYPE_HEB) {
03813                      while ((isheb((int)*(tmp+1)) || _isblank((int)*(tmp+1)) || ispunct((int)*(tmp+1)) || (int)*(tmp+1)=='\n' ) && block_end<str_len-1) {
03814                             tmp++;
03815                             block_end++;
03816                             block_length++;
03817                      }
03818                      for (i = block_start; i<= block_end; i++) {
03819                             *target = str[i];
03820                             switch (*target) {
03821                                    case '(':
03822                                           *target = ')';
03823                                           break;
03824                                    case ')':
03825                                           *target = '(';
03826                                           break;
03827                                    case '[':
03828                                           *target = ']';
03829                                           break;
03830                                    case ']':
03831                                           *target = '[';
03832                                           break;
03833                                    case '{':
03834                                           *target = '}';
03835                                           break;
03836                                    case '}':
03837                                           *target = '{';
03838                                           break;
03839                                    case '<':
03840                                           *target = '>';
03841                                           break;
03842                                    case '>':
03843                                           *target = '<';
03844                                           break;
03845                                    case '\\':
03846                                           *target = '/';
03847                                           break;
03848                                    case '/':
03849                                           *target = '\\';
03850                                           break;
03851                                    default:
03852                                           break;
03853                             }
03854                             target--;
03855                      }
03856                      block_type = _HEB_BLOCK_TYPE_ENG;
03857               } else {
03858                      while (!isheb(*(tmp+1)) && (int)*(tmp+1)!='\n' && block_end < str_len-1) {
03859                             tmp++;
03860                             block_end++;
03861                             block_length++;
03862                      }
03863                      while ((_isblank((int)*tmp) || ispunct((int)*tmp)) && *tmp!='/' && *tmp!='-' && block_end > block_start) {
03864                             tmp--;
03865                             block_end--;
03866                      }
03867                      for (i = block_end; i >= block_start; i--) {
03868                             *target = str[i];
03869                             target--;
03870                      }
03871                      block_type = _HEB_BLOCK_TYPE_HEB;
03872               }
03873               block_start=block_end+1;
03874        } while (block_end < str_len-1);
03875 
03876 
03877        broken_str = (char *) emalloc(str_len+1);
03878        begin=end=str_len-1;
03879        target = broken_str;
03880               
03881        while (1) {
03882               char_count=0;
03883               while ((!max_chars || char_count < max_chars) && begin > 0) {
03884                      char_count++;
03885                      begin--;
03886                      if (begin <= 0 || _isnewline(heb_str[begin])) {
03887                             while (begin > 0 && _isnewline(heb_str[begin-1])) {
03888                                    begin--;
03889                                    char_count++;
03890                             }
03891                             break;
03892                      }
03893               }
03894               if (char_count == max_chars) { /* try to avoid breaking words */
03895                      int new_char_count=char_count, new_begin=begin;
03896                      
03897                      while (new_char_count > 0) {
03898                             if (_isblank(heb_str[new_begin]) || _isnewline(heb_str[new_begin])) {
03899                                    break;
03900                             }
03901                             new_begin++;
03902                             new_char_count--;
03903                      }
03904                      if (new_char_count > 0) {
03905                             char_count=new_char_count;
03906                             begin=new_begin;
03907                      }
03908               }
03909               orig_begin=begin;
03910               
03911               if (_isblank(heb_str[begin])) {
03912                      heb_str[begin]='\n';
03913               }
03914               while (begin <= end && _isnewline(heb_str[begin])) { /* skip leading newlines */
03915                      begin++;
03916               }
03917               for (i = begin; i <= end; i++) { /* copy content */
03918                      *target = heb_str[i];
03919                      target++;
03920               }
03921               for (i = orig_begin; i <= end && _isnewline(heb_str[i]); i++) {
03922                      *target = heb_str[i];
03923                      target++;
03924               }
03925               begin=orig_begin;
03926 
03927               if (begin <= 0) {
03928                      *target = 0;
03929                      break;
03930               }
03931               begin--;
03932               end=begin;
03933        }
03934        efree(heb_str);
03935 
03936        if (convert_newlines) {
03937               php_char_to_str(broken_str, str_len,'\n', "<br />\n", 7, return_value);
03938               efree(broken_str);
03939        } else {
03940               Z_STRVAL_P(return_value) = broken_str;
03941               Z_STRLEN_P(return_value) = str_len;
03942               Z_TYPE_P(return_value) = IS_STRING;
03943        }
03944 }
03945 /* }}} */
03946 
03947 /* {{{ proto string hebrev(string str [, int max_chars_per_line])
03948    Converts logical Hebrew text to visual text */
03949 PHP_FUNCTION(hebrev)
03950 {
03951        php_hebrev(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
03952 }
03953 /* }}} */
03954 
03955 /* {{{ proto string hebrevc(string str [, int max_chars_per_line])
03956    Converts logical Hebrew text to visual text with newline conversion */
03957 PHP_FUNCTION(hebrevc)
03958 {
03959        php_hebrev(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
03960 }
03961 /* }}} */
03962 
03963 /* {{{ proto string nl2br(string str [, bool is_xhtml])
03964    Converts newlines to HTML line breaks */
03965 PHP_FUNCTION(nl2br)
03966 {
03967        /* in brief this inserts <br /> or <br> before matched regexp \n\r?|\r\n? */
03968        char          *tmp, *str;
03969        int           new_length;
03970        char          *end, *target;
03971        int           repl_cnt = 0;
03972        int           str_len;
03973        zend_bool     is_xhtml = 1;
03974        
03975        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &str, &str_len, &is_xhtml) == FAILURE) {
03976               return;
03977        }
03978        
03979        tmp = str;
03980        end = str + str_len;
03981        
03982        /* it is really faster to scan twice and allocate mem once instead of scanning once
03983           and constantly reallocing */
03984        while (tmp < end) {
03985               if (*tmp == '\r') {
03986                      if (*(tmp+1) == '\n') {
03987                             tmp++;
03988                      }
03989                      repl_cnt++;
03990               } else if (*tmp == '\n') {
03991                      if (*(tmp+1) == '\r') {
03992                             tmp++;
03993                      }
03994                      repl_cnt++;
03995               }
03996               
03997               tmp++;
03998        }
03999        
04000        if (repl_cnt == 0) {
04001               RETURN_STRINGL(str, str_len, 1);
04002        }
04003 
04004        if (is_xhtml) {
04005               new_length = str_len + repl_cnt * (sizeof("<br />") - 1);
04006        } else {
04007               new_length = str_len + repl_cnt * (sizeof("<br>") - 1);
04008        }
04009 
04010        tmp = target = emalloc(new_length + 1);
04011 
04012        while (str < end) {
04013               switch (*str) {
04014                      case '\r':
04015                      case '\n':
04016                             *target++ = '<';
04017                             *target++ = 'b';
04018                             *target++ = 'r';
04019 
04020                             if (is_xhtml) {
04021                                    *target++ = ' ';
04022                                    *target++ = '/';
04023                             }
04024 
04025                             *target++ = '>';
04026                             
04027                             if ((*str == '\r' && *(str+1) == '\n') || (*str == '\n' && *(str+1) == '\r')) {
04028                                    *target++ = *str++;
04029                             }
04030                             /* lack of a break; is intentional */
04031                      default:
04032                             *target++ = *str;
04033               }
04034        
04035               str++;
04036        }
04037        
04038        *target = '\0';
04039 
04040        RETURN_STRINGL(tmp, new_length, 0);
04041 }
04042 /* }}} */
04043 
04044 /* {{{ proto string strip_tags(string str [, string allowable_tags])
04045    Strips HTML and PHP tags from a string */
04046 PHP_FUNCTION(strip_tags)
04047 {
04048        char *buf;
04049        char *str;
04050        zval **allow=NULL;
04051        char *allowed_tags=NULL;
04052        int allowed_tags_len=0;
04053        int str_len;
04054        size_t retval_len;
04055 
04056        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|Z", &str, &str_len, &allow) == FAILURE) {
04057               return;
04058        }
04059        
04060        /* To maintain a certain BC, we allow anything for the second parameter and return original string */
04061        if (allow != NULL) {
04062               convert_to_string_ex(allow);
04063               allowed_tags = Z_STRVAL_PP(allow);
04064               allowed_tags_len = Z_STRLEN_PP(allow);
04065        }
04066 
04067        buf = estrndup(str, str_len);
04068        retval_len = php_strip_tags_ex(buf, str_len, NULL, allowed_tags, allowed_tags_len, 0);
04069        RETURN_STRINGL(buf, retval_len, 0);
04070 }
04071 /* }}} */
04072 
04073 /* {{{ proto string setlocale(mixed category, string locale [, string ...])
04074    Set locale information */
04075 PHP_FUNCTION(setlocale)
04076 {
04077        zval ***args = NULL;
04078        zval **pcategory, **plocale;
04079        int num_args, cat, i = 0;
04080        char *loc, *retval;
04081 
04082        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z+", &pcategory, &args, &num_args) == FAILURE) {
04083               return;
04084        }
04085 
04086 #ifdef HAVE_SETLOCALE
04087        if (Z_TYPE_PP(pcategory) == IS_LONG) {
04088               convert_to_long_ex(pcategory);     
04089               cat = Z_LVAL_PP(pcategory);
04090        } else {
04091               /* FIXME: The following behaviour should be removed. */
04092               char *category;
04093               
04094               php_error_docref(NULL TSRMLS_CC, E_DEPRECATED, "Passing locale category name as string is deprecated. Use the LC_* -constants instead");
04095               
04096               convert_to_string_ex(pcategory);
04097               category = Z_STRVAL_PP(pcategory);
04098 
04099               if (!strcasecmp("LC_ALL", category)) {
04100                      cat = LC_ALL;
04101               } else if (!strcasecmp("LC_COLLATE", category)) {
04102                      cat = LC_COLLATE;
04103               } else if (!strcasecmp("LC_CTYPE", category)) {
04104                      cat = LC_CTYPE;
04105 #ifdef LC_MESSAGES
04106               } else if (!strcasecmp("LC_MESSAGES", category)) {
04107                      cat = LC_MESSAGES;
04108 #endif
04109               } else if (!strcasecmp("LC_MONETARY", category)) {
04110                      cat = LC_MONETARY;
04111               } else if (!strcasecmp("LC_NUMERIC", category)) {
04112                      cat = LC_NUMERIC;
04113               } else if (!strcasecmp("LC_TIME", category)) {
04114                      cat = LC_TIME;
04115               } else {
04116                      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);
04117                      
04118                      if (args) {
04119                             efree(args);
04120                      }                    
04121                      RETURN_FALSE;
04122               }
04123        }
04124 
04125        if (Z_TYPE_PP(args[0]) == IS_ARRAY) {
04126               zend_hash_internal_pointer_reset(Z_ARRVAL_PP(args[0]));
04127        }
04128        
04129        while (1) {
04130               if (Z_TYPE_PP(args[0]) == IS_ARRAY) {
04131                      if (!zend_hash_num_elements(Z_ARRVAL_PP(args[0]))) {
04132                             break;
04133                      }
04134                      zend_hash_get_current_data(Z_ARRVAL_PP(args[0]), (void **)&plocale);
04135               } else {
04136                      plocale = args[i];
04137               }
04138 
04139               convert_to_string_ex(plocale);
04140               
04141               if (!strcmp ("0", Z_STRVAL_PP(plocale))) {
04142                      loc = NULL;
04143               } else {
04144                      loc = Z_STRVAL_PP(plocale);
04145                      if (Z_STRLEN_PP(plocale) >= 255) {
04146                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Specified locale name is too long");
04147                             break;
04148                      }
04149               }
04150 
04151               retval = php_my_setlocale(cat, loc);
04152               zend_update_current_locale();
04153               if (retval) {
04154                      /* Remember if locale was changed */
04155                      if (loc) {
04156                             STR_FREE(BG(locale_string));
04157                             BG(locale_string) = estrdup(retval);
04158                      }
04159 
04160                      if (args) {
04161                             efree(args);
04162                      }
04163                      RETURN_STRING(retval, 1);
04164               }
04165               
04166               if (Z_TYPE_PP(args[0]) == IS_ARRAY) {
04167                      if (zend_hash_move_forward(Z_ARRVAL_PP(args[0])) == FAILURE) break;
04168               } else {
04169                      if (++i >= num_args) break;
04170               }
04171        }
04172 
04173 #endif
04174        if (args) {
04175               efree(args);
04176        }
04177        RETURN_FALSE;
04178 }
04179 /* }}} */
04180 
04181 /* {{{ proto void parse_str(string encoded_string [, array result])
04182    Parses GET/POST/COOKIE data and sets global variables */
04183 PHP_FUNCTION(parse_str)
04184 {
04185        char *arg;
04186        zval *arrayArg = NULL;
04187        char *res = NULL;
04188        int arglen;
04189 
04190        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z", &arg, &arglen, &arrayArg) == FAILURE) {
04191               return;
04192        }
04193 
04194        res = estrndup(arg, arglen);
04195 
04196        if (arrayArg == NULL) {
04197               zval tmp;
04198 
04199               if (!EG(active_symbol_table)) {
04200                      zend_rebuild_symbol_table(TSRMLS_C);
04201               }
04202               Z_ARRVAL(tmp) = EG(active_symbol_table);
04203               sapi_module.treat_data(PARSE_STRING, res, &tmp TSRMLS_CC);
04204        } else        {
04205               zval ret;
04206               
04207               array_init(&ret);
04208               sapi_module.treat_data(PARSE_STRING, res, &ret TSRMLS_CC);
04209               /* Clear out the array that was passed in. */
04210               zval_dtor(arrayArg);
04211               arrayArg->type = ret.type;
04212               arrayArg->value = ret.value;
04213        }
04214 }
04215 /* }}} */
04216 
04217 #define PHP_TAG_BUF_SIZE 1023
04218 
04219 /* {{{ php_tag_find
04220  *
04221  * Check if tag is in a set of tags 
04222  *
04223  * states:
04224  * 
04225  * 0 start tag
04226  * 1 first non-whitespace char seen
04227  */
04228 int php_tag_find(char *tag, int len, char *set) {
04229        char c, *n, *t;
04230        int state=0, done=0;
04231        char *norm;
04232 
04233        if (len <= 0) {
04234               return 0;
04235        }
04236        
04237        norm = emalloc(len+1);
04238 
04239        n = norm;
04240        t = tag;
04241        c = tolower(*t);
04242        /* 
04243           normalize the tag removing leading and trailing whitespace
04244           and turn any <a whatever...> into just <a> and any </tag>
04245           into <tag>
04246        */
04247        while (!done) {
04248               switch (c) {
04249                      case '<':
04250                             *(n++) = c;
04251                             break;
04252                      case '>':
04253                             done =1;
04254                             break;
04255                      default:
04256                             if (!isspace((int)c)) {
04257                                    if (state == 0) {
04258                                           state=1;
04259                                    }
04260                                    if (c != '/') {
04261                                           *(n++) = c;
04262                                    }
04263                             } else {
04264                                    if (state == 1)
04265                                           done=1;
04266                             }
04267                             break;
04268               }
04269               c = tolower(*(++t));
04270        }  
04271        *(n++) = '>';
04272        *n = '\0'; 
04273        if (strstr(set, norm)) {
04274               done=1;
04275        } else {
04276               done=0;
04277        }
04278        efree(norm);
04279        return done;
04280 }
04281 /* }}} */
04282 
04283 PHPAPI size_t php_strip_tags(char *rbuf, int len, int *stateptr, char *allow, int allow_len) /* {{{ */
04284 {
04285        return php_strip_tags_ex(rbuf, len, stateptr, allow, allow_len, 0);
04286 }
04287 /* }}} */
04288 
04289 /* {{{ php_strip_tags
04290  
04291        A simple little state-machine to strip out html and php tags 
04292        
04293        State 0 is the output state, State 1 means we are inside a
04294        normal html tag and state 2 means we are inside a php tag.
04295 
04296        The state variable is passed in to allow a function like fgetss
04297        to maintain state across calls to the function.
04298 
04299        lc holds the last significant character read and br is a bracket
04300        counter.
04301 
04302        When an allow string is passed in we keep track of the string
04303        in state 1 and when the tag is closed check it against the
04304        allow string to see if we should allow it.
04305 
04306        swm: Added ability to strip <?xml tags without assuming it PHP
04307        code.
04308 */
04309 PHPAPI size_t php_strip_tags_ex(char *rbuf, int len, int *stateptr, char *allow, int allow_len, zend_bool allow_tag_spaces)
04310 {
04311        char *tbuf, *buf, *p, *tp, *rp, c, lc;
04312        int br, i=0, depth=0, in_q = 0;
04313        int state = 0, pos;
04314 
04315        if (stateptr)
04316               state = *stateptr;
04317 
04318        buf = estrndup(rbuf, len);
04319        c = *buf;
04320        lc = '\0';
04321        p = buf;
04322        rp = rbuf;
04323        br = 0;
04324        if (allow) {
04325               php_strtolower(allow, allow_len);
04326               tbuf = emalloc(PHP_TAG_BUF_SIZE + 1);
04327               tp = tbuf;
04328        } else {
04329               tbuf = tp = NULL;
04330        }
04331 
04332        while (i < len) {
04333               switch (c) {
04334                      case '\0':
04335                             break;
04336                      case '<':
04337                             if (in_q) {
04338                                    break;
04339                             }
04340                             if (isspace(*(p + 1)) && !allow_tag_spaces) {
04341                                    goto reg_char;
04342                             }
04343                             if (state == 0) {
04344                                    lc = '<';
04345                                    state = 1;
04346                                    if (allow) {
04347                                           if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
04348                                                  pos = tp - tbuf;
04349                                                  tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
04350                                                  tp = tbuf + pos;
04351                                           }
04352                                           *(tp++) = '<';
04353                                    }
04354                             } else if (state == 1) {
04355                                    depth++;
04356                             }
04357                             break;
04358 
04359                      case '(':
04360                             if (state == 2) {
04361                                    if (lc != '"' && lc != '\'') {
04362                                           lc = '(';
04363                                           br++;
04364                                    }
04365                             } else if (allow && state == 1) {
04366                                    if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
04367                                           pos = tp - tbuf;
04368                                           tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
04369                                           tp = tbuf + pos;
04370                                    }
04371                                    *(tp++) = c;
04372                             } else if (state == 0) {
04373                                    *(rp++) = c;
04374                             }
04375                             break; 
04376 
04377                      case ')':
04378                             if (state == 2) {
04379                                    if (lc != '"' && lc != '\'') {
04380                                           lc = ')';
04381                                           br--;
04382                                    }
04383                             } else if (allow && state == 1) {
04384                                    if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
04385                                           pos = tp - tbuf;
04386                                           tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
04387                                           tp = tbuf + pos;
04388                                    }
04389                                    *(tp++) = c;
04390                             } else if (state == 0) {
04391                                    *(rp++) = c;
04392                             }
04393                             break; 
04394 
04395                      case '>':
04396                             if (depth) {
04397                                    depth--;
04398                                    break;
04399                             }
04400 
04401                             if (in_q) {
04402                                    break;
04403                             }
04404 
04405                             switch (state) {
04406                                    case 1: /* HTML/XML */
04407                                           lc = '>';
04408                                           in_q = state = 0;
04409                                           if (allow) {
04410                                                  if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
04411                                                         pos = tp - tbuf;
04412                                                         tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
04413                                                         tp = tbuf + pos;
04414                                                  }
04415                                                  *(tp++) = '>';
04416                                                  *tp='\0';
04417                                                  if (php_tag_find(tbuf, tp-tbuf, allow)) {
04418                                                         memcpy(rp, tbuf, tp-tbuf);
04419                                                         rp += tp-tbuf;
04420                                                  }
04421                                                  tp = tbuf;
04422                                           }
04423                                           break;
04424                                           
04425                                    case 2: /* PHP */
04426                                           if (!br && lc != '\"' && *(p-1) == '?') {
04427                                                  in_q = state = 0;
04428                                                  tp = tbuf;
04429                                           }
04430                                           break;
04431                                           
04432                                    case 3:
04433                                           in_q = state = 0;
04434                                           tp = tbuf;
04435                                           break;
04436 
04437                                    case 4: /* JavaScript/CSS/etc... */
04438                                           if (p >= buf + 2 && *(p-1) == '-' && *(p-2) == '-') {
04439                                                  in_q = state = 0;
04440                                                  tp = tbuf;
04441                                           }
04442                                           break;
04443 
04444                                    default:
04445                                           *(rp++) = c;
04446                                           break;
04447                             }
04448                             break;
04449 
04450                      case '"':
04451                      case '\'':
04452                             if (state == 4) {
04453                                    /* Inside <!-- comment --> */
04454                                    break;
04455                             } else if (state == 2 && *(p-1) != '\\') {
04456                                    if (lc == c) {
04457                                           lc = '\0';
04458                                    } else if (lc != '\\') {
04459                                           lc = c;
04460                                    }
04461                             } else if (state == 0) {
04462                                    *(rp++) = c;
04463                             } else if (allow && state == 1) {
04464                                    if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
04465                                           pos = tp - tbuf;
04466                                           tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
04467                                           tp = tbuf + pos;
04468                                    }
04469                                    *(tp++) = c;
04470                             }
04471                             if (state && p != buf && (state == 1 || *(p-1) != '\\') && (!in_q || *p == in_q)) {
04472                                    if (in_q) {
04473                                           in_q = 0;
04474                                    } else {
04475                                           in_q = *p;
04476                                    }
04477                             }
04478                             break;
04479                      
04480                      case '!': 
04481                             /* JavaScript & Other HTML scripting languages */
04482                             if (state == 1 && *(p-1) == '<') { 
04483                                    state = 3;
04484                                    lc = c;
04485                             } else {
04486                                    if (state == 0) {
04487                                           *(rp++) = c;
04488                                    } else if (allow && state == 1) {
04489                                           if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
04490                                                  pos = tp - tbuf;
04491                                                  tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
04492                                                  tp = tbuf + pos;
04493                                           }
04494                                           *(tp++) = c;
04495                                    }
04496                             }
04497                             break;
04498 
04499                      case '-':
04500                             if (state == 3 && p >= buf + 2 && *(p-1) == '-' && *(p-2) == '!') {
04501                                    state = 4;
04502                             } else {
04503                                    goto reg_char;
04504                             }
04505                             break;
04506 
04507                      case '?':
04508 
04509                             if (state == 1 && *(p-1) == '<') { 
04510                                    br=0;
04511                                    state=2;
04512                                    break;
04513                             }
04514 
04515                      case 'E':
04516                      case 'e':
04517                             /* !DOCTYPE exception */
04518                             if (state==3 && p > buf+6
04519                                                && tolower(*(p-1)) == 'p'
04520                                             && tolower(*(p-2)) == 'y'
04521                                                && tolower(*(p-3)) == 't'
04522                                                && tolower(*(p-4)) == 'c'
04523                                                && tolower(*(p-5)) == 'o'
04524                                                && tolower(*(p-6)) == 'd') {
04525                                    state = 1;
04526                                    break;
04527                             }
04528                             /* fall-through */
04529 
04530                      case 'l':
04531                      case 'L':
04532 
04533                             /* swm: If we encounter '<?xml' then we shouldn't be in
04534                              * state == 2 (PHP). Switch back to HTML.
04535                              */
04536 
04537                             if (state == 2 && p > buf+2 && strncasecmp(p-2, "xm", 2) == 0) {
04538                                    state = 1;
04539                                    break;
04540                             }
04541 
04542                             /* fall-through */
04543                      default:
04544 reg_char:
04545                             if (state == 0) {
04546                                    *(rp++) = c;
04547                             } else if (allow && state == 1) {
04548                                    if (tp - tbuf >= PHP_TAG_BUF_SIZE) {
04549                                           pos = tp - tbuf;
04550                                           tbuf = erealloc(tbuf, (tp - tbuf) + PHP_TAG_BUF_SIZE + 1);
04551                                           tp = tbuf + pos;
04552                                    }
04553                                    *(tp++) = c;
04554                             } 
04555                             break;
04556               }
04557               c = *(++p);
04558               i++;
04559        }      
04560        if (rp < rbuf + len) {
04561               *rp = '\0';
04562        }
04563        efree(buf);
04564        if (allow)
04565               efree(tbuf);
04566        if (stateptr)
04567               *stateptr = state;
04568 
04569        return (size_t)(rp - rbuf);
04570 }
04571 /* }}} */
04572 
04573 /* {{{ proto array str_getcsv(string input[, string delimiter[, string enclosure[, string escape]]])
04574 Parse a CSV string into an array */
04575 PHP_FUNCTION(str_getcsv)
04576 {
04577        char *str, delim = ',', enc = '"', esc = '\\';
04578        char *delim_str = NULL, *enc_str = NULL, *esc_str = NULL;
04579        int str_len = 0, delim_len = 0, enc_len = 0, esc_len = 0;
04580 
04581        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|sss", &str, &str_len, &delim_str, &delim_len, 
04582               &enc_str, &enc_len, &esc_str, &esc_len) == FAILURE) {
04583               return;
04584        }
04585        
04586        delim = delim_len ? delim_str[0] : delim;
04587        enc = enc_len ? enc_str[0] : enc;
04588        esc = esc_len ? esc_str[0] : esc;
04589 
04590        php_fgetcsv(NULL, delim, enc, esc, str_len, str, return_value TSRMLS_CC);
04591 }
04592 /* }}} */
04593 
04594 /* {{{ proto string str_repeat(string input, int mult)
04595    Returns the input string repeat mult times */
04596 PHP_FUNCTION(str_repeat)
04597 {
04598        char          *input_str;          /* Input string */
04599        int           input_len;
04600        long          mult;                /* Multiplier */
04601        char          *result;             /* Resulting string */
04602        size_t        result_len;          /* Length of the resulting string */
04603        
04604        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &input_str, &input_len, &mult) == FAILURE) {
04605               return;
04606        }
04607 
04608        if (mult < 0) {
04609               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Second argument has to be greater than or equal to 0");
04610               return;
04611        }
04612 
04613        /* Don't waste our time if it's empty */
04614        /* ... or if the multiplier is zero */
04615        if (input_len == 0 || mult == 0)
04616               RETURN_EMPTY_STRING();
04617 
04618        /* Initialize the result string */ 
04619        result_len = input_len * mult;
04620        result = (char *)safe_emalloc(input_len, mult, 1);
04621        
04622        /* Heavy optimization for situations where input string is 1 byte long */
04623        if (input_len == 1) {
04624               memset(result, *(input_str), mult); 
04625        } else {
04626               char *s, *e, *ee;
04627               int l=0;
04628               memcpy(result, input_str, input_len);
04629               s = result;
04630               e = result + input_len;
04631               ee = result + result_len;
04632               
04633               while (e<ee) {
04634                      l = (e-s) < (ee-e) ? (e-s) : (ee-e);
04635                      memmove(e, s, l);
04636                      e += l;
04637               }
04638        }
04639 
04640        result[result_len] = '\0';
04641        
04642        RETURN_STRINGL(result, result_len, 0);
04643 }
04644 /* }}} */
04645 
04646 /* {{{ proto mixed count_chars(string input [, int mode])
04647    Returns info about what characters are used in input */
04648 PHP_FUNCTION(count_chars)
04649 {
04650        char *input;
04651        int chars[256];
04652        long mymode=0;
04653        unsigned char *buf;
04654        int len, inx;
04655        char retstr[256];
04656        int retlen=0;
04657 
04658        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &input, &len, &mymode) == FAILURE) {
04659               return;
04660        }
04661 
04662        if (mymode < 0 || mymode > 4) {
04663               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown mode");
04664               RETURN_FALSE;
04665        }
04666 
04667        buf = (unsigned char *) input;
04668        memset((void*) chars, 0, sizeof(chars));
04669 
04670        while (len > 0) {
04671               chars[*buf]++;
04672               buf++;
04673               len--;
04674        }
04675 
04676        if (mymode < 3) {
04677               array_init(return_value);
04678        }
04679 
04680        for (inx = 0; inx < 256; inx++) {
04681               switch (mymode) {
04682                      case 0:
04683                             add_index_long(return_value, inx, chars[inx]);
04684                             break;
04685                      case 1:
04686                             if (chars[inx] != 0) {
04687                                    add_index_long(return_value, inx, chars[inx]);
04688                             }
04689                             break;
04690                      case 2:
04691                             if (chars[inx] == 0) {
04692                                    add_index_long(return_value, inx, chars[inx]);
04693                             }
04694                             break;
04695                      case 3:
04696                             if (chars[inx] != 0) {
04697                                    retstr[retlen++] = inx;
04698                             }
04699                             break;
04700                      case 4:
04701                             if (chars[inx] == 0) {
04702                                    retstr[retlen++] = inx;
04703                             }
04704                             break;
04705               }
04706        }
04707        
04708        if (mymode >= 3 && mymode <= 4) {
04709               RETURN_STRINGL(retstr, retlen, 1);
04710        }
04711 }
04712 /* }}} */
04713 
04714 /* {{{ php_strnatcmp
04715  */
04716 static void php_strnatcmp(INTERNAL_FUNCTION_PARAMETERS, int fold_case)
04717 {
04718        char *s1, *s2;
04719        int s1_len, s2_len;
04720 
04721        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &s1, &s1_len, &s2, &s2_len) == FAILURE) {
04722               return;
04723        }
04724 
04725        RETURN_LONG(strnatcmp_ex(s1, s1_len,
04726                                                   s2, s2_len,
04727                                                   fold_case));
04728 }
04729 /* }}} */
04730 
04731 /* {{{ proto int strnatcmp(string s1, string s2)
04732    Returns the result of string comparison using 'natural' algorithm */
04733 PHP_FUNCTION(strnatcmp)
04734 {
04735        php_strnatcmp(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
04736 }
04737 /* }}} */
04738 
04739 /* {{{ proto array localeconv(void)
04740    Returns numeric formatting information based on the current locale */
04741 PHP_FUNCTION(localeconv)
04742 {
04743        zval *grouping, *mon_grouping;
04744        int len, i;
04745 
04746        /* We don't need no stinkin' parameters... */
04747        if (zend_parse_parameters_none() == FAILURE) {
04748               return;
04749        }
04750 
04751        MAKE_STD_ZVAL(grouping);
04752        MAKE_STD_ZVAL(mon_grouping);
04753 
04754        array_init(return_value);
04755        array_init(grouping);
04756        array_init(mon_grouping);
04757 
04758 #ifdef HAVE_LOCALECONV
04759        {
04760               struct lconv currlocdata;
04761 
04762               localeconv_r( &currlocdata );
04763    
04764               /* Grab the grouping data out of the array */
04765               len = strlen(currlocdata.grouping);
04766 
04767               for (i = 0; i < len; i++) {
04768                      add_index_long(grouping, i, currlocdata.grouping[i]);
04769               }
04770 
04771               /* Grab the monetary grouping data out of the array */
04772               len = strlen(currlocdata.mon_grouping);
04773 
04774               for (i = 0; i < len; i++) {
04775                      add_index_long(mon_grouping, i, currlocdata.mon_grouping[i]);
04776               }
04777 
04778               add_assoc_string(return_value, "decimal_point",     currlocdata.decimal_point,     1);
04779               add_assoc_string(return_value, "thousands_sep",     currlocdata.thousands_sep,     1);
04780               add_assoc_string(return_value, "int_curr_symbol",   currlocdata.int_curr_symbol,   1);
04781               add_assoc_string(return_value, "currency_symbol",   currlocdata.currency_symbol,   1);
04782               add_assoc_string(return_value, "mon_decimal_point", currlocdata.mon_decimal_point, 1);
04783               add_assoc_string(return_value, "mon_thousands_sep", currlocdata.mon_thousands_sep, 1);
04784               add_assoc_string(return_value, "positive_sign",     currlocdata.positive_sign,     1);
04785               add_assoc_string(return_value, "negative_sign",     currlocdata.negative_sign,     1);
04786               add_assoc_long(  return_value, "int_frac_digits",   currlocdata.int_frac_digits     );
04787               add_assoc_long(  return_value, "frac_digits",       currlocdata.frac_digits         );
04788               add_assoc_long(  return_value, "p_cs_precedes",     currlocdata.p_cs_precedes       );
04789               add_assoc_long(  return_value, "p_sep_by_space",    currlocdata.p_sep_by_space      );
04790               add_assoc_long(  return_value, "n_cs_precedes",     currlocdata.n_cs_precedes       );
04791               add_assoc_long(  return_value, "n_sep_by_space",    currlocdata.n_sep_by_space      );
04792               add_assoc_long(  return_value, "p_sign_posn",       currlocdata.p_sign_posn         );
04793               add_assoc_long(  return_value, "n_sign_posn",       currlocdata.n_sign_posn         );
04794        }
04795 #else
04796        /* Ok, it doesn't look like we have locale info floating around, so I guess it
04797           wouldn't hurt to just go ahead and return the POSIX locale information?  */
04798 
04799        add_index_long(grouping, 0, -1);
04800        add_index_long(mon_grouping, 0, -1);
04801 
04802        add_assoc_string(return_value, "decimal_point",     "\x2E", 1);
04803        add_assoc_string(return_value, "thousands_sep",     "",     1);
04804        add_assoc_string(return_value, "int_curr_symbol",   "",     1);
04805        add_assoc_string(return_value, "currency_symbol",   "",     1);
04806        add_assoc_string(return_value, "mon_decimal_point", "\x2E", 1);
04807        add_assoc_string(return_value, "mon_thousands_sep", "",     1);
04808        add_assoc_string(return_value, "positive_sign",     "",     1);
04809        add_assoc_string(return_value, "negative_sign",     "",     1);
04810        add_assoc_long(  return_value, "int_frac_digits",   CHAR_MAX );
04811        add_assoc_long(  return_value, "frac_digits",       CHAR_MAX );
04812        add_assoc_long(  return_value, "p_cs_precedes",     CHAR_MAX );
04813        add_assoc_long(  return_value, "p_sep_by_space",    CHAR_MAX );
04814        add_assoc_long(  return_value, "n_cs_precedes",     CHAR_MAX );
04815        add_assoc_long(  return_value, "n_sep_by_space",    CHAR_MAX );
04816        add_assoc_long(  return_value, "p_sign_posn",       CHAR_MAX );
04817        add_assoc_long(  return_value, "n_sign_posn",       CHAR_MAX );
04818 #endif
04819 
04820        zend_hash_update(Z_ARRVAL_P(return_value), "grouping", 9, &grouping, sizeof(zval *), NULL);
04821        zend_hash_update(Z_ARRVAL_P(return_value), "mon_grouping", 13, &mon_grouping, sizeof(zval *), NULL);
04822 }
04823 /* }}} */
04824 
04825 /* {{{ proto int strnatcasecmp(string s1, string s2)
04826    Returns the result of case-insensitive string comparison using 'natural' algorithm */
04827 PHP_FUNCTION(strnatcasecmp)
04828 {
04829        php_strnatcmp(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
04830 }
04831 /* }}} */
04832 
04833 /* {{{ proto int substr_count(string haystack, string needle [, int offset [, int length]])
04834    Returns the number of times a substring occurs in the string */
04835 PHP_FUNCTION(substr_count)
04836 {
04837        char *haystack, *needle;
04838        long offset = 0, length = 0;
04839        int ac = ZEND_NUM_ARGS();
04840        int count = 0;
04841        int haystack_len, needle_len;
04842        char *p, *endp, cmp;
04843 
04844        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|ll", &haystack, &haystack_len, &needle, &needle_len, &offset, &length) == FAILURE) {
04845               return;
04846        }
04847 
04848        if (needle_len == 0) {
04849               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty substring");
04850               RETURN_FALSE;
04851        }
04852        
04853        p = haystack;
04854        endp = p + haystack_len;
04855 
04856        if (offset < 0) {
04857               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset should be greater than or equal to 0");
04858               RETURN_FALSE;        
04859        }
04860 
04861        if (offset > haystack_len) {
04862               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset value %ld exceeds string length", offset);
04863               RETURN_FALSE;        
04864        }
04865        p += offset;
04866 
04867        if (ac == 4) {
04868 
04869               if (length <= 0) {
04870                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Length should be greater than 0");
04871                      RETURN_FALSE;        
04872               }
04873               if (length > (haystack_len - offset)) {
04874                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Length value %ld exceeds string length", length);
04875                      RETURN_FALSE;
04876               }
04877               endp = p + length;
04878        }
04879        
04880        if (needle_len == 1) {
04881               cmp = needle[0];
04882 
04883               while ((p = memchr(p, cmp, endp - p))) {
04884                      count++;
04885                      p++;
04886               }
04887        } else {
04888               while ((p = php_memnstr(p, needle, needle_len, endp))) {
04889                      p += needle_len;
04890                      count++;
04891               }
04892        }
04893 
04894        RETURN_LONG(count);
04895 }
04896 /* }}} */     
04897 
04898 /* {{{ proto string str_pad(string input, int pad_length [, string pad_string [, int pad_type]])
04899    Returns input string padded on the left or right to specified length with pad_string */
04900 PHP_FUNCTION(str_pad)
04901 {
04902        /* Input arguments */
04903        char *input;                       /* Input string */
04904        int  input_len;
04905        long pad_length;                   /* Length to pad to */
04906        
04907        /* Helper variables */
04908        size_t    num_pad_chars;           /* Number of padding characters (total - input size) */
04909        char  *result = NULL;              /* Resulting string */
04910        int       result_len = 0;          /* Length of the resulting string */
04911        char  *pad_str_val = " ";   /* Pointer to padding string */
04912        int    pad_str_len = 1;            /* Length of the padding string */
04913        long   pad_type_val = STR_PAD_RIGHT; /* The padding type value */
04914        int       i, left_pad=0, right_pad=0;
04915 
04916        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|sl", &input, &input_len, &pad_length,
04917                                                                                                                   &pad_str_val, &pad_str_len, &pad_type_val) == FAILURE) {
04918               return;
04919        }
04920 
04921        /* If resulting string turns out to be shorter than input string,
04922           we simply copy the input and return. */
04923        if (pad_length <= 0 || (pad_length - input_len) <= 0) {
04924               RETURN_STRINGL(input, input_len, 1);
04925        }
04926 
04927        if (pad_str_len == 0) {
04928               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Padding string cannot be empty");
04929               return;
04930        }
04931        
04932        if (pad_type_val < STR_PAD_LEFT || pad_type_val > STR_PAD_BOTH) {
04933               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Padding type has to be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH");
04934               return;
04935        }
04936 
04937        num_pad_chars = pad_length - input_len;
04938        if (num_pad_chars >= INT_MAX) {
04939               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Padding length is too long");
04940               return;       
04941        }
04942        result = (char *)emalloc(input_len + num_pad_chars + 1);
04943 
04944        /* We need to figure out the left/right padding lengths. */
04945        switch (pad_type_val) {
04946               case STR_PAD_RIGHT:
04947                      left_pad = 0;
04948                      right_pad = num_pad_chars;
04949                      break;
04950 
04951               case STR_PAD_LEFT:
04952                      left_pad = num_pad_chars;
04953                      right_pad = 0;
04954                      break;
04955 
04956               case STR_PAD_BOTH:
04957                      left_pad = num_pad_chars / 2;
04958                      right_pad = num_pad_chars - left_pad;
04959                      break;
04960        }
04961 
04962        /* First we pad on the left. */
04963        for (i = 0; i < left_pad; i++)
04964               result[result_len++] = pad_str_val[i % pad_str_len];
04965 
04966        /* Then we copy the input string. */
04967        memcpy(result + result_len, input, input_len);
04968        result_len += input_len;
04969 
04970        /* Finally, we pad on the right. */
04971        for (i = 0; i < right_pad; i++)
04972               result[result_len++] = pad_str_val[i % pad_str_len];
04973 
04974        result[result_len] = '\0';
04975 
04976        RETURN_STRINGL(result, result_len, 0);
04977 }
04978 /* }}} */
04979    
04980 /* {{{ proto mixed sscanf(string str, string format [, string ...])
04981    Implements an ANSI C compatible sscanf */
04982 PHP_FUNCTION(sscanf)
04983 {
04984        zval ***args = NULL;
04985        char *str, *format;
04986        int str_len, format_len, result, num_args = 0;
04987 
04988        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss*", &str, &str_len, &format, &format_len, 
04989               &args, &num_args) == FAILURE) {
04990               return;
04991        }
04992        
04993        result = php_sscanf_internal(str, format, num_args, args, 0, &return_value TSRMLS_CC);
04994        
04995        if (args) {
04996               efree(args);
04997        }
04998 
04999        if (SCAN_ERROR_WRONG_PARAM_COUNT == result) {
05000               WRONG_PARAM_COUNT;
05001        }
05002 }
05003 /* }}} */
05004 
05005 static char rot13_from[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
05006 static char rot13_to[] = "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM";
05007 
05008 /* {{{ proto string str_rot13(string str)
05009    Perform the rot13 transform on a string */
05010 PHP_FUNCTION(str_rot13)
05011 {
05012        char *arg;
05013        int arglen;
05014 
05015        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arglen) == FAILURE) {
05016               return;
05017        }
05018 
05019        RETVAL_STRINGL(arg, arglen, 1);
05020 
05021        php_strtr(Z_STRVAL_P(return_value), Z_STRLEN_P(return_value), rot13_from, rot13_to, 52);
05022 }
05023 /* }}} */
05024 
05025 static void php_string_shuffle(char *str, long len TSRMLS_DC) /* {{{ */
05026 {
05027        long n_elems, rnd_idx, n_left;
05028        char temp;
05029        /* The implementation is stolen from array_data_shuffle       */
05030        /* Thus the characteristics of the randomization are the same */
05031        n_elems = len;
05032        
05033        if (n_elems <= 1) {
05034               return;
05035        }
05036 
05037        n_left = n_elems;
05038        
05039        while (--n_left) {
05040               rnd_idx = php_rand(TSRMLS_C);
05041               RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX);
05042               if (rnd_idx != n_left) {
05043                      temp = str[n_left];
05044                      str[n_left] = str[rnd_idx];
05045                      str[rnd_idx] = temp;
05046               }
05047        }
05048 }
05049 /* }}} */
05050 
05051 /* {{{ proto void str_shuffle(string str)
05052    Shuffles string. One permutation of all possible is created */
05053 PHP_FUNCTION(str_shuffle)
05054 {
05055        char *arg;
05056        int arglen;
05057 
05058        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arglen) == FAILURE) {
05059               return;
05060        }
05061 
05062        RETVAL_STRINGL(arg, arglen, 1);
05063        if (Z_STRLEN_P(return_value) > 1) { 
05064               php_string_shuffle(Z_STRVAL_P(return_value), (long) Z_STRLEN_P(return_value) TSRMLS_CC);
05065        }
05066 }
05067 /* }}} */
05068 
05069 /* {{{ proto mixed str_word_count(string str, [int format [, string charlist]])
05070        Counts the number of words inside a string. If format of 1 is specified,
05071        then the function will return an array containing all the words
05072        found inside the string. If format of 2 is specified, then the function
05073        will return an associated array where the position of the word is the key
05074        and the word itself is the value.
05075        
05076        For the purpose of this function, 'word' is defined as a locale dependent
05077        string containing alphabetic characters, which also may contain, but not start
05078        with "'" and "-" characters.
05079 */
05080 PHP_FUNCTION(str_word_count)
05081 {
05082        char *buf, *str, *char_list = NULL, *p, *e, *s, ch[256];
05083        int str_len, char_list_len = 0, word_count = 0;
05084        long type = 0;
05085 
05086        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ls", &str, &str_len, &type, &char_list, &char_list_len) == FAILURE) {
05087               return;
05088        }
05089 
05090        switch(type) {
05091               case 1:
05092               case 2:
05093                      array_init(return_value);
05094                      if (!str_len) {
05095                             return;
05096                      }
05097                      break;
05098               case 0:
05099                      if (!str_len) {
05100                             RETURN_LONG(0);
05101                      }
05102                      /* nothing to be done */
05103                      break;
05104               default:
05105                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid format value %ld", type);
05106                      RETURN_FALSE;
05107        }
05108 
05109        if (char_list) {
05110               php_charmask((unsigned char *)char_list, char_list_len, ch TSRMLS_CC);
05111        }
05112        
05113        p = str;
05114        e = str + str_len;
05115 
05116        /* first character cannot be ' or -, unless explicitly allowed by the user */
05117        if ((*p == '\'' && (!char_list || !ch['\''])) || (*p == '-' && (!char_list || !ch['-']))) {
05118               p++;
05119        }
05120        /* last character cannot be -, unless explicitly allowed by the user */
05121        if (*(e - 1) == '-' && (!char_list || !ch['-'])) {
05122               e--;
05123        }
05124 
05125        while (p < e) {
05126               s = p;
05127               while (p < e && (isalpha((unsigned char)*p) || (char_list && ch[(unsigned char)*p]) || *p == '\'' || *p == '-')) {
05128                      p++;
05129               }
05130               if (p > s) {
05131                      switch (type)
05132                      {
05133                             case 1:
05134                                    buf = estrndup(s, (p-s));
05135                                    add_next_index_stringl(return_value, buf, (p-s), 0);
05136                                    break;
05137                             case 2:
05138                                    buf = estrndup(s, (p-s));
05139                                    add_index_stringl(return_value, (s - str), buf, p-s, 0);
05140                                    break;
05141                             default:
05142                                    word_count++;
05143                                    break;        
05144                      }
05145               }
05146               p++;
05147        }
05148        
05149        if (!type) {
05150               RETURN_LONG(word_count);           
05151        }
05152 }
05153 
05154 /* }}} */
05155 
05156 #if HAVE_STRFMON
05157 /* {{{ proto string money_format(string format , float value)
05158    Convert monetary value(s) to string */
05159 PHP_FUNCTION(money_format)
05160 {
05161        int format_len = 0, str_len;
05162        char *format, *str, *p, *e;
05163        double value;
05164        zend_bool check = 0;
05165 
05166        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sd", &format, &format_len, &value) == FAILURE) {
05167               return;
05168        }
05169 
05170        p = format;
05171        e = p + format_len;
05172        while ((p = memchr(p, '%', (e - p)))) {
05173               if (*(p + 1) == '%') {
05174                      p += 2;       
05175               } else if (!check) {
05176                      check = 1;
05177                      p++;
05178               } else {
05179                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Only a single %%i or %%n token can be used");
05180                      RETURN_FALSE;
05181               }
05182        }
05183 
05184        str_len = format_len + 1024;
05185        str = emalloc(str_len);
05186        if ((str_len = strfmon(str, str_len, format, value)) < 0) {
05187               efree(str);
05188               RETURN_FALSE;
05189        }
05190        str[str_len] = 0;
05191 
05192        RETURN_STRINGL(erealloc(str, str_len + 1), str_len, 0);
05193 }
05194 /* }}} */
05195 #endif
05196 
05197 /* {{{ proto array str_split(string str [, int split_length])
05198    Convert a string to an array. If split_length is specified, break the string down into chunks each split_length characters long. */
05199 PHP_FUNCTION(str_split)
05200 {
05201        char *str;
05202        int str_len;
05203        long split_length = 1;
05204        char *p;
05205        int n_reg_segments;
05206        
05207        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &str, &str_len, &split_length) == FAILURE) {
05208               return;
05209        }
05210 
05211        if (split_length <= 0) {
05212               php_error_docref(NULL TSRMLS_CC, E_WARNING, "The length of each segment must be greater than zero");
05213               RETURN_FALSE;
05214        }
05215 
05216        array_init_size(return_value, ((str_len - 1) / split_length) + 1);
05217 
05218        if (split_length >= str_len) {
05219               add_next_index_stringl(return_value, str, str_len, 1);
05220               return;
05221        }
05222 
05223        n_reg_segments = str_len / split_length;
05224        p = str;
05225 
05226        while (n_reg_segments-- > 0) {
05227               add_next_index_stringl(return_value, p, split_length, 1);
05228               p += split_length;
05229        }
05230 
05231        if (p != (str + str_len)) {
05232               add_next_index_stringl(return_value, p, (str + str_len - p), 1);
05233        }
05234 }
05235 /* }}} */
05236 
05237 /* {{{ proto array strpbrk(string haystack, string char_list)
05238    Search a string for any of a set of characters */
05239 PHP_FUNCTION(strpbrk)
05240 {
05241        char *haystack, *char_list;
05242        int haystack_len, char_list_len;
05243        char *p;
05244        
05245        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &haystack, &haystack_len, &char_list, &char_list_len) == FAILURE) {
05246               RETURN_FALSE;
05247        }
05248 
05249        if (!char_list_len) {
05250               php_error_docref(NULL TSRMLS_CC, E_WARNING, "The character list cannot be empty");
05251               RETURN_FALSE; 
05252        }
05253 
05254        if ((p = strpbrk(haystack, char_list))) {
05255               RETURN_STRINGL(p, (haystack + haystack_len - p), 1);
05256        } else {
05257               RETURN_FALSE;
05258        }
05259 }
05260 /* }}} */
05261 
05262 /* {{{ proto int substr_compare(string main_str, string str, int offset [, int length [, bool case_sensitivity]])
05263    Binary safe optionally case insensitive comparison of 2 strings from an offset, up to length characters */
05264 PHP_FUNCTION(substr_compare)
05265 {
05266        char *s1, *s2;
05267        int s1_len, s2_len;
05268        long offset, len=0;
05269        zend_bool cs=0;
05270        uint cmp_len;
05271 
05272        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssl|lb", &s1, &s1_len, &s2, &s2_len, &offset, &len, &cs) == FAILURE) {
05273               RETURN_FALSE;
05274        }
05275 
05276        if (ZEND_NUM_ARGS() >= 4 && len <= 0) {
05277               php_error_docref(NULL TSRMLS_CC, E_WARNING, "The length must be greater than zero");
05278               RETURN_FALSE;
05279        }
05280 
05281        if (offset < 0) {
05282               offset = s1_len + offset;
05283               offset = (offset < 0) ? 0 : offset;
05284        }
05285 
05286        if (offset >= s1_len) {
05287               php_error_docref(NULL TSRMLS_CC, E_WARNING, "The start position cannot exceed initial string length");
05288               RETURN_FALSE;
05289        }
05290 
05291        if (len > s1_len - offset) {
05292               len = s1_len - offset;
05293        }
05294 
05295        cmp_len = (uint) (len ? len : MAX(s2_len, (s1_len - offset)));
05296 
05297        if (!cs) {
05298               RETURN_LONG(zend_binary_strncmp(s1 + offset, (s1_len - offset), s2, s2_len, cmp_len));
05299        } else {
05300               RETURN_LONG(zend_binary_strncasecmp(s1 + offset, (s1_len - offset), s2, s2_len, cmp_len));
05301        }
05302 }
05303 /* }}} */
05304 
05305 /*
05306  * Local variables:
05307  * tab-width: 4
05308  * c-basic-offset: 4
05309  * End:
05310  * vim600: noet sw=4 ts=4 fdm=marker
05311  * vim<600: noet sw=4 ts=4
05312  */