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 /* make sure to include all global symbols */ 8 #include "nolibc.h" 9 10 #ifndef _NOLIBC_STDIO_H 11 #define _NOLIBC_STDIO_H 12 13 #include "std.h" 14 #include "arch.h" 15 #include "errno.h" 16 #include "types.h" 17 #include "sys.h" 18 #include "stdarg.h" 19 #include "stdlib.h" 20 #include "string.h" 21 #include "compiler.h" 22 23 static const char *strerror(int errnum); 24 25 #ifndef EOF 26 #define EOF (-1) 27 #endif 28 29 /* Buffering mode used by setvbuf. */ 30 #define _IOFBF 0 /* Fully buffered. */ 31 #define _IOLBF 1 /* Line buffered. */ 32 #define _IONBF 2 /* No buffering. */ 33 34 /* just define FILE as a non-empty type. The value of the pointer gives 35 * the FD: FILE=~fd for fd>=0 or NULL for fd<0. This way positive FILE 36 * are immediately identified as abnormal entries (i.e. possible copies 37 * of valid pointers to something else). 38 */ 39 typedef struct FILE { 40 char dummy[1]; 41 } FILE; 42 43 static __attribute__((unused)) FILE* const stdin = (FILE*)(intptr_t)~STDIN_FILENO; 44 static __attribute__((unused)) FILE* const stdout = (FILE*)(intptr_t)~STDOUT_FILENO; 45 static __attribute__((unused)) FILE* const stderr = (FILE*)(intptr_t)~STDERR_FILENO; 46 47 /* provides a FILE* equivalent of fd. The mode is ignored. */ 48 static __attribute__((unused)) 49 FILE *fdopen(int fd, const char *mode __attribute__((unused))) 50 { 51 if (fd < 0) { 52 SET_ERRNO(EBADF); 53 return NULL; 54 } 55 return (FILE*)(intptr_t)~fd; 56 } 57 58 /* provides the fd of stream. */ 59 static __attribute__((unused)) 60 int fileno(FILE *stream) 61 { 62 intptr_t i = (intptr_t)stream; 63 64 if (i >= 0) { 65 SET_ERRNO(EBADF); 66 return -1; 67 } 68 return ~i; 69 } 70 71 /* flush a stream. */ 72 static __attribute__((unused)) 73 int fflush(FILE *stream) 74 { 75 intptr_t i = (intptr_t)stream; 76 77 /* NULL is valid here. */ 78 if (i > 0) { 79 SET_ERRNO(EBADF); 80 return -1; 81 } 82 83 /* Don't do anything, nolibc does not support buffering. */ 84 return 0; 85 } 86 87 /* flush a stream. */ 88 static __attribute__((unused)) 89 int fclose(FILE *stream) 90 { 91 intptr_t i = (intptr_t)stream; 92 93 if (i >= 0) { 94 SET_ERRNO(EBADF); 95 return -1; 96 } 97 98 if (close(~i)) 99 return EOF; 100 101 return 0; 102 } 103 104 /* getc(), fgetc(), getchar() */ 105 106 #define getc(stream) fgetc(stream) 107 108 static __attribute__((unused)) 109 int fgetc(FILE* stream) 110 { 111 unsigned char ch; 112 113 if (read(fileno(stream), &ch, 1) <= 0) 114 return EOF; 115 return ch; 116 } 117 118 static __attribute__((unused)) 119 int getchar(void) 120 { 121 return fgetc(stdin); 122 } 123 124 125 /* putc(), fputc(), putchar() */ 126 127 #define putc(c, stream) fputc(c, stream) 128 129 static __attribute__((unused)) 130 int fputc(int c, FILE* stream) 131 { 132 unsigned char ch = c; 133 134 if (write(fileno(stream), &ch, 1) <= 0) 135 return EOF; 136 return ch; 137 } 138 139 static __attribute__((unused)) 140 int putchar(int c) 141 { 142 return fputc(c, stdout); 143 } 144 145 146 /* fwrite(), puts(), fputs(). Note that puts() emits '\n' but not fputs(). */ 147 148 /* internal fwrite()-like function which only takes a size and returns 0 on 149 * success or EOF on error. It automatically retries on short writes. 150 */ 151 static __attribute__((unused)) 152 int _fwrite(const void *buf, size_t size, FILE *stream) 153 { 154 ssize_t ret; 155 int fd = fileno(stream); 156 157 while (size) { 158 ret = write(fd, buf, size); 159 if (ret <= 0) 160 return EOF; 161 size -= ret; 162 buf += ret; 163 } 164 return 0; 165 } 166 167 static __attribute__((unused)) 168 size_t fwrite(const void *s, size_t size, size_t nmemb, FILE *stream) 169 { 170 size_t written; 171 172 for (written = 0; written < nmemb; written++) { 173 if (_fwrite(s, size, stream) != 0) 174 break; 175 s += size; 176 } 177 return written; 178 } 179 180 static __attribute__((unused)) 181 int fputs(const char *s, FILE *stream) 182 { 183 return _fwrite(s, strlen(s), stream); 184 } 185 186 static __attribute__((unused)) 187 int puts(const char *s) 188 { 189 if (fputs(s, stdout) == EOF) 190 return EOF; 191 return putchar('\n'); 192 } 193 194 195 /* fgets() */ 196 static __attribute__((unused)) 197 char *fgets(char *s, int size, FILE *stream) 198 { 199 int ofs; 200 int c; 201 202 for (ofs = 0; ofs + 1 < size;) { 203 c = fgetc(stream); 204 if (c == EOF) 205 break; 206 s[ofs++] = c; 207 if (c == '\n') 208 break; 209 } 210 if (ofs < size) 211 s[ofs] = 0; 212 return ofs ? s : NULL; 213 } 214 215 216 /* minimal printf(). It supports the following formats: 217 * - %[l*]{d,u,c,x,p} 218 * - %s 219 * - unknown modifiers are ignored. 220 */ 221 typedef int (*__nolibc_printf_cb)(intptr_t state, const char *buf, size_t size); 222 223 static __attribute__((unused, format(printf, 4, 0))) 224 int __nolibc_printf(__nolibc_printf_cb cb, intptr_t state, size_t n, const char *fmt, va_list args) 225 { 226 char escape, lpref, c; 227 unsigned long long v; 228 unsigned int written, width; 229 size_t len, ofs, w; 230 char tmpbuf[21]; 231 const char *outstr; 232 233 written = ofs = escape = lpref = 0; 234 while (1) { 235 c = fmt[ofs++]; 236 width = 0; 237 238 if (escape) { 239 /* we're in an escape sequence, ofs == 1 */ 240 escape = 0; 241 242 /* width */ 243 while (c >= '0' && c <= '9') { 244 width *= 10; 245 width += c - '0'; 246 247 c = fmt[ofs++]; 248 } 249 250 if (c == 'c' || c == 'd' || c == 'u' || c == 'x' || c == 'p') { 251 char *out = tmpbuf; 252 253 if (c == 'p') 254 v = va_arg(args, unsigned long); 255 else if (lpref) { 256 if (lpref > 1) 257 v = va_arg(args, unsigned long long); 258 else 259 v = va_arg(args, unsigned long); 260 } else 261 v = va_arg(args, unsigned int); 262 263 if (c == 'd') { 264 /* sign-extend the value */ 265 if (lpref == 0) 266 v = (long long)(int)v; 267 else if (lpref == 1) 268 v = (long long)(long)v; 269 } 270 271 switch (c) { 272 case 'c': 273 out[0] = v; 274 out[1] = 0; 275 break; 276 case 'd': 277 i64toa_r(v, out); 278 break; 279 case 'u': 280 u64toa_r(v, out); 281 break; 282 case 'p': 283 *(out++) = '0'; 284 *(out++) = 'x'; 285 __nolibc_fallthrough; 286 default: /* 'x' and 'p' above */ 287 u64toh_r(v, out); 288 break; 289 } 290 outstr = tmpbuf; 291 } 292 else if (c == 's') { 293 outstr = va_arg(args, char *); 294 if (!outstr) 295 outstr="(null)"; 296 } 297 #ifndef NOLIBC_IGNORE_ERRNO 298 else if (c == 'm') { 299 outstr = strerror(errno); 300 } 301 #endif /* NOLIBC_IGNORE_ERRNO */ 302 else if (c == '%') { 303 /* queue it verbatim */ 304 continue; 305 } 306 else { 307 /* modifiers or final 0 */ 308 if (c == 'l') { 309 /* long format prefix, maintain the escape */ 310 lpref++; 311 } else if (c == 'j') { 312 lpref = 2; 313 } 314 escape = 1; 315 goto do_escape; 316 } 317 len = strlen(outstr); 318 goto flush_str; 319 } 320 321 /* not an escape sequence */ 322 if (c == 0 || c == '%') { 323 /* flush pending data on escape or end */ 324 escape = 1; 325 lpref = 0; 326 outstr = fmt; 327 len = ofs - 1; 328 flush_str: 329 if (n) { 330 w = len < n ? len : n; 331 n -= w; 332 while (width-- > w) { 333 if (cb(state, " ", 1) != 0) 334 break; 335 written += 1; 336 } 337 if (cb(state, outstr, w) != 0) 338 break; 339 } 340 341 written += len; 342 do_escape: 343 if (c == 0) 344 break; 345 fmt += ofs; 346 ofs = 0; 347 continue; 348 } 349 350 /* literal char, just queue it */ 351 } 352 return written; 353 } 354 355 static int __nolibc_fprintf_cb(intptr_t state, const char *buf, size_t size) 356 { 357 return _fwrite(buf, size, (FILE *)state); 358 } 359 360 static __attribute__((unused, format(printf, 2, 0))) 361 int vfprintf(FILE *stream, const char *fmt, va_list args) 362 { 363 return __nolibc_printf(__nolibc_fprintf_cb, (intptr_t)stream, SIZE_MAX, fmt, args); 364 } 365 366 static __attribute__((unused, format(printf, 1, 0))) 367 int vprintf(const char *fmt, va_list args) 368 { 369 return vfprintf(stdout, fmt, args); 370 } 371 372 static __attribute__((unused, format(printf, 2, 3))) 373 int fprintf(FILE *stream, const char *fmt, ...) 374 { 375 va_list args; 376 int ret; 377 378 va_start(args, fmt); 379 ret = vfprintf(stream, fmt, args); 380 va_end(args); 381 return ret; 382 } 383 384 static __attribute__((unused, format(printf, 1, 2))) 385 int printf(const char *fmt, ...) 386 { 387 va_list args; 388 int ret; 389 390 va_start(args, fmt); 391 ret = vfprintf(stdout, fmt, args); 392 va_end(args); 393 return ret; 394 } 395 396 static __attribute__((unused, format(printf, 2, 0))) 397 int vdprintf(int fd, const char *fmt, va_list args) 398 { 399 FILE *stream; 400 401 stream = fdopen(fd, NULL); 402 if (!stream) 403 return -1; 404 /* Technically 'stream' is leaked, but as it's only a wrapper around 'fd' that is fine */ 405 return vfprintf(stream, fmt, args); 406 } 407 408 static __attribute__((unused, format(printf, 2, 3))) 409 int dprintf(int fd, const char *fmt, ...) 410 { 411 va_list args; 412 int ret; 413 414 va_start(args, fmt); 415 ret = vdprintf(fd, fmt, args); 416 va_end(args); 417 418 return ret; 419 } 420 421 static int __nolibc_sprintf_cb(intptr_t _state, const char *buf, size_t size) 422 { 423 char **state = (char **)_state; 424 425 memcpy(*state, buf, size); 426 *state += size; 427 return 0; 428 } 429 430 static __attribute__((unused, format(printf, 3, 0))) 431 int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) 432 { 433 char *state = buf; 434 int ret; 435 436 ret = __nolibc_printf(__nolibc_sprintf_cb, (intptr_t)&state, size, fmt, args); 437 if (ret < 0) 438 return ret; 439 buf[(size_t)ret < size ? (size_t)ret : size - 1] = '\0'; 440 return ret; 441 } 442 443 static __attribute__((unused, format(printf, 3, 4))) 444 int snprintf(char *buf, size_t size, const char *fmt, ...) 445 { 446 va_list args; 447 int ret; 448 449 va_start(args, fmt); 450 ret = vsnprintf(buf, size, fmt, args); 451 va_end(args); 452 453 return ret; 454 } 455 456 static __attribute__((unused, format(printf, 2, 0))) 457 int vsprintf(char *buf, const char *fmt, va_list args) 458 { 459 return vsnprintf(buf, SIZE_MAX, fmt, args); 460 } 461 462 static __attribute__((unused, format(printf, 2, 3))) 463 int sprintf(char *buf, const char *fmt, ...) 464 { 465 va_list args; 466 int ret; 467 468 va_start(args, fmt); 469 ret = vsprintf(buf, fmt, args); 470 va_end(args); 471 472 return ret; 473 } 474 475 static __attribute__((unused)) 476 int vsscanf(const char *str, const char *format, va_list args) 477 { 478 uintmax_t uval; 479 intmax_t ival; 480 int base; 481 char *endptr; 482 int matches; 483 int lpref; 484 485 matches = 0; 486 487 while (1) { 488 if (*format == '%') { 489 /* start of pattern */ 490 lpref = 0; 491 format++; 492 493 if (*format == 'l') { 494 /* same as in printf() */ 495 lpref = 1; 496 format++; 497 if (*format == 'l') { 498 lpref = 2; 499 format++; 500 } 501 } 502 503 if (*format == '%') { 504 /* literal % */ 505 if ('%' != *str) 506 goto done; 507 str++; 508 format++; 509 continue; 510 } else if (*format == 'd') { 511 ival = strtoll(str, &endptr, 10); 512 if (lpref == 0) 513 *va_arg(args, int *) = ival; 514 else if (lpref == 1) 515 *va_arg(args, long *) = ival; 516 else if (lpref == 2) 517 *va_arg(args, long long *) = ival; 518 } else if (*format == 'u' || *format == 'x' || *format == 'X') { 519 base = *format == 'u' ? 10 : 16; 520 uval = strtoull(str, &endptr, base); 521 if (lpref == 0) 522 *va_arg(args, unsigned int *) = uval; 523 else if (lpref == 1) 524 *va_arg(args, unsigned long *) = uval; 525 else if (lpref == 2) 526 *va_arg(args, unsigned long long *) = uval; 527 } else if (*format == 'p') { 528 *va_arg(args, void **) = (void *)strtoul(str, &endptr, 16); 529 } else { 530 SET_ERRNO(EILSEQ); 531 goto done; 532 } 533 534 format++; 535 str = endptr; 536 matches++; 537 538 } else if (*format == '\0') { 539 goto done; 540 } else if (isspace(*format)) { 541 /* skip spaces in format and str */ 542 while (isspace(*format)) 543 format++; 544 while (isspace(*str)) 545 str++; 546 } else if (*format == *str) { 547 /* literal match */ 548 format++; 549 str++; 550 } else { 551 if (!matches) 552 matches = EOF; 553 goto done; 554 } 555 } 556 557 done: 558 return matches; 559 } 560 561 static __attribute__((unused, format(scanf, 2, 3))) 562 int sscanf(const char *str, const char *format, ...) 563 { 564 va_list args; 565 int ret; 566 567 va_start(args, format); 568 ret = vsscanf(str, format, args); 569 va_end(args); 570 return ret; 571 } 572 573 static __attribute__((unused)) 574 void perror(const char *msg) 575 { 576 fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno); 577 } 578 579 static __attribute__((unused)) 580 int setvbuf(FILE *stream __attribute__((unused)), 581 char *buf __attribute__((unused)), 582 int mode, 583 size_t size __attribute__((unused))) 584 { 585 /* 586 * nolibc does not support buffering so this is a nop. Just check mode 587 * is valid as required by the spec. 588 */ 589 switch (mode) { 590 case _IOFBF: 591 case _IOLBF: 592 case _IONBF: 593 break; 594 default: 595 return EOF; 596 } 597 598 return 0; 599 } 600 601 static __attribute__((unused)) 602 const char *strerror(int errno) 603 { 604 static char buf[18] = "errno="; 605 606 i64toa_r(errno, &buf[6]); 607 608 return buf; 609 } 610 611 #endif /* _NOLIBC_STDIO_H */ 612