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