Coverage Report

Created: 2025-12-14 06:05

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