xref: /openbmc/linux/tools/include/nolibc/stdio.h (revision 88a6f899)
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  */
29 typedef struct FILE {
30 	char dummy[1];
31 } FILE;
32 
33 static __attribute__((unused)) FILE* const stdin  = (FILE*)(intptr_t)~STDIN_FILENO;
34 static __attribute__((unused)) FILE* const stdout = (FILE*)(intptr_t)~STDOUT_FILENO;
35 static __attribute__((unused)) FILE* const stderr = (FILE*)(intptr_t)~STDERR_FILENO;
36 
37 /* provides a FILE* equivalent of fd. The mode is ignored. */
38 static __attribute__((unused))
39 FILE *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. */
49 static __attribute__((unused))
50 int 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. */
62 static __attribute__((unused))
63 int 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. */
78 static __attribute__((unused))
79 int 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 
98 static __attribute__((unused))
99 int 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 
108 static __attribute__((unused))
109 int getchar(void)
110 {
111 	return fgetc(stdin);
112 }
113 
114 
115 /* putc(), fputc(), putchar() */
116 
117 #define putc(c, stream) fputc(c, stream)
118 
119 static __attribute__((unused))
120 int 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 
129 static __attribute__((unused))
130 int 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  */
141 static __attribute__((unused))
142 int _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 
157 static __attribute__((unused))
158 size_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 
170 static __attribute__((unused))
171 int fputs(const char *s, FILE *stream)
172 {
173 	return _fwrite(s, strlen(s), stream);
174 }
175 
176 static __attribute__((unused))
177 int puts(const char *s)
178 {
179 	if (fputs(s, stdout) == EOF)
180 		return EOF;
181 	return putchar('\n');
182 }
183 
184 
185 /* fgets() */
186 static __attribute__((unused))
187 char *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  */
211 static __attribute__((unused))
212 int 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 
317 static __attribute__((unused))
318 int vprintf(const char *fmt, va_list args)
319 {
320 	return vfprintf(stdout, fmt, args);
321 }
322 
323 static __attribute__((unused, format(printf, 2, 3)))
324 int 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 
335 static __attribute__((unused, format(printf, 1, 2)))
336 int 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 
347 static __attribute__((unused))
348 void 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 */
357