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

Reply via email to