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