Merge branches 'pm-cpuidle', 'pm-core' and 'pm-sleep'
[linux-block.git] / tools / include / nolibc / stdio.h
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))
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))
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))
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))
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))
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))
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))
130 int fputs(const char *s, FILE *stream)
131 {
132         return _fwrite(s, strlen(s), stream);
133 }
134
135 static __attribute__((unused))
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))
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))
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)))
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)))
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))
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 */