PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - ext/iconv - iconv.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 893 1201 74.4 %
Date: 2014-10-16 Functions: 35 35 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :    +----------------------------------------------------------------------+
       3             :    | PHP Version 5                                                        |
       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             :    | Authors: Rui Hirokawa <rui_hirokawa@ybb.ne.jp>                       |
      16             :    |          Stig Bakken <ssb@php.net>                                   |
      17             :    |          Moriyoshi Koizumi <moriyoshi@php.net>                       |
      18             :    +----------------------------------------------------------------------+
      19             :  */
      20             : 
      21             : /* $Id$ */
      22             : 
      23             : #ifdef HAVE_CONFIG_H
      24             : #include "config.h"
      25             : #endif
      26             : 
      27             : #include "php.h"
      28             : #include "php_globals.h"
      29             : #include "ext/standard/info.h"
      30             : #include "main/php_output.h"
      31             : #include "SAPI.h"
      32             : #include "php_ini.h"
      33             : 
      34             : #ifdef HAVE_STDLIB_H
      35             : # include <stdlib.h>
      36             : #endif
      37             : 
      38             : #include <errno.h>
      39             : 
      40             : #include "php_iconv.h"
      41             : 
      42             : #ifdef HAVE_ICONV
      43             : 
      44             : #ifdef PHP_ICONV_H_PATH
      45             : #include PHP_ICONV_H_PATH
      46             : #else
      47             : #include <iconv.h>
      48             : #endif
      49             : 
      50             : #ifdef HAVE_GLIBC_ICONV
      51             : #include <gnu/libc-version.h>
      52             : #endif
      53             : 
      54             : #ifdef HAVE_LIBICONV
      55             : #undef iconv
      56             : #endif
      57             : 
      58             : #include "ext/standard/php_smart_str.h"
      59             : #include "ext/standard/base64.h"
      60             : #include "ext/standard/quot_print.h"
      61             : 
      62             : #define _php_iconv_memequal(a, b, c) \
      63             :   ((c) == sizeof(unsigned long) ? *((unsigned long *)(a)) == *((unsigned long *)(b)) : ((c) == sizeof(unsigned int) ? *((unsigned int *)(a)) == *((unsigned int *)(b)) : memcmp(a, b, c) == 0))
      64             : 
      65             : /* {{{ arginfo */
      66             : ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_strlen, 0, 0, 1)
      67             :         ZEND_ARG_INFO(0, str)
      68             :         ZEND_ARG_INFO(0, charset)
      69             : ZEND_END_ARG_INFO()
      70             : 
      71             : ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_substr, 0, 0, 2)
      72             :         ZEND_ARG_INFO(0, str)
      73             :         ZEND_ARG_INFO(0, offset)
      74             :         ZEND_ARG_INFO(0, length)
      75             :         ZEND_ARG_INFO(0, charset)
      76             : ZEND_END_ARG_INFO()
      77             : 
      78             : ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_strpos, 0, 0, 2)
      79             :         ZEND_ARG_INFO(0, haystack)
      80             :         ZEND_ARG_INFO(0, needle)
      81             :         ZEND_ARG_INFO(0, offset)
      82             :         ZEND_ARG_INFO(0, charset)
      83             : ZEND_END_ARG_INFO()
      84             : 
      85             : ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_strrpos, 0, 0, 2)
      86             :         ZEND_ARG_INFO(0, haystack)
      87             :         ZEND_ARG_INFO(0, needle)
      88             :         ZEND_ARG_INFO(0, charset)
      89             : ZEND_END_ARG_INFO()
      90             : 
      91             : ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_mime_encode, 0, 0, 2)
      92             :         ZEND_ARG_INFO(0, field_name)
      93             :         ZEND_ARG_INFO(0, field_value)
      94             :         ZEND_ARG_INFO(0, preference) /* ZEND_ARG_ARRAY_INFO(0, preference, 1) */
      95             : ZEND_END_ARG_INFO()
      96             : 
      97             : ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_mime_decode, 0, 0, 1)
      98             :         ZEND_ARG_INFO(0, encoded_string)
      99             :         ZEND_ARG_INFO(0, mode)
     100             :         ZEND_ARG_INFO(0, charset)
     101             : ZEND_END_ARG_INFO()
     102             : 
     103             : ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_mime_decode_headers, 0, 0, 1)
     104             :         ZEND_ARG_INFO(0, headers)
     105             :         ZEND_ARG_INFO(0, mode)
     106             :         ZEND_ARG_INFO(0, charset)
     107             : ZEND_END_ARG_INFO()
     108             : 
     109             : ZEND_BEGIN_ARG_INFO(arginfo_iconv, 0)
     110             :         ZEND_ARG_INFO(0, in_charset)
     111             :         ZEND_ARG_INFO(0, out_charset)
     112             :         ZEND_ARG_INFO(0, str)
     113             : ZEND_END_ARG_INFO()
     114             : 
     115             : ZEND_BEGIN_ARG_INFO(arginfo_iconv_set_encoding, 0)
     116             :         ZEND_ARG_INFO(0, type)
     117             :         ZEND_ARG_INFO(0, charset)
     118             : ZEND_END_ARG_INFO()
     119             : 
     120             : ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_get_encoding, 0, 0, 0)
     121             :         ZEND_ARG_INFO(0, type)
     122             : ZEND_END_ARG_INFO()
     123             : 
     124             : /* }}} */
     125             : 
     126             : /* {{{ iconv_functions[]
     127             :  */
     128             : const zend_function_entry iconv_functions[] = {
     129             :         PHP_RAW_NAMED_FE(iconv,php_if_iconv,                            arginfo_iconv)
     130             :         PHP_FE(iconv_get_encoding,                                              arginfo_iconv_get_encoding)
     131             :         PHP_FE(iconv_set_encoding,                                              arginfo_iconv_set_encoding)
     132             :         PHP_FE(iconv_strlen,                                                    arginfo_iconv_strlen)
     133             :         PHP_FE(iconv_substr,                                                    arginfo_iconv_substr)
     134             :         PHP_FE(iconv_strpos,                                                    arginfo_iconv_strpos)
     135             :         PHP_FE(iconv_strrpos,                                                   arginfo_iconv_strrpos)
     136             :         PHP_FE(iconv_mime_encode,                                               arginfo_iconv_mime_encode)
     137             :         PHP_FE(iconv_mime_decode,                                               arginfo_iconv_mime_decode)
     138             :         PHP_FE(iconv_mime_decode_headers,                               arginfo_iconv_mime_decode_headers)
     139             :         PHP_FE_END
     140             : };
     141             : /* }}} */
     142             : 
     143             : ZEND_DECLARE_MODULE_GLOBALS(iconv)
     144             : static PHP_GINIT_FUNCTION(iconv);
     145             : 
     146             : /* {{{ iconv_module_entry
     147             :  */
     148             : zend_module_entry iconv_module_entry = {
     149             :         STANDARD_MODULE_HEADER,
     150             :         "iconv",
     151             :         iconv_functions,
     152             :         PHP_MINIT(miconv),
     153             :         PHP_MSHUTDOWN(miconv),
     154             :         NULL,
     155             :         NULL,
     156             :         PHP_MINFO(miconv),
     157             :         NO_VERSION_YET,
     158             :         PHP_MODULE_GLOBALS(iconv),
     159             :         PHP_GINIT(iconv),
     160             :         NULL,
     161             :         NULL,
     162             :         STANDARD_MODULE_PROPERTIES_EX
     163             : };
     164             : /* }}} */
     165             : 
     166             : #ifdef COMPILE_DL_ICONV
     167             : ZEND_GET_MODULE(iconv)
     168             : #endif
     169             : 
     170             : /* {{{ PHP_GINIT_FUNCTION */
     171       20225 : static PHP_GINIT_FUNCTION(iconv)
     172             : {
     173       20225 :         iconv_globals->input_encoding = NULL;
     174       20225 :         iconv_globals->output_encoding = NULL;
     175       20225 :         iconv_globals->internal_encoding = NULL;
     176       20225 : }
     177             : /* }}} */
     178             : 
     179             : #if defined(HAVE_LIBICONV) && defined(ICONV_ALIASED_LIBICONV)
     180             : #define iconv libiconv
     181             : #endif
     182             : 
     183             : /* {{{ typedef enum php_iconv_enc_scheme_t */
     184             : typedef enum _php_iconv_enc_scheme_t {
     185             :         PHP_ICONV_ENC_SCHEME_BASE64,
     186             :         PHP_ICONV_ENC_SCHEME_QPRINT
     187             : } php_iconv_enc_scheme_t;
     188             : /* }}} */
     189             : 
     190             : #define PHP_ICONV_MIME_DECODE_STRICT            (1<<0)
     191             : #define PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR (1<<1)
     192             : 
     193             : /* {{{ prototypes */
     194             : static php_iconv_err_t _php_iconv_appendl(smart_str *d, const char *s, size_t l, iconv_t cd);
     195             : static php_iconv_err_t _php_iconv_appendc(smart_str *d, const char c, iconv_t cd);
     196             : 
     197             : static void _php_iconv_show_error(php_iconv_err_t err, const char *out_charset, const char *in_charset TSRMLS_DC);
     198             : 
     199             : static php_iconv_err_t _php_iconv_strlen(unsigned int *pretval, const char *str, size_t nbytes, const char *enc);
     200             : 
     201             : static php_iconv_err_t _php_iconv_substr(smart_str *pretval, const char *str, size_t nbytes, int offset, int len, const char *enc);
     202             : 
     203             : static php_iconv_err_t _php_iconv_strpos(unsigned int *pretval, const char *haystk, size_t haystk_nbytes, const char *ndl, size_t ndl_nbytes, int offset, const char *enc);
     204             : 
     205             : static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fname, size_t fname_nbytes, const char *fval, size_t fval_nbytes, unsigned int max_line_len, const char *lfchars, php_iconv_enc_scheme_t enc_scheme, const char *out_charset, const char *enc);
     206             : 
     207             : static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *str, size_t str_nbytes, const char *enc, const char **next_pos, int mode);
     208             : 
     209             : static php_iconv_err_t php_iconv_stream_filter_register_factory(TSRMLS_D);
     210             : static php_iconv_err_t php_iconv_stream_filter_unregister_factory(TSRMLS_D);
     211             : 
     212             : static int php_iconv_output_conflict(const char *handler_name, size_t handler_name_len TSRMLS_DC);
     213             : static php_output_handler *php_iconv_output_handler_init(const char *name, size_t name_len, size_t chunk_size, int flags TSRMLS_DC);
     214             : static int php_iconv_output_handler(void **nothing, php_output_context *output_context);
     215             : /* }}} */
     216             : 
     217             : /* {{{ static globals */
     218             : static char _generic_superset_name[] = ICONV_UCS4_ENCODING;
     219             : #define GENERIC_SUPERSET_NAME _generic_superset_name
     220             : #define GENERIC_SUPERSET_NBYTES 4
     221             : /* }}} */
     222             : 
     223       60774 : static PHP_INI_MH(OnUpdateStringIconvCharset)
     224             : {
     225       60774 :         if(new_value_length >= ICONV_CSNMAXLEN) {
     226           1 :                 return FAILURE;
     227             :         }
     228       60773 :         OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
     229       60773 :         return SUCCESS;
     230             : }
     231             : 
     232             : /* {{{ PHP_INI
     233             :  */
     234             : PHP_INI_BEGIN()
     235             :         STD_PHP_INI_ENTRY("iconv.input_encoding",    ICONV_INPUT_ENCODING,    PHP_INI_ALL, OnUpdateStringIconvCharset, input_encoding,    zend_iconv_globals, iconv_globals)
     236             :         STD_PHP_INI_ENTRY("iconv.output_encoding",   ICONV_OUTPUT_ENCODING,   PHP_INI_ALL, OnUpdateStringIconvCharset, output_encoding,   zend_iconv_globals, iconv_globals)
     237             :         STD_PHP_INI_ENTRY("iconv.internal_encoding", ICONV_INTERNAL_ENCODING, PHP_INI_ALL, OnUpdateStringIconvCharset, internal_encoding, zend_iconv_globals, iconv_globals)
     238             : PHP_INI_END()
     239             : /* }}} */
     240             : 
     241             : /* {{{ PHP_MINIT_FUNCTION */
     242       20225 : PHP_MINIT_FUNCTION(miconv)
     243             : {
     244       20225 :         char *version = "unknown";
     245             : 
     246       20225 :         REGISTER_INI_ENTRIES();
     247             : 
     248             : #if HAVE_LIBICONV
     249             :         {
     250             :                 static char buf[16];
     251             :                 snprintf(buf, sizeof(buf), "%d.%d",
     252             :                     ((_libiconv_version >> 8) & 0x0f), (_libiconv_version & 0x0f));
     253             :                 version = buf;
     254             :         }
     255             : #elif HAVE_GLIBC_ICONV
     256       20225 :         version = (char *)gnu_get_libc_version();
     257             : #elif defined(NETWARE)
     258             :         version = "OS built-in";
     259             : #endif
     260             : 
     261             : #ifdef PHP_ICONV_IMPL
     262       20225 :         REGISTER_STRING_CONSTANT("ICONV_IMPL", PHP_ICONV_IMPL, CONST_CS | CONST_PERSISTENT);
     263             : #elif HAVE_LIBICONV
     264             :         REGISTER_STRING_CONSTANT("ICONV_IMPL", "libiconv", CONST_CS | CONST_PERSISTENT);
     265             : #elif defined(NETWARE)
     266             :         REGISTER_STRING_CONSTANT("ICONV_IMPL", "Novell", CONST_CS | CONST_PERSISTENT);
     267             : #else
     268             :         REGISTER_STRING_CONSTANT("ICONV_IMPL", "unknown", CONST_CS | CONST_PERSISTENT);
     269             : #endif
     270       20225 :         REGISTER_STRING_CONSTANT("ICONV_VERSION", version, CONST_CS | CONST_PERSISTENT);
     271             : 
     272       20225 :         REGISTER_LONG_CONSTANT("ICONV_MIME_DECODE_STRICT", PHP_ICONV_MIME_DECODE_STRICT, CONST_CS | CONST_PERSISTENT);
     273       20225 :         REGISTER_LONG_CONSTANT("ICONV_MIME_DECODE_CONTINUE_ON_ERROR", PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR, CONST_CS | CONST_PERSISTENT);
     274             : 
     275       20225 :         if (php_iconv_stream_filter_register_factory(TSRMLS_C) != PHP_ICONV_ERR_SUCCESS) {
     276           0 :                 return FAILURE;
     277             :         }
     278             : 
     279       20225 :         php_output_handler_alias_register(ZEND_STRL("ob_iconv_handler"), php_iconv_output_handler_init TSRMLS_CC);
     280       20225 :         php_output_handler_conflict_register(ZEND_STRL("ob_iconv_handler"), php_iconv_output_conflict TSRMLS_CC);
     281             : 
     282       20225 :         return SUCCESS;
     283             : }
     284             : /* }}} */
     285             : 
     286             : /* {{{ PHP_MSHUTDOWN_FUNCTION */
     287       20261 : PHP_MSHUTDOWN_FUNCTION(miconv)
     288             : {
     289       20261 :         php_iconv_stream_filter_unregister_factory(TSRMLS_C);
     290       20261 :         UNREGISTER_INI_ENTRIES();
     291       20261 :         return SUCCESS;
     292             : }
     293             : /* }}} */
     294             : 
     295             : /* {{{ PHP_MINFO_FUNCTION */
     296         148 : PHP_MINFO_FUNCTION(miconv)
     297             : {
     298             :         zval iconv_impl, iconv_ver;
     299             : 
     300         148 :         zend_get_constant("ICONV_IMPL", sizeof("ICONV_IMPL")-1, &iconv_impl TSRMLS_CC);
     301         148 :         zend_get_constant("ICONV_VERSION", sizeof("ICONV_VERSION")-1, &iconv_ver TSRMLS_CC);
     302             : 
     303         148 :         php_info_print_table_start();
     304         148 :         php_info_print_table_row(2, "iconv support", "enabled");
     305         148 :         php_info_print_table_row(2, "iconv implementation", Z_STRVAL(iconv_impl));
     306         148 :         php_info_print_table_row(2, "iconv library version", Z_STRVAL(iconv_ver));
     307         148 :         php_info_print_table_end();
     308             : 
     309         148 :         DISPLAY_INI_ENTRIES();
     310             : 
     311             :         zval_dtor(&iconv_impl);
     312             :         zval_dtor(&iconv_ver);
     313         148 : }
     314             : /* }}} */
     315             : 
     316           2 : static int php_iconv_output_conflict(const char *handler_name, size_t handler_name_len TSRMLS_DC)
     317             : {
     318           2 :         if (php_output_get_level(TSRMLS_C)) {
     319           0 :                 if (php_output_handler_conflict(handler_name, handler_name_len, ZEND_STRL("ob_iconv_handler") TSRMLS_CC)
     320           0 :                 ||      php_output_handler_conflict(handler_name, handler_name_len, ZEND_STRL("mb_output_handler") TSRMLS_CC)) {
     321           0 :                         return FAILURE;
     322             :                 }
     323             :         }
     324           2 :         return SUCCESS;
     325             : }
     326             : 
     327           2 : static php_output_handler *php_iconv_output_handler_init(const char *handler_name, size_t handler_name_len, size_t chunk_size, int flags TSRMLS_DC)
     328             : {
     329           2 :         return php_output_handler_create_internal(handler_name, handler_name_len, php_iconv_output_handler, chunk_size, flags TSRMLS_CC);
     330             : }
     331             : 
     332           2 : static int php_iconv_output_handler(void **nothing, php_output_context *output_context)
     333             : {
     334           2 :         char *s, *content_type, *mimetype = NULL;
     335           2 :         int output_status, mimetype_len = 0;
     336             :         PHP_OUTPUT_TSRMLS(output_context);
     337             : 
     338           2 :         if (output_context->op & PHP_OUTPUT_HANDLER_START) {
     339           2 :                 output_status = php_output_get_status(TSRMLS_C);
     340           2 :                 if (output_status & PHP_OUTPUT_SENT) {
     341           0 :                         return FAILURE;
     342             :                 }
     343             : 
     344           2 :                 if (SG(sapi_headers).mimetype && !strncasecmp(SG(sapi_headers).mimetype, "text/", 5)) {
     345           0 :                         if ((s = strchr(SG(sapi_headers).mimetype,';')) == NULL){
     346           0 :                                 mimetype = SG(sapi_headers).mimetype;
     347             :                         } else {
     348           0 :                                 mimetype = SG(sapi_headers).mimetype;
     349           0 :                                 mimetype_len = s - SG(sapi_headers).mimetype;
     350             :                         }
     351           2 :                 } else if (SG(sapi_headers).send_default_content_type) {
     352           2 :                         mimetype = SG(default_mimetype) ? SG(default_mimetype) : SAPI_DEFAULT_MIMETYPE;
     353             :                 }
     354             : 
     355           2 :                 if (mimetype != NULL && !(output_context->op & PHP_OUTPUT_HANDLER_CLEAN)) {
     356             :                         int len;
     357           2 :                         char *p = strstr(ICONVG(output_encoding), "//");
     358             : 
     359           2 :                         if (p) {
     360           0 :                                 len = spprintf(&content_type, 0, "Content-Type:%.*s; charset=%.*s", mimetype_len ? mimetype_len : (int) strlen(mimetype), mimetype, (int)(p - ICONVG(output_encoding)), ICONVG(output_encoding));
     361             :                         } else {
     362           2 :                                 len = spprintf(&content_type, 0, "Content-Type:%.*s; charset=%s", mimetype_len ? mimetype_len : (int) strlen(mimetype), mimetype, ICONVG(output_encoding));
     363             :                         }
     364           2 :                         if (content_type && SUCCESS == sapi_add_header(content_type, len, 0)) {
     365           2 :                                 SG(sapi_headers).send_default_content_type = 0;
     366           2 :                                 php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE, NULL TSRMLS_CC);
     367             :                         }
     368             :                 }
     369             :         }
     370             : 
     371           2 :         if (output_context->in.used) {
     372           2 :                 output_context->out.free = 1;
     373           2 :                 _php_iconv_show_error(php_iconv_string(output_context->in.data, output_context->in.used, &output_context->out.data, &output_context->out.used, ICONVG(output_encoding), ICONVG(internal_encoding)), ICONVG(output_encoding), ICONVG(internal_encoding) TSRMLS_CC);
     374             :         }
     375             : 
     376           2 :         return SUCCESS;
     377             : }
     378             : 
     379             : /* {{{ _php_iconv_appendl() */
     380       15456 : static php_iconv_err_t _php_iconv_appendl(smart_str *d, const char *s, size_t l, iconv_t cd)
     381             : {
     382       15456 :         const char *in_p = s;
     383       15456 :         size_t in_left = l;
     384             :         char *out_p;
     385       15456 :         size_t out_left = 0;
     386       15456 :         size_t buf_growth = 128;
     387             : #if !ICONV_SUPPORTS_ERRNO
     388             :         size_t prev_in_left = in_left;
     389             : #endif
     390             : 
     391       15456 :         if (in_p != NULL) {
     392       46324 :                 while (in_left > 0) {
     393       15440 :                         out_left = buf_growth - out_left;
     394             :                         {
     395             :                                 size_t newlen;
     396       15440 :                                 smart_str_alloc((d), out_left, 0);
     397             :                         }
     398             : 
     399       15440 :                         out_p = (d)->c + (d)->len;
     400             : 
     401       15440 :                         if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
     402             : #if ICONV_SUPPORTS_ERRNO
     403           4 :                                 switch (errno) {
     404             :                                         case EINVAL:
     405           0 :                                                 return PHP_ICONV_ERR_ILLEGAL_CHAR;
     406             : 
     407             :                                         case EILSEQ:
     408           4 :                                                 return PHP_ICONV_ERR_ILLEGAL_SEQ;
     409             : 
     410             :                                         case E2BIG:
     411           0 :                                                 break;
     412             : 
     413             :                                         default:
     414           0 :                                                 return PHP_ICONV_ERR_UNKNOWN;
     415             :                                 }
     416             : #else
     417             :                                 if (prev_in_left == in_left) {
     418             :                                         return PHP_ICONV_ERR_UNKNOWN;
     419             :                                 }
     420             : #endif
     421             :                         }
     422             : #if !ICONV_SUPPORTS_ERRNO
     423             :                         prev_in_left = in_left;
     424             : #endif
     425       15436 :                         (d)->len += (buf_growth - out_left);
     426       15436 :                         buf_growth <<= 1;
     427             :                 }
     428             :         } else {
     429             :                 for (;;) {
     430          12 :                         out_left = buf_growth - out_left;
     431             :                         {
     432             :                                 size_t newlen;
     433          12 :                                 smart_str_alloc((d), out_left, 0);
     434             :                         }
     435             : 
     436          12 :                         out_p = (d)->c + (d)->len;
     437             : 
     438          12 :                         if (iconv(cd, NULL, NULL, (char **) &out_p, &out_left) == (size_t)0) {
     439          12 :                                 (d)->len += (buf_growth - out_left);
     440          12 :                                 break;
     441             :                         } else {
     442             : #if ICONV_SUPPORTS_ERRNO
     443           0 :                                 if (errno != E2BIG) {
     444           0 :                                         return PHP_ICONV_ERR_UNKNOWN;
     445             :                                 }
     446             : #else
     447             :                                 if (out_left != 0) {
     448             :                                         return PHP_ICONV_ERR_UNKNOWN;
     449             :                                 }
     450             : #endif
     451             :                         }
     452           0 :                         (d)->len += (buf_growth - out_left);
     453           0 :                         buf_growth <<= 1;
     454           0 :                 }
     455             :         }
     456       15452 :         return PHP_ICONV_ERR_SUCCESS;
     457             : }
     458             : /* }}} */
     459             : 
     460             : /* {{{ _php_iconv_appendc() */
     461       13666 : static php_iconv_err_t _php_iconv_appendc(smart_str *d, const char c, iconv_t cd)
     462             : {
     463       13666 :         return _php_iconv_appendl(d, &c, 1, cd);
     464             : }
     465             : /* }}} */
     466             : 
     467             : /* {{{ php_iconv_string()
     468             :  */
     469         232 : PHP_ICONV_API php_iconv_err_t php_iconv_string(const char *in_p, size_t in_len,
     470             :                                                         char **out, size_t *out_len,
     471             :                                                         const char *out_charset, const char *in_charset)
     472             : {
     473             : #if !ICONV_SUPPORTS_ERRNO
     474             :         size_t in_size, out_size, out_left;
     475             :         char *out_buffer, *out_p;
     476             :         iconv_t cd;
     477             :         size_t result;
     478             : 
     479             :         *out = NULL;
     480             :         *out_len = 0;
     481             : 
     482             :         /*
     483             :           This is not the right way to get output size...
     484             :           This is not space efficient for large text.
     485             :           This is also problem for encoding like UTF-7/UTF-8/ISO-2022 which
     486             :           a single char can be more than 4 bytes.
     487             :           I added 15 extra bytes for safety. <yohgaki@php.net>
     488             :         */
     489             :         out_size = in_len * sizeof(int) + 15;
     490             :         out_left = out_size;
     491             : 
     492             :         in_size = in_len;
     493             : 
     494             :         cd = iconv_open(out_charset, in_charset);
     495             : 
     496             :         if (cd == (iconv_t)(-1)) {
     497             :                 return PHP_ICONV_ERR_UNKNOWN;
     498             :         }
     499             : 
     500             :         out_buffer = (char *) emalloc(out_size + 1);
     501             :         out_p = out_buffer;
     502             : 
     503             : #ifdef NETWARE
     504             :         result = iconv(cd, (char **) &in_p, &in_size, (char **)
     505             : #else
     506             :         result = iconv(cd, (const char **) &in_p, &in_size, (char **)
     507             : #endif
     508             :                                 &out_p, &out_left);
     509             : 
     510             :         if (result == (size_t)(-1)) {
     511             :                 efree(out_buffer);
     512             :                 return PHP_ICONV_ERR_UNKNOWN;
     513             :         }
     514             : 
     515             :         if (out_left < 8) {
     516             :                 size_t pos = out_p - out_buffer;
     517             :                 out_buffer = (char *) safe_erealloc(out_buffer, out_size, 1, 8);
     518             :                 out_p = out_buffer+pos;
     519             :                 out_size += 7;
     520             :                 out_left += 7;
     521             :         }
     522             : 
     523             :         /* flush the shift-out sequences */
     524             :         result = iconv(cd, NULL, NULL, &out_p, &out_left);
     525             : 
     526             :         if (result == (size_t)(-1)) {
     527             :                 efree(out_buffer);
     528             :                 return PHP_ICONV_ERR_UNKNOWN;
     529             :         }
     530             : 
     531             :         *out_len = out_size - out_left;
     532             :         out_buffer[*out_len] = '\0';
     533             :         *out = out_buffer;
     534             : 
     535             :         iconv_close(cd);
     536             : 
     537             :         return PHP_ICONV_ERR_SUCCESS;
     538             : 
     539             : #else
     540             :         /*
     541             :           iconv supports errno. Handle it better way.
     542             :         */
     543             :         iconv_t cd;
     544             :         size_t in_left, out_size, out_left;
     545             :         char *out_p, *out_buf, *tmp_buf;
     546         232 :         size_t bsz, result = 0;
     547         232 :         php_iconv_err_t retval = PHP_ICONV_ERR_SUCCESS;
     548             : 
     549         232 :         *out = NULL;
     550         232 :         *out_len = 0;
     551             : 
     552         232 :         cd = iconv_open(out_charset, in_charset);
     553             : 
     554         232 :         if (cd == (iconv_t)(-1)) {
     555          27 :                 if (errno == EINVAL) {
     556          27 :                         return PHP_ICONV_ERR_WRONG_CHARSET;
     557             :                 } else {
     558           0 :                         return PHP_ICONV_ERR_CONVERTER;
     559             :                 }
     560             :         }
     561         205 :         in_left= in_len;
     562         205 :         out_left = in_len + 32; /* Avoid realloc() most cases */
     563         205 :         out_size = 0;
     564         205 :         bsz = out_left;
     565         205 :         out_buf = (char *) emalloc(bsz+1);
     566         205 :         out_p = out_buf;
     567             : 
     568         431 :         while (in_left > 0) {
     569         226 :                 result = iconv(cd, (char **) &in_p, &in_left, (char **) &out_p, &out_left);
     570         226 :                 out_size = bsz - out_left;
     571         226 :                 if (result == (size_t)(-1)) {
     572          22 :                         if (errno == E2BIG && in_left > 0) {
     573             :                                 /* converted string is longer than out buffer */
     574          21 :                                 bsz += in_len;
     575             : 
     576          21 :                                 tmp_buf = (char*) erealloc(out_buf, bsz+1);
     577          21 :                                 out_p = out_buf = tmp_buf;
     578          21 :                                 out_p += out_size;
     579          21 :                                 out_left = bsz - out_size;
     580          21 :                                 continue;
     581             :                         }
     582             :                 }
     583         205 :                 break;
     584             :         }
     585             : 
     586         205 :         if (result != (size_t)(-1)) {
     587             :                 /* flush the shift-out sequences */
     588             :                 for (;;) {
     589         204 :                         result = iconv(cd, NULL, NULL, (char **) &out_p, &out_left);
     590         204 :                         out_size = bsz - out_left;
     591             : 
     592         204 :                         if (result != (size_t)(-1)) {
     593         204 :                                 break;
     594             :                         }
     595             : 
     596           0 :                         if (errno == E2BIG) {
     597           0 :                                 bsz += 16;
     598           0 :                                 tmp_buf = (char *) erealloc(out_buf, bsz);
     599             : 
     600           0 :                                 out_p = out_buf = tmp_buf;
     601           0 :                                 out_p += out_size;
     602           0 :                                 out_left = bsz - out_size;
     603             :                         } else {
     604           0 :                                 break;
     605             :                         }
     606           0 :                 }
     607             :         }
     608             : 
     609         205 :         iconv_close(cd);
     610             : 
     611         205 :         if (result == (size_t)(-1)) {
     612           1 :                 switch (errno) {
     613             :                         case EINVAL:
     614           0 :                                 retval = PHP_ICONV_ERR_ILLEGAL_CHAR;
     615           0 :                                 break;
     616             : 
     617             :                         case EILSEQ:
     618           1 :                                 retval = PHP_ICONV_ERR_ILLEGAL_SEQ;
     619           1 :                                 break;
     620             : 
     621             :                         case E2BIG:
     622             :                                 /* should not happen */
     623           0 :                                 retval = PHP_ICONV_ERR_TOO_BIG;
     624           0 :                                 break;
     625             : 
     626             :                         default:
     627             :                                 /* other error */
     628           0 :                                 retval = PHP_ICONV_ERR_UNKNOWN;
     629           0 :                                 efree(out_buf);
     630           0 :                                 return PHP_ICONV_ERR_UNKNOWN;
     631             :                 }
     632             :         }
     633         205 :         *out_p = '\0';
     634         205 :         *out = out_buf;
     635         205 :         *out_len = out_size;
     636         205 :         return retval;
     637             : #endif
     638             : }
     639             : /* }}} */
     640             : 
     641             : /* {{{ _php_iconv_strlen() */
     642          97 : static php_iconv_err_t _php_iconv_strlen(unsigned int *pretval, const char *str, size_t nbytes, const char *enc)
     643             : {
     644             :         char buf[GENERIC_SUPERSET_NBYTES*2];
     645             : 
     646          97 :         php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
     647             : 
     648             :         iconv_t cd;
     649             : 
     650             :         const char *in_p;
     651             :         size_t in_left;
     652             : 
     653             :         char *out_p;
     654             :         size_t out_left;
     655             : 
     656             :         unsigned int cnt;
     657             : 
     658          97 :         *pretval = (unsigned int)-1;
     659             : 
     660          97 :         cd = iconv_open(GENERIC_SUPERSET_NAME, enc);
     661             : 
     662          97 :         if (cd == (iconv_t)(-1)) {
     663             : #if ICONV_SUPPORTS_ERRNO
     664          13 :                 if (errno == EINVAL) {
     665          13 :                         return PHP_ICONV_ERR_WRONG_CHARSET;
     666             :                 } else {
     667           0 :                         return PHP_ICONV_ERR_CONVERTER;
     668             :                 }
     669             : #else
     670             :                 return PHP_ICONV_ERR_UNKNOWN;
     671             : #endif
     672             :         }
     673             : 
     674          84 :         errno = out_left = 0;
     675             : 
     676        3321 :         for (in_p = str, in_left = nbytes, cnt = 0; in_left > 0; cnt+=2) {
     677             :                 size_t prev_in_left;
     678        3237 :                 out_p = buf;
     679        3237 :                 out_left = sizeof(buf);
     680             : 
     681        3237 :                 prev_in_left = in_left;
     682             : 
     683        3237 :                 if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
     684        3163 :                         if (prev_in_left == in_left) {
     685           0 :                                 break;
     686             :                         }
     687             :                 }
     688             :         }
     689             : 
     690          84 :         if (out_left > 0) {
     691          32 :                 cnt -= out_left / GENERIC_SUPERSET_NBYTES;
     692             :         }
     693             : 
     694             : #if ICONV_SUPPORTS_ERRNO
     695          84 :         switch (errno) {
     696             :                 case EINVAL:
     697           0 :                         err = PHP_ICONV_ERR_ILLEGAL_CHAR;
     698           0 :                         break;
     699             : 
     700             :                 case EILSEQ:
     701           0 :                         err = PHP_ICONV_ERR_ILLEGAL_SEQ;
     702           0 :                         break;
     703             : 
     704             :                 case E2BIG:
     705             :                 case 0:
     706          84 :                         *pretval = cnt;
     707          84 :                         break;
     708             : 
     709             :                 default:
     710           0 :                         err = PHP_ICONV_ERR_UNKNOWN;
     711             :                         break;
     712             :         }
     713             : #else
     714             :         *pretval = cnt;
     715             : #endif
     716             : 
     717          84 :         iconv_close(cd);
     718             : 
     719          84 :         return err;
     720             : }
     721             : 
     722             : /* }}} */
     723             : 
     724             : /* {{{ _php_iconv_substr() */
     725          15 : static php_iconv_err_t _php_iconv_substr(smart_str *pretval,
     726             :         const char *str, size_t nbytes, int offset, int len, const char *enc)
     727             : {
     728             :         char buf[GENERIC_SUPERSET_NBYTES];
     729             : 
     730          15 :         php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
     731             : 
     732             :         iconv_t cd1, cd2;
     733             : 
     734             :         const char *in_p;
     735             :         size_t in_left;
     736             : 
     737             :         char *out_p;
     738             :         size_t out_left;
     739             : 
     740             :         unsigned int cnt;
     741             :         int total_len;
     742             : 
     743          15 :         err = _php_iconv_strlen(&total_len, str, nbytes, enc);
     744          15 :         if (err != PHP_ICONV_ERR_SUCCESS) {
     745           1 :                 return err;
     746             :         }
     747             : 
     748          14 :         if (len < 0) {
     749           3 :                 if ((len += (total_len - offset)) < 0) {
     750           2 :                         return PHP_ICONV_ERR_SUCCESS;
     751             :                 }
     752             :         }
     753             : 
     754          12 :         if (offset < 0) {
     755           1 :                 if ((offset += total_len) < 0) {
     756           0 :                         return PHP_ICONV_ERR_SUCCESS;
     757             :                 }
     758             :         }
     759             : 
     760          12 :         if(len > total_len) {
     761           1 :                 len = total_len;
     762             :         }
     763             : 
     764             : 
     765          12 :         if (offset >= total_len) {
     766           1 :                 return PHP_ICONV_ERR_SUCCESS;
     767             :         }
     768             : 
     769          11 :         if ((offset + len) > total_len ) {
     770             :                 /* trying to compute the length */
     771           3 :                 len = total_len - offset;
     772             :         }
     773             : 
     774          11 :         if (len == 0) {
     775           0 :                 smart_str_appendl(pretval, "", 0);
     776           0 :                 smart_str_0(pretval);
     777           0 :                 return PHP_ICONV_ERR_SUCCESS;
     778             :         }
     779             : 
     780          11 :         cd1 = iconv_open(GENERIC_SUPERSET_NAME, enc);
     781             : 
     782          11 :         if (cd1 == (iconv_t)(-1)) {
     783             : #if ICONV_SUPPORTS_ERRNO
     784           0 :                 if (errno == EINVAL) {
     785           0 :                         return PHP_ICONV_ERR_WRONG_CHARSET;
     786             :                 } else {
     787           0 :                         return PHP_ICONV_ERR_CONVERTER;
     788             :                 }
     789             : #else
     790             :                 return PHP_ICONV_ERR_UNKNOWN;
     791             : #endif
     792             :         }
     793             : 
     794          11 :         cd2 = (iconv_t)NULL;
     795          11 :         errno = 0;
     796             : 
     797         110 :         for (in_p = str, in_left = nbytes, cnt = 0; in_left > 0 && len > 0; ++cnt) {
     798             :                 size_t prev_in_left;
     799          99 :                 out_p = buf;
     800          99 :                 out_left = sizeof(buf);
     801             : 
     802          99 :                 prev_in_left = in_left;
     803             : 
     804          99 :                 if (iconv(cd1, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
     805          94 :                         if (prev_in_left == in_left) {
     806           0 :                                 break;
     807             :                         }
     808             :                 }
     809             : 
     810          99 :                 if (cnt >= (unsigned int)offset) {
     811          65 :                         if (cd2 == (iconv_t)NULL) {
     812          11 :                                 cd2 = iconv_open(enc, GENERIC_SUPERSET_NAME);
     813             : 
     814          11 :                                 if (cd2 == (iconv_t)(-1)) {
     815           0 :                                         cd2 = (iconv_t)NULL;
     816             : #if ICONV_SUPPORTS_ERRNO
     817           0 :                                         if (errno == EINVAL) {
     818           0 :                                                 err = PHP_ICONV_ERR_WRONG_CHARSET;
     819             :                                         } else {
     820           0 :                                                 err = PHP_ICONV_ERR_CONVERTER;
     821             :                                         }
     822             : #else
     823             :                                         err = PHP_ICONV_ERR_UNKNOWN;
     824             : #endif
     825           0 :                                         break;
     826             :                                 }
     827             :                         }
     828             : 
     829          65 :                         if (_php_iconv_appendl(pretval, buf, sizeof(buf), cd2) != PHP_ICONV_ERR_SUCCESS) {
     830           0 :                                 break;
     831             :                         }
     832          65 :                         --len;
     833             :                 }
     834             : 
     835             :         }
     836             : 
     837             : #if ICONV_SUPPORTS_ERRNO
     838          11 :         switch (errno) {
     839             :                 case EINVAL:
     840           0 :                         err = PHP_ICONV_ERR_ILLEGAL_CHAR;
     841           0 :                         break;
     842             : 
     843             :                 case EILSEQ:
     844           0 :                         err = PHP_ICONV_ERR_ILLEGAL_SEQ;
     845             :                         break;
     846             : 
     847             :                 case E2BIG:
     848             :                         break;
     849             :         }
     850             : #endif
     851          11 :         if (err == PHP_ICONV_ERR_SUCCESS) {
     852          11 :                 if (cd2 != (iconv_t)NULL) {
     853          11 :                         _php_iconv_appendl(pretval, NULL, 0, cd2);
     854             :                 }
     855          11 :                 smart_str_0(pretval);
     856             :         }
     857             : 
     858          11 :         if (cd1 != (iconv_t)NULL) {
     859          11 :                 iconv_close(cd1);
     860             :         }
     861             : 
     862          11 :         if (cd2 != (iconv_t)NULL) {
     863          11 :                 iconv_close(cd2);
     864             :         }
     865          11 :         return err;
     866             : }
     867             : 
     868             : /* }}} */
     869             : 
     870             : /* {{{ _php_iconv_strpos() */
     871         183 : static php_iconv_err_t _php_iconv_strpos(unsigned int *pretval,
     872             :         const char *haystk, size_t haystk_nbytes,
     873             :         const char *ndl, size_t ndl_nbytes,
     874             :         int offset, const char *enc)
     875             : {
     876             :         char buf[GENERIC_SUPERSET_NBYTES];
     877             : 
     878         183 :         php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
     879             : 
     880             :         iconv_t cd;
     881             : 
     882             :         const char *in_p;
     883             :         size_t in_left;
     884             : 
     885             :         char *out_p;
     886             :         size_t out_left;
     887             : 
     888             :         unsigned int cnt;
     889             : 
     890             :         char *ndl_buf;
     891             :         const char *ndl_buf_p;
     892             :         size_t ndl_buf_len, ndl_buf_left;
     893             : 
     894             :         unsigned int match_ofs;
     895             : 
     896         183 :         *pretval = (unsigned int)-1;
     897             : 
     898         183 :         err = php_iconv_string(ndl, ndl_nbytes,
     899             :                 &ndl_buf, &ndl_buf_len, GENERIC_SUPERSET_NAME, enc);
     900             : 
     901         183 :         if (err != PHP_ICONV_ERR_SUCCESS) {
     902          24 :                 if (ndl_buf != NULL) {
     903           0 :                         efree(ndl_buf);
     904             :                 }
     905          24 :                 return err;
     906             :         }
     907             : 
     908         159 :         cd = iconv_open(GENERIC_SUPERSET_NAME, enc);
     909             : 
     910         159 :         if (cd == (iconv_t)(-1)) {
     911           0 :                 if (ndl_buf != NULL) {
     912           0 :                         efree(ndl_buf);
     913             :                 }
     914             : #if ICONV_SUPPORTS_ERRNO
     915           0 :                 if (errno == EINVAL) {
     916           0 :                         return PHP_ICONV_ERR_WRONG_CHARSET;
     917             :                 } else {
     918           0 :                         return PHP_ICONV_ERR_CONVERTER;
     919             :                 }
     920             : #else
     921             :                 return PHP_ICONV_ERR_UNKNOWN;
     922             : #endif
     923             :         }
     924             : 
     925         159 :         ndl_buf_p = ndl_buf;
     926         159 :         ndl_buf_left = ndl_buf_len;
     927         159 :         match_ofs = (unsigned int)-1;
     928             : 
     929        7416 :         for (in_p = haystk, in_left = haystk_nbytes, cnt = 0; in_left > 0; ++cnt) {
     930             :                 size_t prev_in_left;
     931        7300 :                 out_p = buf;
     932        7300 :                 out_left = sizeof(buf);
     933             : 
     934        7300 :                 prev_in_left = in_left;
     935             : 
     936        7300 :                 if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
     937        7184 :                         if (prev_in_left == in_left) {
     938             : #if ICONV_SUPPORTS_ERRNO
     939           0 :                                 switch (errno) {
     940             :                                         case EINVAL:
     941           0 :                                                 err = PHP_ICONV_ERR_ILLEGAL_CHAR;
     942           0 :                                                 break;
     943             : 
     944             :                                         case EILSEQ:
     945           0 :                                                 err = PHP_ICONV_ERR_ILLEGAL_SEQ;
     946           0 :                                                 break;
     947             : 
     948             :                                         case E2BIG:
     949           0 :                                                 break;
     950             : 
     951             :                                         default:
     952           0 :                                                 err = PHP_ICONV_ERR_UNKNOWN;
     953             :                                                 break;
     954             :                                 }
     955             : #endif
     956           0 :                                 break;
     957             :                         }
     958             :                 }
     959        7300 :                 if (offset >= 0) {
     960        1604 :                         if (cnt >= (unsigned int)offset) {
     961        1329 :                                 if (_php_iconv_memequal(buf, ndl_buf_p, sizeof(buf))) {
     962         323 :                                         if (match_ofs == (unsigned int)-1) {
     963          80 :                                                 match_ofs = cnt;
     964             :                                         }
     965         323 :                                         ndl_buf_p += GENERIC_SUPERSET_NBYTES;
     966         323 :                                         ndl_buf_left -= GENERIC_SUPERSET_NBYTES;
     967         323 :                                         if (ndl_buf_left == 0) {
     968          43 :                                                 *pretval = match_ofs;
     969          43 :                                                 break;
     970             :                                         }
     971             :                                 } else {
     972             :                                         unsigned int i, j, lim;
     973             : 
     974        1006 :                                         i = 0;
     975        1006 :                                         j = GENERIC_SUPERSET_NBYTES;
     976        1006 :                                         lim = (unsigned int)(ndl_buf_p - ndl_buf);
     977             : 
     978        2195 :                                         while (j < lim) {
     979         183 :                                                 if (_php_iconv_memequal(&ndl_buf[j], &ndl_buf[i],
     980             :                                                            GENERIC_SUPERSET_NBYTES)) {
     981           0 :                                                         i += GENERIC_SUPERSET_NBYTES;
     982             :                                                 } else {
     983         183 :                                                         j -= i;
     984         183 :                                                         i = 0;
     985             :                                                 }
     986         183 :                                                 j += GENERIC_SUPERSET_NBYTES;
     987             :                                         }
     988             : 
     989        1006 :                                         if (_php_iconv_memequal(buf, &ndl_buf[i], sizeof(buf))) {
     990         123 :                                                 match_ofs += (lim - i) / GENERIC_SUPERSET_NBYTES;
     991         123 :                                                 i += GENERIC_SUPERSET_NBYTES;
     992         123 :                                                 ndl_buf_p = &ndl_buf[i];
     993         123 :                                                 ndl_buf_left = ndl_buf_len - i;
     994             :                                         } else {
     995         883 :                                                 match_ofs = (unsigned int)-1;
     996         883 :                                                 ndl_buf_p = ndl_buf;
     997         883 :                                                 ndl_buf_left = ndl_buf_len;
     998             :                                         }
     999             :                                 }
    1000             :                         }
    1001             :                 } else {
    1002        5696 :                         if (_php_iconv_memequal(buf, ndl_buf_p, sizeof(buf))) {
    1003         567 :                                 if (match_ofs == (unsigned int)-1) {
    1004         176 :                                         match_ofs = cnt;
    1005             :                                 }
    1006         567 :                                 ndl_buf_p += GENERIC_SUPERSET_NBYTES;
    1007         567 :                                 ndl_buf_left -= GENERIC_SUPERSET_NBYTES;
    1008         567 :                                 if (ndl_buf_left == 0) {
    1009          51 :                                         *pretval = match_ofs;
    1010          51 :                                         ndl_buf_p = ndl_buf;
    1011          51 :                                         ndl_buf_left = ndl_buf_len;
    1012          51 :                                         match_ofs = -1;
    1013             :                                 }
    1014             :                         } else {
    1015             :                                 unsigned int i, j, lim;
    1016             : 
    1017        5129 :                                 i = 0;
    1018        5129 :                                 j = GENERIC_SUPERSET_NBYTES;
    1019        5129 :                                 lim = (unsigned int)(ndl_buf_p - ndl_buf);
    1020             : 
    1021       10556 :                                 while (j < lim) {
    1022         298 :                                         if (_php_iconv_memequal(&ndl_buf[j], &ndl_buf[i],
    1023             :                                                            GENERIC_SUPERSET_NBYTES)) {
    1024           0 :                                                 i += GENERIC_SUPERSET_NBYTES;
    1025             :                                         } else {
    1026         298 :                                                 j -= i;
    1027         298 :                                                 i = 0;
    1028             :                                         }
    1029         298 :                                         j += GENERIC_SUPERSET_NBYTES;
    1030             :                                 }
    1031             : 
    1032        5129 :                                 if (_php_iconv_memequal(buf, &ndl_buf[i], sizeof(buf))) {
    1033         179 :                                         match_ofs += (lim - i) / GENERIC_SUPERSET_NBYTES;
    1034         179 :                                         i += GENERIC_SUPERSET_NBYTES;
    1035         179 :                                         ndl_buf_p = &ndl_buf[i];
    1036         179 :                                         ndl_buf_left = ndl_buf_len - i;
    1037             :                                 } else {
    1038        4950 :                                         match_ofs = (unsigned int)-1;
    1039        4950 :                                         ndl_buf_p = ndl_buf;
    1040        4950 :                                         ndl_buf_left = ndl_buf_len;
    1041             :                                 }
    1042             :                         }
    1043             :                 }
    1044             :         }
    1045             : 
    1046         159 :         if (ndl_buf) {
    1047         159 :                 efree(ndl_buf);
    1048             :         }
    1049             : 
    1050         159 :         iconv_close(cd);
    1051             : 
    1052         159 :         return err;
    1053             : }
    1054             : /* }}} */
    1055             : 
    1056             : /* {{{ _php_iconv_mime_encode() */
    1057          83 : static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fname, size_t fname_nbytes, const char *fval, size_t fval_nbytes, unsigned int max_line_len, const char *lfchars, php_iconv_enc_scheme_t enc_scheme, const char *out_charset, const char *enc)
    1058             : {
    1059          83 :         php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
    1060          83 :         iconv_t cd = (iconv_t)(-1), cd_pl = (iconv_t)(-1);
    1061          83 :         unsigned int char_cnt = 0;
    1062             :         size_t out_charset_len;
    1063             :         size_t lfchars_len;
    1064          83 :         char *buf = NULL;
    1065          83 :         char *encoded = NULL;
    1066             :         size_t encoded_len;
    1067             :         const char *in_p;
    1068             :         size_t in_left;
    1069             :         char *out_p;
    1070             :         size_t out_left;
    1071             :         static int qp_table[256] = {
    1072             :                 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x00 */
    1073             :                 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 */
    1074             :                 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x20 */
    1075             :                 1, 1, 1, 1, 1, 1, 1 ,1, 1, 1, 1, 1, 1, 3, 1, 3, /* 0x30 */
    1076             :                 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 */
    1077             :                 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, /* 0x50 */
    1078             :                 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 */
    1079             :                 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, /* 0x70 */
    1080             :                 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x80 */
    1081             :                 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x90 */
    1082             :                 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xA0 */
    1083             :                 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xB0 */
    1084             :                 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xC0 */
    1085             :                 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xD0 */
    1086             :                 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xE0 */
    1087             :                 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3  /* 0xF0 */
    1088             :         };
    1089             : 
    1090          83 :         out_charset_len = strlen(out_charset);
    1091          83 :         lfchars_len = strlen(lfchars);
    1092             : 
    1093         242 :         if ((fname_nbytes + 2) >= max_line_len
    1094         159 :                 || (out_charset_len + 12) >= max_line_len) {
    1095             :                 /* field name is too long */
    1096          24 :                 err = PHP_ICONV_ERR_TOO_BIG;
    1097          24 :                 goto out;
    1098             :         }
    1099             : 
    1100          59 :         cd_pl = iconv_open(ICONV_ASCII_ENCODING, enc);
    1101          59 :         if (cd_pl == (iconv_t)(-1)) {
    1102             : #if ICONV_SUPPORTS_ERRNO
    1103           0 :                 if (errno == EINVAL) {
    1104           0 :                         err = PHP_ICONV_ERR_WRONG_CHARSET;
    1105             :                 } else {
    1106           0 :                         err = PHP_ICONV_ERR_CONVERTER;
    1107             :                 }
    1108             : #else
    1109             :                 err = PHP_ICONV_ERR_UNKNOWN;
    1110             : #endif
    1111           0 :                 goto out;
    1112             :         }
    1113             : 
    1114          59 :         cd = iconv_open(out_charset, enc);
    1115          59 :         if (cd == (iconv_t)(-1)) {
    1116             : #if ICONV_SUPPORTS_ERRNO
    1117           0 :                 if (errno == EINVAL) {
    1118           0 :                         err = PHP_ICONV_ERR_WRONG_CHARSET;
    1119             :                 } else {
    1120           0 :                         err = PHP_ICONV_ERR_CONVERTER;
    1121             :                 }
    1122             : #else
    1123             :                 err = PHP_ICONV_ERR_UNKNOWN;
    1124             : #endif
    1125           0 :                 goto out;
    1126             :         }
    1127             : 
    1128          59 :         buf = safe_emalloc(1, max_line_len, 5);
    1129             : 
    1130          59 :         char_cnt = max_line_len;
    1131             : 
    1132          59 :         _php_iconv_appendl(pretval, fname, fname_nbytes, cd_pl);
    1133          59 :         char_cnt -= fname_nbytes;
    1134          59 :         smart_str_appendl(pretval, ": ", sizeof(": ") - 1);
    1135          59 :         char_cnt -= 2;
    1136             : 
    1137          59 :         in_p = fval;
    1138          59 :         in_left = fval_nbytes;
    1139             : 
    1140             :         do {
    1141             :                 size_t prev_in_left;
    1142             :                 size_t out_size;
    1143             : 
    1144         180 :                 if (char_cnt < (out_charset_len + 12)) {
    1145             :                         /* lfchars must be encoded in ASCII here*/
    1146         126 :                         smart_str_appendl(pretval, lfchars, lfchars_len);
    1147         126 :                         smart_str_appendc(pretval, ' ');
    1148         126 :                         char_cnt = max_line_len - 1;
    1149             :                 }
    1150             : 
    1151         180 :                 smart_str_appendl(pretval, "=?", sizeof("=?") - 1);
    1152         180 :                 char_cnt -= 2;
    1153         180 :                 smart_str_appendl(pretval, out_charset, out_charset_len);
    1154         180 :                 char_cnt -= out_charset_len;
    1155         180 :                 smart_str_appendc(pretval, '?');
    1156         180 :                 char_cnt --;
    1157             : 
    1158         180 :                 switch (enc_scheme) {
    1159             :                         case PHP_ICONV_ENC_SCHEME_BASE64: {
    1160             :                                 size_t ini_in_left;
    1161             :                                 const char *ini_in_p;
    1162         174 :                                 size_t out_reserved = 4;
    1163             :                                 int dummy;
    1164             : 
    1165         174 :                                 smart_str_appendc(pretval, 'B');
    1166         174 :                                 char_cnt--;
    1167         174 :                                 smart_str_appendc(pretval, '?');
    1168         174 :                                 char_cnt--;
    1169             : 
    1170         174 :                                 prev_in_left = ini_in_left = in_left;
    1171         174 :                                 ini_in_p = in_p;
    1172             : 
    1173         174 :                                 out_size = (char_cnt - 2) / 4 * 3;
    1174             : 
    1175             :                                 for (;;) {
    1176         174 :                                         out_p = buf;
    1177             : 
    1178         174 :                                         if (out_size <= out_reserved) {
    1179           6 :                                                 err = PHP_ICONV_ERR_TOO_BIG;
    1180           6 :                                                 goto out;
    1181             :                                         }
    1182             : 
    1183         168 :                                         out_left = out_size - out_reserved;
    1184             : 
    1185         168 :                                         if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
    1186             : #if ICONV_SUPPORTS_ERRNO
    1187         122 :                                                 switch (errno) {
    1188             :                                                         case EINVAL:
    1189           0 :                                                                 err = PHP_ICONV_ERR_ILLEGAL_CHAR;
    1190           0 :                                                                 goto out;
    1191             : 
    1192             :                                                         case EILSEQ:
    1193           0 :                                                                 err = PHP_ICONV_ERR_ILLEGAL_SEQ;
    1194           0 :                                                                 goto out;
    1195             : 
    1196             :                                                         case E2BIG:
    1197         122 :                                                                 if (prev_in_left == in_left) {
    1198           6 :                                                                         err = PHP_ICONV_ERR_TOO_BIG;
    1199           6 :                                                                         goto out;
    1200             :                                                                 }
    1201         116 :                                                                 break;
    1202             : 
    1203             :                                                         default:
    1204           0 :                                                                 err = PHP_ICONV_ERR_UNKNOWN;
    1205           0 :                                                                 goto out;
    1206             :                                                 }
    1207             : #else
    1208             :                                                 if (prev_in_left == in_left) {
    1209             :                                                         err = PHP_ICONV_ERR_UNKNOWN;
    1210             :                                                         goto out;
    1211             :                                                 }
    1212             : #endif
    1213             :                                         }
    1214             : 
    1215         162 :                                         out_left += out_reserved;
    1216             : 
    1217         162 :                                         if (iconv(cd, NULL, NULL, (char **) &out_p, &out_left) == (size_t)-1) {
    1218             : #if ICONV_SUPPORTS_ERRNO
    1219           0 :                                                 if (errno != E2BIG) {
    1220           0 :                                                         err = PHP_ICONV_ERR_UNKNOWN;
    1221           0 :                                                         goto out;
    1222             :                                                 }
    1223             : #else
    1224             :                                                 if (out_left != 0) {
    1225             :                                                         err = PHP_ICONV_ERR_UNKNOWN;
    1226             :                                                         goto out;
    1227             :                                                 }
    1228             : #endif
    1229             :                                         } else {
    1230         162 :                                                 break;
    1231             :                                         }
    1232             : 
    1233           0 :                                         if (iconv(cd, NULL, NULL, NULL, NULL) == (size_t)-1) {
    1234           0 :                                                 err = PHP_ICONV_ERR_UNKNOWN;
    1235           0 :                                                 goto out;
    1236             :                                         }
    1237             : 
    1238           0 :                                         out_reserved += 4;
    1239           0 :                                         in_left = ini_in_left;
    1240           0 :                                         in_p = ini_in_p;
    1241           0 :                                 }
    1242             : 
    1243         162 :                                 prev_in_left = in_left;
    1244             : 
    1245         162 :                                 encoded = (char *) php_base64_encode((unsigned char *) buf, (int)(out_size - out_left), &dummy);
    1246         162 :                                 encoded_len = (size_t)dummy;
    1247             : 
    1248         162 :                                 if (char_cnt < encoded_len) {
    1249             :                                         /* something went wrong! */
    1250           0 :                                         err = PHP_ICONV_ERR_UNKNOWN;
    1251           0 :                                         goto out;
    1252             :                                 }
    1253             : 
    1254         162 :                                 smart_str_appendl(pretval, encoded, encoded_len);
    1255         162 :                                 char_cnt -= encoded_len;
    1256         162 :                                 smart_str_appendl(pretval, "?=", sizeof("?=") - 1);
    1257         162 :                                 char_cnt -= 2;
    1258             : 
    1259         162 :                                 efree(encoded);
    1260         162 :                                 encoded = NULL;
    1261         162 :                         } break; /* case PHP_ICONV_ENC_SCHEME_BASE64: */
    1262             : 
    1263             :                         case PHP_ICONV_ENC_SCHEME_QPRINT: {
    1264             :                                 size_t ini_in_left;
    1265             :                                 const char *ini_in_p;
    1266             :                                 const unsigned char *p;
    1267             :                                 size_t nbytes_required;
    1268             : 
    1269           6 :                                 smart_str_appendc(pretval, 'Q');
    1270           6 :                                 char_cnt--;
    1271           6 :                                 smart_str_appendc(pretval, '?');
    1272           6 :                                 char_cnt--;
    1273             : 
    1274           6 :                                 prev_in_left = ini_in_left = in_left;
    1275           6 :                                 ini_in_p = in_p;
    1276             : 
    1277          12 :                                 for (out_size = (char_cnt - 2) / 3; out_size > 0;) {
    1278             :                                         size_t prev_out_left;
    1279             : 
    1280           6 :                                         nbytes_required = 0;
    1281             : 
    1282           6 :                                         out_p = buf;
    1283           6 :                                         out_left = out_size;
    1284             : 
    1285           6 :                                         if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
    1286             : #if ICONV_SUPPORTS_ERRNO
    1287           5 :                                                 switch (errno) {
    1288             :                                                         case EINVAL:
    1289           0 :                                                                 err = PHP_ICONV_ERR_ILLEGAL_CHAR;
    1290           0 :                                                                 goto out;
    1291             : 
    1292             :                                                         case EILSEQ:
    1293           0 :                                                                 err = PHP_ICONV_ERR_ILLEGAL_SEQ;
    1294           0 :                                                                 goto out;
    1295             : 
    1296             :                                                         case E2BIG:
    1297           5 :                                                                 if (prev_in_left == in_left) {
    1298           0 :                                                                         err = PHP_ICONV_ERR_UNKNOWN;
    1299           0 :                                                                         goto out;
    1300             :                                                                 }
    1301           5 :                                                                 break;
    1302             : 
    1303             :                                                         default:
    1304           0 :                                                                 err = PHP_ICONV_ERR_UNKNOWN;
    1305           0 :                                                                 goto out;
    1306             :                                                 }
    1307             : #else
    1308             :                                                 if (prev_in_left == in_left) {
    1309             :                                                         err = PHP_ICONV_ERR_UNKNOWN;
    1310             :                                                         goto out;
    1311             :                                                 }
    1312             : #endif
    1313             :                                         }
    1314             : 
    1315           6 :                                         prev_out_left = out_left;
    1316           6 :                                         if (iconv(cd, NULL, NULL, (char **) &out_p, &out_left) == (size_t)-1) {
    1317             : #if ICONV_SUPPORTS_ERRNO
    1318           0 :                                                 if (errno != E2BIG) {
    1319           0 :                                                         err = PHP_ICONV_ERR_UNKNOWN;
    1320           0 :                                                         goto out;
    1321             :                                                 }
    1322             : #else
    1323             :                                                 if (out_left == prev_out_left) {
    1324             :                                                         err = PHP_ICONV_ERR_UNKNOWN;
    1325             :                                                         goto out;
    1326             :                                                 }
    1327             : #endif
    1328             :                                         }
    1329             : 
    1330          24 :                                         for (p = (unsigned char *)buf; p < (unsigned char *)out_p; p++) {
    1331          18 :                                                 nbytes_required += qp_table[*p];
    1332             :                                         }
    1333             : 
    1334           6 :                                         if (nbytes_required <= char_cnt - 2) {
    1335           6 :                                                 break;
    1336             :                                         }
    1337             : 
    1338           0 :                                         out_size -= ((nbytes_required - (char_cnt - 2)) + 1) / 3;
    1339           0 :                                         in_left = ini_in_left;
    1340           0 :                                         in_p = ini_in_p;
    1341             :                                 }
    1342             : 
    1343          24 :                                 for (p = (unsigned char *)buf; p < (unsigned char *)out_p; p++) {
    1344          18 :                                         if (qp_table[*p] == 1) {
    1345           0 :                                                 smart_str_appendc(pretval, *(char *)p);
    1346           0 :                                                 char_cnt--;
    1347             :                                         } else {
    1348             :                                                 static char qp_digits[] = "0123456789ABCDEF";
    1349          18 :                                                 smart_str_appendc(pretval, '=');
    1350          18 :                                                 smart_str_appendc(pretval, qp_digits[(*p >> 4) & 0x0f]);
    1351          18 :                                                 smart_str_appendc(pretval, qp_digits[(*p & 0x0f)]);
    1352          18 :                                                 char_cnt -= 3;
    1353             :                                         }
    1354             :                                 }
    1355             : 
    1356           6 :                                 smart_str_appendl(pretval, "?=", sizeof("?=") - 1);
    1357           6 :                                 char_cnt -= 2;
    1358             : 
    1359           6 :                                 if (iconv(cd, NULL, NULL, NULL, NULL) == (size_t)-1) {
    1360           0 :                                         err = PHP_ICONV_ERR_UNKNOWN;
    1361           0 :                                         goto out;
    1362             :                                 }
    1363             : 
    1364             :                         } break; /* case PHP_ICONV_ENC_SCHEME_QPRINT: */
    1365             :                 }
    1366         168 :         } while (in_left > 0);
    1367             : 
    1368          47 :         smart_str_0(pretval);
    1369             : 
    1370             : out:
    1371          83 :         if (cd != (iconv_t)(-1)) {
    1372          59 :                 iconv_close(cd);
    1373             :         }
    1374          83 :         if (cd_pl != (iconv_t)(-1)) {
    1375          59 :                 iconv_close(cd_pl);
    1376             :         }
    1377          83 :         if (encoded != NULL) {
    1378           0 :                 efree(encoded);
    1379             :         }
    1380          83 :         if (buf != NULL) {
    1381          59 :                 efree(buf);
    1382             :         }
    1383          83 :         return err;
    1384             : }
    1385             : /* }}} */
    1386             : 
    1387             : /* {{{ _php_iconv_mime_decode() */
    1388         343 : static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *str, size_t str_nbytes, const char *enc, const char **next_pos, int mode)
    1389             : {
    1390         343 :         php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
    1391             : 
    1392         343 :         iconv_t cd = (iconv_t)(-1), cd_pl = (iconv_t)(-1);
    1393             : 
    1394             :         const char *p1;
    1395             :         size_t str_left;
    1396         343 :         unsigned int scan_stat = 0;
    1397         343 :         const char *csname = NULL;
    1398             :         size_t csname_len;
    1399         343 :         const char *encoded_text = NULL;
    1400         343 :         size_t encoded_text_len = 0;
    1401         343 :         const char *encoded_word = NULL;
    1402         343 :         const char *spaces = NULL;
    1403             : 
    1404         343 :         php_iconv_enc_scheme_t enc_scheme = PHP_ICONV_ENC_SCHEME_BASE64;
    1405             : 
    1406         343 :         if (next_pos != NULL) {
    1407         250 :                 *next_pos = NULL;
    1408             :         }
    1409             : 
    1410         343 :         cd_pl = iconv_open(enc, ICONV_ASCII_ENCODING);
    1411             : 
    1412         343 :         if (cd_pl == (iconv_t)(-1)) {
    1413             : #if ICONV_SUPPORTS_ERRNO
    1414          15 :                 if (errno == EINVAL) {
    1415          15 :                         err = PHP_ICONV_ERR_WRONG_CHARSET;
    1416             :                 } else {
    1417           0 :                         err = PHP_ICONV_ERR_CONVERTER;
    1418             :                 }
    1419             : #else
    1420             :                 err = PHP_ICONV_ERR_UNKNOWN;
    1421             : #endif
    1422          15 :                 goto out;
    1423             :         }
    1424             : 
    1425         328 :         p1 = str;
    1426       21662 :         for (str_left = str_nbytes; str_left > 0; str_left--, p1++) {
    1427       21337 :                 int eos = 0;
    1428             : 
    1429       21337 :                 switch (scan_stat) {
    1430             :                         case 0: /* expecting any character */
    1431       10153 :                                 switch (*p1) {
    1432             :                                         case '\r': /* part of an EOL sequence? */
    1433           0 :                                                 scan_stat = 7;
    1434           0 :                                                 break;
    1435             : 
    1436             :                                         case '\n':
    1437         214 :                                                 scan_stat = 8;
    1438         214 :                                                 break;
    1439             : 
    1440             :                                         case '=': /* first letter of an encoded chunk */
    1441          34 :                                                 encoded_word = p1;
    1442          34 :                                                 scan_stat = 1;
    1443          34 :                                                 break;
    1444             : 
    1445             :                                         case ' ': case '\t': /* a chunk of whitespaces */
    1446        1050 :                                                 spaces = p1;
    1447        1050 :                                                 scan_stat = 11;
    1448        1050 :                                                 break;
    1449             : 
    1450             :                                         default: /* first letter of a non-encoded word */
    1451        8855 :                                                 _php_iconv_appendc(pretval, *p1, cd_pl);
    1452        8855 :                                                 encoded_word = NULL;
    1453        8855 :                                                 if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
    1454          80 :                                                         scan_stat = 12;
    1455             :                                                 }
    1456             :                                                 break;
    1457             :                                 }
    1458       10153 :                                 break;
    1459             : 
    1460             :                         case 1: /* expecting a delimiter */
    1461         197 :                                 if (*p1 != '?') {
    1462          31 :                                         err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
    1463          31 :                                         if (err != PHP_ICONV_ERR_SUCCESS) {
    1464           0 :                                                 goto out;
    1465             :                                         }
    1466          31 :                                         encoded_word = NULL;
    1467          31 :                                         if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
    1468           0 :                                                 scan_stat = 12;
    1469             :                                         } else {
    1470          31 :                                                 scan_stat = 0;
    1471             :                                         }
    1472          31 :                                         break;
    1473             :                                 }
    1474         166 :                                 csname = p1 + 1;
    1475         166 :                                 scan_stat = 2;
    1476         166 :                                 break;
    1477             : 
    1478             :                         case 2: /* expecting a charset name */
    1479        1527 :                                 switch (*p1) {
    1480             :                                         case '?': /* normal delimiter: encoding scheme follows */
    1481         152 :                                                 scan_stat = 3;
    1482         152 :                                                 break;
    1483             : 
    1484             :                                         case '*': /* new style delimiter: locale id follows */
    1485          14 :                                                 scan_stat = 10;
    1486             :                                                 break;
    1487             :                                 }
    1488        1527 :                                 if (scan_stat != 2) {
    1489             :                                         char tmpbuf[80];
    1490             : 
    1491         166 :                                         if (csname == NULL) {
    1492           0 :                                                 err = PHP_ICONV_ERR_MALFORMED;
    1493           0 :                                                 goto out;
    1494             :                                         }
    1495             : 
    1496         166 :                                         csname_len = (size_t)(p1 - csname);
    1497             : 
    1498         166 :                                         if (csname_len > sizeof(tmpbuf) - 1) {
    1499           0 :                                                 if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
    1500           0 :                                                         err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
    1501           0 :                                                         if (err != PHP_ICONV_ERR_SUCCESS) {
    1502           0 :                                                                 goto out;
    1503             :                                                         }
    1504           0 :                                                         encoded_word = NULL;
    1505           0 :                                                         if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
    1506           0 :                                                                 scan_stat = 12;
    1507             :                                                         } else {
    1508           0 :                                                                 scan_stat = 0;
    1509             :                                                         }
    1510           0 :                                                         break;
    1511             :                                                 } else {
    1512           0 :                                                         err = PHP_ICONV_ERR_MALFORMED;
    1513           0 :                                                         goto out;
    1514             :                                                 }
    1515             :                                         }
    1516             : 
    1517         166 :                                         memcpy(tmpbuf, csname, csname_len);
    1518         166 :                                         tmpbuf[csname_len] = '\0';
    1519             : 
    1520         166 :                                         if (cd != (iconv_t)(-1)) {
    1521          66 :                                                 iconv_close(cd);
    1522             :                                         }
    1523             : 
    1524         166 :                                         cd = iconv_open(enc, tmpbuf);
    1525             : 
    1526         166 :                                         if (cd == (iconv_t)(-1)) {
    1527           6 :                                                 if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
    1528             :                                                         /* Bad character set, but the user wants us to
    1529             :                                                          * press on. In this case, we'll just insert the
    1530             :                                                          * undecoded encoded word, since there isn't really
    1531             :                                                          * a more sensible behaviour available; the only
    1532             :                                                          * other options are to swallow the encoded word
    1533             :                                                          * entirely or decode it with an arbitrarily chosen
    1534             :                                                          * single byte encoding, both of which seem to have
    1535             :                                                          * a higher WTF factor than leaving it undecoded.
    1536             :                                                          *
    1537             :                                                          * Given this approach, we need to skip ahead to
    1538             :                                                          * the end of the encoded word. */
    1539           6 :                                                         int qmarks = 2;
    1540          78 :                                                         while (qmarks > 0 && str_left > 1) {
    1541          66 :                                                                 if (*(++p1) == '?') {
    1542          12 :                                                                         --qmarks;
    1543             :                                                                 }
    1544          66 :                                                                 --str_left;
    1545             :                                                         }
    1546             : 
    1547             :                                                         /* Look ahead to check for the terminating = that
    1548             :                                                          * should be there as well; if it's there, we'll
    1549             :                                                          * also include that. If it's not, there isn't much
    1550             :                                                          * we can do at this point. */
    1551           6 :                                                         if (*(p1 + 1) == '=') {
    1552           6 :                                                                 ++p1;
    1553           6 :                                                                 --str_left;
    1554             :                                                         }
    1555             : 
    1556           6 :                                                         err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
    1557           6 :                                                         if (err != PHP_ICONV_ERR_SUCCESS) {
    1558           0 :                                                                 goto out;
    1559             :                                                         }
    1560             : 
    1561             :                                                         /* Let's go back and see if there are further
    1562             :                                                          * encoded words or bare content, and hope they
    1563             :                                                          * might actually have a valid character set. */
    1564           6 :                                                         scan_stat = 12;
    1565           6 :                                                         break;
    1566             :                                                 } else {
    1567             : #if ICONV_SUPPORTS_ERRNO
    1568           0 :                                                         if (errno == EINVAL) {
    1569           0 :                                                                 err = PHP_ICONV_ERR_WRONG_CHARSET;
    1570             :                                                         } else {
    1571           0 :                                                                 err = PHP_ICONV_ERR_CONVERTER;
    1572             :                                                         }
    1573             : #else
    1574             :                                                         err = PHP_ICONV_ERR_UNKNOWN;
    1575             : #endif
    1576           0 :                                                         goto out;
    1577             :                                                 }
    1578             :                                         }
    1579             :                                 }
    1580        1521 :                                 break;
    1581             : 
    1582             :                         case 3: /* expecting a encoding scheme specifier */
    1583         160 :                                 switch (*p1) {
    1584             :                                         case 'b':
    1585             :                                         case 'B':
    1586         109 :                                                 enc_scheme = PHP_ICONV_ENC_SCHEME_BASE64;
    1587         109 :                                                 scan_stat = 4;
    1588         109 :                                                 break;
    1589             : 
    1590             :                                         case 'q':
    1591             :                                         case 'Q':
    1592          49 :                                                 enc_scheme = PHP_ICONV_ENC_SCHEME_QPRINT;
    1593          49 :                                                 scan_stat = 4;
    1594          49 :                                                 break;
    1595             : 
    1596             :                                         default:
    1597           2 :                                                 if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
    1598           2 :                                                         err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
    1599           2 :                                                         if (err != PHP_ICONV_ERR_SUCCESS) {
    1600           0 :                                                                 goto out;
    1601             :                                                         }
    1602           2 :                                                         encoded_word = NULL;
    1603           2 :                                                         if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
    1604           1 :                                                                 scan_stat = 12;
    1605             :                                                         } else {
    1606           1 :                                                                 scan_stat = 0;
    1607             :                                                         }
    1608           2 :                                                         break;
    1609             :                                                 } else {
    1610           0 :                                                         err = PHP_ICONV_ERR_MALFORMED;
    1611           0 :                                                         goto out;
    1612             :                                                 }
    1613             :                                 }
    1614         160 :                                 break;
    1615             : 
    1616             :                         case 4: /* expecting a delimiter */
    1617         158 :                                 if (*p1 != '?') {
    1618           0 :                                         if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
    1619             :                                                 /* pass the entire chunk through the converter */
    1620           0 :                                                 err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
    1621           0 :                                                 if (err != PHP_ICONV_ERR_SUCCESS) {
    1622           0 :                                                         goto out;
    1623             :                                                 }
    1624           0 :                                                 encoded_word = NULL;
    1625           0 :                                                 if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
    1626           0 :                                                         scan_stat = 12;
    1627             :                                                 } else {
    1628           0 :                                                         scan_stat = 0;
    1629             :                                                 }
    1630           0 :                                                 break;
    1631             :                                         } else {
    1632           0 :                                                 err = PHP_ICONV_ERR_MALFORMED;
    1633           0 :                                                 goto out;
    1634             :                                         }
    1635             :                                 }
    1636         158 :                                 encoded_text = p1 + 1;
    1637         158 :                                 scan_stat = 5;
    1638         158 :                                 break;
    1639             : 
    1640             :                         case 5: /* expecting an encoded portion */
    1641        2735 :                                 if (*p1 == '?') {
    1642         158 :                                         encoded_text_len = (size_t)(p1 - encoded_text);
    1643         158 :                                         scan_stat = 6;
    1644             :                                 }
    1645        2735 :                                 break;
    1646             : 
    1647             :                         case 7: /* expecting a "\n" character */
    1648           0 :                                 if (*p1 == '\n') {
    1649           0 :                                         scan_stat = 8;
    1650             :                                 } else {
    1651             :                                         /* bare CR */
    1652           0 :                                         _php_iconv_appendc(pretval, '\r', cd_pl);
    1653           0 :                                         _php_iconv_appendc(pretval, *p1, cd_pl);
    1654           0 :                                         scan_stat = 0;
    1655             :                                 }
    1656           0 :                                 break;
    1657             : 
    1658             :                         case 8: /* checking whether the following line is part of a
    1659             :                                            folded header */
    1660         352 :                                 if (*p1 != ' ' && *p1 != '\t') {
    1661         198 :                                         --p1;
    1662         198 :                                         str_left = 1; /* quit_loop */
    1663         198 :                                         break;
    1664             :                                 }
    1665         154 :                                 if (encoded_word == NULL) {
    1666         106 :                                         _php_iconv_appendc(pretval, ' ', cd_pl);
    1667             :                                 }
    1668         154 :                                 spaces = NULL;
    1669         154 :                                 scan_stat = 11;
    1670         154 :                                 break;
    1671             : 
    1672             :                         case 6: /* expecting a End-Of-Chunk character "=" */
    1673         158 :                                 if (*p1 != '=') {
    1674           4 :                                         if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
    1675             :                                                 /* pass the entire chunk through the converter */
    1676           2 :                                                 err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
    1677           2 :                                                 if (err != PHP_ICONV_ERR_SUCCESS) {
    1678           0 :                                                         goto out;
    1679             :                                                 }
    1680           2 :                                                 encoded_word = NULL;
    1681           2 :                                                 if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
    1682           1 :                                                         scan_stat = 12;
    1683             :                                                 } else {
    1684           1 :                                                         scan_stat = 0;
    1685             :                                                 }
    1686           2 :                                                 break;
    1687             :                                         } else {
    1688           2 :                                                 err = PHP_ICONV_ERR_MALFORMED;
    1689           2 :                                                 goto out;
    1690             :                                         }
    1691             :                                 }
    1692         154 :                                 scan_stat = 9;
    1693         154 :                                 if (str_left == 1) {
    1694          41 :                                         eos = 1;
    1695             :                                 } else {
    1696         113 :                                         break;
    1697             :                                 }
    1698             : 
    1699             :                         case 9: /* choice point, seeing what to do next.*/
    1700         155 :                                 switch (*p1) {
    1701             :                                         default:
    1702             :                                                 /* Handle non-RFC-compliant formats
    1703             :                                                  *
    1704             :                                                  * RFC2047 requires the character that comes right
    1705             :                                                  * after an encoded word (chunk) to be a whitespace,
    1706             :                                                  * while there are lots of broken implementations that
    1707             :                                                  * generate such malformed headers that don't fulfill
    1708             :                                                  * that requirement.
    1709             :                                                  */
    1710          48 :                                                 if (!eos) {
    1711           7 :                                                         if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
    1712             :                                                                 /* pass the entire chunk through the converter */
    1713           2 :                                                                 err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
    1714           2 :                                                                 if (err != PHP_ICONV_ERR_SUCCESS) {
    1715           0 :                                                                         goto out;
    1716             :                                                                 }
    1717           2 :                                                                 scan_stat = 12;
    1718           2 :                                                                 break;
    1719             :                                                         }
    1720             :                                                 }
    1721             :                                                 /* break is omitted intentionally */
    1722             : 
    1723             :                                         case '\r': case '\n': case ' ': case '\t': {
    1724             :                                                 char *decoded_text;
    1725             :                                                 size_t decoded_text_len;
    1726             :                                                 int dummy;
    1727             : 
    1728         153 :                                                 switch (enc_scheme) {
    1729             :                                                         case PHP_ICONV_ENC_SCHEME_BASE64:
    1730         109 :                                                                 decoded_text = (char *)php_base64_decode((unsigned char*)encoded_text, (int)encoded_text_len, &dummy);
    1731         109 :                                                                 decoded_text_len = (size_t)dummy;
    1732         109 :                                                                 break;
    1733             : 
    1734             :                                                         case PHP_ICONV_ENC_SCHEME_QPRINT:
    1735          44 :                                                                 decoded_text = (char *)php_quot_print_decode((unsigned char*)encoded_text, (int)encoded_text_len, &decoded_text_len, 1);
    1736          44 :                                                                 break;
    1737             :                                                         default:
    1738           0 :                                                                 decoded_text = NULL;
    1739             :                                                                 break;
    1740             :                                                 }
    1741             : 
    1742         153 :                                                 if (decoded_text == NULL) {
    1743           0 :                                                         if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
    1744             :                                                                 /* pass the entire chunk through the converter */
    1745           0 :                                                                 err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
    1746           0 :                                                                 if (err != PHP_ICONV_ERR_SUCCESS) {
    1747           0 :                                                                         goto out;
    1748             :                                                                 }
    1749           0 :                                                                 encoded_word = NULL;
    1750           0 :                                                                 if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
    1751           0 :                                                                         scan_stat = 12;
    1752             :                                                                 } else {
    1753           0 :                                                                         scan_stat = 0;
    1754             :                                                                 }
    1755           0 :                                                                 break;
    1756             :                                                         } else {
    1757           0 :                                                                 err = PHP_ICONV_ERR_UNKNOWN;
    1758           0 :                                                                 goto out;
    1759             :                                                         }
    1760             :                                                 }
    1761             : 
    1762         153 :                                                 err = _php_iconv_appendl(pretval, decoded_text, decoded_text_len, cd);
    1763         153 :                                                 efree(decoded_text);
    1764             : 
    1765         153 :                                                 if (err != PHP_ICONV_ERR_SUCCESS) {
    1766           3 :                                                         if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
    1767             :                                                                 /* pass the entire chunk through the converter */
    1768           2 :                                                                 err = _php_iconv_appendl(pretval, encoded_word, (size_t)(p1 - encoded_word), cd_pl);
    1769           2 :                                                                 encoded_word = NULL;
    1770           2 :                                                                 if (err != PHP_ICONV_ERR_SUCCESS) {
    1771           1 :                                                                         break;
    1772             :                                                                 }
    1773             :                                                         } else {
    1774           1 :                                                                 goto out;
    1775             :                                                         }
    1776             :                                                 }
    1777             : 
    1778         151 :                                                 if (eos) { /* reached end-of-string. done. */
    1779          41 :                                                         scan_stat = 0;
    1780          41 :                                                         break;
    1781             :                                                 }
    1782             : 
    1783         110 :                                                 switch (*p1) {
    1784             :                                                         case '\r': /* part of an EOL sequence? */
    1785           0 :                                                                 scan_stat = 7;
    1786           0 :                                                                 break;
    1787             : 
    1788             :                                                         case '\n':
    1789          83 :                                                                 scan_stat = 8;
    1790          83 :                                                                 break;
    1791             : 
    1792             :                                                         case '=': /* first letter of an encoded chunk */
    1793           4 :                                                                 scan_stat = 1;
    1794           4 :                                                                 break;
    1795             : 
    1796             :                                                         case ' ': case '\t': /* medial whitespaces */
    1797          22 :                                                                 spaces = p1;
    1798          22 :                                                                 scan_stat = 11;
    1799          22 :                                                                 break;
    1800             : 
    1801             :                                                         default: /* first letter of a non-encoded word */
    1802           1 :                                                                 _php_iconv_appendc(pretval, *p1, cd_pl);
    1803           1 :                                                                 scan_stat = 12;
    1804             :                                                                 break;
    1805             :                                                 }
    1806             :                                         } break;
    1807             :                                 }
    1808         154 :                                 break;
    1809             : 
    1810             :                         case 10: /* expects a language specifier. dismiss it for now */
    1811          84 :                                 if (*p1 == '?') {
    1812          14 :                                         scan_stat = 3;
    1813             :                                 }
    1814          84 :                                 break;
    1815             : 
    1816             :                         case 11: /* expecting a chunk of whitespaces */
    1817        1973 :                                 switch (*p1) {
    1818             :                                         case '\r': /* part of an EOL sequence? */
    1819           0 :                                                 scan_stat = 7;
    1820           0 :                                                 break;
    1821             : 
    1822             :                                         case '\n':
    1823           6 :                                                 scan_stat = 8;
    1824           6 :                                                 break;
    1825             : 
    1826             :                                         case '=': /* first letter of an encoded chunk */
    1827         160 :                                                 if (spaces != NULL && encoded_word == NULL) {
    1828          98 :                                                         _php_iconv_appendl(pretval, spaces, (size_t)(p1 - spaces), cd_pl);
    1829          98 :                                                         spaces = NULL;
    1830             :                                                 }
    1831         160 :                                                 encoded_word = p1;
    1832         160 :                                                 scan_stat = 1;
    1833         160 :                                                 break;
    1834             : 
    1835             :                                         case ' ': case '\t':
    1836         342 :                                                 break;
    1837             : 
    1838             :                                         default: /* first letter of a non-encoded word */
    1839        1465 :                                                 if (spaces != NULL) {
    1840        1359 :                                                         _php_iconv_appendl(pretval, spaces, (size_t)(p1 - spaces), cd_pl);
    1841        1359 :                                                         spaces = NULL;
    1842             :                                                 }
    1843        1465 :                                                 _php_iconv_appendc(pretval, *p1, cd_pl);
    1844        1465 :                                                 encoded_word = NULL;
    1845        1465 :                                                 if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
    1846         402 :                                                         scan_stat = 12;
    1847             :                                                 } else {
    1848        1063 :                                                         scan_stat = 0;
    1849             :                                                 }
    1850             :                                                 break;
    1851             :                                 }
    1852        1973 :                                 break;
    1853             : 
    1854             :                         case 12: /* expecting a non-encoded word */
    1855        3726 :                                 switch (*p1) {
    1856             :                                         case '\r': /* part of an EOL sequence? */
    1857           0 :                                                 scan_stat = 7;
    1858           0 :                                                 break;
    1859             : 
    1860             :                                         case '\n':
    1861          83 :                                                 scan_stat = 8;
    1862          83 :                                                 break;
    1863             : 
    1864             :                                         case ' ': case '\t':
    1865         405 :                                                 spaces = p1;
    1866         405 :                                                 scan_stat = 11;
    1867         405 :                                                 break;
    1868             : 
    1869             :                                         case '=': /* first letter of an encoded chunk */
    1870          31 :                                                 if (!(mode & PHP_ICONV_MIME_DECODE_STRICT)) {
    1871           0 :                                                         encoded_word = p1;
    1872           0 :                                                         scan_stat = 1;
    1873           0 :                                                         break;
    1874             :                                                 }
    1875             :                                                 /* break is omitted intentionally */
    1876             : 
    1877             :                                         default:
    1878        3238 :                                                 _php_iconv_appendc(pretval, *p1, cd_pl);
    1879             :                                                 break;
    1880             :                                 }
    1881             :                                 break;
    1882             :                 }
    1883             :         }
    1884         325 :         switch (scan_stat) {
    1885             :                 case 0: case 8: case 11: case 12:
    1886         324 :                         break;
    1887             :                 default:
    1888           1 :                         if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
    1889           1 :                                 if (scan_stat == 1) {
    1890           1 :                                         _php_iconv_appendc(pretval, '=', cd_pl);
    1891             :                                 }
    1892           1 :                                 err = PHP_ICONV_ERR_SUCCESS;
    1893             :                         } else {
    1894           0 :                                 err = PHP_ICONV_ERR_MALFORMED;
    1895           0 :                                 goto out;
    1896             :                         }
    1897             :         }
    1898             : 
    1899         325 :         if (next_pos != NULL) {
    1900         250 :                 *next_pos = p1;
    1901             :         }
    1902             : 
    1903         325 :         smart_str_0(pretval);
    1904             : out:
    1905         343 :         if (cd != (iconv_t)(-1)) {
    1906          94 :                 iconv_close(cd);
    1907             :         }
    1908         343 :         if (cd_pl != (iconv_t)(-1)) {
    1909         328 :                 iconv_close(cd_pl);
    1910             :         }
    1911         343 :         return err;
    1912             : }
    1913             : /* }}} */
    1914             : 
    1915             : /* {{{ php_iconv_show_error() */
    1916         505 : static void _php_iconv_show_error(php_iconv_err_t err, const char *out_charset, const char *in_charset TSRMLS_DC)
    1917             : {
    1918         505 :         switch (err) {
    1919             :                 case PHP_ICONV_ERR_SUCCESS:
    1920         410 :                         break;
    1921             : 
    1922             :                 case PHP_ICONV_ERR_CONVERTER:
    1923           0 :                         php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot open converter");
    1924           0 :                         break;
    1925             : 
    1926             :                 case PHP_ICONV_ERR_WRONG_CHARSET:
    1927          55 :                         php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Wrong charset, conversion from `%s' to `%s' is not allowed",
    1928             :                                   in_charset, out_charset);
    1929          55 :                         break;
    1930             : 
    1931             :                 case PHP_ICONV_ERR_ILLEGAL_CHAR:
    1932           0 :                         php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected an incomplete multibyte character in input string");
    1933           0 :                         break;
    1934             : 
    1935             :                 case PHP_ICONV_ERR_ILLEGAL_SEQ:
    1936           2 :                         php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected an illegal character in input string");
    1937           2 :                         break;
    1938             : 
    1939             :                 case PHP_ICONV_ERR_TOO_BIG:
    1940             :                         /* should not happen */
    1941          36 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Buffer length exceeded");
    1942          36 :                         break;
    1943             : 
    1944             :                 case PHP_ICONV_ERR_MALFORMED:
    1945           2 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Malformed string");
    1946           2 :                         break;
    1947             : 
    1948             :                 default:
    1949             :                         /* other error */
    1950           0 :                         php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unknown error (%d)", errno);
    1951             :                         break;
    1952             :         }
    1953         505 : }
    1954             : /* }}} */
    1955             : 
    1956             : /* {{{ proto int iconv_strlen(string str [, string charset])
    1957             :    Returns the character count of str */
    1958          87 : PHP_FUNCTION(iconv_strlen)
    1959             : {
    1960          87 :         char *charset = ICONVG(internal_encoding);
    1961          87 :         int charset_len = 0;
    1962             :         char *str;
    1963             :         int str_len;
    1964             : 
    1965             :         php_iconv_err_t err;
    1966             : 
    1967             :         unsigned int retval;
    1968             : 
    1969          87 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s",
    1970             :                 &str, &str_len, &charset, &charset_len) == FAILURE) {
    1971           4 :                 RETURN_FALSE;
    1972             :         }
    1973             : 
    1974          83 :         if (charset_len >= ICONV_CSNMAXLEN) {
    1975           1 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
    1976           1 :                 RETURN_FALSE;
    1977             :         }
    1978             : 
    1979          82 :         err = _php_iconv_strlen(&retval, str, str_len, charset);
    1980          82 :         _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset TSRMLS_CC);
    1981          82 :         if (err == PHP_ICONV_ERR_SUCCESS) {
    1982          70 :                 RETVAL_LONG(retval);
    1983             :         } else {
    1984          12 :                 RETVAL_FALSE;
    1985             :         }
    1986             : }
    1987             : /* }}} */
    1988             : 
    1989             : /* {{{ proto string iconv_substr(string str, int offset, [int length, string charset])
    1990             :    Returns specified part of a string */
    1991          18 : PHP_FUNCTION(iconv_substr)
    1992             : {
    1993          18 :         char *charset = ICONVG(internal_encoding);
    1994          18 :         int charset_len = 0;
    1995             :         char *str;
    1996             :         int str_len;
    1997          18 :         long offset, length = 0;
    1998             : 
    1999             :         php_iconv_err_t err;
    2000             : 
    2001          18 :         smart_str retval = {0};
    2002             : 
    2003          18 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|ls",
    2004             :                 &str, &str_len, &offset, &length,
    2005             :                 &charset, &charset_len) == FAILURE) {
    2006           2 :                 RETURN_FALSE;
    2007             :         }
    2008             : 
    2009          16 :         if (charset_len >= ICONV_CSNMAXLEN) {
    2010           1 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
    2011           1 :                 RETURN_FALSE;
    2012             :         }
    2013             : 
    2014          15 :         if (ZEND_NUM_ARGS() < 3) {
    2015           3 :                 length = str_len;
    2016             :         }
    2017             : 
    2018          15 :         err = _php_iconv_substr(&retval, str, str_len, offset, length, charset);
    2019          15 :         _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset TSRMLS_CC);
    2020             : 
    2021          15 :         if (err == PHP_ICONV_ERR_SUCCESS && str != NULL && retval.c != NULL) {
    2022          11 :                 RETURN_STRINGL(retval.c, retval.len, 0);
    2023             :         }
    2024           4 :         smart_str_free(&retval);
    2025           4 :         RETURN_FALSE;
    2026             : }
    2027             : /* }}} */
    2028             : 
    2029             : /* {{{ proto int iconv_strpos(string haystack, string needle [, int offset [, string charset]])
    2030             :    Finds position of first occurrence of needle within part of haystack beginning with offset */
    2031         133 : PHP_FUNCTION(iconv_strpos)
    2032             : {
    2033         133 :         char *charset = ICONVG(internal_encoding);
    2034         133 :         int charset_len = 0;
    2035             :         char *haystk;
    2036             :         int haystk_len;
    2037             :         char *ndl;
    2038             :         int ndl_len;
    2039         133 :         long offset = 0;
    2040             : 
    2041             :         php_iconv_err_t err;
    2042             : 
    2043             :         unsigned int retval;
    2044             : 
    2045         133 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|ls",
    2046             :                 &haystk, &haystk_len, &ndl, &ndl_len,
    2047             :                 &offset, &charset, &charset_len) == FAILURE) {
    2048          12 :                 RETURN_FALSE;
    2049             :         }
    2050             : 
    2051         121 :         if (charset_len >= ICONV_CSNMAXLEN) {
    2052           1 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
    2053           1 :                 RETURN_FALSE;
    2054             :         }
    2055             : 
    2056         120 :         if (offset < 0) {
    2057           5 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset not contained in string.");
    2058           5 :                 RETURN_FALSE;
    2059             :         }
    2060             : 
    2061         115 :         if (ndl_len < 1) {
    2062           9 :                 RETURN_FALSE;
    2063             :         }
    2064             : 
    2065         106 :         err = _php_iconv_strpos(&retval, haystk, haystk_len, ndl, ndl_len,
    2066             :                                 offset, charset);
    2067         106 :         _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset TSRMLS_CC);
    2068             : 
    2069         150 :         if (err == PHP_ICONV_ERR_SUCCESS && retval != (unsigned int)-1) {
    2070          44 :                 RETVAL_LONG((long)retval);
    2071             :         } else {
    2072          62 :                 RETVAL_FALSE;
    2073             :         }
    2074             : }
    2075             : /* }}} */
    2076             : 
    2077             : /* {{{ proto int iconv_strrpos(string haystack, string needle [, string charset])
    2078             :    Finds position of last occurrence of needle within part of haystack beginning with offset */
    2079          92 : PHP_FUNCTION(iconv_strrpos)
    2080             : {
    2081          92 :         char *charset = ICONVG(internal_encoding);
    2082          92 :         int charset_len = 0;
    2083             :         char *haystk;
    2084             :         int haystk_len;
    2085             :         char *ndl;
    2086             :         int ndl_len;
    2087             : 
    2088             :         php_iconv_err_t err;
    2089             : 
    2090             :         unsigned int retval;
    2091             : 
    2092          92 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|s",
    2093             :                 &haystk, &haystk_len, &ndl, &ndl_len,
    2094             :                 &charset, &charset_len) == FAILURE) {
    2095           5 :                 RETURN_FALSE;
    2096             :         }
    2097             : 
    2098          87 :         if (ndl_len < 1) {
    2099           9 :                 RETURN_FALSE;
    2100             :         }
    2101             : 
    2102          78 :         if (charset_len >= ICONV_CSNMAXLEN) {
    2103           1 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
    2104           1 :                 RETURN_FALSE;
    2105             :         }
    2106             : 
    2107          77 :         err = _php_iconv_strpos(&retval, haystk, haystk_len, ndl, ndl_len,
    2108             :                                 -1, charset);
    2109          77 :         _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset TSRMLS_CC);
    2110             : 
    2111         108 :         if (err == PHP_ICONV_ERR_SUCCESS && retval != (unsigned int)-1) {
    2112          31 :                 RETVAL_LONG((long)retval);
    2113             :         } else {
    2114          46 :                 RETVAL_FALSE;
    2115             :         }
    2116             : }
    2117             : /* }}} */
    2118             : 
    2119             : /* {{{ proto string iconv_mime_encode(string field_name, string field_value [, array preference])
    2120             :    Composes a mime header field with field_name and field_value in a specified scheme */
    2121          83 : PHP_FUNCTION(iconv_mime_encode)
    2122             : {
    2123          83 :         const char *field_name = NULL;
    2124             :         int field_name_len;
    2125          83 :         const char *field_value = NULL;
    2126             :         int field_value_len;
    2127          83 :         zval *pref = NULL;
    2128          83 :         zval tmp_zv, *tmp_zv_p = NULL;
    2129          83 :         smart_str retval = {0};
    2130             :         php_iconv_err_t err;
    2131             : 
    2132          83 :         const char *in_charset = ICONVG(internal_encoding);
    2133          83 :         const char *out_charset = in_charset;
    2134          83 :         long line_len = 76;
    2135          83 :         const char *lfchars = "\r\n";
    2136          83 :         php_iconv_enc_scheme_t scheme_id = PHP_ICONV_ENC_SCHEME_BASE64;
    2137             : 
    2138          83 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a",
    2139             :                 &field_name, &field_name_len, &field_value, &field_value_len,
    2140             :                 &pref) == FAILURE) {
    2141             : 
    2142           0 :                 RETURN_FALSE;
    2143             :         }
    2144             : 
    2145          83 :         if (pref != NULL) {
    2146             :                 zval **ppval;
    2147             : 
    2148          82 :                 if (zend_hash_find(Z_ARRVAL_P(pref), "scheme", sizeof("scheme"), (void **)&ppval) == SUCCESS) {
    2149          81 :                         if (Z_TYPE_PP(ppval) == IS_STRING && Z_STRLEN_PP(ppval) > 0) {
    2150          81 :                                 switch (Z_STRVAL_PP(ppval)[0]) {
    2151             :                                         case 'B': case 'b':
    2152          80 :                                                 scheme_id = PHP_ICONV_ENC_SCHEME_BASE64;
    2153          80 :                                                 break;
    2154             : 
    2155             :                                         case 'Q': case 'q':
    2156           1 :                                                 scheme_id = PHP_ICONV_ENC_SCHEME_QPRINT;
    2157             :                                                 break;
    2158             :                                 }
    2159             :                         }
    2160             :                 }
    2161             : 
    2162          82 :                 if (zend_hash_find(Z_ARRVAL_P(pref), "input-charset", sizeof("input-charset"), (void **)&ppval) == SUCCESS) {
    2163          81 :                         if (Z_STRLEN_PP(ppval) >= ICONV_CSNMAXLEN) {
    2164           0 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
    2165           0 :                                 RETURN_FALSE;
    2166             :                         }
    2167             : 
    2168          81 :                         if (Z_TYPE_PP(ppval) == IS_STRING && Z_STRLEN_PP(ppval) > 0) {
    2169          81 :                                 in_charset = Z_STRVAL_PP(ppval);
    2170             :                         }
    2171             :                 }
    2172             : 
    2173             : 
    2174          82 :                 if (zend_hash_find(Z_ARRVAL_P(pref), "output-charset", sizeof("output-charset"), (void **)&ppval) == SUCCESS) {
    2175          81 :                         if (Z_STRLEN_PP(ppval) >= ICONV_CSNMAXLEN) {
    2176           0 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
    2177           0 :                                 RETURN_FALSE;
    2178             :                         }
    2179             : 
    2180          81 :                         if (Z_TYPE_PP(ppval) == IS_STRING && Z_STRLEN_PP(ppval) > 0) {
    2181          81 :                                 out_charset = Z_STRVAL_PP(ppval);
    2182             :                         }
    2183             :                 }
    2184             : 
    2185          82 :                 if (zend_hash_find(Z_ARRVAL_P(pref), "line-length", sizeof("line-length"), (void **)&ppval) == SUCCESS) {
    2186          81 :                         zval val, *pval = *ppval;
    2187             : 
    2188          81 :                         if (Z_TYPE_P(pval) != IS_LONG) {
    2189           0 :                                 val = *pval;
    2190             :                                 zval_copy_ctor(&val);
    2191           0 :                                 convert_to_long(&val);
    2192           0 :                                 pval = &val;
    2193             :                         }
    2194             : 
    2195          81 :                         line_len = Z_LVAL_P(pval);
    2196             : 
    2197          81 :                         if (pval == &val) {
    2198             :                                 zval_dtor(&val);
    2199             :                         }
    2200             :                 }
    2201             : 
    2202          82 :                 if (zend_hash_find(Z_ARRVAL_P(pref), "line-break-chars", sizeof("line-break-chars"), (void **)&ppval) == SUCCESS) {
    2203          81 :                         if (Z_TYPE_PP(ppval) != IS_STRING) {
    2204           1 :                                 tmp_zv = **ppval;
    2205             :                                 zval_copy_ctor(&tmp_zv);
    2206           1 :                                 convert_to_string(&tmp_zv);
    2207             : 
    2208           1 :                                 lfchars = Z_STRVAL(tmp_zv);
    2209             : 
    2210           1 :                                 tmp_zv_p = &tmp_zv;
    2211             :                         } else {
    2212          80 :                                 lfchars = Z_STRVAL_PP(ppval);
    2213             :                         }
    2214             :                 }
    2215             :         }
    2216             : 
    2217          83 :         err = _php_iconv_mime_encode(&retval, field_name, field_name_len,
    2218             :                 field_value, field_value_len, line_len, lfchars, scheme_id,
    2219             :                 out_charset, in_charset);
    2220          83 :         _php_iconv_show_error(err, out_charset, in_charset TSRMLS_CC);
    2221             : 
    2222          83 :         if (err == PHP_ICONV_ERR_SUCCESS) {
    2223          47 :                 if (retval.c != NULL) {
    2224          47 :                         RETVAL_STRINGL(retval.c, retval.len, 0);
    2225             :                 } else {
    2226           0 :                         RETVAL_EMPTY_STRING();
    2227             :                 }
    2228             :         } else {
    2229          36 :                 smart_str_free(&retval);
    2230          36 :                 RETVAL_FALSE;
    2231             :         }
    2232             : 
    2233          83 :         if (tmp_zv_p != NULL) {
    2234             :                 zval_dtor(tmp_zv_p);
    2235             :         }
    2236             : }
    2237             : /* }}} */
    2238             : 
    2239             : /* {{{ proto string iconv_mime_decode(string encoded_string [, int mode, string charset])
    2240             :    Decodes a mime header field */
    2241         103 : PHP_FUNCTION(iconv_mime_decode)
    2242             : {
    2243             :         char *encoded_str;
    2244             :         int encoded_str_len;
    2245         103 :         char *charset = ICONVG(internal_encoding);
    2246         103 :         int charset_len = 0;
    2247         103 :         long mode = 0;
    2248             : 
    2249         103 :         smart_str retval = {0};
    2250             : 
    2251             :         php_iconv_err_t err;
    2252             : 
    2253         103 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ls",
    2254             :                 &encoded_str, &encoded_str_len, &mode, &charset, &charset_len) == FAILURE) {
    2255             : 
    2256           9 :                 RETURN_FALSE;
    2257             :         }
    2258             : 
    2259          94 :         if (charset_len >= ICONV_CSNMAXLEN) {
    2260           1 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
    2261           1 :                 RETURN_FALSE;
    2262             :         }
    2263             : 
    2264          93 :         err = _php_iconv_mime_decode(&retval, encoded_str, encoded_str_len, charset, NULL, mode);
    2265          93 :         _php_iconv_show_error(err, charset, "???" TSRMLS_CC);
    2266             : 
    2267          93 :         if (err == PHP_ICONV_ERR_SUCCESS) {
    2268          75 :                 if (retval.c != NULL) {
    2269          67 :                         RETVAL_STRINGL(retval.c, retval.len, 0);
    2270             :                 } else {
    2271           8 :                         RETVAL_EMPTY_STRING();
    2272             :                 }
    2273             :         } else {
    2274          18 :                 smart_str_free(&retval);
    2275          18 :                 RETVAL_FALSE;
    2276             :         }
    2277             : }
    2278             : /* }}} */
    2279             : 
    2280             : /* {{{ proto array iconv_mime_decode_headers(string headers [, int mode, string charset])
    2281             :    Decodes multiple mime header fields */
    2282          76 : PHP_FUNCTION(iconv_mime_decode_headers)
    2283             : {
    2284             :         const char *encoded_str;
    2285             :         int encoded_str_len;
    2286          76 :         char *charset = ICONVG(internal_encoding);
    2287          76 :         int charset_len = 0;
    2288          76 :         long mode = 0;
    2289             : 
    2290          76 :         php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
    2291             : 
    2292          76 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ls",
    2293             :                 &encoded_str, &encoded_str_len, &mode, &charset, &charset_len) == FAILURE) {
    2294             : 
    2295          15 :                 RETURN_FALSE;
    2296             :         }
    2297             : 
    2298          61 :         if (charset_len >= ICONV_CSNMAXLEN) {
    2299           1 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
    2300           1 :                 RETURN_FALSE;
    2301             :         }
    2302             : 
    2303          60 :         array_init(return_value);
    2304             : 
    2305         370 :         while (encoded_str_len > 0) {
    2306         250 :                 smart_str decoded_header = {0};
    2307         250 :                 char *header_name = NULL;
    2308         250 :                 size_t header_name_len = 0;
    2309         250 :                 char *header_value = NULL;
    2310         250 :                 size_t header_value_len = 0;
    2311             :                 char *p, *limit;
    2312             :                 const char *next_pos;
    2313             : 
    2314         250 :                 if (PHP_ICONV_ERR_SUCCESS != (err = _php_iconv_mime_decode(&decoded_header, encoded_str, encoded_str_len, charset, &next_pos, mode))) {
    2315           0 :                         smart_str_free(&decoded_header);
    2316           0 :                         break;
    2317             :                 }
    2318             : 
    2319         250 :                 if (decoded_header.c == NULL) {
    2320           0 :                         break;
    2321             :                 }
    2322             : 
    2323         250 :                 limit = decoded_header.c + decoded_header.len;
    2324        1904 :                 for (p = decoded_header.c; p < limit; p++) {
    2325        1889 :                         if (*p == ':') {
    2326         235 :                                 *p = '\0';
    2327         235 :                                 header_name = decoded_header.c;
    2328         235 :                                 header_name_len = (p - decoded_header.c) + 1;
    2329             : 
    2330         705 :                                 while (++p < limit) {
    2331         470 :                                         if (*p != ' ' && *p != '\t') {
    2332         235 :                                                 break;
    2333             :                                         }
    2334             :                                 }
    2335             : 
    2336         235 :                                 header_value = p;
    2337         235 :                                 header_value_len = limit - p;
    2338             : 
    2339         235 :                                 break;
    2340             :                         }
    2341             :                 }
    2342             : 
    2343         250 :                 if (header_name != NULL) {
    2344             :                         zval **elem, *new_elem;
    2345             : 
    2346         235 :                         if (zend_hash_find(Z_ARRVAL_P(return_value), header_name, header_name_len, (void **)&elem) == SUCCESS) {
    2347          38 :                                 if (Z_TYPE_PP(elem) != IS_ARRAY) {
    2348          36 :                                         MAKE_STD_ZVAL(new_elem);
    2349          36 :                                         array_init(new_elem);
    2350             : 
    2351          36 :                                         Z_ADDREF_PP(elem);
    2352          36 :                                         add_next_index_zval(new_elem, *elem);
    2353             : 
    2354          36 :                                         zend_hash_update(Z_ARRVAL_P(return_value), header_name, header_name_len, (void *)&new_elem, sizeof(new_elem), NULL);
    2355             : 
    2356          36 :                                         elem = &new_elem;
    2357             :                                 }
    2358          38 :                                 add_next_index_stringl(*elem, header_value, header_value_len, 1);
    2359             :                         } else {
    2360         197 :                                 add_assoc_stringl_ex(return_value, header_name, header_name_len, header_value, header_value_len, 1);
    2361             :                         }
    2362             :                 }
    2363         250 :                 encoded_str_len -= next_pos - encoded_str;
    2364         250 :                 encoded_str = next_pos;
    2365             : 
    2366         250 :                 smart_str_free(&decoded_header);
    2367             :         }
    2368             : 
    2369          60 :         if (err != PHP_ICONV_ERR_SUCCESS) {
    2370           0 :                 _php_iconv_show_error(err, charset, "???" TSRMLS_CC);
    2371             :                 zval_dtor(return_value);
    2372           0 :                 RETVAL_FALSE;
    2373             :         }
    2374             : }
    2375             : /* }}} */
    2376             : 
    2377             : /* {{{ proto string iconv(string in_charset, string out_charset, string str)
    2378             :    Returns str converted to the out_charset character set */
    2379          49 : PHP_NAMED_FUNCTION(php_if_iconv)
    2380             : {
    2381             :         char *in_charset, *out_charset, *in_buffer, *out_buffer;
    2382             :         size_t out_len;
    2383          49 :         int in_charset_len = 0, out_charset_len = 0, in_buffer_len;
    2384             :         php_iconv_err_t err;
    2385             : 
    2386          49 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss",
    2387             :                 &in_charset, &in_charset_len, &out_charset, &out_charset_len, &in_buffer, &in_buffer_len) == FAILURE)
    2388           0 :                 return;
    2389             : 
    2390          49 :         if (in_charset_len >= ICONV_CSNMAXLEN || out_charset_len >= ICONV_CSNMAXLEN) {
    2391           2 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
    2392           2 :                 RETURN_FALSE;
    2393             :         }
    2394             : 
    2395          47 :         err = php_iconv_string(in_buffer, (size_t)in_buffer_len,
    2396             :                 &out_buffer, &out_len, out_charset, in_charset);
    2397          47 :         _php_iconv_show_error(err, out_charset, in_charset TSRMLS_CC);
    2398          90 :         if (err == PHP_ICONV_ERR_SUCCESS && out_buffer != NULL) {
    2399          43 :                 RETVAL_STRINGL(out_buffer, out_len, 0);
    2400             :         } else {
    2401           4 :                 if (out_buffer != NULL) {
    2402           1 :                         efree(out_buffer);
    2403             :                 }
    2404           4 :                 RETURN_FALSE;
    2405             :         }
    2406             : }
    2407             : /* }}} */
    2408             : 
    2409             : /* {{{ proto bool iconv_set_encoding(string type, string charset)
    2410             :    Sets internal encoding and output encoding for ob_iconv_handler() */
    2411         113 : PHP_FUNCTION(iconv_set_encoding)
    2412             : {
    2413             :         char *type, *charset;
    2414         113 :         int type_len, charset_len =0, retval;
    2415             : 
    2416         113 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &type, &type_len, &charset, &charset_len) == FAILURE)
    2417           4 :                 return;
    2418             : 
    2419         109 :         if (charset_len >= ICONV_CSNMAXLEN) {
    2420           3 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
    2421           3 :                 RETURN_FALSE;
    2422             :         }
    2423             : 
    2424         106 :         if(!strcasecmp("input_encoding", type)) {
    2425          26 :                 retval = zend_alter_ini_entry("iconv.input_encoding", sizeof("iconv.input_encoding"), charset, charset_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
    2426          80 :         } else if(!strcasecmp("output_encoding", type)) {
    2427          27 :                 retval = zend_alter_ini_entry("iconv.output_encoding", sizeof("iconv.output_encoding"), charset, charset_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
    2428          53 :         } else if(!strcasecmp("internal_encoding", type)) {
    2429          30 :                 retval = zend_alter_ini_entry("iconv.internal_encoding", sizeof("iconv.internal_encoding"), charset, charset_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
    2430             :         } else {
    2431          23 :                 RETURN_FALSE;
    2432             :         }
    2433             : 
    2434          83 :         if (retval == SUCCESS) {
    2435          83 :                 RETURN_TRUE;
    2436             :         } else {
    2437           0 :                 RETURN_FALSE;
    2438             :         }
    2439             : }
    2440             : /* }}} */
    2441             : 
    2442             : /* {{{ proto mixed iconv_get_encoding([string type])
    2443             :    Get internal encoding and output encoding for ob_iconv_handler() */
    2444         118 : PHP_FUNCTION(iconv_get_encoding)
    2445             : {
    2446         118 :         char *type = "all";
    2447         118 :         int type_len = sizeof("all")-1;
    2448             : 
    2449         118 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &type, &type_len) == FAILURE)
    2450           1 :                 return;
    2451             : 
    2452         117 :         if (!strcasecmp("all", type)) {
    2453           8 :                 array_init(return_value);
    2454           8 :                 add_assoc_string(return_value, "input_encoding",    ICONVG(input_encoding), 1);
    2455           8 :                 add_assoc_string(return_value, "output_encoding",   ICONVG(output_encoding), 1);
    2456           8 :                 add_assoc_string(return_value, "internal_encoding", ICONVG(internal_encoding), 1);
    2457         109 :         } else if (!strcasecmp("input_encoding", type)) {
    2458          28 :                 RETVAL_STRING(ICONVG(input_encoding), 1);
    2459          81 :         } else if (!strcasecmp("output_encoding", type)) {
    2460          28 :                 RETVAL_STRING(ICONVG(output_encoding), 1);
    2461          53 :         } else if (!strcasecmp("internal_encoding", type)) {
    2462          28 :                 RETVAL_STRING(ICONVG(internal_encoding), 1);
    2463             :         } else {
    2464          25 :                 RETURN_FALSE;
    2465             :         }
    2466             : 
    2467             : }
    2468             : /* }}} */
    2469             : 
    2470             : /* {{{ iconv stream filter */
    2471             : typedef struct _php_iconv_stream_filter {
    2472             :         iconv_t cd;
    2473             :         int persistent;
    2474             :         char *to_charset;
    2475             :         size_t to_charset_len;
    2476             :         char *from_charset;
    2477             :         size_t from_charset_len;
    2478             :         char stub[128];
    2479             :         size_t stub_len;
    2480             : } php_iconv_stream_filter;
    2481             : /* }}} iconv stream filter */
    2482             : 
    2483             : /* {{{ php_iconv_stream_filter_dtor */
    2484           6 : static void php_iconv_stream_filter_dtor(php_iconv_stream_filter *self)
    2485             : {
    2486           6 :         iconv_close(self->cd);
    2487           6 :         pefree(self->to_charset, self->persistent);
    2488           6 :         pefree(self->from_charset, self->persistent);
    2489           6 : }
    2490             : /* }}} */
    2491             : 
    2492             : /* {{{ php_iconv_stream_filter_ctor() */
    2493           6 : static php_iconv_err_t php_iconv_stream_filter_ctor(php_iconv_stream_filter *self,
    2494             :                 const char *to_charset, size_t to_charset_len,
    2495             :                 const char *from_charset, size_t from_charset_len, int persistent)
    2496             : {
    2497           6 :         if (NULL == (self->to_charset = pemalloc(to_charset_len + 1, persistent))) {
    2498           0 :                 return PHP_ICONV_ERR_ALLOC;
    2499             :         }
    2500           6 :         self->to_charset_len = to_charset_len;
    2501           6 :         if (NULL == (self->from_charset = pemalloc(from_charset_len + 1, persistent))) {
    2502           0 :                 pefree(self->to_charset, persistent);
    2503           0 :                 return PHP_ICONV_ERR_ALLOC;
    2504             :         }
    2505           6 :         self->from_charset_len = from_charset_len;
    2506             : 
    2507           6 :         memcpy(self->to_charset, to_charset, to_charset_len);
    2508           6 :         self->to_charset[to_charset_len] = '\0';
    2509           6 :         memcpy(self->from_charset, from_charset, from_charset_len);
    2510           6 :         self->from_charset[from_charset_len] = '\0';
    2511             : 
    2512           6 :         if ((iconv_t)-1 == (self->cd = iconv_open(self->to_charset, self->from_charset))) {
    2513           0 :                 pefree(self->from_charset, persistent);
    2514           0 :                 pefree(self->to_charset, persistent);
    2515           0 :                 return PHP_ICONV_ERR_UNKNOWN;
    2516             :         }
    2517           6 :         self->persistent = persistent;
    2518           6 :         self->stub_len = 0;
    2519           6 :         return PHP_ICONV_ERR_SUCCESS;
    2520             : }
    2521             : /* }}} */
    2522             : 
    2523             : /* {{{ php_iconv_stream_filter_append_bucket */
    2524           6 : static int php_iconv_stream_filter_append_bucket(
    2525             :                 php_iconv_stream_filter *self,
    2526             :                 php_stream *stream, php_stream_filter *filter,
    2527             :                 php_stream_bucket_brigade *buckets_out,
    2528             :                 const char *ps, size_t buf_len, size_t *consumed,
    2529             :                 int persistent TSRMLS_DC)
    2530             : {
    2531             :         php_stream_bucket *new_bucket;
    2532           6 :         char *out_buf = NULL;
    2533             :         size_t out_buf_size;
    2534             :         char *pd, *pt;
    2535             :         size_t ocnt, prev_ocnt, icnt, tcnt;
    2536             :         size_t initial_out_buf_size;
    2537             : 
    2538           6 :         if (ps == NULL) {
    2539           0 :                 initial_out_buf_size = 64;
    2540           0 :                 icnt = 1;
    2541             :         } else {
    2542           6 :                 initial_out_buf_size = buf_len;
    2543           6 :                 icnt = buf_len;
    2544             :         }
    2545             : 
    2546           6 :         out_buf_size = ocnt = prev_ocnt = initial_out_buf_size;
    2547           6 :         if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) {
    2548           0 :                 return FAILURE;
    2549             :         }
    2550             : 
    2551           6 :         pd = out_buf;
    2552             : 
    2553           6 :         if (self->stub_len > 0) {
    2554           0 :                 pt = self->stub;
    2555           0 :                 tcnt = self->stub_len;
    2556             : 
    2557           0 :                 while (tcnt > 0) {
    2558           0 :                         if (iconv(self->cd, &pt, &tcnt, &pd, &ocnt) == (size_t)-1) {
    2559             : #if ICONV_SUPPORTS_ERRNO
    2560           0 :                                 switch (errno) {
    2561             :                                         case EILSEQ:
    2562           0 :                                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): invalid multibyte sequence", self->from_charset, self->to_charset);
    2563           0 :                                                 goto out_failure;
    2564             : 
    2565             :                                         case EINVAL:
    2566           0 :                                                 if (ps != NULL) {
    2567           0 :                                                         if (icnt > 0) {
    2568           0 :                                                                 if (self->stub_len >= sizeof(self->stub)) {
    2569           0 :                                                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): insufficient buffer", self->from_charset, self->to_charset);
    2570           0 :                                                                         goto out_failure;
    2571             :                                                                 }
    2572           0 :                                                                 self->stub[self->stub_len++] = *(ps++);
    2573           0 :                                                                 icnt--;
    2574           0 :                                                                 pt = self->stub;
    2575           0 :                                                                 tcnt = self->stub_len;
    2576             :                                                         } else {
    2577           0 :                                                                 tcnt = 0;
    2578           0 :                                                                 break;
    2579             :                                                         }
    2580             :                                                 }
    2581           0 :                                                 break;
    2582             : 
    2583             :                                         case E2BIG: {
    2584             :                                                 char *new_out_buf;
    2585             :                                                 size_t new_out_buf_size;
    2586             : 
    2587           0 :                                                 new_out_buf_size = out_buf_size << 1;
    2588             : 
    2589           0 :                                                 if (new_out_buf_size < out_buf_size) {
    2590             :                                                         /* whoa! no bigger buckets are sold anywhere... */
    2591           0 :                                                         if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
    2592           0 :                                                                 goto out_failure;
    2593             :                                                         }
    2594             : 
    2595           0 :                                                         php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
    2596             : 
    2597           0 :                                                         out_buf_size = ocnt = initial_out_buf_size;
    2598           0 :                                                         if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) {
    2599           0 :                                                                 return FAILURE;
    2600             :                                                         }
    2601           0 :                                                         pd = out_buf;
    2602             :                                                 } else {
    2603           0 :                                                         if (NULL == (new_out_buf = perealloc(out_buf, new_out_buf_size, persistent))) {
    2604           0 :                                                                 if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
    2605           0 :                                                                         goto out_failure;
    2606             :                                                                 }
    2607             : 
    2608           0 :                                                                 php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
    2609           0 :                                                                 return FAILURE;
    2610             :                                                         }
    2611           0 :                                                         pd = new_out_buf + (pd - out_buf);
    2612           0 :                                                         ocnt += (new_out_buf_size - out_buf_size);
    2613           0 :                                                         out_buf = new_out_buf;
    2614           0 :                                                         out_buf_size = new_out_buf_size;
    2615             :                                                 }
    2616           0 :                                         } break;
    2617             : 
    2618             :                                         default:
    2619           0 :                                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset);
    2620           0 :                                                 goto out_failure;
    2621             :                                 }
    2622             : #else
    2623             :                                 if (ocnt == prev_ocnt) {
    2624             :                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset);
    2625             :                                         goto out_failure;
    2626             :                                 }
    2627             : #endif
    2628             :                         }
    2629           0 :                         prev_ocnt = ocnt;
    2630             :                 }
    2631           0 :                 memmove(self->stub, pt, tcnt);
    2632           0 :                 self->stub_len = tcnt;
    2633             :         }
    2634             : 
    2635          19 :         while (icnt > 0) {
    2636          14 :                 if ((ps == NULL ? iconv(self->cd, NULL, NULL, &pd, &ocnt):
    2637           7 :                                         iconv(self->cd, (char **)&ps, &icnt, &pd, &ocnt)) == (size_t)-1) {
    2638             : #if ICONV_SUPPORTS_ERRNO
    2639           1 :                         switch (errno) {
    2640             :                                 case EILSEQ:
    2641           0 :                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): invalid multibyte sequence", self->from_charset, self->to_charset);
    2642           0 :                                         goto out_failure;
    2643             : 
    2644             :                                 case EINVAL:
    2645           0 :                                         if (ps != NULL) {
    2646           0 :                                                 if (icnt > sizeof(self->stub)) {
    2647           0 :                                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): insufficient buffer", self->from_charset, self->to_charset);
    2648           0 :                                                         goto out_failure;
    2649             :                                                 }
    2650           0 :                                                 memcpy(self->stub, ps, icnt);
    2651           0 :                                                 self->stub_len = icnt;
    2652           0 :                                                 ps += icnt;
    2653           0 :                                                 icnt = 0;
    2654             :                                         } else {
    2655           0 :                                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unexpected octet values", self->from_charset, self->to_charset);
    2656           0 :                                                 goto out_failure;
    2657             :                                         }
    2658           0 :                                         break;
    2659             : 
    2660             :                                 case E2BIG: {
    2661             :                                         char *new_out_buf;
    2662             :                                         size_t new_out_buf_size;
    2663             : 
    2664           1 :                                         new_out_buf_size = out_buf_size << 1;
    2665             : 
    2666           1 :                                         if (new_out_buf_size < out_buf_size) {
    2667             :                                                 /* whoa! no bigger buckets are sold anywhere... */
    2668           0 :                                                 if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
    2669           0 :                                                         goto out_failure;
    2670             :                                                 }
    2671             : 
    2672           0 :                                                 php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
    2673             : 
    2674           0 :                                                 out_buf_size = ocnt = initial_out_buf_size;
    2675           0 :                                                 if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) {
    2676           0 :                                                         return FAILURE;
    2677             :                                                 }
    2678           0 :                                                 pd = out_buf;
    2679             :                                         } else {
    2680           1 :                                                 if (NULL == (new_out_buf = perealloc(out_buf, new_out_buf_size, persistent))) {
    2681           0 :                                                         if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
    2682           0 :                                                                 goto out_failure;
    2683             :                                                         }
    2684             : 
    2685           0 :                                                         php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
    2686           0 :                                                         return FAILURE;
    2687             :                                                 }
    2688           1 :                                                 pd = new_out_buf + (pd - out_buf);
    2689           1 :                                                 ocnt += (new_out_buf_size - out_buf_size);
    2690           1 :                                                 out_buf = new_out_buf;
    2691           1 :                                                 out_buf_size = new_out_buf_size;
    2692             :                                         }
    2693           1 :                                 } break;
    2694             : 
    2695             :                                 default:
    2696           0 :                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset);
    2697           0 :                                         goto out_failure;
    2698             :                         }
    2699             : #else
    2700             :                         if (ocnt == prev_ocnt) {
    2701             :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset);
    2702             :                                 goto out_failure;
    2703             :                         }
    2704             : #endif
    2705             :                 } else {
    2706           6 :                         if (ps == NULL) {
    2707           0 :                                 break;
    2708             :                         }
    2709             :                 }
    2710           7 :                 prev_ocnt = ocnt;
    2711             :         }
    2712             : 
    2713           6 :         if (out_buf_size - ocnt > 0) {
    2714           6 :                 if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
    2715           0 :                         goto out_failure;
    2716             :                 }
    2717           6 :                 php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
    2718             :         } else {
    2719           0 :                 pefree(out_buf, persistent);
    2720             :         }
    2721           6 :         *consumed += buf_len - icnt;
    2722             : 
    2723           6 :         return SUCCESS;
    2724             : 
    2725             : out_failure:
    2726           0 :         pefree(out_buf, persistent);
    2727           0 :         return FAILURE;
    2728             : }
    2729             : /* }}} php_iconv_stream_filter_append_bucket */
    2730             : 
    2731             : /* {{{ php_iconv_stream_filter_do_filter */
    2732           6 : static php_stream_filter_status_t php_iconv_stream_filter_do_filter(
    2733             :                 php_stream *stream, php_stream_filter *filter,
    2734             :                 php_stream_bucket_brigade *buckets_in,
    2735             :                 php_stream_bucket_brigade *buckets_out,
    2736             :                 size_t *bytes_consumed, int flags TSRMLS_DC)
    2737             : {
    2738           6 :         php_stream_bucket *bucket = NULL;
    2739           6 :         size_t consumed = 0;
    2740           6 :         php_iconv_stream_filter *self = (php_iconv_stream_filter *)filter->abstract;
    2741             : 
    2742          18 :         while (buckets_in->head != NULL) {
    2743           6 :                 bucket = buckets_in->head;
    2744             : 
    2745           6 :                 php_stream_bucket_unlink(bucket TSRMLS_CC);
    2746             : 
    2747          12 :                 if (php_iconv_stream_filter_append_bucket(self, stream, filter,
    2748           6 :                                 buckets_out, bucket->buf, bucket->buflen, &consumed,
    2749             :                                 php_stream_is_persistent(stream) TSRMLS_CC) != SUCCESS) {
    2750           0 :                         goto out_failure;
    2751             :                 }
    2752             : 
    2753           6 :                 php_stream_bucket_delref(bucket TSRMLS_CC);
    2754             :         }
    2755             : 
    2756           6 :         if (flags != PSFS_FLAG_NORMAL) {
    2757           0 :                 if (php_iconv_stream_filter_append_bucket(self, stream, filter,
    2758             :                                 buckets_out, NULL, 0, &consumed,
    2759             :                                 php_stream_is_persistent(stream) TSRMLS_CC) != SUCCESS) {
    2760           0 :                         goto out_failure;
    2761             :                 }
    2762             :         }
    2763             : 
    2764           6 :         if (bytes_consumed != NULL) {
    2765           0 :                 *bytes_consumed = consumed;
    2766             :         }
    2767             : 
    2768           6 :         return PSFS_PASS_ON;
    2769             : 
    2770             : out_failure:
    2771           0 :         if (bucket != NULL) {
    2772           0 :                 php_stream_bucket_delref(bucket TSRMLS_CC);
    2773             :         }
    2774           0 :         return PSFS_ERR_FATAL;
    2775             : }
    2776             : /* }}} */
    2777             : 
    2778             : /* {{{ php_iconv_stream_filter_cleanup */
    2779           6 : static void php_iconv_stream_filter_cleanup(php_stream_filter *filter TSRMLS_DC)
    2780             : {
    2781           6 :         php_iconv_stream_filter_dtor((php_iconv_stream_filter *)filter->abstract);
    2782           6 :         pefree(filter->abstract, ((php_iconv_stream_filter *)filter->abstract)->persistent);
    2783           6 : }
    2784             : /* }}} */
    2785             : 
    2786             : static php_stream_filter_ops php_iconv_stream_filter_ops = {
    2787             :         php_iconv_stream_filter_do_filter,
    2788             :         php_iconv_stream_filter_cleanup,
    2789             :         "convert.iconv.*"
    2790             : };
    2791             : 
    2792             : /* {{{ php_iconv_stream_filter_create */
    2793           7 : static php_stream_filter *php_iconv_stream_filter_factory_create(const char *name, zval *params, int persistent TSRMLS_DC)
    2794             : {
    2795           7 :         php_stream_filter *retval = NULL;
    2796             :         php_iconv_stream_filter *inst;
    2797           7 :         char *from_charset = NULL, *to_charset = NULL;
    2798             :         size_t from_charset_len, to_charset_len;
    2799             : 
    2800           7 :         if ((from_charset = strchr(name, '.')) == NULL) {
    2801           0 :                 return NULL;
    2802             :         }
    2803           7 :         ++from_charset;
    2804           7 :         if ((from_charset = strchr(from_charset, '.')) == NULL) {
    2805           0 :                 return NULL;
    2806             :         }
    2807           7 :         ++from_charset;
    2808           7 :         if ((to_charset = strpbrk(from_charset, "/.")) == NULL) {
    2809           1 :                 return NULL;
    2810             :         }
    2811           6 :         from_charset_len = to_charset - from_charset;
    2812           6 :         ++to_charset;
    2813           6 :         to_charset_len = strlen(to_charset);
    2814             : 
    2815           6 :         if (from_charset_len >= ICONV_CSNMAXLEN || to_charset_len >= ICONV_CSNMAXLEN) {
    2816           0 :                 return NULL;
    2817             :         }
    2818             : 
    2819           6 :         if (NULL == (inst = pemalloc(sizeof(php_iconv_stream_filter), persistent))) {
    2820           0 :                 return NULL;
    2821             :         }
    2822             : 
    2823           6 :         if (php_iconv_stream_filter_ctor(inst, to_charset, to_charset_len, from_charset, from_charset_len, persistent) != PHP_ICONV_ERR_SUCCESS) {
    2824           0 :                 pefree(inst, persistent);
    2825           0 :                 return NULL;
    2826             :         }
    2827             : 
    2828           6 :         if (NULL == (retval = php_stream_filter_alloc(&php_iconv_stream_filter_ops, inst, persistent))) {
    2829           0 :                 php_iconv_stream_filter_dtor(inst);
    2830           0 :                 pefree(inst, persistent);
    2831             :         }
    2832             : 
    2833           6 :         return retval;
    2834             : }
    2835             : /* }}} */
    2836             : 
    2837             : /* {{{ php_iconv_stream_register_factory */
    2838       20225 : static php_iconv_err_t php_iconv_stream_filter_register_factory(TSRMLS_D)
    2839             : {
    2840             :         static php_stream_filter_factory filter_factory = {
    2841             :                 php_iconv_stream_filter_factory_create
    2842             :         };
    2843             : 
    2844       20225 :         if (FAILURE == php_stream_filter_register_factory(
    2845             :                                 php_iconv_stream_filter_ops.label,
    2846             :                                 &filter_factory TSRMLS_CC)) {
    2847           0 :                 return PHP_ICONV_ERR_UNKNOWN;
    2848             :         }
    2849       20225 :         return PHP_ICONV_ERR_SUCCESS;
    2850             : }
    2851             : /* }}} */
    2852             : 
    2853             : /* {{{ php_iconv_stream_unregister_factory */
    2854       20261 : static php_iconv_err_t php_iconv_stream_filter_unregister_factory(TSRMLS_D)
    2855             : {
    2856       20261 :         if (FAILURE == php_stream_filter_unregister_factory(
    2857             :                                 php_iconv_stream_filter_ops.label TSRMLS_CC)) {
    2858           0 :                 return PHP_ICONV_ERR_UNKNOWN;
    2859             :         }
    2860       20261 :         return PHP_ICONV_ERR_SUCCESS;
    2861             : }
    2862             : /* }}} */
    2863             : /* }}} */
    2864             : #endif
    2865             : 
    2866             : /*
    2867             :  * Local variables:
    2868             :  * tab-width: 4
    2869             :  * c-basic-offset: 4
    2870             :  * End:
    2871             :  * vim600: sw=4 ts=4 fdm=marker
    2872             :  * vim<600: sw=4 ts=4
    2873             :  */

Generated by: LCOV version 1.10

Generated at Thu, 16 Oct 2014 05:27:02 +0000 (6 days ago)

Copyright © 2005-2014 The PHP Group
All rights reserved.