Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
2/*
3 * minimal stdio function definitions for NOLIBC
4 * Copyright (C) 2017-2021 Willy Tarreau <w@1wt.eu>
5 */
6
7#ifndef _NOLIBC_STDIO_H
8#define _NOLIBC_STDIO_H
9
10#include <stdarg.h>
11
12#include "std.h"
13#include "arch.h"
14#include "errno.h"
15#include "types.h"
16#include "sys.h"
17#include "stdlib.h"
18#include "string.h"
19
20#ifndef EOF
21#define EOF (-1)
22#endif
23
24/* just define FILE as a non-empty type. The value of the pointer gives
25 * the FD: FILE=~fd for fd>=0 or NULL for fd<0. This way positive FILE
26 * are immediately identified as abnormal entries (i.e. possible copies
27 * of valid pointers to something else).
28 */
29typedef struct FILE {
30 char dummy[1];
31} FILE;
32
33static __attribute__((unused)) FILE* const stdin = (FILE*)(intptr_t)~STDIN_FILENO;
34static __attribute__((unused)) FILE* const stdout = (FILE*)(intptr_t)~STDOUT_FILENO;
35static __attribute__((unused)) FILE* const stderr = (FILE*)(intptr_t)~STDERR_FILENO;
36
37/* provides a FILE* equivalent of fd. The mode is ignored. */
38static __attribute__((unused))
39FILE *fdopen(int fd, const char *mode __attribute__((unused)))
40{
41 if (fd < 0) {
42 SET_ERRNO(EBADF);
43 return NULL;
44 }
45 return (FILE*)(intptr_t)~fd;
46}
47
48/* provides the fd of stream. */
49static __attribute__((unused))
50int fileno(FILE *stream)
51{
52 intptr_t i = (intptr_t)stream;
53
54 if (i >= 0) {
55 SET_ERRNO(EBADF);
56 return -1;
57 }
58 return ~i;
59}
60
61/* flush a stream. */
62static __attribute__((unused))
63int fflush(FILE *stream)
64{
65 intptr_t i = (intptr_t)stream;
66
67 /* NULL is valid here. */
68 if (i > 0) {
69 SET_ERRNO(EBADF);
70 return -1;
71 }
72
73 /* Don't do anything, nolibc does not support buffering. */
74 return 0;
75}
76
77/* flush a stream. */
78static __attribute__((unused))
79int fclose(FILE *stream)
80{
81 intptr_t i = (intptr_t)stream;
82
83 if (i >= 0) {
84 SET_ERRNO(EBADF);
85 return -1;
86 }
87
88 if (close(~i))
89 return EOF;
90
91 return 0;
92}
93
94/* getc(), fgetc(), getchar() */
95
96#define getc(stream) fgetc(stream)
97
98static __attribute__((unused))
99int fgetc(FILE* stream)
100{
101 unsigned char ch;
102
103 if (read(fileno(stream), &ch, 1) <= 0)
104 return EOF;
105 return ch;
106}
107
108static __attribute__((unused))
109int getchar(void)
110{
111 return fgetc(stdin);
112}
113
114
115/* putc(), fputc(), putchar() */
116
117#define putc(c, stream) fputc(c, stream)
118
119static __attribute__((unused))
120int fputc(int c, FILE* stream)
121{
122 unsigned char ch = c;
123
124 if (write(fileno(stream), &ch, 1) <= 0)
125 return EOF;
126 return ch;
127}
128
129static __attribute__((unused))
130int putchar(int c)
131{
132 return fputc(c, stdout);
133}
134
135
136/* fwrite(), puts(), fputs(). Note that puts() emits '\n' but not fputs(). */
137
138/* internal fwrite()-like function which only takes a size and returns 0 on
139 * success or EOF on error. It automatically retries on short writes.
140 */
141static __attribute__((unused))
142int _fwrite(const void *buf, size_t size, FILE *stream)
143{
144 ssize_t ret;
145 int fd = fileno(stream);
146
147 while (size) {
148 ret = write(fd, buf, size);
149 if (ret <= 0)
150 return EOF;
151 size -= ret;
152 buf += ret;
153 }
154 return 0;
155}
156
157static __attribute__((unused))
158size_t fwrite(const void *s, size_t size, size_t nmemb, FILE *stream)
159{
160 size_t written;
161
162 for (written = 0; written < nmemb; written++) {
163 if (_fwrite(s, size, stream) != 0)
164 break;
165 s += size;
166 }
167 return written;
168}
169
170static __attribute__((unused))
171int fputs(const char *s, FILE *stream)
172{
173 return _fwrite(s, strlen(s), stream);
174}
175
176static __attribute__((unused))
177int puts(const char *s)
178{
179 if (fputs(s, stdout) == EOF)
180 return EOF;
181 return putchar('\n');
182}
183
184
185/* fgets() */
186static __attribute__((unused))
187char *fgets(char *s, int size, FILE *stream)
188{
189 int ofs;
190 int c;
191
192 for (ofs = 0; ofs + 1 < size;) {
193 c = fgetc(stream);
194 if (c == EOF)
195 break;
196 s[ofs++] = c;
197 if (c == '\n')
198 break;
199 }
200 if (ofs < size)
201 s[ofs] = 0;
202 return ofs ? s : NULL;
203}
204
205
206/* minimal vfprintf(). It supports the following formats:
207 * - %[l*]{d,u,c,x,p}
208 * - %s
209 * - unknown modifiers are ignored.
210 */
211static __attribute__((unused))
212int vfprintf(FILE *stream, const char *fmt, va_list args)
213{
214 char escape, lpref, c;
215 unsigned long long v;
216 unsigned int written;
217 size_t len, ofs;
218 char tmpbuf[21];
219 const char *outstr;
220
221 written = ofs = escape = lpref = 0;
222 while (1) {
223 c = fmt[ofs++];
224
225 if (escape) {
226 /* we're in an escape sequence, ofs == 1 */
227 escape = 0;
228 if (c == 'c' || c == 'd' || c == 'u' || c == 'x' || c == 'p') {
229 char *out = tmpbuf;
230
231 if (c == 'p')
232 v = va_arg(args, unsigned long);
233 else if (lpref) {
234 if (lpref > 1)
235 v = va_arg(args, unsigned long long);
236 else
237 v = va_arg(args, unsigned long);
238 } else
239 v = va_arg(args, unsigned int);
240
241 if (c == 'd') {
242 /* sign-extend the value */
243 if (lpref == 0)
244 v = (long long)(int)v;
245 else if (lpref == 1)
246 v = (long long)(long)v;
247 }
248
249 switch (c) {
250 case 'c':
251 out[0] = v;
252 out[1] = 0;
253 break;
254 case 'd':
255 i64toa_r(v, out);
256 break;
257 case 'u':
258 u64toa_r(v, out);
259 break;
260 case 'p':
261 *(out++) = '0';
262 *(out++) = 'x';
263 /* fall through */
264 default: /* 'x' and 'p' above */
265 u64toh_r(v, out);
266 break;
267 }
268 outstr = tmpbuf;
269 }
270 else if (c == 's') {
271 outstr = va_arg(args, char *);
272 if (!outstr)
273 outstr="(null)";
274 }
275 else if (c == '%') {
276 /* queue it verbatim */
277 continue;
278 }
279 else {
280 /* modifiers or final 0 */
281 if (c == 'l') {
282 /* long format prefix, maintain the escape */
283 lpref++;
284 }
285 escape = 1;
286 goto do_escape;
287 }
288 len = strlen(outstr);
289 goto flush_str;
290 }
291
292 /* not an escape sequence */
293 if (c == 0 || c == '%') {
294 /* flush pending data on escape or end */
295 escape = 1;
296 lpref = 0;
297 outstr = fmt;
298 len = ofs - 1;
299 flush_str:
300 if (_fwrite(outstr, len, stream) != 0)
301 break;
302
303 written += len;
304 do_escape:
305 if (c == 0)
306 break;
307 fmt += ofs;
308 ofs = 0;
309 continue;
310 }
311
312 /* literal char, just queue it */
313 }
314 return written;
315}
316
317static __attribute__((unused))
318int vprintf(const char *fmt, va_list args)
319{
320 return vfprintf(stdout, fmt, args);
321}
322
323static __attribute__((unused, format(printf, 2, 3)))
324int fprintf(FILE *stream, const char *fmt, ...)
325{
326 va_list args;
327 int ret;
328
329 va_start(args, fmt);
330 ret = vfprintf(stream, fmt, args);
331 va_end(args);
332 return ret;
333}
334
335static __attribute__((unused, format(printf, 1, 2)))
336int printf(const char *fmt, ...)
337{
338 va_list args;
339 int ret;
340
341 va_start(args, fmt);
342 ret = vfprintf(stdout, fmt, args);
343 va_end(args);
344 return ret;
345}
346
347static __attribute__((unused))
348void perror(const char *msg)
349{
350 fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno);
351}
352
353/* make sure to include all global symbols */
354#include "nolibc.h"
355
356#endif /* _NOLIBC_STDIO_H */