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