1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2015 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Author: Tsukada Takuya <tsukada@fminn.nagano.nagano.jp>              |
16   |         Rui Hirokawa <hirokawa@php.net>                              |
17   +----------------------------------------------------------------------+
18 */
19
20/* $Id$ */
21
22/*
23 * PHP 4 Multibyte String module "mbstring"
24 *
25 * History:
26 *   2000.5.19  Release php-4.0RC2_jstring-1.0
27 *   2001.4.1   Release php4_jstring-1.0.91
28 *   2001.4.30  Release php4_jstring-1.1 (contribute to The PHP Group)
29 *   2001.5.1   Renamed from jstring to mbstring (hirokawa@php.net)
30 */
31
32/*
33 * PHP3 Internationalization support program.
34 *
35 * Copyright (c) 1999,2000 by the PHP3 internationalization team.
36 * All rights reserved.
37 *
38 * See README_PHP3-i18n-ja for more detail.
39 *
40 * Authors:
41 *    Hironori Sato <satoh@jpnnet.com>
42 *    Shigeru Kanemoto <sgk@happysize.co.jp>
43 *    Tsukada Takuya <tsukada@fminn.nagano.nagano.jp>
44 *    Rui Hirokawa <rui_hirokawa@ybb.ne.jp>
45 */
46
47/* {{{ includes */
48#ifdef HAVE_CONFIG_H
49#include "config.h"
50#endif
51
52#include "php.h"
53#include "php_ini.h"
54#include "php_variables.h"
55#include "mbstring.h"
56#include "ext/standard/php_string.h"
57#include "ext/standard/php_mail.h"
58#include "ext/standard/exec.h"
59#include "ext/standard/url.h"
60#include "main/php_output.h"
61#include "ext/standard/info.h"
62
63#include "libmbfl/mbfl/mbfl_allocators.h"
64#include "libmbfl/mbfl/mbfilter_pass.h"
65
66#include "php_variables.h"
67#include "php_globals.h"
68#include "rfc1867.h"
69#include "php_content_types.h"
70#include "SAPI.h"
71#include "php_unicode.h"
72#include "TSRM.h"
73
74#include "mb_gpc.h"
75
76#if HAVE_MBREGEX
77#include "php_mbregex.h"
78#endif
79
80#include "zend_multibyte.h"
81
82#if HAVE_ONIG
83#include "php_onig_compat.h"
84#include <oniguruma.h>
85#undef UChar
86#elif HAVE_PCRE || HAVE_BUNDLED_PCRE
87#include "ext/pcre/php_pcre.h"
88#endif
89/* }}} */
90
91#if HAVE_MBSTRING
92
93/* {{{ prototypes */
94ZEND_DECLARE_MODULE_GLOBALS(mbstring)
95
96static PHP_GINIT_FUNCTION(mbstring);
97static PHP_GSHUTDOWN_FUNCTION(mbstring);
98
99static void php_mb_populate_current_detect_order_list(void);
100
101static int php_mb_encoding_translation(void);
102
103static void php_mb_gpc_get_detect_order(const zend_encoding ***list, size_t *list_size);
104
105static void php_mb_gpc_set_input_encoding(const zend_encoding *encoding);
106
107/* }}} */
108
109/* {{{ php_mb_default_identify_list */
110typedef struct _php_mb_nls_ident_list {
111    enum mbfl_no_language lang;
112    const enum mbfl_no_encoding *list;
113    size_t list_size;
114} php_mb_nls_ident_list;
115
116static const enum mbfl_no_encoding php_mb_default_identify_list_ja[] = {
117    mbfl_no_encoding_ascii,
118    mbfl_no_encoding_jis,
119    mbfl_no_encoding_utf8,
120    mbfl_no_encoding_euc_jp,
121    mbfl_no_encoding_sjis
122};
123
124static const enum mbfl_no_encoding php_mb_default_identify_list_cn[] = {
125    mbfl_no_encoding_ascii,
126    mbfl_no_encoding_utf8,
127    mbfl_no_encoding_euc_cn,
128    mbfl_no_encoding_cp936
129};
130
131static const enum mbfl_no_encoding php_mb_default_identify_list_tw_hk[] = {
132    mbfl_no_encoding_ascii,
133    mbfl_no_encoding_utf8,
134    mbfl_no_encoding_euc_tw,
135    mbfl_no_encoding_big5
136};
137
138static const enum mbfl_no_encoding php_mb_default_identify_list_kr[] = {
139    mbfl_no_encoding_ascii,
140    mbfl_no_encoding_utf8,
141    mbfl_no_encoding_euc_kr,
142    mbfl_no_encoding_uhc
143};
144
145static const enum mbfl_no_encoding php_mb_default_identify_list_ru[] = {
146    mbfl_no_encoding_ascii,
147    mbfl_no_encoding_utf8,
148    mbfl_no_encoding_koi8r,
149    mbfl_no_encoding_cp1251,
150    mbfl_no_encoding_cp866
151};
152
153static const enum mbfl_no_encoding php_mb_default_identify_list_hy[] = {
154    mbfl_no_encoding_ascii,
155    mbfl_no_encoding_utf8,
156    mbfl_no_encoding_armscii8
157};
158
159static const enum mbfl_no_encoding php_mb_default_identify_list_tr[] = {
160    mbfl_no_encoding_ascii,
161    mbfl_no_encoding_utf8,
162    mbfl_no_encoding_cp1254,
163    mbfl_no_encoding_8859_9
164};
165
166static const enum mbfl_no_encoding php_mb_default_identify_list_ua[] = {
167    mbfl_no_encoding_ascii,
168    mbfl_no_encoding_utf8,
169    mbfl_no_encoding_koi8u
170};
171
172static const enum mbfl_no_encoding php_mb_default_identify_list_neut[] = {
173    mbfl_no_encoding_ascii,
174    mbfl_no_encoding_utf8
175};
176
177
178static const php_mb_nls_ident_list php_mb_default_identify_list[] = {
179    { mbfl_no_language_japanese, php_mb_default_identify_list_ja, sizeof(php_mb_default_identify_list_ja) / sizeof(php_mb_default_identify_list_ja[0]) },
180    { mbfl_no_language_korean, php_mb_default_identify_list_kr, sizeof(php_mb_default_identify_list_kr) / sizeof(php_mb_default_identify_list_kr[0]) },
181    { mbfl_no_language_traditional_chinese, php_mb_default_identify_list_tw_hk, sizeof(php_mb_default_identify_list_tw_hk) / sizeof(php_mb_default_identify_list_tw_hk[0]) },
182    { mbfl_no_language_simplified_chinese, php_mb_default_identify_list_cn, sizeof(php_mb_default_identify_list_cn) / sizeof(php_mb_default_identify_list_cn[0]) },
183    { mbfl_no_language_russian, php_mb_default_identify_list_ru, sizeof(php_mb_default_identify_list_ru) / sizeof(php_mb_default_identify_list_ru[0]) },
184    { mbfl_no_language_armenian, php_mb_default_identify_list_hy, sizeof(php_mb_default_identify_list_hy) / sizeof(php_mb_default_identify_list_hy[0]) },
185    { mbfl_no_language_turkish, php_mb_default_identify_list_tr, sizeof(php_mb_default_identify_list_tr) / sizeof(php_mb_default_identify_list_tr[0]) },
186    { mbfl_no_language_ukrainian, php_mb_default_identify_list_ua, sizeof(php_mb_default_identify_list_ua) / sizeof(php_mb_default_identify_list_ua[0]) },
187    { mbfl_no_language_neutral, php_mb_default_identify_list_neut, sizeof(php_mb_default_identify_list_neut) / sizeof(php_mb_default_identify_list_neut[0]) }
188};
189
190/* }}} */
191
192/* {{{ mb_overload_def mb_ovld[] */
193static const struct mb_overload_def mb_ovld[] = {
194    {MB_OVERLOAD_MAIL, "mail", "mb_send_mail", "mb_orig_mail"},
195    {MB_OVERLOAD_STRING, "strlen", "mb_strlen", "mb_orig_strlen"},
196    {MB_OVERLOAD_STRING, "strpos", "mb_strpos", "mb_orig_strpos"},
197    {MB_OVERLOAD_STRING, "strrpos", "mb_strrpos", "mb_orig_strrpos"},
198    {MB_OVERLOAD_STRING, "stripos", "mb_stripos", "mb_orig_stripos"},
199    {MB_OVERLOAD_STRING, "strripos", "mb_strripos", "mb_orig_strripos"},
200    {MB_OVERLOAD_STRING, "strstr", "mb_strstr", "mb_orig_strstr"},
201    {MB_OVERLOAD_STRING, "strrchr", "mb_strrchr", "mb_orig_strrchr"},
202    {MB_OVERLOAD_STRING, "stristr", "mb_stristr", "mb_orig_stristr"},
203    {MB_OVERLOAD_STRING, "substr", "mb_substr", "mb_orig_substr"},
204    {MB_OVERLOAD_STRING, "strtolower", "mb_strtolower", "mb_orig_strtolower"},
205    {MB_OVERLOAD_STRING, "strtoupper", "mb_strtoupper", "mb_orig_strtoupper"},
206    {MB_OVERLOAD_STRING, "substr_count", "mb_substr_count", "mb_orig_substr_count"},
207    {0, NULL, NULL, NULL}
208};
209/* }}} */
210
211/* {{{ arginfo */
212ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_language, 0, 0, 0)
213    ZEND_ARG_INFO(0, language)
214ZEND_END_ARG_INFO()
215
216ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_internal_encoding, 0, 0, 0)
217    ZEND_ARG_INFO(0, encoding)
218ZEND_END_ARG_INFO()
219
220ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_http_input, 0, 0, 0)
221    ZEND_ARG_INFO(0, type)
222ZEND_END_ARG_INFO()
223
224ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_http_output, 0, 0, 0)
225    ZEND_ARG_INFO(0, encoding)
226ZEND_END_ARG_INFO()
227
228ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_detect_order, 0, 0, 0)
229    ZEND_ARG_INFO(0, encoding)
230ZEND_END_ARG_INFO()
231
232ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_substitute_character, 0, 0, 0)
233    ZEND_ARG_INFO(0, substchar)
234ZEND_END_ARG_INFO()
235
236ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_preferred_mime_name, 0, 0, 1)
237    ZEND_ARG_INFO(0, encoding)
238ZEND_END_ARG_INFO()
239
240ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_parse_str, 0, 0, 1)
241    ZEND_ARG_INFO(0, encoded_string)
242    ZEND_ARG_INFO(1, result)
243ZEND_END_ARG_INFO()
244
245ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_output_handler, 0, 0, 2)
246    ZEND_ARG_INFO(0, contents)
247    ZEND_ARG_INFO(0, status)
248ZEND_END_ARG_INFO()
249
250ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_strlen, 0, 0, 1)
251    ZEND_ARG_INFO(0, str)
252    ZEND_ARG_INFO(0, encoding)
253ZEND_END_ARG_INFO()
254
255ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_strpos, 0, 0, 2)
256    ZEND_ARG_INFO(0, haystack)
257    ZEND_ARG_INFO(0, needle)
258    ZEND_ARG_INFO(0, offset)
259    ZEND_ARG_INFO(0, encoding)
260ZEND_END_ARG_INFO()
261
262ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_strrpos, 0, 0, 2)
263    ZEND_ARG_INFO(0, haystack)
264    ZEND_ARG_INFO(0, needle)
265    ZEND_ARG_INFO(0, offset)
266    ZEND_ARG_INFO(0, encoding)
267ZEND_END_ARG_INFO()
268
269ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_stripos, 0, 0, 2)
270    ZEND_ARG_INFO(0, haystack)
271    ZEND_ARG_INFO(0, needle)
272    ZEND_ARG_INFO(0, offset)
273    ZEND_ARG_INFO(0, encoding)
274ZEND_END_ARG_INFO()
275
276ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_strripos, 0, 0, 2)
277    ZEND_ARG_INFO(0, haystack)
278    ZEND_ARG_INFO(0, needle)
279    ZEND_ARG_INFO(0, offset)
280    ZEND_ARG_INFO(0, encoding)
281ZEND_END_ARG_INFO()
282
283ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_strstr, 0, 0, 2)
284    ZEND_ARG_INFO(0, haystack)
285    ZEND_ARG_INFO(0, needle)
286    ZEND_ARG_INFO(0, part)
287    ZEND_ARG_INFO(0, encoding)
288ZEND_END_ARG_INFO()
289
290ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_strrchr, 0, 0, 2)
291    ZEND_ARG_INFO(0, haystack)
292    ZEND_ARG_INFO(0, needle)
293    ZEND_ARG_INFO(0, part)
294    ZEND_ARG_INFO(0, encoding)
295ZEND_END_ARG_INFO()
296
297ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_stristr, 0, 0, 2)
298    ZEND_ARG_INFO(0, haystack)
299    ZEND_ARG_INFO(0, needle)
300    ZEND_ARG_INFO(0, part)
301    ZEND_ARG_INFO(0, encoding)
302ZEND_END_ARG_INFO()
303
304ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_strrichr, 0, 0, 2)
305    ZEND_ARG_INFO(0, haystack)
306    ZEND_ARG_INFO(0, needle)
307    ZEND_ARG_INFO(0, part)
308    ZEND_ARG_INFO(0, encoding)
309ZEND_END_ARG_INFO()
310
311ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_substr_count, 0, 0, 2)
312    ZEND_ARG_INFO(0, haystack)
313    ZEND_ARG_INFO(0, needle)
314    ZEND_ARG_INFO(0, encoding)
315ZEND_END_ARG_INFO()
316
317ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_substr, 0, 0, 2)
318    ZEND_ARG_INFO(0, str)
319    ZEND_ARG_INFO(0, start)
320    ZEND_ARG_INFO(0, length)
321    ZEND_ARG_INFO(0, encoding)
322ZEND_END_ARG_INFO()
323
324ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_strcut, 0, 0, 2)
325    ZEND_ARG_INFO(0, str)
326    ZEND_ARG_INFO(0, start)
327    ZEND_ARG_INFO(0, length)
328    ZEND_ARG_INFO(0, encoding)
329ZEND_END_ARG_INFO()
330
331ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_strwidth, 0, 0, 1)
332    ZEND_ARG_INFO(0, str)
333    ZEND_ARG_INFO(0, encoding)
334ZEND_END_ARG_INFO()
335
336ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_strimwidth, 0, 0, 3)
337    ZEND_ARG_INFO(0, str)
338    ZEND_ARG_INFO(0, start)
339    ZEND_ARG_INFO(0, width)
340    ZEND_ARG_INFO(0, trimmarker)
341    ZEND_ARG_INFO(0, encoding)
342ZEND_END_ARG_INFO()
343
344ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_convert_encoding, 0, 0, 2)
345    ZEND_ARG_INFO(0, str)
346    ZEND_ARG_INFO(0, to)
347    ZEND_ARG_INFO(0, from)
348ZEND_END_ARG_INFO()
349
350ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_convert_case, 0, 0, 2)
351    ZEND_ARG_INFO(0, sourcestring)
352    ZEND_ARG_INFO(0, mode)
353    ZEND_ARG_INFO(0, encoding)
354ZEND_END_ARG_INFO()
355
356ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_strtoupper, 0, 0, 1)
357    ZEND_ARG_INFO(0, sourcestring)
358    ZEND_ARG_INFO(0, encoding)
359ZEND_END_ARG_INFO()
360
361ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_strtolower, 0, 0, 1)
362    ZEND_ARG_INFO(0, sourcestring)
363    ZEND_ARG_INFO(0, encoding)
364ZEND_END_ARG_INFO()
365
366ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_detect_encoding, 0, 0, 1)
367    ZEND_ARG_INFO(0, str)
368    ZEND_ARG_INFO(0, encoding_list)
369    ZEND_ARG_INFO(0, strict)
370ZEND_END_ARG_INFO()
371
372ZEND_BEGIN_ARG_INFO(arginfo_mb_list_encodings, 0)
373ZEND_END_ARG_INFO()
374
375ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_encoding_aliases, 0, 0, 1)
376    ZEND_ARG_INFO(0, encoding)
377ZEND_END_ARG_INFO()
378
379ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_encode_mimeheader, 0, 0, 1)
380    ZEND_ARG_INFO(0, str)
381    ZEND_ARG_INFO(0, charset)
382    ZEND_ARG_INFO(0, transfer)
383    ZEND_ARG_INFO(0, linefeed)
384    ZEND_ARG_INFO(0, indent)
385ZEND_END_ARG_INFO()
386
387ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_decode_mimeheader, 0, 0, 1)
388    ZEND_ARG_INFO(0, string)
389ZEND_END_ARG_INFO()
390
391ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_convert_kana, 0, 0, 1)
392    ZEND_ARG_INFO(0, str)
393    ZEND_ARG_INFO(0, option)
394    ZEND_ARG_INFO(0, encoding)
395ZEND_END_ARG_INFO()
396
397ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_convert_variables, 0, 0, 3)
398    ZEND_ARG_INFO(0, to)
399    ZEND_ARG_INFO(0, from)
400    ZEND_ARG_VARIADIC_INFO(1, vars)
401ZEND_END_ARG_INFO()
402
403ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_encode_numericentity, 0, 0, 2)
404    ZEND_ARG_INFO(0, string)
405    ZEND_ARG_INFO(0, convmap)
406    ZEND_ARG_INFO(0, encoding)
407    ZEND_ARG_INFO(0, is_hex)
408ZEND_END_ARG_INFO()
409
410ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_decode_numericentity, 0, 0, 2)
411    ZEND_ARG_INFO(0, string)
412    ZEND_ARG_INFO(0, convmap)
413    ZEND_ARG_INFO(0, encoding)
414ZEND_END_ARG_INFO()
415
416ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_send_mail, 0, 0, 3)
417    ZEND_ARG_INFO(0, to)
418    ZEND_ARG_INFO(0, subject)
419    ZEND_ARG_INFO(0, message)
420    ZEND_ARG_INFO(0, additional_headers)
421    ZEND_ARG_INFO(0, additional_parameters)
422ZEND_END_ARG_INFO()
423
424ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_get_info, 0, 0, 0)
425    ZEND_ARG_INFO(0, type)
426ZEND_END_ARG_INFO()
427
428ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_check_encoding, 0, 0, 0)
429    ZEND_ARG_INFO(0, var)
430    ZEND_ARG_INFO(0, encoding)
431ZEND_END_ARG_INFO()
432
433ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_regex_encoding, 0, 0, 0)
434    ZEND_ARG_INFO(0, encoding)
435ZEND_END_ARG_INFO()
436
437ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_ereg, 0, 0, 2)
438    ZEND_ARG_INFO(0, pattern)
439    ZEND_ARG_INFO(0, string)
440    ZEND_ARG_INFO(1, registers)
441ZEND_END_ARG_INFO()
442
443ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_eregi, 0, 0, 2)
444    ZEND_ARG_INFO(0, pattern)
445    ZEND_ARG_INFO(0, string)
446    ZEND_ARG_INFO(1, registers)
447ZEND_END_ARG_INFO()
448
449ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_ereg_replace, 0, 0, 3)
450    ZEND_ARG_INFO(0, pattern)
451    ZEND_ARG_INFO(0, replacement)
452    ZEND_ARG_INFO(0, string)
453    ZEND_ARG_INFO(0, option)
454ZEND_END_ARG_INFO()
455
456ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_eregi_replace, 0, 0, 3)
457    ZEND_ARG_INFO(0, pattern)
458    ZEND_ARG_INFO(0, replacement)
459    ZEND_ARG_INFO(0, string)
460ZEND_END_ARG_INFO()
461
462ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_ereg_replace_callback, 0, 0, 3)
463    ZEND_ARG_INFO(0, pattern)
464    ZEND_ARG_INFO(0, callback)
465    ZEND_ARG_INFO(0, string)
466    ZEND_ARG_INFO(0, option)
467ZEND_END_ARG_INFO()
468
469ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_split, 0, 0, 2)
470    ZEND_ARG_INFO(0, pattern)
471    ZEND_ARG_INFO(0, string)
472    ZEND_ARG_INFO(0, limit)
473ZEND_END_ARG_INFO()
474
475ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_ereg_match, 0, 0, 2)
476    ZEND_ARG_INFO(0, pattern)
477    ZEND_ARG_INFO(0, string)
478    ZEND_ARG_INFO(0, option)
479ZEND_END_ARG_INFO()
480
481ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_ereg_search, 0, 0, 0)
482    ZEND_ARG_INFO(0, pattern)
483    ZEND_ARG_INFO(0, option)
484ZEND_END_ARG_INFO()
485
486ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_ereg_search_pos, 0, 0, 0)
487    ZEND_ARG_INFO(0, pattern)
488    ZEND_ARG_INFO(0, option)
489ZEND_END_ARG_INFO()
490
491ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_ereg_search_regs, 0, 0, 0)
492    ZEND_ARG_INFO(0, pattern)
493    ZEND_ARG_INFO(0, option)
494ZEND_END_ARG_INFO()
495
496ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_ereg_search_init, 0, 0, 1)
497    ZEND_ARG_INFO(0, string)
498    ZEND_ARG_INFO(0, pattern)
499    ZEND_ARG_INFO(0, option)
500ZEND_END_ARG_INFO()
501
502ZEND_BEGIN_ARG_INFO(arginfo_mb_ereg_search_getregs, 0)
503ZEND_END_ARG_INFO()
504
505ZEND_BEGIN_ARG_INFO(arginfo_mb_ereg_search_getpos, 0)
506ZEND_END_ARG_INFO()
507
508ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_ereg_search_setpos, 0, 0, 1)
509    ZEND_ARG_INFO(0, position)
510ZEND_END_ARG_INFO()
511
512ZEND_BEGIN_ARG_INFO_EX(arginfo_mb_regex_set_options, 0, 0, 0)
513    ZEND_ARG_INFO(0, options)
514ZEND_END_ARG_INFO()
515/* }}} */
516
517/* {{{ zend_function_entry mbstring_functions[] */
518const zend_function_entry mbstring_functions[] = {
519    PHP_FE(mb_convert_case,         arginfo_mb_convert_case)
520    PHP_FE(mb_strtoupper,           arginfo_mb_strtoupper)
521    PHP_FE(mb_strtolower,           arginfo_mb_strtolower)
522    PHP_FE(mb_language,             arginfo_mb_language)
523    PHP_FE(mb_internal_encoding,    arginfo_mb_internal_encoding)
524    PHP_FE(mb_http_input,           arginfo_mb_http_input)
525    PHP_FE(mb_http_output,          arginfo_mb_http_output)
526    PHP_FE(mb_detect_order,         arginfo_mb_detect_order)
527    PHP_FE(mb_substitute_character, arginfo_mb_substitute_character)
528    PHP_FE(mb_parse_str,            arginfo_mb_parse_str)
529    PHP_FE(mb_output_handler,       arginfo_mb_output_handler)
530    PHP_FE(mb_preferred_mime_name,  arginfo_mb_preferred_mime_name)
531    PHP_FE(mb_strlen,               arginfo_mb_strlen)
532    PHP_FE(mb_strpos,               arginfo_mb_strpos)
533    PHP_FE(mb_strrpos,              arginfo_mb_strrpos)
534    PHP_FE(mb_stripos,              arginfo_mb_stripos)
535    PHP_FE(mb_strripos,             arginfo_mb_strripos)
536    PHP_FE(mb_strstr,               arginfo_mb_strstr)
537    PHP_FE(mb_strrchr,              arginfo_mb_strrchr)
538    PHP_FE(mb_stristr,              arginfo_mb_stristr)
539    PHP_FE(mb_strrichr,             arginfo_mb_strrichr)
540    PHP_FE(mb_substr_count,         arginfo_mb_substr_count)
541    PHP_FE(mb_substr,               arginfo_mb_substr)
542    PHP_FE(mb_strcut,               arginfo_mb_strcut)
543    PHP_FE(mb_strwidth,             arginfo_mb_strwidth)
544    PHP_FE(mb_strimwidth,           arginfo_mb_strimwidth)
545    PHP_FE(mb_convert_encoding,     arginfo_mb_convert_encoding)
546    PHP_FE(mb_detect_encoding,      arginfo_mb_detect_encoding)
547    PHP_FE(mb_list_encodings,       arginfo_mb_list_encodings)
548    PHP_FE(mb_encoding_aliases,     arginfo_mb_encoding_aliases)
549    PHP_FE(mb_convert_kana,         arginfo_mb_convert_kana)
550    PHP_FE(mb_encode_mimeheader,    arginfo_mb_encode_mimeheader)
551    PHP_FE(mb_decode_mimeheader,    arginfo_mb_decode_mimeheader)
552    PHP_FE(mb_convert_variables,    arginfo_mb_convert_variables)
553    PHP_FE(mb_encode_numericentity, arginfo_mb_encode_numericentity)
554    PHP_FE(mb_decode_numericentity, arginfo_mb_decode_numericentity)
555    PHP_FE(mb_send_mail,            arginfo_mb_send_mail)
556    PHP_FE(mb_get_info,             arginfo_mb_get_info)
557    PHP_FE(mb_check_encoding,       arginfo_mb_check_encoding)
558#if HAVE_MBREGEX
559    PHP_MBREGEX_FUNCTION_ENTRIES
560#endif
561    PHP_FE_END
562};
563/* }}} */
564
565/* {{{ zend_module_entry mbstring_module_entry */
566zend_module_entry mbstring_module_entry = {
567    STANDARD_MODULE_HEADER,
568    "mbstring",
569    mbstring_functions,
570    PHP_MINIT(mbstring),
571    PHP_MSHUTDOWN(mbstring),
572    PHP_RINIT(mbstring),
573    PHP_RSHUTDOWN(mbstring),
574    PHP_MINFO(mbstring),
575    PHP_MBSTRING_VERSION,
576    PHP_MODULE_GLOBALS(mbstring),
577    PHP_GINIT(mbstring),
578    PHP_GSHUTDOWN(mbstring),
579    NULL,
580    STANDARD_MODULE_PROPERTIES_EX
581};
582/* }}} */
583
584/* {{{ static sapi_post_entry php_post_entries[] */
585static sapi_post_entry php_post_entries[] = {
586    { DEFAULT_POST_CONTENT_TYPE, sizeof(DEFAULT_POST_CONTENT_TYPE)-1, sapi_read_standard_form_data, php_std_post_handler },
587    { MULTIPART_CONTENT_TYPE,    sizeof(MULTIPART_CONTENT_TYPE)-1,    NULL,                         rfc1867_post_handler },
588    { NULL, 0, NULL, NULL }
589};
590/* }}} */
591
592#ifdef COMPILE_DL_MBSTRING
593#ifdef ZTS
594ZEND_TSRMLS_CACHE_DEFINE();
595#endif
596ZEND_GET_MODULE(mbstring)
597#endif
598
599static char *get_internal_encoding(void) {
600    if (PG(internal_encoding) && PG(internal_encoding)[0]) {
601        return PG(internal_encoding);
602    } else if (SG(default_charset)) {
603        return SG(default_charset);
604    }
605    return "";
606}
607
608static char *get_input_encoding(void) {
609    if (PG(input_encoding) && PG(input_encoding)[0]) {
610        return PG(input_encoding);
611    } else if (SG(default_charset)) {
612        return SG(default_charset);
613    }
614    return "";
615}
616
617static char *get_output_encoding(void) {
618    if (PG(output_encoding) && PG(output_encoding)[0]) {
619        return PG(output_encoding);
620    } else if (SG(default_charset)) {
621        return SG(default_charset);
622    }
623    return "";
624}
625
626
627/* {{{ allocators */
628static void *_php_mb_allocators_malloc(unsigned int sz)
629{
630    return emalloc(sz);
631}
632
633static void *_php_mb_allocators_realloc(void *ptr, unsigned int sz)
634{
635    return erealloc(ptr, sz);
636}
637
638static void *_php_mb_allocators_calloc(unsigned int nelems, unsigned int szelem)
639{
640    return ecalloc(nelems, szelem);
641}
642
643static void _php_mb_allocators_free(void *ptr)
644{
645    efree(ptr);
646}
647
648static void *_php_mb_allocators_pmalloc(unsigned int sz)
649{
650    return pemalloc(sz, 1);
651}
652
653static void *_php_mb_allocators_prealloc(void *ptr, unsigned int sz)
654{
655    return perealloc(ptr, sz, 1);
656}
657
658static void _php_mb_allocators_pfree(void *ptr)
659{
660    pefree(ptr, 1);
661}
662
663static mbfl_allocators _php_mb_allocators = {
664    _php_mb_allocators_malloc,
665    _php_mb_allocators_realloc,
666    _php_mb_allocators_calloc,
667    _php_mb_allocators_free,
668    _php_mb_allocators_pmalloc,
669    _php_mb_allocators_prealloc,
670    _php_mb_allocators_pfree
671};
672/* }}} */
673
674/* {{{ static sapi_post_entry mbstr_post_entries[] */
675static sapi_post_entry mbstr_post_entries[] = {
676    { DEFAULT_POST_CONTENT_TYPE, sizeof(DEFAULT_POST_CONTENT_TYPE)-1, sapi_read_standard_form_data, php_mb_post_handler },
677    { MULTIPART_CONTENT_TYPE,    sizeof(MULTIPART_CONTENT_TYPE)-1,    NULL,                         rfc1867_post_handler },
678    { NULL, 0, NULL, NULL }
679};
680/* }}} */
681
682/* {{{ static int php_mb_parse_encoding_list()
683 *  Return 0 if input contains any illegal encoding, otherwise 1.
684 *  Even if any illegal encoding is detected the result may contain a list
685 *  of parsed encodings.
686 */
687static int
688php_mb_parse_encoding_list(const char *value, size_t value_length, const mbfl_encoding ***return_list, size_t *return_size, int persistent)
689{
690    int size, bauto, ret = SUCCESS;
691    size_t n;
692    char *p, *p1, *p2, *endp, *tmpstr;
693    const mbfl_encoding **entry, **list;
694
695    list = NULL;
696    if (value == NULL || value_length <= 0) {
697        if (return_list) {
698            *return_list = NULL;
699        }
700        if (return_size) {
701            *return_size = 0;
702        }
703        return FAILURE;
704    } else {
705        /* copy the value string for work */
706        if (value[0]=='"' && value[value_length-1]=='"' && value_length>2) {
707            tmpstr = (char *)estrndup(value+1, value_length-2);
708            value_length -= 2;
709        }
710        else
711            tmpstr = (char *)estrndup(value, value_length);
712        if (tmpstr == NULL) {
713            return FAILURE;
714        }
715        /* count the number of listed encoding names */
716        endp = tmpstr + value_length;
717        n = 1;
718        p1 = tmpstr;
719        while ((p2 = (char*)php_memnstr(p1, ",", 1, endp)) != NULL) {
720            p1 = p2 + 1;
721            n++;
722        }
723        size = n + MBSTRG(default_detect_order_list_size);
724        /* make list */
725        list = (const mbfl_encoding **)pecalloc(size, sizeof(mbfl_encoding*), persistent);
726        if (list != NULL) {
727            entry = list;
728            n = 0;
729            bauto = 0;
730            p1 = tmpstr;
731            do {
732                p2 = p = (char*)php_memnstr(p1, ",", 1, endp);
733                if (p == NULL) {
734                    p = endp;
735                }
736                *p = '\0';
737                /* trim spaces */
738                while (p1 < p && (*p1 == ' ' || *p1 == '\t')) {
739                    p1++;
740                }
741                p--;
742                while (p > p1 && (*p == ' ' || *p == '\t')) {
743                    *p = '\0';
744                    p--;
745                }
746                /* convert to the encoding number and check encoding */
747                if (strcasecmp(p1, "auto") == 0) {
748                    if (!bauto) {
749                        const enum mbfl_no_encoding *src = MBSTRG(default_detect_order_list);
750                        const size_t identify_list_size = MBSTRG(default_detect_order_list_size);
751                        size_t i;
752                        bauto = 1;
753                        for (i = 0; i < identify_list_size; i++) {
754                            *entry++ = mbfl_no2encoding(*src++);
755                            n++;
756                        }
757                    }
758                } else {
759                    const mbfl_encoding *encoding = mbfl_name2encoding(p1);
760                    if (encoding) {
761                        *entry++ = encoding;
762                        n++;
763                    } else {
764                        ret = 0;
765                    }
766                }
767                p1 = p2 + 1;
768            } while (n < size && p2 != NULL);
769            if (n > 0) {
770                if (return_list) {
771                    *return_list = list;
772                } else {
773                    pefree(list, persistent);
774                }
775            } else {
776                pefree(list, persistent);
777                if (return_list) {
778                    *return_list = NULL;
779                }
780                ret = 0;
781            }
782            if (return_size) {
783                *return_size = n;
784            }
785        } else {
786            if (return_list) {
787                *return_list = NULL;
788            }
789            if (return_size) {
790                *return_size = 0;
791            }
792            ret = 0;
793        }
794        efree(tmpstr);
795    }
796
797    return ret;
798}
799/* }}} */
800
801/* {{{ static int php_mb_parse_encoding_array()
802 *  Return 0 if input contains any illegal encoding, otherwise 1.
803 *  Even if any illegal encoding is detected the result may contain a list
804 *  of parsed encodings.
805 */
806static int
807php_mb_parse_encoding_array(zval *array, const mbfl_encoding ***return_list, size_t *return_size, int persistent)
808{
809    zval *hash_entry;
810    HashTable *target_hash;
811    int i, n, size, bauto, ret = SUCCESS;
812    const mbfl_encoding **list, **entry;
813
814    list = NULL;
815    if (Z_TYPE_P(array) == IS_ARRAY) {
816        target_hash = Z_ARRVAL_P(array);
817        i = zend_hash_num_elements(target_hash);
818        size = i + MBSTRG(default_detect_order_list_size);
819        list = (const mbfl_encoding **)pecalloc(size, sizeof(mbfl_encoding*), persistent);
820        if (list != NULL) {
821            entry = list;
822            bauto = 0;
823            n = 0;
824            ZEND_HASH_FOREACH_VAL(target_hash, hash_entry) {
825                convert_to_string_ex(hash_entry);
826                if (strcasecmp(Z_STRVAL_P(hash_entry), "auto") == 0) {
827                    if (!bauto) {
828                        const enum mbfl_no_encoding *src = MBSTRG(default_detect_order_list);
829                        const size_t identify_list_size = MBSTRG(default_detect_order_list_size);
830                        size_t j;
831
832                        bauto = 1;
833                        for (j = 0; j < identify_list_size; j++) {
834                            *entry++ = mbfl_no2encoding(*src++);
835                            n++;
836                        }
837                    }
838                } else {
839                    const mbfl_encoding *encoding = mbfl_name2encoding(Z_STRVAL_P(hash_entry));
840                    if (encoding) {
841                        *entry++ = encoding;
842                        n++;
843                    } else {
844                        ret = FAILURE;
845                    }
846                }
847                i--;
848            } ZEND_HASH_FOREACH_END();
849            if (n > 0) {
850                if (return_list) {
851                    *return_list = list;
852                } else {
853                    pefree(list, persistent);
854                }
855            } else {
856                pefree(list, persistent);
857                if (return_list) {
858                    *return_list = NULL;
859                }
860                ret = FAILURE;
861            }
862            if (return_size) {
863                *return_size = n;
864            }
865        } else {
866            if (return_list) {
867                *return_list = NULL;
868            }
869            if (return_size) {
870                *return_size = 0;
871            }
872            ret = FAILURE;
873        }
874    }
875
876    return ret;
877}
878/* }}} */
879
880/* {{{ zend_multibyte interface */
881static const zend_encoding* php_mb_zend_encoding_fetcher(const char *encoding_name)
882{
883    return (const zend_encoding*)mbfl_name2encoding(encoding_name);
884}
885
886static const char *php_mb_zend_encoding_name_getter(const zend_encoding *encoding)
887{
888    return ((const mbfl_encoding *)encoding)->name;
889}
890
891static int php_mb_zend_encoding_lexer_compatibility_checker(const zend_encoding *_encoding)
892{
893    const mbfl_encoding *encoding = (const mbfl_encoding*)_encoding;
894    if (encoding->flag & MBFL_ENCTYPE_SBCS) {
895        return 1;
896    }
897    if ((encoding->flag & (MBFL_ENCTYPE_MBCS | MBFL_ENCTYPE_GL_UNSAFE)) == MBFL_ENCTYPE_MBCS) {
898        return 1;
899    }
900    return 0;
901}
902
903static const zend_encoding *php_mb_zend_encoding_detector(const unsigned char *arg_string, size_t arg_length, const zend_encoding **list, size_t list_size)
904{
905    mbfl_string string;
906
907    if (!list) {
908        list = (const zend_encoding **)MBSTRG(current_detect_order_list);
909        list_size = MBSTRG(current_detect_order_list_size);
910    }
911
912    mbfl_string_init(&string);
913    string.no_language = MBSTRG(language);
914    string.val = (unsigned char *)arg_string;
915    string.len = arg_length;
916    return (const zend_encoding *) mbfl_identify_encoding2(&string, (const mbfl_encoding **)list, list_size, 0);
917}
918
919static size_t php_mb_zend_encoding_converter(unsigned char **to, size_t *to_length, const unsigned char *from, size_t from_length, const zend_encoding *encoding_to, const zend_encoding *encoding_from)
920{
921    mbfl_string string, result;
922    mbfl_buffer_converter *convd;
923    int status, loc;
924
925    /* new encoding */
926    /* initialize string */
927    mbfl_string_init(&string);
928    mbfl_string_init(&result);
929    string.no_encoding = ((const mbfl_encoding*)encoding_from)->no_encoding;
930    string.no_language = MBSTRG(language);
931    string.val = (unsigned char*)from;
932    string.len = from_length;
933
934    /* initialize converter */
935    convd = mbfl_buffer_converter_new2((const mbfl_encoding *)encoding_from, (const mbfl_encoding *)encoding_to, string.len);
936    if (convd == NULL) {
937        return -1;
938    }
939    mbfl_buffer_converter_illegal_mode(convd, MBSTRG(current_filter_illegal_mode));
940    mbfl_buffer_converter_illegal_substchar(convd, MBSTRG(current_filter_illegal_substchar));
941
942    /* do it */
943    status = mbfl_buffer_converter_feed2(convd, &string, &loc);
944    if (status) {
945        mbfl_buffer_converter_delete(convd);
946        return (size_t)-1;
947    }
948
949    mbfl_buffer_converter_flush(convd);
950    if (!mbfl_buffer_converter_result(convd, &result)) {
951        mbfl_buffer_converter_delete(convd);
952        return (size_t)-1;
953    }
954
955    *to = result.val;
956    *to_length = result.len;
957
958    mbfl_buffer_converter_delete(convd);
959
960    return loc;
961}
962
963static int php_mb_zend_encoding_list_parser(const char *encoding_list, size_t encoding_list_len, const zend_encoding ***return_list, size_t *return_size, int persistent)
964{
965    return php_mb_parse_encoding_list(encoding_list, encoding_list_len, (const mbfl_encoding ***)return_list, return_size, persistent);
966}
967
968static const zend_encoding *php_mb_zend_internal_encoding_getter(void)
969{
970    return (const zend_encoding *)MBSTRG(internal_encoding);
971}
972
973static int php_mb_zend_internal_encoding_setter(const zend_encoding *encoding)
974{
975    MBSTRG(internal_encoding) = (const mbfl_encoding *)encoding;
976    return SUCCESS;
977}
978
979static zend_multibyte_functions php_mb_zend_multibyte_functions = {
980    "mbstring",
981    php_mb_zend_encoding_fetcher,
982    php_mb_zend_encoding_name_getter,
983    php_mb_zend_encoding_lexer_compatibility_checker,
984    php_mb_zend_encoding_detector,
985    php_mb_zend_encoding_converter,
986    php_mb_zend_encoding_list_parser,
987    php_mb_zend_internal_encoding_getter,
988    php_mb_zend_internal_encoding_setter
989};
990/* }}} */
991
992static void *_php_mb_compile_regex(const char *pattern);
993static int _php_mb_match_regex(void *opaque, const char *str, size_t str_len);
994static void _php_mb_free_regex(void *opaque);
995
996#if HAVE_ONIG
997/* {{{ _php_mb_compile_regex */
998static void *_php_mb_compile_regex(const char *pattern)
999{
1000    php_mb_regex_t *retval;
1001    OnigErrorInfo err_info;
1002    int err_code;
1003
1004    if ((err_code = onig_new(&retval,
1005            (const OnigUChar *)pattern,
1006            (const OnigUChar *)pattern + strlen(pattern),
1007            ONIG_OPTION_IGNORECASE | ONIG_OPTION_DONT_CAPTURE_GROUP,
1008            ONIG_ENCODING_ASCII, &OnigSyntaxPerl, &err_info))) {
1009        OnigUChar err_str[ONIG_MAX_ERROR_MESSAGE_LEN];
1010        onig_error_code_to_str(err_str, err_code, err_info);
1011        php_error_docref(NULL, E_WARNING, "%s: %s", pattern, err_str);
1012        retval = NULL;
1013    }
1014    return retval;
1015}
1016/* }}} */
1017
1018/* {{{ _php_mb_match_regex */
1019static int _php_mb_match_regex(void *opaque, const char *str, size_t str_len)
1020{
1021    return onig_search((php_mb_regex_t *)opaque, (const OnigUChar *)str,
1022            (const OnigUChar*)str + str_len, (const OnigUChar *)str,
1023            (const OnigUChar*)str + str_len, NULL, ONIG_OPTION_NONE) >= 0;
1024}
1025/* }}} */
1026
1027/* {{{ _php_mb_free_regex */
1028static void _php_mb_free_regex(void *opaque)
1029{
1030    onig_free((php_mb_regex_t *)opaque);
1031}
1032/* }}} */
1033#elif HAVE_PCRE || HAVE_BUNDLED_PCRE
1034/* {{{ _php_mb_compile_regex */
1035static void *_php_mb_compile_regex(const char *pattern)
1036{
1037    pcre *retval;
1038    const char *err_str;
1039    int err_offset;
1040
1041    if (!(retval = pcre_compile(pattern,
1042            PCRE_CASELESS, &err_str, &err_offset, NULL))) {
1043        php_error_docref(NULL, E_WARNING, "%s (offset=%d): %s", pattern, err_offset, err_str);
1044    }
1045    return retval;
1046}
1047/* }}} */
1048
1049/* {{{ _php_mb_match_regex */
1050static int _php_mb_match_regex(void *opaque, const char *str, size_t str_len)
1051{
1052    return pcre_exec((pcre *)opaque, NULL, str, (int)str_len, 0,
1053            0, NULL, 0) >= 0;
1054}
1055/* }}} */
1056
1057/* {{{ _php_mb_free_regex */
1058static void _php_mb_free_regex(void *opaque)
1059{
1060    pcre_free(opaque);
1061}
1062/* }}} */
1063#endif
1064
1065/* {{{ php_mb_nls_get_default_detect_order_list */
1066static int php_mb_nls_get_default_detect_order_list(enum mbfl_no_language lang, enum mbfl_no_encoding **plist, size_t *plist_size)
1067{
1068    size_t i;
1069
1070    *plist = (enum mbfl_no_encoding *) php_mb_default_identify_list_neut;
1071    *plist_size = sizeof(php_mb_default_identify_list_neut) / sizeof(php_mb_default_identify_list_neut[0]);
1072
1073    for (i = 0; i < sizeof(php_mb_default_identify_list) / sizeof(php_mb_default_identify_list[0]); i++) {
1074        if (php_mb_default_identify_list[i].lang == lang) {
1075            *plist = (enum mbfl_no_encoding *)php_mb_default_identify_list[i].list;
1076            *plist_size = php_mb_default_identify_list[i].list_size;
1077            return 1;
1078        }
1079    }
1080    return 0;
1081}
1082/* }}} */
1083
1084static char *php_mb_rfc1867_substring_conf(const zend_encoding *encoding, char *start, int len, char quote)
1085{
1086    char *result = emalloc(len + 2);
1087    char *resp = result;
1088    int i;
1089
1090    for (i = 0; i < len && start[i] != quote; ++i) {
1091        if (start[i] == '\\' && (start[i + 1] == '\\' || (quote && start[i + 1] == quote))) {
1092            *resp++ = start[++i];
1093        } else {
1094            size_t j = php_mb_mbchar_bytes_ex(start+i, (const mbfl_encoding *)encoding);
1095
1096            while (j-- > 0 && i < len) {
1097                *resp++ = start[i++];
1098            }
1099            --i;
1100        }
1101    }
1102
1103    *resp = '\0';
1104    return result;
1105}
1106
1107static char *php_mb_rfc1867_getword(const zend_encoding *encoding, char **line, char stop) /* {{{ */
1108{
1109    char *pos = *line, quote;
1110    char *res;
1111
1112    while (*pos && *pos != stop) {
1113        if ((quote = *pos) == '"' || quote == '\'') {
1114            ++pos;
1115            while (*pos && *pos != quote) {
1116                if (*pos == '\\' && pos[1] && pos[1] == quote) {
1117                    pos += 2;
1118                } else {
1119                    ++pos;
1120                }
1121            }
1122            if (*pos) {
1123                ++pos;
1124            }
1125        } else {
1126            pos += php_mb_mbchar_bytes_ex(pos, (const mbfl_encoding *)encoding);
1127
1128        }
1129    }
1130    if (*pos == '\0') {
1131        res = estrdup(*line);
1132        *line += strlen(*line);
1133        return res;
1134    }
1135
1136    res = estrndup(*line, pos - *line);
1137
1138    while (*pos == stop) {
1139        pos += php_mb_mbchar_bytes_ex(pos, (const mbfl_encoding *)encoding);
1140    }
1141
1142    *line = pos;
1143    return res;
1144}
1145/* }}} */
1146
1147static char *php_mb_rfc1867_getword_conf(const zend_encoding *encoding, char *str) /* {{{ */
1148{
1149    while (*str && isspace(*(unsigned char *)str)) {
1150        ++str;
1151    }
1152
1153    if (!*str) {
1154        return estrdup("");
1155    }
1156
1157    if (*str == '"' || *str == '\'') {
1158        char quote = *str;
1159
1160        str++;
1161        return php_mb_rfc1867_substring_conf(encoding, str, strlen(str), quote);
1162    } else {
1163        char *strend = str;
1164
1165        while (*strend && !isspace(*(unsigned char *)strend)) {
1166            ++strend;
1167        }
1168        return php_mb_rfc1867_substring_conf(encoding, str, strend - str, 0);
1169    }
1170}
1171/* }}} */
1172
1173static char *php_mb_rfc1867_basename(const zend_encoding *encoding, char *filename) /* {{{ */
1174{
1175    char *s, *s2;
1176    const size_t filename_len = strlen(filename);
1177
1178    /* The \ check should technically be needed for win32 systems only where
1179     * it is a valid path separator. However, IE in all it's wisdom always sends
1180     * the full path of the file on the user's filesystem, which means that unless
1181     * the user does basename() they get a bogus file name. Until IE's user base drops
1182     * to nill or problem is fixed this code must remain enabled for all systems. */
1183    s = php_mb_safe_strrchr_ex(filename, '\\', filename_len, (const mbfl_encoding *)encoding);
1184    s2 = php_mb_safe_strrchr_ex(filename, '/', filename_len, (const mbfl_encoding *)encoding);
1185
1186    if (s && s2) {
1187        if (s > s2) {
1188            return ++s;
1189        } else {
1190            return ++s2;
1191        }
1192    } else if (s) {
1193        return ++s;
1194    } else if (s2) {
1195        return ++s2;
1196    } else {
1197        return filename;
1198    }
1199}
1200/* }}} */
1201
1202/* {{{ php.ini directive handler */
1203/* {{{ static PHP_INI_MH(OnUpdate_mbstring_language) */
1204static PHP_INI_MH(OnUpdate_mbstring_language)
1205{
1206    enum mbfl_no_language no_language;
1207
1208    no_language = mbfl_name2no_language(new_value->val);
1209    if (no_language == mbfl_no_language_invalid) {
1210        MBSTRG(language) = mbfl_no_language_neutral;
1211        return FAILURE;
1212    }
1213    MBSTRG(language) = no_language;
1214    php_mb_nls_get_default_detect_order_list(no_language, &MBSTRG(default_detect_order_list), &MBSTRG(default_detect_order_list_size));
1215    return SUCCESS;
1216}
1217/* }}} */
1218
1219/* {{{ static PHP_INI_MH(OnUpdate_mbstring_detect_order) */
1220static PHP_INI_MH(OnUpdate_mbstring_detect_order)
1221{
1222    const mbfl_encoding **list;
1223    size_t size;
1224
1225    if (!new_value) {
1226        if (MBSTRG(detect_order_list)) {
1227            pefree(MBSTRG(detect_order_list), 1);
1228        }
1229        MBSTRG(detect_order_list) = NULL;
1230        MBSTRG(detect_order_list_size) = 0;
1231        return SUCCESS;
1232    }
1233
1234    if (FAILURE == php_mb_parse_encoding_list(new_value->val, new_value->len, &list, &size, 1)) {
1235        return FAILURE;
1236    }
1237
1238    if (MBSTRG(detect_order_list)) {
1239        pefree(MBSTRG(detect_order_list), 1);
1240    }
1241    MBSTRG(detect_order_list) = list;
1242    MBSTRG(detect_order_list_size) = size;
1243    return SUCCESS;
1244}
1245/* }}} */
1246
1247/* {{{ static PHP_INI_MH(OnUpdate_mbstring_http_input) */
1248static PHP_INI_MH(OnUpdate_mbstring_http_input)
1249{
1250    const mbfl_encoding **list;
1251    size_t size;
1252
1253    if (!new_value) {
1254        if (MBSTRG(http_input_list)) {
1255            pefree(MBSTRG(http_input_list), 1);
1256        }
1257        if (SUCCESS == php_mb_parse_encoding_list(get_input_encoding(), strlen(get_input_encoding())+1, &list, &size, 1)) {
1258            MBSTRG(http_input_list) = list;
1259            MBSTRG(http_input_list_size) = size;
1260            return SUCCESS;
1261        }
1262        MBSTRG(http_input_list) = NULL;
1263        MBSTRG(http_input_list_size) = 0;
1264        return SUCCESS;
1265    }
1266
1267    if (FAILURE == php_mb_parse_encoding_list(new_value->val, new_value->len, &list, &size, 1)) {
1268        return FAILURE;
1269    }
1270
1271    if (MBSTRG(http_input_list)) {
1272        pefree(MBSTRG(http_input_list), 1);
1273    }
1274    MBSTRG(http_input_list) = list;
1275    MBSTRG(http_input_list_size) = size;
1276
1277    if (stage & (PHP_INI_STAGE_ACTIVATE | PHP_INI_STAGE_RUNTIME)) {
1278        php_error_docref("ref.mbstring", E_DEPRECATED, "Use of mbstring.http_input is deprecated");
1279    }
1280
1281    return SUCCESS;
1282}
1283/* }}} */
1284
1285/* {{{ static PHP_INI_MH(OnUpdate_mbstring_http_output) */
1286static PHP_INI_MH(OnUpdate_mbstring_http_output)
1287{
1288    const mbfl_encoding *encoding;
1289
1290    if (new_value == NULL || new_value->len == 0) {
1291        encoding = mbfl_name2encoding(get_output_encoding());
1292        if (!encoding) {
1293            MBSTRG(http_output_encoding) = &mbfl_encoding_pass;
1294            MBSTRG(current_http_output_encoding) = &mbfl_encoding_pass;
1295            return SUCCESS;
1296        }
1297    } else {
1298        encoding = mbfl_name2encoding(new_value->val);
1299        if (!encoding) {
1300            MBSTRG(http_output_encoding) = &mbfl_encoding_pass;
1301            MBSTRG(current_http_output_encoding) = &mbfl_encoding_pass;
1302            return FAILURE;
1303        }
1304    }
1305    MBSTRG(http_output_encoding) = encoding;
1306    MBSTRG(current_http_output_encoding) = encoding;
1307
1308    if (stage & (PHP_INI_STAGE_ACTIVATE | PHP_INI_STAGE_RUNTIME)) {
1309        php_error_docref("ref.mbstring", E_DEPRECATED, "Use of mbstring.http_output is deprecated");
1310    }
1311
1312    return SUCCESS;
1313}
1314/* }}} */
1315
1316/* {{{ static _php_mb_ini_mbstring_internal_encoding_set */
1317int _php_mb_ini_mbstring_internal_encoding_set(const char *new_value, uint new_value_length)
1318{
1319    const mbfl_encoding *encoding;
1320
1321    if (!new_value || new_value_length == 0 || !(encoding = mbfl_name2encoding(new_value))) {
1322        /* falls back to UTF-8 if an unknown encoding name is given */
1323        encoding = mbfl_no2encoding(mbfl_no_encoding_utf8);
1324    }
1325    MBSTRG(internal_encoding) = encoding;
1326    MBSTRG(current_internal_encoding) = encoding;
1327#if HAVE_MBREGEX
1328    {
1329        const char *enc_name = new_value;
1330        if (FAILURE == php_mb_regex_set_default_mbctype(enc_name)) {
1331            /* falls back to UTF-8 if an unknown encoding name is given */
1332            enc_name = "UTF-8";
1333            php_mb_regex_set_default_mbctype(enc_name);
1334        }
1335        php_mb_regex_set_mbctype(new_value);
1336    }
1337#endif
1338    return SUCCESS;
1339}
1340/* }}} */
1341
1342/* {{{ static PHP_INI_MH(OnUpdate_mbstring_internal_encoding) */
1343static PHP_INI_MH(OnUpdate_mbstring_internal_encoding)
1344{
1345    if (stage & (PHP_INI_STAGE_ACTIVATE | PHP_INI_STAGE_RUNTIME)) {
1346        php_error_docref("ref.mbstring", E_DEPRECATED, "Use of mbstring.internal_encoding is deprecated");
1347    }
1348
1349    if (OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage) == FAILURE) {
1350        return FAILURE;
1351    }
1352
1353    if (stage & (PHP_INI_STAGE_STARTUP | PHP_INI_STAGE_SHUTDOWN | PHP_INI_STAGE_RUNTIME)) {
1354        if (new_value && new_value->len) {
1355            return _php_mb_ini_mbstring_internal_encoding_set(new_value->val, new_value->len);
1356        } else {
1357            return _php_mb_ini_mbstring_internal_encoding_set(get_internal_encoding(), strlen(get_internal_encoding())+1);
1358        }
1359    } else {
1360        /* the corresponding mbstring globals needs to be set according to the
1361         * ini value in the later stage because it never falls back to the
1362         * default value if 1. no value for mbstring.internal_encoding is given,
1363         * 2. mbstring.language directive is processed in per-dir or runtime
1364         * context and 3. call to the handler for mbstring.language is done
1365         * after mbstring.internal_encoding is handled. */
1366        return SUCCESS;
1367    }
1368}
1369/* }}} */
1370
1371/* {{{ static PHP_INI_MH(OnUpdate_mbstring_substitute_character) */
1372static PHP_INI_MH(OnUpdate_mbstring_substitute_character)
1373{
1374    int c;
1375    char *endptr = NULL;
1376
1377    if (new_value != NULL) {
1378        if (strcasecmp("none", new_value->val) == 0) {
1379            MBSTRG(filter_illegal_mode) = MBFL_OUTPUTFILTER_ILLEGAL_MODE_NONE;
1380            MBSTRG(current_filter_illegal_mode) = MBFL_OUTPUTFILTER_ILLEGAL_MODE_NONE;
1381        } else if (strcasecmp("long", new_value->val) == 0) {
1382            MBSTRG(filter_illegal_mode) = MBFL_OUTPUTFILTER_ILLEGAL_MODE_LONG;
1383            MBSTRG(current_filter_illegal_mode) = MBFL_OUTPUTFILTER_ILLEGAL_MODE_LONG;
1384        } else if (strcasecmp("entity", new_value->val) == 0) {
1385            MBSTRG(filter_illegal_mode) = MBFL_OUTPUTFILTER_ILLEGAL_MODE_ENTITY;
1386            MBSTRG(current_filter_illegal_mode) = MBFL_OUTPUTFILTER_ILLEGAL_MODE_ENTITY;
1387        } else {
1388            MBSTRG(filter_illegal_mode) = MBFL_OUTPUTFILTER_ILLEGAL_MODE_CHAR;
1389            MBSTRG(current_filter_illegal_mode) = MBFL_OUTPUTFILTER_ILLEGAL_MODE_CHAR;
1390            if (new_value->len >0) {
1391                c = strtol(new_value->val, &endptr, 0);
1392                if (*endptr == '\0') {
1393                    MBSTRG(filter_illegal_substchar) = c;
1394                    MBSTRG(current_filter_illegal_substchar) = c;
1395                }
1396            }
1397        }
1398    } else {
1399        MBSTRG(filter_illegal_mode) = MBFL_OUTPUTFILTER_ILLEGAL_MODE_CHAR;
1400        MBSTRG(current_filter_illegal_mode) = MBFL_OUTPUTFILTER_ILLEGAL_MODE_CHAR;
1401        MBSTRG(filter_illegal_substchar) = 0x3f;    /* '?' */
1402        MBSTRG(current_filter_illegal_substchar) = 0x3f;    /* '?' */
1403    }
1404
1405    return SUCCESS;
1406}
1407/* }}} */
1408
1409/* {{{ static PHP_INI_MH(OnUpdate_mbstring_encoding_translation) */
1410static PHP_INI_MH(OnUpdate_mbstring_encoding_translation)
1411{
1412    if (new_value == NULL) {
1413        return FAILURE;
1414    }
1415
1416    OnUpdateBool(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
1417
1418    if (MBSTRG(encoding_translation)) {
1419        sapi_unregister_post_entry(php_post_entries);
1420        sapi_register_post_entries(mbstr_post_entries);
1421    } else {
1422        sapi_unregister_post_entry(mbstr_post_entries);
1423        sapi_register_post_entries(php_post_entries);
1424    }
1425
1426    return SUCCESS;
1427}
1428/* }}} */
1429
1430/* {{{ static PHP_INI_MH(OnUpdate_mbstring_http_output_conv_mimetypes */
1431static PHP_INI_MH(OnUpdate_mbstring_http_output_conv_mimetypes)
1432{
1433    zend_string *tmp;
1434    void *re = NULL;
1435
1436    if (!new_value) {
1437        new_value = entry->orig_value;
1438    }
1439    tmp = php_trim(new_value, NULL, 0, 3);
1440
1441    if (tmp->len > 0) {
1442        if (!(re = _php_mb_compile_regex(tmp->val))) {
1443            zend_string_release(tmp);
1444            return FAILURE;
1445        }
1446    }
1447
1448    if (MBSTRG(http_output_conv_mimetypes)) {
1449        _php_mb_free_regex(MBSTRG(http_output_conv_mimetypes));
1450    }
1451
1452    MBSTRG(http_output_conv_mimetypes) = re;
1453
1454    zend_string_release(tmp);
1455    return SUCCESS;
1456}
1457/* }}} */
1458/* }}} */
1459
1460/* {{{ php.ini directive registration */
1461PHP_INI_BEGIN()
1462    PHP_INI_ENTRY("mbstring.language", "neutral", PHP_INI_ALL, OnUpdate_mbstring_language)
1463    PHP_INI_ENTRY("mbstring.detect_order", NULL, PHP_INI_ALL, OnUpdate_mbstring_detect_order)
1464    PHP_INI_ENTRY("mbstring.http_input", NULL, PHP_INI_ALL, OnUpdate_mbstring_http_input)
1465    PHP_INI_ENTRY("mbstring.http_output", NULL, PHP_INI_ALL, OnUpdate_mbstring_http_output)
1466    STD_PHP_INI_ENTRY("mbstring.internal_encoding", NULL, PHP_INI_ALL, OnUpdate_mbstring_internal_encoding, internal_encoding_name, zend_mbstring_globals, mbstring_globals)
1467    PHP_INI_ENTRY("mbstring.substitute_character", NULL, PHP_INI_ALL, OnUpdate_mbstring_substitute_character)
1468    STD_PHP_INI_ENTRY("mbstring.func_overload", "0",
1469    PHP_INI_SYSTEM, OnUpdateLong, func_overload, zend_mbstring_globals, mbstring_globals)
1470
1471    STD_PHP_INI_BOOLEAN("mbstring.encoding_translation", "0",
1472        PHP_INI_SYSTEM | PHP_INI_PERDIR,
1473        OnUpdate_mbstring_encoding_translation,
1474        encoding_translation, zend_mbstring_globals, mbstring_globals)
1475    PHP_INI_ENTRY("mbstring.http_output_conv_mimetypes",
1476        "^(text/|application/xhtml\\+xml)",
1477        PHP_INI_ALL,
1478        OnUpdate_mbstring_http_output_conv_mimetypes)
1479
1480    STD_PHP_INI_BOOLEAN("mbstring.strict_detection", "0",
1481        PHP_INI_ALL,
1482        OnUpdateLong,
1483        strict_detection, zend_mbstring_globals, mbstring_globals)
1484PHP_INI_END()
1485/* }}} */
1486
1487/* {{{ module global initialize handler */
1488static PHP_GINIT_FUNCTION(mbstring)
1489{
1490#if defined(COMPILE_DL_MBSTRING) && defined(ZTS)
1491ZEND_TSRMLS_CACHE_UPDATE();
1492#endif
1493
1494    mbstring_globals->language = mbfl_no_language_uni;
1495    mbstring_globals->internal_encoding = NULL;
1496    mbstring_globals->current_internal_encoding = mbstring_globals->internal_encoding;
1497    mbstring_globals->http_output_encoding = &mbfl_encoding_pass;
1498    mbstring_globals->current_http_output_encoding = &mbfl_encoding_pass;
1499    mbstring_globals->http_input_identify = NULL;
1500    mbstring_globals->http_input_identify_get = NULL;
1501    mbstring_globals->http_input_identify_post = NULL;
1502    mbstring_globals->http_input_identify_cookie = NULL;
1503    mbstring_globals->http_input_identify_string = NULL;
1504    mbstring_globals->http_input_list = NULL;
1505    mbstring_globals->http_input_list_size = 0;
1506    mbstring_globals->detect_order_list = NULL;
1507    mbstring_globals->detect_order_list_size = 0;
1508    mbstring_globals->current_detect_order_list = NULL;
1509    mbstring_globals->current_detect_order_list_size = 0;
1510    mbstring_globals->default_detect_order_list = (enum mbfl_no_encoding *) php_mb_default_identify_list_neut;
1511    mbstring_globals->default_detect_order_list_size = sizeof(php_mb_default_identify_list_neut) / sizeof(php_mb_default_identify_list_neut[0]);
1512    mbstring_globals->filter_illegal_mode = MBFL_OUTPUTFILTER_ILLEGAL_MODE_CHAR;
1513    mbstring_globals->filter_illegal_substchar = 0x3f;  /* '?' */
1514    mbstring_globals->current_filter_illegal_mode = MBFL_OUTPUTFILTER_ILLEGAL_MODE_CHAR;
1515    mbstring_globals->current_filter_illegal_substchar = 0x3f;  /* '?' */
1516    mbstring_globals->illegalchars = 0;
1517    mbstring_globals->func_overload = 0;
1518    mbstring_globals->encoding_translation = 0;
1519    mbstring_globals->strict_detection = 0;
1520    mbstring_globals->outconv = NULL;
1521    mbstring_globals->http_output_conv_mimetypes = NULL;
1522#if HAVE_MBREGEX
1523    mbstring_globals->mb_regex_globals = php_mb_regex_globals_alloc();
1524#endif
1525}
1526/* }}} */
1527
1528/* {{{ PHP_GSHUTDOWN_FUNCTION */
1529static PHP_GSHUTDOWN_FUNCTION(mbstring)
1530{
1531    if (mbstring_globals->http_input_list) {
1532        free(mbstring_globals->http_input_list);
1533    }
1534    if (mbstring_globals->detect_order_list) {
1535        free(mbstring_globals->detect_order_list);
1536    }
1537    if (mbstring_globals->http_output_conv_mimetypes) {
1538        _php_mb_free_regex(mbstring_globals->http_output_conv_mimetypes);
1539    }
1540#if HAVE_MBREGEX
1541    php_mb_regex_globals_free(mbstring_globals->mb_regex_globals);
1542#endif
1543}
1544/* }}} */
1545
1546/* {{{ PHP_MINIT_FUNCTION(mbstring) */
1547PHP_MINIT_FUNCTION(mbstring)
1548{
1549#if defined(COMPILE_DL_MBSTRING) && defined(ZTS)
1550ZEND_TSRMLS_CACHE_UPDATE();
1551#endif
1552    __mbfl_allocators = &_php_mb_allocators;
1553
1554    REGISTER_INI_ENTRIES();
1555
1556    /* This is a global handler. Should not be set in a per-request handler. */
1557    sapi_register_treat_data(mbstr_treat_data);
1558
1559    /* Post handlers are stored in the thread-local context. */
1560    if (MBSTRG(encoding_translation)) {
1561        sapi_register_post_entries(mbstr_post_entries);
1562    }
1563
1564    REGISTER_LONG_CONSTANT("MB_OVERLOAD_MAIL", MB_OVERLOAD_MAIL, CONST_CS | CONST_PERSISTENT);
1565    REGISTER_LONG_CONSTANT("MB_OVERLOAD_STRING", MB_OVERLOAD_STRING, CONST_CS | CONST_PERSISTENT);
1566    REGISTER_LONG_CONSTANT("MB_OVERLOAD_REGEX", MB_OVERLOAD_REGEX, CONST_CS | CONST_PERSISTENT);
1567
1568    REGISTER_LONG_CONSTANT("MB_CASE_UPPER", PHP_UNICODE_CASE_UPPER, CONST_CS | CONST_PERSISTENT);
1569    REGISTER_LONG_CONSTANT("MB_CASE_LOWER", PHP_UNICODE_CASE_LOWER, CONST_CS | CONST_PERSISTENT);
1570    REGISTER_LONG_CONSTANT("MB_CASE_TITLE", PHP_UNICODE_CASE_TITLE, CONST_CS | CONST_PERSISTENT);
1571
1572#if HAVE_MBREGEX
1573    PHP_MINIT(mb_regex) (INIT_FUNC_ARGS_PASSTHRU);
1574#endif
1575
1576    if (FAILURE == zend_multibyte_set_functions(&php_mb_zend_multibyte_functions)) {
1577        return FAILURE;
1578    }
1579
1580    php_rfc1867_set_multibyte_callbacks(
1581        php_mb_encoding_translation,
1582        php_mb_gpc_get_detect_order,
1583        php_mb_gpc_set_input_encoding,
1584        php_mb_rfc1867_getword,
1585        php_mb_rfc1867_getword_conf,
1586        php_mb_rfc1867_basename);
1587
1588    return SUCCESS;
1589}
1590/* }}} */
1591
1592/* {{{ PHP_MSHUTDOWN_FUNCTION(mbstring) */
1593PHP_MSHUTDOWN_FUNCTION(mbstring)
1594{
1595    UNREGISTER_INI_ENTRIES();
1596
1597#if HAVE_MBREGEX
1598    PHP_MSHUTDOWN(mb_regex) (INIT_FUNC_ARGS_PASSTHRU);
1599#endif
1600
1601    return SUCCESS;
1602}
1603/* }}} */
1604
1605/* {{{ PHP_RINIT_FUNCTION(mbstring) */
1606PHP_RINIT_FUNCTION(mbstring)
1607{
1608    zend_function *func, *orig;
1609    const struct mb_overload_def *p;
1610
1611    MBSTRG(current_internal_encoding) = MBSTRG(internal_encoding);
1612    MBSTRG(current_http_output_encoding) = MBSTRG(http_output_encoding);
1613    MBSTRG(current_filter_illegal_mode) = MBSTRG(filter_illegal_mode);
1614    MBSTRG(current_filter_illegal_substchar) = MBSTRG(filter_illegal_substchar);
1615
1616    MBSTRG(illegalchars) = 0;
1617
1618    php_mb_populate_current_detect_order_list();
1619
1620    /* override original function. */
1621    if (MBSTRG(func_overload)){
1622        p = &(mb_ovld[0]);
1623
1624        CG(compiler_options) |= ZEND_COMPILE_NO_BUILTIN_STRLEN;
1625        while (p->type > 0) {
1626            if ((MBSTRG(func_overload) & p->type) == p->type &&
1627                !zend_hash_str_exists(EG(function_table), p->save_func, strlen(p->save_func))
1628            ) {
1629                func = zend_hash_str_find_ptr(EG(function_table), p->ovld_func, strlen(p->ovld_func));
1630
1631                if ((orig = zend_hash_str_find_ptr(EG(function_table), p->orig_func, strlen(p->orig_func))) == NULL) {
1632                    php_error_docref("ref.mbstring", E_WARNING, "mbstring couldn't find function %s.", p->orig_func);
1633                    return FAILURE;
1634                } else {
1635                    ZEND_ASSERT(orig->type == ZEND_INTERNAL_FUNCTION);
1636                    zend_hash_str_add_mem(EG(function_table), p->save_func, strlen(p->save_func), orig, sizeof(zend_internal_function));
1637                    function_add_ref(orig);
1638
1639                    if (zend_hash_str_update_mem(EG(function_table), p->orig_func, strlen(p->orig_func), func, sizeof(zend_internal_function)) == NULL) {
1640                        php_error_docref("ref.mbstring", E_WARNING, "mbstring couldn't replace function %s.", p->orig_func);
1641                        return FAILURE;
1642                    }
1643
1644                    function_add_ref(func);
1645                }
1646            }
1647            p++;
1648        }
1649    }
1650#if HAVE_MBREGEX
1651    PHP_RINIT(mb_regex) (INIT_FUNC_ARGS_PASSTHRU);
1652#endif
1653    zend_multibyte_set_internal_encoding((const zend_encoding *)MBSTRG(internal_encoding));
1654
1655    return SUCCESS;
1656}
1657/* }}} */
1658
1659/* {{{ PHP_RSHUTDOWN_FUNCTION(mbstring) */
1660PHP_RSHUTDOWN_FUNCTION(mbstring)
1661{
1662    const struct mb_overload_def *p;
1663    zend_function *orig;
1664
1665    if (MBSTRG(current_detect_order_list) != NULL) {
1666        efree(MBSTRG(current_detect_order_list));
1667        MBSTRG(current_detect_order_list) = NULL;
1668        MBSTRG(current_detect_order_list_size) = 0;
1669    }
1670    if (MBSTRG(outconv) != NULL) {
1671        MBSTRG(illegalchars) += mbfl_buffer_illegalchars(MBSTRG(outconv));
1672        mbfl_buffer_converter_delete(MBSTRG(outconv));
1673        MBSTRG(outconv) = NULL;
1674    }
1675
1676    /* clear http input identification. */
1677    MBSTRG(http_input_identify) = NULL;
1678    MBSTRG(http_input_identify_post) = NULL;
1679    MBSTRG(http_input_identify_get) = NULL;
1680    MBSTRG(http_input_identify_cookie) = NULL;
1681    MBSTRG(http_input_identify_string) = NULL;
1682
1683    /*  clear overloaded function. */
1684    if (MBSTRG(func_overload)){
1685        p = &(mb_ovld[0]);
1686        while (p->type > 0) {
1687            if ((MBSTRG(func_overload) & p->type) == p->type &&
1688                (orig = zend_hash_str_find_ptr(EG(function_table), p->save_func, strlen(p->save_func)))) {
1689
1690                zend_hash_str_update_mem(EG(function_table), p->orig_func, strlen(p->orig_func), orig, sizeof(zend_internal_function));
1691                function_add_ref(orig);
1692                zend_hash_str_del(EG(function_table), p->save_func, strlen(p->save_func));
1693            }
1694            p++;
1695        }
1696        CG(compiler_options) &= ~ZEND_COMPILE_NO_BUILTIN_STRLEN;
1697    }
1698
1699#if HAVE_MBREGEX
1700    PHP_RSHUTDOWN(mb_regex) (INIT_FUNC_ARGS_PASSTHRU);
1701#endif
1702
1703    return SUCCESS;
1704}
1705/* }}} */
1706
1707/* {{{ PHP_MINFO_FUNCTION(mbstring) */
1708PHP_MINFO_FUNCTION(mbstring)
1709{
1710    php_info_print_table_start();
1711    php_info_print_table_row(2, "Multibyte Support", "enabled");
1712    php_info_print_table_row(2, "Multibyte string engine", "libmbfl");
1713    php_info_print_table_row(2, "HTTP input encoding translation", MBSTRG(encoding_translation) ? "enabled": "disabled");
1714    {
1715        char tmp[256];
1716        snprintf(tmp, sizeof(tmp), "%d.%d.%d", MBFL_VERSION_MAJOR, MBFL_VERSION_MINOR, MBFL_VERSION_TEENY);
1717        php_info_print_table_row(2, "libmbfl version", tmp);
1718    }
1719    {
1720        char tmp[256];
1721        snprintf(tmp, sizeof(tmp), "%d.%d.%d", ONIGURUMA_VERSION_MAJOR, ONIGURUMA_VERSION_MINOR, ONIGURUMA_VERSION_TEENY);
1722        php_info_print_table_row(2, "oniguruma version", tmp);
1723    }
1724    php_info_print_table_end();
1725
1726    php_info_print_table_start();
1727    php_info_print_table_header(1, "mbstring extension makes use of \"streamable kanji code filter and converter\", which is distributed under the GNU Lesser General Public License version 2.1.");
1728    php_info_print_table_end();
1729
1730#if HAVE_MBREGEX
1731    PHP_MINFO(mb_regex)(ZEND_MODULE_INFO_FUNC_ARGS_PASSTHRU);
1732#endif
1733
1734    DISPLAY_INI_ENTRIES();
1735}
1736/* }}} */
1737
1738/* {{{ proto string mb_language([string language])
1739   Sets the current language or Returns the current language as a string */
1740PHP_FUNCTION(mb_language)
1741{
1742    zend_string *name = NULL;
1743
1744    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S", &name) == FAILURE) {
1745        return;
1746    }
1747    if (name == NULL) {
1748        RETVAL_STRING((char *)mbfl_no_language2name(MBSTRG(language)));
1749    } else {
1750        zend_string *ini_name = zend_string_init("mbstring.language", sizeof("mbstring.language") - 1, 0);
1751        if (FAILURE == zend_alter_ini_entry(ini_name, name, PHP_INI_USER, PHP_INI_STAGE_RUNTIME)) {
1752            php_error_docref(NULL, E_WARNING, "Unknown language \"%s\"", name->val);
1753            RETVAL_FALSE;
1754        } else {
1755            RETVAL_TRUE;
1756        }
1757        zend_string_release(ini_name);
1758    }
1759}
1760/* }}} */
1761
1762/* {{{ proto string mb_internal_encoding([string encoding])
1763   Sets the current internal encoding or Returns the current internal encoding as a string */
1764PHP_FUNCTION(mb_internal_encoding)
1765{
1766    const char *name = NULL;
1767    size_t name_len;
1768    const mbfl_encoding *encoding;
1769
1770    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s", &name, &name_len) == FAILURE) {
1771        return;
1772    }
1773    if (name == NULL) {
1774        name = MBSTRG(current_internal_encoding) ? MBSTRG(current_internal_encoding)->name: NULL;
1775        if (name != NULL) {
1776            RETURN_STRING(name);
1777        } else {
1778            RETURN_FALSE;
1779        }
1780    } else {
1781        encoding = mbfl_name2encoding(name);
1782        if (!encoding) {
1783            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", name);
1784            RETURN_FALSE;
1785        } else {
1786            MBSTRG(current_internal_encoding) = encoding;
1787            RETURN_TRUE;
1788        }
1789    }
1790}
1791/* }}} */
1792
1793/* {{{ proto mixed mb_http_input([string type])
1794   Returns the input encoding */
1795PHP_FUNCTION(mb_http_input)
1796{
1797    char *typ = NULL;
1798    size_t typ_len;
1799    int retname;
1800    char *list, *temp;
1801    const mbfl_encoding *result = NULL;
1802
1803    retname = 1;
1804    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s", &typ, &typ_len) == FAILURE) {
1805        return;
1806    }
1807    if (typ == NULL) {
1808        result = MBSTRG(http_input_identify);
1809    } else {
1810        switch (*typ) {
1811        case 'G':
1812        case 'g':
1813            result = MBSTRG(http_input_identify_get);
1814            break;
1815        case 'P':
1816        case 'p':
1817            result = MBSTRG(http_input_identify_post);
1818            break;
1819        case 'C':
1820        case 'c':
1821            result = MBSTRG(http_input_identify_cookie);
1822            break;
1823        case 'S':
1824        case 's':
1825            result = MBSTRG(http_input_identify_string);
1826            break;
1827        case 'I':
1828        case 'i':
1829            {
1830                const mbfl_encoding **entry = MBSTRG(http_input_list);
1831                const size_t n = MBSTRG(http_input_list_size);
1832                size_t i;
1833                array_init(return_value);
1834                for (i = 0; i < n; i++) {
1835                    add_next_index_string(return_value, (*entry)->name);
1836                    entry++;
1837                }
1838                retname = 0;
1839            }
1840            break;
1841        case 'L':
1842        case 'l':
1843            {
1844                const mbfl_encoding **entry = MBSTRG(http_input_list);
1845                const size_t n = MBSTRG(http_input_list_size);
1846                size_t i;
1847                list = NULL;
1848                for (i = 0; i < n; i++) {
1849                    if (list) {
1850                        temp = list;
1851                        spprintf(&list, 0, "%s,%s", temp, (*entry)->name);
1852                        efree(temp);
1853                        if (!list) {
1854                            break;
1855                        }
1856                    } else {
1857                        list = estrdup((*entry)->name);
1858                    }
1859                    entry++;
1860                }
1861            }
1862            if (!list) {
1863                RETURN_FALSE;
1864            }
1865            RETVAL_STRING(list);
1866            efree(list);
1867            retname = 0;
1868            break;
1869        default:
1870            result = MBSTRG(http_input_identify);
1871            break;
1872        }
1873    }
1874
1875    if (retname) {
1876        if (result) {
1877            RETVAL_STRING(result->name);
1878        } else {
1879            RETVAL_FALSE;
1880        }
1881    }
1882}
1883/* }}} */
1884
1885/* {{{ proto string mb_http_output([string encoding])
1886   Sets the current output_encoding or returns the current output_encoding as a string */
1887PHP_FUNCTION(mb_http_output)
1888{
1889    const char *name = NULL;
1890    size_t name_len;
1891    const mbfl_encoding *encoding;
1892
1893    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s", &name, &name_len) == FAILURE) {
1894        return;
1895    }
1896
1897    if (name == NULL) {
1898        name = MBSTRG(current_http_output_encoding) ? MBSTRG(current_http_output_encoding)->name: NULL;
1899        if (name != NULL) {
1900            RETURN_STRING(name);
1901        } else {
1902            RETURN_FALSE;
1903        }
1904    } else {
1905        encoding = mbfl_name2encoding(name);
1906        if (!encoding) {
1907            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", name);
1908            RETURN_FALSE;
1909        } else {
1910            MBSTRG(current_http_output_encoding) = encoding;
1911            RETURN_TRUE;
1912        }
1913    }
1914}
1915/* }}} */
1916
1917/* {{{ proto bool|array mb_detect_order([mixed encoding-list])
1918   Sets the current detect_order or Return the current detect_order as a array */
1919PHP_FUNCTION(mb_detect_order)
1920{
1921    zval *arg1 = NULL;
1922
1923    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|z", &arg1) == FAILURE) {
1924        return;
1925    }
1926
1927    if (!arg1) {
1928        size_t i;
1929        size_t n = MBSTRG(current_detect_order_list_size);
1930        const mbfl_encoding **entry = MBSTRG(current_detect_order_list);
1931        array_init(return_value);
1932        for (i = 0; i < n; i++) {
1933            add_next_index_string(return_value, (*entry)->name);
1934            entry++;
1935        }
1936    } else {
1937        const mbfl_encoding **list = NULL;
1938        size_t size = 0;
1939        switch (Z_TYPE_P(arg1)) {
1940            case IS_ARRAY:
1941                if (FAILURE == php_mb_parse_encoding_array(arg1, &list, &size, 0)) {
1942                    if (list) {
1943                        efree(list);
1944                    }
1945                    RETURN_FALSE;
1946                }
1947                break;
1948            default:
1949                convert_to_string_ex(arg1);
1950                if (FAILURE == php_mb_parse_encoding_list(Z_STRVAL_P(arg1), Z_STRLEN_P(arg1), &list, &size, 0)) {
1951                    if (list) {
1952                        efree(list);
1953                    }
1954                    RETURN_FALSE;
1955                }
1956                break;
1957        }
1958
1959        if (list == NULL) {
1960            RETURN_FALSE;
1961        }
1962
1963        if (MBSTRG(current_detect_order_list)) {
1964            efree(MBSTRG(current_detect_order_list));
1965        }
1966        MBSTRG(current_detect_order_list) = list;
1967        MBSTRG(current_detect_order_list_size) = size;
1968        RETURN_TRUE;
1969    }
1970}
1971/* }}} */
1972
1973/* {{{ proto mixed mb_substitute_character([mixed substchar])
1974   Sets the current substitute_character or returns the current substitute_character */
1975PHP_FUNCTION(mb_substitute_character)
1976{
1977    zval *arg1 = NULL;
1978
1979    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|z", &arg1) == FAILURE) {
1980        return;
1981    }
1982
1983    if (!arg1) {
1984        if (MBSTRG(current_filter_illegal_mode) == MBFL_OUTPUTFILTER_ILLEGAL_MODE_NONE) {
1985            RETURN_STRING("none");
1986        } else if (MBSTRG(current_filter_illegal_mode) == MBFL_OUTPUTFILTER_ILLEGAL_MODE_LONG) {
1987            RETURN_STRING("long");
1988        } else if (MBSTRG(current_filter_illegal_mode) == MBFL_OUTPUTFILTER_ILLEGAL_MODE_ENTITY) {
1989            RETURN_STRING("entity");
1990        } else {
1991            RETURN_LONG(MBSTRG(current_filter_illegal_substchar));
1992        }
1993    } else {
1994        RETVAL_TRUE;
1995
1996        switch (Z_TYPE_P(arg1)) {
1997            case IS_STRING:
1998                if (strncasecmp("none", Z_STRVAL_P(arg1), Z_STRLEN_P(arg1)) == 0) {
1999                    MBSTRG(current_filter_illegal_mode) = MBFL_OUTPUTFILTER_ILLEGAL_MODE_NONE;
2000                } else if (strncasecmp("long", Z_STRVAL_P(arg1), Z_STRLEN_P(arg1)) == 0) {
2001                    MBSTRG(current_filter_illegal_mode) = MBFL_OUTPUTFILTER_ILLEGAL_MODE_LONG;
2002                } else if (strncasecmp("entity", Z_STRVAL_P(arg1), Z_STRLEN_P(arg1)) == 0) {
2003                    MBSTRG(current_filter_illegal_mode) = MBFL_OUTPUTFILTER_ILLEGAL_MODE_ENTITY;
2004                } else {
2005                    convert_to_long_ex(arg1);
2006
2007                    if (Z_LVAL_P(arg1) < 0xffff && Z_LVAL_P(arg1) > 0x0) {
2008                        MBSTRG(current_filter_illegal_mode) = MBFL_OUTPUTFILTER_ILLEGAL_MODE_CHAR;
2009                        MBSTRG(current_filter_illegal_substchar) = Z_LVAL_P(arg1);
2010                    } else {
2011                        php_error_docref(NULL, E_WARNING, "Unknown character.");
2012                        RETURN_FALSE;
2013                    }
2014                }
2015                break;
2016            default:
2017                convert_to_long_ex(arg1);
2018                if (Z_LVAL_P(arg1) < 0xffff && Z_LVAL_P(arg1) > 0x0) {
2019                    MBSTRG(current_filter_illegal_mode) = MBFL_OUTPUTFILTER_ILLEGAL_MODE_CHAR;
2020                    MBSTRG(current_filter_illegal_substchar) = Z_LVAL_P(arg1);
2021                } else {
2022                    php_error_docref(NULL, E_WARNING, "Unknown character.");
2023                    RETURN_FALSE;
2024                }
2025                break;
2026        }
2027    }
2028}
2029/* }}} */
2030
2031/* {{{ proto string mb_preferred_mime_name(string encoding)
2032   Return the preferred MIME name (charset) as a string */
2033PHP_FUNCTION(mb_preferred_mime_name)
2034{
2035    enum mbfl_no_encoding no_encoding;
2036    char *name = NULL;
2037    size_t name_len;
2038
2039    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
2040        return;
2041    } else {
2042        no_encoding = mbfl_name2no_encoding(name);
2043        if (no_encoding == mbfl_no_encoding_invalid) {
2044            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", name);
2045            RETVAL_FALSE;
2046        } else {
2047            const char *preferred_name = mbfl_no2preferred_mime_name(no_encoding);
2048            if (preferred_name == NULL || *preferred_name == '\0') {
2049                php_error_docref(NULL, E_WARNING, "No MIME preferred name corresponding to \"%s\"", name);
2050                RETVAL_FALSE;
2051            } else {
2052                RETVAL_STRING((char *)preferred_name);
2053            }
2054        }
2055    }
2056}
2057/* }}} */
2058
2059#define IS_SJIS1(c) ((((c)>=0x81 && (c)<=0x9f) || ((c)>=0xe0 && (c)<=0xf5)) ? 1 : 0)
2060#define IS_SJIS2(c) ((((c)>=0x40 && (c)<=0x7e) || ((c)>=0x80 && (c)<=0xfc)) ? 1 : 0)
2061
2062/* {{{ proto bool mb_parse_str(string encoded_string [, array result])
2063   Parses GET/POST/COOKIE data and sets global variables */
2064PHP_FUNCTION(mb_parse_str)
2065{
2066    zval *track_vars_array = NULL;
2067    char *encstr = NULL;
2068    size_t encstr_len;
2069    php_mb_encoding_handler_info_t info;
2070    const mbfl_encoding *detected;
2071
2072    track_vars_array = NULL;
2073    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|z/", &encstr, &encstr_len, &track_vars_array) == FAILURE) {
2074        return;
2075    }
2076
2077    if (track_vars_array != NULL) {
2078        /* Clear out the array */
2079        zval_dtor(track_vars_array);
2080        array_init(track_vars_array);
2081    }
2082
2083    encstr = estrndup(encstr, encstr_len);
2084
2085    info.data_type              = PARSE_STRING;
2086    info.separator              = PG(arg_separator).input;
2087    info.report_errors          = 1;
2088    info.to_encoding            = MBSTRG(current_internal_encoding);
2089    info.to_language            = MBSTRG(language);
2090    info.from_encodings         = MBSTRG(http_input_list);
2091    info.num_from_encodings     = MBSTRG(http_input_list_size);
2092    info.from_language          = MBSTRG(language);
2093
2094    if (track_vars_array != NULL) {
2095        detected = _php_mb_encoding_handler_ex(&info, track_vars_array, encstr);
2096    } else {
2097        zval tmp;
2098        zend_array *symbol_table = zend_rebuild_symbol_table();
2099
2100        ZVAL_ARR(&tmp, symbol_table);
2101        detected = _php_mb_encoding_handler_ex(&info, &tmp, encstr);
2102    }
2103
2104    MBSTRG(http_input_identify) = detected;
2105
2106    RETVAL_BOOL(detected);
2107
2108    if (encstr != NULL) efree(encstr);
2109}
2110/* }}} */
2111
2112/* {{{ proto string mb_output_handler(string contents, int status)
2113   Returns string in output buffer converted to the http_output encoding */
2114PHP_FUNCTION(mb_output_handler)
2115{
2116    char *arg_string;
2117    size_t arg_string_len;
2118    zend_long arg_status;
2119    mbfl_string string, result;
2120    const char *charset;
2121    char *p;
2122    const mbfl_encoding *encoding;
2123    int last_feed, len;
2124    unsigned char send_text_mimetype = 0;
2125    char *s, *mimetype = NULL;
2126
2127    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl", &arg_string, &arg_string_len, &arg_status) == FAILURE) {
2128        return;
2129    }
2130
2131    encoding = MBSTRG(current_http_output_encoding);
2132
2133    /* start phase only */
2134    if ((arg_status & PHP_OUTPUT_HANDLER_START) != 0) {
2135        /* delete the converter just in case. */
2136        if (MBSTRG(outconv)) {
2137            MBSTRG(illegalchars) += mbfl_buffer_illegalchars(MBSTRG(outconv));
2138            mbfl_buffer_converter_delete(MBSTRG(outconv));
2139            MBSTRG(outconv) = NULL;
2140        }
2141        if (encoding == &mbfl_encoding_pass) {
2142            RETURN_STRINGL(arg_string, arg_string_len);
2143        }
2144
2145        /* analyze mime type */
2146        if (SG(sapi_headers).mimetype &&
2147            _php_mb_match_regex(
2148                MBSTRG(http_output_conv_mimetypes),
2149                SG(sapi_headers).mimetype,
2150                strlen(SG(sapi_headers).mimetype))) {
2151            if ((s = strchr(SG(sapi_headers).mimetype,';')) == NULL){
2152                mimetype = estrdup(SG(sapi_headers).mimetype);
2153            } else {
2154                mimetype = estrndup(SG(sapi_headers).mimetype,s-SG(sapi_headers).mimetype);
2155            }
2156            send_text_mimetype = 1;
2157        } else if (SG(sapi_headers).send_default_content_type) {
2158            mimetype = SG(default_mimetype) ? SG(default_mimetype) : SAPI_DEFAULT_MIMETYPE;
2159        }
2160
2161        /* if content-type is not yet set, set it and activate the converter */
2162        if (SG(sapi_headers).send_default_content_type || send_text_mimetype) {
2163            charset = encoding->mime_name;
2164            if (charset) {
2165                len = spprintf( &p, 0, "Content-Type: %s; charset=%s",  mimetype, charset );
2166                if (sapi_add_header(p, len, 0) != FAILURE) {
2167                    SG(sapi_headers).send_default_content_type = 0;
2168                }
2169            }
2170            /* activate the converter */
2171            MBSTRG(outconv) = mbfl_buffer_converter_new2(MBSTRG(current_internal_encoding), encoding, 0);
2172            if (send_text_mimetype){
2173                efree(mimetype);
2174            }
2175        }
2176    }
2177
2178    /* just return if the converter is not activated. */
2179    if (MBSTRG(outconv) == NULL) {
2180        RETURN_STRINGL(arg_string, arg_string_len);
2181    }
2182
2183    /* flag */
2184    last_feed = ((arg_status & PHP_OUTPUT_HANDLER_END) != 0);
2185    /* mode */
2186    mbfl_buffer_converter_illegal_mode(MBSTRG(outconv), MBSTRG(current_filter_illegal_mode));
2187    mbfl_buffer_converter_illegal_substchar(MBSTRG(outconv), MBSTRG(current_filter_illegal_substchar));
2188
2189    /* feed the string */
2190    mbfl_string_init(&string);
2191    /* these are not needed. convd has encoding info.
2192    string.no_language = MBSTRG(language);
2193    string.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2194    */
2195    string.val = (unsigned char *)arg_string;
2196    string.len = arg_string_len;
2197    mbfl_buffer_converter_feed(MBSTRG(outconv), &string);
2198    if (last_feed) {
2199        mbfl_buffer_converter_flush(MBSTRG(outconv));
2200    }
2201    /* get the converter output, and return it */
2202    mbfl_buffer_converter_result(MBSTRG(outconv), &result);
2203    // TODO: avoid reallocation ???
2204    RETVAL_STRINGL((char *)result.val, result.len);     /* the string is already strdup()'ed */
2205    efree(result.val);
2206
2207    /* delete the converter if it is the last feed. */
2208    if (last_feed) {
2209        MBSTRG(illegalchars) += mbfl_buffer_illegalchars(MBSTRG(outconv));
2210        mbfl_buffer_converter_delete(MBSTRG(outconv));
2211        MBSTRG(outconv) = NULL;
2212    }
2213}
2214/* }}} */
2215
2216/* {{{ proto int mb_strlen(string str [, string encoding])
2217   Get character numbers of a string */
2218PHP_FUNCTION(mb_strlen)
2219{
2220    int n;
2221    mbfl_string string;
2222    char *enc_name = NULL;
2223    size_t enc_name_len;
2224
2225    mbfl_string_init(&string);
2226
2227    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", (char **)&string.val, &string.len, &enc_name, &enc_name_len) == FAILURE) {
2228        return;
2229    }
2230
2231    string.no_language = MBSTRG(language);
2232    if (enc_name == NULL) {
2233        string.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2234    } else {
2235        string.no_encoding = mbfl_name2no_encoding(enc_name);
2236        if (string.no_encoding == mbfl_no_encoding_invalid) {
2237            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", enc_name);
2238            RETURN_FALSE;
2239        }
2240    }
2241
2242    n = mbfl_strlen(&string);
2243    if (n >= 0) {
2244        RETVAL_LONG(n);
2245    } else {
2246        RETVAL_FALSE;
2247    }
2248}
2249/* }}} */
2250
2251/* {{{ proto int mb_strpos(string haystack, string needle [, int offset [, string encoding]])
2252   Find position of first occurrence of a string within another */
2253PHP_FUNCTION(mb_strpos)
2254{
2255    int n, reverse = 0;
2256    zend_long offset;
2257    mbfl_string haystack, needle;
2258    char *enc_name = NULL;
2259    size_t enc_name_len;
2260
2261    mbfl_string_init(&haystack);
2262    mbfl_string_init(&needle);
2263    haystack.no_language = MBSTRG(language);
2264    haystack.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2265    needle.no_language = MBSTRG(language);
2266    needle.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2267    offset = 0;
2268
2269    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|ls", (char **)&haystack.val, &haystack.len, (char **)&needle.val, &needle.len, &offset, &enc_name, &enc_name_len) == FAILURE) {
2270        return;
2271    }
2272
2273    if (enc_name != NULL) {
2274        haystack.no_encoding = needle.no_encoding = mbfl_name2no_encoding(enc_name);
2275        if (haystack.no_encoding == mbfl_no_encoding_invalid) {
2276            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", enc_name);
2277            RETURN_FALSE;
2278        }
2279    }
2280
2281    if (offset < 0 || offset > mbfl_strlen(&haystack)) {
2282        php_error_docref(NULL, E_WARNING, "Offset not contained in string");
2283        RETURN_FALSE;
2284    }
2285    if (needle.len == 0) {
2286        php_error_docref(NULL, E_WARNING, "Empty delimiter");
2287        RETURN_FALSE;
2288    }
2289
2290    n = mbfl_strpos(&haystack, &needle, offset, reverse);
2291    if (n >= 0) {
2292        RETVAL_LONG(n);
2293    } else {
2294        switch (-n) {
2295        case 1:
2296            break;
2297        case 2:
2298            php_error_docref(NULL, E_WARNING, "Needle has not positive length");
2299            break;
2300        case 4:
2301            php_error_docref(NULL, E_WARNING, "Unknown encoding or conversion error");
2302            break;
2303        case 8:
2304            php_error_docref(NULL, E_NOTICE, "Argument is empty");
2305            break;
2306        default:
2307            php_error_docref(NULL, E_WARNING, "Unknown error in mb_strpos");
2308            break;
2309        }
2310        RETVAL_FALSE;
2311    }
2312}
2313/* }}} */
2314
2315/* {{{ proto int mb_strrpos(string haystack, string needle [, int offset [, string encoding]])
2316   Find position of last occurrence of a string within another */
2317PHP_FUNCTION(mb_strrpos)
2318{
2319    int n;
2320    mbfl_string haystack, needle;
2321    char *enc_name = NULL;
2322    size_t enc_name_len;
2323    zval *zoffset = NULL;
2324    long offset = 0, str_flg;
2325    char *enc_name2 = NULL;
2326    int enc_name_len2;
2327
2328    mbfl_string_init(&haystack);
2329    mbfl_string_init(&needle);
2330    haystack.no_language = MBSTRG(language);
2331    haystack.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2332    needle.no_language = MBSTRG(language);
2333    needle.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2334
2335    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|zs", (char **)&haystack.val, &haystack.len, (char **)&needle.val, &needle.len, &zoffset, &enc_name, &enc_name_len) == FAILURE) {
2336        return;
2337    }
2338
2339    if (zoffset) {
2340        if (Z_TYPE_P(zoffset) == IS_STRING) {
2341            enc_name2     = Z_STRVAL_P(zoffset);
2342            enc_name_len2 = Z_STRLEN_P(zoffset);
2343            str_flg       = 1;
2344
2345            if (enc_name2 != NULL) {
2346                switch (*enc_name2) {
2347                    case '0':
2348                    case '1':
2349                    case '2':
2350                    case '3':
2351                    case '4':
2352                    case '5':
2353                    case '6':
2354                    case '7':
2355                    case '8':
2356                    case '9':
2357                    case ' ':
2358                    case '-':
2359                    case '.':
2360                        break;
2361                    default :
2362                        str_flg = 0;
2363                        break;
2364                }
2365            }
2366
2367            if (str_flg) {
2368                convert_to_long_ex(zoffset);
2369                offset   = Z_LVAL_P(zoffset);
2370            } else {
2371                enc_name     = enc_name2;
2372                enc_name_len = enc_name_len2;
2373            }
2374        } else {
2375            convert_to_long_ex(zoffset);
2376            offset = Z_LVAL_P(zoffset);
2377        }
2378    }
2379
2380    if (enc_name != NULL) {
2381        haystack.no_encoding = needle.no_encoding = mbfl_name2no_encoding(enc_name);
2382        if (haystack.no_encoding == mbfl_no_encoding_invalid) {
2383            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", enc_name);
2384            RETURN_FALSE;
2385        }
2386    }
2387
2388    if (haystack.len <= 0) {
2389        RETURN_FALSE;
2390    }
2391    if (needle.len <= 0) {
2392        RETURN_FALSE;
2393    }
2394
2395    {
2396        int haystack_char_len = mbfl_strlen(&haystack);
2397        if ((offset > 0 && offset > haystack_char_len) ||
2398            (offset < 0 && -offset > haystack_char_len)) {
2399            php_error_docref(NULL, E_WARNING, "Offset is greater than the length of haystack string");
2400            RETURN_FALSE;
2401        }
2402    }
2403
2404    n = mbfl_strpos(&haystack, &needle, offset, 1);
2405    if (n >= 0) {
2406        RETVAL_LONG(n);
2407    } else {
2408        RETVAL_FALSE;
2409    }
2410}
2411/* }}} */
2412
2413/* {{{ proto int mb_stripos(string haystack, string needle [, int offset [, string encoding]])
2414   Finds position of first occurrence of a string within another, case insensitive */
2415PHP_FUNCTION(mb_stripos)
2416{
2417    int n;
2418    zend_long offset;
2419    mbfl_string haystack, needle;
2420    const char *from_encoding = MBSTRG(current_internal_encoding)->mime_name;
2421    size_t from_encoding_len;
2422    n = -1;
2423    offset = 0;
2424
2425    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|ls", (char **)&haystack.val, (int *)&haystack.len, (char **)&needle.val, (int *)&needle.len, &offset, &from_encoding, &from_encoding_len) == FAILURE) {
2426        return;
2427    }
2428    if (needle.len == 0) {
2429        php_error_docref(NULL, E_WARNING, "Empty delimiter");
2430        RETURN_FALSE;
2431    }
2432    n = php_mb_stripos(0, (char *)haystack.val, haystack.len, (char *)needle.val, needle.len, offset, from_encoding);
2433
2434    if (n >= 0) {
2435        RETVAL_LONG(n);
2436    } else {
2437        RETVAL_FALSE;
2438    }
2439}
2440/* }}} */
2441
2442/* {{{ proto int mb_strripos(string haystack, string needle [, int offset [, string encoding]])
2443   Finds position of last occurrence of a string within another, case insensitive */
2444PHP_FUNCTION(mb_strripos)
2445{
2446    int n;
2447    zend_long offset;
2448    mbfl_string haystack, needle;
2449    const char *from_encoding = MBSTRG(current_internal_encoding)->mime_name;
2450    size_t from_encoding_len;
2451    n = -1;
2452    offset = 0;
2453
2454    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|ls", (char **)&haystack.val, (int *)&haystack.len, (char **)&needle.val, (int *)&needle.len, &offset, &from_encoding, &from_encoding_len) == FAILURE) {
2455        return;
2456    }
2457
2458    n = php_mb_stripos(1, (char *)haystack.val, haystack.len, (char *)needle.val, needle.len, offset, from_encoding);
2459
2460    if (n >= 0) {
2461        RETVAL_LONG(n);
2462    } else {
2463        RETVAL_FALSE;
2464    }
2465}
2466/* }}} */
2467
2468/* {{{ proto string mb_strstr(string haystack, string needle[, bool part[, string encoding]])
2469   Finds first occurrence of a string within another */
2470PHP_FUNCTION(mb_strstr)
2471{
2472    int n, len, mblen;
2473    mbfl_string haystack, needle, result, *ret = NULL;
2474    char *enc_name = NULL;
2475    size_t enc_name_len;
2476    zend_bool part = 0;
2477
2478    mbfl_string_init(&haystack);
2479    mbfl_string_init(&needle);
2480    haystack.no_language = MBSTRG(language);
2481    haystack.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2482    needle.no_language = MBSTRG(language);
2483    needle.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2484
2485    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|bs", (char **)&haystack.val, (int *)&haystack.len, (char **)&needle.val, (int *)&needle.len, &part, &enc_name, &enc_name_len) == FAILURE) {
2486        return;
2487    }
2488
2489    if (enc_name != NULL) {
2490        haystack.no_encoding = needle.no_encoding = mbfl_name2no_encoding(enc_name);
2491        if (haystack.no_encoding == mbfl_no_encoding_invalid) {
2492            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", enc_name);
2493            RETURN_FALSE;
2494        }
2495    }
2496
2497    if (needle.len <= 0) {
2498        php_error_docref(NULL, E_WARNING, "Empty delimiter");
2499        RETURN_FALSE;
2500    }
2501    n = mbfl_strpos(&haystack, &needle, 0, 0);
2502    if (n >= 0) {
2503        mblen = mbfl_strlen(&haystack);
2504        if (part) {
2505            ret = mbfl_substr(&haystack, &result, 0, n);
2506            if (ret != NULL) {
2507                // TODO: avoid reallocation ???
2508                RETVAL_STRINGL((char *)ret->val, ret->len);
2509                efree(ret->val);
2510            } else {
2511                RETVAL_FALSE;
2512            }
2513        } else {
2514            len = (mblen - n);
2515            ret = mbfl_substr(&haystack, &result, n, len);
2516            if (ret != NULL) {
2517                // TODO: avoid reallocation ???
2518                RETVAL_STRINGL((char *)ret->val, ret->len);
2519                efree(ret->val);
2520            } else {
2521                RETVAL_FALSE;
2522            }
2523        }
2524    } else {
2525        RETVAL_FALSE;
2526    }
2527}
2528/* }}} */
2529
2530/* {{{ proto string mb_strrchr(string haystack, string needle[, bool part[, string encoding]])
2531   Finds the last occurrence of a character in a string within another */
2532PHP_FUNCTION(mb_strrchr)
2533{
2534    int n, len, mblen;
2535    mbfl_string haystack, needle, result, *ret = NULL;
2536    char *enc_name = NULL;
2537    size_t enc_name_len;
2538    zend_bool part = 0;
2539
2540    mbfl_string_init(&haystack);
2541    mbfl_string_init(&needle);
2542    haystack.no_language = MBSTRG(language);
2543    haystack.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2544    needle.no_language = MBSTRG(language);
2545    needle.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2546
2547    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|bs", (char **)&haystack.val, &haystack.len, (char **)&needle.val, &needle.len, &part, &enc_name, &enc_name_len) == FAILURE) {
2548        return;
2549    }
2550
2551    if (enc_name != NULL) {
2552        haystack.no_encoding = needle.no_encoding = mbfl_name2no_encoding(enc_name);
2553        if (haystack.no_encoding == mbfl_no_encoding_invalid) {
2554            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", enc_name);
2555            RETURN_FALSE;
2556        }
2557    }
2558
2559    if (haystack.len <= 0) {
2560        RETURN_FALSE;
2561    }
2562    if (needle.len <= 0) {
2563        RETURN_FALSE;
2564    }
2565    n = mbfl_strpos(&haystack, &needle, 0, 1);
2566    if (n >= 0) {
2567        mblen = mbfl_strlen(&haystack);
2568        if (part) {
2569            ret = mbfl_substr(&haystack, &result, 0, n);
2570            if (ret != NULL) {
2571                // TODO: avoid reallocation ???
2572                RETVAL_STRINGL((char *)ret->val, ret->len);
2573                efree(ret->val);
2574            } else {
2575                RETVAL_FALSE;
2576            }
2577        } else {
2578            len = (mblen - n);
2579            ret = mbfl_substr(&haystack, &result, n, len);
2580            if (ret != NULL) {
2581                // TODO: avoid reallocation ???
2582                RETVAL_STRINGL((char *)ret->val, ret->len);
2583                efree(ret->val);
2584            } else {
2585                RETVAL_FALSE;
2586            }
2587        }
2588    } else {
2589        RETVAL_FALSE;
2590    }
2591}
2592/* }}} */
2593
2594/* {{{ proto string mb_stristr(string haystack, string needle[, bool part[, string encoding]])
2595   Finds first occurrence of a string within another, case insensitive */
2596PHP_FUNCTION(mb_stristr)
2597{
2598    zend_bool part = 0;
2599    size_t from_encoding_len, len, mblen;
2600    int n;
2601    mbfl_string haystack, needle, result, *ret = NULL;
2602    const char *from_encoding = MBSTRG(current_internal_encoding)->mime_name;
2603    mbfl_string_init(&haystack);
2604    mbfl_string_init(&needle);
2605    haystack.no_language = MBSTRG(language);
2606    haystack.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2607    needle.no_language = MBSTRG(language);
2608    needle.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2609
2610
2611    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|bs", (char **)&haystack.val, &haystack.len, (char **)&needle.val, &needle.len, &part, &from_encoding, &from_encoding_len) == FAILURE) {
2612        return;
2613    }
2614
2615    if (!needle.len) {
2616        php_error_docref(NULL, E_WARNING, "Empty delimiter");
2617        RETURN_FALSE;
2618    }
2619
2620    haystack.no_encoding = needle.no_encoding = mbfl_name2no_encoding(from_encoding);
2621    if (haystack.no_encoding == mbfl_no_encoding_invalid) {
2622        php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", from_encoding);
2623        RETURN_FALSE;
2624    }
2625
2626    n = php_mb_stripos(0, (char *)haystack.val, haystack.len, (char *)needle.val, needle.len, 0, from_encoding);
2627
2628    if (n <0) {
2629        RETURN_FALSE;
2630    }
2631
2632    mblen = mbfl_strlen(&haystack);
2633
2634    if (part) {
2635        ret = mbfl_substr(&haystack, &result, 0, n);
2636        if (ret != NULL) {
2637            // TODO: avoid reallocation ???
2638            RETVAL_STRINGL((char *)ret->val, ret->len);
2639            efree(ret->val);
2640        } else {
2641            RETVAL_FALSE;
2642        }
2643    } else {
2644        len = (mblen - n);
2645        ret = mbfl_substr(&haystack, &result, n, len);
2646        if (ret != NULL) {
2647            // TODO: avoid reallocaton ???
2648            RETVAL_STRINGL((char *)ret->val, ret->len);
2649            efree(ret->val);
2650        } else {
2651            RETVAL_FALSE;
2652        }
2653    }
2654}
2655/* }}} */
2656
2657/* {{{ proto string mb_strrichr(string haystack, string needle[, bool part[, string encoding]])
2658   Finds the last occurrence of a character in a string within another, case insensitive */
2659PHP_FUNCTION(mb_strrichr)
2660{
2661    zend_bool part = 0;
2662    int n, len, mblen;
2663    size_t from_encoding_len;
2664    mbfl_string haystack, needle, result, *ret = NULL;
2665    const char *from_encoding = MBSTRG(current_internal_encoding)->name;
2666    mbfl_string_init(&haystack);
2667    mbfl_string_init(&needle);
2668    haystack.no_language = MBSTRG(language);
2669    haystack.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2670    needle.no_language = MBSTRG(language);
2671    needle.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2672
2673
2674    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|bs", (char **)&haystack.val, &haystack.len, (char **)&needle.val, &needle.len, &part, &from_encoding, &from_encoding_len) == FAILURE) {
2675        return;
2676    }
2677
2678    haystack.no_encoding = needle.no_encoding = mbfl_name2no_encoding(from_encoding);
2679    if (haystack.no_encoding == mbfl_no_encoding_invalid) {
2680        php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", from_encoding);
2681        RETURN_FALSE;
2682    }
2683
2684    n = php_mb_stripos(1, (char *)haystack.val, haystack.len, (char *)needle.val, needle.len, 0, from_encoding);
2685
2686    if (n <0) {
2687        RETURN_FALSE;
2688    }
2689
2690    mblen = mbfl_strlen(&haystack);
2691
2692    if (part) {
2693        ret = mbfl_substr(&haystack, &result, 0, n);
2694        if (ret != NULL) {
2695            // TODO: avoid reallocation ???
2696            RETVAL_STRINGL((char *)ret->val, ret->len);
2697            efree(ret->val);
2698        } else {
2699            RETVAL_FALSE;
2700        }
2701    } else {
2702        len = (mblen - n);
2703        ret = mbfl_substr(&haystack, &result, n, len);
2704        if (ret != NULL) {
2705            // TODO: avoid reallocation ???
2706            RETVAL_STRINGL((char *)ret->val, ret->len);
2707            efree(ret->val);
2708        } else {
2709            RETVAL_FALSE;
2710        }
2711    }
2712}
2713/* }}} */
2714
2715/* {{{ proto int mb_substr_count(string haystack, string needle [, string encoding])
2716   Count the number of substring occurrences */
2717PHP_FUNCTION(mb_substr_count)
2718{
2719    int n;
2720    mbfl_string haystack, needle;
2721    char *enc_name = NULL;
2722    size_t enc_name_len;
2723
2724    mbfl_string_init(&haystack);
2725    mbfl_string_init(&needle);
2726    haystack.no_language = MBSTRG(language);
2727    haystack.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2728    needle.no_language = MBSTRG(language);
2729    needle.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2730
2731    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|s", (char **)&haystack.val, &haystack.len, (char **)&needle.val, &needle.len, &enc_name, &enc_name_len) == FAILURE) {
2732        return;
2733    }
2734
2735    if (enc_name != NULL) {
2736        haystack.no_encoding = needle.no_encoding = mbfl_name2no_encoding(enc_name);
2737        if (haystack.no_encoding == mbfl_no_encoding_invalid) {
2738            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", enc_name);
2739            RETURN_FALSE;
2740        }
2741    }
2742
2743    if (needle.len <= 0) {
2744        php_error_docref(NULL, E_WARNING, "Empty substring");
2745        RETURN_FALSE;
2746    }
2747
2748    n = mbfl_substr_count(&haystack, &needle);
2749    if (n >= 0) {
2750        RETVAL_LONG(n);
2751    } else {
2752        RETVAL_FALSE;
2753    }
2754}
2755/* }}} */
2756
2757/* {{{ proto string mb_substr(string str, int start [, int length [, string encoding]])
2758   Returns part of a string */
2759PHP_FUNCTION(mb_substr)
2760{
2761    size_t argc = ZEND_NUM_ARGS();
2762    char *str, *encoding;
2763    zend_long from, len;
2764    int mblen;
2765    size_t str_len, encoding_len;
2766    zval *z_len = NULL;
2767    mbfl_string string, result, *ret;
2768
2769    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl|zs", &str, &str_len, &from, &z_len, &encoding, &encoding_len) == FAILURE) {
2770        return;
2771    }
2772
2773    mbfl_string_init(&string);
2774    string.no_language = MBSTRG(language);
2775    string.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2776
2777    if (argc == 4) {
2778        string.no_encoding = mbfl_name2no_encoding(encoding);
2779        if (string.no_encoding == mbfl_no_encoding_invalid) {
2780            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", encoding);
2781            RETURN_FALSE;
2782        }
2783    }
2784
2785    string.val = (unsigned char *)str;
2786    string.len = str_len;
2787
2788    if (argc < 3 || Z_TYPE_P(z_len) == IS_NULL) {
2789        len = str_len;
2790    } else {
2791        convert_to_long_ex(z_len);
2792        len = Z_LVAL_P(z_len);
2793    }
2794
2795    /* measures length */
2796    mblen = 0;
2797    if (from < 0 || len < 0) {
2798        mblen = mbfl_strlen(&string);
2799    }
2800
2801    /* if "from" position is negative, count start position from the end
2802     * of the string
2803     */
2804    if (from < 0) {
2805        from = mblen + from;
2806        if (from < 0) {
2807            from = 0;
2808        }
2809    }
2810
2811    /* if "length" position is negative, set it to the length
2812     * needed to stop that many chars from the end of the string
2813     */
2814    if (len < 0) {
2815        len = (mblen - from) + len;
2816        if (len < 0) {
2817            len = 0;
2818        }
2819    }
2820
2821    if (((MBSTRG(func_overload) & MB_OVERLOAD_STRING) == MB_OVERLOAD_STRING)
2822        && (from >= mbfl_strlen(&string))) {
2823        RETURN_FALSE;
2824    }
2825
2826    ret = mbfl_substr(&string, &result, from, len);
2827    if (NULL == ret) {
2828        RETURN_FALSE;
2829    }
2830
2831    // TODO: avoid reallocation ???
2832    RETVAL_STRINGL((char *)ret->val, ret->len); /* the string is already strdup()'ed */
2833    efree(ret->val);
2834}
2835/* }}} */
2836
2837/* {{{ proto string mb_strcut(string str, int start [, int length [, string encoding]])
2838   Returns part of a string */
2839PHP_FUNCTION(mb_strcut)
2840{
2841    size_t argc = ZEND_NUM_ARGS();
2842    char *encoding;
2843    zend_long from, len;
2844    size_t encoding_len;
2845    zval *z_len = NULL;
2846    mbfl_string string, result, *ret;
2847
2848    mbfl_string_init(&string);
2849    string.no_language = MBSTRG(language);
2850    string.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2851
2852    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl|zs", (char **)&string.val, (int **)&string.len, &from, &z_len, &encoding, &encoding_len) == FAILURE) {
2853        return;
2854    }
2855
2856    if (argc == 4) {
2857        string.no_encoding = mbfl_name2no_encoding(encoding);
2858        if (string.no_encoding == mbfl_no_encoding_invalid) {
2859            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", encoding);
2860            RETURN_FALSE;
2861        }
2862    }
2863
2864    if (argc < 3 || Z_TYPE_P(z_len) == IS_NULL) {
2865        len = string.len;
2866    } else {
2867        convert_to_long_ex(z_len);
2868        len = Z_LVAL_P(z_len);
2869    }
2870
2871    /* if "from" position is negative, count start position from the end
2872     * of the string
2873     */
2874    if (from < 0) {
2875        from = string.len + from;
2876        if (from < 0) {
2877            from = 0;
2878        }
2879    }
2880
2881    /* if "length" position is negative, set it to the length
2882     * needed to stop that many chars from the end of the string
2883     */
2884    if (len < 0) {
2885        len = (string.len - from) + len;
2886        if (len < 0) {
2887            len = 0;
2888        }
2889    }
2890
2891    if ((unsigned int)from > string.len) {
2892        RETURN_FALSE;
2893    }
2894
2895    ret = mbfl_strcut(&string, &result, from, len);
2896    if (ret == NULL) {
2897        RETURN_FALSE;
2898    }
2899
2900    // TODO: avoid reallocation ???
2901    RETVAL_STRINGL((char *)ret->val, ret->len); /* the string is already strdup()'ed */
2902    efree(ret->val);
2903}
2904/* }}} */
2905
2906/* {{{ proto int mb_strwidth(string str [, string encoding])
2907   Gets terminal width of a string */
2908PHP_FUNCTION(mb_strwidth)
2909{
2910    int n;
2911    mbfl_string string;
2912    char *enc_name = NULL;
2913    size_t enc_name_len;
2914
2915    mbfl_string_init(&string);
2916
2917    string.no_language = MBSTRG(language);
2918    string.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2919
2920    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", (char **)&string.val, &string.len, &enc_name, &enc_name_len) == FAILURE) {
2921        return;
2922    }
2923
2924    if (enc_name != NULL) {
2925        string.no_encoding = mbfl_name2no_encoding(enc_name);
2926        if (string.no_encoding == mbfl_no_encoding_invalid) {
2927            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", enc_name);
2928            RETURN_FALSE;
2929        }
2930    }
2931
2932    n = mbfl_strwidth(&string);
2933    if (n >= 0) {
2934        RETVAL_LONG(n);
2935    } else {
2936        RETVAL_FALSE;
2937    }
2938}
2939/* }}} */
2940
2941/* {{{ proto string mb_strimwidth(string str, int start, int width [, string trimmarker [, string encoding]])
2942   Trim the string in terminal width */
2943PHP_FUNCTION(mb_strimwidth)
2944{
2945    char *str, *trimmarker, *encoding;
2946    zend_long from, width;
2947    size_t str_len, trimmarker_len, encoding_len;
2948    mbfl_string string, result, marker, *ret;
2949
2950    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll|ss", &str, &str_len, &from, &width, &trimmarker, &trimmarker_len, &encoding, &encoding_len) == FAILURE) {
2951        return;
2952    }
2953
2954    mbfl_string_init(&string);
2955    mbfl_string_init(&marker);
2956    string.no_language = MBSTRG(language);
2957    string.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2958    marker.no_language = MBSTRG(language);
2959    marker.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2960    marker.val = NULL;
2961    marker.len = 0;
2962
2963    if (ZEND_NUM_ARGS() == 5) {
2964        string.no_encoding = marker.no_encoding = mbfl_name2no_encoding(encoding);
2965        if (string.no_encoding == mbfl_no_encoding_invalid) {
2966            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", encoding);
2967            RETURN_FALSE;
2968        }
2969    }
2970
2971    string.val = (unsigned char *)str;
2972    string.len = str_len;
2973
2974    if (from < 0 || (size_t)from > str_len) {
2975        php_error_docref(NULL, E_WARNING, "Start position is out of range");
2976        RETURN_FALSE;
2977    }
2978
2979    if (width < 0) {
2980        php_error_docref(NULL, E_WARNING, "Width is negative value");
2981        RETURN_FALSE;
2982    }
2983
2984    if (ZEND_NUM_ARGS() >= 4) {
2985        marker.val = (unsigned char *)trimmarker;
2986        marker.len = trimmarker_len;
2987    }
2988
2989    ret = mbfl_strimwidth(&string, &marker, &result, from, width);
2990
2991    if (ret == NULL) {
2992        RETURN_FALSE;
2993    }
2994    // TODO: avoid reallocation ???
2995    RETVAL_STRINGL((char *)ret->val, ret->len); /* the string is already strdup()'ed */
2996    efree(ret->val);
2997}
2998/* }}} */
2999
3000/* {{{ MBSTRING_API char *php_mb_convert_encoding() */
3001MBSTRING_API char * php_mb_convert_encoding(const char *input, size_t length, const char *_to_encoding, const char *_from_encodings, size_t *output_len)
3002{
3003    mbfl_string string, result, *ret;
3004    const mbfl_encoding *from_encoding, *to_encoding;
3005    mbfl_buffer_converter *convd;
3006    size_t size;
3007    const mbfl_encoding **list;
3008    char *output=NULL;
3009
3010    if (output_len) {
3011        *output_len = 0;
3012    }
3013    if (!input) {
3014        return NULL;
3015    }
3016    /* new encoding */
3017    if (_to_encoding && strlen(_to_encoding)) {
3018        to_encoding = mbfl_name2encoding(_to_encoding);
3019        if (!to_encoding) {
3020            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", _to_encoding);
3021            return NULL;
3022        }
3023    } else {
3024        to_encoding = MBSTRG(current_internal_encoding);
3025    }
3026
3027    /* initialize string */
3028    mbfl_string_init(&string);
3029    mbfl_string_init(&result);
3030    from_encoding = MBSTRG(current_internal_encoding);
3031    string.no_encoding = from_encoding->no_encoding;
3032    string.no_language = MBSTRG(language);
3033    string.val = (unsigned char *)input;
3034    string.len = length;
3035
3036    /* pre-conversion encoding */
3037    if (_from_encodings) {
3038        list = NULL;
3039        size = 0;
3040        php_mb_parse_encoding_list(_from_encodings, strlen(_from_encodings), &list, &size, 0);
3041        if (size == 1) {
3042            from_encoding = *list;
3043            string.no_encoding = from_encoding->no_encoding;
3044        } else if (size > 1) {
3045            /* auto detect */
3046            from_encoding = mbfl_identify_encoding2(&string, list, size, MBSTRG(strict_detection));
3047            if (from_encoding) {
3048                string.no_encoding = from_encoding->no_encoding;
3049            } else {
3050                php_error_docref(NULL, E_WARNING, "Unable to detect character encoding");
3051                from_encoding = &mbfl_encoding_pass;
3052                to_encoding = from_encoding;
3053                string.no_encoding = from_encoding->no_encoding;
3054            }
3055        } else {
3056            php_error_docref(NULL, E_WARNING, "Illegal character encoding specified");
3057        }
3058        if (list != NULL) {
3059            efree((void *)list);
3060        }
3061    }
3062
3063    /* initialize converter */
3064    convd = mbfl_buffer_converter_new2(from_encoding, to_encoding, string.len);
3065    if (convd == NULL) {
3066        php_error_docref(NULL, E_WARNING, "Unable to create character encoding converter");
3067        return NULL;
3068    }
3069    mbfl_buffer_converter_illegal_mode(convd, MBSTRG(current_filter_illegal_mode));
3070    mbfl_buffer_converter_illegal_substchar(convd, MBSTRG(current_filter_illegal_substchar));
3071
3072    /* do it */
3073    ret = mbfl_buffer_converter_feed_result(convd, &string, &result);
3074    if (ret) {
3075        if (output_len) {
3076            *output_len = ret->len;
3077        }
3078        output = (char *)ret->val;
3079    }
3080
3081    MBSTRG(illegalchars) += mbfl_buffer_illegalchars(convd);
3082    mbfl_buffer_converter_delete(convd);
3083    return output;
3084}
3085/* }}} */
3086
3087/* {{{ proto string mb_convert_encoding(string str, string to-encoding [, mixed from-encoding])
3088   Returns converted string in desired encoding */
3089PHP_FUNCTION(mb_convert_encoding)
3090{
3091    char *arg_str, *arg_new;
3092    size_t str_len, new_len;
3093    zval *arg_old;
3094    size_t size, l, n;
3095    char *_from_encodings = NULL, *ret, *s_free = NULL;
3096
3097    zval *hash_entry;
3098    HashTable *target_hash;
3099
3100    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|z", &arg_str, &str_len, &arg_new, &new_len, &arg_old) == FAILURE) {
3101        return;
3102    }
3103
3104    if (ZEND_NUM_ARGS() == 3) {
3105        switch (Z_TYPE_P(arg_old)) {
3106            case IS_ARRAY:
3107                target_hash = Z_ARRVAL_P(arg_old);
3108                _from_encodings = NULL;
3109
3110                ZEND_HASH_FOREACH_VAL(target_hash, hash_entry) {
3111
3112                    convert_to_string_ex(hash_entry);
3113
3114                    if ( _from_encodings) {
3115                        l = strlen(_from_encodings);
3116                        n = strlen(Z_STRVAL_P(hash_entry));
3117                        _from_encodings = erealloc(_from_encodings, l+n+2);
3118                        memcpy(_from_encodings + l, ",", 1);
3119                        memcpy(_from_encodings + l + 1, Z_STRVAL_P(hash_entry), Z_STRLEN_P(hash_entry) + 1);
3120                    } else {
3121                        _from_encodings = estrdup(Z_STRVAL_P(hash_entry));
3122                    }
3123                } ZEND_HASH_FOREACH_END();
3124
3125                if (_from_encodings != NULL && !strlen(_from_encodings)) {
3126                    efree(_from_encodings);
3127                    _from_encodings = NULL;
3128                }
3129                s_free = _from_encodings;
3130                break;
3131            default:
3132                convert_to_string(arg_old);
3133                _from_encodings = Z_STRVAL_P(arg_old);
3134                break;
3135            }
3136    }
3137
3138    /* new encoding */
3139    ret = php_mb_convert_encoding(arg_str, str_len, arg_new, _from_encodings, &size);
3140    if (ret != NULL) {
3141        // TODO: avoid reallocation ???
3142        RETVAL_STRINGL(ret, size);      /* the string is already strdup()'ed */
3143        efree(ret);
3144    } else {
3145        RETVAL_FALSE;
3146    }
3147
3148    if ( s_free) {
3149        efree(s_free);
3150    }
3151}
3152/* }}} */
3153
3154/* {{{ proto string mb_convert_case(string sourcestring, int mode [, string encoding])
3155   Returns a case-folded version of sourcestring */
3156PHP_FUNCTION(mb_convert_case)
3157{
3158    const char *from_encoding = MBSTRG(current_internal_encoding)->mime_name;
3159    char *str;
3160    size_t str_len, from_encoding_len;
3161    zend_long case_mode = 0;
3162    char *newstr;
3163    size_t ret_len;
3164
3165    RETVAL_FALSE;
3166    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl|s!", &str, &str_len,
3167                &case_mode, &from_encoding, &from_encoding_len) == FAILURE) {
3168        return;
3169    }
3170
3171    newstr = php_unicode_convert_case(case_mode, str, (size_t) str_len, &ret_len, from_encoding);
3172
3173    if (newstr) {
3174        // TODO: avoid reallocation ???
3175        RETVAL_STRINGL(newstr, ret_len);
3176        efree(newstr);
3177    }
3178}
3179/* }}} */
3180
3181/* {{{ proto string mb_strtoupper(string sourcestring [, string encoding])
3182 *  Returns a uppercased version of sourcestring
3183 */
3184PHP_FUNCTION(mb_strtoupper)
3185{
3186    const char *from_encoding = MBSTRG(current_internal_encoding)->mime_name;
3187    char *str;
3188    size_t str_len, from_encoding_len;
3189    char *newstr;
3190    size_t ret_len;
3191
3192    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s!", &str, &str_len,
3193                &from_encoding, &from_encoding_len) == FAILURE) {
3194        return;
3195    }
3196    newstr = php_unicode_convert_case(PHP_UNICODE_CASE_UPPER, str, (size_t) str_len, &ret_len, from_encoding);
3197
3198    if (newstr) {
3199        // TODO: avoid reallocation ???
3200        RETVAL_STRINGL(newstr, ret_len);
3201        efree(newstr);
3202        return;
3203    }
3204    RETURN_FALSE;
3205}
3206/* }}} */
3207
3208/* {{{ proto string mb_strtolower(string sourcestring [, string encoding])
3209 *  Returns a lowercased version of sourcestring
3210 */
3211PHP_FUNCTION(mb_strtolower)
3212{
3213    const char *from_encoding = MBSTRG(current_internal_encoding)->mime_name;
3214    char *str;
3215    size_t str_len, from_encoding_len;
3216    char *newstr;
3217    size_t ret_len;
3218
3219    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s!", &str, &str_len,
3220                &from_encoding, &from_encoding_len) == FAILURE) {
3221        return;
3222    }
3223    newstr = php_unicode_convert_case(PHP_UNICODE_CASE_LOWER, str, (size_t) str_len, &ret_len, from_encoding);
3224
3225    if (newstr) {
3226        // TODO: avoid reallocation ???
3227        RETVAL_STRINGL(newstr, ret_len);
3228        efree(newstr);
3229        return;
3230    }
3231    RETURN_FALSE;
3232}
3233/* }}} */
3234
3235/* {{{ proto string mb_detect_encoding(string str [, mixed encoding_list [, bool strict]])
3236   Encodings of the given string is returned (as a string) */
3237PHP_FUNCTION(mb_detect_encoding)
3238{
3239    char *str;
3240    size_t str_len;
3241    zend_bool strict=0;
3242    zval *encoding_list;
3243
3244    mbfl_string string;
3245    const mbfl_encoding *ret;
3246    const mbfl_encoding **elist, **list;
3247    size_t size;
3248
3249    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|zb", &str, &str_len, &encoding_list, &strict) == FAILURE) {
3250        return;
3251    }
3252
3253    /* make encoding list */
3254    list = NULL;
3255    size = 0;
3256    if (ZEND_NUM_ARGS() >= 2 && !Z_ISNULL_P(encoding_list)) {
3257        switch (Z_TYPE_P(encoding_list)) {
3258        case IS_ARRAY:
3259            if (FAILURE == php_mb_parse_encoding_array(encoding_list, &list, &size, 0)) {
3260                if (list) {
3261                    efree(list);
3262                    list = NULL;
3263                    size = 0;
3264                }
3265            }
3266            break;
3267        default:
3268            convert_to_string(encoding_list);
3269            if (FAILURE == php_mb_parse_encoding_list(Z_STRVAL_P(encoding_list), Z_STRLEN_P(encoding_list), &list, &size, 0)) {
3270                if (list) {
3271                    efree(list);
3272                    list = NULL;
3273                    size = 0;
3274                }
3275            }
3276            break;
3277        }
3278        if (size <= 0) {
3279            php_error_docref(NULL, E_WARNING, "Illegal argument");
3280        }
3281    }
3282
3283    if (ZEND_NUM_ARGS() < 3) {
3284        strict = (zend_bool)MBSTRG(strict_detection);
3285    }
3286
3287    if (size > 0 && list != NULL) {
3288        elist = list;
3289    } else {
3290        elist = MBSTRG(current_detect_order_list);
3291        size = MBSTRG(current_detect_order_list_size);
3292    }
3293
3294    mbfl_string_init(&string);
3295    string.no_language = MBSTRG(language);
3296    string.val = (unsigned char *)str;
3297    string.len = str_len;
3298    ret = mbfl_identify_encoding2(&string, elist, size, strict);
3299
3300    if (list != NULL) {
3301        efree((void *)list);
3302    }
3303
3304    if (ret == NULL) {
3305        RETURN_FALSE;
3306    }
3307
3308    RETVAL_STRING((char *)ret->name);
3309}
3310/* }}} */
3311
3312/* {{{ proto mixed mb_list_encodings()
3313   Returns an array of all supported entity encodings */
3314PHP_FUNCTION(mb_list_encodings)
3315{
3316    const mbfl_encoding **encodings;
3317    const mbfl_encoding *encoding;
3318    int i;
3319
3320    array_init(return_value);
3321    i = 0;
3322    encodings = mbfl_get_supported_encodings();
3323    while ((encoding = encodings[i++]) != NULL) {
3324        add_next_index_string(return_value, (char *) encoding->name);
3325    }
3326}
3327/* }}} */
3328
3329/* {{{ proto array mb_encoding_aliases(string encoding)
3330   Returns an array of the aliases of a given encoding name */
3331PHP_FUNCTION(mb_encoding_aliases)
3332{
3333    const mbfl_encoding *encoding;
3334    char *name = NULL;
3335    size_t name_len;
3336
3337    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
3338        return;
3339    }
3340
3341    encoding = mbfl_name2encoding(name);
3342    if (!encoding) {
3343        php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", name);
3344        RETURN_FALSE;
3345    }
3346
3347    array_init(return_value);
3348    if (encoding->aliases != NULL) {
3349        const char **alias;
3350        for (alias = *encoding->aliases; *alias; ++alias) {
3351            add_next_index_string(return_value, (char *)*alias);
3352        }
3353    }
3354}
3355/* }}} */
3356
3357/* {{{ proto string mb_encode_mimeheader(string str [, string charset [, string transfer-encoding [, string linefeed [, int indent]]]])
3358   Converts the string to MIME "encoded-word" in the format of =?charset?(B|Q)?encoded_string?= */
3359PHP_FUNCTION(mb_encode_mimeheader)
3360{
3361    enum mbfl_no_encoding charset, transenc;
3362    mbfl_string  string, result, *ret;
3363    char *charset_name = NULL;
3364    size_t charset_name_len;
3365    char *trans_enc_name = NULL;
3366    size_t trans_enc_name_len;
3367    char *linefeed = "\r\n";
3368    size_t linefeed_len;
3369    zend_long indent = 0;
3370
3371    mbfl_string_init(&string);
3372    string.no_language = MBSTRG(language);
3373    string.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
3374
3375    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|sssl", (char **)&string.val, &string.len, &charset_name, &charset_name_len, &trans_enc_name, &trans_enc_name_len, &linefeed, &linefeed_len, &indent) == FAILURE) {
3376        return;
3377    }
3378
3379    charset = mbfl_no_encoding_pass;
3380    transenc = mbfl_no_encoding_base64;
3381
3382    if (charset_name != NULL) {
3383        charset = mbfl_name2no_encoding(charset_name);
3384        if (charset == mbfl_no_encoding_invalid) {
3385            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", charset_name);
3386            RETURN_FALSE;
3387        }
3388    } else {
3389        const mbfl_language *lang = mbfl_no2language(MBSTRG(language));
3390        if (lang != NULL) {
3391            charset = lang->mail_charset;
3392            transenc = lang->mail_header_encoding;
3393        }
3394    }
3395
3396    if (trans_enc_name != NULL) {
3397        if (*trans_enc_name == 'B' || *trans_enc_name == 'b') {
3398            transenc = mbfl_no_encoding_base64;
3399        } else if (*trans_enc_name == 'Q' || *trans_enc_name == 'q') {
3400            transenc = mbfl_no_encoding_qprint;
3401        }
3402    }
3403
3404    mbfl_string_init(&result);
3405    ret = mbfl_mime_header_encode(&string, &result, charset, transenc, linefeed, indent);
3406    if (ret != NULL) {
3407        // TODO: avoid reallocation ???
3408        RETVAL_STRINGL((char *)ret->val, ret->len); /* the string is already strdup()'ed */
3409        efree(ret->val);
3410    } else {
3411        RETVAL_FALSE;
3412    }
3413}
3414/* }}} */
3415
3416/* {{{ proto string mb_decode_mimeheader(string string)
3417   Decodes the MIME "encoded-word" in the string */
3418PHP_FUNCTION(mb_decode_mimeheader)
3419{
3420    mbfl_string string, result, *ret;
3421
3422    mbfl_string_init(&string);
3423    string.no_language = MBSTRG(language);
3424    string.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
3425
3426    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", (char **)&string.val, &string.len) == FAILURE) {
3427        return;
3428    }
3429
3430    mbfl_string_init(&result);
3431    ret = mbfl_mime_header_decode(&string, &result, MBSTRG(current_internal_encoding)->no_encoding);
3432    if (ret != NULL) {
3433        // TODO: avoid reallocation ???
3434        RETVAL_STRINGL((char *)ret->val, ret->len); /* the string is already strdup()'ed */
3435        efree(ret->val);
3436    } else {
3437        RETVAL_FALSE;
3438    }
3439}
3440/* }}} */
3441
3442/* {{{ proto string mb_convert_kana(string str [, string option] [, string encoding])
3443   Conversion between full-width character and half-width character (Japanese) */
3444PHP_FUNCTION(mb_convert_kana)
3445{
3446    int opt, i;
3447    mbfl_string string, result, *ret;
3448    char *optstr = NULL;
3449    size_t optstr_len;
3450    char *encname = NULL;
3451    size_t encname_len;
3452
3453    mbfl_string_init(&string);
3454    string.no_language = MBSTRG(language);
3455    string.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
3456
3457    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ss", (char **)&string.val, &string.len, &optstr, &optstr_len, &encname, &encname_len) == FAILURE) {
3458        return;
3459    }
3460
3461    /* option */
3462    if (optstr != NULL) {
3463        char *p = optstr;
3464        int n = optstr_len;
3465        i = 0;
3466        opt = 0;
3467        while (i < n) {
3468            i++;
3469            switch (*p++) {
3470            case 'A':
3471                opt |= 0x1;
3472                break;
3473            case 'a':
3474                opt |= 0x10;
3475                break;
3476            case 'R':
3477                opt |= 0x2;
3478                break;
3479            case 'r':
3480                opt |= 0x20;
3481                break;
3482            case 'N':
3483                opt |= 0x4;
3484                break;
3485            case 'n':
3486                opt |= 0x40;
3487                break;
3488            case 'S':
3489                opt |= 0x8;
3490                break;
3491            case 's':
3492                opt |= 0x80;
3493                break;
3494            case 'K':
3495                opt |= 0x100;
3496                break;
3497            case 'k':
3498                opt |= 0x1000;
3499                break;
3500            case 'H':
3501                opt |= 0x200;
3502                break;
3503            case 'h':
3504                opt |= 0x2000;
3505                break;
3506            case 'V':
3507                opt |= 0x800;
3508                break;
3509            case 'C':
3510                opt |= 0x10000;
3511                break;
3512            case 'c':
3513                opt |= 0x20000;
3514                break;
3515            case 'M':
3516                opt |= 0x100000;
3517                break;
3518            case 'm':
3519                opt |= 0x200000;
3520                break;
3521            }
3522        }
3523    } else {
3524        opt = 0x900;
3525    }
3526
3527    /* encoding */
3528    if (encname != NULL) {
3529        string.no_encoding = mbfl_name2no_encoding(encname);
3530        if (string.no_encoding == mbfl_no_encoding_invalid) {
3531            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", encname);
3532            RETURN_FALSE;
3533        }
3534    }
3535
3536    ret = mbfl_ja_jp_hantozen(&string, &result, opt);
3537    if (ret != NULL) {
3538        // TODO: avoid reallocation ???
3539        RETVAL_STRINGL((char *)ret->val, ret->len);     /* the string is already strdup()'ed */
3540        efree(ret->val);
3541    } else {
3542        RETVAL_FALSE;
3543    }
3544}
3545/* }}} */
3546
3547#define PHP_MBSTR_STACK_BLOCK_SIZE 32
3548
3549/* {{{ proto string mb_convert_variables(string to-encoding, mixed from-encoding, mixed vars [, ...])
3550   Converts the string resource in variables to desired encoding */
3551PHP_FUNCTION(mb_convert_variables)
3552{
3553    zval *args, *stack, *var, *hash_entry, *hash_entry_ptr, *zfrom_enc;
3554    HashTable *target_hash;
3555    mbfl_string string, result, *ret;
3556    const mbfl_encoding *from_encoding, *to_encoding;
3557    mbfl_encoding_detector *identd;
3558    mbfl_buffer_converter *convd;
3559    int n, argc, stack_level, stack_max;
3560    size_t to_enc_len;
3561    size_t elistsz;
3562    const mbfl_encoding **elist;
3563    char *to_enc;
3564    void *ptmp;
3565
3566    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz+", &to_enc, &to_enc_len, &zfrom_enc, &args, &argc) == FAILURE) {
3567        return;
3568    }
3569
3570    /* new encoding */
3571    to_encoding = mbfl_name2encoding(to_enc);
3572    if (!to_encoding) {
3573        php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", to_enc);
3574        RETURN_FALSE;
3575    }
3576
3577    /* initialize string */
3578    mbfl_string_init(&string);
3579    mbfl_string_init(&result);
3580    from_encoding = MBSTRG(current_internal_encoding);
3581    string.no_encoding = from_encoding->no_encoding;
3582    string.no_language = MBSTRG(language);
3583
3584    /* pre-conversion encoding */
3585    elist = NULL;
3586    elistsz = 0;
3587    switch (Z_TYPE_P(zfrom_enc)) {
3588        case IS_ARRAY:
3589            php_mb_parse_encoding_array(zfrom_enc, &elist, &elistsz, 0);
3590            break;
3591        default:
3592            convert_to_string_ex(zfrom_enc);
3593            php_mb_parse_encoding_list(Z_STRVAL_P(zfrom_enc), Z_STRLEN_P(zfrom_enc), &elist, &elistsz, 0);
3594            break;
3595    }
3596
3597    if (elistsz <= 0) {
3598        from_encoding = &mbfl_encoding_pass;
3599    } else if (elistsz == 1) {
3600        from_encoding = *elist;
3601    } else {
3602        /* auto detect */
3603        from_encoding = NULL;
3604        stack_max = PHP_MBSTR_STACK_BLOCK_SIZE;
3605        stack = (zval *)safe_emalloc(stack_max, sizeof(zval), 0);
3606        stack_level = 0;
3607        identd = mbfl_encoding_detector_new2(elist, elistsz, MBSTRG(strict_detection));
3608        if (identd != NULL) {
3609            n = 0;
3610            while (n < argc || stack_level > 0) {
3611                if (stack_level <= 0) {
3612                    var = &args[n++];
3613                    ZVAL_DEREF(var);
3614                    SEPARATE_ZVAL_NOREF(var);
3615                    if (Z_TYPE_P(var) == IS_ARRAY || Z_TYPE_P(var) == IS_OBJECT) {
3616                        target_hash = HASH_OF(var);
3617                        if (target_hash != NULL) {
3618                            zend_hash_internal_pointer_reset(target_hash);
3619                        }
3620                    }
3621                } else {
3622                    stack_level--;
3623                    var = &stack[stack_level];
3624                }
3625                if (Z_TYPE_P(var) == IS_ARRAY || Z_TYPE_P(var) == IS_OBJECT) {
3626                    target_hash = HASH_OF(var);
3627                    if (target_hash != NULL) {
3628                        while ((hash_entry = zend_hash_get_current_data(target_hash)) != NULL) {
3629                            zend_hash_move_forward(target_hash);
3630                            if (Z_TYPE_P(hash_entry) == IS_INDIRECT) {
3631                                hash_entry = Z_INDIRECT_P(hash_entry);
3632                            }
3633                            ZVAL_DEREF(hash_entry);
3634                            if (Z_TYPE_P(hash_entry) == IS_ARRAY || Z_TYPE_P(hash_entry) == IS_OBJECT) {
3635                                if (stack_level >= stack_max) {
3636                                    stack_max += PHP_MBSTR_STACK_BLOCK_SIZE;
3637                                    ptmp = erealloc(stack, sizeof(zval) * stack_max);
3638                                    stack = (zval *)ptmp;
3639                                }
3640                                ZVAL_COPY_VALUE(&stack[stack_level], var);
3641                                stack_level++;
3642                                var = hash_entry;
3643                                target_hash = HASH_OF(var);
3644                                if (target_hash != NULL) {
3645                                    zend_hash_internal_pointer_reset(target_hash);
3646                                    continue;
3647                                }
3648                            } else if (Z_TYPE_P(hash_entry) == IS_STRING) {
3649                                string.val = (unsigned char *)Z_STRVAL_P(hash_entry);
3650                                string.len = Z_STRLEN_P(hash_entry);
3651                                if (mbfl_encoding_detector_feed(identd, &string)) {
3652                                    goto detect_end;        /* complete detecting */
3653                                }
3654                            }
3655                        }
3656                    }
3657                } else if (Z_TYPE_P(var) == IS_STRING) {
3658                    string.val = (unsigned char *)Z_STRVAL_P(var);
3659                    string.len = Z_STRLEN_P(var);
3660                    if (mbfl_encoding_detector_feed(identd, &string)) {
3661                        goto detect_end;        /* complete detecting */
3662                    }
3663                }
3664            }
3665detect_end:
3666            from_encoding = mbfl_encoding_detector_judge2(identd);
3667            mbfl_encoding_detector_delete(identd);
3668        }
3669        efree(stack);
3670
3671        if (!from_encoding) {
3672            php_error_docref(NULL, E_WARNING, "Unable to detect encoding");
3673            from_encoding = &mbfl_encoding_pass;
3674        }
3675    }
3676    if (elist != NULL) {
3677        efree((void *)elist);
3678    }
3679    /* create converter */
3680    convd = NULL;
3681    if (from_encoding != &mbfl_encoding_pass) {
3682        convd = mbfl_buffer_converter_new2(from_encoding, to_encoding, 0);
3683        if (convd == NULL) {
3684            php_error_docref(NULL, E_WARNING, "Unable to create converter");
3685            RETURN_FALSE;
3686        }
3687        mbfl_buffer_converter_illegal_mode(convd, MBSTRG(current_filter_illegal_mode));
3688        mbfl_buffer_converter_illegal_substchar(convd, MBSTRG(current_filter_illegal_substchar));
3689    }
3690
3691    /* convert */
3692    if (convd != NULL) {
3693        stack_max = PHP_MBSTR_STACK_BLOCK_SIZE;
3694        stack = (zval*)safe_emalloc(stack_max, sizeof(zval), 0);
3695        stack_level = 0;
3696        n = 0;
3697        while (n < argc || stack_level > 0) {
3698            if (stack_level <= 0) {
3699                var = &args[n++];
3700                ZVAL_DEREF(var);
3701                SEPARATE_ZVAL_NOREF(var);
3702                if (Z_TYPE_P(var) == IS_ARRAY || Z_TYPE_P(var) == IS_OBJECT) {
3703                    target_hash = HASH_OF(var);
3704                    if (target_hash != NULL) {
3705                        zend_hash_internal_pointer_reset(target_hash);
3706                    }
3707                }
3708            } else {
3709                stack_level--;
3710                var = &stack[stack_level];
3711            }
3712            if (Z_TYPE_P(var) == IS_ARRAY || Z_TYPE_P(var) == IS_OBJECT) {
3713                target_hash = HASH_OF(var);
3714                if (target_hash != NULL) {
3715                    while ((hash_entry_ptr = zend_hash_get_current_data(target_hash)) != NULL) {
3716                        zend_hash_move_forward(target_hash);
3717                        if (Z_TYPE_P(hash_entry_ptr) == IS_INDIRECT) {
3718                            hash_entry_ptr = Z_INDIRECT_P(hash_entry_ptr);
3719                        }
3720                        hash_entry = hash_entry_ptr;
3721                        ZVAL_DEREF(hash_entry);
3722                        if (Z_TYPE_P(hash_entry) == IS_ARRAY || Z_TYPE_P(hash_entry) == IS_OBJECT) {
3723                            if (stack_level >= stack_max) {
3724                                stack_max += PHP_MBSTR_STACK_BLOCK_SIZE;
3725                                ptmp = erealloc(stack, sizeof(zval) * stack_max);
3726                                stack = (zval *)ptmp;
3727                            }
3728                            ZVAL_COPY_VALUE(&stack[stack_level], var);
3729                            stack_level++;
3730                            var = hash_entry;
3731                            SEPARATE_ZVAL(hash_entry);
3732                            target_hash = HASH_OF(var);
3733                            if (target_hash != NULL) {
3734                                zend_hash_internal_pointer_reset(target_hash);
3735                                continue;
3736                            }
3737                        } else if (Z_TYPE_P(hash_entry) == IS_STRING) {
3738                            string.val = (unsigned char *)Z_STRVAL_P(hash_entry);
3739                            string.len = Z_STRLEN_P(hash_entry);
3740                            ret = mbfl_buffer_converter_feed_result(convd, &string, &result);
3741                            if (ret != NULL) {
3742                                zval_ptr_dtor(hash_entry_ptr);
3743                                // TODO: avoid reallocation ???
3744                                ZVAL_STRINGL(hash_entry_ptr, (char *)ret->val, ret->len);
3745                                efree(ret->val);
3746                            }
3747                        }
3748                    }
3749                }
3750            } else if (Z_TYPE_P(var) == IS_STRING) {
3751                string.val = (unsigned char *)Z_STRVAL_P(var);
3752                string.len = Z_STRLEN_P(var);
3753                ret = mbfl_buffer_converter_feed_result(convd, &string, &result);
3754                if (ret != NULL) {
3755                    zval_ptr_dtor(var);
3756                    // TODO: avoid reallocation ???
3757                    ZVAL_STRINGL(var, (char *)ret->val, ret->len);
3758                    efree(ret->val);
3759                }
3760            }
3761        }
3762        efree(stack);
3763
3764        MBSTRG(illegalchars) += mbfl_buffer_illegalchars(convd);
3765        mbfl_buffer_converter_delete(convd);
3766    }
3767
3768    if (from_encoding) {
3769        RETURN_STRING(from_encoding->name);
3770    } else {
3771        RETURN_FALSE;
3772    }
3773}
3774/* }}} */
3775
3776/* {{{ HTML numeric entity */
3777/* {{{ static void php_mb_numericentity_exec() */
3778static void
3779php_mb_numericentity_exec(INTERNAL_FUNCTION_PARAMETERS, int type)
3780{
3781    char *str, *encoding;
3782    size_t str_len, encoding_len;
3783    zval *zconvmap, *hash_entry;
3784    HashTable *target_hash;
3785    int argc = ZEND_NUM_ARGS();
3786    int i, *convmap, *mapelm, mapsize=0;
3787    zend_bool is_hex = 0;
3788    mbfl_string string, result, *ret;
3789    enum mbfl_no_encoding no_encoding;
3790
3791    if (zend_parse_parameters(argc, "sz|sb", &str, &str_len, &zconvmap, &encoding, &encoding_len, &is_hex) == FAILURE) {
3792        return;
3793    }
3794
3795    mbfl_string_init(&string);
3796    string.no_language = MBSTRG(language);
3797    string.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
3798    string.val = (unsigned char *)str;
3799    string.len = str_len;
3800
3801    /* encoding */
3802    if ((argc == 3 || argc == 4) && encoding_len > 0) {
3803        no_encoding = mbfl_name2no_encoding(encoding);
3804        if (no_encoding == mbfl_no_encoding_invalid) {
3805            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", encoding);
3806            RETURN_FALSE;
3807        } else {
3808            string.no_encoding = no_encoding;
3809        }
3810    }
3811
3812    if (argc == 4) {
3813        if (type == 0 && is_hex) {
3814            type = 2; /* output in hex format */
3815        }
3816    }
3817
3818    /* conversion map */
3819    convmap = NULL;
3820    if (Z_TYPE_P(zconvmap) == IS_ARRAY) {
3821        target_hash = Z_ARRVAL_P(zconvmap);
3822        i = zend_hash_num_elements(target_hash);
3823        if (i > 0) {
3824            convmap = (int *)safe_emalloc(i, sizeof(int), 0);
3825            mapelm = convmap;
3826            mapsize = 0;
3827            ZEND_HASH_FOREACH_VAL(target_hash, hash_entry) {
3828                convert_to_long_ex(hash_entry);
3829                *mapelm++ = Z_LVAL_P(hash_entry);
3830                mapsize++;
3831            } ZEND_HASH_FOREACH_END();
3832        }
3833    }
3834    if (convmap == NULL) {
3835        RETURN_FALSE;
3836    }
3837    mapsize /= 4;
3838
3839    ret = mbfl_html_numeric_entity(&string, &result, convmap, mapsize, type);
3840    if (ret != NULL) {
3841        // TODO: avoid reallocation ???
3842        RETVAL_STRINGL((char *)ret->val, ret->len);
3843        efree(ret->val);
3844    } else {
3845        RETVAL_FALSE;
3846    }
3847    efree((void *)convmap);
3848}
3849/* }}} */
3850
3851/* {{{ proto string mb_encode_numericentity(string string, array convmap [, string encoding [, bool is_hex]])
3852   Converts specified characters to HTML numeric entities */
3853PHP_FUNCTION(mb_encode_numericentity)
3854{
3855    php_mb_numericentity_exec(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
3856}
3857/* }}} */
3858
3859/* {{{ proto string mb_decode_numericentity(string string, array convmap [, string encoding])
3860   Converts HTML numeric entities to character code */
3861PHP_FUNCTION(mb_decode_numericentity)
3862{
3863    php_mb_numericentity_exec(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
3864}
3865/* }}} */
3866/* }}} */
3867
3868/* {{{ proto int mb_send_mail(string to, string subject, string message [, string additional_headers [, string additional_parameters]])
3869 *  Sends an email message with MIME scheme
3870 */
3871
3872#define SKIP_LONG_HEADER_SEP_MBSTRING(str, pos)                                     \
3873    if (str[pos] == '\r' && str[pos + 1] == '\n' && (str[pos + 2] == ' ' || str[pos + 2] == '\t')) {    \
3874        pos += 2;                                           \
3875        while (str[pos + 1] == ' ' || str[pos + 1] == '\t') {                           \
3876            pos++;                                          \
3877        }                                               \
3878        continue;                                           \
3879    }
3880
3881#define MAIL_ASCIIZ_CHECK_MBSTRING(str, len)            \
3882    pp = str;                   \
3883    ee = pp + len;                  \
3884    while ((pp = memchr(pp, '\0', (ee - pp)))) {    \
3885        *pp = ' ';              \
3886    }                       \
3887
3888static int _php_mbstr_parse_mail_headers(HashTable *ht, const char *str, size_t str_len)
3889{
3890    const char *ps;
3891    size_t icnt;
3892    int state = 0;
3893    int crlf_state = -1;
3894    char *token = NULL;
3895    size_t token_pos = 0;
3896    zend_string *fld_name, *fld_val;
3897
3898    ps = str;
3899    icnt = str_len;
3900    fld_name = fld_val = NULL;
3901
3902    /*
3903     *             C o n t e n t - T y p e :   t e x t / h t m l \r\n
3904     *             ^ ^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^ ^^^^
3905     *      state  0            1           2          3
3906     *
3907     *             C o n t e n t - T y p e :   t e x t / h t m l \r\n
3908     *             ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^
3909     * crlf_state -1                       0                     1 -1
3910     *
3911     */
3912
3913    while (icnt > 0) {
3914        switch (*ps) {
3915            case ':':
3916                if (crlf_state == 1) {
3917                    token_pos++;
3918                }
3919
3920                if (state == 0 || state == 1) {
3921                    if(token && token_pos > 0) {
3922                        fld_name = zend_string_init(token, token_pos, 0);
3923                    }
3924                    state = 2;
3925                } else {
3926                    token_pos++;
3927                }
3928
3929                crlf_state = 0;
3930                break;
3931
3932            case '\n':
3933                if (crlf_state == -1) {
3934                    goto out;
3935                }
3936                crlf_state = -1;
3937                break;
3938
3939            case '\r':
3940                if (crlf_state == 1) {
3941                    token_pos++;
3942                } else {
3943                    crlf_state = 1;
3944                }
3945                break;
3946
3947            case ' ': case '\t':
3948                if (crlf_state == -1) {
3949                    if (state == 3) {
3950                        /* continuing from the previous line */
3951                        state = 4;
3952                    } else {
3953                        /* simply skipping this new line */
3954                        state = 5;
3955                    }
3956                } else {
3957                    if (crlf_state == 1) {
3958                        token_pos++;
3959                    }
3960                    if (state == 1 || state == 3) {
3961                        token_pos++;
3962                    }
3963                }
3964                crlf_state = 0;
3965                break;
3966
3967            default:
3968                switch (state) {
3969                    case 0:
3970                        token = (char*)ps;
3971                        token_pos = 0;
3972                        state = 1;
3973                        break;
3974
3975                    case 2:
3976                        if (crlf_state != -1) {
3977                            token = (char*)ps;
3978                            token_pos = 0;
3979
3980                            state = 3;
3981                            break;
3982                        }
3983                        /* break is missing intentionally */
3984
3985                    case 3:
3986                        if (crlf_state == -1) {
3987                            if(token && token_pos > 0) {
3988                                fld_val = zend_string_init(token, token_pos, 0);
3989                            }
3990
3991                            if (fld_name != NULL && fld_val != NULL) {
3992                                zval val;
3993                                /* FIXME: some locale free implementation is
3994                                 * really required here,,, */
3995                                php_strtoupper(fld_name->val, fld_name->len);
3996                                ZVAL_STR(&val, fld_val);
3997
3998                                zend_hash_update(ht, fld_name, &val);
3999
4000                                zend_string_release(fld_name);
4001                            }
4002
4003                            fld_name = fld_val = NULL;
4004                            token = (char*)ps;
4005                            token_pos = 0;
4006
4007                            state = 1;
4008                        }
4009                        break;
4010
4011                    case 4:
4012                        token_pos++;
4013                        state = 3;
4014                        break;
4015                }
4016
4017                if (crlf_state == 1) {
4018                    token_pos++;
4019                }
4020
4021                token_pos++;
4022
4023                crlf_state = 0;
4024                break;
4025        }
4026        ps++, icnt--;
4027    }
4028out:
4029    if (state == 2) {
4030        token = "";
4031        token_pos = 0;
4032
4033        state = 3;
4034    }
4035    if (state == 3) {
4036        if(token && token_pos > 0) {
4037            fld_val = zend_string_init(token, token_pos, 0);
4038        }
4039        if (fld_name != NULL && fld_val != NULL) {
4040            zval val;
4041            /* FIXME: some locale free implementation is
4042             * really required here,,, */
4043            php_strtoupper(fld_name->val, fld_name->len);
4044            ZVAL_STR(&val, fld_val);
4045
4046            zend_hash_update(ht, fld_name, &val);
4047
4048            zend_string_release(fld_name);
4049        }
4050    }
4051    return state;
4052}
4053
4054PHP_FUNCTION(mb_send_mail)
4055{
4056    int n;
4057    char *to = NULL;
4058    size_t to_len;
4059    char *message = NULL;
4060    size_t message_len;
4061    char *headers = NULL;
4062    size_t headers_len;
4063    char *subject = NULL;
4064    zend_string *extra_cmd = NULL;
4065    size_t subject_len;
4066    int i;
4067    char *to_r = NULL;
4068    char *force_extra_parameters = INI_STR("mail.force_extra_parameters");
4069    struct {
4070        int cnt_type:1;
4071        int cnt_trans_enc:1;
4072    } suppressed_hdrs = { 0, 0 };
4073
4074    char *message_buf = NULL, *subject_buf = NULL, *p;
4075    mbfl_string orig_str, conv_str;
4076    mbfl_string *pstr;  /* pointer to mbfl string for return value */
4077    enum mbfl_no_encoding
4078        tran_cs,    /* transfar text charset */
4079        head_enc,   /* header transfar encoding */
4080        body_enc;   /* body transfar encoding */
4081    mbfl_memory_device device;  /* automatic allocateable buffer for additional header */
4082    const mbfl_language *lang;
4083    int err = 0;
4084    HashTable ht_headers;
4085    zval *s;
4086    extern void mbfl_memory_device_unput(mbfl_memory_device *device);
4087    char *pp, *ee;
4088
4089    /* initialize */
4090    mbfl_memory_device_init(&device, 0, 0);
4091    mbfl_string_init(&orig_str);
4092    mbfl_string_init(&conv_str);
4093
4094    /* character-set, transfer-encoding */
4095    tran_cs = mbfl_no_encoding_utf8;
4096    head_enc = mbfl_no_encoding_base64;
4097    body_enc = mbfl_no_encoding_base64;
4098    lang = mbfl_no2language(MBSTRG(language));
4099    if (lang != NULL) {
4100        tran_cs = lang->mail_charset;
4101        head_enc = lang->mail_header_encoding;
4102        body_enc = lang->mail_body_encoding;
4103    }
4104
4105    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|sS", &to, &to_len, &subject, &subject_len, &message, &message_len, &headers, &headers_len, &extra_cmd) == FAILURE) {
4106        return;
4107    }
4108
4109    /* ASCIIZ check */
4110    MAIL_ASCIIZ_CHECK_MBSTRING(to, to_len);
4111    MAIL_ASCIIZ_CHECK_MBSTRING(subject, subject_len);
4112    MAIL_ASCIIZ_CHECK_MBSTRING(message, message_len);
4113    if (headers) {
4114        MAIL_ASCIIZ_CHECK_MBSTRING(headers, headers_len);
4115    }
4116    if (extra_cmd) {
4117        MAIL_ASCIIZ_CHECK_MBSTRING(extra_cmd->val, extra_cmd->len);
4118    }
4119
4120    zend_hash_init(&ht_headers, 0, NULL, ZVAL_PTR_DTOR, 0);
4121
4122    if (headers != NULL) {
4123        _php_mbstr_parse_mail_headers(&ht_headers, headers, headers_len);
4124    }
4125
4126    if ((s = zend_hash_str_find_ptr(&ht_headers, "CONTENT-TYPE", sizeof("CONTENT-TYPE") - 1))) {
4127        char *tmp;
4128        char *param_name;
4129        char *charset = NULL;
4130
4131        p = strchr(Z_STRVAL_P(s), ';');
4132
4133        if (p != NULL) {
4134            /* skipping the padded spaces */
4135            do {
4136                ++p;
4137            } while (*p == ' ' || *p == '\t');
4138
4139            if (*p != '\0') {
4140                if ((param_name = php_strtok_r(p, "= ", &tmp)) != NULL) {
4141                    if (strcasecmp(param_name, "charset") == 0) {
4142                        enum mbfl_no_encoding _tran_cs = tran_cs;
4143
4144                        charset = php_strtok_r(NULL, "= \"", &tmp);
4145                        if (charset != NULL) {
4146                            _tran_cs = mbfl_name2no_encoding(charset);
4147                        }
4148
4149                        if (_tran_cs == mbfl_no_encoding_invalid) {
4150                            php_error_docref(NULL, E_WARNING, "Unsupported charset \"%s\" - will be regarded as ascii", charset);
4151                            _tran_cs = mbfl_no_encoding_ascii;
4152                        }
4153                        tran_cs = _tran_cs;
4154                    }
4155                }
4156            }
4157        }
4158        suppressed_hdrs.cnt_type = 1;
4159    }
4160
4161    if ((s = zend_hash_str_find_ptr(&ht_headers, "CONTENT-TRANSFER-ENCODING", sizeof("CONTENT-TRANSFER-ENCODING") - 1))) {
4162        enum mbfl_no_encoding _body_enc;
4163
4164        _body_enc = mbfl_name2no_encoding(Z_STRVAL_P(s));
4165        switch (_body_enc) {
4166            case mbfl_no_encoding_base64:
4167            case mbfl_no_encoding_7bit:
4168            case mbfl_no_encoding_8bit:
4169                body_enc = _body_enc;
4170                break;
4171
4172            default:
4173                php_error_docref(NULL, E_WARNING, "Unsupported transfer encoding \"%s\" - will be regarded as 8bit", Z_STRVAL_P(s));
4174                body_enc =  mbfl_no_encoding_8bit;
4175                break;
4176        }
4177        suppressed_hdrs.cnt_trans_enc = 1;
4178    }
4179
4180    /* To: */
4181    if (to != NULL) {
4182        if (to_len > 0) {
4183            to_r = estrndup(to, to_len);
4184            for (; to_len; to_len--) {
4185                if (!isspace((unsigned char) to_r[to_len - 1])) {
4186                    break;
4187                }
4188                to_r[to_len - 1] = '\0';
4189            }
4190            for (i = 0; to_r[i]; i++) {
4191            if (iscntrl((unsigned char) to_r[i])) {
4192                /* According to RFC 822, section 3.1.1 long headers may be separated into
4193                 * parts using CRLF followed at least one linear-white-space character ('\t' or ' ').
4194                 * To prevent these separators from being replaced with a space, we use the
4195                 * SKIP_LONG_HEADER_SEP_MBSTRING to skip over them.
4196                 */
4197                SKIP_LONG_HEADER_SEP_MBSTRING(to_r, i);
4198                to_r[i] = ' ';
4199            }
4200            }
4201        } else {
4202            to_r = to;
4203        }
4204    } else {
4205        php_error_docref(NULL, E_WARNING, "Missing To: field");
4206        err = 1;
4207    }
4208
4209    /* Subject: */
4210    if (subject != NULL) {
4211        orig_str.no_language = MBSTRG(language);
4212        orig_str.val = (unsigned char *)subject;
4213        orig_str.len = subject_len;
4214        orig_str.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
4215        if (orig_str.no_encoding == mbfl_no_encoding_invalid || orig_str.no_encoding == mbfl_no_encoding_pass) {
4216            const mbfl_encoding *encoding = mbfl_identify_encoding2(&orig_str, MBSTRG(current_detect_order_list), MBSTRG(current_detect_order_list_size), MBSTRG(strict_detection));
4217            orig_str.no_encoding = encoding ? encoding->no_encoding: mbfl_no_encoding_invalid;
4218        }
4219        pstr = mbfl_mime_header_encode(&orig_str, &conv_str, tran_cs, head_enc, "\n", sizeof("Subject: [PHP-jp nnnnnnnn]"));
4220        if (pstr != NULL) {
4221            subject_buf = subject = (char *)pstr->val;
4222        }
4223    } else {
4224        php_error_docref(NULL, E_WARNING, "Missing Subject: field");
4225        err = 1;
4226    }
4227
4228    /* message body */
4229    if (message != NULL) {
4230        orig_str.no_language = MBSTRG(language);
4231        orig_str.val = (unsigned char *)message;
4232        orig_str.len = (unsigned int)message_len;
4233        orig_str.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
4234
4235        if (orig_str.no_encoding == mbfl_no_encoding_invalid || orig_str.no_encoding == mbfl_no_encoding_pass) {
4236            const mbfl_encoding *encoding = mbfl_identify_encoding2(&orig_str, MBSTRG(current_detect_order_list), MBSTRG(current_detect_order_list_size), MBSTRG(strict_detection));
4237            orig_str.no_encoding = encoding ? encoding->no_encoding: mbfl_no_encoding_invalid;
4238        }
4239
4240        pstr = NULL;
4241        {
4242            mbfl_string tmpstr;
4243
4244            if (mbfl_convert_encoding(&orig_str, &tmpstr, tran_cs) != NULL) {
4245                tmpstr.no_encoding=mbfl_no_encoding_8bit;
4246                pstr = mbfl_convert_encoding(&tmpstr, &conv_str, body_enc);
4247                efree(tmpstr.val);
4248            }
4249        }
4250        if (pstr != NULL) {
4251            message_buf = message = (char *)pstr->val;
4252        }
4253    } else {
4254        /* this is not really an error, so it is allowed. */
4255        php_error_docref(NULL, E_WARNING, "Empty message body");
4256        message = NULL;
4257    }
4258
4259    /* other headers */
4260#define PHP_MBSTR_MAIL_MIME_HEADER1 "MIME-Version: 1.0"
4261#define PHP_MBSTR_MAIL_MIME_HEADER2 "Content-Type: text/plain"
4262#define PHP_MBSTR_MAIL_MIME_HEADER3 "; charset="
4263#define PHP_MBSTR_MAIL_MIME_HEADER4 "Content-Transfer-Encoding: "
4264    if (headers != NULL) {
4265        p = headers;
4266        n = headers_len;
4267        mbfl_memory_device_strncat(&device, p, n);
4268        if (n > 0 && p[n - 1] != '\n') {
4269            mbfl_memory_device_strncat(&device, "\n", 1);
4270        }
4271    }
4272
4273    if (!zend_hash_str_exists(&ht_headers, "MIME-VERSION", sizeof("MIME-VERSION") - 1)) {
4274        mbfl_memory_device_strncat(&device, PHP_MBSTR_MAIL_MIME_HEADER1, sizeof(PHP_MBSTR_MAIL_MIME_HEADER1) - 1);
4275        mbfl_memory_device_strncat(&device, "\n", 1);
4276    }
4277
4278    if (!suppressed_hdrs.cnt_type) {
4279        mbfl_memory_device_strncat(&device, PHP_MBSTR_MAIL_MIME_HEADER2, sizeof(PHP_MBSTR_MAIL_MIME_HEADER2) - 1);
4280
4281        p = (char *)mbfl_no2preferred_mime_name(tran_cs);
4282        if (p != NULL) {
4283            mbfl_memory_device_strncat(&device, PHP_MBSTR_MAIL_MIME_HEADER3, sizeof(PHP_MBSTR_MAIL_MIME_HEADER3) - 1);
4284            mbfl_memory_device_strcat(&device, p);
4285        }
4286        mbfl_memory_device_strncat(&device, "\n", 1);
4287    }
4288    if (!suppressed_hdrs.cnt_trans_enc) {
4289        mbfl_memory_device_strncat(&device, PHP_MBSTR_MAIL_MIME_HEADER4, sizeof(PHP_MBSTR_MAIL_MIME_HEADER4) - 1);
4290        p = (char *)mbfl_no2preferred_mime_name(body_enc);
4291        if (p == NULL) {
4292            p = "7bit";
4293        }
4294        mbfl_memory_device_strcat(&device, p);
4295        mbfl_memory_device_strncat(&device, "\n", 1);
4296    }
4297
4298    mbfl_memory_device_unput(&device);
4299    mbfl_memory_device_output('\0', &device);
4300    headers = (char *)device.buffer;
4301
4302    if (force_extra_parameters) {
4303        extra_cmd = php_escape_shell_cmd(force_extra_parameters);
4304    } else if (extra_cmd) {
4305        extra_cmd = php_escape_shell_cmd(extra_cmd->val);
4306    }
4307
4308    if (!err && php_mail(to_r, subject, message, headers, extra_cmd ? extra_cmd->val : NULL)) {
4309        RETVAL_TRUE;
4310    } else {
4311        RETVAL_FALSE;
4312    }
4313
4314    if (extra_cmd) {
4315        zend_string_release(extra_cmd);
4316    }
4317
4318    if (to_r != to) {
4319        efree(to_r);
4320    }
4321    if (subject_buf) {
4322        efree((void *)subject_buf);
4323    }
4324    if (message_buf) {
4325        efree((void *)message_buf);
4326    }
4327    mbfl_memory_device_clear(&device);
4328    zend_hash_destroy(&ht_headers);
4329}
4330
4331#undef SKIP_LONG_HEADER_SEP_MBSTRING
4332#undef MAIL_ASCIIZ_CHECK_MBSTRING
4333#undef PHP_MBSTR_MAIL_MIME_HEADER1
4334#undef PHP_MBSTR_MAIL_MIME_HEADER2
4335#undef PHP_MBSTR_MAIL_MIME_HEADER3
4336#undef PHP_MBSTR_MAIL_MIME_HEADER4
4337/* }}} */
4338
4339/* {{{ proto mixed mb_get_info([string type])
4340   Returns the current settings of mbstring */
4341PHP_FUNCTION(mb_get_info)
4342{
4343    char *typ = NULL;
4344    size_t typ_len;
4345    size_t n;
4346    char *name;
4347    const struct mb_overload_def *over_func;
4348    zval row1, row2;
4349    const mbfl_language *lang = mbfl_no2language(MBSTRG(language));
4350    const mbfl_encoding **entry;
4351
4352    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s", &typ, &typ_len) == FAILURE) {
4353        return;
4354    }
4355
4356    if (!typ || !strcasecmp("all", typ)) {
4357        array_init(return_value);
4358        if (MBSTRG(current_internal_encoding)) {
4359            add_assoc_string(return_value, "internal_encoding", (char *)MBSTRG(current_internal_encoding)->name);
4360        }
4361        if (MBSTRG(http_input_identify)) {
4362            add_assoc_string(return_value, "http_input", (char *)MBSTRG(http_input_identify)->name);
4363        }
4364        if (MBSTRG(current_http_output_encoding)) {
4365            add_assoc_string(return_value, "http_output", (char *)MBSTRG(current_http_output_encoding)->name);
4366        }
4367        if ((name = (char *)zend_ini_string("mbstring.http_output_conv_mimetypes", sizeof("mbstring.http_output_conv_mimetypes") - 1, 0)) != NULL) {
4368            add_assoc_string(return_value, "http_output_conv_mimetypes", name);
4369        }
4370        add_assoc_long(return_value, "func_overload", MBSTRG(func_overload));
4371        if (MBSTRG(func_overload)){
4372            over_func = &(mb_ovld[0]);
4373            array_init(&row1);
4374            while (over_func->type > 0) {
4375                if ((MBSTRG(func_overload) & over_func->type) == over_func->type ) {
4376                    add_assoc_string(&row1, over_func->orig_func, over_func->ovld_func);
4377                }
4378                over_func++;
4379            }
4380            add_assoc_zval(return_value, "func_overload_list", &row1);
4381        } else {
4382            add_assoc_string(return_value, "func_overload_list", "no overload");
4383        }
4384        if (lang != NULL) {
4385            if ((name = (char *)mbfl_no_encoding2name(lang->mail_charset)) != NULL) {
4386                add_assoc_string(return_value, "mail_charset", name);
4387            }
4388            if ((name = (char *)mbfl_no_encoding2name(lang->mail_header_encoding)) != NULL) {
4389                add_assoc_string(return_value, "mail_header_encoding", name);
4390            }
4391            if ((name = (char *)mbfl_no_encoding2name(lang->mail_body_encoding)) != NULL) {
4392                add_assoc_string(return_value, "mail_body_encoding", name);
4393            }
4394        }
4395        add_assoc_long(return_value, "illegal_chars", MBSTRG(illegalchars));
4396        if (MBSTRG(encoding_translation)) {
4397            add_assoc_string(return_value, "encoding_translation", "On");
4398        } else {
4399            add_assoc_string(return_value, "encoding_translation", "Off");
4400        }
4401        if ((name = (char *)mbfl_no_language2name(MBSTRG(language))) != NULL) {
4402            add_assoc_string(return_value, "language", name);
4403        }
4404        n = MBSTRG(current_detect_order_list_size);
4405        entry = MBSTRG(current_detect_order_list);
4406        if (n > 0) {
4407            size_t i;
4408            array_init(&row2);
4409            for (i = 0; i < n; i++) {
4410                add_next_index_string(&row2, (*entry)->name);
4411                entry++;
4412            }
4413            add_assoc_zval(return_value, "detect_order", &row2);
4414        }
4415        if (MBSTRG(current_filter_illegal_mode) == MBFL_OUTPUTFILTER_ILLEGAL_MODE_NONE) {
4416            add_assoc_string(return_value, "substitute_character", "none");
4417        } else if (MBSTRG(current_filter_illegal_mode) == MBFL_OUTPUTFILTER_ILLEGAL_MODE_LONG) {
4418            add_assoc_string(return_value, "substitute_character", "long");
4419        } else if (MBSTRG(current_filter_illegal_mode) == MBFL_OUTPUTFILTER_ILLEGAL_MODE_ENTITY) {
4420            add_assoc_string(return_value, "substitute_character", "entity");
4421        } else {
4422            add_assoc_long(return_value, "substitute_character", MBSTRG(current_filter_illegal_substchar));
4423        }
4424        if (MBSTRG(strict_detection)) {
4425            add_assoc_string(return_value, "strict_detection", "On");
4426        } else {
4427            add_assoc_string(return_value, "strict_detection", "Off");
4428        }
4429    } else if (!strcasecmp("internal_encoding", typ)) {
4430        if (MBSTRG(current_internal_encoding)) {
4431            RETVAL_STRING((char *)MBSTRG(current_internal_encoding)->name);
4432        }
4433    } else if (!strcasecmp("http_input", typ)) {
4434        if (MBSTRG(http_input_identify)) {
4435            RETVAL_STRING((char *)MBSTRG(http_input_identify)->name);
4436        }
4437    } else if (!strcasecmp("http_output", typ)) {
4438        if (MBSTRG(current_http_output_encoding)) {
4439            RETVAL_STRING((char *)MBSTRG(current_http_output_encoding)->name);
4440        }
4441    } else if (!strcasecmp("http_output_conv_mimetypes", typ)) {
4442        if ((name = (char *)zend_ini_string("mbstring.http_output_conv_mimetypes", sizeof("mbstring.http_output_conv_mimetypes") - 1, 0)) != NULL) {
4443            RETVAL_STRING(name);
4444        }
4445    } else if (!strcasecmp("func_overload", typ)) {
4446        RETVAL_LONG(MBSTRG(func_overload));
4447    } else if (!strcasecmp("func_overload_list", typ)) {
4448        if (MBSTRG(func_overload)){
4449                over_func = &(mb_ovld[0]);
4450                array_init(return_value);
4451                while (over_func->type > 0) {
4452                    if ((MBSTRG(func_overload) & over_func->type) == over_func->type ) {
4453                        add_assoc_string(return_value, over_func->orig_func, over_func->ovld_func);
4454                    }
4455                    over_func++;
4456                }
4457        } else {
4458            RETVAL_STRING("no overload");
4459        }
4460    } else if (!strcasecmp("mail_charset", typ)) {
4461        if (lang != NULL && (name = (char *)mbfl_no_encoding2name(lang->mail_charset)) != NULL) {
4462            RETVAL_STRING(name);
4463        }
4464    } else if (!strcasecmp("mail_header_encoding", typ)) {
4465        if (lang != NULL && (name = (char *)mbfl_no_encoding2name(lang->mail_header_encoding)) != NULL) {
4466            RETVAL_STRING(name);
4467        }
4468    } else if (!strcasecmp("mail_body_encoding", typ)) {
4469        if (lang != NULL && (name = (char *)mbfl_no_encoding2name(lang->mail_body_encoding)) != NULL) {
4470            RETVAL_STRING(name);
4471        }
4472    } else if (!strcasecmp("illegal_chars", typ)) {
4473        RETVAL_LONG(MBSTRG(illegalchars));
4474    } else if (!strcasecmp("encoding_translation", typ)) {
4475        if (MBSTRG(encoding_translation)) {
4476            RETVAL_STRING("On");
4477        } else {
4478            RETVAL_STRING("Off");
4479        }
4480    } else if (!strcasecmp("language", typ)) {
4481        if ((name = (char *)mbfl_no_language2name(MBSTRG(language))) != NULL) {
4482            RETVAL_STRING(name);
4483        }
4484    } else if (!strcasecmp("detect_order", typ)) {
4485        n = MBSTRG(current_detect_order_list_size);
4486        entry = MBSTRG(current_detect_order_list);
4487        if (n > 0) {
4488            size_t i;
4489            array_init(return_value);
4490            for (i = 0; i < n; i++) {
4491                add_next_index_string(return_value, (*entry)->name);
4492                entry++;
4493            }
4494        }
4495    } else if (!strcasecmp("substitute_character", typ)) {
4496        if (MBSTRG(current_filter_illegal_mode) == MBFL_OUTPUTFILTER_ILLEGAL_MODE_NONE) {
4497            RETVAL_STRING("none");
4498        } else if (MBSTRG(current_filter_illegal_mode) == MBFL_OUTPUTFILTER_ILLEGAL_MODE_LONG) {
4499            RETVAL_STRING("long");
4500        } else if (MBSTRG(current_filter_illegal_mode) == MBFL_OUTPUTFILTER_ILLEGAL_MODE_ENTITY) {
4501            RETVAL_STRING("entity");
4502        } else {
4503            RETVAL_LONG(MBSTRG(current_filter_illegal_substchar));
4504        }
4505    } else if (!strcasecmp("strict_detection", typ)) {
4506        if (MBSTRG(strict_detection)) {
4507            RETVAL_STRING("On");
4508        } else {
4509            RETVAL_STRING("Off");
4510        }
4511    } else {
4512        RETURN_FALSE;
4513    }
4514}
4515/* }}} */
4516
4517/* {{{ proto bool mb_check_encoding([string var[, string encoding]])
4518   Check if the string is valid for the specified encoding */
4519PHP_FUNCTION(mb_check_encoding)
4520{
4521    char *var = NULL;
4522    size_t var_len;
4523    char *enc = NULL;
4524    size_t enc_len;
4525    mbfl_buffer_converter *convd;
4526    const mbfl_encoding *encoding = MBSTRG(current_internal_encoding);
4527    mbfl_string string, result, *ret = NULL;
4528    long illegalchars = 0;
4529
4530    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ss", &var, &var_len, &enc, &enc_len) == FAILURE) {
4531        return;
4532    }
4533
4534    if (var == NULL) {
4535        RETURN_BOOL(MBSTRG(illegalchars) == 0);
4536    }
4537
4538    if (enc != NULL) {
4539        encoding = mbfl_name2encoding(enc);
4540        if (!encoding || encoding == &mbfl_encoding_pass) {
4541            php_error_docref(NULL, E_WARNING, "Invalid encoding \"%s\"", enc);
4542            RETURN_FALSE;
4543        }
4544    }
4545
4546    convd = mbfl_buffer_converter_new2(encoding, encoding, 0);
4547    if (convd == NULL) {
4548        php_error_docref(NULL, E_WARNING, "Unable to create converter");
4549        RETURN_FALSE;
4550    }
4551    mbfl_buffer_converter_illegal_mode(convd, MBFL_OUTPUTFILTER_ILLEGAL_MODE_NONE);
4552    mbfl_buffer_converter_illegal_substchar(convd, 0);
4553
4554    /* initialize string */
4555    mbfl_string_init_set(&string, mbfl_no_language_neutral, encoding->no_encoding);
4556    mbfl_string_init(&result);
4557
4558    string.val = (unsigned char *)var;
4559    string.len = var_len;
4560    ret = mbfl_buffer_converter_feed_result(convd, &string, &result);
4561    illegalchars = mbfl_buffer_illegalchars(convd);
4562    mbfl_buffer_converter_delete(convd);
4563
4564    RETVAL_FALSE;
4565    if (ret != NULL) {
4566        if (illegalchars == 0 && string.len == result.len && memcmp(string.val, result.val, string.len) == 0) {
4567            RETVAL_TRUE;
4568        }
4569        mbfl_string_clear(&result);
4570    }
4571}
4572/* }}} */
4573
4574/* {{{ php_mb_populate_current_detect_order_list */
4575static void php_mb_populate_current_detect_order_list(void)
4576{
4577    const mbfl_encoding **entry = 0;
4578    size_t nentries;
4579
4580    if (MBSTRG(current_detect_order_list)) {
4581        return;
4582    }
4583
4584    if (MBSTRG(detect_order_list) && MBSTRG(detect_order_list_size)) {
4585        nentries = MBSTRG(detect_order_list_size);
4586        entry = (const mbfl_encoding **)safe_emalloc(nentries, sizeof(mbfl_encoding*), 0);
4587        memcpy(entry, MBSTRG(detect_order_list), sizeof(mbfl_encoding*) * nentries);
4588    } else {
4589        const enum mbfl_no_encoding *src = MBSTRG(default_detect_order_list);
4590        size_t i;
4591        nentries = MBSTRG(default_detect_order_list_size);
4592        entry = (const mbfl_encoding **)safe_emalloc(nentries, sizeof(mbfl_encoding*), 0);
4593        for (i = 0; i < nentries; i++) {
4594            entry[i] = mbfl_no2encoding(src[i]);
4595        }
4596    }
4597    MBSTRG(current_detect_order_list) = entry;
4598    MBSTRG(current_detect_order_list_size) = nentries;
4599}
4600/* }}} */
4601
4602/* {{{ static int php_mb_encoding_translation() */
4603static int php_mb_encoding_translation(void)
4604{
4605    return MBSTRG(encoding_translation);
4606}
4607/* }}} */
4608
4609/* {{{ MBSTRING_API size_t php_mb_mbchar_bytes_ex() */
4610MBSTRING_API size_t php_mb_mbchar_bytes_ex(const char *s, const mbfl_encoding *enc)
4611{
4612    if (enc != NULL) {
4613        if (enc->flag & MBFL_ENCTYPE_MBCS) {
4614            if (enc->mblen_table != NULL) {
4615                if (s != NULL) return enc->mblen_table[*(unsigned char *)s];
4616            }
4617        } else if (enc->flag & (MBFL_ENCTYPE_WCS2BE | MBFL_ENCTYPE_WCS2LE)) {
4618            return 2;
4619        } else if (enc->flag & (MBFL_ENCTYPE_WCS4BE | MBFL_ENCTYPE_WCS4LE)) {
4620            return 4;
4621        }
4622    }
4623    return 1;
4624}
4625/* }}} */
4626
4627/* {{{ MBSTRING_API size_t php_mb_mbchar_bytes() */
4628MBSTRING_API size_t php_mb_mbchar_bytes(const char *s)
4629{
4630    return php_mb_mbchar_bytes_ex(s, MBSTRG(internal_encoding));
4631}
4632/* }}} */
4633
4634/* {{{ MBSTRING_API char *php_mb_safe_strrchr_ex() */
4635MBSTRING_API char *php_mb_safe_strrchr_ex(const char *s, unsigned int c, size_t nbytes, const mbfl_encoding *enc)
4636{
4637    register const char *p = s;
4638    char *last=NULL;
4639
4640    if (nbytes == (size_t)-1) {
4641        size_t nb = 0;
4642
4643        while (*p != '\0') {
4644            if (nb == 0) {
4645                if ((unsigned char)*p == (unsigned char)c) {
4646                    last = (char *)p;
4647                }
4648                nb = php_mb_mbchar_bytes_ex(p, enc);
4649                if (nb == 0) {
4650                    return NULL; /* something is going wrong! */
4651                }
4652            }
4653            --nb;
4654            ++p;
4655        }
4656    } else {
4657        register size_t bcnt = nbytes;
4658        register size_t nbytes_char;
4659        while (bcnt > 0) {
4660            if ((unsigned char)*p == (unsigned char)c) {
4661                last = (char *)p;
4662            }
4663            nbytes_char = php_mb_mbchar_bytes_ex(p, enc);
4664            if (bcnt < nbytes_char) {
4665                return NULL;
4666            }
4667            p += nbytes_char;
4668            bcnt -= nbytes_char;
4669        }
4670    }
4671    return last;
4672}
4673/* }}} */
4674
4675/* {{{ MBSTRING_API char *php_mb_safe_strrchr() */
4676MBSTRING_API char *php_mb_safe_strrchr(const char *s, unsigned int c, size_t nbytes)
4677{
4678    return php_mb_safe_strrchr_ex(s, c, nbytes, MBSTRG(internal_encoding));
4679}
4680/* }}} */
4681
4682/* {{{ MBSTRING_API int php_mb_stripos()
4683 */
4684MBSTRING_API int php_mb_stripos(int mode, const char *old_haystack, unsigned int old_haystack_len, const char *old_needle, unsigned int old_needle_len, long offset, const char *from_encoding)
4685{
4686    int n;
4687    mbfl_string haystack, needle;
4688    n = -1;
4689
4690    mbfl_string_init(&haystack);
4691    mbfl_string_init(&needle);
4692    haystack.no_language = MBSTRG(language);
4693    haystack.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
4694    needle.no_language = MBSTRG(language);
4695    needle.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
4696
4697    do {
4698        size_t len = 0;
4699        haystack.val = (unsigned char *)php_unicode_convert_case(PHP_UNICODE_CASE_UPPER, (char *)old_haystack, old_haystack_len, &len, from_encoding);
4700        haystack.len = len;
4701
4702        if (!haystack.val) {
4703            break;
4704        }
4705
4706        if (haystack.len <= 0) {
4707            break;
4708        }
4709
4710        needle.val = (unsigned char *)php_unicode_convert_case(PHP_UNICODE_CASE_UPPER, (char *)old_needle, old_needle_len, &len, from_encoding);
4711        needle.len = len;
4712
4713        if (!needle.val) {
4714            break;
4715        }
4716
4717        if (needle.len <= 0) {
4718            break;
4719        }
4720
4721        haystack.no_encoding = needle.no_encoding = mbfl_name2no_encoding(from_encoding);
4722        if (haystack.no_encoding == mbfl_no_encoding_invalid) {
4723            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", from_encoding);
4724            break;
4725        }
4726
4727        {
4728            int haystack_char_len = mbfl_strlen(&haystack);
4729
4730            if (mode) {
4731                if ((offset > 0 && offset > haystack_char_len) ||
4732                    (offset < 0 && -offset > haystack_char_len)) {
4733                    php_error_docref(NULL, E_WARNING, "Offset is greater than the length of haystack string");
4734                    break;
4735                }
4736            } else {
4737                if (offset < 0 || offset > haystack_char_len) {
4738                    php_error_docref(NULL, E_WARNING, "Offset not contained in string");
4739                    break;
4740                }
4741            }
4742        }
4743
4744        n = mbfl_strpos(&haystack, &needle, offset, mode);
4745    } while(0);
4746
4747    if (haystack.val) {
4748        efree(haystack.val);
4749    }
4750
4751    if (needle.val) {
4752        efree(needle.val);
4753    }
4754
4755    return n;
4756}
4757/* }}} */
4758
4759static void php_mb_gpc_get_detect_order(const zend_encoding ***list, size_t *list_size) /* {{{ */
4760{
4761    *list = (const zend_encoding **)MBSTRG(http_input_list);
4762    *list_size = MBSTRG(http_input_list_size);
4763}
4764/* }}} */
4765
4766static void php_mb_gpc_set_input_encoding(const zend_encoding *encoding) /* {{{ */
4767{
4768    MBSTRG(http_input_identify) = (const mbfl_encoding*)encoding;
4769}
4770/* }}} */
4771
4772#endif  /* HAVE_MBSTRING */
4773
4774/*
4775 * Local variables:
4776 * tab-width: 4
4777 * c-basic-offset: 4
4778 * End:
4779 * vim600: fdm=marker
4780 * vim: noet sw=4 ts=4
4781 */
4782