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#if HAVE_ONIG
1720    {
1721        char tmp[256];
1722        snprintf(tmp, sizeof(tmp), "%d.%d.%d", ONIGURUMA_VERSION_MAJOR, ONIGURUMA_VERSION_MINOR, ONIGURUMA_VERSION_TEENY);
1723        php_info_print_table_row(2, "oniguruma version", tmp);
1724    }
1725#endif
1726    php_info_print_table_end();
1727
1728    php_info_print_table_start();
1729    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.");
1730    php_info_print_table_end();
1731
1732#if HAVE_MBREGEX
1733    PHP_MINFO(mb_regex)(ZEND_MODULE_INFO_FUNC_ARGS_PASSTHRU);
1734#endif
1735
1736    DISPLAY_INI_ENTRIES();
1737}
1738/* }}} */
1739
1740/* {{{ proto string mb_language([string language])
1741   Sets the current language or Returns the current language as a string */
1742PHP_FUNCTION(mb_language)
1743{
1744    zend_string *name = NULL;
1745
1746    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S", &name) == FAILURE) {
1747        return;
1748    }
1749    if (name == NULL) {
1750        RETVAL_STRING((char *)mbfl_no_language2name(MBSTRG(language)));
1751    } else {
1752        zend_string *ini_name = zend_string_init("mbstring.language", sizeof("mbstring.language") - 1, 0);
1753        if (FAILURE == zend_alter_ini_entry(ini_name, name, PHP_INI_USER, PHP_INI_STAGE_RUNTIME)) {
1754            php_error_docref(NULL, E_WARNING, "Unknown language \"%s\"", name->val);
1755            RETVAL_FALSE;
1756        } else {
1757            RETVAL_TRUE;
1758        }
1759        zend_string_release(ini_name);
1760    }
1761}
1762/* }}} */
1763
1764/* {{{ proto string mb_internal_encoding([string encoding])
1765   Sets the current internal encoding or Returns the current internal encoding as a string */
1766PHP_FUNCTION(mb_internal_encoding)
1767{
1768    const char *name = NULL;
1769    size_t name_len;
1770    const mbfl_encoding *encoding;
1771
1772    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s", &name, &name_len) == FAILURE) {
1773        return;
1774    }
1775    if (name == NULL) {
1776        name = MBSTRG(current_internal_encoding) ? MBSTRG(current_internal_encoding)->name: NULL;
1777        if (name != NULL) {
1778            RETURN_STRING(name);
1779        } else {
1780            RETURN_FALSE;
1781        }
1782    } else {
1783        encoding = mbfl_name2encoding(name);
1784        if (!encoding) {
1785            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", name);
1786            RETURN_FALSE;
1787        } else {
1788            MBSTRG(current_internal_encoding) = encoding;
1789            RETURN_TRUE;
1790        }
1791    }
1792}
1793/* }}} */
1794
1795/* {{{ proto mixed mb_http_input([string type])
1796   Returns the input encoding */
1797PHP_FUNCTION(mb_http_input)
1798{
1799    char *typ = NULL;
1800    size_t typ_len;
1801    int retname;
1802    char *list, *temp;
1803    const mbfl_encoding *result = NULL;
1804
1805    retname = 1;
1806    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s", &typ, &typ_len) == FAILURE) {
1807        return;
1808    }
1809    if (typ == NULL) {
1810        result = MBSTRG(http_input_identify);
1811    } else {
1812        switch (*typ) {
1813        case 'G':
1814        case 'g':
1815            result = MBSTRG(http_input_identify_get);
1816            break;
1817        case 'P':
1818        case 'p':
1819            result = MBSTRG(http_input_identify_post);
1820            break;
1821        case 'C':
1822        case 'c':
1823            result = MBSTRG(http_input_identify_cookie);
1824            break;
1825        case 'S':
1826        case 's':
1827            result = MBSTRG(http_input_identify_string);
1828            break;
1829        case 'I':
1830        case 'i':
1831            {
1832                const mbfl_encoding **entry = MBSTRG(http_input_list);
1833                const size_t n = MBSTRG(http_input_list_size);
1834                size_t i;
1835                array_init(return_value);
1836                for (i = 0; i < n; i++) {
1837                    add_next_index_string(return_value, (*entry)->name);
1838                    entry++;
1839                }
1840                retname = 0;
1841            }
1842            break;
1843        case 'L':
1844        case 'l':
1845            {
1846                const mbfl_encoding **entry = MBSTRG(http_input_list);
1847                const size_t n = MBSTRG(http_input_list_size);
1848                size_t i;
1849                list = NULL;
1850                for (i = 0; i < n; i++) {
1851                    if (list) {
1852                        temp = list;
1853                        spprintf(&list, 0, "%s,%s", temp, (*entry)->name);
1854                        efree(temp);
1855                        if (!list) {
1856                            break;
1857                        }
1858                    } else {
1859                        list = estrdup((*entry)->name);
1860                    }
1861                    entry++;
1862                }
1863            }
1864            if (!list) {
1865                RETURN_FALSE;
1866            }
1867            RETVAL_STRING(list);
1868            efree(list);
1869            retname = 0;
1870            break;
1871        default:
1872            result = MBSTRG(http_input_identify);
1873            break;
1874        }
1875    }
1876
1877    if (retname) {
1878        if (result) {
1879            RETVAL_STRING(result->name);
1880        } else {
1881            RETVAL_FALSE;
1882        }
1883    }
1884}
1885/* }}} */
1886
1887/* {{{ proto string mb_http_output([string encoding])
1888   Sets the current output_encoding or returns the current output_encoding as a string */
1889PHP_FUNCTION(mb_http_output)
1890{
1891    const char *name = NULL;
1892    size_t name_len;
1893    const mbfl_encoding *encoding;
1894
1895    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s", &name, &name_len) == FAILURE) {
1896        return;
1897    }
1898
1899    if (name == NULL) {
1900        name = MBSTRG(current_http_output_encoding) ? MBSTRG(current_http_output_encoding)->name: NULL;
1901        if (name != NULL) {
1902            RETURN_STRING(name);
1903        } else {
1904            RETURN_FALSE;
1905        }
1906    } else {
1907        encoding = mbfl_name2encoding(name);
1908        if (!encoding) {
1909            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", name);
1910            RETURN_FALSE;
1911        } else {
1912            MBSTRG(current_http_output_encoding) = encoding;
1913            RETURN_TRUE;
1914        }
1915    }
1916}
1917/* }}} */
1918
1919/* {{{ proto bool|array mb_detect_order([mixed encoding-list])
1920   Sets the current detect_order or Return the current detect_order as a array */
1921PHP_FUNCTION(mb_detect_order)
1922{
1923    zval *arg1 = NULL;
1924
1925    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|z", &arg1) == FAILURE) {
1926        return;
1927    }
1928
1929    if (!arg1) {
1930        size_t i;
1931        size_t n = MBSTRG(current_detect_order_list_size);
1932        const mbfl_encoding **entry = MBSTRG(current_detect_order_list);
1933        array_init(return_value);
1934        for (i = 0; i < n; i++) {
1935            add_next_index_string(return_value, (*entry)->name);
1936            entry++;
1937        }
1938    } else {
1939        const mbfl_encoding **list = NULL;
1940        size_t size = 0;
1941        switch (Z_TYPE_P(arg1)) {
1942            case IS_ARRAY:
1943                if (FAILURE == php_mb_parse_encoding_array(arg1, &list, &size, 0)) {
1944                    if (list) {
1945                        efree(list);
1946                    }
1947                    RETURN_FALSE;
1948                }
1949                break;
1950            default:
1951                convert_to_string_ex(arg1);
1952                if (FAILURE == php_mb_parse_encoding_list(Z_STRVAL_P(arg1), Z_STRLEN_P(arg1), &list, &size, 0)) {
1953                    if (list) {
1954                        efree(list);
1955                    }
1956                    RETURN_FALSE;
1957                }
1958                break;
1959        }
1960
1961        if (list == NULL) {
1962            RETURN_FALSE;
1963        }
1964
1965        if (MBSTRG(current_detect_order_list)) {
1966            efree(MBSTRG(current_detect_order_list));
1967        }
1968        MBSTRG(current_detect_order_list) = list;
1969        MBSTRG(current_detect_order_list_size) = size;
1970        RETURN_TRUE;
1971    }
1972}
1973/* }}} */
1974
1975/* {{{ proto mixed mb_substitute_character([mixed substchar])
1976   Sets the current substitute_character or returns the current substitute_character */
1977PHP_FUNCTION(mb_substitute_character)
1978{
1979    zval *arg1 = NULL;
1980
1981    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|z", &arg1) == FAILURE) {
1982        return;
1983    }
1984
1985    if (!arg1) {
1986        if (MBSTRG(current_filter_illegal_mode) == MBFL_OUTPUTFILTER_ILLEGAL_MODE_NONE) {
1987            RETURN_STRING("none");
1988        } else if (MBSTRG(current_filter_illegal_mode) == MBFL_OUTPUTFILTER_ILLEGAL_MODE_LONG) {
1989            RETURN_STRING("long");
1990        } else if (MBSTRG(current_filter_illegal_mode) == MBFL_OUTPUTFILTER_ILLEGAL_MODE_ENTITY) {
1991            RETURN_STRING("entity");
1992        } else {
1993            RETURN_LONG(MBSTRG(current_filter_illegal_substchar));
1994        }
1995    } else {
1996        RETVAL_TRUE;
1997
1998        switch (Z_TYPE_P(arg1)) {
1999            case IS_STRING:
2000                if (strncasecmp("none", Z_STRVAL_P(arg1), Z_STRLEN_P(arg1)) == 0) {
2001                    MBSTRG(current_filter_illegal_mode) = MBFL_OUTPUTFILTER_ILLEGAL_MODE_NONE;
2002                } else if (strncasecmp("long", Z_STRVAL_P(arg1), Z_STRLEN_P(arg1)) == 0) {
2003                    MBSTRG(current_filter_illegal_mode) = MBFL_OUTPUTFILTER_ILLEGAL_MODE_LONG;
2004                } else if (strncasecmp("entity", Z_STRVAL_P(arg1), Z_STRLEN_P(arg1)) == 0) {
2005                    MBSTRG(current_filter_illegal_mode) = MBFL_OUTPUTFILTER_ILLEGAL_MODE_ENTITY;
2006                } else {
2007                    convert_to_long_ex(arg1);
2008
2009                    if (Z_LVAL_P(arg1) < 0xffff && Z_LVAL_P(arg1) > 0x0) {
2010                        MBSTRG(current_filter_illegal_mode) = MBFL_OUTPUTFILTER_ILLEGAL_MODE_CHAR;
2011                        MBSTRG(current_filter_illegal_substchar) = Z_LVAL_P(arg1);
2012                    } else {
2013                        php_error_docref(NULL, E_WARNING, "Unknown character.");
2014                        RETURN_FALSE;
2015                    }
2016                }
2017                break;
2018            default:
2019                convert_to_long_ex(arg1);
2020                if (Z_LVAL_P(arg1) < 0xffff && Z_LVAL_P(arg1) > 0x0) {
2021                    MBSTRG(current_filter_illegal_mode) = MBFL_OUTPUTFILTER_ILLEGAL_MODE_CHAR;
2022                    MBSTRG(current_filter_illegal_substchar) = Z_LVAL_P(arg1);
2023                } else {
2024                    php_error_docref(NULL, E_WARNING, "Unknown character.");
2025                    RETURN_FALSE;
2026                }
2027                break;
2028        }
2029    }
2030}
2031/* }}} */
2032
2033/* {{{ proto string mb_preferred_mime_name(string encoding)
2034   Return the preferred MIME name (charset) as a string */
2035PHP_FUNCTION(mb_preferred_mime_name)
2036{
2037    enum mbfl_no_encoding no_encoding;
2038    char *name = NULL;
2039    size_t name_len;
2040
2041    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
2042        return;
2043    } else {
2044        no_encoding = mbfl_name2no_encoding(name);
2045        if (no_encoding == mbfl_no_encoding_invalid) {
2046            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", name);
2047            RETVAL_FALSE;
2048        } else {
2049            const char *preferred_name = mbfl_no2preferred_mime_name(no_encoding);
2050            if (preferred_name == NULL || *preferred_name == '\0') {
2051                php_error_docref(NULL, E_WARNING, "No MIME preferred name corresponding to \"%s\"", name);
2052                RETVAL_FALSE;
2053            } else {
2054                RETVAL_STRING((char *)preferred_name);
2055            }
2056        }
2057    }
2058}
2059/* }}} */
2060
2061#define IS_SJIS1(c) ((((c)>=0x81 && (c)<=0x9f) || ((c)>=0xe0 && (c)<=0xf5)) ? 1 : 0)
2062#define IS_SJIS2(c) ((((c)>=0x40 && (c)<=0x7e) || ((c)>=0x80 && (c)<=0xfc)) ? 1 : 0)
2063
2064/* {{{ proto bool mb_parse_str(string encoded_string [, array result])
2065   Parses GET/POST/COOKIE data and sets global variables */
2066PHP_FUNCTION(mb_parse_str)
2067{
2068    zval *track_vars_array = NULL;
2069    char *encstr = NULL;
2070    size_t encstr_len;
2071    php_mb_encoding_handler_info_t info;
2072    const mbfl_encoding *detected;
2073
2074    track_vars_array = NULL;
2075    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|z/", &encstr, &encstr_len, &track_vars_array) == FAILURE) {
2076        return;
2077    }
2078
2079    if (track_vars_array != NULL) {
2080        /* Clear out the array */
2081        zval_dtor(track_vars_array);
2082        array_init(track_vars_array);
2083    }
2084
2085    encstr = estrndup(encstr, encstr_len);
2086
2087    info.data_type              = PARSE_STRING;
2088    info.separator              = PG(arg_separator).input;
2089    info.report_errors          = 1;
2090    info.to_encoding            = MBSTRG(current_internal_encoding);
2091    info.to_language            = MBSTRG(language);
2092    info.from_encodings         = MBSTRG(http_input_list);
2093    info.num_from_encodings     = MBSTRG(http_input_list_size);
2094    info.from_language          = MBSTRG(language);
2095
2096    if (track_vars_array != NULL) {
2097        detected = _php_mb_encoding_handler_ex(&info, track_vars_array, encstr);
2098    } else {
2099        zval tmp;
2100        zend_array *symbol_table = zend_rebuild_symbol_table();
2101
2102        ZVAL_ARR(&tmp, symbol_table);
2103        detected = _php_mb_encoding_handler_ex(&info, &tmp, encstr);
2104    }
2105
2106    MBSTRG(http_input_identify) = detected;
2107
2108    RETVAL_BOOL(detected);
2109
2110    if (encstr != NULL) efree(encstr);
2111}
2112/* }}} */
2113
2114/* {{{ proto string mb_output_handler(string contents, int status)
2115   Returns string in output buffer converted to the http_output encoding */
2116PHP_FUNCTION(mb_output_handler)
2117{
2118    char *arg_string;
2119    size_t arg_string_len;
2120    zend_long arg_status;
2121    mbfl_string string, result;
2122    const char *charset;
2123    char *p;
2124    const mbfl_encoding *encoding;
2125    int last_feed, len;
2126    unsigned char send_text_mimetype = 0;
2127    char *s, *mimetype = NULL;
2128
2129    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl", &arg_string, &arg_string_len, &arg_status) == FAILURE) {
2130        return;
2131    }
2132
2133    encoding = MBSTRG(current_http_output_encoding);
2134
2135    /* start phase only */
2136    if ((arg_status & PHP_OUTPUT_HANDLER_START) != 0) {
2137        /* delete the converter just in case. */
2138        if (MBSTRG(outconv)) {
2139            MBSTRG(illegalchars) += mbfl_buffer_illegalchars(MBSTRG(outconv));
2140            mbfl_buffer_converter_delete(MBSTRG(outconv));
2141            MBSTRG(outconv) = NULL;
2142        }
2143        if (encoding == &mbfl_encoding_pass) {
2144            RETURN_STRINGL(arg_string, arg_string_len);
2145        }
2146
2147        /* analyze mime type */
2148        if (SG(sapi_headers).mimetype &&
2149            _php_mb_match_regex(
2150                MBSTRG(http_output_conv_mimetypes),
2151                SG(sapi_headers).mimetype,
2152                strlen(SG(sapi_headers).mimetype))) {
2153            if ((s = strchr(SG(sapi_headers).mimetype,';')) == NULL){
2154                mimetype = estrdup(SG(sapi_headers).mimetype);
2155            } else {
2156                mimetype = estrndup(SG(sapi_headers).mimetype,s-SG(sapi_headers).mimetype);
2157            }
2158            send_text_mimetype = 1;
2159        } else if (SG(sapi_headers).send_default_content_type) {
2160            mimetype = SG(default_mimetype) ? SG(default_mimetype) : SAPI_DEFAULT_MIMETYPE;
2161        }
2162
2163        /* if content-type is not yet set, set it and activate the converter */
2164        if (SG(sapi_headers).send_default_content_type || send_text_mimetype) {
2165            charset = encoding->mime_name;
2166            if (charset) {
2167                len = spprintf( &p, 0, "Content-Type: %s; charset=%s",  mimetype, charset );
2168                if (sapi_add_header(p, len, 0) != FAILURE) {
2169                    SG(sapi_headers).send_default_content_type = 0;
2170                }
2171            }
2172            /* activate the converter */
2173            MBSTRG(outconv) = mbfl_buffer_converter_new2(MBSTRG(current_internal_encoding), encoding, 0);
2174            if (send_text_mimetype){
2175                efree(mimetype);
2176            }
2177        }
2178    }
2179
2180    /* just return if the converter is not activated. */
2181    if (MBSTRG(outconv) == NULL) {
2182        RETURN_STRINGL(arg_string, arg_string_len);
2183    }
2184
2185    /* flag */
2186    last_feed = ((arg_status & PHP_OUTPUT_HANDLER_END) != 0);
2187    /* mode */
2188    mbfl_buffer_converter_illegal_mode(MBSTRG(outconv), MBSTRG(current_filter_illegal_mode));
2189    mbfl_buffer_converter_illegal_substchar(MBSTRG(outconv), MBSTRG(current_filter_illegal_substchar));
2190
2191    /* feed the string */
2192    mbfl_string_init(&string);
2193    /* these are not needed. convd has encoding info.
2194    string.no_language = MBSTRG(language);
2195    string.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2196    */
2197    string.val = (unsigned char *)arg_string;
2198    string.len = arg_string_len;
2199    mbfl_buffer_converter_feed(MBSTRG(outconv), &string);
2200    if (last_feed) {
2201        mbfl_buffer_converter_flush(MBSTRG(outconv));
2202    }
2203    /* get the converter output, and return it */
2204    mbfl_buffer_converter_result(MBSTRG(outconv), &result);
2205    // TODO: avoid reallocation ???
2206    RETVAL_STRINGL((char *)result.val, result.len);     /* the string is already strdup()'ed */
2207    efree(result.val);
2208
2209    /* delete the converter if it is the last feed. */
2210    if (last_feed) {
2211        MBSTRG(illegalchars) += mbfl_buffer_illegalchars(MBSTRG(outconv));
2212        mbfl_buffer_converter_delete(MBSTRG(outconv));
2213        MBSTRG(outconv) = NULL;
2214    }
2215}
2216/* }}} */
2217
2218/* {{{ proto int mb_strlen(string str [, string encoding])
2219   Get character numbers of a string */
2220PHP_FUNCTION(mb_strlen)
2221{
2222    int n;
2223    mbfl_string string;
2224    char *enc_name = NULL;
2225    size_t enc_name_len;
2226
2227    mbfl_string_init(&string);
2228
2229    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", (char **)&string.val, &string.len, &enc_name, &enc_name_len) == FAILURE) {
2230        return;
2231    }
2232
2233    string.no_language = MBSTRG(language);
2234    if (enc_name == NULL) {
2235        string.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2236    } else {
2237        string.no_encoding = mbfl_name2no_encoding(enc_name);
2238        if (string.no_encoding == mbfl_no_encoding_invalid) {
2239            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", enc_name);
2240            RETURN_FALSE;
2241        }
2242    }
2243
2244    n = mbfl_strlen(&string);
2245    if (n >= 0) {
2246        RETVAL_LONG(n);
2247    } else {
2248        RETVAL_FALSE;
2249    }
2250}
2251/* }}} */
2252
2253/* {{{ proto int mb_strpos(string haystack, string needle [, int offset [, string encoding]])
2254   Find position of first occurrence of a string within another */
2255PHP_FUNCTION(mb_strpos)
2256{
2257    int n, reverse = 0;
2258    zend_long offset;
2259    mbfl_string haystack, needle;
2260    char *enc_name = NULL;
2261    size_t enc_name_len;
2262
2263    mbfl_string_init(&haystack);
2264    mbfl_string_init(&needle);
2265    haystack.no_language = MBSTRG(language);
2266    haystack.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2267    needle.no_language = MBSTRG(language);
2268    needle.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2269    offset = 0;
2270
2271    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) {
2272        return;
2273    }
2274
2275    if (enc_name != NULL) {
2276        haystack.no_encoding = needle.no_encoding = mbfl_name2no_encoding(enc_name);
2277        if (haystack.no_encoding == mbfl_no_encoding_invalid) {
2278            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", enc_name);
2279            RETURN_FALSE;
2280        }
2281    }
2282
2283    if (offset < 0 || offset > mbfl_strlen(&haystack)) {
2284        php_error_docref(NULL, E_WARNING, "Offset not contained in string");
2285        RETURN_FALSE;
2286    }
2287    if (needle.len == 0) {
2288        php_error_docref(NULL, E_WARNING, "Empty delimiter");
2289        RETURN_FALSE;
2290    }
2291
2292    n = mbfl_strpos(&haystack, &needle, offset, reverse);
2293    if (n >= 0) {
2294        RETVAL_LONG(n);
2295    } else {
2296        switch (-n) {
2297        case 1:
2298            break;
2299        case 2:
2300            php_error_docref(NULL, E_WARNING, "Needle has not positive length");
2301            break;
2302        case 4:
2303            php_error_docref(NULL, E_WARNING, "Unknown encoding or conversion error");
2304            break;
2305        case 8:
2306            php_error_docref(NULL, E_NOTICE, "Argument is empty");
2307            break;
2308        default:
2309            php_error_docref(NULL, E_WARNING, "Unknown error in mb_strpos");
2310            break;
2311        }
2312        RETVAL_FALSE;
2313    }
2314}
2315/* }}} */
2316
2317/* {{{ proto int mb_strrpos(string haystack, string needle [, int offset [, string encoding]])
2318   Find position of last occurrence of a string within another */
2319PHP_FUNCTION(mb_strrpos)
2320{
2321    int n;
2322    mbfl_string haystack, needle;
2323    char *enc_name = NULL;
2324    size_t enc_name_len;
2325    zval *zoffset = NULL;
2326    long offset = 0, str_flg;
2327    char *enc_name2 = NULL;
2328    int enc_name_len2;
2329
2330    mbfl_string_init(&haystack);
2331    mbfl_string_init(&needle);
2332    haystack.no_language = MBSTRG(language);
2333    haystack.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2334    needle.no_language = MBSTRG(language);
2335    needle.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2336
2337    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) {
2338        return;
2339    }
2340
2341    if (zoffset) {
2342        if (Z_TYPE_P(zoffset) == IS_STRING) {
2343            enc_name2     = Z_STRVAL_P(zoffset);
2344            enc_name_len2 = Z_STRLEN_P(zoffset);
2345            str_flg       = 1;
2346
2347            if (enc_name2 != NULL) {
2348                switch (*enc_name2) {
2349                    case '0':
2350                    case '1':
2351                    case '2':
2352                    case '3':
2353                    case '4':
2354                    case '5':
2355                    case '6':
2356                    case '7':
2357                    case '8':
2358                    case '9':
2359                    case ' ':
2360                    case '-':
2361                    case '.':
2362                        break;
2363                    default :
2364                        str_flg = 0;
2365                        break;
2366                }
2367            }
2368
2369            if (str_flg) {
2370                convert_to_long_ex(zoffset);
2371                offset   = Z_LVAL_P(zoffset);
2372            } else {
2373                enc_name     = enc_name2;
2374                enc_name_len = enc_name_len2;
2375            }
2376        } else {
2377            convert_to_long_ex(zoffset);
2378            offset = Z_LVAL_P(zoffset);
2379        }
2380    }
2381
2382    if (enc_name != NULL) {
2383        haystack.no_encoding = needle.no_encoding = mbfl_name2no_encoding(enc_name);
2384        if (haystack.no_encoding == mbfl_no_encoding_invalid) {
2385            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", enc_name);
2386            RETURN_FALSE;
2387        }
2388    }
2389
2390    if (haystack.len <= 0) {
2391        RETURN_FALSE;
2392    }
2393    if (needle.len <= 0) {
2394        RETURN_FALSE;
2395    }
2396
2397    {
2398        int haystack_char_len = mbfl_strlen(&haystack);
2399        if ((offset > 0 && offset > haystack_char_len) ||
2400            (offset < 0 && -offset > haystack_char_len)) {
2401            php_error_docref(NULL, E_WARNING, "Offset is greater than the length of haystack string");
2402            RETURN_FALSE;
2403        }
2404    }
2405
2406    n = mbfl_strpos(&haystack, &needle, offset, 1);
2407    if (n >= 0) {
2408        RETVAL_LONG(n);
2409    } else {
2410        RETVAL_FALSE;
2411    }
2412}
2413/* }}} */
2414
2415/* {{{ proto int mb_stripos(string haystack, string needle [, int offset [, string encoding]])
2416   Finds position of first occurrence of a string within another, case insensitive */
2417PHP_FUNCTION(mb_stripos)
2418{
2419    int n;
2420    zend_long offset;
2421    mbfl_string haystack, needle;
2422    const char *from_encoding = MBSTRG(current_internal_encoding)->mime_name;
2423    size_t from_encoding_len;
2424    n = -1;
2425    offset = 0;
2426
2427    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) {
2428        return;
2429    }
2430    if (needle.len == 0) {
2431        php_error_docref(NULL, E_WARNING, "Empty delimiter");
2432        RETURN_FALSE;
2433    }
2434    n = php_mb_stripos(0, (char *)haystack.val, haystack.len, (char *)needle.val, needle.len, offset, from_encoding);
2435
2436    if (n >= 0) {
2437        RETVAL_LONG(n);
2438    } else {
2439        RETVAL_FALSE;
2440    }
2441}
2442/* }}} */
2443
2444/* {{{ proto int mb_strripos(string haystack, string needle [, int offset [, string encoding]])
2445   Finds position of last occurrence of a string within another, case insensitive */
2446PHP_FUNCTION(mb_strripos)
2447{
2448    int n;
2449    zend_long offset;
2450    mbfl_string haystack, needle;
2451    const char *from_encoding = MBSTRG(current_internal_encoding)->mime_name;
2452    size_t from_encoding_len;
2453    n = -1;
2454    offset = 0;
2455
2456    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) {
2457        return;
2458    }
2459
2460    n = php_mb_stripos(1, (char *)haystack.val, haystack.len, (char *)needle.val, needle.len, offset, from_encoding);
2461
2462    if (n >= 0) {
2463        RETVAL_LONG(n);
2464    } else {
2465        RETVAL_FALSE;
2466    }
2467}
2468/* }}} */
2469
2470/* {{{ proto string mb_strstr(string haystack, string needle[, bool part[, string encoding]])
2471   Finds first occurrence of a string within another */
2472PHP_FUNCTION(mb_strstr)
2473{
2474    int n, len, mblen;
2475    mbfl_string haystack, needle, result, *ret = NULL;
2476    char *enc_name = NULL;
2477    size_t enc_name_len;
2478    zend_bool part = 0;
2479
2480    mbfl_string_init(&haystack);
2481    mbfl_string_init(&needle);
2482    haystack.no_language = MBSTRG(language);
2483    haystack.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2484    needle.no_language = MBSTRG(language);
2485    needle.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2486
2487    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) {
2488        return;
2489    }
2490
2491    if (enc_name != NULL) {
2492        haystack.no_encoding = needle.no_encoding = mbfl_name2no_encoding(enc_name);
2493        if (haystack.no_encoding == mbfl_no_encoding_invalid) {
2494            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", enc_name);
2495            RETURN_FALSE;
2496        }
2497    }
2498
2499    if (needle.len <= 0) {
2500        php_error_docref(NULL, E_WARNING, "Empty delimiter");
2501        RETURN_FALSE;
2502    }
2503    n = mbfl_strpos(&haystack, &needle, 0, 0);
2504    if (n >= 0) {
2505        mblen = mbfl_strlen(&haystack);
2506        if (part) {
2507            ret = mbfl_substr(&haystack, &result, 0, n);
2508            if (ret != NULL) {
2509                // TODO: avoid reallocation ???
2510                RETVAL_STRINGL((char *)ret->val, ret->len);
2511                efree(ret->val);
2512            } else {
2513                RETVAL_FALSE;
2514            }
2515        } else {
2516            len = (mblen - n);
2517            ret = mbfl_substr(&haystack, &result, n, len);
2518            if (ret != NULL) {
2519                // TODO: avoid reallocation ???
2520                RETVAL_STRINGL((char *)ret->val, ret->len);
2521                efree(ret->val);
2522            } else {
2523                RETVAL_FALSE;
2524            }
2525        }
2526    } else {
2527        RETVAL_FALSE;
2528    }
2529}
2530/* }}} */
2531
2532/* {{{ proto string mb_strrchr(string haystack, string needle[, bool part[, string encoding]])
2533   Finds the last occurrence of a character in a string within another */
2534PHP_FUNCTION(mb_strrchr)
2535{
2536    int n, len, mblen;
2537    mbfl_string haystack, needle, result, *ret = NULL;
2538    char *enc_name = NULL;
2539    size_t enc_name_len;
2540    zend_bool part = 0;
2541
2542    mbfl_string_init(&haystack);
2543    mbfl_string_init(&needle);
2544    haystack.no_language = MBSTRG(language);
2545    haystack.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2546    needle.no_language = MBSTRG(language);
2547    needle.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2548
2549    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) {
2550        return;
2551    }
2552
2553    if (enc_name != NULL) {
2554        haystack.no_encoding = needle.no_encoding = mbfl_name2no_encoding(enc_name);
2555        if (haystack.no_encoding == mbfl_no_encoding_invalid) {
2556            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", enc_name);
2557            RETURN_FALSE;
2558        }
2559    }
2560
2561    if (haystack.len <= 0) {
2562        RETURN_FALSE;
2563    }
2564    if (needle.len <= 0) {
2565        RETURN_FALSE;
2566    }
2567    n = mbfl_strpos(&haystack, &needle, 0, 1);
2568    if (n >= 0) {
2569        mblen = mbfl_strlen(&haystack);
2570        if (part) {
2571            ret = mbfl_substr(&haystack, &result, 0, n);
2572            if (ret != NULL) {
2573                // TODO: avoid reallocation ???
2574                RETVAL_STRINGL((char *)ret->val, ret->len);
2575                efree(ret->val);
2576            } else {
2577                RETVAL_FALSE;
2578            }
2579        } else {
2580            len = (mblen - n);
2581            ret = mbfl_substr(&haystack, &result, n, len);
2582            if (ret != NULL) {
2583                // TODO: avoid reallocation ???
2584                RETVAL_STRINGL((char *)ret->val, ret->len);
2585                efree(ret->val);
2586            } else {
2587                RETVAL_FALSE;
2588            }
2589        }
2590    } else {
2591        RETVAL_FALSE;
2592    }
2593}
2594/* }}} */
2595
2596/* {{{ proto string mb_stristr(string haystack, string needle[, bool part[, string encoding]])
2597   Finds first occurrence of a string within another, case insensitive */
2598PHP_FUNCTION(mb_stristr)
2599{
2600    zend_bool part = 0;
2601    size_t from_encoding_len, len, mblen;
2602    int n;
2603    mbfl_string haystack, needle, result, *ret = NULL;
2604    const char *from_encoding = MBSTRG(current_internal_encoding)->mime_name;
2605    mbfl_string_init(&haystack);
2606    mbfl_string_init(&needle);
2607    haystack.no_language = MBSTRG(language);
2608    haystack.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2609    needle.no_language = MBSTRG(language);
2610    needle.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2611
2612
2613    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) {
2614        return;
2615    }
2616
2617    if (!needle.len) {
2618        php_error_docref(NULL, E_WARNING, "Empty delimiter");
2619        RETURN_FALSE;
2620    }
2621
2622    haystack.no_encoding = needle.no_encoding = mbfl_name2no_encoding(from_encoding);
2623    if (haystack.no_encoding == mbfl_no_encoding_invalid) {
2624        php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", from_encoding);
2625        RETURN_FALSE;
2626    }
2627
2628    n = php_mb_stripos(0, (char *)haystack.val, haystack.len, (char *)needle.val, needle.len, 0, from_encoding);
2629
2630    if (n <0) {
2631        RETURN_FALSE;
2632    }
2633
2634    mblen = mbfl_strlen(&haystack);
2635
2636    if (part) {
2637        ret = mbfl_substr(&haystack, &result, 0, n);
2638        if (ret != NULL) {
2639            // TODO: avoid reallocation ???
2640            RETVAL_STRINGL((char *)ret->val, ret->len);
2641            efree(ret->val);
2642        } else {
2643            RETVAL_FALSE;
2644        }
2645    } else {
2646        len = (mblen - n);
2647        ret = mbfl_substr(&haystack, &result, n, len);
2648        if (ret != NULL) {
2649            // TODO: avoid reallocaton ???
2650            RETVAL_STRINGL((char *)ret->val, ret->len);
2651            efree(ret->val);
2652        } else {
2653            RETVAL_FALSE;
2654        }
2655    }
2656}
2657/* }}} */
2658
2659/* {{{ proto string mb_strrichr(string haystack, string needle[, bool part[, string encoding]])
2660   Finds the last occurrence of a character in a string within another, case insensitive */
2661PHP_FUNCTION(mb_strrichr)
2662{
2663    zend_bool part = 0;
2664    int n, len, mblen;
2665    size_t from_encoding_len;
2666    mbfl_string haystack, needle, result, *ret = NULL;
2667    const char *from_encoding = MBSTRG(current_internal_encoding)->name;
2668    mbfl_string_init(&haystack);
2669    mbfl_string_init(&needle);
2670    haystack.no_language = MBSTRG(language);
2671    haystack.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2672    needle.no_language = MBSTRG(language);
2673    needle.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2674
2675
2676    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) {
2677        return;
2678    }
2679
2680    haystack.no_encoding = needle.no_encoding = mbfl_name2no_encoding(from_encoding);
2681    if (haystack.no_encoding == mbfl_no_encoding_invalid) {
2682        php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", from_encoding);
2683        RETURN_FALSE;
2684    }
2685
2686    n = php_mb_stripos(1, (char *)haystack.val, haystack.len, (char *)needle.val, needle.len, 0, from_encoding);
2687
2688    if (n <0) {
2689        RETURN_FALSE;
2690    }
2691
2692    mblen = mbfl_strlen(&haystack);
2693
2694    if (part) {
2695        ret = mbfl_substr(&haystack, &result, 0, n);
2696        if (ret != NULL) {
2697            // TODO: avoid reallocation ???
2698            RETVAL_STRINGL((char *)ret->val, ret->len);
2699            efree(ret->val);
2700        } else {
2701            RETVAL_FALSE;
2702        }
2703    } else {
2704        len = (mblen - n);
2705        ret = mbfl_substr(&haystack, &result, n, len);
2706        if (ret != NULL) {
2707            // TODO: avoid reallocation ???
2708            RETVAL_STRINGL((char *)ret->val, ret->len);
2709            efree(ret->val);
2710        } else {
2711            RETVAL_FALSE;
2712        }
2713    }
2714}
2715/* }}} */
2716
2717/* {{{ proto int mb_substr_count(string haystack, string needle [, string encoding])
2718   Count the number of substring occurrences */
2719PHP_FUNCTION(mb_substr_count)
2720{
2721    int n;
2722    mbfl_string haystack, needle;
2723    char *enc_name = NULL;
2724    size_t enc_name_len;
2725
2726    mbfl_string_init(&haystack);
2727    mbfl_string_init(&needle);
2728    haystack.no_language = MBSTRG(language);
2729    haystack.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2730    needle.no_language = MBSTRG(language);
2731    needle.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2732
2733    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) {
2734        return;
2735    }
2736
2737    if (enc_name != NULL) {
2738        haystack.no_encoding = needle.no_encoding = mbfl_name2no_encoding(enc_name);
2739        if (haystack.no_encoding == mbfl_no_encoding_invalid) {
2740            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", enc_name);
2741            RETURN_FALSE;
2742        }
2743    }
2744
2745    if (needle.len <= 0) {
2746        php_error_docref(NULL, E_WARNING, "Empty substring");
2747        RETURN_FALSE;
2748    }
2749
2750    n = mbfl_substr_count(&haystack, &needle);
2751    if (n >= 0) {
2752        RETVAL_LONG(n);
2753    } else {
2754        RETVAL_FALSE;
2755    }
2756}
2757/* }}} */
2758
2759/* {{{ proto string mb_substr(string str, int start [, int length [, string encoding]])
2760   Returns part of a string */
2761PHP_FUNCTION(mb_substr)
2762{
2763    size_t argc = ZEND_NUM_ARGS();
2764    char *str, *encoding;
2765    zend_long from, len;
2766    int mblen;
2767    size_t str_len, encoding_len;
2768    zval *z_len = NULL;
2769    mbfl_string string, result, *ret;
2770
2771    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl|zs", &str, &str_len, &from, &z_len, &encoding, &encoding_len) == FAILURE) {
2772        return;
2773    }
2774
2775    mbfl_string_init(&string);
2776    string.no_language = MBSTRG(language);
2777    string.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2778
2779    if (argc == 4) {
2780        string.no_encoding = mbfl_name2no_encoding(encoding);
2781        if (string.no_encoding == mbfl_no_encoding_invalid) {
2782            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", encoding);
2783            RETURN_FALSE;
2784        }
2785    }
2786
2787    string.val = (unsigned char *)str;
2788    string.len = str_len;
2789
2790    if (argc < 3 || Z_TYPE_P(z_len) == IS_NULL) {
2791        len = str_len;
2792    } else {
2793        convert_to_long_ex(z_len);
2794        len = Z_LVAL_P(z_len);
2795    }
2796
2797    /* measures length */
2798    mblen = 0;
2799    if (from < 0 || len < 0) {
2800        mblen = mbfl_strlen(&string);
2801    }
2802
2803    /* if "from" position is negative, count start position from the end
2804     * of the string
2805     */
2806    if (from < 0) {
2807        from = mblen + from;
2808        if (from < 0) {
2809            from = 0;
2810        }
2811    }
2812
2813    /* if "length" position is negative, set it to the length
2814     * needed to stop that many chars from the end of the string
2815     */
2816    if (len < 0) {
2817        len = (mblen - from) + len;
2818        if (len < 0) {
2819            len = 0;
2820        }
2821    }
2822
2823    if (((MBSTRG(func_overload) & MB_OVERLOAD_STRING) == MB_OVERLOAD_STRING)
2824        && (from >= mbfl_strlen(&string))) {
2825        RETURN_FALSE;
2826    }
2827
2828    ret = mbfl_substr(&string, &result, from, len);
2829    if (NULL == ret) {
2830        RETURN_FALSE;
2831    }
2832
2833    // TODO: avoid reallocation ???
2834    RETVAL_STRINGL((char *)ret->val, ret->len); /* the string is already strdup()'ed */
2835    efree(ret->val);
2836}
2837/* }}} */
2838
2839/* {{{ proto string mb_strcut(string str, int start [, int length [, string encoding]])
2840   Returns part of a string */
2841PHP_FUNCTION(mb_strcut)
2842{
2843    size_t argc = ZEND_NUM_ARGS();
2844    char *encoding;
2845    zend_long from, len;
2846    size_t encoding_len;
2847    zval *z_len = NULL;
2848    mbfl_string string, result, *ret;
2849
2850    mbfl_string_init(&string);
2851    string.no_language = MBSTRG(language);
2852    string.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2853
2854    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl|zs", (char **)&string.val, (int **)&string.len, &from, &z_len, &encoding, &encoding_len) == FAILURE) {
2855        return;
2856    }
2857
2858    if (argc == 4) {
2859        string.no_encoding = mbfl_name2no_encoding(encoding);
2860        if (string.no_encoding == mbfl_no_encoding_invalid) {
2861            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", encoding);
2862            RETURN_FALSE;
2863        }
2864    }
2865
2866    if (argc < 3 || Z_TYPE_P(z_len) == IS_NULL) {
2867        len = string.len;
2868    } else {
2869        convert_to_long_ex(z_len);
2870        len = Z_LVAL_P(z_len);
2871    }
2872
2873    /* if "from" position is negative, count start position from the end
2874     * of the string
2875     */
2876    if (from < 0) {
2877        from = string.len + from;
2878        if (from < 0) {
2879            from = 0;
2880        }
2881    }
2882
2883    /* if "length" position is negative, set it to the length
2884     * needed to stop that many chars from the end of the string
2885     */
2886    if (len < 0) {
2887        len = (string.len - from) + len;
2888        if (len < 0) {
2889            len = 0;
2890        }
2891    }
2892
2893    if ((unsigned int)from > string.len) {
2894        RETURN_FALSE;
2895    }
2896
2897    ret = mbfl_strcut(&string, &result, from, len);
2898    if (ret == NULL) {
2899        RETURN_FALSE;
2900    }
2901
2902    // TODO: avoid reallocation ???
2903    RETVAL_STRINGL((char *)ret->val, ret->len); /* the string is already strdup()'ed */
2904    efree(ret->val);
2905}
2906/* }}} */
2907
2908/* {{{ proto int mb_strwidth(string str [, string encoding])
2909   Gets terminal width of a string */
2910PHP_FUNCTION(mb_strwidth)
2911{
2912    int n;
2913    mbfl_string string;
2914    char *enc_name = NULL;
2915    size_t enc_name_len;
2916
2917    mbfl_string_init(&string);
2918
2919    string.no_language = MBSTRG(language);
2920    string.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2921
2922    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", (char **)&string.val, &string.len, &enc_name, &enc_name_len) == FAILURE) {
2923        return;
2924    }
2925
2926    if (enc_name != NULL) {
2927        string.no_encoding = mbfl_name2no_encoding(enc_name);
2928        if (string.no_encoding == mbfl_no_encoding_invalid) {
2929            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", enc_name);
2930            RETURN_FALSE;
2931        }
2932    }
2933
2934    n = mbfl_strwidth(&string);
2935    if (n >= 0) {
2936        RETVAL_LONG(n);
2937    } else {
2938        RETVAL_FALSE;
2939    }
2940}
2941/* }}} */
2942
2943/* {{{ proto string mb_strimwidth(string str, int start, int width [, string trimmarker [, string encoding]])
2944   Trim the string in terminal width */
2945PHP_FUNCTION(mb_strimwidth)
2946{
2947    char *str, *trimmarker, *encoding;
2948    zend_long from, width;
2949    size_t str_len, trimmarker_len, encoding_len;
2950    mbfl_string string, result, marker, *ret;
2951
2952    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll|ss", &str, &str_len, &from, &width, &trimmarker, &trimmarker_len, &encoding, &encoding_len) == FAILURE) {
2953        return;
2954    }
2955
2956    mbfl_string_init(&string);
2957    mbfl_string_init(&marker);
2958    string.no_language = MBSTRG(language);
2959    string.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2960    marker.no_language = MBSTRG(language);
2961    marker.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
2962    marker.val = NULL;
2963    marker.len = 0;
2964
2965    if (ZEND_NUM_ARGS() == 5) {
2966        string.no_encoding = marker.no_encoding = mbfl_name2no_encoding(encoding);
2967        if (string.no_encoding == mbfl_no_encoding_invalid) {
2968            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", encoding);
2969            RETURN_FALSE;
2970        }
2971    }
2972
2973    string.val = (unsigned char *)str;
2974    string.len = str_len;
2975
2976    if (from < 0 || (size_t)from > str_len) {
2977        php_error_docref(NULL, E_WARNING, "Start position is out of range");
2978        RETURN_FALSE;
2979    }
2980
2981    if (width < 0) {
2982        php_error_docref(NULL, E_WARNING, "Width is negative value");
2983        RETURN_FALSE;
2984    }
2985
2986    if (ZEND_NUM_ARGS() >= 4) {
2987        marker.val = (unsigned char *)trimmarker;
2988        marker.len = trimmarker_len;
2989    }
2990
2991    ret = mbfl_strimwidth(&string, &marker, &result, from, width);
2992
2993    if (ret == NULL) {
2994        RETURN_FALSE;
2995    }
2996    // TODO: avoid reallocation ???
2997    RETVAL_STRINGL((char *)ret->val, ret->len); /* the string is already strdup()'ed */
2998    efree(ret->val);
2999}
3000/* }}} */
3001
3002/* {{{ MBSTRING_API char *php_mb_convert_encoding() */
3003MBSTRING_API char * php_mb_convert_encoding(const char *input, size_t length, const char *_to_encoding, const char *_from_encodings, size_t *output_len)
3004{
3005    mbfl_string string, result, *ret;
3006    const mbfl_encoding *from_encoding, *to_encoding;
3007    mbfl_buffer_converter *convd;
3008    size_t size;
3009    const mbfl_encoding **list;
3010    char *output=NULL;
3011
3012    if (output_len) {
3013        *output_len = 0;
3014    }
3015    if (!input) {
3016        return NULL;
3017    }
3018    /* new encoding */
3019    if (_to_encoding && strlen(_to_encoding)) {
3020        to_encoding = mbfl_name2encoding(_to_encoding);
3021        if (!to_encoding) {
3022            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", _to_encoding);
3023            return NULL;
3024        }
3025    } else {
3026        to_encoding = MBSTRG(current_internal_encoding);
3027    }
3028
3029    /* initialize string */
3030    mbfl_string_init(&string);
3031    mbfl_string_init(&result);
3032    from_encoding = MBSTRG(current_internal_encoding);
3033    string.no_encoding = from_encoding->no_encoding;
3034    string.no_language = MBSTRG(language);
3035    string.val = (unsigned char *)input;
3036    string.len = length;
3037
3038    /* pre-conversion encoding */
3039    if (_from_encodings) {
3040        list = NULL;
3041        size = 0;
3042        php_mb_parse_encoding_list(_from_encodings, strlen(_from_encodings), &list, &size, 0);
3043        if (size == 1) {
3044            from_encoding = *list;
3045            string.no_encoding = from_encoding->no_encoding;
3046        } else if (size > 1) {
3047            /* auto detect */
3048            from_encoding = mbfl_identify_encoding2(&string, list, size, MBSTRG(strict_detection));
3049            if (from_encoding) {
3050                string.no_encoding = from_encoding->no_encoding;
3051            } else {
3052                php_error_docref(NULL, E_WARNING, "Unable to detect character encoding");
3053                from_encoding = &mbfl_encoding_pass;
3054                to_encoding = from_encoding;
3055                string.no_encoding = from_encoding->no_encoding;
3056            }
3057        } else {
3058            php_error_docref(NULL, E_WARNING, "Illegal character encoding specified");
3059        }
3060        if (list != NULL) {
3061            efree((void *)list);
3062        }
3063    }
3064
3065    /* initialize converter */
3066    convd = mbfl_buffer_converter_new2(from_encoding, to_encoding, string.len);
3067    if (convd == NULL) {
3068        php_error_docref(NULL, E_WARNING, "Unable to create character encoding converter");
3069        return NULL;
3070    }
3071    mbfl_buffer_converter_illegal_mode(convd, MBSTRG(current_filter_illegal_mode));
3072    mbfl_buffer_converter_illegal_substchar(convd, MBSTRG(current_filter_illegal_substchar));
3073
3074    /* do it */
3075    ret = mbfl_buffer_converter_feed_result(convd, &string, &result);
3076    if (ret) {
3077        if (output_len) {
3078            *output_len = ret->len;
3079        }
3080        output = (char *)ret->val;
3081    }
3082
3083    MBSTRG(illegalchars) += mbfl_buffer_illegalchars(convd);
3084    mbfl_buffer_converter_delete(convd);
3085    return output;
3086}
3087/* }}} */
3088
3089/* {{{ proto string mb_convert_encoding(string str, string to-encoding [, mixed from-encoding])
3090   Returns converted string in desired encoding */
3091PHP_FUNCTION(mb_convert_encoding)
3092{
3093    char *arg_str, *arg_new;
3094    size_t str_len, new_len;
3095    zval *arg_old;
3096    size_t size, l, n;
3097    char *_from_encodings = NULL, *ret, *s_free = NULL;
3098
3099    zval *hash_entry;
3100    HashTable *target_hash;
3101
3102    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|z", &arg_str, &str_len, &arg_new, &new_len, &arg_old) == FAILURE) {
3103        return;
3104    }
3105
3106    if (ZEND_NUM_ARGS() == 3) {
3107        switch (Z_TYPE_P(arg_old)) {
3108            case IS_ARRAY:
3109                target_hash = Z_ARRVAL_P(arg_old);
3110                _from_encodings = NULL;
3111
3112                ZEND_HASH_FOREACH_VAL(target_hash, hash_entry) {
3113
3114                    convert_to_string_ex(hash_entry);
3115
3116                    if ( _from_encodings) {
3117                        l = strlen(_from_encodings);
3118                        n = strlen(Z_STRVAL_P(hash_entry));
3119                        _from_encodings = erealloc(_from_encodings, l+n+2);
3120                        memcpy(_from_encodings + l, ",", 1);
3121                        memcpy(_from_encodings + l + 1, Z_STRVAL_P(hash_entry), Z_STRLEN_P(hash_entry) + 1);
3122                    } else {
3123                        _from_encodings = estrdup(Z_STRVAL_P(hash_entry));
3124                    }
3125                } ZEND_HASH_FOREACH_END();
3126
3127                if (_from_encodings != NULL && !strlen(_from_encodings)) {
3128                    efree(_from_encodings);
3129                    _from_encodings = NULL;
3130                }
3131                s_free = _from_encodings;
3132                break;
3133            default:
3134                convert_to_string(arg_old);
3135                _from_encodings = Z_STRVAL_P(arg_old);
3136                break;
3137            }
3138    }
3139
3140    /* new encoding */
3141    ret = php_mb_convert_encoding(arg_str, str_len, arg_new, _from_encodings, &size);
3142    if (ret != NULL) {
3143        // TODO: avoid reallocation ???
3144        RETVAL_STRINGL(ret, size);      /* the string is already strdup()'ed */
3145        efree(ret);
3146    } else {
3147        RETVAL_FALSE;
3148    }
3149
3150    if ( s_free) {
3151        efree(s_free);
3152    }
3153}
3154/* }}} */
3155
3156/* {{{ proto string mb_convert_case(string sourcestring, int mode [, string encoding])
3157   Returns a case-folded version of sourcestring */
3158PHP_FUNCTION(mb_convert_case)
3159{
3160    const char *from_encoding = MBSTRG(current_internal_encoding)->mime_name;
3161    char *str;
3162    size_t str_len, from_encoding_len;
3163    zend_long case_mode = 0;
3164    char *newstr;
3165    size_t ret_len;
3166
3167    RETVAL_FALSE;
3168    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl|s!", &str, &str_len,
3169                &case_mode, &from_encoding, &from_encoding_len) == FAILURE) {
3170        return;
3171    }
3172
3173    newstr = php_unicode_convert_case(case_mode, str, (size_t) str_len, &ret_len, from_encoding);
3174
3175    if (newstr) {
3176        // TODO: avoid reallocation ???
3177        RETVAL_STRINGL(newstr, ret_len);
3178        efree(newstr);
3179    }
3180}
3181/* }}} */
3182
3183/* {{{ proto string mb_strtoupper(string sourcestring [, string encoding])
3184 *  Returns a uppercased version of sourcestring
3185 */
3186PHP_FUNCTION(mb_strtoupper)
3187{
3188    const char *from_encoding = MBSTRG(current_internal_encoding)->mime_name;
3189    char *str;
3190    size_t str_len, from_encoding_len;
3191    char *newstr;
3192    size_t ret_len;
3193
3194    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s!", &str, &str_len,
3195                &from_encoding, &from_encoding_len) == FAILURE) {
3196        return;
3197    }
3198    newstr = php_unicode_convert_case(PHP_UNICODE_CASE_UPPER, str, (size_t) str_len, &ret_len, from_encoding);
3199
3200    if (newstr) {
3201        // TODO: avoid reallocation ???
3202        RETVAL_STRINGL(newstr, ret_len);
3203        efree(newstr);
3204        return;
3205    }
3206    RETURN_FALSE;
3207}
3208/* }}} */
3209
3210/* {{{ proto string mb_strtolower(string sourcestring [, string encoding])
3211 *  Returns a lowercased version of sourcestring
3212 */
3213PHP_FUNCTION(mb_strtolower)
3214{
3215    const char *from_encoding = MBSTRG(current_internal_encoding)->mime_name;
3216    char *str;
3217    size_t str_len, from_encoding_len;
3218    char *newstr;
3219    size_t ret_len;
3220
3221    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s!", &str, &str_len,
3222                &from_encoding, &from_encoding_len) == FAILURE) {
3223        return;
3224    }
3225    newstr = php_unicode_convert_case(PHP_UNICODE_CASE_LOWER, str, (size_t) str_len, &ret_len, from_encoding);
3226
3227    if (newstr) {
3228        // TODO: avoid reallocation ???
3229        RETVAL_STRINGL(newstr, ret_len);
3230        efree(newstr);
3231        return;
3232    }
3233    RETURN_FALSE;
3234}
3235/* }}} */
3236
3237/* {{{ proto string mb_detect_encoding(string str [, mixed encoding_list [, bool strict]])
3238   Encodings of the given string is returned (as a string) */
3239PHP_FUNCTION(mb_detect_encoding)
3240{
3241    char *str;
3242    size_t str_len;
3243    zend_bool strict=0;
3244    zval *encoding_list;
3245
3246    mbfl_string string;
3247    const mbfl_encoding *ret;
3248    const mbfl_encoding **elist, **list;
3249    size_t size;
3250
3251    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|zb", &str, &str_len, &encoding_list, &strict) == FAILURE) {
3252        return;
3253    }
3254
3255    /* make encoding list */
3256    list = NULL;
3257    size = 0;
3258    if (ZEND_NUM_ARGS() >= 2 && !Z_ISNULL_P(encoding_list)) {
3259        switch (Z_TYPE_P(encoding_list)) {
3260        case IS_ARRAY:
3261            if (FAILURE == php_mb_parse_encoding_array(encoding_list, &list, &size, 0)) {
3262                if (list) {
3263                    efree(list);
3264                    list = NULL;
3265                    size = 0;
3266                }
3267            }
3268            break;
3269        default:
3270            convert_to_string(encoding_list);
3271            if (FAILURE == php_mb_parse_encoding_list(Z_STRVAL_P(encoding_list), Z_STRLEN_P(encoding_list), &list, &size, 0)) {
3272                if (list) {
3273                    efree(list);
3274                    list = NULL;
3275                    size = 0;
3276                }
3277            }
3278            break;
3279        }
3280        if (size <= 0) {
3281            php_error_docref(NULL, E_WARNING, "Illegal argument");
3282        }
3283    }
3284
3285    if (ZEND_NUM_ARGS() < 3) {
3286        strict = (zend_bool)MBSTRG(strict_detection);
3287    }
3288
3289    if (size > 0 && list != NULL) {
3290        elist = list;
3291    } else {
3292        elist = MBSTRG(current_detect_order_list);
3293        size = MBSTRG(current_detect_order_list_size);
3294    }
3295
3296    mbfl_string_init(&string);
3297    string.no_language = MBSTRG(language);
3298    string.val = (unsigned char *)str;
3299    string.len = str_len;
3300    ret = mbfl_identify_encoding2(&string, elist, size, strict);
3301
3302    if (list != NULL) {
3303        efree((void *)list);
3304    }
3305
3306    if (ret == NULL) {
3307        RETURN_FALSE;
3308    }
3309
3310    RETVAL_STRING((char *)ret->name);
3311}
3312/* }}} */
3313
3314/* {{{ proto mixed mb_list_encodings()
3315   Returns an array of all supported entity encodings */
3316PHP_FUNCTION(mb_list_encodings)
3317{
3318    const mbfl_encoding **encodings;
3319    const mbfl_encoding *encoding;
3320    int i;
3321
3322    array_init(return_value);
3323    i = 0;
3324    encodings = mbfl_get_supported_encodings();
3325    while ((encoding = encodings[i++]) != NULL) {
3326        add_next_index_string(return_value, (char *) encoding->name);
3327    }
3328}
3329/* }}} */
3330
3331/* {{{ proto array mb_encoding_aliases(string encoding)
3332   Returns an array of the aliases of a given encoding name */
3333PHP_FUNCTION(mb_encoding_aliases)
3334{
3335    const mbfl_encoding *encoding;
3336    char *name = NULL;
3337    size_t name_len;
3338
3339    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
3340        return;
3341    }
3342
3343    encoding = mbfl_name2encoding(name);
3344    if (!encoding) {
3345        php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", name);
3346        RETURN_FALSE;
3347    }
3348
3349    array_init(return_value);
3350    if (encoding->aliases != NULL) {
3351        const char **alias;
3352        for (alias = *encoding->aliases; *alias; ++alias) {
3353            add_next_index_string(return_value, (char *)*alias);
3354        }
3355    }
3356}
3357/* }}} */
3358
3359/* {{{ proto string mb_encode_mimeheader(string str [, string charset [, string transfer-encoding [, string linefeed [, int indent]]]])
3360   Converts the string to MIME "encoded-word" in the format of =?charset?(B|Q)?encoded_string?= */
3361PHP_FUNCTION(mb_encode_mimeheader)
3362{
3363    enum mbfl_no_encoding charset, transenc;
3364    mbfl_string  string, result, *ret;
3365    char *charset_name = NULL;
3366    size_t charset_name_len;
3367    char *trans_enc_name = NULL;
3368    size_t trans_enc_name_len;
3369    char *linefeed = "\r\n";
3370    size_t linefeed_len;
3371    zend_long indent = 0;
3372
3373    mbfl_string_init(&string);
3374    string.no_language = MBSTRG(language);
3375    string.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
3376
3377    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) {
3378        return;
3379    }
3380
3381    charset = mbfl_no_encoding_pass;
3382    transenc = mbfl_no_encoding_base64;
3383
3384    if (charset_name != NULL) {
3385        charset = mbfl_name2no_encoding(charset_name);
3386        if (charset == mbfl_no_encoding_invalid) {
3387            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", charset_name);
3388            RETURN_FALSE;
3389        }
3390    } else {
3391        const mbfl_language *lang = mbfl_no2language(MBSTRG(language));
3392        if (lang != NULL) {
3393            charset = lang->mail_charset;
3394            transenc = lang->mail_header_encoding;
3395        }
3396    }
3397
3398    if (trans_enc_name != NULL) {
3399        if (*trans_enc_name == 'B' || *trans_enc_name == 'b') {
3400            transenc = mbfl_no_encoding_base64;
3401        } else if (*trans_enc_name == 'Q' || *trans_enc_name == 'q') {
3402            transenc = mbfl_no_encoding_qprint;
3403        }
3404    }
3405
3406    mbfl_string_init(&result);
3407    ret = mbfl_mime_header_encode(&string, &result, charset, transenc, linefeed, indent);
3408    if (ret != NULL) {
3409        // TODO: avoid reallocation ???
3410        RETVAL_STRINGL((char *)ret->val, ret->len); /* the string is already strdup()'ed */
3411        efree(ret->val);
3412    } else {
3413        RETVAL_FALSE;
3414    }
3415}
3416/* }}} */
3417
3418/* {{{ proto string mb_decode_mimeheader(string string)
3419   Decodes the MIME "encoded-word" in the string */
3420PHP_FUNCTION(mb_decode_mimeheader)
3421{
3422    mbfl_string string, result, *ret;
3423
3424    mbfl_string_init(&string);
3425    string.no_language = MBSTRG(language);
3426    string.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
3427
3428    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", (char **)&string.val, &string.len) == FAILURE) {
3429        return;
3430    }
3431
3432    mbfl_string_init(&result);
3433    ret = mbfl_mime_header_decode(&string, &result, MBSTRG(current_internal_encoding)->no_encoding);
3434    if (ret != NULL) {
3435        // TODO: avoid reallocation ???
3436        RETVAL_STRINGL((char *)ret->val, ret->len); /* the string is already strdup()'ed */
3437        efree(ret->val);
3438    } else {
3439        RETVAL_FALSE;
3440    }
3441}
3442/* }}} */
3443
3444/* {{{ proto string mb_convert_kana(string str [, string option] [, string encoding])
3445   Conversion between full-width character and half-width character (Japanese) */
3446PHP_FUNCTION(mb_convert_kana)
3447{
3448    int opt, i;
3449    mbfl_string string, result, *ret;
3450    char *optstr = NULL;
3451    size_t optstr_len;
3452    char *encname = NULL;
3453    size_t encname_len;
3454
3455    mbfl_string_init(&string);
3456    string.no_language = MBSTRG(language);
3457    string.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
3458
3459    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ss", (char **)&string.val, &string.len, &optstr, &optstr_len, &encname, &encname_len) == FAILURE) {
3460        return;
3461    }
3462
3463    /* option */
3464    if (optstr != NULL) {
3465        char *p = optstr;
3466        int n = optstr_len;
3467        i = 0;
3468        opt = 0;
3469        while (i < n) {
3470            i++;
3471            switch (*p++) {
3472            case 'A':
3473                opt |= 0x1;
3474                break;
3475            case 'a':
3476                opt |= 0x10;
3477                break;
3478            case 'R':
3479                opt |= 0x2;
3480                break;
3481            case 'r':
3482                opt |= 0x20;
3483                break;
3484            case 'N':
3485                opt |= 0x4;
3486                break;
3487            case 'n':
3488                opt |= 0x40;
3489                break;
3490            case 'S':
3491                opt |= 0x8;
3492                break;
3493            case 's':
3494                opt |= 0x80;
3495                break;
3496            case 'K':
3497                opt |= 0x100;
3498                break;
3499            case 'k':
3500                opt |= 0x1000;
3501                break;
3502            case 'H':
3503                opt |= 0x200;
3504                break;
3505            case 'h':
3506                opt |= 0x2000;
3507                break;
3508            case 'V':
3509                opt |= 0x800;
3510                break;
3511            case 'C':
3512                opt |= 0x10000;
3513                break;
3514            case 'c':
3515                opt |= 0x20000;
3516                break;
3517            case 'M':
3518                opt |= 0x100000;
3519                break;
3520            case 'm':
3521                opt |= 0x200000;
3522                break;
3523            }
3524        }
3525    } else {
3526        opt = 0x900;
3527    }
3528
3529    /* encoding */
3530    if (encname != NULL) {
3531        string.no_encoding = mbfl_name2no_encoding(encname);
3532        if (string.no_encoding == mbfl_no_encoding_invalid) {
3533            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", encname);
3534            RETURN_FALSE;
3535        }
3536    }
3537
3538    ret = mbfl_ja_jp_hantozen(&string, &result, opt);
3539    if (ret != NULL) {
3540        // TODO: avoid reallocation ???
3541        RETVAL_STRINGL((char *)ret->val, ret->len);     /* the string is already strdup()'ed */
3542        efree(ret->val);
3543    } else {
3544        RETVAL_FALSE;
3545    }
3546}
3547/* }}} */
3548
3549#define PHP_MBSTR_STACK_BLOCK_SIZE 32
3550
3551/* {{{ proto string mb_convert_variables(string to-encoding, mixed from-encoding, mixed vars [, ...])
3552   Converts the string resource in variables to desired encoding */
3553PHP_FUNCTION(mb_convert_variables)
3554{
3555    zval *args, *stack, *var, *hash_entry, *hash_entry_ptr, *zfrom_enc;
3556    HashTable *target_hash;
3557    mbfl_string string, result, *ret;
3558    const mbfl_encoding *from_encoding, *to_encoding;
3559    mbfl_encoding_detector *identd;
3560    mbfl_buffer_converter *convd;
3561    int n, argc, stack_level, stack_max;
3562    size_t to_enc_len;
3563    size_t elistsz;
3564    const mbfl_encoding **elist;
3565    char *to_enc;
3566    void *ptmp;
3567
3568    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz+", &to_enc, &to_enc_len, &zfrom_enc, &args, &argc) == FAILURE) {
3569        return;
3570    }
3571
3572    /* new encoding */
3573    to_encoding = mbfl_name2encoding(to_enc);
3574    if (!to_encoding) {
3575        php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", to_enc);
3576        RETURN_FALSE;
3577    }
3578
3579    /* initialize string */
3580    mbfl_string_init(&string);
3581    mbfl_string_init(&result);
3582    from_encoding = MBSTRG(current_internal_encoding);
3583    string.no_encoding = from_encoding->no_encoding;
3584    string.no_language = MBSTRG(language);
3585
3586    /* pre-conversion encoding */
3587    elist = NULL;
3588    elistsz = 0;
3589    switch (Z_TYPE_P(zfrom_enc)) {
3590        case IS_ARRAY:
3591            php_mb_parse_encoding_array(zfrom_enc, &elist, &elistsz, 0);
3592            break;
3593        default:
3594            convert_to_string_ex(zfrom_enc);
3595            php_mb_parse_encoding_list(Z_STRVAL_P(zfrom_enc), Z_STRLEN_P(zfrom_enc), &elist, &elistsz, 0);
3596            break;
3597    }
3598
3599    if (elistsz <= 0) {
3600        from_encoding = &mbfl_encoding_pass;
3601    } else if (elistsz == 1) {
3602        from_encoding = *elist;
3603    } else {
3604        /* auto detect */
3605        from_encoding = NULL;
3606        stack_max = PHP_MBSTR_STACK_BLOCK_SIZE;
3607        stack = (zval *)safe_emalloc(stack_max, sizeof(zval), 0);
3608        stack_level = 0;
3609        identd = mbfl_encoding_detector_new2(elist, elistsz, MBSTRG(strict_detection));
3610        if (identd != NULL) {
3611            n = 0;
3612            while (n < argc || stack_level > 0) {
3613                if (stack_level <= 0) {
3614                    var = &args[n++];
3615                    ZVAL_DEREF(var);
3616                    SEPARATE_ZVAL_NOREF(var);
3617                    if (Z_TYPE_P(var) == IS_ARRAY || Z_TYPE_P(var) == IS_OBJECT) {
3618                        target_hash = HASH_OF(var);
3619                        if (target_hash != NULL) {
3620                            zend_hash_internal_pointer_reset(target_hash);
3621                        }
3622                    }
3623                } else {
3624                    stack_level--;
3625                    var = &stack[stack_level];
3626                }
3627                if (Z_TYPE_P(var) == IS_ARRAY || Z_TYPE_P(var) == IS_OBJECT) {
3628                    target_hash = HASH_OF(var);
3629                    if (target_hash != NULL) {
3630                        while ((hash_entry = zend_hash_get_current_data(target_hash)) != NULL) {
3631                            zend_hash_move_forward(target_hash);
3632                            if (Z_TYPE_P(hash_entry) == IS_INDIRECT) {
3633                                hash_entry = Z_INDIRECT_P(hash_entry);
3634                            }
3635                            ZVAL_DEREF(hash_entry);
3636                            if (Z_TYPE_P(hash_entry) == IS_ARRAY || Z_TYPE_P(hash_entry) == IS_OBJECT) {
3637                                if (stack_level >= stack_max) {
3638                                    stack_max += PHP_MBSTR_STACK_BLOCK_SIZE;
3639                                    ptmp = erealloc(stack, sizeof(zval) * stack_max);
3640                                    stack = (zval *)ptmp;
3641                                }
3642                                ZVAL_COPY_VALUE(&stack[stack_level], var);
3643                                stack_level++;
3644                                var = hash_entry;
3645                                target_hash = HASH_OF(var);
3646                                if (target_hash != NULL) {
3647                                    zend_hash_internal_pointer_reset(target_hash);
3648                                    continue;
3649                                }
3650                            } else if (Z_TYPE_P(hash_entry) == IS_STRING) {
3651                                string.val = (unsigned char *)Z_STRVAL_P(hash_entry);
3652                                string.len = Z_STRLEN_P(hash_entry);
3653                                if (mbfl_encoding_detector_feed(identd, &string)) {
3654                                    goto detect_end;        /* complete detecting */
3655                                }
3656                            }
3657                        }
3658                    }
3659                } else if (Z_TYPE_P(var) == IS_STRING) {
3660                    string.val = (unsigned char *)Z_STRVAL_P(var);
3661                    string.len = Z_STRLEN_P(var);
3662                    if (mbfl_encoding_detector_feed(identd, &string)) {
3663                        goto detect_end;        /* complete detecting */
3664                    }
3665                }
3666            }
3667detect_end:
3668            from_encoding = mbfl_encoding_detector_judge2(identd);
3669            mbfl_encoding_detector_delete(identd);
3670        }
3671        efree(stack);
3672
3673        if (!from_encoding) {
3674            php_error_docref(NULL, E_WARNING, "Unable to detect encoding");
3675            from_encoding = &mbfl_encoding_pass;
3676        }
3677    }
3678    if (elist != NULL) {
3679        efree((void *)elist);
3680    }
3681    /* create converter */
3682    convd = NULL;
3683    if (from_encoding != &mbfl_encoding_pass) {
3684        convd = mbfl_buffer_converter_new2(from_encoding, to_encoding, 0);
3685        if (convd == NULL) {
3686            php_error_docref(NULL, E_WARNING, "Unable to create converter");
3687            RETURN_FALSE;
3688        }
3689        mbfl_buffer_converter_illegal_mode(convd, MBSTRG(current_filter_illegal_mode));
3690        mbfl_buffer_converter_illegal_substchar(convd, MBSTRG(current_filter_illegal_substchar));
3691    }
3692
3693    /* convert */
3694    if (convd != NULL) {
3695        stack_max = PHP_MBSTR_STACK_BLOCK_SIZE;
3696        stack = (zval*)safe_emalloc(stack_max, sizeof(zval), 0);
3697        stack_level = 0;
3698        n = 0;
3699        while (n < argc || stack_level > 0) {
3700            if (stack_level <= 0) {
3701                var = &args[n++];
3702                ZVAL_DEREF(var);
3703                SEPARATE_ZVAL_NOREF(var);
3704                if (Z_TYPE_P(var) == IS_ARRAY || Z_TYPE_P(var) == IS_OBJECT) {
3705                    target_hash = HASH_OF(var);
3706                    if (target_hash != NULL) {
3707                        zend_hash_internal_pointer_reset(target_hash);
3708                    }
3709                }
3710            } else {
3711                stack_level--;
3712                var = &stack[stack_level];
3713            }
3714            if (Z_TYPE_P(var) == IS_ARRAY || Z_TYPE_P(var) == IS_OBJECT) {
3715                target_hash = HASH_OF(var);
3716                if (target_hash != NULL) {
3717                    while ((hash_entry_ptr = zend_hash_get_current_data(target_hash)) != NULL) {
3718                        zend_hash_move_forward(target_hash);
3719                        if (Z_TYPE_P(hash_entry_ptr) == IS_INDIRECT) {
3720                            hash_entry_ptr = Z_INDIRECT_P(hash_entry_ptr);
3721                        }
3722                        hash_entry = hash_entry_ptr;
3723                        ZVAL_DEREF(hash_entry);
3724                        if (Z_TYPE_P(hash_entry) == IS_ARRAY || Z_TYPE_P(hash_entry) == IS_OBJECT) {
3725                            if (stack_level >= stack_max) {
3726                                stack_max += PHP_MBSTR_STACK_BLOCK_SIZE;
3727                                ptmp = erealloc(stack, sizeof(zval) * stack_max);
3728                                stack = (zval *)ptmp;
3729                            }
3730                            ZVAL_COPY_VALUE(&stack[stack_level], var);
3731                            stack_level++;
3732                            var = hash_entry;
3733                            SEPARATE_ZVAL(hash_entry);
3734                            target_hash = HASH_OF(var);
3735                            if (target_hash != NULL) {
3736                                zend_hash_internal_pointer_reset(target_hash);
3737                                continue;
3738                            }
3739                        } else if (Z_TYPE_P(hash_entry) == IS_STRING) {
3740                            string.val = (unsigned char *)Z_STRVAL_P(hash_entry);
3741                            string.len = Z_STRLEN_P(hash_entry);
3742                            ret = mbfl_buffer_converter_feed_result(convd, &string, &result);
3743                            if (ret != NULL) {
3744                                zval_ptr_dtor(hash_entry_ptr);
3745                                // TODO: avoid reallocation ???
3746                                ZVAL_STRINGL(hash_entry_ptr, (char *)ret->val, ret->len);
3747                                efree(ret->val);
3748                            }
3749                        }
3750                    }
3751                }
3752            } else if (Z_TYPE_P(var) == IS_STRING) {
3753                string.val = (unsigned char *)Z_STRVAL_P(var);
3754                string.len = Z_STRLEN_P(var);
3755                ret = mbfl_buffer_converter_feed_result(convd, &string, &result);
3756                if (ret != NULL) {
3757                    zval_ptr_dtor(var);
3758                    // TODO: avoid reallocation ???
3759                    ZVAL_STRINGL(var, (char *)ret->val, ret->len);
3760                    efree(ret->val);
3761                }
3762            }
3763        }
3764        efree(stack);
3765
3766        MBSTRG(illegalchars) += mbfl_buffer_illegalchars(convd);
3767        mbfl_buffer_converter_delete(convd);
3768    }
3769
3770    if (from_encoding) {
3771        RETURN_STRING(from_encoding->name);
3772    } else {
3773        RETURN_FALSE;
3774    }
3775}
3776/* }}} */
3777
3778/* {{{ HTML numeric entity */
3779/* {{{ static void php_mb_numericentity_exec() */
3780static void
3781php_mb_numericentity_exec(INTERNAL_FUNCTION_PARAMETERS, int type)
3782{
3783    char *str, *encoding;
3784    size_t str_len, encoding_len;
3785    zval *zconvmap, *hash_entry;
3786    HashTable *target_hash;
3787    int argc = ZEND_NUM_ARGS();
3788    int i, *convmap, *mapelm, mapsize=0;
3789    zend_bool is_hex = 0;
3790    mbfl_string string, result, *ret;
3791    enum mbfl_no_encoding no_encoding;
3792
3793    if (zend_parse_parameters(argc, "sz|sb", &str, &str_len, &zconvmap, &encoding, &encoding_len, &is_hex) == FAILURE) {
3794        return;
3795    }
3796
3797    mbfl_string_init(&string);
3798    string.no_language = MBSTRG(language);
3799    string.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
3800    string.val = (unsigned char *)str;
3801    string.len = str_len;
3802
3803    /* encoding */
3804    if ((argc == 3 || argc == 4) && encoding_len > 0) {
3805        no_encoding = mbfl_name2no_encoding(encoding);
3806        if (no_encoding == mbfl_no_encoding_invalid) {
3807            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", encoding);
3808            RETURN_FALSE;
3809        } else {
3810            string.no_encoding = no_encoding;
3811        }
3812    }
3813
3814    if (argc == 4) {
3815        if (type == 0 && is_hex) {
3816            type = 2; /* output in hex format */
3817        }
3818    }
3819
3820    /* conversion map */
3821    convmap = NULL;
3822    if (Z_TYPE_P(zconvmap) == IS_ARRAY) {
3823        target_hash = Z_ARRVAL_P(zconvmap);
3824        i = zend_hash_num_elements(target_hash);
3825        if (i > 0) {
3826            convmap = (int *)safe_emalloc(i, sizeof(int), 0);
3827            mapelm = convmap;
3828            mapsize = 0;
3829            ZEND_HASH_FOREACH_VAL(target_hash, hash_entry) {
3830                convert_to_long_ex(hash_entry);
3831                *mapelm++ = Z_LVAL_P(hash_entry);
3832                mapsize++;
3833            } ZEND_HASH_FOREACH_END();
3834        }
3835    }
3836    if (convmap == NULL) {
3837        RETURN_FALSE;
3838    }
3839    mapsize /= 4;
3840
3841    ret = mbfl_html_numeric_entity(&string, &result, convmap, mapsize, type);
3842    if (ret != NULL) {
3843        // TODO: avoid reallocation ???
3844        RETVAL_STRINGL((char *)ret->val, ret->len);
3845        efree(ret->val);
3846    } else {
3847        RETVAL_FALSE;
3848    }
3849    efree((void *)convmap);
3850}
3851/* }}} */
3852
3853/* {{{ proto string mb_encode_numericentity(string string, array convmap [, string encoding [, bool is_hex]])
3854   Converts specified characters to HTML numeric entities */
3855PHP_FUNCTION(mb_encode_numericentity)
3856{
3857    php_mb_numericentity_exec(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
3858}
3859/* }}} */
3860
3861/* {{{ proto string mb_decode_numericentity(string string, array convmap [, string encoding])
3862   Converts HTML numeric entities to character code */
3863PHP_FUNCTION(mb_decode_numericentity)
3864{
3865    php_mb_numericentity_exec(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
3866}
3867/* }}} */
3868/* }}} */
3869
3870/* {{{ proto int mb_send_mail(string to, string subject, string message [, string additional_headers [, string additional_parameters]])
3871 *  Sends an email message with MIME scheme
3872 */
3873
3874#define SKIP_LONG_HEADER_SEP_MBSTRING(str, pos)                                     \
3875    if (str[pos] == '\r' && str[pos + 1] == '\n' && (str[pos + 2] == ' ' || str[pos + 2] == '\t')) {    \
3876        pos += 2;                                           \
3877        while (str[pos + 1] == ' ' || str[pos + 1] == '\t') {                           \
3878            pos++;                                          \
3879        }                                               \
3880        continue;                                           \
3881    }
3882
3883#define MAIL_ASCIIZ_CHECK_MBSTRING(str, len)            \
3884    pp = str;                   \
3885    ee = pp + len;                  \
3886    while ((pp = memchr(pp, '\0', (ee - pp)))) {    \
3887        *pp = ' ';              \
3888    }                       \
3889
3890static int _php_mbstr_parse_mail_headers(HashTable *ht, const char *str, size_t str_len)
3891{
3892    const char *ps;
3893    size_t icnt;
3894    int state = 0;
3895    int crlf_state = -1;
3896    char *token = NULL;
3897    size_t token_pos = 0;
3898    zend_string *fld_name, *fld_val;
3899
3900    ps = str;
3901    icnt = str_len;
3902    fld_name = fld_val = NULL;
3903
3904    /*
3905     *             C o n t e n t - T y p e :   t e x t / h t m l \r\n
3906     *             ^ ^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^ ^^^^
3907     *      state  0            1           2          3
3908     *
3909     *             C o n t e n t - T y p e :   t e x t / h t m l \r\n
3910     *             ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^
3911     * crlf_state -1                       0                     1 -1
3912     *
3913     */
3914
3915    while (icnt > 0) {
3916        switch (*ps) {
3917            case ':':
3918                if (crlf_state == 1) {
3919                    token_pos++;
3920                }
3921
3922                if (state == 0 || state == 1) {
3923                    if(token && token_pos > 0) {
3924                        fld_name = zend_string_init(token, token_pos, 0);
3925                    }
3926                    state = 2;
3927                } else {
3928                    token_pos++;
3929                }
3930
3931                crlf_state = 0;
3932                break;
3933
3934            case '\n':
3935                if (crlf_state == -1) {
3936                    goto out;
3937                }
3938                crlf_state = -1;
3939                break;
3940
3941            case '\r':
3942                if (crlf_state == 1) {
3943                    token_pos++;
3944                } else {
3945                    crlf_state = 1;
3946                }
3947                break;
3948
3949            case ' ': case '\t':
3950                if (crlf_state == -1) {
3951                    if (state == 3) {
3952                        /* continuing from the previous line */
3953                        state = 4;
3954                    } else {
3955                        /* simply skipping this new line */
3956                        state = 5;
3957                    }
3958                } else {
3959                    if (crlf_state == 1) {
3960                        token_pos++;
3961                    }
3962                    if (state == 1 || state == 3) {
3963                        token_pos++;
3964                    }
3965                }
3966                crlf_state = 0;
3967                break;
3968
3969            default:
3970                switch (state) {
3971                    case 0:
3972                        token = (char*)ps;
3973                        token_pos = 0;
3974                        state = 1;
3975                        break;
3976
3977                    case 2:
3978                        if (crlf_state != -1) {
3979                            token = (char*)ps;
3980                            token_pos = 0;
3981
3982                            state = 3;
3983                            break;
3984                        }
3985                        /* break is missing intentionally */
3986
3987                    case 3:
3988                        if (crlf_state == -1) {
3989                            if(token && token_pos > 0) {
3990                                fld_val = zend_string_init(token, token_pos, 0);
3991                            }
3992
3993                            if (fld_name != NULL && fld_val != NULL) {
3994                                zval val;
3995                                /* FIXME: some locale free implementation is
3996                                 * really required here,,, */
3997                                php_strtoupper(fld_name->val, fld_name->len);
3998                                ZVAL_STR(&val, fld_val);
3999
4000                                zend_hash_update(ht, fld_name, &val);
4001
4002                                zend_string_release(fld_name);
4003                            }
4004
4005                            fld_name = fld_val = NULL;
4006                            token = (char*)ps;
4007                            token_pos = 0;
4008
4009                            state = 1;
4010                        }
4011                        break;
4012
4013                    case 4:
4014                        token_pos++;
4015                        state = 3;
4016                        break;
4017                }
4018
4019                if (crlf_state == 1) {
4020                    token_pos++;
4021                }
4022
4023                token_pos++;
4024
4025                crlf_state = 0;
4026                break;
4027        }
4028        ps++, icnt--;
4029    }
4030out:
4031    if (state == 2) {
4032        token = "";
4033        token_pos = 0;
4034
4035        state = 3;
4036    }
4037    if (state == 3) {
4038        if(token && token_pos > 0) {
4039            fld_val = zend_string_init(token, token_pos, 0);
4040        }
4041        if (fld_name != NULL && fld_val != NULL) {
4042            zval val;
4043            /* FIXME: some locale free implementation is
4044             * really required here,,, */
4045            php_strtoupper(fld_name->val, fld_name->len);
4046            ZVAL_STR(&val, fld_val);
4047
4048            zend_hash_update(ht, fld_name, &val);
4049
4050            zend_string_release(fld_name);
4051        }
4052    }
4053    return state;
4054}
4055
4056PHP_FUNCTION(mb_send_mail)
4057{
4058    int n;
4059    char *to = NULL;
4060    size_t to_len;
4061    char *message = NULL;
4062    size_t message_len;
4063    char *headers = NULL;
4064    size_t headers_len;
4065    char *subject = NULL;
4066    zend_string *extra_cmd = NULL;
4067    size_t subject_len;
4068    int i;
4069    char *to_r = NULL;
4070    char *force_extra_parameters = INI_STR("mail.force_extra_parameters");
4071    struct {
4072        int cnt_type:1;
4073        int cnt_trans_enc:1;
4074    } suppressed_hdrs = { 0, 0 };
4075
4076    char *message_buf = NULL, *subject_buf = NULL, *p;
4077    mbfl_string orig_str, conv_str;
4078    mbfl_string *pstr;  /* pointer to mbfl string for return value */
4079    enum mbfl_no_encoding
4080        tran_cs,    /* transfar text charset */
4081        head_enc,   /* header transfar encoding */
4082        body_enc;   /* body transfar encoding */
4083    mbfl_memory_device device;  /* automatic allocateable buffer for additional header */
4084    const mbfl_language *lang;
4085    int err = 0;
4086    HashTable ht_headers;
4087    zval *s;
4088    extern void mbfl_memory_device_unput(mbfl_memory_device *device);
4089    char *pp, *ee;
4090
4091    /* initialize */
4092    mbfl_memory_device_init(&device, 0, 0);
4093    mbfl_string_init(&orig_str);
4094    mbfl_string_init(&conv_str);
4095
4096    /* character-set, transfer-encoding */
4097    tran_cs = mbfl_no_encoding_utf8;
4098    head_enc = mbfl_no_encoding_base64;
4099    body_enc = mbfl_no_encoding_base64;
4100    lang = mbfl_no2language(MBSTRG(language));
4101    if (lang != NULL) {
4102        tran_cs = lang->mail_charset;
4103        head_enc = lang->mail_header_encoding;
4104        body_enc = lang->mail_body_encoding;
4105    }
4106
4107    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|sS", &to, &to_len, &subject, &subject_len, &message, &message_len, &headers, &headers_len, &extra_cmd) == FAILURE) {
4108        return;
4109    }
4110
4111    /* ASCIIZ check */
4112    MAIL_ASCIIZ_CHECK_MBSTRING(to, to_len);
4113    MAIL_ASCIIZ_CHECK_MBSTRING(subject, subject_len);
4114    MAIL_ASCIIZ_CHECK_MBSTRING(message, message_len);
4115    if (headers) {
4116        MAIL_ASCIIZ_CHECK_MBSTRING(headers, headers_len);
4117    }
4118    if (extra_cmd) {
4119        MAIL_ASCIIZ_CHECK_MBSTRING(extra_cmd->val, extra_cmd->len);
4120    }
4121
4122    zend_hash_init(&ht_headers, 0, NULL, ZVAL_PTR_DTOR, 0);
4123
4124    if (headers != NULL) {
4125        _php_mbstr_parse_mail_headers(&ht_headers, headers, headers_len);
4126    }
4127
4128    if ((s = zend_hash_str_find_ptr(&ht_headers, "CONTENT-TYPE", sizeof("CONTENT-TYPE") - 1))) {
4129        char *tmp;
4130        char *param_name;
4131        char *charset = NULL;
4132
4133        p = strchr(Z_STRVAL_P(s), ';');
4134
4135        if (p != NULL) {
4136            /* skipping the padded spaces */
4137            do {
4138                ++p;
4139            } while (*p == ' ' || *p == '\t');
4140
4141            if (*p != '\0') {
4142                if ((param_name = php_strtok_r(p, "= ", &tmp)) != NULL) {
4143                    if (strcasecmp(param_name, "charset") == 0) {
4144                        enum mbfl_no_encoding _tran_cs = tran_cs;
4145
4146                        charset = php_strtok_r(NULL, "= \"", &tmp);
4147                        if (charset != NULL) {
4148                            _tran_cs = mbfl_name2no_encoding(charset);
4149                        }
4150
4151                        if (_tran_cs == mbfl_no_encoding_invalid) {
4152                            php_error_docref(NULL, E_WARNING, "Unsupported charset \"%s\" - will be regarded as ascii", charset);
4153                            _tran_cs = mbfl_no_encoding_ascii;
4154                        }
4155                        tran_cs = _tran_cs;
4156                    }
4157                }
4158            }
4159        }
4160        suppressed_hdrs.cnt_type = 1;
4161    }
4162
4163    if ((s = zend_hash_str_find_ptr(&ht_headers, "CONTENT-TRANSFER-ENCODING", sizeof("CONTENT-TRANSFER-ENCODING") - 1))) {
4164        enum mbfl_no_encoding _body_enc;
4165
4166        _body_enc = mbfl_name2no_encoding(Z_STRVAL_P(s));
4167        switch (_body_enc) {
4168            case mbfl_no_encoding_base64:
4169            case mbfl_no_encoding_7bit:
4170            case mbfl_no_encoding_8bit:
4171                body_enc = _body_enc;
4172                break;
4173
4174            default:
4175                php_error_docref(NULL, E_WARNING, "Unsupported transfer encoding \"%s\" - will be regarded as 8bit", Z_STRVAL_P(s));
4176                body_enc =  mbfl_no_encoding_8bit;
4177                break;
4178        }
4179        suppressed_hdrs.cnt_trans_enc = 1;
4180    }
4181
4182    /* To: */
4183    if (to != NULL) {
4184        if (to_len > 0) {
4185            to_r = estrndup(to, to_len);
4186            for (; to_len; to_len--) {
4187                if (!isspace((unsigned char) to_r[to_len - 1])) {
4188                    break;
4189                }
4190                to_r[to_len - 1] = '\0';
4191            }
4192            for (i = 0; to_r[i]; i++) {
4193            if (iscntrl((unsigned char) to_r[i])) {
4194                /* According to RFC 822, section 3.1.1 long headers may be separated into
4195                 * parts using CRLF followed at least one linear-white-space character ('\t' or ' ').
4196                 * To prevent these separators from being replaced with a space, we use the
4197                 * SKIP_LONG_HEADER_SEP_MBSTRING to skip over them.
4198                 */
4199                SKIP_LONG_HEADER_SEP_MBSTRING(to_r, i);
4200                to_r[i] = ' ';
4201            }
4202            }
4203        } else {
4204            to_r = to;
4205        }
4206    } else {
4207        php_error_docref(NULL, E_WARNING, "Missing To: field");
4208        err = 1;
4209    }
4210
4211    /* Subject: */
4212    if (subject != NULL) {
4213        orig_str.no_language = MBSTRG(language);
4214        orig_str.val = (unsigned char *)subject;
4215        orig_str.len = subject_len;
4216        orig_str.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
4217        if (orig_str.no_encoding == mbfl_no_encoding_invalid || orig_str.no_encoding == mbfl_no_encoding_pass) {
4218            const mbfl_encoding *encoding = mbfl_identify_encoding2(&orig_str, MBSTRG(current_detect_order_list), MBSTRG(current_detect_order_list_size), MBSTRG(strict_detection));
4219            orig_str.no_encoding = encoding ? encoding->no_encoding: mbfl_no_encoding_invalid;
4220        }
4221        pstr = mbfl_mime_header_encode(&orig_str, &conv_str, tran_cs, head_enc, "\n", sizeof("Subject: [PHP-jp nnnnnnnn]"));
4222        if (pstr != NULL) {
4223            subject_buf = subject = (char *)pstr->val;
4224        }
4225    } else {
4226        php_error_docref(NULL, E_WARNING, "Missing Subject: field");
4227        err = 1;
4228    }
4229
4230    /* message body */
4231    if (message != NULL) {
4232        orig_str.no_language = MBSTRG(language);
4233        orig_str.val = (unsigned char *)message;
4234        orig_str.len = (unsigned int)message_len;
4235        orig_str.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
4236
4237        if (orig_str.no_encoding == mbfl_no_encoding_invalid || orig_str.no_encoding == mbfl_no_encoding_pass) {
4238            const mbfl_encoding *encoding = mbfl_identify_encoding2(&orig_str, MBSTRG(current_detect_order_list), MBSTRG(current_detect_order_list_size), MBSTRG(strict_detection));
4239            orig_str.no_encoding = encoding ? encoding->no_encoding: mbfl_no_encoding_invalid;
4240        }
4241
4242        pstr = NULL;
4243        {
4244            mbfl_string tmpstr;
4245
4246            if (mbfl_convert_encoding(&orig_str, &tmpstr, tran_cs) != NULL) {
4247                tmpstr.no_encoding=mbfl_no_encoding_8bit;
4248                pstr = mbfl_convert_encoding(&tmpstr, &conv_str, body_enc);
4249                efree(tmpstr.val);
4250            }
4251        }
4252        if (pstr != NULL) {
4253            message_buf = message = (char *)pstr->val;
4254        }
4255    } else {
4256        /* this is not really an error, so it is allowed. */
4257        php_error_docref(NULL, E_WARNING, "Empty message body");
4258        message = NULL;
4259    }
4260
4261    /* other headers */
4262#define PHP_MBSTR_MAIL_MIME_HEADER1 "MIME-Version: 1.0"
4263#define PHP_MBSTR_MAIL_MIME_HEADER2 "Content-Type: text/plain"
4264#define PHP_MBSTR_MAIL_MIME_HEADER3 "; charset="
4265#define PHP_MBSTR_MAIL_MIME_HEADER4 "Content-Transfer-Encoding: "
4266    if (headers != NULL) {
4267        p = headers;
4268        n = headers_len;
4269        mbfl_memory_device_strncat(&device, p, n);
4270        if (n > 0 && p[n - 1] != '\n') {
4271            mbfl_memory_device_strncat(&device, "\n", 1);
4272        }
4273    }
4274
4275    if (!zend_hash_str_exists(&ht_headers, "MIME-VERSION", sizeof("MIME-VERSION") - 1)) {
4276        mbfl_memory_device_strncat(&device, PHP_MBSTR_MAIL_MIME_HEADER1, sizeof(PHP_MBSTR_MAIL_MIME_HEADER1) - 1);
4277        mbfl_memory_device_strncat(&device, "\n", 1);
4278    }
4279
4280    if (!suppressed_hdrs.cnt_type) {
4281        mbfl_memory_device_strncat(&device, PHP_MBSTR_MAIL_MIME_HEADER2, sizeof(PHP_MBSTR_MAIL_MIME_HEADER2) - 1);
4282
4283        p = (char *)mbfl_no2preferred_mime_name(tran_cs);
4284        if (p != NULL) {
4285            mbfl_memory_device_strncat(&device, PHP_MBSTR_MAIL_MIME_HEADER3, sizeof(PHP_MBSTR_MAIL_MIME_HEADER3) - 1);
4286            mbfl_memory_device_strcat(&device, p);
4287        }
4288        mbfl_memory_device_strncat(&device, "\n", 1);
4289    }
4290    if (!suppressed_hdrs.cnt_trans_enc) {
4291        mbfl_memory_device_strncat(&device, PHP_MBSTR_MAIL_MIME_HEADER4, sizeof(PHP_MBSTR_MAIL_MIME_HEADER4) - 1);
4292        p = (char *)mbfl_no2preferred_mime_name(body_enc);
4293        if (p == NULL) {
4294            p = "7bit";
4295        }
4296        mbfl_memory_device_strcat(&device, p);
4297        mbfl_memory_device_strncat(&device, "\n", 1);
4298    }
4299
4300    mbfl_memory_device_unput(&device);
4301    mbfl_memory_device_output('\0', &device);
4302    headers = (char *)device.buffer;
4303
4304    if (force_extra_parameters) {
4305        extra_cmd = php_escape_shell_cmd(force_extra_parameters);
4306    } else if (extra_cmd) {
4307        extra_cmd = php_escape_shell_cmd(extra_cmd->val);
4308    }
4309
4310    if (!err && php_mail(to_r, subject, message, headers, extra_cmd ? extra_cmd->val : NULL)) {
4311        RETVAL_TRUE;
4312    } else {
4313        RETVAL_FALSE;
4314    }
4315
4316    if (extra_cmd) {
4317        zend_string_release(extra_cmd);
4318    }
4319
4320    if (to_r != to) {
4321        efree(to_r);
4322    }
4323    if (subject_buf) {
4324        efree((void *)subject_buf);
4325    }
4326    if (message_buf) {
4327        efree((void *)message_buf);
4328    }
4329    mbfl_memory_device_clear(&device);
4330    zend_hash_destroy(&ht_headers);
4331}
4332
4333#undef SKIP_LONG_HEADER_SEP_MBSTRING
4334#undef MAIL_ASCIIZ_CHECK_MBSTRING
4335#undef PHP_MBSTR_MAIL_MIME_HEADER1
4336#undef PHP_MBSTR_MAIL_MIME_HEADER2
4337#undef PHP_MBSTR_MAIL_MIME_HEADER3
4338#undef PHP_MBSTR_MAIL_MIME_HEADER4
4339/* }}} */
4340
4341/* {{{ proto mixed mb_get_info([string type])
4342   Returns the current settings of mbstring */
4343PHP_FUNCTION(mb_get_info)
4344{
4345    char *typ = NULL;
4346    size_t typ_len;
4347    size_t n;
4348    char *name;
4349    const struct mb_overload_def *over_func;
4350    zval row1, row2;
4351    const mbfl_language *lang = mbfl_no2language(MBSTRG(language));
4352    const mbfl_encoding **entry;
4353
4354    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s", &typ, &typ_len) == FAILURE) {
4355        return;
4356    }
4357
4358    if (!typ || !strcasecmp("all", typ)) {
4359        array_init(return_value);
4360        if (MBSTRG(current_internal_encoding)) {
4361            add_assoc_string(return_value, "internal_encoding", (char *)MBSTRG(current_internal_encoding)->name);
4362        }
4363        if (MBSTRG(http_input_identify)) {
4364            add_assoc_string(return_value, "http_input", (char *)MBSTRG(http_input_identify)->name);
4365        }
4366        if (MBSTRG(current_http_output_encoding)) {
4367            add_assoc_string(return_value, "http_output", (char *)MBSTRG(current_http_output_encoding)->name);
4368        }
4369        if ((name = (char *)zend_ini_string("mbstring.http_output_conv_mimetypes", sizeof("mbstring.http_output_conv_mimetypes") - 1, 0)) != NULL) {
4370            add_assoc_string(return_value, "http_output_conv_mimetypes", name);
4371        }
4372        add_assoc_long(return_value, "func_overload", MBSTRG(func_overload));
4373        if (MBSTRG(func_overload)){
4374            over_func = &(mb_ovld[0]);
4375            array_init(&row1);
4376            while (over_func->type > 0) {
4377                if ((MBSTRG(func_overload) & over_func->type) == over_func->type ) {
4378                    add_assoc_string(&row1, over_func->orig_func, over_func->ovld_func);
4379                }
4380                over_func++;
4381            }
4382            add_assoc_zval(return_value, "func_overload_list", &row1);
4383        } else {
4384            add_assoc_string(return_value, "func_overload_list", "no overload");
4385        }
4386        if (lang != NULL) {
4387            if ((name = (char *)mbfl_no_encoding2name(lang->mail_charset)) != NULL) {
4388                add_assoc_string(return_value, "mail_charset", name);
4389            }
4390            if ((name = (char *)mbfl_no_encoding2name(lang->mail_header_encoding)) != NULL) {
4391                add_assoc_string(return_value, "mail_header_encoding", name);
4392            }
4393            if ((name = (char *)mbfl_no_encoding2name(lang->mail_body_encoding)) != NULL) {
4394                add_assoc_string(return_value, "mail_body_encoding", name);
4395            }
4396        }
4397        add_assoc_long(return_value, "illegal_chars", MBSTRG(illegalchars));
4398        if (MBSTRG(encoding_translation)) {
4399            add_assoc_string(return_value, "encoding_translation", "On");
4400        } else {
4401            add_assoc_string(return_value, "encoding_translation", "Off");
4402        }
4403        if ((name = (char *)mbfl_no_language2name(MBSTRG(language))) != NULL) {
4404            add_assoc_string(return_value, "language", name);
4405        }
4406        n = MBSTRG(current_detect_order_list_size);
4407        entry = MBSTRG(current_detect_order_list);
4408        if (n > 0) {
4409            size_t i;
4410            array_init(&row2);
4411            for (i = 0; i < n; i++) {
4412                add_next_index_string(&row2, (*entry)->name);
4413                entry++;
4414            }
4415            add_assoc_zval(return_value, "detect_order", &row2);
4416        }
4417        if (MBSTRG(current_filter_illegal_mode) == MBFL_OUTPUTFILTER_ILLEGAL_MODE_NONE) {
4418            add_assoc_string(return_value, "substitute_character", "none");
4419        } else if (MBSTRG(current_filter_illegal_mode) == MBFL_OUTPUTFILTER_ILLEGAL_MODE_LONG) {
4420            add_assoc_string(return_value, "substitute_character", "long");
4421        } else if (MBSTRG(current_filter_illegal_mode) == MBFL_OUTPUTFILTER_ILLEGAL_MODE_ENTITY) {
4422            add_assoc_string(return_value, "substitute_character", "entity");
4423        } else {
4424            add_assoc_long(return_value, "substitute_character", MBSTRG(current_filter_illegal_substchar));
4425        }
4426        if (MBSTRG(strict_detection)) {
4427            add_assoc_string(return_value, "strict_detection", "On");
4428        } else {
4429            add_assoc_string(return_value, "strict_detection", "Off");
4430        }
4431    } else if (!strcasecmp("internal_encoding", typ)) {
4432        if (MBSTRG(current_internal_encoding)) {
4433            RETVAL_STRING((char *)MBSTRG(current_internal_encoding)->name);
4434        }
4435    } else if (!strcasecmp("http_input", typ)) {
4436        if (MBSTRG(http_input_identify)) {
4437            RETVAL_STRING((char *)MBSTRG(http_input_identify)->name);
4438        }
4439    } else if (!strcasecmp("http_output", typ)) {
4440        if (MBSTRG(current_http_output_encoding)) {
4441            RETVAL_STRING((char *)MBSTRG(current_http_output_encoding)->name);
4442        }
4443    } else if (!strcasecmp("http_output_conv_mimetypes", typ)) {
4444        if ((name = (char *)zend_ini_string("mbstring.http_output_conv_mimetypes", sizeof("mbstring.http_output_conv_mimetypes") - 1, 0)) != NULL) {
4445            RETVAL_STRING(name);
4446        }
4447    } else if (!strcasecmp("func_overload", typ)) {
4448        RETVAL_LONG(MBSTRG(func_overload));
4449    } else if (!strcasecmp("func_overload_list", typ)) {
4450        if (MBSTRG(func_overload)){
4451                over_func = &(mb_ovld[0]);
4452                array_init(return_value);
4453                while (over_func->type > 0) {
4454                    if ((MBSTRG(func_overload) & over_func->type) == over_func->type ) {
4455                        add_assoc_string(return_value, over_func->orig_func, over_func->ovld_func);
4456                    }
4457                    over_func++;
4458                }
4459        } else {
4460            RETVAL_STRING("no overload");
4461        }
4462    } else if (!strcasecmp("mail_charset", typ)) {
4463        if (lang != NULL && (name = (char *)mbfl_no_encoding2name(lang->mail_charset)) != NULL) {
4464            RETVAL_STRING(name);
4465        }
4466    } else if (!strcasecmp("mail_header_encoding", typ)) {
4467        if (lang != NULL && (name = (char *)mbfl_no_encoding2name(lang->mail_header_encoding)) != NULL) {
4468            RETVAL_STRING(name);
4469        }
4470    } else if (!strcasecmp("mail_body_encoding", typ)) {
4471        if (lang != NULL && (name = (char *)mbfl_no_encoding2name(lang->mail_body_encoding)) != NULL) {
4472            RETVAL_STRING(name);
4473        }
4474    } else if (!strcasecmp("illegal_chars", typ)) {
4475        RETVAL_LONG(MBSTRG(illegalchars));
4476    } else if (!strcasecmp("encoding_translation", typ)) {
4477        if (MBSTRG(encoding_translation)) {
4478            RETVAL_STRING("On");
4479        } else {
4480            RETVAL_STRING("Off");
4481        }
4482    } else if (!strcasecmp("language", typ)) {
4483        if ((name = (char *)mbfl_no_language2name(MBSTRG(language))) != NULL) {
4484            RETVAL_STRING(name);
4485        }
4486    } else if (!strcasecmp("detect_order", typ)) {
4487        n = MBSTRG(current_detect_order_list_size);
4488        entry = MBSTRG(current_detect_order_list);
4489        if (n > 0) {
4490            size_t i;
4491            array_init(return_value);
4492            for (i = 0; i < n; i++) {
4493                add_next_index_string(return_value, (*entry)->name);
4494                entry++;
4495            }
4496        }
4497    } else if (!strcasecmp("substitute_character", typ)) {
4498        if (MBSTRG(current_filter_illegal_mode) == MBFL_OUTPUTFILTER_ILLEGAL_MODE_NONE) {
4499            RETVAL_STRING("none");
4500        } else if (MBSTRG(current_filter_illegal_mode) == MBFL_OUTPUTFILTER_ILLEGAL_MODE_LONG) {
4501            RETVAL_STRING("long");
4502        } else if (MBSTRG(current_filter_illegal_mode) == MBFL_OUTPUTFILTER_ILLEGAL_MODE_ENTITY) {
4503            RETVAL_STRING("entity");
4504        } else {
4505            RETVAL_LONG(MBSTRG(current_filter_illegal_substchar));
4506        }
4507    } else if (!strcasecmp("strict_detection", typ)) {
4508        if (MBSTRG(strict_detection)) {
4509            RETVAL_STRING("On");
4510        } else {
4511            RETVAL_STRING("Off");
4512        }
4513    } else {
4514        RETURN_FALSE;
4515    }
4516}
4517/* }}} */
4518
4519/* {{{ proto bool mb_check_encoding([string var[, string encoding]])
4520   Check if the string is valid for the specified encoding */
4521PHP_FUNCTION(mb_check_encoding)
4522{
4523    char *var = NULL;
4524    size_t var_len;
4525    char *enc = NULL;
4526    size_t enc_len;
4527    mbfl_buffer_converter *convd;
4528    const mbfl_encoding *encoding = MBSTRG(current_internal_encoding);
4529    mbfl_string string, result, *ret = NULL;
4530    long illegalchars = 0;
4531
4532    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ss", &var, &var_len, &enc, &enc_len) == FAILURE) {
4533        return;
4534    }
4535
4536    if (var == NULL) {
4537        RETURN_BOOL(MBSTRG(illegalchars) == 0);
4538    }
4539
4540    if (enc != NULL) {
4541        encoding = mbfl_name2encoding(enc);
4542        if (!encoding || encoding == &mbfl_encoding_pass) {
4543            php_error_docref(NULL, E_WARNING, "Invalid encoding \"%s\"", enc);
4544            RETURN_FALSE;
4545        }
4546    }
4547
4548    convd = mbfl_buffer_converter_new2(encoding, encoding, 0);
4549    if (convd == NULL) {
4550        php_error_docref(NULL, E_WARNING, "Unable to create converter");
4551        RETURN_FALSE;
4552    }
4553    mbfl_buffer_converter_illegal_mode(convd, MBFL_OUTPUTFILTER_ILLEGAL_MODE_NONE);
4554    mbfl_buffer_converter_illegal_substchar(convd, 0);
4555
4556    /* initialize string */
4557    mbfl_string_init_set(&string, mbfl_no_language_neutral, encoding->no_encoding);
4558    mbfl_string_init(&result);
4559
4560    string.val = (unsigned char *)var;
4561    string.len = var_len;
4562    ret = mbfl_buffer_converter_feed_result(convd, &string, &result);
4563    illegalchars = mbfl_buffer_illegalchars(convd);
4564    mbfl_buffer_converter_delete(convd);
4565
4566    RETVAL_FALSE;
4567    if (ret != NULL) {
4568        if (illegalchars == 0 && string.len == result.len && memcmp(string.val, result.val, string.len) == 0) {
4569            RETVAL_TRUE;
4570        }
4571        mbfl_string_clear(&result);
4572    }
4573}
4574/* }}} */
4575
4576/* {{{ php_mb_populate_current_detect_order_list */
4577static void php_mb_populate_current_detect_order_list(void)
4578{
4579    const mbfl_encoding **entry = 0;
4580    size_t nentries;
4581
4582    if (MBSTRG(current_detect_order_list)) {
4583        return;
4584    }
4585
4586    if (MBSTRG(detect_order_list) && MBSTRG(detect_order_list_size)) {
4587        nentries = MBSTRG(detect_order_list_size);
4588        entry = (const mbfl_encoding **)safe_emalloc(nentries, sizeof(mbfl_encoding*), 0);
4589        memcpy(entry, MBSTRG(detect_order_list), sizeof(mbfl_encoding*) * nentries);
4590    } else {
4591        const enum mbfl_no_encoding *src = MBSTRG(default_detect_order_list);
4592        size_t i;
4593        nentries = MBSTRG(default_detect_order_list_size);
4594        entry = (const mbfl_encoding **)safe_emalloc(nentries, sizeof(mbfl_encoding*), 0);
4595        for (i = 0; i < nentries; i++) {
4596            entry[i] = mbfl_no2encoding(src[i]);
4597        }
4598    }
4599    MBSTRG(current_detect_order_list) = entry;
4600    MBSTRG(current_detect_order_list_size) = nentries;
4601}
4602/* }}} */
4603
4604/* {{{ static int php_mb_encoding_translation() */
4605static int php_mb_encoding_translation(void)
4606{
4607    return MBSTRG(encoding_translation);
4608}
4609/* }}} */
4610
4611/* {{{ MBSTRING_API size_t php_mb_mbchar_bytes_ex() */
4612MBSTRING_API size_t php_mb_mbchar_bytes_ex(const char *s, const mbfl_encoding *enc)
4613{
4614    if (enc != NULL) {
4615        if (enc->flag & MBFL_ENCTYPE_MBCS) {
4616            if (enc->mblen_table != NULL) {
4617                if (s != NULL) return enc->mblen_table[*(unsigned char *)s];
4618            }
4619        } else if (enc->flag & (MBFL_ENCTYPE_WCS2BE | MBFL_ENCTYPE_WCS2LE)) {
4620            return 2;
4621        } else if (enc->flag & (MBFL_ENCTYPE_WCS4BE | MBFL_ENCTYPE_WCS4LE)) {
4622            return 4;
4623        }
4624    }
4625    return 1;
4626}
4627/* }}} */
4628
4629/* {{{ MBSTRING_API size_t php_mb_mbchar_bytes() */
4630MBSTRING_API size_t php_mb_mbchar_bytes(const char *s)
4631{
4632    return php_mb_mbchar_bytes_ex(s, MBSTRG(internal_encoding));
4633}
4634/* }}} */
4635
4636/* {{{ MBSTRING_API char *php_mb_safe_strrchr_ex() */
4637MBSTRING_API char *php_mb_safe_strrchr_ex(const char *s, unsigned int c, size_t nbytes, const mbfl_encoding *enc)
4638{
4639    register const char *p = s;
4640    char *last=NULL;
4641
4642    if (nbytes == (size_t)-1) {
4643        size_t nb = 0;
4644
4645        while (*p != '\0') {
4646            if (nb == 0) {
4647                if ((unsigned char)*p == (unsigned char)c) {
4648                    last = (char *)p;
4649                }
4650                nb = php_mb_mbchar_bytes_ex(p, enc);
4651                if (nb == 0) {
4652                    return NULL; /* something is going wrong! */
4653                }
4654            }
4655            --nb;
4656            ++p;
4657        }
4658    } else {
4659        register size_t bcnt = nbytes;
4660        register size_t nbytes_char;
4661        while (bcnt > 0) {
4662            if ((unsigned char)*p == (unsigned char)c) {
4663                last = (char *)p;
4664            }
4665            nbytes_char = php_mb_mbchar_bytes_ex(p, enc);
4666            if (bcnt < nbytes_char) {
4667                return NULL;
4668            }
4669            p += nbytes_char;
4670            bcnt -= nbytes_char;
4671        }
4672    }
4673    return last;
4674}
4675/* }}} */
4676
4677/* {{{ MBSTRING_API char *php_mb_safe_strrchr() */
4678MBSTRING_API char *php_mb_safe_strrchr(const char *s, unsigned int c, size_t nbytes)
4679{
4680    return php_mb_safe_strrchr_ex(s, c, nbytes, MBSTRG(internal_encoding));
4681}
4682/* }}} */
4683
4684/* {{{ MBSTRING_API int php_mb_stripos()
4685 */
4686MBSTRING_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)
4687{
4688    int n;
4689    mbfl_string haystack, needle;
4690    n = -1;
4691
4692    mbfl_string_init(&haystack);
4693    mbfl_string_init(&needle);
4694    haystack.no_language = MBSTRG(language);
4695    haystack.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
4696    needle.no_language = MBSTRG(language);
4697    needle.no_encoding = MBSTRG(current_internal_encoding)->no_encoding;
4698
4699    do {
4700        size_t len = 0;
4701        haystack.val = (unsigned char *)php_unicode_convert_case(PHP_UNICODE_CASE_UPPER, (char *)old_haystack, old_haystack_len, &len, from_encoding);
4702        haystack.len = len;
4703
4704        if (!haystack.val) {
4705            break;
4706        }
4707
4708        if (haystack.len <= 0) {
4709            break;
4710        }
4711
4712        needle.val = (unsigned char *)php_unicode_convert_case(PHP_UNICODE_CASE_UPPER, (char *)old_needle, old_needle_len, &len, from_encoding);
4713        needle.len = len;
4714
4715        if (!needle.val) {
4716            break;
4717        }
4718
4719        if (needle.len <= 0) {
4720            break;
4721        }
4722
4723        haystack.no_encoding = needle.no_encoding = mbfl_name2no_encoding(from_encoding);
4724        if (haystack.no_encoding == mbfl_no_encoding_invalid) {
4725            php_error_docref(NULL, E_WARNING, "Unknown encoding \"%s\"", from_encoding);
4726            break;
4727        }
4728
4729        {
4730            int haystack_char_len = mbfl_strlen(&haystack);
4731
4732            if (mode) {
4733                if ((offset > 0 && offset > haystack_char_len) ||
4734                    (offset < 0 && -offset > haystack_char_len)) {
4735                    php_error_docref(NULL, E_WARNING, "Offset is greater than the length of haystack string");
4736                    break;
4737                }
4738            } else {
4739                if (offset < 0 || offset > haystack_char_len) {
4740                    php_error_docref(NULL, E_WARNING, "Offset not contained in string");
4741                    break;
4742                }
4743            }
4744        }
4745
4746        n = mbfl_strpos(&haystack, &needle, offset, mode);
4747    } while(0);
4748
4749    if (haystack.val) {
4750        efree(haystack.val);
4751    }
4752
4753    if (needle.val) {
4754        efree(needle.val);
4755    }
4756
4757    return n;
4758}
4759/* }}} */
4760
4761static void php_mb_gpc_get_detect_order(const zend_encoding ***list, size_t *list_size) /* {{{ */
4762{
4763    *list = (const zend_encoding **)MBSTRG(http_input_list);
4764    *list_size = MBSTRG(http_input_list_size);
4765}
4766/* }}} */
4767
4768static void php_mb_gpc_set_input_encoding(const zend_encoding *encoding) /* {{{ */
4769{
4770    MBSTRG(http_input_identify) = (const mbfl_encoding*)encoding;
4771}
4772/* }}} */
4773
4774#endif  /* HAVE_MBSTRING */
4775
4776/*
4777 * Local variables:
4778 * tab-width: 4
4779 * c-basic-offset: 4
4780 * End:
4781 * vim600: fdm=marker
4782 * vim: noet sw=4 ts=4
4783 */
4784