lh | 9ed821d | 2023-04-07 01:36:19 -0700 | [diff] [blame^] | 1 | /* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> |
| 2 | * |
| 3 | * GNU Library General Public License (LGPL) version 2 or later. |
| 4 | * |
| 5 | * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. |
| 6 | */ |
| 7 | |
| 8 | #include "_stdio.h" |
| 9 | |
| 10 | |
| 11 | |
| 12 | #ifdef __DO_UNLOCKED |
| 13 | |
| 14 | static void munge_stream(register FILE *stream, unsigned char *buf) |
| 15 | { |
| 16 | stream->__bufend = stream->__bufstart = buf; |
| 17 | __STDIO_STREAM_INIT_BUFREAD_BUFPOS(stream); |
| 18 | __STDIO_STREAM_DISABLE_GETC(stream); |
| 19 | __STDIO_STREAM_DISABLE_PUTC(stream); |
| 20 | } |
| 21 | |
| 22 | wint_t fgetwc_unlocked(register FILE *stream) |
| 23 | { |
| 24 | wint_t wi; |
| 25 | wchar_t wc[1]; |
| 26 | int n; |
| 27 | size_t r; |
| 28 | unsigned char sbuf[1]; |
| 29 | |
| 30 | __STDIO_STREAM_VALIDATE(stream); |
| 31 | |
| 32 | wi = WEOF; /* Prepare for failure. */ |
| 33 | |
| 34 | if (__STDIO_STREAM_IS_WIDE_READING(stream) |
| 35 | || !__STDIO_STREAM_TRANS_TO_READ(stream, __FLAG_WIDE) |
| 36 | ) { |
| 37 | if (stream->__modeflags & __FLAG_UNGOT) { /* Any ungetwc()s? */ |
| 38 | if (((stream->__modeflags & 1) || stream->__ungot[1])) { |
| 39 | stream->__ungot_width[0] = 0; /* Application ungot... */ |
| 40 | } else { /* scanf ungot */ |
| 41 | stream->__ungot_width[0] = stream->__ungot_width[1]; |
| 42 | } |
| 43 | |
| 44 | wi = stream->__ungot[(stream->__modeflags--) & 1]; |
| 45 | stream->__ungot[1] = 0; |
| 46 | goto DONE; |
| 47 | } |
| 48 | |
| 49 | if (!stream->__bufstart) { /* Ugh... stream isn't buffered! */ |
| 50 | /* Munge the stream temporarily to use a 1-byte buffer. */ |
| 51 | munge_stream(stream, sbuf); |
| 52 | ++stream->__bufend; |
| 53 | } |
| 54 | |
| 55 | if (stream->__state.__mask == 0) { /* If last was a complete char */ |
| 56 | stream->__ungot_width[0] = 0; /* then reset the width. */ |
| 57 | } |
| 58 | |
| 59 | LOOP: |
| 60 | if ((n = __STDIO_STREAM_BUFFER_RAVAIL(stream)) == 0) { |
| 61 | goto FILL_BUFFER; |
| 62 | } |
| 63 | |
| 64 | r = mbrtowc(wc, (const char*) stream->__bufpos, n, &stream->__state); |
| 65 | if (((ssize_t) r) >= 0) { /* Success... */ |
| 66 | if (r == 0) { /* Nul wide char... means 0 byte for us so */ |
| 67 | ++r; /* increment r and handle below as single. */ |
| 68 | } |
| 69 | stream->__bufpos += r; |
| 70 | stream->__ungot_width[0] += r; |
| 71 | wi = *wc; |
| 72 | goto DONE; |
| 73 | } |
| 74 | |
| 75 | if (r == ((size_t) -2)) { |
| 76 | /* Potentially valid but incomplete and no more buffered. */ |
| 77 | stream->__bufpos += n; /* Update bufpos for stream. */ |
| 78 | stream->__ungot_width[0] += n; |
| 79 | FILL_BUFFER: |
| 80 | if(__STDIO_FILL_READ_BUFFER(stream)) { /* Refill succeeded? */ |
| 81 | goto LOOP; |
| 82 | } |
| 83 | if (!__FERROR_UNLOCKED(stream)) { /* EOF with no error. */ |
| 84 | if (!stream->__state.__mask) { /* No partial wchar. */ |
| 85 | goto DONE; |
| 86 | } |
| 87 | /* EOF but partially complete wchar. */ |
| 88 | /* TODO: should EILSEQ be set? */ |
| 89 | __set_errno(EILSEQ); |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | /* If we reach here, either r == ((size_t)-1) and mbrtowc set errno |
| 94 | * to EILSEQ, or r == ((size_t)-2) and stream is in an error state |
| 95 | * or at EOF with a partially complete wchar. Make sure stream's |
| 96 | * error indicator is set. */ |
| 97 | stream->__modeflags |= __FLAG_ERROR; |
| 98 | |
| 99 | DONE: |
| 100 | if (stream->__bufstart == sbuf) { /* Need to un-munge the stream. */ |
| 101 | munge_stream(stream, NULL); |
| 102 | } |
| 103 | |
| 104 | } |
| 105 | |
| 106 | __STDIO_STREAM_VALIDATE(stream); |
| 107 | |
| 108 | return wi; |
| 109 | } |
| 110 | libc_hidden_def(fgetwc_unlocked) |
| 111 | |
| 112 | strong_alias(fgetwc_unlocked,getwc_unlocked) |
| 113 | #ifndef __UCLIBC_HAS_THREADS__ |
| 114 | strong_alias(fgetwc_unlocked,fgetwc) |
| 115 | libc_hidden_def(fgetwc) |
| 116 | |
| 117 | strong_alias(fgetwc_unlocked,getwc) |
| 118 | #endif |
| 119 | |
| 120 | #elif defined __UCLIBC_HAS_THREADS__ |
| 121 | |
| 122 | wint_t fgetwc(register FILE *stream) |
| 123 | { |
| 124 | wint_t retval; |
| 125 | __STDIO_AUTO_THREADLOCK_VAR; |
| 126 | |
| 127 | __STDIO_AUTO_THREADLOCK(stream); |
| 128 | |
| 129 | retval = fgetwc_unlocked(stream); |
| 130 | |
| 131 | __STDIO_AUTO_THREADUNLOCK(stream); |
| 132 | |
| 133 | return retval; |
| 134 | } |
| 135 | libc_hidden_def(fgetwc) |
| 136 | |
| 137 | strong_alias(fgetwc,getwc) |
| 138 | #endif |