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