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