summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS3
-rw-r--r--ext/iconv/iconv.c85
-rw-r--r--ext/iconv/tests/bug79200.phpt18
3 files changed, 68 insertions, 38 deletions
diff --git a/NEWS b/NEWS
index 8f8c7fb39b..9c726b306e 100644
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,9 @@ PHP NEWS
. Fixed bug #79396 (DateTime hour incorrect during DST jump forward). (Nate
Brunette)
+- Iconv:
+ . Fixed bug #79200 (Some iconv functions cut Windows-1258). (cmb)
+
- SimpleXML:
. Fixed bug #61597 (SXE properties may lack attributes and content). (cmb)
diff --git a/ext/iconv/iconv.c b/ext/iconv/iconv.c
index d4244b19f4..eeca0340a0 100644
--- a/ext/iconv/iconv.c
+++ b/ext/iconv/iconv.c
@@ -724,6 +724,7 @@ static php_iconv_err_t _php_iconv_strlen(size_t *pretval, const char *str, size_
size_t out_left;
size_t cnt;
+ int more;
*pretval = (size_t)-1;
@@ -743,25 +744,23 @@ static php_iconv_err_t _php_iconv_strlen(size_t *pretval, const char *str, size_
errno = 0;
out_left = 0;
+ more = nbytes > 0;
- for (in_p = str, in_left = nbytes, cnt = 0; in_left > 0; cnt+=2) {
- size_t prev_in_left;
+ for (in_p = str, in_left = nbytes, cnt = 0; more;) {
out_p = buf;
out_left = sizeof(buf);
- prev_in_left = in_left;
+ more = in_left > 0;
- if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
- if (prev_in_left == in_left) {
- break;
- }
+ iconv(cd, more ? (char **)&in_p : NULL, more ? &in_left : NULL, (char **) &out_p, &out_left);
+ if (out_left == sizeof(buf)) {
+ break;
+ } else {
+ ZEND_ASSERT((sizeof(buf) - out_left) % GENERIC_SUPERSET_NBYTES == 0);
+ cnt += (sizeof(buf) - out_left) / GENERIC_SUPERSET_NBYTES;
}
}
- if (out_left > 0) {
- cnt -= out_left / GENERIC_SUPERSET_NBYTES;
- }
-
#if ICONV_SUPPORTS_ERRNO
switch (errno) {
case EINVAL:
@@ -810,6 +809,7 @@ static php_iconv_err_t _php_iconv_substr(smart_str *pretval,
size_t cnt;
size_t total_len;
+ int more;
err = _php_iconv_strlen(&total_len, str, nbytes, enc);
if (err != PHP_ICONV_ERR_SUCCESS) {
@@ -864,18 +864,17 @@ static php_iconv_err_t _php_iconv_substr(smart_str *pretval,
cd2 = (iconv_t)NULL;
errno = 0;
+ more = nbytes > 0 && len > 0;
- for (in_p = str, in_left = nbytes, cnt = 0; in_left > 0 && len > 0; ++cnt) {
- size_t prev_in_left;
+ for (in_p = str, in_left = nbytes, cnt = 0; more; ++cnt) {
out_p = buf;
out_left = sizeof(buf);
- prev_in_left = in_left;
+ more = in_left > 0 && len > 0;
- if (iconv(cd1, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
- if (prev_in_left == in_left) {
- break;
- }
+ iconv(cd1, more ? (char **)&in_p : NULL, more ? &in_left : NULL, (char **) &out_p, &out_left);
+ if (out_left == sizeof(buf)) {
+ break;
}
if ((zend_long)cnt >= offset) {
@@ -963,6 +962,8 @@ static php_iconv_err_t _php_iconv_strpos(size_t *pretval,
size_t ndl_buf_left;
size_t match_ofs;
+ int more;
+ size_t iconv_ret;
*pretval = (size_t)-1;
@@ -995,37 +996,38 @@ static php_iconv_err_t _php_iconv_strpos(size_t *pretval,
ndl_buf_p = ZSTR_VAL(ndl_buf);
ndl_buf_left = ZSTR_LEN(ndl_buf);
match_ofs = (size_t)-1;
+ more = haystk_nbytes > 0;
- for (in_p = haystk, in_left = haystk_nbytes, cnt = 0; in_left > 0; ++cnt) {
- size_t prev_in_left;
+ for (in_p = haystk, in_left = haystk_nbytes, cnt = 0; more; ++cnt) {
out_p = buf;
out_left = sizeof(buf);
- prev_in_left = in_left;
+ more = in_left > 0;
- if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
- if (prev_in_left == in_left) {
+ iconv_ret = iconv(cd, more ? (char **)&in_p : NULL, more ? &in_left : NULL, (char **) &out_p, &out_left);
+ if (out_left == sizeof(buf)) {
+ break;
+ }
#if ICONV_SUPPORTS_ERRNO
- switch (errno) {
- case EINVAL:
- err = PHP_ICONV_ERR_ILLEGAL_CHAR;
- break;
+ if (iconv_ret == (size_t)-1) {
+ switch (errno) {
+ case EINVAL:
+ err = PHP_ICONV_ERR_ILLEGAL_CHAR;
+ break;
- case EILSEQ:
- err = PHP_ICONV_ERR_ILLEGAL_SEQ;
- break;
+ case EILSEQ:
+ err = PHP_ICONV_ERR_ILLEGAL_SEQ;
+ break;
- case E2BIG:
- break;
+ case E2BIG:
+ break;
- default:
- err = PHP_ICONV_ERR_UNKNOWN;
- break;
- }
-#endif
- break;
+ default:
+ err = PHP_ICONV_ERR_UNKNOWN;
+ break;
}
}
+#endif
if (offset >= 0) {
if (cnt >= (size_t)offset) {
if (_php_iconv_memequal(buf, ndl_buf_p, sizeof(buf))) {
@@ -1997,6 +1999,13 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
*next_pos = p1;
}
+ if (cd != (iconv_t)(-1)) {
+ _php_iconv_appendl(pretval, NULL, 0, cd);
+ }
+ if (cd_pl != (iconv_t)(-1)) {
+ _php_iconv_appendl(pretval, NULL, 0, cd_pl);
+ }
+
smart_str_0(pretval);
out:
if (cd != (iconv_t)(-1)) {
diff --git a/ext/iconv/tests/bug79200.phpt b/ext/iconv/tests/bug79200.phpt
new file mode 100644
index 0000000000..e8d86086a3
--- /dev/null
+++ b/ext/iconv/tests/bug79200.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Bug #79200 (Some iconv functions cut Windows-1258)
+--SKIPIF--
+<?php
+if (!extension_loaded('iconv')) die('skip iconv extension not available');
+?>
+--FILE--
+<?php
+var_dump(iconv_mime_decode('=?windows-1258?Q?test=20test?=', 0, 'UTF-8'));
+var_dump(iconv_strlen('test test', 'WINDOWS-1258'));
+var_dump(iconv_strpos('test test', 'test test', 0, 'WINDOWS-1258'));
+var_dump(iconv_substr('test test', 0 , 9, 'WINDOWS-1258'));
+?>
+--EXPECT--
+string(9) "test test"
+int(9)
+int(0)
+string(9) "test test"