moriyoshi               Sun Dec  7 19:38:01 2003 EDT

  Modified files:              
    /php-src/ext/iconv  iconv.c 
  Log:
  Fix iconv stream filter to properly handle multibyte characters that
  span at the alignment of a hunk.
  
  
Index: php-src/ext/iconv/iconv.c
diff -u php-src/ext/iconv/iconv.c:1.108 php-src/ext/iconv/iconv.c:1.109
--- php-src/ext/iconv/iconv.c:1.108     Sat Dec  6 06:10:13 2003
+++ php-src/ext/iconv/iconv.c   Sun Dec  7 19:38:00 2003
@@ -18,7 +18,7 @@
    +----------------------------------------------------------------------+
  */
 
-/* $Id: iconv.c,v 1.108 2003/12/06 11:10:13 moriyoshi Exp $ */
+/* $Id: iconv.c,v 1.109 2003/12/08 00:38:00 moriyoshi Exp $ */
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
@@ -2234,6 +2234,8 @@
        size_t to_charset_len;
        char *from_charset;
        size_t from_charset_len;
+       char stub[128];
+       size_t stub_len;
 } php_iconv_stream_filter;
 
 /* {{{ php_iconv_stream_filter_dtor */
@@ -2271,39 +2273,43 @@
                return PHP_ICONV_ERR_UNKNOWN;
        }
        self->persistent = persistent;
-
+       self->stub_len = 0;
        return PHP_ICONV_ERR_SUCCESS;
 }
 /* }}} */
 
-/* {{{ php_iconv_stream_filter_do_filter */
-static php_stream_filter_status_t php_iconv_stream_filter_do_filter(
+/* {{{ php_iconv_stream_filter_append_bucket */
+static size_t php_iconv_stream_filter_append_bucket(
+               php_iconv_stream_filter *self,
                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)
+               const char *ps, size_t buf_len, size_t *consumed,
+               int persistent TSRMLS_DC)
 {
-       php_stream_bucket *bucket = NULL, *new_bucket;
-       size_t consumed = 0;
-       php_iconv_stream_filter *self = (php_iconv_stream_filter *)filter->abstract;
+       php_stream_bucket *new_bucket;
        char *out_buf = NULL;
        size_t out_buf_size;
-       char *pd;
-       size_t ocnt, prev_ocnt;
-
-       if (flags != PSFS_FLAG_NORMAL) {
-               /* flush operation */
+       char *pd, *pt;
+       size_t ocnt, prev_ocnt, icnt, tcnt;
+       size_t initial_out_buf_size;
+       
+       if (ps == NULL) {
+               initial_out_buf_size = 64;
+               icnt = 1;
+       } else {
+               initial_out_buf_size = buf_len;
+               icnt = buf_len;
+       }
 
-               out_buf_size = 64;
-               out_buf = pemalloc(out_buf_size, self->persistent);
-               ocnt = prev_ocnt = out_buf_size;
-               pd = out_buf;
+       out_buf_size = ocnt = prev_ocnt = initial_out_buf_size; 
+       out_buf = pd = pemalloc(out_buf_size, self->persistent);
 
-               /* trying hard to reduce the number of buckets to hand to the
-                * next filter */ 
+       if (self->stub_len > 0) {
+               pt = self->stub;
+               tcnt = self->stub_len;
 
-               for (;;) {
-                       if (iconv(self->cd, NULL, NULL, &pd, &ocnt) == (size_t)-1) {
+               while (tcnt > 0) {
+                       if (iconv(self->cd, &pt, &tcnt, &pd, &ocnt) == (size_t)-1) {
 #if ICONV_SUPPORTS_ERRNO
                                switch (errno) {
                                        case EILSEQ:
@@ -2311,12 +2317,46 @@
                                                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:
+                                               if (ps != NULL) {
+                                                       if (icnt > 0) {
+                                                               if (self->stub_len >= 
sizeof(self->stub)) {
+                                                                       
php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): 
insufficient buffer", self->from_charset, self->to_charset);
+                                                                       goto 
out_failure;
+                                                               }
+                                                               
self->stub[self->stub_len++] = *(ps++);
+                                                               icnt--;
+                                                               pt = self->stub;
+                                                               tcnt = self->stub_len;
+                                                       } else {
+                                                               tcnt = 0;
+                                                               break;
+                                                       }
+                                               }
                                                break;
 
+                                       case E2BIG: {
+                                               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 = ocnt = 
initial_out_buf_size;
+                                                       out_buf = pd = 
pemalloc(out_buf_size, self->persistent);
+                                               } 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;
+                                               }
+                                       } 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;
@@ -2327,7 +2367,39 @@
                                        goto out_failure;
                                }
 #endif
-                               {
+                       }
+                       prev_ocnt = ocnt;
+               }
+               memmove(self->stub, pt, tcnt);
+               self->stub_len = tcnt;
+       }
+
+       while (icnt > 0) {
+               if ((ps == NULL ? iconv(self->cd, NULL, NULL, &pd, &ocnt):
+                                       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:
+                                       if (ps != NULL) {
+                                               if (icnt > sizeof(self->stub)) {
+                                                       php_error_docref(NULL 
TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): insufficient buffer", 
self->from_charset, self->to_charset);
+                                                       goto out_failure;
+                                               }
+                                               memcpy(self->stub, ps, icnt);
+                                               self->stub_len = icnt;
+                                               ps += icnt;
+                                               icnt = 0;
+                                       } else {
+                                               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;
+                                       }
+                                       break;
+
+                               case E2BIG: {
                                        char *new_out_buf;
                                        size_t new_out_buf_size;
 
@@ -2339,10 +2411,8 @@
 
                                                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;
+                                               out_buf_size = ocnt = 
initial_out_buf_size;
+                                               out_buf = pd = pemalloc(out_buf_size, 
self->persistent);
                                        } else {
                                                new_out_buf = perealloc(out_buf, 
new_out_buf_size, self->persistent);
                                                pd = new_out_buf + (pd - out_buf);
@@ -2350,101 +2420,63 @@
                                                out_buf = new_out_buf;
                                                out_buf_size = new_out_buf_size;
                                        }
-                               }
-                       } else {
-                               break;
-                       }
+                               } 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);
+                               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
                } else {
-                       pefree(out_buf, self->persistent);
+                       if (ps == NULL) {
+                               break;
+                       }
                }
-       } 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;
+               prev_ocnt = ocnt;
+       }
 
-                                               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;
+       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);
+       }
+       *consumed += buf_len - icnt;
 
-                                               new_out_buf_size = out_buf_size << 1;
+       return SUCCESS;
 
-                                               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);
+out_failure:
+       pefree(out_buf, self->persistent);
+       return FAILURE;
+}
 
-                                                       
php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
+/* {{{ 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;
+       size_t consumed = 0;
+       php_iconv_stream_filter *self = (php_iconv_stream_filter *)filter->abstract;
 
-                                                       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;
-                       }
+       if (flags != PSFS_FLAG_NORMAL) {
+               if (php_iconv_stream_filter_append_bucket(self, stream, filter, 
buckets_out, NULL, 0, &consumed, self->persistent TSRMLS_CC) != SUCCESS) {
+                       goto out_failure;
+               }
+       } else {
+               while (buckets_in->head != NULL) {
+                       bucket = buckets_in->head;
 
-                       /* update consumed by the number of bytes just used */
-                       consumed = bucket->buflen - icnt;
+                       php_stream_bucket_unlink(bucket TSRMLS_CC);
 
-                       /* 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);
+                       if (php_iconv_stream_filter_append_bucket(self, stream, 
filter, buckets_out, bucket->buf, bucket->buflen, &consumed, self->persistent 
TSRMLS_CC) != SUCCESS) {                          goto out_failure;
                        }
 
                        php_stream_bucket_delref(bucket TSRMLS_CC);
@@ -2458,9 +2490,6 @@
        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);
        }

-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to