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