summaryrefslogtreecommitdiff
path: root/libc/stdio/fgetwc.c
blob: a78f522123185c8277eb7057c689645f2837ad1c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/* 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"

#ifdef __DO_UNLOCKED

static void munge_stream(register FILE *stream, unsigned char *buf)
{
	stream->__bufend = stream->__bufstart = buf;
	__STDIO_STREAM_INIT_BUFREAD_BUFPOS(stream);
	__STDIO_STREAM_DISABLE_GETC(stream);
	__STDIO_STREAM_DISABLE_PUTC(stream);
}

wint_t __fgetwc_unlocked(register FILE *stream)
{
	wint_t wi;
	wchar_t wc[1];
	int n;
	size_t r;
	unsigned char sbuf[1];

	__STDIO_STREAM_VALIDATE(stream);

	wi = WEOF;					/* Prepare for failure. */

	if (__STDIO_STREAM_IS_WIDE_READING(stream)
		|| !__STDIO_STREAM_TRANS_TO_READ(stream, __FLAG_WIDE)
		) {
		if (stream->__modeflags & __FLAG_UNGOT) { /* Any ungetwc()s? */
			if (((stream->__modeflags & 1) || stream->__ungot[1])) {
				stream->__ungot_width[0] = 0;	/* Application ungot... */
			} else {			/* scanf ungot */
				stream->__ungot_width[0] = stream->__ungot_width[1];
			}

			wi = stream->__ungot[(stream->__modeflags--) & 1];
			stream->__ungot[1] = 0;
			goto DONE;
		}

		if (!stream->__bufstart) {	/* Ugh... stream isn't buffered! */
			/* Munge the stream temporarily to use a 1-byte buffer. */
			munge_stream(stream, sbuf);
			++stream->__bufend;
		}

		if (stream->__state.__mask == 0) { /* If last was a complete char */
			stream->__ungot_width[0] = 0; /* then reset the width. */
		}

	LOOP:
		if ((n = __STDIO_STREAM_BUFFER_RAVAIL(stream)) == 0) {
			goto FILL_BUFFER;
		}

		r = mbrtowc(wc, stream->__bufpos, n, &stream->__state);
		if (((ssize_t) r) >= 0) { /* Success... */
			if (r == 0) { /* Nul wide char... means 0 byte for us so */
				++r;	 /* increment r and handle below as single. */
			}
			stream->__bufpos += r;
			stream->__ungot_width[0] += r;
			wi = *wc;
			goto DONE;
		}

		if (r == ((size_t) -2)) {
			/* Potentially valid but incomplete and no more buffered. */
			stream->__bufpos += n; /* Update bufpos for stream. */
			stream->__ungot_width[0] += n;
		FILL_BUFFER:
			if(__STDIO_FILL_READ_BUFFER(stream)) { /* Refill succeeded? */
				goto LOOP;
			}
			if (!__FERROR_UNLOCKED(stream)) { /* EOF with no error. */
				if (!stream->__state.__mask) { /* No partial wchar. */
					goto DONE;
				}
				/* EOF but partially complete wchar. */
				/* TODO: should EILSEQ be set? */
				__set_errno(EILSEQ);
			}
		}

		/* If we reach here, either r == ((size_t)-1) and mbrtowc set errno
		 * to EILSEQ, or r == ((size_t)-2) and stream is in an error state
		 * or at EOF with a partially complete wchar.  Make sure stream's
		 * error indicator is set. */
		stream->__modeflags |= __FLAG_ERROR;

	DONE:
		if (stream->__bufstart == sbuf) { /* Need to un-munge the stream. */
			munge_stream(stream, NULL);
		}

	}

	__STDIO_STREAM_VALIDATE(stream);

	return wi;
}

weak_alias(__fgetwc_unlocked,fgetwc_unlocked);
weak_alias(__fgetwc_unlocked,getwc_unlocked);
#ifndef __UCLIBC_HAS_THREADS__
weak_alias(__fgetwc_unlocked,fgetwc);
weak_alias(__fgetwc_unlocked,getwc);
#endif

#elif defined __UCLIBC_HAS_THREADS__

wint_t fgetwc(register FILE *stream)
{
	wint_t retval;
	__STDIO_AUTO_THREADLOCK_VAR;

	__STDIO_AUTO_THREADLOCK(stream);

	retval = __fgetwc_unlocked(stream);

	__STDIO_AUTO_THREADUNLOCK(stream);

	return retval;
}

weak_alias(fgetwc,getwc);

#endif