moriyoshi Thu Dec 4 18:31:32 2003 EDT Modified files: /php-src/ext/iconv iconv.c Log: Add iconv stream filter. # a quick synopsis: # # <?php # stream_filter_append(STDIN, 'convert.iconv.UTF-8/ISO-8859-15'); # # fpassthru(STDIN); # ?> #
Index: php-src/ext/iconv/iconv.c diff -u php-src/ext/iconv/iconv.c:1.103 php-src/ext/iconv/iconv.c:1.104 --- php-src/ext/iconv/iconv.c:1.103 Tue Dec 2 02:36:42 2003 +++ php-src/ext/iconv/iconv.c Thu Dec 4 18:31:31 2003 @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: iconv.c,v 1.103 2003/12/02 07:36:42 moriyoshi Exp $ */ +/* $Id: iconv.c,v 1.104 2003/12/04 23:31:31 moriyoshi Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -115,7 +115,8 @@ PHP_ICONV_ERR_ILLEGAL_SEQ = 4, PHP_ICONV_ERR_ILLEGAL_CHAR = 5, PHP_ICONV_ERR_UNKNOWN = 6, - PHP_ICONV_ERR_MALFORMED = 7 + PHP_ICONV_ERR_MALFORMED = 7, + PHP_ICONV_ERR_ALLOC = 8 } php_iconv_err_t; /* }}} */ @@ -146,6 +147,9 @@ 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); 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); + +static php_iconv_err_t php_iconv_stream_filter_register_factory(); +static php_iconv_err_t php_iconv_stream_filter_unregister_factory(); /* }}} */ /* {{{ static globals */ @@ -203,6 +207,10 @@ REGISTER_LONG_CONSTANT("ICONV_MIME_DECODE_STRICT", PHP_ICONV_MIME_DECODE_STRICT, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("ICONV_MIME_DECODE_CONTINUE_ON_ERROR", PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR, CONST_CS | CONST_PERSISTENT); + if (php_iconv_stream_filter_register_factory() != PHP_ICONV_ERR_SUCCESS) { + return FAILURE; + } + return SUCCESS; } /* }}} */ @@ -210,6 +218,7 @@ /* {{{ PHP_MSHUTDOWN_FUNCTION */ PHP_MSHUTDOWN_FUNCTION(miconv) { + php_iconv_stream_filter_unregister_factory(); UNREGISTER_INI_ENTRIES(); return SUCCESS; } @@ -2212,6 +2221,328 @@ } /* }}} */ +/* {{{ iconv stream filter */ +typedef struct _php_iconv_stream_filter { + iconv_t cd; + int persistent; + char *to_charset; + size_t to_charset_len; + char *from_charset; + size_t from_charset_len; +} php_iconv_stream_filter; + +/* {{{ php_iconv_stream_filter_dtor */ +static void php_iconv_stream_filter_dtor(php_iconv_stream_filter *self) +{ + pefree(self->to_charset, self->persistent); + pefree(self->from_charset, self->persistent); +} +/* }}} */ + +/* {{{ php_iconv_stream_filter_ctor() */ +static php_iconv_err_t php_iconv_stream_filter_ctor(php_iconv_stream_filter *self, + const char *to_charset, size_t to_charset_len, + const char *from_charset, size_t from_charset_len, int persistent) +{ + if (NULL == (self->to_charset = pemalloc(to_charset_len + 1, persistent))) { + return PHP_ICONV_ERR_ALLOC; + } + self->to_charset_len = to_charset_len; + if (NULL == (self->from_charset = pemalloc(from_charset_len + 1, persistent))) { + pefree(self->to_charset, persistent); + return PHP_ICONV_ERR_ALLOC; + } + self->from_charset_len = from_charset_len; + + memcpy(self->to_charset, to_charset, to_charset_len); + self->to_charset[to_charset_len] = '\0'; + memcpy(self->from_charset, from_charset, from_charset_len); + self->from_charset[from_charset_len] = '\0'; + + if ((iconv_t)-1 == (self->cd = iconv_open(self->to_charset, self->from_charset))) { + pefree(self->from_charset, persistent); + pefree(self->to_charset, persistent); + return PHP_ICONV_ERR_UNKNOWN; + } + self->persistent = persistent; + + return PHP_ICONV_ERR_SUCCESS; +} +/* }}} */ + +/* {{{ php_iconv_stream_filter_do_filter */ +static php_stream_filter_status_t php_iconv_stream_filter_do_filter( + php_stream *stream, php_stream_filter *filter, + php_stream_bucket_brigade *buckets_in, + php_stream_bucket_brigade *buckets_out, + size_t *bytes_consumed, int flags TSRMLS_DC) +{ + php_stream_bucket *bucket = NULL, *new_bucket; + size_t consumed = 0; + php_iconv_stream_filter *self = (php_iconv_stream_filter *)filter->abstract; + char *out_buf = NULL; + size_t out_buf_size; + char *pd; + size_t ocnt, prev_ocnt; + + if (flags != PSFS_FLAG_NORMAL) { + /* flush operation */ + + out_buf_size = 64; + out_buf = pemalloc(out_buf_size, self->persistent); + ocnt = prev_ocnt = out_buf_size; + pd = out_buf; + + /* trying hard to reduce the number of buckets to hand to the + * next filter */ + + for (;;) { + if (iconv(self->cd, NULL, NULL, &pd, &ocnt) == (size_t)-1) { +#if ICONV_SUPPORTS_ERRNO + switch (errno) { + case EILSEQ: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): invalid multibyte sequence", self->from_charset, self->to_charset); + goto out_failure; + + case EINVAL: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unexpected octet values", self->from_charset, self->to_charset); + goto out_failure; + + case E2BIG: + break; + + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset); + goto out_failure; + } +#else + if (ocnt == prev_ocnt) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset); + goto out_failure; + } +#endif + { + char *new_out_buf; + size_t new_out_buf_size; + + new_out_buf_size = out_buf_size << 1; + + if (new_out_buf_size < out_buf_size) { + /* whoa! no bigger buckets are sold anywhere... */ + new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, self->persistent TSRMLS_CC); + + php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC); + + out_buf_size = bucket->buflen; + out_buf = pemalloc(out_buf_size, self->persistent); + ocnt = out_buf_size; + pd = out_buf; + } else { + new_out_buf = perealloc(out_buf, new_out_buf_size, self->persistent); + pd = new_out_buf + (pd - out_buf); + ocnt += (new_out_buf_size - out_buf_size); + out_buf = new_out_buf; + out_buf_size = new_out_buf_size; + } + } + } else { + break; + } + + prev_ocnt = ocnt; + } + /* give output bucket to next in chain */ + if (out_buf_size - ocnt > 0) { + new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, self->persistent TSRMLS_CC); + php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC); + } else { + pefree(out_buf, self->persistent); + } + } else { + while (buckets_in->head != NULL) { + const char *ps; + size_t icnt, prev_icnt; + + bucket = buckets_in->head; + + php_stream_bucket_unlink(bucket TSRMLS_CC); + icnt = prev_icnt = bucket->buflen; + ps = bucket->buf; + + out_buf_size = bucket->buflen; + out_buf = pemalloc(out_buf_size, self->persistent); + ocnt = out_buf_size; + pd = out_buf; + + /* trying hard to reduce the number of buckets to hand to the + * next filter */ + + while (icnt > 0) { + if (iconv(self->cd, &ps, &icnt, &pd, &ocnt) == (size_t)-1) { +#if ICONV_SUPPORTS_ERRNO + switch (errno) { + case EILSEQ: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): invalid multibyte sequence", self->from_charset, self->to_charset); + goto out_failure; + + case EINVAL: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unexpected octet values", self->from_charset, self->to_charset); + goto out_failure; + + case E2BIG: + break; + + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset); + goto out_failure; + } +#else + if (prev_icnt == icnt) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset); + goto out_failure; + } +#endif + { + char *new_out_buf; + size_t new_out_buf_size; + + new_out_buf_size = out_buf_size << 1; + + if (new_out_buf_size < out_buf_size) { + /* whoa! no bigger buckets are sold anywhere... */ + new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, self->persistent TSRMLS_CC); + + php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC); + + out_buf_size = bucket->buflen; + out_buf = pemalloc(out_buf_size, self->persistent); + ocnt = out_buf_size; + pd = out_buf; + } else { + new_out_buf = perealloc(out_buf, new_out_buf_size, self->persistent); + pd = new_out_buf + (pd - out_buf); + ocnt += (new_out_buf_size - out_buf_size); + out_buf = new_out_buf; + out_buf_size = new_out_buf_size; + } + } + } + prev_icnt = icnt; + } + + /* update consumed by the number of bytes just used */ + consumed = bucket->buflen - icnt; + + /* give output bucket to next in chain */ + if (out_buf_size - ocnt > 0) { + new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, self->persistent TSRMLS_CC); + php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC); + } else { + pefree(out_buf, self->persistent); + } + + php_stream_bucket_delref(bucket TSRMLS_CC); + } + } + + if (bytes_consumed) { + *bytes_consumed = consumed; + } + + return PSFS_PASS_ON; + +out_failure: + if (out_buf != NULL) { + pefree(out_buf, self->persistent); + } + if (bucket != NULL) { + php_stream_bucket_delref(bucket TSRMLS_CC); + } + return PSFS_ERR_FATAL; +} +/* }}} */ + +/* {{{ php_iconv_stream_filter_cleanup */ +static void php_iconv_stream_filter_cleanup(php_stream_filter *filter TSRMLS_DC) +{ + php_iconv_stream_filter_dtor((php_iconv_stream_filter *)filter->abstract); + pefree(filter->abstract, ((php_iconv_stream_filter *)filter->abstract)->persistent); +} +/* }}} */ + +static php_stream_filter_ops php_iconv_stream_filter_ops = { + php_iconv_stream_filter_do_filter, + php_iconv_stream_filter_cleanup, + "convert.iconv.*" +}; + +/* {{{ php_iconv_stream_filter_create */ +static php_stream_filter *php_iconv_stream_filter_factory_create(const char *name, zval *params, int persistent TSRMLS_DC) +{ + php_stream_filter *retval = NULL; + php_iconv_stream_filter *inst; + char *from_charset = NULL, *to_charset = NULL; + size_t from_charset_len, to_charset_len; + + if ((from_charset = strchr(name, '.')) == NULL) { + return NULL; + } + ++from_charset; + if ((from_charset = strchr(from_charset, '.')) == NULL) { + return NULL; + } + ++from_charset; + if ((to_charset = strchr(from_charset, '/')) == NULL) { + return NULL; + } + from_charset_len = to_charset - from_charset; + ++to_charset; + to_charset_len = strlen(to_charset); + + if (NULL == (inst = pemalloc(sizeof(php_iconv_stream_filter), persistent))) { + return NULL; + } + + if (php_iconv_stream_filter_ctor(inst, to_charset, to_charset_len, from_charset, from_charset_len, persistent) != PHP_ICONV_ERR_SUCCESS) { + pefree(inst, persistent); + return NULL; + } + + if (NULL == (retval = php_stream_filter_alloc(&php_iconv_stream_filter_ops, inst, persistent))) { + php_iconv_stream_filter_dtor(inst); + pefree(inst, persistent); + } + + return retval; +} +/* }}} */ + +/* {{{ php_iconv_stream_register_factory */ +static php_iconv_err_t php_iconv_stream_filter_register_factory() +{ + static php_stream_filter_factory filter_factory = { + php_iconv_stream_filter_factory_create + }; + + if (FAILURE == php_stream_filter_register_factory( + php_iconv_stream_filter_ops.label, + &filter_factory TSRMLS_CC)) { + return PHP_ICONV_ERR_UNKNOWN; + } + return PHP_ICONV_ERR_SUCCESS; +} +/* }}} */ + +/* {{{ php_iconv_stream_unregister_factory */ +static php_iconv_err_t php_iconv_stream_filter_unregister_factory() +{ + if (FAILURE == php_stream_filter_unregister_factory( + php_iconv_stream_filter_ops.label TSRMLS_CC)) { + return PHP_ICONV_ERR_UNKNOWN; + } + return PHP_ICONV_ERR_SUCCESS; +} +/* }}} */ #endif /*
-- PHP CVS Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php