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: 886 1204 73.6 %
Date: 2014-07-27 Functions: 33 33 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10

Generated at Sun, 27 Jul 2014 12:58:29 +0000 (5 days ago)

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