Ingo Schwarze
2015-12-24 21:24:01 UTC
Third file, third bug.
SO FAR, EACH AND EVERY BLOODY PLACE I LOOKED AT WAS BROKEN.
When fputwc(3) encounters an encoding error, it neglects to set the
error indicator, just like fgetwc(3) did before i fixed it today.
Setting the error indicator is required by the manual and by the
standard.
Instead, it overrides errno again. That's pointless because
wcrtomb(3) already did that, and is required to do so by the standard.
It is even slightly dangerous because it might hide internal coding
errors in libc. For example, if libc would ever neglect to initialize
the state object correctly, wcrtomb(3) might fail with EINVAL.
That should NOT be plastered over by setting errno to EILSEQ.
Consider this test program:
#include <err.h>
#include <stdio.h>
#include <wchar.h>
int
main(void)
{
wchar_t ws[2];
int irc;
ws[0] = 0xdc00; /* UTF-16 surrogate, invalid code point. */
ws[1] = L'\0';
irc = fputws(ws, stdout);
printf("fputws returned %d\n", irc);
printf("error status %d\n", ferror(stdout));
err(1, "fputws");
}
Output before:
$ ./putws
fputws returned -1
error status 0
putws: fputws: Illegal byte sequence
Output with the patch below:
$ ./putws
fputws returned -1
error status 1
putws: fputws: Illegal byte sequence
OK?
Ingo
Index: fputwc.c
===================================================================
RCS file: /cvs/src/lib/libc/stdio/fputwc.c,v
retrieving revision 1.6
diff -u -p -r1.6 fputwc.c
--- fputwc.c 1 Oct 2015 02:32:07 -0000 1.6
+++ fputwc.c 24 Dec 2015 21:01:10 -0000
@@ -62,7 +62,7 @@ __fputwc_unlock(wchar_t wc, FILE *fp)
size = wcrtomb(buf, wc, st);
if (size == (size_t)-1) {
- errno = EILSEQ;
+ fp->_flags |= __SERR;
return WEOF;
}
SO FAR, EACH AND EVERY BLOODY PLACE I LOOKED AT WAS BROKEN.
When fputwc(3) encounters an encoding error, it neglects to set the
error indicator, just like fgetwc(3) did before i fixed it today.
Setting the error indicator is required by the manual and by the
standard.
Instead, it overrides errno again. That's pointless because
wcrtomb(3) already did that, and is required to do so by the standard.
It is even slightly dangerous because it might hide internal coding
errors in libc. For example, if libc would ever neglect to initialize
the state object correctly, wcrtomb(3) might fail with EINVAL.
That should NOT be plastered over by setting errno to EILSEQ.
Consider this test program:
#include <err.h>
#include <stdio.h>
#include <wchar.h>
int
main(void)
{
wchar_t ws[2];
int irc;
ws[0] = 0xdc00; /* UTF-16 surrogate, invalid code point. */
ws[1] = L'\0';
irc = fputws(ws, stdout);
printf("fputws returned %d\n", irc);
printf("error status %d\n", ferror(stdout));
err(1, "fputws");
}
Output before:
$ ./putws
fputws returned -1
error status 0
putws: fputws: Illegal byte sequence
Output with the patch below:
$ ./putws
fputws returned -1
error status 1
putws: fputws: Illegal byte sequence
OK?
Ingo
Index: fputwc.c
===================================================================
RCS file: /cvs/src/lib/libc/stdio/fputwc.c,v
retrieving revision 1.6
diff -u -p -r1.6 fputwc.c
--- fputwc.c 1 Oct 2015 02:32:07 -0000 1.6
+++ fputwc.c 24 Dec 2015 21:01:10 -0000
@@ -62,7 +62,7 @@ __fputwc_unlock(wchar_t wc, FILE *fp)
size = wcrtomb(buf, wc, st);
if (size == (size_t)-1) {
- errno = EILSEQ;
+ fp->_flags |= __SERR;
return WEOF;
}