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