Coverage Report

Created: 2022-02-19 20:28

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