Coverage Report

Created: 2022-10-14 11:23

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