summaryrefslogtreecommitdiff
path: root/libc/stdio/setvbuf.c
diff options
context:
space:
mode:
Diffstat (limited to 'libc/stdio/setvbuf.c')
-rw-r--r--libc/stdio/setvbuf.c106
1 files changed, 106 insertions, 0 deletions
diff --git a/libc/stdio/setvbuf.c b/libc/stdio/setvbuf.c
new file mode 100644
index 000000000..3fe62c6a8
--- /dev/null
+++ b/libc/stdio/setvbuf.c
@@ -0,0 +1,106 @@
+/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * GNU Library General Public License (LGPL) version 2 or later.
+ *
+ * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details.
+ */
+
+#include "_stdio.h"
+
+#if (_IOFBF != 0) || (_IOLBF != 1) || (_IONBF != 2)
+#error Assumption violated -- values of _IOFBF, _IOLBF, _IONBF
+#endif
+#if (__FLAG_FBF != 0) || (__FLAG_NBF != (2*__FLAG_LBF))
+#error Assumption violated for buffering mode flags
+#endif
+
+int setvbuf(register FILE * __restrict stream, register char * __restrict buf,
+ int mode, size_t size)
+{
+#ifdef __STDIO_BUFFERS
+
+ int retval = EOF;
+ int alloc_flag = 0;
+ __STDIO_AUTO_THREADLOCK_VAR;
+
+ __STDIO_AUTO_THREADLOCK(stream);
+ __STDIO_STREAM_VALIDATE(stream);
+
+ if (((unsigned int) mode) > 2) {
+ __set_errno(EINVAL);
+ goto ERROR;
+ }
+
+ /* C99 states that setvbuf may only be used between a successful
+ * open of the stream and before any other operation other than
+ * an unsuccessful call to setvbuf. */
+
+#ifdef __STDIO_FLEXIBLE_SETVBUF
+ /* If we aren't currently reading (including ungots) or writing,
+ * then allow the request to proceed. */
+
+ if (stream->__modeflags & (__MASK_READING|__FLAG_WRITING)) {
+ goto ERROR;
+ }
+#else
+ /* The following test isn't quite as strict as C99, as it will
+ * not detect file positioning operations. */
+
+ if (stream->__modeflags & (__MASK_READING|__FLAG_WRITING
+ |__FLAG_NARROW|__FLAG_WIDE
+ |__FLAG_ERROR|__FLAG_EOF)
+ ) {
+ goto ERROR;
+ }
+#endif
+
+ stream->__modeflags &= ~(__MASK_BUFMODE); /* Clear current mode */
+ stream->__modeflags |= mode * __FLAG_LBF; /* and set new one. */
+
+ if ((mode == _IONBF) || !size) {
+ size = 0;
+ buf = NULL;
+ } else if (!buf) {
+ if ((__STDIO_STREAM_BUFFER_SIZE(stream) == size) /* Same size or */
+ || !(buf = malloc(size)) /* malloc failed, so don't change. */
+ ) {
+ goto DONE;
+ }
+ alloc_flag = __FLAG_FREEBUF;
+ }
+
+ if (stream->__modeflags & __FLAG_FREEBUF) {
+ stream->__modeflags &= ~(__FLAG_FREEBUF);
+ free(stream->__bufstart);
+ }
+
+ stream->__modeflags |= alloc_flag;
+ stream->__bufstart = buf;
+ stream->__bufend = buf + size;
+ __STDIO_STREAM_INIT_BUFREAD_BUFPOS(stream);
+ __STDIO_STREAM_DISABLE_GETC(stream);
+ __STDIO_STREAM_DISABLE_PUTC(stream);
+
+ DONE:
+ retval = 0;
+
+ ERROR:
+ __STDIO_STREAM_VALIDATE(stream);
+ __STDIO_AUTO_THREADUNLOCK(stream);
+
+ return retval;
+
+#else /* __STDIO_BUFFERS */
+
+ if (mode == _IONBF) {
+ return 0;
+ }
+
+ if (((unsigned int) mode) > 2) {
+ __set_errno(EINVAL);
+ }
+
+ return EOF;
+
+#endif
+}