Coverage Report

Created: 2026-10-28 06:20

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