Serenity Operating System
1/*
2 * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/Assertions.h>
8#include <AK/BitCast.h>
9#include <AK/PrintfImplementation.h>
10#include <AK/StringBuilder.h>
11#include <AK/Types.h>
12#include <bits/stdio_file_implementation.h>
13#include <errno.h>
14#include <stdio.h>
15#include <wchar.h>
16
17static_assert(AssertSize<wchar_t, sizeof(u32)>());
18
19extern "C" {
20
21// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fwide.html
22int fwide(FILE*, int mode)
23{
24 // Nope Nope Nope.
25 return mode;
26}
27
28// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fgetwc.html
29wint_t fgetwc(FILE* stream)
30{
31 VERIFY(stream);
32 Array<u8, 4> underlying;
33 auto underlying_bytes = underlying.span();
34 size_t encoded_length = 0;
35 size_t bytes_read = 0;
36 do {
37 size_t nread = fread(underlying_bytes.offset_pointer(bytes_read), 1, 1, stream);
38 if (nread != 1) {
39 errno = EILSEQ;
40 return WEOF;
41 }
42 ++bytes_read;
43 if (bytes_read == 1) {
44 if (underlying[0] >> 7 == 0) {
45 encoded_length = 1;
46 } else if (underlying[0] >> 5 == 6) {
47 encoded_length = 2;
48 } else if (underlying[0] >> 4 == 0xe) {
49 encoded_length = 3;
50 } else if (underlying[0] >> 3 == 0x1e) {
51 encoded_length = 4;
52 } else {
53 errno = EILSEQ;
54 return WEOF;
55 }
56 }
57 } while (bytes_read < encoded_length);
58
59 wchar_t code_point;
60 auto read_bytes = mbrtowc(&code_point, bit_cast<char const*>(underlying.data()), encoded_length, nullptr);
61 VERIFY(read_bytes == encoded_length);
62 return code_point;
63}
64
65// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getwc.html
66wint_t getwc(FILE* stream)
67{
68 return fgetwc(stream);
69}
70
71// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getwchar.html
72wint_t getwchar()
73{
74 return getwc(stdin);
75}
76
77// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fputwc.html
78wint_t fputwc(wchar_t wc, FILE* stream)
79{
80 VERIFY(stream);
81 // Negative wide chars are weird
82 if constexpr (IsSigned<wchar_t>) {
83 if (wc < 0) {
84 errno = EILSEQ;
85 return WEOF;
86 }
87 }
88 StringBuilder sb;
89 sb.append_code_point(static_cast<u32>(wc));
90 auto bytes = sb.string_view().bytes();
91 ScopedFileLock lock(stream);
92 size_t nwritten = stream->write(bytes.data(), bytes.size());
93 if (nwritten < bytes.size())
94 return WEOF;
95 return wc;
96}
97
98// https://pubs.opengroup.org/onlinepubs/9699919799/functions/putwc.html
99wint_t putwc(wchar_t wc, FILE* stream)
100{
101 return fputwc(wc, stream);
102}
103
104// https://pubs.opengroup.org/onlinepubs/9699919799/functions/putwchar.html
105wint_t putwchar(wchar_t wc)
106{
107 return fputwc(wc, stdout);
108}
109
110// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fgetws.html
111wchar_t* fgetws(wchar_t* __restrict buffer, int size, FILE* __restrict stream)
112{
113 VERIFY(stream);
114 ScopedFileLock lock(stream);
115 bool ok = stream->gets(bit_cast<u32*>(buffer), size);
116 return ok ? buffer : nullptr;
117}
118
119// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fputws.html
120int fputws(wchar_t const* __restrict ws, FILE* __restrict stream)
121{
122 VERIFY(stream);
123 ScopedFileLock lock(stream);
124 int size = 0;
125 for (auto const* p = ws; *p != 0; ++p, ++size) {
126 if (putwc(*p, stream) == WEOF)
127 return WEOF;
128 }
129 return size;
130}
131
132// https://pubs.opengroup.org/onlinepubs/9699919799/functions/ungetwc.html
133wint_t ungetwc(wint_t wc, FILE* stream)
134{
135 VERIFY(stream);
136 ScopedFileLock lock(stream);
137 StringBuilder sb;
138 sb.append_code_point(static_cast<u32>(wc));
139 auto bytes = sb.string_view().bytes();
140 size_t ok_bytes = 0;
141 for (auto byte : bytes) {
142 if (!stream->ungetc(byte)) {
143 // Discard the half-ungotten bytes.
144 stream->read(const_cast<u8*>(bytes.data()), ok_bytes);
145 return WEOF;
146 }
147 ++ok_bytes;
148 }
149 return wc;
150}
151
152// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wprintf.html
153int wprintf(wchar_t const* __restrict format, ...)
154{
155 va_list ap;
156 va_start(ap, format);
157 auto rc = vfwprintf(stdout, format, ap);
158 va_end(ap);
159 return rc;
160}
161
162// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fwprintf.html
163int fwprintf(FILE* __restrict stream, wchar_t const* __restrict format, ...)
164{
165 va_list ap;
166 va_start(ap, format);
167 auto rc = vfwprintf(stream, format, ap);
168 va_end(ap);
169 return rc;
170}
171
172// https://pubs.opengroup.org/onlinepubs/9699919799/functions/swprintf.html
173int swprintf(wchar_t* __restrict wcs, size_t max_length, wchar_t const* __restrict format, ...)
174{
175 va_list ap;
176 va_start(ap, format);
177 auto rc = vswprintf(wcs, max_length, format, ap);
178 va_end(ap);
179 return rc;
180}
181
182// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vwprintf.html
183int vwprintf(wchar_t const* __restrict format, va_list args)
184{
185 return vfwprintf(stdout, format, args);
186}
187
188// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vfwprintf.html
189int vfwprintf(FILE* __restrict stream, wchar_t const* __restrict format, va_list args)
190{
191 auto const* fmt = bit_cast<wchar_t const*>(format);
192 return printf_internal([stream](wchar_t*&, wchar_t wc) {
193 putwc(wc, stream);
194 },
195 nullptr, fmt, args);
196}
197
198// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vswprintf.html
199int vswprintf(wchar_t* __restrict wcs, size_t max_length, wchar_t const* __restrict format, va_list args)
200{
201 auto const* fmt = bit_cast<wchar_t const*>(format);
202 size_t length_so_far = 0;
203 printf_internal([max_length, &length_so_far](wchar_t*& buffer, wchar_t wc) {
204 if (length_so_far > max_length)
205 return;
206 *buffer++ = wc;
207 ++length_so_far;
208 },
209 wcs, fmt, args);
210 if (length_so_far < max_length)
211 wcs[length_so_far] = L'\0';
212 else
213 wcs[max_length - 1] = L'\0';
214 return static_cast<int>(length_so_far);
215}
216
217// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fwscanf.html
218int fwscanf(FILE* __restrict stream, wchar_t const* __restrict format, ...)
219{
220 va_list ap;
221 va_start(ap, format);
222 auto rc = vfwscanf(stream, format, ap);
223 va_end(ap);
224 return rc;
225}
226
227// https://pubs.opengroup.org/onlinepubs/9699919799/functions/swscanf.html
228int swscanf(wchar_t const* __restrict ws, wchar_t const* __restrict format, ...)
229{
230 va_list ap;
231 va_start(ap, format);
232 auto rc = vswscanf(ws, format, ap);
233 va_end(ap);
234 return rc;
235}
236
237// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wscanf.html
238int wscanf(wchar_t const* __restrict format, ...)
239{
240 va_list ap;
241 va_start(ap, format);
242 auto rc = vfwscanf(stdout, format, ap);
243 va_end(ap);
244 return rc;
245}
246
247// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vfwscanf.html
248int vfwscanf(FILE* __restrict stream, wchar_t const* __restrict format, va_list arg)
249{
250 (void)stream;
251 (void)format;
252 (void)arg;
253 dbgln("FIXME: Implement vfwscanf()");
254 TODO();
255}
256
257// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vswscanf.html
258int vswscanf(wchar_t const* __restrict ws, wchar_t const* __restrict format, va_list arg)
259{
260 (void)ws;
261 (void)format;
262 (void)arg;
263 dbgln("FIXME: Implement vswscanf()");
264 TODO();
265}
266
267// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vwscanf.html
268int vwscanf(wchar_t const* __restrict format, va_list arg)
269{
270 (void)format;
271 (void)arg;
272 dbgln("FIXME: Implement vwscanf()");
273 TODO();
274}
275}