1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Rasmus Lerdorf <rasmus@php.net> |
14 | Stig Sæther Bakken <ssb@php.net> |
15 | Zeev Suraski <zeev@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #include <stdio.h>
20 #include "php.h"
21 #include "php_string.h"
22 #include "php_variables.h"
23 #include <locale.h>
24 #ifdef HAVE_LANGINFO_H
25 # include <langinfo.h>
26 #endif
27
28 #ifdef HAVE_LIBINTL
29 # include <libintl.h> /* For LC_MESSAGES */
30 #endif
31
32 #include "scanf.h"
33 #include "zend_API.h"
34 #include "zend_execute.h"
35 #include "php_globals.h"
36 #include "basic_functions.h"
37 #include "zend_smart_str.h"
38 #include <Zend/zend_exceptions.h>
39 #ifdef ZTS
40 #include "TSRM.h"
41 #endif
42
43 /* For str_getcsv() support */
44 #include "ext/standard/file.h"
45 /* For php_next_utf8_char() */
46 #include "ext/standard/html.h"
47 #include "ext/random/php_random.h"
48
49 #ifdef __SSE2__
50 #include <emmintrin.h>
51 #include "Zend/zend_bitset.h"
52 #endif
53
54 /* this is read-only, so it's ok */
55 ZEND_SET_ALIGNED(16, static const char hexconvtab[]) = "0123456789abcdef";
56
57 /* localeconv mutex */
58 #ifdef ZTS
59 static MUTEX_T locale_mutex = NULL;
60 #endif
61
62 /* {{{ php_bin2hex */
php_bin2hex(const unsigned char * old,const size_t oldlen)63 static zend_string *php_bin2hex(const unsigned char *old, const size_t oldlen)
64 {
65 zend_string *result;
66 size_t i, j;
67
68 result = zend_string_safe_alloc(oldlen, 2 * sizeof(char), 0, 0);
69
70 for (i = j = 0; i < oldlen; i++) {
71 ZSTR_VAL(result)[j++] = hexconvtab[old[i] >> 4];
72 ZSTR_VAL(result)[j++] = hexconvtab[old[i] & 15];
73 }
74 ZSTR_VAL(result)[j] = '\0';
75
76 return result;
77 }
78 /* }}} */
79
80 /* {{{ php_hex2bin */
php_hex2bin(const unsigned char * old,const size_t oldlen)81 static zend_string *php_hex2bin(const unsigned char *old, const size_t oldlen)
82 {
83 size_t target_length = oldlen >> 1;
84 zend_string *str = zend_string_alloc(target_length, 0);
85 unsigned char *ret = (unsigned char *)ZSTR_VAL(str);
86 size_t i, j;
87
88 for (i = j = 0; i < target_length; i++) {
89 unsigned char c = old[j++];
90 unsigned char l = c & ~0x20;
91 int is_letter = ((unsigned int) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(unsigned int) - 1);
92 unsigned char d;
93
94 /* basically (c >= '0' && c <= '9') || (l >= 'A' && l <= 'F') */
95 if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(unsigned int) - 1)) | is_letter)) {
96 d = (l - 0x10 - 0x27 * is_letter) << 4;
97 } else {
98 zend_string_efree(str);
99 return NULL;
100 }
101 c = old[j++];
102 l = c & ~0x20;
103 is_letter = ((unsigned int) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(unsigned int) - 1);
104 if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(unsigned int) - 1)) | is_letter)) {
105 d |= l - 0x10 - 0x27 * is_letter;
106 } else {
107 zend_string_efree(str);
108 return NULL;
109 }
110 ret[i] = d;
111 }
112 ret[i] = '\0';
113
114 return str;
115 }
116 /* }}} */
117
118 /* {{{ localeconv_r
119 * glibc's localeconv is not reentrant, so lets make it so ... sorta */
localeconv_r(struct lconv * out)120 PHPAPI struct lconv *localeconv_r(struct lconv *out)
121 {
122
123 #ifdef ZTS
124 tsrm_mutex_lock( locale_mutex );
125 #endif
126
127 /* cur->locinfo is struct __crt_locale_info which implementation is
128 hidden in vc14. TODO revisit this and check if a workaround available
129 and needed. */
130 #if defined(PHP_WIN32) && _MSC_VER < 1900 && defined(ZTS)
131 {
132 /* Even with the enabled per thread locale, localeconv
133 won't check any locale change in the master thread. */
134 _locale_t cur = _get_current_locale();
135 *out = *cur->locinfo->lconv;
136 _free_locale(cur);
137 }
138 #else
139 /* localeconv doesn't return an error condition */
140 *out = *localeconv();
141 #endif
142
143 #ifdef ZTS
144 tsrm_mutex_unlock( locale_mutex );
145 #endif
146
147 return out;
148 }
149 /* }}} */
150
151 #ifdef ZTS
152 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(localeconv)153 PHP_MINIT_FUNCTION(localeconv)
154 {
155 locale_mutex = tsrm_mutex_alloc();
156 return SUCCESS;
157 }
158 /* }}} */
159
160 /* {{{ PHP_MSHUTDOWN_FUNCTION */
PHP_MSHUTDOWN_FUNCTION(localeconv)161 PHP_MSHUTDOWN_FUNCTION(localeconv)
162 {
163 tsrm_mutex_free( locale_mutex );
164 locale_mutex = NULL;
165 return SUCCESS;
166 }
167 /* }}} */
168 #endif
169
170 /* {{{ Converts the binary representation of data to hex */
PHP_FUNCTION(bin2hex)171 PHP_FUNCTION(bin2hex)
172 {
173 zend_string *result;
174 zend_string *data;
175
176 ZEND_PARSE_PARAMETERS_START(1, 1)
177 Z_PARAM_STR(data)
178 ZEND_PARSE_PARAMETERS_END();
179
180 result = php_bin2hex((unsigned char *)ZSTR_VAL(data), ZSTR_LEN(data));
181
182 RETURN_STR(result);
183 }
184 /* }}} */
185
186 /* {{{ Converts the hex representation of data to binary */
PHP_FUNCTION(hex2bin)187 PHP_FUNCTION(hex2bin)
188 {
189 zend_string *result, *data;
190
191 ZEND_PARSE_PARAMETERS_START(1, 1)
192 Z_PARAM_STR(data)
193 ZEND_PARSE_PARAMETERS_END();
194
195 if (ZSTR_LEN(data) % 2 != 0) {
196 php_error_docref(NULL, E_WARNING, "Hexadecimal input string must have an even length");
197 RETURN_FALSE;
198 }
199
200 result = php_hex2bin((unsigned char *)ZSTR_VAL(data), ZSTR_LEN(data));
201
202 if (!result) {
203 php_error_docref(NULL, E_WARNING, "Input string must be hexadecimal string");
204 RETURN_FALSE;
205 }
206
207 RETVAL_STR(result);
208 }
209 /* }}} */
210
php_spn_common_handler(INTERNAL_FUNCTION_PARAMETERS,int behavior)211 static void php_spn_common_handler(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
212 {
213 zend_string *s11, *s22;
214 zend_long start = 0, len = 0;
215 bool len_is_null = 1;
216
217 ZEND_PARSE_PARAMETERS_START(2, 4)
218 Z_PARAM_STR(s11)
219 Z_PARAM_STR(s22)
220 Z_PARAM_OPTIONAL
221 Z_PARAM_LONG(start)
222 Z_PARAM_LONG_OR_NULL(len, len_is_null)
223 ZEND_PARSE_PARAMETERS_END();
224
225 size_t remain_len = ZSTR_LEN(s11);
226 if (start < 0) {
227 start += remain_len;
228 if (start < 0) {
229 start = 0;
230 }
231 } else if ((size_t) start > remain_len) {
232 start = remain_len;
233 }
234
235 remain_len -= start;
236 if (!len_is_null) {
237 if (len < 0) {
238 len += remain_len;
239 if (len < 0) {
240 len = 0;
241 }
242 } else if ((size_t) len > remain_len) {
243 len = remain_len;
244 }
245 } else {
246 len = remain_len;
247 }
248
249 if (len == 0) {
250 RETURN_LONG(0);
251 }
252
253 if (behavior == PHP_STR_STRSPN) {
254 RETURN_LONG(php_strspn(ZSTR_VAL(s11) + start /*str1_start*/,
255 ZSTR_VAL(s22) /*str2_start*/,
256 ZSTR_VAL(s11) + start + len /*str1_end*/,
257 ZSTR_VAL(s22) + ZSTR_LEN(s22) /*str2_end*/));
258 } else {
259 ZEND_ASSERT(behavior == PHP_STR_STRCSPN);
260 RETURN_LONG(php_strcspn(ZSTR_VAL(s11) + start /*str1_start*/,
261 ZSTR_VAL(s22) /*str2_start*/,
262 ZSTR_VAL(s11) + start + len /*str1_end*/,
263 ZSTR_VAL(s22) + ZSTR_LEN(s22) /*str2_end*/));
264 }
265 }
266 /* }}} */
267
268 /* {{{ 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)269 PHP_FUNCTION(strspn)
270 {
271 php_spn_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_STR_STRSPN);
272 }
273 /* }}} */
274
275 /* {{{ 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)276 PHP_FUNCTION(strcspn)
277 {
278 php_spn_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_STR_STRCSPN);
279 }
280 /* }}} */
281
282 #ifdef HAVE_NL_LANGINFO
283 /* {{{ Query language and locale information */
PHP_FUNCTION(nl_langinfo)284 PHP_FUNCTION(nl_langinfo)
285 {
286 zend_long item;
287 char *value;
288
289 ZEND_PARSE_PARAMETERS_START(1, 1)
290 Z_PARAM_LONG(item)
291 ZEND_PARSE_PARAMETERS_END();
292
293 switch(item) { /* {{{ */
294 #ifdef ABDAY_1
295 case ABDAY_1:
296 case ABDAY_2:
297 case ABDAY_3:
298 case ABDAY_4:
299 case ABDAY_5:
300 case ABDAY_6:
301 case ABDAY_7:
302 #endif
303 #ifdef DAY_1
304 case DAY_1:
305 case DAY_2:
306 case DAY_3:
307 case DAY_4:
308 case DAY_5:
309 case DAY_6:
310 case DAY_7:
311 #endif
312 #ifdef ABMON_1
313 case ABMON_1:
314 case ABMON_2:
315 case ABMON_3:
316 case ABMON_4:
317 case ABMON_5:
318 case ABMON_6:
319 case ABMON_7:
320 case ABMON_8:
321 case ABMON_9:
322 case ABMON_10:
323 case ABMON_11:
324 case ABMON_12:
325 #endif
326 #ifdef MON_1
327 case MON_1:
328 case MON_2:
329 case MON_3:
330 case MON_4:
331 case MON_5:
332 case MON_6:
333 case MON_7:
334 case MON_8:
335 case MON_9:
336 case MON_10:
337 case MON_11:
338 case MON_12:
339 #endif
340 #ifdef AM_STR
341 case AM_STR:
342 #endif
343 #ifdef PM_STR
344 case PM_STR:
345 #endif
346 #ifdef D_T_FMT
347 case D_T_FMT:
348 #endif
349 #ifdef D_FMT
350 case D_FMT:
351 #endif
352 #ifdef T_FMT
353 case T_FMT:
354 #endif
355 #ifdef T_FMT_AMPM
356 case T_FMT_AMPM:
357 #endif
358 #ifdef ERA
359 case ERA:
360 #endif
361 #ifdef ERA_YEAR
362 case ERA_YEAR:
363 #endif
364 #ifdef ERA_D_T_FMT
365 case ERA_D_T_FMT:
366 #endif
367 #ifdef ERA_D_FMT
368 case ERA_D_FMT:
369 #endif
370 #ifdef ERA_T_FMT
371 case ERA_T_FMT:
372 #endif
373 #ifdef ALT_DIGITS
374 case ALT_DIGITS:
375 #endif
376 #ifdef INT_CURR_SYMBOL
377 case INT_CURR_SYMBOL:
378 #endif
379 #ifdef CURRENCY_SYMBOL
380 case CURRENCY_SYMBOL:
381 #endif
382 #ifdef CRNCYSTR
383 case CRNCYSTR:
384 #endif
385 #ifdef MON_DECIMAL_POINT
386 case MON_DECIMAL_POINT:
387 #endif
388 #ifdef MON_THOUSANDS_SEP
389 case MON_THOUSANDS_SEP:
390 #endif
391 #ifdef MON_GROUPING
392 case MON_GROUPING:
393 #endif
394 #ifdef POSITIVE_SIGN
395 case POSITIVE_SIGN:
396 #endif
397 #ifdef NEGATIVE_SIGN
398 case NEGATIVE_SIGN:
399 #endif
400 #ifdef INT_FRAC_DIGITS
401 case INT_FRAC_DIGITS:
402 #endif
403 #ifdef FRAC_DIGITS
404 case FRAC_DIGITS:
405 #endif
406 #ifdef P_CS_PRECEDES
407 case P_CS_PRECEDES:
408 #endif
409 #ifdef P_SEP_BY_SPACE
410 case P_SEP_BY_SPACE:
411 #endif
412 #ifdef N_CS_PRECEDES
413 case N_CS_PRECEDES:
414 #endif
415 #ifdef N_SEP_BY_SPACE
416 case N_SEP_BY_SPACE:
417 #endif
418 #ifdef P_SIGN_POSN
419 case P_SIGN_POSN:
420 #endif
421 #ifdef N_SIGN_POSN
422 case N_SIGN_POSN:
423 #endif
424 #ifdef DECIMAL_POINT
425 case DECIMAL_POINT:
426 #elif defined(RADIXCHAR)
427 case RADIXCHAR:
428 #endif
429 #ifdef THOUSANDS_SEP
430 case THOUSANDS_SEP:
431 #elif defined(THOUSEP)
432 case THOUSEP:
433 #endif
434 #ifdef GROUPING
435 case GROUPING:
436 #endif
437 #ifdef YESEXPR
438 case YESEXPR:
439 #endif
440 #ifdef NOEXPR
441 case NOEXPR:
442 #endif
443 #ifdef YESSTR
444 case YESSTR:
445 #endif
446 #ifdef NOSTR
447 case NOSTR:
448 #endif
449 #ifdef CODESET
450 case CODESET:
451 #endif
452 break;
453 default:
454 php_error_docref(NULL, E_WARNING, "Item '" ZEND_LONG_FMT "' is not valid", item);
455 RETURN_FALSE;
456 }
457 /* }}} */
458
459 value = nl_langinfo(item);
460 if (value == NULL) {
461 RETURN_FALSE;
462 } else {
463 RETURN_STRING(value);
464 }
465 }
466 #endif
467 /* }}} */
468
469 /* {{{ Compares two strings using the current locale */
PHP_FUNCTION(strcoll)470 PHP_FUNCTION(strcoll)
471 {
472 zend_string *s1, *s2;
473
474 ZEND_PARSE_PARAMETERS_START(2, 2)
475 Z_PARAM_STR(s1)
476 Z_PARAM_STR(s2)
477 ZEND_PARSE_PARAMETERS_END();
478
479 RETURN_LONG(strcoll((const char *) ZSTR_VAL(s1),
480 (const char *) ZSTR_VAL(s2)));
481 }
482 /* }}} */
483
484 /* {{{ php_charmask
485 * Fills a 256-byte bytemask with input. You can specify a range like 'a..z',
486 * it needs to be incrementing.
487 * Returns: FAILURE/SUCCESS whether the input was correct (i.e. no range errors)
488 */
php_charmask(const unsigned char * input,size_t len,char * mask)489 static inline zend_result php_charmask(const unsigned char *input, size_t len, char *mask)
490 {
491 const unsigned char *end;
492 unsigned char c;
493 zend_result result = SUCCESS;
494
495 memset(mask, 0, 256);
496 for (end = input+len; input < end; input++) {
497 c=*input;
498 if ((input+3 < end) && input[1] == '.' && input[2] == '.'
499 && input[3] >= c) {
500 memset(mask+c, 1, input[3] - c + 1);
501 input+=3;
502 } else if ((input+1 < end) && input[0] == '.' && input[1] == '.') {
503 /* Error, try to be as helpful as possible:
504 (a range ending/starting with '.' won't be captured here) */
505 if (end-len >= input) { /* there was no 'left' char */
506 php_error_docref(NULL, E_WARNING, "Invalid '..'-range, no character to the left of '..'");
507 result = FAILURE;
508 continue;
509 }
510 if (input+2 >= end) { /* there is no 'right' char */
511 php_error_docref(NULL, E_WARNING, "Invalid '..'-range, no character to the right of '..'");
512 result = FAILURE;
513 continue;
514 }
515 if (input[-1] > input[2]) { /* wrong order */
516 php_error_docref(NULL, E_WARNING, "Invalid '..'-range, '..'-range needs to be incrementing");
517 result = FAILURE;
518 continue;
519 }
520 /* FIXME: better error (a..b..c is the only left possibility?) */
521 php_error_docref(NULL, E_WARNING, "Invalid '..'-range");
522 result = FAILURE;
523 continue;
524 } else {
525 mask[c]=1;
526 }
527 }
528 return result;
529 }
530 /* }}} */
531
532 /* {{{ php_trim_int()
533 * mode 1 : trim left
534 * mode 2 : trim right
535 * mode 3 : trim left and right
536 * what indicates which chars are to be trimmed. NULL->default (' \t\n\r\v\0')
537 */
php_trim_int(zend_string * str,const char * what,size_t what_len,int mode)538 static zend_always_inline zend_string *php_trim_int(zend_string *str, const char *what, size_t what_len, int mode)
539 {
540 const char *start = ZSTR_VAL(str);
541 const char *end = start + ZSTR_LEN(str);
542 char mask[256];
543
544 if (what) {
545 if (what_len == 1) {
546 char p = *what;
547 if (mode & 1) {
548 while (start != end) {
549 if (*start == p) {
550 start++;
551 } else {
552 break;
553 }
554 }
555 }
556 if (mode & 2) {
557 while (start != end) {
558 if (*(end-1) == p) {
559 end--;
560 } else {
561 break;
562 }
563 }
564 }
565 } else {
566 php_charmask((const unsigned char *) what, what_len, mask);
567
568 if (mode & 1) {
569 while (start != end) {
570 if (mask[(unsigned char)*start]) {
571 start++;
572 } else {
573 break;
574 }
575 }
576 }
577 if (mode & 2) {
578 while (start != end) {
579 if (mask[(unsigned char)*(end-1)]) {
580 end--;
581 } else {
582 break;
583 }
584 }
585 }
586 }
587 } else {
588 if (mode & 1) {
589 while (start != end) {
590 unsigned char c = (unsigned char)*start;
591
592 if (c <= ' ' &&
593 (c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\v' || c == '\0')) {
594 start++;
595 } else {
596 break;
597 }
598 }
599 }
600 if (mode & 2) {
601 while (start != end) {
602 unsigned char c = (unsigned char)*(end-1);
603
604 if (c <= ' ' &&
605 (c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\v' || c == '\0')) {
606 end--;
607 } else {
608 break;
609 }
610 }
611 }
612 }
613
614 if (ZSTR_LEN(str) == end - start) {
615 return zend_string_copy(str);
616 } else if (end - start == 0) {
617 return ZSTR_EMPTY_ALLOC();
618 } else {
619 return zend_string_init(start, end - start, 0);
620 }
621 }
622 /* }}} */
623
624 /* {{{ php_trim_int()
625 * mode 1 : trim left
626 * mode 2 : trim right
627 * mode 3 : trim left and right
628 * what indicates which chars are to be trimmed. NULL->default (' \t\n\r\v\0')
629 */
php_trim(zend_string * str,const char * what,size_t what_len,int mode)630 PHPAPI zend_string *php_trim(zend_string *str, const char *what, size_t what_len, int mode)
631 {
632 return php_trim_int(str, what, what_len, mode);
633 }
634 /* }}} */
635
636 /* {{{ php_do_trim
637 * Base for trim(), rtrim() and ltrim() functions.
638 */
php_do_trim(INTERNAL_FUNCTION_PARAMETERS,int mode)639 static zend_always_inline void php_do_trim(INTERNAL_FUNCTION_PARAMETERS, int mode)
640 {
641 zend_string *str;
642 zend_string *what = NULL;
643
644 ZEND_PARSE_PARAMETERS_START(1, 2)
645 Z_PARAM_STR(str)
646 Z_PARAM_OPTIONAL
647 Z_PARAM_STR(what)
648 ZEND_PARSE_PARAMETERS_END();
649
650 ZVAL_STR(return_value, php_trim_int(str, (what ? ZSTR_VAL(what) : NULL), (what ? ZSTR_LEN(what) : 0), mode));
651 }
652 /* }}} */
653
654 /* {{{ Strips whitespace from the beginning and end of a string */
PHP_FUNCTION(trim)655 PHP_FUNCTION(trim)
656 {
657 php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3);
658 }
659 /* }}} */
660
661 /* {{{ Removes trailing whitespace */
PHP_FUNCTION(rtrim)662 PHP_FUNCTION(rtrim)
663 {
664 php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 2);
665 }
666 /* }}} */
667
668 /* {{{ Strips whitespace from the beginning of a string */
PHP_FUNCTION(ltrim)669 PHP_FUNCTION(ltrim)
670 {
671 php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
672 }
673 /* }}} */
674
675 /* {{{ Wraps buffer to selected number of characters using string break char */
PHP_FUNCTION(wordwrap)676 PHP_FUNCTION(wordwrap)
677 {
678 zend_string *text;
679 char *breakchar = "\n";
680 size_t newtextlen, chk, breakchar_len = 1;
681 size_t alloced;
682 zend_long current = 0, laststart = 0, lastspace = 0;
683 zend_long linelength = 75;
684 bool docut = 0;
685 zend_string *newtext;
686
687 ZEND_PARSE_PARAMETERS_START(1, 4)
688 Z_PARAM_STR(text)
689 Z_PARAM_OPTIONAL
690 Z_PARAM_LONG(linelength)
691 Z_PARAM_STRING(breakchar, breakchar_len)
692 Z_PARAM_BOOL(docut)
693 ZEND_PARSE_PARAMETERS_END();
694
695 if (ZSTR_LEN(text) == 0) {
696 RETURN_EMPTY_STRING();
697 }
698
699 if (breakchar_len == 0) {
700 zend_argument_value_error(3, "cannot be empty");
701 RETURN_THROWS();
702 }
703
704 if (linelength == 0 && docut) {
705 zend_argument_value_error(4, "cannot be true when argument #2 ($width) is 0");
706 RETURN_THROWS();
707 }
708
709 /* Special case for a single-character break as it needs no
710 additional storage space */
711 if (breakchar_len == 1 && !docut) {
712 newtext = zend_string_init(ZSTR_VAL(text), ZSTR_LEN(text), 0);
713
714 laststart = lastspace = 0;
715 for (current = 0; current < (zend_long)ZSTR_LEN(text); current++) {
716 if (ZSTR_VAL(text)[current] == breakchar[0]) {
717 laststart = lastspace = current + 1;
718 } else if (ZSTR_VAL(text)[current] == ' ') {
719 if (current - laststart >= linelength) {
720 ZSTR_VAL(newtext)[current] = breakchar[0];
721 laststart = current + 1;
722 }
723 lastspace = current;
724 } else if (current - laststart >= linelength && laststart != lastspace) {
725 ZSTR_VAL(newtext)[lastspace] = breakchar[0];
726 laststart = lastspace + 1;
727 }
728 }
729
730 RETURN_NEW_STR(newtext);
731 } else {
732 /* Multiple character line break or forced cut */
733 if (linelength > 0) {
734 chk = (size_t)(ZSTR_LEN(text)/linelength + 1);
735 newtext = zend_string_safe_alloc(chk, breakchar_len, ZSTR_LEN(text), 0);
736 alloced = ZSTR_LEN(text) + chk * breakchar_len + 1;
737 } else {
738 chk = ZSTR_LEN(text);
739 alloced = ZSTR_LEN(text) * (breakchar_len + 1) + 1;
740 newtext = zend_string_safe_alloc(ZSTR_LEN(text), breakchar_len + 1, 0, 0);
741 }
742
743 /* now keep track of the actual new text length */
744 newtextlen = 0;
745
746 laststart = lastspace = 0;
747 for (current = 0; current < (zend_long)ZSTR_LEN(text); current++) {
748 if (chk == 0) {
749 alloced += (size_t) (((ZSTR_LEN(text) - current + 1)/linelength + 1) * breakchar_len) + 1;
750 newtext = zend_string_extend(newtext, alloced, 0);
751 chk = (size_t) ((ZSTR_LEN(text) - current)/linelength) + 1;
752 }
753 /* when we hit an existing break, copy to new buffer, and
754 * fix up laststart and lastspace */
755 if (ZSTR_VAL(text)[current] == breakchar[0]
756 && current + breakchar_len < ZSTR_LEN(text)
757 && !strncmp(ZSTR_VAL(text) + current, breakchar, breakchar_len)) {
758 memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart + breakchar_len);
759 newtextlen += current - laststart + breakchar_len;
760 current += breakchar_len - 1;
761 laststart = lastspace = current + 1;
762 chk--;
763 }
764 /* if it is a space, check if it is at the line boundary,
765 * copy and insert a break, or just keep track of it */
766 else if (ZSTR_VAL(text)[current] == ' ') {
767 if (current - laststart >= linelength) {
768 memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart);
769 newtextlen += current - laststart;
770 memcpy(ZSTR_VAL(newtext) + newtextlen, breakchar, breakchar_len);
771 newtextlen += breakchar_len;
772 laststart = current + 1;
773 chk--;
774 }
775 lastspace = current;
776 }
777 /* if we are cutting, and we've accumulated enough
778 * characters, and we haven't see a space for this line,
779 * copy and insert a break. */
780 else if (current - laststart >= linelength
781 && docut && laststart >= lastspace) {
782 memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart);
783 newtextlen += current - laststart;
784 memcpy(ZSTR_VAL(newtext) + newtextlen, breakchar, breakchar_len);
785 newtextlen += breakchar_len;
786 laststart = lastspace = current;
787 chk--;
788 }
789 /* if the current word puts us over the linelength, copy
790 * back up until the last space, insert a break, and move
791 * up the laststart */
792 else if (current - laststart >= linelength
793 && laststart < lastspace) {
794 memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, lastspace - laststart);
795 newtextlen += lastspace - laststart;
796 memcpy(ZSTR_VAL(newtext) + newtextlen, breakchar, breakchar_len);
797 newtextlen += breakchar_len;
798 laststart = lastspace = lastspace + 1;
799 chk--;
800 }
801 }
802
803 /* copy over any stragglers */
804 if (laststart != current) {
805 memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart);
806 newtextlen += current - laststart;
807 }
808
809 ZSTR_VAL(newtext)[newtextlen] = '\0';
810 /* free unused memory */
811 newtext = zend_string_truncate(newtext, newtextlen, 0);
812
813 RETURN_NEW_STR(newtext);
814 }
815 }
816 /* }}} */
817
818 /* {{{ php_explode */
php_explode(const zend_string * delim,zend_string * str,zval * return_value,zend_long limit)819 PHPAPI void php_explode(const zend_string *delim, zend_string *str, zval *return_value, zend_long limit)
820 {
821 const char *p1 = ZSTR_VAL(str);
822 const char *endp = ZSTR_VAL(str) + ZSTR_LEN(str);
823 const char *p2 = php_memnstr(ZSTR_VAL(str), ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
824 zval tmp;
825
826 if (p2 == NULL) {
827 ZVAL_STR_COPY(&tmp, str);
828 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
829 } else {
830 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
831 ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
832 do {
833 ZEND_HASH_FILL_GROW();
834 ZEND_HASH_FILL_SET_STR(zend_string_init_fast(p1, p2 - p1));
835 ZEND_HASH_FILL_NEXT();
836 p1 = p2 + ZSTR_LEN(delim);
837 p2 = php_memnstr(p1, ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
838 } while (p2 != NULL && --limit > 1);
839
840 if (p1 <= endp) {
841 ZEND_HASH_FILL_GROW();
842 ZEND_HASH_FILL_SET_STR(zend_string_init_fast(p1, endp - p1));
843 ZEND_HASH_FILL_NEXT();
844 }
845 } ZEND_HASH_FILL_END();
846 }
847 }
848 /* }}} */
849
850 /* {{{ php_explode_negative_limit */
php_explode_negative_limit(const zend_string * delim,zend_string * str,zval * return_value,zend_long limit)851 PHPAPI void php_explode_negative_limit(const zend_string *delim, zend_string *str, zval *return_value, zend_long limit)
852 {
853 #define EXPLODE_ALLOC_STEP 64
854 const char *p1 = ZSTR_VAL(str);
855 const char *endp = ZSTR_VAL(str) + ZSTR_LEN(str);
856 const char *p2 = php_memnstr(ZSTR_VAL(str), ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
857 zval tmp;
858
859 if (p2 == NULL) {
860 /*
861 do nothing since limit <= -1, thus if only one chunk - 1 + (limit) <= 0
862 by doing nothing we return empty array
863 */
864 } else {
865 size_t allocated = EXPLODE_ALLOC_STEP, found = 0;
866 zend_long i, to_return;
867 const char **positions = emalloc(allocated * sizeof(char *));
868
869 positions[found++] = p1;
870 do {
871 if (found >= allocated) {
872 allocated = found + EXPLODE_ALLOC_STEP;/* make sure we have enough memory */
873 positions = erealloc(ZEND_VOIDP(positions), allocated*sizeof(char *));
874 }
875 positions[found++] = p1 = p2 + ZSTR_LEN(delim);
876 p2 = php_memnstr(p1, ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
877 } while (p2 != NULL);
878
879 to_return = limit + found;
880 /* limit is at least -1 therefore no need of bounds checking : i will be always less than found */
881 for (i = 0; i < to_return; i++) { /* this checks also for to_return > 0 */
882 ZVAL_STRINGL(&tmp, positions[i], (positions[i+1] - ZSTR_LEN(delim)) - positions[i]);
883 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
884 }
885 efree((void *)positions);
886 }
887 #undef EXPLODE_ALLOC_STEP
888 }
889 /* }}} */
890
891 /* {{{ 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)892 PHP_FUNCTION(explode)
893 {
894 zend_string *str, *delim;
895 zend_long limit = ZEND_LONG_MAX; /* No limit */
896 zval tmp;
897
898 ZEND_PARSE_PARAMETERS_START(2, 3)
899 Z_PARAM_STR(delim)
900 Z_PARAM_STR(str)
901 Z_PARAM_OPTIONAL
902 Z_PARAM_LONG(limit)
903 ZEND_PARSE_PARAMETERS_END();
904
905 if (ZSTR_LEN(delim) == 0) {
906 zend_argument_value_error(1, "cannot be empty");
907 RETURN_THROWS();
908 }
909
910 array_init(return_value);
911
912 if (ZSTR_LEN(str) == 0) {
913 if (limit >= 0) {
914 ZVAL_EMPTY_STRING(&tmp);
915 zend_hash_index_add_new(Z_ARRVAL_P(return_value), 0, &tmp);
916 }
917 return;
918 }
919
920 if (limit > 1) {
921 php_explode(delim, str, return_value, limit);
922 } else if (limit < 0) {
923 php_explode_negative_limit(delim, str, return_value, limit);
924 } else {
925 ZVAL_STR_COPY(&tmp, str);
926 zend_hash_index_add_new(Z_ARRVAL_P(return_value), 0, &tmp);
927 }
928 }
929 /* }}} */
930
931 /* {{{ php_implode */
php_implode(const zend_string * glue,HashTable * pieces,zval * return_value)932 PHPAPI void php_implode(const zend_string *glue, HashTable *pieces, zval *return_value)
933 {
934 zval *tmp;
935 uint32_t numelems;
936 zend_string *str;
937 char *cptr;
938 size_t len = 0;
939 struct {
940 zend_string *str;
941 zend_long lval;
942 } *strings, *ptr;
943 ALLOCA_FLAG(use_heap)
944
945 numelems = zend_hash_num_elements(pieces);
946
947 if (numelems == 0) {
948 RETURN_EMPTY_STRING();
949 } else if (numelems == 1) {
950 /* loop to search the first not undefined element... */
951 ZEND_HASH_FOREACH_VAL(pieces, tmp) {
952 RETURN_STR(zval_get_string(tmp));
953 } ZEND_HASH_FOREACH_END();
954 }
955
956 ptr = strings = do_alloca((sizeof(*strings)) * numelems, use_heap);
957
958 uint32_t flags = ZSTR_GET_COPYABLE_CONCAT_PROPERTIES(glue);
959
960 ZEND_HASH_FOREACH_VAL(pieces, tmp) {
961 if (EXPECTED(Z_TYPE_P(tmp) == IS_STRING)) {
962 ptr->str = Z_STR_P(tmp);
963 len += ZSTR_LEN(ptr->str);
964 ptr->lval = 0;
965 flags &= ZSTR_GET_COPYABLE_CONCAT_PROPERTIES(ptr->str);
966 ptr++;
967 } else if (UNEXPECTED(Z_TYPE_P(tmp) == IS_LONG)) {
968 zend_long val = Z_LVAL_P(tmp);
969
970 ptr->str = NULL;
971 ptr->lval = val;
972 ptr++;
973 if (val <= 0) {
974 len++;
975 }
976 while (val) {
977 val /= 10;
978 len++;
979 }
980 } else {
981 ptr->str = zval_get_string_func(tmp);
982 len += ZSTR_LEN(ptr->str);
983 ptr->lval = 1;
984 flags &= ZSTR_GET_COPYABLE_CONCAT_PROPERTIES(ptr->str);
985 ptr++;
986 }
987 } ZEND_HASH_FOREACH_END();
988
989 /* numelems cannot be 0, we checked above */
990 str = zend_string_safe_alloc(numelems - 1, ZSTR_LEN(glue), len, 0);
991 GC_ADD_FLAGS(str, flags);
992 cptr = ZSTR_VAL(str) + ZSTR_LEN(str);
993 *cptr = 0;
994
995 while (1) {
996 ptr--;
997 if (EXPECTED(ptr->str)) {
998 cptr -= ZSTR_LEN(ptr->str);
999 memcpy(cptr, ZSTR_VAL(ptr->str), ZSTR_LEN(ptr->str));
1000 if (ptr->lval) {
1001 zend_string_release_ex(ptr->str, 0);
1002 }
1003 } else {
1004 char *oldPtr = cptr;
1005 char oldVal = *cptr;
1006 cptr = zend_print_long_to_buf(cptr, ptr->lval);
1007 *oldPtr = oldVal;
1008 }
1009
1010 if (ptr == strings) {
1011 break;
1012 }
1013
1014 cptr -= ZSTR_LEN(glue);
1015 memcpy(cptr, ZSTR_VAL(glue), ZSTR_LEN(glue));
1016 }
1017
1018 free_alloca(strings, use_heap);
1019 RETURN_NEW_STR(str);
1020 }
1021 /* }}} */
1022
1023 /* {{{ Joins array elements placing glue string between items and return one string */
PHP_FUNCTION(implode)1024 PHP_FUNCTION(implode)
1025 {
1026 zend_string *arg1_str = NULL;
1027 HashTable *arg1_array = NULL;
1028 zend_array *pieces = NULL;
1029
1030 ZEND_PARSE_PARAMETERS_START(1, 2)
1031 Z_PARAM_ARRAY_HT_OR_STR(arg1_array, arg1_str)
1032 Z_PARAM_OPTIONAL
1033 Z_PARAM_ARRAY_HT_OR_NULL(pieces)
1034 ZEND_PARSE_PARAMETERS_END();
1035
1036 if (pieces == NULL) {
1037 if (arg1_array == NULL) {
1038 zend_type_error("%s(): Argument #1 ($pieces) must be of type array, string given", get_active_function_name());
1039 RETURN_THROWS();
1040 }
1041
1042 arg1_str = ZSTR_EMPTY_ALLOC();
1043 pieces = arg1_array;
1044 } else {
1045 if (arg1_str == NULL) {
1046 zend_argument_type_error(1, "must be of type string, array given");
1047 RETURN_THROWS();
1048 }
1049 }
1050
1051 php_implode(arg1_str, pieces, return_value);
1052 }
1053 /* }}} */
1054
1055 #define STRTOK_TABLE(p) BG(strtok_table)[(unsigned char) *p]
1056
1057 /* {{{ Tokenize a string */
PHP_FUNCTION(strtok)1058 PHP_FUNCTION(strtok)
1059 {
1060 zend_string *str, *tok = NULL;
1061 char *token;
1062 char *token_end;
1063 char *p;
1064 char *pe;
1065 size_t skipped = 0;
1066
1067 ZEND_PARSE_PARAMETERS_START(1, 2)
1068 Z_PARAM_STR(str)
1069 Z_PARAM_OPTIONAL
1070 Z_PARAM_STR_OR_NULL(tok)
1071 ZEND_PARSE_PARAMETERS_END();
1072
1073 if (!tok) {
1074 tok = str;
1075 } else {
1076 if (BG(strtok_string)) {
1077 zend_string_release(BG(strtok_string));
1078 }
1079 BG(strtok_string) = zend_string_copy(str);
1080 BG(strtok_last) = ZSTR_VAL(str);
1081 BG(strtok_len) = ZSTR_LEN(str);
1082 }
1083
1084 if (!BG(strtok_string)) {
1085 /* String to tokenize not set. */
1086 php_error_docref(NULL, E_WARNING, "Both arguments must be provided when starting tokenization");
1087 RETURN_FALSE;
1088 }
1089
1090 p = BG(strtok_last); /* Where we start to search */
1091 pe = ZSTR_VAL(BG(strtok_string)) + BG(strtok_len);
1092 if (p >= pe) {
1093 /* Reached the end of the string. */
1094 RETURN_FALSE;
1095 }
1096
1097 token = ZSTR_VAL(tok);
1098 token_end = token + ZSTR_LEN(tok);
1099
1100 while (token < token_end) {
1101 STRTOK_TABLE(token++) = 1;
1102 }
1103
1104 /* Skip leading delimiters */
1105 while (STRTOK_TABLE(p)) {
1106 if (++p >= pe) {
1107 /* no other chars left */
1108 goto return_false;
1109 }
1110 skipped++;
1111 }
1112
1113 /* We know at this place that *p is no delimiter, so skip it */
1114 while (++p < pe) {
1115 if (STRTOK_TABLE(p)) {
1116 goto return_token;
1117 }
1118 }
1119
1120 if (p - BG(strtok_last)) {
1121 return_token:
1122 RETVAL_STRINGL(BG(strtok_last) + skipped, (p - BG(strtok_last)) - skipped);
1123 BG(strtok_last) = p + 1;
1124 } else {
1125 return_false:
1126 RETVAL_FALSE;
1127 zend_string_release(BG(strtok_string));
1128 BG(strtok_string) = NULL;
1129 }
1130
1131 /* Restore table -- usually faster then memset'ing the table on every invocation */
1132 token = ZSTR_VAL(tok);
1133 while (token < token_end) {
1134 STRTOK_TABLE(token++) = 0;
1135 }
1136 }
1137 /* }}} */
1138
1139 /* {{{ php_strtoupper */
php_strtoupper(char * s,size_t len)1140 PHPAPI char *php_strtoupper(char *s, size_t len)
1141 {
1142 zend_str_toupper(s, len);
1143 return s;
1144 }
1145 /* }}} */
1146
1147 /* {{{ php_string_toupper */
php_string_toupper(zend_string * s)1148 PHPAPI zend_string *php_string_toupper(zend_string *s)
1149 {
1150 return zend_string_toupper(s);
1151 }
1152 /* }}} */
1153
1154 /* {{{ Makes a string uppercase */
PHP_FUNCTION(strtoupper)1155 PHP_FUNCTION(strtoupper)
1156 {
1157 zend_string *arg;
1158
1159 ZEND_PARSE_PARAMETERS_START(1, 1)
1160 Z_PARAM_STR(arg)
1161 ZEND_PARSE_PARAMETERS_END();
1162
1163 RETURN_STR(zend_string_toupper(arg));
1164 }
1165 /* }}} */
1166
1167 /* {{{ php_strtolower */
php_strtolower(char * s,size_t len)1168 PHPAPI char *php_strtolower(char *s, size_t len)
1169 {
1170 zend_str_tolower(s, len);
1171 return s;
1172 }
1173 /* }}} */
1174
1175 /* {{{ php_string_tolower */
php_string_tolower(zend_string * s)1176 PHPAPI zend_string *php_string_tolower(zend_string *s)
1177 {
1178 return zend_string_tolower(s);
1179 }
1180 /* }}} */
1181
1182 /* {{{ Makes a string lowercase */
PHP_FUNCTION(strtolower)1183 PHP_FUNCTION(strtolower)
1184 {
1185 zend_string *str;
1186
1187 ZEND_PARSE_PARAMETERS_START(1, 1)
1188 Z_PARAM_STR(str)
1189 ZEND_PARSE_PARAMETERS_END();
1190
1191 RETURN_STR(zend_string_tolower(str));
1192 }
1193 /* }}} */
1194
1195 #if defined(PHP_WIN32)
_is_basename_start(const char * start,const char * pos)1196 static bool _is_basename_start(const char *start, const char *pos)
1197 {
1198 if (pos - start >= 1
1199 && *(pos-1) != '/'
1200 && *(pos-1) != '\\') {
1201 if (pos - start == 1) {
1202 return 1;
1203 } else if (*(pos-2) == '/' || *(pos-2) == '\\') {
1204 return 1;
1205 } else if (*(pos-2) == ':'
1206 && _is_basename_start(start, pos - 2)) {
1207 return 1;
1208 }
1209 }
1210 return 0;
1211 }
1212 #endif
1213
1214 /* {{{ php_basename */
php_basename(const char * s,size_t len,const char * suffix,size_t suffix_len)1215 PHPAPI zend_string *php_basename(const char *s, size_t len, const char *suffix, size_t suffix_len)
1216 {
1217 const char *basename_start;
1218 const char *basename_end;
1219
1220 if (CG(ascii_compatible_locale)) {
1221 basename_end = s + len - 1;
1222
1223 /* Strip trailing slashes */
1224 while (basename_end >= s
1225 #ifdef PHP_WIN32
1226 && (*basename_end == '/'
1227 || *basename_end == '\\'
1228 || (*basename_end == ':'
1229 && _is_basename_start(s, basename_end)))) {
1230 #else
1231 && *basename_end == '/') {
1232 #endif
1233 basename_end--;
1234 }
1235 if (basename_end < s) {
1236 return ZSTR_EMPTY_ALLOC();
1237 }
1238
1239 /* Extract filename */
1240 basename_start = basename_end;
1241 basename_end++;
1242 while (basename_start > s
1243 #ifdef PHP_WIN32
1244 && *(basename_start-1) != '/'
1245 && *(basename_start-1) != '\\') {
1246
1247 if (*(basename_start-1) == ':' &&
1248 _is_basename_start(s, basename_start - 1)) {
1249 break;
1250 }
1251 #else
1252 && *(basename_start-1) != '/') {
1253 #endif
1254 basename_start--;
1255 }
1256 } else {
1257 /* State 0 is directly after a directory separator (or at the start of the string).
1258 * State 1 is everything else. */
1259 int state = 0;
1260
1261 basename_start = s;
1262 basename_end = s;
1263 while (len > 0) {
1264 int inc_len = (*s == '\0' ? 1 : php_mblen(s, len));
1265
1266 switch (inc_len) {
1267 case 0:
1268 goto quit_loop;
1269 case 1:
1270 #ifdef PHP_WIN32
1271 if (*s == '/' || *s == '\\') {
1272 #else
1273 if (*s == '/') {
1274 #endif
1275 if (state == 1) {
1276 state = 0;
1277 basename_end = s;
1278 }
1279 #ifdef PHP_WIN32
1280 /* Catch relative paths in c:file.txt style. They're not to confuse
1281 with the NTFS streams. This part ensures also, that no drive
1282 letter traversing happens. */
1283 } else if ((*s == ':' && (s - basename_start == 1))) {
1284 if (state == 0) {
1285 basename_start = s;
1286 state = 1;
1287 } else {
1288 basename_end = s;
1289 state = 0;
1290 }
1291 #endif
1292 } else {
1293 if (state == 0) {
1294 basename_start = s;
1295 state = 1;
1296 }
1297 }
1298 break;
1299 default:
1300 if (inc_len < 0) {
1301 /* If character is invalid, treat it like other non-significant characters. */
1302 inc_len = 1;
1303 php_mb_reset();
1304 }
1305 if (state == 0) {
1306 basename_start = s;
1307 state = 1;
1308 }
1309 break;
1310 }
1311 s += inc_len;
1312 len -= inc_len;
1313 }
1314
1315 quit_loop:
1316 if (state == 1) {
1317 basename_end = s;
1318 }
1319 }
1320
1321 if (suffix != NULL && suffix_len < (size_t)(basename_end - basename_start) &&
1322 memcmp(basename_end - suffix_len, suffix, suffix_len) == 0) {
1323 basename_end -= suffix_len;
1324 }
1325
1326 return zend_string_init(basename_start, basename_end - basename_start, 0);
1327 }
1328 /* }}} */
1329
1330 /* {{{ Returns the filename component of the path */
1331 PHP_FUNCTION(basename)
1332 {
1333 char *string, *suffix = NULL;
1334 size_t string_len, suffix_len = 0;
1335
1336 ZEND_PARSE_PARAMETERS_START(1, 2)
1337 Z_PARAM_STRING(string, string_len)
1338 Z_PARAM_OPTIONAL
1339 Z_PARAM_STRING(suffix, suffix_len)
1340 ZEND_PARSE_PARAMETERS_END();
1341
1342 RETURN_STR(php_basename(string, string_len, suffix, suffix_len));
1343 }
1344 /* }}} */
1345
1346 /* {{{ php_dirname
1347 Returns directory name component of path */
1348 PHPAPI size_t php_dirname(char *path, size_t len)
1349 {
1350 return zend_dirname(path, len);
1351 }
1352 /* }}} */
1353
1354 /* {{{ Returns the directory name component of the path */
1355 PHP_FUNCTION(dirname)
1356 {
1357 char *str;
1358 size_t str_len;
1359 zend_string *ret;
1360 zend_long levels = 1;
1361
1362 ZEND_PARSE_PARAMETERS_START(1, 2)
1363 Z_PARAM_STRING(str, str_len)
1364 Z_PARAM_OPTIONAL
1365 Z_PARAM_LONG(levels)
1366 ZEND_PARSE_PARAMETERS_END();
1367
1368 ret = zend_string_init(str, str_len, 0);
1369
1370 if (levels == 1) {
1371 /* Default case */
1372 #ifdef PHP_WIN32
1373 ZSTR_LEN(ret) = php_win32_ioutil_dirname(ZSTR_VAL(ret), str_len);
1374 #else
1375 ZSTR_LEN(ret) = zend_dirname(ZSTR_VAL(ret), str_len);
1376 #endif
1377 } else if (levels < 1) {
1378 zend_argument_value_error(2, "must be greater than or equal to 1");
1379 zend_string_efree(ret);
1380 RETURN_THROWS();
1381 } else {
1382 /* Some levels up */
1383 do {
1384 #ifdef PHP_WIN32
1385 ZSTR_LEN(ret) = php_win32_ioutil_dirname(ZSTR_VAL(ret), str_len = ZSTR_LEN(ret));
1386 #else
1387 ZSTR_LEN(ret) = zend_dirname(ZSTR_VAL(ret), str_len = ZSTR_LEN(ret));
1388 #endif
1389 } while (ZSTR_LEN(ret) < str_len && --levels);
1390 }
1391
1392 RETURN_NEW_STR(ret);
1393 }
1394 /* }}} */
1395
1396 /* {{{ Returns information about a certain string */
1397 PHP_FUNCTION(pathinfo)
1398 {
1399 zval tmp;
1400 char *path, *dirname;
1401 size_t path_len;
1402 bool have_basename;
1403 zend_long opt = PHP_PATHINFO_ALL;
1404 zend_string *ret = NULL;
1405
1406 ZEND_PARSE_PARAMETERS_START(1, 2)
1407 Z_PARAM_STRING(path, path_len)
1408 Z_PARAM_OPTIONAL
1409 Z_PARAM_LONG(opt)
1410 ZEND_PARSE_PARAMETERS_END();
1411
1412 have_basename = ((opt & PHP_PATHINFO_BASENAME) == PHP_PATHINFO_BASENAME);
1413
1414 array_init(&tmp);
1415
1416 if ((opt & PHP_PATHINFO_DIRNAME) == PHP_PATHINFO_DIRNAME) {
1417 dirname = estrndup(path, path_len);
1418 php_dirname(dirname, path_len);
1419 if (*dirname) {
1420 add_assoc_string(&tmp, "dirname", dirname);
1421 }
1422 efree(dirname);
1423 }
1424
1425 if (have_basename) {
1426 ret = php_basename(path, path_len, NULL, 0);
1427 add_assoc_str(&tmp, "basename", zend_string_copy(ret));
1428 }
1429
1430 if ((opt & PHP_PATHINFO_EXTENSION) == PHP_PATHINFO_EXTENSION) {
1431 const char *p;
1432 ptrdiff_t idx;
1433
1434 if (!have_basename) {
1435 ret = php_basename(path, path_len, NULL, 0);
1436 }
1437
1438 p = zend_memrchr(ZSTR_VAL(ret), '.', ZSTR_LEN(ret));
1439
1440 if (p) {
1441 idx = p - ZSTR_VAL(ret);
1442 add_assoc_stringl(&tmp, "extension", ZSTR_VAL(ret) + idx + 1, ZSTR_LEN(ret) - idx - 1);
1443 }
1444 }
1445
1446 if ((opt & PHP_PATHINFO_FILENAME) == PHP_PATHINFO_FILENAME) {
1447 const char *p;
1448 ptrdiff_t idx;
1449
1450 /* Have we already looked up the basename? */
1451 if (!have_basename && !ret) {
1452 ret = php_basename(path, path_len, NULL, 0);
1453 }
1454
1455 p = zend_memrchr(ZSTR_VAL(ret), '.', ZSTR_LEN(ret));
1456
1457 idx = p ? (p - ZSTR_VAL(ret)) : (ptrdiff_t)ZSTR_LEN(ret);
1458 add_assoc_stringl(&tmp, "filename", ZSTR_VAL(ret), idx);
1459 }
1460
1461 if (ret) {
1462 zend_string_release_ex(ret, 0);
1463 }
1464
1465 if (opt == PHP_PATHINFO_ALL) {
1466 RETURN_COPY_VALUE(&tmp);
1467 } else {
1468 zval *element;
1469 if ((element = zend_hash_get_current_data(Z_ARRVAL(tmp))) != NULL) {
1470 RETVAL_COPY_DEREF(element);
1471 } else {
1472 RETVAL_EMPTY_STRING();
1473 }
1474 zval_ptr_dtor(&tmp);
1475 }
1476 }
1477 /* }}} */
1478
1479 /* {{{ php_stristr
1480 case insensitive strstr */
1481 PHPAPI char *php_stristr(char *s, char *t, size_t s_len, size_t t_len)
1482 {
1483 return (char*)php_memnistr(s, t, t_len, s + s_len);
1484 }
1485 /* }}} */
1486
1487 /* {{{ php_strspn */
1488 PHPAPI size_t php_strspn(const char *s1, const char *s2, const char *s1_end, const char *s2_end)
1489 {
1490 const char *p = s1, *spanp;
1491 char c = *p;
1492
1493 cont:
1494 for (spanp = s2; p != s1_end && spanp != s2_end;) {
1495 if (*spanp++ == c) {
1496 c = *(++p);
1497 goto cont;
1498 }
1499 }
1500 return (p - s1);
1501 }
1502 /* }}} */
1503
1504 /* {{{ php_strcspn */
1505 PHPAPI size_t php_strcspn(const char *s1, const char *s2, const char *s1_end, const char *s2_end)
1506 {
1507 const char *p, *spanp;
1508 char c = *s1;
1509
1510 for (p = s1;;) {
1511 spanp = s2;
1512 do {
1513 if (*spanp == c || p == s1_end) {
1514 return p - s1;
1515 }
1516 } while (spanp++ < (s2_end - 1));
1517 c = *++p;
1518 }
1519 /* NOTREACHED */
1520 }
1521 /* }}} */
1522
1523 /* {{{ Finds first occurrence of a string within another, case insensitive */
1524 PHP_FUNCTION(stristr)
1525 {
1526 zend_string *haystack, *needle;
1527 const char *found = NULL;
1528 size_t found_offset;
1529 bool part = 0;
1530
1531 ZEND_PARSE_PARAMETERS_START(2, 3)
1532 Z_PARAM_STR(haystack)
1533 Z_PARAM_STR(needle)
1534 Z_PARAM_OPTIONAL
1535 Z_PARAM_BOOL(part)
1536 ZEND_PARSE_PARAMETERS_END();
1537
1538 found = php_stristr(ZSTR_VAL(haystack), ZSTR_VAL(needle), ZSTR_LEN(haystack), ZSTR_LEN(needle));
1539
1540 if (UNEXPECTED(!found)) {
1541 RETURN_FALSE;
1542 }
1543 found_offset = found - ZSTR_VAL(haystack);
1544 if (part) {
1545 RETURN_STRINGL(ZSTR_VAL(haystack), found_offset);
1546 }
1547 RETURN_STRINGL(ZSTR_VAL(haystack) + found_offset, ZSTR_LEN(haystack) - found_offset);
1548 }
1549 /* }}} */
1550
1551 /* {{{ Finds first occurrence of a string within another */
1552 PHP_FUNCTION(strstr)
1553 {
1554 zend_string *haystack, *needle;
1555 const char *found = NULL;
1556 zend_long found_offset;
1557 bool part = 0;
1558
1559 ZEND_PARSE_PARAMETERS_START(2, 3)
1560 Z_PARAM_STR(haystack)
1561 Z_PARAM_STR(needle)
1562 Z_PARAM_OPTIONAL
1563 Z_PARAM_BOOL(part)
1564 ZEND_PARSE_PARAMETERS_END();
1565
1566 found = php_memnstr(ZSTR_VAL(haystack), ZSTR_VAL(needle), ZSTR_LEN(needle), ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1567
1568 if (UNEXPECTED(!found)) {
1569 RETURN_FALSE;
1570 }
1571 found_offset = found - ZSTR_VAL(haystack);
1572 if (part) {
1573 RETURN_STRINGL(ZSTR_VAL(haystack), found_offset);
1574 }
1575 RETURN_STRINGL(ZSTR_VAL(haystack) + found_offset, ZSTR_LEN(haystack) - found_offset);
1576 }
1577 /* }}} */
1578
1579 /* {{{ Checks if a string contains another */
1580 PHP_FUNCTION(str_contains)
1581 {
1582 zend_string *haystack, *needle;
1583
1584 ZEND_PARSE_PARAMETERS_START(2, 2)
1585 Z_PARAM_STR(haystack)
1586 Z_PARAM_STR(needle)
1587 ZEND_PARSE_PARAMETERS_END();
1588
1589 RETURN_BOOL(php_memnstr(ZSTR_VAL(haystack), ZSTR_VAL(needle), ZSTR_LEN(needle), ZSTR_VAL(haystack) + ZSTR_LEN(haystack)));
1590 }
1591 /* }}} */
1592
1593 /* {{{ Checks if haystack starts with needle */
1594 PHP_FUNCTION(str_starts_with)
1595 {
1596 zend_string *haystack, *needle;
1597
1598 ZEND_PARSE_PARAMETERS_START(2, 2)
1599 Z_PARAM_STR(haystack)
1600 Z_PARAM_STR(needle)
1601 ZEND_PARSE_PARAMETERS_END();
1602
1603 RETURN_BOOL(zend_string_starts_with(haystack, needle));
1604 }
1605 /* }}} */
1606
1607 /* {{{ Checks if haystack ends with needle */
1608 PHP_FUNCTION(str_ends_with)
1609 {
1610 zend_string *haystack, *needle;
1611
1612 ZEND_PARSE_PARAMETERS_START(2, 2)
1613 Z_PARAM_STR(haystack)
1614 Z_PARAM_STR(needle)
1615 ZEND_PARSE_PARAMETERS_END();
1616
1617 if (ZSTR_LEN(needle) > ZSTR_LEN(haystack)) {
1618 RETURN_FALSE;
1619 }
1620
1621 RETURN_BOOL(memcmp(
1622 ZSTR_VAL(haystack) + ZSTR_LEN(haystack) - ZSTR_LEN(needle),
1623 ZSTR_VAL(needle), ZSTR_LEN(needle)) == 0);
1624 }
1625 /* }}} */
1626
1627 /* {{{ Finds position of first occurrence of a string within another */
1628 PHP_FUNCTION(strpos)
1629 {
1630 zend_string *haystack, *needle;
1631 const char *found = NULL;
1632 zend_long offset = 0;
1633
1634 ZEND_PARSE_PARAMETERS_START(2, 3)
1635 Z_PARAM_STR(haystack)
1636 Z_PARAM_STR(needle)
1637 Z_PARAM_OPTIONAL
1638 Z_PARAM_LONG(offset)
1639 ZEND_PARSE_PARAMETERS_END();
1640
1641 if (offset < 0) {
1642 offset += (zend_long)ZSTR_LEN(haystack);
1643 }
1644 if (offset < 0 || (size_t)offset > ZSTR_LEN(haystack)) {
1645 zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
1646 RETURN_THROWS();
1647 }
1648
1649 found = (char*)php_memnstr(ZSTR_VAL(haystack) + offset,
1650 ZSTR_VAL(needle), ZSTR_LEN(needle),
1651 ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1652
1653 if (UNEXPECTED(!found)) {
1654 RETURN_FALSE;
1655 }
1656 RETURN_LONG(found - ZSTR_VAL(haystack));
1657 }
1658 /* }}} */
1659
1660 /* {{{ Finds position of first occurrence of a string within another, case insensitive */
1661 PHP_FUNCTION(stripos)
1662 {
1663 const char *found = NULL;
1664 zend_string *haystack, *needle;
1665 zend_long offset = 0;
1666
1667 ZEND_PARSE_PARAMETERS_START(2, 3)
1668 Z_PARAM_STR(haystack)
1669 Z_PARAM_STR(needle)
1670 Z_PARAM_OPTIONAL
1671 Z_PARAM_LONG(offset)
1672 ZEND_PARSE_PARAMETERS_END();
1673
1674 if (offset < 0) {
1675 offset += (zend_long)ZSTR_LEN(haystack);
1676 }
1677 if (offset < 0 || (size_t)offset > ZSTR_LEN(haystack)) {
1678 zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
1679 RETURN_THROWS();
1680 }
1681
1682 found = (char*)php_memnistr(ZSTR_VAL(haystack) + offset,
1683 ZSTR_VAL(needle), ZSTR_LEN(needle), ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
1684
1685 if (UNEXPECTED(!found)) {
1686 RETURN_FALSE;
1687 }
1688 RETURN_LONG(found - ZSTR_VAL(haystack));
1689 }
1690 /* }}} */
1691
1692 /* {{{ Finds position of last occurrence of a string within another string */
1693 PHP_FUNCTION(strrpos)
1694 {
1695 zend_string *needle;
1696 zend_string *haystack;
1697 zend_long offset = 0;
1698 const char *p, *e, *found;
1699
1700 ZEND_PARSE_PARAMETERS_START(2, 3)
1701 Z_PARAM_STR(haystack)
1702 Z_PARAM_STR(needle)
1703 Z_PARAM_OPTIONAL
1704 Z_PARAM_LONG(offset)
1705 ZEND_PARSE_PARAMETERS_END();
1706
1707 if (offset >= 0) {
1708 if ((size_t)offset > ZSTR_LEN(haystack)) {
1709 zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
1710 RETURN_THROWS();
1711 }
1712 p = ZSTR_VAL(haystack) + (size_t)offset;
1713 e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
1714 } else {
1715 if (offset < -ZEND_LONG_MAX || (size_t)(-offset) > ZSTR_LEN(haystack)) {
1716 zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
1717 RETURN_THROWS();
1718 }
1719
1720 p = ZSTR_VAL(haystack);
1721 if ((size_t)-offset < ZSTR_LEN(needle)) {
1722 e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
1723 } else {
1724 e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack) + offset + ZSTR_LEN(needle);
1725 }
1726 }
1727
1728 found = zend_memnrstr(p, ZSTR_VAL(needle), ZSTR_LEN(needle), e);
1729
1730 if (UNEXPECTED(!found)) {
1731 RETURN_FALSE;
1732 }
1733 RETURN_LONG(found - ZSTR_VAL(haystack));
1734 }
1735 /* }}} */
1736
1737 /* {{{ Finds position of last occurrence of a string within another string */
1738 PHP_FUNCTION(strripos)
1739 {
1740 zend_string *needle;
1741 zend_string *haystack;
1742 zend_long offset = 0;
1743 const char *p, *e, *found;
1744 zend_string *needle_dup, *haystack_dup;
1745
1746 ZEND_PARSE_PARAMETERS_START(2, 3)
1747 Z_PARAM_STR(haystack)
1748 Z_PARAM_STR(needle)
1749 Z_PARAM_OPTIONAL
1750 Z_PARAM_LONG(offset)
1751 ZEND_PARSE_PARAMETERS_END();
1752
1753 if (ZSTR_LEN(needle) == 1) {
1754 /* Single character search can shortcut memcmps
1755 Can also avoid tolower emallocs */
1756 char lowered;
1757 if (offset >= 0) {
1758 if ((size_t)offset > ZSTR_LEN(haystack)) {
1759 zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
1760 RETURN_THROWS();
1761 }
1762 p = ZSTR_VAL(haystack) + (size_t)offset;
1763 e = ZSTR_VAL(haystack) + ZSTR_LEN(haystack) - 1;
1764 } else {
1765 p = ZSTR_VAL(haystack);
1766 if (offset < -ZEND_LONG_MAX || (size_t)(-offset) > ZSTR_LEN(haystack)) {
1767 zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
1768 RETURN_THROWS();
1769 }
1770 e = ZSTR_VAL(haystack) + (ZSTR_LEN(haystack) + (size_t)offset);
1771 }
1772 lowered = zend_tolower_ascii(*ZSTR_VAL(needle));
1773 while (e >= p) {
1774 if (zend_tolower_ascii(*e) == lowered) {
1775 RETURN_LONG(e - p + (offset > 0 ? offset : 0));
1776 }
1777 e--;
1778 }
1779 RETURN_FALSE;
1780 }
1781
1782 haystack_dup = zend_string_tolower(haystack);
1783 if (offset >= 0) {
1784 if ((size_t)offset > ZSTR_LEN(haystack)) {
1785 zend_string_release_ex(haystack_dup, 0);
1786 zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
1787 RETURN_THROWS();
1788 }
1789 p = ZSTR_VAL(haystack_dup) + offset;
1790 e = ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack);
1791 } else {
1792 if (offset < -ZEND_LONG_MAX || (size_t)(-offset) > ZSTR_LEN(haystack)) {
1793 zend_string_release_ex(haystack_dup, 0);
1794 zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
1795 RETURN_THROWS();
1796 }
1797
1798 p = ZSTR_VAL(haystack_dup);
1799 if ((size_t)-offset < ZSTR_LEN(needle)) {
1800 e = ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack);
1801 } else {
1802 e = ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack) + offset + ZSTR_LEN(needle);
1803 }
1804 }
1805
1806 needle_dup = zend_string_tolower(needle);
1807 if ((found = (char *)zend_memnrstr(p, ZSTR_VAL(needle_dup), ZSTR_LEN(needle_dup), e))) {
1808 RETVAL_LONG(found - ZSTR_VAL(haystack_dup));
1809 zend_string_release_ex(needle_dup, 0);
1810 zend_string_release_ex(haystack_dup, 0);
1811 } else {
1812 zend_string_release_ex(needle_dup, 0);
1813 zend_string_release_ex(haystack_dup, 0);
1814 RETURN_FALSE;
1815 }
1816 }
1817 /* }}} */
1818
1819 /* {{{ Finds the last occurrence of a character in a string within another */
1820 PHP_FUNCTION(strrchr)
1821 {
1822 zend_string *haystack, *needle;
1823 const char *found = NULL;
1824 zend_long found_offset;
1825
1826 ZEND_PARSE_PARAMETERS_START(2, 2)
1827 Z_PARAM_STR(haystack)
1828 Z_PARAM_STR(needle)
1829 ZEND_PARSE_PARAMETERS_END();
1830
1831 found = zend_memrchr(ZSTR_VAL(haystack), *ZSTR_VAL(needle), ZSTR_LEN(haystack));
1832 if (UNEXPECTED(!found)) {
1833 RETURN_FALSE;
1834 }
1835 found_offset = found - ZSTR_VAL(haystack);
1836 RETURN_STRINGL(found, ZSTR_LEN(haystack) - found_offset);
1837 }
1838 /* }}} */
1839
1840 /* {{{ php_chunk_split */
1841 static zend_string *php_chunk_split(const char *src, size_t srclen, const char *end, size_t endlen, size_t chunklen)
1842 {
1843 char *q;
1844 const char *p;
1845 size_t chunks;
1846 size_t restlen;
1847 zend_string *dest;
1848
1849 chunks = srclen / chunklen;
1850 restlen = srclen - chunks * chunklen; /* srclen % chunklen */
1851 if (restlen) {
1852 /* We want chunks to be rounded up rather than rounded down.
1853 * Increment can't overflow because chunks <= SIZE_MAX/2 at this point. */
1854 chunks++;
1855 }
1856
1857 dest = zend_string_safe_alloc(chunks, endlen, srclen, 0);
1858
1859 for (p = src, q = ZSTR_VAL(dest); p < (src + srclen - chunklen + 1); ) {
1860 memcpy(q, p, chunklen);
1861 q += chunklen;
1862 memcpy(q, end, endlen);
1863 q += endlen;
1864 p += chunklen;
1865 }
1866
1867 if (restlen) {
1868 memcpy(q, p, restlen);
1869 q += restlen;
1870 memcpy(q, end, endlen);
1871 q += endlen;
1872 }
1873
1874 *q = '\0';
1875 ZEND_ASSERT(q - ZSTR_VAL(dest) == ZSTR_LEN(dest));
1876
1877 return dest;
1878 }
1879 /* }}} */
1880
1881 /* {{{ Returns split line */
1882 PHP_FUNCTION(chunk_split)
1883 {
1884 zend_string *str;
1885 char *end = "\r\n";
1886 size_t endlen = 2;
1887 zend_long chunklen = 76;
1888 zend_string *result;
1889
1890 ZEND_PARSE_PARAMETERS_START(1, 3)
1891 Z_PARAM_STR(str)
1892 Z_PARAM_OPTIONAL
1893 Z_PARAM_LONG(chunklen)
1894 Z_PARAM_STRING(end, endlen)
1895 ZEND_PARSE_PARAMETERS_END();
1896
1897 if (chunklen <= 0) {
1898 zend_argument_value_error(2, "must be greater than 0");
1899 RETURN_THROWS();
1900 }
1901
1902 if ((size_t)chunklen > ZSTR_LEN(str)) {
1903 /* to maintain BC, we must return original string + ending */
1904 result = zend_string_safe_alloc(ZSTR_LEN(str), 1, endlen, 0);
1905 memcpy(ZSTR_VAL(result), ZSTR_VAL(str), ZSTR_LEN(str));
1906 memcpy(ZSTR_VAL(result) + ZSTR_LEN(str), end, endlen);
1907 ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
1908 RETURN_NEW_STR(result);
1909 }
1910
1911 if (!ZSTR_LEN(str)) {
1912 RETURN_EMPTY_STRING();
1913 }
1914
1915 result = php_chunk_split(ZSTR_VAL(str), ZSTR_LEN(str), end, endlen, (size_t)chunklen);
1916
1917 RETURN_STR(result);
1918 }
1919 /* }}} */
1920
1921 /* {{{ Returns part of a string */
1922 PHP_FUNCTION(substr)
1923 {
1924 zend_string *str;
1925 zend_long l = 0, f;
1926 bool len_is_null = 1;
1927
1928 ZEND_PARSE_PARAMETERS_START(2, 3)
1929 Z_PARAM_STR(str)
1930 Z_PARAM_LONG(f)
1931 Z_PARAM_OPTIONAL
1932 Z_PARAM_LONG_OR_NULL(l, len_is_null)
1933 ZEND_PARSE_PARAMETERS_END();
1934
1935 if (f < 0) {
1936 /* if "from" position is negative, count start position from the end
1937 * of the string
1938 */
1939 if (-(size_t)f > ZSTR_LEN(str)) {
1940 f = 0;
1941 } else {
1942 f = (zend_long)ZSTR_LEN(str) + f;
1943 }
1944 } else if ((size_t)f > ZSTR_LEN(str)) {
1945 RETURN_EMPTY_STRING();
1946 }
1947
1948 if (!len_is_null) {
1949 if (l < 0) {
1950 /* if "length" position is negative, set it to the length
1951 * needed to stop that many chars from the end of the string
1952 */
1953 if (-(size_t)l > ZSTR_LEN(str) - (size_t)f) {
1954 l = 0;
1955 } else {
1956 l = (zend_long)ZSTR_LEN(str) - f + l;
1957 }
1958 } else if ((size_t)l > ZSTR_LEN(str) - (size_t)f) {
1959 l = (zend_long)ZSTR_LEN(str) - f;
1960 }
1961 } else {
1962 l = (zend_long)ZSTR_LEN(str) - f;
1963 }
1964
1965 if (l == ZSTR_LEN(str)) {
1966 RETURN_STR_COPY(str);
1967 } else {
1968 RETURN_STRINGL_FAST(ZSTR_VAL(str) + f, l);
1969 }
1970 }
1971 /* }}} */
1972
1973 /* {{{ Replaces part of a string with another string */
1974 PHP_FUNCTION(substr_replace)
1975 {
1976 zend_string *str, *repl_str;
1977 HashTable *str_ht, *repl_ht;
1978 HashTable *from_ht;
1979 zend_long from_long;
1980 HashTable *len_ht = NULL;
1981 zend_long len_long;
1982 bool len_is_null = 1;
1983 zend_long l = 0;
1984 zend_long f;
1985 zend_string *result;
1986 HashPosition from_idx, repl_idx, len_idx;
1987 zval *tmp_str = NULL, *tmp_repl, *tmp_from = NULL, *tmp_len= NULL;
1988
1989 ZEND_PARSE_PARAMETERS_START(3, 4)
1990 Z_PARAM_ARRAY_HT_OR_STR(str_ht, str)
1991 Z_PARAM_ARRAY_HT_OR_STR(repl_ht, repl_str)
1992 Z_PARAM_ARRAY_HT_OR_LONG(from_ht, from_long)
1993 Z_PARAM_OPTIONAL
1994 Z_PARAM_ARRAY_HT_OR_LONG_OR_NULL(len_ht, len_long, len_is_null)
1995 ZEND_PARSE_PARAMETERS_END();
1996
1997 if (len_is_null) {
1998 if (str) {
1999 l = ZSTR_LEN(str);
2000 }
2001 } else if (!len_ht) {
2002 l = len_long;
2003 }
2004
2005 if (str) {
2006 if (from_ht) {
2007 zend_argument_type_error(3, "cannot be an array when working on a single string");
2008 RETURN_THROWS();
2009 }
2010 if (len_ht) {
2011 zend_argument_type_error(4, "cannot be an array when working on a single string");
2012 RETURN_THROWS();
2013 }
2014
2015 f = from_long;
2016
2017 /* if "from" position is negative, count start position from the end
2018 * of the string
2019 */
2020 if (f < 0) {
2021 f = (zend_long)ZSTR_LEN(str) + f;
2022 if (f < 0) {
2023 f = 0;
2024 }
2025 } else if ((size_t)f > ZSTR_LEN(str)) {
2026 f = ZSTR_LEN(str);
2027 }
2028 /* if "length" position is negative, set it to the length
2029 * needed to stop that many chars from the end of the string
2030 */
2031 if (l < 0) {
2032 l = ((zend_long)ZSTR_LEN(str) - f) + l;
2033 if (l < 0) {
2034 l = 0;
2035 }
2036 }
2037
2038 if ((size_t)l > ZSTR_LEN(str) || (l < 0 && (size_t)(-l) > ZSTR_LEN(str))) {
2039 l = ZSTR_LEN(str);
2040 }
2041
2042 if ((f + l) > (zend_long)ZSTR_LEN(str)) {
2043 l = ZSTR_LEN(str) - f;
2044 }
2045
2046 zend_string *tmp_repl_str = NULL;
2047 if (repl_ht) {
2048 repl_idx = 0;
2049 if (HT_IS_PACKED(repl_ht)) {
2050 while (repl_idx < repl_ht->nNumUsed) {
2051 tmp_repl = &repl_ht->arPacked[repl_idx];
2052 if (Z_TYPE_P(tmp_repl) != IS_UNDEF) {
2053 break;
2054 }
2055 repl_idx++;
2056 }
2057 } else {
2058 while (repl_idx < repl_ht->nNumUsed) {
2059 tmp_repl = &repl_ht->arData[repl_idx].val;
2060 if (Z_TYPE_P(tmp_repl) != IS_UNDEF) {
2061 break;
2062 }
2063 repl_idx++;
2064 }
2065 }
2066 if (repl_idx < repl_ht->nNumUsed) {
2067 repl_str = zval_get_tmp_string(tmp_repl, &tmp_repl_str);
2068 } else {
2069 repl_str = STR_EMPTY_ALLOC();
2070 }
2071 }
2072
2073 result = zend_string_safe_alloc(1, ZSTR_LEN(str) - l + ZSTR_LEN(repl_str), 0, 0);
2074
2075 memcpy(ZSTR_VAL(result), ZSTR_VAL(str), f);
2076 if (ZSTR_LEN(repl_str)) {
2077 memcpy((ZSTR_VAL(result) + f), ZSTR_VAL(repl_str), ZSTR_LEN(repl_str));
2078 }
2079 memcpy((ZSTR_VAL(result) + f + ZSTR_LEN(repl_str)), ZSTR_VAL(str) + f + l, ZSTR_LEN(str) - f - l);
2080 ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
2081 zend_tmp_string_release(tmp_repl_str);
2082 RETURN_NEW_STR(result);
2083 } else { /* str is array of strings */
2084 zend_string *str_index = NULL;
2085 size_t result_len;
2086 zend_ulong num_index;
2087
2088 /* TODO
2089 if (!len_is_null && from_ht) {
2090 if (zend_hash_num_elements(from_ht) != zend_hash_num_elements(len_ht)) {
2091 php_error_docref(NULL, E_WARNING, "'start' and 'length' should have the same number of elements");
2092 RETURN_STR_COPY(str);
2093 }
2094 }
2095 */
2096
2097 array_init(return_value);
2098
2099 from_idx = len_idx = repl_idx = 0;
2100
2101 ZEND_HASH_FOREACH_KEY_VAL(str_ht, num_index, str_index, tmp_str) {
2102 zend_string *tmp_orig_str;
2103 zend_string *orig_str = zval_get_tmp_string(tmp_str, &tmp_orig_str);
2104
2105 if (from_ht) {
2106 if (HT_IS_PACKED(from_ht)) {
2107 while (from_idx < from_ht->nNumUsed) {
2108 tmp_from = &from_ht->arPacked[from_idx];
2109 if (Z_TYPE_P(tmp_from) != IS_UNDEF) {
2110 break;
2111 }
2112 from_idx++;
2113 }
2114 } else {
2115 while (from_idx < from_ht->nNumUsed) {
2116 tmp_from = &from_ht->arData[from_idx].val;
2117 if (Z_TYPE_P(tmp_from) != IS_UNDEF) {
2118 break;
2119 }
2120 from_idx++;
2121 }
2122 }
2123 if (from_idx < from_ht->nNumUsed) {
2124 f = zval_get_long(tmp_from);
2125
2126 if (f < 0) {
2127 f = (zend_long)ZSTR_LEN(orig_str) + f;
2128 if (f < 0) {
2129 f = 0;
2130 }
2131 } else if (f > (zend_long)ZSTR_LEN(orig_str)) {
2132 f = ZSTR_LEN(orig_str);
2133 }
2134 from_idx++;
2135 } else {
2136 f = 0;
2137 }
2138 } else {
2139 f = from_long;
2140 if (f < 0) {
2141 f = (zend_long)ZSTR_LEN(orig_str) + f;
2142 if (f < 0) {
2143 f = 0;
2144 }
2145 } else if (f > (zend_long)ZSTR_LEN(orig_str)) {
2146 f = ZSTR_LEN(orig_str);
2147 }
2148 }
2149
2150 if (len_ht) {
2151 if (HT_IS_PACKED(len_ht)) {
2152 while (len_idx < len_ht->nNumUsed) {
2153 tmp_len = &len_ht->arPacked[len_idx];
2154 if (Z_TYPE_P(tmp_len) != IS_UNDEF) {
2155 break;
2156 }
2157 len_idx++;
2158 }
2159 } else {
2160 while (len_idx < len_ht->nNumUsed) {
2161 tmp_len = &len_ht->arData[len_idx].val;
2162 if (Z_TYPE_P(tmp_len) != IS_UNDEF) {
2163 break;
2164 }
2165 len_idx++;
2166 }
2167 }
2168 if (len_idx < len_ht->nNumUsed) {
2169 l = zval_get_long(tmp_len);
2170 len_idx++;
2171 } else {
2172 l = ZSTR_LEN(orig_str);
2173 }
2174 } else if (!len_is_null) {
2175 l = len_long;
2176 } else {
2177 l = ZSTR_LEN(orig_str);
2178 }
2179
2180 if (l < 0) {
2181 l = (ZSTR_LEN(orig_str) - f) + l;
2182 if (l < 0) {
2183 l = 0;
2184 }
2185 }
2186
2187 ZEND_ASSERT(0 <= f && f <= ZEND_LONG_MAX);
2188 ZEND_ASSERT(0 <= l && l <= ZEND_LONG_MAX);
2189 if (((size_t) f + l) > ZSTR_LEN(orig_str)) {
2190 l = ZSTR_LEN(orig_str) - f;
2191 }
2192
2193 result_len = ZSTR_LEN(orig_str) - l;
2194
2195 if (repl_ht) {
2196 if (HT_IS_PACKED(repl_ht)) {
2197 while (repl_idx < repl_ht->nNumUsed) {
2198 tmp_repl = &repl_ht->arPacked[repl_idx];
2199 if (Z_TYPE_P(tmp_repl) != IS_UNDEF) {
2200 break;
2201 }
2202 repl_idx++;
2203 }
2204 } else {
2205 while (repl_idx < repl_ht->nNumUsed) {
2206 tmp_repl = &repl_ht->arData[repl_idx].val;
2207 if (Z_TYPE_P(tmp_repl) != IS_UNDEF) {
2208 break;
2209 }
2210 repl_idx++;
2211 }
2212 }
2213 if (repl_idx < repl_ht->nNumUsed) {
2214 zend_string *tmp_repl_str;
2215 zend_string *repl_str = zval_get_tmp_string(tmp_repl, &tmp_repl_str);
2216
2217 result_len += ZSTR_LEN(repl_str);
2218 repl_idx++;
2219 result = zend_string_safe_alloc(1, result_len, 0, 0);
2220
2221 memcpy(ZSTR_VAL(result), ZSTR_VAL(orig_str), f);
2222 memcpy((ZSTR_VAL(result) + f), ZSTR_VAL(repl_str), ZSTR_LEN(repl_str));
2223 memcpy((ZSTR_VAL(result) + f + ZSTR_LEN(repl_str)), ZSTR_VAL(orig_str) + f + l, ZSTR_LEN(orig_str) - f - l);
2224 zend_tmp_string_release(tmp_repl_str);
2225 } else {
2226 result = zend_string_safe_alloc(1, result_len, 0, 0);
2227
2228 memcpy(ZSTR_VAL(result), ZSTR_VAL(orig_str), f);
2229 memcpy((ZSTR_VAL(result) + f), ZSTR_VAL(orig_str) + f + l, ZSTR_LEN(orig_str) - f - l);
2230 }
2231 } else {
2232 result_len += ZSTR_LEN(repl_str);
2233
2234 result = zend_string_safe_alloc(1, result_len, 0, 0);
2235
2236 memcpy(ZSTR_VAL(result), ZSTR_VAL(orig_str), f);
2237 memcpy((ZSTR_VAL(result) + f), ZSTR_VAL(repl_str), ZSTR_LEN(repl_str));
2238 memcpy((ZSTR_VAL(result) + f + ZSTR_LEN(repl_str)), ZSTR_VAL(orig_str) + f + l, ZSTR_LEN(orig_str) - f - l);
2239 }
2240
2241 ZSTR_VAL(result)[ZSTR_LEN(result)] = '\0';
2242
2243 if (str_index) {
2244 zval tmp;
2245
2246 ZVAL_NEW_STR(&tmp, result);
2247 zend_symtable_update(Z_ARRVAL_P(return_value), str_index, &tmp);
2248 } else {
2249 add_index_str(return_value, num_index, result);
2250 }
2251
2252 zend_tmp_string_release(tmp_orig_str);
2253 } ZEND_HASH_FOREACH_END();
2254 } /* if */
2255 }
2256 /* }}} */
2257
2258 /* {{{ Quotes meta characters */
2259 PHP_FUNCTION(quotemeta)
2260 {
2261 zend_string *old;
2262 const char *old_end, *p;
2263 char *q;
2264 char c;
2265 zend_string *str;
2266
2267 ZEND_PARSE_PARAMETERS_START(1, 1)
2268 Z_PARAM_STR(old)
2269 ZEND_PARSE_PARAMETERS_END();
2270
2271 old_end = ZSTR_VAL(old) + ZSTR_LEN(old);
2272
2273 if (ZSTR_LEN(old) == 0) {
2274 RETURN_EMPTY_STRING();
2275 }
2276
2277 str = zend_string_safe_alloc(2, ZSTR_LEN(old), 0, 0);
2278
2279 for (p = ZSTR_VAL(old), q = ZSTR_VAL(str); p != old_end; p++) {
2280 c = *p;
2281 switch (c) {
2282 case '.':
2283 case '\\':
2284 case '+':
2285 case '*':
2286 case '?':
2287 case '[':
2288 case '^':
2289 case ']':
2290 case '$':
2291 case '(':
2292 case ')':
2293 *q++ = '\\';
2294 ZEND_FALLTHROUGH;
2295 default:
2296 *q++ = c;
2297 }
2298 }
2299
2300 *q = '\0';
2301
2302 RETURN_NEW_STR(zend_string_truncate(str, q - ZSTR_VAL(str), 0));
2303 }
2304 /* }}} */
2305
2306 /* {{{ Returns ASCII value of character
2307 Warning: This function is special-cased by zend_compile.c and so is bypassed for constant string argument */
2308 PHP_FUNCTION(ord)
2309 {
2310 zend_string *str;
2311
2312 ZEND_PARSE_PARAMETERS_START(1, 1)
2313 Z_PARAM_STR(str)
2314 ZEND_PARSE_PARAMETERS_END();
2315
2316 RETURN_LONG((unsigned char) ZSTR_VAL(str)[0]);
2317 }
2318 /* }}} */
2319
2320 /* {{{ Converts ASCII code to a character
2321 Warning: This function is special-cased by zend_compile.c and so is bypassed for constant integer argument */
2322 PHP_FUNCTION(chr)
2323 {
2324 zend_long c;
2325
2326 ZEND_PARSE_PARAMETERS_START(1, 1)
2327 Z_PARAM_LONG(c)
2328 ZEND_PARSE_PARAMETERS_END();
2329
2330 c &= 0xff;
2331 RETURN_CHAR(c);
2332 }
2333 /* }}} */
2334
2335 /* {{{ php_ucfirst
2336 Uppercase the first character of the word in a native string */
2337 static zend_string* php_ucfirst(zend_string *str)
2338 {
2339 const unsigned char ch = ZSTR_VAL(str)[0];
2340 unsigned char r = zend_toupper_ascii(ch);
2341 if (r == ch) {
2342 return zend_string_copy(str);
2343 } else {
2344 zend_string *s = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
2345 ZSTR_VAL(s)[0] = r;
2346 return s;
2347 }
2348 }
2349 /* }}} */
2350
2351 /* {{{ Makes a string's first character uppercase */
2352 PHP_FUNCTION(ucfirst)
2353 {
2354 zend_string *str;
2355
2356 ZEND_PARSE_PARAMETERS_START(1, 1)
2357 Z_PARAM_STR(str)
2358 ZEND_PARSE_PARAMETERS_END();
2359
2360 if (!ZSTR_LEN(str)) {
2361 RETURN_EMPTY_STRING();
2362 }
2363
2364 RETURN_STR(php_ucfirst(str));
2365 }
2366 /* }}} */
2367
2368 /* {{{
2369 Lowercase the first character of the word in a native string */
2370 static zend_string* php_lcfirst(zend_string *str)
2371 {
2372 unsigned char r = zend_tolower_ascii(ZSTR_VAL(str)[0]);
2373 if (r == ZSTR_VAL(str)[0]) {
2374 return zend_string_copy(str);
2375 } else {
2376 zend_string *s = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
2377 ZSTR_VAL(s)[0] = r;
2378 return s;
2379 }
2380 }
2381 /* }}} */
2382
2383 /* {{{ Make a string's first character lowercase */
2384 PHP_FUNCTION(lcfirst)
2385 {
2386 zend_string *str;
2387
2388 ZEND_PARSE_PARAMETERS_START(1, 1)
2389 Z_PARAM_STR(str)
2390 ZEND_PARSE_PARAMETERS_END();
2391
2392 if (!ZSTR_LEN(str)) {
2393 RETURN_EMPTY_STRING();
2394 }
2395
2396 RETURN_STR(php_lcfirst(str));
2397 }
2398 /* }}} */
2399
2400 /* {{{ Uppercase the first character of every word in a string */
2401 PHP_FUNCTION(ucwords)
2402 {
2403 zend_string *str;
2404 char *delims = " \t\r\n\f\v";
2405 char *r;
2406 const char *r_end;
2407 size_t delims_len = 6;
2408 char mask[256];
2409
2410 ZEND_PARSE_PARAMETERS_START(1, 2)
2411 Z_PARAM_STR(str)
2412 Z_PARAM_OPTIONAL
2413 Z_PARAM_STRING(delims, delims_len)
2414 ZEND_PARSE_PARAMETERS_END();
2415
2416 if (!ZSTR_LEN(str)) {
2417 RETURN_EMPTY_STRING();
2418 }
2419
2420 php_charmask((const unsigned char *) delims, delims_len, mask);
2421
2422 ZVAL_STRINGL(return_value, ZSTR_VAL(str), ZSTR_LEN(str));
2423 r = Z_STRVAL_P(return_value);
2424
2425 *r = zend_toupper_ascii((unsigned char) *r);
2426 for (r_end = r + Z_STRLEN_P(return_value) - 1; r < r_end; ) {
2427 if (mask[(unsigned char)*r++]) {
2428 *r = zend_toupper_ascii((unsigned char) *r);
2429 }
2430 }
2431 }
2432 /* }}} */
2433
2434 /* {{{ php_strtr */
2435 PHPAPI char *php_strtr(char *str, size_t len, const char *str_from, const char *str_to, size_t trlen)
2436 {
2437 size_t i;
2438
2439 if (UNEXPECTED(trlen < 1)) {
2440 return str;
2441 } else if (trlen == 1) {
2442 char ch_from = *str_from;
2443 char ch_to = *str_to;
2444
2445 for (i = 0; i < len; i++) {
2446 if (str[i] == ch_from) {
2447 str[i] = ch_to;
2448 }
2449 }
2450 } else {
2451 unsigned char xlat[256];
2452
2453 memset(xlat, 0, sizeof(xlat));
2454
2455 for (i = 0; i < trlen; i++) {
2456 xlat[(size_t)(unsigned char) str_from[i]] = str_to[i] - str_from[i];
2457 }
2458
2459 for (i = 0; i < len; i++) {
2460 str[i] += xlat[(size_t)(unsigned char) str[i]];
2461 }
2462 }
2463
2464 return str;
2465 }
2466 /* }}} */
2467
2468 /* {{{ php_strtr_ex */
2469 static zend_string *php_strtr_ex(zend_string *str, const char *str_from, const char *str_to, size_t trlen)
2470 {
2471 zend_string *new_str = NULL;
2472 size_t i;
2473
2474 if (UNEXPECTED(trlen < 1)) {
2475 return zend_string_copy(str);
2476 } else if (trlen == 1) {
2477 char ch_from = *str_from;
2478 char ch_to = *str_to;
2479 char *output;
2480 char *input = ZSTR_VAL(str);
2481 size_t len = ZSTR_LEN(str);
2482
2483 #ifdef __SSE2__
2484 if (ZSTR_LEN(str) >= sizeof(__m128i)) {
2485 __m128i search = _mm_set1_epi8(ch_from);
2486 __m128i delta = _mm_set1_epi8(ch_to - ch_from);
2487
2488 do {
2489 __m128i src = _mm_loadu_si128((__m128i*)(input));
2490 __m128i mask = _mm_cmpeq_epi8(src, search);
2491 if (_mm_movemask_epi8(mask)) {
2492 new_str = zend_string_alloc(ZSTR_LEN(str), 0);
2493 memcpy(ZSTR_VAL(new_str), ZSTR_VAL(str), input - ZSTR_VAL(str));
2494 output = ZSTR_VAL(new_str) + (input - ZSTR_VAL(str));
2495 _mm_storeu_si128((__m128i *)(output),
2496 _mm_add_epi8(src,
2497 _mm_and_si128(mask, delta)));
2498 input += sizeof(__m128i);
2499 output += sizeof(__m128i);
2500 len -= sizeof(__m128i);
2501 for (; len >= sizeof(__m128i); input += sizeof(__m128i), output += sizeof(__m128i), len -= sizeof(__m128i)) {
2502 src = _mm_loadu_si128((__m128i*)(input));
2503 mask = _mm_cmpeq_epi8(src, search);
2504 _mm_storeu_si128((__m128i *)(output),
2505 _mm_add_epi8(src,
2506 _mm_and_si128(mask, delta)));
2507 }
2508 for (; len > 0; input++, output++, len--) {
2509 *output = (*input == ch_from) ? ch_to : *input;
2510 }
2511 *output = 0;
2512 return new_str;
2513 }
2514 input += sizeof(__m128i);
2515 len -= sizeof(__m128i);
2516 } while (len >= sizeof(__m128i));
2517 }
2518 #endif
2519 for (; len > 0; input++, len--) {
2520 if (*input == ch_from) {
2521 new_str = zend_string_alloc(ZSTR_LEN(str), 0);
2522 memcpy(ZSTR_VAL(new_str), ZSTR_VAL(str), input - ZSTR_VAL(str));
2523 output = ZSTR_VAL(new_str) + (input - ZSTR_VAL(str));
2524 *output = ch_to;
2525 input++;
2526 output++;
2527 len--;
2528 for (; len > 0; input++, output++, len--) {
2529 *output = (*input == ch_from) ? ch_to : *input;
2530 }
2531 *output = 0;
2532 return new_str;
2533 }
2534 }
2535 } else {
2536 unsigned char xlat[256];
2537
2538 memset(xlat, 0, sizeof(xlat));;
2539
2540 for (i = 0; i < trlen; i++) {
2541 xlat[(size_t)(unsigned char) str_from[i]] = str_to[i] - str_from[i];
2542 }
2543
2544 for (i = 0; i < ZSTR_LEN(str); i++) {
2545 if (xlat[(size_t)(unsigned char) ZSTR_VAL(str)[i]]) {
2546 new_str = zend_string_alloc(ZSTR_LEN(str), 0);
2547 memcpy(ZSTR_VAL(new_str), ZSTR_VAL(str), i);
2548 do {
2549 ZSTR_VAL(new_str)[i] = ZSTR_VAL(str)[i] + xlat[(size_t)(unsigned char) ZSTR_VAL(str)[i]];
2550 i++;
2551 } while (i < ZSTR_LEN(str));
2552 ZSTR_VAL(new_str)[i] = 0;
2553 return new_str;
2554 }
2555 }
2556 }
2557
2558 return zend_string_copy(str);
2559 }
2560 /* }}} */
2561
2562 /* {{{ php_strtr_array */
2563 static void php_strtr_array(zval *return_value, zend_string *input, HashTable *pats)
2564 {
2565 const char *str = ZSTR_VAL(input);
2566 size_t slen = ZSTR_LEN(input);
2567 zend_ulong num_key;
2568 zend_string *str_key;
2569 size_t len, pos, old_pos;
2570 bool has_num_keys = false;
2571 size_t minlen = 128*1024;
2572 size_t maxlen = 0;
2573 HashTable str_hash;
2574 zval *entry;
2575 const char *key;
2576 smart_str result = {0};
2577 zend_ulong bitset[256/sizeof(zend_ulong)];
2578 zend_ulong *num_bitset;
2579
2580 /* we will collect all possible key lengths */
2581 num_bitset = ecalloc((slen + sizeof(zend_ulong)) / sizeof(zend_ulong), sizeof(zend_ulong));
2582 memset(bitset, 0, sizeof(bitset));
2583
2584 /* check if original array has numeric keys */
2585 ZEND_HASH_FOREACH_STR_KEY(pats, str_key) {
2586 if (UNEXPECTED(!str_key)) {
2587 has_num_keys = true;
2588 } else {
2589 len = ZSTR_LEN(str_key);
2590 if (UNEXPECTED(len == 0)) {
2591 php_error_docref(NULL, E_WARNING, "Ignoring replacement of empty string");
2592 continue;
2593 } else if (UNEXPECTED(len > slen)) {
2594 /* skip long patterns */
2595 continue;
2596 }
2597 if (len > maxlen) {
2598 maxlen = len;
2599 }
2600 if (len < minlen) {
2601 minlen = len;
2602 }
2603 /* remember possible key length */
2604 num_bitset[len / sizeof(zend_ulong)] |= Z_UL(1) << (len % sizeof(zend_ulong));
2605 bitset[((unsigned char)ZSTR_VAL(str_key)[0]) / sizeof(zend_ulong)] |= Z_UL(1) << (((unsigned char)ZSTR_VAL(str_key)[0]) % sizeof(zend_ulong));
2606 }
2607 } ZEND_HASH_FOREACH_END();
2608
2609 if (UNEXPECTED(has_num_keys)) {
2610 zend_string *key_used;
2611 /* we have to rebuild HashTable with numeric keys */
2612 zend_hash_init(&str_hash, zend_hash_num_elements(pats), NULL, NULL, 0);
2613 ZEND_HASH_FOREACH_KEY_VAL(pats, num_key, str_key, entry) {
2614 if (UNEXPECTED(!str_key)) {
2615 key_used = zend_long_to_str(num_key);
2616 len = ZSTR_LEN(key_used);
2617 if (UNEXPECTED(len > slen)) {
2618 /* skip long patterns */
2619 zend_string_release(key_used);
2620 continue;
2621 }
2622 if (len > maxlen) {
2623 maxlen = len;
2624 }
2625 if (len < minlen) {
2626 minlen = len;
2627 }
2628 /* remember possible key length */
2629 num_bitset[len / sizeof(zend_ulong)] |= Z_UL(1) << (len % sizeof(zend_ulong));
2630 bitset[((unsigned char)ZSTR_VAL(key_used)[0]) / sizeof(zend_ulong)] |= Z_UL(1) << (((unsigned char)ZSTR_VAL(key_used)[0]) % sizeof(zend_ulong));
2631 } else {
2632 key_used = str_key;
2633 len = ZSTR_LEN(key_used);
2634 if (UNEXPECTED(len > slen)) {
2635 /* skip long patterns */
2636 continue;
2637 }
2638 }
2639 zend_hash_add(&str_hash, key_used, entry);
2640 if (UNEXPECTED(!str_key)) {
2641 zend_string_release_ex(key_used, 0);
2642 }
2643 } ZEND_HASH_FOREACH_END();
2644 pats = &str_hash;
2645 }
2646
2647 if (UNEXPECTED(minlen > maxlen)) {
2648 /* return the original string */
2649 if (pats == &str_hash) {
2650 zend_hash_destroy(&str_hash);
2651 }
2652 efree(num_bitset);
2653 RETURN_STR_COPY(input);
2654 }
2655
2656 old_pos = pos = 0;
2657 while (pos <= slen - minlen) {
2658 key = str + pos;
2659 if (bitset[((unsigned char)key[0]) / sizeof(zend_ulong)] & (Z_UL(1) << (((unsigned char)key[0]) % sizeof(zend_ulong)))) {
2660 len = maxlen;
2661 if (len > slen - pos) {
2662 len = slen - pos;
2663 }
2664 while (len >= minlen) {
2665 if ((num_bitset[len / sizeof(zend_ulong)] & (Z_UL(1) << (len % sizeof(zend_ulong))))) {
2666 entry = zend_hash_str_find(pats, key, len);
2667 if (entry != NULL) {
2668 zend_string *tmp;
2669 zend_string *s = zval_get_tmp_string(entry, &tmp);
2670 smart_str_appendl(&result, str + old_pos, pos - old_pos);
2671 smart_str_append(&result, s);
2672 old_pos = pos + len;
2673 pos = old_pos - 1;
2674 zend_tmp_string_release(tmp);
2675 break;
2676 }
2677 }
2678 len--;
2679 }
2680 }
2681 pos++;
2682 }
2683
2684 if (result.s) {
2685 smart_str_appendl(&result, str + old_pos, slen - old_pos);
2686 RETVAL_STR(smart_str_extract(&result));
2687 } else {
2688 smart_str_free(&result);
2689 RETVAL_STR_COPY(input);
2690 }
2691
2692 if (pats == &str_hash) {
2693 zend_hash_destroy(&str_hash);
2694 }
2695 efree(num_bitset);
2696 }
2697 /* }}} */
2698
2699 /* {{{ count_chars */
2700 static zend_always_inline zend_long count_chars(const char *p, zend_long length, char ch)
2701 {
2702 zend_long count = 0;
2703 const char *endp;
2704
2705 #ifdef __SSE2__
2706 if (length >= sizeof(__m128i)) {
2707 __m128i search = _mm_set1_epi8(ch);
2708
2709 do {
2710 __m128i src = _mm_loadu_si128((__m128i*)(p));
2711 uint32_t mask = _mm_movemask_epi8(_mm_cmpeq_epi8(src, search));
2712 // TODO: It would be great to use POPCNT, but it's available only with SSE4.1
2713 #if 1
2714 while (mask != 0) {
2715 count++;
2716 mask = mask & (mask - 1);
2717 }
2718 #else
2719 if (mask) {
2720 mask = mask - ((mask >> 1) & 0x5555);
2721 mask = (mask & 0x3333) + ((mask >> 2) & 0x3333);
2722 mask = (mask + (mask >> 4)) & 0x0F0F;
2723 mask = (mask + (mask >> 8)) & 0x00ff;
2724 count += mask;
2725 }
2726 #endif
2727 p += sizeof(__m128i);
2728 length -= sizeof(__m128i);
2729 } while (length >= sizeof(__m128i));
2730 }
2731 endp = p + length;
2732 while (p != endp) {
2733 count += (*p == ch);
2734 p++;
2735 }
2736 #else
2737 endp = p + length;
2738 while ((p = memchr(p, ch, endp-p))) {
2739 count++;
2740 p++;
2741 }
2742 #endif
2743 return count;
2744 }
2745 /* }}} */
2746
2747 /* {{{ php_char_to_str_ex */
2748 static zend_string* php_char_to_str_ex(zend_string *str, char from, char *to, size_t to_len, bool case_sensitivity, zend_long *replace_count)
2749 {
2750 zend_string *result;
2751 size_t char_count;
2752 int lc_from = 0;
2753 const char *source, *source_end;
2754 char *target;
2755
2756 if (case_sensitivity) {
2757 char_count = count_chars(ZSTR_VAL(str), ZSTR_LEN(str), from);
2758 } else {
2759 char_count = 0;
2760 lc_from = zend_tolower_ascii(from);
2761 source_end = ZSTR_VAL(str) + ZSTR_LEN(str);
2762 for (source = ZSTR_VAL(str); source < source_end; source++) {
2763 if (zend_tolower_ascii(*source) == lc_from) {
2764 char_count++;
2765 }
2766 }
2767 }
2768
2769 if (char_count == 0) {
2770 return zend_string_copy(str);
2771 }
2772
2773 if (replace_count) {
2774 *replace_count += char_count;
2775 }
2776
2777 if (to_len > 0) {
2778 result = zend_string_safe_alloc(char_count, to_len - 1, ZSTR_LEN(str), 0);
2779 } else {
2780 result = zend_string_alloc(ZSTR_LEN(str) - char_count, 0);
2781 }
2782 target = ZSTR_VAL(result);
2783
2784 if (case_sensitivity) {
2785 char *p = ZSTR_VAL(str), *e = p + ZSTR_LEN(str), *s = ZSTR_VAL(str);
2786
2787 while ((p = memchr(p, from, (e - p)))) {
2788 memcpy(target, s, (p - s));
2789 target += p - s;
2790 memcpy(target, to, to_len);
2791 target += to_len;
2792 p++;
2793 s = p;
2794 if (--char_count == 0) break;
2795 }
2796 if (s < e) {
2797 memcpy(target, s, (e - s));
2798 target += e - s;
2799 }
2800 } else {
2801 source_end = ZSTR_VAL(str) + ZSTR_LEN(str);
2802 for (source = ZSTR_VAL(str); source < source_end; source++) {
2803 if (zend_tolower_ascii(*source) == lc_from) {
2804 memcpy(target, to, to_len);
2805 target += to_len;
2806 } else {
2807 *target = *source;
2808 target++;
2809 }
2810 }
2811 }
2812 *target = 0;
2813 return result;
2814 }
2815 /* }}} */
2816
2817 /* {{{ php_str_to_str_ex */
2818 static zend_string *php_str_to_str_ex(zend_string *haystack,
2819 const char *needle, size_t needle_len, const char *str, size_t str_len, zend_long *replace_count)
2820 {
2821
2822 if (needle_len < ZSTR_LEN(haystack)) {
2823 zend_string *new_str;
2824 const char *end;
2825 const char *p, *r;
2826 char *e;
2827
2828 if (needle_len == str_len) {
2829 new_str = NULL;
2830 end = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
2831 for (p = ZSTR_VAL(haystack); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
2832 if (!new_str) {
2833 new_str = zend_string_init(ZSTR_VAL(haystack), ZSTR_LEN(haystack), 0);
2834 }
2835 memcpy(ZSTR_VAL(new_str) + (r - ZSTR_VAL(haystack)), str, str_len);
2836 (*replace_count)++;
2837 }
2838 if (!new_str) {
2839 goto nothing_todo;
2840 }
2841 return new_str;
2842 } else {
2843 size_t count = 0;
2844 const char *o = ZSTR_VAL(haystack);
2845 const char *n = needle;
2846 const char *endp = o + ZSTR_LEN(haystack);
2847
2848 while ((o = (char*)php_memnstr(o, n, needle_len, endp))) {
2849 o += needle_len;
2850 count++;
2851 }
2852 if (count == 0) {
2853 /* Needle doesn't occur, shortcircuit the actual replacement. */
2854 goto nothing_todo;
2855 }
2856 if (str_len > needle_len) {
2857 new_str = zend_string_safe_alloc(count, str_len - needle_len, ZSTR_LEN(haystack), 0);
2858 } else {
2859 new_str = zend_string_alloc(count * (str_len - needle_len) + ZSTR_LEN(haystack), 0);
2860 }
2861
2862 e = ZSTR_VAL(new_str);
2863 end = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);
2864 for (p = ZSTR_VAL(haystack); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
2865 memcpy(e, p, r - p);
2866 e += r - p;
2867 memcpy(e, str, str_len);
2868 e += str_len;
2869 (*replace_count)++;
2870 }
2871
2872 if (p < end) {
2873 memcpy(e, p, end - p);
2874 e += end - p;
2875 }
2876
2877 *e = '\0';
2878 return new_str;
2879 }
2880 } else if (needle_len > ZSTR_LEN(haystack) || memcmp(ZSTR_VAL(haystack), needle, ZSTR_LEN(haystack))) {
2881 nothing_todo:
2882 return zend_string_copy(haystack);
2883 } else {
2884 (*replace_count)++;
2885 return zend_string_init_fast(str, str_len);
2886 }
2887 }
2888 /* }}} */
2889
2890 /* {{{ php_str_to_str_i_ex */
2891 static zend_string *php_str_to_str_i_ex(zend_string *haystack, const char *lc_haystack,
2892 zend_string *needle, const char *str, size_t str_len, zend_long *replace_count)
2893 {
2894 zend_string *new_str = NULL;
2895 zend_string *lc_needle;
2896
2897 if (ZSTR_LEN(needle) < ZSTR_LEN(haystack)) {
2898 const char *end;
2899 const char *p, *r;
2900 char *e;
2901
2902 if (ZSTR_LEN(needle) == str_len) {
2903 lc_needle = zend_string_tolower(needle);
2904 end = lc_haystack + ZSTR_LEN(haystack);
2905 for (p = lc_haystack; (r = (char*)php_memnstr(p, ZSTR_VAL(lc_needle), ZSTR_LEN(lc_needle), end)); p = r + ZSTR_LEN(lc_needle)) {
2906 if (!new_str) {
2907 new_str = zend_string_init(ZSTR_VAL(haystack), ZSTR_LEN(haystack), 0);
2908 }
2909 memcpy(ZSTR_VAL(new_str) + (r - lc_haystack), str, str_len);
2910 (*replace_count)++;
2911 }
2912 zend_string_release_ex(lc_needle, 0);
2913
2914 if (!new_str) {
2915 goto nothing_todo;
2916 }
2917 return new_str;
2918 } else {
2919 size_t count = 0;
2920 const char *o = lc_haystack;
2921 const char *n;
2922 const char *endp = o + ZSTR_LEN(haystack);
2923
2924 lc_needle = zend_string_tolower(needle);
2925 n = ZSTR_VAL(lc_needle);
2926
2927 while ((o = (char*)php_memnstr(o, n, ZSTR_LEN(lc_needle), endp))) {
2928 o += ZSTR_LEN(lc_needle);
2929 count++;
2930 }
2931 if (count == 0) {
2932 /* Needle doesn't occur, shortcircuit the actual replacement. */
2933 zend_string_release_ex(lc_needle, 0);
2934 goto nothing_todo;
2935 }
2936
2937 if (str_len > ZSTR_LEN(lc_needle)) {
2938 new_str = zend_string_safe_alloc(count, str_len - ZSTR_LEN(lc_needle), ZSTR_LEN(haystack), 0);
2939 } else {
2940 new_str = zend_string_alloc(count * (str_len - ZSTR_LEN(lc_needle)) + ZSTR_LEN(haystack), 0);
2941 }
2942
2943 e = ZSTR_VAL(new_str);
2944 end = lc_haystack + ZSTR_LEN(haystack);
2945
2946 for (p = lc_haystack; (r = (char*)php_memnstr(p, ZSTR_VAL(lc_needle), ZSTR_LEN(lc_needle), end)); p = r + ZSTR_LEN(lc_needle)) {
2947 memcpy(e, ZSTR_VAL(haystack) + (p - lc_haystack), r - p);
2948 e += r - p;
2949 memcpy(e, str, str_len);
2950 e += str_len;
2951 (*replace_count)++;
2952 }
2953
2954 if (p < end) {
2955 memcpy(e, ZSTR_VAL(haystack) + (p - lc_haystack), end - p);
2956 e += end - p;
2957 }
2958 *e = '\0';
2959
2960 zend_string_release_ex(lc_needle, 0);
2961
2962 return new_str;
2963 }
2964 } else if (ZSTR_LEN(needle) > ZSTR_LEN(haystack)) {
2965 nothing_todo:
2966 return zend_string_copy(haystack);
2967 } else {
2968 lc_needle = zend_string_tolower(needle);
2969
2970 if (memcmp(lc_haystack, ZSTR_VAL(lc_needle), ZSTR_LEN(lc_needle))) {
2971 zend_string_release_ex(lc_needle, 0);
2972 goto nothing_todo;
2973 }
2974 zend_string_release_ex(lc_needle, 0);
2975
2976 new_str = zend_string_init(str, str_len, 0);
2977
2978 (*replace_count)++;
2979 return new_str;
2980 }
2981 }
2982 /* }}} */
2983
2984 /* {{{ php_str_to_str */
2985 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)
2986 {
2987 zend_string *new_str;
2988
2989 if (needle_len < length) {
2990 const char *end;
2991 const char *s, *p;
2992 char *e, *r;
2993
2994 if (needle_len == str_len) {
2995 new_str = zend_string_init(haystack, length, 0);
2996 end = ZSTR_VAL(new_str) + length;
2997 for (p = ZSTR_VAL(new_str); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
2998 memcpy(r, str, str_len);
2999 }
3000 return new_str;
3001 } else {
3002 if (str_len < needle_len) {
3003 new_str = zend_string_alloc(length, 0);
3004 } else {
3005 size_t count = 0;
3006 const char *o = haystack;
3007 const char *n = needle;
3008 const char *endp = o + length;
3009
3010 while ((o = (char*)php_memnstr(o, n, needle_len, endp))) {
3011 o += needle_len;
3012 count++;
3013 }
3014 if (count == 0) {
3015 /* Needle doesn't occur, shortcircuit the actual replacement. */
3016 new_str = zend_string_init(haystack, length, 0);
3017 return new_str;
3018 } else {
3019 if (str_len > needle_len) {
3020 new_str = zend_string_safe_alloc(count, str_len - needle_len, length, 0);
3021 } else {
3022 new_str = zend_string_alloc(count * (str_len - needle_len) + length, 0);
3023 }
3024 }
3025 }
3026
3027 s = e = ZSTR_VAL(new_str);
3028 end = haystack + length;
3029 for (p = haystack; (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) {
3030 memcpy(e, p, r - p);
3031 e += r - p;
3032 memcpy(e, str, str_len);
3033 e += str_len;
3034 }
3035
3036 if (p < end) {
3037 memcpy(e, p, end - p);
3038 e += end - p;
3039 }
3040
3041 *e = '\0';
3042 new_str = zend_string_truncate(new_str, e - s, 0);
3043 return new_str;
3044 }
3045 } else if (needle_len > length || memcmp(haystack, needle, length)) {
3046 new_str = zend_string_init(haystack, length, 0);
3047 return new_str;
3048 } else {
3049 new_str = zend_string_init(str, str_len, 0);
3050
3051 return new_str;
3052 }
3053 }
3054 /* }}} */
3055
3056 /* {{{ Translates characters in str using given translation tables */
3057 PHP_FUNCTION(strtr)
3058 {
3059 zend_string *str, *from_str = NULL;
3060 HashTable *from_ht = NULL;
3061 char *to = NULL;
3062 size_t to_len = 0;
3063
3064 ZEND_PARSE_PARAMETERS_START(2, 3)
3065 Z_PARAM_STR(str)
3066 Z_PARAM_ARRAY_HT_OR_STR(from_ht, from_str)
3067 Z_PARAM_OPTIONAL
3068 Z_PARAM_STRING_OR_NULL(to, to_len)
3069 ZEND_PARSE_PARAMETERS_END();
3070
3071 if (!to && from_ht == NULL) {
3072 zend_argument_type_error(2, "must be of type array, string given");
3073 RETURN_THROWS();
3074 } else if (to && from_str == NULL) {
3075 zend_argument_type_error(2, "must be of type string, array given");
3076 RETURN_THROWS();
3077 }
3078
3079 /* shortcut for empty string */
3080 if (ZSTR_LEN(str) == 0) {
3081 RETURN_EMPTY_STRING();
3082 }
3083
3084 if (!to) {
3085 if (zend_hash_num_elements(from_ht) < 1) {
3086 RETURN_STR_COPY(str);
3087 } else if (zend_hash_num_elements(from_ht) == 1) {
3088 zend_long num_key;
3089 zend_string *str_key, *tmp_str, *replace, *tmp_replace;
3090 zval *entry;
3091
3092 ZEND_HASH_FOREACH_KEY_VAL(from_ht, num_key, str_key, entry) {
3093 tmp_str = NULL;
3094 if (UNEXPECTED(!str_key)) {
3095 str_key = tmp_str = zend_long_to_str(num_key);
3096 }
3097 replace = zval_get_tmp_string(entry, &tmp_replace);
3098 if (ZSTR_LEN(str_key) < 1) {
3099 php_error_docref(NULL, E_WARNING, "Ignoring replacement of empty string");
3100 RETVAL_STR_COPY(str);
3101 } else if (ZSTR_LEN(str_key) == 1) {
3102 RETVAL_STR(php_char_to_str_ex(str,
3103 ZSTR_VAL(str_key)[0],
3104 ZSTR_VAL(replace),
3105 ZSTR_LEN(replace),
3106 /* case_sensitive */ true,
3107 NULL));
3108 } else {
3109 zend_long dummy;
3110 RETVAL_STR(php_str_to_str_ex(str,
3111 ZSTR_VAL(str_key), ZSTR_LEN(str_key),
3112 ZSTR_VAL(replace), ZSTR_LEN(replace), &dummy));
3113 }
3114 zend_tmp_string_release(tmp_str);
3115 zend_tmp_string_release(tmp_replace);
3116 return;
3117 } ZEND_HASH_FOREACH_END();
3118 } else {
3119 php_strtr_array(return_value, str, from_ht);
3120 }
3121 } else {
3122 RETURN_STR(php_strtr_ex(str,
3123 ZSTR_VAL(from_str),
3124 to,
3125 MIN(ZSTR_LEN(from_str), to_len)));
3126 }
3127 }
3128 /* }}} */
3129
3130 /* {{{ Reverse a string */
3131 #ifdef ZEND_INTRIN_SSSE3_NATIVE
3132 #include <tmmintrin.h>
3133 #elif defined(__aarch64__) || defined(_M_ARM64)
3134 #include <arm_neon.h>
3135 #endif
3136 PHP_FUNCTION(strrev)
3137 {
3138 zend_string *str;
3139 const char *s, *e;
3140 char *p;
3141 zend_string *n;
3142
3143 ZEND_PARSE_PARAMETERS_START(1, 1)
3144 Z_PARAM_STR(str)
3145 ZEND_PARSE_PARAMETERS_END();
3146
3147 n = zend_string_alloc(ZSTR_LEN(str), 0);
3148 p = ZSTR_VAL(n);
3149
3150 s = ZSTR_VAL(str);
3151 e = s + ZSTR_LEN(str);
3152 --e;
3153 #ifdef ZEND_INTRIN_SSSE3_NATIVE
3154 if (e - s > 15) {
3155 const __m128i map = _mm_set_epi8(
3156 0, 1, 2, 3,
3157 4, 5, 6, 7,
3158 8, 9, 10, 11,
3159 12, 13, 14, 15);
3160 do {
3161 const __m128i str = _mm_loadu_si128((__m128i *)(e - 15));
3162 _mm_storeu_si128((__m128i *)p, _mm_shuffle_epi8(str, map));
3163 p += 16;
3164 e -= 16;
3165 } while (e - s > 15);
3166 }
3167 #elif defined(__aarch64__)
3168 if (e - s > 15) {
3169 do {
3170 const uint8x16_t str = vld1q_u8((uint8_t *)(e - 15));
3171 /* Synthesize rev128 with a rev64 + ext. */
3172 const uint8x16_t rev = vrev64q_u8(str);
3173 const uint8x16_t ext = (uint8x16_t)
3174 vextq_u64((uint64x2_t)rev, (uint64x2_t)rev, 1);
3175 vst1q_u8((uint8_t *)p, ext);
3176 p += 16;
3177 e -= 16;
3178 } while (e - s > 15);
3179 }
3180 #elif defined(_M_ARM64)
3181 if (e - s > 15) {
3182 do {
3183 const __n128 str = vld1q_u8((uint8_t *)(e - 15));
3184 /* Synthesize rev128 with a rev64 + ext. */
3185 /* strange force cast limit on windows: you cannot convert anything */
3186 const __n128 rev = vrev64q_u8(str);
3187 const __n128 ext = vextq_u64(rev, rev, 1);
3188 vst1q_u8((uint8_t *)p, ext);
3189 p += 16;
3190 e -= 16;
3191 } while (e - s > 15);
3192 }
3193 #endif
3194 while (e >= s) {
3195 *p++ = *e--;
3196 }
3197
3198 *p = '\0';
3199
3200 RETVAL_NEW_STR(n);
3201 }
3202 /* }}} */
3203
3204 /* {{{ php_similar_str */
3205 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)
3206 {
3207 const char *p, *q;
3208 const char *end1 = (char *) txt1 + len1;
3209 const char *end2 = (char *) txt2 + len2;
3210 size_t l;
3211
3212 *max = 0;
3213 *count = 0;
3214 for (p = (char *) txt1; p < end1; p++) {
3215 for (q = (char *) txt2; q < end2; q++) {
3216 for (l = 0; (p + l < end1) && (q + l < end2) && (p[l] == q[l]); l++);
3217 if (l > *max) {
3218 *max = l;
3219 *count += 1;
3220 *pos1 = p - txt1;
3221 *pos2 = q - txt2;
3222 }
3223 }
3224 }
3225 }
3226 /* }}} */
3227
3228 /* {{{ php_similar_char */
3229 static size_t php_similar_char(const char *txt1, size_t len1, const char *txt2, size_t len2)
3230 {
3231 size_t sum;
3232 size_t pos1 = 0, pos2 = 0, max, count;
3233
3234 php_similar_str(txt1, len1, txt2, len2, &pos1, &pos2, &max, &count);
3235 if ((sum = max)) {
3236 if (pos1 && pos2 && count > 1) {
3237 sum += php_similar_char(txt1, pos1,
3238 txt2, pos2);
3239 }
3240 if ((pos1 + max < len1) && (pos2 + max < len2)) {
3241 sum += php_similar_char(txt1 + pos1 + max, len1 - pos1 - max,
3242 txt2 + pos2 + max, len2 - pos2 - max);
3243 }
3244 }
3245
3246 return sum;
3247 }
3248 /* }}} */
3249
3250 /* {{{ Calculates the similarity between two strings */
3251 PHP_FUNCTION(similar_text)
3252 {
3253 zend_string *t1, *t2;
3254 zval *percent = NULL;
3255 bool compute_percentage = ZEND_NUM_ARGS() >= 3;
3256 size_t sim;
3257
3258 ZEND_PARSE_PARAMETERS_START(2, 3)
3259 Z_PARAM_STR(t1)
3260 Z_PARAM_STR(t2)
3261 Z_PARAM_OPTIONAL
3262 Z_PARAM_ZVAL(percent)
3263 ZEND_PARSE_PARAMETERS_END();
3264
3265 if (ZSTR_LEN(t1) + ZSTR_LEN(t2) == 0) {
3266 if (compute_percentage) {
3267 ZEND_TRY_ASSIGN_REF_DOUBLE(percent, 0);
3268 }
3269
3270 RETURN_LONG(0);
3271 }
3272
3273 sim = php_similar_char(ZSTR_VAL(t1), ZSTR_LEN(t1), ZSTR_VAL(t2), ZSTR_LEN(t2));
3274
3275 if (compute_percentage) {
3276 ZEND_TRY_ASSIGN_REF_DOUBLE(percent, sim * 200.0 / (ZSTR_LEN(t1) + ZSTR_LEN(t2)));
3277 }
3278
3279 RETURN_LONG(sim);
3280 }
3281 /* }}} */
3282
3283 /* {{{ 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...) */
3284 PHP_FUNCTION(addcslashes)
3285 {
3286 zend_string *str, *what;
3287
3288 ZEND_PARSE_PARAMETERS_START(2, 2)
3289 Z_PARAM_STR(str)
3290 Z_PARAM_STR(what)
3291 ZEND_PARSE_PARAMETERS_END();
3292
3293 if (ZSTR_LEN(str) == 0) {
3294 RETURN_EMPTY_STRING();
3295 }
3296
3297 if (ZSTR_LEN(what) == 0) {
3298 RETURN_STR_COPY(str);
3299 }
3300
3301 RETURN_STR(php_addcslashes_str(ZSTR_VAL(str), ZSTR_LEN(str), ZSTR_VAL(what), ZSTR_LEN(what)));
3302 }
3303 /* }}} */
3304
3305 /* {{{ Escapes single quote, double quotes and backslash characters in a string with backslashes */
3306 PHP_FUNCTION(addslashes)
3307 {
3308 zend_string *str;
3309
3310 ZEND_PARSE_PARAMETERS_START(1, 1)
3311 Z_PARAM_STR(str)
3312 ZEND_PARSE_PARAMETERS_END();
3313
3314 if (ZSTR_LEN(str) == 0) {
3315 RETURN_EMPTY_STRING();
3316 }
3317
3318 RETURN_STR(php_addslashes(str));
3319 }
3320 /* }}} */
3321
3322 /* {{{ Strips backslashes from a string. Uses C-style conventions */
3323 PHP_FUNCTION(stripcslashes)
3324 {
3325 zend_string *str;
3326
3327 ZEND_PARSE_PARAMETERS_START(1, 1)
3328 Z_PARAM_STR(str)
3329 ZEND_PARSE_PARAMETERS_END();
3330
3331 ZVAL_STRINGL(return_value, ZSTR_VAL(str), ZSTR_LEN(str));
3332 php_stripcslashes(Z_STR_P(return_value));
3333 }
3334 /* }}} */
3335
3336 /* {{{ Strips backslashes from a string */
3337 PHP_FUNCTION(stripslashes)
3338 {
3339 zend_string *str;
3340
3341 ZEND_PARSE_PARAMETERS_START(1, 1)
3342 Z_PARAM_STR(str)
3343 ZEND_PARSE_PARAMETERS_END();
3344
3345 ZVAL_STRINGL(return_value, ZSTR_VAL(str), ZSTR_LEN(str));
3346 php_stripslashes(Z_STR_P(return_value));
3347 }
3348 /* }}} */
3349
3350 /* {{{ php_stripcslashes */
3351 PHPAPI void php_stripcslashes(zend_string *str)
3352 {
3353 const char *source, *end;
3354 char *target;
3355 size_t nlen = ZSTR_LEN(str), i;
3356 char numtmp[4];
3357
3358 for (source = (char*)ZSTR_VAL(str), end = source + ZSTR_LEN(str), target = ZSTR_VAL(str); source < end; source++) {
3359 if (*source == '\\' && source + 1 < end) {
3360 source++;
3361 switch (*source) {
3362 case 'n': *target++='\n'; nlen--; break;
3363 case 'r': *target++='\r'; nlen--; break;
3364 case 'a': *target++='\a'; nlen--; break;
3365 case 't': *target++='\t'; nlen--; break;
3366 case 'v': *target++='\v'; nlen--; break;
3367 case 'b': *target++='\b'; nlen--; break;
3368 case 'f': *target++='\f'; nlen--; break;
3369 case '\\': *target++='\\'; nlen--; break;
3370 case 'x':
3371 if (source+1 < end && isxdigit((int)(*(source+1)))) {
3372 numtmp[0] = *++source;
3373 if (source+1 < end && isxdigit((int)(*(source+1)))) {
3374