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