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