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