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 */
25 typedef struct FILE {
26 	char dummy[1];
27 } FILE;
28 
29 /* We define the 3 common stdio files as constant invalid pointers that
30  * are easily recognized.
31  */
32 static __attribute__((unused)) FILE* const stdin  = (FILE*)-3;
33 static __attribute__((unused)) FILE* const stdout = (FILE*)-2;
34 static __attribute__((unused)) FILE* const stderr = (FILE*)-1;
35 
36 /* getc(), fgetc(), getchar() */
37 
38 #define getc(stream) fgetc(stream)
39 
40 static __attribute__((unused))
fgetc(FILE * stream)41 int fgetc(FILE* stream)
42 {
43 	unsigned char ch;
44 	int fd;
45 
46 	if (stream < stdin || stream > stderr)
47 		return EOF;
48 
49 	fd = 3 + (long)stream;
50 
51 	if (read(fd, &ch, 1) <= 0)
52 		return EOF;
53 	return ch;
54 }
55 
56 static __attribute__((unused))
getchar(void)57 int getchar(void)
58 {
59 	return fgetc(stdin);
60 }
61 
62 
63 /* putc(), fputc(), putchar() */
64 
65 #define putc(c, stream) fputc(c, stream)
66 
67 static __attribute__((unused))
fputc(int c,FILE * stream)68 int fputc(int c, FILE* stream)
69 {
70 	unsigned char ch = c;
71 	int fd;
72 
73 	if (stream < stdin || stream > stderr)
74 		return EOF;
75 
76 	fd = 3 + (long)stream;
77 
78 	if (write(fd, &ch, 1) <= 0)
79 		return EOF;
80 	return ch;
81 }
82 
83 static __attribute__((unused))
putchar(int c)84 int putchar(int c)
85 {
86 	return fputc(c, stdout);
87 }
88 
89 
90 /* fwrite(), puts(), fputs(). Note that puts() emits '\n' but not fputs(). */
91 
92 /* internal fwrite()-like function which only takes a size and returns 0 on
93  * success or EOF on error. It automatically retries on short writes.
94  */
95 static __attribute__((unused))
_fwrite(const void * buf,size_t size,FILE * stream)96 int _fwrite(const void *buf, size_t size, FILE *stream)
97 {
98 	ssize_t ret;
99 	int fd;
100 
101 	if (stream < stdin || stream > stderr)
102 		return EOF;
103 
104 	fd = 3 + (long)stream;
105 
106 	while (size) {
107 		ret = write(fd, buf, size);
108 		if (ret <= 0)
109 			return EOF;
110 		size -= ret;
111 		buf += ret;
112 	}
113 	return 0;
114 }
115 
116 static __attribute__((unused))
fwrite(const void * s,size_t size,size_t nmemb,FILE * stream)117 size_t fwrite(const void *s, size_t size, size_t nmemb, FILE *stream)
118 {
119 	size_t written;
120 
121 	for (written = 0; written < nmemb; written++) {
122 		if (_fwrite(s, size, stream) != 0)
123 			break;
124 		s += size;
125 	}
126 	return written;
127 }
128 
129 static __attribute__((unused))
fputs(const char * s,FILE * stream)130 int fputs(const char *s, FILE *stream)
131 {
132 	return _fwrite(s, strlen(s), stream);
133 }
134 
135 static __attribute__((unused))
puts(const char * s)136 int puts(const char *s)
137 {
138 	if (fputs(s, stdout) == EOF)
139 		return EOF;
140 	return putchar('\n');
141 }
142 
143 
144 /* fgets() */
145 static __attribute__((unused))
fgets(char * s,int size,FILE * stream)146 char *fgets(char *s, int size, FILE *stream)
147 {
148 	int ofs;
149 	int c;
150 
151 	for (ofs = 0; ofs + 1 < size;) {
152 		c = fgetc(stream);
153 		if (c == EOF)
154 			break;
155 		s[ofs++] = c;
156 		if (c == '\n')
157 			break;
158 	}
159 	if (ofs < size)
160 		s[ofs] = 0;
161 	return ofs ? s : NULL;
162 }
163 
164 
165 /* minimal vfprintf(). It supports the following formats:
166  *  - %[l*]{d,u,c,x,p}
167  *  - %s
168  *  - unknown modifiers are ignored.
169  */
170 static __attribute__((unused))
vfprintf(FILE * stream,const char * fmt,va_list args)171 int vfprintf(FILE *stream, const char *fmt, va_list args)
172 {
173 	char escape, lpref, c;
174 	unsigned long long v;
175 	unsigned int written;
176 	size_t len, ofs;
177 	char tmpbuf[21];
178 	const char *outstr;
179 
180 	written = ofs = escape = lpref = 0;
181 	while (1) {
182 		c = fmt[ofs++];
183 
184 		if (escape) {
185 			/* we're in an escape sequence, ofs == 1 */
186 			escape = 0;
187 			if (c == 'c' || c == 'd' || c == 'u' || c == 'x' || c == 'p') {
188 				char *out = tmpbuf;
189 
190 				if (c == 'p')
191 					v = va_arg(args, unsigned long);
192 				else if (lpref) {
193 					if (lpref > 1)
194 						v = va_arg(args, unsigned long long);
195 					else
196 						v = va_arg(args, unsigned long);
197 				} else
198 					v = va_arg(args, unsigned int);
199 
200 				if (c == 'd') {
201 					/* sign-extend the value */
202 					if (lpref == 0)
203 						v = (long long)(int)v;
204 					else if (lpref == 1)
205 						v = (long long)(long)v;
206 				}
207 
208 				switch (c) {
209 				case 'c':
210 					out[0] = v;
211 					out[1] = 0;
212 					break;
213 				case 'd':
214 					i64toa_r(v, out);
215 					break;
216 				case 'u':
217 					u64toa_r(v, out);
218 					break;
219 				case 'p':
220 					*(out++) = '0';
221 					*(out++) = 'x';
222 					/* fall through */
223 				default: /* 'x' and 'p' above */
224 					u64toh_r(v, out);
225 					break;
226 				}
227 				outstr = tmpbuf;
228 			}
229 			else if (c == 's') {
230 				outstr = va_arg(args, char *);
231 				if (!outstr)
232 					outstr="(null)";
233 			}
234 			else if (c == '%') {
235 				/* queue it verbatim */
236 				continue;
237 			}
238 			else {
239 				/* modifiers or final 0 */
240 				if (c == 'l') {
241 					/* long format prefix, maintain the escape */
242 					lpref++;
243 				}
244 				escape = 1;
245 				goto do_escape;
246 			}
247 			len = strlen(outstr);
248 			goto flush_str;
249 		}
250 
251 		/* not an escape sequence */
252 		if (c == 0 || c == '%') {
253 			/* flush pending data on escape or end */
254 			escape = 1;
255 			lpref = 0;
256 			outstr = fmt;
257 			len = ofs - 1;
258 		flush_str:
259 			if (_fwrite(outstr, len, stream) != 0)
260 				break;
261 
262 			written += len;
263 		do_escape:
264 			if (c == 0)
265 				break;
266 			fmt += ofs;
267 			ofs = 0;
268 			continue;
269 		}
270 
271 		/* literal char, just queue it */
272 	}
273 	return written;
274 }
275 
276 static __attribute__((unused, format(printf, 2, 3)))
fprintf(FILE * stream,const char * fmt,...)277 int fprintf(FILE *stream, const char *fmt, ...)
278 {
279 	va_list args;
280 	int ret;
281 
282 	va_start(args, fmt);
283 	ret = vfprintf(stream, fmt, args);
284 	va_end(args);
285 	return ret;
286 }
287 
288 static __attribute__((unused, format(printf, 1, 2)))
printf(const char * fmt,...)289 int printf(const char *fmt, ...)
290 {
291 	va_list args;
292 	int ret;
293 
294 	va_start(args, fmt);
295 	ret = vfprintf(stdout, fmt, args);
296 	va_end(args);
297 	return ret;
298 }
299 
300 static __attribute__((unused))
perror(const char * msg)301 void perror(const char *msg)
302 {
303 	fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno);
304 }
305 
306 /* make sure to include all global symbols */
307 #include "nolibc.h"
308 
309 #endif /* _NOLIBC_STDIO_H */
310