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