diff --git a/library/stdio_fwrite.c b/library/stdio_fwrite.c index 4dd3505..d4621b0 100644 --- a/library/stdio_fwrite.c +++ b/library/stdio_fwrite.c @@ -1,10 +1,8 @@ /* - * $Id: stdio_fwrite.c,v 1.12 2010-10-20 13:12:58 obarthel Exp $ - * * :ts=4 * * Portable ISO 'C' (1994) runtime library for the Amiga computer - * Copyright (c) 2002-2015 by Olaf Barthel + * Copyright (c) 2002-2019 by Olaf Barthel * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -78,7 +76,6 @@ fwrite(const void *ptr,size_t element_size,size_t count,FILE *stream) assert( __is_valid_iob(file) ); assert( FLAG_IS_SET(file->iob_Flags,IOBF_IN_USE) ); - assert( file->iob_BufferSize > 0 ); if(FLAG_IS_CLEAR(file->iob_Flags,IOBF_IN_USE)) { @@ -102,9 +99,11 @@ fwrite(const void *ptr,size_t element_size,size_t count,FILE *stream) goto out; } + clearerr((FILE *)file); + if(element_size > 0 && count > 0) { - const unsigned char * data = (unsigned char *)ptr; + const unsigned char * s = (unsigned char *)ptr; unsigned char c; int buffer_mode; size_t total_bytes_written = 0; @@ -115,6 +114,9 @@ fwrite(const void *ptr,size_t element_size,size_t count,FILE *stream) if(__fputc_check((FILE *)file) < 0) goto out; + /* If this is an unbuffered interactiv stream, we will switch + to line buffered mode in order to improve readability of + the output. */ buffer_mode = (file->iob_Flags & IOBF_BUFFER_MODE); if(buffer_mode == IOBF_BUFFER_MODE_NONE) { @@ -128,43 +130,166 @@ fwrite(const void *ptr,size_t element_size,size_t count,FILE *stream) __fd_unlock(fd); } - if(buffer_mode == IOBF_BUFFER_MODE_LINE) + if (buffer_mode == IOBF_BUFFER_MODE_LINE) { - while(total_size-- > 0) + assert( file->iob_BufferSize > 0 ); + + while(total_size > 0) { - c = (*data++); + /* Is there still room in the write buffer to store + more of the data? */ + if(file->iob_BufferWriteBytes < file->iob_BufferSize) + { + unsigned char * buffer = &file->iob_Buffer[file->iob_BufferWriteBytes]; + size_t num_buffer_bytes; + const unsigned char * lf; + + /* Store only as many characters as will fit into the write buffer. */ + num_buffer_bytes = file->iob_BufferSize - file->iob_BufferWriteBytes; + if(total_size < num_buffer_bytes) + num_buffer_bytes = total_size; + + /* Try to find a line feed in the string. If there is one, + reduce the number of characters to write to the sequence + which ends with the line feed character. */ + lf = (unsigned char *)memchr(s, '\n', num_buffer_bytes); + if(lf != NULL) + num_buffer_bytes = lf + 1 - s; + + memmove(buffer, s, num_buffer_bytes); + s += num_buffer_bytes; + + file->iob_BufferWriteBytes += num_buffer_bytes; + + /* Write the buffer to disk if it's full or contains a line feed. */ + if((lf != NULL || __iob_write_buffer_is_full(file)) && __flush_iob_write_buffer(file) < 0) + { + /* Abort with error. */ + break; + } + + total_bytes_written += num_buffer_bytes; + + /* Stop as soon as no further data needs to be written. */ + total_size -= num_buffer_bytes; + if(total_size == 0) + break; + + /* If there is again room in the output buffer, + repeat this optimization. */ + if(file->iob_BufferWriteBytes < file->iob_BufferSize) + continue; + } + + c = (*s++); if(__putc_line_buffered(c,(FILE *)file) == EOF) - goto out; + break; + total_size--; total_bytes_written++; } } + else if (buffer_mode == IOBF_BUFFER_MODE_NONE) + { + ssize_t num_bytes_written; + + /* We bypass the buffer entirely. */ + num_bytes_written = write(file->iob_Descriptor, s, total_size); + if(num_bytes_written == -1) + { + SET_FLAG(file->iob_Flags,IOBF_ERROR); + goto out; + } + + total_bytes_written = (size_t)num_bytes_written; + } else { - while(total_size-- > 0) + assert( file->iob_BufferSize > 0 ); + + while(total_size > 0) { - c = (*data++); + /* If there is more data to be written than the write buffer will hold + and the write buffer is empty anyway, then we'll bypass the write + buffer entirely. */ + if(file->iob_BufferWriteBytes == 0 && total_size >= (size_t)file->iob_BufferSize) + { + ssize_t num_bytes_written; + + /* We bypass the buffer entirely. */ + num_bytes_written = write(file->iob_Descriptor, s, total_size); + if(num_bytes_written == -1) + { + SET_FLAG(file->iob_Flags,IOBF_ERROR); + goto out; + } + + total_bytes_written += num_bytes_written; + break; + } + + /* Is there still room in the write buffer to store + more of the data? */ + if(file->iob_BufferWriteBytes < file->iob_BufferSize) + { + unsigned char * buffer = &file->iob_Buffer[file->iob_BufferWriteBytes]; + size_t num_buffer_bytes; + + /* Store only as many bytes as will fit into the write buffer. */ + num_buffer_bytes = file->iob_BufferSize - file->iob_BufferWriteBytes; + if(total_size < num_buffer_bytes) + num_buffer_bytes = total_size; + + memmove(buffer, s, num_buffer_bytes); + s += num_buffer_bytes; + + file->iob_BufferWriteBytes += num_buffer_bytes; + + /* Write a full buffer to disk. */ + if(__iob_write_buffer_is_full(file) && __flush_iob_write_buffer(file) < 0) + { + /* Abort with error. */ + break; + } + + total_bytes_written += num_buffer_bytes; + + /* Stop as soon as no further data needs to be written. */ + total_size -= num_buffer_bytes; + if(total_size == 0) + break; + + /* If there is again room in the output buffer, + try this optimization again. */ + if(file->iob_BufferWriteBytes < file->iob_BufferSize) + continue; + } + + c = (*s++); if(__putc_fully_buffered(c,(FILE *)file) == EOF) - goto out; + break; + total_size--; total_bytes_written++; } } - + if((file->iob_Flags & IOBF_BUFFER_MODE) == IOBF_BUFFER_MODE_NONE) { - if(__iob_write_buffer_is_valid(file) && __flush_iob_write_buffer(file) < 0) - goto out; + if(__iob_write_buffer_is_valid(file)) + __flush_iob_write_buffer(file); } result = total_bytes_written / element_size; } else { - /* Don't let this appear like an EOF or error. */ - clearerr((FILE *)file); + SHOWVALUE(element_size); + SHOWVALUE(count); + + SHOWMSG("either element size or count is zero"); } out: