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