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)) 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)) 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)) 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)) 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)) 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)) 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)) 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)) 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)) 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)) 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)) 175 int fputs(const char *s, FILE *stream) 176 { 177 return _fwrite(s, strlen(s), stream); 178 } 179 180 static __attribute__((unused)) 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)) 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))) 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))) 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))) 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))) 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)) 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)) 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 /* make sure to include all global symbols */ 380 #include "nolibc.h" 381 382 #endif /* _NOLIBC_STDIO_H */ 383