xref: /linux/tools/include/nolibc/stdio.h (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
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))
fdopen(int fd,const char * mode)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))
fileno(FILE * stream)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))
fflush(FILE * stream)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))
fclose(FILE * stream)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))
fgetc(FILE * stream)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))
getchar(void)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))
fputc(int c,FILE * stream)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))
putchar(int c)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))
_fwrite(const void * buf,size_t size,FILE * stream)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))
fwrite(const void * s,size_t size,size_t nmemb,FILE * stream)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))
fputs(const char * s,FILE * stream)175 int fputs(const char *s, FILE *stream)
176 {
177 	return _fwrite(s, strlen(s), stream);
178 }
179 
180 static __attribute__((unused))
puts(const char * s)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))
fgets(char * s,int size,FILE * stream)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)))
vfprintf(FILE * stream,const char * fmt,va_list args)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)))
vprintf(const char * fmt,va_list args)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)))
fprintf(FILE * stream,const char * fmt,...)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)))
printf(const char * fmt,...)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))
perror(const char * msg)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))
setvbuf(FILE * stream,char * buf,int mode,size_t size)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 static __attribute__((unused))
strerror(int errno)380 const char *strerror(int errno)
381 {
382 	static char buf[18] = "errno=";
383 
384 	i64toa_r(errno, &buf[6]);
385 
386 	return buf;
387 }
388 
389 /* make sure to include all global symbols */
390 #include "nolibc.h"
391 
392 #endif /* _NOLIBC_STDIO_H */
393