summaryrefslogtreecommitdiff
path: root/libc/stdio/ungetc.c
diff options
context:
space:
mode:
Diffstat (limited to 'libc/stdio/ungetc.c')
-rw-r--r--libc/stdio/ungetc.c77
1 files changed, 77 insertions, 0 deletions
diff --git a/libc/stdio/ungetc.c b/libc/stdio/ungetc.c
new file mode 100644
index 000000000..1b9197a18
--- /dev/null
+++ b/libc/stdio/ungetc.c
@@ -0,0 +1,77 @@
+/* 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"
+
+/* Having ungotten characters implies the stream is reading.
+ * The scheme used here treats the least significant 2 bits of
+ * the stream's modeflags member as follows:
+ * 0 0 Not currently reading.
+ * 0 1 Reading, but no ungetc() or scanf() push back chars.
+ * 1 0 Reading with one ungetc() char (ungot[1] is 1)
+ * or one scanf() pushed back char (ungot[1] is 0).
+ * 1 1 Reading with both an ungetc() char and a scanf()
+ * pushed back char. Note that this must be the result
+ * of a scanf() push back (in ungot[0]) _followed_ by
+ * an ungetc() call (in ungot[1]).
+ *
+ * Notes:
+ * scanf() can NOT use ungetc() to push back characters.
+ * (See section 7.19.6.2 of the C9X rationale -- WG14/N897.)
+ */
+
+int ungetc(int c, register FILE *stream)
+{
+ __STDIO_AUTO_THREADLOCK_VAR;
+
+ __STDIO_AUTO_THREADLOCK(stream);
+ __STDIO_STREAM_VALIDATE(stream);
+
+#ifdef __UCLIBC_MJN3_ONLY__
+#warning CONSIDER: Make fast ungetc an option?
+#endif
+#ifdef __UCLIBC_HAS_STDIO_GETC_MACRO__
+ /* If buffered narrow reading with no ungot slots filled, and if not
+ * ungetting a different char than the one last read from the buffer,
+ * we can simply decrement the position and not worry about disabling
+ * the getc macros. This will cut down on overhead in applications
+ * that use getc/ungetc extensively (like gcc). */
+ /* NOTE: If we can use getc, then we are buffered narrow reading with
+ * no ungot slots filled. */
+ if (__STDIO_STREAM_CAN_USE_BUFFER_GET(stream)
+ && (c != EOF)
+ && (stream->__bufpos > stream->__bufstart)
+ && (stream->__bufpos[-1] == ((unsigned char)c))
+ ) {
+ --stream->__bufpos;
+ __STDIO_STREAM_CLEAR_EOF(stream); /* Must clear end-of-file flag. */
+ } else
+#endif
+ /* Note: Even if c == EOF, we need to initialize/verify the
+ * stream's orientation and ensure the stream is in reading
+ * mode (if readable and properly oriented). */
+ if ((!__STDIO_STREAM_IS_NARROW_READING(stream)
+ && __STDIO_STREAM_TRANS_TO_READ(stream, __FLAG_NARROW))
+ || ((stream->__modeflags & __FLAG_UNGOT)
+ && ((stream->__modeflags & 1) || stream->__ungot[1]))
+ ) {
+ c = EOF;
+ } else if (c != EOF) {
+ __STDIO_STREAM_DISABLE_GETC(stream);
+
+ /* Flag this as a user ungot, as scanf does the necessary fixup. */
+ stream->__ungot[1] = 1;
+ stream->__ungot[(++stream->__modeflags) & 1] = c;
+
+ __STDIO_STREAM_CLEAR_EOF(stream); /* Must clear end-of-file flag. */
+ }
+
+ __STDIO_STREAM_VALIDATE(stream);
+ __STDIO_AUTO_THREADUNLOCK(stream);
+
+ return c;
+}