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