Coverage Report

Created: 2026-06-02 06:36

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