diff options
Diffstat (limited to 'usr/klibc/stdio/fwrite.c')
-rw-r--r-- | usr/klibc/stdio/fwrite.c | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/usr/klibc/stdio/fwrite.c b/usr/klibc/stdio/fwrite.c new file mode 100644 index 0000000..9f32ae4 --- /dev/null +++ b/usr/klibc/stdio/fwrite.c @@ -0,0 +1,97 @@ +/* + * fwrite.c + */ + +#include <string.h> +#include "stdioint.h" + +static size_t fwrite_noflush(const void *buf, size_t count, + struct _IO_file_pvt *f) +{ + size_t bytes = 0; + size_t nb; + const char *p = buf; + ssize_t rv; + + while (count) { + if (f->ibytes || f->obytes >= f->bufsiz || + (f->obytes && count >= f->bufsiz)) + if (__fflush(f)) + break; + + if (count >= f->bufsiz) { + /* + * The write is large, so bypass + * buffering entirely. + */ + rv = write(f->pub._IO_fileno, p, count); + if (rv == -1) { + if (errno == EINTR || errno == EAGAIN) + continue; + f->pub._IO_error = true; + break; + } else if (rv == 0) { + /* EOF on output? */ + f->pub._IO_eof = true; + break; + } + + p += rv; + bytes += rv; + count -= rv; + } else { + nb = f->bufsiz - f->obytes; + nb = (count < nb) ? count : nb; + if (nb) { + memcpy(f->buf+f->obytes, p, nb); + p += nb; + f->obytes += nb; + count -= nb; + bytes += nb; + } + } + } + return bytes; +} + +size_t _fwrite(const void *buf, size_t count, FILE *file) +{ + struct _IO_file_pvt *f = stdio_pvt(file); + size_t bytes = 0; + size_t pf_len, pu_len; + const char *p = buf; + const char *q; + + /* We divide the data into two chunks, flushed (pf) + and unflushed (pu) depending on buffering mode + and contents. */ + + switch (f->bufmode) { + case _IOFBF: + pf_len = 0; + break; + + case _IOLBF: + q = memrchr(p, '\n', count); + pf_len = q ? q - p + 1 : 0; + break; + + case _IONBF: + default: + pf_len = count; + break; + } + + if (pf_len) { + bytes = fwrite_noflush(p, pf_len, f); + p += bytes; + if (__fflush(f) || bytes != pf_len) + return bytes; + } + + pu_len = count - pf_len; + if (pu_len) + bytes += fwrite_noflush(p, pu_len, f); + + return bytes; +} |