Skip to content

Commit 324337a

Browse files
committed
Merge branch 'PHP-7.4'
* PHP-7.4: Fix #79200: Some iconv functions cut Windows-1258
2 parents 816a1fd + a7bbfc9 commit 324337a

File tree

2 files changed

+64
-37
lines changed

2 files changed

+64
-37
lines changed

ext/iconv/iconv.c

Lines changed: 46 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,7 @@ static php_iconv_err_t _php_iconv_strlen(size_t *pretval, const char *str, size_
578578
size_t out_left;
579579

580580
size_t cnt;
581+
int more;
581582

582583
*pretval = (size_t)-1;
583584

@@ -593,25 +594,23 @@ static php_iconv_err_t _php_iconv_strlen(size_t *pretval, const char *str, size_
593594

594595
errno = 0;
595596
out_left = 0;
597+
more = nbytes > 0;
596598

597-
for (in_p = str, in_left = nbytes, cnt = 0; in_left > 0; cnt+=2) {
598-
size_t prev_in_left;
599+
for (in_p = str, in_left = nbytes, cnt = 0; more;) {
599600
out_p = buf;
600601
out_left = sizeof(buf);
601602

602-
prev_in_left = in_left;
603+
more = in_left > 0;
603604

604-
if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
605-
if (prev_in_left == in_left) {
606-
break;
607-
}
605+
iconv(cd, more ? (char **)&in_p : NULL, more ? &in_left : NULL, (char **) &out_p, &out_left);
606+
if (out_left == sizeof(buf)) {
607+
break;
608+
} else {
609+
ZEND_ASSERT((sizeof(buf) - out_left) % GENERIC_SUPERSET_NBYTES == 0);
610+
cnt += (sizeof(buf) - out_left) / GENERIC_SUPERSET_NBYTES;
608611
}
609612
}
610613

611-
if (out_left > 0) {
612-
cnt -= out_left / GENERIC_SUPERSET_NBYTES;
613-
}
614-
615614
switch (errno) {
616615
case EINVAL:
617616
err = PHP_ICONV_ERR_ILLEGAL_CHAR;
@@ -656,6 +655,7 @@ static php_iconv_err_t _php_iconv_substr(smart_str *pretval,
656655

657656
size_t cnt;
658657
size_t total_len;
658+
int more;
659659

660660
err = _php_iconv_strlen(&total_len, str, nbytes, enc);
661661
if (err != PHP_ICONV_ERR_SUCCESS) {
@@ -706,18 +706,17 @@ static php_iconv_err_t _php_iconv_substr(smart_str *pretval,
706706

707707
cd2 = (iconv_t)NULL;
708708
errno = 0;
709+
more = nbytes > 0 && len > 0;
709710

710-
for (in_p = str, in_left = nbytes, cnt = 0; in_left > 0 && len > 0; ++cnt) {
711-
size_t prev_in_left;
711+
for (in_p = str, in_left = nbytes, cnt = 0; more; ++cnt) {
712712
out_p = buf;
713713
out_left = sizeof(buf);
714714

715-
prev_in_left = in_left;
715+
more = in_left > 0 && len > 0;
716716

717-
if (iconv(cd1, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
718-
if (prev_in_left == in_left) {
719-
break;
720-
}
717+
iconv(cd1, more ? (char **)&in_p : NULL, more ? &in_left : NULL, (char **) &out_p, &out_left);
718+
if (out_left == sizeof(buf)) {
719+
break;
721720
}
722721

723722
if ((zend_long)cnt >= offset) {
@@ -799,6 +798,8 @@ static php_iconv_err_t _php_iconv_strpos(size_t *pretval,
799798
size_t ndl_buf_left;
800799

801800
size_t match_ofs;
801+
int more;
802+
size_t iconv_ret;
802803

803804
*pretval = (size_t)-1;
804805

@@ -827,33 +828,34 @@ static php_iconv_err_t _php_iconv_strpos(size_t *pretval,
827828
ndl_buf_p = ZSTR_VAL(ndl_buf);
828829
ndl_buf_left = ZSTR_LEN(ndl_buf);
829830
match_ofs = (size_t)-1;
831+
more = haystk_nbytes > 0;
830832

831-
for (in_p = haystk, in_left = haystk_nbytes, cnt = 0; in_left > 0; ++cnt) {
832-
size_t prev_in_left;
833+
for (in_p = haystk, in_left = haystk_nbytes, cnt = 0; more; ++cnt) {
833834
out_p = buf;
834835
out_left = sizeof(buf);
835836

836-
prev_in_left = in_left;
837+
more = in_left > 0;
837838

838-
if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
839-
if (prev_in_left == in_left) {
840-
switch (errno) {
841-
case EINVAL:
842-
err = PHP_ICONV_ERR_ILLEGAL_CHAR;
843-
break;
839+
iconv_ret = iconv(cd, more ? (char **)&in_p : NULL, more ? &in_left : NULL, (char **) &out_p, &out_left);
840+
if (out_left == sizeof(buf)) {
841+
break;
842+
}
843+
if (iconv_ret == (size_t)-1) {
844+
switch (errno) {
845+
case EINVAL:
846+
err = PHP_ICONV_ERR_ILLEGAL_CHAR;
847+
break;
844848

845-
case EILSEQ:
846-
err = PHP_ICONV_ERR_ILLEGAL_SEQ;
847-
break;
849+
case EILSEQ:
850+
err = PHP_ICONV_ERR_ILLEGAL_SEQ;
851+
break;
848852

849-
case E2BIG:
850-
break;
853+
case E2BIG:
854+
break;
851855

852-
default:
853-
err = PHP_ICONV_ERR_UNKNOWN;
854-
break;
855-
}
856-
break;
856+
default:
857+
err = PHP_ICONV_ERR_UNKNOWN;
858+
break;
857859
}
858860
}
859861
if (offset >= 0) {
@@ -1777,6 +1779,13 @@ static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *st
17771779
*next_pos = p1;
17781780
}
17791781

1782+
if (cd != (iconv_t)(-1)) {
1783+
_php_iconv_appendl(pretval, NULL, 0, cd);
1784+
}
1785+
if (cd_pl != (iconv_t)(-1)) {
1786+
_php_iconv_appendl(pretval, NULL, 0, cd_pl);
1787+
}
1788+
17801789
smart_str_0(pretval);
17811790
out:
17821791
if (cd != (iconv_t)(-1)) {

ext/iconv/tests/bug79200.phpt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Bug #79200 (Some iconv functions cut Windows-1258)
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded('iconv')) die('skip iconv extension not available');
6+
?>
7+
--FILE--
8+
<?php
9+
var_dump(iconv_mime_decode('=?windows-1258?Q?test=20test?=', 0, 'UTF-8'));
10+
var_dump(iconv_strlen('test test', 'WINDOWS-1258'));
11+
var_dump(iconv_strpos('test test', 'test test', 0, 'WINDOWS-1258'));
12+
var_dump(iconv_substr('test test', 0 , 9, 'WINDOWS-1258'));
13+
?>
14+
--EXPECT--
15+
string(9) "test test"
16+
int(9)
17+
int(0)
18+
string(9) "test test"

0 commit comments

Comments
 (0)