Loading...
Note: File does not exist in v5.4.
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 */
25typedef 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 */
32static __attribute__((unused)) FILE* const stdin = (FILE*)-3;
33static __attribute__((unused)) FILE* const stdout = (FILE*)-2;
34static __attribute__((unused)) FILE* const stderr = (FILE*)-1;
35
36/* getc(), fgetc(), getchar() */
37
38#define getc(stream) fgetc(stream)
39
40static __attribute__((unused))
41int 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
56static __attribute__((unused))
57int getchar(void)
58{
59 return fgetc(stdin);
60}
61
62
63/* putc(), fputc(), putchar() */
64
65#define putc(c, stream) fputc(c, stream)
66
67static __attribute__((unused))
68int 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
83static __attribute__((unused))
84int 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 */
95static __attribute__((unused))
96int _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
116static __attribute__((unused))
117size_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
129static __attribute__((unused))
130int fputs(const char *s, FILE *stream)
131{
132 return _fwrite(s, strlen(s), stream);
133}
134
135static __attribute__((unused))
136int puts(const char *s)
137{
138 if (fputs(s, stdout) == EOF)
139 return EOF;
140 return putchar('\n');
141}
142
143
144/* fgets() */
145static __attribute__((unused))
146char *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 */
170static __attribute__((unused))
171int 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
276static __attribute__((unused, format(printf, 2, 3)))
277int 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
288static __attribute__((unused, format(printf, 1, 2)))
289int 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
300static __attribute__((unused))
301void 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 */