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