correctly handle write errors encountered by printf-family functions
authorRich Felker <dalias@aerifal.cx>
Wed, 17 Dec 2014 08:08:53 +0000 (03:08 -0500)
committerRich Felker <dalias@aerifal.cx>
Wed, 17 Dec 2014 08:08:53 +0000 (03:08 -0500)
previously, write errors neither stopped further output attempts nor
caused the function to return an error to the caller. this could
result in silent loss of output, possibly in the middle of output in
the event of a non-permanent error.

the simplest solution is temporarily clearing the error flag for the
target stream, then suppressing further output when the error flag is
set and checking/restoring it at the end of the operation to determine
the correct return value.

since the wide version of the code internally calls the narrow fprintf
to perform some of its underlying operations, initial clearing of the
error flag is suppressed when performing a narrow vfprintf on a
wide-oriented stream. this is not a problem since the behavior of
narrow operations on wide-oriented streams is undefined.

src/stdio/vfprintf.c
src/stdio/vfwprintf.c

index 39c1e86771899dfbcb0805f5c1ee520c402a116f..d421817ce4a686d4cb5d7a2bbf6a51388809b8ca 100644 (file)
@@ -158,7 +158,7 @@ static void pop_arg(union arg *arg, int type, va_list *ap)
 
 static void out(FILE *f, const char *s, size_t l)
 {
-       __fwritex((void *)s, l, f);
+       if (!(f->flags & F_ERR)) __fwritex((void *)s, l, f);
 }
 
 static void pad(FILE *f, char c, int w, int l, int fl)
@@ -656,6 +656,7 @@ int vfprintf(FILE *restrict f, const char *restrict fmt, va_list ap)
        int nl_type[NL_ARGMAX+1] = {0};
        union arg nl_arg[NL_ARGMAX+1];
        unsigned char internal_buf[80], *saved_buf = 0;
+       int olderr;
        int ret;
 
        /* the copy allows passing va_list* even if va_list is an array */
@@ -666,6 +667,8 @@ int vfprintf(FILE *restrict f, const char *restrict fmt, va_list ap)
        }
 
        FLOCK(f);
+       olderr = f->flags & F_ERR;
+       if (f->mode < 1) f->flags &= ~F_ERR;
        if (!f->buf_size) {
                saved_buf = f->buf;
                f->wpos = f->wbase = f->buf = internal_buf;
@@ -680,6 +683,8 @@ int vfprintf(FILE *restrict f, const char *restrict fmt, va_list ap)
                f->buf_size = 0;
                f->wpos = f->wbase = f->wend = 0;
        }
+       if (f->flags & F_ERR) ret = -1;
+       f->flags |= olderr;
        FUNLOCK(f);
        va_end(ap2);
        return ret;
index c6400591a01ac7d6e257721eb0ed19b4f3a5a556..ebdff00101cfb000309c2aee8f0de3649c271b58 100644 (file)
@@ -149,7 +149,7 @@ static void pop_arg(union arg *arg, int type, va_list *ap)
 
 static void out(FILE *f, const wchar_t *s, size_t l)
 {
-       while (l--) fputwc(*s++, f);
+       while (l-- && !(f->flags & F_ERR)) fputwc(*s++, f);
 }
 
 static int getint(wchar_t **s) {
@@ -345,6 +345,7 @@ int vfwprintf(FILE *restrict f, const wchar_t *restrict fmt, va_list ap)
        va_list ap2;
        int nl_type[NL_ARGMAX] = {0};
        union arg nl_arg[NL_ARGMAX];
+       int olderr;
        int ret;
 
        /* the copy allows passing va_list* even if va_list is an array */
@@ -356,7 +357,11 @@ int vfwprintf(FILE *restrict f, const wchar_t *restrict fmt, va_list ap)
 
        FLOCK(f);
        f->mode |= f->mode+1;
+       olderr = f->flags & F_ERR;
+       f->flags &= ~F_ERR;
        ret = wprintf_core(f, fmt, &ap2, nl_arg, nl_type);
+       if (f->flags & F_ERR) ret = -1;
+       f->flags |= olderr;
        FUNLOCK(f);
        va_end(ap2);
        return ret;