CLib line buffering bug in 5.18
Martin Wuerthner (146) 20 posts |
There is a problem with line buffered output streams in CLib 5.66 as supplied with RISC OS 5.18. If you set an output stream to be line buffered and write to it using fwrite, then the stream is not flushed when a ‘\n’ is written. This used to work correctly in 5.16 and causes the PostScript 3 driver to fail in some cases because it relies on this behaviour (actually, these are fputs calls in our code, but GCC replaces fputs calls with constant string arguments by fwrite calls). Test case: /* linbuf2.c - test file for CLib line buffering */ /* Martin Wuerthner, 30/03/12 */ /* This file demonstrates a bug in CLib 5.66 as supplied with RISC OS 5.18. fwrite does not flush line-buffered files when writing a buffer containing '\n'. This worked correctly in RISC OS 5.16. In the simplest case (TESTSIZE = 1): expected output: xy<LF>z<LF> output on RISC OS 5.18: z<LF>xy<LF> In the general case: expected output: <TESTSIZE times x>yz output on RISC OS 5.18: <0x1000 * (TESTSIZE div 0x1000) times x>z<LF><TESTSIZE mod 0x1000 times x>y<LF> In other words, the 'z' written at the end ends up between the 'x' characters, and exactly at the final block boundary of 0x1000 characters. That suggests that the buffer was not flushed by the fwrite call. When using GCC, this affects both fwrite and fputs with a constant string literal because GCC silently replaces fputs by fwrite in that case. When using Norcroft, it only affects fwrite. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <oslib/os.h> #include <oslib/fileswitch.h> #define TESTSIZE 1 /* this macro simulates what GCC does - it silently replaces fputs calls with constant strings by fwrite calls */ #define FPUTS(s,f) fwrite(s, 1, strlen(s), f) int main(int argc, char* argv[]) { FILE* f = fopen("<Boot$Dir>.^.LinBuf", "w"); if (f) { /* We make this stream line buffered */ if (setvbuf(f, NULL, _IOLBF, BUFSIZ)) { fprintf(stderr, "Buffer allocation failed\n"); exit(1); } /* There is an fwrite including '\n' */ FPUTS("linbuf test file\n", f); /* Followed by one or more fputc calls other than '\n' */ int i; for (i = 0; i < TESTSIZE; i++) fputc ('x', f); /* Followed by an fwrite terminated by '\n' - this should flush the buffer! */ /* If you replace the following by two equivalent fputc calls, the problem goes away. */ FPUTS("y\n", f); /* At this point, nothing should be buffered due to the '\n' at the end of the previous fputs, so it should be safe to write to the file handle directly */ if (xos_bput ('z', f->__file) || xos_bput ('\n', f->__file)) fprintf(stderr, "Cannot write to file\n"); fclose(f); } } |