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