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