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