summaryrefslogtreecommitdiff
path: root/libc/stdio/_adjust_pos.c
diff options
context:
space:
mode:
Diffstat (limited to 'libc/stdio/_adjust_pos.c')
-rw-r--r--libc/stdio/_adjust_pos.c68
1 files changed, 68 insertions, 0 deletions
diff --git a/libc/stdio/_adjust_pos.c b/libc/stdio/_adjust_pos.c
new file mode 100644
index 000000000..bc48d32b7
--- /dev/null
+++ b/libc/stdio/_adjust_pos.c
@@ -0,0 +1,68 @@
+/* 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"
+
+/* Both ftell() and fseek() (for SEEK_CUR) need to correct the stream's
+ * position to take into account buffered data and ungotten chars.
+ *
+ * If successful, store corrected position in *pos and return >= 0.
+ * Otherwise return < 0.
+ *
+ * If position is unrepresentable, set errno to EOVERFLOW.
+ */
+
+int __stdio_adjust_position(register FILE * __restrict stream,
+ register __offmax_t *pos)
+{
+ __offmax_t oldpos;
+ int corr;
+
+ if ((corr = stream->__modeflags & __MASK_READING) != 0) {
+ --corr; /* Correct for ungots. Assume narrow, and fix below. */
+ }
+
+#ifdef __UCLIBC_HAS_WCHAR__
+ if (corr && __STDIO_STREAM_IS_WIDE(stream)) {
+ /* A wide stream and we have at least one ungotten wchar.
+ * If it is a user ungot, we need to fail since position
+ * is unspecified as per C99. */
+ if ((corr > 1) || stream->__ungot[1]) { /* User ungetwc, */
+ return -1; /* so position is undefined. */
+ }
+ corr -= (1 + stream->__ungot_width[1]);
+ if (stream->__state.__mask > 0) { /* Incomplete (bad?) mb char. */
+ corr -= stream->__ungot_width[0];
+ }
+ }
+#endif
+
+#ifdef __STDIO_BUFFERS
+ corr += (((__STDIO_STREAM_IS_WRITING(stream))
+ ? stream->__bufstart : stream->__bufread)
+ - stream->__bufpos);
+#endif
+
+ oldpos = *pos;
+
+ /* Range checking cases:
+ * (pos - corr > pos) && (corr > 0) : underflow? return -corr < 0
+ * (pos - corr > pos) && (corr < 0) : ok .. return -corr > 0
+ * (pos - corr <= pos) && (corr >= 0) : ok .. return corr > 0
+ * (pos - corr <= pos) && (corr < 0) : overflow .. return corr < 0
+ */
+
+ if ((*pos -= corr) > oldpos) {
+ corr = -corr;
+ }
+
+ if (corr < 0) {
+ __set_errno(EOVERFLOW);
+ }
+
+ return corr;
+}